Existe uma maneira de esperar por outro script Python chamado a partir do script atual (usando subprocess.Propen ()) até sua conclusão?

Oct 16 2020

Estou tentando executar 2 scripts python (digamos dp_01.py e dp_02.py) a partir de um script python mestre. Eu quero executá-los um após o outro. Este é o meu código atual em script python mestre

job1_exec = "python dp_01.py"
try:
    #os.system(job1_exec)
    command1 = subprocess.Popen(job1_exec, shell=True)
    command1.wait()
except:
    print("processing of dp_01 code failed")
print("Processing of dp_01 code completed")


job2_exec = "python dp_02.py"
try:
    #os.system(job2_exec)
    command2 = subprocess.Popen(job2_exec, shell=True)
    command2.wait()
except:
    print("processing of dp_02 code failed")
print("Processing of dp_02 code completed")

O problema aqui é que o script mestre não está esperando que dp_01.py conclua sua execução. Ele imediatamente começa a executar dp_02.py. Como esperar a execução de dp_01.py ser concluída antes de iniciar a execução de dp_02.py?

Respostas

2 Wololo Oct 16 2020 at 18:49

Uma solução pode ser substituir Popenpor check_outputou run. check_outputé um wrapper runque preserva e retorna o subprocesso ' stdout, e também bloqueia a thread principal durante a execução.

De acordo com esta pergunta do SO ,

A principal diferença [entre 'run' e 'Popen'] é que subprocess.run executa um comando e espera que ele termine, enquanto com subprocess.Popen você pode continuar fazendo suas coisas enquanto o processo termina e então chamar o subprocesso repetidamente. comunique-se para repassar e receber dados para o seu processo.

Vamos considerar dois trabalhos diferentes onde o número 1 leva consideravelmente mais tempo para ser executado do que o trabalho número 2, simulado aqui por um sleep(7)

# dp_01.py
import time
time.sleep(7)
print("--> Hello from dp_01", end="")

e,

# dp_02.py
print("--> Hello from dp_02", end="")

Em seguida, para a simplicidade do teste, movo o desempenho do trabalho do script principal para funções,

import time
import subprocess

jobs = ["dp_01.py", "dp_02.py"]

# The current approach of the OP, using 'Popen':
def do(job):
  subprocess.Popen("python "+job, shell=True)


# Alternative, main-thread-blocking approach,
def block_and_do(job):
  out = subprocess.check_output("python "+job, shell=True)
  print(out.decode('ascii')) # 'out' is a byte string


# Helper function for testing the synchronization
# of the two different job-workers above
def test_sync_of_function(worker_func, jobs):
  test_started = time.time()
  for job in jobs:
    print("started job '%s', at time %d" % (job, time.time() - test_started))
    worker_func(job)
    print("completed job '%s', at time %d" % (job, time.time() - test_started))
    time.sleep(1)

Isto resulta em:

test_sync_of(do, jobs)
starting job 'dp_01.py', at time 0
completed job 'dp_01.py', at time 0
starting job 'dp_02.py', at time 1
completed job 'dp_02.py', at time 1
 --> Hello from dp_02 !
 --> Hello from dp_01 !

enquanto,

test_sync_of(block_and_do, jobs)
starting job 'dp_01.py', at time 0
 --> Hello from dp_01 !
completed job 'dp_01.py', at time 7
starting job 'dp_02.py', at time 8
 --> Hello from dp_02 !
completed job 'dp_02.py', at time 8

Finalmente, espero que isso resolva seu problema. No entanto, esta pode não ser a melhor solução para o seu grande problema? Você pode querer dar uma olhada no multiprocessingmódulo. Talvez seus trabalhos nos scripts separados possam ser importados como módulos e seu trabalho encadeado?

Uma nota final: você deve ter muito cuidado ao usar shell=True: consulte esta outra pergunta do SO para obter detalhes.