Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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 stubs/python-dateutil/@tests/test_cases/check_inheritance.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@ class MyDateTime(datetime):
d2 = date.today()
x2 = d2 - relativedelta(days=1)
assert_type(x2, date)


class MyDate(date):
pass


# A date subclass can be promoted to datetime when the delta includes time.
assert_type(MyDate(2024, 1, 1) + relativedelta(hours=1), date)
64 changes: 64 additions & 0 deletions stubs/python-dateutil/@tests/test_cases/check_structural.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from collections.abc import Sequence
from datetime import date, datetime, time, timedelta, tzinfo
from io import StringIO
from typing_extensions import assert_type

from dateutil import parser, utils
from dateutil.relativedelta import relativedelta
from dateutil.rrule import WEEKLY, rrule, rruleset
from dateutil.tz import UTC, tzoffset, tzstr


class CharacterStream:
def __init__(self, text: str) -> None:
self.stream = StringIO(text)

def read(self, size: int, /) -> str:
return self.stream.read(size)


class TimezoneLookup:
def get(self, name: str, /) -> tzinfo | None:
return UTC if name == "CSTM" else None

def __contains__(self, name: object, /) -> bool:
return name == "CSTM"


class Monday:
weekday = 0
n = None


class Offset:
def total_seconds(self) -> float:
return 5400


def check_parser(flag: bool) -> None:
assert_type(parser.parse(CharacterStream("2024-01-01")), datetime)
assert_type(parser.parse("2024-01-01", fuzzy_with_tokens=flag), datetime | tuple[datetime, tuple[str, ...]])
assert_type(parser.parser().parse("2024-01-01", fuzzy_with_tokens=flag), datetime | tuple[datetime, tuple[str, ...]])
parser.parse("2024-01-01 00:00 CSTM", tzinfos=TimezoneLookup())


def check_utilities() -> None:
assert_type(utils.default_tzinfo(time(12), UTC), time)
assert_type(utils.default_tzinfo(datetime(2024, 1, 1), None), datetime)
utils.within_delta(3, 4, 1)
utils.within_delta(date(2024, 1, 1), date(2024, 1, 2), timedelta(days=1))


def check_recurrence() -> None:
rule = rrule(WEEKLY, dtstart=datetime(2024, 1, 1), byweekday=(day for day in (Monday(), 2)), count=3)
assert_type(rule[0], datetime)
assert_type(rule[:2], Sequence[datetime])
rules = rruleset()
rules.rrule(iter(rule))
rules.exrule((datetime(2024, 1, 1),))
assert_type(relativedelta(days=1.5, weekday=Monday()) * "2", relativedelta)


def check_offsets() -> None:
assert_type(tzoffset("custom", Offset()).utcoffset(None), timedelta)
assert_type(tzstr.instance("EST5"), tzstr)
2 changes: 1 addition & 1 deletion stubs/python-dateutil/METADATA.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version = "2.9.*"
version = "3.0.*"
upstream-repository = "https://github.com/dateutil/dateutil"
4 changes: 2 additions & 2 deletions stubs/python-dateutil/dateutil/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dateutil import easter, parser, relativedelta, rrule, tz, utils, zoneinfo
from dateutil import easter, parser, relativedelta, rrule, tz, utils

__all__ = ["easter", "parser", "relativedelta", "rrule", "tz", "utils", "zoneinfo"]
__all__ = ["easter", "parser", "relativedelta", "rrule", "tz", "utils"]

def __dir__() -> list[str]: ...
35 changes: 33 additions & 2 deletions stubs/python-dateutil/dateutil/_common.pyi
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
from typing import Protocol, TypeVar, type_check_only
from typing_extensions import Self

class weekday:
__slots__ = ["weekday", "n"]
def __init__(self, weekday: int, n: int | None = None) -> None: ...
def __call__(self, n: int) -> Self: ...
def __call__(self, n: int | None) -> Self: ...
def __eq__(self, other: object) -> bool: ...
def __hash__(self) -> int: ...
weekday: int
n: int
n: int | None

_T_co = TypeVar("_T_co", covariant=True)
_T_contra = TypeVar("_T_contra", contravariant=True)

@type_check_only
class _SizedReader(Protocol[_T_co]): # noqa: Y046 # Used by other modules.
def read(self, size: int, /) -> _T_co: ...

@type_check_only
class _ReadAll(Protocol[_T_co]): # noqa: Y046 # Used by other modules.
def read(self) -> _T_co: ...

@type_check_only
class _TotalSeconds(Protocol): # noqa: Y046 # Used by other modules.
def total_seconds(self) -> float: ...

@type_check_only
class _Weekday(Protocol): # noqa: Y046 # Used by other modules.
@property
def weekday(self) -> int: ...
@property
def n(self) -> int | None: ...

@type_check_only
class _Gettable(Protocol[_T_contra, _T_co]):
def get(self, key: _T_contra, /) -> _T_co | None: ...

@type_check_only
class _ContainsGet(_Gettable[_T_contra, _T_co], Protocol[_T_contra, _T_co]): # noqa: Y046 # Used by other modules.
def __contains__(self, key: object, /) -> bool: ...
94 changes: 78 additions & 16 deletions stubs/python-dateutil/dateutil/parser/_parser.pyi
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import re
from _typeshed import SupportsRead
from collections.abc import Callable, Mapping
from collections.abc import Callable, Sequence
from datetime import _TzInfo, datetime
from io import StringIO
from typing import IO, Any, Literal, TypeAlias, overload
from typing import Literal, Protocol, TypeAlias, overload, type_check_only
from typing_extensions import Self

_FileOrStr: TypeAlias = bytes | str | IO[str] | IO[Any]
from .._common import _ContainsGet, _SizedReader

_FileOrStr: TypeAlias = bytes | bytearray | str | _SizedReader[str]
_TzData: TypeAlias = _TzInfo | int | str | None
_TzInfos: TypeAlias = Mapping[str, _TzData] | Callable[[str, int], _TzData]
_TzInfos: TypeAlias = _ContainsGet[str, _TzData] | Callable[[str | None, int | None], _TzData]

__all__ = ["parse", "parserinfo", "ParserError"]

class _timelex:
_split_decimal: re.Pattern[str]
instream: StringIO | SupportsRead[str]
instream: StringIO | _SizedReader[str]
charstack: list[str]
tokenstack: list[str]
eof: bool
def __init__(self, instream: str | bytes | bytearray | SupportsRead[str]) -> None: ...
def __init__(self, instream: str | bytes | bytearray | _SizedReader[str]) -> None: ...
def get_token(self) -> str | None: ...
def __iter__(self) -> Self: ...
def __next__(self) -> str: ...
Expand All @@ -37,7 +38,28 @@ class _resultbase:
def _repr(self, classname: str) -> str: ...
def __len__(self) -> int: ...

@type_check_only
class _ParserInfo(Protocol):
@property
def dayfirst(self) -> bool: ...
@property
def yearfirst(self) -> bool: ...
@property
def UTCZONE(self) -> Sequence[str]: ...
def convertyear(self, year: int, century_specified: bool = False) -> int: ...
def validate(self, res: parser._result) -> bool: ...
def jump(self, name: str) -> bool: ...
def pertain(self, name: str) -> bool: ...
def utczone(self, name: str) -> bool: ...
def weekday(self, name: str) -> int | None: ...
def month(self, name: str) -> int | None: ...
def hms(self, name: str) -> int | None: ...
def ampm(self, name: str) -> int | None: ...
def tzoffset(self, name: str) -> int | None: ...

class parserinfo:
dayfirst: bool
yearfirst: bool
JUMP: list[str]
WEEKDAYS: list[tuple[str, str]]
MONTHS: list[tuple[str, str] | tuple[str, str, str]]
Expand All @@ -56,7 +78,7 @@ class parserinfo:
def utczone(self, name: str) -> bool: ...
def tzoffset(self, name: str) -> int | None: ...
def convertyear(self, year: int, century_specified: bool = False) -> int: ...
def validate(self, res: datetime) -> bool: ...
def validate(self, res: parser._result) -> bool: ...

class _ymd(list[int]):
century_specified: bool
Expand All @@ -75,15 +97,29 @@ class _ymd(list[int]):
def resolve_ymd(self, yearfirst: bool | None, dayfirst: bool | None) -> tuple[int, int, int]: ...

class parser:
info: parserinfo
def __init__(self, info: parserinfo | None = None) -> None: ...
class _result(_resultbase):
year: int | None
month: int | None
day: int | None
weekday: int | None
hour: int | None
minute: int | None
second: int | None
microsecond: int | None
tzname: str | None
tzoffset: int | None
ampm: int | None
any_unused_tokens: bool | None

info: _ParserInfo
def __init__(self, info: _ParserInfo | None = None) -> None: ...

@overload
def parse(
self,
timestr: _FileOrStr,
default: datetime | None = None,
ignoretz: bool = False,
ignoretz: bool | None = False,
tzinfos: _TzInfos | None = None,
*,
dayfirst: bool | None = ...,
Expand All @@ -96,25 +132,38 @@ class parser:
self,
timestr: _FileOrStr,
default: datetime | None = None,
ignoretz: bool = False,
ignoretz: bool | None = False,
tzinfos: _TzInfos | None = None,
*,
dayfirst: bool | None = ...,
yearfirst: bool | None = ...,
fuzzy: bool = ...,
fuzzy_with_tokens: Literal[True],
) -> tuple[datetime, tuple[str, ...]]: ...
@overload
def parse(
self,
timestr: _FileOrStr,
default: datetime | None = None,
ignoretz: bool | None = False,
tzinfos: _TzInfos | None = None,
*,
dayfirst: bool | None = ...,
yearfirst: bool | None = ...,
fuzzy: bool = ...,
fuzzy_with_tokens: bool,
) -> datetime | tuple[datetime, tuple[str, ...]]: ...

DEFAULTPARSER: parser

@overload
def parse(
timestr: _FileOrStr,
parserinfo: parserinfo | None = None,
parserinfo: _ParserInfo | None = None,
*,
dayfirst: bool | None = ...,
yearfirst: bool | None = ...,
ignoretz: bool = ...,
ignoretz: bool | None = ...,
fuzzy: bool = ...,
fuzzy_with_tokens: Literal[False] = False,
default: datetime | None = ...,
Expand All @@ -123,16 +172,29 @@ def parse(
@overload
def parse(
timestr: _FileOrStr,
parserinfo: parserinfo | None = None,
parserinfo: _ParserInfo | None = None,
*,
dayfirst: bool | None = ...,
yearfirst: bool | None = ...,
ignoretz: bool = ...,
ignoretz: bool | None = ...,
fuzzy: bool = ...,
fuzzy_with_tokens: Literal[True],
default: datetime | None = ...,
tzinfos: _TzInfos | None = ...,
) -> tuple[datetime, tuple[str, ...]]: ...
@overload
def parse(
timestr: _FileOrStr,
parserinfo: _ParserInfo | None = None,
*,
dayfirst: bool | None = ...,
yearfirst: bool | None = ...,
ignoretz: bool | None = ...,
fuzzy: bool = ...,
fuzzy_with_tokens: bool,
default: datetime | None = ...,
tzinfos: _TzInfos | None = ...,
) -> datetime | tuple[datetime, tuple[str, ...]]: ...

class _tzparser:
class _result(_resultbase):
Expand Down
12 changes: 7 additions & 5 deletions stubs/python-dateutil/dateutil/parser/isoparser.pyi
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
from _typeshed import SupportsRead
from datetime import date, datetime, time, tzinfo
from datetime import date, datetime, time
from typing import TypeAlias

_Readable: TypeAlias = SupportsRead[str | bytes]
from .._common import _ReadAll
from ..tz import tzoffset, tzutc

_Readable: TypeAlias = _ReadAll[str | bytes]
_TakesAscii: TypeAlias = str | bytes | _Readable

__all__ = ["isoparse", "isoparser"]

class isoparser:
def __init__(self, sep: str | bytes | None = None) -> None: ...
def __init__(self, sep: str | None = None) -> None: ...
def isoparse(self, dt_str: _TakesAscii) -> datetime: ...
def parse_isodate(self, datestr: _TakesAscii) -> date: ...
def parse_isotime(self, timestr: _TakesAscii) -> time: ...
def parse_tzstr(self, tzstr: _TakesAscii, zero_as_utc: bool = True) -> tzinfo: ...
def parse_tzstr(self, tzstr: _TakesAscii, zero_as_utc: bool = True) -> tzutc | tzoffset: ...

def isoparse(dt_str: _TakesAscii) -> datetime: ...
Loading
Loading