cmd2 4.0.0b4__tar.gz → 4.0.0b6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/CHANGELOG.md +29 -3
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/PKG-INFO +1 -1
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/__init__.py +0 -2
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/argparse_completer.py +2 -15
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/argparse_utils.py +148 -48
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/cmd2.py +232 -184
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/completion.py +22 -6
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/constants.py +3 -3
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/decorators.py +9 -8
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/parsing.py +10 -10
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/pt_utils.py +3 -4
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/py_bridge.py +5 -14
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/rich_utils.py +43 -71
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/string_utils.py +1 -3
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/utils.py +3 -6
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2.egg-info/PKG-INFO +1 -1
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2.egg-info/SOURCES.txt +2 -1
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/argument_processing.md +1 -1
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/builtin_commands.md +1 -1
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/generating_output.md +3 -1
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/initialization.md +0 -1
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/settings.md +1 -6
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/README.md +2 -0
- cmd2-4.0.0b6/examples/completion_item_choices.py +145 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/scripts/save_help_text.py +2 -2
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/conftest.py +2 -9
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/pyscript/environment.py +0 -2
- cmd2-4.0.0b6/tests/pyscript/test_print.py +29 -0
- cmd2-4.0.0b6/tests/scripts/postcmds.txt +1 -0
- cmd2-4.0.0b6/tests/scripts/precmds.txt +1 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_argparse_utils.py +191 -8
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_cmd2.py +172 -106
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_commandset.py +5 -5
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_completion.py +32 -1
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_pt_utils.py +0 -24
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_rich_utils.py +33 -45
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_run_pyscript.py +69 -20
- cmd2-4.0.0b4/tests/pyscript/stdout_capture.py +0 -4
- cmd2-4.0.0b4/tests/scripts/postcmds.txt +0 -1
- cmd2-4.0.0b4/tests/scripts/precmds.txt +0 -1
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/.pre-commit-config.yaml +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/.prettierignore +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/.prettierrc +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/.typos.toml +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/GEMINI.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/LICENSE +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/MANIFEST.in +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/Makefile +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/README.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/clipboard.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/colors.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/command_set.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/exceptions.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/history.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/plugin.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/py.typed +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/styles.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2/types.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2.egg-info/dependency_links.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2.egg-info/requires.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2.egg-info/top_level.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/cmd2.png +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/codecov.yml +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/argparse_completer.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/argparse_utils.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/clipboard.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/cmd.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/colors.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/command_set.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/completion.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/constants.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/decorators.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/exceptions.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/history.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/index.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/parsing.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/plugin.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/pt_utils.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/py_bridge.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/rich_utils.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/string_utils.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/styles.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/api/utils.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/doc_conventions.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/examples/alternate_event_loops.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/examples/examples.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/examples/getting_started.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/examples/index.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/async_commands.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/clipboard.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/commands.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/completion.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/disable_commands.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/embedded_python_shells.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/help.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/history.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/hooks.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/index.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/misc.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/modular_commands.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/multiline_commands.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/os.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/packaging.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/plugins.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/prompt.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/redirection.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/scripting.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/shortcuts_aliases_macros.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/startup_commands.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/table_creation.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/features/theme.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/index.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/javascripts/readthedocs.js +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/migrating/incompatibilities.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/migrating/index.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/migrating/minimum.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/migrating/next_steps.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/migrating/why.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/mixins/index.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/mixins/mixin_template.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/overrides/main.html +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/overview/alternatives.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/overview/index.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/overview/installation.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/overview/integrating.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/overview/resources.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/stylesheets/cmd2.css +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/testing.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/docs/upgrades.md +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/.cmd2rc +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/argparse_completion.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/argparse_example.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/async_call.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/async_commands.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/async_printing.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/basic_completion.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/cmd_as_argument.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/color.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/command_sets.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/custom_parser.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/custom_types.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/default_categories.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/dynamic_commands.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/environment.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/event_loops.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/exit_code.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/getting_started.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/hello_cmd2.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/help_categories.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/hooks.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/migrating.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/mixin.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/modular_commands/__init__.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/modular_commands/commandset_basic.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/modular_commands/commandset_complex.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/modular_commands/commandset_custominit.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/modular_commandsets.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/paged_output.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/persistent_history.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/pretty_print.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/python_scripting.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/read_input.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/remove_builtin_commands.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/remove_settable.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/rich_tables.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/rich_theme.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/scripts/arg_printer.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/scripts/conditional.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/scripts/nested.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/scripts/quit.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/scripts/script.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/scripts/script.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/tmux_launch.sh +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/tmux_split.sh +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/examples/unicode_commands.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/mkdocs.yml +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/package.json +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/pyproject.toml +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/ruff.toml +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/scripts/validate_tag.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/setup.cfg +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/.cmd2rc +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/__init__.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/pyscript/echo.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/pyscript/help.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/pyscript/py_locals.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/pyscript/pyscript_dir.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/pyscript/raises_exception.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/pyscript/recursive.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/pyscript/self_in_py.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/pyscript/stop.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/relative_multiple.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/script.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/script.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/scripts/binary.bin +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/scripts/empty.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/scripts/help.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/scripts/nested.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/scripts/one_down.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/scripts/utf8.txt +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_argparse.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_argparse_completer.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_argparse_subcommands.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_categories.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_dynamic_complete_style.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_future_annotations.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_history.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_parsing.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_plugin.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_py_completion.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_string_utils.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_utils.py +0 -0
- {cmd2-4.0.0b4 → cmd2-4.0.0b6}/tests/test_utils_defining_class.py +0 -0
|
@@ -81,7 +81,9 @@ prompt is displayed.
|
|
|
81
81
|
- `cmd2` no longer sets a default title for a subparsers group. If you desire a title, you will
|
|
82
82
|
need to pass one in like this `parser.add_subparsers(title="subcommands")`. This is standard
|
|
83
83
|
`argparse` behavior.
|
|
84
|
-
- `
|
|
84
|
+
- Added `HelpFormatterRenderable` protocol and `HelpContent` type alias to support context-aware
|
|
85
|
+
help content in `argparse`.
|
|
86
|
+
- `TextGroup` now implements `HelpFormatterRenderable`.
|
|
85
87
|
- Removed `formatter_creator` parameter from `TextGroup.__init__()`.
|
|
86
88
|
- Removed `Cmd2ArgumentParser.create_text_group()` method.
|
|
87
89
|
- `argparse` and `Rich` integration refactoring:
|
|
@@ -96,6 +98,17 @@ prompt is displayed.
|
|
|
96
98
|
- Replaced the global `APP_THEME` constant in `rich_utils.py` with `get_theme()` and
|
|
97
99
|
`set_theme()` functions to support lazy initialization and safer in-place updates of the
|
|
98
100
|
theme.
|
|
101
|
+
- Renamed `Cmd._command_parsers` to `Cmd.command_parsers`.
|
|
102
|
+
- Removed `RichPrintKwargs` `TypedDict` in favor of using `Mapping[str, Any]`, allowing for
|
|
103
|
+
greater flexibility in passing keyword arguments to `console.print()` calls.
|
|
104
|
+
- Removed `always_show_hint` settable as it provided a poor user experience with
|
|
105
|
+
`prompt-toolkit`
|
|
106
|
+
- `cmd2` redirection only captures output directed to `self.stdout` (e.g., via
|
|
107
|
+
`self.poutput()`). Standard `print()` calls write directly to `sys.stdout` and are not
|
|
108
|
+
captured. However, `print()` calls within `pyscripts` and the interactive Python shell are
|
|
109
|
+
treated as command output and sent to `self.stdout`, allowing them to be captured.
|
|
110
|
+
- Verbose help table descriptions are no longer generated from help function output. The system
|
|
111
|
+
now relies exclusively on command function docstrings.
|
|
99
112
|
- Enhancements
|
|
100
113
|
- New `cmd2.Cmd` parameters
|
|
101
114
|
- **auto_suggest**: (boolean) if `True`, provide fish shell style auto-suggestions. These
|
|
@@ -113,8 +126,9 @@ prompt is displayed.
|
|
|
113
126
|
- **read_secret**: read secrets like passwords without displaying them to the terminal
|
|
114
127
|
- **ppretty**: a cmd2-compatible replacement for `rich.pretty.pprint()`
|
|
115
128
|
- New settables:
|
|
116
|
-
- **max_column_completion_results**: (int)
|
|
117
|
-
|
|
129
|
+
- **max_column_completion_results**: (int) Maximum number of completion results to display
|
|
130
|
+
in a single column
|
|
131
|
+
- **traceback_show_locals**: (bool) Display local variables in tracebacks
|
|
118
132
|
- `cmd2.Cmd.select` has been revamped to use the
|
|
119
133
|
[choice](https://python-prompt-toolkit.readthedocs.io/en/3.0.52/pages/asking_for_a_choice.html)
|
|
120
134
|
function from `prompt-toolkit` when both **stdin** and **stdout** are TTYs
|
|
@@ -130,6 +144,18 @@ prompt is displayed.
|
|
|
130
144
|
specific `cmd2.Cmd` subclass (e.g.,`class MyCommandSet(CommandSet[MyApp]):`). This provides
|
|
131
145
|
full type hints and IDE autocompletion for `self._cmd` without needing to override and cast
|
|
132
146
|
the property.
|
|
147
|
+
- Added `traceback_kwargs` attribute to allow customization of Rich-based tracebacks.
|
|
148
|
+
- The `print()` function available in a `pyscript` writes to `self.stdout` and respects the
|
|
149
|
+
`allow_style` setting. It also supports printing `Rich` objects.
|
|
150
|
+
- Added `Cmd2ArgumentParser.output_to()` context manager to temporarily set the output stream
|
|
151
|
+
during `argparse` operations. This is helpful for directing output for functions like
|
|
152
|
+
`parse_args()`, which default to `sys.stdout` and lack a `file` argument.
|
|
153
|
+
|
|
154
|
+
## 3.5.1 (April 24, 2026)
|
|
155
|
+
|
|
156
|
+
- Bug Fixes
|
|
157
|
+
- Fixed `ArgparseCompleter.print_help()` not passing file stream to recursive call.
|
|
158
|
+
- Fixed issue where `constants.REDIRECTION_TOKENS` was being mutated.
|
|
133
159
|
|
|
134
160
|
## 3.5.0 (April 13, 2026)
|
|
135
161
|
|
|
@@ -50,7 +50,6 @@ from .rich_utils import (
|
|
|
50
50
|
MetavarTypeCmd2HelpFormatter,
|
|
51
51
|
RawDescriptionCmd2HelpFormatter,
|
|
52
52
|
RawTextCmd2HelpFormatter,
|
|
53
|
-
RichPrintKwargs,
|
|
54
53
|
TextGroup,
|
|
55
54
|
get_theme,
|
|
56
55
|
set_theme,
|
|
@@ -105,7 +104,6 @@ __all__: list[str] = [ # noqa: RUF022
|
|
|
105
104
|
"MetavarTypeCmd2HelpFormatter",
|
|
106
105
|
"RawDescriptionCmd2HelpFormatter",
|
|
107
106
|
"RawTextCmd2HelpFormatter",
|
|
108
|
-
"RichPrintKwargs",
|
|
109
107
|
"set_theme",
|
|
110
108
|
"TextGroup",
|
|
111
109
|
# String Utils
|
|
@@ -491,13 +491,6 @@ class ArgparseCompleter:
|
|
|
491
491
|
|
|
492
492
|
# If we have results, then return them
|
|
493
493
|
if completions:
|
|
494
|
-
if not completions.hint:
|
|
495
|
-
# Add a hint even though there are results in case Cmd.always_show_hint is True.
|
|
496
|
-
completions = dataclasses.replace(
|
|
497
|
-
completions,
|
|
498
|
-
hint=_build_hint(self._parser, flag_arg_state.action),
|
|
499
|
-
)
|
|
500
|
-
|
|
501
494
|
return completions
|
|
502
495
|
|
|
503
496
|
# Otherwise, print a hint if the flag isn't finished or text isn't possibly the start of a flag
|
|
@@ -519,12 +512,6 @@ class ArgparseCompleter:
|
|
|
519
512
|
|
|
520
513
|
# If we have results, then return them
|
|
521
514
|
if completions:
|
|
522
|
-
if not completions.hint:
|
|
523
|
-
# Add a hint even though there are results in case Cmd.always_show_hint is True.
|
|
524
|
-
completions = dataclasses.replace(
|
|
525
|
-
completions,
|
|
526
|
-
hint=_build_hint(self._parser, pos_arg_state.action),
|
|
527
|
-
)
|
|
528
515
|
return completions
|
|
529
516
|
|
|
530
517
|
# Otherwise, print a hint if text isn't possibly the start of a flag
|
|
@@ -705,9 +692,9 @@ class ArgparseCompleter:
|
|
|
705
692
|
if parser is not None:
|
|
706
693
|
completer_type = self._cmd2_app._determine_ap_completer_type(parser)
|
|
707
694
|
completer = completer_type(parser, self._cmd2_app)
|
|
708
|
-
completer.print_help(tokens[1:])
|
|
695
|
+
completer.print_help(tokens[1:], file)
|
|
709
696
|
return
|
|
710
|
-
self._parser.print_help(file
|
|
697
|
+
self._parser.print_help(file)
|
|
711
698
|
|
|
712
699
|
def _choices_to_items(self, arg_state: _ArgumentState) -> list[CompletionItem]:
|
|
713
700
|
"""Convert choices from action to list of CompletionItems."""
|
|
@@ -225,27 +225,35 @@ available on the resulting ``Action`` object to access its underlying attribute:
|
|
|
225
225
|
"""
|
|
226
226
|
|
|
227
227
|
import argparse
|
|
228
|
+
import contextlib
|
|
228
229
|
import re
|
|
229
230
|
import sys
|
|
231
|
+
import threading
|
|
230
232
|
from argparse import ArgumentError
|
|
231
233
|
from collections.abc import (
|
|
232
234
|
Callable,
|
|
233
235
|
Iterable,
|
|
236
|
+
Iterator,
|
|
234
237
|
Sequence,
|
|
235
238
|
)
|
|
239
|
+
from dataclasses import dataclass
|
|
236
240
|
from typing import (
|
|
241
|
+
IO,
|
|
237
242
|
TYPE_CHECKING,
|
|
238
243
|
Any,
|
|
244
|
+
ClassVar,
|
|
239
245
|
NoReturn,
|
|
240
246
|
cast,
|
|
241
247
|
)
|
|
242
248
|
|
|
243
|
-
from rich.console import RenderableType
|
|
244
249
|
from rich.table import Column
|
|
245
250
|
|
|
246
251
|
from . import constants
|
|
247
252
|
from .completion import CompletionItem
|
|
248
|
-
from .rich_utils import
|
|
253
|
+
from .rich_utils import (
|
|
254
|
+
Cmd2HelpFormatter,
|
|
255
|
+
HelpContent,
|
|
256
|
+
)
|
|
249
257
|
from .styles import Cmd2Style
|
|
250
258
|
from .types import (
|
|
251
259
|
CmdOrSetT,
|
|
@@ -494,16 +502,93 @@ def _ActionsContainer_add_argument( # noqa: N802
|
|
|
494
502
|
# Overwrite _ActionsContainer.add_argument with our patch
|
|
495
503
|
argparse._ActionsContainer.add_argument = _ActionsContainer_add_argument # type: ignore[method-assign]
|
|
496
504
|
|
|
505
|
+
############################################################################################################
|
|
506
|
+
# Patch argparse._SubParsersAction by adding remove_parser() function
|
|
507
|
+
############################################################################################################
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def _SubParsersAction_remove_parser( # noqa: N802
|
|
511
|
+
self: argparse._SubParsersAction, # type: ignore[type-arg]
|
|
512
|
+
name: str,
|
|
513
|
+
) -> argparse.ArgumentParser:
|
|
514
|
+
"""Remove a subparser from a subparsers group.
|
|
515
|
+
|
|
516
|
+
This function is added by cmd2 as a method called ``remove_parser()``
|
|
517
|
+
to ``argparse._SubParsersAction`` class.
|
|
518
|
+
|
|
519
|
+
To call: ``action.remove_parser(name)``
|
|
520
|
+
|
|
521
|
+
:param self: instance of the _SubParsersAction being edited
|
|
522
|
+
:param name: name of the subcommand for the subparser to remove
|
|
523
|
+
:return: the removed parser
|
|
524
|
+
:raises ValueError: if the subcommand doesn't exist
|
|
525
|
+
"""
|
|
526
|
+
if name not in self._name_parser_map:
|
|
527
|
+
raise ValueError(f"Subcommand '{name}' does not exist")
|
|
528
|
+
|
|
529
|
+
subparser = self._name_parser_map[name]
|
|
530
|
+
|
|
531
|
+
# Find all names (primary and aliases) that map to this subparser
|
|
532
|
+
all_names = [cur_name for cur_name, cur_parser in self._name_parser_map.items() if cur_parser is subparser]
|
|
533
|
+
|
|
534
|
+
# Remove the help entry for this subparser. To handle the case where
|
|
535
|
+
# name is an alias, we remove the action whose 'dest' matches any of
|
|
536
|
+
# the names mapped to this subparser.
|
|
537
|
+
for choice_action in self._choices_actions:
|
|
538
|
+
if choice_action.dest in all_names:
|
|
539
|
+
self._choices_actions.remove(choice_action)
|
|
540
|
+
break
|
|
541
|
+
|
|
542
|
+
# Remove all references to this subparser, including aliases.
|
|
543
|
+
for cur_name in all_names:
|
|
544
|
+
del self._name_parser_map[cur_name]
|
|
545
|
+
|
|
546
|
+
return cast(argparse.ArgumentParser, subparser)
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
argparse._SubParsersAction.remove_parser = _SubParsersAction_remove_parser # type: ignore[attr-defined]
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
@dataclass
|
|
553
|
+
class _ParserThreadLocals(threading.local):
|
|
554
|
+
"""Thread-local storage used by Cmd2ArgumentParser to manage execution context."""
|
|
555
|
+
|
|
556
|
+
# The active output stream for help, usage, and errors. Since argparse does not
|
|
557
|
+
# pass the destination stream to the formatter factory, this transient value
|
|
558
|
+
# provides the context needed to synchronize Rich's rendering with the specific
|
|
559
|
+
# capabilities of the destination file descriptor. It is managed via the
|
|
560
|
+
# output_to() context manager.
|
|
561
|
+
current_output_file: IO[str] | None = None
|
|
562
|
+
|
|
497
563
|
|
|
498
564
|
class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
499
565
|
"""Custom ArgumentParser class that improves error and help output."""
|
|
500
566
|
|
|
567
|
+
# Thread-local storage shared by all parser instances (including subparsers)
|
|
568
|
+
_thread_locals: ClassVar[_ParserThreadLocals] = _ParserThreadLocals()
|
|
569
|
+
|
|
570
|
+
@contextlib.contextmanager
|
|
571
|
+
def output_to(self, file: IO[str] | None) -> Iterator[None]:
|
|
572
|
+
"""Context manager to temporarily set the output stream during argparse operations.
|
|
573
|
+
|
|
574
|
+
This is helpful for directing output for functions like `parse_args()`, which
|
|
575
|
+
default to `sys.stdout` and lack a `file` argument.
|
|
576
|
+
|
|
577
|
+
:param file: the file stream to use for output
|
|
578
|
+
"""
|
|
579
|
+
previous = self._thread_locals.current_output_file
|
|
580
|
+
self._thread_locals.current_output_file = file
|
|
581
|
+
try:
|
|
582
|
+
yield
|
|
583
|
+
finally:
|
|
584
|
+
self._thread_locals.current_output_file = previous
|
|
585
|
+
|
|
501
586
|
def __init__(
|
|
502
587
|
self,
|
|
503
588
|
prog: str | None = None,
|
|
504
589
|
usage: str | None = None,
|
|
505
|
-
description:
|
|
506
|
-
epilog:
|
|
590
|
+
description: HelpContent | None = None,
|
|
591
|
+
epilog: HelpContent | None = None,
|
|
507
592
|
parents: Sequence[argparse.ArgumentParser] = (),
|
|
508
593
|
formatter_class: type[Cmd2HelpFormatter] = Cmd2HelpFormatter,
|
|
509
594
|
prefix_chars: str = "-",
|
|
@@ -553,10 +638,26 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
553
638
|
|
|
554
639
|
# To assist type checkers, recast these to reflect our usage of rich-argparse.
|
|
555
640
|
self.formatter_class: type[Cmd2HelpFormatter]
|
|
556
|
-
self.description:
|
|
557
|
-
self.epilog:
|
|
641
|
+
self.description: HelpContent | None # type: ignore[assignment]
|
|
642
|
+
self.epilog: HelpContent | None # type: ignore[assignment]
|
|
643
|
+
|
|
644
|
+
def print_usage(self, file: IO[str] | None = None) -> None: # type:ignore[override]
|
|
645
|
+
"""Override to ensure the formatter is aware of the target file."""
|
|
646
|
+
if file is None:
|
|
647
|
+
file = self._thread_locals.current_output_file
|
|
558
648
|
|
|
559
|
-
|
|
649
|
+
with self.output_to(file):
|
|
650
|
+
super().print_usage(file)
|
|
651
|
+
|
|
652
|
+
def print_help(self, file: IO[str] | None = None) -> None: # type:ignore[override]
|
|
653
|
+
"""Override to ensure the formatter is aware of the target file."""
|
|
654
|
+
if file is None:
|
|
655
|
+
file = self._thread_locals.current_output_file
|
|
656
|
+
|
|
657
|
+
with self.output_to(file):
|
|
658
|
+
super().print_help(file)
|
|
659
|
+
|
|
660
|
+
def get_subparsers_action(self) -> "argparse._SubParsersAction[Cmd2ArgumentParser]":
|
|
560
661
|
"""Get the _SubParsersAction for this parser if it exists.
|
|
561
662
|
|
|
562
663
|
:return: the _SubParsersAction for this parser
|
|
@@ -619,7 +720,7 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
619
720
|
self.prog = prog
|
|
620
721
|
|
|
621
722
|
try:
|
|
622
|
-
subparsers_action = self.
|
|
723
|
+
subparsers_action = self.get_subparsers_action()
|
|
623
724
|
except ValueError:
|
|
624
725
|
# This parser has no subcommands
|
|
625
726
|
return
|
|
@@ -638,12 +739,14 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
638
739
|
# add_parser() will have the correct prog value.
|
|
639
740
|
subparsers_action._prog_prefix = self._build_subparsers_prog_prefix(positionals)
|
|
640
741
|
|
|
641
|
-
# subparsers_action.
|
|
642
|
-
# we skip already updated parsers to ensure primary names are used in 'prog'.
|
|
742
|
+
# subparsers_action._name_parser_map includes aliases. Since primary names are inserted
|
|
743
|
+
# first, we skip already updated parsers to ensure primary names are used in 'prog'.
|
|
744
|
+
# We can't rely on subparsers_action._choices_actions to filter out aliases because while
|
|
745
|
+
# it contains only primary names, it omits any subcommands that lack help text.
|
|
643
746
|
updated_parsers: set[Cmd2ArgumentParser] = set()
|
|
644
747
|
|
|
645
748
|
# Set the prog value for each subcommand's parser
|
|
646
|
-
for subcmd_name, subcmd_parser in subparsers_action.
|
|
749
|
+
for subcmd_name, subcmd_parser in subparsers_action._name_parser_map.items():
|
|
647
750
|
if subcmd_parser in updated_parsers:
|
|
648
751
|
continue
|
|
649
752
|
|
|
@@ -651,7 +754,7 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
651
754
|
subcmd_parser.update_prog(subcmd_prog)
|
|
652
755
|
updated_parsers.add(subcmd_parser)
|
|
653
756
|
|
|
654
|
-
def
|
|
757
|
+
def find_parser(self, subcommand_path: Iterable[str]) -> "Cmd2ArgumentParser":
|
|
655
758
|
"""Find a parser in the hierarchy based on a sequence of subcommand names.
|
|
656
759
|
|
|
657
760
|
:param subcommand_path: sequence of subcommand names leading to the target parser
|
|
@@ -660,10 +763,10 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
660
763
|
"""
|
|
661
764
|
parser = self
|
|
662
765
|
for name in subcommand_path:
|
|
663
|
-
subparsers_action = parser.
|
|
664
|
-
if name not in subparsers_action.
|
|
665
|
-
raise ValueError(f"Subcommand '{name}' not
|
|
666
|
-
parser = subparsers_action.
|
|
766
|
+
subparsers_action = parser.get_subparsers_action()
|
|
767
|
+
if name not in subparsers_action._name_parser_map:
|
|
768
|
+
raise ValueError(f"Subcommand '{name}' does not exist for '{parser.prog}'")
|
|
769
|
+
parser = subparsers_action._name_parser_map[name]
|
|
667
770
|
return parser
|
|
668
771
|
|
|
669
772
|
def attach_subcommand(
|
|
@@ -683,7 +786,8 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
683
786
|
:raises TypeError: if subcommand_parser is not an instance of the following or their subclasses:
|
|
684
787
|
1. Cmd2ArgumentParser
|
|
685
788
|
2. The parser_class configured for the target subcommand group
|
|
686
|
-
:raises ValueError: if the command path is invalid
|
|
789
|
+
:raises ValueError: if the command path is invalid, doesn't support subcommands, or the
|
|
790
|
+
subcommand already exists
|
|
687
791
|
"""
|
|
688
792
|
if not isinstance(subcommand_parser, Cmd2ArgumentParser):
|
|
689
793
|
raise TypeError(
|
|
@@ -691,8 +795,8 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
691
795
|
f"Received: '{type(subcommand_parser).__name__}'."
|
|
692
796
|
)
|
|
693
797
|
|
|
694
|
-
target_parser = self.
|
|
695
|
-
subparsers_action = target_parser.
|
|
798
|
+
target_parser = self.find_parser(subcommand_path)
|
|
799
|
+
subparsers_action = target_parser.get_subparsers_action()
|
|
696
800
|
|
|
697
801
|
# Verify the parser is compatible with the 'parser_class' configured for this
|
|
698
802
|
# subcommand group. We use isinstance() here to allow for subclasses, providing
|
|
@@ -705,6 +809,12 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
705
809
|
f"Received: '{type(subcommand_parser).__name__}'."
|
|
706
810
|
)
|
|
707
811
|
|
|
812
|
+
# Do not overwrite existing subcommands or aliases
|
|
813
|
+
all_names = (subcommand, *add_parser_kwargs.get("aliases", ()))
|
|
814
|
+
for name in all_names:
|
|
815
|
+
if name in subparsers_action._name_parser_map:
|
|
816
|
+
raise ValueError(f"Subcommand '{name}' already exists for '{target_parser.prog}'")
|
|
817
|
+
|
|
708
818
|
# Use add_parser to register the subcommand name and any aliases
|
|
709
819
|
placeholder_parser = subparsers_action.add_parser(subcommand, **add_parser_kwargs)
|
|
710
820
|
|
|
@@ -728,28 +838,16 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
728
838
|
:return: the detached parser
|
|
729
839
|
:raises ValueError: if the command path is invalid or the subcommand doesn't exist
|
|
730
840
|
"""
|
|
731
|
-
target_parser = self.
|
|
732
|
-
subparsers_action = target_parser.
|
|
733
|
-
|
|
734
|
-
subparser = subparsers_action._name_parser_map.get(subcommand)
|
|
735
|
-
if subparser is None:
|
|
736
|
-
raise ValueError(f"Subcommand '{subcommand}' not found in '{target_parser.prog}'")
|
|
737
|
-
|
|
738
|
-
# Remove this subcommand and all its aliases from the base command
|
|
739
|
-
to_remove = []
|
|
740
|
-
for cur_name, cur_parser in subparsers_action._name_parser_map.items():
|
|
741
|
-
if cur_parser is subparser:
|
|
742
|
-
to_remove.append(cur_name)
|
|
743
|
-
for cur_name in to_remove:
|
|
744
|
-
del subparsers_action._name_parser_map[cur_name]
|
|
745
|
-
|
|
746
|
-
# Remove this subcommand from its base command's help text
|
|
747
|
-
for choice_action in subparsers_action._choices_actions:
|
|
748
|
-
if choice_action.dest == subcommand:
|
|
749
|
-
subparsers_action._choices_actions.remove(choice_action)
|
|
750
|
-
break
|
|
841
|
+
target_parser = self.find_parser(subcommand_path)
|
|
842
|
+
subparsers_action = target_parser.get_subparsers_action()
|
|
751
843
|
|
|
752
|
-
|
|
844
|
+
try:
|
|
845
|
+
return cast(
|
|
846
|
+
Cmd2ArgumentParser,
|
|
847
|
+
subparsers_action.remove_parser(subcommand), # type: ignore[attr-defined]
|
|
848
|
+
)
|
|
849
|
+
except ValueError:
|
|
850
|
+
raise ValueError(f"Subcommand '{subcommand}' does not exist for '{target_parser.prog}'") from None
|
|
753
851
|
|
|
754
852
|
def error(self, message: str) -> NoReturn:
|
|
755
853
|
"""Override that applies custom formatting to the error message."""
|
|
@@ -761,19 +859,21 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
761
859
|
else:
|
|
762
860
|
formatted_message += "\n " + line
|
|
763
861
|
|
|
764
|
-
self.
|
|
862
|
+
with self.output_to(sys.stderr):
|
|
863
|
+
self.print_usage(sys.stderr)
|
|
765
864
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
console.
|
|
770
|
-
|
|
865
|
+
# Use console to add style since it will respect ALLOW_STYLE's value.
|
|
866
|
+
# Now _get_formatter() will return a formatter bound to stderr.
|
|
867
|
+
console = self._get_formatter().console
|
|
868
|
+
with console.capture() as capture:
|
|
869
|
+
console.print(formatted_message, style=Cmd2Style.ERROR)
|
|
870
|
+
formatted_message = f"{capture.get()}"
|
|
771
871
|
|
|
772
872
|
self.exit(2, f"{formatted_message}\n")
|
|
773
873
|
|
|
774
|
-
def _get_formatter(self, **
|
|
874
|
+
def _get_formatter(self, **_kwargs: Any) -> Cmd2HelpFormatter:
|
|
775
875
|
"""Override with customizations for Cmd2HelpFormatter."""
|
|
776
|
-
return
|
|
876
|
+
return self.formatter_class(prog=self.prog, file=self._thread_locals.current_output_file)
|
|
777
877
|
|
|
778
878
|
def format_help(self) -> str:
|
|
779
879
|
"""Override to add a newline."""
|