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.10.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.10-py3-none-any.whl (12.3 kB view details)

Uploaded Python 3

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

Hashes for mnemoreg-0.3.10.tar.gz
Algorithm Hash digest
SHA256 fbb920dcdda4831592326b846417450135fc13d06ce3b07d5a4fad847b965da1
MD5 385f0d7b5063d01edf900192b36cd184
BLAKE2b-256 598eb6c95fae6017f15722fb683765aa098b2498f69d6988449c7a05066f05a7

See more details on using hashes here.

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

Hashes for mnemoreg-0.3.10-py3-none-any.whl
Algorithm Hash digest
SHA256 99933b5dc70af68baa0cc7d3c351ebeeebd7a01f6c074a21fa5ee44e832ae2c5
MD5 c1b24981d010b32b793aa39063ecf921
BLAKE2b-256 b594e666788e87f7390cd7b409fc55d687f08d16209ba72fc5b898828f173fac

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