Running pyrsistent on GraalPy 25.0.2 and hit a crash on import. Took a while to track down the root cause so figured I'd report it properly.
Error
TypeError: object.__new__(PMap) is not safe, use PMap.__new__()
Full traceback from import pyrsistent:
File ".../pyrsistent/_pmap.py", line 557, in <module>
_EMPTY_PMAP = _turbo_mapping({}, 0)
File ".../pyrsistent/_pmap.py", line 554, in _turbo_mapping
return PMap(len(initial), pvector().extend(buckets))
File ".../pyrsistent/_pmap.py", line 151, in __new__
self = super(PMap, cls).__new__(cls)
TypeError: object.__new__(PMap) is not safe, use PMap.__new__()
Minimal reproducer
from typing import Generic, TypeVar
KT = TypeVar('KT')
VT = TypeVar('VT')
class Foo(Generic[KT, VT]):
__slots__ = ('x', 'y')
def __new__(cls, x, y):
self = super(Foo, cls).__new__(cls)
self.x = x
self.y = y
return self
f = Foo(1, 2)
print(f.x) # should print 1
This raises TypeError: object.__new__(Foo) is not safe, use Foo.__new__() on GraalPy. Works fine on CPython 3.12 and PyPy.
Removing the Generic[KT, VT] base makes the error go away. So does removing __new__ and using __init__ instead. The issue is specifically the combination of inheriting from Generic[...] and defining __new__.
Why this matters
pyrsistent added Generic[...] bases to its persistent collection classes (PMap, PSet, PList, PDeque) in October 2023 purely for type-checker support. Those classes already had __new__ for __slots__ initialization. CPython treats this combination as completely valid — object.__new__(cls) inside a custom __new__ is the standard pattern for allocating an instance.
Where the check lives
Found it in WrapTpNew.java. The check walks up the MRO looking for the first non-Python (builtin) tp_new slot. For Foo(Generic[KT, VT]), it lands on Generic before reaching object, and Generic.tp_new != object.tp_new, so the safety check fires. In CPython, Generic doesn't override tp_new at all, so the walk goes straight through to object.
Workaround
Replacing __new__ with __init__ in the affected classes works around it. But this is a valid Python pattern and the error should not be raised — CPython's own tp_new_wrapper check is specifically guarding against things like object.__new__(dict) where you pass a completely unrelated type, not the normal super().__new__(cls) pattern inside a class's own __new__.
Environment
- GraalPy 3.12.8 (GraalVM CE Native 25.0.2), macOS aarch64
- pyrsistent 0.20.0
- CPython 3.12.x: no issue
Running pyrsistent on GraalPy 25.0.2 and hit a crash on import. Took a while to track down the root cause so figured I'd report it properly.
Error
Full traceback from
import pyrsistent:Minimal reproducer
This raises
TypeError: object.__new__(Foo) is not safe, use Foo.__new__()on GraalPy. Works fine on CPython 3.12 and PyPy.Removing the
Generic[KT, VT]base makes the error go away. So does removing__new__and using__init__instead. The issue is specifically the combination of inheriting fromGeneric[...]and defining__new__.Why this matters
pyrsistent added
Generic[...]bases to its persistent collection classes (PMap,PSet,PList,PDeque) in October 2023 purely for type-checker support. Those classes already had__new__for__slots__initialization. CPython treats this combination as completely valid —object.__new__(cls)inside a custom__new__is the standard pattern for allocating an instance.Where the check lives
Found it in
WrapTpNew.java. The check walks up the MRO looking for the first non-Python (builtin)tp_newslot. ForFoo(Generic[KT, VT]), it lands onGenericbefore reachingobject, andGeneric.tp_new != object.tp_new, so the safety check fires. In CPython,Genericdoesn't overridetp_newat all, so the walk goes straight through toobject.Workaround
Replacing
__new__with__init__in the affected classes works around it. But this is a valid Python pattern and the error should not be raised — CPython's owntp_new_wrappercheck is specifically guarding against things likeobject.__new__(dict)where you pass a completely unrelated type, not the normalsuper().__new__(cls)pattern inside a class's own__new__.Environment