From 70a8f3a1a2a1a8355a7c457da4140bae630f3044 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Fri, 3 Oct 2025 20:20:38 -0400 Subject: [PATCH 1/3] feat: Restore CompletionItem.description Restores the `CompletionItem.description` field and expands its type to work with either `str` or `Sequence[str | rich.Column]`. This reverts a breaking change introduced in 3.0.0 and makes it easier for users to migrate from version 2.7.0. --- CHANGELOG.md | 4 ++-- cmd2/argparse_completer.py | 2 +- cmd2/argparse_custom.py | 22 +++++++++++++--------- tests/test_cmd2.py | 8 ++++---- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f08e1c178..5258f8b43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,8 +31,8 @@ time reading the [rich documentation](https://rich.readthedocs.io/). - `descriptive_header: str` replaced with `descriptive_headers: Sequence[str | rich.Column]` - Applies to parameter name when adding an argument to a parser as well as `set_descriptive_headers` and `get_descriptive_headers` - - `CompletionItem.description: str` changed to - `CompletionItem.descriptive_data: Sequence[str | rich.Column]` + - Restored `CompletionItem.description` and expanded its type to work with either `str` or + `Sequence[str | rich.Column]` - `decorators` module breaking changes: - `_set_parser_prog` renamed to `set_parser_prog` (without the leading underscore) and moved to `argparse_custom` module diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 523afad40..69f8de777 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -582,7 +582,7 @@ def _format_completions(self, arg_state: _ArgumentState, completions: list[str] border_style=Cmd2Style.TABLE_BORDER, ) for item in completion_items: - hint_table.add_row(item, *item.descriptive_data) + hint_table.add_row(item, *item.description) # Generate the hint table string console = Cmd2GeneralConsole() diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index 68541f316..508091e4c 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -139,7 +139,7 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens) that value's name. The right column header is defined using the ``descriptive_headers`` parameter of add_argument(), which is a list of header names that defaults to ["Description"]. The right column values come from the -``CompletionItem.descriptive_data`` member, which is a list with the same number +``CompletionItem.description`` member, which is a list with the same number of items as columns defined in descriptive_headers. To use CompletionItems, just return them from your choices_provider or @@ -166,7 +166,7 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens) def get_items(self) -> list[CompletionItems]: \"\"\"choices_provider which returns CompletionItems\"\"\" - # CompletionItem's second argument is descriptive_data. + # CompletionItem's second argument is description. # Its item count should match that of descriptive_headers. return [ CompletionItem(1, ["My item", True, "02/02/2022"]), @@ -194,14 +194,14 @@ def get_items(self) -> list[CompletionItems]: truncated with an ellipsis at the end. You can override this and other settings when you create the ``Column``. -``descriptive_data`` items can include Rich objects, including styled Text and Tables. +``description`` items can include Rich objects, including styled Text and Tables. To avoid printing a excessive information to the screen at once when a user presses tab, there is a maximum threshold for the number of CompletionItems that will be shown. Its value is defined in ``cmd2.Cmd.max_completion_items``. It defaults to 50, but can be changed. If the number of completion suggestions exceeds this number, they will be displayed in the typical columnized format -and will not include the descriptive_data of the CompletionItems. +and will not include the description of the CompletionItems. **Patched argparse functions** @@ -384,22 +384,26 @@ def __new__(cls, value: object, *_args: Any, **_kwargs: Any) -> 'CompletionItem' """Responsible for creating and returning a new instance, called before __init__ when an object is instantiated.""" return super().__new__(cls, value) - def __init__(self, value: object, descriptive_data: Sequence[Any], *args: Any) -> None: + def __init__(self, value: object, description: str | Sequence[Any], *args: Any) -> None: """CompletionItem Initializer. :param value: the value being tab completed - :param descriptive_data: a list of descriptive data to display in the columns that follow - the completion value. The number of items in this list must equal + :param description: a string or list of descriptive data to display in the columns that follow + the completion value. If a list, the number of items in this list must equal the number of descriptive headers defined for the argument. :param args: args for str __init__ """ super().__init__(*args) + # If description is a string, wrap it in a list + if isinstance(description, str): + description = [description] + # Make sure all objects are renderable by a Rich table. - renderable_data = [obj if is_renderable(obj) else str(obj) for obj in descriptive_data] + renderable_data = [obj if is_renderable(obj) else str(obj) for obj in description] # Convert strings containing ANSI style sequences to Rich Text objects for correct display width. - self.descriptive_data = ru.prepare_objects_for_rendering(*renderable_data) + self.description = ru.prepare_objects_for_rendering(*renderable_data) # Save the original value to support CompletionItems as argparse choices. # cmd2 has patched argparse so input is compared to this value instead of the CompletionItem instance. diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 070046b17..355ce3977 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -2306,7 +2306,7 @@ def test_get_alias_completion_items(base_app) -> None: for cur_res in results: assert cur_res in base_app.aliases # Strip trailing spaces from table output - assert cur_res.descriptive_data[0].rstrip() == base_app.aliases[cur_res] + assert cur_res.description[0].rstrip() == base_app.aliases[cur_res] def test_get_macro_completion_items(base_app) -> None: @@ -2319,7 +2319,7 @@ def test_get_macro_completion_items(base_app) -> None: for cur_res in results: assert cur_res in base_app.macros # Strip trailing spaces from table output - assert cur_res.descriptive_data[0].rstrip() == base_app.macros[cur_res].value + assert cur_res.description[0].rstrip() == base_app.macros[cur_res].value def test_get_settable_completion_items(base_app) -> None: @@ -2333,11 +2333,11 @@ def test_get_settable_completion_items(base_app) -> None: # These CompletionItem descriptions are a two column table (Settable Value and Settable Description) # First check if the description text starts with the value str_value = str(cur_settable.value) - assert cur_res.descriptive_data[0].startswith(str_value) + assert cur_res.description[0].startswith(str_value) # The second column is likely to have wrapped long text. So we will just examine the # first couple characters to look for the Settable's description. - assert cur_settable.description[0:10] in cur_res.descriptive_data[1] + assert cur_settable.description[0:10] in cur_res.description[1] def test_alias_no_subcommand(base_app) -> None: From 7faed3b75e47fb5a1a439635cd8be5c53e04eaf9 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Fri, 3 Oct 2025 20:38:53 -0400 Subject: [PATCH 2/3] Improved working in Changelog --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5258f8b43..f1a944eff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,8 +31,7 @@ time reading the [rich documentation](https://rich.readthedocs.io/). - `descriptive_header: str` replaced with `descriptive_headers: Sequence[str | rich.Column]` - Applies to parameter name when adding an argument to a parser as well as `set_descriptive_headers` and `get_descriptive_headers` - - Restored `CompletionItem.description` and expanded its type to work with either `str` or - `Sequence[str | rich.Column]` + - `CompletionItem.description` type expanded from `str` to `str | Sequence[Any]` - `decorators` module breaking changes: - `_set_parser_prog` renamed to `set_parser_prog` (without the leading underscore) and moved to `argparse_custom` module From 4bb80ba6b1fb8d91e25a120d42069303cd6fc5b5 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Fri, 3 Oct 2025 20:44:29 -0400 Subject: [PATCH 3/3] Update cmd2/argparse_custom.py Improve docstring to make it more accurate Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- cmd2/argparse_custom.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index 508091e4c..fe70c05fb 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -388,8 +388,8 @@ def __init__(self, value: object, description: str | Sequence[Any], *args: Any) """CompletionItem Initializer. :param value: the value being tab completed - :param description: a string or list of descriptive data to display in the columns that follow - the completion value. If a list, the number of items in this list must equal + :param description: a string or sequence of descriptive data to display in the columns that follow + the completion value. If a sequence, the number of items in this sequence must equal the number of descriptive headers defined for the argument. :param args: args for str __init__ """