python macropy

Añadiendo macros a Python



macropy (7)

Me gustaría invocar el siguiente código in situ donde me refiero a MY_MACRO en mi código a continuación.

# MY_MACRO frameinfo = getframeinfo(currentframe()) msg = ''We are on file '' + frameinfo.filename + '' and line '' + str(frameinfo.lineno) # Assumes access to namespace and the variables in which `MY_MACRO` is called. current_state = locals().items()

Aquí hay un código que usaría MY_MACRO :

def some_function: MY_MACRO def some_other_function: some_function() MY_MACRO class some_class: def some_method: MY_MACRO

En caso de que ayude:

  1. Una de las razones por las que me gustaría tener esta capacidad es porque me gustaría evitar repetir el código de MY_MACRO donde lo necesite. Tener algo corto y fácil sería muy útil.
  2. Otra razón es porque quiero incrustar un shell IPython dentro de la macro y me gustaría tener acceso a todas las variables en locals().items() (vea esta otra pregunta )

¿Es esto posible en Python? ¿Cuál sería la forma más fácil de hacer que esto funcione?

TENGA EN CUENTA que la macro asume el acceso a todo el espacio de nombres del ámbito en el que se llama (es decir, simplemente no sería MY_MACRO colocar el código MY_MACRO en una función ). Tenga en cuenta también que si MY_MACRO en una función, lineno emitirá el número de línea incorrecto.


¿Qué tal una función que puede llamar? Esta función accede al marco de la persona que llama, y ​​en lugar de usar locals() , usa frame.f_locals para obtener el espacio de nombres de la persona que llama.

def my_function(): frame = currentframe().f_back msg = ''We are on file {0.f_code.co_filename} and line {0.f_lineno}''.format(frame) current_state = frame.f_locals print current_state[''some_variable'']

Entonces solo llámalo:

def some_function: my_function() def some_other_function: some_function() my_function() class some_class: def some_method: my_function()


El uso del exec está mal visto, pero debería hacer el truco aquí. Por ejemplo, tome la siguiente macro:

MY_MACRO = """ print foo """

y ejecútalo usando el siguiente código:

foo = "breaking the rules" exec MY_MACRO in globals(),locals()

Siempre tenga cuidado con exec , ya que puede tener efectos secundarios extraños y abre oportunidades para la inyección de código.


No estoy seguro de si esta es una buena solución, pero al menos vale la pena considerar un preprocesador de macros.

Hay algunos proyectos de extensión con Python con macros diferentes, o proyectos más amplios que deberían hacer algo más fácil de hacer, pero solo tengo enlaces caducados para todos ellos (Logix, MetaPython, Mython, Espy) ... podría ser Vale la pena buscar enlaces actuales y / o nuevos proyectos / liver.

Puedes usar algo como m4 o cpp , o algo más poderoso, o incluso construir uno tú mismo. Pero en realidad, acaba de obtener un conjunto pequeño y estático (hasta ahora, uno) de macros puramente textuales. En el peor de los casos, debe detectar el nivel de sangría de MY_MACRO y agregarlo al inicio de cada línea, lo que es trivial de hacer en una expresión regular. Significa que sed , o un script de Python de 3 líneas, puede ser tu preprocesador.

Sin embargo, hay dos problemas, o al menos molestias.

Primero, necesitas preprocesar tus archivos. Si ya está utilizando módulos de extensión C o código generado o cualquier otro código que necesite que setup.py (o make o scons o lo que sea) antes de poder ejecutarlo, o está usando un IDE en el que acaba de pulsar cmd- R o ctrl-shift-B o lo que sea para probar su código, esto no es un problema. Pero para el típico bucle de edición de prueba con un editor de texto en una ventana y un intérprete interactivo en otra ... bueno, lo acaba de convertir en un bucle de edición-compilación-prueba. Ugh La única solución que se me ocurre es un enlace de importación que preprocesa cada archivo antes de importarlo como un módulo, lo que parece ser mucho trabajo para un pequeño beneficio.

En segundo lugar, sus números de línea y fuente (desde MY_MACRO sí, así como desde inspect.getsource e inspect.getsource etc.) serán los números de línea de los archivos preprocesados, no la fuente original que tiene abierto para editar. Ya que sus archivos preprocesados ​​son bastante legibles, eso no es terrible (no es tan malo como codificar CoffeeScript y depurarlo como JavaScript, que la mayoría de la comunidad de CoffeeScript hace todos los días ...), pero definitivamente es una molestia.

Por supuesto, una forma de resolver esto es construir su propio procesador de macros en el intérprete, en cualquier etapa del proceso de análisis / compilación que desee. Supongo que es mucho más trabajo del que desea hacer, pero si lo hace , bueno, Guido siempre prefiere tener un diseño de trabajo real y una implementación para rechazar en lugar de tener que seguir rechazando sugerencias vagas de "Oye, agreguemos macros a pitón ". :)


Podrías usar la función si quisieras:

def MY_MACRO(): frame = currentframe() try: macro_caller_locals = frame.f_back.f_locals print(macro_caller_locals[''a'']) finally: del frame def some_function: a = 1 MY_MACRO()


Si solo necesita la línea y el nombre de la función de la persona que llama como la que necesitaba para la depuración, puede obtener información de la función de la persona que llama mediante el link inspect.getouterframes.

import inspect def printDebugInfo(): (frame,filename,line_number,function_name, lines, index) = inspect.getouterframes(inspect.currentframe())[1] print(filename, line_number, function_name, lines, index) def f1(): printDebugInfo() if __name__==''__main__'': f1()


Yo diría que debería definir una función para hacer esto, ya que no hay macros en Python. Parece que desea capturar el marco de pila actual, lo que podría simplificar pasando en currentframe() desde el sitio de llamada a su función compartida. Lo mismo ocurre con los lugareños.

def print_frame_info(frameinfo, locals): msg = ''We are on file '' + frameinfo.filename + '' and line '' + str(frameinfo.lineno) current_state = locals.items() def some_other_function: some_function() print_frame_info(currentframe(), locals())


MacroPy es un proyecto mío que trae macros sintácticas a Python. El proyecto tiene solo 3 semanas de antigüedad, pero si miras el enlace, verás que tenemos una colección bastante buena de demostraciones, y la funcionalidad que deseas definitivamente puede implementarse al usarla.

Por otro lado, Python tiene algunas capacidades de introspección bastante sorprendentes, por lo que sospecho que puedes lograr lo que quieras simplemente usando esa funcionalidad.