Skip to content

Commit 2671720

Browse files
authored
Extend rendering of formatted strings in patching (#14)
The patching string needs to consider more cases of the original source code. Namely, we have to join strings together as micropython can not deal with them (``"test" "me"`` needs to be represented as ``"testme"``). So far, we only handled variables (``ast.Name``) and constants (``ast.Constant``), but recently we also have to handle a call and an attribute access (``type(value).__name__``). To that end, we extend the visitor for rendering formatted strings.
1 parent aa431e1 commit 2671720

File tree

1 file changed

+69
-2
lines changed

1 file changed

+69
-2
lines changed

dev_scripts/patch_aas_core_python.py

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,10 @@ def visit_JoinedStr(self, node):
665665
class _RendererInFormattedValue(ast.NodeVisitor):
666666
"""Render the formatted values in a joined string."""
667667

668+
# NOTE (mristin):
669+
# This class is needed since atok does not hold the strings corresponding to
670+
# the nodes of values in joined strings. Hence, we have to render them ourselves.
671+
668672
# pylint: disable=missing-function-docstring
669673

670674
def __init__(self) -> None:
@@ -683,6 +687,47 @@ def visit_Name(self, node):
683687
def visit_Constant(self, node):
684688
return self._writer.write(repr(node))
685689

690+
def visit_Attribute(self, node):
691+
assert isinstance(node, ast.Attribute)
692+
693+
needs_parentheses = not isinstance(
694+
node.value, (ast.Call, ast.Attribute, ast.Name)
695+
)
696+
697+
if needs_parentheses:
698+
self._writer.write("(")
699+
700+
self.visit(node.value)
701+
702+
if needs_parentheses:
703+
self._writer.write(")")
704+
705+
self._writer.write(f".{node.attr}")
706+
707+
def visit_Call(self, node):
708+
assert isinstance(node, ast.Call)
709+
710+
needs_parentheses = not isinstance(
711+
node.func, (ast.Call, ast.Attribute, ast.Name)
712+
)
713+
714+
if needs_parentheses:
715+
self._writer.write("(")
716+
717+
self.visit(node.func)
718+
self._writer.write("(")
719+
720+
for i, arg in enumerate(node.args):
721+
if i > 0:
722+
self._writer.write(", ")
723+
724+
self.visit(arg)
725+
726+
self._writer.write(")")
727+
728+
if needs_parentheses:
729+
self._writer.write(")")
730+
686731
def generic_visit(self, node):
687732
raise NotImplementedError(
688733
f"Unhandled node in a FormattedValue: {ast.dump(node)}"
@@ -708,7 +753,6 @@ def _join_joined_strs(node: ast.JoinedStr, atok: asttokens.ASTTokens) -> str:
708753
elif isinstance(value, ast.FormattedValue):
709754
renderer = _RendererInFormattedValue()
710755
renderer.visit(value.value)
711-
712756
parts.append("{")
713757
parts.append(renderer.get_text())
714758
parts.append("}")
@@ -730,7 +774,30 @@ def _join_strings(node: ast.Constant) -> str:
730774

731775

732776
def _join_strings_and_joined_strings(text: str) -> str:
733-
"""Join the strings since Micropython does not support consecutive literals."""
777+
"""
778+
Join the strings since Micropython does not support consecutive literals.
779+
780+
>>> _join_strings_and_joined_strings('"testme"')
781+
'"testme"'
782+
783+
>>> _join_strings_and_joined_strings('"test" "me"')
784+
"'testme'"
785+
786+
>>> _join_strings_and_joined_strings('f"test{x}me"')
787+
'f"test{x}me"'
788+
789+
>>> _join_strings_and_joined_strings('f"test{x}" f"{y}me"')
790+
'f"test{x}{y}me"'
791+
792+
>>> _join_strings_and_joined_strings('f"test{x.y}" f"me"')
793+
'f"test{x.y}me"'
794+
795+
>>> _join_strings_and_joined_strings('f"test{x()}" f"me"')
796+
'f"test{x()}me"'
797+
798+
>>> _join_strings_and_joined_strings('f"test{type(variable).__name__}" f"me"')
799+
'f"test{type(variable).__name__}me"'
800+
"""
734801
atok = asttokens.ASTTokens(text, parse=True)
735802
assert isinstance(atok.tree, ast.Module)
736803

0 commit comments

Comments
 (0)