Fix builtin functions incorrectly exposing descriptor attributes (#2983)
Fix builtin functions incorrectly exposing descriptor attributes
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>
diff --git a/ChangeLog b/ChangeLog
index 8c7a1f7..a6d6335 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -26,6 +26,10 @@
Closes #2628
+* Fix ``FunctionModel`` returning descriptor attributes for builtin functions.
+
+ Closes #2743
+
* Catch ``MemoryError`` when inferring f-strings with extremely large format
widths (e.g. ``f'{0:11111111111}'``) so that inference yields ``Uninferable``
instead of crashing.
diff --git a/astroid/interpreter/objectmodel.py b/astroid/interpreter/objectmodel.py
index 62cc02e..148e7d7 100644
--- a/astroid/interpreter/objectmodel.py
+++ b/astroid/interpreter/objectmodel.py
@@ -272,6 +272,10 @@
class FunctionModel(ObjectModel):
+ def _is_builtin_func(self) -> bool:
+ func = self._instance
+ return isinstance(func.parent, nodes.Module) and not func.root().pure_python
+
@property
def attr___name__(self):
return node_classes.Const(value=self._instance.name, parent=self._instance)
@@ -290,6 +294,8 @@
@property
def attr___defaults__(self):
func = self._instance
+ if self._is_builtin_func():
+ raise AttributeInferenceError(target=func, attribute="__defaults__")
if not func.args.defaults:
return node_classes.Const(value=None, parent=func)
@@ -299,6 +305,10 @@
@property
def attr___annotations__(self):
+ if self._is_builtin_func():
+ raise AttributeInferenceError(
+ target=self._instance, attribute="__annotations__"
+ )
obj = node_classes.Dict(
parent=self._instance,
lineno=self._instance.lineno,
@@ -339,6 +349,8 @@
@property
def attr___dict__(self):
+ if self._is_builtin_func():
+ raise AttributeInferenceError(target=self._instance, attribute="__dict__")
return node_classes.Dict(
parent=self._instance,
lineno=self._instance.lineno,
@@ -347,10 +359,27 @@
end_col_offset=self._instance.end_col_offset,
)
- attr___globals__ = attr___dict__
+ @property
+ def attr___globals__(self):
+ if self._is_builtin_func():
+ raise AttributeInferenceError(
+ target=self._instance, attribute="__globals__"
+ )
+ return node_classes.Dict(
+ parent=self._instance,
+ lineno=self._instance.lineno,
+ col_offset=self._instance.col_offset,
+ end_lineno=self._instance.end_lineno,
+ end_col_offset=self._instance.end_col_offset,
+ )
@property
def attr___kwdefaults__(self):
+ if self._is_builtin_func():
+ raise AttributeInferenceError(
+ target=self._instance, attribute="__kwdefaults__"
+ )
+
def _default_args(args, parent):
for arg in args.kwonlyargs:
try:
@@ -382,6 +411,9 @@
def attr___get__(self):
func = self._instance
+ if self._is_builtin_func():
+ raise AttributeInferenceError(target=func, attribute="__get__")
+
class DescriptorBoundMethod(bases.BoundMethod):
"""Bound method which knows how to understand calling descriptor
binding.
diff --git a/tests/test_object_model.py b/tests/test_object_model.py
index 86298c4..89d27ae 100644
--- a/tests/test_object_model.py
+++ b/tests/test_object_model.py
@@ -998,3 +998,21 @@
thisclass_inferred = next(thisclass_node.infer())
assert isinstance(thisclass_inferred, nodes.ClassDef)
assert thisclass_inferred.name == "Base"
+
+
+@pytest.mark.parametrize(
+ "attr",
+ [
+ "__get__",
+ "__defaults__",
+ "__annotations__",
+ "__dict__",
+ "__globals__",
+ "__kwdefaults__",
+ ],
+)
+def test_builtin_func_no_descriptor_attrs(attr: str) -> None:
+ """Test builtin functions lack descriptor protocol attributes."""
+ node = builder.extract_node(f"eval.{attr}")
+ with pytest.raises(InferenceError):
+ next(node.infer())
diff --git a/tests/test_regrtest.py b/tests/test_regrtest.py
index 26bc452..a207057 100644
--- a/tests/test_regrtest.py
+++ b/tests/test_regrtest.py
@@ -486,9 +486,8 @@
assert node.name == "c"
-@pytest.mark.xfail(reason="Not fixed yet")
def test_regression_eval_get_of_arg() -> None:
- """Regression test for #2743"""
+ """Regression test for #2743."""
node = _extract_single_node("eval.__get__(1)")
with pytest.raises(InferenceError):
next(node.infer())