Skip to content

Commit

Permalink
Implementing DateProperty and TimeProperty in ndb. (#6452)
Browse files Browse the repository at this point in the history
  • Loading branch information
dhermes authored Nov 8, 2018
1 parent 890c15d commit 1e1a352
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 12 deletions.
130 changes: 124 additions & 6 deletions ndb/src/google/cloud/ndb/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2363,9 +2363,9 @@ def _validate(self, value):

@staticmethod
def _now():
"""datetime.datetime: Return current time.
"""datetime.datetime: Return current datetime.
This is in place so it can be patched in tests.
Subclasses will override this to return different forms of "now".
"""
return datetime.datetime.utcnow()

Expand Down Expand Up @@ -2407,17 +2407,135 @@ def _db_get_value(self, v, unused_p):


class DateProperty(DateTimeProperty):
"""A property that contains :class:`~datetime.date` values.
.. automethod:: _to_base_type
.. automethod:: _from_base_type
.. automethod:: _validate
"""

__slots__ = ()

def __init__(self, *args, **kwargs):
raise NotImplementedError
def _validate(self, value):
"""Validate a ``value`` before setting it.
Args:
value (~datetime.date): The value to check.
Raises:
.BadValueError: If ``value`` is not a :class:`~datetime.date`.
"""
if not isinstance(value, datetime.date):
raise exceptions.BadValueError(
"Expected date, got {!r}".format(value)
)

def _to_base_type(self, value):
"""Convert a value to the "base" value type for this property.
Args:
value (~datetime.date): The value to be converted.
Returns:
~datetime.datetime: The converted value: a datetime object with the
time set to ``00:00``.
Raises:
TypeError: If ``value`` is not a :class:`~datetime.date`.
"""
if not isinstance(value, datetime.date):
raise TypeError(
"Cannot convert to datetime expected date value; "
"received {}".format(value)
)
return datetime.datetime(value.year, value.month, value.day)

def _from_base_type(self, value):
"""Convert a value from the "base" value type for this property.
Args:
value (~datetime.datetime): The value to be converted.
Returns:
~datetime.date: The converted value: the date that ``value``
occurs on.
"""
return value.date()

@staticmethod
def _now():
"""datetime.datetime: Return current date."""
return datetime.datetime.utcnow().date()


class TimeProperty(DateTimeProperty):
"""A property that contains :class:`~datetime.time` values.
.. automethod:: _to_base_type
.. automethod:: _from_base_type
.. automethod:: _validate
"""

__slots__ = ()

def __init__(self, *args, **kwargs):
raise NotImplementedError
def _validate(self, value):
"""Validate a ``value`` before setting it.
Args:
value (~datetime.time): The value to check.
Raises:
.BadValueError: If ``value`` is not a :class:`~datetime.time`.
"""
if not isinstance(value, datetime.time):
raise exceptions.BadValueError(
"Expected time, got {!r}".format(value)
)

def _to_base_type(self, value):
"""Convert a value to the "base" value type for this property.
Args:
value (~datetime.time): The value to be converted.
Returns:
~datetime.datetime: The converted value: a datetime object with the
date set to ``1970-01-01``.
Raises:
TypeError: If ``value`` is not a :class:`~datetime.time`.
"""
if not isinstance(value, datetime.time):
raise TypeError(
"Cannot convert to datetime expected time value; "
"received {}".format(value)
)
return datetime.datetime(
1970,
1,
1,
value.hour,
value.minute,
value.second,
value.microsecond,
)

def _from_base_type(self, value):
"""Convert a value from the "base" value type for this property.
Args:
value (~datetime.datetime): The value to be converted.
Returns:
~datetime.time: The converted value: the time that ``value``
occurs at.
"""
return value.time()

@staticmethod
def _now():
"""datetime.datetime: Return current time."""
return datetime.datetime.utcnow().time()


class StructuredProperty(Property):
Expand Down
70 changes: 64 additions & 6 deletions ndb/tests/unit/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1917,16 +1917,74 @@ def test__db_get_value():

class TestDateProperty:
@staticmethod
def test_constructor():
with pytest.raises(NotImplementedError):
model.DateProperty()
def test__validate():
prop = model.DateProperty(name="d_val")
value = datetime.datetime.utcnow().date()
assert prop._validate(value) is None

@staticmethod
def test__validate_invalid():
prop = model.DateProperty(name="d_val")
with pytest.raises(exceptions.BadValueError):
prop._validate(None)

@staticmethod
def test__now():
d_val = model.DateProperty._now()
assert isinstance(d_val, datetime.date)

def test__to_base_type(self):
prop = model.DateProperty(name="d_val")
value = datetime.date(2014, 10, 7)
expected = datetime.datetime(2014, 10, 7)
assert prop._to_base_type(value) == expected

def test__to_base_type_invalid(self):
prop = model.DateProperty(name="d_val")
with pytest.raises(TypeError):
prop._to_base_type(None)

def test__from_base_type(self):
prop = model.DateProperty(name="d_val")
value = datetime.datetime(2014, 10, 7)
expected = datetime.date(2014, 10, 7)
assert prop._from_base_type(value) == expected


class TestTimeProperty:
@staticmethod
def test_constructor():
with pytest.raises(NotImplementedError):
model.TimeProperty()
def test__validate():
prop = model.TimeProperty(name="t_val")
value = datetime.datetime.utcnow().time()
assert prop._validate(value) is None

@staticmethod
def test__validate_invalid():
prop = model.TimeProperty(name="t_val")
with pytest.raises(exceptions.BadValueError):
prop._validate(None)

@staticmethod
def test__now():
t_val = model.TimeProperty._now()
assert isinstance(t_val, datetime.time)

def test__to_base_type(self):
prop = model.TimeProperty(name="t_val")
value = datetime.time(17, 57, 18, 453529)
expected = datetime.datetime(1970, 1, 1, 17, 57, 18, 453529)
assert prop._to_base_type(value) == expected

def test__to_base_type_invalid(self):
prop = model.TimeProperty(name="t_val")
with pytest.raises(TypeError):
prop._to_base_type(None)

def test__from_base_type(self):
prop = model.TimeProperty(name="t_val")
value = datetime.datetime(1970, 1, 1, 1, 15, 59, 900101)
expected = datetime.time(1, 15, 59, 900101)
assert prop._from_base_type(value) == expected


class TestStructuredProperty:
Expand Down

0 comments on commit 1e1a352

Please sign in to comment.