You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm trying to write a transformer that inserts a certain statement before existing statements that contains some code pattern, but I'm hitting a problem with If nodes that represent the elif part of an if statement.
More specifically, I'm trying to insert an additional statement whenever an attribute with a specific name on any object is accessed. The attribute access could be anywhere, inside a simple expression statement, the right side of an assignment, the condition of an if, etc.
Below is a simplified version of the code I'm using. It works well in almost all cases. Shown is the case of the attribute access happening in the condition of an if statement:
fromtextwrapimportdedentfromlibcstimportAttribute, CSTNode, BaseStatement, CSTTransformer, \
FlattenSentinel, parse_module, parse_statementimportlibcst.matchersasmclassTransformer(CSTTransformer):
def__init__(self):
super().__init__()
# Attribute accesses within each nested BaseStatement node.self.attr_accesses_stack: list[list[Attribute]] = []
defon_visit(self, node: CSTNode) ->bool:
ifisinstance(node, BaseStatement):
self.attr_accesses_stack.append([])
ifm.matches(node, m.Attribute(attr=m.Name("special_attribute"))):
self.attr_accesses_stack[-1].append(node)
returnTruedefon_leave(
self, original_node: CSTNode, updated_node: CSTNode
) ->CSTNode|FlattenSentinel[CSTNode]:
ifisinstance(updated_node, BaseStatement):
attr_accesses=self.attr_accesses_stack.pop()
new_nodes= [updated_node]
# The actual code does something more complex with the collected attribute nodes.foriinattr_accesses:
new_nodes.insert(0, parse_statement("print('attribute accessed')"))
returnFlattenSentinel(new_nodes)
returnupdated_nodeexample_code=dedent(
"""\ if x.special_attribute: pass # elif y.special_attribute: # pass """
)
print(parse_module(example_code).visit(Transformer()).code)
The problem arises when the attribute access is in the elif's condition. Uncommenting the two lines in example_code produces the following error:
File [...]/venv/lib/python3.12/site-packages/libcst/_nodes/internal.py:112 in visit_optional
raise TypeError(
TypeError: We got a FlattenSentinel while visiting a If. This node's parent does not allow for it to be it to be replaced with a sequence.
The problem is clear: An If node is used in two distinct cases:
As an item in a list of statements to represent the a whole if statement including any elif and else parts.
To represent an elif part of an if statement. There can only be an If, an Else or no node in that place.
The second case is where my code fails because it returns a FlattenSentinel instance.
My question: What is the best way to handle this case? What I'd like to do is to ignore these If nodes in the traversal so that the attribute accesses are instead collected for the top-level If node of each if statement, i.e. so that the resulting code would look like this:
I'm trying to write a transformer that inserts a certain statement before existing statements that contains some code pattern, but I'm hitting a problem with
If
nodes that represent theelif
part of anif
statement.More specifically, I'm trying to insert an additional statement whenever an attribute with a specific name on any object is accessed. The attribute access could be anywhere, inside a simple expression statement, the right side of an assignment, the condition of an
if
, etc.Below is a simplified version of the code I'm using. It works well in almost all cases. Shown is the case of the attribute access happening in the condition of an
if
statement:Output:
The problem arises when the attribute access is in the
elif
's condition. Uncommenting the two lines inexample_code
produces the following error:The problem is clear: An
If
node is used in two distinct cases:if
statement including anyelif
andelse
parts.elif
part of anif
statement. There can only be anIf
, anElse
or no node in that place.The second case is where my code fails because it returns a
FlattenSentinel
instance.My question: What is the best way to handle this case? What I'd like to do is to ignore these
If
nodes in the traversal so that the attribute accesses are instead collected for the top-levelIf
node of eachif
statement, i.e. so that the resulting code would look like this:Is there any way to do this?
The text was updated successfully, but these errors were encountered: