|
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