From 451fde8a918b0710d7066c5957202062c6d7a04d Mon Sep 17 00:00:00 2001 From: GeoffreyCoulaud Date: Mon, 1 May 2023 22:46:18 +0200 Subject: [PATCH] Locations with decorators --- src/importer/decorators.py | 41 ++++++++++++++++++++ src/importer/location.py | 35 ----------------- src/importer/source.py | 11 ------ src/importer/sources/lutris_source.py | 55 +++++++++++++-------------- 4 files changed, 67 insertions(+), 75 deletions(-) create mode 100644 src/importer/decorators.py delete mode 100644 src/importer/location.py diff --git a/src/importer/decorators.py b/src/importer/decorators.py new file mode 100644 index 0000000..5c36753 --- /dev/null +++ b/src/importer/decorators.py @@ -0,0 +1,41 @@ +""" +A decorator takes a callable A and returns a callable B that will override the name of A. +A decorator with arguments returns a closure decorator having access to the arguments. + +Example usage for the location decorators: + +class MyClass(): + @cached_property + @replaced_by_schema_key(key="source-location") + @replaced_by_path(override="/somewhere/that/doesnt/exist") + @replaced_by_path(override="/somewhere/that/exists") + def location(self): + return None +""" + +from pathlib import Path +from os import PathLike +from functools import wraps + +def replaced_by_path(path: PathLike): # Decorator builder + """Replace the method's returned path with the override if the override exists on disk""" + def decorator(original_function): # Built decorator (closure) + @wraps(original_function) + def wrapper(*args, **kwargs): # func's override + p = Path(path).expanduser() + if p.exists(): return p + else: return original_function(*args, **kwargs) + return wrapper + return decorator + +def replaced_by_schema_key(key: str): # Decorator builder + """Replace the method's returned path with the path pointed by the key if it exists on disk""" + def decorator(original_function): # Built decorator (closure) + @wraps(original_function) + def wrapper(*args, **kwargs): # func's override + schema = args[0].win.schema + try: override = schema.get_string(key) + except Exception: return original_function(*args, **kwargs) + else: return replaced_by_path(override)(*args, **kwargs) + return wrapper + return decorator \ No newline at end of file diff --git a/src/importer/location.py b/src/importer/location.py deleted file mode 100644 index ee664fe..0000000 --- a/src/importer/location.py +++ /dev/null @@ -1,35 +0,0 @@ -from dataclasses import dataclass -from functools import cached_property -from pathlib import Path -from os import PathLike - - -@dataclass -class Location(): - """Abstraction for a location that has multiple candidate paths""" - - candidates: list[PathLike] = None - - def __init__(self, *candidates): - self.candidates = list() - self.candidates.extend(candidates) - return self - - def add(self, canddiate): - """Add a candidate (last evaluated)""" - self.candidates.append(canddiate) - return self - - def add_override(self, candidate): - """Add a canddiate (first evaluated)""" - self.candidates.insert(0, candidate) - return self - - @cached_property - def path(self): - """Chosen path depending on availability on the disk.""" - for candidate in self.candidates: - p = Path(candidate).expanduser() - if p.exists: - return p - return None \ No newline at end of file diff --git a/src/importer/source.py b/src/importer/source.py index d9685c1..957fc45 100644 --- a/src/importer/source.py +++ b/src/importer/source.py @@ -27,7 +27,6 @@ class Source(Iterable): """Source of games. Can be a program location on disk with a config file that points to game for example""" win = None - schema_keys: dict name: str variant: str @@ -36,11 +35,6 @@ class Source(Iterable): def __init__(self, win) -> None: super().__init__() self.win = win - self.__init_schema_keys__() - - def __init_schema_keys__(self): - """Initialize schema keys needed by the source if necessary""" - raise NotImplementedError() @property def full_name(self): @@ -61,9 +55,4 @@ class Source(Iterable): def __iter__(self): """Get the source's iterator, to use in for loops""" - raise NotImplementedError() - - def __init_locations__(self): - """Initialize locations needed by the source. - Extended and called by **final** children.""" raise NotImplementedError() \ No newline at end of file diff --git a/src/importer/sources/lutris_source.py b/src/importer/sources/lutris_source.py index 28e1f2a..a167f10 100644 --- a/src/importer/sources/lutris_source.py +++ b/src/importer/sources/lutris_source.py @@ -1,10 +1,9 @@ -from pathlib import Path from functools import cached_property from sqlite3 import connect from cartridges.game2 import Game from cartridges.importer.source import Source, SourceIterator -from cartridges.importer.location import Location +from cartridges.importer.decorators import replaced_by_schema_key, replaced_by_path class LutrisSourceIterator(SourceIterator): @@ -73,50 +72,48 @@ class LutrisSource(Source): name = "Lutris" executable_format = "xdg-open lutris:rungameid/{game_id}" - schema_keys = { - "location": None, - "cache_location": None - } + + location = None + cache_location = None def __init__(self, win) -> None: super().__init__(win) - self.location = Location() - self.cache_location = Location() def __iter__(self): return LutrisSourceIterator(source=self) - def __init_locations__(self): - super().__init_locations__() - self.location.add_override(self.win.schema.get_string(self.schema_keys["location"])) - self.cache_location.add_override(self.win.schema.get_string(self.schema_keys["cache_location"])) - class LutrisNativeSource(LutrisSource): """Class representing an installation of Lutris using native packaging""" variant = "native" - schema_keys = { - "location": "lutris-location", - "cache_location": "lutris-cache-location" - } - def __init_locations__(self): - super().__init_locations__() - self.location.add("~/.local/share/lutris/") - self.cache_location.add("~/.local/share/lutris/covers") + @cached_property + @replaced_by_schema_key("lutris-location") + @replaced_by_path("~/.local/share/lutris/") + def location(self): + raise FileNotFoundError() + + @cached_property + @replaced_by_schema_key("lutris-cache-location") + @replaced_by_path("~/.local/share/lutris/covers") + def cache_location(self): + raise FileNotFoundError() class LutrisFlatpakSource(LutrisSource): """Class representing an installation of Lutris using flatpak""" variant = "flatpak" - schema_keys = { - "location": "lutris-flatpak-location", - "cache_location": "lutris-flatpak-cache-location" - } - def __init_locations__(self): - super().__init_locations__() - self.location.add("~/.var/app/net.lutris.Lutris/data/lutris") - self.cache_location.add("~/.var/app/net.lutris.Lutris/data/lutris/covers") \ No newline at end of file + @cached_property + @replaced_by_schema_key("lutris-flatpak-location") + @replaced_by_path("~/.var/app/net.lutris.Lutris/data/lutris") + def location(self): + raise FileNotFoundError() + + @cached_property + @replaced_by_schema_key("lutris-flatpak-cache-location") + @replaced_by_path("~/.var/app/net.lutris.Lutris/data/lutris/covers") + def cache_location(self): + raise FileNotFoundError() \ No newline at end of file