Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Entrypoints #300

Merged
merged 8 commits into from
Jan 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/release.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Unreleased

.. _release_0.9.1:

* Add ability to find codecs via entrypoints
By :user:`Martin Durant <martindurant>`, :issue:`290`.

0.9.1
-----

Expand Down
27 changes: 24 additions & 3 deletions numcodecs/registry.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
"""The registry module provides some simple convenience functions to enable
applications to dynamically register and look-up codec classes."""
import logging


logger = logging.getLogger("numcodecs")
codec_registry = dict()
entries = {}


def run_entrypoints():
import entrypoints
entries.clear()
entries.update(entrypoints.get_group_named("numcodecs.codecs"))


try:
run_entrypoints()
except (ImportError, ModuleNotFoundError): # pragma: no cover
# marked "no cover" since we will include entrypoints in test env
pass


def get_codec(config):
Expand Down Expand Up @@ -30,8 +45,13 @@ def get_codec(config):
codec_id = config.pop('id', None)
cls = codec_registry.get(codec_id)
if cls is None:
raise ValueError('codec not available: %r' % codec_id)
return cls.from_config(config)
if codec_id in entries:
logger.debug("Auto loading codec '%s' from entrypoint", codec_id)
cls = entries[codec_id].load()
register_codec(cls, codec_id=codec_id)
if cls:
return cls.from_config(config)
raise ValueError('codec not available: %r' % codec_id)


def register_codec(cls, codec_id=None):
Expand All @@ -50,4 +70,5 @@ def register_codec(cls, codec_id=None):
"""
if codec_id is None:
codec_id = cls.codec_id
logger.debug("Registering codec '%s'", codec_id)
codec_registry[codec_id] = cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[numcodecs.codecs]
test = package_with_entrypoint:TestCodec
12 changes: 12 additions & 0 deletions numcodecs/tests/package_with_entrypoint/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from numcodecs.abc import Codec


class TestCodec(Codec):

codec_id = "test"

def encode(self, buf): # pragma: no cover
pass

def decode(self, buf, out=None): # pragma: no cover
pass
25 changes: 25 additions & 0 deletions numcodecs/tests/test_entrypoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import os.path
import sys

import pytest

import numcodecs.registry


here = os.path.abspath(os.path.dirname(__file__))
pytest.importorskip("entrypoints")


@pytest.fixture()
def set_path():
sys.path.append(here)
numcodecs.registry.run_entrypoints()
yield
sys.path.remove(here)
numcodecs.registry.run_entrypoints()
numcodecs.registry.codec_registry.pop("test")


def test_entrypoint_codec(set_path):
cls = numcodecs.registry.get_codec({"id": "test"})
assert cls.codec_id == "test"
1 change: 1 addition & 0 deletions requirements_test.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
coverage
coveralls
entrypoints
flake8
pytest
pytest-cov
Expand Down