codetransformer 0.7.2
pip install codetransformer
Released:
Python code object transformers
Navigation
Unverified details
These details havenot been verified by PyPIProject links
Meta
- License: GNU General Public License v2 (GPLv2) (GPL-2)
- Author:Joe Jevnik and Scott Sanderson
Classifiers
- Development Status
- License
- Natural Language
- Operating System
- Programming Language
- Topic
Project description
Bytecode transformers for CPython inspired by theast module’sNodeTransformer.
What iscodetransformer?
codetransformer is a library that allows us to work with CPython’s bytecoderepresentation at runtime.codetransformer provides a level of abstractionbetween the programmer and the raw bytes read by the eval loop so that we canmore easily inspect and modify bytecode.
codetransformer is motivated by the need to override parts of the pythonlanguage that are not already hooked into through data model methods. For example:
Override theis andnot operators.
Custom data structure literals.
Syntax features that cannot be represented with valid python AST or source.
Run without a modified CPython interpreter.
codetransformer was originally developed as part oflazy to implementthe transformations needed to override the code objects at runtime.
Example Uses
Overloading Literals
While this can be done as an AST transformation, we will often need to executethe constructor for the literal multiple times. Also, we need to be sure thatany additional names required to run our code are provided when we run. Withcodetransformer, we can pre compute our new literals and emit code that isas fast as loading our unmodified literals without requiring any additionalnames be available implicitly.
In the following block we demonstrate overloading dictionary syntax to result incollections.OrderedDict objects.OrderedDict is like adict;however, the order of the keys is preserved.
>>>fromcodetransformer.transformers.literalsimportordereddict_literals>>>@ordereddict_literals...deff():...return{'a':1,'b':2,'c':3}>>>f()OrderedDict([('a',1),('b',2),('c',3)])This also supports dictionary comprehensions:
>>>@ordereddict_literals...deff():...return{k:vfork,vinzip('abc',(1,2,3))}>>>f()OrderedDict([('a',1),('b',2),('c',3)])The next block overridesfloat literals withdecimal.Decimalobjects. These objects support arbitrary precision arithmetic.
>>>fromcodetransformer.transformers.literalsimportdecimal_literals>>>@decimal_literals...deff():...return1.5>>>f()Decimal('1.5')Pattern Matched Exceptions
Pattern matched exceptions are a good example of aCodeTransformer thatwould be very complicated to implement at the AST level. This transformationextends thetry/except syntax to accept instances ofBaseException aswell subclasses ofBaseException. When excepting an instance, theargsof the exception will be compared for equality to determine which exceptionhandler should be invoked. For example:
>>>@pattern_matched_exceptions()...deffoo():...try:...raiseValueError('bar')...exceptValueError('buzz'):...return'buzz'...exceptValueError('bar'):...return'bar'>>>foo()'bar'This function raises an instance ofValueError and attempts to catch it. Thefirst check looks for instances ofValueError that were constructed with anargument of'buzz'. Because our custom exception is raised with'bar',these are not equal and we do not enter this handler. The next handler looks forValueError('bar') which does match the exception we raised. We then enterthis block and normal python rules take over.
We may also pass their own exception matching function:
>>>defmatch_greater(match_expr,exc_type,exc_value,exc_traceback):...returnmath_expr>exc_value.args[0]>>>@pattern_matched_exceptions(match_greater)...deffoo():...try:...raiseValueError(5)...except4:...return4...except5:...return5...except6:...return6>>>foo()6This matches on when the match expression is greater in value than the firstargument of any exception type that is raised. This particular behavior would bevery hard to mimic through AST level transformations.
Core Abstractions
The three core abstractions ofcodetransformer are:
TheInstruction object which represents anopcode which may be pairedwith some argument.
TheCode object which represents a collection ofInstructions.
TheCodeTransformer object which represents a set of rules formanipulatingCode objects.
Instructions
TheInstruction object represents an atomic operation that can be performedby the CPython virtual machine. These are things likeLOAD_NAME which loadsa name onto the stack, orROT_TWO which rotates the top two stack elements.
Some instructions accept an argument, for exampleLOAD_NAME, which modifiesthe behavior of the instruction. This is much like a function call where somefunctions accept arguments. Because the bytecode is always packed as raw bytes,the argument must be some integer (CPython stores all arguments two in bytes).This means that things that need a more rich argument system (likeLOAD_NAMEwhich needs the actual name to look up) must carry around the actual argumentsin some table and use the integer as an offset into this array. One of the keyabstractions of theInstruction object is that the argument is always somepython object that represents the actual argument. Any lookup table managementis handled for the user. This is helpful because some arguments share this tableso we don’t want to add extra entries or forget to add them at all.
Another annoyance is that the instructions that handle control flow use theirargument to say what bytecode offset to jump to. Some jumps use the absoluteindex, others use a relative index. This also makes it hard if you want to addor remove instructions because all of the offsets must be recomputed. Incodetransformer, the jump instructions all accept anotherInstruction asthe argument so that the assembler can manage this for the user. We also providean easy way for new instructions to “steal” jumps that targeted anotherinstruction so that can manage altering the bytecode around jump targets.
Code
Code objects are a nice abstraction over python’stypes.CodeType. Quoting theCodeType constructor docstring:
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.
Thecodetransformer abstraction is designed to make it easy to dynamicallyconstruct and inspect these objects. This allows us to easy set things like theargument names, and manipulate the line number mappings.
TheCode object provides methods for converting to and from Python’s coderepresentation:
from_pycode
to_pycode.
This allows us to take an existing function, parse the meaning from it, modifyit, and then assemble this back into a new python code object.
Note
Code objects are immutable. When we say “modify”, we mean create a copywith different values.
CodeTransformers
This is the set of rules that are used to actually modify theCodeobjects. These rules are defined as a set ofpatterns which are a DSL usedto define a DFA for matching against sequences ofInstruction objects. Oncewe have matched a segment, we yield new instructions to replace what we havematched. A simple codetransformer looks like:
fromcodetransformerimportCodeTransformer,instructionsclassFoldNames(CodeTransformer):@pattern(instructions.LOAD_GLOBAL,instructions.LOAD_GLOBAL,instructions.BINARY_ADD,)def_load_fast(self,a,b,add):yieldinstructions.LOAD_FAST(a.arg+b.arg).steal(a)ThisCodeTransformer uses the+ operator to implement something likeCPPs token pasting for local variables. We read this pattern as a sequenceof twoLOAD_GLOBAL (global name lookups) followed by aBINARY_ADDinstruction (+ operator call). This will then call the function with thethree instructions passed positionally. This handler replaces this sequence witha single instruction that emits aLOAD_FAST (local name lookup) that is theresult of adding the two names together. We then steal any jumps that used totarget the firstLOAD_GLOBAL.
We can execute this transformer by calling an instance of it on afunction object, or using it like a decorator. For example:
>>>@FoldNames()...deff():...ab=3...returna+b>>>f()3License
codetransformer is free software, licensed under the GNU General PublicLicense, version 2. For more information see theLICENSE file.
Source
Source code is hosted on github athttps://github.com/llllllllll/codetransformer.
Project details
Unverified details
These details havenot been verified by PyPIProject links
Meta
- License: GNU General Public License v2 (GPLv2) (GPL-2)
- Author:Joe Jevnik and Scott Sanderson
Classifiers
- Development Status
- License
- Natural Language
- Operating System
- Programming Language
- Topic
Release historyRelease notifications |RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more aboutinstalling packages.
Source Distribution
File details
Details for the filecodetransformer-0.7.2.tar.gz.
File metadata
- Download URL:codetransformer-0.7.2.tar.gz
- Upload date:
- Size: 73.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 | 442aca89cc961f8c9234dd17e35d36cdeaca670f167610e67cef232426705118 | |
| MD5 | 1ca91458bc5a82d9f3f1a5b468af785c | |
| BLAKE2b-256 | 7bde7b1dc2314b04b7d3a6ab3d1e0c2e24b3db51f9147dbcde5bd3840475368b |