139 lines
4.2 KiB
Python
139 lines
4.2 KiB
Python
'''
|
|
Contains backwards compatibility helpers for different python versions.
|
|
If something is relevant to HPI itself, please put it in .hpi_compat instead
|
|
'''
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from typing import TYPE_CHECKING
|
|
|
|
if sys.version_info[:2] >= (3, 13):
|
|
from warnings import deprecated
|
|
else:
|
|
from typing_extensions import deprecated
|
|
|
|
|
|
# keeping just for backwards compatibility, used to have compat implementation for 3.6
|
|
if not TYPE_CHECKING:
|
|
import sqlite3
|
|
|
|
@deprecated('use .backup method on sqlite3.Connection directly instead')
|
|
def sqlite_backup(*, source: sqlite3.Connection, dest: sqlite3.Connection, **kwargs) -> None:
|
|
# TODO warn here?
|
|
source.backup(dest, **kwargs)
|
|
|
|
# keeping for runtime backwards compatibility (added in 3.9)
|
|
@deprecated('use .removeprefix method on string directly instead')
|
|
def removeprefix(text: str, prefix: str) -> str:
|
|
return text.removeprefix(prefix)
|
|
|
|
@deprecated('use .removesuffix method on string directly instead')
|
|
def removesuffix(text: str, suffix: str) -> str:
|
|
return text.removesuffix(suffix)
|
|
|
|
##
|
|
|
|
## used to have compat function before 3.8 for these, keeping for runtime back compatibility
|
|
from functools import cached_property
|
|
from typing import Literal, Protocol, TypedDict
|
|
##
|
|
|
|
|
|
if sys.version_info[:2] >= (3, 10):
|
|
from typing import ParamSpec
|
|
else:
|
|
from typing_extensions import ParamSpec
|
|
|
|
|
|
# bisect_left doesn't have a 'key' parameter (which we use)
|
|
# till python3.10
|
|
if sys.version_info[:2] <= (3, 9):
|
|
from typing import Any, Callable, List, Optional, TypeVar # noqa: UP035
|
|
|
|
X = TypeVar('X')
|
|
|
|
# copied from python src
|
|
# fmt: off
|
|
def bisect_left(a: list[Any], x: Any, lo: int=0, hi: int | None=None, *, key: Callable[..., Any] | None=None) -> int:
|
|
if lo < 0:
|
|
raise ValueError('lo must be non-negative')
|
|
if hi is None:
|
|
hi = len(a)
|
|
# Note, the comparison uses "<" to match the
|
|
# __lt__() logic in list.sort() and in heapq.
|
|
if key is None:
|
|
while lo < hi:
|
|
mid = (lo + hi) // 2
|
|
if a[mid] < x:
|
|
lo = mid + 1
|
|
else:
|
|
hi = mid
|
|
else:
|
|
while lo < hi:
|
|
mid = (lo + hi) // 2
|
|
if key(a[mid]) < x:
|
|
lo = mid + 1
|
|
else:
|
|
hi = mid
|
|
return lo
|
|
# fmt: on
|
|
|
|
else:
|
|
from bisect import bisect_left
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
if sys.version_info[:2] >= (3, 11):
|
|
fromisoformat = datetime.fromisoformat
|
|
else:
|
|
# fromisoformat didn't support Z as "utc" before 3.11
|
|
# https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat
|
|
|
|
def fromisoformat(date_string: str) -> datetime:
|
|
if date_string.endswith('Z'):
|
|
date_string = date_string[:-1] + '+00:00'
|
|
return datetime.fromisoformat(date_string)
|
|
|
|
|
|
def test_fromisoformat() -> None:
|
|
from datetime import timezone
|
|
|
|
# fmt: off
|
|
# 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,
|
|
)
|
|
# fmt: on
|
|
|
|
# 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,
|
|
# )
|
|
|
|
|
|
if sys.version_info[:2] >= (3, 10):
|
|
from types import NoneType
|
|
from typing import TypeAlias
|
|
else:
|
|
NoneType = type(None)
|
|
from typing_extensions import TypeAlias
|
|
|
|
|
|
if sys.version_info[:2] >= (3, 11):
|
|
from typing import Never, assert_never, assert_type
|
|
else:
|
|
from typing_extensions import Never, assert_never, assert_type
|