如何获取Python中函数的调用表达式?

2023-12-02

出于教育目的,我希望能够打印complete当前函数的调用表达式。不一定来自异常处理程序。

经过一番研究,我最终得到了这段非常简单的代码:

import inspect
import linecache

def print_callexp(*args, **kwargs):
    try:
        frame = inspect.currentframe()

        # method 1, using inspect module only
        print(inspect.getframeinfo(frame.f_back).code_context)

        # method 2, just for the heck of it
        linecache.checkcache(frame.f_code.co_filename)
        line = linecache.getline(
            frame.f_back.f_code.co_filename,
            frame.f_back.f_lineno,
            frame.f_back.f_globals)
        print(line)

        # there's probably a similar method with traceback as well
    except:
        print("Omagad")

a_var = "but"
print_callexp(a_var, "why?!", 345, hello="world")

Result:

['    print_callexp(a_var, "why?!", 345, hello="world")\n']
    print_callexp(a_var, "why?!", 345, hello="world")

只要调用表达式位于一行上,它就完全符合我的要求。但是对于多行表达式,它只会得到最后一行,显然需要我进一步挖掘调用上下文。

# same example but with a multiple lines call
a_var = "but"
print_callexp(
    a_var, "why?!", 345, hello="world")

这给了我们:

['        a_var, "why?!", 345, hello="world")\n']
        a_var, "why?!", 345, hello="world")

如何正确打印完整的调用表达式?

“使用 lineno 值并应用一些正则表达式/评估技巧”不是一个可接受的答案。我更喜欢一些更干净、更有效的东西。我不介意导入更多模块,只要它们是 Python 3.x 标准库的一部分即可。但尽管如此,我会对任何参考资料感兴趣。


出于好奇,这是我出于这种非生产性目的的最终工作代码。乐趣无处不在! (几乎)

我不会立即将此标记为已接受的答案,希望有人能在不久的将来为我们提供更好的选择......

它按预期提取整个调用表达式。此代码假定调用表达式是一个裸函数调用,没有任何魔法、特殊技巧或嵌套/递归调用。这些特殊情况显然会使检测部分变得不那么琐碎,并且无论如何都是偏离主题的。

具体来说,我使用当前函数名称来帮助定位调用表达式的 AST 节点,以及由inspect作为起点。

我无法使用inspect.getsource()隔离调用者的块,这本来可以更加优化,因为我发现它返回不完整的源代码的情况。例如,当调用者的代码直接位于main的范围。不知道这应该是一个错误还是一个功能......

一旦我们有了源代码,我们只需要提供ast.parse()获取根 AST 节点并遍历树以查找对当前函数的最新调用,瞧!

#!/usr/bin/env python3

import inspect
import ast

def print_callexp(*args, **kwargs):

    def _find_caller_node(root_node, func_name, last_lineno):
        # init search state
        found_node = None
        lineno = 0

        def _luke_astwalker(parent):
            nonlocal found_node
            nonlocal lineno
            for child in ast.iter_child_nodes(parent):
                # break if we passed the last line
                if hasattr(child, "lineno"):
                    lineno = child.lineno
                if lineno > last_lineno:
                    break

                # is it our candidate?
                if (isinstance(child, ast.Name)
                        and isinstance(parent, ast.Call)
                        and child.id == func_name):
                    # we have a candidate, but continue to walk the tree
                    # in case there's another one following. we can safely
                    # break here because the current node is a Name
                    found_node = parent
                    break

                # walk through children nodes, if any
                _luke_astwalker(child)

        # dig recursively to find caller's node
        _luke_astwalker(root_node)
        return found_node

    # get some info from 'inspect'
    frame = inspect.currentframe()
    backf = frame.f_back
    this_func_name = frame.f_code.co_name

    # get the source code of caller's module
    # note that we have to reload the entire module file since the
    # inspect.getsource() function doesn't work in some cases (i.e.: returned
    # source content was incomplete... Why?!).
    # --> is inspect.getsource broken???
    #     source = inspect.getsource(backf.f_code)
    #source = inspect.getsource(backf.f_code)
    with open(backf.f_code.co_filename, "r") as f:
        source = f.read()

    # get the ast node of caller's module
    # we don't need to use ast.increment_lineno() since we've loaded the whole
    # module
    ast_root = ast.parse(source, backf.f_code.co_filename)
    #ast.increment_lineno(ast_root, backf.f_code.co_firstlineno - 1)

    # find caller's ast node
    caller_node = _find_caller_node(ast_root, this_func_name, backf.f_lineno)

    # now, if caller's node has been found, we have the first line and the last
    # line of the caller's source
    if caller_node:
        #start_index = caller_node.lineno - backf.f_code.co_firstlineno
        #end_index = backf.f_lineno - backf.f_code.co_firstlineno + 1
        print("Hoooray! Found it!")
        start_index = caller_node.lineno - 1
        end_index = backf.f_lineno
        lineno = caller_node.lineno
        for ln in source.splitlines()[start_index:end_index]:
            print("  {:04d} {}".format(lineno, ln))
            lineno += 1

def main():
    a_var = "but"
    print_callexp(
        a_var, "why?!",
        345, (1, 2, 3), hello="world")

if __name__ == "__main__":
    main()

你应该得到这样的东西:

Hoooray! Found it!
  0079     print_callexp(
  0080         a_var, "why?!",
  0081         345, (1, 2, 3), hello="world")

它仍然感觉有点混乱,但是哦,这是一个非常不寻常的目标。至少在 Python 中看起来很不寻常。 例如,乍一看,我希望找到一种方法来直接访问已加载的 AST 节点,该节点可以由inspect通过框架对象或以类似的方式,而不必手动创建新的 AST 节点。

请注意,我完全不知道这是否是 CPython 特定代码。不应该是这样。至少从我从文档中读到的内容来看。

我也想知道怎么没有官方的漂亮的印刷品函数在ast模块(或作为侧面模块)。ast.dump()可能会用额外的东西来完成这项工作indent参数允许格式化输出并调试AST更容易。

作为旁注,我发现这个非常整洁和小function帮助使用 AST。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何获取Python中函数的调用表达式? 的相关文章

  • 计算温度的偏导数(温度的水平平流)

    我想知道哪种方法计算x和y方向温度的偏导数 温度的水平平流 最正确 第二个代码使用温度 纬向风和经向风的数据矩阵 提取温度 T 纬向风分量 u 和经向风分量 v 的数据 import matplotlib pyplot as plt imp
  • Python 按文件夹模块导入

    我有一个目录结构 example py templates init py a py b py a py and b py只有一个类 名称与文件相同 因为它们是猎豹模板 纯粹出于风格原因 我希望能够在中导入和使用这些类example py像
  • Accel 无法在 gedit 3 插件中工作

    我试图为 Gedit 3 编写一个使用 GObject 自省的小插件 下面显示的代码的相关部分只是为了建立一个环境 然后我可以将函数放入按钮的回调中 但是 该按钮的加速器不起作用 这段代码有什么问题 我正在使用教程here http www
  • SMTPAuthenticationError: (535, b'5.7.8 用户名和密码在 Django 生产中不被接受?

    我在 Heroku 上部署了一个 Django 应用程序 在其中一节中 我使用 SMTP Gmail 设置向用户发送电子邮件 当我在本地运行项目时 电子邮件发送成功 但在 Heroku 上部署的项目上却发送失败 我在 Stackoverfl
  • 如何更改条形图上的 y 轴限制?

    我有一个df 我从中索引了europe n我绘制了一个条形图 europe n r 5 c 45 looks like this df Country string df Population numeric 变量 plt bar df C
  • WTForms 中的小数字段舍入

    我有一个包含价格小数字段的表单 如下所示 from flask ext wtf import Form import wtforms from wtforms validators import DataRequired from deci
  • Python MySQL 模块

    我正在开发一个需要与 MySQL 数据库交互的 Web 应用程序 但我似乎找不到任何真正适合 Python 的模块 我特别寻找快速模块 能够处理数十万个连接 和查询 所有这些都在短时间内完成 而不会对速度产生重大影响 我想我的答案将是游戏领
  • 将列表传递给 PyCrypto 中的 AES 密钥生成器

    我尝试使用 Pycrypto 生成 AES 密钥 但收到以下错误 类型错误 列表 不支持缓冲区接口 对于以下声明 aescipher AES new mykey AES MODE ECB mykey 属于类型list并包含 18854347
  • Python 函数可能会引发哪些异常? [复制]

    这个问题在这里已经有答案了 Python 中有什么方法可以确定 内置 函数可能引发哪些异常 例如 文档 http docs python org lib built in funcs html http docs python org li
  • 将数据框列打包到 pandas 中列出

    我需要将 pandas DataFrame 列打包到包含列表的一列中 例子 For gt gt gt df a b c 0 81 88 1 1 42 7 23 2 8 37 63 3 18 22 20 制作列表栏 list col 0 81
  • 在 (i)python 脚本中从 jupyter 内核获取输出

    我想从单个 ipython 会话中打开多个内核 在这些内核上运行代码 然后收集结果 但我不知道如何收集结果 甚至不知道如何查看 stdout stderr 我怎样才能做这些事情呢 到目前为止我所得到的 我已经使用如下代码管理了前两个步骤 打
  • 监控单个文件

    我需要监控 使用watchdog http pythonhosted org watchdog index html 单个文件 而不是整个目录 避免监视整个目录的最佳方法是什么 我想this http pythonhosted org wa
  • 收到的标签值 1 超出了 [0, 1) 的有效范围 - Python、Keras

    我正在使用具有张量流背景的 keras 开发一个简单的 cnn 分类器 def cnnKeras training data training labels test data test labels n dim print Initiat
  • Tornado websocket handler , self.close() 正在关闭连接而不触发 on_close() 方法

    我是 python stackoverflow tornado 的新手 所以请耐心等待 纠正我 我正在使用龙卷风开发实时应用程序 当我在 Websocket 处理程序类中调用 self close 时 on close 方法不会启动 这次我
  • 从 sublime_plugin.WindowCommand 获取当前文件名

    我开发插件sublime text 3 并想要获取当前打开的文件路径 absolute1 self window view file name 在哪里self is sublime plugin WindowCommand 但失败了 Att
  • Python 类方法的示例用例是什么?

    我读了Python 中的类方法有什么用 https stackoverflow com questions 38238 what are class methods in python for但那篇文章中的例子很复杂 我正在寻找 Pytho
  • 用于桌面数据库应用程序的 Python 框架

    是否有一个框架可以为Python开发桌面数据库应用程序 一些带有CRUD屏幕的屏幕 我正在寻找类似于 Windows 窗体的东西 能够将 TextField Combos 和其他 UI 隐喻与datasets连接到关系数据库例如 MySQL
  • 深入了解崩溃的根源

    我遇到了以下崩溃 我无法按需复制 但确实发生了足以引起用户注意的事件 该崩溃报告也已被符号化 怎么查原因啊 这似乎只发生在 4 1 上 Exception Type SIGSEGV Exception Codes SEGV ACCERR a
  • Pandas 替换特定列上的值

    我知道这两个类似的问题 熊猫替换值 https stackoverflow com questions 27117773 pandas replace values Pandas 替换数据框中的列值 https stackoverflow
  • 调试客户端时使用 Chrome/Firefox

    我正在使用带有 getUserMedia 的相机 但出现了一些需要修复的错误 问题是 Visual Studio 只允许我使用 IE 调试 JavaScript 我的意思是命中断点 而 IE 不支持 getUserMedia 如果您想在 I

随机推荐