2017-08-14 6 views
0

pythonスクリプトで関数のソース、呼び出し先リスト、デフォルト、キーワード、args、varargsを取得しようとしています。スクリプトをインポートせずにinspect.getmembers()と似たソーススクリプトの詳細を取得します。

現在、私はモジュールをインポートするとPython inspectモジュールのgetmembers機能を使用し、そのようisfunctionパラメータを渡している:

members = inspect.getmembers(myModule, inspect.isfunction) 

myModuleの輸入が上がらない "場合は、この方法では動作しません。私には利用できません(最初にmyModuleをインポートする必要があるため)。

私はparsedumpへのpythonにastモジュールを使用して構文木を試してみましたが、機能のソースは非常にハック技術および/または疑わしいと保守がサードパーティのライブラリから遠くに巻き込ま。私は、ドキュメントとstackoverflowをかなり徹底的に調べて、適切な解決策を見つけることができなかったと考えています。何か不足していますか?

+0

はどのようにインポートできないモジュールですが、それはすることができます 'ASTことが可能です。そのソースを解析しますか? –

答えて

0

私はフランケンシュタインの解決策をいくつか見てみましたが、this dude's answerを使って各機能のソースを取得しました。それはまだどこにも完璧に近いではありませんが、ここであなたが興味があるなら、それは次のようになります。

import ast 
import re 
import json 


st = open('filename.py').read() 
tree = ast.parse(st) 
functions_info = {} 


def parse(function): 
    global st 
    global functions_info 
    fn_info = {} 
    fn_info['Args'] = [] 
    fn_info['Source'] = [] 
    fn_info['Callees'] = [] 

    print(function.name) 

    for arg in function.args.args: 
     fn_info['Args'].append(arg.arg) 

    lastBody = function.body[-1] 

    while isinstance (lastBody,(ast.For,ast.While,ast.If)): 
     lastBody = lastBody.Body[-1] 
    lastLine = lastBody.lineno 

    if isinstance(st,str): 
     st = st.split("\n") 
    for i , line in enumerate(st,1): 
     if i in range(function.lineno,lastLine+1): 
      # print(line) 
      fn_info['Source'].append(line) 

    for line in fn_info['Source']: 
     if not line.lstrip().startswith('#'): 
      fn_pattern = r'(\w+)\(' 
      match = re.search(fn_pattern, line) 
      if match: 
       callee = match.group(1) 
       fn_info['Callees'].append(callee) 

    functions_info[function.name] = fn_info 

for obj in tree.body: 
    if isinstance(obj, ast.ClassDef): 
     for func in obj.body: 
      if isinstance(func, (ast.FunctionDef)): 
       parse(func) 

    if isinstance(obj, ast.FunctionDef): 
     parse(obj) 

print(json.dumps(functions_info, indent=4)) 

出力:

{ 
    "displayWonder": { 
     "Source": [ 
      " def displayWonder(self):", 
      "  return \"Hello \" + str(self.displayGreeting())" 
     ], 
     "Args": [ 
      "self" 
     ], 
     "Callees": [] 
    }, 
    "displayGreeting": { 
     "Source": [ 
      " def displayGreeting(self):", 
      "  string = \"Greetings \" + self.myName", 
      "  return string" 
     ], 
     "Args": [ 
      "self" 
     ], 
     "Callees": [] 
    }, 
    "myStatic": { 
     "Source": [ 
      " @staticmethod", 
      " def myStatic():", 
      "  return \"I am static\"" 
     ], 
     "Args": [], 
     "Callees": [] 
    }, 
    "displayHello": { 
     "Source": [ 
      " def displayHello(self):", 
      "  return \"Hello \" + self.myName" 
     ], 
     "Args": [ 
      "self" 
     ], 
     "Callees": [] 
    }, 
    "__init__": { 
     "Source": [ 
      " def __init__(self):", 
      "  self.myName = 'Wonder?'" 
     ], 
     "Args": [ 
      "self" 
     ], 
     "Callees": [] 
    }, 
    "main": { 
     "Source": [ 
      "def main():", 
      " hello = Hello(\"Wonderful!!!\")", 
      " # name = unicode(raw_input(\"Enter name: \"), 'utf8')", 
      " # print(\"User specified:\", name)", 
      " print(hello.displayGreeting())" 
     ], 
     "Args": [], 
     "Callees": [] 
    } 
} 
0

可能な回避策ははImportErrorをスローすることはありませんカスタム関数と__import__機能をモンキーパッチすることで、代わりにダミーモジュールを返します。その依存関係をインポートすることができない場合でも、これはあなたがmyModuleをインポートできるようになる

import builtins 

def force_import(module): 
    original_import = __import__ 

    def fake_import(*args): 
     try: 
      return original_import(*args) 
     except ImportError: 
      return builtins 
    builtins.__import__ = fake_import 

    module = original_import(module) 

    builtins.__import__ = original_import 
    return module 

。この解決策の問題は、それが唯一の失敗の輸入を中心に動作することである

myModule = force_import('myModule') 
members = inspect.getmembers(myModule, inspect.isfunction) 

:通常どおり、その後、あなたはinspect.getmembersを使用することができます。インポートされたモジュールのいずれかのメンバーにアクセスするためのmyModuleしようとすると、そのインポートは失敗します:

# myModule.py 

import this_module_doesnt_exist # works 

print(this_module_doesnt_exist.variable) # fails 
force_import('myModule') 
# AttributeError: module 'builtins' has no attribute 'variable' 

これを回避するためには、はAttributeErrorを投げたことがないダミーのクラスを作成することができます

class DummyValue: 
    def __call__(self, *args, **kwargs): 
     return self 

    __getitem__ = __setitem__ = __delitem__ = __call__ 
    __len__ = __length_hint__ = __bool__ = __call__ 
    __iter__ = __next__ = __call__ 
    __getattribute__ = __call__ 
    __enter__ = __leave__ = __call__ 
    __str__ = __repr__ = __format__ = __bytes__ = __call__ 
    # etc 

(あなたが実装する必要がありdunder方法のリストについては、the data model documentationを参照してください。)

force_importがこのクラスのインスタンスを返した場合(return builtinsからreturn DummyValue()に変更)、myModuleのインポートは成功します。

+0

私はこれを考えて、まったく同じ問題に遭遇しました:) ありがとうとにかく! –

+0

@AnuragPande AttributeErrorにも同様の回避策を追加しました。 –

関連する問題