fst.fst
Executes ast.parse() and then adds FST nodes to the parsed tree. Drop-in replacement for ast.parse(). For
parameters, see ast.parse(). Returned AST tree has added .f attribute at each node which accesses the parallel
FST tree.
Parameters:
source: The python source to parse.filename:ast.parse()parameter.mode: Parse mode, extendedast.parse()parameter, Seefst.parsex.Mode.type_comments:ast.parse()parameter.feature_version:ast.parse()parameter.
Returns:
AST: Tree with anFST.fattribute added to eachASTnode.
Examples:
>>> import ast, fst
>>> a = fst.parse('if 1:\n i = 2')
>>> type(a)
<class 'ast.Module'>
>>> a.f # FST node
<Module ROOT 0,0..1,7>
>>> print(fst.dump(a, indent=2))
Module(
body=[
If(
test=Constant(value=1),
body=[
Assign(
targets=[
Name(id='i', ctx=Store())],
value=Constant(value=2))],
orelse=[])],
type_ignores=[])
>>> a.f.dump()
Module - ROOT 0,0..1,7
.body[1]
0] If - 0,0..1,7
.test Constant 1 - 0,3..0,4
.body[1]
0] Assign - 1,2..1,7
.targets[1]
0] Name 'i' Store - 1,2..1,3
.value Constant 2 - 1,6..1,7
Returns the formatted source that is kept for this tree. Drop-in replacement for ast.unparse() If there is no
FST information in the AST tree then just executes ast.unparse().
Parameters:
ast_obj: TheASTto unparse.
Returns:
str: The unparsed source code, formatted if it came fromFST.
Examples:
>>> import ast, fst
>>> a = fst.parse('''
... if 1: i = 1 # same line
... else:
... j=2 # comment
... '''.strip())
>>> print(ast.unparse(a))
if 1:
i = 1
else:
j = 2
>>> print(fst.unparse(a))
if 1: i = 1 # same line
else:
j=2 # comment
>>> a = ast.parse('i = 1')
>>> ast.unparse(a)
'i = 1'
>>> fst.unparse(a) # also unparses regular AST
'i = 1'
This function is a convenience function and only exists to make python version 3.13 and above ast.dump() output
compatible on a default call with previous python versions (important for doctests). All arguments correspond to
their respective ast.dump() arguments and show_empty is eaten on python versions below 3.13.
Class which maintains structure and formatted source code for an AST tree. An instance of this class is added
to each AST node in a tree. It provides format-preserving operations as well as ability to navigate the tree in
any direction.
Create a new individual FST node or full tree. The main way to use this constructor is as a shortcut for
FST.fromsrc() or FST.fromast(), the usage is:
FST(ast_or_src, mode=None)
This will create an FST from either an AST or source code in the form of a string or list of lines. The
first parameter can be None instead of an AST or source to indicate a blank new module of one of the three
types 'exec', 'eval' or 'single'. Otherwise if there is source or an AST then mode specifies how it
will be parsed / reparsed and it can take any of the values from fst.parsex.Mode.
Parameters:
ast_or_src: Source code, anASTnode orNone.mode: Seefst.parsex.Mode. If this isNonethen ifast_or_srcis anASTthe mode defaults to the type of theAST. Otherwise if theast_or_srcis actual source code thenmodeused is'all'to allow parsing anything. And ifast_or_srcisNonethenmodemust be provided and be one of'exec','eval'or'single'.
The other forms of this function are meant for internal use and their parameters are below:
Parameters:
ast_or_src:ASTnode forFSTor source code in the form of astror a list of lines. If anASTthen will be processed differently depending on if creating child node, top level node or using this as a shortcut for a fullfromsrc()orfromast().mode: Is reallymode_or_lines_or_parent. Parent node for this child node or lines for a root node creating a new tree. IfpfieldisFalsethen this is a shortcut to create a full tree from anASTnode or source provided inast_or_src.pfield:astfieldindication position in parent of this node. If provided then creating a simple child node and it is created with theself.parentset tomodenode andself.pfieldset to this. IfNonethen it means the creation of a full newFSTtree and this is the root node withmodeproviding the source. IfFalsethen this is a shortcut forFST.fromast()orFST.fromsrc()orFST.new().kwargs: Contextual parameters:from_: If this is provided then it must be anFSTnode from which this node is being created. This allows to copy parse parameters and already determined default indentation.parse_params: Adictwith values forfilename,type_commentsandfeature_versionwhich will be used for anyASTreparse done on this tree. Only valid when creating a root node.indent: Indentation string to use as default indentation. If not provided and not gotten fromfrom_then indentation will be inferred from source. Only valid when creating a root node.filename,type_commentsandfeature_version: If creating from anASTor source only then these are the parameteres passed to the respective.new(),.fromsrc()or.fromast()functions. Only valid whenmodeandpfieldareNone.lcopy: Whether to copy lines of source on root node create or just use what is passed in, which in this case must be a list ofbistrand this node takes ownership of the list.
The astfield location of this node in the parent, None in root node.
The parameters to use for any ast.parse() that needs to be done (filename, type_comments, feature_version), root node only.
The default single level of block indentation string for this tree when not available from context, root node only.
Whole lines which contain this node, may also contain parts of enclosing nodes. If gotten at root then the entire source is returned, which may extend beyond the location of the top level node (mostly for statements which may have leading / trailing comments or empty lines).
Note: The lines list returned is always a copy so safe to modify.
Source code of this node clipped out of as a single string, without any dedentation. Will have indentation as it appears in the top level source if multiple lines. If gotten at root then the entire source is returned, regardless of whether the actual top level node location includes it or not.
Whole source location, from 0,0 to end of source. Works from any node (not just root).
Zero based character indexed location of node (may not be entire location if node has decorators). Not all
nodes have locations (like expr_context). Other nodes which normally don't have locations like arguments or
most operators have this location calculated from their children or source. NOTE: Empty arguments do NOT have
a location even though the AST exists.
Bounding location of node. For non-statements or non-block statements is same as loc. For block statements
will include any leading decorators and a trailing line comment on the last child (or self if no children).
Examples:
>>> f = FST('''
... @decorator
... def func():
... pass # comment
... '''.strip(), 'exec')
>>> f.body[0].bloc
fstloc(0, 0, 2, 19)
>>> f.body[0].loc
fstloc(1, 0, 2, 8)
>>> f = FST('stmt # comment', 'exec')
>>> f.body[0].bloc # non-block statement doesn't include comment
fstloc(0, 0, 0, 4)
CHARACTER column of the end of the last line of this node, past a trailing line comment on last child if
self is a block statement.
AST-style Line number of the first line of this node (1 based), available for all nodes which have loc.
AST-style BYTE index of the start of this node (0 based), available for all nodes which have loc.
AST-style Line number of the LAST LINE of this node (1 based), available for all nodes which have loc.
AST-style BYTE index one past the end of this node (0 based), available for all nodes which have loc.
Pure bool for whether this node has a docstring if it is a FunctionDef, AsyncFunctionDef, ClassDef or
Module. For quick use as starting index.
The docstring of this node if it is a FunctionDef, AsyncFunctionDef, ClassDef or Module. None if
not one of those nodes or has no docstring. Keep in mind an empty docstring may exist but will be a falsey
value so make sure to check for None. If you want the actual docstring node then just check for presence and
get .body[0].
Create a new empty FST tree with the top level node dictated by the mode parameter.
Parameters:
mode:ast.parse()parameter, can only be'exec','eval'or'single'here.filename:ast.parse()parameter.type_comments:ast.parse()parameter.feature_version:ast.parse()parameter.
Returns:
Examples:
>>> FST.new()
<Module ROOT 0,0..0,0>
>>> FST.new(mode='single')
<Interactive ROOT 0,0..0,0>
>>> FST.new(mode='eval')
<Expression ROOT 0,0..0,4>
>>> _.dump()
Expression - ROOT 0,0..0,4
.body Constant None - 0,0..0,4
>>> _.src
'None'
Parse and create a new FST tree from source, preserving the original source and locations.
Parameters:
src: The source to parse as a singlestror list of individual line strings (without newlines).mode: Parse mode, extendedast.parse()parameter, Seefst.parsex.Mode.filename:ast.parse()parameter.type_comments:ast.parse()parameter.feature_version:ast.parse()parameter.
Returns:
Examples:
>>> FST.fromsrc('var').dump()
Module - ROOT 0,0..0,3
.body[1]
0] Expr - 0,0..0,3
.value Name 'var' Load - 0,0..0,3
>>> FST.fromsrc('var', mode='stmt').dump()
Expr - ROOT 0,0..0,3
.value Name 'var' Load - 0,0..0,3
>>> FST.fromsrc('var', mode='expr').dump()
Name 'var' Load - ROOT 0,0..0,3
>>> FST.fromsrc('except Exception: pass', 'ExceptHandler').dump()
ExceptHandler - ROOT 0,0..0,22
.type Name 'Exception' Load - 0,7..0,16
.body[1]
0] Pass - 0,18..0,22
>>> FST.fromsrc('case f(a=1): pass', 'match_case').dump()
match_case - ROOT 0,0..0,17
.pattern MatchClass - 0,5..0,11
.cls Name 'f' Load - 0,5..0,6
.kwd_attrs[1]
0] 'a'
.kwd_patterns[1]
0] MatchValue - 0,9..0,10
.value Constant 1 - 0,9..0,10
.body[1]
0] Pass - 0,13..0,17
>>> import ast
>>> FST.fromsrc('a:b', ast.Slice).dump()
Slice - ROOT 0,0..0,3
.lower Name 'a' Load - 0,0..0,1
.upper Name 'b' Load - 0,2..0,3
Unparse and reparse an AST for new FST (the reparse is necessary to make sure locations are correct).
Parameters:
ast: The rootASTnode.mode: Parse mode, extendedast.parse()parameter, seefst.parsex.Mode. Two special values are added:None: This will attempt to reparse to the same node type as was passed in. This is the default and all other values should be considered overrides for special cases.False: This will skip the reparse and justast.unparse()theASTto generate source for theFST. Use this only if you are absolutely certain that theASTunparsed source will correspond with the locations already present in theAST. This is almost never the case unless theASTwasast.parse()d from an explicitlyast.unparse()dAST.
filename:ast.parse()parameter.type_comments:ast.parse()parameter.feature_version:ast.parse()parameter.ctx: Whether to make sure that thectxfield of the reparsedASTmatches or not.Falsefor convenience,Trueif you're feeling pedantic.
Returns:
Examples:
>>> import ast
>>> from ast import Assign, Slice, Constant
>>> FST.fromast(Assign(targets=[Name(id='var')],
... value=Constant(value=123))).dump('stmt')
0: var = 123
Assign - ROOT 0,0..0,9
.targets[1]
0] Name 'var' Store - 0,0..0,3
.value Constant 123 - 0,6..0,9
>>> FST.fromast(ast.parse('if 1:\n j = 5')).dump('stmt')
Module - ROOT 0,0..1,9
.body[1]
0: if 1:
0] If - 0,0..1,9
.test Constant 1 - 0,3..0,4
.body[1]
1: j = 5
0] Assign - 1,4..1,9
.targets[1]
0] Name 'j' Store - 1,4..1,5
.value Constant 5 - 1,8..1,9
>>> FST.fromast(Slice(lower=Constant(value=1), step=Name(id='step'))).dump('node')
0: 1::step
Slice - ROOT 0,0..0,7
0: 1
.lower Constant 1 - 0,0..0,1
0: step
.step Name 'step' Load - 0,3..0,7
Get a dictionary of ALL options. These are the same options that can be passed to operations and this
function returns their global defaults which are used when those options are not passed to operations or if they
are passed with a value of None.
When these options are missing or None in a call to an operation, then the default option as specified here is
used.
Parameters:
Returns:
{option: value, ...}: Dictionary of all global default options.
Examples:
>>> from pprint import pp
>>> pp(FST.get_options())
{'raw': False,
'trivia': True,
'coerce': True,
'elif_': True,
'pep8space': True,
'docstr': True,
'pars': 'auto',
'pars_walrus': True,
'pars_arglike': True,
'norm': False,
'norm_self': None,
'norm_get': None,
'norm_put': None,
'set_norm': 'both',
'matchor_norm': 'value',
'op_side': 'left'}
Get a single option from options dict or global default if option not in dict or is None there. For a
list of options used see options().
Parameters:
option: Name of option to get, seeoptions().options: Dictionary which may or may not contain the requested option.
Returns:
Any: Theoptionvalue from the passedoptionsdict, if passed and notNonethere, else the global default value foroption.
Examples:
>>> FST.get_option('pars')
'auto'
>>> FST.get_option('pars', {'pars': True})
True
>>> FST.get_option('pars', {'pars': None})
'auto'
Set global defaults for options parameters.
Parameters:
options: Names / values of parameters to set. These can also be passed to various methods to override the defaults set here for those individual operations, seeoptions().
Returns:
options:dictof previous values of changed parameters, reset withset_options(**options).
Examples:
>>> FST.get_option('pars')
'auto'
>>> FST.set_options(pars=False)
{'pars': 'auto'}
>>> FST.get_option('pars')
False
>>> FST.set_options(**{'pars': 'auto'})
{'pars': False}
>>> FST.get_option('pars')
'auto'
>>> FST.set_options(pars=True, raw=True, docstr=False)
{'pars': 'auto', 'raw': False, 'docstr': True}
Context manager to temporarily set global options defaults for a group of operations.
WARNING! Only the options specified in the call to this function will be returned to their original values when the context manager exits.
Options:
raw: When to do raw source operations. This may result in more nodes changed than just the targeted one(s).False: Do not do raw source operations.True: Only do raw source operations.'auto': Only do raw source operations if the normal operation fails in a way that raw might not.
trivia: What comments and empty lines to copy / delete when doing operations on elements which may have leading or trailing lines of stuff.False: Don't copy / delete any trivia.True: Same as('block', 'line').'all': Same as('all', 'all').'block': Same as('block', 'block').(leading, trailing): Tuple specifying individual behavior for leading and trailing trivia. The text options can also have a suffix of the form'+/-[#]', meaning plus or minus an optional integer which adds behavior for leading or trailing empty lines, explained below. The values for each element of the tuple can be:False: Don't copy / delete any trivia.True: For leading means'block', for trailing means'line'.'all[+/-[#]]': Get all leading or trailing comments regardless of if they are contiguous or not.'block[+/-[#]]': Get a single contiguous leading or trailing block of comments, an empty line ends the block.'line[+/-[#]]': Valid for trailing trivia only, means just the comment on the last line of the element.int: A specific line number specifying the first or last line that can be returned as a comment or empty line. If not interrupted by other code, will always return up to this line.
coerce: Whether to allow coercion between compatibleAST/FSTtypes on put. For example allow put a non-sliceexpras a slice to something that expects a slice ofexprs or allowing use ofargwhereargumentsis expected.elif_: How to handle loneIfstatements as the only statements in anIfstatementorelsefield.False: Always put as a standaloneIfstatement.True: If putting a singleIfstatement to anorelsefield of a parentIfstatement then put it as anelif.
pep8space: Preceding and trailing empty lines for function and class definitions.False: No empty lines.True: Two empty lines at module scope and one empty line in other scopes.1: One empty line in all scopes.
docstr: Which docstrings are indentable / dedentable.False: None.True: AllExprstring constants (as they serve no other coding purpose).'strict': Only string constants in expected docstring positions (functions, classes and top of module).
pars: How parentheses are handled, can beFalse,Trueor'auto'. This is for individual element operations, slice operations ignore this as parentheses usually cannot be removed or may need to be added to keep the slices usable. Raw puts generally do not have parentheses added or removed automatically, except maybe removed according to this from the destination node if putting to a node instead of a pure location.False: Parentheses are not MODIFIED, doesn't mean remove all parentheses. Not copied with nodes or removed on put from source or destination. Using incorrectly can result in invalid trees.True: Parentheses are copied with nodes, added to copies if needed and not present, removed from destination on put if not needed there (but not source).'auto': Same asTrueexcept they are not returned with a copy and possibly removed from source on put if not needed (removed from destination first if needed and present on both).
pars_walrus: Whether to ADD parentheses to top level cut / copiedNamedExprnodes or not. If parentheses were already copied due topars=Truethen setting this toFalsewill not remove them.True: Parenthesize cut / copiedNamedExprwalrus expressions.False: Do not parenthesize cut / copiedNamedExprwalrus expressions.None: Parenthesize according to theparsoption.
pars_arglike: Whether to ADD parentheses to argument-like expressions (*not a,*b or c) when cut / copied either as single element or as part of a slice. If parentheses were already present then setting this toFalsewill not remove them.True: Parenthesize cut / copied argument-like expressions.False: Do not parenthesize cut / copied argument-like expressions.None: Parenthesize according to theparsoption.
norm: Default normalize option for puts, gets and self target. Determines howASTs which would otherwise be invalid because of an operation are handled. Mostly how zero or sometimes one-length elements which normally cannot be zero or one length are left / put / returned, e.g. zero-lengthSet. This option can be overridden individually for the three cases ofnorm_self(target),norm_get(return from aget()) andnorm_put(what is being put if it is invalid or an alternate representation).False: Allow theASTto go to an unsupported length or state and become invalid. ASetwill result in empty curlies which reparse to aDict. AMatchOrcan go to 1 or zero length. OtherASTtypes can also go to zero length. Useful for easier editing.True: Do not allow theASTto go to an unsupported length or state. Can result in an exception being thrown no alternative exists or an alternate representation being used or accepted, e.g. ASetwhich results in zero length gets converted to{*()}.str: In some cases an alternate representation can be specified instead of justTrue, e.g.'call'forSetoperations.
norm_self: Override fornormwhich only applies to a targetselfon which an operation is being carried out. If this isNonethennormis used.norm_get: Override fornormwhich only applies to the return value from aget()operation. If this isNonethennormis used.norm_put: Override fornormwhich only applies to the value to put for aput()operation. If this isNonethennormis used.set_norm: The alternate representation for an emptySetnormalization bynorm. This can also be set toFalseto disable normalization for all operations on aSet(unless one of thenormoptions is set to one of these string modes).'star': Starred sequence{*()}returned, this or other starred sequences{*[]}and{*{}}accepted to mean empty set on put operations.'call':set()call returned and recognized as empty.'both': Both'star'and'call'recognized on put as empty,'start'used for return from get operation and normalization ofself.False: NoSetnormalization regardless ofnormornorm_*options, just leave or return an invalidSetobject.
matchor_norm: The possible alternate representation forMatchOrnodes with length 1. Length zero is always error unlessFalse. This can also be set toFalseto disable normalization for all operations on aMatchOr(unless one of thenormoptions is set to one of these string modes).value: Convert to single elementpatternif length 1.strict: Error on length 1 as well as length zero.False: NoMatchOrnormalization regardless ofnormornorm_*options, just leave or return an invalidMatchOrobject. -op_side: When doing slice operations on aBoolOpor aCompareit may be necessary to specify which side operator is to be deleted or inserted before or after. This can take the values of'left'or'right'and specifies which side operator to delete for delete operations. For insert operations this specifies whether to insert before an operator'left'or operand'right', roughly translating to which side operator is treated as part of the operator+operand unit. This option is treated as a hint and may be overridden by source code passed to the slice operation or slice position constraints, it will never raise an error if set incompatible with what the operation needs. When inserting into aComparea non-globalopoption may be necessary to specify extra missing operator to add if a dangling operator is not passed in the source to insert.'left': Delete preceding operator on left side of slice or insert before preceding operator.'right': Delete trailing operator on right side of slice or insert after preceding operator.
Note: pars behavior:
False True 'auto'
Copy pars from source on copy / cut: no yes no
Add pars needed for parsability to copy: no yes yes
Remove unneeded pars from destination on put: no yes yes
Remove unneeded pars from source on put: no no yes
Add pars needed for parse / precedence to source on put: no yes yes
Note: trivia text suffix behavior:
'+[#]'On copy and delete an extra number of lines after any comments specified by the#. If no number is specified and just a'+'then all empty lines will be copied / deleted.'-[#]'On delete but not copy, an extra number of lines after any comments specified by the#. If no number is specified and just a'-'then all empty lines will be deleted.
Examples:
>>> print(FST.get_option('pars'), FST.get_option('elif_'))
auto True
>>> with FST.options(pars=False, elif_=False):
... print(FST.get_option('pars'), FST.get_option('elif_'))
False False
>>> print(FST.get_option('pars'), FST.get_option('elif_'))
auto True
Dump a representation of the tree to stdout or other TextIO or return as a str or list of lines, or
call a provided function once with each line of the output.
Parameters:
src: Either what level of source to show along with the nodes or a shorthand string which can specify almost all the formatting parameters as a string of characters.'stmt'or'stmt+'means output statement source lines (includingExceptHandlerandmatch_case) or top level source if level is below statement. The+means put all lines of source including non-coding ones so that whole source is shown.'node'or'node+'means output source for each individual node. The+means the same as for'stmt+'.Nonedoes not output any source.str: Can be a string for shortcut specification of source and flags by first letter,'s+feL'would be equivalent to.dump(src='stmt+', full=True, expand=True, loc=False).'s'or's+'meanssrc='stmt'orsrc='stmt+''n'or 'n+'meanssrc='node'orsrc='node+''f'meansfull=True'e'meansexpand=True'i'meanslist_indent=indent'I'meanslist_indent=False'L'meansloc=False'c'meanscolor=True'C'meanscolor=False
full: IfTruethen will list all fields in nodes including empty ones, otherwise will exclude most empty fields.expand: IfTruethen the output is a nice compact representation. IfFalsethen it is ugly and wasteful.indent: Indentation per level as an integer (number of spaces) or a string.list_indent: Extra indentation for elements of lists as an integer or string (added to indent, normally 0). IfTruethen will be same asindent.loc: Whether to put location of node in source or not.color:TrueorFalsemeans whether to use ANSI color codes or not. IfNonethen will only do so ifout=printandsys.stdout.isatty()and not overridden by environment variables.out:printmeans print to stdout,listreturns a list of lines andstrreturns a whole string.TextIOwill cann thewritemethod for each line of output. Otherwise aCallable[[str], None]which is called for each line of output individually.eol: What to put at the end of each text line,Nonemeans newline forTextIOout and nothing for other.
Returns:
str | list[str]: If those were requested without=strorout=listelseNoneand the output is sent one line at a time tolinefunc, which by default isprint.None: Otherwise.
Examples:
>>> f = FST('''
... if 1:
... call(a=b, **c)
... '''.strip())
>>> f.dump()
If - ROOT 0,0..1,18
.test Constant 1 - 0,3..0,4
.body[1]
0] Expr - 1,4..1,18
.value Call - 1,4..1,18
.func Name 'call' Load - 1,4..1,8
.keywords[2]
0] keyword - 1,9..1,12
.arg 'a'
.value Name 'b' Load - 1,11..1,12
1] keyword - 1,14..1,17
.value Name 'c' Load - 1,16..1,17
>>> f.dump(src='node', indent=3, list_indent=True)
0: if 1:
If - ROOT 0,0..1,18
0: 1
.test Constant 1 - 0,3..0,4
.body[1]
1: call(a=b, **c)
0] Expr - 1,4..1,18
.value Call - 1,4..1,18
1: call
.func Name 'call' Load - 1,4..1,8
.keywords[2]
1: a=b
0] keyword - 1,9..1,12
.arg 'a'
1: b
.value Name 'b' Load - 1,11..1,12
1: **c
1] keyword - 1,14..1,17
1: c
.value Name 'c' Load - 1,16..1,17
>>> f.dump(out=str)[:64]
'If - ROOT 0,0..1,18\n .test Constant 1 - 0,3..0,4\n .body[1]\n '
>>> from pprint import pp
>>> pp(f.dump('stmt', loc=False, out=list))
['0: if 1:',
'If - ROOT',
' .test Constant 1',
' .body[1]',
'1: call(a=b, **c)',
' 0] Expr',
' .value Call',
" .func Name 'call' Load",
' .keywords[2]',
' 0] keyword',
" .arg 'a'",
" .value Name 'b' Load",
' 1] keyword',
" .value Name 'c' Load"]
Sanity check. Walk the tree and make sure all ASTs have corresponding FST nodes with valid parent / child
links, then (optionally) reparse source and make sure parsed tree matches currently stored tree (locations and
everything). The reparse can only be carried out on root nodes but the link validation can be done on any level.
SPECIAL SLICEs like _decorator_list will verify ok with reparse but invalid nodes like an empty Set or
block statements with empty bodies will not.
Parameters:
mode: Parse mode to use, otherwise ifNonethen use the top level AST node type for the mode. Depending on how this is set will determine whether the verification is checking if is parsable by python ('exec'or'strict'for example), or if the node itself is just in a valid state (whereNoneis good). Seefst.parsex.Mode.reparse: Whether to reparse the source and compare ASTs (including location). Otherwise the check is limited to a structure check that all children haveFSTnodes which are all liked correctly to their parents.reparse=Trueonly allowed on root node.locs: Whether to compare locations after reparse or not.ctx: Whether to comparectxnodes after reparse or not.raise_: Whether to raise an exception on verify failed or returnNone.
Returns:
Noneon failure to verify (if notraise_), otherwiseself.
Examples:
>>> FST('var = 123').verify()
<Assign ROOT 0,0..0,9>
>>> FST('a:b:c').verify()
<Slice ROOT 0,0..0,5>
>>> bool(FST('a:b:c').verify('exec', raise_=False))
False
>>> FST('if a if b').verify()
<_comprehension_ifs ROOT 0,0..0,9>
>>> bool(FST('if a if b').verify('strict', raise_=False))
False
Return an object marking the current state of this FST tree. Used to reconcile() later for non-FST
operation changes made (changing AST nodes directly). Currently is just a copy of the original tree but may
change in the future.
Returns:
FST: A marked copy ofselfwith any necessary information added for a laterreconcile().
Reconcile self with a previously marked version and return a new valid FST tree. This is meant for
allowing non-FST modifications to an FST tree and later converting it to a valid FST tree to preserve as
much formatting as possible and maybe continue operating in FST land. Only AST nodes from the original tree
carry formatting information, so the more of those are replaced the more formatting is lost.
Note: When replacing the AST nodes, make sure you are replacing the nodes in the parent AST fields, not
the .a attribute in FST nodes, that won't do anything.
WARNING! Just like an ast.unparse(), the fact that this function completes successfully does NOT mean the
output is syntactically correct if you put weird nodes where they don't belong, maybe accidentally. In order to
make sure the result is valid (syntactically) you should run verify() on the output. This still won't
guarantee you have actual valid code, def f(x, x): pass parses ok but will cause an error if you try to
compile it.
Parameters:
mark: A previously marked snapshot ofself. This object is not consumed on use, success or failure.options: Seeoptions().
Returns:
Examples:
>>> f = FST('''
... @decorator # something
... def function(a: int, b=2)->int: # blah
... return a+b # return this
...
... def other_function(a, b):
... return a - b # return that
... '''.strip())
>>> m = f.mark()
>>> f.a.body[0].returns = Name('float') # pure AST
>>> f.a.body[0].args.args[0].annotation = Name('float')
>>> f.a.body[0].decorator_list[0] = FST('call_decorator(1, 2, 3)').a
>>> f.a.body[1].name = 'last_function' # can change non-AST
>>> f.a.body[1].body[0] = f.a.body[0].body[0] # AST from same FST tree
>>> other = FST('def first_function(a, b): return a * b # yay!')
>>> f.a.body.insert(0, other.a) # AST from other FST tree
>>> f = f.reconcile(m, pep8space=1)
>>> print('\n'.join(l or '.' for l in f.lines)) # print this way for doctest
def first_function(a, b): return a * b # yay!
.
@call_decorator(1, 2, 3) # something
def function(a: float, b=2)->float: # blah
return a+b # return this
.
def last_function(a, b):
return a+b # return this
>>> m = f.mark()
>>> body = f.a.body[1].body
>>> f.a.body[1] = FST('def f(): pass').a
>>> f.a.body[1].body = body
>>> f = f.reconcile(m, pep8space=1)
>>> print('\n'.join(l or '.' for l in f.lines))
def first_function(a, b): return a * b # yay!
.
def f():
return a+b # return this
.
def last_function(a, b):
return a+b # return this
Copy the AST node tree of this FST node, not including any FST stuff. Use when you just want a copy of
the AST tree from this point down.
Needless to say since this just returns an AST all formatting is lost, except that the AST nodes will have
the same lineno, col_offset, end_lineno and end_col_offset values as they had in the FST tree.
Returns:
AST: CopiedASTtree from this point down.
Examples:
>>> a = FST('[0, 1, 2, 3]').copy_ast()
>>> print(type(a))
<class 'ast.List'>
>>> print(dump(a))
List(elts=[Constant(value=0), Constant(value=1), Constant(value=2), Constant(value=3)], ctx=Load())
Replace or delete (if code=None, if possible) this node. Returns the new node for self, not the old
replaced node, or None if was deleted or raw replaced and the old node disappeared. Cannot delete root node.
CAN replace root node, in which case the accessing FST node remains the same but the top-level AST and
source change.
Parameters:
code:FST,ASTor sourcestrorlist[str]to put at this location.Noneto delete this node.one: DefaultTruemeans replace with a single element. IfFalseand field allows it then can replace single element with a slice.options: Seeoptions().to: Special option which only applies replacing inrawmode (either throughTrueor'auto'). Instead of replacing just this node, will replace the entire span from this node to the node specified intowith thecodepassed.
Returns:
FSTorNone: Returns the new node if successfully replaced orNoneif deleted or raw replace and corresponding new node could not be found.
Examples:
>>> FST('[0, 1, 2, 3]').elts[1].replace('4').root.src
'[0, 4, 2, 3]'
>>> f = FST('def f(a, /, b, *c, **d) -> int: pass')
>>> f.args.posonlyargs[0].replace(')', to=f.returns, raw=True) # raw reparse
>>> f.src
'def f(): pass'
Copy or cut an individual child node or a slice of child nodes from self if possible. This function can do
everything that get_slice() can.
Parameters:
idx: The index of the child node to get if the field being gotten from contains multiple elements or the start of the slice to get if getting a slice (by specifyingstop). If the field being gotten from is an individual element then this should beNone. Ifstopis specified and getting a slice then aNonehere means copy from the start of the list.stop: The end index (exclusive) of the child node to get if getting a slice from a field that contains multiple elements. This should be one past the last element to get (like python list indexing). If this isFalsethen it indicates that a single element is being requested and not a slice. If this isNonethen it indicates a slice operation to the end of the list (like pythona[start:]).field: The name of the field to get the element(s) from, which can be an individual element like avalueor a list likebody. If this isNonethen the default field for the node type is used. Most node types have a common-sense default field, e.g.bodyfor all block statements,valuefor things likeReturnandYield.Dict,MatchMappingandComparenodes have special-case handling for aNonefield.cut: Whether to cut out the child node (if possible) or not (just copy).options: Seeoptions().
Note: The field value can be passed positionally in either the idx or stop parameter. If passed in
idx then the field is assumed individual and if passed in stop then it is a list and an individual element
is being gotten from idx and not a slice.
Returns:
FST: When getting an actual node (most situations).str: When getting am identifier, like fromName.id.constant: When getting a constant (fst.astutil.constant), like fromMatchSingleton.value.
Examples:
>>> FST('[0, 1, 2, 3]').get(1).src
'1'
>>> (f := FST('[0, 1, 2, 3]')).get(1, 3).src
'[1, 2]'
>>> f.src
'[0, 1, 2, 3]'
>>> (f := FST('[0, 1, 2, 3]')).get(1, 3, cut=True).src
'[1, 2]'
>>> f.src
'[0, 3]'
>>> FST('[0, 1, 2, 3]').get(None, 3).src
'[0, 1, 2]'
>>> FST('[0, 1, 2, 3]').get(-3, None).src
'[1, 2, 3]'
>>> FST('if 1: i = 1\nelse: j = 2').get(0).src
'i = 1'
>>> FST('if 1: i = 1\nelse: j = 2').get('orelse').src
'j = 2'
>>> FST('if 1: i = 1\nelse: j = 2; k = 3').get(1, 'orelse').src
'k = 3'
>>> FST('if 1: i = 1\nelse: j = 2; k = 3; l = 4; m = 5').get(1, 3, 'orelse').src
'k = 3; l = 4'
>>> FST('return 1').get().src # default field is 'value'
'1'
>>> FST('[0, 1, 2, 3]').get().src # 'elts' slice copy is made
'[0, 1, 2, 3]'
Put an individual node or a slice of nodes to self if possible. This function can do everything that
put_slice() can. The node is passed as an existing top-level FST, AST, string or list of string lines. If
passed as an FST then it should be considered "consumed" after this function returns and is no logner valid,
even on failure. AST is copied.
WARNING! The original self node may be invalidated during the operation if using raw mode (either
specifically or as a fallback), so make sure to swap it out for the return value of this function if you will
keep using the variable you called this method on. It will be changed accordingly in the tree but any other
outside references to the node may become invalid.
Parameters:
code: The node to put as anFST(must be root node),AST, a string or list of line strings. If putting to an identifier field then this should be a string and it will be taken literally (no parsing). If putting to a constant likewMatchSingleton.valueorConstant.valuethen this should be an appropriate primitive constant value.idx: The index of the field node to put to if the field being put to contains multiple elements or the start of the slice to put if putting a slice (by specifyingstop). If the field being put to is an individual element then this should beNone. Ifstopis specified and putting a slice then aNonehere means put starting from the beginning of the list.stop: The end index (exclusive) of the field node to put to if putting a slice to a field that contains multiple elements. This should be one past the last element to put (like python list indexing). If this isFalsethen it indicates that a single element is being put and not a slice. If this isNonethen it indicates a slice operation to the end of the list (like pythona[start:]).field: The name of the field to put the element(s) to, which can be an individual element like avalueor a list likebody. If this isNonethen the default field for the node type is used. Most node types have a common-sense default field, e.g.bodyfor all block statements,valuefor things likeReturnandYield.Dict,MatchMappingandComparenodes have special-case handling for aNonefield.one: Only has meaning if putting a slice, and in this caseTruespecifies that the source should be put as a single element to the range specified even if it is a valid slice.Falseindicates a true slice operation replacing the range with the slice passed, which must in this case be a compatible slice type.options: Seeoptions().to: Special option which only applies when putting a single element inrawmode (either throughTrueor'auto'). Instead of replacing just the target node, will replace the entire span from the target node to the node specified intowith thecodepassed.
Note: The field value can be passed positionally in either the idx or stop parameter. If passed in
idx then the field is assumed individual and if passed in stop then it is a list and an individual element
is being gotten from idx and not a slice.
Returns:
selforNoneif a raw put was done and corresponding new node could not be found.
Examples:
>>> FST('[0, 1, 2, 3]').put('4', 1).src
'[0, 4, 2, 3]'
>>> FST('[0, 1, 2, 3]').put('4, 5', 1, 3).src
'[0, (4, 5), 3]'
>>> FST('[0, 1, 2, 3]').put('4, 5', 1, 3, one=False).src
'[0, 4, 5, 3]'
>>> FST('[0, 1, 2, 3]').put('4, 5', None, 3).src
'[(4, 5), 3]'
>>> (f := FST('[0, 1, 2, 3]')).put('4, 5', -3, None, one=False).src
'[0, 4, 5]'
>>> print(FST('if 1: i = 1\nelse: j = 2').put('z = -1', 0).src)
if 1:
z = -1
else: j = 2
>>> print(FST('if 1: i = 1\nelse: j = 2').put('z = -1', 0, 'orelse').src)
if 1: i = 1
else:
z = -1
>>> print(FST('if 1: i = 1\nelse: j = 2')
... .put('z = -1\ny = -2\nx = -3', 'orelse', one=False).src)
if 1: i = 1
else:
z = -1
y = -2
x = -3
>>> print((f := FST('if 1: i = 1\nelse: j = 2'))
... .put('z = -1', 0, raw=True, to=f.orelse[0]).root.src)
if 1: z = -1
Copy or cut a slice of child nodes from self if possible.
Parameters:
start: The start of the slice to get, orNonefor the beginning of the entire range.stop: The end index (exclusive) of the slice to get. This should be one past the last element to get (like python list indexing). If this isNonethen it indicates a slice operation to the end of the list (like pythona[start:]).field: The name of the field to get the elements from, which can be an individual element like avalueor a list likebody. If this isNonethen the default field for the node type is used. Most node types have a common-sense default field, e.g.bodyfor all block statements,eltsfor things likeListandTuple.MatchMappingandComparenodes have special-case handling for aNonefield.cut: Whether to cut out the slice or not (just copy).options: Seeoptions().
Note: The field value can be passed positionally in either the start or stop parameter. If passed in
start then the slice is assumed to be the entire range, and if passed in stop then the slice goes from
start to the end of the range.
Returns:
FST: Slice node of nodes gotten.
Examples:
>>> FST('[0, 1, 2, 3]').get_slice(1).src
'[1, 2, 3]'
>>> FST('[0, 1, 2, 3]').get_slice(None, -1).src
'[0, 1, 2]'
>>> (f := FST('[0, 1, 2, 3]')).get_slice(1, 3, cut=True).src
'[1, 2]'
>>> f.src
'[0, 3]'
>>> f = FST('if 1: i = 1\nelse: j = 2; k = 3; l = 4; m = 5')
>>> print(f.src)
if 1: i = 1
else: j = 2; k = 3; l = 4; m = 5
>>> print(f.get_slice(1, 3, 'orelse', cut=True).src)
k = 3; l = 4
>>> print(f.src)
if 1: i = 1
else: j = 2; m = 5
Put a slice of nodes to self if possible. The node is passed as an existing top-level FST, AST, string
or list of string lines. If passed as an FST then it should be considered "consumed" after this function
returns and is no logner valid, even on failure. AST is copied.
WARNING! The original self node may be invalidated during the operation if using raw mode (either
specifically or as a fallback), so make sure to swap it out for the return value of this function if you will
keep using the variable you called this method on. It will be changed accordingly in the tree but any other
outside references to the node may become invalid.
Parameters:
code: The slice to put as anFST(must be root node),AST, a string or list of line strings.start: The start of the slice to put, orNonefor the beginning of the entire range.stop: The end index (exclusive) of the slice. This should be one past the last element to put (like python list indexing). If this isNonethen it indicates a slice operation to the end of the list (like pythona[start:]).field: The name of the field to put the elements to. If this isNonethen the default field for the node type is used. Most node types have a common-sense default field, e.g.bodyfor all block statements,eltsfor things likeListandTuple.MatchMappingandComparenodes have special-case handling for aNonefield.one:Truespecifies that the source should be put as a single element to the range specified even if it is a valid slice.Falseindicates a true slice operation replacing the range with the slice passed, which must in this case be a compatible slice type.options: Seeoptions().
Note: The field value can be passed positionally in either the start or stop parameter. If passed in
start then the slice is assumed to be the entire range, and if passed in stop then the slice goes from
start to the end of the range.
Returns:
selforNoneif a raw put was done and corresponding new node could not be found.
Examples:
>>> FST('[0, 1, 2, 3]').put('4', 1).src
'[0, 4, 2, 3]'
>>> FST('[0, 1, 2, 3]').put('4, 5', 1, 3).src
'[0, (4, 5), 3]'
>>> FST('[0, 1, 2, 3]').put('4, 5', 1, 3, one=False).src
'[0, 4, 5, 3]'
>>> FST('[0, 1, 2, 3]').put('4, 5', None, 3).src
'[(4, 5), 3]'
>>> FST('[0, 1, 2, 3]').put('4, 5', -3, None, one=False).src
'[0, 4, 5]'
>>> print(FST('if 1: i = 1\nelse: j = 2').put('z = -1', 0).src)
if 1:
z = -1
else: j = 2
>>> print(FST('if 1: i = 1\nelse: j = 2').put('z = -1', 0, 'orelse').src)
if 1: i = 1
else:
z = -1
>>> print(FST('if 1: i = 1\nelse: j = 2')
... .put('z = -1\ny = -2\nx = -3', 'orelse', one=False).src)
if 1: i = 1
else:
z = -1
y = -2
x = -3
>>> print((f := FST('if 1: i = 1\nelse: j = 2'))
... .put('z = -1', 0, raw=True, to=f.orelse[0]).root.src)
if 1: z = -1
Get source at location, without dedenting or any other modification, returned as a string or individual
lines. The first and last lines are cropped to start col and end_col.
Can call on any node in tree to access source for the whole tree.
The coordinates passed in are clipped to the whole valid source area.
Parameters:
ln: Start line of span to get (0 based).col: Start column (character) on start line.end_ln: End line of span to get (0 based, inclusive).end_col: End column (character, exclusive) on end line.as_lines: IfFalsethen source is returned as a single string with embedded newlines. IfTruethen source is returned as a list of line strings (without newlines).
Returns:
str | list[str]: A single string or a list of lines ifas_lines=True. If lines then there are no trailing newlines in the individual line strings.
Examples:
>>> FST('if 1:\n i = 2').get_src(0, 3, 1, 5)
'1:\n i ='
>>> FST('if 1:\n i = 2').get_src(0, 3, 1, 5, as_lines=True)
['1:', ' i =']
>>> (f := FST('if 1:\n i = 2')).get_src(*f.body[0].bloc)
'i = 2'
Put source and maybe adjust AST tree for source modified. The adjustment may be a reparse of the area
changed, an offset of nodes (assuming put source was just trivia and wouldn't affect the tree) or nothing at
all. The action options are:
'reparse': Put source and reparse. There are no rules on what is put, it is simply put and parse is attempted.The reparse that is triggered is of at least a statement level node or a statement block header, and can be multiple statements if the location spans those or even statements outside of the location if the reparse affects things like
elif.FSTnodes in the region of the put or even outside of it can become invalid. The onlyFSTnode guaranteed not to change is the root node (identity, theASTit holds can change).When putting source raw by location like this there are no automatic modifications made to the source or destination. No parenthesization, prefixes or suffixes or indentation, the source is just put and parsed so you are responsible for the correct indentation and precedence.
After put and successful reparse a new node can be found using
find_loc()functions from the original startlnandcoland newly returnedend_lnandend_col. It is possible thatNoneis returned from these functions if no good candidate is found (since this can be used to delete or merge nodes).selfdoesn't matter in this case, can call on any node in tree, even one which is not touched by the source change and the appropriate nodes will be found and reparsed.'offset': Put source and offset nodes around it according to what node theput_src()was called on. In this caseselfmatters and should be the last node down within which the location is contained. Any child nodes of this node are offset differently from thisselfnode and its parents.The
selfnode and its parents will not have their start locations offset if the put is an insert at the start (as the location is considered to be INSIDE these nodes), whereas child nodes will be moved.The
selfnode and its parents will have their end locations offset if the put ends at this location whereas child nodes will not.None: Nothing is reparsed or offset, the source is just put. There are few cases where this will result in a valid tree but can include removal of comments and trailing whitespace on a line or changing lines between empty and comment.
If the code is passed as an AST then it is unparsed to a string and that string is put into the location. If
code is an FST then the exact source of the FST is put. If passed as a string or lines then that is put
directly.
The coordinates passed in are clipped to the whole valid source area.
Parameters:
code: The code to put as anFST(must be root node),AST, a string or list of line strings.ln: Start line of span to put (0 based).col: Start column (character) on start line.end_ln: End line of span to put (0 based, inclusive).end_col: End column (character, exclusive) on end line.action: What action to take on theASTtree, the options are'reparse','offset'orNone.
Returns:
(end_ln, end_col): New end location of source put (all source after this was not modified).
Examples:
>>> f = FST('i = 1')
>>> f.put_src('2', 0, 4, 0, 5)
(0, 5)
>>> f.src
'i = 2'
>>> f = FST('i = 1')
>>> f.put_src('+= 3', 0, 2, 0, 5)
(0, 6)
>>> f.src
'i += 3'
>>> f = FST('{a: b, c: d, e: f}')
>>> f.put_src('**', 0, 7, 0, 10)
(0, 9)
>>> f.src
'{a: b, **d, e: f}'
>>> f = FST('''
... if a:
... i = 2
... elif b:
... j = 3
... '''.strip())
>>> print(f.src)
if a:
i = 2
elif b:
j = 3
>>> f.put_src('''
... else:
... if b:
... k = 4
... '''.strip(), *f.orelse[0].loc[:2], *f.loc[2:])
(4, 9)
>>> print(f.src)
if a:
i = 2
else:
if b:
k = 4
>>> (f := FST('a, b')).put_src(' ', 0, 4, 0, 4, 'offset')
(0, 5)
>>> f.src, f.loc, f.elts[1].loc
('a, b ', fstloc(0, 0, 0, 5), fstloc(0, 3, 0, 4))
Return the location of enclosing GROUPING parentheses if present. Will balance parentheses if self is an
element of a tuple and not return the parentheses of the tuple. Likwise will not normally return the parentheses
of an enclosing arguments parent or class bases list (unless shared=None, but that is mostly for internal
use).
Only normally works on (and makes sense for) expr or pattern nodes, otherwise returns self.bloc and count
of 0. Also handles special case of a single generator expression argument to a function sharing parameters with
the call arguments, in which case a count of -1 and the location of the GeneratorExp without its enclosing
parentheses may be returned, if this is enabled with shared=False.
This function is cached so feel free to call as often as is needed.
Note: For a Starred this will always return the location of the whole Starred since that cannot be
parenthesized itself but rather its child. If you want he parenteses of the child then do
starred.value.pars().
Parameters:
shared: IfTruethen will include parentheses of a single call argument generator expression if they are shared with the call arguments enclosing parentheses with a count of 0. IfFalsethen does not return these and returns a count of -1, and thus the location is not a full validGeneratorExplocation. IfNonethen returns ANY directly enclosing parentheses, whether they belong to this node or not.
Returns:
fstloc | None: Location of enclosing parentheses if present elseself.bloc(which can beNone). Negative parentheses count (from shared parens solo call arg generator expression) can also be checked in the case ofshared=Falseviafst.pars() > fst.bloc. If only loc is returned, it will be anfstlocwhich will still have the count of parentheses in an attribute.n.
Examples:
>>> FST('i').pars()
fstlocn(0, 0, 0, 1, n=0)
>>> FST('(i)').pars()
fstlocn(0, 0, 0, 3, n=1)
>>> FST('((i))').pars()
fstlocn(0, 0, 0, 5, n=2)
>>> FST('(1, 2)').pars() # tuple pars are not considered grouping pars
fstlocn(0, 0, 0, 6, n=0)
>>> FST('((1, 2))').pars()
fstlocn(0, 0, 0, 8, n=1)
>>> FST('call(a)').args[0].pars() # any node, not just root
fstlocn(0, 5, 0, 6, n=0)
>>> FST('call((a))').args[0].pars()
fstlocn(0, 5, 0, 8, n=1)
>>> FST('call(i for i in j)').args[0].pars()
fstlocn(0, 4, 0, 18, n=0)
>>> FST('call(i for i in j)').args[0].pars(shared=False) # exclude shared pars
fstlocn(0, 5, 0, 17, n=-1)
>>> FST('call((i for i in j))').args[0].pars(shared=False)
fstlocn(0, 5, 0, 19, n=0)
Parenthesize node if it MAY need it. Will not parenthesize atoms which are always enclosed like List, or
nodes which are not _is_parenthesizable(), unless force=True. Will add parentheses to unparenthesized
Tuple and brackets to unbracketed MatchSequence adjusting the node location. If dealing with a Starred
then the parentheses are applied to the child.
WARNING! This function doesn't do any higher level syntactic validation. So if you parenthesize something that shouldn't be parenthesized, and you wind up poking an eye out, that's on you.
Parameters:
force: IfTruethen will add another layer of parentheses regardless if any already present.whole: If at root then parenthesize whole source instead of just node, ifFalsethen only node.
Returns:
self
Examples:
>>> FST('a + b').par().src
'(a + b)'
>>> FST('(a + b)').par().src # already parenthesized, so nothing done
'(a + b)'
>>> FST('(a + b)').par(force=True).src # force it
'((a + b))'
>>> FST('1, 2').par().src # parenthesize tuple
'(1, 2)'
>>> FST('i').par().src # an atom doesn't need parentheses
'i'
>>> FST('i').par(force=True).src # so must be forced
'(i)'
>>> # parethesize MatchSequence puts brackets like ast.unparse()
>>> FST('1, 2', 'pattern').par().src
'[1, 2]'
>>> FST('*a or b').par().src # par() a Starred parenthesizes its child
'*(a or b)'
>>> FST('call(i = 1 + 2)').keywords[0].value.par().root.src # not just root node
'call(i = (1 + 2))'
Remove all parentheses if present. Normally removes just grouping parentheses but can also remove Tuple
parentheses and MatchSequence parentheses or brackets intrinsic to the node if node=True. If dealing with a
Starred then the parentheses are checked in and removed from the child. If shared=None then will also remove
parentheses which do not belong to this node but enclose it directly, this is mostly for internal use.
WARNING! This function doesn't do any higher level syntactic validation. So if you unparenthesize something that shouldn't be unparenthesized, and you wind up poking an eye out, that's on you.
Parameters:
node: IfTruethen will remove intrinsic parentheses from a parenthesizedTupleand parentheses / brackets from parenthesized / bracketedMatchSequence, otherwise only removes grouping parentheses if present.shared: Whether to allow merge of parentheses of single call argument generator expression withCallparentheses or not. IfNonethen will attempt to unparenthesize any enclosing parentheses, whether they belong to this node or not (meant for internal use).
Returns:
self
Examples:
>>> FST('a + b').unpar().src # nothing done if no pars
'a + b'
>>> FST('(a + b)').unpar().src
'a + b'
>>> FST('((a + b))').unpar().src # removes all
'a + b'
>>> FST('(1, 2)').unpar().src # but not from tuple
'(1, 2)'
>>> FST('(1, 2)').unpar(node=True).src # unless explicitly specified
'1, 2'
>>> FST('(((1, 2)))').unpar().src
'(1, 2)'
>>> FST('(((1, 2)))').unpar(node=True).src
'1, 2'
>>> FST('[1, 2]', 'pattern').unpar().src
'[1, 2]'
>>> FST('[1, 2]', 'pattern').unpar(node=True).src
'1, 2'
>>> FST('*(a or b)').unpar().src # unpar() a Starred unparenthesizes its child
'*a or b'
>>> # not just root node
>>> FST('call(i = (1 + 2))').keywords[0].value.unpar().root.src
'call(i = 1 + 2)'
>>> # by default allows sharing
>>> FST('call(((i for i in j)))').args[0].unpar().root.src
'call(i for i in j)'
>>> # unless told not to
>>> FST('call(((i for i in j)))').args[0].unpar(shared=False).root.src
'call((i for i in j))'
Get next sibling of self in syntactic order, only within parent.
Parameters:
with_loc: Return nodes depending on their location information.False: All nodes with or without location.True: Only nodes which have intrinsicASTlocations and also larger computed location nodes likecomprehension,withitem,match_caseandarguments(the last one only if there are actually arguments present).'all': Same asTruebut also operators with calculated locations (excludingandandorsince they do not always have a well defined location).'own': Only nodes with their own intrinsicASTlocations, same asTruebut excludes those larger nodes with calculated locations.
Returns:
Noneif last valid sibling in parent, otherwise next node.
Examples:
>>> f = FST('[[1, 2], [3, 4]]')
>>> f.elts[0].next().src
'[3, 4]'
>>> print(f.elts[1].next())
None
Get previous sibling of self in syntactic order, only within parent.
Parameters:
with_loc: Return nodes depending on their location information.False: All nodes with or without location.True: Only nodes which have intrinsicASTlocations and also larger computed location nodes likecomprehension,withitem,match_caseandarguments(the last one only if there are actually arguments present).'all': Same asTruebut also operators with calculated locations (excludingandandorsince they do not always have a well defined location).'own': Only nodes with their own intrinsicASTlocations, same asTruebut excludes those larger nodes with calculated locations.
Returns:
Noneif first valid sibling in parent, otherwise previous node.
Examples:
>>> f = FST('[[1, 2], [3, 4]]')
>>> f.elts[1].prev().src
'[1, 2]'
>>> print(f.elts[0].prev())
None
Get first valid child in syntactic order.
Parameters:
with_loc: Return nodes depending on their location information.False: All nodes with or without location.True: Only nodes which have intrinsicASTlocations and also larger computed location nodes likecomprehension,withitem,match_caseandarguments(the last one only if there are actually arguments present).'all': Same asTruebut also operators with calculated locations (excludingandandorsince they do not always have a well defined location).'own': Only nodes with their own intrinsicASTlocations, same asTruebut excludes those larger nodes with calculated locations.
Returns:
Noneif no valid children, otherwise first valid child.
Examples:
>>> f = FST('def f(a: list[str], /, reject: int, *c, d=100, **e): pass')
>>> f.first_child().src
'a: list[str], /, reject: int, *c, d=100, **e'
>>> f.args.first_child().src
'a: list[str]'
>>> f.args.first_child().first_child().src
'list[str]'
Get last valid child in syntactic order.
Parameters:
with_loc: Return nodes depending on their location information.False: All nodes with or without location.True: Only nodes which have intrinsicASTlocations and also larger computed location nodes likecomprehension,withitem,match_caseandarguments(the last one only if there are actually arguments present).'all': Same asTruebut also operators with calculated locations (excludingandandorsince they do not always have a well defined location).'own': Only nodes with their own intrinsicASTlocations, same asTruebut excludes those larger nodes with calculated locations.
Returns:
Noneif no valid children, otherwise last valid child.
Examples:
>>> f = FST('def f(a: list[str], /, reject: int, *c, d=100, **e): pass')
>>> f.last_child().src
'pass'
>>> f.args.last_child().src
'e'
Get last valid child in syntactic order in a block header (before the :), e.g. the something in
if something: pass.
Parameters:
with_loc: Return nodes depending on their location information.False: All nodes with or without location.True: Only nodes which have intrinsicASTlocations and also larger computed location nodes likecomprehension,withitem,match_caseandarguments(the last one only if there are actually arguments present).'all': Same asTruebut also operators with calculated locations (excludingandandorsince they do not always have a well defined location).'own': Only nodes with their own intrinsicASTlocations, same asTruebut excludes those larger nodes with calculated locations.
Returns:
Noneif no valid children or ifselfis not a block statement, otherwise last valid child in the block header.
Examples:
>>> print(FST('if something:\n i = 2\n i = 3')
... .last_header_child().src)
something
>>> print(FST('try: pass\nexcept Exception as exc: pass').handlers[0]
... .last_header_child().src)
Exception
>>> print(FST('with a, b: pass').last_header_child().src)
b
>>> print(FST('try: pass\nfinally: pass').last_header_child())
None
>>> print(FST('i = 1').last_header_child())
None
Get the next child in syntactic order, meant for simple iteration.
This is a slower way to iterate vs. walk(), but will work correctly if ANYTHING in the tree is modified during
the walk as long as the replaced node and its parent is used for the following call.
Parameters:
from_child: Child node we are coming from which may or may not have location.with_loc: Return nodes depending on their location information.False: All nodes with or without location.True: Only nodes which have intrinsicASTlocations and also larger computed location nodes likecomprehension,withitem,match_caseandarguments(the last one only if there are actually arguments present).'all': Same asTruebut also operators with calculated locations (excludingandandorsince they do not always have a well defined location).'own': Only nodes with their own intrinsicASTlocations, same asTruebut excludes those larger nodes with calculated locations.
Returns:
Noneif last valid child inself, otherwise next child node.
Examples:
>>> f = FST('[[1, 2], [3, 4]]')
>>> f.next_child(f.elts[0]).src
'[3, 4]'
>>> print(f.next_child(f.elts[1]))
None
>>> f = FST('[this, is_, reparsed, each, step, and_, still, walks, ok]')
>>> n = None
>>> while n := f.next_child(n):
... if isinstance(n.a, Name):
... n = n.replace(n.id[::-1])
>>> f.src
'[siht, _si, desraper, hcae, pets, _dna, llits, sklaw, ko]'
Get the previous child in syntactic order, meant for simple iteration.
This is a slower way to iterate vs. walk(), but will work correctly if ANYTHING in the tree is modified during the
walk as long as the replaced node and its parent is used for the following call.
Parameters:
from_child: Child node we are coming from which may or may not have location.with_loc: Return nodes depending on their location information.False: All nodes with or without location.True: Only nodes which have intrinsicASTlocations and also larger computed location nodes likecomprehension,withitem,match_caseandarguments(the last one only if there are actually arguments present).'all': Same asTruebut also operators with calculated locations (excludingandandorsince they do not always have a well defined location).'own': Only nodes with their own intrinsicASTlocations, same asTruebut excludes those larger nodes with calculated locations.
Returns:
Noneif first valid child inself, otherwise previous child node.
Examples:
>>> f = FST('[[1, 2], [3, 4]]')
>>> f.prev_child(f.elts[1]).src
'[1, 2]'
>>> print(f.prev_child(f.elts[0]))
None
>>> f = FST('[this, is_, reparsed, each, step, and_, still, walks, ok]')
>>> n = None
>>> while n := f.prev_child(n):
... if isinstance(n.a, Name):
... n = n.replace(n.id[::-1])
>>> f.src
'[siht, _si, desraper, hcae, pets, _dna, llits, sklaw, ko]'
Step forward in the tree in syntactic order, as if walk()ing forward, NOT the inverse of step_back(). Will
walk up parents and down children to get the next node, returning None only when we are at the end of the whole
thing.
Parameters:
with_loc: Return nodes depending on their location information.False: All nodes with or without location.True: Only nodes which have intrinsicASTlocations and also larger computed location nodes likecomprehension,withitem,match_caseandarguments(the last one only if there are actually arguments present).'all': Same asTruebut also operators with calculated locations (excludingandandorsince they do not always have a well defined location).'own': Only nodes with their own intrinsicASTlocations, same asTruebut excludes those larger nodes with calculated locations.'allown'Same as'own'but recurse into nodes with non-own locations (even though those nodes are not returned). This is only really meant for internal use to safely call from.loclocation calculation.
recurse_self: Whether to allow recursion intoselfto return children or move directly to next nodes ofselfon start.
Returns:
Noneif last valid node in tree, otherwise next node in order.
Examples:
>>> f = FST('[[1, 2], [3, 4]]')
>>> f.elts[0].src
'[1, 2]'
>>> f.elts[0].step_fwd().src
'1'
>>> f.elts[0].step_fwd(recurse_self=False).src
'[3, 4]'
>>> f.elts[0].elts[1].src
'2'
>>> f.elts[0].elts[1].step_fwd().src
'[3, 4]'
>>> f = FST('[this, [is_, [reparsed, each], step, and_, still], walks, ok]')
>>> n = f.elts[0]
>>> while True:
... if isinstance(n.a, Name):
... n = n.replace(n.id[::-1])
... if not (n := n.step_fwd()):
... break
>>> f.src
'[siht, [_si, [desraper, hcae], pets, _dna, llits], sklaw, ko]'
Step backward in the tree in syntactic order, as if walk()ing backward, NOT the inverse of step_fwd().
Will walk up parents and down children to get the next node, returning None only when we are at the beginning
of the whole thing.
Parameters:
with_loc: Return nodes depending on their location information.False: All nodes with or without location.True: Only nodes which have intrinsicASTlocations and also larger computed location nodes likecomprehension,withitem,match_caseandarguments(the last one only if there are actually arguments present).'all': Same asTruebut also operators with calculated locations (excludingandandorsince they do not always have a well defined location).'own': Only nodes with their own intrinsicASTlocations, same asTruebut excludes those larger nodes with calculated locations.'allown'Same as'own'but recurse into nodes with non-own locations (even though those nodes are not returned). This is only really meant for internal use to safely call from.loclocation calculation.
recurse_self: Whether to allow recursion intoselfto return children or move directly to previous nodes ofselfon start.
Returns:
Noneif first valid node in tree, otherwise previous node in order.
Examples:
>>> f = FST('[[1, 2], [3, 4]]')
>>> f.elts[1].src
'[3, 4]'
>>> f.elts[1].step_back().src
'4'
>>> f.elts[1].step_back(recurse_self=False).src
'[1, 2]'
>>> f.elts[1].elts[0].src
'3'
>>> f.elts[1].elts[0].step_back().src
'[1, 2]'
>>> f = FST('[this, [is_, [reparsed, each], step, and_, still], walks, ok]')
>>> n = f.elts[-1]
>>> while True:
... if isinstance(n.a, Name):
... n = n.replace(n.id[::-1])
... if not (n := n.step_back()):
... break
>>> f.src
'[siht, [_si, [desraper, hcae], pets, _dna, llits], sklaw, ko]'
Walk self and descendants in syntactic order. When walking, you can send(False) to the generator to skip
recursion into the current child. send(True) to allow recursion into child if called with recurse=False or
scope=True would otherwise disallow it. Can send multiple times, last value sent takes effect.
The walk is defined forwards or backwards in that it returns a parent, then recurses into the children and walks
those in the given direction, recursing into each child's children before continuing with siblings. Walking
backwards will not generate the same sequence as list(walk())[::-1] due to this behavior.
It is safe to modify the nodes (or previous nodes) as they are being walked as long as those modifications don't
touch the parent or following nodes. This means normal .replace() is fine as long as raw=False.
The walk is relatively efficient but if all you need to do is just walk ALL the AST children without any bells
or whistles then ast.walk() will be a bit faster.
Parameters:
with_loc: Return nodes depending on their location information.False: All nodes with or without location.True: Only nodes which have intrinsicASTlocations and also larger computed location nodes likecomprehension,withitem,match_caseandarguments(the last one only if there are actually arguments present).'all': Same asTruebut also operators with calculated locations (excludingandandorsince they do not always have a well defined location).'own': Only nodes with their own intrinsicASTlocations, same asTruebut excludes those larger nodes with calculated locations.
self_: IfTruethen self will be returned first with the possibility to skip children withsend(False), otherwise will start directly with children.recurse: Whether to recurse into children by default,send(True)for a given node will always override this.scope: IfTruethen will walk only within the scope ofself. Meaning if called on aFunctionDefthen will only walk children which are within the function scope. Will yield children which have their own scopes, and the parts of them which are visible in this scope (like default argument values), but will not recurse into them unlesssend(True)is done for that child.back: IfTruethen walk every node in reverse syntactic order. This is not the same as a full forwards walk reversed due to recursion (parents are still returned before children, only in reverse sibling order).
Examples:
>>> import ast
>>> f = FST('def f(a: list[str], /, reject: int, *c, d=100, **e): pass')
>>> for g in (gen := f.walk(with_loc=True)):
... if isinstance(g.a, ast.arg) and g.a.arg == 'reject':
... _ = gen.send(False)
... else:
... print(f'{g!r:<30}{g.src[:50]!r}')
<FunctionDef ROOT 0,0..0,57> 'def f(a: list[str], /, reject: int, *c, d=100, **e'
<arguments 0,6..0,50> 'a: list[str], /, reject: int, *c, d=100, **e'
<arg 0,6..0,18> 'a: list[str]'
<Subscript 0,9..0,18> 'list[str]'
<Name 0,9..0,13> 'list'
<Name 0,14..0,17> 'str'
<arg 0,37..0,38> 'c'
<arg 0,40..0,41> 'd'
<Constant 0,42..0,45> '100'
<arg 0,49..0,50> 'e'
<Pass 0,53..0,57> 'pass'
>>> f = FST('''
... def f():
... def g(arg=1) -> int:
... pass
... val = [i for i in iterator]
... '''.strip())
>>> for g in f.walk(True, scope=True):
... print(f'{g!r:<30}{g.src[:47]!r}')
<FunctionDef ROOT 0,0..3,31> 'def f():\n def g(arg=1) -> int:\n pass\n'
<FunctionDef 1,4..2,12> 'def g(arg=1) -> int:\n pass'
<Constant 1,14..1,15> '1'
<Assign 3,4..3,31> 'val = [i for i in iterator]'
<Name 3,4..3,7> 'val'
<ListComp 3,10..3,31> '[i for i in iterator]'
<Name 3,22..3,30> 'iterator'
>>> for g in f.walk(True, back=True):
... print(f'{g!r:<30}{g.src[:47]!r}')
<FunctionDef ROOT 0,0..3,31> 'def f():\n def g(arg=1) -> int:\n pass\n'
<Assign 3,4..3,31> 'val = [i for i in iterator]'
<ListComp 3,10..3,31> '[i for i in iterator]'
<comprehension 3,13..3,30> 'for i in iterator'
<Name 3,22..3,30> 'iterator'
<Name 3,17..3,18> 'i'
<Name 3,11..3,12> 'i'
<Name 3,4..3,7> 'val'
<FunctionDef 1,4..2,12> 'def g(arg=1) -> int:\n pass'
<Pass 2,8..2,12> 'pass'
<Name 1,20..1,23> 'int'
<arguments 1,10..1,15> 'arg=1'
<Constant 1,14..1,15> '1'
<arg 1,10..1,13> 'arg'
Generator which yields parents all the way up to root. If self_ is True then will yield self first.
Parameters:
self_: Whether to yieldselffirst.
Examples:
>>> list(FST('i = (f(), g())', 'exec').body[0].value.elts[0].parents())
[<Tuple 0,4..0,14>, <Assign 0,0..0,14>, <Module ROOT 0,0..0,14>]
>>> list(FST('i = (f(), g())', 'exec').body[0].value.elts[0].parents(self_=True))
[<Call 0,5..0,8>, <Tuple 0,4..0,14>, <Assign 0,0..0,14>, <Module ROOT 0,0..0,14>]
The first parent which is a stmt or optionally mod node (if any). If self_ is True then will check
self first (possibly returning self), otherwise only checks parents.
Parameters:
self_: Whether to includeselfin the search, if so andselfmatches criteria then it is returned.mod: Whether to returnmodnodes if found.
Examples:
>>> FST('if 1: i = 1', 'exec').body[0].body[0].value.parent_stmt()
<Assign 0,6..0,11>
>>> FST('if 1: i = 1', 'exec').body[0].body[0].parent_stmt()
<If 0,0..0,11>
>>> FST('if 1: i = 1', 'exec').body[0].parent_stmt()
<Module ROOT 0,0..0,11>
>>> print(FST('if 1: i = 1', 'exec').body[0].parent_stmt(mod=False))
None
>>> FST('if 1: i = 1', 'exec').body[0].parent_stmt(self_=True)
<If 0,0..0,11>
The first parent which is a stmt, ExceptHandler, match_case or optionally mod node (if any). If
self_ is True then will check self first, otherwise only checks parents.
Examples:
>>> (FST('try: pass\nexcept: pass', 'exec')
... .body[0].handlers[0].body[0].parent_stmtish())
<ExceptHandler 1,0..1,12>
>>> FST('try: pass\nexcept: pass', 'exec').body[0].handlers[0].parent_stmtish()
<Try 0,0..1,12>
>>> FST('try: pass\nexcept: pass', 'exec').body[0].parent_stmtish()
<Module ROOT 0,0..1,12>
>>> FST('match a:\n case 1: pass').cases[0].body[0].parent_stmtish()
<match_case 1,2..1,14>
>>> FST('match a:\n case 1: pass').cases[0].pattern.parent_stmtish()
<match_case 1,2..1,14>
The first parent which opens a block that self lives in (if any). Types include FunctionDef,
AsyncFunctionDef, ClassDef, For, AsyncFor, While, If, With, AsyncWith, Match, Try,
TryStar, ExceptHandler, match_case or optionally mod node (if any). If self_ is True then will check
self first, otherwise only checks parents.
Examples:
>>> FST('if 1: i = 1', 'exec').body[0].body[0].value.parent_block()
<If 0,0..0,11>
>>> FST('if 1: i = 1', 'exec').body[0].parent_block()
<Module ROOT 0,0..0,11>
The first parent which opens a scope that self lives in (if any). Types include FunctionDef,
AsyncFunctionDef, ClassDef, Lambda, ListComp, SetComp, DictComp, GeneratorExp or optionally mod
node (if any). If self_ is True then will check self first, otherwise only checks parents.
Examples:
>>> FST('if 1: i = 1', 'exec').body[0].body[0].value.parent_scope()
<Module ROOT 0,0..0,11>
>>> (FST('def f():\n if 1: i = 1', 'exec')
... .body[0].body[0].body[0].value.parent_scope())
<FunctionDef 0,0..1,13>
>>> FST('lambda: None', 'exec').body[0].value.body.parent_scope()
<Lambda 0,0..0,12>
>>> FST('[i for i in j]', 'exec').body[0].value.elt.parent_scope()
<ListComp 0,0..0,14>
The first parent which opens a named scope that self lives in (if any). Types include FunctionDef,
AsyncFunctionDef, ClassDef or optionally mod node (if any). If self_ is True then will check self
first, otherwise only checks parents.
Examples:
>>> FST('if 1: i = 1', 'exec').body[0].body[0].value.parent_named_scope()
<Module ROOT 0,0..0,11>
>>> (FST('def f():\n if 1: i = 1', 'exec')
... .body[0].body[0].body[0].value.parent_named_scope())
<FunctionDef 0,0..1,13>
>>> (FST('def f(): lambda: None', 'exec')
... .body[0].body[0].value.body.parent_named_scope())
<FunctionDef 0,0..0,21>
>>> (FST('class cls: [i for i in j]', 'exec')
... .body[0].body[0].value.elt.parent_named_scope())
<ClassDef 0,0..0,25>
The first parent which is not an expr. If self_ is True then will check self first (possibly
returning self), otherwise only checks parents.
Parameters:
self_: Whether to includeselfin the search, if so andselfmatches criteria then it is returned.strict:Falsemeans considercomprehension,arguments,argandkeywordnodes asexprfor the sake of the walk up since these nodes can have otherexprparents (meaning they will be skipped).Truemeans onlyexprnodes, which means you could get anargorcomprehensionnode for example which still hasexprparents. Alsoexpr_context,boolop,operator,unaruopandcmpopare included ifstrict=Falsebut this only makes sense ifself_=Trueand you are calling this function on one of those.
Examples:
>>> FST('if 1: i = 1 + a[b]').body[0].value.right.value.parent_non_expr()
<Assign 0,6..0,18>
>>> (FST('match a:\n case {a.b.c: 1}: pass')
... .cases[0].pattern.keys[0].value.value.parent_non_expr())
<MatchMapping 1,6..1,16>
>>> FST('var = call(a, b=1)').value.keywords[0].value.parent_non_expr()
<Assign ROOT 0,0..0,18>
>>> (FST('var = call(a, b=1)')
... .value.keywords[0].value.parent_non_expr(strict=True))
<keyword 0,14..0,17>
The first parent which is a pattern. If self_ is True then will check self first (possibly returning
self), otherwise only checks parents.
Parameters:
self_: Whether to includeselfin the search, if so andselfmatches criteria then it is returned.
Examples:
>>> FST('case 1+1j: pass').pattern.value.left.parent_pattern().src
'1+1j'
>>> (FST('case 1 | {a.b: c}: pass')
... .pattern.patterns[1].patterns[0].parent_pattern(self_=True))
<MatchAs 0,15..0,16>
>>> (FST('case 1 | {a.b: c}: pass')
... .pattern.patterns[1].patterns[0].parent_pattern())
<MatchMapping 0,9..0,17>
>>> (FST('case 1 | {a.b: c}: pass')
... .pattern.patterns[1].patterns[0].parent_pattern().parent_pattern())
<MatchOr 0,5..0,17>
Get path to child node from self which can later be used on a copy of this tree to get to the same
relative child node.
Parameters:
child: Child node to get path to, can beselfin which case an empty path is returned.as_str: IfTruewill return the path as a python-ish string suitable for attribute access, else a list ofastfields which can be used more directly.
Returns:
list[astfield] | str: Path to child if exists, otherwise raises.
Examples:
>>> (f := FST('[i for i in j]', 'exec')).child_path(f.body[0].value.elt)
[astfield('body', 0), astfield('value'), astfield('elt')]
>>> ((f := FST('[i for i in j]', 'exec'))
... .child_path(f.body[0].value.elt, as_str=True))
'body[0].value.elt'
>>> (f := FST('i')).child_path(f)
[]
>>> (f := FST('i')).child_path(f, as_str=True)
''
Get child node specified by path if it exists. If succeeds then it doesn't mean that the child node is
guaranteed to be the same or even same type as was originally used to get the path, just that the path is valid.
For example after deleting an element from a list the item at the former element's location will be the previous
next element.
Parameters:
path: Path to child as a list ofastfields or string.last_valid: IfTruethen return the last valid node along the path, will not fail, can returnself.
Returns:
FST: Child node if path is valid, otherwiseFalseif path invalid.Falseand notNonebecauseNonecan be in a field that can hold anASTbutFalsecan not.
Examples:
>>> f = FST('[i for i in j]', 'exec')
>>> f.child_from_path(f.child_path(f.body[0].value.elt)).src
'i'
>>> f.child_from_path(f.child_path(f.body[0].value.elt, True)).src
'i'
>>> FST('[0, 1, 2, 3]', 'exec').child_from_path('body[0].value.elts[4]')
False
>>> (FST('[0, 1, 2, 3]', 'exec')
... .child_from_path('body[0].value.elts[4]', last_valid=True).src)
'[0, 1, 2, 3]'
>>> (f := FST('i')).child_from_path([]) is f
True
>>> (f := FST('i')).child_from_path('') is f
True
Recalculate self from path from root. Useful if self has been replaced by another node by some operation.
When nodes are deleted the corresponding FST.a and AST.f attributes are set to None. The root, parent
and pfield attributes are left so that things like this can work. Useful when a node has been deleted but you
want to know where it was and what may be there now.
Returns:
Examples:
>>> f = FST('[0, 1, 2, 3]')
>>> g = f.elts[1]
>>> print(type(g.a), g.root)
<class 'ast.Constant'> <List ROOT 0,0..0,12>
>>> f.put('x', 1, raw=True) # raw forces reparse at List
<List ROOT 0,0..0,12>
>>> print(g.a, g.root)
None <List ROOT 0,0..0,12>
>>> g = g.repath()
>>> print(type(g.a), g.root)
<class 'ast.Name'> <List ROOT 0,0..0,12>
Find the lowest level node which entirely contains location (starting search at self). The search will
only find nodes at self or below, no parents.
Parameters:
ln: Start line of location to search for (0 based).col: Start column (character) on start line.end_ln: End line of location to search for (0 based, inclusive).end_col: End column (character, inclusive withFST.end_col, exclusive withFST.col) on end line.allow_exact: Whether to allow return of exact location match with node or not.Truemeans allow return of node which matches location exactly.Falsemeans location must be inside the node but cannot be touching BOTH ends of the node. This basically determines whether you can get the exact node of the location or its parent. A value of'top'specifies an exact match is allowed and return the highest level node with the match, otherwise the lowest level exact match is returned. This only applies to nodes likeExprwhich will have the same location as the containedexpror aModulewhich only contains a single statement without any other junk like comments or empty lines surrounding it.
Returns:
FST | None: Node which entirely contains location, either exactly or not, orNoneif no such node.
Examples:
>>> FST('i = val', 'exec').find_loc_in(0, 6, 0, 7)
<Name 0,4..0,7>
>>> FST('i = val', 'exec').find_loc_in(0, 4, 0, 7)
<Name 0,4..0,7>
>>> FST('i = val', 'exec').find_loc_in(0, 4, 0, 7, allow_exact=False)
<Assign 0,0..0,7>
>>> FST('i = val', 'exec').find_loc_in(0, 5, 0, 7, allow_exact=False)
<Name 0,4..0,7>
>>> FST('i = val', 'exec').find_loc_in(0, 4, 0, 6, allow_exact=False)
<Name 0,4..0,7>
>>> FST('i = val', 'exec').find_loc_in(0, 3, 0, 7)
<Assign 0,0..0,7>
>>> FST('i = val', 'exec').find_loc_in(0, 3, 0, 7, allow_exact=False)
<Assign 0,0..0,7>
>>> print(FST('i = val', 'exec').find_loc_in(0, 0, 0, 7, allow_exact=False))
None
>>> FST('i = val\n', 'exec').find_loc_in(0, 0, 0, 7, allow_exact=False)
<Module ROOT 0,0..1,0>
>>> FST('i = val', 'exec').find_loc_in(0, 0, 0, 7)
<Assign 0,0..0,7>
>>> FST('i = val', 'exec').find_loc_in(0, 0, 0, 7, allow_exact='top')
<Module ROOT 0,0..0,7>
Find the first highest level node which is contained entirely in location (inclusive, starting search at
self). To reiterate, the search will only find nodes at self or below, no parents.
Parameters:
ln: Start line of location to search (0 based).col: Start column (character) on start line.end_ln: End line of location to search (0 based, inclusive).end_col: End column (character, inclusive withFST.end_col, exclusive withFST.col) on end line.
Returns:
FST | None: First node in syntactic order which is entirely contained in the location orNoneif no such node.
Examples:
>>> FST('i = val', 'exec').find_in_loc(0, 0, 0, 7)
<Module ROOT 0,0..0,7>
>>> FST('i = val', 'exec').find_in_loc(0, 1, 0, 7)
<Name 0,4..0,7>
>>> FST('i = val', 'exec').find_in_loc(0, 4, 0, 7)
<Name 0,4..0,7>
>>> print(FST('i = val', 'exec').find_in_loc(0, 5, 0, 7))
None
Find node which best fits location. If an exact location match is found then that is returned with the top
parameter specifying which of multiple nodes at same location is returned if that is the case. Otherwise
find_in_loc() is preferred if there is a match and if not then find_loc_in(). The search is done efficiently
so that the same nodes are not walked multiple times.
Parameters:
ln: Start line of location to search for (0 based).col: Start column (character) on start line.end_ln: End line of location to search for (0 based, inclusive).end_col: End column (character, inclusive withFST.end_col, exclusive withFST.col) on end line.exact_top: If an exact location match is found and multiple nodes share this location (Exprandexpr) then this decides whether the highest level node is returned or the lowest (Truemeans highest).
Returns:
FST | None: Node found if any.
Examples:
>>> FST('var', 'exec').find_loc(0, 0, 0, 3)
<Name 0,0..0,3>
>>> FST('var', 'exec').find_loc(0, 0, 0, 3, exact_top=True)
<Module ROOT 0,0..0,3>
>>> FST('var', 'exec').find_loc(0, 1, 0, 2)
<Name 0,0..0,3>
>>> FST('var', 'exec').find_loc(0, 1, 0, 2, exact_top=True)
<Name 0,0..0,3>
>>> FST('var', 'exec').find_loc(0, 0, 1, 4)
<Module ROOT 0,0..0,3>
>>> repr(FST('var', 'exec').find_loc(0, 2, 1, 4))
'None'
Is a node which opens a block. Types include FunctionDef, AsyncFunctionDef, ClassDef, For,
AsyncFor, While, If, With, AsyncWith, Match, Try, TryStar, ExceptHandler or match_case.
Is a node which opens a block. Types include FunctionDef, AsyncFunctionDef, ClassDef, For,
AsyncFor, While, If, With, AsyncWith, Match, Try, TryStar, ExceptHandler, match_case or
mod.
Is a node which opens a scope. Types include FunctionDef, AsyncFunctionDef, ClassDef, Lambda,
ListComp, SetComp, DictComp or GeneratorExp.
Is a node which opens a scope. Types include FunctionDef, AsyncFunctionDef, ClassDef, Lambda,
ListComp, SetComp, DictComp, GeneratorExp or mod.
Is a node which opens a named scope. Types include FunctionDef, AsyncFunctionDef or ClassDef.
Is a node which opens a named scope. Types include FunctionDef, AsyncFunctionDef, ClassDef or mod.
Is a node which opens an anonymous scope. Types include Lambda, ListComp, SetComp, DictComp or
GeneratorExp.
Is a sync or async function or class definition node, FunctionDef, AsyncFunctionDef or ClassDef.
Is a sync or async function or class definition node, FunctionDef, AsyncFunctionDef, ClassDef or
mod.
Whether self is an elif or not, or not an If at all.
Returns:
Trueif iselifIf,Falseif is normalIf,Noneif is notIfat all.
Examples:
>>> FST('if 1: pass\nelif 2: pass').orelse[0].is_elif()
True
>>> FST('if 1: pass\nelse:\n if 2: pass').orelse[0].is_elif()
False
>>> print(FST('if 1: pass\nelse:\n i = 2').orelse[0].is_elif())
None