sourcery_analytics.visitors¶
Classes for handling and recursing into tree structures.
- class sourcery_analytics.visitors.Visitor¶
-
Abstract visitor class.
Visitors implement two methods:
_touch
and_enter
._touch
should return a “fact” about a node, for instance its name or its depth. It should not directly recurse into the node’s children, except where this represents part of the “fact” being calculated (seeTreeVisitor
). “Facts” which require context, such as depth, can be derived from custom (mutable) attributes manipulated in the_enter
method, which, as a context manager, should yield after pre-node calculations and before post-node calculations.With both these methods implemented, the visitor provides the public
.visit
method to at once enter and calculate over a node.Examples
>>> class DepthVisitor(Visitor[int]): ... def __init__(self): ... self.depth = 0 ... @contextlib.contextmanager ... def _enter(self, node: astroid.nodes.NodeNG): ... self.depth += 1 ... yield ... self.depth -= 1 ... def visit(self, node: astroid.nodes.NodeNG) -> int: ... return self.depth - 1
- class sourcery_analytics.visitors.IdentityVisitor¶
-
No-op visitor, returning the node itself.
Useful as a default sub-visitor for other visitors.
- class sourcery_analytics.visitors.FunctionVisitor(function: Callable[[NodeNG], P])¶
-
Generic visitor returning the result of its function calculated on the node.
Examples
>>> name_visitor = FunctionVisitor(lambda node: node.__class__.__name__) >>> indentation_visitor = FunctionVisitor(lambda node: node.col_offset)
- class sourcery_analytics.visitors.ConditionalVisitor(sub_visitor: Visitor[P] = IdentityVisitor(), condition: Callable[[NodeNG], bool] = always)¶
Bases:
Visitor
[Optional
[P
]],Generic
[P
]Returns the result of its sub-visitor when condition is True, and None otherwise.
By default, this visitor will unconditionally return the node itself.
- sub_visitor¶
a Visitor to optionally return the result from.
- condition¶
a Condition describing when to return the result.
Examples
>>> from sourcery_analytics.conditions import is_type >>> from astroid.nodes import FunctionDef >>> method_visitor = ConditionalVisitor(condition=is_type(FunctionDef)) >>> method = astroid.extract_node("def foo(): pass") >>> method_visitor.visit(method).name 'foo' >>> binop = astroid.extract_node("a + b") >>> method_visitor.visit(binop) is None True
See also
- class sourcery_analytics.visitors.CompoundVisitor(*visitors: Visitor[P], collector: Callable[[Iterable[P]], Q] = tuple)¶
Bases:
Visitor
[Q
],Generic
[P
,Q
]Combines a collection of other visitors into a single visitor
Handles context and collection of the results.
This is most useful when you need a single sub-visitor for some other visitor.
Examples
Collect the name and indentation of every sub-node in a tree:
>>> name_visitor = FunctionVisitor(lambda node: node.__class__.__name__) >>> line_visitor = FunctionVisitor(lambda node: node.lineno) >>> name_line_visitor = CompoundVisitor(name_visitor, line_visitor) >>> every_node_visitor = TreeVisitor(name_line_visitor, collector=list) >>> source = ''' ... def one(): ... return 1 ... ''' >>> from sourcery_analytics.utils import clean_source >>> node = astroid.parse(clean_source(source)) >>> every_node_visitor.visit(node) [('Module', 0), ('FunctionDef', 1), ('Arguments', None), ('Return', 2)...
- class sourcery_analytics.visitors.TreeVisitor(sub_visitor: Visitor[P] = IdentityVisitor(), collector: Callable[[Iterator[P]], Q] = iter)¶
-
Collects the result of the sub-visitor applied to every sub-node of a node.
By default, returns an iterable of every sub-node of a node. The type parameters indicate the expected output of the sub-visitor and the output of the tree visitor respectively (as handled by the collector).
Examples
>>> name_visitor = FunctionVisitor(lambda node: node.__class__.__name__) >>> every_name_visitor = TreeVisitor(name_visitor, list) >>> source = "x + y" >>> node = astroid.extract_node(source) >>> every_name_visitor.visit(node) ['BinOp', 'Name', 'Name']