Module geode.api
Expand source code
# ------------------------------------------------------------------------------
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
# ------------------------------------------------------------------------------
# API
from geode.db import Database
from geode.conf import Configuration
from geode.utils import get_data
from geode.lib.game import Game
from geode.lib.console import Console
from geode.lib.emulator import Emulator
# Filesystem
from os import W_OK
from os import getpid
from os import getlogin
from os.path import exists
from os.path import expanduser
from pathlib import Path
# Logging
import logging
from logging.config import fileConfig
# Regex
from re import IGNORECASE
from re import sub as re_sub
from re import compile as re_compile
# Structure
from enum import Enum
# System
import tempfile
try:
from xdg.BaseDirectory import xdg_data_home
from xdg.BaseDirectory import xdg_config_home
except ImportError as error:
from os import environ
if "XDG_DATA_HOME" in environ:
xdg_data_home = environ["XDG_DATA_HOME"]
else:
xdg_data_home = expanduser("~/.local/share")
if "XDG_CONFIG_HOME" in environ:
xdg_config_home = environ["XDG_CONFIG_HOME"]
else:
xdg_config_home = expanduser("~/.config")
# ------------------------------------------------------------------------------
# Class
# ------------------------------------------------------------------------------
class API(object):
class Metadata(Enum):
""" Represent metadata
"""
NAME = "geode"
VERSION = "1.0"
def __init__(self, **kwargs):
""" Constructor
Raises
------
TypeError
when filepath parameter is not a string
when instance parameter is not a string
when configuration parameter is not a string
when storage parameter is not a string
OSError
when filepath parameter file not exists
ValueError
when instance name is missing
Notes
-----
Two methods are available to initialize Geode API. You can use a
configuration file which contains default values as:
[metadata]
instance = geode
[path]
configuration = ~/.config
storage = ~/.local/share
And use the filepath parameter as:
>>> API(filepath="geode.conf")
Or you can use the manual parameters instance, configuration and storage
as:
>>> API(instance="geode",
configuration="~/.config", storage="~/.local/share")
The two parameters configuration and storage are optional. They use the
default values write in the example above if missing.
"""
self.__lock = None
self.__pid = getpid()
self.__instance_name = None
self.__storage_path = None
self.__configuration_path = None
self.__storage = {
"consoles": list(),
"emulators": list()
}
self.__paths = {
"configuration": None,
"consoles": None,
"emulators": None,
"home": Path.home(),
"storage": None
}
# ----------------------------------------
# Check misc parameters
# ----------------------------------------
self.__debug = False
if "debug" in kwargs:
self.__debug = kwargs["debug"]
# ----------------------------------------
# Filepath parameters
# ----------------------------------------
# Use a configuration file which contains default values
if "filepath" in kwargs:
if type(kwargs["filepath"]) is not str:
raise TypeError("Wrong type for path, expected str")
filepath = Path(kwargs["filepath"]).expanduser()
if not filepath.exists():
raise OSError(2, "Cannot found path file: %s" % filepath)
# Generate configuration instance
self.__configuration = Configuration(str(filepath))
self.__instance_name = self.__configuration.get(
"metadata", "instance", fallback=None)
self.__configuration_path = Path(self.__configuration.get(
"path", "configuration", fallback=xdg_config_home))
self.__storage_path = Path(self.__configuration.get(
"path", "storage", fallback=xdg_data_home))
# ----------------------------------------
# Manual parameters
# ----------------------------------------
# Retrieve instance name
if "instance" in kwargs:
if type(kwargs["instance"]) is not str:
raise TypeError("Wrong type for instance, expected str")
self.__instance_name = kwargs["instance"]
# Retrieve configuration folder path
if "configuration" in kwargs:
if type(kwargs["configuration"]) is not str:
raise TypeError("Wrong type for configuration, expected str")
self.__configuration_path = Path(kwargs["configuration"])
# Retrieve storage folder path
if "storage" in kwargs:
if type(kwargs["storage"]) is not str:
raise TypeError("Wrong type for storage, expected str")
self.__storage_path = Path(kwargs["storage"])
# ----------------------------------------
# Check instance name
# ----------------------------------------
if self.__instance_name is None:
raise ValueError("Missing instance name")
if self.__configuration_path is None:
self.__configuration_path = Path(xdg_config_home)
if self.__storage_path is None:
self.__storage_path = Path(xdg_data_home)
# ----------------------------------------
# Initialize API
# ----------------------------------------
# Check and generate default folders
self.__init_folders()
# Start logger module
self.__init_logger()
# Check lock file
self.__init_lock()
def __init_folders(self):
""" Initialize default folders using to store data
"""
# Store config folder
self.__paths["configuration"] = \
self.__configuration_path.expanduser().joinpath(
self.__instance_name)
# Store local folder
self.__paths["storage"] = \
self.__storage_path.expanduser().joinpath(
self.__instance_name)
# API main folder
for key, path in self.__paths.items():
if path is not None and not path.exists():
self.__logger.info("Generate %s folder" % path)
# Create subfolder for current instance
path.mkdir(0o755, True)
# API specific configuration folders
for key in ("consoles", "emulators"):
self.__paths[key] = self.get_configuration_path().joinpath(key)
if not self.__paths[key].exists():
self.__logger.info("Generate %s folder" % self.__paths[key])
# Create subfolder for current instance
self.__paths[key].mkdir(0o755, True)
def __init_logger(self):
""" Initialize logger module
"""
# Set logging file path
logging.log_path = self.get_storage_path().joinpath(
"%s.log" % self.__instance_name)
# Save previous logging file
if logging.log_path.exists():
logging.log_path.rename("%s.old" % str(logging.log_path))
# Generate logger from configuration file
fileConfig(Path(get_data("config", "logger.conf")))
self.__logger = logging.getLogger("geode")
if not self.__debug:
self.__logger.setLevel(logging.INFO)
self.__logger.info("Initialize %s instance with API v.%s" % (
self.__instance_name, API.Metadata.VERSION.value))
def __init_lock(self):
""" Initialize lock file to avoid multiple instance
Raises
------
RuntimeError
when an instance already exists
"""
pattern = "%s.*:%s" % (self.__instance_name, getlogin())
# Check if a temporary lock file already exists
if len(list(Path(tempfile.gettempdir()).glob(pattern))) > 0:
raise RuntimeError("An instance already exists")
# Generate a new lock file
self.__lock = tempfile.NamedTemporaryFile(
suffix=":%s" % getlogin(), prefix="%s." % self.__instance_name)
self.__logger.info("Register with PID %d" % self.__pid)
def __init_objects(self, key, element):
""" Initialize an object
Parameters
----------
key : str
Storage key (emulators or consoles)
element : geode.lib.console.Console or geode.lib.emulator.Emulator
Object type
Raises
------
OSError
when storage folder not exists
"""
# Reset storage content
self.__storage[key].clear()
path = self.__paths[key]
# Check if folder path still exists
if not path.exists():
raise OSError(2, "Cannot found %s folder: %s" % (key, path))
# Retrieve every configuration file from folder
for filepath in sorted(path.glob("*.conf")):
try:
self.__storage[key].append(element(self, filepath))
except TypeError as error:
self.__logger.error(
"Cannot instantiate %s: %s" % (key, str(error)))
def __add_object(self, key, element, **kwargs):
""" Add a new object in API storage
Parameters
----------
key : str
Storage key (emulators or consoles)
element : geode.lib.console.Console or geode.lib.emulator.Emulator
Object type
Raises
------
KeyError
when name parameter is missing
OSError
when folder path not exists
when folder path is not a directory
"""
if not "name" in kwargs:
raise KeyError("Missing name key in parameters")
folder = self.__paths[key]
if not folder.exists():
raise OSError(2, "Cannot found folder: %s" % str(folder))
if not folder.is_dir():
raise OSError(1, "Path is not a directory: %s" % str(folder))
# ----------------------------------------
# Generate a new identifier
# ----------------------------------------
# Retrieve only alphanumeric element from filename
identifier = re_sub(r"[^\w\d]+", ' ', kwargs["name"].lower())
# Remove useless spaces and replace the others with a dash
identifier = re_sub(r"[\s|_]+", '-', identifier.strip())
filepath = folder.joinpath("%s.conf" % identifier)
# Check if other files has the same identifier
matched = sorted(list(folder.glob("%s-[0-9].conf" % identifier)))
if len(matched) > 0 or filepath.exists():
# Retrieve the last used number
if len(matched) > 0:
number = int(matched[-1].stem.split('-')[-1]) + 1
# Start a new counter
else:
number = 2
filepath = folder.joinpath("%s-%d.conf" % (identifier, number))
# ----------------------------------------
# Generate configuration file
# ----------------------------------------
configuration = Configuration(str(filepath))
# Retrieve default value from wanted CommonObject
for section, option, value in element.attributes:
# Use value from parameters
if option in kwargs:
value = kwargs[option]
configuration.modify(section, option, value)
# Save configuration file
configuration.update()
# ----------------------------------------
# Store object
# ----------------------------------------
try:
self.__storage[key].append(element(self, filepath))
return self.__storage[key][-1]
except TypeError as error:
self.__logger.error(
"Cannot instantiate %s: %s" % (key, str(error)))
return None
@property
def log(self):
""" Return logger instance
Returns
-------
logging.Logger
Logger instance
"""
return self.__logger
@property
def pid(self):
""" Return application process identifier
Returns
-------
int
Process identifier
"""
return self.__pid
def init(self):
""" Initialize API structure and data
"""
# ----------------------------------------
# Database
# ----------------------------------------
self.__database = Database(
path=self.get_storage_path().joinpath("games.db"),
scheme=Path(get_data("config", "database.conf")))
self.__database.connect()
# Check if the database respect the specified scheme
if not self.__database.check_integrity():
# Generate a new database
if not self.__database.path.exists():
self.__database.generate_database()
self.__database.insert("metadata",
data={ "version": API.Metadata.VERSION.value })
# ----------------------------------------
# Games environment keys
# ----------------------------------------
self.__environment = Configuration(
str(self.get_configuration_path().joinpath("games.conf")))
# ----------------------------------------
# Objects
# ----------------------------------------
self.__init_objects("emulators", Emulator)
self.__init_objects("consoles", Console)
def get_database(self):
""" Return database instance
Returns
-------
geode.db.Database
Database instance
"""
return self.__database
def get_environment(self):
""" Return games environment configuration file
Returns
-------
geode.conf.Configuration
Environment configuration instance
"""
return self.__environment
def get_instance(self):
""" Retrieve API instance name
Returns
-------
str
Current API instance name
"""
return self.__instance_name
def get_paths(self):
""" Retrieve API paths
Returns
-------
list
List which contains key and path tuples
"""
return list(self.__paths.items())
def get_storage_path(self):
""" Retrieve storage folder path
Returns
-------
pathlib.Path
Storage folder
"""
return self.__paths["storage"]
def get_configuration_path(self):
""" Retrieve configuration folder path
Returns
-------
pathlib.Path
Configuration folder
"""
return self.__paths["configuration"]
def get_emulators(self):
""" Retrieve available emulators
Returns
-------
list
Emulator objects as list
"""
return self.__storage["emulators"]
def get_emulators_path(self):
""" Retrieve emulators folder path
Returns
-------
pathlib.Path
Emulators folder
"""
return self.__paths["emulators"]
def get_emulator(self, key):
""" Retrieve a specific emulator
Parameters
----------
key : str
Emulator identifier key
Returns
-------
geode.lib.emulator.Emulator or None
Emulator instance if found, None otherwise
"""
return next((emulator for emulator in self.__storage["emulators"] \
if emulator.id == key), None)
def add_emulator(self, **kwargs):
""" Append a new emulator
Returns
-------
geode.lib.emulator.Emulator or None
Emulator instance if success, None otherwise
"""
return self.__add_object("emulators", Emulator, **kwargs)
def delete_emulator(self, key):
""" Delete a specific emulator
Parameters
----------
key : str or geode.lib.emulator.Emulator
Emulator identifier key or instance
Returns
-------
bool
True if emulator has been successfully removed, False otherwise
"""
if type(key) is Emulator:
emulator = key
elif type(key) is str:
emulator = self.get_emulator(key)
if emulator is not None:
try:
# Remove configuration file
emulator.get_path().unlink()
# Remove instance from storage
self.get_emulators().remove(emulator)
# Remove instance from consoles
for console in self.get_consoles():
# Remove emulator entry from console instance
if console.emulator == emulator:
console.emulator = None
console.save()
# Remove instance from memory
del emulator
return True
except Exception as error:
return False
return False
def get_consoles(self):
""" Retrieve available consoles
Returns
-------
list
Console objects as list
"""
return self.__storage["consoles"]
def get_consoles_path(self):
""" Retrieve consoles folder path
Returns
-------
pathlib.Path
Consoles folder
"""
return self.__paths["consoles"]
def get_console(self, key):
""" Retrieve a specific console
Parameters
----------
key : str
Console identifier key
Returns
-------
geode.lib.console.Console or None
Console instance if found, None otherwise
"""
return next((console for console in self.__storage["consoles"] \
if console.id == key), None)
def add_console(self, **kwargs):
""" Append a new console
Returns
-------
geode.lib.console.Console or None
Console instance if success, None otherwise
"""
return self.__add_object("consoles", Console, **kwargs)
def delete_console(self, key):
""" Delete a specific console
Parameters
----------
key : str or geode.lib.console.Console
Console identifier key or instance
Returns
-------
bool
True if console has been successfully removed, False otherwise
"""
if type(key) is Console:
console = key
elif type(key) is str:
console = self.get_console(key)
if console is not None:
try:
# Remove configuration file
console.get_path().unlink()
# Remove instance from storage
self.get_consoles().remove(console)
# Remove instance from memory
del console
return True
except Exception as error:
return False
return False
def get_games(self, sort=False):
""" Retrieve every games available
Parameters
----------
sort : bool, optional
Sort list before return it (Default: False)
Returns
-------
list
Games list
"""
games = list()
for console in self.__storage["consoles"]:
games.extend(console.get_games())
if sort:
games.sort(key=lambda game: game.name.lower().replace(' ', ''))
return games
def get_game(self, key):
""" Retrieve a specific game
Parameters
----------
key : str
Game identifier key
sort : bool, optional
Sort list before return it (Default: False)
Returns
-------
geode.lib.game.Game or None
Game instance if found, None otherwise
"""
return next((game for game in self.get_games() if game.id == key), None)
def search_game(self, key, sort=False):
""" Search games from a specific key
Parameters
----------
key : str
Key to search in games list (based on identifier and name)
Returns
-------
generator
Game instances
"""
regex = re_compile(key, IGNORECASE)
return (game for game in self.get_games(sort) \
if regex.search(game.name) or regex.search(game.id))
def delete_game(self, key):
""" Delete a specific game from database
This function only remove the game from database. If you want to remove
the game file, you need to do it manually.
Parameters
----------
key : str or geode.lib.game.Game
Game identifier key or instance
"""
if type(key) is Game:
game = key
elif type(key) is str:
game = self.get_game(key)
if game is not None:
console = game.console
# Remove instance from storage
return console.delete_game(game)
return False
Classes
class API (**kwargs)
-
Constructor
Raises
TypeError
- when filepath parameter is not a string when instance parameter is not a string when configuration parameter is not a string when storage parameter is not a string
OSError
- when filepath parameter file not exists
ValueError
- when instance name is missing
Notes
Two methods are available to initialize Geode API. You can use a configuration file which contains default values as:
[metadata] instance = geode [path] configuration = ~/.config storage = ~/.local/share
And use the filepath parameter as:
>>> API(filepath="geode.conf")
Or you can use the manual parameters instance, configuration and storage as:
>>> API(instance="geode", configuration="~/.config", storage="~/.local/share")
The two parameters configuration and storage are optional. They use the default values write in the example above if missing.
Expand source code
class API(object): class Metadata(Enum): """ Represent metadata """ NAME = "geode" VERSION = "1.0" def __init__(self, **kwargs): """ Constructor Raises ------ TypeError when filepath parameter is not a string when instance parameter is not a string when configuration parameter is not a string when storage parameter is not a string OSError when filepath parameter file not exists ValueError when instance name is missing Notes ----- Two methods are available to initialize Geode API. You can use a configuration file which contains default values as: [metadata] instance = geode [path] configuration = ~/.config storage = ~/.local/share And use the filepath parameter as: >>> API(filepath="geode.conf") Or you can use the manual parameters instance, configuration and storage as: >>> API(instance="geode", configuration="~/.config", storage="~/.local/share") The two parameters configuration and storage are optional. They use the default values write in the example above if missing. """ self.__lock = None self.__pid = getpid() self.__instance_name = None self.__storage_path = None self.__configuration_path = None self.__storage = { "consoles": list(), "emulators": list() } self.__paths = { "configuration": None, "consoles": None, "emulators": None, "home": Path.home(), "storage": None } # ---------------------------------------- # Check misc parameters # ---------------------------------------- self.__debug = False if "debug" in kwargs: self.__debug = kwargs["debug"] # ---------------------------------------- # Filepath parameters # ---------------------------------------- # Use a configuration file which contains default values if "filepath" in kwargs: if type(kwargs["filepath"]) is not str: raise TypeError("Wrong type for path, expected str") filepath = Path(kwargs["filepath"]).expanduser() if not filepath.exists(): raise OSError(2, "Cannot found path file: %s" % filepath) # Generate configuration instance self.__configuration = Configuration(str(filepath)) self.__instance_name = self.__configuration.get( "metadata", "instance", fallback=None) self.__configuration_path = Path(self.__configuration.get( "path", "configuration", fallback=xdg_config_home)) self.__storage_path = Path(self.__configuration.get( "path", "storage", fallback=xdg_data_home)) # ---------------------------------------- # Manual parameters # ---------------------------------------- # Retrieve instance name if "instance" in kwargs: if type(kwargs["instance"]) is not str: raise TypeError("Wrong type for instance, expected str") self.__instance_name = kwargs["instance"] # Retrieve configuration folder path if "configuration" in kwargs: if type(kwargs["configuration"]) is not str: raise TypeError("Wrong type for configuration, expected str") self.__configuration_path = Path(kwargs["configuration"]) # Retrieve storage folder path if "storage" in kwargs: if type(kwargs["storage"]) is not str: raise TypeError("Wrong type for storage, expected str") self.__storage_path = Path(kwargs["storage"]) # ---------------------------------------- # Check instance name # ---------------------------------------- if self.__instance_name is None: raise ValueError("Missing instance name") if self.__configuration_path is None: self.__configuration_path = Path(xdg_config_home) if self.__storage_path is None: self.__storage_path = Path(xdg_data_home) # ---------------------------------------- # Initialize API # ---------------------------------------- # Check and generate default folders self.__init_folders() # Start logger module self.__init_logger() # Check lock file self.__init_lock() def __init_folders(self): """ Initialize default folders using to store data """ # Store config folder self.__paths["configuration"] = \ self.__configuration_path.expanduser().joinpath( self.__instance_name) # Store local folder self.__paths["storage"] = \ self.__storage_path.expanduser().joinpath( self.__instance_name) # API main folder for key, path in self.__paths.items(): if path is not None and not path.exists(): self.__logger.info("Generate %s folder" % path) # Create subfolder for current instance path.mkdir(0o755, True) # API specific configuration folders for key in ("consoles", "emulators"): self.__paths[key] = self.get_configuration_path().joinpath(key) if not self.__paths[key].exists(): self.__logger.info("Generate %s folder" % self.__paths[key]) # Create subfolder for current instance self.__paths[key].mkdir(0o755, True) def __init_logger(self): """ Initialize logger module """ # Set logging file path logging.log_path = self.get_storage_path().joinpath( "%s.log" % self.__instance_name) # Save previous logging file if logging.log_path.exists(): logging.log_path.rename("%s.old" % str(logging.log_path)) # Generate logger from configuration file fileConfig(Path(get_data("config", "logger.conf"))) self.__logger = logging.getLogger("geode") if not self.__debug: self.__logger.setLevel(logging.INFO) self.__logger.info("Initialize %s instance with API v.%s" % ( self.__instance_name, API.Metadata.VERSION.value)) def __init_lock(self): """ Initialize lock file to avoid multiple instance Raises ------ RuntimeError when an instance already exists """ pattern = "%s.*:%s" % (self.__instance_name, getlogin()) # Check if a temporary lock file already exists if len(list(Path(tempfile.gettempdir()).glob(pattern))) > 0: raise RuntimeError("An instance already exists") # Generate a new lock file self.__lock = tempfile.NamedTemporaryFile( suffix=":%s" % getlogin(), prefix="%s." % self.__instance_name) self.__logger.info("Register with PID %d" % self.__pid) def __init_objects(self, key, element): """ Initialize an object Parameters ---------- key : str Storage key (emulators or consoles) element : geode.lib.console.Console or geode.lib.emulator.Emulator Object type Raises ------ OSError when storage folder not exists """ # Reset storage content self.__storage[key].clear() path = self.__paths[key] # Check if folder path still exists if not path.exists(): raise OSError(2, "Cannot found %s folder: %s" % (key, path)) # Retrieve every configuration file from folder for filepath in sorted(path.glob("*.conf")): try: self.__storage[key].append(element(self, filepath)) except TypeError as error: self.__logger.error( "Cannot instantiate %s: %s" % (key, str(error))) def __add_object(self, key, element, **kwargs): """ Add a new object in API storage Parameters ---------- key : str Storage key (emulators or consoles) element : geode.lib.console.Console or geode.lib.emulator.Emulator Object type Raises ------ KeyError when name parameter is missing OSError when folder path not exists when folder path is not a directory """ if not "name" in kwargs: raise KeyError("Missing name key in parameters") folder = self.__paths[key] if not folder.exists(): raise OSError(2, "Cannot found folder: %s" % str(folder)) if not folder.is_dir(): raise OSError(1, "Path is not a directory: %s" % str(folder)) # ---------------------------------------- # Generate a new identifier # ---------------------------------------- # Retrieve only alphanumeric element from filename identifier = re_sub(r"[^\w\d]+", ' ', kwargs["name"].lower()) # Remove useless spaces and replace the others with a dash identifier = re_sub(r"[\s|_]+", '-', identifier.strip()) filepath = folder.joinpath("%s.conf" % identifier) # Check if other files has the same identifier matched = sorted(list(folder.glob("%s-[0-9].conf" % identifier))) if len(matched) > 0 or filepath.exists(): # Retrieve the last used number if len(matched) > 0: number = int(matched[-1].stem.split('-')[-1]) + 1 # Start a new counter else: number = 2 filepath = folder.joinpath("%s-%d.conf" % (identifier, number)) # ---------------------------------------- # Generate configuration file # ---------------------------------------- configuration = Configuration(str(filepath)) # Retrieve default value from wanted CommonObject for section, option, value in element.attributes: # Use value from parameters if option in kwargs: value = kwargs[option] configuration.modify(section, option, value) # Save configuration file configuration.update() # ---------------------------------------- # Store object # ---------------------------------------- try: self.__storage[key].append(element(self, filepath)) return self.__storage[key][-1] except TypeError as error: self.__logger.error( "Cannot instantiate %s: %s" % (key, str(error))) return None @property def log(self): """ Return logger instance Returns ------- logging.Logger Logger instance """ return self.__logger @property def pid(self): """ Return application process identifier Returns ------- int Process identifier """ return self.__pid def init(self): """ Initialize API structure and data """ # ---------------------------------------- # Database # ---------------------------------------- self.__database = Database( path=self.get_storage_path().joinpath("games.db"), scheme=Path(get_data("config", "database.conf"))) self.__database.connect() # Check if the database respect the specified scheme if not self.__database.check_integrity(): # Generate a new database if not self.__database.path.exists(): self.__database.generate_database() self.__database.insert("metadata", data={ "version": API.Metadata.VERSION.value }) # ---------------------------------------- # Games environment keys # ---------------------------------------- self.__environment = Configuration( str(self.get_configuration_path().joinpath("games.conf"))) # ---------------------------------------- # Objects # ---------------------------------------- self.__init_objects("emulators", Emulator) self.__init_objects("consoles", Console) def get_database(self): """ Return database instance Returns ------- geode.db.Database Database instance """ return self.__database def get_environment(self): """ Return games environment configuration file Returns ------- geode.conf.Configuration Environment configuration instance """ return self.__environment def get_instance(self): """ Retrieve API instance name Returns ------- str Current API instance name """ return self.__instance_name def get_paths(self): """ Retrieve API paths Returns ------- list List which contains key and path tuples """ return list(self.__paths.items()) def get_storage_path(self): """ Retrieve storage folder path Returns ------- pathlib.Path Storage folder """ return self.__paths["storage"] def get_configuration_path(self): """ Retrieve configuration folder path Returns ------- pathlib.Path Configuration folder """ return self.__paths["configuration"] def get_emulators(self): """ Retrieve available emulators Returns ------- list Emulator objects as list """ return self.__storage["emulators"] def get_emulators_path(self): """ Retrieve emulators folder path Returns ------- pathlib.Path Emulators folder """ return self.__paths["emulators"] def get_emulator(self, key): """ Retrieve a specific emulator Parameters ---------- key : str Emulator identifier key Returns ------- geode.lib.emulator.Emulator or None Emulator instance if found, None otherwise """ return next((emulator for emulator in self.__storage["emulators"] \ if emulator.id == key), None) def add_emulator(self, **kwargs): """ Append a new emulator Returns ------- geode.lib.emulator.Emulator or None Emulator instance if success, None otherwise """ return self.__add_object("emulators", Emulator, **kwargs) def delete_emulator(self, key): """ Delete a specific emulator Parameters ---------- key : str or geode.lib.emulator.Emulator Emulator identifier key or instance Returns ------- bool True if emulator has been successfully removed, False otherwise """ if type(key) is Emulator: emulator = key elif type(key) is str: emulator = self.get_emulator(key) if emulator is not None: try: # Remove configuration file emulator.get_path().unlink() # Remove instance from storage self.get_emulators().remove(emulator) # Remove instance from consoles for console in self.get_consoles(): # Remove emulator entry from console instance if console.emulator == emulator: console.emulator = None console.save() # Remove instance from memory del emulator return True except Exception as error: return False return False def get_consoles(self): """ Retrieve available consoles Returns ------- list Console objects as list """ return self.__storage["consoles"] def get_consoles_path(self): """ Retrieve consoles folder path Returns ------- pathlib.Path Consoles folder """ return self.__paths["consoles"] def get_console(self, key): """ Retrieve a specific console Parameters ---------- key : str Console identifier key Returns ------- geode.lib.console.Console or None Console instance if found, None otherwise """ return next((console for console in self.__storage["consoles"] \ if console.id == key), None) def add_console(self, **kwargs): """ Append a new console Returns ------- geode.lib.console.Console or None Console instance if success, None otherwise """ return self.__add_object("consoles", Console, **kwargs) def delete_console(self, key): """ Delete a specific console Parameters ---------- key : str or geode.lib.console.Console Console identifier key or instance Returns ------- bool True if console has been successfully removed, False otherwise """ if type(key) is Console: console = key elif type(key) is str: console = self.get_console(key) if console is not None: try: # Remove configuration file console.get_path().unlink() # Remove instance from storage self.get_consoles().remove(console) # Remove instance from memory del console return True except Exception as error: return False return False def get_games(self, sort=False): """ Retrieve every games available Parameters ---------- sort : bool, optional Sort list before return it (Default: False) Returns ------- list Games list """ games = list() for console in self.__storage["consoles"]: games.extend(console.get_games()) if sort: games.sort(key=lambda game: game.name.lower().replace(' ', '')) return games def get_game(self, key): """ Retrieve a specific game Parameters ---------- key : str Game identifier key sort : bool, optional Sort list before return it (Default: False) Returns ------- geode.lib.game.Game or None Game instance if found, None otherwise """ return next((game for game in self.get_games() if game.id == key), None) def search_game(self, key, sort=False): """ Search games from a specific key Parameters ---------- key : str Key to search in games list (based on identifier and name) Returns ------- generator Game instances """ regex = re_compile(key, IGNORECASE) return (game for game in self.get_games(sort) \ if regex.search(game.name) or regex.search(game.id)) def delete_game(self, key): """ Delete a specific game from database This function only remove the game from database. If you want to remove the game file, you need to do it manually. Parameters ---------- key : str or geode.lib.game.Game Game identifier key or instance """ if type(key) is Game: game = key elif type(key) is str: game = self.get_game(key) if game is not None: console = game.console # Remove instance from storage return console.delete_game(game) return False
Class variables
var Metadata
-
Represent metadata
Expand source code
class Metadata(Enum): """ Represent metadata """ NAME = "geode" VERSION = "1.0"
Instance variables
var log
-
Return logger instance
Returns
logging.Logger
- Logger instance
Expand source code
@property def log(self): """ Return logger instance Returns ------- logging.Logger Logger instance """ return self.__logger
var pid
-
Return application process identifier
Returns
int
- Process identifier
Expand source code
@property def pid(self): """ Return application process identifier Returns ------- int Process identifier """ return self.__pid
Methods
def add_console(self, **kwargs)
-
Append a new console
Returns
Console
orNone
- Console instance if success, None otherwise
Expand source code
def add_console(self, **kwargs): """ Append a new console Returns ------- geode.lib.console.Console or None Console instance if success, None otherwise """ return self.__add_object("consoles", Console, **kwargs)
def add_emulator(self, **kwargs)
-
Append a new emulator
Returns
Emulator
orNone
- Emulator instance if success, None otherwise
Expand source code
def add_emulator(self, **kwargs): """ Append a new emulator Returns ------- geode.lib.emulator.Emulator or None Emulator instance if success, None otherwise """ return self.__add_object("emulators", Emulator, **kwargs)
def delete_console(self, key)
-
Delete a specific console
Parameters
key
:str
orConsole
- Console identifier key or instance
Returns
bool
- True if console has been successfully removed, False otherwise
Expand source code
def delete_console(self, key): """ Delete a specific console Parameters ---------- key : str or geode.lib.console.Console Console identifier key or instance Returns ------- bool True if console has been successfully removed, False otherwise """ if type(key) is Console: console = key elif type(key) is str: console = self.get_console(key) if console is not None: try: # Remove configuration file console.get_path().unlink() # Remove instance from storage self.get_consoles().remove(console) # Remove instance from memory del console return True except Exception as error: return False return False
def delete_emulator(self, key)
-
Delete a specific emulator
Parameters
key
:str
orEmulator
- Emulator identifier key or instance
Returns
bool
- True if emulator has been successfully removed, False otherwise
Expand source code
def delete_emulator(self, key): """ Delete a specific emulator Parameters ---------- key : str or geode.lib.emulator.Emulator Emulator identifier key or instance Returns ------- bool True if emulator has been successfully removed, False otherwise """ if type(key) is Emulator: emulator = key elif type(key) is str: emulator = self.get_emulator(key) if emulator is not None: try: # Remove configuration file emulator.get_path().unlink() # Remove instance from storage self.get_emulators().remove(emulator) # Remove instance from consoles for console in self.get_consoles(): # Remove emulator entry from console instance if console.emulator == emulator: console.emulator = None console.save() # Remove instance from memory del emulator return True except Exception as error: return False return False
def delete_game(self, key)
-
Delete a specific game from database
This function only remove the game from database. If you want to remove the game file, you need to do it manually.
Parameters
key
:str
orGame
- Game identifier key or instance
Expand source code
def delete_game(self, key): """ Delete a specific game from database This function only remove the game from database. If you want to remove the game file, you need to do it manually. Parameters ---------- key : str or geode.lib.game.Game Game identifier key or instance """ if type(key) is Game: game = key elif type(key) is str: game = self.get_game(key) if game is not None: console = game.console # Remove instance from storage return console.delete_game(game) return False
def get_configuration_path(self)
-
Retrieve configuration folder path
Returns
pathlib.Path
- Configuration folder
Expand source code
def get_configuration_path(self): """ Retrieve configuration folder path Returns ------- pathlib.Path Configuration folder """ return self.__paths["configuration"]
def get_console(self, key)
-
Retrieve a specific console
Parameters
key
:str
- Console identifier key
Returns
Console
orNone
- Console instance if found, None otherwise
Expand source code
def get_console(self, key): """ Retrieve a specific console Parameters ---------- key : str Console identifier key Returns ------- geode.lib.console.Console or None Console instance if found, None otherwise """ return next((console for console in self.__storage["consoles"] \ if console.id == key), None)
def get_consoles(self)
-
Retrieve available consoles
Returns
list
- Console objects as list
Expand source code
def get_consoles(self): """ Retrieve available consoles Returns ------- list Console objects as list """ return self.__storage["consoles"]
def get_consoles_path(self)
-
Retrieve consoles folder path
Returns
pathlib.Path
- Consoles folder
Expand source code
def get_consoles_path(self): """ Retrieve consoles folder path Returns ------- pathlib.Path Consoles folder """ return self.__paths["consoles"]
def get_database(self)
-
Return database instance
Returns
Database
- Database instance
Expand source code
def get_database(self): """ Return database instance Returns ------- geode.db.Database Database instance """ return self.__database
def get_emulator(self, key)
-
Retrieve a specific emulator
Parameters
key
:str
- Emulator identifier key
Returns
Emulator
orNone
- Emulator instance if found, None otherwise
Expand source code
def get_emulator(self, key): """ Retrieve a specific emulator Parameters ---------- key : str Emulator identifier key Returns ------- geode.lib.emulator.Emulator or None Emulator instance if found, None otherwise """ return next((emulator for emulator in self.__storage["emulators"] \ if emulator.id == key), None)
def get_emulators(self)
-
Retrieve available emulators
Returns
list
- Emulator objects as list
Expand source code
def get_emulators(self): """ Retrieve available emulators Returns ------- list Emulator objects as list """ return self.__storage["emulators"]
def get_emulators_path(self)
-
Retrieve emulators folder path
Returns
pathlib.Path
- Emulators folder
Expand source code
def get_emulators_path(self): """ Retrieve emulators folder path Returns ------- pathlib.Path Emulators folder """ return self.__paths["emulators"]
def get_environment(self)
-
Return games environment configuration file
Returns
Configuration
- Environment configuration instance
Expand source code
def get_environment(self): """ Return games environment configuration file Returns ------- geode.conf.Configuration Environment configuration instance """ return self.__environment
def get_game(self, key)
-
Retrieve a specific game
Parameters
key
:str
- Game identifier key
sort
:bool
, optional- Sort list before return it (Default: False)
Returns
Game
orNone
- Game instance if found, None otherwise
Expand source code
def get_game(self, key): """ Retrieve a specific game Parameters ---------- key : str Game identifier key sort : bool, optional Sort list before return it (Default: False) Returns ------- geode.lib.game.Game or None Game instance if found, None otherwise """ return next((game for game in self.get_games() if game.id == key), None)
def get_games(self, sort=False)
-
Retrieve every games available
Parameters
sort
:bool
, optional- Sort list before return it (Default: False)
Returns
list
- Games list
Expand source code
def get_games(self, sort=False): """ Retrieve every games available Parameters ---------- sort : bool, optional Sort list before return it (Default: False) Returns ------- list Games list """ games = list() for console in self.__storage["consoles"]: games.extend(console.get_games()) if sort: games.sort(key=lambda game: game.name.lower().replace(' ', '')) return games
def get_instance(self)
-
Retrieve API instance name
Returns
str
- Current API instance name
Expand source code
def get_instance(self): """ Retrieve API instance name Returns ------- str Current API instance name """ return self.__instance_name
def get_paths(self)
-
Retrieve API paths
Returns
list
- List which contains key and path tuples
Expand source code
def get_paths(self): """ Retrieve API paths Returns ------- list List which contains key and path tuples """ return list(self.__paths.items())
def get_storage_path(self)
-
Retrieve storage folder path
Returns
pathlib.Path
- Storage folder
Expand source code
def get_storage_path(self): """ Retrieve storage folder path Returns ------- pathlib.Path Storage folder """ return self.__paths["storage"]
def init(self)
-
Initialize API structure and data
Expand source code
def init(self): """ Initialize API structure and data """ # ---------------------------------------- # Database # ---------------------------------------- self.__database = Database( path=self.get_storage_path().joinpath("games.db"), scheme=Path(get_data("config", "database.conf"))) self.__database.connect() # Check if the database respect the specified scheme if not self.__database.check_integrity(): # Generate a new database if not self.__database.path.exists(): self.__database.generate_database() self.__database.insert("metadata", data={ "version": API.Metadata.VERSION.value }) # ---------------------------------------- # Games environment keys # ---------------------------------------- self.__environment = Configuration( str(self.get_configuration_path().joinpath("games.conf"))) # ---------------------------------------- # Objects # ---------------------------------------- self.__init_objects("emulators", Emulator) self.__init_objects("consoles", Console)
def search_game(self, key, sort=False)
-
Search games from a specific key
Parameters
key
:str
- Key to search in games list (based on identifier and name)
Returns
generator
- Game instances
Expand source code
def search_game(self, key, sort=False): """ Search games from a specific key Parameters ---------- key : str Key to search in games list (based on identifier and name) Returns ------- generator Game instances """ regex = re_compile(key, IGNORECASE) return (game for game in self.get_games(sort) \ if regex.search(game.name) or regex.search(game.id))