22import dataclasses as dc
33import datetime as dt
44import heapq
5- from math import ceil
65import operator as op
6+ from math import ceil
7+ from typing import Any , ClassVar , Dict , Generator , Generic , Iterable , Iterator , Optional , Tuple , Type , TypeVar , Union
8+ from typing import cast
9+
710import tzlocal
8- from typing import Any , ClassVar , Dict , Generic , Iterator , Iterable , Optional , Type , TypeVar , Tuple
911
1012SENTINEL = object ()
1113
@@ -83,19 +85,19 @@ def fromstr(cls, string: str) -> 'Range':
8385 begin , end , step = None , None , None
8486 string = string .strip ()
8587
86- interval , * maybe_step = string .split ('/' , maxsplit = 1 )
87- if maybe_step :
88- maybe_step = maybe_step [0 ]
88+ interval , * rem = string .split ('/' , maxsplit = 1 )
89+ if rem :
90+ maybe_step = rem [0 ]
8991 step = cls ._parse_number (maybe_step )
9092
9193 if interval == '*' :
9294 return cls (None , None , step )
9395
94- begin , * maybe_end = interval .split ('-' , maxsplit = 1 )
96+ begin , * rem = interval .split ('-' , maxsplit = 1 )
9597 begin = cls .aliases .get (begin , begin ) if cls .aliases else begin
9698 begin = cls ._parse_number (begin )
97- if maybe_end :
98- maybe_end = maybe_end [0 ]
99+ if rem :
100+ maybe_end = rem [0 ]
99101 maybe_end = cls .aliases .get (maybe_end , maybe_end ) if cls .aliases else maybe_end
100102
101103 end = cls ._parse_number (maybe_end )
@@ -105,16 +107,18 @@ def fromstr(cls, string: str) -> 'Range':
105107 return cls (begin , end , step )
106108
107109 @classmethod
108- def _parse_number (cls , value : str ) :
110+ def _parse_number (cls , value : Union [ str , int ]) -> int :
109111 try :
110- value = int (value )
112+ parsed_value = int (value )
111113 except ValueError :
112114 raise ValueError (f"{ cls .title } value must be of type int, got: { value } " )
113115
114- if not (cls .min_value <= value <= cls .max_value ):
115- raise ValueError (f"{ cls .title } value must be of range [{ cls .min_value } , { cls .max_value } ], got: { value } " )
116+ if not (cls .min_value <= parsed_value <= cls .max_value ):
117+ raise ValueError (
118+ f"{ cls .title } value must be of range [{ cls .min_value } , { cls .max_value } ], got: { parsed_value } " ,
119+ )
116120
117- return value
121+ return parsed_value
118122
119123 @property
120124 def is_default (self ) -> bool :
@@ -136,6 +140,8 @@ def iter(self, start_from: Optional[int] = None) -> Iterator[int]:
136140 begin = self .min_value
137141 end = self .max_value
138142 else :
143+ assert self .begin is not None
144+
139145 begin = self .begin
140146 end = self .begin if self .end is None else self .end
141147
@@ -263,9 +269,9 @@ def fromstr(cls, string: str) -> 'Field[RangeType]':
263269 :return: field
264270 """
265271
266- ranges = ( cls .range_type .fromstr (item ) for item in string .split (',' ))
272+ ranges = cast ( Iterable [ RangeType ], ( cls .range_type .fromstr (item ) for item in string .split (',' ) ))
267273
268- return cls (ranges = tuple (sorted (ranges , key = lambda rng : rng .begin )))
274+ return cls (ranges = tuple (sorted (ranges , key = lambda rng : rng .begin if rng . begin is not None else rng . min_value )))
269275
270276 @property
271277 def is_default (self ) -> bool :
@@ -313,23 +319,27 @@ def iter(self, year: Optional[int] = None, month: Optional[int] = None, start_fr
313319 year = now .year if year is None else year
314320 month = now .month if month is None else month
315321
322+ day_iter : Iterable [int ]
316323 if self ._weekday_field .is_default :
317324 day_iter = self ._monthday_iter (year , month , start_from )
318325 elif self ._monthday_field .is_default :
319326 day_iter = self ._weekday_iter (year , month , start_from )
320327 else :
321- day_iter = heapq .merge (self ._monthday_iter (year , month , start_from ), self ._weekday_iter (year , month , start_from ))
328+ day_iter = heapq .merge (
329+ self ._monthday_iter (year , month , start_from ),
330+ self ._weekday_iter (year , month , start_from ),
331+ )
322332
323333 return unique (day_iter )
324334
325- def _monthday_iter (self , year : int , month : int , start_from : int = 1 ) -> Iterator [int ]:
335+ def _monthday_iter (self , year : int , month : int , start_from : int = 1 ) -> Generator [int , None , None ]:
326336 for day in self ._monthday_field .iter (start_from = start_from ):
327337 if day > calendar .monthrange (year , month )[1 ]:
328338 break
329339
330340 yield day
331341
332- def _weekday_iter (self , year : int , month : int , start_day : int = 1 ) -> Iterator [int ]:
342+ def _weekday_iter (self , year : int , month : int , start_day : int = 1 ) -> Generator [int , None , None ]:
333343 curr_day = start_day
334344 curr_weekday = calendar .weekday (year , month , curr_day ) + 1
335345 weekday_iter = self ._weekday_field .iter (start_from = curr_weekday )
@@ -430,6 +440,7 @@ def parse(
430440
431441 fields_iter = iter (fields )
432442 now = (now or dt .datetime .now (tz = tz )).astimezone (tz )
443+ assert now .tzinfo is not None
433444
434445 return cls (
435446 second_field = SecondsField .fromstr (next (fields_iter )) if seconds_ext else SecondsField .fromstr ('0' ),
@@ -452,7 +463,8 @@ def __iter__(self) -> Iterator[dt.datetime]:
452463 for minute in self .minute_field :
453464 for second in self .second_field :
454465 yield dt .datetime (
455- year = year , month = month , day = day , hour = hour , minute = minute , second = second , tzinfo = self .tz ,
466+ year = year , month = month , day = day ,
467+ hour = hour , minute = minute , second = second , tzinfo = self .tz ,
456468 )
457469
458470 def iter (self , start_from : dt .datetime ) -> Iterator [dt .datetime ]:
@@ -508,7 +520,8 @@ def iter(self, start_from: dt.datetime) -> Iterator[dt.datetime]:
508520 continue
509521
510522 yield dt .datetime (
511- year = year , month = month , day = day , hour = hour , minute = minute , second = second , tzinfo = self .tz ,
523+ year = year , month = month , day = day ,
524+ hour = hour , minute = minute , second = second , tzinfo = self .tz ,
512525 )
513526
514527 first_run = False
0 commit comments