Skip to content

Commit 92224e0

Browse files
committed
ENH: setncattr_string support (GH2044)
1 parent a0bdbfb commit 92224e0

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

doc/whats-new.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ v0.10.4 (unreleased)
3434
Enhancements
3535
~~~~~~~~~~~~
3636

37+
- Support reading and writing lists of strings as attributes to netCDF
38+
(:issue:`2044`).
39+
By `Dan Nowacki <https://github.com/dnowacki-usgs>`_.
40+
3741
Bug fixes
3842
~~~~~~~~~
3943

xarray/backends/netCDF4_.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,31 @@ def _disable_auto_decode_group(ds):
229229
_disable_auto_decode_variable(var)
230230

231231

232+
def _is_list_of_strings(value):
233+
if (np.asarray(value).dtype.kind in ['U', 'S'] and
234+
np.asarray(value).size > 1):
235+
return True
236+
else:
237+
return False
238+
239+
240+
def _set_nc_attribute(obj, key, value):
241+
if _is_list_of_strings(value):
242+
# encode as NC_STRING if attr is list of strings
243+
try:
244+
obj.setncattr_string(key, value)
245+
except AttributeError:
246+
# Inform users with old netCDF that does not support
247+
# NC_STRING that we can't serialize lists of strings
248+
# as attrs
249+
msg = ('Attributes which are lists of strings are not '
250+
'supported with this version of netCDF. Please '
251+
'upgrade to netCDF4-python 1.2.4 or greater.')
252+
raise AttributeError(msg)
253+
else:
254+
obj.setncattr(key, value)
255+
256+
232257
class NetCDF4DataStore(WritableCFDataStore, DataStorePickleMixin):
233258
"""Store for reading and writing data via the Python-NetCDF4 library.
234259
@@ -347,7 +372,7 @@ def set_attribute(self, key, value):
347372
with self.ensure_open(autoclose=False):
348373
if self.format != 'NETCDF4':
349374
value = encode_nc3_attr_value(value)
350-
self.ds.setncattr(key, value)
375+
_set_nc_attribute(self.ds, key, value)
351376

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

407432
target = NetCDF4ArrayWrapper(name, self)
408433

xarray/tests/test_backends.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,23 @@ def test_88_character_filename_segmentation_fault(self):
11441144
# Need to construct 88 character filepath
11451145
xr.Dataset().to_netcdf('a' * (88 - len(os.getcwd()) - 1))
11461146

1147+
def test_setncattr_string(self):
1148+
list_of_strings = ['list', 'of', 'strings']
1149+
one_element_list_of_strings = ['one element']
1150+
one_string = 'one string'
1151+
attrs = {'foo': list_of_strings,
1152+
'bar': one_element_list_of_strings,
1153+
'baz': one_string}
1154+
ds = Dataset({'x': ('y', [1, 2, 3], attrs)},
1155+
attrs=attrs)
1156+
1157+
with self.roundtrip(ds) as actual:
1158+
for totest in [actual, actual['x']]:
1159+
assert_array_equal(list_of_strings, totest.attrs['foo'])
1160+
assert_array_equal(one_element_list_of_strings,
1161+
totest.attrs['bar'])
1162+
assert one_string == totest.attrs['baz']
1163+
11471164

11481165
class NetCDF4DataStoreAutocloseTrue(NetCDF4DataTest):
11491166
autoclose = True

0 commit comments

Comments
 (0)