forked from xpublish-community/xpublish
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Builds on top of @benbovy 's work in building router factories in xpublish-community#89 to build a plugin system. The plugin system uses entry points, which are most commonly used for console or GUI scripts. The entry_point group is `xpublish.plugin` Right now plugins can provide dataset specific and general (app) routes, with default prefixes and tags for both. Xpublish will by default load plugins via the entry point. Additionally, plugins can also be loaded directly via the init, as well as being disabled, or configured. The existing dataset router pattern also still works, so that folks aren't forced into using plugins Entry point reference: - https://setuptools.pypa.io/en/latest/userguide/entry_point.html - https://packaging.python.org/en/latest/specifications/entry-points/ - https://amir.rachum.com/amp/blog/2017/07/28/python-entry-points.html
- Loading branch information
Showing
12 changed files
with
281 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .factory import XpublishPluginFactory # noqa: F401 | ||
from .load import configure_plugins, find_plugins # noqa: F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from dataclasses import dataclass, field | ||
from typing import Callable, List, Optional | ||
|
||
import cachey | ||
import xarray as xr | ||
from fastapi import APIRouter | ||
|
||
from ..dependencies import get_cache, get_dataset, get_dataset_ids | ||
|
||
|
||
@dataclass | ||
class XpublishPluginFactory: | ||
"""Xpublish plugin factory. | ||
Xpublish plugins are designed to be automatically loaded via the entry point | ||
group `xpublish.plugin` from any installed package. | ||
Plugins can define both app (top-level) and dataset based routes, and | ||
default prefixes and tags for both. | ||
""" | ||
|
||
app_router: APIRouter = field(default_factory=APIRouter) | ||
app_router_prefix: Optional[str] = None | ||
app_router_tags: List[str] = field(default_factory=list) | ||
|
||
dataset_router: APIRouter = field(default_factory=APIRouter) | ||
dataset_router_prefix: Optional[str] = None | ||
dataset_router_tags: List[str] = field(default_factory=list) | ||
|
||
dataset_ids_dependency: Callable[..., List[str]] = get_dataset_ids | ||
dataset_dependency: Callable[..., xr.Dataset] = get_dataset | ||
cache_dependency: Callable[..., cachey.Cache] = get_cache | ||
|
||
def __post_init__(self): | ||
self.register_routes() | ||
|
||
def register_routes(self): | ||
"""Register xpublish routes.""" | ||
raise NotImplementedError() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
""" | ||
Load and configure Xpublish plugins from entry point group `xpublish.plugin` | ||
""" | ||
from importlib.metadata import entry_points | ||
from typing import Dict, List, Optional | ||
|
||
from .factory import XpublishPluginFactory | ||
|
||
|
||
def find_plugins(exclude_plugins: Optional[List[str]] = None): | ||
"""Find Xpublish plugins from entry point group `xpublish.plugin`""" | ||
exclude_plugin_names = set(exclude_plugins or []) | ||
|
||
plugins: Dict[str, XpublishPluginFactory] = {} | ||
|
||
for entry_point in entry_points()['xpublish.plugin']: | ||
if entry_point.name not in exclude_plugin_names: | ||
plugins[entry_point.name] = entry_point.load() | ||
|
||
return plugins | ||
|
||
|
||
def configure_plugins( | ||
plugins: Dict[str, XpublishPluginFactory], plugin_configs: Optional[Dict] = None | ||
): | ||
"""Initialize and configure plugins""" | ||
initialized_plugins: Dict[str, XpublishPluginFactory] = {} | ||
plugin_configs = plugin_configs or {} | ||
|
||
for name, plugin in plugins.items(): | ||
kwargs = plugin_configs.get(name, {}) | ||
initialized_plugins[name] = plugin(**kwargs) | ||
|
||
return initialized_plugins |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
""" | ||
Version information router | ||
""" | ||
from dataclasses import dataclass | ||
import importlib | ||
import sys | ||
from typing import List | ||
|
||
from ..utils.info import get_sys_info, netcdf_and_hdf5_versions | ||
from .factory import XpublishPluginFactory | ||
|
||
|
||
@dataclass | ||
class ModuleVersionPlugin(XpublishPluginFactory): | ||
"""Module and system version information""" | ||
|
||
def register_routes(self): | ||
@self.app_router.get("/versions") | ||
def get_versions(): | ||
versions = dict(get_sys_info() + netcdf_and_hdf5_versions()) | ||
modules = [ | ||
'xarray', | ||
'zarr', | ||
'numcodecs', | ||
'fastapi', | ||
'starlette', | ||
'pandas', | ||
'numpy', | ||
'dask', | ||
'distributed', | ||
'uvicorn', | ||
] | ||
for modname in modules: | ||
try: | ||
if modname in sys.modules: | ||
mod = sys.modules[modname] | ||
else: # pragma: no cover | ||
mod = importlib.import_module(modname) | ||
versions[modname] = getattr(mod, '__version__', None) | ||
except ImportError: # pragma: no cover | ||
pass | ||
return versions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.