paquetes - Enganche la búsqueda de nombre global en un intérprete de python
instalar pip python windows (2)
Aquí está la cosa, tengo un proxy que contiene la referencia a un módulo remoto, y puse algunos de estos proxies a los sys.modules
tal manera que puedo usarlos como módulos locales. Pero algunos otros objetos se colocan en el módulo __builtin__
en el entorno remoto (como una variable mágica para mayor comodidad de depuración o referencia). No quiero hacer referencia a estas vars como conn.__builtin__.var
, y tengo que reemplazar el __builtin__
local (que parece no funcionar para reemplazar sys.modules[''__builtin__'']
o para sys.modules[''__builtin__'']
reglas de búsqueda de nombres globales. Cómo "Para un módulo, simplemente puede sobrecargar un getattr
para hacer esto. Pero en un intérprete interactivo como IPython
, ¿quién es el módulo principal o cómo hacerlo? Actualización: Como lo señaló @Nizam Mohamed, sí, puedo obtener el módulo __main__
, pero todavía no puedo modificar el nombre de la función de búsqueda de la misma.
Me gustaría convertir el entorno local completamente para que sea remoto (para una consola de depuración)
ACTUALIZAR
Por ahora, simplemente __builtin__.__dict__
todos los __builtin__.__dict__
y si hay un nombre que no está en el local __builtin__
. Agrego el nombre al local __builtin__
. Pero no es tan dinámico en comparación con una regla de búsqueda de nombres que dice: si no puedo encontrar el nombre en __builtin__
local, intente con el remoto.
here hay una discusión similar.
Y this pregunta da una simulación de módulo por reemplazarlo con un objeto en sys.modules
. Pero esto no funcionará para la búsqueda de nombre __builtin__
, también traté de reemplazar la __builtin__.__getattribute__
con una personalizada que primero usará la búsqueda original seguida de una personalizada cuando falla. Pero la búsqueda de nombre global de __builtin__
nunca se __builtin__
en __builtin__.__getattribute__
incluso __builtin__.__getattribute__(''name'')
devuelve el valor deseado, __builtin__.name
o name
nunca devuelve uno.
Usar la transformación AST del shell IPython
Como dijo @asmeurer, puede escribir un transformador AST simple para "enganchar" la búsqueda de nombre de variable. La clase base ast.NodeTransformer
proporciona un método visit_Name que puede manipular. Solo necesita sobrecargar este método para redefinir las variables existentes en el módulo remoto pero no localmente.
El siguiente módulo se puede utilizar como una extensión de IPython :
testAST.py
import ast
modName = "undefined"
modAttr = []
user_ns = {}
class MyTransformer(ast.NodeTransformer):
def visit_Name(self, node):
if node.id in modAttr and not node.id in user_ns:
return self.getName(node)
return node
def getName(self, NameNode):
return ast.Attribute(value=ast.Name(id=modName, ctx=ast.Load()),
attr = NameNode.id,
ctx = NameNode.ctx)
def magic_import(self, line):
global modName, modAttr, user_ns
modName = str(line)
if not self.shell.run_code( compile(''import {0}''.format(line), ''<string>'', ''exec'') ):
user_ns = self.shell.user_ns
modAttr = user_ns[line.strip()].__dict__
self.shell.ast_transformers.append(MyTransformer())
print modName, ''imported''
def load_ipython_extension(ip):
ip.define_magic(''magic_import'', magic_import)
dummyModule.py
robot=" World"
Uso:
In [1]: %load_ext testAST
In [2]: %magic_import dummyModule
In [3]: print "Hello" , robot
Hello World
In [4]: dummyModule.robot_II = "Human"
In [5]: print "Hi", robot_II
Hi Human
El beneficio de este método es que cualquier modificación al módulo remoto surte efecto inmediatamente porque la búsqueda se realiza en el nivel de idioma y no se copia y almacena en caché ningún objeto.
Un inconveniente de este método es que no se puede manejar la búsqueda dinámica. Si eso es importante para ti, tal vez el enganche python_line_transforms
sea más adecuado.
Hay una manera de obtener una lista de todos los nombres que utilizará el módulo. Aunque no modifica el mecanismo de búsqueda, creo que resuelve tu problema.
Aquí hay un código (ojala que sea lo suficientemente comprensible) que puedes poner en un módulo, llamémoslo magic
:
import sys
def magic():
# Get the caller frame
frame = sys._getframe().f_back
# Unwind all internal Python import-related stuff
while frame.f_code.co_filename.startswith(''<''):
frame = frame.f_back
importer = frame
# Iterate through names the module has/will use
for name in importer.f_code.co_names:
# If the module has not yet defined/imported this name
if name not in importer.f_globals and /
name not in importer.f_locals and /
name not in __builtins__:
# Replace the name in the importer''s namespace
# You''ll have to replace the right-hand side by your specific code
importer.f_globals[name] = ''hello world''
Luego puedes importarlo para hacer magia:
import magic
magic.magic()
print(hi)
Sin embargo, hay dos desventajas. Primero, las búsquedas dinámicas fallarán:
import magic
magic.magic()
print(globals()[''hi'']) # KeyError: ''hi''
Aunque ese caso en particular puede resolverse mirando las cadenas en el importer.f_code.co_consts
, no funcionará con búsquedas dinámicas más avanzadas como print(globals()[''h''+''i''])
El segundo inconveniente es que no funcionará en las funciones:
import magic
magic.magic()
def f():
print(hi) # NameError: name ''hi'' is not defined
f()
Esto se debe a que, en ese caso, el nombre hi
está en el f.__code__.co_names
lugar de los co_names
del módulo.
Una posible solución sería modificar la magic
para intentar encontrar todas las funciones del módulo y modificar su código, pero no funcionará con las funciones definidas dentro de las funciones, etc.
Otra solución es llamar magic
en la función:
import magic
def f():
magic.magic()
print(hi)
f()