mrkeyoor.com_
Tue 22 Sept 22:34 UTC
PyPIUtilsupdated 21 Sept 2026

catalogue review

catalogue 2.0.10 is a small Python registry that turns a string such as `json` into a function or other object kept in process memory. A package can expose named extension points, let another module register implementations with a decorator, and store the chosen name in config instead of trying to serialize a callable. It can also resolve plugins published through Python entry points. Our install occupied 1 MB and imported in 0.21 seconds, but the package has no `py.typed` marker and does not check the signature of anything registered. The current release only updates packaging and tests for Python 3.12; it does not add a new registry operation.

Verdict

catalogue 2.0.10 installed in 0.2 seconds and used 1 MB in our sandbox, making it a cheap fit for libraries that need one serializable name per extension. Skip it when you need typed hook contracts, several handlers per event, or plugins added without restarting the process.

We installed it

Lab card: what happened when we installed catalogueScreenshot of catalogue documentation
Install✓ · 0.2s1 package on disk · 1 MB
Importimport catalogue in 0.21s · pure Python · requires Python >=3.6
Known vulns0(pip-audit)

Answers from our run

Does catalogue install cleanly?

Yes. In a fresh container with an empty cache, pip install catalogue finished in 0.2s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.

What does catalogue need to run?

Python >=3.6, and nothing compiled: it is pure Python. In our run import catalogue succeeded in 0.21s.

catalogue or pluggy: which should you use?

pluggy: Use it when a declared hook may have several implementations and ordering or wrappers matter. catalogue 2.0.10 installed in 0.2 seconds and used 1 MB in our sandbox, making it a cheap fit for libraries that need one serializable name per extension.

When should you not use catalogue?

The callbacks live inside one application and never need serializable names. A normal dict or an injected callable avoids global state and import-order behavior.

API stability5/5Version 2.0.10 keeps a short public API: create a Registry, register an object, resolve one name, list a namespace, inspect an object, or opt into package entry points. Its release note covers Python 3.12 packaging and test updates rather than behavior changes. The README also states the compatibility boundary plainly: 2.x requires Python 3.6 or newer, while Python 2 users stay on 1.x.
Docs4/5The repository README follows one loader example through namespace creation, decorator registration, lookup, serialization, and entry-point discovery. It documents each Registry method with runnable fragments. The operational details that affect real projects are absent: entry points are captured once at import, duplicate registrations overwrite values, the mapping is process-wide, and no public cleanup method exists. Those facts require reading the compact source file.
Maintenance3/5GitHub reported 183 stars, 6 open issues and pull requests, no archive flag, and a repository push on 2026-03-27. PyPI still serves 2.0.10, uploaded on 2023-09-25, whose release note only mentions Python 3.12 packaging and test-suite updates. Continued repository activity is useful evidence, but users on later Python releases have not received a newer published compatibility release.
Ecosystem4/5The recorded week shows 7,087,841 downloads, and catalogue uses standard Python package entry points instead of defining a separate plugin manifest. The README's concrete extension example comes from spaCy, where saved component names need to resolve after installation. The tradeoff is scope: catalogue supplies naming and loading, while hook order, validation, isolation, lifecycle, and error policy stay in the host library.

Use it if

  • A Python library needs callable names that remain readable in JSON, TOML, logs, or saved pipeline metadata.
  • Optional wheels should add implementations through a packaging entry-point group without requiring users to import each plugin module.
  • Your extension model is exactly one name resolving to one object, and your own package can validate that object's callable contract.
  • Registration can happen during process startup, before workers begin serving requests or loading saved configuration.
Skip it if

Setup reality

Our fresh Python 3.12 install of catalogue 2.0.10 finished in 0.2 seconds. It left 1 package and 1 MB on disk, with 2 direct dependencies and no native build step. import catalogue worked in 0.21 seconds, and pip-audit reported 0 known vulnerabilities. The package requires Python 3.6 or newer and is pure Python. Its wheel does not contain py.typed, so treat values returned by the registry as untyped at package boundaries.

No service, credential, environment variable, or config file is required. The setup choice is where the namespace is created. Define catalogue.create('acme', 'loaders') once in a stable module, then import that Registry everywhere else. A second create call for the same namespace raises RegistryError, even if it comes from another test or import path in the same interpreter.

Decorator registration runs only when Python executes the module containing the decorator. For separately installed plugins, create the Registry with entry_points=True and publish a group whose name is the namespace parts joined with underscores. catalogue 2.0.10 loads the selected entry point during lookup. An import error inside that plugin therefore appears when get, get_all, or even a membership check tries to load it.

Entry-point metadata is snapshotted once at import catalogue, so installing a new plugin into a live process does not refresh discovery. Registered objects also remain in the module-level mapping for the interpreter's lifetime. Reusing a full name overwrites its value. Give tests unique namespaces or restore the mapping in a fixture, and validate callable arguments yourself before invoking anything returned as Any.

Patterns

Create one shared namespace create-registry

# acme/loaders.py
import catalogue

loaders = catalogue.create('acme', 'loaders')

Version 2.0.10 raises catalogue.RegistryError if the same namespace has already been created in this interpreter.

Register a function when its module imports register-decorator

from acme.loaders import loaders

@loaders.register('json')
def read_json(path):
    import json
    with open(path, encoding='utf8') as stream:
        return json.load(stream)

The name appears only after Python executes this module, so startup code must import it before calling `get('json')`.

Register an existing object register-function

from acme.loaders import loaders

def read_bytes(path):
    return path.read_bytes()

loaders.register('bytes', func=read_bytes)

`func` is keyword-only. Registering `bytes` again replaces the existing object instead of raising an error.

Use the Registry itself as a decorator callable-registry

from acme.loaders import loaders

@loaders('text')
def read_text(path):
    return path.read_text(encoding='utf8')

Calling a Registry forwards to `register`, so it has the same import-time side effect and duplicate-name behavior.

Resolve a loader named in configuration resolve-name

from acme.loaders import loaders

def load_file(path, config):
    loader = loaders.get(config['loader'])
    return loader(path)

An unknown name raises RegistryError and includes the names currently visible under this namespace.

List every object in a namespace list-registrations

from acme.loaders import loaders

for name, loader in loaders.get_all().items():
    print(f'{name}: {loader!r}')

With entry points enabled, `get_all()` loads every matching plugin; one broken plugin import can stop the whole call.

Check for a loader before resolving it check-name

from acme.loaders import loaders

loader = loaders.get('json') if 'json' in loaders else read_text
result = loader(path)

For an entry-point Registry, the membership check can load plugin code instead of performing a side-effect-free dictionary test.

Discover extensions from installed wheels enable-entry-points

import catalogue

renderers = catalogue.create(
    'acme_reports',
    'renderers',
    entry_points=True,
)

This Registry reads the `acme_reports_renderers` entry-point group that catalogue captured when the process imported it.

Publish an extension in pyproject.toml publish-entry-point

[project.entry-points.acme_reports_renderers]
pdf = 'acme_pdf:render_pdf'

The group must match the Registry namespace joined with underscores, and the value must identify an importable module object.

Fall back when one entry point is absent optional-plugin

renderer = renderers.get_entry_point('pdf', default=None)
if renderer is None:
    renderer = render_text

output = renderer(report)

The default handles a missing name. If an installed `pdf` plugin raises while importing, catalogue propagates that exception.

Find where a registered function came from inspect-registration

details = loaders.find('json')
print(details['module'])
print(details['file'], details['line_no'])
print(details['docstring'])

For Cython and other non-inspectable objects, version 2.0.10 can return `None` for the file and line number.

Give each test a fresh namespace isolate-tests

from uuid import uuid4
import catalogue

registry = catalogue.create('tests', uuid4().hex)
registry.register('fake', func=lambda value: value)

There is no public unregister API. A unique namespace avoids collisions while the module-level registry survives for the test process.

Alternatives

PackageRegistryPick it when
pluggyPyPIUse it when a declared hook may have several implementations and ordering or wrappers matter.
stevedorePyPIUse it when loading named drivers and managing enabled extensions are the center of the plugin system.
importlib-metadataPyPIUse it on older Python versions when direct entry-point enumeration is enough and you do not need a registry wrapper.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.