Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

isinstance(constantdict(), dict) returns True #35

Open
matthiasdiener opened this issue Jan 24, 2025 · 3 comments
Open

isinstance(constantdict(), dict) returns True #35

matthiasdiener opened this issue Jan 24, 2025 · 3 comments

Comments

@matthiasdiener
Copy link
Owner

matthiasdiener commented Jan 24, 2025

Same for:

  • isinstance(constantdict(), MutableMapping)
  • issubclass(constantdict, dict)
  • issubclass(constantdict, MutableMapping)

https://stackoverflow.com/questions/57982946/how-to-register-implementation-of-abc-mutablemapping-as-a-dict-subclass

@matthiasdiener
Copy link
Owner Author

matthiasdiener commented Jan 28, 2025

Sample error from loopy (via inducer/loopy#884):

  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/site-packages/pytools/persistent_dict.py", line 708, in store
    v = pickle.dumps((key, value))
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/loopy/loopy/translation_unit.py", line 469, in __getstate__
    return asdict(self)
           ^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 1286, in asdict
    return _asdict_inner(obj, dict_factory)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 1293, in _asdict_inner
    value = _asdict_inner(getattr(obj, f.name), dict_factory)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 1323, in _asdict_inner
    return type(obj)((_asdict_inner(k, dict_factory),
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 1324, in <genexpr>
    _asdict_inner(v, dict_factory))
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 1293, in _asdict_inner
    value = _asdict_inner(getattr(obj, f.name), dict_factory)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 1293, in _asdict_inner
    value = _asdict_inner(getattr(obj, f.name), dict_factory)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 1321, in _asdict_inner
    return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 1321, in <genexpr>
    return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 1323, in _asdict_inner
    return type(obj)((_asdict_inner(k, dict_factory),
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: unhashable type: 'dict'

@matthiasdiener
Copy link
Owner Author

matthiasdiener commented Jan 28, 2025

An approach similar to https://github.com/inducer/arraycontext/blob/029026cca4655be0314751cb2a2abfc26bc3732b/arraycontext/container/arithmetic.py#L126-L132 may not work:

>>> class CDMetaClass(type):
...     def __instancecheck__(cls, instance) -> bool:
...         print(" -- instancecheck", cls, instance, cls == type(instance))
...         return cls == type(instance)
...
>>> class constantdict(dict, metaclass=CDMetaClass):
...     pass
...
>>> obj1 = constantdict({1:2})
>>>
>>> print(isinstance(obj1, dict))
True  # should be False, but __instancecheck__ isn't even called
>>> print(isinstance(obj1, constantdict))
True  # correct result, but __instancecheck__ was also not called

cc @inducer

@inducer
Copy link
Collaborator

inducer commented Jan 28, 2025

Ugh, interesting. The root base class sets the metaclass. Recreating a custom dict with the desired metaclass also doesn't work:

class CDMetaClass(type):
    def __instancecheck__(cls, instance) -> bool:
        print(" -- instancecheck", cls, instance, cls is type(instance))
        return cls is type(instance)
basedict = CDMetaClass('basedict', dict.__bases__, dict(vars(dict)))
class constantdict(basedict):
    pass
obj1 = constantdict({"a": 1, "b": 2})
print(isinstance(obj1, dict))
print(isinstance(obj1, constantdict))

Multiple inheritance also can't change metaclasses:

class CDMetaClass(type):
    def __instancecheck__(cls, instance) -> bool:
        print(" -- instancecheck", cls, instance, cls is type(instance))
        return cls is type(instance)
class MetaBase(metaclass=CDMetaClass):
    pass
class constantdict(dict, MetaBase):
    pass
class constantdict2(MetaBase, dict): # also nope
    pass
obj1 = constantdict({"a": 1, "b": 2})
print(isinstance(obj1, dict))
print(isinstance(obj1, constantdict))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants