2012-11-13 2 views
3

어떤 함수/메소드에서든 내성 검사를하고 싶습니다. 내 모든 예제에서 파이썬 2.7을 사용하고 있지만 3.3을 사용하면 문제가되지 않는다.코드 객체를 검사/디스 어셈블 할 때 호출 가능 객체를 동적으로 따라갈 수 있습니다.

import inspect 
import foobar 
inspect.getsource(foobar.foo) 

나는 또한에서 분해 바이트 코드를 얻을 수 있습니다 : 나는 동적으로 foo는 실행의 코드를 볼 수 있습니다

def foo(): 
    bar() 

:

내가 모듈에 다음 코드를 foobar.py라고 한 말 이 함수의 코드 객체는 다음과 같습니다.

import dis 
dis.dis(foobar.foo) 

foo 메서드가 다른 함수 (이 경우 bar)를 호출 한 다음 동적으로이를 역 분석/검사합니까?

나는 코드 개체 자체가 같은 속성의 모든 종류를 가지고 있음을 알고 다음 나는 그들의 대부분 그냥 둘러보고 검사를했지만, 확실히 내가 무엇을 찾고 발견하지 않았습니다

>>> dir(foobar.foo.__code__) 
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames'] 

.

최종 목표는 가져 오기 이외의 코드를 실행하지 않고 호출 스택을 재귀 적으로 인쇄 할 수 있는지 확인하기위한 약간의 실험입니다. 이론적 인 호출 스택은 특정 변수의 상태와 같은 런타임 일을 설명 할 수 없다는 것을 알고 있습니다. 특정 호출이 주어진 경우 중첩 된 모든 함수의 소스를 인쇄하고 싶습니다. 런타임 상태).

또한 inspectdis 모듈은 일단 CPython 코드에 들어가면 도움이되지 않습니다. 궁극적으로 inspectdis이 고장 났을 때 도달하는 CPython 코드를 보여주는 일종의 매핑을 인쇄하는 것이 재미있을 수 있습니다. 그러나 그것이 가능한지 확실하지 않습니다.

+2

'bar'는 런타임까지 확인할 수 없으므로 가능한지 의심 스럽습니다. 다른 함수가 있다면? def baz() : 전역 바; bar = 3' 같은 모듈에? 그런 다음'qux'가 호출되면'bar'는'foo'를 호출 할 때 함수가 아니기 때문에 예외가 발생합니다 – mgilson

+0

그래, 어쨌든 이것이 가능한지 확실하지 않습니다. 그러나, pylint와 같은 일부 정적 분석기는 메소드가 해결되었는지 확인하기 위해 이와 같은 작업을 수행하지 않습니까? 나는 여기서 미친 생각을 던지고있다.나는 궁극적으로 주어진 함수에 의해 (순수한 파이썬에서) 발생할 수있는 모든 가능한 예외를 출력 할 수있는 스크립트를 작성할 수 있는지보고있다. –

+0

프로그램을 실행하여이 작업을 수행 할 수 있습니다. 또는 프로그램을 분석하여 이름을 리 바인드 할 수있는 코드를 확인하거나 영리한 트릭이 아닌 사용자 자신의 평가기를 작성하여 모든 참조를 정적으로 해결하려고 할 수 있습니다. 이것들 중 어느 것도 쉽지 않은 오후의 해킹이 될 수 없습니다. – Marcin

답변

1

모든 컴파일러/해석기는 원본을 구문 분석 할 때 추상 구문 트리를 작성합니다. 이것은 문맥 자유 문법을 기반으로 한 프로그램의 표현이며,이 문법은 컴퓨터가 실행할 수있는 코드를 생성하기 위해 재귀 적으로 걸을 수 있습니다.

파이썬은 AST에 대한 액세스를 제공하며이 트리를 직접 걸어보고 ast.Call 개의 개체가 ast.FunctionDef 안에 있는지 확인할 수 있습니다. 간단한 예를 아래에 붙여 넣습니다.

import ast 

source = """ 
def foo(): 
    bar() 

def bar(): 
    baz() 

def baz(): 
    print "hello!" 
""" 

def get_method_name_for_call(call_obj): 
    for fieldname, value in ast.iter_fields(call_obj): 
     if fieldname == "func": 
      return value.id 

def walk_method_calls(node, cur_func): 
    if not node: 
     return 

    for cur_node in ast.iter_child_nodes(node): 
     if type(cur_node) == ast.Call: 
      method_called = get_method_name_for_call(cur_node) 
      print "Found a call to %s in body of %s." % (method_called, cur_func) 
     walk_method_calls(cur_node, cur_func) 


def walk_function_defs(node): 
    if not node: 
     return 

    for cur_node in ast.iter_child_nodes(node): 
     if type(cur_node) == ast.FunctionDef: 
      walk_method_calls(cur_node, cur_node.name) 

# we pass <string> as a recognizable identifier since 
# we have no filename 
ast_root = compile(source, "<string>", "exec", ast.PyCF_ONLY_AST) 
walk_function_defs(ast_root) 

그리고 실행 예 :

$ python abstract_tree.py 
Found a call to bar in body of foo. 
Found a call to baz in body of bar. 
이 반드시 모든 가능한 모든 통화를 포착하지 않습니다 불구하고 통화가 등 eval 식으로, 다른 표현에 포함 숨겨져있을 수 있기 때문에 여기에 간단한 예제가 주
관련 문제