2012-04-11 1 views
1

Я пытаюсь динамически добавлять код python в модуль песочницы для выполнения на удаленной машине. У меня возникла проблема с обработкой импортированных методов. Например, часто можно увидеть сценарии, написанные, например, как:Как сделать встроенный модуль python для удаленной обработки песочницы?

from test_module import g 
import other_module 

def f(): 
    g() 
    other_module.z() 

Я знаю, что могу мариновать е д и потенциально г, но как я сохранить «other_module» простор для г? Если я помещу как f, так и g в песочницу, тогда z не будет корректно разрешено при вызове f. Можно ли использовать какой-либо встроенный модуль для правильного выбора z, т. Е. Sandbox.other_module?

Моя цель для загрузки удаленного кода в песочницу - не загрязнять глобальное пространство имен. Например, если другой удаленный метод вызывается с собственным графиком зависимости, он не должен мешать другому набору удаленных кодов. Является ли реалистичным ожидать, что python будет стабильным с использованием модулей песочницы, входящих и выходящих из использования? Я говорю об этом из-за этого сообщения: How do I unload (reload) a Python module? , который заставляет меня чувствовать, что это может быть проблематичным удаление модулей, таких как различные песочницы в этом случае.

+0

Из этого следует вопрос [Как распиливать функцию python со своими зависимостями?] (Http://stackoverflow.com/q/10048061/448474) – hynekcer

ответ

1

Другие модули могут быть импортированы в песочнице (вы имеете в виду модули, которые создаются динамически во время выполнения) от

sandbox.other_module = __import__('other_module') 

или:

exec 'import other_module' in sandbox.__dict__ 

Если вы называете «песочницы» модули из других модулей или других модулей песочницы, и вы хотите перезагрузить какой-то новый код позже, проще импортировать только модуль, а не имена из него, как «из импорта песочницы f», и вызывать «sandbox.f» не «f». Тогда перезагрузка легко. (Но naturarely команда перезарядки не является полезной для него)


Классов

>>> class A(object): pass 
... 
>>> a = A() 
>>> A.f = lambda self, x: 2 * x # or a pickled function 
>>> a.f(1) 
2 
>>> A.f = lambda self, x: 3 * x 
>>> a.f(1) 
3 

кажется, что методы перегрузочные могут быть легкими. Я помню, что классы перезагрузки, определенные в модифицированном исходном коде, могут быть сложными, потому что старый код класса может храниться каким-то экземпляром. Код Экземпляр может/должен быть обновлен в индивидуальном порядке, в худшем случае:

some_instance.__class__ = sandbox.SomeClass # that means the same reloaded class 

Я использовал последний с сервисом питона доступ через win32com автоматизации и перезарядка классов кода был успешным без инстанций потери данных

1

Текущий подход Я собираюсь включить как «импорт x», так и «из x import y». Одна обратная сторона этой текущей реализации заключается в том, что она создает копии методов в каждом используемом модуле, в отличие от исходного кода, где каждое использование является лишь ссылкой на тот же метод в памяти (хотя у меня есть противоречивые результаты здесь - см. раздел после кода).

/// /// analysis_script.py (зависимостей, исключенные для краткости)

import test_module 
from third_level_module import z 

def f(): 
    for i in range(1,5): 
     test_module.g('blah string used by g') 
     z() 

/// /// driver.py

import modutil 
import analysis_script 

modutil.serialize_module_with_dependencies(analysis_script) 

/// modutil.ру ///

import sys 
import modulefinder 
import os 
import inspect 
import marshal 

def dump_module(funcfile, name, module): 
    functions_list = [o for o in inspect.getmembers(module) if inspect.isfunction(o[1])] 
    print 'module name:' + name 
    marshal.dump(name, funcfile) 
    for func in functions_list: 
     print func 
     marshal.dump(func[1].func_code, funcfile) 

def serialize_module_with_dependencies(module): 

    python_path = os.environ['PYTHONPATH'].split(os.pathsep) 
    module_path = os.path.dirname(module.__file__) 

    #planning to search for modules only on this python path and under the current scripts working directory 
    #standard libraries should be expected to be installed on the target platform 
    search_dir = [python_path, module_path] 

    mf = modulefinder.ModuleFinder(search_dir) 

    #__file__ returns the pyc after first run 
    #in this case we use replace to get the py file since we need that for our call to  mf.run_script 
    src_file = module.__file__ 
    if '.pyc' in src_file: 
     src_file = src_file.replace('.pyc', '.py') 

    mf.run_script(src_file) 

    funcfile = open("functions.pickle", "wb") 

    dump_module(funcfile, 'sandbox', module) 

    for name, mod in mf.modules.iteritems(): 
     #the sys module is included by default but has no file and we don't want it anyway, i.e. should 
     #be on the remote systems path. __main__ we also don't want since it should be virtual empty and 
     #just used to invoke this function. 
     if not name == 'sys' and not name == '__main__': 
      dump_module(funcfile, name, sys.modules[name]) 

    funcfile.close() 

/// /// sandbox_reader.py

import marshal 
import types 
import imp 

sandbox_module = imp.new_module('sandbox') 

dynamic_modules = {} 
current_module = '' 
with open("functions.pickle", "rb") as funcfile: 
    while True: 
     try: 
      code = marshal.load(funcfile) 
     except EOFError: 
      break 

     if isinstance(code,types.StringType): 
      print "module name:" + code 
      if code == 'sandbox': 
       current_module = "sandbox" 
      else: 
       current_module = imp.new_module(code) 
       dynamic_modules[code] = current_module 
       exec 'import '+code in sandbox_module.__dict__ 
     elif isinstance(code,types.CodeType): 
      print "func" 
      if current_module == "sandbox": 
       func = types.FunctionType(code, sandbox_module.__dict__, code.co_name) 
       setattr(sandbox_module, code.co_name, func) 
      else: 
       func = types.FunctionType(code, current_module.__dict__, code.co_name) 
       setattr(current_module, code.co_name, func) 
     else: 
      raise Exception("unknown type received") 

#yaa! actually invoke the method 
sandbox_module.f() 
del sandbox_module 

Например функция график выглядит, как это раньше серийности:

module name:sandbox 
('f', <function f at 0x15e07d0>) 
('z', <function z at 0x7f47d719ade8>) 
module name:test_module 
('g', <function g at 0x15e0758>) 
('z', <function z at 0x7f47d719ade8>) 
module name:third_level_module 
('z', <function z at 0x7f47d719ade8>) 

В частности, глядя на функции z видно, что все ссылки указывают на один и тот же адрес, то есть 0x7f47d719ade8.

На удаленном процессе после реконструкции песочницы мы имеем:

print sandbox_module.z 
<function z at 0x1a071b8> 
print sandbox_module.third_level_module.z 
<function z at 0x1a072a8> 
print sandbox_module.test_module.z 
<function z at 0x1a072a8> 

Это взрывает мой разум! Я бы подумал, что все адреса здесь будут уникальными после реконструкции, но по какой-то причине sandbox_module.test_module.z и sandbox_module.third_level_module.z имеют одинаковый адрес?

+0

Я думаю, что он не создает больше копий, а больше ссылок на эту функцию , Однако исходные функции не удаляются из памяти, пока все ссылки не будут удалены или не заменены. Вот почему я предпочитал называть «some_mod.f» вместо вызова «f». – hynekcer

+0

Если я импортирую имена из нормального модуля, я должен также обновлять ссылки на имена на перезагруженный объект по отдельности. Какая разница между обычным и «песочницей» в использовании памяти? КПП. Как вы думаете, мой оригинальный ответ на травление полезен для вас или вы не уверены? (кнопка) – hynekcer

+0

Оба ваши ответы были очень полезны! Дайте +1 на обоих и установите флажок для этой темы. –

1
  • Возможно, вы не хотите сериализовать импортированные функции из библиотеки Python, например. математических функций или больших пакетов, смешанных с Python + C, но ваш код сериализует его. Это может вызвать ненужные проблемы, в которых у них нет атрибута func_code и т. Д.
  • Вам не нужно многократно сериализовывать функции, которые были сериализованы ранее. Вы можете отправить полное имя и импортировать их в соответствии с этим. Именно по этой причине у вас это было несколько раз в памяти.
  • Оригинальный формат <module_name> <serialized_func1> <serialized_func2>... не достаточно общий. Функция может быть на локальном компьютере, импортированном под разными именами, в разделе «... import ... as ...». Вы можете сериализовать список кортежей смешанных строк и объектов кода.

Подсказка

def some_filter(module_name): 
    mod_path = sys.modules[module_name].__file__ 
    # or if getattr(sys.modules[module_name], 'some_my_attr', None) 
    return not mod_path.startswith('/usr/lib/python2.7/') 

dumped_funcs = {} 

def dump_module(... 
    ... 
    data = [] 
    for func_name, func_obj in functions_list: 
     if some_filter(func_obj.__module__) and not func_obj in dumped_funcs and \ 
        hasattr(func_obj, 'func_code'): 
      data.append((func_name, func_obj.func_code)) 
      dumped_funcs[func_obj] = True # maybe later will be saved package.mod.fname 
     else: 
      data.append((func_name, '%s.%s' % (func_obj.__module__, \ 
               func_obj.func_code.co_name))) 
    marshal.dump(data, funcfile) 

Теперь не важно, разница между песочницей и другим сериализованном модулем. Условия «песочница» могут быть вскоре удалены.

+0

Это сработало хорошо. Это было именно то, что мне нужно было сделать! Благодарю. –

 Смежные вопросы

  • Нет связанных вопросов^_^