# HG changeset patch # User Matt Harbison # Date 2021-03-06 20:26:46 # Node ID 15c2f9220ae881ca91ee8b4314965e43493b3a41 # Parent e571fec5b606667d062b7822589879a08324a1a8 typing: add type annotations to mercurial/utils/dateutil.py For now, I'm just typing around the edges to help find issues with TortoiseHg. If the custom `hgdate` type is useful elsewhere as I go, I'll move it to a file dedicated to custom types. I'm not loving the ban on camelcase type names here that test-check-code.t flagged, but I'm not sure how to disable that even if everyone agreed that it's a bad idea to go against the normal convention for types. While here, fix an issue that pytype found in `parsedate` when an invalid date tuple is passed by raising a ProgrammingError instead of crashing. (Tuple doesn't have a `strip` attribute.) Differential Revision: https://phab.mercurial-scm.org/D10123 diff --git a/mercurial/utils/dateutil.py b/mercurial/utils/dateutil.py --- a/mercurial/utils/dateutil.py +++ b/mercurial/utils/dateutil.py @@ -18,6 +18,18 @@ from .. import ( pycompat, ) +if pycompat.TYPE_CHECKING: + from typing import ( + Callable, + Dict, + Iterable, + Optional, + Tuple, + Union, + ) + + hgdate = Tuple[float, int] # (unixtime, offset) + # used by parsedate defaultdateformats = ( b'%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601 @@ -62,6 +74,7 @@ extendeddateformats = defaultdateformats def makedate(timestamp=None): + # type: (Optional[float]) -> hgdate """Return a unix timestamp (or the current time) as a (unixtime, offset) tuple based off the local timezone.""" if timestamp is None: @@ -79,6 +92,7 @@ def makedate(timestamp=None): def datestr(date=None, format=b'%a %b %d %H:%M:%S %Y %1%2'): + # type: (Optional[hgdate], bytes) -> bytes """represent a (unixtime, offset) tuple as a localized time. unixtime is seconds since the epoch, and offset is the time zone's number of seconds away from UTC. @@ -116,11 +130,13 @@ def datestr(date=None, format=b'%a %b %d def shortdate(date=None): + # type: (Optional[hgdate]) -> bytes """turn (timestamp, tzoff) tuple into iso 8631 date.""" return datestr(date, format=b'%Y-%m-%d') def parsetimezone(s): + # type: (bytes) -> Tuple[Optional[int], bytes] """find a trailing timezone, if any, in string, and return a (offset, remainder) pair""" s = pycompat.bytestr(s) @@ -156,6 +172,7 @@ def parsetimezone(s): def strdate(string, format, defaults=None): + # type: (bytes, bytes, Optional[Dict[bytes, Tuple[bytes, bytes]]]) -> hgdate """parse a localized time string and return a (unixtime, offset) tuple. if the string cannot be parsed, ValueError is raised.""" if defaults is None: @@ -198,6 +215,7 @@ def strdate(string, format, defaults=Non def parsedate(date, formats=None, bias=None): + # type: (Union[bytes, hgdate], Optional[Iterable[bytes]], Optional[Dict[bytes, bytes]]) -> hgdate """parse a localized date/time and return a (unixtime, offset) tuple. The date may be a "unixtime offset" string or in one of the specified @@ -223,8 +241,11 @@ def parsedate(date, formats=None, bias=N bias = {} if not date: return 0, 0 - if isinstance(date, tuple) and len(date) == 2: - return date + if isinstance(date, tuple): + if len(date) == 2: + return date + else: + raise error.ProgrammingError(b"invalid date format") if not formats: formats = defaultdateformats date = date.strip() @@ -284,6 +305,7 @@ def parsedate(date, formats=None, bias=N def matchdate(date): + # type: (bytes) -> Callable[[float], bool] """Return a function that matches a given date match specifier Formats include: @@ -313,10 +335,12 @@ def matchdate(date): """ def lower(date): + # type: (bytes) -> float d = {b'mb': b"1", b'd': b"1"} return parsedate(date, extendeddateformats, d)[0] def upper(date): + # type: (bytes) -> float d = {b'mb': b"12", b'HI': b"23", b'M': b"59", b'S': b"59"} for days in (b"31", b"30", b"29"): try: