Skip to content

Commit

Permalink
Update _can_assign_attr to return False for builtins.object (#946)
Browse files Browse the repository at this point in the history
* Add tests of expected behaviour for delayed attr assign

Ref #945

* Update _can_assign_attr to return False for builtins.object

Ref #945, pylint#4232, pylint#3970, pylint#3595. Various interactions
had been previously noticed with the typing/collections modules and
methods named prev/next on objects. This was due to inference setting
these values as instance attributes on the builtin object class due to
a sentinel object and an incorrectly inferred return value in the
OrderedDict definition. This change updates _can_assign_attr (and the
resulting delayed_assattr behaviour) to ignore attempts to assign to
object() since these would fail.

* Fix test definition for MRO inference

* Update changelog
  • Loading branch information
nelfin authored May 11, 2021
1 parent 2108f3a commit cd8b6a4
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 3 deletions.
9 changes: 9 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ What's New in astroid 2.6.0?
============================
Release Date: TBA

* Do not set instance attributes on builtin object()

Closes #945
Closes PyCQA/pylint#4232
Closes PyCQA/pylint#4221
Closes PyCQA/pylint#3970
Closes PyCQA/pylint#3595


What's New in astroid 2.5.6?
============================
Release Date: 2021-04-25
Expand Down
2 changes: 1 addition & 1 deletion astroid/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def _can_assign_attr(node, attrname):
else:
if slots and attrname not in {slot.value for slot in slots}:
return False
return True
return node.qname() != "builtins.object"


class AstroidBuilder(raw_building.InspectBuilder):
Expand Down
49 changes: 48 additions & 1 deletion tests/unittest_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

import pytest

from astroid import builder, exceptions, manager, nodes, test_utils, util
from astroid import Instance, builder, exceptions, manager, nodes, test_utils, util

from . import resources

Expand Down Expand Up @@ -476,6 +476,53 @@ def A_assign_type(self):
self.assertIn("assign_type", lclass.locals)
self.assertIn("type", lclass.locals)

def test_infer_can_assign_regular_object(self):
mod = builder.parse(
"""
class A:
pass
a = A()
a.value = "is set"
a.other = "is set"
"""
)
obj = list(mod.igetattr("a"))
self.assertEqual(len(obj), 1)
obj = obj[0]
self.assertIsInstance(obj, Instance)
self.assertIn("value", obj.instance_attrs)
self.assertIn("other", obj.instance_attrs)

def test_infer_can_assign_has_slots(self):
mod = builder.parse(
"""
class A:
__slots__ = ('value',)
a = A()
a.value = "is set"
a.other = "not set"
"""
)
obj = list(mod.igetattr("a"))
self.assertEqual(len(obj), 1)
obj = obj[0]
self.assertIsInstance(obj, Instance)
self.assertIn("value", obj.instance_attrs)
self.assertNotIn("other", obj.instance_attrs)

def test_infer_can_assign_no_classdict(self):
mod = builder.parse(
"""
a = object()
a.value = "not set"
"""
)
obj = list(mod.igetattr("a"))
self.assertEqual(len(obj), 1)
obj = obj[0]
self.assertIsInstance(obj, Instance)
self.assertNotIn("value", obj.instance_attrs)

def test_augassign_attr(self):
builder.parse(
"""
Expand Down
4 changes: 3 additions & 1 deletion tests/unittest_scoped_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,9 @@ class A:
pass
class B:
pass
scope = object()
class Scope:
pass
scope = Scope()
scope.A = A
scope.B = B
class C(scope.A, scope.B):
Expand Down

0 comments on commit cd8b6a4

Please sign in to comment.