1

我正在浏览一些使用attrs模块的代码。因此,在调试单步执行某些代码时,我最终会得到不作为实际文件存在但自动生成的源。

这个问题是关于使用什么 python 技术来实现这种行为的。用于示例的实际库或示例本身只是展示概念的具体内容。

例子

安装库

virtualenv -p python3 labgrid-venv                                                                                                                                                                      
source labgrid-venv/bin/activate                                                                                                                                                                        
                                                                                                                                                                                                                   
git clone https://github.com/labgrid-project/labgrid                                                                                                                                                               
cd labgrid                                                                                                                                                                                              
pip install -r requirements.txt                                                                                                                                                                         
pip install .                                                                                                                                                                                           

表现出行为(test.py)的代码:

from labgrid import Target                                                   
from labgrid.resource import RawSerialPort                                   
 
rpi = Target("RPi")
import pdb
pdb.set_trace()
raw_serial_port = RawSerialPort(rpi, None, port="/dev/ttyUSB0", speed=115200)

执行该代码并单步执行会RawSerialPort导致调试器在自动生成的源中发现自己:

(venv) project_root$ python test.py           
[0] > project_root/test.py(15)<module>()                      
-> raw_serial_port = RawSerialPort(rpi, None, port="/dev/ttyUSB0", speed=115200)                      
(Pdb++) s                                                                                             
--Call--                                                                                              
[1] > <attrs generated init labgrid.resource.serialport.RawSerialPort>(1)__init__()                   
-> def __init__(self, target, name, port=attr_dict['port'].default, speed=attr_dict['speed'].default):
(Pdb++)                                                                                               

注意def __init__定义,实际源文件中不存在这样的定义,而是由attrs.

我的问题是这里有什么 python 机制来实现这种甚至欺骗调试器的行为?

4

2 回答 2

2

PDB 使用linecache模块来查找您正在单步执行的 Python 文件的源代码。

因此,当attrs创建一个新方法时,它会执行以下操作:

  1. 为每个方法创建一个虚假的、唯一的文件名
  2. compile使用内置函数将生成的代码编译为字节码,并告诉它代码来自那个假文件名
  3. 它在第 2 步中编译的源代码附加到它在第 1 步中在linecache中创建的文件名。

现在,当 pdb 偶然发现它看到文件名的方法时,它会在 linecache 中查找它并具有用于单步执行的原始源代码。


能够单步执行 attrs 生成的任何内容以消除魔术的概念并显示代码中发生的事情对我来说总是非常重要的。

于 2020-11-18T06:20:37.003 回答
0

这是RawSerialPort文档的定义:

@target_factory.reg_resource
@attr.s(eq=False)
class RawSerialPort(SerialPort, Resource):
    """RawSerialPort describes a serialport which is available on the local computer."""
    def __attrs_post_init__(self):
        super().__attrs_post_init__()
        if self.port is None:
            ValueError("RawSerialPort must be configured with a port")

class语句创建的类不是最终绑定到名称的RawSerialPort;它是由 . 返回的任何对象target_factory.reg_resource。装饰器语法脱糖

class RawSerialPort(SerialPort, Resource):
    """RawSerialPort describes a serialport which is available on the local computer."""
[docs]    def __attrs_post_init__(self):
        super().__attrs_post_init__()
        if self.port is None:
            ValueError("RawSerialPort must be configured with a port")

RawSerialPort = target_factory.reg_resource(attr.s(eq=False)(RawSerialPort))

原始类首先被传递给 attr.s(eq=False),它返回一个新的、增强的类(一个包含__init__调试器会话中引用的方法),并且该类被传递给target_factory.reg_resource它(没有实际查找定义),返回另一个类(可能同一个类)基于attr模块创建的任何内容。

所以调试器不会被愚弄;恰恰相反,它准确地显示了正在发生的事情,仅从test.py.

于 2020-11-17T16:44:44.200 回答