fst.view

Slice-access views on FST lists of children.

class FSTView:

View for a list of AST nodes in a body, or any other field which is a list of values (not necessarily AST nodes), of an FST node.

Nodes can be gotten or put via indexing. Nodes which are accessed through indexing (normal or slice) are not automatically copied, if a copy is desired then do fst.body[start:stop].copy(). Slice assignments also work but will always assign a slice to the range. If you want to assign an individual item to this range or a subrange then use fst.body[start:stop].replace(..., one=True).

>>> from fst import FST
>>> view = FST('[1, 2, 3]').elts
>>> view[1].remove()
>>> view
<<List ROOT 0,0..0,6>.elts>
>>> view[1:].cut()
<List ROOT 0,0..0,3>
>>> view
<<List ROOT 0,0..0,3>.elts>

This object is meant to be, and is normally created automatically by accessing AST list fields on an FST node.

Examples:

>>> from fst import FST
>>> f = FST('[0, 1, 2, 3]')
>>> f
<List ROOT 0,0..0,12>
>>> f.elts
<<List ROOT 0,0..0,12>.elts>
>>> f.elts[1]
<Constant 0,4..0,5>
>>> f.elts[1].src
'1'
>>> f.elts[1:3]
<<List ROOT 0,0..0,12>.elts[1:3]>
>>> f.elts[1:3].copy()
<List ROOT 0,0..0,6>
>>> _.src
'[1, 2]'
>>> f.elts[1:3] = '[4]'
>>> f.src
'[0, [4], 3]'
>>> f.elts[1:2] = '4'
>>> f.src
'[0, 4, 3]'
>>> del f.elts[1:]
>>> f.src
'[0]'
>>> f.elts[0] = '*star'
>>> f.src
'[*star]'
base: fst.fst.FST

The target FST node this view references (the container).

field: str

The target field this view references. Can be virtual field like _all.

is_FST: bool = False

Allows to quickly differentiate between actual FST nodes vs. views or locations.

is_item_FST: bool = True

Whether single item indexing on this view yields an FST node or not (could also be None). This is False for multi-node sequences like a Dict, MatchMapping and arguments and string sequences like Global/Nonlocal.names and MatchClass.kwd_attrs.

is_item_multinode: bool = False

Whether individual items of this view are composed of (possibly) multiple nodes. This is True for sequences like a Dict, MatchMapping and arguments.

is_one: bool

Whether this view represents a single dereferenced view for fields which do not dereference to a single FST or not. To be clear, this is only True when one of those views is dereferenced as a single item to produce a single-item view of that field.

This is mostly informative and it is possible to manipulate an is_one=True view back into a slice view with slice operations. This can affect a few individual node operations for Global/Nonlocal.names views, like when a copy is made. For a is_one=True the copy will be a single node whereas for a slice view it will be a slice.

start: int

Start position within the target field list this view references. For a _body field with a docstring an index of 0 here really means 1 in the actual body list.

stop: int

Position one past the last item within the target field list this view references. For a _body field with a docstring this index tops out at 1 less than the length of the actual body list.

start_and_stop: tuple[int, int]

Start and stop positions within the target field list this view references.

item: FSTView | fst.fst.FST | str | None

Convenience function for singleton FSTView to get the item it is referencing. Multinode FSTView objects do not have a single item they can dereference and so return themselves.

root: fst.fst.FST

Root node of the tree this view belongs to.

lines: list[str]

Whole lines of this view from the RAW SOURCE, without any dedentation, may also contain parts of enclosing nodes. Will have indentation as it appears in the top level source if multiple lines.

A valid list of strings is always returned, even for empty views. The lines list returned is always a copy so safe to modify.

Examples:

>>> from fst import *
>>> FST('a\nb\nc\nd').body[1:3].lines
['b', 'c']
src: str

Source code of this view from the RAW SOURCE clipped out as a single string, without any dedentation. Will have indentation as it appears in the top level source if multiple lines.

A string is always returned, even for empty views.

Examples:

>>> from fst import *
>>> FST('[a, b, c, d]').elts[1:3].src
'b, c'
loc: fst.common.fstloc | None

Zero based character indexed location of view (including parentheses and / or decorators where present). May return an fstloc or an fstlocn with an n parameter.

ln: int | None

Line number of the first line of this view (0 based).

col: int | None

CHARACTER index of the start of this view (0 based).

end_ln: int | None

Line number of the LAST LINE of this view (0 based).

end_col: int | None

CHARACTER index one past the end of this view (0 based).

bloc: fst.common.fstloc | None

Zero based character indexed location of view (including parentheses and / or decorators where present). This is identical to .loc for views. May return an fstloc or an fstlocn with an n parameter.

bln: int | None

Line number of the first line of this view (0 based). This is identical to .ln for views.

bcol: int | None

CHARACTER index of the start of this view (0 based). This is identical to .col for views.

bend_ln: int | None

Line number of the LAST LINE of this view (0 based). This is identical to .end_ln for views.

bend_col: int | None

CHARACTER index one past the end of this view (0 based). This is identical to .end_col for views.

def pars(self, *, shared: bool | None = True) -> fst.common.fstlocn | None:

Convenience function just returns the .loc of this view, as it will already have parentheses included.

Parameters:

  • IGNORED!

Returns:

  • fstlocn: Full location of view from first item to last, if items present.
  • None: If view is empty.

Examples:

>>> from fst import *
>>> FST('[a, b, c, d]').elts[1:3].pars()
fstlocn(0, 4, 0, 8, n=0)
>>> FST('[(a), (b), (c), (d)]').elts[1:3].pars()
fstlocn(0, 6, 0, 14, n=0)

WARNING! Views always return their fully parenthesized locations so the shared parameter here is ignored for those pesky single-argument GeneratorExp expressions which share their parentheses with the Call.

>>> FST('call(i for i in j)')._args.pars(shared=False)  # FSTView.pars()
fstlocn(0, 4, 0, 18, n=0)
>>> FST('call(i for i in j)')._args[0].pars(shared=False)  # FST.pars()
fstlocn(0, 5, 0, 17, n=-1)
def __getitem__( self, idx: int | slice | str) -> FSTView | fst.fst.FST | str | None:

Get a single item or a slice view from this view. All indices (including negative) are relative to the bounds of this view. This is just an access, not a cut or a copy, so if you want a copy you must explicitly do .copy() on the returned value.

Note that FSTView can also hold references to non-AST lists of items, so keep this in mind when dealing with return values which may be None or may not be FST nodes.

Parameters:

  • idx: The index or slice where to get the item(s) from, or a str for a single-item name search.

Returns:

  • FSTView | FST | str | None: Either a single FST node if accessing a single item or a new FSTView view according to the slice passed. str can also be returned from a view of Global.names or None from a Dict.keys.

Examples:

>>> from fst import FST
>>> FST('[0, 1, 2, 3]').elts[1].src
'1'
>>> FST('[0, 1, 2, 3]').elts[:3]
<<List ROOT 0,0..0,12>.elts[:3]>
>>> FST('[0, 1, 2, 3]').elts[:3].copy().src
'[0, 1, 2]'
>>> FST('[0, 1, 2, 3]').elts[-3:]
<<List ROOT 0,0..0,12>.elts[1:4]>
>>> FST('def fun(): pass\nclass cls: pass\nvar = val').body[1]
<ClassDef 1,0..1,15>
>>> FST('global a, b, c').names
<<Global ROOT 0,0..0,14>.names>

If indexing a single item does not give an FST node then it will give another FSTView.

>>> FST('global a, b, c').names.at(1)
<<Global ROOT 0,0..0,14>.names[1]>
def __setitem__( self, idx: int | slice | str, code: Union[fst.fst.FST, ast.AST, str, list[str], NoneType]) -> None:

Set a single item or a slice view in this view. All indices (including negative) are relative to the bounds of this view.

Note that FSTView can also hold references to non-AST lists of items, so keep this in mind when assigning values.

Parameters:

  • idx: The index or slice where to put the item(s). Or a str for a single-item name search.

Examples:

>>> from fst import FST
>>> (f := FST('[0, 1, 2, 3]')).elts[1] = '4'; f.src
'[0, 4, 2, 3]'
>>> (f := FST('[0, 1, 2, 3]')).elts[:3] = '[5]'; f.src
'[[5], 3]'
>>> (f := FST('[0, 1, 2, 3]')).elts[:3] = '5'; f.src
'[5, 3]'
>>> (f := FST('[0, 1, 2, 3]')).elts[:3] = '5,'; f.src
'[5, 3]'
>>> (f := FST('[0, 1, 2, 3]')).elts[-3:] = '[6]'; f.src
'[0, [6]]'
>>> (f := FST('[0, 1, 2, 3]')).elts[-3:] = '6'; f.src
'[0, 6]'
>>> (f := FST('[0, 1, 2, 3]')).elts[:] = '7, 8'; f.src
'[7, 8]'
>>> f = FST('[0, 1, 2, 3]')
>>> f.elts[2:2] = f.elts[1:3].copy()
>>> f.src
'[0, 1, 1, 2, 2, 3]'
def __delitem__(self, idx: int | slice | str) -> None:

Delete a single item or a slice from this view. All indices (including negative) are relative to the bounds of this view.

Note that FSTView can also hold references to non-AST lists of items, so keep this in mind when assigning values.

Parameters:

  • idx: The index or slice to delete. Or a str for a single-item name search.

Examples:

>>> from fst import FST
>>> del (f := FST('[0, 1, 2, 3]')).elts[1]; f.src
'[0, 2, 3]'
>>> del (f := FST('[0, 1, 2, 3]')).elts[:3]; f.src
'[3]'
>>> del (f := FST('[0, 1, 2, 3]')).elts[-3:]; f.src
'[0]'
>>> del (f := FST('[0, 1, 2, 3]')).elts[:]; f.src
'[]'
def at( self, idx: int | str, force_view: bool = False) -> FSTView | fst.fst.FST | str | None:

Get a single item from this view. All indices (including negative) are relative to the bounds of this view. This is just an access, not a cut or a copy, so if you want a copy you must explicitly do .copy() on the returned value.

This function differs from [] indexing in that indexing will always return the actual value at the index, including if it is a None or a str value (Global/Nonlocal.names or MatchClass.kwd_attrs). This function will return an FST where that exists, but for primitive values like None or a str, this function will return a singleton FSTView of the item. This is in order to make it possible to retrieve the item's information getting it.

Parameters:

  • idx: The index where to get the item from, or a str for a name search.
  • force_view: If True then will return all items as singleton FSTView objects, regardless of if they reference FST nodes or not. If an item was indexed with a string and it is not a direct child of this view then a view with that node's base and index is returned.

Returns:

  • FSTView | FST | str | None: Either a single FST node if accessing a single item if that is possible, or a new FSTView view if item is not an FST.

Examples:

>>> from fst import FST
>>> print(FST('{**a}').keys[0])
None
>>> print(FST('{**a}').keys.at(0))
<<Dict ROOT 0,0..0,5>.keys[0]>
>>> print(FST('{**a}').keys.at(0).copy())
None
>>> FST('global a, b, c').names[1]
'b'
>>> FST('global a, b, c').names.at(1)
<<Global ROOT 0,0..0,14>.names[1]>
>>> FST('global a, b, c').names.at(1).copy()
<Name ROOT 0,0..0,1>
>>> FST('global a, b, c').names.at(1).copy().src
'b'
>>> FST('global a, b, c').names.at(1).copy(promote=False)
'b'
>>> FST('[a, b, c]').elts.at(1, force_view=True)
<<List ROOT 0,0..0,9>.elts[1]>
>>> FST('[a, b, c]').elts.at(1, force_view=True).item
<Name 0,4..0,5>
def copy(self, **options: object) -> fst.fst.FST | list[str] | str:

Copy this slice to a new top-level tree, dedenting and fixing as necessary.

Parameters:

Returns:

  • FST: Copied slice.

Examples:

>>> from fst import FST
>>> FST('[0, 1, 2, 3]').elts[1:3].copy().src
'[1, 2]'
>>> FST('global a, b, c').names.copy().src
'a, b, c'
>>> FST('global a, b, c').names.at(1).copy().src
'b'
>>> FST('global a, b, c').names.copy(promote=False)
['a', 'b', 'c']
>>> FST('global a, b, c').names.at(1).copy(promote=False)
'b'
def cut(self, **options: object) -> fst.fst.FST | list[str] | str:

Cut out this slice to a new top-level tree (if possible), dedenting and fixing as necessary. Cannot cut root node.

Parameters:

Returns:

  • FST: Cut slice.

Examples:

>>> from fst import FST
>>> (f := FST('[0, 1, 2, 3]')).elts[1:3].cut().src
'[1, 2]'
>>> f.src
'[0, 3]'
>>> (f := FST('global a, b, c')).names[:-1].cut().src
'a, b'
>>> f.src
'global c'
>>> (f := FST('global a, b, c')).names.at(1).cut().src
'b'
>>> f.src
'global a, c'
>>> (f := FST('global a, b, c')).names[:-1].cut(promote=False)
['a', 'b']
>>> f.src
'global c'
>>> (f := FST('global a, b, c')).names.at(1).cut(promote=False)
'b'
>>> f.src
'global a, c'
def replace( self, code: Union[fst.fst.FST, ast.AST, str, list[str], NoneType], one: bool | None = True, **options: object) -> FSTView:

Replace or delete (if code=None) this slice.

Returns:

  • self

Parameters:

  • code: FST, AST or source str or list[str] to put. None to delete this slice.
  • one: If True then will replace the range of this slice with a single item. Otherwise False will attempt a slice replacement (type must be compatible).
  • options: See fst.fst.FST.options().

Examples:

>>> from fst import FST
>>> FST('[0, 1, 2, 3]').elts[1:3].replace('(4, 5)').base.src
'[0, (4, 5), 3]'
>>> FST('[0, 1, 2, 3]').elts[1:3].replace('4, 5').base.src
'[0, (4, 5), 3]'
>>> FST('[0, 1, 2, 3]').elts[1:3].replace('(4, 5)', one=False).base.src
'[0, (4, 5), 3]'
>>> FST('[0, 1, 2, 3]').elts[1:3].replace('4, 5', one=False).base.src
'[0, 4, 5, 3]'
def remove(self, **options: object) -> FSTView:

Delete this slice, equivalent to replace(None, ...)

Parameters:

Returns:

  • self

Examples:

>>> from fst import FST
>>> FST('[0, 1, 2, 3]').elts[1:3].remove().base.src
'[0, 3]'
def insert( self, code: Union[fst.fst.FST, ast.AST, str, list[str]], idx: Union[int, Literal['end']] = 0, *, one: bool | None = True, **options: object) -> FSTView:

Insert into this slice at a specific index.

Returns:

  • self

Parameters:

  • code: FST, AST or source str or list[str] to insert.
  • idx: Index to insert before. Can be 'end' to indicate add at end of slice.
  • one: If True then will insert code as a single item. Otherwise False will attempt a slice insertion (type must be compatible).
  • options: See fst.fst.FST.options().

Examples:

>>> from fst import FST
>>> FST('[0, 1, 2, 3]').elts.insert('4, 5', 1).base.src
'[0, (4, 5), 1, 2, 3]'
>>> FST('[0, 1, 2, 3]').elts.insert('(4, 5)', 1).base.src
'[0, (4, 5), 1, 2, 3]'
>>> FST('[0, 1, 2, 3]').elts.insert('4, 5', 'end', one=False).base.src
'[0, 1, 2, 3, 4, 5]'
>>> FST('[0, 1, 2, 3]').elts.insert('(4, 5)', 'end', one=False).base.src
'[0, 1, 2, 3, (4, 5)]'
>>> # same as 'end' but 'end' is always 'end'
>>> FST('[0, 1, 2, 3]').elts.insert('4, 5', 4, one=False).base.src
'[0, 1, 2, 3, 4, 5]'
>>> FST('[0, 1, 2, 3]').elts.insert('(4, 5)', 4, one=False).base.src
'[0, 1, 2, 3, (4, 5)]'
>>> FST('[0, 1, 2, 3]').elts[1:3].insert('*star').base.src
'[0, *star, 1, 2, 3]'
def append( self, code: Union[fst.fst.FST, ast.AST, str, list[str]], **options: object) -> FSTView:

Append code as a single item to the end of this slice.

Returns:

  • self

Parameters:

Examples:

>>> from fst import FST
>>> FST('[0, 1, 2, 3]').elts.append('(4, 5)').base.src
'[0, 1, 2, 3, (4, 5)]'
>>> FST('[0, 1, 2, 3]').elts[1:3].append('*star').base.src
'[0, 1, 2, *star, 3]'
def extend( self, code: Union[fst.fst.FST, ast.AST, str, list[str]], one: Optional[Literal[False]] = False, **options: object) -> FSTView:

Extend this slice with the slice in code (type must be compatible).

Returns:

  • self

Parameters:

Examples:

>>> from fst import FST
>>> FST('[0, 1, 2, 3]').elts.extend('4, 5').base.src
'[0, 1, 2, 3, 4, 5]'
>>> FST('[0, 1, 2, 3]').elts.extend('(4, 5)').base.src
'[0, 1, 2, 3, (4, 5)]'
>>> FST('[0, 1, 2, 3]').elts[1:3].extend('4, 5').base.src
'[0, 1, 2, 4, 5, 3]'
>>> FST('[0, 1, 2, 3]').elts[1:3].extend('(4, 5)').base.src
'[0, 1, 2, (4, 5), 3]'
def prepend( self, code: Union[fst.fst.FST, ast.AST, str, list[str]], **options: object) -> FSTView:

prepend code as a single item to the beginning of this slice.

Returns:

  • self

Parameters:

Examples:

>>> from fst import FST
>>> FST('[0, 1, 2, 3]').elts.prepend('(4, 5)').base.src
'[(4, 5), 0, 1, 2, 3]'
>>> FST('[0, 1, 2, 3]').elts[1:3].prepend('*star').base.src
'[0, *star, 1, 2, 3]'
def prextend( self, code: Union[fst.fst.FST, ast.AST, str, list[str]], one: Optional[Literal[False]] = False, **options: object) -> FSTView:

Extend the beginning of this slice with the slice in code (type must be compatible).

Returns:

  • self

Parameters:

Examples:

>>> from fst import FST
>>> FST('[0, 1, 2, 3]').elts.prextend('4, 5').base.src
'[4, 5, 0, 1, 2, 3]'
>>> FST('[0, 1, 2, 3]').elts.prextend('(4, 5)').base.src
'[(4, 5), 0, 1, 2, 3]'
>>> FST('[0, 1, 2, 3]').elts[1:3].prextend('4, 5').base.src
'[0, 4, 5, 1, 2, 3]'
>>> FST('[0, 1, 2, 3]').elts[1:3].prextend('(4, 5)').base.src
'[0, (4, 5), 1, 2, 3]'