Patchwork D10123: typing: add type annotations to mercurial/utils/dateutil.py

login
register
mail settings
Submitter phabricator
Date March 7, 2021, 12:45 a.m.
Message ID <differential-rev-PHID-DREV-cgkhpbkqqcwvvjwvbg4m-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/48437/
State Superseded
Headers show

Comments

phabricator - March 7, 2021, 12:45 a.m.
mharbison72 created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  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.)

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D10123

AFFECTED FILES
  mercurial/utils/dateutil.py

CHANGE DETAILS




To: mharbison72, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

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 @@ 
     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 @@ 
 
 
 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 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 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 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 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 @@ 
         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 matchdate(date):
+    # type: (bytes) -> Callable[[float], bool]
     """Return a function that matches a given date match specifier
 
     Formats include:
@@ -313,10 +335,12 @@ 
     """
 
     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: