From 46c53382aa594bd04e1c1b01e34187c11c34d694 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sat, 16 Apr 2022 13:49:49 +1200 Subject: rename codemod: reset cache vars between modules (From my LibCST fork e6b990836204b) The rename codemod currently has a few variables cached on `self` which are used to transfer information between different visitor methods. For example one of the nodes being visited inside an import from statement might cause the import to be skipped in `leave_Import` using `self.bypass_import`. Or in the test case below relevant seen imports are cached in `self.scheduled_removals` so they can be given special attention in `leave_Module`. The lifecycles don't work out though as the transforms that codemods run ass are only initialised once for a whole run. So stuff can leak between modules. There is probably a better place for them, but this works for now. I'm not sure if this is reproducable outside of this wildcard rename support. Here is an example: 1. Have a folder with two test files ==> context_test/1.py <== from a.b import c c.x.foo ==> context_test/2.py <== from a.b.g import d d.bar 2. Rename something from the first file python3 -m libcst.tool codemod rename.RenameCommand context_test/ -j 1 --old_name=a.b.c.* --new_name=a.b:c.* --no-format ; head -n-0 context_test/* 3. Observe a removed import from the first file leaks into the second ==> context_test/1.py <== from a.b import c c.x.foo ==> context_test/2.py <== from a.b.g import d from a.b import c d.bar --- scripts/codemods/rename_pyqt.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/codemods/rename_pyqt.py b/scripts/codemods/rename_pyqt.py index ba20f4f54..e4cf82b0b 100644 --- a/scripts/codemods/rename_pyqt.py +++ b/scripts/codemods/rename_pyqt.py @@ -117,6 +117,13 @@ class RenameCommand(VisitorBasedCodemodCommand): # this so that we do not end up with two of the same import. self.bypass_import = False + def per_module_reset(self): + """Reset attributes that are cached on self.""" + # They should probably really be on self.context.scratch? + self.as_name = None + self.scheduled_removals = set() + self.bypass_import = False + def visit_Import(self, node: cst.Import) -> None: for import_alias in node.names: alias_name = get_full_name_for_node(import_alias.name) @@ -315,6 +322,9 @@ class RenameCommand(VisitorBasedCodemodCommand): return updated_node + def visit_Module(self, node: cst.Module) -> Optional[bool]: + self.per_module_reset() + def leave_Module( self, original_node: cst.Module, updated_node: cst.Module ) -> cst.Module: -- cgit v1.2.3-54-g00ecf