Skip to content

Commit ed22a59

Browse files
authored
Entrypoints (#300)
* 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
1 parent 500c048 commit ed22a59

File tree

6 files changed

+67
-3
lines changed

6 files changed

+67
-3
lines changed

docs/release.rst

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ Unreleased
88

99
.. _release_0.9.1:
1010

11+
* Add ability to find codecs via entrypoints
12+
By :user:`Martin Durant <martindurant>`, :issue:`290`.
13+
1114
0.9.1
1215
-----
1316

numcodecs/registry.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
11
"""The registry module provides some simple convenience functions to enable
22
applications to dynamically register and look-up codec classes."""
3+
import logging
34

4-
5+
logger = logging.getLogger("numcodecs")
56
codec_registry = dict()
7+
entries = {}
8+
9+
10+
def run_entrypoints():
11+
import entrypoints
12+
entries.clear()
13+
entries.update(entrypoints.get_group_named("numcodecs.codecs"))
14+
15+
16+
try:
17+
run_entrypoints()
18+
except (ImportError, ModuleNotFoundError): # pragma: no cover
19+
# marked "no cover" since we will include entrypoints in test env
20+
pass
621

722

823
def get_codec(config):
@@ -30,8 +45,13 @@ def get_codec(config):
3045
codec_id = config.pop('id', None)
3146
cls = codec_registry.get(codec_id)
3247
if cls is None:
33-
raise ValueError('codec not available: %r' % codec_id)
34-
return cls.from_config(config)
48+
if codec_id in entries:
49+
logger.debug("Auto loading codec '%s' from entrypoint", codec_id)
50+
cls = entries[codec_id].load()
51+
register_codec(cls, codec_id=codec_id)
52+
if cls:
53+
return cls.from_config(config)
54+
raise ValueError('codec not available: %r' % codec_id)
3555

3656

3757
def register_codec(cls, codec_id=None):
@@ -50,4 +70,5 @@ def register_codec(cls, codec_id=None):
5070
"""
5171
if codec_id is None:
5272
codec_id = cls.codec_id
73+
logger.debug("Registering codec '%s'", codec_id)
5374
codec_registry[codec_id] = cls
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[numcodecs.codecs]
2+
test = package_with_entrypoint:TestCodec
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from numcodecs.abc import Codec
2+
3+
4+
class TestCodec(Codec):
5+
6+
codec_id = "test"
7+
8+
def encode(self, buf): # pragma: no cover
9+
pass
10+
11+
def decode(self, buf, out=None): # pragma: no cover
12+
pass

numcodecs/tests/test_entrypoints.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import os.path
2+
import sys
3+
4+
import pytest
5+
6+
import numcodecs.registry
7+
8+
9+
here = os.path.abspath(os.path.dirname(__file__))
10+
pytest.importorskip("entrypoints")
11+
12+
13+
@pytest.fixture()
14+
def set_path():
15+
sys.path.append(here)
16+
numcodecs.registry.run_entrypoints()
17+
yield
18+
sys.path.remove(here)
19+
numcodecs.registry.run_entrypoints()
20+
numcodecs.registry.codec_registry.pop("test")
21+
22+
23+
def test_entrypoint_codec(set_path):
24+
cls = numcodecs.registry.get_codec({"id": "test"})
25+
assert cls.codec_id == "test"

requirements_test.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
coverage
22
coveralls
3+
entrypoints
34
flake8
45
pytest
56
pytest-cov

0 commit comments

Comments
 (0)