Skip to content

mineproductivity.registry

Auto-generated API reference for the Registry package, rendered from the package's own docstrings. For the narrative overview, dependency rules, and extension guide, see Packages -> Registry.

registry

mineproductivity.registry -- the plugin-first backbone of MineProductivity: the single, generic discovery-and-lookup mechanism that lets KPIs, connectors, ontology entity types, and analytics models be added to the platform as separate, installable packages, with zero change to the core.

Implements docs/architecture/03_Registry_Framework_Design_Specification.md exactly. registry depends only on core -- see README.md for the full set of architectural rules this package must satisfy.

Everything documented here is part of the public API and can be imported directly from mineproductivity.registry, e.g.::

from mineproductivity.registry import Registry, EntryPointDiscovery

DiscoveryCache

DiscoveryCache()

Memoizes :meth:EntryPointDiscovery.discover results per :class:EntryPointSpec for the lifetime of the process -- entry-point scanning touches the filesystem/importlib metadata and is not free to repeat on every lookup (design spec §22).

Invalidated only by an explicit call to :meth:invalidate, never implicitly -- e.g. a test harness that installs a plugin mid-process must call invalidate() itself to see it.

A single lock serializes get_or_discover calls, so concurrent calls for the same spec are guaranteed to trigger exactly one underlying discovery pass (design spec §25); the public contract only guarantees that result, not this specific locking strategy.

get_or_discover

get_or_discover(spec, discovery)

Return the cached discovery result for spec, computing and caching it via discovery.discover(spec) on first access.

invalidate

invalidate(spec=None)

Explicitly drop the cached result for spec, or every cached result if spec is None.

EntryPointDiscovery

Scans installed packages' entry-points (via :mod:importlib.metadata) and imports each one, running whatever @register-decorated side effects the imported module contains.

An exception raised while importing any one entry-point's target module is caught, logged, and skipped -- it MUST NOT prevent discovery of the remaining entry-points in the same group (design spec §11, §26, the isolation rule: "a mine with 30 installed KPI packs cannot have its whole platform go down because pack #17 has a typo").

discover

discover(spec)

Import every entry-point in spec.group.

Returns:

Type Description
Result[Sequence[str]]

Result.ok wrapping the names of the entry-points that imported successfully (in entry-point-name order), or Result.err if the entry-point scan itself failed (e.g. a corrupted installed-package metadata database) -- a systemic failure distinct from any single entry-point's import error, which is isolated rather than propagated.

EntryPointSpec dataclass

EntryPointSpec(group, target_registry)

Bases: BaseValueObject

One discoverable entry-point group, e.g. EntryPointSpec(group="mineproductivity.connectors", target_registry="connectors").

DuplicateRegistrationError

DuplicateRegistrationError(message, *, details=None)

Bases: RegistrationError

Registry.register() called with a key that already exists.

Registration is add-only (design spec AD-RG-04) -- re-registering an existing key is always rejected, never silently accepted as an update.

RegistrationError

RegistrationError(message, *, details=None)

Bases: MineProductivityError

Base of registry-specific errors.

UnregisteredLookupError

UnregisteredLookupError(message, *, details=None)

Bases: NotFoundError

Registry.get() found no item for the given key.

VersionIncompatibleError

VersionIncompatibleError(message, *, details=None)

Bases: RegistrationError

A plugin's declared core_version_range excludes the installed core version.

Registry

Registry(*, name)

Bases: Generic[TKey, TItem]

A generic, type-safe, metadata-aware registration container.

Every domain-specific registry (KPIRegistry, ConnectorRegistry, EntityTypeRegistry, AnalyticsRegistry) is a type alias over this class, not a subclass with new behavior -- composition over inheritance, and one implementation to trust platform-wide (design spec AD-RG-01).

A deliberate structural echo of :class:~mineproductivity.core.repository.BaseRepository: a registry is, conceptually, a repository whose entities are types/classes/ callables instead of domain entities. Registry does not subclass BaseRepository, since its keys are typically strings/codes rather than BaseEntity ids.

Examples:

>>> registry: Registry[str, type] = Registry(name="widgets")
>>> class Widget: pass
>>> registry.register("widget", Widget).is_ok
True
>>> registry.get("widget") is Widget
True
>>> registry.register("widget", Widget).is_ok
False

name property

name

The domain-facing name this registry was constructed with (e.g. "kpis", "connectors") -- used in log messages and error details.

register

register(key, item, *, metadata=None)

Register item under key.

Registration is add-only (design spec §19, AD-RG-04): re- registering an existing key -- even with the exact same item -- is always rejected, never silently accepted as an update.

Returns:

Type Description
Result[None]

Result.ok(None) on success, or Result.err(DuplicateRegistrationError) if key is already registered.

lookup

lookup(key)

Non-raising lookup of key.

get

get(key)

Raising lookup of key.

Raises:

Type Description
UnregisteredLookupError

If no item is registered under key.

list

list(specification=None)

All registered items, optionally filtered by specification -- mirrors :meth:~mineproductivity.core.repository.BaseRepository.list's shape deliberately.

metadata_for

metadata_for(key)

Non-raising lookup of the metadata attached to key at registration time, if any.

VersionCompatibility

Stateless version-range compatibility checks.

is_compatible staticmethod

is_compatible(plugin_range, core_version)

Whether core_version falls within plugin_range's half-open [min_version, max_version_exclusive) range.

check_or_raise staticmethod

check_or_raise(plugin_range, core_version)

Raise :class:~mineproductivity.registry.exceptions.VersionIncompatibleError if core_version falls outside plugin_range.

VersionRange dataclass

VersionRange(min_version, max_version_exclusive)

Bases: BaseValueObject

A half-open version range: [min_version, max_version_exclusive), e.g. mineproductivity>=1.0,<2.0 (Cookbook Part I, Ch. 9).

registered_in

registered_in(registry, *, key_of, metadata_of=None)

Build a decorator that registers the decorated item into registry, deriving its key (and, optionally, its metadata) from the item itself.

Suitable whenever registration needs nothing beyond "derive a key, reject a duplicate" -- mineproductivity.connectors.register_connector is exactly this, a thin, partially-applied wrapper around registered_in(). A domain package that needs additional registration-time validation implements its own @register instead, calling Registry.register() directly: mineproductivity.kpis.register does this to also raise KPICircularDependencyError at registration time (never deferred to first use). mineproductivity.ontology.register_equipment is unrelated to this mechanism entirely -- ontology sits below registry in the dependency stack (core -> ontology -> events -> registry) and cannot import it; its entity-type registry is a separate, internal mechanism. "One discovery pattern for the whole ecosystem" (Cookbook Part I, Ch. 9) means every domain package builds its @register the same shape -- decorator + typed registry + raising lookup -- not that every one is literally built from this one function.

Examples:

>>> REGISTRY: Registry[str, type] = Registry(name="kpis")
>>> register = registered_in(REGISTRY, key_of=lambda cls: cls.__name__)
>>> @register
... class FuelPerTonne: pass
>>> REGISTRY.get("FuelPerTonne") is FuelPerTonne
True