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

refactor: add typing to & do maintenance of periods #1223

Merged
merged 20 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

### 41.5.7 [#1223](https://github.com/openfisca/openfisca-core/pull/1223)

#### Technical changes

- Update `pendulum'
- Remove run-time type-checks
- Add typing to the periods module

### 41.5.6 [#1185](https://github.com/openfisca/openfisca-core/pull/1185)

#### Technical changes
Expand Down
53 changes: 42 additions & 11 deletions openfisca_core/periods/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,57 @@
#
# See: https://www.python.org/dev/peps/pep-0008/#imports

from .config import ( # noqa: F401
DAY,
ETERNITY,
from . import types
from ._errors import ParserError
from .config import (
INSTANT_PATTERN,
MONTH,
WEEK,
WEEKDAY,
YEAR,
date_by_instant_cache,
str_by_instant_cache,
year_or_month_or_day_re,
)
from .date_unit import DateUnit # noqa: F401
from .helpers import ( # noqa: F401
from .date_unit import DateUnit
from .helpers import (
instant,
instant_date,
key_period_size,
period,
unit_weight,
unit_weights,
)
from .instant_ import Instant # noqa: F401
from .period_ import Period # noqa: F401
from .instant_ import Instant
from .period_ import Period

WEEKDAY = DateUnit.WEEKDAY
WEEK = DateUnit.WEEK
DAY = DateUnit.DAY
MONTH = DateUnit.MONTH
YEAR = DateUnit.YEAR
ETERNITY = DateUnit.ETERNITY
ISOFORMAT = DateUnit.isoformat
ISOCALENDAR = DateUnit.isocalendar

__all__ = [
"INSTANT_PATTERN",
"date_by_instant_cache",
"str_by_instant_cache",
"year_or_month_or_day_re",
"DateUnit",
"instant",
"instant_date",
"key_period_size",
"period",
"unit_weight",
"unit_weights",
"Instant",
"Period",
"WEEKDAY",
"WEEK",
"DAY",
"MONTH",
"YEAR",
"ETERNITY",
"ISOFORMAT",
"ISOCALENDAR",
"ParserError",
"types",
]
3 changes: 3 additions & 0 deletions openfisca_core/periods/_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from pendulum.parsing.exceptions import ParserError

__all__ = ["ParserError"]
18 changes: 11 additions & 7 deletions openfisca_core/periods/_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
import re

import pendulum
from pendulum.datetime import Date
from pendulum.parsing import ParserError

from . import types as t
from ._errors import ParserError
from .date_unit import DateUnit
from .instant_ import Instant
from .period_ import Period

invalid_week = re.compile(r".*(W[1-9]|W[1-9]-[0-9]|W[0-5][0-9]-0)$")


def _parse_period(value: str) -> Optional[Period]:
def _parse_period(value: str) -> Optional[t.Period]:
"""Parses ISO format/calendar periods.

Such as "2012" or "2015-03".
Expand All @@ -34,7 +34,7 @@ def _parse_period(value: str) -> Optional[Period]:
return None

# Check for a non-empty string.
if not (value and isinstance(value, str)):
if len(value) == 0:
bonjourmauko marked this conversation as resolved.
Show resolved Hide resolved
raise AttributeError

# If it's negative, next!
Expand All @@ -48,15 +48,15 @@ def _parse_period(value: str) -> Optional[Period]:
unit = _parse_unit(value)
date = pendulum.parse(value, exact=True)

if not isinstance(date, Date):
if not isinstance(date, pendulum.Date):
raise ValueError

instant = Instant((date.year, date.month, date.day))

return Period((unit, instant, 1))


def _parse_unit(value: str) -> DateUnit:
def _parse_unit(value: str) -> t.DateUnit:
"""Determine the date unit of a date string.

Args:
Expand Down Expand Up @@ -94,4 +94,8 @@ def _parse_unit(value: str) -> DateUnit:

return DateUnit.DAY

raise ValueError
else:
raise ValueError


__all__ = ["_parse_period", "_parse_unit"]
16 changes: 7 additions & 9 deletions openfisca_core/periods/config.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import re

from .date_unit import DateUnit
import pendulum

WEEKDAY = DateUnit.WEEKDAY
WEEK = DateUnit.WEEK
DAY = DateUnit.DAY
MONTH = DateUnit.MONTH
YEAR = DateUnit.YEAR
ETERNITY = DateUnit.ETERNITY
from . import types as t

# Matches "2015", "2015-01", "2015-01-01"
# Does not match "2015-13", "2015-12-32"
INSTANT_PATTERN = re.compile(
r"^\d{4}(-(0[1-9]|1[012]))?(-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))?$",
)

date_by_instant_cache: dict = {}
str_by_instant_cache: dict = {}
date_by_instant_cache: dict[t.Instant, pendulum.Date] = {}
str_by_instant_cache: dict[t.Instant, t.InstantStr] = {}
year_or_month_or_day_re = re.compile(
r"(18|19|20)\d{2}(-(0?[1-9]|1[0-2])(-([0-2]?\d|3[0-1]))?)?$",
)


__all__ = ["INSTANT_PATTERN", "date_by_instant_cache", "str_by_instant_cache"]
16 changes: 13 additions & 3 deletions openfisca_core/periods/date_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

from strenum import StrEnum

from . import types as t


class DateUnitMeta(EnumMeta):
@property
def isoformat(cls) -> tuple[DateUnit, ...]:
def isoformat(self) -> tuple[t.DateUnit, ...]:
"""Creates a :obj:`tuple` of ``key`` with isoformat items.

Returns:
Expand All @@ -27,7 +29,7 @@ def isoformat(cls) -> tuple[DateUnit, ...]:
return DateUnit.DAY, DateUnit.MONTH, DateUnit.YEAR

@property
def isocalendar(cls) -> tuple[DateUnit, ...]:
def isocalendar(self) -> tuple[t.DateUnit, ...]:
"""Creates a :obj:`tuple` of ``key`` with isocalendar items.

Returns:
Expand Down Expand Up @@ -64,7 +66,7 @@ class DateUnit(StrEnum, metaclass=DateUnitMeta):
{<DateUnit.DAY: 'day'>: 'day'}

>>> list(DateUnit)
[<DateUnit.WEEKDAY: 'weekday'>, <DateUnit.WEEK: 'week'>, <DateUnit.DAY: 'day'>, ...]
[<DateUnit.WEEKDAY: 'weekday'>, <DateUnit.WEEK: 'week'>, <DateUnit.D...

>>> len(DateUnit)
6
Expand Down Expand Up @@ -94,9 +96,17 @@ class DateUnit(StrEnum, metaclass=DateUnitMeta):

"""

def __contains__(self, other: object) -> bool:
if isinstance(other, str):
return super().__contains__(other)
return NotImplemented

WEEKDAY = "weekday"
WEEK = "week"
DAY = "day"
MONTH = "month"
YEAR = "year"
ETERNITY = "eternity"


__all__ = ["DateUnit"]
Loading
Loading