2012-04-11 5 views
1

私はリモートマシン上で実行するために、Pythonコードをサンドボックスモジュールに動的に追加しようとしています。私は輸入された方法に対処する方法に問題が発生しています。リモートサンドボックスの実行のために埋め込みPythonモジュールを実行する方法は?

from test_module import g 
import other_module 

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

私はグラムと、潜在的にZ fをpickle化することができますが、どのように私は、zのために、「other_module」スコープを保存するか知っている:例えば、次のような記述されたスクリプトを見るのが一般的でしょうか?私がfとgの両方をサンドボックスに入れると、fが呼び出されたときにzが正しく解決されることはありません。いくつかのタイプの組み込みモジュールを使用して、zを正しく解決することは可能ですか?つまり、sandbox.other_module?

リモートコードをサンドボックスにロードする私の目的は、グローバル名前空間を汚染しないことです。例えば、それ自身の依存関係グラフで別のリモートメソッドが呼び出された場合、それは別のリモートコードセットと干渉しないはずです。 Pythonがサンドボックスモジュールの使用中と使用中に安定していると期待するのは現実的ですか?私はこの投稿のためにこう言っています: How do I unload (reload) a Python module? この場合、異なるサンドボックスなどのモジュールを取り外すことが問題になることがあります。あなたが他からの「サンドボックス」のモジュールを呼び出す場合

exec 'import other_module' in sandbox.__dict__ 

+0

[それは依存関係を持つpython関数をpickleする方法]の質問に続きます。(http://stackoverflow.com/q/10048061/448474) – hynekcer

答えて

1

他のモジュールは

sandbox.other_module = __import__('other_module') 

かによってサンドボックス(実行時に動的に作成されたモジュールを意味する)にインポートすることができますモジュールやその他のサンドボックスモジュールを使用していて、新しいコードを後でリロードしたい場合は、モジュールからインポートする方が簡単です。 "from sandbox import f"のような名前ではなく、 "f"ではなく "sandbox.f"その後、簡単にリロードします。リロードの方法が簡単にすることができますように思わ


クラス

>>> 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 

(しかしnaturarely reloadコマンドは、それのために有用ではありません)。変更されたソースコードで定義されたクラスを再ロードすることは、古いクラスコードをあるインスタンスで保持できるため、複雑になる可能性があることを覚えています。インスタンスのコードCAN /最悪の場合には、個別に更新される必要があります:

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

私はクラスコードのwin32com自動化とリロードを介してアクセスのpythonサービスと後者を使用するには、損失のインスタンスデータ

1

現在のアプローチせずに成功しました私は両方の 'import x'と 'from x import y' dependency bundlingを有効にするつもりです。この現在の実装では、コードの起源とは対照的に、使用されている各モジュールにメソッドのコピーを作成していますが、各使用法はメモリ内の同じメソッドへの参照に過ぎません(ただし、コードの後の節を参照してください)。 ///のmodutil

/// /// 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) 

。例えばPY ///

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に同じアドレスがありますか?あなたはおそらく、例えばPythonライブラリからインポート機能をシリアル化する必要はありません

+0

より多くのコピーを作成するのではなく、その関数への参照を増やすと思います。ただし、元の関数はすべての参照が削除または置換されるまでメモリから削除されません。これが私が "f"の代わりに "some_mod.f"という呼出しを好む理由です。 – hynekcer

+0

通常のモジュールから名前をインポートする場合、名前のリファレンス済みオブジェクトへの参照も個別に更新する必要があります。メモリ使用量のうち、通常と "サンドボックス"の違いは何ですか? Btw。酸洗いに関する私の元々の答えはあなたにとって有用であったと思いますか、それともわからないのですか? (ボタン) – hynekcer

+0

あなたの答えは非常に便利でした!両方とも+1し、このスレッドのチェックマークを付けてください。 –

1
  • 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

これはうまくいきました。それはまさに私がする必要があったものでした!ありがとう。 –