Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dask: Data units and calendar methods inherited from cfdm #357

Merged
merged 2 commits into from
Mar 22, 2022
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
166 changes: 116 additions & 50 deletions cf/data/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -7722,7 +7722,7 @@ def get_units(self, default=ValueError()):
The units.
**Examples:**
**Examples**
>>> d.set_units('metres')
>>> d.get_units()
Expand Down Expand Up @@ -7775,7 +7775,8 @@ def get_calendar(self, default=ValueError()):
def set_calendar(self, calendar):
"""Set the calendar.
.. seealso:: `del_calendar`, `get_calendar`
.. seealso:: `override_calendar`, `override_units`,
`del_calendar`, `get_calendar`
:Parameters:
Expand All @@ -7786,7 +7787,7 @@ def set_calendar(self, calendar):
`None`
**Examples:**
**Examples**
>>> d.set_calendar('none')
>>> d.get_calendar
Expand All @@ -7803,7 +7804,8 @@ def set_calendar(self, calendar):
def set_units(self, value):
"""Set the units.
.. seealso:: `del_units`, `get_units`, `has_units`
.. seealso:: `override_units`, `del_units`, `get_units`,
`has_units`, `Units`
:Parameters:
Expand All @@ -7814,7 +7816,7 @@ def set_units(self, value):
`None`
**Examples:**
**Examples**
>>> d.set_units('watt')
>>> d.get_units()
Expand Down Expand Up @@ -9865,6 +9867,70 @@ def harden_mask(self):
self._map_blocks(cf_harden_mask, dtype=self.dtype)
self._hardmask = True

def has_calendar(self):
"""Whether a calendar has been set.
.. seealso:: `del_calendar`, `get_calendar`, `set_calendar`,
`has_units`, `Units`
:Returns:
`bool`
True if the calendar has been set, otherwise False.
**Examples**
>>> d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
>>> d.has_calendar()
True
>>> d = cf.Data(1, calendar="noleap")
>>> d.has_calendar()
True
>>> d = cf.Data(1, "days since 2000-1-1")
>>> d.has_calendar()
False
>>> d = cf.Data(1, "m")
>>> d.has_calendar()
False
"""
return hasattr(self.Units, "calendar")

def has_units(self):
"""Whether units have been set.
.. seealso:: `del_units`, `get_units`, `set_units`,
`has_calendar`, `Units`
:Returns:
`bool`
True if units have been set, otherwise False.
**Examples**
>>> d = cf.Data(1, "")
>>> d.has_units()
True
>>> d = cf.Data(1, "m")
>>> d.has_units()
True
>>> d = cf.Data(1)
>>> d.has_units()
False
>>> d = cf.Data(1, calendar='noleap')
>>> d.has_units()
False
"""
return hasattr(self.Units, "units")

def soften_mask(self):
"""Force the mask to soft.
Expand Down Expand Up @@ -10819,7 +10885,7 @@ def del_calendar(self, default=ValueError()):
"""Delete the calendar.
.. seealso:: `get_calendar`, `has_calendar`, `set_calendar`,
`del_units`
`del_units`, `Units`
:Parameters:
Expand All @@ -10834,38 +10900,42 @@ def del_calendar(self, default=ValueError()):
`str`
The value of the deleted calendar.
**Examples:**
**Examples**
>>> d.set_calendar('360_day')
>>> d.has_calendar()
True
>>> d.get_calendar()
'360_day'
>>> d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
>>> d.del_calendar()
>>> d.has_calendar()
False
>>> d.get_calendar()
ValueError: Can't get non-existent calendar
>>> print(d.get_calendar(None))
'noleap'
>>> print(d.del_calendar())
None
>>> print(d.del_calendar(None))
>>> d = cf.Data(1, "days since 2000-1-1")
>>> print(d.del_calendar())
None
>>> d = cf.Data(1, "m")
Traceback (most recent call last):
...
ValueError: Units <Units: m> have no calendar
"""
calendar = getattr(self.Units, "calendar", None)
units = self.Units
if not units.isreftime:
return self._default(default, f"Units {units!r} have no calendar")

if calendar is not None:
self.override_calendar(None, inplace=True)
return calendar
calendar = getattr(units, "calendar", None)
if calendar is None:
return self._default(
default, f"{self.__class__.__name__} has no calendar"
)

raise self._default(
default, f"{self.__class__.__name__} has no 'calendar' component"
)
self.override_calendar(None, inplace=True)
return calendar

def del_units(self, default=ValueError()):
"""Delete the units.
.. seealso:: `get_units`, `has_units`, `set_units`, `del_calendar`
.. seealso:: `get_units`, `has_units`, `set_units`,
`del_calendar`, `Units`
:Parameters:
Expand All @@ -10880,39 +10950,35 @@ def del_units(self, default=ValueError()):
`str`
The value of the deleted units.
**Examples:**
**Examples**
>>> d.set_units('metres')
>>> d.has_units()
True
>>> d.get_units()
'metres'
>>> d = cf.Data(1, "m")
>>> d.del_units()
>>> d.has_units()
False
>>> d.get_units()
ValueError: Can't get non-existent units
>>> print(d.get_units(None))
None
>>> print(d.del_units(None))
None
"""
out = self.Units
'm'
>>> d.Units
<Units: >
>>> d.del_units()
Traceback (most recent call last):
...
ValueError: Data has no units
units = getattr(out, "units", None)
calendar = getattr(out, "calendar", None)
>>> d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
>>> d.del_units()
'days since 2000-1-1'
>>> d.Units
<Units: noleap>
if calendar is not None:
self.Units = Units(None, calendar)
else:
del self.Units
"""
u = self.Units
units = getattr(u, "units", None)
calendar = getattr(u, "calendar", None)
self.override_units(Units(None, calendar), inplace=True)

if units is not None:
return units

return self._default(
default, f"{self.__class__.__name__} has no 'units' component"
default, f"{self.__class__.__name__} has no units"
)

@classmethod
Expand Down
92 changes: 64 additions & 28 deletions cf/test/test_Data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3592,67 +3592,66 @@ def test_Data_filled(self):
d = cf.Data(["a", "b", "c"], mask=[1, 0, 0])
self.assertTrue((d.filled().array == ["", "b", "c"]).all())

@unittest.skipIf(TEST_DASKIFIED_ONLY, "units-related problem")
def test_Data_del_units(self):
d = cf.Data(1)
with self.assertRaises(ValueError):
d.del_units()

d = cf.Data(1, "")
self.assertEqual(d.del_units(), "")
d = cf.Data(1, "m")
self.assertEqual(d.del_units(), "m")
with self.assertRaises(ValueError):
d.del_units()

d = cf.Data(1, "days since 2000-1-1")
self.assertTrue(d.del_units(), "days since 2000-1-1")
self.assertEqual(d.del_units(), "days since 2000-1-1")
with self.assertRaises(ValueError):
d.del_units()

d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
self.assertEqual(d.del_units(), "days since 2000-1-1")
self.assertEqual(d.Units, cf.Units(None, "noleap"))
with self.assertRaises(ValueError):
d.del_units()

def test_Data_del_calendar(self):
d = cf.Data(1)
with self.assertRaises(ValueError):
d.del_calendar()

d = cf.Data(1, "")
with self.assertRaises(ValueError):
d.del_calendar()
for units in (None, "", "m", "days since 2000-1-1"):
d = cf.Data(1, units)
with self.assertRaises(ValueError):
d.del_calendar()

d = cf.Data(1, "m")
d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
self.assertEqual(d.del_calendar(), "noleap")
with self.assertRaises(ValueError):
d.del_calendar()

d = cf.Data(1, "days since 2000-1-1")
with self.assertRaises(ValueError):
d.del_calendar()
def test_Data_get_calendar(self):
for units in (None, "", "m", "days since 2000-1-1"):
d = cf.Data(1, units)
with self.assertRaises(ValueError):
d.get_calendar()

d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
self.assertTrue(d.del_calendar(), "noleap")
self.assertTrue(d.get_calendar(), "noleap")

@unittest.skipIf(TEST_DASKIFIED_ONLY, "units-related problem")
def test_Data_has_units(self):
d = cf.Data(1)
self.assertFalse(d.has_units())
d = cf.Data(1, "")
self.assertTrue(d.has_units())
d = cf.Data(1, "m")
self.assertTrue(d.has_units())

@unittest.skipIf(TEST_DASKIFIED_ONLY, "units-related problem")
def test_Data_has_calendar(self):
d = cf.Data(1)
self.assertFalse(d.has_calendar())
d = cf.Data(1, "")
self.assertFalse(d.has_calendar())
d = cf.Data(1, "m")
self.assertFalse(d.has_calendar())
self.assertFalse(d.has_units())
d = cf.Data(1, calendar="noleap")
self.assertFalse(d.has_units())

d = cf.Data(1, "days since 2000-1-1")
self.assertFalse(d.has_calendar())
def test_Data_has_calendar(self):
d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
self.assertTrue(d.has_calendar())

for units in (None, "", "m", "days since 2000-1-1"):
d = cf.Data(1, units)
self.assertFalse(d.has_calendar())

def test_Data_where(self):
a = np.arange(10)
d = cf.Data(a)
Expand Down Expand Up @@ -3891,6 +3890,43 @@ def test_Data_rechunk(self):
self.assertEqual(e.chunks, ((4,), (5,)))
self.assertTrue(e.equals(d))

def test_Data_get_units(self):
for units in ("", "m", "days since 2000-01-01"):
d = cf.Data(1, units)
self.assertEqual(d.get_units(), units)

d = cf.Data(1)
with self.assertRaises(ValueError):
d.get_units()

def test_Data_set_calendar(self):
d = cf.Data(1, "days since 2000-01-01")
d.set_calendar("standard")

with self.assertRaises(ValueError):
d.set_calendar("noleap")

d = cf.Data(1, "m")
d.set_calendar("noleap")
self.assertEqual(d.Units, cf.Units("m"))

def test_Data_set_units(self):
for units in (None, "", "m", "days since 2000-01-01"):
d = cf.Data(1, units)
self.assertEqual(d.Units, cf.Units(units))

d = cf.Data(1, "m")
d.set_units("km")
self.assertEqual(d.array, 0.001)

d = cf.Data(1, "days since 2000-01-01", calendar="noleap")
d.set_units("days since 1999-12-31")
self.assertEqual(d.array, 2)

# Can't set to Units that are not equivalent
with self.assertRaises(ValueError):
d.set_units("km")


if __name__ == "__main__":
print("Run date:", datetime.datetime.now())
Expand Down