2012-07-06 33 views
5

Digamos que tengo dos scripts python A.py y B.py. Estoy buscando una manera de funcionar B desde dentro de una de una manera tal que:Ejecutar programa python desde otro programa python (con ciertos requisitos)

  1. B cree que es __main__ (para que el código en un bloque de if __name__=="__main__" en B se ejecutará)
  2. B no es en realidad __main__ (para que no sobrescriba, por ejemplo, la entrada "__main__" en sys.modules)
  3. Las excepciones aumentadas dentro de B se propagan a A (es decir, podrían capturarse con una cláusula except en A).
  4. Esas excepciones, si no se detecta, generar una línea números de rastreo de referenciación correctas dentro B.

He intentado varias técnicas, pero ninguno parecen satisfacer todas mis necesidades.

  • el uso de herramientas del módulo subproceso significa excepciones en B no se propagan a A.
  • execfile("B.py", {}) carreras B, pero no se piensa que es principal.
  • execfile("B.py", {'__name__': '__main__'}) hace que B.py piense que es principal, pero también parece estropear la impresión de traceback de excepción, de modo que los tracebacks se refieren a líneas dentro de A (es decir, el __main__ real).
  • usando imp.load_source con __main__ como el nombre casi funciona, excepto que en realidad modifica sys.modules, pisando fuerte tanto en el valor actual de __main__

¿Hay alguna manera de obtener lo que quiero?

(La razón por la que hago esto es porque estoy realizando una limpieza en una biblioteca existente. Esta biblioteca no tiene un conjunto de pruebas real, solo un conjunto de scripts "de ejemplo" que producen cierto resultado. Estoy intentando aprovechar estos como pruebas para garantizar que mi limpieza no afecte la capacidad de la biblioteca de ejecutar estos ejemplos, por lo que quiero ejecutar cada script de ejemplo desde mi suite de pruebas. Me gustaría poder ver las excepciones de estos guiones dentro de el script de prueba para que el script de prueba pueda informar el tipo de falla, en lugar de solo informar un SubprocessError genérico cada vez que un script de ejemplo presente alguna excepción).

Respuesta

2

responder a mi propia pregunta, porque el resultado es bastante interesante y podría ser útil a los demás:

Resulta que estaba equivocado: execfile("B.py", {'__name__': '__main__'} es el camino a seguir después de todo. Es hace correctamente producir los tracebacks. Lo que estaba viendo con números de línea incorrectos no eran excepciones pero advertencias. Estas advertencias se produjeron usando warnings.warn("blah", stacklevel=2). Se supone que el argumento stacklevel=2 permite que se produzcan advertencias de depreciación cuando se utiliza el objeto en desuso, en lugar de en la llamada de advertencia (consulte the documentation).

Sin embargo, parece que el archivo execfile-d no cuenta como un "nivel de pila" para este propósito, y es invisible para propósitos de stacklevel. Entonces, si el código en el nivel superior de un módulo execfile-d causa una advertencia con stacklevel 2, la advertencia no se genera en el número de línea correcto en el archivo fuente execfile-d; en su lugar, se genera en el número de línea correspondiente del archivo que ejecuta el archivo exec.

Esto es desafortunado, pero puedo vivir con él, ya que estos son solo advertencias, por lo que no afectarán el rendimiento real de las pruebas. (Al principio no me di cuenta de que solo las advertencias se veían afectadas por los desajustes de los números de línea, porque había muchas advertencias y excepciones entremezcladas en la salida de prueba).

+0

Solución interesante. Dudo (y espero ...) que alguna vez lo necesite, pero las cosas que Python permite y permite son geniales. –

2

Su caso de uso tiene sentido, pero aún así creo que estaría mejor. refactorizando las pruebas para que puedan ejecutarse de forma externa.

¿Los guiones de prueba tienen algo como esto?

def test(): 
    pass 

if __name__ == '__main__': 
    test() 

Si no es así, tal vez debería convertir sus pruebas para llamar a una función como test. Luego, a partir de la secuencia de comandos de prueba principal, puede simplemente:

import test1 
test1.test() 
import test2 
test2.test() 

proporcionar una interfaz común a las pruebas de funcionamiento, que las propias pruebas utilizan. Tener un gran bloque de código en un cheque __main__ no es una buena cosa.

Disculpa que no he respondido la pregunta que hiciste, pero creo que esta es la solución correcta sin desviar demasiado del código de la prueba original.

+0

Desafortunadamente no. Algunos de los scripts de prueba lo hacen, pero otros solo tienen el código como código de módulo de nivel superior. Es solo una mezcla de casos extraños. Descubrí el problema y respondí mi propia pregunta, pero gracias por tu consejo. ¡Ojalá pudiera seguirlo! :-) – BrenBarn

Cuestiones relacionadas