Unalias Python中的导入函数?

时间:2015-11-04 22:22:16

标签: python python-2.7 reflection

在显示调用函数的参数和值的实用程序函数中,我需要知道从另一个模块导入的可能别名函数的原始名称。这可能是针对导入别名的简单情况吗?

这是一个简化的用例,我首先介绍utilities.py模块中的一些代码:

import inspect

DEBUG_FLAG = True

def _log_args(*args):
    """Uses reflection to returning passing argument code with values."""

    prev_frame = inspect.currentframe().f_back
    func_name = prev_frame.f_code.co_name
    code_context = inspect.getframeinfo(prev_frame.f_back).code_context[0].strip()

    # Do some magic, which does work _unless_ func_name is aliased :-)
    print('code context: {}'.format(code_context))
    print('func_name   : {}'.format(func_name))
    return ', '.join(str(arg) for arg in args)

def format_args(*args):
    """Returns string with name of arguments with values."""
    return _log_args(args)

def debug_print(*args):
    """Prints name of arguments with values."""
    if DEBUG_FLAG:
        print _log_args(args)

以下是一些代码首先按原始名称访问这些函数,然后使用别名:

from utilities import debug_print, format_args, debug_print as debug, format_args as fargs

def main():
    a, b = "text", (12, 13)

    print "== Unaliased =="
    test_text = format_args(a, b)
    print test_text   # Returns 
    debug_print(a, b)

    print "\n== Aliased =="
    test_text = fargs(a, b)
    print test_text
    debug(a, b)

if __name__ == '__main__':
    main()

这个输出是:

== Unaliased ==
code context: test_text = format_args(a, b)
func_name   : format_args
('text', (12, 13))
code context: debug_print(a, b)
func_name   : debug_print
('text', (12, 13))

== Aliased ==
code context: test_text = fargs(a, b)
func_name   : format_args
('text', (12, 13))
code context: debug(a, b)
func_name   : debug_print
('text', (12, 13))

可以看出我找到了正确的代码上下文,并且找到了调用函数的名称,但是第一个报告了别名,后者报告了实际名称。所以我的问题是是否可以撤销操作,以便我知道format_args已被别名化为fargs,而debug_print已被别名化为debug }?

一些相关的问题,解决了这种别名的反转:

1 个答案:

答案 0 :(得分:0)

事实证明,找出为debug_printformat_args定义的别名是相当困难的,但幸运的是我确实有代码上下文,可以执行相反的操作来查找我的哪一部分代码上下文实际上是我的功能之一。

导致此解决方案的以下思路部分受到Martijn Pieters与抽象语法树相关的评论的启发,部分受SuperBiasedMan给出的与做help(fargs)相关的提示的启发。 }}:

  • help(fargs)实际列出了format_args函数
  • 在IPython中,使用help??,我发现它使用了pydoc.help
  • 的提示
  • 找到了pydoc.py的源代码here
  • 找到了呼叫顺序: help > doc > render_doc > 解决> name = getattr(thing, '__name__', None)
  • 在我的测试代码中尝试了getattr(fargs, '__name__', None),并且它正常工作
  • 尝试getattr('fargs', ...),但失败了
  • 经过一番搜索后发现globals()['fargs']确实返回了函数对象
  • 从我的code_context中提取令牌,并编写了一些代码来执行各种查找

所有这些都产生了以下工作代码:

def _log_args(*args):
    """Uses reflection to returning passing argument code with values."""

    prev_frame = inspect.currentframe().f_back
    func_name = prev_frame.f_code.co_name
    code_context = inspect.getframeinfo(prev_frame.f_back).code_context[0].strip()

    # Do some magic, which does work _unless_ func_name is aliased :-)
    print('code context     : {}'.format(code_context))
    print('func_name        : {}'.format(func_name))

    # Get globals from the calling frame
    globals_copy = prev_frame.f_back.f_globals

    tokens = re.compile('[_a-zA-Z][a-zA-Z_0-9]*').findall(code_context)
    for token in tokens:
        print( '  Checking token : {}'.format(token))

        # Check if token is found as an object in globals()        
        code_object = globals_copy.get(token, None)
        if not code_object:
            continue

        # Check if code_object is one of my userdefined functions
        if inspect.isfunction(code_object):
            code_func_name = getattr(code_object, '__name__', None)
        else:
            continue

        # Check if expanded token is actually an alias (or equal) to func_name
        if code_func_name == func_name:
            func_name = token
            break
    else:
        # For-loop went through all tokens, and didn't find anything
        func_name = None

    if func_name:
        print('Calling function : {}'.format(func_name))
    else:
        print('Didn\'t find a calling function?!')

    return ', '.join(str(arg) for arg in args)

我知道这取决于代码上下文中存在的调用函数,如果您将代码分解为多行,则会破坏此方法。另一个警告是,如果有人通过列表或词典调用该函数。但是,这主要是出于调试目的,并且可以记录它们不应该做那样的事情。

输出现在是:

== Unaliased ==
code context     : test_text = format_args(a, b)
func_name        : format_args
Calling function : format_args
('text', (12, 13))
code context     : debug_print(a, b)
func_name        : debug_print
Calling function : debug_print
('text', (12, 13))

== Aliased ==
code context     : test_text = fargs(a, b)
func_name        : format_args
Calling function : fargs
('text', (12, 13))
code context     : debug(a, b)
func_name        : debug_print
Calling function : debug
('text', (12, 13)

此输出现在可以继续我寻求制作一个不错的debug_print()。如果您发现此设计有任何改进或缺陷,请发表评论(或回答)。