def _migration(attrs: Attrs) -> Attrs: roots = 'roots' if roots in attrs: # legacy name attrs['paths'] = attrs[roots] from my.core.warnings import high high(f'"{roots}" is deprecated! Use "paths" instead.') return attrs
def migration(attrs: Attrs) -> Attrs: export_dir = 'export_dir' if export_dir in attrs: # legacy name attrs['export_path'] = attrs[export_dir] from my.core.warnings import high high(f'"{export_dir}" is deprecated! Please use "export_path" instead."') return attrs
def _fd_path() -> str: # todo move it to core fd_path: Optional[str] = shutil.which("fdfind") or shutil.which("fd-find") or shutil.which("fd") if fd_path is None: high(f"my.coding.commits requires 'fd' to be installed, See https://github.com/sharkdp/fd#installation") assert fd_path is not None return fd_path
def repo(name: str) -> str: """ e.g., converts to ~/Repos/name ~/Repos/ is where I store a lot of my git repositories """ if "REPOS" in environ: return path.join(environ["REPOS"], name) else: high( r"Hey I use the $REPOS environment variable to determine where repositories are on my computer. If you have some directory you put those -- set something like 'export REPOS=~/projects' in your shell config. Otherwise, you can edit this 'def repo' function, or remove it from whatevers using it -- its probably some of my personal config" ) return name
def _locations() -> Iterator[Tuple[LatLon, datetime]]: try: import my.location.all for loc in my.location.all.locations(): if loc.accuracy is not None and loc.accuracy > config.require_accuracy: continue yield ((loc.lat, loc.lon), loc.dt) except Exception as e: from my.core.warnings import high logger.exception( "Could not setup via_location using my.location.all provider, falling back to legacy google implemetation", exc_info=e) high( "Setup my.google.takeout.parser, then my.location.all for better google takeout/location data" ) import my.location.google for gloc in my.location.google.locations(): yield ((gloc.lat, gloc.lon), gloc.dt)
def migration(attrs: Attrs) -> Attrs: # new structure, take top-level config and extract 'rexport' class # previously, 'rexport' key could be location of the rexport repo on disk if 'rexport' in attrs and not isinstance(attrs['rexport'], (str, Path)): ex: uconfig.rexport = attrs['rexport'] attrs['export_path'] = ex.export_path else: from my.core.warnings import high high("""DEPRECATED! Please modify your reddit config to look like: class reddit: class rexport: export_path: Paths = '/path/to/rexport/data' """) export_dir = 'export_dir' if export_dir in attrs: # legacy name attrs['export_path'] = attrs[export_dir] high( f'"{export_dir}" is deprecated! Please use "export_path" instead."' ) return attrs
def _extract_locations(path: Path) -> Iterator[Res[Location]]: try: import gpxpy # type: ignore[import] with path.open("r") as gf: gpx_obj = gpxpy.parse(gf) for track in gpx_obj.tracks: for segment in track.segments: for point in segment.points: # TODO: use elevation? yield Location( lat=point.latitude, lng=point.longitude, dt=datetime.replace(point.time, tzinfo=timezone.utc), ) except ImportError: high( "Should install 'gpxpy' to parse .gpx files, falling back to basic XML parsing" ) yield from _extract_xml_locations(path)
def commited_dt(self) -> datetime: high( "DEPRECATED! Please replace 'commited_dt' with 'committed_dt' (two 't's instead of one)" ) return self.committed_dt
def config() -> commits_cfg: res = make_config(commits_cfg) if res.emails is None and res.names is None: # todo error policy? throw/warn/ignore high("Set either 'emails' or 'names', otherwise you'll get no commits") return res
from typing import Iterator from my.config import fbmessenger as user_config import fbmessengerexport.dal as messenger ### # support old style config _new_section = getattr(user_config, 'fbmessengerexport', None) _old_attr = getattr(user_config, 'export_db', None) if _new_section is None and _old_attr is not None: from my.core.warnings import high high("""DEPRECATED! Please modify your fbmessenger config to look like: class fbmessenger: class fbmessengerexport: export_db: PathIsh = '/path/to/fbmessengerexport/database' """) class fbmessengerexport: export_db = _old_attr setattr(user_config, 'fbmessengerexport', fbmessengerexport) ### from ..core import PathIsh @dataclass class config(user_config.fbmessengerexport): export_db: PathIsh
# seems that when a submodule is imported, at some point it'll call some internal import machinery # with 'parent' set to the parent module # if parent module is imported first (i.e. in case of deprecated usage), it won't be the case args = inspect.getargvalues(f.frame) if args.locals.get('parent') == f'my.{mname}': imported_as_parent = True # this we can only detect from the code I guess line = '\n'.join(f.code_context or []) if re.match(rf'from\s+my\.{mname}\s+import\s+export', line): # todo 'export' is hardcoded, not sure how to infer allowed objects anutomatically.. importing_submodule = True legacy = not (imported_as_parent or importing_submodule) if legacy: from my.core import warnings as W # TODO: add link to instructions to migrate W.high( "DEPRECATED! Instead of my.fbmessengerexport, import from my.fbmessengerexport.export" ) # only import in legacy mode # otherswise might have unfortunate side effects (e.g. missing imports) from .export import * # kinda annoying to keep it, but it's so legacy 'hpi module install my.fbmessenger' work # needs to be on the top level (since it's extracted via ast module), but hopefully it doesn't hurt here REQUIRES = [ 'git+https://github.com/karlicoss/fbmessengerexport', ]
REQUIRES = [ 'git+https://github.com/karlicoss/rexport', ] import re import traceback # some hacky traceback to inspect the current stack # to see if the user is using the old style of importing warn = False for f in traceback.extract_stack(): line = f.line or '' # just in case it's None, who knows.. # cover the most common ways of previously interacting with the module if 'import my.reddit ' in (line + ' '): warn = True elif 'from my import reddit' in line: warn = True elif re.match( r"from my\.reddit\simport\s(comments|saved|submissions|upvoted)", line): warn = True # TODO: add link to instructions to migrate if warn: from my.core import warnings as W W.high( "DEPRECATED! Instead of my.reddit, import from my.reddit.all instead.") from .rexport import *
from itertools import islice from pathlib import Path from subprocess import Popen, PIPE from typing import Iterable, NamedTuple, Optional, Sequence, IO, Tuple import re # pip3 install geopy import geopy # type: ignore from ..core.common import LazyLogger, mcachew from ..core.cachew import cache_dir from ..core import kompress from my.core.warnings import high high( "Please set up my.google.takeout.parser module for better takeout support") # otherwise uses ijson # todo move to config?? USE_GREP = False logger = LazyLogger(__name__) class Location(NamedTuple): dt: datetime lat: float lon: float alt: Optional[float]