Skip to content
Merged
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
6 changes: 2 additions & 4 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
defaults:
run:
shell: bash -l {0}

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -34,7 +31,8 @@ jobs:

- name: Install erddapy
run: |
pip install -e ".[dev,extras,docs]"
pip install --group docs
pip install -e .

- name: Install pandoc
uses: pandoc/actions/setup@86321b6dd4675f5014c611e05088e10d4939e09e # v1.1.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish to PyPI
name: PyPI

on:
release:
Expand Down
23 changes: 14 additions & 9 deletions .github/workflows/tests.yml → .github/workflows/tests-core.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Full Tests
name: Core Tests

# no permissions by default
permissions: {}
Expand All @@ -13,15 +13,15 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [ "3.11", "3.14" ]
python-version: [ "3.11", "3.15-dev" ]
os: [ ubuntu-latest ]
include:
- os: windows-latest
python-version: "3.14"
python-version: "3.15-dev"
- os: macos-latest
python-version: "3.14"
python-version: "3.15-dev"
- os: macos-15-intel
python-version: "3.14"
python-version: "3.15-dev"
fail-fast: false
permissions:
actions: none
Expand All @@ -32,12 +32,17 @@ jobs:
fetch-depth: 0
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}

- name: Install erddapy
run: |
pip install -e ".[dev,extras,docs]"
pip install --group dev
pip install -e .

- name: Full Tests
# Use the --record-mode=rewrite if you need to re-record the cassetts.
run: >
pushd tests && python download_test_data.py && popd
&& python -m pytest -rxs tests
run: |
python -m pytest -rxs tests
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Code coverage report
name: Full Tests

# no permissions by default
permissions: {}
Expand All @@ -23,13 +23,13 @@ jobs:

- name: Install erddapy
run: |
pip install -e ".[dev,extras,docs]"
pip install --group docs
pip install -e .

- name: Run tests with coverage
run: >
pushd tests && python download_test_data.py && popd
&& python -m pytest -rxs --cov=erddapy tests
run: |
python -m pytest -rxs --cov=erddapy tests

- name: Run linters
run: >
run: |
prek run --all-files
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ repos:
- id: add-trailing-comma

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.7
rev: v0.16.8
hooks:
- id: ruff
args: ["--fix", "--show-fixes"]
Expand Down
78 changes: 42 additions & 36 deletions erddapy/xarray_erddap.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,55 @@
import urllib.parse
from collections.abc import Iterable

import xarray as xr
from xarray.backends.common import T_PathFileOrDataStore
try:
import xarray as xr
from xarray.backends.common import T_PathFileOrDataStore

XARRAY_IMPORT = True
except ImportError:
XARRAY_IMPORT = False

from erddapy.core.interfaces import to_xarray
from erddapy.core.url import _is_netcdf, _is_url

if XARRAY_IMPORT:

def _make_opendap(url: str) -> str:
parts = urllib.parse.urlparse(url)
opendap_url = urllib.parse.urlunparse(
[parts.scheme, parts.netloc, parts.path, "", "", ""],
)
return opendap_url.split(".nc")[0]


class ERDDAPyBackendEntrypoint(xr.backends.BackendEntrypoint):
"""Erddapy backend entrypoint for xarray."""
def _make_opendap(url: str) -> str:
parts = urllib.parse.urlparse(url)
opendap_url = urllib.parse.urlunparse(
[parts.scheme, parts.netloc, parts.path, "", "", ""],
)
return opendap_url.split(".nc")[0]

def open_dataset(
self,
filename_or_obj: T_PathFileOrDataStore,
*,
drop_variables: str | Iterable[str] | None = None, # noqa: ARG002
) -> xr.Dataset:
"""Open ERDDAP URLs as xarray datasets."""
return open_erddap_dataset(filename_or_obj)
class ERDDAPyBackendEntrypoint(xr.backends.BackendEntrypoint):
"""Erddapy backend entrypoint for xarray."""

open_dataset_parameters = ("filename_or_obj", "drop_variables")
def open_dataset(
self,
filename_or_obj: T_PathFileOrDataStore,
*,
drop_variables: str | Iterable[str] | None = None, # noqa: ARG002
) -> xr.Dataset:
"""Open ERDDAP URLs as xarray datasets."""
return open_erddap_dataset(filename_or_obj)

description = "Load ERDDAP URLs in xarray."
open_dataset_parameters = ("filename_or_obj", "drop_variables")

description = "Load ERDDAP URLs in xarray."

def open_erddap_dataset(filename_or_obj: T_PathFileOrDataStore) -> xr.Dataset:
"""Open an ERDDAP URL with a netcdf-like response as an xarray object."""
if not _is_url(filename_or_obj):
msg = f"Expected an ERDDAP URL, got {filename_or_obj!r}."
raise ValueError(msg)

url = str(filename_or_obj)
if _is_netcdf(url):
response = "nc"
else:
filename_or_obj = _make_opendap(url)
response = "opendap"

return to_xarray(url, response=response)
def open_erddap_dataset(
filename_or_obj: T_PathFileOrDataStore,
) -> xr.Dataset:
"""Open an ERDDAP URL response as an xarray object."""
if not _is_url(filename_or_obj):
msg = f"Expected an ERDDAP URL, got {filename_or_obj!r}."
raise ValueError(msg)

url = str(filename_or_obj)
if _is_netcdf(url):
response = "nc"
else:
filename_or_obj = _make_opendap(url)
response = "opendap"

return to_xarray(url, response=response)
20 changes: 14 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,19 @@ dynamic = [ "version" ]
dependencies = [
"pandas",
"requests",
"xarray",
]
optional-dependencies.dev = [
"dask",
entry-points."xarray.backends".erddapy = "erddapy.xarray_erddap:ERDDAPyBackendEntrypoint"

[dependency-groups]
dev = [
"pooch",
"prek",
"pytest",
"pytest-cov",
"pytest-recording",
"xarray",
]
optional-dependencies.docs = [
docs = [
"cartopy>=0.21",
"geopandas",
"joblib",
Expand All @@ -44,16 +46,22 @@ optional-dependencies.docs = [
"nbsphinx",
"regionmask>=0.9",
"sphinx",
{ include-group = "dev" },
{ include-group = "objects" },
{ include-group = "extras" },
]
optional-dependencies.extras = [
extras = [
"joblib",
]
objects = [
"dask",
"netcdf4",
"scitools-iris>=3.3",
"xarray",
]
urls.documentation = "https://ioos.github.io/erddapy"
urls.homepage = "https://github.com/ioos/erddapy"
urls.repository = "https://github.com/ioos/erddapy"
entry-points."xarray.backends".erddapy = "erddapy.xarray_erddap:ERDDAPyBackendEntrypoint"

[tool.setuptools]
packages = [ "erddapy" ]
Expand Down
6 changes: 2 additions & 4 deletions tests/download_test_data.py → tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from pathlib import Path

import pooch
import pytest


@pytest.fixture(scope="session", autouse=True)
def download_test_data() -> None:
"""Fetch cassettes yaml data from cassettes test release."""
url = "https://github.com/ioos/erddapy/releases/download"
Expand All @@ -19,7 +21,3 @@ def download_test_data() -> None:
here = Path(__file__).resolve().parent
with zipfile.ZipFile(fname, "r") as zip_ref:
zip_ref.extractall(here)


if __name__ == "__main__":
download_test_data()
11 changes: 9 additions & 2 deletions tests/test_multiple_server_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

from erddapy.multiple_server_search import fetch_results, search_servers

try:
import joblib # noqa: F401

JOBLIB_INSTALLED = True
except ImportError:
JOBLIB_INSTALLED = False


@pytest.mark.web
@pytest.mark.vcr
Expand Down Expand Up @@ -37,7 +44,7 @@ def test_fetch_no_results():

@pytest.mark.web
@pytest.mark.skipif(
sys.platform in ("win32", "darwin"),
(sys.platform in ("win32", "darwin") or not JOBLIB_INSTALLED),
reason="run only on linux to avoid extra load on the server",
)
def test_search_awesome_erddap_servers_true():
Expand Down Expand Up @@ -82,7 +89,7 @@ def servers_list():

@pytest.mark.web
@pytest.mark.skipif(
(sys.platform in ("win32", "darwin") or sys.version_info < (3, 10)),
(sys.platform in ("win32", "darwin") or not JOBLIB_INSTALLED),
reason="run only on linux and latest to avoid extra load on the server",
)
def test_search_servers_with_a_list_parallel_true(servers_list):
Expand Down
15 changes: 13 additions & 2 deletions tests/test_netcdf_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,26 @@
import platform
from pathlib import Path

try:
import netCDF4 # noqa: F401

NETCDF4_INSTALLED = True
except ImportError:
NETCDF4_INSTALLED = False

import pytest

from erddapy.core.netcdf import _nc_dataset, _tempnc
from erddapy.core.url import urlopen


@pytest.mark.web
# For some reason we cannot use vcr with requests with in_memory
# (also all the to_objects that uses in_memory).
@pytest.mark.web
@pytest.mark.skipif(
not NETCDF4_INSTALLED,
reason="Optional deps are tested in coverage and oldest Python only.",
)
def test__nc_dataset_in_memory_https():
"""Test loading a netcdf dataset in-memory."""
from netCDF4 import Dataset # noqa: PLC0415
Expand All @@ -25,7 +36,7 @@ def test__nc_dataset_in_memory_https():
@pytest.mark.web
@pytest.mark.vcr
@pytest.mark.skipif(
platform.system().lower() == "windows",
(platform.system().lower() == "windows" or not NETCDF4_INSTALLED),
reason="does not remove the file on windows",
)
def test__tempnc():
Expand Down
Loading