diff --git a/evennia/contrib/tests.py b/evennia/contrib/tests.py index 8bae39410..bbf6d29d4 100644 --- a/evennia/contrib/tests.py +++ b/evennia/contrib/tests.py @@ -947,14 +947,14 @@ class CmdDummy(UnixCommand): """A dummy UnixCommand.""" - def init(self): + key = "dummy" + + def init_parser(self): """Fill out options.""" self.parser.add_argument("nb1", type=int, help="the first number") self.parser.add_argument("nb2", type=int, help="the second number") self.parser.add_argument("-v", "--verbose", action="store_true") - key = "dummy" - def func(self): nb1 = self.opts.nb1 nb2 = self.opts.nb2 diff --git a/evennia/contrib/unixcommand.py b/evennia/contrib/unixcommand.py index dde36207d..cb65011bb 100644 --- a/evennia/contrib/unixcommand.py +++ b/evennia/contrib/unixcommand.py @@ -8,7 +8,7 @@ options. The UnixCommand can be ovverridden to have your commands parsed. You will need to override two methods: -- The `init` method, which adds options to the parser. +- The `init_parser` method, which adds options to the parser. - The `func` method, called to execute the command once parsed. Here's a short example: @@ -30,7 +30,7 @@ class CmdPlant(UnixCommand): key = "plant" - def init(self): + def init_parser(self): "Add the arguments to the parser." # 'self.parser' inherits `argparse.ArgumentParser` self.parser.add_argument("key", @@ -62,6 +62,112 @@ from textwrap import dedent from evennia import Command, InterruptCommand from evennia.utils.ansi import raw +class ParseError(Exception): + + """An error occurred during parsing.""" + + pass + + +class UnixCommandParser(argparse.ArgumentParser): + + """A modifier command parser for unix commands. + + This parser is used to replace `argparse.ArgumentParser`. It + is aware of the command calling it, and can more easily report to + the caller. Some features (like the "brutal exit" of the original + parser) are disabled or replaced. This parser is used by UnixCommand + and creating one directly isn't recommended nor necessary. Even + adding a sub-command will use this replaced parser automatically. + + """ + + def __init__(self, prog, description="", epilogue="", command=None, **kwargs): + prog = prog or command.key + super(UnixCommandParser, self).__init__( + prog=prog, description=description, + conflict_handler='resolve', add_help=False, **kwargs) + self.command = command + self.post_help = epilogue + def n_exit(code=None, msg=None): + raise ParseError(msg) + + self.exit = n_exit + + # Replace the -h/--help + self.add_argument("-h", "--hel", nargs=0, action=HelpAction, + help="display the command help") + + def format_usage(self): + """Return the usage line. + + Note: + This method is present to return the raw-escaped usage line, + in order to avoid unintentional color codes. + + """ + return raw(super(UnixCommandParser, self).format_usage()) + + def format_help(self): + """Return the parser help, including its epilogue. + + Note: + This method is present to return the raw-escaped help, + in order to avoid unintentional color codes. Color codes + in the epilogue (the command docstring) are supported. + + """ + autohelp = raw(super(UnixCommandParser, self).format_help()) + return "\n" + autohelp + "\n" + self.post_help + + def print_usage(self, file=None): + """Print the usage to the caller. + + Args: + file (file-object): not used here, the caller is used. + + Note: + This method will override `argparse.ArgumentParser`'s in order + to not display the help on stdout or stderr, but to the + command's caller. + + """ + if self.command: + self.command.msg(self.format_usage().strip()) + + def print_help(self, file=None): + """Print the help to the caller. + + Args: + file (file-object): not used here, the caller is used. + + Note: + This method will override `argparse.ArgumentParser`'s in order + to not display the help on stdout or stderr, but to the + command's caller. + + """ + if self.command: + self.command.msg(self.format_help().strip()) + + +class HelpAction(argparse.Action): + + """Override the -h/--help action in the default parser. + + Using the default -h/--help will call the exit function in different + ways, preventing the entire help message to be provided. Hence + this override. + + """ + + def __call__(self, parser, namespace, values, option_string=None): + """If asked for help, display to the caller.""" + if parser.command: + parser.command.msg(parser.format_help().strip()) + parser.exit(0, "") + + class UnixCommand(Command): """ Unix-type commands, supporting short and long options. @@ -71,7 +177,7 @@ class UnixCommand(Command): used to parse the command. In order to use it, you should override two methods: - - `init`: the init method is called when the command is created. + - `init_parser`: this method is called when the command is created. It can be used to set options in the parser. `self.parser` contains the `argparse.ArgumentParser`, so you can add arguments here. @@ -84,7 +190,7 @@ class UnixCommand(Command): slightly different way than usual: the first line of the docstring is used to represent the program description (the very short line at the top of the help message). The other lines below are - used as the program's "epilog", displayed below the options. It + used as the program's "epilogue", displayed below the options. It means in your docstring, you don't have to write the options. They will be automatically provided by the parser and displayed accordingly. The `argparse` module provides a default '-h' or @@ -97,16 +203,16 @@ class UnixCommand(Command): def __init__(self, **kwargs): super(UnixCommand, self).__init__() - # Create the empty EvenniaParser, inheriting argparse.ArgumentParser + # Create the empty UnixCommandParser, inheriting argparse.ArgumentParser lines = dedent(self.__doc__.strip("\n")).splitlines() description = lines[0].strip() - epilog = "\n".join(lines[1:]).strip() - self.parser = EvenniaParser(None, description, epilog, command=self) + epilogue = "\n".join(lines[1:]).strip() + self.parser = UnixCommandParser(None, description, epilogue, command=self) # Fill the argument parser - self.init() + self.init_parser() - def init(self): + def init_parser(self): """ Configure the argument parser, adding in options. @@ -143,7 +249,7 @@ class UnixCommand(Command): Note: You should not override this method. Consider overriding - `init` instead. + `init_parser` instead. """ try: @@ -153,59 +259,3 @@ class UnixCommand(Command): if msg: self.msg(msg) raise InterruptCommand - - -class ParseError(Exception): - - """An error occurred during parsing.""" - - pass - - -class EvenniaParser(argparse.ArgumentParser): - - """A parser just for Evennia.""" - - def __init__(self, prog, description="", epilog="", command=None, **kwargs): - prog = prog or command.key - super(EvenniaParser, self).__init__( - prog=prog, description=description, - conflict_handler='resolve', add_help=False, **kwargs) - self.command = command - self.post_help = epilog - def n_exit(code=None, msg=None): - raise ParseError(msg) - - self.exit = n_exit - - # Replace the -h/--help - self.add_argument("-h", "--hel", nargs=0, action=HelpAction, help="display heeeelp") - - def format_usage(self): - """Return the usage line.""" - return raw(super(EvenniaParser, self).format_usage()) - - def format_help(self): - """Return the parser help, including its epilog.""" - autohelp = raw(super(EvenniaParser, self).format_help()) - return "\n" + autohelp + "\n" + self.post_help - - def print_usage(self, file=None): - """Print the usage to the caller.""" - if self.command: - self.command.msg(self.format_usage().strip()) - - def print_help(self, file=None): - """Print the help to the caller.""" - if self.command: - self.command.msg(self.format_help().strip()) - - -class HelpAction(argparse.Action): - - """Override the -h/--he.p.""" - - def __call__(self, parser, namespace, values, option_string=None): - if parser.command: - parser.command.msg(parser.format_help().strip()) - parser.exit(0, "")