simplify parser and config with getattr, fiddle with args
This commit is contained in:
parent
00b2f54381
commit
deae9af4c6
@ -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'},
|
||||
},),]
|
||||
|
||||
@ -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}")
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user