Skip to content

Commit

Permalink
Enforce that NcAttribute.value is always an 0- or 1-D array. (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
pp-mo committed Jan 15, 2025
1 parent cb7a8f6 commit 4eb50b7
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 11 deletions.
17 changes: 14 additions & 3 deletions lib/ncdata/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,12 +503,23 @@ def __init__(self, name: str, value): # noqa: D107
#: attribute name
self.name: str = name
# Attribute values are arraylike, have dtype
# TODO: may need to regularise string representations?
if not hasattr(value, "dtype"):
value = np.asanyarray(value)
#: attribute value
self.value: np.ndarray = value

@property
def value(self): # noqa: D102
return self._value

@value.setter
def value(self, value):
if not hasattr(value, "dtype"):
value = np.asanyarray(value)
if value.ndim > 1:
raise ValueError(
"Attribute value should only be 0- or 1-dimensional."
)
self._value = value

def as_python_value(self):
"""
Return the content, but converting any character data to Python strings.
Expand Down
37 changes: 30 additions & 7 deletions lib/ncdata/netcdf4.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,7 @@ def to_nc4(
nc4ds.close()


def _from_nc4_group(
nc4ds: Union[nc.Dataset, nc.Group],
) -> NcData:
def _from_nc4_group(nc4ds: Union[nc.Dataset, nc.Group], dim_chunks) -> NcData:
"""
Inner routine for :func:`from_nc4`.
Expand Down Expand Up @@ -261,8 +259,9 @@ def _from_nc4_group(
variable_name=varname,
group_names_path=group_names_path,
)
chunks = [dim_chunks.get(name, "auto") for name in var.dimensions]
var.data = da.from_array(
proxy, chunks="auto", asarray=True, meta=np.ndarray
proxy, chunks=chunks, asarray=True, meta=np.ndarray
)

for attrname in nc4var.ncattrs():
Expand All @@ -277,13 +276,16 @@ def _from_nc4_group(

# And finally, groups -- by the magic of recursion ...
for group_name, group in nc4ds.groups.items():
ncdata.groups[group_name] = _from_nc4_group(nc4ds.groups[group_name])
ncdata.groups[group_name] = _from_nc4_group(
nc4ds.groups[group_name], dim_chunks=dim_chunks
)

return ncdata


def from_nc4(
nc4_dataset_or_file: Union[nc.Dataset, nc.Group, Path, str]
nc4_dataset_or_file: Union[nc.Dataset, nc.Group, Path, str],
dim_chunks: Dict[str, Union[int, str]] = None,
) -> NcData:
"""
Load NcData from a :class:`netCDF4.Dataset` or netCDF file.
Expand All @@ -294,18 +296,39 @@ def from_nc4(
source of load data. Can be either a :class:`netCDF4.Dataset`,
a :class:`netCDF4.Group`, a :class:`pathlib.Path` or a string.
dim_chunks
a dictionary of chunk sizes (number, or -1 or "auto") for specific
dimensions, specified by dimension name.
Defaults to "auto" for all unspecified dimensions.
Returns
-------
ncdata : NcData
Examples
--------
For example, to avoid cases where a simple dask ``from_array(chunks="auto")``
will fail
>>> from ncdata.netcdf4 import from_nc4
>>> from tests import testdata_dir
>>> path = testdata_dir / "toa_brightness_temperature.nc"
>>> ds = from_nc4(path, dim_chunks={"x": 15})
>>> ds.variables["data"].data.chunksize
(160, 15)
>>>
"""
if dim_chunks is None:
dim_chunks = {}
caller_owns_dataset = hasattr(nc4_dataset_or_file, "variables")
if caller_owns_dataset:
nc4ds = nc4_dataset_or_file
else:
nc4ds = nc.Dataset(nc4_dataset_or_file)

try:
ncdata = _from_nc4_group(nc4ds)
ncdata = _from_nc4_group(nc4ds, dim_chunks)
finally:
if not caller_owns_dataset:
nc4ds.close()
Expand Down
2 changes: 1 addition & 1 deletion lib/ncdata/utils/_compare_nc_datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ def getdata(var):
isnans, isnans2 = (np.isnan(arr) for arr in (flatdata, flatdata2))
if np.any(isnans) or np.any(isnans2):
nandiffs = np.where(isnans != isnans2)[0]
if nandiffs:
if nandiffs.size > 0:
flat_diff_inds += list(nandiffs)
anynans = isnans | isnans2
flatdata[anynans] = safe_fill_const
Expand Down

0 comments on commit 4eb50b7

Please sign in to comment.