跳转到主要内容

Python代码对象转换器

项目描述

build status Documentation Status

受ast模块的NodeTransformer启发的CPython字节码转换器。

什么是codetransformer?

codetransformer是一个库,允许我们在运行时与CPython的字节码表示形式一起工作。codetransformer在程序员和eval循环读取的原始字节之间提供了一层抽象,使我们能够更容易地检查和修改字节码。

codetransformer是由需要覆盖通过数据模型方法尚未连接到Python语言的部分而引发的。

  • 覆盖isnot运算符。

  • 自定义数据结构字面量。

  • 无法用有效的Python AST或源表示的语法功能。

  • 无需修改CPython解释器即可运行。

codetransformer最初是作为lazy项目的一部分开发的,用于实现运行时覆盖代码对象所需的转换。

示例用法

重载字面量

虽然这可以通过AST转换来完成,但我们通常需要多次执行字面量的构造函数。此外,我们需要确保在运行时提供任何运行我们代码所需的其他名称。使用codetransformer,我们可以预先计算我们新的字面量并生成与加载未经修改的字面量一样快的代码,而无需提供任何额外的名称。

以下示例中,我们展示了通过重载字典语法来生成collections.OrderedDict对象。与dict类似,但键的顺序被保留。

>>> from codetransformer.transformers.literals import ordereddict_literals
>>> @ordereddict_literals
... def f():
...     return {'a': 1, 'b': 2, 'c': 3}
>>> f()
OrderedDict([('a', 1), ('b', 2), ('c', 3)])

这也支持字典推导式

>>> @ordereddict_literals
... def f():
...     return {k: v for k, v in zip('abc', (1, 2, 3))}
>>> f()
OrderedDict([('a', 1), ('b', 2), ('c', 3)])

下一个示例用decimal.Decimal对象覆盖浮点字面量。这些对象支持任意精度算术。

>>> from codetransformer.transformers.literals import decimal_literals
>>> @decimal_literals
... def f():
...     return 1.5
>>> f()
Decimal('1.5')

模式匹配异常

模式匹配异常是CodeTransformer的一个很好的例子,它在AST级别实现起来会非常复杂。这种转换扩展了try/except语法,以接受BaseException的实例以及其子类。当捕获实例时,异常的args将被比较以确定应调用哪个异常处理程序。例如

>>> @pattern_matched_exceptions()
... def foo():
...     try:
...         raise ValueError('bar')
...     except ValueError('buzz'):
...         return 'buzz'
...     except ValueError('bar'):
...         return 'bar'
>>> foo()
'bar'

此函数引发一个ValueError实例并尝试捕获它。第一个检查查找使用参数'buzz'构造的ValueError实例。由于我们的自定义异常是用'bar'引发的,因此它们不相等,我们没有进入此处理程序。下一个处理程序查找ValueError('bar'),这与我们引发的异常匹配。然后我们进入此代码块,并按照正常的Python规则执行。

我们还可以传递自己的异常匹配函数

>>> def match_greater(match_expr, exc_type, exc_value, exc_traceback):
...     return math_expr > exc_value.args[0]

>>> @pattern_matched_exceptions(match_greater)
... def foo():
...     try:
...         raise ValueError(5)
...     except 4:
...         return 4
...     except 5:
...         return 5
...     except 6:
...         return 6
>>> foo()
6

此匹配基于匹配表达式是否大于引发的任何异常类型的第一个参数的值。这种特定行为很难通过AST级别的转换来模拟。

核心抽象

codetransformer的三个核心抽象是:

  1. 代表可能带有一些参数的opcodeInstruction对象。

  2. 代表一系列InstructionCode对象。

  3. 代表操作Code对象规则的CodeTransformer对象。

指令

Instruction对象代表由CPython虚拟机执行的原子操作。这些是像LOAD_NAME(将名称加载到堆栈上)或ROT_TWO(旋转堆栈顶部两个元素)这样的操作。

一些指令接受一个参数,例如 LOAD_NAME,这会改变指令的行为。这类似于函数调用,其中一些函数接受参数。因为字节码总是以原始字节的形式打包,所以参数必须是某个整数(CPython 将所有参数存储在字节中)。这意味着需要更丰富的参数系统的事物(如需要查找实际名称的 LOAD_NAME)必须携带一些表中的实际参数,并使用整数作为对此数组的偏移量。Instruction 对象的一个关键抽象是参数总是代表实际参数的某个 Python 对象。任何查找表管理都由用户处理。这很有帮助,因为一些参数共享这个表,我们不希望添加额外的条目或忘记添加它们。

另一个烦恼是处理控制流的指令使用它们的参数来说明要跳转到哪个字节码偏移量。一些跳转使用绝对索引,而另一些使用相对索引。这也使得如果你想要添加或删除指令很困难,因为所有偏移量都必须重新计算。在 codetransformer 中,所有跳转指令都接受另一个 Instruction 作为参数,这样汇编器就可以为用户管理这些指令。我们还提供了一个简单的方法,让新的指令“窃取”目标为其他指令的跳转,以便可以管理跳转目标周围的字节码更改。

代码

Code 对象是 Python 的 types.CodeType 的一种很好的抽象。引用 CodeType 构造函数的文档字符串

code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,
      constants, names, varnames, filename, name, firstlineno,
      lnotab[, freevars[, cellvars]])

Create a code object.  Not for the faint of heart.

codetransformer 抽象设计用来简化动态构造和检查这些对象。这允许我们轻松设置诸如参数名称和操作行号映射等事项。

Code 对象提供了将 Python 的代码表示转换为其他形式的方法

  1. from_pycode

  2. to_pycode.

这允许我们取一个现有的函数,从中解析出意义,修改它,然后将其组装成一个新的 Python 代码对象。

CodeTransformers

这是用来实际修改 Code 对象的规则集。这些规则定义为一组 patterns,这是一个 DSL,用于定义用于匹配 Instruction 对象序列的 DFA。一旦我们匹配了一个段,我们就产生新的指令来替换我们已匹配的内容。一个简单的 codetransformer 看起来像这样

from codetransformer import CodeTransformer, instructions

class FoldNames(CodeTransformer):
    @pattern(
        instructions.LOAD_GLOBAL,
        instructions.LOAD_GLOBAL,
        instructions.BINARY_ADD,
    )
    def _load_fast(self, a, b, add):
        yield instructions.LOAD_FAST(a.arg + b.arg).steal(a)

这个 CodeTransformer 使用 + 运算符实现类似于 C++ 的本地变量标记粘贴。我们将此模式解读为两个 LOAD_GLOBAL(全局名称查找)后跟一个 BINARY_ADD 指令(+ 操作符调用)。这将调用函数,并传递三个指令作为位置参数。此处理程序将此序列替换为单个指令,该指令发出一个 LOAD_FAST(局部名称查找),该名称是添加两个名称的结果。然后我们“窃取”任何曾以第一个 LOAD_GLOBAL 为目标的所有跳转。

我们可以通过在函数对象上调用它的实例或将其用作装饰器来执行此转换器。例如

>>> @FoldNames()
... def f():
...     ab = 3
...     return a + b
>>> f()
3

许可

codetransformer 是免费软件,根据 GNU 通用公共许可证,版本 2 授予许可。有关更多信息,请参阅 LICENSE 文件。

来源

源代码托管在GitHub上,地址为:https://github.com/llllllllll/codetransformer

项目详情


下载文件

下载适用于您平台的自定义文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源代码分发

codetransformer-0.7.2.tar.gz (73.6 kB 查看哈希值)

上传时间

由以下支持