From bd3f55ec324a413c6f1934c181ec6710a6ba51c6 Mon Sep 17 00:00:00 2001 From: Robin Tweedie Date: Sat, 7 Aug 2021 22:47:07 +0100 Subject: [PATCH 1/6] wip cache ancestors --- astroid/nodes/scoped_nodes.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/astroid/nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes.py index 84fbd09bea..66d7ce815d 100644 --- a/astroid/nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes.py @@ -1962,6 +1962,9 @@ def my_meth(self, arg): # a dictionary of class instances attributes _astroid_fields = ("decorators", "bases", "keywords", "body") # name + all_ancestors = None + direct_ancestors = None + decorators = None """The decorators that are applied to this class. @@ -2331,13 +2334,25 @@ def ancestors(self, recurs=True, context=None): :returns: The base classes :rtype: iterable(NodeNG) """ + if recurs and self.all_ancestors is not None: + for ancestor in self.all_ancestors: + yield ancestor + elif not recurs and self.direct_ancestors is not None: + for ancestor in self.direct_ancestors: + yield ancestor + # FIXME: should be possible to choose the resolution order # FIXME: inference make infinite loops possible here yielded = {self} if context is None: context = contextmod.InferenceContext() if not self.bases and self.qname() != "builtins.object": - yield builtin_lookup("object")[1][0] + result = builtin_lookup("object")[1][0] + if recurs: + self.all_ancestors = {result} + else: + self.direct_ancestors = {result} + yield result return for stmt in self.bases: @@ -2367,6 +2382,12 @@ def ancestors(self, recurs=True, context=None): except InferenceError: continue + if recurs: + self.all_ancestors = yielded + else: + self.direct_ancestors = yielded + + def local_attr_ancestors(self, name, context=None): """Iterate over the parents that define the given name. From 066ad3f76c6a9b899d93cee0124e9a80f7a0a735 Mon Sep 17 00:00:00 2001 From: Robin Tweedie Date: Sat, 7 Aug 2021 23:17:21 +0100 Subject: [PATCH 2/6] keep yielded ancestors in OrderedDict keys --- astroid/nodes/scoped_nodes.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/astroid/nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes.py index 66d7ce815d..2458f99ac8 100644 --- a/astroid/nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes.py @@ -43,6 +43,7 @@ import io import itertools import typing +from collections import OrderedDict from typing import List, Optional from astroid import bases @@ -2335,23 +2336,26 @@ def ancestors(self, recurs=True, context=None): :rtype: iterable(NodeNG) """ if recurs and self.all_ancestors is not None: - for ancestor in self.all_ancestors: + for ancestor in self.all_ancestors.keys(): yield ancestor elif not recurs and self.direct_ancestors is not None: - for ancestor in self.direct_ancestors: + for ancestor in self.direct_ancestors.keys(): yield ancestor # FIXME: should be possible to choose the resolution order # FIXME: inference make infinite loops possible here - yielded = {self} + yielded = OrderedDict() + yielded[self] = None if context is None: context = contextmod.InferenceContext() if not self.bases and self.qname() != "builtins.object": result = builtin_lookup("object")[1][0] + yielded[result] = None + del yielded[self] if recurs: - self.all_ancestors = {result} + self.all_ancestors = yielded else: - self.direct_ancestors = {result} + self.direct_ancestors = yielded yield result return @@ -2367,7 +2371,7 @@ def ancestors(self, recurs=True, context=None): if not baseobj.hide: if baseobj in yielded: continue - yielded.add(baseobj) + yielded[baseobj] = None yield baseobj if not recurs: continue @@ -2377,11 +2381,12 @@ def ancestors(self, recurs=True, context=None): break if grandpa in yielded: continue - yielded.add(grandpa) + yielded[grandpa] = None yield grandpa except InferenceError: continue + del yielded[self] if recurs: self.all_ancestors = yielded else: From b12ca1144cd595ebd5ef4bcd12272995173377cd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Aug 2021 22:19:31 +0000 Subject: [PATCH 3/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- astroid/nodes/scoped_nodes.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/astroid/nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes.py index 2458f99ac8..2c7f66c006 100644 --- a/astroid/nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes.py @@ -2336,11 +2336,9 @@ def ancestors(self, recurs=True, context=None): :rtype: iterable(NodeNG) """ if recurs and self.all_ancestors is not None: - for ancestor in self.all_ancestors.keys(): - yield ancestor + yield from self.all_ancestors.keys() elif not recurs and self.direct_ancestors is not None: - for ancestor in self.direct_ancestors.keys(): - yield ancestor + yield from self.direct_ancestors.keys() # FIXME: should be possible to choose the resolution order # FIXME: inference make infinite loops possible here @@ -2392,7 +2390,6 @@ def ancestors(self, recurs=True, context=None): else: self.direct_ancestors = yielded - def local_attr_ancestors(self, name, context=None): """Iterate over the parents that define the given name. From d102dda19457b535768fe80da8814e5ac3ebf44f Mon Sep 17 00:00:00 2001 From: Robin Tweedie Date: Sat, 7 Aug 2021 23:40:44 +0100 Subject: [PATCH 4/6] cache ancestors by context --- astroid/nodes/scoped_nodes.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/astroid/nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes.py index 2c7f66c006..ea60d6568c 100644 --- a/astroid/nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes.py @@ -43,7 +43,7 @@ import io import itertools import typing -from collections import OrderedDict +from collections import defaultdict, OrderedDict from typing import List, Optional from astroid import bases @@ -1963,8 +1963,8 @@ def my_meth(self, arg): # a dictionary of class instances attributes _astroid_fields = ("decorators", "bases", "keywords", "body") # name - all_ancestors = None - direct_ancestors = None + all_ancestors = {} + direct_ancestors = {} decorators = None """The decorators that are applied to this class. @@ -2335,10 +2335,10 @@ def ancestors(self, recurs=True, context=None): :returns: The base classes :rtype: iterable(NodeNG) """ - if recurs and self.all_ancestors is not None: - yield from self.all_ancestors.keys() - elif not recurs and self.direct_ancestors is not None: - yield from self.direct_ancestors.keys() + if recurs and context in self.all_ancestors: + yield from self.all_ancestors[context].keys() + elif not recurs and context in self.direct_ancestors: + yield from self.direct_ancestors[context].keys() # FIXME: should be possible to choose the resolution order # FIXME: inference make infinite loops possible here @@ -2351,9 +2351,9 @@ def ancestors(self, recurs=True, context=None): yielded[result] = None del yielded[self] if recurs: - self.all_ancestors = yielded + self.all_ancestors[context] = yielded else: - self.direct_ancestors = yielded + self.direct_ancestors[context] = yielded yield result return @@ -2386,9 +2386,9 @@ def ancestors(self, recurs=True, context=None): del yielded[self] if recurs: - self.all_ancestors = yielded + self.all_ancestors[context] = yielded else: - self.direct_ancestors = yielded + self.direct_ancestors[context] = yielded def local_attr_ancestors(self, name, context=None): """Iterate over the parents that define the given name. From ff31cd0cf0970cb8f38374a95a630d879043044e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Aug 2021 22:41:24 +0000 Subject: [PATCH 5/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- astroid/nodes/scoped_nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/astroid/nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes.py index ea60d6568c..9ad5e864ca 100644 --- a/astroid/nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes.py @@ -43,7 +43,7 @@ import io import itertools import typing -from collections import defaultdict, OrderedDict +from collections import OrderedDict from typing import List, Optional from astroid import bases From 826ce5baf480fa4abf09fcc7e3e57ea24a33554c Mon Sep 17 00:00:00 2001 From: Robin Tweedie Date: Sat, 7 Aug 2021 23:50:19 +0100 Subject: [PATCH 6/6] inspect attribute failing assertion --- tests/unittest_scoped_nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unittest_scoped_nodes.py b/tests/unittest_scoped_nodes.py index a868e564d4..3bfe3a1d13 100644 --- a/tests/unittest_scoped_nodes.py +++ b/tests/unittest_scoped_nodes.py @@ -1040,7 +1040,7 @@ class Past(Present): astroid = builder.parse(data) past = astroid["Past"] attr = past.getattr("attr") - self.assertEqual(len(attr), 1) + self.assertEqual(len(attr), 1, attr) attr1 = attr[0] self.assertIsInstance(attr1, nodes.AssignName) self.assertEqual(attr1.name, "attr")