general: migrate modules to use 3.9 features
This commit is contained in:
parent
d3f9a8e8b6
commit
8496d131e7
125 changed files with 889 additions and 739 deletions
|
@ -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")
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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__)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()]
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue