python installer #236
Replies: 3 comments 1 reply
-
|
@dieBakterie I'll push a local branch soon. The main idea here is upon HyDE installation/updates we will prepare a Venv. And all hydepy scripts will use that as its environment. It's all good except for the fact that it's slow on loading the click module (<250 ms to load). I already discussed this stuff with @rubiin. Hehe Thanks @rubiin . I'm planning this ao we can easily integrate the hydepanel.😉 Here's a snippit hydepy (entrypoint)import os
import sys
import click
import importlib.util
# Add the current directory to the path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
try:
from utils import fmt_logger, get_venv_path # noqa: E402
except ImportError:
print("Error: Cannot import lib/utils.py")
print("Is hyde installed correctly?")
sys.exit(1)
log = fmt_logger()
sys.path.insert(0, get_venv_path())
module_cache = {}
module_dirs = [
os.path.expanduser("~/.local/lib/hydepy/modules"),
"/usr/local/lib/hydepy/modules",
"/usr/lib/hydepy/modules",
]
def list_modules():
modules = []
for module_dir in module_dirs:
if os.path.exists(module_dir):
sys.path.insert(0, module_dir)
for filename in os.listdir(module_dir):
if filename.endswith(".py") and filename != "__init__.py":
module_name = filename[:-3]
try:
module = importlib.import_module(module_name)
if hasattr(module, "hyde") and callable(
getattr(module, "hyde")
):
modules.append(module_name)
except ImportError as e:
log.error(f"Error importing module {module_name}: {e}")
click.echo(f"Error importing module {module_name}: {e}")
sys.path.pop(0)
return modules
class LazyGroup(click.Group):
"""Lazy Load the cached modules, this is to avoid loading all modules at once = faster"""
def list_commands(self, ctx):
return list_modules()
def get_command(self, ctx, cmd_name):
if cmd_name in module_cache:
return module_cache[cmd_name]
for module_dir in module_dirs:
module_path = os.path.join(module_dir, f"{cmd_name}.py")
if os.path.exists(module_path):
try:
spec = importlib.util.spec_from_file_location(cmd_name, module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
module_cache[cmd_name] = module
@click.command(
name=cmd_name,
context_settings=dict(
ignore_unknown_options=True,
allow_extra_args=True,
help_option_names=[], # Disable the default --help option
),
help=module.hyde.__doc__
if hasattr(module, "hyde")
else f"Help for {cmd_name}",
)
@click.pass_context
def _command(ctx):
try:
module.hyde(ctx.args)
except Exception as e:
log.error(f"Error executing command {cmd_name}: {e}")
click.echo(f"Error executing command {cmd_name}: {e}")
return _command
except Exception as e:
log.error(f"Error loading module {cmd_name}: {e}")
click.echo(f"Error loading module {cmd_name}: {e}")
return None
@click.command(cls=LazyGroup, invoke_without_command=True)
@click.pass_context
def cli(ctx):
log.debug(f"Starting hydepy... Using logger: {log.get_logger_type()}")
ctx.ensure_object(dict)
if ctx.invoked_subcommand is None:
click.echo(ctx.get_help())
ctx.exit()
if __name__ == "__main__":
cli()
os._exitutils.pyimport os
import sys
import importlib
def get_venv_path():
"""Set up the virtual environment path and modify sys.path."""
# Determine the virtual environment path using XDG Base Directory Specification
xdg_state_home = os.getenv("XDG_STATE_HOME", os.path.expanduser("~/.local/state"))
venv_path = os.path.join(xdg_state_home, "hydepy", "venv")
# Fall back to $HOME/.local/lib/hyde/venv if XDG_state_HOME is not set
if not os.path.exists(venv_path):
venv_path = os.path.expanduser("~/.local/state/hydepy/venv")
site_packages_path = os.path.join(
venv_path,
"lib",
f"python{sys.version_info.major}.{sys.version_info.minor}",
"site-packages",
)
sys.path.insert(0, site_packages_path)
return venv_path
def fmt_logger():
"""Format logger to provide a unified interface."""
log_level = os.getenv("LOG_LEVEL")
if not log_level:
class NoOpLogger:
def __getattr__(self, name):
def no_op(*args, **kwargs):
pass
return no_op
def get_logger_type(self):
return "NoOpLogger"
return NoOpLogger()
log_level = log_level.upper()
# Dynamically import logging or loguru
try:
log = importlib.import_module("loguru")
log.logger.remove()
log.logger.add(sys.stderr, level=log_level)
logger_type = "loguru"
except ModuleNotFoundError:
import logging as log
log.basicConfig(level=getattr(log, log_level))
logger_type = "logging"
class UnifiedLogger:
def __init__(self, logger, logger_type):
self.logger = logger
self.logger_type = logger_type
def debug(self, msg, *args, **kwargs):
if hasattr(self.logger, "logger"):
self.logger.logger.debug(msg, *args, **kwargs)
else:
self.logger.debug(msg, *args, **kwargs)
def info(self, msg, *args, **kwargs):
if hasattr(self.logger, "logger"):
self.logger.logger.info(msg, *args, **kwargs)
else:
self.logger.info(msg, *args, **kwargs)
def warning(self, msg, *args, **kwargs):
if hasattr(self.logger, "logger"):
self.logger.logger.warning(msg, *args, **kwargs)
else:
self.logger.warning(msg, *args, **kwargs)
def error(self, msg, *args, **kwargs):
if hasattr(self.logger, "logger"):
self.logger.logger.error(msg, *args, **kwargs)
else:
self.logger.error(msg, *args, **kwargs)
def critical(self, msg, *args, **kwargs):
if hasattr(self.logger, "logger"):
self.logger.logger.critical(msg, *args, **kwargs)
else:
self.logger.critical(msg, *args, **kwargs)
def get_logger_type(self):
return self.logger_type
return UnifiedLogger(log, logger_type)
def load_modules(module_dir):
modules = []
sys.path.insert(0, module_dir)
for filename in os.listdir(module_dir):
if filename.endswith(".py") and filename != "__init__.py":
module_name = filename[:-3]
try:
module = importlib.import_module(module_name)
if hasattr(module, "hyde") and callable(getattr(module, "hyde")):
modules.append(module_name)
except ImportError:
continue
sys.path.pop(0)
return modules
def load_module(module_dir, command):
sys.path.insert(0, module_dir)
try:
module = importlib.import_module(command)
except ModuleNotFoundError:
module = None
sys.path.pop(0)
return module
# Call get_venv_path() to set up the virtual environment path
sys.path.insert(0, get_venv_path())I'll be very grateful as I can see that average HyDE users know a bunch of python. |
Beta Was this translation helpful? Give feedback.
-
|
#236 (comment) |
Beta Was this translation helpful? Give feedback.
-
|
Wanna keep and use this discussion for planning the migration and pre-work needed for python transfer? Like which/what libs we use, when we write our own code and when we use built-in and when external libs... |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Idea how to migrate from shell and bash to python
First we should make an helper script with the
fmt_loggingand other stuff that's used in all scripts, like withglobalcontrol.shright now for the shell/bash scripts/installer.Example:
globalcontrol.pyThen in
parse.json.pywe do:could be that we've to run the
globalcontrol.pyscript in the python scripts like for theshell/bashscripts via subprocess or we run the scripts(parse.json.py, ...) via subprocess from theglobalcontrol.pyvia args like--json,--config, ...Beta Was this translation helpful? Give feedback.
All reactions