A tiny thread-safe registry mapping for simple plugin/registry use-cases.
Project description
mnemoreg
A tiny, dependency-free, thread-safe registry mapping useful for registering callables and values by string keys. Designed to be embedded in other projects or used as a small standalone utility.
Key points
- Small, stdlib-only implementation.
- Thread-safe (uses RLock for mutations).
- Simple decorator-based registration for callables.
- Optional descriptions for stored values: each stored entry can carry an
optional human-readable description (string) that is accessible via
Registry.snapshot().
Quick facts
- Package exports:
Registry,AlreadyRegisteredError,NotRegisteredError,StorageProtocol, and__version__. - Main implementation:
mnemoreg/core.py. - Storage helpers live in
mnemoreg/_storage(default in-memory backend is exported asMemoryStorage— note: the name contains a historical typo).
Install
From PyPI:
pip install mnemoreg
From source (editable/development):
git clone https://github.com/i3iorn/mnemoreg.git
cd mnemoreg
python -m venv .venv
# Windows cmd.exe:
.venv\Scripts\activate
# PowerShell:
.venv\Scripts\Activate.ps1
# Unix/macOS:
source .venv/bin/activate
pip install -e .
# dev extras:
pip install -e .[dev]
Usage (quick)
The Registry behaves like a mapping from string keys to values. Examples:
from mnemoreg import Registry, OverwritePolicy
r = Registry[str, int]()
r['one'] = 1
assert r['one'] == 1
# decorator registration (explicit key) with optional description
@r.register('plus', description='adds one')
def plus(x: int) -> int:
return x + 1
assert r['plus'](4) == 5
# decorator registration (uses function.__name__ when key omitted)
@r.register()
def multiply(x: int, y: int) -> int:
return x * y
assert r['multiply'](3, 4) == 12
# bulk operations (acquires the same lock for the block)
with r.bulk():
r['a'] = 1
r['b'] = 2
# removal
del r['a']
# snapshot returns a mapping of StoredItem objects that wrap the value and expose an optional description
snap = r.snapshot() # shallow dict copy; values are StoredItem objects
item = snap['plus']
assert item.value(2) == 3
assert item.description == 'adds one'
# to_dict / to_json produce a shallow mapping of *values* (descriptions are not included by default)
# if you need to persist descriptions you can use from_dict/update with the Stored tuple form described below.
s = r.to_json() # JSON string of values only
new_r = Registry.from_json(s)
Storing descriptions and the Stored tuple
Internally, the storage backend stores entries as a 2-tuple (value, description).
This is exposed via a convenient type alias in the library (see mnemoreg._types.Stored).
The tuple form is:
(value_or_None, Optional[str])
Examples:
-
Register via decorator with description:
@r.register('f', description='does something') def f(x): ...
-
Update or create entries using the Stored form (keeps description):
Stored tuple (value, description)
r.update({'a': (1, 'one')})
-
Create a Registry from a dict that already contains descriptions:
new = Registry.from_dict({'k': (42, 'answer')})
Notes about serialization and round-tripping
-
Registry.to_dict()andRegistry.to_json()produce mappings of values only (they callsnapshot()and extract.value). Descriptions are intentionally omitted from that shallow JSON representation to keepto_json()focused on JSON-serializable payloads. -
If you want to persist descriptions you must use the Stored tuple form when calling
Registry.from_dict()orRegistry.update()so the description is kept in the backend. Example:saved = registry._store.to_dict() # low-level store representation: {k: (value, description)} Registry.from_dict(saved)
API / behavior summary
-
class
Registry(Generic[K, V])- Mapping-like:
__getitem__,__setitem__,__delitem__,__iter__,__len__,__contains__. register(key: Optional[str] = None, description: Optional[str] = None)— decorator to register functions/values with optional description.get(key, default=None),snapshot()— read helpers.snapshot()returns a dict ofStoredItemobjects which provide.valueand.description.from_dict(mapping),from_json(s)— classmethods to build a Registry from serialized data;from_dictaccepts either a plain mapping of values or the internal Stored tuple mapping(value, description).to_json(**kwargs)— serialize shallowly to JSON (values only).bulk()— context manager that acquires the registry lock for batched operations.update(mapping),clear().
- Mapping-like:
-
Overwrite behavior: controlled by
OverwritePolicy(FORBID = 0, ALLOW = 1, WARN = 2). The default is FORBID. -
Exceptions:
AlreadyRegisteredError,NotRegisteredError.
Notes and caveats (important)
- Keys must be strings (type parameter
Kis bound tostr). The registry will raise on invalid keys (empty string, contains whitespace, or wrong type). to_dict()/to_json()intentionally drop descriptions. Usefrom_dict()/update()with Stored tuples if you need to preserve descriptions.- The
Storedalias (Tuple[Optional[V], Optional[str]]) is available atmnemoreg._typesfor advanced uses.
Development
This project uses pytest, ruff, black, isort, mypy, and pre-commit hooks.
Typical local developer setup:
python -m venv .venv
# activate the venv
pip install -e .[dev]
Run tests:
python -m pytest -q
Format / lint / checks (examples):
ruff check --fix .
isort --profile black .
black .
pre-commit run --all-files
python -m mypy mnemoreg --ignore-missing-imports
CI & publishing
There are GitHub Actions workflows under .github/workflows/.
The publish.yml workflow (current behavior):
- Triggers on: push to
mainormaster, tag pushes matchingv*.*.*, pull requests tomain/master, andworkflow_dispatch. - The workflow runs tests, then attempts to determine/create a semver tag and push it, builds distributions, and (optionally) uploads to PyPI if the
MNEMOREG_PY_PI_TOKENsecret is configured.
If you don't want to trigger the publish flow or accidental publishes:
- Work from a fork and open PRs from the fork — forks cannot push tags to the upstream repo and won't have the PyPI secret.
- Work on non-main branches; pushes to non-main branches do not trigger the
pushtrigger (but PRs tomasterdo triggerpull_request). - Repository maintainers can tighten the workflow triggers (recommended): e.g. remove
pull_requesttrigger, only run publish on explicit tag push or manualworkflow_dispatch. - Ensure
MNEMOREG_PY_PI_TOKENis never added unless you intend to publish.
Contributing
Please follow the project's code style (black/ruff/isort) and add tests for new behavior. Suggested flow for external contributors:
- Fork the repo.
- Create a feature branch in your fork and open a PR to
master. - Add tests and run the test suite locally.
License
MIT — see the LICENSE file.
Maintainers
i3iorn
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mnemoreg-0.3.10.tar.gz.
File metadata
- Download URL: mnemoreg-0.3.10.tar.gz
- Upload date:
- Size: 28.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbb920dcdda4831592326b846417450135fc13d06ce3b07d5a4fad847b965da1
|
|
| MD5 |
385f0d7b5063d01edf900192b36cd184
|
|
| BLAKE2b-256 |
598eb6c95fae6017f15722fb683765aa098b2498f69d6988449c7a05066f05a7
|
File details
Details for the file mnemoreg-0.3.10-py3-none-any.whl.
File metadata
- Download URL: mnemoreg-0.3.10-py3-none-any.whl
- Upload date:
- Size: 12.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
99933b5dc70af68baa0cc7d3c351ebeeebd7a01f6c074a21fa5ee44e832ae2c5
|
|
| MD5 |
c1b24981d010b32b793aa39063ecf921
|
|
| BLAKE2b-256 |
b594e666788e87f7390cd7b409fc55d687f08d16209ba72fc5b898828f173fac
|