Вызов BCP из python вызывает исключение тайм-аута, а затем немедленно завершается

Я запускаю скрипт Python, который вызывает подпроцесс. Время ожидания истекает, и затем я сразу же вижу завершение подпроцесса (в данном случае вызова BCP). Действительно, я вижу, что это завершено в базе данных. Кроме того, я могу запустить команду BCP прямо в командной строке, и она отлично работает.

Вот что мой скрипт Python выдает в командную строку:

C:\FaceIAPS\StudyDataFiles> py .\RUN_DATA.py
Synchronizing 80 subjects
Subject 11

Starting copy...
Traceback (most recent call last):
  File ".\RUN_DATA.py", line 261, in <module>
    bulk_import(upload_file, 'Facet_Data')
  File ".\RUN_DATA.py", line 171, in bulk_import
    subprocess.check_call("bcp BehaviorResearch.." + table_to_upload_to + " in " +     filename_to_be_uploaded + " -T -c -S
PBB-C202B-2\BEHAVIORRESEARCH -e bulk_copy_errors.log", shell=True, timeout=5)
  File "C:\Python34\lib\subprocess.py", line 554, in check_call
    retcode = call(*popenargs, **kwargs)
  File "C:\Python34\lib\subprocess.py", line 537, in call
    return p.wait(timeout=timeout)
  File "C:\Python34\lib\subprocess.py", line 1157, in wait
    raise TimeoutExpired(self.args, timeout)
subprocess.TimeoutExpired: Command 'bcp BehaviorResearch..Facet_Data in _temp_ -T -c -S     PBB-C202B-2\BEHAVIORRESEARCH -e
bulk_copy_errors.log' timed out after 5 seconds
1000 rows sent to SQL Server. Total sent: 1000
1000 rows sent to SQL Server. Total sent: 2000
PS C:\FaceIAPS\StudyDataFiles> 1000 rows sent to SQL Server. Total sent: 3000
1000 rows sent to SQL Server. Total sent: 4000
1000 rows sent to SQL Server. Total sent: 5000
1000 rows sent to SQL Server. Total sent: 6000
1000 rows sent to SQL Server. Total sent: 7000
1000 rows sent to SQL Server. Total sent: 8000
1000 rows sent to SQL Server. Total sent: 9000
1000 rows sent to SQL Server. Total sent: 10000
1000 rows sent to SQL Server. Total sent: 11000
1000 rows sent to SQL Server. Total sent: 12000
1000 rows sent to SQL Server. Total sent: 13000
1000 rows sent to SQL Server. Total sent: 14000
1000 rows sent to SQL Server. Total sent: 15000
1000 rows sent to SQL Server. Total sent: 16000

16102 rows copied.
Network packet size (bytes): 4096
Clock Time (ms.) Total     : 5164   Average : (3118.13 rows per sec.)

Как видите, командная строка разделена с выходными данными вызова BCP.
Что происходит и как это исправить?

РЕДАКТИРОВАТЬ: как я это исправил

Измените вызов подпроцесса на:

arguments = ["bcp", "BehaviorResearch.." + table_to_upload_to, "in", filename_to_be_uploaded, "-T", "-c", "-S PBB-C202B-2\BEHAVIORRESEARCH", "-e bulk_copy_errors.log"]
subprocess.call(arguments, timeout=30)

К вашему сведению для непосвященных, "в" - это собственный аргумент.


person David Maddox    schedule 25.04.2014    source источник
comment
Какой именно вопрос вы задаете?   -  person AHuman    schedule 26.04.2014


Ответы (1)


Документы для subprocess.check_call() говорят:

Аргумент тайм-аута передается в Popen.wait(). Если тайм-аут истекает, дочерний процесс будет убит, а затем снова ожидается. Исключение TimeoutExpired будет повторно вызвано после завершения дочернего процесса.

subprocess исходный код подтверждает это:

def call(*popenargs, timeout=None, **kwargs):
    with Popen(*popenargs, **kwargs) as p:
        try:
            return p.wait(timeout=timeout)
        except:
            p.kill() # kill on any exception including TimeoutExpired
            p.wait()
            raise

т. е. то, что вы видите, является ожидаемым поведением: если тайм-аут происходит, то оболочка (%COMSPEC% или cmd.exe), которая запускает процесс bcp, должна быть немедленно завершена, что, в свою очередь, может завершить сам процесс bcp.

Вы видите, что буферизованный вывод сбрасывается в консоль после того, как подпроцесс уже вышел или (я не уверен) вы видите вывод живого внучатого процесса bcp, в то время как его родительский cmd.exe уже завершен (новая подсказка Показано).

Удалите shell=True, чтобы избежать создания ненужного промежуточного процесса cmd.exe, чтобы .kill() вызывался непосредственно в процессе bcp, а не в процессе оболочки.

person jfs    schedule 26.04.2014
comment
Это правильно. По какой-то причине в оболочке были проблемы, но bcp работал отлично. В конечном счете, чтобы обойти оболочку, мне пришлось не просто удалить shell=True. Мне также пришлось переписать вызов для передачи каждого аргумента в списке. - person David Maddox; 03.05.2014