From 46cdb676c8a8e919b9d56ed7f691dda71e0f2e98 Mon Sep 17 00:00:00 2001 From: climbus Date: Sat, 25 Sep 2021 14:54:07 +0200 Subject: [PATCH 01/14] simple scope for list comprehension --- rope/base/pyobjects.py | 4 ++++ rope/base/pyobjectsdef.py | 32 +++++++++++++++++++++++++++++--- rope/base/pyscopes.py | 27 +++++++++++++++++++++++++++ ropetest/pyscopestest.py | 18 +++++++++++++++++- 4 files changed, 77 insertions(+), 4 deletions(-) diff --git a/rope/base/pyobjects.py b/rope/base/pyobjects.py index 912d79825..8c2caedf6 100644 --- a/rope/base/pyobjects.py +++ b/rope/base/pyobjects.py @@ -254,6 +254,10 @@ class PyFunction(PyDefinedObject, AbstractFunction): """Only a placeholder""" +class PyComp(PyDefinedObject, PyObject): + """Only a placeholder""" + + class PyClass(PyDefinedObject, AbstractClass): """Only a placeholder""" diff --git a/rope/base/pyobjectsdef.py b/rope/base/pyobjectsdef.py index 22946ebce..f66a05a16 100644 --- a/rope/base/pyobjectsdef.py +++ b/rope/base/pyobjectsdef.py @@ -112,6 +112,18 @@ def decorators(self): return getattr(self.ast_node, 'decorators', None) +class ListComp(pyobjects.PyComp): + def __init__(self, pycore, ast_node, parent): + self.visitor_class = _ScopeVisitor + rope.base.pyobjects.PyObject.__init__(self, type_="Comp") + rope.base.pyobjects.PyDefinedObject.__init__( + self, pycore, ast_node, parent) + + def _create_scope(self): + return rope.base.pyscopes.CompScope(self.pycore, self, + _CompVisitor) + + class PyClass(pyobjects.PyClass): def __init__(self, pycore, ast_node, parent): @@ -338,12 +350,16 @@ def _GeneratorExp(self, node): for if_ in comp.ifs: ast.walk(if_, self) - def _ListComp(self, node): - self._GeneratorExp(node) - def _SetComp(self, node): self._GeneratorExp(node) + def _ListComp(self, node): + if hasattr(self, "scope_visitor"): + list_comp = ListComp(self.scope_visitor.pycore, node, self.scope_visitor.owner_object) + self.scope_visitor.defineds.append(list_comp) + else: + self._GeneratorExp(node) + def _DictComp(self, node): self._GeneratorExp(node) @@ -390,6 +406,16 @@ def _Subscript(self, node): def _Slice(self, node): pass +class _CompVisitor(_ExpressionVisitor): + + def __init__(self, pycore, owner_object): + self.pycore = pycore + self.owner_object = owner_object + self.names = {} + self.defineds = [] + + def _comprehension(self, node): + self.names[node.target.id] = node.target class _ScopeVisitor(_ExpressionVisitor): diff --git a/rope/base/pyscopes.py b/rope/base/pyscopes.py index 0bed19a92..76ad297a8 100644 --- a/rope/base/pyscopes.py +++ b/rope/base/pyscopes.py @@ -144,6 +144,33 @@ def builtin_names(self): return rope.base.builtins.builtins.get_attributes() +class CompScope(Scope): + + def __init__(self, pycore, pyobject, visitor): + super(CompScope, self).__init__(pycore, pyobject, + pyobject.parent.get_scope()) + self.names = None + self.returned_asts = None + self.defineds = None + self.visitor = visitor + + def _get_names(self): + if self.names is None: + self._visit_comp() + return self.names + + def get_names(self): + return self._get_names() + + def _visit_comp(self): + if self.names is None: + new_visitor = self.visitor(self.pycore, self.pyobject) + for n in ast.get_child_nodes(self.pyobject.get_ast()): + ast.walk(n, new_visitor) + self.names = new_visitor.names + self.defineds = new_visitor.defineds + + class FunctionScope(Scope): def __init__(self, pycore, pyobject, visitor): diff --git a/ropetest/pyscopestest.py b/ropetest/pyscopestest.py index 05e1ec56f..ae92cd837 100644 --- a/ropetest/pyscopestest.py +++ b/ropetest/pyscopestest.py @@ -49,7 +49,7 @@ def test_list_comprehension_scope_inside_assignment(self): self.project, 'a_var = [b_var + d_var for b_var, c_var in e_var]\n') self.assertEqual( list(sorted(scope.get_defined_names())), - ['a_var', 'b_var', 'c_var'], + ['a_var'], ) def test_list_comprehension_scope(self): @@ -315,6 +315,22 @@ def test_getting_defined_names_for_modules(self): self.assertTrue('open' not in scope.get_defined_names()) self.assertTrue('A' in scope.get_defined_names()) + def test_get_inner_scope_for_list_comprhension(self): + scope = libutils.get_string_scope( + self.project, + 'a = [i for i in range(10)]\n') + self.assertGreater(len(scope.get_scopes()), 0) + self.assertNotIn("i", scope) + self.assertIn("i", scope.get_scopes()[0]) + + def test_funnc(self): + scope = libutils.get_string_scope( + self.project, + 'def funkcja():\n' + ' ala = 1\n') + names = scope.get_scopes()[0] + self.assertIn("ala", names) + def suite(): result = unittest.TestSuite() From 9d55d430754ed654a458d14a96bc5d02ef9f8636 Mon Sep 17 00:00:00 2001 From: climbus Date: Sun, 26 Sep 2021 09:41:45 +0200 Subject: [PATCH 02/14] comprehension scope with many targets --- rope/base/pyobjectsdef.py | 18 +++++++++++------- rope/base/pyscopes.py | 12 +++++++++--- ropetest/pyscopestest.py | 11 +++++++---- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/rope/base/pyobjectsdef.py b/rope/base/pyobjectsdef.py index 61ab86047..97574e800 100644 --- a/rope/base/pyobjectsdef.py +++ b/rope/base/pyobjectsdef.py @@ -125,12 +125,10 @@ class ListComp(pyobjects.PyComp): def __init__(self, pycore, ast_node, parent): self.visitor_class = _ScopeVisitor rope.base.pyobjects.PyObject.__init__(self, type_="Comp") - rope.base.pyobjects.PyDefinedObject.__init__( - self, pycore, ast_node, parent) + rope.base.pyobjects.PyDefinedObject.__init__(self, pycore, ast_node, parent) def _create_scope(self): - return rope.base.pyscopes.CompScope(self.pycore, self, - _CompVisitor) + return rope.base.pyscopes.CompScope(self.pycore, self, _CompVisitor) class PyClass(pyobjects.PyClass): @@ -359,7 +357,9 @@ def _SetComp(self, node): def _ListComp(self, node): if hasattr(self, "scope_visitor"): - list_comp = ListComp(self.scope_visitor.pycore, node, self.scope_visitor.owner_object) + list_comp = ListComp( + self.scope_visitor.pycore, node, self.scope_visitor.owner_object + ) self.scope_visitor.defineds.append(list_comp) else: self._GeneratorExp(node) @@ -409,8 +409,8 @@ def _Subscript(self, node): def _Slice(self, node): pass -class _CompVisitor(_ExpressionVisitor): +class _CompVisitor(_ExpressionVisitor): def __init__(self, pycore, owner_object): self.pycore = pycore self.owner_object = owner_object @@ -418,7 +418,11 @@ def __init__(self, pycore, owner_object): self.defineds = [] def _comprehension(self, node): - self.names[node.target.id] = node.target + if isinstance(node.target, ast.Tuple): + self.names.update({target.id: target for target in node.target.elts}) + else: + self.names[node.target.id] = node.target + class _ScopeVisitor(_ExpressionVisitor): def __init__(self, pycore, owner_object): diff --git a/rope/base/pyscopes.py b/rope/base/pyscopes.py index 778569a12..d0ad29681 100644 --- a/rope/base/pyscopes.py +++ b/rope/base/pyscopes.py @@ -144,10 +144,8 @@ def builtin_names(self): class CompScope(Scope): - def __init__(self, pycore, pyobject, visitor): - super(CompScope, self).__init__(pycore, pyobject, - pyobject.parent.get_scope()) + super(CompScope, self).__init__(pycore, pyobject, pyobject.parent.get_scope()) self.names = None self.returned_asts = None self.defineds = None @@ -169,6 +167,14 @@ def _visit_comp(self): self.names = new_visitor.names self.defineds = new_visitor.defineds + def get_logical_end(self): + return self.get_start() + + logical_end = property(get_logical_end) + + def get_body_start(self): + return self.get_start() + class FunctionScope(Scope): def __init__(self, pycore, pyobject, visitor): diff --git a/ropetest/pyscopestest.py b/ropetest/pyscopestest.py index 2452b554f..413f77acd 100644 --- a/ropetest/pyscopestest.py +++ b/ropetest/pyscopestest.py @@ -337,12 +337,15 @@ def test_get_inner_scope_for_list_comprhension(self): self.assertNotIn("i", scope) self.assertIn("i", scope.get_scopes()[0]) - def test_funnc(self): + def test_get_inner_scope_for_list_comprhension_with_many_targets(self): scope = libutils.get_string_scope( - self.project, "def funkcja():\n" " ala = 1\n" + self.project, "a = [(i, j) for i,j in enumerate(range(10))]\n" ) - names = scope.get_scopes()[0] - self.assertIn("ala", names) + self.assertGreater(len(scope.get_scopes()), 0) + self.assertNotIn("i", scope) + self.assertNotIn("j", scope) + self.assertIn("i", scope.get_scopes()[0]) + self.assertIn("j", scope.get_scopes()[0]) def suite(): From c4f3170ed3302c6aaefe24f193409f23c55fc048 Mon Sep 17 00:00:00 2001 From: climbus Date: Sun, 26 Sep 2021 15:10:58 +0200 Subject: [PATCH 03/14] all test pass with comprehension scope --- rope/base/pyobjectsdef.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/rope/base/pyobjectsdef.py b/rope/base/pyobjectsdef.py index 97574e800..c0597eaff 100644 --- a/rope/base/pyobjectsdef.py +++ b/rope/base/pyobjectsdef.py @@ -1,3 +1,4 @@ +from rope.base.pynames import DefinedName import rope.base.builtins import rope.base.codeanalyze import rope.base.evaluate @@ -419,9 +420,17 @@ def __init__(self, pycore, owner_object): def _comprehension(self, node): if isinstance(node.target, ast.Tuple): - self.names.update({target.id: target for target in node.target.elts}) + self.names.update( + { + target.id: DefinedName(self._get_pyobject(node)) + for target in node.target.elts + } + ) else: - self.names[node.target.id] = node.target + self.names[node.target.id] = DefinedName(self._get_pyobject(node)) + + def _get_pyobject(self, node): + return pyobjects.PyDefinedObject(None, node.target, self.owner_object) class _ScopeVisitor(_ExpressionVisitor): From 7b210473fb8f38a152f6fc484d89635bca2acf61 Mon Sep 17 00:00:00 2001 From: climbus Date: Sun, 26 Sep 2021 19:55:37 +0200 Subject: [PATCH 04/14] changed some names and scopes for set, dict, gens and nested scopes --- rope/base/pyobjects.py | 2 +- rope/base/pyobjectsdef.py | 46 ++++++++++++++++++++++----------------- rope/base/pyscopes.py | 15 ++++++++----- ropetest/pyscopestest.py | 37 +++++++++++++++++++++++++++++-- 4 files changed, 71 insertions(+), 29 deletions(-) diff --git a/rope/base/pyobjects.py b/rope/base/pyobjects.py index 182fad644..8f0e03e19 100644 --- a/rope/base/pyobjects.py +++ b/rope/base/pyobjects.py @@ -247,7 +247,7 @@ class PyFunction(PyDefinedObject, AbstractFunction): """Only a placeholder""" -class PyComp(PyDefinedObject, PyObject): +class PyComprehension(PyDefinedObject, PyObject): """Only a placeholder""" diff --git a/rope/base/pyobjectsdef.py b/rope/base/pyobjectsdef.py index c0597eaff..593a01aed 100644 --- a/rope/base/pyobjectsdef.py +++ b/rope/base/pyobjectsdef.py @@ -122,14 +122,16 @@ def decorators(self): return getattr(self.ast_node, "decorators", None) -class ListComp(pyobjects.PyComp): +class PyComprehension(pyobjects.PyComprehension): def __init__(self, pycore, ast_node, parent): - self.visitor_class = _ScopeVisitor + self.visitor_class = _ComprehensionVisitor rope.base.pyobjects.PyObject.__init__(self, type_="Comp") rope.base.pyobjects.PyDefinedObject.__init__(self, pycore, ast_node, parent) def _create_scope(self): - return rope.base.pyscopes.CompScope(self.pycore, self, _CompVisitor) + return rope.base.pyscopes.ComprehensionScope( + self.pycore, self, _ComprehensionVisitor + ) class PyClass(pyobjects.PyClass): @@ -344,26 +346,26 @@ def _assigned(self, name, assignment=None): self.scope_visitor._assigned(name, assignment) def _GeneratorExp(self, node): - for child in ["elt", "key", "value"]: - if hasattr(node, child): - ast.walk(getattr(node, child), self) - for comp in node.generators: - ast.walk(comp.target, _AssignVisitor(self)) - ast.walk(comp, self) - for if_ in comp.ifs: - ast.walk(if_, self) - - def _SetComp(self, node): - self._GeneratorExp(node) - - def _ListComp(self, node): if hasattr(self, "scope_visitor"): - list_comp = ListComp( + list_comp = PyComprehension( self.scope_visitor.pycore, node, self.scope_visitor.owner_object ) self.scope_visitor.defineds.append(list_comp) else: - self._GeneratorExp(node) + for child in ["elt", "key", "value"]: + if hasattr(node, child): + ast.walk(getattr(node, child), self) + for comp in node.generators: + ast.walk(comp.target, _AssignVisitor(self)) + ast.walk(comp, self) + for if_ in comp.ifs: + ast.walk(if_, self) + + def _SetComp(self, node): + self._GeneratorExp(node) + + def _ListComp(self, node): + self._GeneratorExp(node) def _DictComp(self, node): self._GeneratorExp(node) @@ -411,10 +413,10 @@ def _Slice(self, node): pass -class _CompVisitor(_ExpressionVisitor): +class _ComprehensionVisitor(_ExpressionVisitor): def __init__(self, pycore, owner_object): - self.pycore = pycore self.owner_object = owner_object + self.pycore = pycore self.names = {} self.defineds = [] @@ -432,6 +434,10 @@ def _comprehension(self, node): def _get_pyobject(self, node): return pyobjects.PyDefinedObject(None, node.target, self.owner_object) + def _GeneratorExp(self, node): + list_comp = PyComprehension(self.pycore, node, self.owner_object) + self.defineds.append(list_comp) + class _ScopeVisitor(_ExpressionVisitor): def __init__(self, pycore, owner_object): diff --git a/rope/base/pyscopes.py b/rope/base/pyscopes.py index d0ad29681..83c644743 100644 --- a/rope/base/pyscopes.py +++ b/rope/base/pyscopes.py @@ -143,9 +143,11 @@ def builtin_names(self): return rope.base.builtins.builtins.get_attributes() -class CompScope(Scope): +class ComprehensionScope(Scope): def __init__(self, pycore, pyobject, visitor): - super(CompScope, self).__init__(pycore, pyobject, pyobject.parent.get_scope()) + super(ComprehensionScope, self).__init__( + pycore, pyobject, pyobject.parent.get_scope() + ) self.names = None self.returned_asts = None self.defineds = None @@ -153,18 +155,19 @@ def __init__(self, pycore, pyobject, visitor): def _get_names(self): if self.names is None: - self._visit_comp() + self._visit_comprehension() return self.names def get_names(self): return self._get_names() - def _visit_comp(self): + def _visit_comprehension(self): if self.names is None: new_visitor = self.visitor(self.pycore, self.pyobject) - for n in ast.get_child_nodes(self.pyobject.get_ast()): - ast.walk(n, new_visitor) + for node in ast.get_child_nodes(self.pyobject.get_ast()): + ast.walk(node, new_visitor) self.names = new_visitor.names + self.names.update(self.parent.get_names()) self.defineds = new_visitor.defineds def get_logical_end(self): diff --git a/ropetest/pyscopestest.py b/ropetest/pyscopestest.py index 413f77acd..411240fd2 100644 --- a/ropetest/pyscopestest.py +++ b/ropetest/pyscopestest.py @@ -333,7 +333,7 @@ def test_getting_defined_names_for_modules(self): def test_get_inner_scope_for_list_comprhension(self): scope = libutils.get_string_scope(self.project, "a = [i for i in range(10)]\n") - self.assertGreater(len(scope.get_scopes()), 0) + self.assertEqual(len(scope.get_scopes()), 1) self.assertNotIn("i", scope) self.assertIn("i", scope.get_scopes()[0]) @@ -341,12 +341,45 @@ def test_get_inner_scope_for_list_comprhension_with_many_targets(self): scope = libutils.get_string_scope( self.project, "a = [(i, j) for i,j in enumerate(range(10))]\n" ) - self.assertGreater(len(scope.get_scopes()), 0) + self.assertEqual(len(scope.get_scopes()), 1) self.assertNotIn("i", scope) self.assertNotIn("j", scope) self.assertIn("i", scope.get_scopes()[0]) self.assertIn("j", scope.get_scopes()[0]) + def test_get_inner_scope_for_generator(self): + scope = libutils.get_string_scope(self.project, "a = (i for i in range(10))\n") + self.assertEqual(len(scope.get_scopes()), 1) + self.assertNotIn("i", scope) + self.assertIn("i", scope.get_scopes()[0]) + + def test_get_inner_scope_for_set_comprehension(self): + scope = libutils.get_string_scope(self.project, "a = {i for i in range(10)}\n") + self.assertEqual(len(scope.get_scopes()), 1) + self.assertNotIn("i", scope) + self.assertIn("i", scope.get_scopes()[0]) + + def test_get_inner_scope_for_dict_comprehension(self): + scope = libutils.get_string_scope( + self.project, "a = {i:i for i in range(10)}\n" + ) + self.assertEqual(len(scope.get_scopes()), 1) + self.assertNotIn("i", scope) + self.assertIn("i", scope.get_scopes()[0]) + + def test_get_inner_scope_for_nested_list_comprhension(self): + scope = libutils.get_string_scope( + self.project, "a = [[i + j for j in range(10)] for i in range(10)]\n" + ) + + self.assertEqual(len(scope.get_scopes()), 1) + self.assertNotIn("i", scope) + self.assertNotIn("j", scope) + self.assertIn("i", scope.get_scopes()[0]) + self.assertEqual(len(scope.get_scopes()[0].get_scopes()), 1) + self.assertIn("j", scope.get_scopes()[0].get_scopes()[0]) + self.assertIn("i", scope.get_scopes()[0].get_scopes()[0]) + def suite(): result = unittest.TestSuite() From 39f9feef99867d0879e56eff121a2279a44655f5 Mon Sep 17 00:00:00 2001 From: climbus Date: Mon, 27 Sep 2021 13:03:17 +0200 Subject: [PATCH 05/14] all tests works --- rope/base/pyobjectsdef.py | 90 +++++++++++++++++++++------------------ ropetest/pyscopestest.py | 30 +++++++++---- 2 files changed, 71 insertions(+), 49 deletions(-) diff --git a/rope/base/pyobjectsdef.py b/rope/base/pyobjectsdef.py index 593a01aed..4e4f0bba8 100644 --- a/rope/base/pyobjectsdef.py +++ b/rope/base/pyobjectsdef.py @@ -346,20 +346,10 @@ def _assigned(self, name, assignment=None): self.scope_visitor._assigned(name, assignment) def _GeneratorExp(self, node): - if hasattr(self, "scope_visitor"): - list_comp = PyComprehension( - self.scope_visitor.pycore, node, self.scope_visitor.owner_object - ) - self.scope_visitor.defineds.append(list_comp) - else: - for child in ["elt", "key", "value"]: - if hasattr(node, child): - ast.walk(getattr(node, child), self) - for comp in node.generators: - ast.walk(comp.target, _AssignVisitor(self)) - ast.walk(comp, self) - for if_ in comp.ifs: - ast.walk(if_, self) + list_comp = PyComprehension( + self.scope_visitor.pycore, node, self.scope_visitor.owner_object + ) + self.scope_visitor.defineds.append(list_comp) def _SetComp(self, node): self._GeneratorExp(node) @@ -413,33 +403,7 @@ def _Slice(self, node): pass -class _ComprehensionVisitor(_ExpressionVisitor): - def __init__(self, pycore, owner_object): - self.owner_object = owner_object - self.pycore = pycore - self.names = {} - self.defineds = [] - - def _comprehension(self, node): - if isinstance(node.target, ast.Tuple): - self.names.update( - { - target.id: DefinedName(self._get_pyobject(node)) - for target in node.target.elts - } - ) - else: - self.names[node.target.id] = DefinedName(self._get_pyobject(node)) - - def _get_pyobject(self, node): - return pyobjects.PyDefinedObject(None, node.target, self.owner_object) - - def _GeneratorExp(self, node): - list_comp = PyComprehension(self.pycore, node, self.owner_object) - self.defineds.append(list_comp) - - -class _ScopeVisitor(_ExpressionVisitor): +class _ScopeVisitor(object): def __init__(self, pycore, owner_object): self.pycore = pycore self.owner_object = owner_object @@ -606,6 +570,50 @@ def _Global(self, node): pyname = pynames.AssignedName(node.lineno) self.names[name] = pyname + def _GeneratorExp(self, node): + list_comp = PyComprehension(self.pycore, node, self.owner_object) + self.defineds.append(list_comp) + + def _SetComp(self, node): + self._GeneratorExp(node) + + def _ListComp(self, node): + self._GeneratorExp(node) + + def _DictComp(self, node): + self._GeneratorExp(node) + + def _NamedExpr(self, node): + ast.walk(node.target, _AssignVisitor(self)) + ast.walk(node.value, self) + + +class _ComprehensionVisitor(_ScopeVisitor): + def __init__(self, pycore, owner_object): + self.owner_object = owner_object + self.pycore = pycore + self.names = {} + self.defineds = [] + + def _comprehension(self, node): + if isinstance(node.target, ast.Tuple): + self.names.update( + { + target.id: DefinedName(self._get_pyobject(node)) + for target in node.target.elts + } + ) + else: + self.names[node.target.id] = DefinedName(self._get_pyobject(node)) + ast.walk(node.iter, self) + + def _get_pyobject(self, node): + return pyobjects.PyDefinedObject(None, node.target, self.owner_object) + + def _GeneratorExp(self, node): + list_comp = PyComprehension(self.pycore, node, self.owner_object) + self.defineds.append(list_comp) + class _GlobalVisitor(_ScopeVisitor): def __init__(self, pycore, owner_object): diff --git a/ropetest/pyscopestest.py b/ropetest/pyscopestest.py index 411240fd2..5f6f80430 100644 --- a/ropetest/pyscopestest.py +++ b/ropetest/pyscopestest.py @@ -60,7 +60,7 @@ def test_list_comprehension_scope(self): self.project, "[b_var + d_var for b_var, c_var in e_var]\n" ) self.assertEqual( - list(sorted(scope.get_defined_names())), + list(sorted(scope.get_scopes()[0].get_defined_names())), ["b_var", "c_var"], ) @@ -69,7 +69,7 @@ def test_set_comprehension_scope(self): self.project, "{b_var + d_var for b_var, c_var in e_var}\n" ) self.assertEqual( - list(sorted(scope.get_defined_names())), + list(sorted(scope.get_scopes()[0].get_defined_names())), ["b_var", "c_var"], ) @@ -78,7 +78,7 @@ def test_generator_comprehension_scope(self): self.project, "(b_var + d_var for b_var, c_var in e_var)\n" ) self.assertEqual( - list(sorted(scope.get_defined_names())), + list(sorted(scope.get_scopes()[0].get_defined_names())), ["b_var", "c_var"], ) @@ -87,7 +87,7 @@ def test_dict_comprehension_scope(self): self.project, "{b_var: d_var for b_var, c_var in e_var}\n" ) self.assertEqual( - list(sorted(scope.get_defined_names())), + list(sorted(scope.get_scopes()[0].get_defined_names())), ["b_var", "c_var"], ) @@ -102,8 +102,12 @@ def test_inline_assignment_in_comprehensions(self): ]""", ) self.assertEqual( - list(sorted(scope.get_defined_names())), - ["a_var", "b_var", "f_var", "h_var", "i_var", "j_var"], + list(sorted(scope.get_scopes()[0].get_defined_names())), + ["a_var", "b_var", "f_var"], + ) + self.assertEqual( + list(sorted(scope.get_scopes()[0].get_scopes()[0].get_defined_names())), + ["i_var", "j_var"], ) def test_nested_comprehension(self): @@ -116,8 +120,12 @@ def test_nested_comprehension(self): ]\n""", ) self.assertEqual( - list(sorted(scope.get_defined_names())), - ["b_var", "c_var", "e_var"], + list(sorted(scope.get_scopes()[0].get_defined_names())), + ["b_var", "c_var"], + ) + self.assertEqual( + list(sorted(scope.get_scopes()[0].get_scopes()[0].get_defined_names())), + ["e_var"], ) def test_simple_class_scope(self): @@ -332,6 +340,12 @@ def test_getting_defined_names_for_modules(self): self.assertTrue("A" in scope.get_defined_names()) def test_get_inner_scope_for_list_comprhension(self): + scope = libutils.get_string_scope(self.project, "[i for i in range(10)]\n") + self.assertEqual(len(scope.get_scopes()), 1) + self.assertNotIn("i", scope) + self.assertIn("i", scope.get_scopes()[0]) + + def test_get_inner_scope_for_list_comprhension_in_assingment(self): scope = libutils.get_string_scope(self.project, "a = [i for i in range(10)]\n") self.assertEqual(len(scope.get_scopes()), 1) self.assertNotIn("i", scope) From 400a941d40557c5e25453fd224e508503c9edb96 Mon Sep 17 00:00:00 2001 From: climbus Date: Mon, 27 Sep 2021 14:54:48 +0200 Subject: [PATCH 06/14] ExpressionVisitor inside ScopeVisitor --- rope/base/pyobjectsdef.py | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/rope/base/pyobjectsdef.py b/rope/base/pyobjectsdef.py index 4e4f0bba8..f40950b3e 100644 --- a/rope/base/pyobjectsdef.py +++ b/rope/base/pyobjectsdef.py @@ -403,8 +403,10 @@ def _Slice(self, node): pass -class _ScopeVisitor(object): +class _ScopeVisitor(_ExpressionVisitor): def __init__(self, pycore, owner_object): + _ExpressionVisitor.__init__(self, scope_visitor=self) + self.scope_visitor = self self.pycore = pycore self.owner_object = owner_object self.names = {} @@ -570,31 +572,8 @@ def _Global(self, node): pyname = pynames.AssignedName(node.lineno) self.names[name] = pyname - def _GeneratorExp(self, node): - list_comp = PyComprehension(self.pycore, node, self.owner_object) - self.defineds.append(list_comp) - - def _SetComp(self, node): - self._GeneratorExp(node) - - def _ListComp(self, node): - self._GeneratorExp(node) - - def _DictComp(self, node): - self._GeneratorExp(node) - - def _NamedExpr(self, node): - ast.walk(node.target, _AssignVisitor(self)) - ast.walk(node.value, self) - class _ComprehensionVisitor(_ScopeVisitor): - def __init__(self, pycore, owner_object): - self.owner_object = owner_object - self.pycore = pycore - self.names = {} - self.defineds = [] - def _comprehension(self, node): if isinstance(node.target, ast.Tuple): self.names.update( @@ -610,10 +589,6 @@ def _comprehension(self, node): def _get_pyobject(self, node): return pyobjects.PyDefinedObject(None, node.target, self.owner_object) - def _GeneratorExp(self, node): - list_comp = PyComprehension(self.pycore, node, self.owner_object) - self.defineds.append(list_comp) - class _GlobalVisitor(_ScopeVisitor): def __init__(self, pycore, owner_object): From 75383e53cfb8c9e2da226bf3aa681f1876de45eb Mon Sep 17 00:00:00 2001 From: climbus Date: Mon, 27 Sep 2021 15:20:09 +0200 Subject: [PATCH 07/14] Simplified ComprehensionVisitor --- rope/base/pyobjectsdef.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/rope/base/pyobjectsdef.py b/rope/base/pyobjectsdef.py index f40950b3e..5ce217205 100644 --- a/rope/base/pyobjectsdef.py +++ b/rope/base/pyobjectsdef.py @@ -342,9 +342,6 @@ class _ExpressionVisitor(object): def __init__(self, scope_visitor): self.scope_visitor = scope_visitor - def _assigned(self, name, assignment=None): - self.scope_visitor._assigned(name, assignment) - def _GeneratorExp(self, node): list_comp = PyComprehension( self.scope_visitor.pycore, node, self.scope_visitor.owner_object @@ -575,19 +572,15 @@ def _Global(self, node): class _ComprehensionVisitor(_ScopeVisitor): def _comprehension(self, node): - if isinstance(node.target, ast.Tuple): - self.names.update( - { - target.id: DefinedName(self._get_pyobject(node)) - for target in node.target.elts - } - ) - else: - self.names[node.target.id] = DefinedName(self._get_pyobject(node)) + ast.walk(node.target, self) ast.walk(node.iter, self) + def _Name(self, node): + if isinstance(node.ctx, ast.Store): + self.names[node.id] = DefinedName(self._get_pyobject(node)) + def _get_pyobject(self, node): - return pyobjects.PyDefinedObject(None, node.target, self.owner_object) + return pyobjects.PyDefinedObject(None, node, self.owner_object) class _GlobalVisitor(_ScopeVisitor): From 938098aef9a92940908173ceb04ddbca31065ac8 Mon Sep 17 00:00:00 2001 From: climbus Date: Mon, 27 Sep 2021 15:30:31 +0200 Subject: [PATCH 08/14] Added condition on node.body --- rope/base/utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rope/base/utils/__init__.py b/rope/base/utils/__init__.py index 884d71c08..2db623357 100644 --- a/rope/base/utils/__init__.py +++ b/rope/base/utils/__init__.py @@ -136,7 +136,7 @@ def is_inline_body(): body_col_offset = node.body[0].col_offset return indent_col_offset < body_col_offset - if sys.version_info >= (3, 8): + if sys.version_info >= (3, 8) or not hasattr(node, "body"): return node.lineno possible_def_line = ( From 25211ffad969ed4fb9ecaae8a08e669cf2a2afaa Mon Sep 17 00:00:00 2001 From: climbus Date: Mon, 27 Sep 2021 18:47:52 +0200 Subject: [PATCH 09/14] Removed duplicate test --- ropetest/pyscopestest.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/ropetest/pyscopestest.py b/ropetest/pyscopestest.py index 5f6f80430..3db277d65 100644 --- a/ropetest/pyscopestest.py +++ b/ropetest/pyscopestest.py @@ -54,6 +54,10 @@ def test_list_comprehension_scope_inside_assignment(self): list(sorted(scope.get_defined_names())), ["a_var"], ) + self.assertEqual( + list(sorted(scope.get_scopes()[0].get_defined_names())), + ["b_var", "c_var"], + ) def test_list_comprehension_scope(self): scope = libutils.get_string_scope( @@ -339,18 +343,6 @@ def test_getting_defined_names_for_modules(self): self.assertTrue("open" not in scope.get_defined_names()) self.assertTrue("A" in scope.get_defined_names()) - def test_get_inner_scope_for_list_comprhension(self): - scope = libutils.get_string_scope(self.project, "[i for i in range(10)]\n") - self.assertEqual(len(scope.get_scopes()), 1) - self.assertNotIn("i", scope) - self.assertIn("i", scope.get_scopes()[0]) - - def test_get_inner_scope_for_list_comprhension_in_assingment(self): - scope = libutils.get_string_scope(self.project, "a = [i for i in range(10)]\n") - self.assertEqual(len(scope.get_scopes()), 1) - self.assertNotIn("i", scope) - self.assertIn("i", scope.get_scopes()[0]) - def test_get_inner_scope_for_list_comprhension_with_many_targets(self): scope = libutils.get_string_scope( self.project, "a = [(i, j) for i,j in enumerate(range(10))]\n" From ba0ed7f551471c5500daafc7a76de24f34c8de51 Mon Sep 17 00:00:00 2001 From: climbus Date: Mon, 27 Sep 2021 18:53:12 +0200 Subject: [PATCH 10/14] Removed unused property --- rope/base/pyobjectsdef.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rope/base/pyobjectsdef.py b/rope/base/pyobjectsdef.py index 5ce217205..ec26d80a0 100644 --- a/rope/base/pyobjectsdef.py +++ b/rope/base/pyobjectsdef.py @@ -403,7 +403,6 @@ def _Slice(self, node): class _ScopeVisitor(_ExpressionVisitor): def __init__(self, pycore, owner_object): _ExpressionVisitor.__init__(self, scope_visitor=self) - self.scope_visitor = self self.pycore = pycore self.owner_object = owner_object self.names = {} From 248c206a3d9304cf37a4158aac362d953e327cdb Mon Sep 17 00:00:00 2001 From: climbus Date: Mon, 27 Sep 2021 19:08:29 +0200 Subject: [PATCH 11/14] Added to changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf3d5a966..d661bcd79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ ## Bug fixes - #391, #396 Extract method similar no longer replace the left-hand side of assignment - #303 Fix inlining into f-string containing quote characters - +- Added scopes for comprehensions expressions as part of #293 From 118f477a51d04f17282622b8de73407d7ccd4948 Mon Sep 17 00:00:00 2001 From: climbus Date: Mon, 27 Sep 2021 19:14:02 +0200 Subject: [PATCH 12/14] Typo in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d661bcd79..f11924587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ ## Bug fixes - #391, #396 Extract method similar no longer replace the left-hand side of assignment - #303 Fix inlining into f-string containing quote characters -- Added scopes for comprehensions expressions as part of #293 +- Added scopes for comprehension expressions as part of #293 From 5083c9742a4fcc62823f7a7eee75c5633363e552 Mon Sep 17 00:00:00 2001 From: Lie Ryan Date: Wed, 29 Sep 2021 08:21:54 +1000 Subject: [PATCH 13/14] Add credit in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f11924587..12e2e9c30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ ## Bug fixes - #391, #396 Extract method similar no longer replace the left-hand side of assignment - #303 Fix inlining into f-string containing quote characters -- Added scopes for comprehension expressions as part of #293 +- Added scopes for comprehension expressions as part of #293 (@climbus) From c37bdac25e169ce36bf648a754f4336a46302290 Mon Sep 17 00:00:00 2001 From: Lie Ryan Date: Wed, 29 Sep 2021 08:28:27 +1000 Subject: [PATCH 14/14] Remove unittest TestSuite building --- ropetest/pyscopestest.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ropetest/pyscopestest.py b/ropetest/pyscopestest.py index 3db277d65..75a309c4c 100644 --- a/ropetest/pyscopestest.py +++ b/ropetest/pyscopestest.py @@ -385,13 +385,3 @@ def test_get_inner_scope_for_nested_list_comprhension(self): self.assertEqual(len(scope.get_scopes()[0].get_scopes()), 1) self.assertIn("j", scope.get_scopes()[0].get_scopes()[0]) self.assertIn("i", scope.get_scopes()[0].get_scopes()[0]) - - -def suite(): - result = unittest.TestSuite() - result.addTests(unittest.makeSuite(PyCoreScopesTest)) - return result - - -if __name__ == "__main__": - unittest.main()