fst
Version 0.2.4
Overview
This module exists in order to facilitate quick and easy high level editing of Python source in the form of an AST
tree while preserving formatting. It is meant to allow you to change python code functionality while not having to deal
with the miniutae of precedence, indentation, parentheses, commas, comments, docstrings, semicolons, line continuations,
else vs. elif, and all the various other niche special cases of Python syntax across different versions of the language.
See Example Recipes for more in-depth examples.
Example:
>>> import fst
>>> ext_ast = fst.parse('if a: b = c, d # comment')
>>> print(fst.unparse(ext_ast)) # formatting is preserved
if a: b = c, d # comment
Straightforward operations.
>>> ext_ast.f.body[0].body[0].value.elts[1:1] = 'u,
v # blah'
>>> print(fst.unparse(ext_ast))
if a: b = (c, u,
v, # blah
d) # comment
The tree is just normal AST with metadata.
>>> import ast
>>> print(ast.unparse(ext_ast))
if a:
b = (c, u, v, d)
fst works by adding FST nodes to existing AST nodes as an .f attribute which keep extra structure information,
the original source, and provide the interface to format-preserving operations. Each operation through fst is a
simultaneous edit of the AST tree and the source code and those are kept synchronized so that the current source will
always parse to the current tree.
Index
Links
Notes
fst was written and tested on Python versions 3.10 through 3.14.
fst works by keeping a copy of the entire source at the root FST node of a tree and modifying this source alongside
the node tree anytime an operation is performed natively.
fst does not do any parsing of its own but rather relies on the builtin Python parser and unparser. This means you
get perfect parsing but also that it is limited to the syntax of the running Python version (many options exist for
running any specific verison of Python).
fst does use standard Python parsing to parse things that can not normally be parsed, like individual exception
handlers or match cases, by wrapping them in corresponding code then pulling out and adjusting the locations of the
parsed ASTs. fst.docs.d01_parse.
fst validates for parsability, not compilability. This means that for fst, *a, *b = c and def f(a, a): pass are
both valid even though they are uncompilable.
Format preserving native modification operations exist in two flavors (see the documentation on how to use either):
Prescribed put operations which do specific things for each type of node being put, including indentation, precedence and syntax parenthesization, etc...
fst.docs.d05_put.Raw mode put operations which just put the raw source you want to replace and then attempt to reparse a small part of the full source around the changes (at least statement level).
fst.docs.d07_raw,fst.fst.FST.put_src().
There is also a mechanism for allowing outside editing of the AST tree and then reconciling with a marked snapshot
to preserve formatting where possible. This is intended for existing code or third-party libraries which don't know
anything about fst to maybe gain the ability to preserve some existing formatting when editing a tree.
fst.docs.d10_reconcile.
If you will be playing with this module then the FST.dump() method will be your friend.
Disclaimer: The intended use of this module is if you want to change code functionality without having to deal with
syntax details, not lint or format, there are better options for that. The main focus of fst is not necessarily to be
fast but rather to handle all the weird cases of python syntax correctly so that functional code always results, use a
formatter afterwards as needed.