EDIT 2: FINALLY I was able produce an MWE:
from typing import Generic, TypeVar
T = TypeVar('T')
class Cache:
__dict = {}
@classmethod
def add(cls, item):
cls.__dict[item] = (item, [item, item, item, {item: item}])
print('On setting:', item in cls.__dict)
def __init_subclass__(cls, **kwargs):
Cache.add(cls)
class Class(Cache, Generic[T]):
pass
d = Cache._Cache__dict
tp = list(d)[0]
print('On checking:', tp in d)
In python 3.6 the output is:
On setting: True
On checking: False
While in 3.8 it is:
On setting: True
On checking: True
If that's not curious enough, if I remove the inheritance from Generic[T]
, everything is AOK.
ORIGINAL
I'm using Python 3.6 and I get a KeyError
when trying to get a key from a dictionary:
# d: Dict[type, Any]
tp = list(d.keys())[0]
d[tp]
# KeyError: ...
Meaning that the very key taken from the dictionary causes this exception. Note that d
has only one entry. The key-type is a type-object with GenericMeta
as its metaclass, so might this be the issue?
I verified the following properties using the debugger:
id(tp)
is the same in multiple calls.hash(tp)
is the same in multiple calls.tp is list(d.keys())[0]
tp == list(d.keys())[0]
len(d) == 1
EDIT:
print(type(tp)) # <class 'typing.GenericMeta'>
print(type(tp)) # <class 'dict'>
My question is this: what might be the cause for this behavior?
I can't update the python version due to some of the packages' compatibility issues, so please don't tell me to update unless it's a known bug that's solved in a later version.
CodePudding user response:
This is an initialization order issue.
On Python 3.6, Class
is an instance of typing.GenericMeta
. typing.GenericMeta
performs important initialization in __new__
, but that initialization can only begin once type.__new__
returns something to initialize. type.__new__
is responsible for calling __init_subclass__
, so your __init_subclass__
runs before any GenericMeta
initialization can happen.
When your __init_subclass__
adds Class
to the dict, initialization necessary for ==
and hash
to work correctly has not yet been performed. This operation ends up using an invalid hash. Later, once initialization is complete, the lookup uses the correct hash and can't find Class
.
On later Python versions, the whole generic class implementation was changed completely. typing.GenericMeta
no longer exists.