|
23 | 23 | Union, |
24 | 24 | ) |
25 | 25 |
|
26 | | -import wrapt |
27 | | - |
28 | 26 | from astroid import bases, decorators, helpers, nodes, protocols, util |
29 | 27 | from astroid.context import ( |
30 | 28 | CallContext, |
@@ -1037,42 +1035,32 @@ def infer_ifexp(self, context=None): |
1037 | 1035 | nodes.IfExp._infer = infer_ifexp # type: ignore[assignment] |
1038 | 1036 |
|
1039 | 1037 |
|
1040 | | -# pylint: disable=dangerous-default-value |
1041 | | -@wrapt.decorator |
1042 | | -def _cached_generator( |
1043 | | - func, instance: _FunctionDefT, args, kwargs, _cache={} # noqa: B006 |
1044 | | -): |
1045 | | - node = instance |
1046 | | - try: |
1047 | | - return iter(_cache[func, id(node)]) |
1048 | | - except KeyError: |
1049 | | - result = func(*args, **kwargs) |
1050 | | - # Need to keep an iterator around |
1051 | | - original, copy = itertools.tee(result) |
1052 | | - _cache[func, id(node)] = list(copy) |
1053 | | - return original |
1054 | | - |
1055 | | - |
1056 | | -# When inferring a property, we instantiate a new `objects.Property` object, |
1057 | | -# which in turn, because it inherits from `FunctionDef`, sets itself in the locals |
1058 | | -# of the wrapping frame. This means that every time we infer a property, the locals |
1059 | | -# are mutated with a new instance of the property. This is why we cache the result |
1060 | | -# of the function's inference. |
1061 | | -@_cached_generator |
1062 | 1038 | def infer_functiondef( |
1063 | 1039 | self: _FunctionDefT, context: Optional[InferenceContext] = None |
1064 | 1040 | ) -> Generator[Union["Property", _FunctionDefT], None, InferenceErrorInfo]: |
1065 | 1041 | if not self.decorators or not bases._is_property(self): |
1066 | 1042 | yield self |
1067 | 1043 | return InferenceErrorInfo(node=self, context=context) |
1068 | 1044 |
|
| 1045 | + # When inferring a property, we instantiate a new `objects.Property` object, |
| 1046 | + # which in turn, because it inherits from `FunctionDef`, sets itself in the locals |
| 1047 | + # of the wrapping frame. This means that every time we infer a property, the locals |
| 1048 | + # are mutated with a new instance of the property. To avoid this, we detect this |
| 1049 | + # scenario and avoid passing the `parent` argument to the constructor. |
| 1050 | + parent_frame = self.parent.frame(future=True) |
| 1051 | + property_already_in_parent_locals = self.name in parent_frame.locals and any( |
| 1052 | + isinstance(val, objects.Property) for val in parent_frame.locals[self.name] |
| 1053 | + ) |
| 1054 | + |
1069 | 1055 | prop_func = objects.Property( |
1070 | 1056 | function=self, |
1071 | 1057 | name=self.name, |
1072 | 1058 | lineno=self.lineno, |
1073 | | - parent=self.parent, |
| 1059 | + parent=self.parent if not property_already_in_parent_locals else None, |
1074 | 1060 | col_offset=self.col_offset, |
1075 | 1061 | ) |
| 1062 | + if property_already_in_parent_locals: |
| 1063 | + prop_func.parent = self.parent |
1076 | 1064 | prop_func.postinit(body=[], args=self.args, doc_node=self.doc_node) |
1077 | 1065 | yield prop_func |
1078 | 1066 | return InferenceErrorInfo(node=self, context=context) |
|
0 commit comments