Skip to main content

ReadonlyDict

Release Python Downloads DOI Tests

Drop-in read-only dictionary with 100% typing and runtime compatibility

Overview: Why ReadonlyDict?

This package is built strictly on the following formula: ReadonlyDict = (built-in dictionary features) - (in-place features) + (read-only features).

  • 100% compatibility and zero custom API: Our goal is to achieve flawless compatibility with Python's built-in dictionary in both static type checking (e.g., mypy, Pyright) and runtime behavior. We simply removed in-place methods (e.g., pop(), update()). We do not introduce any custom methods.
  • True immutable semantics: The only additions are those strictly required for a read-only data structure: it is fully hashable (only if all values are hashable), and shallow copies (i.e., self.copy(), copy.copy(self)) simply return itself to save memory.
  • When to use this package: If you want extended read-only features, existing packages like frozendict, immutabledict, or immutables are better choices. However, if your priority is pure compatibility and perfect static type inference, ReadonlyDict should be the optimal choice.

Installation

pip install readonlydict

Basic Usage

It works exactly like a built-in dictionary, but raises an error if you try to modify it.

from readonlydict import ReadonlyDict


# Initialization works just like the built-in dictionary:
>>> ro = ReadonlyDict(a=0, b=1)
>>> ro
ReadonlyDict({'a': 0, 'b': 1})


# It is fully hashable (can be used as a dictionary key or in a set):
>>> hash(ro)
-5925576189957013898
>>> {ro, ro}
{ReadonlyDict({'a': 0, 'b': 1})}


# Mutation is strictly prohibited (static type checkers will also warn you):
>>> ro["c"] = 2
TypeError: 'ReadonlyDict' object does not support item assignment
>>> ro.update(c=2)
AttributeError: 'ReadonlyDict' object has no attribute 'update'

Advanced Usage: Subclassing with Type Hints

If you want to create your own custom read-only dictionary by subclassing ReadonlyDict, you can maintain static type inference by utilizing TYPE_CHECKING and @overload. Here is the best-practice template for subclassing:

# standard library
from collections.abc import Hashable, Iterable, Mapping
from typing import TYPE_CHECKING, Any, TypeVar, overload

# dependencies
from readonlydict import Items, ReadonlyDict

# type variables
K = TypeVar("K", bound=Hashable)
V = TypeVar("V", covariant=True)
K2 = TypeVar("K2", bound=Hashable)
V2 = TypeVar("V2")


class CustomDict(ReadonlyDict[K, V]):
    # Modify the return types to guarantee type inference:
    if TYPE_CHECKING:

        @overload
        def __new__(cls, **kwargs: V) -> "CustomDict[str, V]": ...
        @overload
        def __new__(cls, iterable: Items[K, V], /, **kwargs: V2) -> "CustomDict[K | str, V | V2]": ...
        @overload
        def __new__(cls, mapping: Mapping[K, V], /, **kwargs: V2) -> "CustomDict[K | str, V | V2]": ...
        def __new__(cls, *args: Any, **kwargs: Any) -> Any: ... # type: ignore

        @overload
        @classmethod
        def fromkeys(cls, iterable: Iterable[K2], /) -> "CustomDict[K2, None]": ...
        @overload
        @classmethod
        def fromkeys(cls, iterable: Iterable[K2], value: V2, /) -> "CustomDict[K2, V2]": ...
        @classmethod
        def fromkeys(cls, *args: Any, **kwargs: Any) -> Any: ...

        def __or__(self, other: Mapping[K2, V2], /) -> "CustomDict[K | K2, V | V2]": ...

    # Then add your custom properties or methods:
    @property
    def first(self) -> tuple[K, V]:
        return next(iter(self.items()))

Metadata

Release files for readonlydict 2.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for readonlydict 2.1.0
File Size Uploaded
readonlydict-2.1.0.tar.gz 92.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for readonlydict 2.1.0
File Interpreter ABI Platform
readonlydict-2.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 99.2 kB

Release files / readonlydict-2.1.0.tar.gz

Download URL readonlydict-2.1.0.tar.gz
Size 92.9 kB
Tags Source
SHA-256 checksum
How to use checksums
eee7b7c435af7624afe307d14ede1051c0117d16d63d1f4e94f546cdf34118f3
BLAKE2b-256 checksum
How to use checksums
28cbfaf5d4be0e4aa3455fcab1c270eefe8559c6ea913153550ea14f0d3f97c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / readonlydict-2.1.0-py3-none-any.whl

Download URL readonlydict-2.1.0-py3-none-any.whl
Size 6.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
552c6b5246810964c31548d5d8993a5836553de40e41e5e26f2d323aeda66a9b
BLAKE2b-256 checksum
How to use checksums
b3b73211b36c0e9c7b58cb55b3d56e974f96c0a593adcf5a7576ffb5760a3155
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

2.1.1

2 release files

This release

2.1.0 This release

2 release files

2.0.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page