Skip to main content

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 as MemoryStorage — 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() and Registry.to_json() produce mappings of values only (they call snapshot() and extract .value). Descriptions are intentionally omitted from that shallow JSON representation to keep to_json() focused on JSON-serializable payloads.

  • If you want to persist descriptions you must use the Stored tuple form when calling Registry.from_dict() or Registry.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 of StoredItem objects which provide .value and .description.
    • from_dict(mapping), from_json(s) — classmethods to build a Registry from serialized data; from_dict accepts 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().
  • 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 K is bound to str). The registry will raise on invalid keys (empty string, contains whitespace, or wrong type).
  • to_dict() / to_json() intentionally drop descriptions. Use from_dict()/update() with Stored tuples if you need to preserve descriptions.
  • The Stored alias (Tuple[Optional[V], Optional[str]]) is available at mnemoreg._types for 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 main or master, tag pushes matching v*.*.*, pull requests to main/master, and workflow_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_TOKEN secret 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 push trigger (but PRs to master do trigger pull_request).
  • Repository maintainers can tighten the workflow triggers (recommended): e.g. remove pull_request trigger, only run publish on explicit tag push or manual workflow_dispatch.
  • Ensure MNEMOREG_PY_PI_TOKEN is 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mnemoreg-0.3.11.tar.gz (28.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mnemoreg-0.3.11-py3-none-any.whl (12.3 kB view details)

Uploaded Python 3

File details

Details for the file mnemoreg-0.3.11.tar.gz.

File metadata

  • Download URL: mnemoreg-0.3.11.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

Hashes for mnemoreg-0.3.11.tar.gz
Algorithm Hash digest
SHA256 f7fc792023268c6ccab86182ba1a423f8d81375ba352ca52daacfb6cb16d9991
MD5 66b2460148289b1875e8762ce508429a
BLAKE2b-256 c5e964325660adebade62dea58645f113363856cfab874f6096554301ade582a

See more details on using hashes here.

File details

Details for the file mnemoreg-0.3.11-py3-none-any.whl.

File metadata

  • Download URL: mnemoreg-0.3.11-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

Hashes for mnemoreg-0.3.11-py3-none-any.whl
Algorithm Hash digest
SHA256 d89c6f2ada8e89c989a26cf7ad75bde3d1d1b7d958c3e3e8eeeacd3a8500a0ad
MD5 caea7aed131421168347fd77f4150b1d
BLAKE2b-256 bd360f82bb35553c648c0753a3168d2aed99a2d06ee208b37386587d80b98c23

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page