core/compat: move fromisoformat to .core.compat module
This commit is contained in:
parent
09e0f66892
commit
a843407e40
8 changed files with 69 additions and 34 deletions
|
@ -23,7 +23,7 @@ def inputs() -> Sequence[Path]:
|
|||
|
||||
|
||||
from .core import dataclass, Json, PathIsh, datetime_aware
|
||||
from .core.common import isoparse
|
||||
from .core.compat import fromisoformat
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -39,6 +39,7 @@ class Entry:
|
|||
@property
|
||||
def dt(self) -> datetime_aware:
|
||||
# contains utc already
|
||||
# TODO after python>=3.11, could just use fromisoformat
|
||||
ds = self.json['date']
|
||||
elen = 27
|
||||
lds = len(ds)
|
||||
|
@ -46,10 +47,10 @@ class Entry:
|
|||
# ugh. sometimes contains less that 6 decimal points
|
||||
ds = ds[:-1] + '0' * (elen - lds) + 'Z'
|
||||
elif lds > elen:
|
||||
# ahd sometimes more...
|
||||
# and sometimes more...
|
||||
ds = ds[:elen - 1] + 'Z'
|
||||
|
||||
return isoparse(ds)
|
||||
return fromisoformat(ds)
|
||||
|
||||
@property
|
||||
def active(self) -> Optional[str]:
|
||||
|
|
|
@ -313,20 +313,18 @@ class classproperty(Generic[_R]):
|
|||
# def __get__(self) -> _R:
|
||||
# return self.f()
|
||||
|
||||
# TODO deprecate in favor of datetime_aware
|
||||
tzdatetime = datetime
|
||||
# for now just serves documentation purposes... but one day might make it statically verifiable where possible?
|
||||
# TODO e.g. maybe use opaque mypy alias?
|
||||
datetime_naive = datetime
|
||||
datetime_aware = datetime
|
||||
|
||||
|
||||
# TODO doctests?
|
||||
def isoparse(s: str) -> tzdatetime:
|
||||
"""
|
||||
Parses timestamps formatted like 2020-05-01T10:32:02.925961Z
|
||||
"""
|
||||
# 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 datetime.fromisoformat(s)
|
||||
# TODO deprecate
|
||||
tzdatetime = datetime_aware
|
||||
|
||||
|
||||
# TODO deprecate (although could be used in modules)
|
||||
from .compat import fromisoformat as isoparse
|
||||
|
||||
|
||||
import re
|
||||
|
@ -590,12 +588,6 @@ def asdict(thing: Any) -> Json:
|
|||
raise TypeError(f'Could not convert object {thing} to dict')
|
||||
|
||||
|
||||
# for now just serves documentation purposes... but one day might make it statically verifiable where possible?
|
||||
# TODO e.g. maybe use opaque mypy alias?
|
||||
datetime_naive = datetime
|
||||
datetime_aware = datetime
|
||||
|
||||
|
||||
def assert_subpackage(name: str) -> None:
|
||||
# can lead to some unexpected issues if you 'import cachew' which being in my/core directory.. so let's protect against it
|
||||
# NOTE: if we use overlay, name can be smth like my.origg.my.core.cachew ...
|
||||
|
|
|
@ -76,3 +76,42 @@ if sys.version_info[:2] <= (3, 9):
|
|||
return lo
|
||||
else:
|
||||
from bisect import bisect_left
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
if sys.version_info[:2] >= (3, 11):
|
||||
fromisoformat = datetime.fromisoformat
|
||||
else:
|
||||
def fromisoformat(date_string: str) -> datetime:
|
||||
# didn't support Z as "utc" before 3.11
|
||||
if date_string.endswith('Z'):
|
||||
# NOTE: can be removed from 3.11?
|
||||
# https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat
|
||||
date_string = date_string[:-1] + '+00:00'
|
||||
return datetime.fromisoformat(date_string)
|
||||
|
||||
|
||||
def test_fromisoformat() -> None:
|
||||
from datetime import timezone
|
||||
|
||||
# feedbin has this format
|
||||
assert fromisoformat('2020-05-01T10:32:02.925961Z') == datetime(
|
||||
2020, 5, 1, 10, 32, 2, 925961, timezone.utc,
|
||||
)
|
||||
|
||||
# polar has this format
|
||||
assert fromisoformat('2018-11-28T22:04:01.304Z') == datetime(
|
||||
2018, 11, 28, 22, 4, 1, 304000, timezone.utc,
|
||||
)
|
||||
|
||||
# stackexchange, runnerup has this format
|
||||
assert fromisoformat('2020-11-30T00:53:12Z') == datetime(
|
||||
2020, 11, 30, 0, 53, 12, 0, timezone.utc,
|
||||
)
|
||||
|
||||
# arbtt has this format (sometimes less/more than 6 digits in milliseconds)
|
||||
# TODO doesn't work atm, not sure if really should be supported...
|
||||
# maybe should have flags for weird formats?
|
||||
# assert isoparse('2017-07-18T18:59:38.21731Z') == datetime(
|
||||
# 2017, 7, 18, 18, 59, 38, 217310, timezone.utc,
|
||||
# )
|
||||
|
|
|
@ -24,7 +24,7 @@ from .query import (
|
|||
ET,
|
||||
)
|
||||
|
||||
from .common import isoparse
|
||||
from .compat import fromisoformat
|
||||
|
||||
|
||||
timedelta_regex = re.compile(r"^((?P<weeks>[\.\d]+?)w)?((?P<days>[\.\d]+?)d)?((?P<hours>[\.\d]+?)h)?((?P<minutes>[\.\d]+?)m)?((?P<seconds>[\.\d]+?)s)?$")
|
||||
|
@ -78,7 +78,7 @@ def parse_datetime_float(date_str: str) -> float:
|
|||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return isoparse(ds).timestamp()
|
||||
return fromisoformat(ds).timestamp()
|
||||
except (AssertionError, ValueError):
|
||||
pass
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ from typing import List, Dict, Iterable, NamedTuple, Sequence, Optional
|
|||
import json
|
||||
|
||||
from .core import LazyLogger, Json, Res
|
||||
from .core.common import isoparse
|
||||
from .core.compat import fromisoformat
|
||||
from .core.error import echain, sort_res_by
|
||||
from .core.konsume import wrap, Zoomable, Wdict
|
||||
|
||||
|
@ -145,7 +145,7 @@ class Loader:
|
|||
cmap[hlid] = ccs
|
||||
ccs.append(Comment(
|
||||
cid=cid.value,
|
||||
created=isoparse(crt.value),
|
||||
created=fromisoformat(crt.value),
|
||||
text=html.value, # TODO perhaps coonvert from html to text or org?
|
||||
))
|
||||
v.consume()
|
||||
|
@ -183,7 +183,7 @@ class Loader:
|
|||
|
||||
yield Highlight(
|
||||
hid=hid,
|
||||
created=isoparse(crt),
|
||||
created=fromisoformat(crt),
|
||||
selection=text,
|
||||
comments=tuple(comments),
|
||||
tags=tuple(htags),
|
||||
|
@ -221,7 +221,7 @@ class Loader:
|
|||
path = Path(config.polar_dir) / 'stash' / filename
|
||||
|
||||
yield Book(
|
||||
created=isoparse(added),
|
||||
created=fromisoformat(added),
|
||||
uid=self.uid,
|
||||
path=path,
|
||||
title=title,
|
||||
|
|
|
@ -7,7 +7,8 @@ from my.config import feedbin as config
|
|||
from pathlib import Path
|
||||
from typing import Sequence
|
||||
|
||||
from ..core.common import listify, get_files, isoparse
|
||||
from ..core.common import listify, get_files
|
||||
from ..core.compat import fromisoformat
|
||||
from .common import Subscription
|
||||
|
||||
|
||||
|
@ -22,7 +23,7 @@ def parse_file(f: Path):
|
|||
raw = json.loads(f.read_text())
|
||||
for r in raw:
|
||||
yield Subscription(
|
||||
created_at=isoparse(r['created_at']),
|
||||
created_at=fromisoformat(r['created_at']),
|
||||
title=r['title'],
|
||||
url=r['site_url'],
|
||||
id=r['id'],
|
||||
|
|
|
@ -11,7 +11,8 @@ from pathlib import Path
|
|||
from typing import Iterable
|
||||
|
||||
from .core import Res, get_files
|
||||
from .core.common import isoparse, Json
|
||||
from .core.common import Json
|
||||
from .core.compat import fromisoformat
|
||||
|
||||
import tcxparser # type: ignore[import-untyped]
|
||||
|
||||
|
@ -44,7 +45,7 @@ def _parse(f: Path) -> Workout:
|
|||
|
||||
return {
|
||||
'id' : f.name, # not sure?
|
||||
'start_time' : isoparse(tcx.started_at),
|
||||
'start_time' : fromisoformat(tcx.started_at),
|
||||
'duration' : timedelta(seconds=tcx.duration),
|
||||
'sport' : sport,
|
||||
'heart_rate_avg': tcx.hr_avg,
|
||||
|
@ -58,7 +59,7 @@ def _parse(f: Path) -> Workout:
|
|||
# tcx.hr_values(),
|
||||
# # todo cadence?
|
||||
# ):
|
||||
# t = isoparse(ts)
|
||||
# t = fromisoformat(ts)
|
||||
|
||||
|
||||
def workouts() -> Iterable[Res[Workout]]:
|
||||
|
|
|
@ -16,7 +16,8 @@ config = make_config(stackexchange)
|
|||
|
||||
# TODO just merge all of them and then filter?.. not sure
|
||||
|
||||
from ..core.common import Json, isoparse
|
||||
from ..core.common import Json
|
||||
from ..core.compat import fromisoformat
|
||||
from typing import NamedTuple, Iterable
|
||||
from datetime import datetime
|
||||
class Vote(NamedTuple):
|
||||
|
@ -25,7 +26,7 @@ class Vote(NamedTuple):
|
|||
|
||||
@property
|
||||
def when(self) -> datetime:
|
||||
return isoparse(self.j['eventTime'])
|
||||
return fromisoformat(self.j['eventTime'])
|
||||
|
||||
# todo Url return type?
|
||||
@property
|
||||
|
|
Loading…
Add table
Reference in a new issue