Skip to content

Commit

Permalink
Minor improvements in command boostrapping code
Browse files Browse the repository at this point in the history
- Modified argparse building process to handle loader failures (specifically
  caused by a SyntaxError to build a bogus place-holder for the subcommand
  with an invalid '.py' file.  This eliminates the "unknown command" and,
  depending on the args given, may report the correct syntax error, but it may
  just say "unrecognized arguments", but overall it's still a step in the right
  direction.  This is primarily a developer issue.
- Allow pre_run() command to return an exit_code and abort before run() is
  called, without explicitly requiring an exception.
  • Loading branch information
lowell80 committed Jun 13, 2020
1 parent 96f7a18 commit 0bf256d
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 4 deletions.
24 changes: 24 additions & 0 deletions ksconf/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,28 @@ def check_py_sane():
return True


def handle_cmd_failed(subparser, ep):
""" Build a bogus subparser for a cmd that can't be loaded, with the only purpose of providing
a more consistent user experiencee. """
# Not sure how much *better* this is. But it at least it gets away from the dumb stares
# when the subcommand silently disappears. (Visible from ksconf --version, but still...
# It's confusing, even if *just* during development)
marker = "*" * 80
description = "{0}\n*** {1}\n{0}".format(marker, ep.error)
badparser = subparser.add_parser(ep.name, description=description,
help="****** {} ******".format(ep.error),
formatter_class=DescriptionHelpFormatterPreserveLayout)

def handler(args):
sys.stderr.write("Unable to process due to internal error in '{}'\n{}\n"
.format(ep.name, ep.error))
return EXIT_CODE_INTERNAL_ERROR
badparser.set_defaults(funct=handler)
# Consume all remaining args. But if params are passed first sometimes the user still sees
# "unrecognized arguments". A deeper hack is needed to improve beyond this.
badparser.add_argument('args', nargs=argparse.REMAINDER)


def build_cli_parser(do_formatter=False):
parser_kwargs = dict(
fromfile_prefix_chars="@",
Expand Down Expand Up @@ -128,6 +150,8 @@ def build_cli_parser(do_formatter=False):
cmd = ep.cmd_cls(ep.entry.name)
# XXX: Find a better way to handle argparse errors: (TypeError) ex: invalid arguments
cmd.add_parser(subparsers)
elif ep.error:
handle_cmd_failed(subparsers, ep)

for distro_name, items in sorted(subcommands.items()):
if distro_name:
Expand Down
10 changes: 6 additions & 4 deletions ksconf/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,23 +370,25 @@ def register_args(self, parser): # pragma: no cover
def launch(self, args):
""" Handle flow control between pre_run() / run() / post_run() """
# If this fails, exception is passed up, no handling errors/logging done here.
self.pre_run(args)
return_code = self.pre_run(args)
if return_code:
return return_code

exc = None
try:
return_code = self.run(args)
except KsconfCmdReadConfException as e:
return_code = e.returncode
except: # pragma: no cover
except BaseException: # pragma: no cover
exc = sys.exc_info()
raise
finally:
# No matter what, post_run is called.
# No matter what, if run() was called, so is post_run()
self.post_run(args, exc)
return return_code

def pre_run(self, args):
""" Pre-run hook. Any exceptions here prevent run() from being called. """
""" Pre-run hook. Any exceptions or true return code, prevents run()/post_run() from being called. """
pass

def run(self, args): # pragma: no cover
Expand Down

0 comments on commit 0bf256d

Please sign in to comment.