From 8136f10e2e71c55384ca5a9d0f1bbc5015b790a5 Mon Sep 17 00:00:00 2001 From: Jimmy Lai Date: Thu, 27 Feb 2020 20:55:41 -0800 Subject: [PATCH] Optimize Scope Analysis memory usage by removing unnecessary Assignment creation --- libcst/metadata/scope_provider.py | 19 +++++++++++++++---- libcst/metadata/tests/test_scope_provider.py | 9 +++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/libcst/metadata/scope_provider.py b/libcst/metadata/scope_provider.py index 794acd97..9109251f 100644 --- a/libcst/metadata/scope_provider.py +++ b/libcst/metadata/scope_provider.py @@ -36,6 +36,8 @@ from libcst.metadata.expression_context_provider import ( ) +@add_slots +@dataclass(frozen=False) class Access: """ An Access records an access of an assignment. @@ -698,10 +700,19 @@ class ScopeVisitor(cst.CSTVisitor): attr.visit(self) return False - def visit_Arg_keyword(self, node: cst.Arg) -> None: - keyword = node.keyword - if keyword is not None: - self.scope.record_assignment(keyword.value, node) + def visit_Arg(self, node: cst.Arg) -> Optional[bool]: + # The keyword of Arg is neither an Assignment nor an Access and we explicitly don't visit it. + for attr in [ + node.value, + node.equal, + node.comma, + node.star, + node.whitespace_after_star, + node.whitespace_after_arg, + ]: + if isinstance(attr, cst.CSTNode): + attr.visit(self) + return False def visit_ClassDef(self, node: cst.ClassDef) -> Optional[bool]: self.scope.record_assignment(node.name.value, node) diff --git a/libcst/metadata/tests/test_scope_provider.py b/libcst/metadata/tests/test_scope_provider.py index 4088d5e9..f90e9b66 100644 --- a/libcst/metadata/tests/test_scope_provider.py +++ b/libcst/metadata/tests/test_scope_provider.py @@ -987,3 +987,12 @@ class ScopeProviderTest(UnitTest): {ensure_type(del_a_b.target, cst.Attribute).value}, ) self.assertEqual(scope["b"], ()) + + def test_keyword_arg_in_call(self) -> None: + m, scopes = get_scope_metadata_provider("call(arg=val)") + call = ensure_type( + ensure_type(m.body[0], cst.SimpleStatementLine).body[0], cst.Expr + ).value + scope = scopes[call] + self.assertIsInstance(scope, GlobalScope) + self.assertEqual(len(scope["arg"]), 0) # no assignment should exist