Подтвердить что ты не робот

Захватите глобальный поиск имени в интерпретаторе python

Вот что, у меня есть прокси-сервер, содержащий ссылку на удаленный модуль, и я поместил некоторые из этих прокси в sys.modules, чтобы я мог использовать его так же, как локальные модули. Но некоторые другие объекты помещаются в модуль __builtin__ в удаленной среде (например, волшебная переменная для удобства отладки или ссылки). Я не хочу ссылаться на эти vars как conn.__builtin__.var, и мне нужно либо заменить локальный __builtin__ (который, кажется, не работает для замены sys.modules['__builtin__'], или для привязки глобальных правил поиска имен. Как? Для модуля вы можете просто перегрузить getattr, чтобы сделать это. Но в интерактивном интерпретаторе, например IPython, кто является основным модулем или как это сделать? update: Как отметил @Nizam Mohamed, да, я могу получить модуль __main__, но все же я не могу изменить роль поиска имени.

Я хочу, чтобы локальная среда полностью была удаленной (для консоли отладки)

UPDATE

Теперь я просто перебираю все __builtin__.__dict__ и если есть имя, которое не находится в локальном __builtin__. Я добавляю имя в локальный __builtin__. Но это не так динамично по сравнению с правилом поиска имени, скажем, если я не могу найти имя в локальном __builtin__ попробуйте удаленный.

здесь является аналогичным обсуждением.

И этот вопрос дает симуляцию модуля, заменив его на объект в sys.modules. Но это не будет работать для поиска __builtin__, я также попытался заменить __builtin__.__getattribute__ на пользовательский, который сначала будет использовать исходный поиск, а затем пользовательский при неудачной попытке. Но глобальный поиск по имени __builtin__, который никогда не вызывался в __builtin__.__getattribute__ even __builtin__.__getattribute__('name'), возвращает желаемое значение, __builtin__.name или name никогда не возвращает один.

4b9b3361

Ответ 1

Использовать преобразование AST оболочки IPython

Как сказал @asmeurer, вы можете написать простой трансформатор АСТ, чтобы "перехватить" поиск имени переменной. Базовый класс 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)

dummyModule.py

robot=" World"

Использование:

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

Преимущество этого метода заключается в том, что любая модификация удаленного модуля вступает в силу немедленно, потому что поиск выполняется на уровне языка, и никакой объект не копируется и не кэшируется.

Одним из недостатков этого метода является невозможность обработки динамического поиска. Если это важно для вас, возможно, крючок python_line_transforms более подходит.

Ответ 2

Существует способ получить список всех имен, которые будет использовать модуль. Хотя он не изменяет механизм поиска, я считаю, что он решает вашу проблему.

Вот некоторый код (надеюсь, достаточно понятный), вы можете поместить в модуль, позвоните ему 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 namespace
            # You'll have to replace the right-hand side by your specific code
            importer.f_globals[name] = 'hello world'

Затем вы можете импортировать его для создания магии:

import magic
magic.magic()

print(hi)

Однако есть два недостатка. Во-первых, не удастся выполнить динамический поиск:

import magic
magic.magic()

print(globals()['hi']) # KeyError: 'hi'

Хотя этот конкретный случай можно решить, посмотрев строки в importer.f_code.co_consts, он не будет работать с более продвинутыми динамическими поисками, такими как print(globals()['h'+'i'])

Второй недостаток заключается в том, что он не будет работать в функциях:

import magic
magic.magic()

def f():
    print(hi) # NameError: name 'hi' is not defined

f()

Это потому, что в этом случае имя hi находится в f.__code__.co_names вместо модуля co_names.

Одним из возможных решений было бы изменить magic, чтобы попытаться найти все функции модуля и изменить их код, но он не будет работать с функциями, определенными внутри функций и т.д.

Другим решением является вызов magic в функции:

import magic

def f():
    magic.magic()
    print(hi)

f()