Skip to content

Commit

Permalink
ENH: setncattr_string support (GH2044)
Browse files Browse the repository at this point in the history
  • Loading branch information
dnowacki-usgs committed Apr 17, 2018
1 parent a0bdbfb commit 94e2934
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 2 deletions.
3 changes: 3 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ v0.10.4 (unreleased)
Enhancements
~~~~~~~~~~~~

- Support writing lists of strings as netCDF attributes (:issue:`2044`).
By `Dan Nowacki <https://github.com/dnowacki-usgs>`_.

Bug fixes
~~~~~~~~~

Expand Down
29 changes: 27 additions & 2 deletions xarray/backends/netCDF4_.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,31 @@ def _disable_auto_decode_group(ds):
_disable_auto_decode_variable(var)


def _is_list_of_strings(value):
if (np.asarray(value).dtype.kind in ['U', 'S'] and
np.asarray(value).size > 1):
return True
else:
return False


def _set_nc_attribute(obj, key, value):
if _is_list_of_strings(value):
# encode as NC_STRING if attr is list of strings
try:
obj.setncattr_string(key, value)
except AttributeError:
# Inform users with old netCDF that does not support
# NC_STRING that we can't serialize lists of strings
# as attrs
msg = ('Attributes which are lists of strings are not '
'supported with this version of netCDF. Please '
'upgrade to netCDF4-python 1.2.4 or greater.')
raise AttributeError(msg)
else:
obj.setncattr(key, value)


class NetCDF4DataStore(WritableCFDataStore, DataStorePickleMixin):
"""Store for reading and writing data via the Python-NetCDF4 library.
Expand Down Expand Up @@ -347,7 +372,7 @@ def set_attribute(self, key, value):
with self.ensure_open(autoclose=False):
if self.format != 'NETCDF4':
value = encode_nc3_attr_value(value)
self.ds.setncattr(key, value)
_set_nc_attribute(self.ds, key, value)

def set_variables(self, *args, **kwargs):
with self.ensure_open(autoclose=False):
Expand Down Expand Up @@ -402,7 +427,7 @@ def prepare_variable(self, name, variable, check_encoding=False,
for k, v in iteritems(attrs):
# set attributes one-by-one since netCDF4<1.0.10 can't handle
# OrderedDict as the input to setncatts
nc4_var.setncattr(k, v)
_set_nc_attribute(nc4_var, k, v)

target = NetCDF4ArrayWrapper(name, self)

Expand Down
17 changes: 17 additions & 0 deletions xarray/tests/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,23 @@ def test_88_character_filename_segmentation_fault(self):
# Need to construct 88 character filepath
xr.Dataset().to_netcdf('a' * (88 - len(os.getcwd()) - 1))

def test_setncattr_string(self):
list_of_strings = ['list', 'of', 'strings']
one_element_list_of_strings = ['one element']
one_string = 'one string'
attrs = {'foo': list_of_strings,
'bar': one_element_list_of_strings,
'baz': one_string}
ds = Dataset({'x': ('y', [1, 2, 3], attrs)},
attrs=attrs)

with self.roundtrip(ds) as actual:
for totest in [actual, actual['x']]:
assert_array_equal(list_of_strings, totest.attrs['foo'])
assert_array_equal(one_element_list_of_strings,
totest.attrs['bar'])
assert one_string == totest.attrs['baz']


class NetCDF4DataStoreAutocloseTrue(NetCDF4DataTest):
autoclose = True
Expand Down

0 comments on commit 94e2934

Please sign in to comment.