Skip to content

Commit

Permalink
CLN: Remove entry points for models, metrics. Fixes #601
Browse files Browse the repository at this point in the history
Remove since they are basically unused
by anyone but us, but require maintenance, test, etc.

Squash commits:
- Remove vak/entry_points.py
- Remove import of entry_points from vak/__init__.py
- Remove model and metric entry points from pyproject.toml
- Rewrite models/models.py to not use entry points
- Fix use of models.find in config/validators.py
- Fix use of find in config/models.py
- Remove entry point test in
  tests/test_models/test_teenytweetynet.py
- Fix entry point test in tests/test_models/test_tweetynet.py
  • Loading branch information
NickleDave committed Feb 24, 2023
1 parent 8c71b7d commit b897863
Show file tree
Hide file tree
Showing 9 changed files with 36 additions and 95 deletions.
11 changes: 1 addition & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,10 @@ Documentation = "https://vak.readthedocs.io"
[project.scripts]
vak = 'vak.__main__:main'

[project.entry-points."vak.models"]
TeenyTweetyNetModel = 'vak.models.teenytweetynet:TeenyTweetyNet'
TweetyNetModel = 'vak.models.tweetynet:TweetyNet'

[project.entry-points."vak.metrics"]
Accuracy = 'vak.metrics.Accuracy'
Levenshtein = 'vak.metrics.Levenshtein'
SegmentErrorRate = 'vak.metrics.SegmentErrorRate'

[tool.flit.sdist]
exclude = [
"tests/data_for_tests"
]

[tool.pytest.ini_options]
filterwarnings = ["ignore:::.*torch.utils.tensorboard",]
filterwarnings = ["ignore:::.*torch.utils.tensorboard",]
2 changes: 0 additions & 2 deletions src/vak/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
curvefit,
datasets,
device,
entry_points,
files,
io,
labeled_timebins,
Expand Down Expand Up @@ -54,7 +53,6 @@
"csv",
"datasets",
"device",
"entry_points",
"files",
"io",
"labeled_timebins",
Expand Down
14 changes: 5 additions & 9 deletions src/vak/config/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,12 @@ def map_from_config_dict(config_dict, model_names):
# to avoid circular dependencies
# (user would be unable to import models in other packages
# if the module in the other package needed to `import vak`)
MODELS = {model_name: model_builder for model_name, model_builder in models.find()}
MODEL_NAMES = list(models.models.BUILTIN_MODELS.keys())
for model_name in model_names:
if model_name not in MODELS:
# try appending 'Model' to name
tmp_model_name = f"{model_name}Model"
if tmp_model_name not in MODELS:
raise ValueError(
f"Did not find an installed model named {model_name} or {tmp_model_name}. "
f"Installed models are: {list(MODELS.keys())}"
)
if model_name not in MODEL_NAMES:
raise ValueError(
f"Invalid model name: {model_name}.\nValid model names are: {MODEL_NAMES}"
)

# now see if we can find corresponding sections in config
sections = list(config_dict.keys())
Expand Down
8 changes: 4 additions & 4 deletions src/vak/config/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ def is_a_file(instance, attribute, value):


def is_valid_model_name(instance, attribute, value):
MODEL_NAMES = [model_name for model_name, model_builder in models.find()]
MODEL_NAMES = list(models.models.BUILTIN_MODELS.keys())
for model_name in value:
if model_name not in MODEL_NAMES and f"{model_name}Model" not in MODEL_NAMES:
if model_name not in MODEL_NAMES:
raise ValueError(
f"Model {model_name} not found when importing installed models."
f"Invalid model name: {model_name}.\nValid model names are: {MODEL_NAMES}"
)


Expand Down Expand Up @@ -91,7 +91,7 @@ def are_sections_valid(config_dict, toml_path=None):
f"Please use just one command besides `prep` per .toml configuration file"
)

MODEL_NAMES = [model_name for model_name, model_builder in models.find()]
MODEL_NAMES = list(models.models.BUILTIN_MODELS.keys())
# add model names to valid sections so users can define model config in sections
valid_sections = VALID_SECTIONS + MODEL_NAMES
for section in sections:
Expand Down
8 changes: 0 additions & 8 deletions src/vak/entry_points.py

This file was deleted.

3 changes: 1 addition & 2 deletions src/vak/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
definition,
)
from .base import Model
from .models import find, from_model_config_map
from .models import from_model_config_map
from .teenytweetynet import TeenyTweetyNet
from .tweetynet import TweetyNet
from .windowed_frame_classification_model import WindowedFrameClassificationModel
Expand All @@ -14,7 +14,6 @@
"base",
"decorator",
"definition",
"find",
"from_model_config_map",
"Model",
"TeenyTweetyNet",
Expand Down
65 changes: 25 additions & 40 deletions src/vak/models/models.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,25 @@
"""module that contains helper function to load models
"""Helper function to load models"""
from __future__ import annotations

Models in separate packages should make themselves available to vak by including
'vak.models' in the entry_points of their setup.py file.
from .tweetynet import TweetyNet
from .teenytweetynet import TeenyTweetyNet

For example, if you had a package `grunet` containing a model
that was instantiated by a function `GRUnet`,
then that package would include the following in its setup.py file:

setup(
...
entry_points={'vak.models': 'GRUnet = grunet:GRUnet'},
...
)
# TODO: Replace constant with decorator that registers models, https://github.com/vocalpy/vak/issues/623
BUILTIN_MODELS = {
'TweetyNet': TweetyNet,
'TeenyTweetyNet': TeenyTweetyNet
}

For more detail on entry points in Python, see:
https://packaging.python.org/guides/creating-and-discovering-plugins/#using-package-metadata
https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins
https://amir.rachum.com/blog/2017/07/28/python-entry-points/
"""
from .. import entry_points
MODEL_NAMES = list(BUILTIN_MODELS.keys())

MODELS_ENTRY_POINT = "vak.models"


def find():
"""find installed vak.models
returns generator that yields model name and function for loading
"""
for entrypoint in entry_points._iter(MODELS_ENTRY_POINT):
yield entrypoint.name, entrypoint.load()


def from_model_config_map(model_config_map,
def from_model_config_map(model_config_map: dict[str: dict],
# TODO: move num_classes / input_shape into model configs
num_classes,
input_shape,
labelmap):
"""get models that are ready to train, given their names and configurations.
num_classes: int,
input_shape: tuple[int, int, int],
labelmap: dict) -> dict:
"""Get models that are ready to train, given their names and configurations.
Given a dictionary that maps model names to configurations,
along with the number of classes they should be trained to discriminate and their input shape,
Expand Down Expand Up @@ -66,7 +48,7 @@ def from_model_config_map(model_config_map,
models_map : dict
where keys are model names and values are instances of the models, ready for training
"""
MODELS = {model_name: model_builder for model_name, model_builder in find()}
import vak.models

models_map = {}
for model_name, model_config in model_config_map.items():
Expand All @@ -77,12 +59,15 @@ def from_model_config_map(model_config_map,
num_classes=num_classes,
input_shape=input_shape,
)

try:
model = MODELS[model_name].from_config(config=model_config, labelmap=labelmap)
except KeyError:
model = MODELS[f"{model_name}Model"].from_config(
config=model_config,
labelmap=labelmap
)
model_class = getattr(vak.models, model_name)
except AttributeError as e:
raise ValueError(
f"Invalid model name: '{model_name}'.\nValid model names are: {MODEL_NAMES}"
) from e

model = model_class.from_config(config=model_config, labelmap=labelmap)
models_map[model_name] = model

return models_map
10 changes: 0 additions & 10 deletions tests/test_models/test_teenytweetynet.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,6 @@
from .test_tweetynet import TEST_INIT_ARGVALS


def test_installed():
# makes sure the entry point loads properly
if "vak" in sys.modules:
sys.modules.pop("vak")
import vak

models = [name for name, class_ in sorted(vak.models.find())]
assert "TeenyTweetyNetModel" in models


class TestTeenyTweetyNet:
def test_model_is_decorated(self):
assert issubclass(vak.models.TeenyTweetyNet,
Expand Down
10 changes: 0 additions & 10 deletions tests/test_models/test_tweetynet.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,6 @@
TEST_INIT_ARGVALS = itertools.product(LABELMAPS, INPUT_SHAPES)


def test_installed():
# makes sure the entry point loads properly
if "vak" in sys.modules:
sys.modules.pop("vak")
import vak

models = [name for name, class_ in sorted(vak.models.find())]
assert "TweetyNetModel" in models


class TestTweetyNet:
def test_model_is_decorated(self):
assert issubclass(vak.models.TweetyNet,
Expand Down

0 comments on commit b897863

Please sign in to comment.