Skip to content

Commit

Permalink
Automatically load external frontends in load()
Browse files Browse the repository at this point in the history
  • Loading branch information
Xarthisius committed Jan 9, 2023
1 parent ed665c5 commit 8f783fa
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 0 deletions.
26 changes: 26 additions & 0 deletions doc/source/developing/extensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,32 @@ In subsequent versions, we plan to include in yt a catalog of known extensions
and where to find them; this will put discoverability directly into the code
base.

Frontend as an extension
------------------------

Starting with version 4.2 of yt, any externally installed package that exports
:class:`~yt.data_objects.static_output.Dataset` subclass as an entrypoint in
``yt.frontends`` namespace in ``setup.py``:

.. code-block:: python
setup(
# ...,
entry_points={"yt.frontends": ["myFrontend = my_frontend.api.MyFrontendDataset"]}
)
or ``pyproject.toml``:

.. code-block:: toml
[project.entry-points."yt.frontends"]
IdefixVtkDataset = "yt_idefix.data_structures:IdefixVtkDataset"
IdefixDmpDataset = "yt_idefix.data_structures:IdefixDmpDataset"
PlutoVtkDataset = "yt_idefix.data_structures:PlutoVtkDataset"
will be automatically loaded and immediately available in
:func:`~yt.loaders.load`.

Extension Template
------------------

Expand Down
15 changes: 15 additions & 0 deletions yt/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,21 @@ def load(
if not fn.startswith("http"):
fn = str(lookup_on_disk_data(fn))

# import it here cause it's costly
from importlib.metadata import entry_points

# overly complicated dance to suppress spurious warning
# https://github.com/python/importlib_metadata/pull/278
entrypoints = entry_points()
if hasattr(entrypoints, "select"):
external_frontends = entrypoints.select(group="yt.frontends")
else:
external_frontends = entrypoints.get("yt.frontends", [])

# Ensure that external frontends are loaded
for entrypoint in external_frontends:
entrypoint.load()

candidates = []
for cls in output_type_registry.values():
if cls._is_valid(fn, *args, **kwargs):
Expand Down
42 changes: 42 additions & 0 deletions yt/tests/test_external_frontends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import tempfile
from unittest import mock

from yt.data_objects.static_output import Dataset
from yt.geometry.grid_geometry_handler import GridIndex
from yt.loaders import load
from yt.utilities.object_registries import output_type_registry


class MockEntryPoint:
@classmethod
def load(cls):
class MockHierarchy(GridIndex):
grid = None

class MockDataset(Dataset):
_index_class = MockHierarchy

def _parse_parameter_file(self):
self.current_time = 1.0

def _set_code_unit_attributes(self):
self.length_unit = self.quan(1.0, "code_length")
self.mass_unit = self.quan(1.0, "code_mass")
self.time_unit = self.quan(1.0, "code_time")

@classmethod
def _is_valid(cls, filename, *args, **kwargs):
return True


def test_external_frontend():
assert "MockDataset" not in output_type_registry

with tempfile.NamedTemporaryFile() as test_file:
with mock.patch("importlib.metadata.entry_points") as ep:
ep.return_value = {"yt.frontends": [MockEntryPoint]}
ds = load(test_file.name)
assert "MockDataset" in str(ds.__class__)

assert "MockDataset" in output_type_registry
output_type_registry.pop("MockDataset")

0 comments on commit 8f783fa

Please sign in to comment.