core: remove vendorized py37 isoformat code

This commit is contained in:
Sean Breckenridge 2022-01-27 09:54:59 -08:00 committed by karlicoss
parent 03dd1271f4
commit 7493770d4d
8 changed files with 7 additions and 146 deletions

View file

@ -358,12 +358,11 @@ def isoparse(s: str) -> tzdatetime:
""" """
Parses timestamps formatted like 2020-05-01T10:32:02.925961Z 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 could use dateutil? but it's quite slow as far as I remember..
# TODO support non-utc.. somehow? # TODO support non-utc.. somehow?
assert s.endswith('Z'), s assert s.endswith('Z'), s
s = s[:-1] + '+00:00' s = s[:-1] + '+00:00'
return fromisoformat(s) return datetime.fromisoformat(s)
from .compat import Literal from .compat import Literal

View file

@ -3,7 +3,6 @@ Some backwards compatibility stuff/deprecation helpers
''' '''
from types import ModuleType from types import ModuleType
from typing import Callable from typing import Callable
from datetime import datetime
from . import warnings from . import warnings
from .common import LazyLogger from .common import LazyLogger
@ -12,16 +11,6 @@ from .common import LazyLogger
logger = LazyLogger('my.core.compat') 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( def pre_pip_dal_handler(
name: str, name: str,
e: ModuleNotFoundError, e: ModuleNotFoundError,

View file

@ -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) # 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]: def extract_error_datetime(e: Exception) -> Optional[datetime]:
from .compat import fromisoformat
import re import re
from datetime import datetime
for x in reversed(e.args): for x in reversed(e.args):
if isinstance(x, datetime): if isinstance(x, datetime):
return x return x
@ -139,7 +139,7 @@ def extract_error_datetime(e: Exception) -> Optional[datetime]:
continue continue
ss = m.group(0) ss = m.group(0)
# todo not sure if should be defensive?? # todo not sure if should be defensive??
return fromisoformat(ss) return datetime.fromisoformat(ss)
return None return None

View file

@ -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))

View file

@ -24,7 +24,6 @@ from .query import (
ET, ET,
) )
from .compat import fromisoformat
from .common import isoparse from .common import isoparse
@ -74,7 +73,7 @@ def parse_datetime_float(date_str: str) -> float:
return ds_float return ds_float
try: try:
# isoformat - default format when you call str() on datetime # isoformat - default format when you call str() on datetime
return fromisoformat(ds).timestamp() return datetime.fromisoformat(ds).timestamp()
except ValueError: except ValueError:
pass pass
try: try:

View file

@ -6,8 +6,6 @@ from datetime import datetime, date, time, timezone
from functools import lru_cache from functools import lru_cache
from typing import Sequence, Tuple, Union, cast from typing import Sequence, Tuple, Union, cast
from ..core.compat import fromisoformat
from my.config import location as user_config from my.config import location as user_config
@ -44,7 +42,7 @@ class Config(user_config):
for x, loc in home2: for x, loc in home2:
dt: datetime dt: datetime
if isinstance(x, str): if isinstance(x, str):
dt = fromisoformat(x) dt = datetime.fromisoformat(x)
elif isinstance(x, datetime): elif isinstance(x, datetime):
dt = x dt = x
else: else:

View file

@ -6,7 +6,6 @@ from datetime import datetime
from typing import NamedTuple, Dict, Optional, Iterable from typing import NamedTuple, Dict, Optional, Iterable
from .core import get_files from .core import get_files
from .core.compat import fromisoformat
from my.config import taplog as user_config from my.config import taplog as user_config
@ -41,7 +40,7 @@ class Entry(NamedTuple):
ts = self.row['timestamp'] ts = self.row['timestamp']
# already with timezone apparently # already with timezone apparently
# TODO not sure if should stil localize though? it only kept tz offset, not real tz # 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! # TODO also has gps info!

View file

@ -141,7 +141,6 @@ def localize(dt: datetime) -> tzdatetime:
from ...core import stat, Stats from ...core import stat, Stats
def stats() -> Stats: def stats() -> Stats:
from ...core.compat import fromisoformat
# TODO not sure what would be a good stat() for this module... # TODO not sure what would be a good stat() for this module...
# might be nice to print some actual timezones? # might be nice to print some actual timezones?
# there aren't really any great iterables to expose # 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 # note: deliberately take + 2 years, so the iterator exhausts. otherwise stuff might never get cached
# need to think about it... # need to think about it...
for Y in range(1990, last): 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) yield localize(dt)
return stat(localized_years) return stat(localized_years)