This PEP was rejected by its author.
This PEP was seen as blessing new Python-like programming languageswhich are close but incompatible with the regular Python language. Itwas decided to not promote syntaxes incompatible with Python.
This PEP was also seen as a nice tool to experiment new Python features,but it is already possible to experiment them without the PEP, only withimportlib hooks. If a feature becomes useful, it should be directly partof Python, instead of depending on an third party Python module.
Finally, this PEP was driven was the FAT Python optimization projectwhich was abandoned in 2016, since it was not possible to show anysignificant speedup, but also because of the lack of time to implementthe most advanced and complex optimizations.
Propose an API to register bytecode and AST transformers. Add also-oOPTIM_TAG command line option to change.pyc filenames,-onoopt disables the peephole optimizer. Raise anImportErrorexception on import if the.pyc file is missing and the codetransformers required to transform the code are missing. codetransformers are not needed code transformed ahead of time (loaded from.pyc files).
Python does not provide a standard way to transform the code. Projectstransforming the code use various hooks. The MacroPy project uses animport hook: it adds its own module finder insys.meta_path tohook its AST transformer. Another option is to monkey-patch thebuiltincompile() function. There are even more options tohook a code transformer.
Python 3.4 added acompile_source() method toimportlib.abc.SourceLoader. But code transformation is wider thanjust importing modules, see described use cases below.
Writing an optimizer or a preprocessor is out of the scope of this PEP.
Transforming an Abstract Syntax Tree (AST) is a convenientway to implement an optimizer. It’s easier to work on the AST thanworking on the bytecode, AST contains more information and is more highlevel.
Since the optimization can done ahead of time, complex but slowoptimizations can be implemented.
Example of optimizations which can be implemented with an AST optimizer:
x=1;y=x withx=1;y=11+1 with2Using guards (seePEP 510), it is possible toimplement a much wider choice of optimizations. Examples:
range(3) with(0,1,2) when usedas iterablelen("abc") with3The following issues can be implemented with an AST optimizer:
A preprocessor can be easily implemented with an AST transformer. Apreprocessor has various and different usages.
Some examples:
MacroPy has a long list ofexamples and use cases.
This PEP does not add any new code transformer. Using a code transformerwill require an external module and to register it manually.
See alsoPyXfuscator: Pythonobfuscator, deobfuscator, and user-assisted decompiler.
Ned Batchelder asked to add an option to disable the peephole optimizerbecause it makes code coverage more difficult to implement. See thediscussion on the python-ideas mailing list:Disable all peepholeoptimizations.
This PEP adds a new-onoopt command line option to disable thepeephole optimizer. In Python, it’s as easy as:
sys.set_code_transformers([])
It will fix theIssue #2506: Addmechanism to disable optimizations.
Python 3.6 optimizes the code using a peephole optimizer. Bydefinition, a peephole optimizer has a narrow view of the code and socan only implement basic optimizations. The optimizer rewrites thebytecode. It is difficult to enhance it, because it written in C.
With this PEP, it becomes possible to implement a new bytecode optimizerin pure Python and experiment new optimizations.
Some optimizations are easier to implement on the AST like constantfolding, but optimizations on the bytecode are still useful. Forexample, when the AST is compiled to bytecode, useless jumps can beemitted because the compiler is naive and does not try to optimizeanything.
This section give examples of use cases explaining when and how codetransformers will be used.
It will be possible to use code transformers with the interactiveinterpreter which is popular in Python and commonly used to demonstratePython.
The code is transformed at runtime and so the interpreter can be slowerwhen expensive code transformers are used.
It will be possible to build a package of the transformed code.
A transformer can have a configuration. The configuration is not storedin the package.
All.pyc files of the package must be transformed with the same codetransformers and the same transformers configuration.
It is possible to build different.pyc files using differentoptimizer tags. Example:fat for the default configuration andfat_inline for a different configuration with function inliningenabled.
A package can contain.pyc files with different optimizer tags.
It will be possible to install a package which contains transformed.pyc files.
All.pyc files with any optimizer tag contained in the package areinstalled, not only for the current optimizer tag.
If a package does not contain any.pyc files of the currentoptimizer tag (or some.pyc files are missing), the.pyc arecreated during the installation.
Code transformers of the optimizer tag are required. Otherwise, theinstallation fails with an error.
It will be possible to execute transformed code.
Raise anImportError exception on import if the.pyc file of thecurrent optimizer tag is missing and the code transformers required totransform the code are missing.
The interesting point here is that code transformers are not needed toexecute the transformed code if all required.pyc files are alreadyavailable.
A code transformer is a class withast_transformer() and/orcode_transformer() methods (API described below) and anameattribute.
For efficiency, do not define acode_transformer() orast_transformer() method if it does nothing.
Thename attribute (str) must be a short string used to identifyan optimizer. It is used to build a.pyc filename. The name must notcontain dots ('.'), dashes ('-') or directory separators: dotsare used to separated fields in a.pyc filename and dashes areusedto join code transformer names to build the optimizer tag.
Note
It would be nice to pass the fully qualified name of a module in thecontext when an AST transformer is used to transform a module onimport, but it looks like the information is not available inPyParser_ASTFromStringObject().
Prototype:
defcode_transformer(self,code,context):...new_code=......returnnew_code
Parameters:
int), the optimizationlevel (0, 1 or 2). The value of theoptimize attribute comes from theoptimize parameter of thecompile() function, it is equal tosys.flags.optimize by default.Each implementation of Python can add extra attributes tocontext. Forexample, on CPython,context will also have the following attribute:
bool): true if in interactive modeXXX add more flags?
XXX replace flags int with a sub-namespace, or with specific attributes?
The method must return a code object.
The code transformer is run after the compilation to bytecode
Prototype:
defast_transformer(self,tree,context):...returntree
Parameters:
filename attribute (str)It must return an AST tree. It can modify the AST tree in place, orcreate a new AST tree.
The AST transformer is called after the creation of the AST by theparser and before the compilation to bytecode. New attributes may beadded tocontext in the future.
In short, add:
Add new functions to register code transformers:
sys.set_code_transformers(transformers): set the list of codetransformers and updatesys.implementation.optim_tagsys.get_code_transformers(): get the list of codetransformers.The order of code transformers matter. Running transformer A and thentransformer B can give a different output than running transformer B anthen transformer A.
Example to prepend a new code transformer:
transformers=sys.get_code_transformers()transformers.insert(0,new_cool_transformer)sys.set_code_transformers(transformers)
All AST transformers are run sequentially (ex: the second transformergets the input of the first transformer), and then all bytecodetransformers are run sequentially.
Changes:
sys.implementation.optim_tag (str): optimization tag.The default optimization tag is'opt'.-oOPTIM_TAG command line option to setsys.implementation.optim_tag.Changes onimportlib:
importlib usessys.implementation.optim_tag to build the.pyc filename to importing modules, instead of always usingopt. Remove also the special case for the optimizer level0with the default optimizer tag'opt' to simplify the code..pyc file is missing but the.pyis available, the.py is only used if code optimizers have thesame optimizer tag than the current tag, otherwise anImportErrorexception is raised.Pseudo-code of ause_py() function to decide if a.py file canbe compiled to import a module:
deftransformers_tag():transformers=sys.get_code_transformers()ifnottransformers:return'noopt'return'-'.join(transformer.namefortransformerintransformers)defuse_py():return(transformers_tag()==sys.implementation.optim_tag)
The order ofsys.get_code_transformers() matter. For example, thefat transformer followed by thepythran transformer gives theoptimizer tagfat-pythran.
The behaviour of theimportlib module is unchanged with the defaultoptimizer tag ('opt').
By default,sys.implementation.optim_tag isopt andsys.get_code_transformers() returns a list of one code transformer:the peephole optimizer (optimize the bytecode).
Use-onoopt to disable the peephole optimizer. In this case, theoptimizer tag isnoopt and no code transformer is registered.
Using the-oopt option has not effect.
Enhancements to simplify the implementation of AST transformers:
PyCF_TRANSFORMED_AST to get thetransformed AST.PyCF_ONLY_AST returns the AST before thetransformers.Example of.pyc filenames of theos module.
With the default optimizer tag'opt':
| .pyc filename | Optimization level |
|---|---|
os.cpython-36.opt-0.pyc | 0 |
os.cpython-36.opt-1.pyc | 1 |
os.cpython-36.opt-2.pyc | 2 |
With the'fat' optimizer tag:
| .pyc filename | Optimization level |
|---|---|
os.cpython-36.fat-0.pyc | 0 |
os.cpython-36.fat-1.pyc | 1 |
os.cpython-36.fat-2.pyc | 2 |
Scary bytecode transformer replacing all strings with"Ni!Ni!Ni!":
importsysimporttypesclassBytecodeTransformer:name="knights_who_say_ni"defcode_transformer(self,code,context):consts=['Ni! Ni! Ni!'ifisinstance(const,str)elseconstforconstincode.co_consts]returntypes.CodeType(code.co_argcount,code.co_kwonlyargcount,code.co_nlocals,code.co_stacksize,code.co_flags,code.co_code,tuple(consts),code.co_names,code.co_varnames,code.co_filename,code.co_name,code.co_firstlineno,code.co_lnotab,code.co_freevars,code.co_cellvars)# replace existing code transformers with the new bytecode transformersys.set_code_transformers([BytecodeTransformer()])# execute code which will be transformed by code_transformer()exec("print('Hello World!')")
Output:
Ni! Ni! Ni!
Similarly to the bytecode transformer example, the AST transformer alsoreplaces all strings with"Ni!Ni!Ni!":
importastimportsysclassKnightsWhoSayNi(ast.NodeTransformer):defvisit_Str(self,node):node.s='Ni! Ni! Ni!'returnnodeclassASTTransformer:name="knights_who_say_ni"def__init__(self):self.transformer=KnightsWhoSayNi()defast_transformer(self,tree,context):self.transformer.visit(tree)returntree# replace existing code transformers with the new AST transformersys.set_code_transformers([ASTTransformer()])# execute code which will be transformed by ast_transformer()exec("print('Hello World!')")
Output:
Ni! Ni! Ni!
ThePEP 511 should be implemented by all Python implementation, but thebytecode and the AST are not standardized.
By the way, even between minor version of CPython, there are changes onthe AST API. There are differences, but only minor differences. It isquite easy to write an AST transformer which works on Python 2.7 andPython 3.5 for example.
The Issue #17515“Add sys.setasthook() to allow to use a custom AST”optimizer was a first attempt ofAPI for code transformers, but specific to AST.
In 2015, Victor Stinner wrote thefatoptimizer project, an AST optimizerspecializing functions using guards.
In 2014, Kevin Conway created thePyCCoptimizer.
In 2012, Victor Stinner wrote theastoptimizer project, an AST optimizerimplementing various optimizations. Most interesting optimizations breakthe Python semantics since no guard is used to disable optimization ifsomething changes.
In 2011, Eugene Toder proposed to rewrite some peephole optimizations ina new AST optimizer: issue #11549,Build-out an AST optimizer, movingsome functionality out of the peephole optimizer. The patch addsast.Lit (itwas proposed to rename it toast.Literal).
#define and#ifdefast module’sNodeTransformer.See also:
This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-0511.rst
Last modified:2025-02-01 08:59:27 GMT