Skip to content

Commit

Permalink
Add datetime type cast
Browse files Browse the repository at this point in the history
  • Loading branch information
yar-kik committed Dec 11, 2022
1 parent 74327ed commit 7fbb073
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 2 deletions.
2 changes: 1 addition & 1 deletion app_properties/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .main import properties

__all__ = ("properties",)
__version__ = "1.1.2"
__version__ = "1.2.0"
21 changes: 21 additions & 0 deletions app_properties/type_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Any,
Optional,
Tuple,
Type,
TypeVar,
Union,
get_args,
Expand All @@ -12,6 +13,7 @@
import inspect
from collections.abc import Iterable, Mapping
from dataclasses import MISSING, Field, fields, is_dataclass
from datetime import date, datetime, time, timedelta
from itertools import zip_longest


Expand All @@ -36,6 +38,8 @@ def cast_types(self, type_: Union[type, Any], value: Any) -> Any:
return self._apply_dict(type_, args, value)
if issubclass(type_, (set, frozenset)):
return self._apply_set(type_, args, value)
if issubclass(type_, (datetime, date, time, timedelta)):
return self._apply_datetime(type_, value)
if is_dataclass(type_):
return self._apply_dataclass(type_, value)
return self._cast_base(type_, value)
Expand Down Expand Up @@ -121,6 +125,23 @@ def _apply_dataclass(self, type_: type, values: Any) -> Any:
)
return type_(**field_mapping)

def _apply_datetime(
self, type_: Type[Union[datetime, date, time, timedelta]], values: Any
) -> Any:
if issubclass(type_, timedelta):
if isinstance(values, Mapping):
return timedelta(**values)
raise ValueError(f"Cannot cast value {values} to timedelta")
if isinstance(values, str):
return type_.fromisoformat(values)
if isinstance(values, Mapping):
return type_(**values)
if isinstance(values, Iterable):
return type_(*values)
if issubclass(type_, datetime) and isinstance(values, (int, float)):
return datetime.utcfromtimestamp(values)
raise ValueError(f"Cannot cast value {values} to {type_.__name__}!")

def _get_dataclass_default(self, field: Field) -> Any:
if field.default is not MISSING:
return field.default
Expand Down
118 changes: 118 additions & 0 deletions tests/test_type_converter/test_datetime_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import datetime
import pytest

from app_properties import properties


@pytest.fixture
def datetime_class_fixt():
@properties(filename="types_cast.yml", root="datetime")
class DatetimeClass:
timestamp_datetime_var: datetime.datetime
keywords_datetime_var: datetime.datetime
position_datetime_var: datetime.datetime
text_datetime_var: datetime.datetime

return DatetimeClass


@pytest.fixture
def date_class_fixt():
@properties(filename="types_cast.yml", root="datetime")
class DateClass:
keywords_date_var: datetime.date
position_date_var: datetime.date
text_date_var: datetime.date

return DateClass


@pytest.fixture
def time_class_fixt():
@properties(filename="types_cast.yml", root="datetime")
class TimeClass:
keywords_time_var: datetime.time
position_time_var: datetime.time
text_time_var: datetime.time

return TimeClass


@pytest.fixture
def timedelta_class_fixt():
@properties(filename="types_cast.yml", root="datetime")
class TimedeltaClass:
keywords_timedelta_var: datetime.timedelta

return TimedeltaClass


def test_datetime_field(datetime_class_fixt):
assert datetime_class_fixt.timestamp_datetime_var == datetime.datetime(
year=2022, month=12, day=10, hour=20, minute=56, second=24
)
assert datetime_class_fixt.keywords_datetime_var == datetime.datetime(
year=2022, month=12, day=10, hour=22, minute=43, second=0
)
assert datetime_class_fixt.position_datetime_var == datetime.datetime(
year=2022, month=12, day=10, hour=22, minute=44, second=20
)
assert datetime_class_fixt.text_datetime_var == datetime.datetime(
year=2022, month=12, day=10, hour=22, minute=44, second=56
)


def test_date_field(date_class_fixt):
assert date_class_fixt.keywords_date_var == datetime.date(
year=2022, month=11, day=11
)
assert date_class_fixt.position_date_var == datetime.date(
year=2022, month=10, day=20
)
assert date_class_fixt.text_date_var == datetime.date(
year=2022, month=11, day=24
)


def test_time_field(time_class_fixt):
assert time_class_fixt.keywords_time_var == datetime.time(
hour=2, minute=3, second=0
)
assert time_class_fixt.position_time_var == datetime.time(
hour=10, minute=23, second=20
)
assert time_class_fixt.text_time_var == datetime.time(
hour=8, minute=49, second=56
)


def test_timedelta_field(timedelta_class_fixt):
assert timedelta_class_fixt.keywords_timedelta_var == datetime.timedelta(
days=1, hours=12, minutes=42, seconds=34, weeks=1
)


def test_wrong_datetime_value():
with pytest.raises(ValueError):

@properties(filename="types_cast.yml", root="datetime")
class WrongDatetimeClass:
wrong_datetime_var: datetime.datetime

with pytest.raises(ValueError):

@properties(filename="types_cast.yml", root="datetime")
class WrongDateClass:
wrong_date_var: datetime.date

with pytest.raises(ValueError):

@properties(filename="types_cast.yml", root="datetime")
class WrongTimeClass:
wrong_time_var: datetime.time

with pytest.raises(ValueError):

@properties(filename="types_cast.yml", root="datetime")
class WrongTimedeltaClass:
wrong_timedelta_var: datetime.timedelta
47 changes: 46 additions & 1 deletion tests/test_type_converter/types_cast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,49 @@ union:
- "str"
- "str2"
- 60
wrong_union_dict_list: wrong_value
wrong_union_dict_list: wrong_value

datetime:
timestamp_datetime_var: 1670705784
keywords_datetime_var:
year: 2022
month: 12
day: 10
hour: 22
minute: 43
second: 0
position_datetime_var:
- 2022
- 12
- 10
- 22
- 44
- 20
text_datetime_var: 2022-12-10T22:44:56

keywords_date_var:
year: 2022
month: 11
day: 11
position_date_var:
- 2022
- 10
- 20
text_date_var: 2022-11-24

keywords_time_var:
hour: 2
minute: 3
second: 0
position_time_var:
- 10
- 23
- 20
text_time_var: "08:49:56"

keywords_timedelta_var:
days: 1
hours: 12
minutes: 42
seconds: 34
weeks: 1

0 comments on commit 7fbb073

Please sign in to comment.