Skip to content

Commit

Permalink
mypy checker added
Browse files Browse the repository at this point in the history
  • Loading branch information
dapper91 committed Feb 25, 2023
1 parent 940e405 commit 539d10a
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 20 deletions.
10 changes: 10 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,13 @@ repos:
- --line-length=120
- --multi-line=9
- --project=crontools
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.0.1
hooks:
- id: mypy
stages:
- commit
name: mypy
pass_filenames: false
args: ["--package", "crontools"]
additional_dependencies: ["types-tzlocal"]
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Changelog
------------------

- pipenv substituted by poetry
- mypy checker added


0.1.5 (2022-04-24)
Expand Down
53 changes: 33 additions & 20 deletions crontools/crontab.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
import dataclasses as dc
import datetime as dt
import heapq
from math import ceil
import operator as op
from math import ceil
from typing import Any, ClassVar, Dict, Generator, Generic, Iterable, Iterator, Optional, Tuple, Type, TypeVar, Union
from typing import cast

import tzlocal
from typing import Any, ClassVar, Dict, Generic, Iterator, Iterable, Optional, Type, TypeVar, Tuple

SENTINEL = object()

Expand Down Expand Up @@ -83,19 +85,19 @@ def fromstr(cls, string: str) -> 'Range':
begin, end, step = None, None, None
string = string.strip()

interval, *maybe_step = string.split('/', maxsplit=1)
if maybe_step:
maybe_step = maybe_step[0]
interval, *rem = string.split('/', maxsplit=1)
if rem:
maybe_step = rem[0]
step = cls._parse_number(maybe_step)

if interval == '*':
return cls(None, None, step)

begin, *maybe_end = interval.split('-', maxsplit=1)
begin, *rem = interval.split('-', maxsplit=1)
begin = cls.aliases.get(begin, begin) if cls.aliases else begin
begin = cls._parse_number(begin)
if maybe_end:
maybe_end = maybe_end[0]
if rem:
maybe_end = rem[0]
maybe_end = cls.aliases.get(maybe_end, maybe_end) if cls.aliases else maybe_end

end = cls._parse_number(maybe_end)
Expand All @@ -105,16 +107,18 @@ def fromstr(cls, string: str) -> 'Range':
return cls(begin, end, step)

@classmethod
def _parse_number(cls, value: str):
def _parse_number(cls, value: Union[str, int]) -> int:
try:
value = int(value)
parsed_value = int(value)
except ValueError:
raise ValueError(f"{cls.title} value must be of type int, got: {value}")

if not (cls.min_value <= value <= cls.max_value):
raise ValueError(f"{cls.title} value must be of range [{cls.min_value}, {cls.max_value}], got: {value}")
if not (cls.min_value <= parsed_value <= cls.max_value):
raise ValueError(
f"{cls.title} value must be of range [{cls.min_value}, {cls.max_value}], got: {parsed_value}",
)

return value
return parsed_value

@property
def is_default(self) -> bool:
Expand All @@ -136,6 +140,8 @@ def iter(self, start_from: Optional[int] = None) -> Iterator[int]:
begin = self.min_value
end = self.max_value
else:
assert self.begin is not None

begin = self.begin
end = self.begin if self.end is None else self.end

Expand Down Expand Up @@ -263,9 +269,9 @@ def fromstr(cls, string: str) -> 'Field[RangeType]':
:return: field
"""

ranges = (cls.range_type.fromstr(item) for item in string.split(','))
ranges = cast(Iterable[RangeType], (cls.range_type.fromstr(item) for item in string.split(',')))

return cls(ranges=tuple(sorted(ranges, key=lambda rng: rng.begin)))
return cls(ranges=tuple(sorted(ranges, key=lambda rng: rng.begin if rng.begin is not None else rng.min_value)))

@property
def is_default(self) -> bool:
Expand Down Expand Up @@ -313,23 +319,27 @@ def iter(self, year: Optional[int] = None, month: Optional[int] = None, start_fr
year = now.year if year is None else year
month = now.month if month is None else month

day_iter: Iterable[int]
if self._weekday_field.is_default:
day_iter = self._monthday_iter(year, month, start_from)
elif self._monthday_field.is_default:
day_iter = self._weekday_iter(year, month, start_from)
else:
day_iter = heapq.merge(self._monthday_iter(year, month, start_from), self._weekday_iter(year, month, start_from))
day_iter = heapq.merge(
self._monthday_iter(year, month, start_from),
self._weekday_iter(year, month, start_from),
)

return unique(day_iter)

def _monthday_iter(self, year: int, month: int, start_from: int = 1) -> Iterator[int]:
def _monthday_iter(self, year: int, month: int, start_from: int = 1) -> Generator[int, None, None]:
for day in self._monthday_field.iter(start_from=start_from):
if day > calendar.monthrange(year, month)[1]:
break

yield day

def _weekday_iter(self, year: int, month: int, start_day: int = 1) -> Iterator[int]:
def _weekday_iter(self, year: int, month: int, start_day: int = 1) -> Generator[int, None, None]:
curr_day = start_day
curr_weekday = calendar.weekday(year, month, curr_day) + 1
weekday_iter = self._weekday_field.iter(start_from=curr_weekday)
Expand Down Expand Up @@ -430,6 +440,7 @@ def parse(

fields_iter = iter(fields)
now = (now or dt.datetime.now(tz=tz)).astimezone(tz)
assert now.tzinfo is not None

return cls(
second_field=SecondsField.fromstr(next(fields_iter)) if seconds_ext else SecondsField.fromstr('0'),
Expand All @@ -452,7 +463,8 @@ def __iter__(self) -> Iterator[dt.datetime]:
for minute in self.minute_field:
for second in self.second_field:
yield dt.datetime(
year=year, month=month, day=day, hour=hour, minute=minute, second=second, tzinfo=self.tz,
year=year, month=month, day=day,
hour=hour, minute=minute, second=second, tzinfo=self.tz,
)

def iter(self, start_from: dt.datetime) -> Iterator[dt.datetime]:
Expand Down Expand Up @@ -508,7 +520,8 @@ def iter(self, start_from: dt.datetime) -> Iterator[dt.datetime]:
continue

yield dt.datetime(
year=year, month=month, day=day, hour=hour, minute=minute, second=second, tzinfo=self.tz,
year=year, month=month, day=day,
hour=hour, minute=minute, second=second, tzinfo=self.tz,
)

first_run = False
Expand Down
Empty file added crontools/py.typed
Empty file.
14 changes: 14 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ classifiers = [
"Topic :: Software Development :: Libraries",
"Topic :: Utilities"
]
include = ["crontools/py.typed"]

[tool.poetry.dependencies]
python = "^3.7"
Expand All @@ -25,8 +26,21 @@ types-tzlocal = "^4.1.0"
pytest = "^6.0"
pytest-cov = "^2.0"
freezegun = "^1.2"
mypy = "^1.0.1"
pre-commit = "^2.18.1"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"


[tool.mypy]
allow_redefinition = true
disallow_incomplete_defs = true
disallow_any_generics = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
show_error_codes = true
strict_equality = true
warn_unused_ignores = true

0 comments on commit 539d10a

Please sign in to comment.