Skip to content
Snippets Groups Projects

Draft: Support multiple APIs

Open Duncan Macleod requested to merge duncanmmacleod/gwdatafind:multi-api into main
+ 104
22
@@ -23,6 +23,7 @@ referred to in usage as ``gwdatafind.<function>`` and not
"""
from functools import wraps
from importlib import import_module
from re import compile as compile_regex
from urllib.parse import urlparse
from warnings import warn
@@ -31,12 +32,25 @@ import igwn_segments as segments
import requests
from igwn_auth_utils.requests import get as _get
from . import api
from .api import DEFAULT_API
from .utils import (
file_segment,
get_default_host,
)
try:
from functools import cache
except ImportError: # python < 3.9
from functools import (
lru_cache,
partial,
)
@wraps(lru_cache)
def cache(func):
"""Return `functools.lru_cache` with ``maxsize=None``."""
return lru_cache(maxsize=None)(func)
__author__ = "Duncan Macleod <duncan.macleod@ligo.org>"
__all__ = [
@@ -49,6 +63,27 @@ __all__ = [
"find_urls",
]
DEFAULT_EXT = "gwf"
# -- api handling --------------------
def _api_mod(api):
"""Return the API implementation module with the module name ``api``."""
api = (api or DEFAULT_API).lower()
try:
return import_module(f".api.{api}", package=__package__)
except ImportError:
raise ValueError(f"unsupported api '{api}'")
@cache
def _api_func(api, name):
"""Return the function with ``name`` for the matching ``api``."""
return getattr(_api_mod(api), name)
# -- user interface ------------------
@wraps(_get)
def get(url, *args, **kwargs):
@@ -90,17 +125,26 @@ def get_json(*args, **kwargs):
return response.json()
def _url(host, api_func, *args, **kwargs):
def _url(
host,
api,
funcname,
*args,
**kwargs,
):
"""Construct the full URL for a query to ``host`` using an API function.
Parameters
----------
host : `str`, `None`
the host to query, if `None` `~gwdatafind.utils.get_default_host()`
The host to query, if `None` `~gwdatafind.utils.get_default_host()`
will be used to discover the default host.
api_func : `callable`
the function from the :mod:`gwdatafind.api` module to use in
api : `str`, optional
The API version to use.
funcname: `str`
The name of the function from the API implementation to use in
constructing the URL path.
*args, **kwargs
@@ -112,6 +156,7 @@ def _url(host, api_func, *args, **kwargs):
url : `str`
a full URL including scheme, host, and path
"""
api_func = _api_func(api, funcname)
path = api_func(*args, **kwargs)
if host is None:
host = get_default_host()
@@ -127,7 +172,12 @@ def _url(host, api_func, *args, **kwargs):
return f"{host.rstrip('/')}/{path}"
def ping(host=None, ext=api.DEFAULT_EXT, session=None, **request_kw):
def ping(
host=None,
api=DEFAULT_API,
session=None,
**request_kw,
):
"""Ping the GWDataFind host to test for life.
Parameters
@@ -137,8 +187,8 @@ def ping(host=None, ext=api.DEFAULT_EXT, session=None, **request_kw):
:func:`~gwdatafind.utils.get_default_host` will be used to
discover the default host.
ext : `str`, optional
the file extension for which to search.
api : `str`, optional
The API version to use.
session : `requests.Session`, optional
the connection session to use; if not given, a
@@ -156,20 +206,26 @@ def ping(host=None, ext=api.DEFAULT_EXT, session=None, **request_kw):
request_kw
other keywords are passed to :func:`igwn_auth_utils.get`
Returns
-------
version_info : `dict`
The information returned from the `/api/version` API endpoint.
For LDR-era servers this returns an empty `list`.
Raises
------
requests.RequestException
if the request fails for any reason
"""
qurl = _url(host, api.ping_path, ext=ext)
response = get(qurl, session=session, **request_kw)
response.raise_for_status()
qurl = _url(host, api, "ping_path")
return get_json(qurl, session=session, **request_kw)
def find_observatories(
match=None,
host=None,
ext=api.DEFAULT_EXT,
api=DEFAULT_API,
ext=DEFAULT_EXT,
session=None,
**request_kw,
):
@@ -186,6 +242,9 @@ def find_observatories(
:func:`~gwdatafind.utils.get_default_host` will be used to
discover the default host.
api : `str`, optional
The API version to use.
ext : `str`, optional
the file extension for which to search.
@@ -222,7 +281,7 @@ def find_observatories(
>>> find_observatories(match="H", host="datafind.gwosc.org")
['H']
"""
qurl = _url(host, api.find_observatories_path, ext=ext)
qurl = _url(host, api, "find_observatories_path", ext=ext)
sites = set(get_json(qurl, session=session, **request_kw))
if match:
match = compile_regex(match).search
@@ -234,7 +293,8 @@ def find_types(
site=None,
match=None,
host=None,
ext=api.DEFAULT_EXT,
api=DEFAULT_API,
ext=DEFAULT_EXT,
session=None,
**request_kw,
):
@@ -254,6 +314,9 @@ def find_types(
:func:`~gwdatafind.utils.get_default_host` will be used to
discover the default host.
api : `str`, optional
The API version to use.
ext : `str`, optional
the file extension for which to search.
@@ -292,7 +355,7 @@ def find_types(
(accurate as of Nov 18 2021)
""" # noqa: E501
qurl = _url(host, api.find_types_path, site=site, ext=ext)
qurl = _url(host, api, "find_types_path", site=site, ext=ext)
types = set(get_json(qurl, session=session, **request_kw))
if match:
match = compile_regex(match).search
@@ -306,7 +369,8 @@ def find_times(
gpsstart=None,
gpsend=None,
host=None,
ext=api.DEFAULT_EXT,
api=DEFAULT_API,
ext=DEFAULT_EXT,
session=None,
**request_kw,
):
@@ -334,6 +398,9 @@ def find_times(
:func:`~gwdatafind.utils.get_default_host` will be used to
discover the default host.
api : `str`, optional
The API version to use.
ext : `str`, optional
the file extension for which to search.
@@ -377,7 +444,8 @@ def find_times(
""" # noqa: E501
qurl = _url(
host,
api.find_times_path,
api,
"find_times_path",
site,
frametype,
gpsstart,
@@ -410,6 +478,7 @@ def find_url(
urltype="file",
on_missing="error",
host=None,
api=DEFAULT_API,
session=None,
**request_kw,
):
@@ -436,6 +505,9 @@ def find_url(
:func:`~gwdatafind.utils.get_default_host` will be used to
discover the default host.
api : `str`, optional
The API version to use.
session : `requests.Session`, optional
the connection session to use; if not given, a
:class:`igwn_auth_utils.requests.Session` will be
@@ -465,7 +537,7 @@ def find_url(
RuntimeError
if no matching URLs are found and ``on_missing="error"`` was given
"""
qurl = _url(host, api.find_url_path, framefile)
qurl = _url(host, api, "find_url_path", framefile)
return _get_urls(
qurl,
scheme=urltype,
@@ -481,7 +553,8 @@ def find_latest(
urltype="file",
on_missing="error",
host=None,
ext=api.DEFAULT_EXT,
api=DEFAULT_API,
ext=DEFAULT_EXT,
session=None,
**request_kw,
):
@@ -510,6 +583,9 @@ def find_latest(
:func:`~gwdatafind.utils.get_default_host` will be used to
discover the default host.
api : `str`, optional
The API version to use.
ext : `str`, optional
the file extension for which to search.
@@ -549,7 +625,8 @@ def find_latest(
""" # noqa: E501
qurl = _url(
host,
api.find_latest_path,
api,
"find_latest_path",
site,
frametype,
ext=ext,
@@ -567,7 +644,8 @@ def find_urls(
urltype="file",
on_gaps="warn",
host=None,
ext=api.DEFAULT_EXT,
api=DEFAULT_API,
ext=DEFAULT_EXT,
session=None,
**request_kw,
):
@@ -606,6 +684,9 @@ def find_urls(
:func:`~gwdatafind.utils.get_default_host` will be used to
discover the default host.
api : `str`, optional
The API version to use.
ext : `str`, optional
the file extension for which to search.
@@ -635,7 +716,8 @@ def find_urls(
"""
qurl = _url(
host,
api.find_urls_path,
api,
"find_urls_path",
site,
frametype,
gpsstart,
Loading