diff --git a/rsbbs/commands.py b/rsbbs/commands.py index 23f8209..5f43d93 100644 --- a/rsbbs/commands.py +++ b/rsbbs/commands.py @@ -29,10 +29,9 @@ class Commands(): # aliases, # helpmsg, # function, - # callback, - # {arg: - # {arg attributes}, - # ...}) + # arg: + # arg attributes..., + # ) ('bye', ['b', 'q'], 'Sign off and disconnect', @@ -94,7 +93,7 @@ class Commands(): 'Send a new message to a user', self.responder.send, { - 'callsign': {'help': 'Message recipient callsign'}, + '--callsign': {'help': 'Message recipient callsign'}, '--subject': {'help': 'Message subject'}, '--message': {'help': 'Message'}, },), @@ -104,7 +103,7 @@ class Commands(): 'Send a private message to a user', self.responder.send_private, { - 'callsign': {'help': 'Message recipient callsign'}, + '--callsign': {'help': 'Message recipient callsign'}, '--subject': {'help': 'Message subject'}, '--message': {'help': 'Message'}, },),] diff --git a/rsbbs/config.py b/rsbbs/config.py index e40b16b..d5842a2 100644 --- a/rsbbs/config.py +++ b/rsbbs/config.py @@ -25,45 +25,52 @@ import yaml class Config(): def __init__(self, app_name, args): - self._app_name = app_name - self._config_file_path = args.config_file + self.app_name = app_name + self._argv_config_file = args.config_file self._load_config() + # Put the messages db file in the system's user data directory self.config['db_path'] = os.path.join( - platformdirs.user_data_dir(appname=app_name, ensure_exists=True), + platformdirs.user_data_dir( + appname=self.app_name, + ensure_exists=True), 'messages.db') - self.config['debug'] = args.debug + # Grab some config from the command line for convenience self.config['args'] = args self.config['calling_station'] = args.calling_station.upper() or None + self.config['debug'] = args.debug + # The main thing people want from Config is config values, so let's pretend + # everything anyone asks of Config that isn't otherwise defined is probably + # a config value they want def __getattr__(self, __name: str): return self.config[__name] @property - def config_path(self): + def config_file(self): # Use either the specified file or a file in a system config location - config_path = self._config_file_path or os.path.join( + config_file = self._argv_config_file or os.path.join( platformdirs.user_config_dir( - appname=self._app_name, + appname=self.app_name, ensure_exists=True), 'config.yaml' ) - return config_path + return config_file def _init_config_file(self): # If the file doesn't exist there, create it from the default file # included in the package - if not os.path.exists(self.config_path): - config_default_file_path = pkg_resources.resource_filename( + if not os.path.exists(self.config_file): + config_default_file = pkg_resources.resource_filename( __name__, 'config_default.yaml') try: - with open(config_default_file_path, 'r') as f: - config_template = yaml.load(f, Loader=yaml.FullLoader) - with open(self.config_path, 'w') as f: - yaml.dump(config_template, f) + with open(config_default_file, 'r') as f: + config_default = yaml.load(f, Loader=yaml.FullLoader) + with open(self.config_file, 'w') as f: + yaml.dump(config_default, f) except Exception as e: print(f"Error creating configuration file: {e}") exit(1) @@ -77,7 +84,7 @@ class Config(): """ # Load it try: - with open(self.config_path, 'r') as f: + with open(self.config_file, 'r') as f: self.config = yaml.load(f, Loader=yaml.FullLoader) except Exception as e: print(f"Error loading configuration file: {e}") diff --git a/rsbbs/console.py b/rsbbs/console.py index d672d67..e1a77e6 100644 --- a/rsbbs/console.py +++ b/rsbbs/console.py @@ -174,7 +174,7 @@ class Console(): self._write_output(f"Heard stations not available.") def help(self, args): - self.parser.parser.print_help() + self.parser.print_help() def list(self, args): """List all public messages and private messages to the caller.""" @@ -267,7 +267,7 @@ class Console(): # Parse the BBS interactive commands for the rest of time for line in sys.stdin: try: - args = self.parser.parser.parse_args(line.split()) + args = self.parser.parse_args(line.split()) args.func(args) except Exception: if self.config.debug: diff --git a/rsbbs/parser.py b/rsbbs/parser.py index 6df8d99..318bee3 100644 --- a/rsbbs/parser.py +++ b/rsbbs/parser.py @@ -21,15 +21,12 @@ import argparse from rsbbs.commands import Commands -# We want to override the error and exit methods of ArgumentParser -# to prevent it exiting unexpectedly or spewing error data over the air - class BBSArgumentParser(argparse.ArgumentParser): - # Override the error handler to prevent exiting on error + # Override the error handler to prevent spewing error cruft over the air # def error(self, message): # print(message) - # raise + # Override the exit handler to prevent disconnecting unexpectedly def exit(self, arg1, arg2): pass @@ -37,9 +34,17 @@ class BBSArgumentParser(argparse.ArgumentParser): class Parser(BBSArgumentParser): def __init__(self, commands: Commands): - self.init_parser(commands) + self._init_parser(commands) - def init_parser(self, commands): + # The only thing anyone should ever access from Parser is its parser + # attribute, so let's save everyone a step. + def __getattribute__(self, attr): + try: + return object.__getattribute__(self, attr) + except AttributeError: + return getattr(self.parser, attr) + + def _init_parser(self, commands): # Root parser for BBS commands self.parser = BBSArgumentParser( description='BBS Main Menu', @@ -55,11 +60,14 @@ class Parser(BBSArgumentParser): # Loop through the commands and add each as a subparser for name, aliases, help_msg, func, arguments in commands.commands: + # Add the command attributes subparser = subparsers.add_parser( name, aliases=aliases, help=help_msg, ) + # Add the command parameters for arg_name, options in arguments.items(): subparser.add_argument(arg_name, **options) + # Trick to pass a function to call when the command is entered subparser.set_defaults(func=func) diff --git a/rsbbs/rsbbs.py b/rsbbs/rsbbs.py index c82b9cd..a77430f 100755 --- a/rsbbs/rsbbs.py +++ b/rsbbs/rsbbs.py @@ -30,7 +30,7 @@ from rsbbs.parser import Parser def main(): # Parse and handle the system invocation arguments - sysv_parser = argparse.ArgumentParser( + argv_parser = argparse.ArgumentParser( description=("The BBS for ax.25 and packet radio " "that is really simple.")) @@ -45,23 +45,23 @@ def main(): 'Path to config.yaml file', False], ] for arg in args_list: - sysv_parser.add_argument( + argv_parser.add_argument( arg[0], arg[1], action=arg[2], default=arg[3], dest=arg[4], help=arg[5], required=arg[6]) # Version arg is special: - sysv_parser.add_argument( + argv_parser.add_argument( '-v', '--version', action='version', - version=f"{sysv_parser.prog} version {__version__}") + version=f"{argv_parser.prog} version {__version__}") # Parse the args from the system - sysv_args = sysv_parser.parse_args(sys.argv[1:]) + argv_args = argv_parser.parse_args(sys.argv[1:]) # Load configuration config = Config( app_name='rsbbs', - args=sysv_args) + args=argv_args) # Init the contoller controller = Controller(config)