Skip to content

Releases: tom-pytel/pfst

v0.3.2

v0.3.2 Pre-release
Pre-release

Choose a tag to compare

@tom-pytel tom-pytel released this 27 Mar 15:31

0.3.2 - alpha - 2026-03-27

Added

  • Python 3.15 lazy import support
  • MatchClass.patterns+kwd_attrs=kwd_patterns slice get and put
    • _pattern_attrlikes special slice container holds both combined unnamed and keyword patterns, e.g a, b, c=d, e=f
    • _attrs virtual field on MatchClass and _pattern_attrlikes
  • filled out lots of non-basic coercions, mostly between forms like **a, keywords kw=val and annotations v: ann
    • arguments, _arglikes, _expr_arglikes, _pattern_attrlikes and _type_params between each other
    • TypeVar=default_value and **ParamSpec to the previous and also to single _arglike and to/from keyword
    • TypeVar: bound <-> arg: annotation
    • singleton arguments and _pattern_attrlikes to single arg and keyword if possible

Changed

  • concretized behavior of locations of nonstandard slices like comprehension.ifs, Assign.targets or .decorator_list
    • includes extra syntax in location when accessed as slice but not when accessed as single item
  • parse unspecified type will not reduce statement Expr to single expression if has trailing semicolon
  • dump(loc=False) will no longer output ROOT for root node, easier to compare just structure dumps

Fixed

  • special FSTView classes for decorator lists, comprehension.ifs and Assign.targets so that slice locations from these field views are correct
  • honor dump() eol parameter when outputting out='str'
  • coercing FST expression to a non-arglike expression, if it is arglike-only then it will be appropriately parenthesized, e.g. FST(*not a, 'expr_arglike').as_('expr') -> *(not a)
  • fst.cli.sub properly prints multiple statement substution lines
  • fixed a few cases of parse expression where trailing semicolon was accepted for an expression

v0.3.1

v0.3.1 Pre-release
Pre-release

Choose a tag to compare

@tom-pytel tom-pytel released this 13 Mar 14:09

0.3.1 - alpha - 2026-03-13

Added

  • substitution and traversal
    • added on parameter to allow walk() and search() yield on 'enter' or 'leave' node or 'both'
    • added on parameter to sub() can do substitution on 'enter' or 'leave', allowing bottom-up substitution for easier collapsing of nested structures
    • added callback and callback_after parameters to sub() and subn() to better observe and control individual substitutions
    • added MExceptHandler._star parameter to allow differentiating between except and except* on match
    • new CLI modules fst.cli.search and fst.cli.sub for convenient application to source files
  • allow use of FSTView as a singleton item reference
    • types which dereference to singleton FSTView get a flag is_one to indicate this and can return single node on copy/cut() instead of slice
    • at() method to get an item as a singleton FSTView if getting that item alone would lose information like a None or str
    • .item property for quick access to singleton FSTView item
  • promote option to control whether primitive fields are gotten as primitives or nodes, covers all primitive fields
  • FST.parse_ast() to parse a pure AST without FST nodes using our extended parsing
  • put slice to Dict and MatchMapping can accept undelimited Dict and MatchMapping source
  • op can be set as a global option for convenience for putting slices to Compare
  • added --color/--no-color options to main fst CLI module

Changed

  • tweaked certain cases of multiline slice put indentation to be slightly less horrible, aesthetics still need a rework when functionality is no longer the priority
  • non-raise fail substitution inside f-strings on Python < 3.12, warn instead, as those replacements are not implemented and probably will not be
  • get/put/get_slice/put_slice() typing tweaked for easier to swizzle parameters with type checking enabled
  • added FST.strip() which removes trivia surrounding node at root, was previously private function _sanitize()
  • identifier puts using FST nodes as source (which doesn't consume them), will now unmake those nodes to stay consistent with all other puts
  • loosened Constant rules
    • fromast() and as_() will accept negative numerical Constant.value and normalize those to UnaryOp(USub, abs(Constant))
    • put to primitive int field will accept UnaryOp(USub, Constant) numerical values
  • put one to Subscript.slice
    • automatically parenthesize Tuple containing Starred instead of raising on Python 3.10
    • automatically unparenthesize Tuple containing Slices instead of raising

Fixed

  • fixed bug in sub() when putting certain slices when repl_options present which caused errors like ValueError: expecting single item pair for put as 'one=True'
  • corrected walk(scope=True) of Comprehension first generator iterator if it itself is a scope, e.g. a = [i for i in [j for j in iter]]
  • fixed reparse empty AST _ExceptHandlers slice
  • fixed swap Try <-> TryStar on replace all handlers with an empty slice (not delete, replace with empty)

v0.3.0

v0.3.0 Pre-release
Pre-release

Choose a tag to compare

@tom-pytel tom-pytel released this 28 Feb 14:38

0.3.0 - alpha - 2026-02-28

Added

  • Python 3.15a support
    • unpacking in comprehensions, [*st for st in itr], {**key for key in itr}
    • lazy imports not on main branch yet
  • structural pattern matching
    • FST.match() match - f.match(Mstmt(body=[Expr(Constant(str)), ...]))
    • FST.search() search - for m in f.search(Assign([Name], ListComp))
    • FST.sub() substitute - f.sub(MCall('old_name', _args=M(all_args=...)), 'new_name(__FST_all_args, new_kw=newval)')
  • FST.find_def() easily find (possibly nested) function and class definitions by name in a given scope by dotted name
    • added usage of this in indexing (single-element only, not slice): method = module['class.method'], del someclass['method'], etc...
  • FST / fstview interoperability for convenience
    • added src, lines, loc/ln/col/end_ln/end_col, bloc/bln/bcol/bend_ln/bend_col and pars() to fstview
    • added base to FST so that you can do f.base.get_src(*f.loc) without knowing if is FST or fstview
  • walk()
    • can take explicit list of nodes to walk instead of just "all children of self"
    • can take user callback for the all parameter for deciding if node matches
  • added top parameter to step_fwd/back() to restrict stepping above a given parent
  • finally validating options
  • trivia option shorthand () for (False, False)

Changed

  • allow get and put as one=True single-item Dict or MatchMapping instead of error, makes more sense
  • simplified trivia option to always specify entire state, no more merging with current state
  • Global/Nonlocal.names views now return a single-element FSTView when dereferencing a single element instead of the string value at that index
  • allow delete Raise.exc if cause exists by automatically deleting cause first
  • allow delete ExceptHandler.type if name exists by automatically deleting name first

Fixed

  • fixed step_fwd/back() for anything that wasn't all=True/False/'loc'
  • replacement of single statement no longer replaces that statement's FST, allowing walk() to recurse into replaced children
  • disallow put _ as name to MatchMapping.rest and MatchClass.kwd_attrs
  • disallow put * or dotted as name to alias.name if context does not allow
  • disallow put * to _aliases.names if there are multiple names (only allowed to replace a single one)
  • put empty slice of statementlikes containing only trivia erasing whole orelse or finalbody field now deletes the whole field instead of leaving a hanging else: or finally:

v0.2.6

v0.2.6 Pre-release
Pre-release

Choose a tag to compare

@tom-pytel tom-pytel released this 31 Jan 22:14

0.2.6 - alpha - 2026-01-31

Breaking Changes

  • when putting source instead of an FST or AST node directly as a slice to an expression, if the source is a delimited sequence then it will always be put as one node and not unpacked, previous behavior of .put_slice('[1, 2, 3]') being put as a slice of three distinct elements can be selected by passing one=None

Added

  • coercion expanded greatly, can now put more nodes which are not the exact type but that "should work", e.g. TupleFST.elts = MatchSequenceFST.patterns.copy()
    • pattern, arguments, arg, alias, withitem, TypeVar and TypeVarTuple to expr
    • custom slice types _Assign_targets, _decorator_list, _arglikes, _comprehension_ifs, _aliases, _withitems and _type_params to Tuple, List or Set
    • all sequence types, custom and standard, to custom slice types
    • all expressions and custom slice types to pattern
    • keyword to arguments
  • FST.as_(mode) for explicit coercion directly to mode (which can be an explicit type[AST] or a parse mode)
  • FST(FST, mode) for constructor default non-destructive coerce copy from other FST node, a-la list(other_list) is not other_list
  • FST(AST, mode) and FST.fromast(AST, type[AST] or mode) can use the new coercion to convert AST
    • with these changes, FST(x[, mode]) now works like list(x) in that it converts source, AST or FST to the given mode FST if possible, or just AN FST if mode not provided
  • put slice as one=True for _Assign_targets, _decorator_list, _arglikes, _comprehension_ifs, _aliases, _withitems, _type_params and _expr_arglikes
  • prescribed slicing for MatchClass.patterns
  • prescribed slicing for arguments._all, e.g. FunctionDefFST.args[x:y] = 'a: int, /, b=c'
  • unpar('invalid') can remove delimiters from List, Set, Dict, MatchMapping, ListComp, SetComp, DictComp and GeneratorExp, if needed for some reason

Changed

  • FST.dump() returns self when not returning str or lines and those are now specified with out='str' or out='lines'
  • concretized behavior of put slice with one=True for custom special slices, will not put multiple elements now in this mode when one requested
  • parse_withitem('x,') now parses to singleton Tuple withitem instead of single Name withitem with trailing comma, makes more sense
  • allow put Starred to value field of Expr, Return, AnnAssign and Yield even though not compilable, for consistency, our metric is parsability, not compilability
  • REMOVED norm_put option as was too annoying to maintain everywhere needed for the little good it did, norm_self and norm_get remain
  • improved put multiline slice aesthetics, specifically better needs-own-line detection and keeping line comment on pre-insert element line instead of moving it
  • parse withitem and _withitems no longer accept single unparenthesized Yield, NamedExpr or Tuple as a convenience, causes too many problems downstream
  • empty arguments now always have a location, having potentially zero-length arguments is less ugly than having arguments with no location
  • Dict and MatchMapping slice views dereferenced with a single index will now return a view of that single key:value pair instead of raising

Fixed

  • fixed comprehension_ifs and _arglikes coercions where unparenthesized tuple needed to be parenthesized
  • FST.dump() will no longer output trailing whitespace on lines when the line is completely empty, better for tests
  • fixed delimit of whole node at root when degenerate last line has line continuation without trailing newline
  • fixed parse location correction of multiline unparenthesized tuple with group parenthesized first and / or last elements
  • fixed parse of unparenthesized tuple with trailing comma to _Assign_targets
  • don't accept Starred node for a withitem, _withitems normal or _comprehension_ifs coerce
  • don't try to parse *starred = value as TypeVarTuple in place of Assign on py < 3.13
  • allow put Starred to TypeVarTuple.default_value
  • fixed parse withitem of a solo GeneratorExp
  • will not parenthesize arg or arguments if putting multiline to a Lambda but instead parenthesize the Lambda
  • will not parenthesize multiline withitem put to With.items with existing items but rather the whole items field
  • fixed unpar(node=True) node if first and or last elements are parenthesized

v0.2.5

v0.2.5 Pre-release
Pre-release

Choose a tag to compare

@tom-pytel tom-pytel released this 06 Jan 12:23

v0.2.5

0.2.4 alpha

0.2.4 alpha Pre-release
Pre-release

Choose a tag to compare

@tom-pytel tom-pytel released this 08 Dec 18:10

Full Changelog: v0.2.3...v0.2.4

v0.2.2

v0.2.2 Pre-release
Pre-release

Choose a tag to compare

@tom-pytel tom-pytel released this 23 Sep 13:20
v0.2.2

v0.1.1

v0.1.1 Pre-release
Pre-release

Choose a tag to compare

@tom-pytel tom-pytel released this 07 Jul 13:12
update docs, stable point

Initial release

Initial release Pre-release
Pre-release

Choose a tag to compare

@tom-pytel tom-pytel released this 27 Jun 16:19
v0.1.0

include documentation