cmd2 4.0.0b3__tar.gz → 4.0.0b5__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.0b3 → cmd2-4.0.0b5}/CHANGELOG.md +7 -2
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/PKG-INFO +1 -1
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/__init__.py +0 -2
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/argparse_utils.py +77 -34
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/cmd2.py +168 -103
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/completion.py +50 -15
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/decorators.py +4 -4
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/rich_utils.py +0 -22
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2.egg-info/PKG-INFO +1 -1
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2.egg-info/SOURCES.txt +1 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/argument_processing.md +1 -1
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/builtin_commands.md +1 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/generating_output.md +3 -1
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/README.md +2 -0
- cmd2-4.0.0b5/examples/completion_item_choices.py +145 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/scripts/save_help_text.py +2 -2
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_argparse_utils.py +54 -7
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_cmd2.py +122 -38
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_commandset.py +5 -5
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_completion.py +170 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/.pre-commit-config.yaml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/.prettierignore +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/.prettierrc +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/.typos.toml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/GEMINI.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/LICENSE +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/MANIFEST.in +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/Makefile +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/README.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/argparse_completer.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/clipboard.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/colors.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/command_set.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/constants.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/exceptions.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/history.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/parsing.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/plugin.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/pt_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/py.typed +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/py_bridge.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/string_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/styles.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/types.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2/utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2.egg-info/dependency_links.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2.egg-info/requires.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2.egg-info/top_level.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/cmd2.png +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/codecov.yml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/argparse_completer.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/argparse_utils.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/clipboard.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/cmd.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/colors.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/command_set.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/completion.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/constants.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/decorators.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/exceptions.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/history.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/parsing.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/plugin.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/pt_utils.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/py_bridge.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/rich_utils.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/string_utils.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/styles.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/api/utils.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/doc_conventions.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/examples/alternate_event_loops.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/examples/examples.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/examples/getting_started.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/examples/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/async_commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/clipboard.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/completion.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/disable_commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/embedded_python_shells.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/help.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/history.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/hooks.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/initialization.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/misc.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/modular_commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/multiline_commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/os.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/packaging.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/plugins.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/prompt.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/redirection.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/scripting.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/settings.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/shortcuts_aliases_macros.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/startup_commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/table_creation.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/features/theme.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/javascripts/readthedocs.js +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/migrating/incompatibilities.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/migrating/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/migrating/minimum.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/migrating/next_steps.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/migrating/why.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/mixins/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/mixins/mixin_template.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/overrides/main.html +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/overview/alternatives.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/overview/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/overview/installation.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/overview/integrating.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/overview/resources.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/stylesheets/cmd2.css +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/testing.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/docs/upgrades.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/.cmd2rc +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/argparse_completion.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/argparse_example.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/async_call.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/async_commands.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/async_printing.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/basic_completion.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/cmd_as_argument.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/color.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/command_sets.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/custom_parser.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/custom_types.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/default_categories.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/dynamic_commands.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/environment.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/event_loops.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/exit_code.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/getting_started.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/hello_cmd2.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/help_categories.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/hooks.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/migrating.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/mixin.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/modular_commands/__init__.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/modular_commands/commandset_basic.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/modular_commands/commandset_complex.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/modular_commands/commandset_custominit.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/modular_commandsets.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/paged_output.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/persistent_history.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/pretty_print.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/python_scripting.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/read_input.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/remove_builtin_commands.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/remove_settable.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/rich_tables.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/rich_theme.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/scripts/arg_printer.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/scripts/conditional.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/scripts/nested.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/scripts/quit.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/scripts/script.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/scripts/script.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/tmux_launch.sh +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/tmux_split.sh +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/examples/unicode_commands.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/mkdocs.yml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/package.json +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/pyproject.toml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/ruff.toml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/scripts/validate_tag.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/setup.cfg +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/.cmd2rc +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/__init__.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/conftest.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/pyscript/echo.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/pyscript/environment.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/pyscript/help.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/pyscript/py_locals.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/pyscript/pyscript_dir.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/pyscript/raises_exception.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/pyscript/recursive.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/pyscript/self_in_py.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/pyscript/stdout_capture.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/pyscript/stop.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/relative_multiple.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/script.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/script.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/scripts/binary.bin +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/scripts/empty.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/scripts/help.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/scripts/nested.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/scripts/one_down.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/scripts/postcmds.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/scripts/precmds.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/scripts/utf8.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_argparse.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_argparse_completer.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_argparse_subcommands.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_categories.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_dynamic_complete_style.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_future_annotations.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_history.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_parsing.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_plugin.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_pt_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_py_completion.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_rich_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_run_pyscript.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_string_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b5}/tests/test_utils_defining_class.py +0 -0
|
@@ -96,6 +96,9 @@ prompt is displayed.
|
|
|
96
96
|
- Replaced the global `APP_THEME` constant in `rich_utils.py` with `get_theme()` and
|
|
97
97
|
`set_theme()` functions to support lazy initialization and safer in-place updates of the
|
|
98
98
|
theme.
|
|
99
|
+
- Renamed `Cmd._command_parsers` to `Cmd.command_parsers`.
|
|
100
|
+
- Removed `RichPrintKwargs` `TypedDict` in favor of using `Mapping[str, Any]`, allowing for
|
|
101
|
+
greater flexibility in passing keyword arguments to `console.print()` calls.
|
|
99
102
|
- Enhancements
|
|
100
103
|
- New `cmd2.Cmd` parameters
|
|
101
104
|
- **auto_suggest**: (boolean) if `True`, provide fish shell style auto-suggestions. These
|
|
@@ -113,8 +116,9 @@ prompt is displayed.
|
|
|
113
116
|
- **read_secret**: read secrets like passwords without displaying them to the terminal
|
|
114
117
|
- **ppretty**: a cmd2-compatible replacement for `rich.pretty.pprint()`
|
|
115
118
|
- New settables:
|
|
116
|
-
- **max_column_completion_results**: (int)
|
|
117
|
-
|
|
119
|
+
- **max_column_completion_results**: (int) Maximum number of completion results to display
|
|
120
|
+
in a single column
|
|
121
|
+
- **traceback_show_locals**: (bool) Display local variables in tracebacks
|
|
118
122
|
- `cmd2.Cmd.select` has been revamped to use the
|
|
119
123
|
[choice](https://python-prompt-toolkit.readthedocs.io/en/3.0.52/pages/asking_for_a_choice.html)
|
|
120
124
|
function from `prompt-toolkit` when both **stdin** and **stdout** are TTYs
|
|
@@ -130,6 +134,7 @@ prompt is displayed.
|
|
|
130
134
|
specific `cmd2.Cmd` subclass (e.g.,`class MyCommandSet(CommandSet[MyApp]):`). This provides
|
|
131
135
|
full type hints and IDE autocompletion for `self._cmd` without needing to override and cast
|
|
132
136
|
the property.
|
|
137
|
+
- Added `traceback_kwargs` attribute to allow customization of Rich-based tracebacks.
|
|
133
138
|
|
|
134
139
|
## 3.5.0 (April 13, 2026)
|
|
135
140
|
|
|
@@ -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
|
|
@@ -494,6 +494,52 @@ def _ActionsContainer_add_argument( # noqa: N802
|
|
|
494
494
|
# Overwrite _ActionsContainer.add_argument with our patch
|
|
495
495
|
argparse._ActionsContainer.add_argument = _ActionsContainer_add_argument # type: ignore[method-assign]
|
|
496
496
|
|
|
497
|
+
############################################################################################################
|
|
498
|
+
# Patch argparse._SubParsersAction by adding remove_parser() function
|
|
499
|
+
############################################################################################################
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def _SubParsersAction_remove_parser( # noqa: N802
|
|
503
|
+
self: argparse._SubParsersAction, # type: ignore[type-arg]
|
|
504
|
+
name: str,
|
|
505
|
+
) -> argparse.ArgumentParser:
|
|
506
|
+
"""Remove a subparser from a subparsers group.
|
|
507
|
+
|
|
508
|
+
This function is added by cmd2 as a method called ``remove_parser()``
|
|
509
|
+
to ``argparse._SubParsersAction`` class.
|
|
510
|
+
|
|
511
|
+
To call: ``action.remove_parser(name)``
|
|
512
|
+
|
|
513
|
+
:param self: instance of the _SubParsersAction being edited
|
|
514
|
+
:param name: name of the subcommand for the subparser to remove
|
|
515
|
+
:return: the removed parser
|
|
516
|
+
:raises ValueError: if the subcommand doesn't exist
|
|
517
|
+
"""
|
|
518
|
+
if name not in self._name_parser_map:
|
|
519
|
+
raise ValueError(f"Subcommand '{name}' does not exist")
|
|
520
|
+
|
|
521
|
+
subparser = self._name_parser_map[name]
|
|
522
|
+
|
|
523
|
+
# Find all names (primary and aliases) that map to this subparser
|
|
524
|
+
all_names = [cur_name for cur_name, cur_parser in self._name_parser_map.items() if cur_parser is subparser]
|
|
525
|
+
|
|
526
|
+
# Remove the help entry for this subparser. To handle the case where
|
|
527
|
+
# name is an alias, we remove the action whose 'dest' matches any of
|
|
528
|
+
# the names mapped to this subparser.
|
|
529
|
+
for choice_action in self._choices_actions:
|
|
530
|
+
if choice_action.dest in all_names:
|
|
531
|
+
self._choices_actions.remove(choice_action)
|
|
532
|
+
break
|
|
533
|
+
|
|
534
|
+
# Remove all references to this subparser, including aliases.
|
|
535
|
+
for cur_name in all_names:
|
|
536
|
+
del self._name_parser_map[cur_name]
|
|
537
|
+
|
|
538
|
+
return cast(argparse.ArgumentParser, subparser)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
argparse._SubParsersAction.remove_parser = _SubParsersAction_remove_parser # type: ignore[attr-defined]
|
|
542
|
+
|
|
497
543
|
|
|
498
544
|
class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
499
545
|
"""Custom ArgumentParser class that improves error and help output."""
|
|
@@ -556,7 +602,7 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
556
602
|
self.description: RenderableType | None # type: ignore[assignment]
|
|
557
603
|
self.epilog: RenderableType | None # type: ignore[assignment]
|
|
558
604
|
|
|
559
|
-
def
|
|
605
|
+
def get_subparsers_action(self) -> "argparse._SubParsersAction[Cmd2ArgumentParser]":
|
|
560
606
|
"""Get the _SubParsersAction for this parser if it exists.
|
|
561
607
|
|
|
562
608
|
:return: the _SubParsersAction for this parser
|
|
@@ -619,7 +665,7 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
619
665
|
self.prog = prog
|
|
620
666
|
|
|
621
667
|
try:
|
|
622
|
-
subparsers_action = self.
|
|
668
|
+
subparsers_action = self.get_subparsers_action()
|
|
623
669
|
except ValueError:
|
|
624
670
|
# This parser has no subcommands
|
|
625
671
|
return
|
|
@@ -638,12 +684,14 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
638
684
|
# add_parser() will have the correct prog value.
|
|
639
685
|
subparsers_action._prog_prefix = self._build_subparsers_prog_prefix(positionals)
|
|
640
686
|
|
|
641
|
-
# subparsers_action.
|
|
642
|
-
# we skip already updated parsers to ensure primary names are used in 'prog'.
|
|
687
|
+
# subparsers_action._name_parser_map includes aliases. Since primary names are inserted
|
|
688
|
+
# first, we skip already updated parsers to ensure primary names are used in 'prog'.
|
|
689
|
+
# We can't rely on subparsers_action._choices_actions to filter out aliases because while
|
|
690
|
+
# it contains only primary names, it omits any subcommands that lack help text.
|
|
643
691
|
updated_parsers: set[Cmd2ArgumentParser] = set()
|
|
644
692
|
|
|
645
693
|
# Set the prog value for each subcommand's parser
|
|
646
|
-
for subcmd_name, subcmd_parser in subparsers_action.
|
|
694
|
+
for subcmd_name, subcmd_parser in subparsers_action._name_parser_map.items():
|
|
647
695
|
if subcmd_parser in updated_parsers:
|
|
648
696
|
continue
|
|
649
697
|
|
|
@@ -651,7 +699,7 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
651
699
|
subcmd_parser.update_prog(subcmd_prog)
|
|
652
700
|
updated_parsers.add(subcmd_parser)
|
|
653
701
|
|
|
654
|
-
def
|
|
702
|
+
def find_parser(self, subcommand_path: Iterable[str]) -> "Cmd2ArgumentParser":
|
|
655
703
|
"""Find a parser in the hierarchy based on a sequence of subcommand names.
|
|
656
704
|
|
|
657
705
|
:param subcommand_path: sequence of subcommand names leading to the target parser
|
|
@@ -660,10 +708,10 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
660
708
|
"""
|
|
661
709
|
parser = self
|
|
662
710
|
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.
|
|
711
|
+
subparsers_action = parser.get_subparsers_action()
|
|
712
|
+
if name not in subparsers_action._name_parser_map:
|
|
713
|
+
raise ValueError(f"Subcommand '{name}' does not exist for '{parser.prog}'")
|
|
714
|
+
parser = subparsers_action._name_parser_map[name]
|
|
667
715
|
return parser
|
|
668
716
|
|
|
669
717
|
def attach_subcommand(
|
|
@@ -683,7 +731,8 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
683
731
|
:raises TypeError: if subcommand_parser is not an instance of the following or their subclasses:
|
|
684
732
|
1. Cmd2ArgumentParser
|
|
685
733
|
2. The parser_class configured for the target subcommand group
|
|
686
|
-
:raises ValueError: if the command path is invalid
|
|
734
|
+
:raises ValueError: if the command path is invalid, doesn't support subcommands, or the
|
|
735
|
+
subcommand already exists
|
|
687
736
|
"""
|
|
688
737
|
if not isinstance(subcommand_parser, Cmd2ArgumentParser):
|
|
689
738
|
raise TypeError(
|
|
@@ -691,8 +740,8 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
691
740
|
f"Received: '{type(subcommand_parser).__name__}'."
|
|
692
741
|
)
|
|
693
742
|
|
|
694
|
-
target_parser = self.
|
|
695
|
-
subparsers_action = target_parser.
|
|
743
|
+
target_parser = self.find_parser(subcommand_path)
|
|
744
|
+
subparsers_action = target_parser.get_subparsers_action()
|
|
696
745
|
|
|
697
746
|
# Verify the parser is compatible with the 'parser_class' configured for this
|
|
698
747
|
# subcommand group. We use isinstance() here to allow for subclasses, providing
|
|
@@ -705,6 +754,12 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
705
754
|
f"Received: '{type(subcommand_parser).__name__}'."
|
|
706
755
|
)
|
|
707
756
|
|
|
757
|
+
# Do not overwrite existing subcommands or aliases
|
|
758
|
+
all_names = (subcommand, *add_parser_kwargs.get("aliases", ()))
|
|
759
|
+
for name in all_names:
|
|
760
|
+
if name in subparsers_action._name_parser_map:
|
|
761
|
+
raise ValueError(f"Subcommand '{name}' already exists for '{target_parser.prog}'")
|
|
762
|
+
|
|
708
763
|
# Use add_parser to register the subcommand name and any aliases
|
|
709
764
|
placeholder_parser = subparsers_action.add_parser(subcommand, **add_parser_kwargs)
|
|
710
765
|
|
|
@@ -728,28 +783,16 @@ class Cmd2ArgumentParser(argparse.ArgumentParser):
|
|
|
728
783
|
:return: the detached parser
|
|
729
784
|
:raises ValueError: if the command path is invalid or the subcommand doesn't exist
|
|
730
785
|
"""
|
|
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
|
|
786
|
+
target_parser = self.find_parser(subcommand_path)
|
|
787
|
+
subparsers_action = target_parser.get_subparsers_action()
|
|
751
788
|
|
|
752
|
-
|
|
789
|
+
try:
|
|
790
|
+
return cast(
|
|
791
|
+
Cmd2ArgumentParser,
|
|
792
|
+
subparsers_action.remove_parser(subcommand), # type: ignore[attr-defined]
|
|
793
|
+
)
|
|
794
|
+
except ValueError:
|
|
795
|
+
raise ValueError(f"Subcommand '{subcommand}' does not exist for '{target_parser.prog}'") from None
|
|
753
796
|
|
|
754
797
|
def error(self, message: str) -> NoReturn:
|
|
755
798
|
"""Override that applies custom formatting to the error message."""
|