zou*_*yjs 23 python ipython python-import rpyc
这是事情,我有一个代理控制远程模块的引用,我把这些代理中的一些放在sys.modules这样我可以像本地模块一样使用它.但是一些其他对象放在__builtin__远程环境的模块中(就像一个魔术变量,以方便调试或引用).我不想引用这些变量conn.__builtin__.var,我必须要么替换本地__builtin__(这似乎不适用于替换sys.modules['__builtin__']或挂钩全局名称查找规则.如何?对于一个模块,你可以重载一个getattr来做这个.但是在一个交互式翻译中,如IPython谁是主要模块或如何做到这一点? 更新:正如@Nizam Mohamed指出的那样,是的,我可以得到__main__ 模块,但我仍然无法修改它的名称查找角色.
我想完全将本地环境变为远程环境(用于调试控制台)
UPDATE
现在我只是迭代所有的__builtin__.__dict__,如果有一个不在本地的名称__builtin__.我将名称添加到本地__builtin__.但是,与名称查找规则相比,它不是那么动态,如果我在本地找不到该名称,请__builtin__尝试远程名称.
这是一个类似的讨论.
而这个问题通过用一个对象替换它来模拟模块sys.modules.但这对于__builtin__名称查找不起作用,我也尝试__builtin__.__getattribute__用自定义的一个替换,首先使用原始查找,然后在失败时使用自定义查找.但是__builtin__从未调用过__builtin__.__getattribute__偶数的全局名称查找__builtin__.__getattribute__('name')返回所需的值,__builtin__.name或者name永远不会返回一个.
有一种方法可以获取模块将使用的所有名称的列表。虽然它没有修改查找机制,但我相信它可以解决您的问题。
\n\n这是一些可以放入模块中的代码(希望足够容易理解),我们称之为magic:
import sys\n\ndef magic():\n # Get the caller frame\n frame = sys._getframe().f_back\n\n # Unwind all internal Python import-related stuff\n while frame.f_code.co_filename.startswith('<'):\n frame = frame.f_back\n\n importer = frame\n\n # Iterate through names the module has/will use\n for name in importer.f_code.co_names:\n\n # If the module has not yet defined/imported this name\n if name not in importer.f_globals and \\\n name not in importer.f_locals and \\\n name not in __builtins__:\n\n # Replace the name in the importer's namespace\n # You'll have to replace the right-hand side by your specific code\n importer.f_globals[name] = 'hello world'\nRun Code Online (Sandbox Code Playgroud)\n\n然后你可以导入它来施展魔法:
\n\nimport magic\nmagic.magic()\n\nprint(hi)\nRun Code Online (Sandbox Code Playgroud)\n\n然而,有两个缺点。首先,动态查找将失败:
\n\nimport magic\nmagic.magic()\n\nprint(globals()['hi']) # KeyError: 'hi'\nRun Code Online (Sandbox Code Playgroud)\n\n尽管可以通过查看 中的字符串来解决这种特殊情况importer.f_code.co_consts,但它不适用于更高级的动态查找,例如print(globals()['h'+'i'])
第二个缺点是它不能在函数中工作:
\n\nimport magic\nmagic.magic()\n\ndef f():\n print(hi) # NameError: name 'hi' is not defined\n\nf()\nRun Code Online (Sandbox Code Playgroud)\n\n这是因为在这种情况下,名称hi位于f.__code__.co_names\xc2\xa0 而不是模块的co_names.
一种可能的解决方案是进行修改magic以尝试查找模块的所有函数并更改其代码,但它不适用于函数内部定义的函数等。
另一种解决方案是调用magic该函数:
import magic\n\ndef f():\n magic.magic()\n print(hi)\n\nf()\nRun Code Online (Sandbox Code Playgroud)\n
正如@asmeurer所说,你可以编写一个简单的AST变换器来"挂钩"变量名查找.基类ast.NodeTransformer提供了一个可以操作的visit_Name方法.您只需要重载此方法以重新定义远程模块中存在但不在本地的变量.
以下模块可用作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)
Run Code Online (Sandbox Code Playgroud)
dummyModule.py
robot=" World"
Run Code Online (Sandbox Code Playgroud)
用法:
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
Run Code Online (Sandbox Code Playgroud)
此方法的好处是对远程模块的任何修改都会立即生效,因为查找是在语言级别完成的,并且没有对象被复制和缓存.
此方法的一个缺点是无法处理动态查找.如果这对你很重要,也许python_line_transforms钩子更合适.