Skip to content

Commit deecef7

Browse files
authored
Merge pull request #57 from weiji14/opendap/auth
Allow for no authentication and NASA Earthdata (URS) authentication for OPeNDAP
2 parents 597e907 + 2f12b56 commit deecef7

File tree

6 files changed

+78
-9
lines changed

6 files changed

+78
-9
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ data drivers included in this package.
1111
In `intake-xarray`, there are plugins provided for reading data into [xarray](http://xarray.pydata.org/en/stable/)
1212
containers:
1313
- NetCDF
14+
- OPeNDAP
1415
- Rasterio
1516
- Zarr
1617
- images
@@ -28,5 +29,5 @@ conda install -c conda-forge intake-xarray
2829
To install optional dependencies:
2930

3031
```
31-
conda install -c conda-forge rasterio
32+
conda install -c conda-forge pydap rasterio
3233
```

ci/environment-py36.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ dependencies:
88
- zarr
99
- pytest
1010
- netcdf4
11+
- pydap
1112
- rasterio
1213
- scikit-image

ci/environment-py37-nodefaults.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ dependencies:
99
- zarr
1010
- pytest
1111
- netcdf4
12+
- pydap
1213
- rasterio
1314
- scikit-image

intake_xarray/opendap.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,44 @@ class OpenDapSource(DataSourceMixin):
1515
Chunks is used to load the new dataset into dask
1616
arrays. ``chunks={}`` loads the dataset with dask using a single
1717
chunk for all arrays.
18+
auth: None, "esgf" or "urs"
19+
Method of authenticating to the OPeNDAP server.
20+
Choose from one of the following:
21+
'esgf' - [Default] Earth System Grid Federation.
22+
'urs' - NASA Earthdata Login, also known as URS.
23+
None - No authentication.
24+
Note that you will need to set your username and password respectively using the
25+
environment variables DAP_USER and DAP_PASSWORD.
1826
"""
1927
name = 'opendap'
2028

21-
def __init__(self, urlpath, chunks, xarray_kwargs=None, metadata=None,
29+
def __init__(self, urlpath, chunks, auth="esgf", xarray_kwargs=None, metadata=None,
2230
**kwargs):
2331
self.urlpath = urlpath
2432
self.chunks = chunks
33+
self.auth = auth
2534
self._kwargs = xarray_kwargs or kwargs
2635
self._ds = None
2736
super(OpenDapSource, self).__init__(metadata=metadata)
2837

2938
def _get_session(self):
30-
from pydap.cas.esgf import setup_session
31-
username = os.getenv('DAP_USER', None)
32-
password = os.getenv('DAP_PASSWORD', None)
33-
return setup_session(
34-
username,
35-
password,
36-
check_url=self.urlpath)
39+
if self.auth is None:
40+
session = None
41+
else:
42+
if self.auth == "esgf":
43+
from pydap.cas.esgf import setup_session
44+
elif self.auth == "urs":
45+
from pydap.cas.urs import setup_session
46+
else:
47+
raise ValueError(
48+
"Authentication method should either be None, 'esgf' or 'urs', "
49+
f"got '{self.auth}' instead."
50+
)
51+
username = os.getenv('DAP_USER', None)
52+
password = os.getenv('DAP_PASSWORD', None)
53+
session = setup_session(username, password, check_url=self.urlpath)
54+
55+
return session
3756

3857
def _open_dataset(self):
3958
import xarray as xr

intake_xarray/tests/data/catalog.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,10 @@ sources:
9696
driver: zarr
9797
args:
9898
urlpath: "{{CATALOG_DIR}}/blank.zarr"
99+
opendap_source:
100+
description: example OPeNDAP source
101+
driver: opendap
102+
args:
103+
urlpath: http://test.opendap.org/opendap/hyrax/data/nc/data.nc
104+
chunks: {}
105+
auth: null

intake_xarray/tests/test_intake_xarray.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# -*- coding: utf-8 -*-
22
import os
3+
from unittest.mock import patch
4+
35
import numpy as np
46
import pytest
57

@@ -303,3 +305,41 @@ def test_read_jpg_image():
303305
im = ImageSource(os.path.join(here, 'data', 'dog.jpg'))
304306
da = im.read()
305307
assert da.shape == (192, 192)
308+
309+
310+
def test_read_opendap_no_auth():
311+
pytest.importorskip("pydap")
312+
cat = intake.open_catalog(os.path.join(here, "data", "catalog.yaml"))
313+
source = cat.opendap_source
314+
info = source.discover()
315+
assert info["metadata"]["dims"] == {"TIME": 12}
316+
x = source.read()
317+
assert x.TIME.shape == (12,)
318+
319+
320+
@pytest.mark.parametrize("auth", ["esgf", "urs"])
321+
def test_read_opendap_with_auth(auth):
322+
pytest.importorskip("pydap")
323+
from intake_xarray.opendap import OpenDapSource
324+
325+
os.environ["DAP_USER"] = "username"
326+
os.environ["DAP_PASSWORD"] = "password"
327+
urlpath = "http://test.opendap.org/opendap/hyrax/data/nc/123.nc"
328+
329+
with patch(
330+
f"pydap.cas.{auth}.setup_session", return_value=None
331+
) as mock_setup_session:
332+
source = OpenDapSource(urlpath=urlpath, chunks={}, auth=auth)
333+
source.discover()
334+
mock_setup_session.assert_called_once_with(
335+
os.environ["DAP_USER"], os.environ["DAP_PASSWORD"], check_url=urlpath
336+
)
337+
338+
339+
def test_read_opendap_invalid_auth():
340+
pytest.importorskip("pydap")
341+
from intake_xarray.opendap import OpenDapSource
342+
343+
source = OpenDapSource(urlpath="https://test.url", chunks={}, auth="abcd")
344+
with pytest.raises(ValueError):
345+
source.discover()

0 commit comments

Comments
 (0)