python threading.timer establece el límite de tiempo cuando el progtwig se queda sin tiempo

Tengo algunas preguntas relacionadas con la configuración del tiempo de ejecución máximo de una función en Python. De hecho, me gustaría usar pdfminer para convertir los archivos .pdf a .txt .

El problema es que muy a menudo, algunos archivos no se pueden decodificar y toman mucho tiempo. Así que quiero establecer threading.Timer() para limitar el tiempo de conversión para cada archivo a 5 segundos. Además, corro bajo Windows, así que no puedo usar el módulo de signal para esto.

pdfminer.convert_pdf_to_txt() ejecutar el código de conversión con pdfminer.convert_pdf_to_txt() (en mi código es ” c “), pero no estoy seguro de que funcione en el siguiente código, threading.Timer() . (No creo que limite adecuadamente el tiempo para cada procesamiento)

En resumen, quiero:

  1. Convertir el pdf a txt

  2. El límite de tiempo para cada conversión es de 5 segundos, si se acaba el tiempo, lance una excepción y guarde un archivo vacío

  3. Guarda todos los archivos txt en la misma carpeta

  4. Si hay excepciones / errores, todavía guarde el archivo pero con contenido vacío.

Aquí está el código actual:

 import converter as c import os import timeit import time import threading import thread yourpath = 'D:/hh/' def iftimesout(): print("no") with open("D:/f/"+g+"&"+t+"&"+name+".txt", mode="w") as newfile: newfile.write("") for root, dirs, files in os.walk(yourpath, topdown=False): for name in files: try: timer = threading.Timer(5.0,iftimesout) timer.start() t=os.path.split(os.path.dirname(os.path.join(root, name)))[1] a=str(os.path.split(os.path.dirname(os.path.join(root, name)))[0]) g=str(a.split("\\")[1]) with open("D:/f/"+g+"&"+t+"&"+name+".txt", mode="w") as newfile: newfile.write(c.convert_pdf_to_txt(os.path.join(root, name))) print("yes") timer.cancel() except KeyboardInterrupt: raise except: for name in files: t=os.path.split(os.path.dirname(os.path.join(root, name)))[1] a=str(os.path.split(os.path.dirname(os.path.join(root, name)))[0]) g=str(a.split("\\")[1]) with open("D:/f/"+g+"&"+t+"&"+name+".txt", mode="w") as newfile: newfile.write("") 

Revise el siguiente código y hágame saber en caso de cualquier problema. También avíseme si todavía desea usar la función de terminación forzada ( KeyboardInterruption )

 path_to_pdf = "C:\\Path\\To\\Main\\PDFs" # No "\\" at the end of path! path_to_text = "C:\\Path\\To\\Save\\Text\\" # There is "\\" at the end of path! TIMEOUT = 5 # seconds TIME_TO_CHECK = 1 # seconds # Save PDF content into text file or save empty file in case of conversion timeout def convert(path_to, my_pdf): my_txt = text_file_name(my_pdf) with open(my_txt, "w") as my_text_file: try: my_text_file.write(convert_pdf_to_txt(path_to + '\\' + my_pdf)) except: print "Error. %s file wasn't converted" % my_pdf # Convert file_name.pdf from PDF folder to file_name.text in Text folder def text_file_name(pdf_file): return path_to_text + (pdf_file.split('.')[0]+ ".txt") if __name__ == "__main__": # for each pdf file in PDF folder for root, dirs, files in os.walk(path_to_pdf, topdown=False): for my_file in files: count = 0 p = Process(target=convert, args=(root, my_file,)) p.start() # some delay to be sure that text file created while not os.path.isfile(text_file_name(my_file)): time.sleep(0.001) while True: # if not run out of $TIMEOUT and file still empty: wait for $TIME_TO_CHECK, # else: close file and start new iteration if count < TIMEOUT and os.stat(text_file_name(my_file)).st_size == 0: count += TIME_TO_CHECK time.sleep(TIME_TO_CHECK) else: p.terminate() break 

¡Finalmente lo resolví!

En primer lugar, defina una función para llamar a otra función con un tiempo de espera limitado:

 import multiprocessing def call_timeout(timeout, func, args=(), kwargs={}): if type(timeout) not in [int, float] or timeout <= 0.0: print("Invalid timeout!") elif not callable(func): print("{} is not callable!".format(type(func))) else: p = multiprocessing.Process(target=func, args=args, kwargs=kwargs) p.start() p.join(timeout) if p.is_alive(): p.terminate() return False else: return True 

¿Qué hace la función?

  • Compruebe el tiempo de espera y la función para ser válido
  • Inicie la función dada en un nuevo proceso, que tiene algunas ventajas sobre los subprocesos
  • Bloquee el progtwig durante x segundos ( p.join() ) y permita que la función se ejecute en este momento
  • Después de que el tiempo de espera expire, verifique si la función todavía se está ejecutando

    • Sí: terminalo y devuelve False
    • No: bien, no hay tiempo de espera! Volver True

Podemos probarlo con time.sleep() :

 import time finished = call_timeout(2, time.sleep, args=(1, )) if finished: print("No timeout") else: print("Timeout") 

Ejecutamos una función que necesita un segundo para finalizar, el tiempo de espera se establece en dos segundos:

 No timeout 

Si ejecutamos time.sleep(10) y configuramos el tiempo de espera en dos segundos:

 finished = call_timeout(2, time.sleep, args=(10, )) 

Resultado:

 Timeout 

Observe que el progtwig se detiene después de dos segundos sin finalizar la función llamada.

Tu código final se verá así:

 import converter as c import os import timeit import time import multiprocessing yourpath = 'D:/hh/' def call_timeout(timeout, func, args=(), kwargs={}): if type(timeout) not in [int, float] or timeout <= 0.0: print("Invalid timeout!") elif not callable(func): print("{} is not callable!".format(type(func))) else: p = multiprocessing.Process(target=func, args=args, kwargs=kwargs) p.start() p.join(timeout) if p.is_alive(): p.terminate() return False else: return True def convert(root, name, g, t): with open("D:/f/"+g+"&"+t+"&"+name+".txt", mode="w") as newfile: newfile.write(c.convert_pdf_to_txt(os.path.join(root, name))) for root, dirs, files in os.walk(yourpath, topdown=False): for name in files: try: t=os.path.split(os.path.dirname(os.path.join(root, name)))[1] a=str(os.path.split(os.path.dirname(os.path.join(root, name)))[0]) g=str(a.split("\\")[1]) finished = call_timeout(5, convert, args=(root, name, g, t)) if finished: print("yes") else: print("no") with open("D:/f/"+g+"&"+t+"&"+name+".txt", mode="w") as newfile: newfile.write("") except KeyboardInterrupt: raise except: for name in files: t=os.path.split(os.path.dirname(os.path.join(root, name)))[1] a=str(os.path.split(os.path.dirname(os.path.join(root, name)))[0]) g=str(a.split("\\")[1]) with open("D:/f/"+g+"&"+t+"&"+name+".txt", mode="w") as newfile: newfile.write("") 

El código debe ser fácil de entender, si no, no dude en preguntar.

¡Realmente espero que esto ayude ( ya que nos llevó algo de tiempo hacerlo bien;) )!