mtdt
Type-safe mapping-like data structure for arbitrary metadata annotations.
mtdt provides a MetadataStore and a Key abstraction that allows attaching typed, validated, and namespaced metadata to objects or processes. It supports strong and weak reference storage, structural or identity-based key comparison, and immutability constraints.
Features
- Type-safe Keys:
Keyinstances define the expected type of their values. While thetypeparameter acts as a phantom type for static analysis, runtime validation is supported via embedded or custom validators. - Strong and Weak Storage: Values can be stored strongly (default) or weakly. Weak entries are automatically evicted from the store when the value has no other strong references.
- Identity vs Structural Keys: Keys compare by identity by default. Setting
use_identity=Falseswitches equality and hashing to a structural tuple of the key's fields. - Immutability: Keys can be marked
immutable=True, preventing overwriting or deletion of their values once set. - Strict and Non-strict Modes: In strict mode (default), all keys must be
Keyinstances. In non-strict mode, any hashable object can be used as a key. - Annotated Type Integration: Validators can be automatically inferred from
typing.Annotatedmetadata usingEmbeddedValidator.
Installation
pip install mtdt
Usage
Basic Storage
from mtdt import MetadataStore, Key
store = MetadataStore()
# Create identity-based keys
name_key = Key(value="name", type=str)
age_key = Key(value="age", type=int)
store[name_key] = "Alice"
store[age_key] = 30
print(store[name_key]) # Alice
print(len(store)) # 2
Structural Keys
By default, two distinct Key instances are not equal even if they have the same fields. For dictionary-like behavior where keys with the same fields collide, use use_identity=False.
from mtdt import MetadataStore, Key
store = MetadataStore()
k1 = Key(value="config", use_identity=False)
k2 = Key(value="config", use_identity=False)
store[k1] = "value"
print(store[k2]) # "value"
Key Factories and Namespacing
Key.factory is the recommended way to create keys with shared defaults. It supports extra_value for namespacing and automatic validator selection.
from mtdt import key_factory
# Create a factory for a specific namespace
AppFactory = key_factory(extra_value="my_app", use_identity=False)
# Keys produced will have value == ("my_app", "user_id")
k1 = AppFactory(value="user_id")
k2 = AppFactory(value="session_id")
Validators
Validators are callables that raise InvalidValue (or a subclass) if a value is invalid. They can be passed explicitly or inferred from Annotated types.
from typing import Annotated
from mtdt import MetadataStore, Key, EmbeddedValidator
from mtdt.exceptions import InvalidValue
def validate_positive(v: int):
if v < 0:
raise InvalidValue(what="age", expected="positive int", got=v)
Age = Annotated[int, EmbeddedValidator(validate_positive)]
# Using the default factory, the validator is automatically pulled from the Annotated type.
AgeKey = Key.factory()
store = MetadataStore()
k = AgeKey(value="user_age", type=Age)
store[k] = 25 # OK
# store[k] = -5 # Raises InvalidValue
Weak References
If a key is marked weakref=True, its values are held weakly. When the value is garbage collected, the entry is automatically removed from the store.
import gc
from mtdt import MetadataStore, Key
class Resource:
pass
store = MetadataStore()
k = Key(value="res", weakref=True)
res = Resource()
store[k] = res
print(k in store) # True
del res
gc.collect()
print(k in store) # False
Immutable Keys
Immutable keys prevent overwriting or deletion once a value has been set.
from mtdt import MetadataStore, Key
from mtdt.exceptions import InvalidUsage
store = MetadataStore()
k = Key(value="config", immutable=True)
store[k] = {"db": "localhost"}
try:
store[k] = {"db": "remote"}
except InvalidUsage:
print("Cannot overwrite immutable key")
try:
del store[k]
except InvalidUsage:
print("Cannot delete immutable key")
Strict and Non-Strict Modes
In strict mode (default), only Key instances are accepted. In non-strict mode, any hashable object can be used as a key, bypassing validation and immutability checks.
from mtdt import MetadataStore
# Non-strict mode
store = MetadataStore(strict=False)
store["string_key"] = 100
print(store["string_key"]) # 100
Exceptions
mtdt provides a hierarchy of exceptions under the Invalid base class:
Invalid: Base exception.InvalidValue: Raised when a value fails a validation check. SubclassesRuntimeError.InvalidType: Raised when a key or value fails a type check. SubclassesTypeError.InvalidUsage: Raised when the API is used incorrectly (e.g., updating an immutable key).
License
MIT License
Copyright (c) [Year] [Your Name/Organization]
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Release files for mtdt 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mtdt-1.0.0.tar.gz | 21.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mtdt-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 32.8 kB
Release files / mtdt-1.0.0.tar.gz
| Download URL | mtdt-1.0.0.tar.gz |
|---|---|
| Size | 21.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bdaaca16a91faa6354c6ad95e7f43dc9fbbf37f8f72595a6dbbbde0e0ef62b03
|
|
BLAKE2b-256 checksum How to use checksums |
157d872be33e95f0cac08f9d6ae818abc3187d9ec76067dde69825e807616309
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / mtdt-1.0.0-py3-none-any.whl
| Download URL | mtdt-1.0.0-py3-none-any.whl |
|---|---|
| Size | 11.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
884b7c5e5158310c33ab5c4c170933f55c44e933437503f4c582cedd2c04beb6
|
|
BLAKE2b-256 checksum How to use checksums |
f3b0e76ae39a641212c0efa17218448d171942ef716a73bdc5d9530afbbfe1a4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|