ReadonlyDict
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, 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]": ...
@overload
def __new__(cls, **kwargs: V2) -> "CustomDict[str, V2]": ...
def __new__(cls, *args: Any, **kwargs: Any) -> Any: ...
@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.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| readonlydict-2.1.1.tar.gz | 92.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| readonlydict-2.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 99.2 kB
Release files / readonlydict-2.1.1.tar.gz
| Download URL | readonlydict-2.1.1.tar.gz |
|---|---|
| Size | 92.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4fabf13017ebb2bfde869be7a52043f47a29eb84d119a3c9397b8398ace05f80
|
|
BLAKE2b-256 checksum How to use checksums |
f8d921ce3c72b854df1c4c36ffa6688afba8c59675579ba2916872f2dd47026e
|
| 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.1-py3-none-any.whl
| Download URL | readonlydict-2.1.1-py3-none-any.whl |
|---|---|
| Size | 6.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d42e57fa8a301d8e5c8e221ed9c0fc8878cd4ad3e2fe5f6476b3feaed2a03d51
|
|
BLAKE2b-256 checksum How to use checksums |
643a2f20787349112bafb972792025322e5ad02787bec69a16f9bb2fa38d94bb
|
| 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}
|