获取在命令行中键入的Python函数的调用字符串

时间:2019-05-06 10:11:01

标签: python read-eval-print-loop introspection

我想使用自省功能从函数本身存储为函数调用键入的确切字符串(我不能/不想破解命令行解释器-因此,例如,从readline或什么都不是我想要的)。

假设,如果用户键入:

"W" != "w"

我想获取通话字符串(即>>> myfunc('a', 'b', 1, mykwarg='hello') ) 来自myfunc('a', 'b', 1, mykwarg='hello')内部的代码。

我可以制作这样的东西:

myfunc

我得到:

def myfunc(a,b,c,mykwarg=None):
    frame = inspect.currentframe()
    sig = inspect.signature(myfunc)
    args = []
    for param in sig.parameters.values():
        if param.name in frame.f_locals:
                args.append(f"{param.name}={str(frame.f_locals[param.name])}")
    cmd = f"{frame.f_code.co_name}({','.join(args)})"

    print(cmd)

这不是用户键入的确切内容。另外,我希望还有更多 健壮且不太“ hackish”尝试...

用例:我希望能够将库中的命令调用与其结果相关联。我不想为每个函数硬编码命令调用存储,我更喜欢使用装饰器或类似的东西。通过REPL可以很容易地做到这一点,但是我不希望依赖它(例如,如果用户从其自己的程序中调用该函数,它仍然应该能够将命令调用与结果相关联)。

1 个答案:

答案 0 :(得分:0)

最后,我回答了我自己的问题,希望有一天能对其他人有所帮助。

我决定尝试采用dis的方式,即“反汇编”的Python代码对象 调用我的函数的外部框架,以了解如何调用它 重建命令行:

import dis
import inspect
import io
import ast
import re

def get_function_call_string():
    in_func = False
    args=[]
    kwargs=[]
    func_name = inspect.stack()[1][3]
    frame = inspect.currentframe().f_back.f_back
    dis_output = io.StringIO()
    dis.dis(frame.f_code, file=dis_output)
    for line in dis_output.getvalue().split('\n'):
        instr = re.findall(r'^.*\s([A-Z_]+)\s*', line)[0]
        if instr.startswith("CALL_FUNCTION") and in_func:
           break
        elif instr.startswith('LOAD_'):
            name = re.findall(r'^.*\s\((.+)\)$', line)[0]
            if in_func:
                if name.startswith('('):
                    kwargs = ast.literal_eval(name)
                else:
                    args.append(name)
            elif name == func_name:
                in_func = True
    kwargs = [f"{a}={args.pop()}" for a in kwargs]
    return f"{func_name}({', '.join(args)}{', ' if kwargs else ''}{', '.join(kwargs)})"

示例:

>>> def f(a,b,arg="toto"):
        print(get_function_call_string())
>>> f(1,"test","example")
f(1, 'test', 'example')
>>> f(1, "test", arg="hello")
(1, 'test', arg='hello')

这不是一个完整的答案,因为它无法处理某些参数类型,例如 dict或list ...我可能会继续这样做或改变主意并做不同的事情。