From 7493770d4dc277d68f1b541bfe4c367d2313dbc9 Mon Sep 17 00:00:00 2001 From: Sean Breckenridge Date: Thu, 27 Jan 2022 09:54:59 -0800 Subject: [PATCH] core: remove vendorized py37 isoformat code --- my/core/common.py | 3 +- my/core/compat.py | 11 ---- my/core/error.py | 4 +- my/core/py37.py | 122 ------------------------------------- my/core/query_range.py | 3 +- my/location/home.py | 4 +- my/taplog.py | 3 +- my/time/tz/via_location.py | 3 +- 8 files changed, 7 insertions(+), 146 deletions(-) delete mode 100644 my/core/py37.py diff --git a/my/core/common.py b/my/core/common.py index e2b7a37..fba7efb 100644 --- a/my/core/common.py +++ b/my/core/common.py @@ -358,12 +358,11 @@ def isoparse(s: str) -> tzdatetime: """ Parses timestamps formatted like 2020-05-01T10:32:02.925961Z """ - from .compat import fromisoformat # TODO could use dateutil? but it's quite slow as far as I remember.. # TODO support non-utc.. somehow? assert s.endswith('Z'), s s = s[:-1] + '+00:00' - return fromisoformat(s) + return datetime.fromisoformat(s) from .compat import Literal diff --git a/my/core/compat.py b/my/core/compat.py index e0e1dd8..8fc3ef5 100644 --- a/my/core/compat.py +++ b/my/core/compat.py @@ -3,7 +3,6 @@ Some backwards compatibility stuff/deprecation helpers ''' from types import ModuleType from typing import Callable -from datetime import datetime from . import warnings from .common import LazyLogger @@ -12,16 +11,6 @@ from .common import LazyLogger logger = LazyLogger('my.core.compat') -fromisoformat: Callable[[str], datetime] -import sys -if sys.version_info[:2] >= (3, 7): - # prevent mypy on py3.6 from complaining... - fromisoformat_real = datetime.fromisoformat - fromisoformat = fromisoformat_real -else: - from .py37 import fromisoformat - - def pre_pip_dal_handler( name: str, e: ModuleNotFoundError, diff --git a/my/core/error.py b/my/core/error.py index e11103e..5e358dc 100644 --- a/my/core/error.py +++ b/my/core/error.py @@ -127,8 +127,8 @@ def attach_dt(e: Exception, *, dt: Optional[datetime]) -> Exception: # todo it might be problematic because might mess with timezones (when it's converted to string, it's converted to a shift) def extract_error_datetime(e: Exception) -> Optional[datetime]: - from .compat import fromisoformat import re + from datetime import datetime for x in reversed(e.args): if isinstance(x, datetime): return x @@ -139,7 +139,7 @@ def extract_error_datetime(e: Exception) -> Optional[datetime]: continue ss = m.group(0) # todo not sure if should be defensive?? - return fromisoformat(ss) + return datetime.fromisoformat(ss) return None diff --git a/my/core/py37.py b/my/core/py37.py deleted file mode 100644 index 6a52593..0000000 --- a/my/core/py37.py +++ /dev/null @@ -1,122 +0,0 @@ -# borrowed from /usr/lib/python3.7/datetime.py -from datetime import datetime, timezone, timedelta - -def _parse_isoformat_date(dtstr): - # It is assumed that this function will only be called with a - # string of length exactly 10, and (though this is not used) ASCII-only - year = int(dtstr[0:4]) - if dtstr[4] != '-': - raise ValueError('Invalid date separator: %s' % dtstr[4]) - - month = int(dtstr[5:7]) - - if dtstr[7] != '-': - raise ValueError('Invalid date separator') - - day = int(dtstr[8:10]) - - return [year, month, day] - - -def _parse_hh_mm_ss_ff(tstr): - # Parses things of the form HH[:MM[:SS[.fff[fff]]]] - len_str = len(tstr) - - time_comps = [0, 0, 0, 0] - pos = 0 - for comp in range(0, 3): - if (len_str - pos) < 2: - raise ValueError('Incomplete time component') - - time_comps[comp] = int(tstr[pos:pos+2]) - - pos += 2 - next_char = tstr[pos:pos+1] - - if not next_char or comp >= 2: - break - - if next_char != ':': - raise ValueError('Invalid time separator: %c' % next_char) - - pos += 1 - - if pos < len_str: - if tstr[pos] != '.': - raise ValueError('Invalid microsecond component') - else: - pos += 1 - - len_remainder = len_str - pos - if len_remainder not in (3, 6): - raise ValueError('Invalid microsecond component') - - time_comps[3] = int(tstr[pos:]) - if len_remainder == 3: - time_comps[3] *= 1000 - - return time_comps - - -def _parse_isoformat_time(tstr): - # Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]] - len_str = len(tstr) - if len_str < 2: - raise ValueError('Isoformat time too short') - - # This is equivalent to re.search('[+-]', tstr), but faster - tz_pos = (tstr.find('-') + 1 or tstr.find('+') + 1) - timestr = tstr[:tz_pos-1] if tz_pos > 0 else tstr - - time_comps = _parse_hh_mm_ss_ff(timestr) - - tzi = None - if tz_pos > 0: - tzstr = tstr[tz_pos:] - - # Valid time zone strings are: - # HH:MM len: 5 - # HH:MM:SS len: 8 - # HH:MM:SS.ffffff len: 15 - - if len(tzstr) not in (5, 8, 15): - raise ValueError('Malformed time zone string') - - tz_comps = _parse_hh_mm_ss_ff(tzstr) - if all(x == 0 for x in tz_comps): - tzi = timezone.utc - else: - tzsign = -1 if tstr[tz_pos - 1] == '-' else 1 - - td = timedelta(hours=tz_comps[0], minutes=tz_comps[1], - seconds=tz_comps[2], microseconds=tz_comps[3]) - - tzi = timezone(tzsign * td) - - time_comps.append(tzi) - - return time_comps - -def fromisoformat(date_string, cls=datetime): - """Construct a datetime from the output of datetime.isoformat().""" - if not isinstance(date_string, str): - raise TypeError('fromisoformat: argument must be str') - - # Split this at the separator - dstr = date_string[0:10] - tstr = date_string[11:] - - try: - date_components = _parse_isoformat_date(dstr) - except ValueError: - raise ValueError('Invalid isoformat string: %s' % date_string) - - if tstr: - try: - time_components = _parse_isoformat_time(tstr) - except ValueError: - raise ValueError('Invalid isoformat string: %s' % date_string) - else: - time_components = [0, 0, 0, 0, None] - - return cls(*(date_components + time_components)) diff --git a/my/core/query_range.py b/my/core/query_range.py index 480c174..04952d4 100644 --- a/my/core/query_range.py +++ b/my/core/query_range.py @@ -24,7 +24,6 @@ from .query import ( ET, ) -from .compat import fromisoformat from .common import isoparse @@ -74,7 +73,7 @@ def parse_datetime_float(date_str: str) -> float: return ds_float try: # isoformat - default format when you call str() on datetime - return fromisoformat(ds).timestamp() + return datetime.fromisoformat(ds).timestamp() except ValueError: pass try: diff --git a/my/location/home.py b/my/location/home.py index 11de568..dd7209f 100644 --- a/my/location/home.py +++ b/my/location/home.py @@ -6,8 +6,6 @@ from datetime import datetime, date, time, timezone from functools import lru_cache from typing import Sequence, Tuple, Union, cast -from ..core.compat import fromisoformat - from my.config import location as user_config @@ -44,7 +42,7 @@ class Config(user_config): for x, loc in home2: dt: datetime if isinstance(x, str): - dt = fromisoformat(x) + dt = datetime.fromisoformat(x) elif isinstance(x, datetime): dt = x else: diff --git a/my/taplog.py b/my/taplog.py index 8e45f5b..f668a10 100644 --- a/my/taplog.py +++ b/my/taplog.py @@ -6,7 +6,6 @@ from datetime import datetime from typing import NamedTuple, Dict, Optional, Iterable from .core import get_files -from .core.compat import fromisoformat from my.config import taplog as user_config @@ -41,7 +40,7 @@ class Entry(NamedTuple): ts = self.row['timestamp'] # already with timezone apparently # TODO not sure if should stil localize though? it only kept tz offset, not real tz - return fromisoformat(ts) + return datetime.fromisoformat(ts) # TODO also has gps info! diff --git a/my/time/tz/via_location.py b/my/time/tz/via_location.py index fe3cd69..e390c43 100644 --- a/my/time/tz/via_location.py +++ b/my/time/tz/via_location.py @@ -141,7 +141,6 @@ def localize(dt: datetime) -> tzdatetime: from ...core import stat, Stats def stats() -> Stats: - from ...core.compat import fromisoformat # TODO not sure what would be a good stat() for this module... # might be nice to print some actual timezones? # there aren't really any great iterables to expose @@ -150,6 +149,6 @@ def stats() -> Stats: # note: deliberately take + 2 years, so the iterator exhausts. otherwise stuff might never get cached # need to think about it... for Y in range(1990, last): - dt = fromisoformat(f'{Y}-01-01 01:01:01') + dt = datetime.fromisoformat(f'{Y}-01-01 01:01:01') yield localize(dt) return stat(localized_years)