Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add router factory classes #89

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8]
python-version: [3.7, 3.8, 3.9]
steps:
- uses: actions/[email protected]
- name: Setup Python
Expand All @@ -44,7 +44,7 @@ jobs:
python -m pytest --cov=./ --cov-report=xml --verbose
- name: Upload coverage to Codecov
uses: codecov/[email protected]
if: ${{ matrix.python-version }} == 3.7
if: ${{ matrix.python-version }} == 3.8
with:
file: ./coverage.xml
fail_ci_if_error: false
Expand Down
12 changes: 7 additions & 5 deletions xpublish/dependencies.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""
Helper functions to use a FastAPI dependencies.
"""
from typing import List

import cachey
import xarray as xr
from fastapi import Depends
Expand All @@ -9,7 +11,7 @@
from .utils.zarr import create_zmetadata, create_zvariables, zarr_metadata_key


def get_dataset_ids():
def get_dataset_ids() -> List[str]:
"""FastAPI dependency for getting the list of ids (string keys)
of the collection of datasets being served.

Expand All @@ -23,7 +25,7 @@ def get_dataset_ids():
return [] # pragma: no cover


def get_dataset(dataset_id: str):
def get_dataset(dataset_id: str) -> xr.Dataset:
"""FastAPI dependency for accessing the published xarray dataset object.

Use this callable as dependency in any FastAPI path operation
Expand All @@ -33,10 +35,10 @@ def get_dataset(dataset_id: str):
application.

"""
return None # pragma: no cover
return xr.Dataset()


def get_cache():
def get_cache() -> cachey.Cache:
"""FastAPI dependency for accessing the application's cache.

Use this callable as dependency in any FastAPI path operation
Expand All @@ -47,7 +49,7 @@ def get_cache():
application.

"""
return None # pragma: no cover
return cachey.Cache(available_bytes=1e6)


def get_zvariables(
Expand Down
6 changes: 3 additions & 3 deletions xpublish/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from fastapi import FastAPI, HTTPException

from .dependencies import get_cache, get_dataset, get_dataset_ids
from .routers import base_router, common_router, dataset_collection_router, zarr_router
from .routers import BaseFactory, ZarrFactory, common_router, dataset_collection_router
from .utils.api import (
SingleDatasetOpenAPIOverrider,
check_route_conflicts,
Expand Down Expand Up @@ -53,8 +53,8 @@ def _set_app_routers(dataset_routers=None, dataset_route_prefix=''):
# dataset-specifc api endpoints
if dataset_routers is None:
dataset_routers = [
(base_router, {'tags': ['info']}),
(zarr_router, {'tags': ['zarr']}),
(BaseFactory().router, {'tags': ['info']}),
(ZarrFactory().router, {'tags': ['zarr']}),
]

app_routers += normalize_app_routers(dataset_routers, dataset_route_prefix)
Expand Down
4 changes: 2 additions & 2 deletions xpublish/routers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .base import base_router
from .base import BaseFactory
from .common import common_router, dataset_collection_router
from .zarr import zarr_router
from .zarr import ZarrFactory
85 changes: 48 additions & 37 deletions xpublish/routers/base.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,65 @@
from dataclasses import dataclass

import xarray as xr
from fastapi import APIRouter, Depends
from fastapi import Depends
from starlette.responses import HTMLResponse
from zarr.storage import attrs_key

from ..dependencies import get_dataset, get_zmetadata, get_zvariables

base_router = APIRouter()

from ..dependencies import get_zmetadata, get_zvariables
from .factory import XpublishFactory

@base_router.get('/')
def html_representation(dataset: xr.Dataset = Depends(get_dataset)):
"""Returns a HTML representation of the dataset."""

with xr.set_options(display_style='html'):
return HTMLResponse(dataset._repr_html_())
@dataclass
class BaseFactory(XpublishFactory):
"""API entry-points providing basic information about the dataset(s)."""

def register_routes(self):
@self.router.get('/')
def html_representation(
dataset=Depends(self.dataset_dependency),
):
"""Returns a HTML representation of the dataset."""

@base_router.get('/keys')
def list_keys(dataset: xr.Dataset = Depends(get_dataset)):
return list(dataset.variables)
with xr.set_options(display_style='html'):
return HTMLResponse(dataset._repr_html_())

@self.router.get('/keys')
def list_keys(
dataset=Depends(self.dataset_dependency),
):
return list(dataset.variables)

@base_router.get('/dict')
def to_dict(dataset: xr.Dataset = Depends(get_dataset)):
return dataset.to_dict(data=False)
@self.router.get('/dict')
def to_dict(
dataset=Depends(self.dataset_dependency),
):
return dataset.to_dict(data=False)

@self.router.get('/info')
def info(
dataset=Depends(self.dataset_dependency),
cache=Depends(self.cache_dependency),
):
"""Dataset schema (close to the NCO-JSON schema)."""

@base_router.get('/info')
def info(
dataset: xr.Dataset = Depends(get_dataset),
zvariables: dict = Depends(get_zvariables),
zmetadata: dict = Depends(get_zmetadata),
):
"""Dataset schema (close to the NCO-JSON schema)."""
zvariables = get_zvariables(dataset, cache)
zmetadata = get_zmetadata(dataset, cache, zvariables)

info = {}
info['dimensions'] = dict(dataset.dims.items())
info['variables'] = {}
info = {}
info['dimensions'] = dict(dataset.dims.items())
info['variables'] = {}

meta = zmetadata['metadata']
meta = zmetadata['metadata']

for name, var in zvariables.items():
attrs = meta[f'{name}/{attrs_key}']
attrs.pop('_ARRAY_DIMENSIONS')
info['variables'][name] = {
'type': var.data.dtype.name,
'dimensions': list(var.dims),
'attributes': attrs,
}
for name, var in zvariables.items():
attrs = meta[f'{name}/{attrs_key}']
attrs.pop('_ARRAY_DIMENSIONS')
info['variables'][name] = {
'type': var.data.dtype.name,
'dimensions': list(var.dims),
'attributes': attrs,
}

info['global_attributes'] = meta[attrs_key]
info['global_attributes'] = meta[attrs_key]

return info
return info
26 changes: 26 additions & 0 deletions xpublish/routers/factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from dataclasses import dataclass, field
from typing import Callable, List

import cachey
import xarray as xr
from fastapi import APIRouter

from ..dependencies import get_cache, get_dataset, get_dataset_ids


@dataclass
class XpublishFactory:
"""Xpublish API router factory."""

router: APIRouter = field(default_factory=APIRouter)

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()
Loading