Repository navigation
Conversation
…und property Fixes zopefoundation#249. getObjectSpecification(ob) computes implementedBy(ob.__class__), so inspect.getmembers(type(Interface)) ends up calling _implements_name with cls=_InterfaceMetaClass. That metaclass defines __module__ as a property meant for its own instances; fetched on the metaclass itself it stays an unbound property object rather than a string, and the '+' concatenation in _implements_name raised TypeError. Fall back to '?' when the fetched value isn't a str, same as the existing fallback for a missing or None value.
|
Sorry, this is more complex as it seems. I also can not 100% tell if the probably correct side effects are behavior we want in zope.interface. This needs review form others too. Further I am not sure if you signed the Contributor License Agreement? Summary:
I reviewed with AI Assist, see below. Overview
Before merge1. No CHANGES.rst entry — every fix gets one under 2. The fix changes what
Follow-up material, not for this PR3. Root cause stays — L509–L523
4.
Tests and style5.
6.
7. Two identical guard blocks — L380–L387
Observation, no action: Transparency: AI-assisted review
|
Fixes #249.
getObjectSpecification(ob)computesimplementedBy(ob.__class__). Sinceobhere isInterfaceClass,ob.__class__is_InterfaceMetaClass— the metaclass — so_implements_nameends up called withcls=_InterfaceMetaClass. That metaclass defines__module__as aproperty, meant to be computed for its own instances (InterfaceClass/Interfaceobjects). Fetched directly on the metaclass itself rather than through an instance, it stays the raw, unboundpropertyobject instead of a string:_implements_nameassumes this is always a string (or falls back to'?'for a missing/Nonevalue) and blows up trying to concatenate it. Since this is any object's__module__/__name__, not just this specific metaclass, the fix checks the type generally: fall back to'?'when the fetched value isn't astr, alongside the existing missing/Nonefallback.Added a targeted unit test with a synthetic metaclass reproducing the exact mechanism (
getattron a metaclass whose__module__is aproperty), plus the originalgetmembers(type(Interface))repro from the issue, both added to the fallback test classes so they run against the Python implementation (and the C-accelerated one via the existingTest_implementedBy/Test_getObjectSpecificationinheritance, when built). Both fail onmain; full suite passes (1135,PURE_PYTHON=1— no C toolchain available here to build/test the optimized path, but_implements_name/implementedByFallbackis the only implementation of this logic and is what the C extension already delegates to for this path).flake8clean.