Skip to content

Commit 7aa5c2d

Browse files
Restore setting a Call as a base for classes using six.with_metaclass (#2049)
Harden support for using enums as metaclasses. Fixes the crash in pylint-dev/pylint#5935 by adopting the check for not-none bases as in ClassDef._inferred_bases without recausing the false positive reported in pylint-dev/pylint#7506, which requires correct bases. (cherry picked from commit b5ebf99)
1 parent edf88c6 commit 7aa5c2d

File tree

5 files changed

+31
-5
lines changed

5 files changed

+31
-5
lines changed

ChangeLog

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ What's New in astroid 2.15.1?
1212
=============================
1313
Release date: TBA
1414

15+
* Restore behavior of setting a Call as a base for classes created using ``six.with_metaclass()``,
16+
and harden support for using enums as metaclasses in this case.
17+
18+
Reverts #1622
19+
Refs PyCQA/pylint#5935
20+
Refs PyCQA/pylint#7506
1521

1622

1723
What's New in astroid 2.15.0?

astroid/brain/brain_six.py

-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,6 @@ def transform_six_with_metaclass(node):
219219
"""
220220
call = node.bases[0]
221221
node._metaclass = call.args[0]
222-
node.bases = call.args[1:]
223222
return node
224223

225224

astroid/nodes/scoped_nodes/scoped_nodes.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -1703,7 +1703,15 @@ def infer_call_result(self, caller=None, context: InferenceContext | None = None
17031703
metaclass = next(caller.args[0].infer(context), None)
17041704
if isinstance(metaclass, ClassDef):
17051705
try:
1706-
class_bases = [next(arg.infer(context)) for arg in caller.args[1:]]
1706+
class_bases = [
1707+
# Find the first non-None inferred base value
1708+
next(
1709+
b
1710+
for b in arg.infer(context=context.clone())
1711+
if not (isinstance(b, Const) and b.value is None)
1712+
)
1713+
for arg in caller.args[1:]
1714+
]
17071715
except StopIteration as e:
17081716
raise InferenceError(node=caller.args[1:], context=context) from e
17091717
new_class = ClassDef(name="temporary_class")

tests/brain/test_six.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,7 @@ class B(six.with_metaclass(A, C)):
110110
inferred = next(ast_node.infer())
111111
self.assertIsInstance(inferred, nodes.ClassDef)
112112
self.assertEqual(inferred.name, "B")
113-
self.assertIsInstance(inferred.bases[0], nodes.Name)
114-
self.assertEqual(inferred.bases[0].name, "C")
113+
self.assertIsInstance(inferred.bases[0], nodes.Call)
115114
ancestors = tuple(inferred.ancestors())
116115
self.assertIsInstance(ancestors[0], nodes.ClassDef)
117116
self.assertEqual(ancestors[0].name, "C")
@@ -131,7 +130,7 @@ class Foo(six.with_metaclass(FooMeta, Enum)): #@
131130
bar = 1
132131
"""
133132
klass = astroid.extract_node(code)
134-
assert list(klass.ancestors())[-1].name == "Enum"
133+
assert next(klass.ancestors()).name == "Enum"
135134

136135
def test_six_with_metaclass_with_additional_transform(self) -> None:
137136
def transform_class(cls: Any) -> ClassDef:

tests/test_scoped_nodes.py

+14
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,20 @@ class WithMeta(six.with_metaclass(type, object)): #@
14031403
self.assertEqual(["object"], [base.name for base in klass.ancestors()])
14041404
self.assertEqual("type", klass.metaclass().name)
14051405

1406+
@unittest.skipUnless(HAS_SIX, "These tests require the six library")
1407+
def test_metaclass_generator_hack_enum_base(self):
1408+
"""Regression test for https://github.com/PyCQA/pylint/issues/5935"""
1409+
klass = builder.extract_node(
1410+
"""
1411+
import six
1412+
from enum import Enum, EnumMeta
1413+
1414+
class PetEnumPy2Metaclass(six.with_metaclass(EnumMeta, Enum)): #@
1415+
DOG = "dog"
1416+
"""
1417+
)
1418+
self.assertEqual(list(klass.local_attr_ancestors("DOG")), [])
1419+
14061420
def test_add_metaclass(self) -> None:
14071421
klass = builder.extract_node(
14081422
"""

0 commit comments

Comments
 (0)