Skip to content

Many pure python tools that have a special cases for dict should accept frozendict as well. #145056

Description

@rhettinger

I'll fix the ones in collections. Some other priorities are pprint and ElementTree

Here's a rg dict | rg isinstance scan of the Lib directory:

unittest/mock.py:        if isinstance(self.in_dict, str):
trace.py:                             if isinstance(d, dict)]
rlcompleter.py:        if namespace and not isinstance(namespace, dict):
rlcompleter.py:                        isinstance(thisobject.__dict__.get(word),
tomllib/_parser.py:            if not isinstance(cont, dict):
tomllib/_parser.py:    if isinstance(value, (dict, list)):
tomllib/_parser.py:        if isinstance(value, (dict, list)):
tomllib/_parser.py:        if isinstance(float_value, (dict, list)):
multiprocessing/managers.py:                if not isinstance(method_to_typeid, dict):
functools.py:           (kwds is not None and not isinstance(kwds, dict)) or
functools.py:           (namespace is not None and not isinstance(namespace, dict))):
pprint.py:        if isinstance(object, self._dict_items_view):
turtle.py:        if isinstance(pen, dict):
optparse.py:                not isinstance(self.callback_kwargs, dict)):
optparse.py:        elif isinstance(other, dict):
linecache.py:    if module_globals is not None and not isinstance(module_globals, dict):
xml/etree/ElementTree.py:        if not isinstance(attrib, dict):
_pyrepl/input.py:        if isinstance(d, dict):
tkinter/__init__.py:    if isinstance(cnfs, dict):
tkinter/__init__.py:        if isinstance(cnf, (dict, tuple)):
_pyrepl/base_eventqueue.py:            if isinstance(k, dict):
test/_test_multiprocessing.py:    def test_dict_isinstance(self):
_pyrepl/readline.py:    if not isinstance(namespace, dict):
logging/config.py:        if not isinstance(value, ConvertingDict) and isinstance(value, dict):
logging/config.py:                    elif isinstance(qspec, dict):
logging/config.py:                        elif isinstance(lspec, dict):
logging/config.py:                            assert isinstance(d, dict)
test/test_pyrepl/test_terminfo.py:                        isinstance(ncurses_data[key], dict)
test/test_pyrepl/test_terminfo.py:                            isinstance(ncurses_data[key], dict)
test/test_launcher.py:        if isinstance(value, dict):
test/test_launcher.py:            tags = [t for t in company_data if isinstance(company_data[t], dict)]
test/test_launcher.py:            tags = [t for t in company_data if isinstance(company_data[t], dict)]
test/test_isinstance.py:        self.assertFalse(issubclass(dict, float|str))
test/test_marshal.py:    elif isinstance(obj, dict):
test/test_format.py:    if isinstance(args, dict):
test/test_build_details.py:                        self.assertFalse(isinstance(sys.implementation.version, dict))
_pydecimal.py:        elif not isinstance(traps, dict):
_pydecimal.py:        elif not isinstance(flags, dict):
_pydecimal.py:        if not isinstance(d, dict):
collections/__init__.py:        if not isinstance(other, dict):
collections/__init__.py:        if not isinstance(other, dict):
collections/__init__.py:        if isinstance(other, dict):
collections/__init__.py:        if isinstance(other, dict):
test/test_module/__init__.py:        self.assertTrue(isinstance(foo.__dict__, dict))
profiling/sampling/heatmap_collector.py:            if isinstance(data, dict):
test/test_code.py:                if isinstance(globalvars, dict):
test/test_urllib2_localnet.py:        assert isinstance(users, dict)
annotationlib.py:            or isinstance(self.__cell__, dict)
annotationlib.py:        if isinstance(self.__cell__, dict):
annotationlib.py:                if isinstance(self.__cell__, dict) and isinstance(other.__cell__, dict)
annotationlib.py:                if isinstance(self.__cell__, dict) else id(self.__cell__),
annotationlib.py:        if not isinstance(ann, dict):
annotationlib.py:    if not isinstance(ann, dict):
test/test_io/test_general.py:                if isinstance(v, dict):
test/test_compiler_assemble.py:            elif isinstance(value, dict):
json/encoder.py:                    elif isinstance(value, dict):
json/encoder.py:                    elif isinstance(value, dict):
json/encoder.py:        elif isinstance(o, dict):
plistlib.py:            if not isinstance(self.stack[-1], dict):
plistlib.py:        if self.current_key or not isinstance(self.stack[-1], dict):
plistlib.py:        elif isinstance(value, dict):
plistlib.py:        if isinstance(value, dict):
plistlib.py:        elif isinstance(value, dict):
test/test_tomllib/burntsushi.py:    elif isinstance(obj, dict):
test/test_tomllib/burntsushi.py:    if isinstance(obj, dict):
inspect.py:        if isinstance(dict_obj, (staticmethod, types.BuiltinMethodType)):
inspect.py:        elif isinstance(dict_obj, (classmethod, types.ClassMethodDescriptorType)):
inspect.py:        elif isinstance(dict_obj, property):
inspect.py:            if isinstance(slots, dict) and name in slots:
inspect.py:            (kwdefaults is None or isinstance(kwdefaults, dict)))
xmlrpc/client.py:        if isinstance(item, dict):
test/test_importlib/resources/zip.py:        if isinstance(contents, dict):
importlib/_bootstrap_external.py:    if not isinstance(module_globals, dict):
test/test_descrtut.py:    >>> isinstance([], dict)
enum.py:    if isinstance(obj, dict):
test/test_import/test_lazy_imports.py:            assert isinstance(sys.lazy_modules, dict), "sys.lazy_modules is not a dict"
test/test_type_annotations.py:                if not isinstance(self.my_annotations, dict):
test/test_type_annotations.py:                if not isinstance(value, dict):
dataclasses.py:    elif isinstance(obj, dict):
test/string_tests.py:        elif isinstance(obj, dict):
test/test_pyclbr.py:                if (not isinstance(classdict[name], ClassMethodType) or
compression/zstd/_zstdfile.py:        if options is not None and not isinstance(options, dict):
compression/zstd/__init__.py:    if not isinstance(dict_size, int):
compression/zstd/__init__.py:    if not isinstance(zstd_dict, ZstdDict):
compression/zstd/__init__.py:    if not isinstance(dict_size, int):

Linked PRs

Activity

  1. eendebakpt commented on Feb 20, 2026

    @eendebakpt
    Contributor

    I looked into the dataclasses cases. A frozendict is currently handled fine, but not exactly as a dict. However, the same is true for classes that behave like a mapping so maybe this is ok. A minimal example:

    from dataclasses import dataclass, asdict, astuple
    
    @dataclass
    class A:
        d: dict
        f: frozendict
    
    
    lst = []
    d = {"a": lst, "b": lst}
    a = A(d, frozendict(d))
    
    at = astuple(a)  # runs fine
    print(at)  # looks good
    print(at[0]["a"] is at[0]["b"])  # False
    print(
        at[1]["a"] is at[1]["b"]
    )  # frozendict is handled by deepcopy, so this returns True
    
    ad = asdict(a)  # runs fine
    print(ad)  # looks good
    print(at["d"]["a"] is at["d"]["b"])  # False
    print(at["f"]["a"] is at["f"]["b"])  # True

    Note: the search could be extended with a pattern for issubclass(obj_type, dict) (found in dataclasses.py)

  2. added
    type-featureA feature request or enhancement
    stdlibStandard Library Python modules in the Lib/ directory
    on Feb 20, 2026
  3. added a commit that references this issue on Feb 21, 2026
  4. devdanzin commented on Feb 21, 2026

    @devdanzin
    Member

    I'll try to handle pprint.

  5. hugovk commented on Feb 21, 2026

    @hugovk
    Member

    I'll try to handle pprint.

    See also #144908 for some pprint changes and the umbrella issue #141510.

  6. devdanzin commented on Feb 21, 2026

    @devdanzin
    Member

    Oh, that makes my issue a duplicate. Thanks for the heads up.

    Edit: leaving it open as it's more focused than the umbrella issue, feel free to close if you disagree.

  7. abdoulrasheed commented on Mar 1, 2026

    @abdoulrasheed
    Contributor

    Related incomplete migration: #144904 changed _EMPTY_METADATA to frozendict() but left the non-empty case as types.MappingProxyType(metadata) at dataclasses:306.

    This creates a type inconsistency:

    from dataclasses import dataclass, field, fields
    
    @dataclass
    class C:
        a: int
        b: int = field(metadata={'k': 'v'})
    
    f_a, f_b = fields(C)
    type(f_a.metadata)  # frozendict
    type(f_b.metadata)  # mappingproxy
    hash(f_a.metadata)  # works
    hash(f_b.metadata)  # TypeError: unhashable type: 'dict'

    This could potentially be folded into #145125 since it's already touching dataclasses.py.

  8. added a commit that references this issue on Mar 4, 2026
  9. vstinner commented on Mar 4, 2026

    @vstinner
    Member

    I'll fix the ones in collections. Some other priorities are pprint and ElementTree

    I created #145508 to accept frozendict in xml.etree.

  10. added a commit that references this issue on Mar 5, 2026
  11. added 7 commits that reference this issue on Mar 26, 2026
  12. added 2 commits that reference this issue on Apr 16, 2026
  13. added a commit that references this issue on Apr 23, 2026
  14. added 4 commits that reference this issue on Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions