From 1b8a49254fa7af8441e87e203060fc04a92681f8 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 29 Aug 2026 19:39:02 -0400 Subject: [PATCH 1/2] Update typos to 1.50.0 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0510bb303..348a48c05 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,7 +30,7 @@ repos: - prettier-plugin-toml@2.0.6 - repo: https://github.com/crate-ci/typos - rev: v1.49.1 + rev: v1.50.0 hooks: - id: typos exclude: | From c90f45d398e097834824d54f618baee64851433d Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 29 Aug 2026 19:49:57 -0400 Subject: [PATCH 2/2] Upgrade ruff to 0.15.6 This new version of ruff now auto-formats Python code blocks in Markdown files. The main changes applies are whitespace and converting single quotes to double quotes in our Python code blocks in the Markdown documentation. I had to add a couple Markdown comments to turn formatting off and back on in some limited sections that use extra syntax for snippet inclusion that isn't supported by generic Markdown. --- .pre-commit-config.yaml | 2 +- README.md | 6 +- docs/examples/alternate_event_loops.md | 4 +- docs/examples/getting_started.md | 32 ++++--- docs/features/annotated.md | 55 ++++++++--- docs/features/argument_processing.md | 61 ++++++------ docs/features/async_commands.md | 7 +- docs/features/builtin_commands.md | 2 +- docs/features/commands.md | 10 +- docs/features/disable_commands.md | 23 ++--- docs/features/embedded_python_shells.md | 4 + docs/features/help.md | 30 +++--- docs/features/hooks.md | 4 +- docs/features/initialization.md | 2 + docs/features/misc.md | 6 +- docs/features/modular_commands.md | 109 ++++++++++++---------- docs/features/plugins.md | 8 +- docs/features/prompt.md | 17 ++-- docs/features/redirection.md | 2 + docs/features/scripting.md | 63 +++++++------ docs/features/settings.md | 2 + docs/features/shortcuts_aliases_macros.md | 2 +- docs/features/startup_commands.md | 6 +- docs/mixins/mixin_template.md | 8 +- docs/testing.md | 8 +- 25 files changed, 275 insertions(+), 198 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 348a48c05..0314693cc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.16.4" + rev: "v0.16.5" hooks: - id: ruff-format args: [--config=ruff.toml] diff --git a/README.md b/README.md index 18b569969..780d41b2e 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ examples. ```python #!/usr/bin/env python """A simple cmd2 application.""" + import cmd2 @@ -128,15 +129,14 @@ class FirstApp(cmd2.Cmd): """A simple cmd2 application.""" def do_hello_world(self, _: cmd2.Statement): - self.poutput('Hello World') + self.poutput("Hello World") -if __name__ == '__main__': +if __name__ == "__main__": import sys c = FirstApp() sys.exit(c.cmdloop()) - ``` ## Found a bug? diff --git a/docs/examples/alternate_event_loops.md b/docs/examples/alternate_event_loops.md index 0dbe1f01d..7fdc3b66e 100644 --- a/docs/examples/alternate_event_loops.md +++ b/docs/examples/alternate_event_loops.md @@ -36,13 +36,15 @@ by using code like the following: ```py import cmd2 + class Cmd2EventBased(cmd2.Cmd): def __init__(self): cmd2.Cmd.__init__(self) # ... your class code here ... -if __name__ == '__main__': + +if __name__ == "__main__": app = Cmd2EventBased() app.preloop() diff --git a/docs/examples/getting_started.md b/docs/examples/getting_started.md index 070158891..3c4c14163 100644 --- a/docs/examples/getting_started.md +++ b/docs/examples/getting_started.md @@ -19,9 +19,11 @@ click the **Copy** button in the top-right): !!! example "getting_started.py" + ```py --8<-- "examples/getting_started.py" ``` + ## Basic Application @@ -31,6 +33,7 @@ following contents: ```py #!/usr/bin/env python """A basic cmd2 application.""" + import cmd2 @@ -38,8 +41,9 @@ class BasicApp(cmd2.Cmd): """Cmd2 application to demonstrate many common features.""" -if __name__ == '__main__': +if __name__ == "__main__": import sys + app = BasicApp() sys.exit(app.cmdloop()) ``` @@ -69,7 +73,7 @@ def __init__(self): # Make maxrepeats settable at runtime self.maxrepeats = 3 - self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self)) + self.add_settable(cmd2.Settable("maxrepeats", int, "max repetitions for speak command", self)) ``` In that initializer, the first thing to do is to make sure we initialize `cmd2`. That's what the @@ -94,10 +98,11 @@ that the `speak_parser` attribute and the `do_speak()` method are part of the `B ```py speak_parser = cmd2.Cmd2ArgumentParser() -speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') -speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') -speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') -speak_parser.add_argument('words', nargs='+', help='words to say') +speak_parser.add_argument("-p", "--piglatin", action="store_true", help="atinLay") +speak_parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE") +speak_parser.add_argument("-r", "--repeat", type=int, help="output [n] times") +speak_parser.add_argument("words", nargs="+", help="words to say") + @cmd2.with_argparser(speak_parser) def do_speak(self, args): @@ -105,14 +110,14 @@ def do_speak(self, args): words = [] for word in args.words: if args.piglatin: - word = '%s%say' % (word[1:], word[0]) + word = "%s%say" % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for _ in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too - self.poutput(' '.join(words)) + self.poutput(" ".join(words)) ``` Up at the top of the script, you'll also need to add: @@ -186,12 +191,12 @@ Let's add a shortcut for our `speak` command. Change the `__init__()` method so ```py def __init__(self): shortcuts = cmd2.DEFAULT_SHORTCUTS - shortcuts.update({'&': 'speak'}) + shortcuts.update({"&": "speak"}) super().__init__(shortcuts=shortcuts) # Make maxrepeats settable at runtime self.maxrepeats = 3 - self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self)) + self.add_settable(cmd2.Settable("maxrepeats", int, "max repetitions for speak command", self)) ``` Shortcuts are passed to the `cmd2` initializer, and if you want the built-in shortcuts of `cmd2` you @@ -224,14 +229,15 @@ def do_speak(self, args): words = [] for word in args.words: if args.piglatin: - word = '%s%say' % (word[1:], word[0]) + word = "%s%say" % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for _ in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too - self.poutput(' '.join(words)) + self.poutput(" ".join(words)) + # orate is a synonym for speak which takes multiline input do_orate = do_speak @@ -241,7 +247,7 @@ With the new command created, we need to tell `cmd2` to treat that command as a Modify the super initialization line to look like this: ```py -super().__init__(multiline_commands=['orate'], shortcuts=shortcuts) +super().__init__(multiline_commands=["orate"], shortcuts=shortcuts) ``` Now when you run the example, you can type something like this: diff --git a/docs/features/annotated.md b/docs/features/annotated.md index 98d087a97..ec2f790aa 100644 --- a/docs/features/annotated.md +++ b/docs/features/annotated.md @@ -31,9 +31,10 @@ The two decorators are interchangeable -- here is the same command written both ```py parser = Cmd2ArgumentParser() - parser.add_argument('name', help='person to greet') - parser.add_argument('--count', type=int, default=1, help='repetitions') - parser.add_argument('--loud', action='store_true', help='shout') + parser.add_argument("name", help="person to greet") + parser.add_argument("--count", type=int, default=1, help="repetitions") + parser.add_argument("--loud", action="store_true", help="shout") + @with_argparser(parser) def do_greet(self, args): @@ -61,6 +62,7 @@ Underscores in parameter names are converted to dashes in the generated flag, so ```py from cmd2.annotated import with_annotated + class MyApp(cmd2.Cmd): @with_annotated def do_greet(self, name: str, count: int = 1, loud: bool = False): @@ -132,6 +134,7 @@ For finer control, use `typing.Annotated` with [Argument][cmd2.annotated.Argumen from typing import Annotated from cmd2.annotated import Argument, Option, with_annotated + class MyApp(cmd2.Cmd): def sport_choices(self) -> cmd2.Choices: return cmd2.Choices.from_values(["football", "basketball"]) @@ -139,15 +142,22 @@ class MyApp(cmd2.Cmd): @with_annotated def do_play( self, - sport: Annotated[str, Argument( - choices_provider=sport_choices, - help_text="Sport to play", - )], - venue: Annotated[str, Option( - "--venue", "-v", - help_text="Where to play", - completer=cmd2.Cmd.path_complete, - )] = "home", + sport: Annotated[ + str, + Argument( + choices_provider=sport_choices, + help_text="Sport to play", + ), + ], + venue: Annotated[ + str, + Option( + "--venue", + "-v", + help_text="Where to play", + completer=cmd2.Cmd.path_complete, + ), + ] = "home", ): self.poutput(f"Playing {sport} at {venue}") ``` @@ -173,6 +183,7 @@ import enum from typing import Annotated from cmd2.annotated import Argument, with_annotated + class Color(enum.Enum): red = "red" green = "green" @@ -183,6 +194,7 @@ class Color(enum.Enum): # map a special keyword onto a real member; return None to reject return cls.red if str(value).lower() == "auto" else None + class MyApp(cmd2.Cmd): @with_annotated def do_theme(self, choice: Annotated[Color, Argument(allow_unknown_entry=True)]) -> None: @@ -265,6 +277,7 @@ class UpperAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): setattr(namespace, self.dest, values.upper()) + @with_annotated def do_shout(self, name: Annotated[str, Option("--name", action=UpperAction)] = ""): self.poutput(name) @@ -298,6 +311,7 @@ forms are equivalent: # Signature default def do_x(self, name: Annotated[str, Option("--name")] = "HI"): ... + # Metadata default (same behaviour) def do_x(self, name: Annotated[str, Option("--name", default="HI")]): ... ``` @@ -359,11 +373,13 @@ import datetime from typing import Annotated from cmd2.annotated import Argument, Option, with_annotated + def parse_size(value: str) -> int: """Parse an integer with an optional K/M/G suffix.""" multiplier = {"K": 1_000, "M": 1_000_000, "G": 1_000_000_000}.get(value[-1:].upper(), 1) return int(value[:-1] if multiplier != 1 else value) * multiplier + class MyApp(cmd2.Cmd): @with_annotated def do_alloc(self, size: Annotated[int, Argument(converter=parse_size)]) -> None: @@ -382,9 +398,11 @@ instead infer `nargs` and split the input across several tokens: ```py from typing import Annotated, Any + def parse_intset(value: str) -> set[int]: return {int(piece) for piece in value.split(",")} + @with_annotated def do_select(self, idx: Annotated[Any, Option("--idx", converter=parse_intset)]) -> None: self.poutput(sorted(idx)) # `select --idx 1,3,5` -> [1, 3, 5] @@ -404,6 +422,7 @@ import os from typing import Annotated from cmd2.annotated import Argument, with_annotated + class MyApp(cmd2.Cmd): @with_annotated def do_tag(self, color: Annotated[Color, Argument(preprocess=str.lower)]) -> None: @@ -486,6 +505,7 @@ and `description` for a titled help section (omit them for an untitled group): ```py from cmd2.annotated import Group, with_annotated + class App(cmd2.Cmd): @with_annotated( description="Open a network connection.", @@ -509,6 +529,8 @@ def do_greet(self, name: str): :param name: who to greet """ self.poutput(f"hello {name}") + + # parser.description == "Greet someone by name." ``` @@ -532,9 +554,7 @@ choice reads as `[--json | --csv]` instead of expanding to `--json`/`--no-json` ```py @with_annotated( - mutually_exclusive_groups=( - Group("json", "csv", title="output", description="how to write results"), - ), + mutually_exclusive_groups=(Group("json", "csv", title="output", description="how to write results"),), ) def do_render( self, @@ -560,6 +580,7 @@ class Conn(ArgumentBlock): host: Annotated[str, Option("--host")] = "localhost" port: Annotated[int, Option("--port")] = 8080 + @with_annotated(groups=(Group("host", "port", title="connection"),)) def do_connect(self, conn: Conn) -> None: ... ``` @@ -611,6 +632,7 @@ def do_manage(self, *, cmd2_subcommand_func): if cmd2_subcommand_func: cmd2_subcommand_func() + @with_annotated(subcommand_to="manage", help="list projects") def manage_list(self): self.poutput("listing") @@ -626,6 +648,7 @@ def manage_project(self, *, cmd2_subcommand_func): if cmd2_subcommand_func: cmd2_subcommand_func() + @with_annotated(subcommand_to="manage project", help="add a project") def manage_project_add(self, name: str): self.poutput(f"added {name}") @@ -770,9 +793,11 @@ decorator, it skips the first parameter as the method receiver (`self`/`cls`). ```py from cmd2.annotated import build_parser_from_function + def greet(self, name: str, count: int = 1): """Greet someone.""" + parser = build_parser_from_function(greet) namespace = parser.parse_args(["Alice", "--count", "3"]) # namespace.name == "Alice", namespace.count == 3 diff --git a/docs/features/argument_processing.md b/docs/features/argument_processing.md index aeadf7c8d..145965b15 100644 --- a/docs/features/argument_processing.md +++ b/docs/features/argument_processing.md @@ -81,17 +81,18 @@ Here's what it looks like: from cmd2 import Cmd2ArgumentParser, with_argparser argparser = Cmd2ArgumentParser() -argparser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') -argparser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') -argparser.add_argument('-r', '--repeat', type=int, help='output [n] times') -argparser.add_argument('word', nargs='?', help='word to say') +argparser.add_argument("-p", "--piglatin", action="store_true", help="atinLay") +argparser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE") +argparser.add_argument("-r", "--repeat", type=int, help="output [n] times") +argparser.add_argument("word", nargs="?", help="word to say") + @with_argparser(argparser) def do_speak(self, opts): """Repeats what you tell me to.""" arg = opts.word if opts.piglatin: - arg = '%s%say' % (arg[1:], arg[0]) + arg = "%s%say" % (arg[1:], arg[0]) if opts.shout: arg = arg.upper() repetitions = opts.repeat or 1 @@ -125,13 +126,15 @@ With this code: from cmd2 import Cmd2ArgumentParser, with_argparser argparser = Cmd2ArgumentParser() -argparser.add_argument('tag', help='tag') -argparser.add_argument('content', nargs='+', help='content to surround with tag') +argparser.add_argument("tag", help="tag") +argparser.add_argument("content", nargs="+", help="content to surround with tag") + + @with_argparser(argparser) def do_tag(self, args): """Create an HTML tag""" - self.stdout.write('<{0}>{1}'.format(args.tag, ' '.join(args.content))) - self.stdout.write('\n') + self.stdout.write("<{0}>{1}".format(args.tag, " ".join(args.content))) + self.stdout.write("\n") ``` the `help tag` command displays: @@ -155,13 +158,15 @@ leave the docstring on your method blank: ```py from cmd2 import Cmd2ArgumentParser, with_argparser -argparser = Cmd2ArgumentParser(description='create an HTML tag') -argparser.add_argument('tag', help='tag') -argparser.add_argument('content', nargs='+', help='content to surround with tag') +argparser = Cmd2ArgumentParser(description="create an HTML tag") +argparser.add_argument("tag", help="tag") +argparser.add_argument("content", nargs="+", help="content to surround with tag") + + @with_argparser(argparser) def do_tag(self, args): - self.stdout.write('<{0}>{1}'.format(args.tag, ' '.join(args.content))) - self.stdout.write('\n') + self.stdout.write("<{0}>{1}".format(args.tag, " ".join(args.content))) + self.stdout.write("\n") ``` Now when the user enters `help tag` they see: @@ -184,14 +189,17 @@ To add additional text to the end of the generated help message, use the `epilog ```py from cmd2 import Cmd2ArgumentParser, with_argparser -argparser = Cmd2ArgumentParser(description='create an HTML tag', - epilog='This command cannot generate tags with no content, like
.') -argparser.add_argument('tag', help='tag') -argparser.add_argument('content', nargs='+', help='content to surround with tag') +argparser = Cmd2ArgumentParser( + description="create an HTML tag", epilog="This command cannot generate tags with no content, like
." +) +argparser.add_argument("tag", help="tag") +argparser.add_argument("content", nargs="+", help="content to surround with tag") + + @with_argparser(argparser) def do_tag(self, args): - self.stdout.write('<{0}>{1}'.format(args.tag, ' '.join(args.content))) - self.stdout.write('\n') + self.stdout.write("<{0}>{1}".format(args.tag, " ".join(args.content))) + self.stdout.write("\n") ``` Which yields: @@ -248,7 +256,7 @@ additional attributes that may be helpful, including `arg_list` and `argv`: ```py class CmdLineApp(cmd2.Cmd): - """ Example cmd2 application. """ + """Example cmd2 application.""" def do_say(self, statement): # statement contains a string @@ -276,8 +284,9 @@ methods that should receive an argument list instead of a string: ```py from cmd2 import with_argument_list + class CmdLineApp(cmd2.Cmd): - """ Example cmd2 application. """ + """Example cmd2 application.""" def do_say(self, cmdline): # cmdline contains a string @@ -300,8 +309,8 @@ Here's what it looks like: from cmd2 import Cmd2ArgumentParser, with_argparser dir_parser = Cmd2ArgumentParser() -dir_parser.add_argument('-l', '--long', action='store_true', - help="display in long format with one item per line") +dir_parser.add_argument("-l", "--long", action="store_true", help="display in long format with one item per line") + @with_argparser(dir_parser, with_unknown_args=True) def do_dir(self, args, unknown): @@ -309,8 +318,8 @@ def do_dir(self, args, unknown): # No arguments for this command if unknown: self.perror("dir does not take any positional arguments:") - self.do_help('dir') - self.last_result = 'Bad arguments' + self.do_help("dir") + self.last_result = "Bad arguments" return # Get the contents as a list diff --git a/docs/features/async_commands.md b/docs/features/async_commands.md index c8430d4ae..0cc5915ba 100644 --- a/docs/features/async_commands.md +++ b/docs/features/async_commands.md @@ -32,6 +32,7 @@ import cmd2 _event_loop = None _event_lock = threading.Lock() + def _get_event_loop() -> asyncio.AbstractEventLoop: """Get or create the background event loop.""" global _event_loop @@ -42,22 +43,26 @@ def _get_event_loop() -> asyncio.AbstractEventLoop: _event_loop = asyncio.new_event_loop() thread = threading.Thread( target=_event_loop.run_forever, - name='Async Runner', + name="Async Runner", daemon=True, ) thread.start() return _event_loop + def with_async_loop(func: Callable[..., Any]) -> Callable[..., Any]: """Decorator to run a command method asynchronously in a background thread.""" + @functools.wraps(func) def wrapper(self: cmd2.Cmd, *args: Any, **kwargs: Any) -> Any: loop = _get_event_loop() coro = func(self, *args, **kwargs) future = asyncio.run_coroutine_threadsafe(coro, loop) return future.result() + return wrapper + class AsyncApp(cmd2.Cmd): @with_async_loop async def do_my_async(self, _: cmd2.Statement) -> None: diff --git a/docs/features/builtin_commands.md b/docs/features/builtin_commands.md index 8957a1f0f..f2751f8ae 100644 --- a/docs/features/builtin_commands.md +++ b/docs/features/builtin_commands.md @@ -125,5 +125,5 @@ from your application: class NoShellApp(cmd2.Cmd): """A simple cmd2 application.""" - delattr(cmd2.Cmd, 'do_shell') + delattr(cmd2.Cmd, "do_shell") ``` diff --git a/docs/features/commands.md b/docs/features/commands.md index 21a7864a9..df46f1f1a 100644 --- a/docs/features/commands.md +++ b/docs/features/commands.md @@ -15,6 +15,7 @@ The simplest `cmd2` application looks like this: ```py #!/usr/bin/env python """A simple cmd2 application.""" + import cmd2 @@ -22,8 +23,9 @@ class App(cmd2.Cmd): """A simple cmd2 application.""" -if __name__ == '__main__': +if __name__ == "__main__": import sys + c = App() sys.exit(c.cmdloop()) ``` @@ -142,20 +144,24 @@ You can use this capability to easily return your own values to the operating sy ```py #!/usr/bin/env python """A simple cmd2 application.""" + import cmd2 class App(cmd2.Cmd): """A simple cmd2 application.""" + def do_bail(self, line): """Exit the application""" self.perror("fatal error, exiting") self.exit_code = 2 return True -if __name__ == '__main__': + +if __name__ == "__main__": import sys + c = App() sys.exit(c.cmdloop()) ``` diff --git a/docs/features/disable_commands.md b/docs/features/disable_commands.md index 1a22f6541..37e1dae50 100644 --- a/docs/features/disable_commands.md +++ b/docs/features/disable_commands.md @@ -40,22 +40,23 @@ the `hidden_commands` list: ```py class HiddenCommands(cmd2.Cmd): """An app which demonstrates how to hide a command""" + def __init__(self): super().__init__() - self.hidden_commands.append('py') + self.hidden_commands.append("py") ``` As shown above, you would typically do this as part of initializing your application. If you decide you want to unhide a command later in the execution of your application, you can by doing: ```py -self.hidden_commands = [cmd for cmd in self.hidden_commands if cmd != 'py'] +self.hidden_commands = [cmd for cmd in self.hidden_commands if cmd != "py"] ``` You might be thinking that the list comprehension is overkill and you'd rather do something like: ```py -self.hidden_commands.remove('py') +self.hidden_commands.remove("py") ``` You may be right, but `remove()` will raise a `ValueError` if `py` isn't in the list, and it will @@ -75,16 +76,16 @@ class DisabledCommands(cmd2.Cmd): """An application which disables and enables commands""" def do_lock(self, line): - self.disable_command('open', "you can't open the door because it is locked") - self.poutput('the door is locked') + self.disable_command("open", "you can't open the door because it is locked") + self.poutput("the door is locked") def do_unlock(self, line): - self.enable_command('open') - self.poutput('the door is unlocked') + self.enable_command("open") + self.poutput("the door is unlocked") def do_open(self, line): """open the door""" - self.poutput('opening the door') + self.poutput("opening the door") ``` This method has the added benefit of removing disabled commands from the help menu. But, this method @@ -99,14 +100,14 @@ all the commands in a category with a single method call. Say you have created a commands called "Server Information". You can disable all commands in that category: ```py -not_connected_msg = 'You must be connected to use this command' -self.disable_category('Server Information', not_connected_msg) +not_connected_msg = "You must be connected to use this command" +self.disable_category("Server Information", not_connected_msg) ``` Similarly, you can re-enable all the commands in a category: ```py -self.enable_category('Server Information') +self.enable_category("Server Information") ``` See [help_categories.py](https://github.com/python-cmd2/cmd2/blob/main/examples/help_categories.py) diff --git a/docs/features/embedded_python_shells.md b/docs/features/embedded_python_shells.md index 0a4b60f42..8d693a3fe 100644 --- a/docs/features/embedded_python_shells.md +++ b/docs/features/embedded_python_shells.md @@ -7,6 +7,8 @@ will be present and run an interactive Python shell: ```py from cmd2 import Cmd + + class App(Cmd): def __init__(self): Cmd.__init__(self, include_py=True) @@ -59,6 +61,8 @@ interactive IPython shell: ```py from cmd2 import Cmd + + class App(Cmd): def __init__(self): Cmd.__init__(self, include_ipy=True) diff --git a/docs/features/help.md b/docs/features/help.md index 11150ee46..8dee6fba5 100644 --- a/docs/features/help.md +++ b/docs/features/help.md @@ -70,7 +70,7 @@ By default, `cmd2.Cmd` defines its `DEFAULT_CATEGORY` as `"Cmd2 Commands"`. ```py class MyApp(cmd2.Cmd): # All commands defined in this class will be grouped here - DEFAULT_CATEGORY = 'Application Commands' + DEFAULT_CATEGORY = "Application Commands" def do_echo(self, arg): """Echo command""" @@ -81,11 +81,11 @@ This also works for [Command Sets](./modular_commands.md): ```py class Plugin(cmd2.CommandSet): - DEFAULT_CATEGORY = 'Plugin Commands' + DEFAULT_CATEGORY = "Plugin Commands" def do_plugin_cmd(self, _): """Plugin command""" - self._cmd.poutput('Plugin') + self._cmd.poutput("Plugin") ``` When using inheritance, `cmd2` uses the `DEFAULT_CATEGORY` of the class where the command was @@ -98,10 +98,10 @@ If you want to rename the built-in category itself, you can do so by reassigning ```py class MyApp(cmd2.Cmd): # Rename the framework's built-in category - cmd2.Cmd.DEFAULT_CATEGORY = 'Shell Commands' + cmd2.Cmd.DEFAULT_CATEGORY = "Shell Commands" # Set the category for your own commands - DEFAULT_CATEGORY = 'Application Commands' + DEFAULT_CATEGORY = "Application Commands" ``` For a complete demonstration of this functionality, see the @@ -117,10 +117,10 @@ precedence over the `DEFAULT_CATEGORY`. Using the `@with_category` decorator: ```py -@with_category('Connecting') +@with_category("Connecting") def do_which(self, _): """Which command""" - self.poutput('Which') + self.poutput("Which") ``` Using the `categorize()` function: @@ -130,7 +130,8 @@ You can call with a single function: ```py def do_connect(self, _): """Connect command""" - self.poutput('Connect') + self.poutput("Connect") + # Tag the above command functions under the category Connecting categorize(do_connect, CMD_CAT_CONNECTING) @@ -141,20 +142,21 @@ Or with an Iterable container of functions: ```py def do_undeploy(self, _): """Undeploy command""" - self.poutput('Undeploy') + self.poutput("Undeploy") + def do_stop(self, _): """Stop command""" - self.poutput('Stop') + self.poutput("Stop") + def do_findleakers(self, _): """Find Leakers command""" - self.poutput('Find Leakers') + self.poutput("Find Leakers") + # Tag the above command functions under the category Application Management -categorize((do_undeploy, - do_stop, - do_findleakers), CMD_CAT_APP_MGMT) +categorize((do_undeploy, do_stop, do_findleakers), CMD_CAT_APP_MGMT) ``` The `help` command also has a verbose option (`help -v` or `help --verbose`) that combines the help diff --git a/docs/features/hooks.md b/docs/features/hooks.md index 6fa59bb38..a34f70b98 100644 --- a/docs/features/hooks.md +++ b/docs/features/hooks.md @@ -172,8 +172,8 @@ simple example which shows the proper technique: ```py def myhookmethod(self, params: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: - if not '|' in params.statement.raw: - newinput = params.statement.raw + ' | less' + if not "|" in params.statement.raw: + newinput = params.statement.raw + " | less" params.statement = self.statement_parser.parse(newinput) return params ``` diff --git a/docs/features/initialization.md b/docs/features/initialization.md index ea93b2d78..fc084ff3d 100644 --- a/docs/features/initialization.md +++ b/docs/features/initialization.md @@ -4,9 +4,11 @@ Here is a basic example `cmd2` application which demonstrates many capabilities !!! example "examples/getting_started.py" + ```py --8<-- "examples/getting_started.py" ``` + ## Cmd class initializer diff --git a/docs/features/misc.md b/docs/features/misc.md index 7e5fa9628..a8fe7f723 100644 --- a/docs/features/misc.md +++ b/docs/features/misc.md @@ -20,10 +20,10 @@ Presents numbered options to user, as bash `select`. ```py def do_eat(self, arg): - sauce = self.select('sweet salty', 'Sauce? ') - result = '{food} with {sauce} sauce, yum!' + sauce = self.select("sweet salty", "Sauce? ") + result = "{food} with {sauce} sauce, yum!" result = result.format(food=arg, sauce=sauce) - self.stdout.write(result + '\n') + self.stdout.write(result + "\n") ``` ```text diff --git a/docs/features/modular_commands.md b/docs/features/modular_commands.md index dbd9c5d86..28c775eef 100644 --- a/docs/features/modular_commands.md +++ b/docs/features/modular_commands.md @@ -52,30 +52,33 @@ initializer arguments, see [Manual CommandSet Construction](#manual-commandset-c import cmd2 from cmd2 import CommandSet + class ExampleApp(cmd2.Cmd): """ CommandSets are automatically loaded. Nothing needs to be done. """ + def __init__(self, *args, **kwargs): super().__init__(*args, auto_load_commands=True, **kwargs) def do_something(self, arg): """Something Command.""" - self.poutput('this is the something command') + self.poutput("this is the something command") + class AutoLoadCommandSet(CommandSet[ExampleApp]): - DEFAULT_CATEGORY = 'My Category' + DEFAULT_CATEGORY = "My Category" def __init__(self): super().__init__() def do_hello(self, _: cmd2.Statement): """Hello Command.""" - self._cmd.poutput('Hello') + self._cmd.poutput("Hello") def do_world(self, _: cmd2.Statement): """World Command.""" - self._cmd.poutput('World') + self._cmd.poutput("World") ``` ### Manual CommandSet Construction @@ -87,10 +90,12 @@ construct CommandSets and pass in the initializer to Cmd2. import cmd2 from cmd2 import CommandSet + class ExampleApp(cmd2.Cmd): """ CommandSets with initializer parameters are provided in the initializer """ + def __init__(self, *args, **kwargs): # gotta have this or neither the plugin or cmd2 will initialize super().__init__(*args, auto_load_commands=True, **kwargs) @@ -98,10 +103,11 @@ class ExampleApp(cmd2.Cmd): def do_something(self, arg): """Something Command.""" self.last_result = 5 - self.poutput('this is the something command') + self.poutput("this is the something command") + class CustomInitCommandSet(CommandSet[ExampleApp]): - DEFAULT_CATEGORY = 'My Category' + DEFAULT_CATEGORY = "My Category" def __init__(self, arg1, arg2): super().__init__() @@ -111,11 +117,11 @@ class CustomInitCommandSet(CommandSet[ExampleApp]): def do_show_arg1(self, _: cmd2.Statement): """Show Arg 1.""" - self._cmd.poutput(f'Arg1: {self._arg1}') + self._cmd.poutput(f"Arg1: {self._arg1}") def do_show_arg2(self, _: cmd2.Statement): """Show Arg 2.""" - self._cmd.poutput(f'Arg2: {self._arg2}') + self._cmd.poutput(f"Arg2: {self._arg2}") def main(): @@ -139,11 +145,13 @@ validation when accessing custom attributes or methods on your main application import cmd2 from cmd2 import CommandSet + class MyApp(cmd2.Cmd): def __init__(self): super().__init__() self.custom_state = "Some important data" + class MyCommands(CommandSet[MyApp]): def do_check_state(self, _: cmd2.Statement): # Type checkers know self._cmd is an instance of MyApp @@ -165,33 +173,33 @@ from cmd2 import CommandSet, with_argparser, with_category class LoadableFruits(CommandSet["ExampleApp"]): - DEFAULT_CATEGORY = 'Fruits' + DEFAULT_CATEGORY = "Fruits" def __init__(self): super().__init__() def do_apple(self, _: cmd2.Statement): """Apple Command.""" - self._cmd.poutput('Apple') + self._cmd.poutput("Apple") def do_banana(self, _: cmd2.Statement): """Banana Command.""" - self._cmd.poutput('Banana') + self._cmd.poutput("Banana") class LoadableVegetables(CommandSet["ExampleApp"]): - DEFAULT_CATEGORY = 'Vegetables' + DEFAULT_CATEGORY = "Vegetables" def __init__(self): super().__init__() def do_arugula(self, _: cmd2.Statement): """Arugula Command.""" - self._cmd.poutput('Arugula') + self._cmd.poutput("Arugula") def do_bokchoy(self, _: cmd2.Statement): """Bok Choy Command.""" - self._cmd.poutput('Bok Choy') + self._cmd.poutput("Bok Choy") class ExampleApp(cmd2.Cmd): @@ -207,39 +215,39 @@ class ExampleApp(cmd2.Cmd): self._vegetables = LoadableVegetables() load_parser = cmd2.Cmd2ArgumentParser() - load_parser.add_argument('cmds', choices=['fruits', 'vegetables']) + load_parser.add_argument("cmds", choices=["fruits", "vegetables"]) @with_argparser(load_parser) - @with_category('Command Loading') + @with_category("Command Loading") def do_load(self, ns: argparse.Namespace): """Load Command.""" - if ns.cmds == 'fruits': + if ns.cmds == "fruits": try: self.register_command_set(self._fruits) - self.poutput('Fruits loaded') + self.poutput("Fruits loaded") except ValueError: - self.poutput('Fruits already loaded') + self.poutput("Fruits already loaded") - if ns.cmds == 'vegetables': + if ns.cmds == "vegetables": try: self.register_command_set(self._vegetables) - self.poutput('Vegetables loaded') + self.poutput("Vegetables loaded") except ValueError: - self.poutput('Vegetables already loaded') + self.poutput("Vegetables already loaded") @with_argparser(load_parser) def do_unload(self, ns: argparse.Namespace): """Unload Command.""" - if ns.cmds == 'fruits': + if ns.cmds == "fruits": self.unregister_command_set(self._fruits) - self.poutput('Fruits unloaded') + self.poutput("Fruits unloaded") - if ns.cmds == 'vegetables': + if ns.cmds == "vegetables": self.unregister_command_set(self._vegetables) - self.poutput('Vegetables unloaded') + self.poutput("Vegetables unloaded") -if __name__ == '__main__': +if __name__ == "__main__": app = ExampleApp() app.cmdloop() ``` @@ -296,41 +304,41 @@ from cmd2 import CommandSet, with_argparser, with_category class LoadableFruits(CommandSet["ExampleApp"]): - DEFAULT_CATEGORY = 'Fruits' + DEFAULT_CATEGORY = "Fruits" def __init__(self): super().__init__() def do_apple(self, _: cmd2.Statement): """Apple Command.""" - self._cmd.poutput('Apple') + self._cmd.poutput("Apple") banana_parser = cmd2.Cmd2ArgumentParser() - banana_parser.add_argument('direction', choices=['discs', 'lengthwise']) + banana_parser.add_argument("direction", choices=["discs", "lengthwise"]) - @cmd2.as_subcommand_to('cut', 'banana', banana_parser) + @cmd2.as_subcommand_to("cut", "banana", banana_parser) def cut_banana(self, ns: argparse.Namespace): """Cut banana""" - self._cmd.poutput('cutting banana: ' + ns.direction) + self._cmd.poutput("cutting banana: " + ns.direction) class LoadableVegetables(CommandSet["ExampleApp"]): - DEFAULT_CATEGORY = 'Vegetables' + DEFAULT_CATEGORY = "Vegetables" def __init__(self): super().__init__() def do_arugula(self, _: cmd2.Statement): """Arugula Command.""" - self._cmd.poutput('Arugula') + self._cmd.poutput("Arugula") bokchoy_parser = cmd2.Cmd2ArgumentParser() - bokchoy_parser.add_argument('style', choices=['quartered', 'diced']) + bokchoy_parser.add_argument("style", choices=["quartered", "diced"]) - @cmd2.as_subcommand_to('cut', 'bokchoy', bokchoy_parser) + @cmd2.as_subcommand_to("cut", "bokchoy", bokchoy_parser) def cut_bokchoy(self, _: argparse.Namespace): """Cut bok choy.""" - self._cmd.poutput('Bok Choy') + self._cmd.poutput("Bok Choy") class ExampleApp(cmd2.Cmd): @@ -346,36 +354,36 @@ class ExampleApp(cmd2.Cmd): self._vegetables = LoadableVegetables() load_parser = cmd2.Cmd2ArgumentParser() - load_parser.add_argument('cmds', choices=['fruits', 'vegetables']) + load_parser.add_argument("cmds", choices=["fruits", "vegetables"]) @with_argparser(load_parser) - @with_category('Command Loading') + @with_category("Command Loading") def do_load(self, ns: argparse.Namespace): """Load Command.""" - if ns.cmds == 'fruits': + if ns.cmds == "fruits": try: self.register_command_set(self._fruits) - self.poutput('Fruits loaded') + self.poutput("Fruits loaded") except ValueError: - self.poutput('Fruits already loaded') + self.poutput("Fruits already loaded") - if ns.cmds == 'vegetables': + if ns.cmds == "vegetables": try: self.register_command_set(self._vegetables) - self.poutput('Vegetables loaded') + self.poutput("Vegetables loaded") except ValueError: - self.poutput('Vegetables already loaded') + self.poutput("Vegetables already loaded") @with_argparser(load_parser) def do_unload(self, ns: argparse.Namespace): """Unload Command.""" - if ns.cmds == 'fruits': + if ns.cmds == "fruits": self.unregister_command_set(self._fruits) - self.poutput('Fruits unloaded') + self.poutput("Fruits unloaded") - if ns.cmds == 'vegetables': + if ns.cmds == "vegetables": self.unregister_command_set(self._vegetables) - self.poutput('Vegetables unloaded') + self.poutput("Vegetables unloaded") cut_parser = cmd2.Cmd2ArgumentParser() cut_parser.add_subparsers(title="item", help="item to cut", metavar="ITEM", required=True) @@ -387,8 +395,7 @@ class ExampleApp(cmd2.Cmd): ns.cmd2_subcommand_func(ns) - -if __name__ == '__main__': +if __name__ == "__main__": app = ExampleApp() app.cmdloop() ``` diff --git a/docs/features/plugins.md b/docs/features/plugins.md index 62fc9ab22..0c9bec76d 100644 --- a/docs/features/plugins.md +++ b/docs/features/plugins.md @@ -28,8 +28,10 @@ and an example app which uses the plugin: import cmd2 import cmd2_myplugin + class Example(cmd2_myplugin.MyPlugin, cmd2.Cmd): """An class to show how to use a plugin""" + def __init__(self, *args, **kwargs): # code placed here runs before cmd2.Cmd or # any plugins initialize @@ -75,8 +77,8 @@ class MyPlugin: # code placed here runs before cmd2.Cmd initializes super().__init__(*args, **kwargs) # code placed here runs after cmd2.Cmd initializes - self.mysetting = 'somevalue' - self.add_settable(cmd2.Settable('mysetting', str, 'short help message for mysetting', self)) + self.mysetting = "somevalue" + self.add_settable(cmd2.Settable("mysetting", str, "short help message for mysetting", self)) ``` You can hide settings from the user by calling [cmd2.Cmd.remove_settable][]. See @@ -119,7 +121,7 @@ class MyPlugin: def cmd2_myplugin_postparsing_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: """Method to be called after parsing user input, but before running the command""" - self.poutput('in postparsing_hook') + self.poutput("in postparsing_hook") return data ``` diff --git a/docs/features/prompt.md b/docs/features/prompt.md index 93fb6b495..0f8a09082 100644 --- a/docs/features/prompt.md +++ b/docs/features/prompt.md @@ -79,14 +79,15 @@ You can customize the content of the toolbar by overriding the [cmd2.Cmd.get_bot method. ```py - from prompt_toolkit.formatted_text import AnyFormattedText - - def get_bottom_toolbar(self) -> AnyFormattedText: - return [ - ('ansigreen', 'My Application Name'), - ('', ' - '), - ('ansiyellow', 'Current Status: Idle'), - ] +from prompt_toolkit.formatted_text import AnyFormattedText + + +def get_bottom_toolbar(self) -> AnyFormattedText: + return [ + ("ansigreen", "My Application Name"), + ("", " - "), + ("ansiyellow", "Current Status: Idle"), + ] ``` ### Refreshing the Toolbar diff --git a/docs/features/redirection.md b/docs/features/redirection.md index 8b2ab68e4..27a238caa 100644 --- a/docs/features/redirection.md +++ b/docs/features/redirection.md @@ -49,6 +49,8 @@ output of that to a file called _output.txt_. ```py from cmd2 import Cmd + + class App(Cmd): def __init__(self): super().__init__(allow_redirection=False) diff --git a/docs/features/scripting.md b/docs/features/scripting.md index 429fba210..531027afc 100644 --- a/docs/features/scripting.md +++ b/docs/features/scripting.md @@ -154,26 +154,27 @@ As a baseline, let's start with the following `cmd2` application called `FirstAp ```py #!/usr/bin/env python """A simple cmd2 application.""" + import cmd2 class FirstApp(cmd2.Cmd): """A simple cmd2 application.""" + def __init__(self): shortcuts = cmd2.DEFAULT_SHORTCUTS - shortcuts.update({'&': 'speak'}) + shortcuts.update({"&": "speak"}) super().__init__(shortcuts=shortcuts) # Make maxrepeats settable at runtime self.maxrepeats = 3 - self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self)) - + self.add_settable(cmd2.Settable("maxrepeats", int, "max repetitions for speak command", self)) speak_parser = cmd2.Cmd2ArgumentParser() - speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') - speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') - speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') - speak_parser.add_argument('words', nargs='+', help='words to say') + speak_parser.add_argument("-p", "--piglatin", action="store_true", help="atinLay") + speak_parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE") + speak_parser.add_argument("-r", "--repeat", type=int, help="output [n] times") + speak_parser.add_argument("words", nargs="+", help="words to say") @cmd2.with_argparser(speak_parser) def do_speak(self, args): @@ -181,17 +182,19 @@ class FirstApp(cmd2.Cmd): words = [] for word in args.words: if args.piglatin: - word = '%s%say' % (word[1:], word[0]) + word = "%s%say" % (word[1:], word[0]) if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for _ in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too - self.poutput(' '.join(words)) + self.poutput(" ".join(words)) -if __name__ == '__main__': + +if __name__ == "__main__": import sys + c = FirstApp() sys.exit(c.cmdloop()) ``` @@ -226,7 +229,7 @@ Even though this is a fully qualified `cmd2` error, the pyscript must check for perform error checking.: ```py -app('speak') +app("speak") print("Working") ``` @@ -238,7 +241,7 @@ You should notice that no error message is printed. Let's utilize the `CommandRe inspect the actual returned data.: ```py -result = app('speak') +result = app("speak") print(result) ``` @@ -248,7 +251,7 @@ print(result) Now we can see that there has been an error. Let's rewrite the script to perform error checking.: ```py -result = app('speak') +result = app("speak") if not result: print(result.stderr) @@ -262,7 +265,7 @@ In Python development, it is good practice to fail fast after user input.: ```py import sys -result = app('speak TRUTH!!') +result = app("speak TRUTH!!") if not result: print("Something went wrong") @@ -280,8 +283,8 @@ the `CommandResult`: ```py import sys -#Syntax error -result = app('speak TRUTH!!!') +# Syntax error +result = app("speak TRUTH!!!") if not result: print("Something went wrong") sys.exit() @@ -317,8 +320,8 @@ In the following command example we return a list containing directory elements. ```py dir_parser = cmd2.Cmd2ArgumentParser() -dir_parser.add_argument('-l', '--long', action='store_true', - help="display in long format with one item per line") +dir_parser.add_argument("-l", "--long", action="store_true", help="display in long format with one item per line") + @cmd2.with_argparser(dir_parser, with_unknown_args=True) def do_dir(self, args, unknown): @@ -326,15 +329,15 @@ def do_dir(self, args, unknown): # No arguments for this command if unknown: self.perror("dir does not take any positional arguments:") - self.do_help('dir') + self.do_help("dir") return # Get the contents as a list contents = os.listdir(self.cwd) for f in contents: - self.poutput(f'{f}') - self.poutput('') + self.poutput(f"{f}") + self.poutput("") self.last_result = contents ``` @@ -342,7 +345,7 @@ def do_dir(self, args, unknown): The following script retrieves the array contents.: ```py -result = app('dir') +result = app("dir") print(result.data) ``` @@ -373,6 +376,7 @@ app.py: ```py #!/usr/bin/env python """A simple cmd2 application.""" + import sys from dataclasses import dataclass from random import choice, randint @@ -419,9 +423,7 @@ class FirstApp(cmd2.Cmd): status = self._start_build(args.name) self._status_cache[args.name] = status - self.poutput( - f"Build {args.name.upper()} successfully started with id : {status.id}" - ) + self.poutput(f"Build {args.name.upper()} successfully started with id : {status.id}") self.last_result = status status_parser = cmd2.Cmd2ArgumentParser() @@ -451,11 +453,11 @@ import sys import time # start build -result = app('build tower') +result = app("build tower") # If there was an error then exit if not result: - print('Build failed') + print("Build failed") sys.exit() # This is a BuildStatus dataclass object @@ -465,11 +467,10 @@ print(f"Build {build.name} : {build.status}") # Poll status while True: - # Perform status check - result = app('status tower') + result = app("status tower") - #error checking + # error checking if not result: print("Unable to determine status") break @@ -477,7 +478,7 @@ while True: build_status = result.data # If the status shows complete then the script is done - if build_status.status in ['finished', 'canceled']: + if build_status.status in ["finished", "canceled"]: print(f"Build {build.name} has completed") break diff --git a/docs/features/settings.md b/docs/features/settings.md index 568356686..ed663ef45 100644 --- a/docs/features/settings.md +++ b/docs/features/settings.md @@ -91,9 +91,11 @@ Here's an example, from !!! example "examples/environment.py" + ```py --8<-- "examples/environment.py" ``` + If you want to be notified when a setting changes (as we do above), then be sure to supply a method to the `onchange_cb` parameter of the `cmd2.utils.Settable`. This method will be called after the diff --git a/docs/features/shortcuts_aliases_macros.md b/docs/features/shortcuts_aliases_macros.md index cd14dce29..bf88b9fb3 100644 --- a/docs/features/shortcuts_aliases_macros.md +++ b/docs/features/shortcuts_aliases_macros.md @@ -19,7 +19,7 @@ format `{'shortcut': 'command_name'}` where you omit `do_` from the command name class App(Cmd): def __init__(self): shortcuts = cmd2.DEFAULT_SHORTCUTS - shortcuts.update({'*': 'sneeze', '~': 'squirm'}) + shortcuts.update({"*": "sneeze", "~": "squirm"}) cmd2.Cmd.__init__(self, shortcuts=shortcuts) ``` diff --git a/docs/features/startup_commands.md b/docs/features/startup_commands.md index fc44d505c..008efde57 100644 --- a/docs/features/startup_commands.md +++ b/docs/features/startup_commands.md @@ -32,6 +32,8 @@ application and easily used in automation. ```py from cmd2 import Cmd + + class App(Cmd): def __init__(self): super().__init__(allow_cli_args=False) @@ -56,7 +58,7 @@ You can execute commands from an initialization script by passing a file path to ```py class StartupApp(cmd2.Cmd): def __init__(self): - cmd2.Cmd.__init__(self, startup_script='.cmd2rc') + cmd2.Cmd.__init__(self, startup_script=".cmd2rc") ``` This text file should contain a [Command Script](./scripting.md#command-scripts). See the @@ -66,7 +68,7 @@ example for a demonstration. You can silence a startup script's output by setting `silence_startup_script` to True: ```py -cmd2.Cmd.__init__(self, startup_script='.cmd2rc', silence_startup_script=True) +cmd2.Cmd.__init__(self, startup_script=".cmd2rc", silence_startup_script=True) ``` !!! warning diff --git a/docs/mixins/mixin_template.md b/docs/mixins/mixin_template.md index 10aaa0b06..749717260 100644 --- a/docs/mixins/mixin_template.md +++ b/docs/mixins/mixin_template.md @@ -89,7 +89,6 @@ Your mixin can add user visible commands. You do it the same way in a mixin that ```python class MyMixin: - def do_say(self, statement): """Simple say command""" self.poutput(statement) @@ -108,8 +107,8 @@ class MyMixin: # code placed here runs before cmd2.Cmd initializes super().__init__(*args, **kwargs) # code placed here runs after cmd2.Cmd initializes - self.mysetting = 'somevalue' - self.settable.update({'mysetting': 'short help message for mysetting'}) + self.mysetting = "somevalue" + self.settable.update({"mysetting": "short help message for mysetting"}) ``` You can also hide settings from the user by removing them from `self.settable`. @@ -142,7 +141,6 @@ Here's a simple example: ```python class MyMixin: - def __init__(self, *args, **kwargs): # code placed here runs before cmd2 initializes super().__init__(*args, **kwargs) @@ -152,7 +150,7 @@ class MyMixin: def cmd2_mymixin_postparsing_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: """Method to be called after parsing user input, but before running the command""" - self.poutput('in postparsing_hook') + self.poutput("in postparsing_hook") return data ``` diff --git a/docs/testing.md b/docs/testing.md index 176e9d94b..d7379c19c 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -46,8 +46,8 @@ Another one using [pytest-mock](https://pypi.org/project/pytest-mock) to provide ```py def test_mocked_methods2(mocker): - mock_cmdloop = mocker.patch("cmd2.Cmd.cmdloop", autospec=True) - cli = cmd2.Cmd() - cli.cmdloop() - assert mock_cmdloop.call_count == 1 + mock_cmdloop = mocker.patch("cmd2.Cmd.cmdloop", autospec=True) + cli = cmd2.Cmd() + cli.cmdloop() + assert mock_cmdloop.call_count == 1 ```