general: migrate modules to use 3.9 features

This commit is contained in:
Dima Gerasimov 2024-10-19 22:10:40 +01:00 committed by karlicoss
parent d3f9a8e8b6
commit 8496d131e7
125 changed files with 889 additions and 739 deletions

View file

@ -2,14 +2,13 @@
Merges location data from multiple sources
"""
from typing import Iterator
from collections.abc import Iterator
from my.core import Stats, LazyLogger
from my.core import LazyLogger, Stats
from my.core.source import import_source
from .common import Location
logger = LazyLogger(__name__, level="warning")

View file

@ -1,12 +1,13 @@
from datetime import date, datetime
from typing import Union, Tuple, Optional, Iterable, TextIO, Iterator, Protocol
from dataclasses import dataclass
from my.core import __NOT_HPI_MODULE__ # isort: skip
from my.core import __NOT_HPI_MODULE__
from collections.abc import Iterable, Iterator
from dataclasses import dataclass
from datetime import date, datetime
from typing import Optional, Protocol, TextIO, Union
DateIsh = Union[datetime, date, str]
LatLon = Tuple[float, float]
LatLon = tuple[float, float]
class LocationProtocol(Protocol):

View file

@ -1,14 +1,16 @@
# TODO: add config here which passes kwargs to estimate_from (under_accuracy)
# overwritable by passing the kwarg name here to the top-level estimate_location
from typing import Iterator, Optional
from __future__ import annotations
from collections.abc import Iterator
from my.core.source import import_source
from my.location.fallback.common import (
estimate_from,
FallbackLocation,
DateExact,
FallbackLocation,
LocationEstimator,
estimate_from,
)
@ -24,7 +26,7 @@ def fallback_estimators() -> Iterator[LocationEstimator]:
yield _home_estimate
def estimate_location(dt: DateExact, *, first_match: bool=False, under_accuracy: Optional[int] = None) -> FallbackLocation:
def estimate_location(dt: DateExact, *, first_match: bool=False, under_accuracy: int | None = None) -> FallbackLocation:
loc = estimate_from(dt, estimators=list(fallback_estimators()), first_match=first_match, under_accuracy=under_accuracy)
# should never happen if the user has home configured
if loc is None:

View file

@ -1,9 +1,12 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Optional, Callable, Sequence, Iterator, List, Union
from datetime import datetime, timedelta, timezone
from ..common import LocationProtocol, Location
from collections.abc import Iterator, Sequence
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from typing import Callable, Union
from ..common import Location, LocationProtocol
DateExact = Union[datetime, float, int] # float/int as epoch timestamps
Second = float
@ -13,10 +16,10 @@ class FallbackLocation(LocationProtocol):
lat: float
lon: float
dt: datetime
duration: Optional[Second] = None
accuracy: Optional[float] = None
elevation: Optional[float] = None
datasource: Optional[str] = None # which module provided this, useful for debugging
duration: Second | None = None
accuracy: float | None = None
elevation: float | None = None
datasource: str | None = None # which module provided this, useful for debugging
def to_location(self, *, end: bool = False) -> Location:
'''
@ -43,9 +46,9 @@ class FallbackLocation(LocationProtocol):
lon: float,
dt: datetime,
end_dt: datetime,
accuracy: Optional[float] = None,
elevation: Optional[float] = None,
datasource: Optional[str] = None,
accuracy: float | None = None,
elevation: float | None = None,
datasource: str | None = None,
) -> FallbackLocation:
'''
Create FallbackLocation from a start date and an end date
@ -93,13 +96,13 @@ def estimate_from(
estimators: LocationEstimators,
*,
first_match: bool = False,
under_accuracy: Optional[int] = None,
) -> Optional[FallbackLocation]:
under_accuracy: int | None = None,
) -> FallbackLocation | None:
'''
first_match: if True, return the first location found
under_accuracy: if set, only return locations with accuracy under this value
'''
found: List[FallbackLocation] = []
found: list[FallbackLocation] = []
for loc in _iter_estimate_from(dt, estimators):
if under_accuracy is not None and loc.accuracy is not None and loc.accuracy > under_accuracy:
continue

View file

@ -2,25 +2,22 @@
Simple location provider, serving as a fallback when more detailed data isn't available
'''
from __future__ import annotations
from collections.abc import Iterator, Sequence
from dataclasses import dataclass
from datetime import datetime, time, timezone
from functools import lru_cache
from typing import Sequence, Tuple, Union, cast, List, Iterator
from functools import cache
from typing import cast
from my.config import location as user_config
from my.location.common import DateIsh, LatLon
from my.location.fallback.common import DateExact, FallbackLocation
from my.location.common import LatLon, DateIsh
from my.location.fallback.common import FallbackLocation, DateExact
@dataclass
class Config(user_config):
home: Union[
LatLon, # either single, 'current' location
Sequence[Tuple[ # or, a sequence of location history
DateIsh, # date when you moved to
LatLon, # the location
]]
]
home: LatLon | Sequence[tuple[DateIsh, LatLon]]
# default ~30km accuracy
# this is called 'home_accuracy' since it lives on the base location.config object,
@ -29,13 +26,13 @@ class Config(user_config):
# TODO could make current Optional and somehow determine from system settings?
@property
def _history(self) -> Sequence[Tuple[datetime, LatLon]]:
def _history(self) -> Sequence[tuple[datetime, LatLon]]:
home1 = self.home
# todo ugh, can't test for isnstance LatLon, it's a tuple itself
home2: Sequence[Tuple[DateIsh, LatLon]]
home2: Sequence[tuple[DateIsh, LatLon]]
if isinstance(home1[0], tuple):
# already a sequence
home2 = cast(Sequence[Tuple[DateIsh, LatLon]], home1)
home2 = cast(Sequence[tuple[DateIsh, LatLon]], home1)
else:
# must be a pair of coordinates. also doesn't really matter which date to pick?
loc = cast(LatLon, home1)
@ -60,10 +57,11 @@ class Config(user_config):
from ...core.cfg import make_config
config = make_config(Config)
@lru_cache(maxsize=None)
@cache
def get_location(dt: datetime) -> LatLon:
'''
Interpolates the location at dt
@ -74,8 +72,8 @@ def get_location(dt: datetime) -> LatLon:
# TODO: in python3.8, use functools.cached_property instead?
@lru_cache(maxsize=None)
def homes_cached() -> List[Tuple[datetime, LatLon]]:
@cache
def homes_cached() -> list[tuple[datetime, LatLon]]:
return list(config._history)

View file

@ -7,8 +7,8 @@ REQUIRES = ["git+https://github.com/seanbreckenridge/ipgeocache"]
from dataclasses import dataclass
from datetime import timedelta
from my.core import Stats, make_config
from my.config import location
from my.core import Stats, make_config
from my.core.warnings import medium
@ -24,13 +24,13 @@ class ip_config(location.via_ip):
config = make_config(ip_config)
from collections.abc import Iterator
from functools import lru_cache
from typing import Iterator, List
from my.core import make_logger
from my.core.compat import bisect_left
from my.location.common import Location
from my.location.fallback.common import FallbackLocation, DateExact, _datetime_timestamp
from my.location.fallback.common import DateExact, FallbackLocation, _datetime_timestamp
logger = make_logger(__name__, level="warning")
@ -60,7 +60,7 @@ def locations() -> Iterator[Location]:
@lru_cache(1)
def _sorted_fallback_locations() -> List[FallbackLocation]:
def _sorted_fallback_locations() -> list[FallbackLocation]:
fl = list(filter(lambda l: l.duration is not None, fallback_locations()))
logger.debug(f"Fallback locations: {len(fl)}, sorting...:")
fl.sort(key=lambda l: l.dt.timestamp())

View file

@ -3,28 +3,27 @@ Location data from Google Takeout
DEPRECATED: setup my.google.takeout.parser and use my.location.google_takeout instead
"""
from __future__ import annotations
REQUIRES = [
'geopy', # checking that coordinates are valid
'ijson',
]
import re
from collections.abc import Iterable, Sequence
from datetime import datetime, timezone
from itertools import islice
from pathlib import Path
from subprocess import Popen, PIPE
from typing import Iterable, NamedTuple, Optional, Sequence, IO, Tuple
import re
from subprocess import PIPE, Popen
from typing import IO, NamedTuple, Optional
# pip3 install geopy
import geopy # type: ignore
import geopy # type: ignore
from my.core import stat, Stats, make_logger
from my.core import Stats, make_logger, stat, warnings
from my.core.cachew import cache_dir, mcachew
from my.core import warnings
warnings.high("Please set up my.google.takeout.parser module for better takeout support")
@ -43,7 +42,7 @@ class Location(NamedTuple):
alt: Optional[float]
TsLatLon = Tuple[int, int, int]
TsLatLon = tuple[int, int, int]
def _iter_via_ijson(fo) -> Iterable[TsLatLon]:
@ -51,10 +50,10 @@ def _iter_via_ijson(fo) -> Iterable[TsLatLon]:
# todo extract to common?
try:
# pip3 install ijson cffi
import ijson.backends.yajl2_cffi as ijson # type: ignore
import ijson.backends.yajl2_cffi as ijson # type: ignore
except:
warnings.medium("Falling back to default ijson because 'cffi' backend isn't found. It's up to 2x faster, you might want to check it out")
import ijson # type: ignore
import ijson # type: ignore
for d in ijson.items(fo, 'locations.item'):
yield (

View file

@ -4,13 +4,14 @@ Extracts locations using google_takeout_parser -- no shared code with the deprec
REQUIRES = ["git+https://github.com/seanbreckenridge/google_takeout_parser"]
from typing import Iterator
from collections.abc import Iterator
from my.google.takeout.parser import events, _cachew_depends_on
from google_takeout_parser.models import Location as GoogleLocation
from my.core import stat, Stats, LazyLogger
from my.core import LazyLogger, Stats, stat
from my.core.cachew import mcachew
from my.google.takeout.parser import _cachew_depends_on, events
from .common import Location
logger = LazyLogger(__name__)

View file

@ -7,21 +7,24 @@ Extracts semantic location history using google_takeout_parser
REQUIRES = ["git+https://github.com/seanbreckenridge/google_takeout_parser"]
from collections.abc import Iterator
from dataclasses import dataclass
from typing import Iterator, List
from my.google.takeout.parser import events, _cachew_depends_on as _parser_cachew_depends_on
from google_takeout_parser.models import PlaceVisit as SemanticLocation
from my.core import make_config, stat, LazyLogger, Stats
from my.core import LazyLogger, Stats, make_config, stat
from my.core.cachew import mcachew
from my.core.error import Res
from my.google.takeout.parser import _cachew_depends_on as _parser_cachew_depends_on
from my.google.takeout.parser import events
from .common import Location
logger = LazyLogger(__name__)
from my.config import location as user_config
@dataclass
class semantic_locations_config(user_config.google_takeout_semantic):
# a value between 0 and 100, 100 being the most confident
@ -36,7 +39,7 @@ config = make_config(semantic_locations_config)
# add config to cachew dependency so it recomputes on config changes
def _cachew_depends_on() -> List[str]:
def _cachew_depends_on() -> list[str]:
dep = _parser_cachew_depends_on()
dep.insert(0, f"require_confidence={config.require_confidence} accuracy={config.accuracy}")
return dep

View file

@ -20,20 +20,20 @@ class config(location.gpslogger):
accuracy: float = 50.0
from itertools import chain
from collections.abc import Iterator, Sequence
from datetime import datetime, timezone
from itertools import chain
from pathlib import Path
from typing import Iterator, Sequence, List
import gpxpy
from gpxpy.gpx import GPXXMLSyntaxException
from more_itertools import unique_everseen
from my.core import Stats, LazyLogger
from my.core import LazyLogger, Stats
from my.core.cachew import mcachew
from my.core.common import get_files
from .common import Location
from .common import Location
logger = LazyLogger(__name__, level="warning")
@ -49,7 +49,7 @@ def inputs() -> Sequence[Path]:
return sorted(get_files(config.export_path, glob="*.gpx", sort=False), key=_input_sort_key)
def _cachew_depends_on() -> List[float]:
def _cachew_depends_on() -> list[float]:
return [p.stat().st_mtime for p in inputs()]

View file

@ -1,7 +1,7 @@
from .fallback.via_home import *
from my.core.warnings import high
from .fallback.via_home import *
high(
"my.location.home is deprecated, use my.location.fallback.via_home instead, or estimate locations using the higher-level my.location.fallback.all.estimate_location"
)

View file

@ -1,7 +1,7 @@
REQUIRES = ["git+https://github.com/seanbreckenridge/ipgeocache"]
from .fallback.via_ip import *
from my.core.warnings import high
from .fallback.via_ip import *
high("my.location.via_ip is deprecated, use my.location.fallback.via_ip instead")