Skip to content

Commit

Permalink
Entrypoints (#300)
Browse files Browse the repository at this point in the history
* Allow codec registry via entrypoints

* remove debug

* Add release note

* Add test

* Mark required but unused test methods as no cover

* better exception

* Skip test is no entryoints

* Add logging
  • Loading branch information
martindurant authored Jan 26, 2022
1 parent 500c048 commit ed22a59
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 3 deletions.
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

0 comments on commit ed22a59

Please sign in to comment.