Originally reported by: Claudiu Popa (BitBucket: PCManticore, GitHub: @PCManticore)
Class.bool_value returns true for a class whose metaclass defines __len__ to return 0 — i.e. astroid ignores the metaclass-level falseyness.
from astroid import parse
cls = parse('''
class Meta(type):
def __len__(self):
return 0
class C(metaclass=Meta):
pass
''')['C']
print(cls.bool_value())
Confirmed still reproducing on astroid 4.2.0b3 (2026-05-25)
Both the bool_value() API and the inference of bool(C) ignore the metaclass __len__:
import astroid
ast = astroid.parse('''
class Meta(type):
def __len__(self):
return 0
class C(metaclass=Meta):
pass
''')
print(ast['C'].bool_value())
# astroid 4.2.0b3 -> True ❌ (real Python: False)
node = astroid.extract_node('''
class Meta(type):
def __len__(cls):
return 0
class C(metaclass=Meta):
pass
bool(C) #@
''')
print(node.inferred())
# astroid 4.2.0b3 -> [<Const.bool ... value=True>] ❌
# real Python -> False
Both ClassDef.bool_value and the builtin-bool() inference path need to consult the metaclass's __bool__/__len__ (in MRO order) before falling back to the default truthy-class assumption.
Originally reported by: Claudiu Popa (BitBucket: PCManticore, GitHub: @PCManticore)
Class.bool_valuereturns true for a class whose metaclass defines__len__to return 0 — i.e. astroid ignores the metaclass-level falseyness.Confirmed still reproducing on astroid 4.2.0b3 (2026-05-25)
Both the
bool_value()API and the inference ofbool(C)ignore the metaclass__len__:Both
ClassDef.bool_valueand the builtin-bool()inference path need to consult the metaclass's__bool__/__len__(in MRO order) before falling back to the default truthy-class assumption.