From 6cecf64dbb488949a67eb080bf27d06f51533f40 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Mon, 4 May 2026 22:48:19 +0200 Subject: Pulls logging functions from C into python code --- salis.py | 67 ++++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 27 deletions(-) (limited to 'salis.py') diff --git a/salis.py b/salis.py index 64584a5..2800d73 100755 --- a/salis.py +++ b/salis.py @@ -1,6 +1,6 @@ #!/usr/bin/env -S PYTHONDONTWRITEBYTECODE=1 python -import colorlog +import ctypes import enum import json import os @@ -18,11 +18,10 @@ from types import SimpleNamespace # ------------------------------------------------------------------------------ # Argument parser # ------------------------------------------------------------------------------ -description = "Salis: Simple A-Life Simulator" prog = sys.argv[0] epilog = f"Use '-h' to show command arguments; e.g. '{prog} new -h'" -parser = ArgumentParser(description=description, epilog=epilog, formatter_class=RawTextHelpFormatter, prog=prog) +parser = ArgumentParser(description="Salis: Simple A-Life Simulator", epilog=epilog, formatter_class=RawTextHelpFormatter, prog=prog) sub_parsers = parser.add_subparsers(dest="command", required=True) formatter_class = lambda prog: ArgumentDefaultsHelpFormatter(max_help_position=32, prog=prog) @@ -90,23 +89,16 @@ for (name, sub_parsers, _), kwargs in options.items(): args = parser.parse_args() -# ------------------------------------------------------------------------------ -# Logging -# ------------------------------------------------------------------------------ -handler = colorlog.StreamHandler() -handler.setFormatter(colorlog.ColoredFormatter("%(log_color)s%(asctime)s %(process)07d [%(levelname).4s] %(reset)s%(message)s")) - -log = colorlog.getLogger() -log.setLevel(colorlog.INFO) -log.addHandler(handler) - # ------------------------------------------------------------------------------ # Build class # ------------------------------------------------------------------------------ class Build: - def __init__(self, path, library=False): + def __init__(self, path, log, library=False): + self.log = log + self.library = library + self.tempdir = TemporaryDirectory(prefix="salis_", delete=not args.keep_temp_dir) - log.info(f"Generated temporary directory for C builds at: {self.tempdir.name}") + self.log.info(f"Generated temporary directory for C builds at: {self.tempdir.name}") self.name = os.path.splitext(os.path.basename(path))[0] self.binfile = os.path.join(self.tempdir.name, f"{self.name}{".so" if library else ""}") @@ -117,8 +109,8 @@ class Build: self.links = set() self.build_cmd = [args.compiler, f"@{self.argsfile}", path, "-o", self.binfile] - log.info(f"Build class initialized for {"library" if library else "executable"}: {path}") - log.info(f"Compiler flags stored at: {self.argsfile}") + self.log.info(f"Build class initialized for {"library" if library else "executable"}: {path}") + self.log.info(f"Compiler flags stored at: {self.argsfile}") def build(self): fmt_nl = lambda line: f"{line}\n" @@ -129,14 +121,16 @@ class Build: f.writelines(map(fmt_define, sorted(self.defines))) f.writelines(map(fmt_nl, sorted(self.links))) - log.info(f"Running build command: '{self.build_cmd}'") + self.log.info(f"Running build command: '{self.build_cmd}'") subprocess.run(self.build_cmd, check=True) def exec(self): + assert not self.library + run_cmd = args.pre_cmd.split() if args.pre_cmd else [] run_cmd.append(self.binfile) - log.info(f"Running binary with command: '{" ".join(run_cmd)}'") + self.log.info(f"Running binary with command: '{" ".join(run_cmd)}'") proc = subprocess.Popen(run_cmd, stdout=sys.stdout, stderr=sys.stderr) # When using signals (e.g. SIGTERM), they must be sent to the entire process group @@ -151,6 +145,26 @@ class Build: if code != 0: raise RuntimeError(f"Binary returned code: {code}") +# ------------------------------------------------------------------------------ +# Logging +# ------------------------------------------------------------------------------ +log_logs = [] +log_ns = SimpleNamespace() +log_ns.info = lambda msg: log_logs.append(("info", msg)) +log_ns.warn = lambda msg: log_logs.append(("warn", msg)) +log_b = Build("core/logger.c", log_ns, library=True) +log_b.build() + +# Pull in logging functions from C +# This way there's only a single unified logging system to care about +log = SimpleNamespace() +log_dll = ctypes.CDLL(log_b.binfile) +log.info = lambda msg: log_dll.log_info(msg.encode()) +log.warn = lambda msg: log_dll.log_warn(msg.encode()) + +for log_level, log_msg in log_logs: + getattr(log, log_level)(log_msg) + # ------------------------------------------------------------------------------ # Options dict formatter # ------------------------------------------------------------------------------ @@ -175,7 +189,6 @@ def fmt_opts(opts): # ------------------------------------------------------------------------------ # Source configuration # ------------------------------------------------------------------------------ -log.info(description) log.info(f"Called '{prog} {args.command}' with the following options: {fmt_opts(vars(args))}") ns = SimpleNamespace() @@ -215,7 +228,7 @@ if args.command == "new": raise RuntimeError("Data push power must be equal or greater than core sync power") if os.path.isdir(ns.sim_dir) and args.force: - log.warning(f"Force flag used! Wiping old simulation at: {ns.sim_dir}") + log.warn(f"Force flag used! Wiping old simulation at: {ns.sim_dir}") shutil.rmtree(ns.sim_dir) if os.path.isdir(ns.sim_dir): @@ -331,14 +344,14 @@ def pop_data_push_vars(): ns.b.links.add("-lz") log.info(f"Data will be aggregated at: {ns.sim_db}") else: - log.warning("Data aggregation disabled") + log.warn("Data aggregation disabled") if not args.no_compress: ns.b.defines.add("-DCOMPRESS") ns.b.links.add("-lz") log.info("Save file compression enabled") else: - log.warning("Save file compression disabled") + log.warn("Save file compression disabled") def pop_sim_path_vars(): ns.b.defines.add(f"-DSIM_OPTS=\"{ns.sim_opts}\"") @@ -385,7 +398,7 @@ def pop_general(): # Populate for new if args.command == "new": - ns.b = Build("core/salis.c") + ns.b = Build("core/salis.c", log) pop_ui_vars() pop_data_push_vars() pop_sim_path_vars() @@ -396,7 +409,7 @@ if args.command == "new": # Populate for load if args.command == "load": - ns.b = Build("core/salis.c") + ns.b = Build("core/salis.c", log) pop_ui_vars() pop_data_push_vars() pop_sim_path_vars() @@ -407,14 +420,14 @@ if args.command == "load": # Populate for server if args.command == "server": - ns.b = Build("data/server.c") + ns.b = Build("data/server.c", log) pop_sim_path_vars() pop_net_vars() pop_general() # Populate for client if args.command == "client": - ns.b = Build("data/client.c") + ns.b = Build("data/client.c", log) pop_net_vars() pop_general() ns.b.defines.add(f"-DIP={args.ip}") -- cgit v1.3