diff --git a/pyproject.toml b/pyproject.toml index feed8b2..c282025 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,42 @@ [build-system] -requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"] +requires = ["setuptools>=61", "wheel", "setuptools_scm[toml]>=3.4"] build-backend = "setuptools.build_meta" +[project] +name = "xpublish_opendap" +description = "" +readme = "README.md" +requires-python = ">=3.9" +keywords = [] +license = { file = "LICENSE.txt" } + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Science/Research", + "Operating System :: OS Independent", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Topic :: Scientific/Engineering", +] + +dynamic = ["version", "dependencies"] + +[tool.setuptools] +packages = ["xpublish_opendap"] + +[tool.setuptools.dynamic] +dependencies = { file = ["requirements.txt"] } + +[tool.setuptools_scm] +write_to = "xpublish_opendap/_version.py" + +[project.entry-points."xpublish.plugin"] +opendap = "xpublish_opendap.plugin:OpenDapPlugin" + [tool.interrogate] ignore-init-method = true ignore-init-module = false diff --git a/requirements.txt b/requirements.txt index bd28589..4b7188f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ opendap-protocol<1.2.0 xarray -xpublish<=0.1.0 +xpublish diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 4d7ef6a..0000000 --- a/setup.cfg +++ /dev/null @@ -1,50 +0,0 @@ -[metadata] -name = xpublish_opendap -description = My Awesome module -author = AUTHOR NAME -author_email = AUTHOR@EMAIL.COM -url = https://github.com/ioos/ioos-python-package-skeleton -long_description = file: README.md -long_description_content_type = text/markdown -license = BSD-3-Clause -license_file = LICENSE.txt -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Science/Research - Operating System :: OS Independent - License :: OSI Approved :: BSD License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Topic :: Scientific/Engineering - -[options] -zip_safe = False -install_requires = - numpy - requests -python_requires = >=3.6 -packages = find: - -[sdist] -formats = gztar - -[check-manifest] -ignore = - *.yml - *.yaml - .coveragerc - docs - docs/* - *.enc - notebooks - notebooks/* - tests - tests/* - -[flake8] -max-line-length = 105 -ignore = E203, E501, W503 -exclude = xpublish_opendap/_version.py diff --git a/setup.py b/setup.py deleted file mode 100644 index 4fec67b..0000000 --- a/setup.py +++ /dev/null @@ -1,12 +0,0 @@ -from setuptools import setup - -# CHANGE PKG NAME HERE -pkg_name = "xpublish_opendap" - -setup( - use_scm_version={ - "write_to": f"{pkg_name}/_version.py", - "write_to_template": '__version__ = "{version}"', - "tag_regex": r"^(?Pv)?(?P[^\+]+)(?P.*)?$", - }, -) diff --git a/xpublish_opendap/__init__.py b/xpublish_opendap/__init__.py index 2d44561..c844332 100644 --- a/xpublish_opendap/__init__.py +++ b/xpublish_opendap/__init__.py @@ -2,9 +2,9 @@ xpublish_opendap provides an OpenDAP router for Xpublish """ -from xpublish_opendap.router import dap_router +from xpublish_opendap.plugin import OpenDapPlugin -__all__ = ["dap_router"] +__all__ = ["OpenDapPlugin"] try: from ._version import __version__ diff --git a/xpublish_opendap/plugin.py b/xpublish_opendap/plugin.py new file mode 100644 index 0000000..d30e9c2 --- /dev/null +++ b/xpublish_opendap/plugin.py @@ -0,0 +1,85 @@ +""" +xpublish_opendap + +OpenDAP router for Xpublish +""" +import logging +from typing import List +from urllib import parse + +import cachey +import opendap_protocol as dap +import xarray as xr +from fastapi import APIRouter, Depends, Request +from fastapi.responses import StreamingResponse +from xpublish import Plugin, hookimpl + +from .dap_xarray import dap_dataset + +logger = logging.getLogger("uvicorn") + + + +class OpenDapPlugin(Plugin): + name = "opendap" + + dataset_router_prefix = "/opendap" + dataset_router_tags: List[str] = ["opendap"] + + @hookimpl + def dataset_router(self): + router = APIRouter(prefix=self.dataset_router_prefix, tags=self.dataset_router_tags) + + def get_dap_dataset( + dataset_id: str, + ds: xr.Dataset = Depends(self.dependencies.dataset), + cache: cachey.Cache = Depends(self.dependencies.cache), + ): + """ + Get a dataset that has been translated to opendap + """ + cache_key = f"opendap_dataset_{dataset_id}" + dataset = cache.get(cache_key) + + if dataset is None: + dataset = dap_dataset(ds, dataset_id) + + cache.put(cache_key, dataset, 99999) + + return dataset + + def dap_constraint(request: Request) -> str: + """Parse DAP constraints from request""" + constraint = parse.unquote(request.url.components[3]) + + return constraint + + @router.get(".dds") + def dds_response( + constraint=Depends(dap_constraint), dataset: dap.Dataset = Depends(get_dap_dataset), + ): + """OpenDAP DDS response (types and dimension metadata)""" + return StreamingResponse( + dataset.dds(constraint=constraint), media_type="text/plain", + ) + + @router.get(".das") + def das_response( + constraint=Depends(dap_constraint), dataset: dap.Dataset = Depends(get_dap_dataset), + ): + """OpenDAP DAS response (attribute metadata)""" + return StreamingResponse( + dataset.das(constraint=constraint), media_type="text/plain", + ) + + + @router.get(".dods") + def dods_response( + constraint=Depends(dap_constraint), dataset: dap.Dataset = Depends(get_dap_dataset), + ): + """OpenDAP dods response (data access)""" + return StreamingResponse( + dataset.dods(constraint=constraint), media_type="application/octet-stream", + ) + + return router diff --git a/xpublish_opendap/router.py b/xpublish_opendap/router.py deleted file mode 100644 index d871bf5..0000000 --- a/xpublish_opendap/router.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -xpublish_opendap - -OpenDAP router for Xpublish -""" -import logging -from urllib import parse - -import cachey -import opendap_protocol as dap -import xarray as xr -from fastapi import APIRouter, Depends, Request -from fastapi.responses import StreamingResponse -from xpublish.dependencies import get_cache, get_dataset - -from .dap_xarray import dap_dataset - -logger = logging.getLogger("uvicorn") - - -dap_router = APIRouter() - - -def get_dap_dataset( - dataset_id: str, - ds: xr.Dataset = Depends(get_dataset), - cache: cachey.Cache = Depends(get_cache), -): - """ - Get a dataset that has been translated to opendap - """ - cache_key = f"opendap_dataset_{dataset_id}" - dataset = cache.get(cache_key) - - if dataset is None: - dataset = dap_dataset(ds, dataset_id) - - cache.put(cache_key, dataset, 99999) - - return dataset - - -def dap_constraint(request: Request) -> str: - """Parse DAP constraints from request""" - constraint = parse.unquote(request.url.components[3]) - - return constraint - - -@dap_router.get(".dds") -def dds_response( - constraint=Depends(dap_constraint), dataset: dap.Dataset = Depends(get_dap_dataset), -): - """OpenDAP DDS response (types and dimension metadata)""" - return StreamingResponse( - dataset.dds(constraint=constraint), media_type="text/plain", - ) - - -@dap_router.get(".das") -def das_response( - constraint=Depends(dap_constraint), dataset: dap.Dataset = Depends(get_dap_dataset), -): - """OpenDAP DAS response (attribute metadata)""" - return StreamingResponse( - dataset.das(constraint=constraint), media_type="text/plain", - ) - - -@dap_router.get(".dods") -def dods_response( - constraint=Depends(dap_constraint), dataset: dap.Dataset = Depends(get_dap_dataset), -): - """OpenDAP dods response (data access)""" - return StreamingResponse( - dataset.dods(constraint=constraint), media_type="application/octet-stream", - )