From a4fb791649b9ac9abaa1d40e75f8e15e1b8a4d79 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Thu, 8 Dec 2022 16:44:49 +0100 Subject: [PATCH] Cmd / Cmds: Add config file support An application based on Cmds will now try to read StringTree formatted config data from a file $HOME/.rc, and make the data available via the newly added conf_value() method. Signed-off-by: Jan Lindemann --- tools/python/jwutils/Cmd.py | 7 +++++++ tools/python/jwutils/Cmds.py | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/tools/python/jwutils/Cmd.py b/tools/python/jwutils/Cmd.py index 79ce341..c2005d1 100644 --- a/tools/python/jwutils/Cmd.py +++ b/tools/python/jwutils/Cmd.py @@ -22,6 +22,7 @@ class Cmd(abc.ABC): # export self.parent = None self.children: List[Cmd] = [] self.child_classes: List[Type[Cmd]] = [] + self.app: Optional[Cmds] = None async def _run(self, args): pass @@ -52,3 +53,9 @@ class Cmd(abc.ABC): # export # Will be called from App base class constructor and set up the parser hierarchy def add_arguments(self, parser: ArgumentParser) -> None: pass + + def conf_value(self, path, default=None): + ret = None if self.app is None else self.app.conf_value(path, default) + if ret is None and default is not None: + return default + return ret diff --git a/tools/python/jwutils/Cmds.py b/tools/python/jwutils/Cmds.py index 0540157..dd4402f 100644 --- a/tools/python/jwutils/Cmds.py +++ b/tools/python/jwutils/Cmds.py @@ -3,9 +3,11 @@ from typing import Optional import os, sys, argcomplete, argparse, importlib, inspect, re, pickle, asyncio, cProfile from argparse import ArgumentParser +from pathlib import Path, PurePath import jwutils from jwutils.log import * +from jwutils.stree import serdes class Cmds: # export @@ -30,10 +32,19 @@ class Cmds: # export for sub_cmd in cmd.children: self.__add_cmd_to_parser(sub_cmd, subparsers) + def __parse_config(self): + exe_stem = str(PurePath(sys.argv[0]).stem) + path = str(Path.home()) + '/.' + exe_stem + 'rc' + if not os.path.exists(path): + return None, [] + slog(DEBUG, 'Reading configuration "{}"'.format(path)) + return serdes.read(path, ''), [path] + def __init__(self, description: str = '', filter: str = '^Cmd.*', modules: None=None, eloop: None=None) -> None: self.__description = description self.__filter = filter self.__modules = modules + self.__conf, self.__conf_paths = self.__parse_config() self.__cmds = [] self.eloop = eloop self.__own_eloop = False @@ -43,6 +54,7 @@ class Cmds: # export log_level = "notice" log_flags = 'stderr,position,prio,color' + log_file = None # poor man's parsing in the absence of a complete command-line definition for i in range(1, len(sys.argv)): if i >= len(sys.argv) - 1: @@ -64,6 +76,7 @@ class Cmds: # export self.__parser.add_argument('--log-level', help='Log level', default=log_level) self.__parser.add_argument('--backtrace', help='Show exception backtraces', action='store_true', default=False) self.__parser.add_argument('--write-profile', help='Profile code and store output to file', default=None) + self.__parser.add_argument('--log-file', help='Log file', default=log_file) if self.__modules == None: self.__modules = [ '__main__' ] subcmds = set() @@ -118,6 +131,11 @@ class Cmds: # export if pr is not None: pr.enable() + if self.__conf: + self.__conf.dump(DEBUG, "Configuration") + if self.args.log_file is not None: + add_log_file(self.args.log_file) + try: ret = await self._run(self.args) except Exception as e: @@ -144,6 +162,12 @@ class Cmds: # export self.eloop = None self.__own_eloop = False + def conf_value(self, path, default=None): + ret = None if self.__conf is None else self.__conf.value(path) + if ret is None and default is not None: + return default + return ret + def parser(self) -> ArgumentParser: return self.__parser