Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions cmd2/completion.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Provides classes and functions related to command-line completion."""

import copy
import re
import sys
from collections.abc import (
Expand Down Expand Up @@ -124,6 +125,19 @@ def __post_init__(self) -> None:
ru.prepare_objects_for_rendering(*renderable_data),
)

def __deepcopy__(self, memo: dict[int, Any]) -> "CompletionItem":
"""Return a shallow copy of this CompletionItem during a deepcopy operation.

This is necessary because cmd2 deepcopies argument parsers to keep them unique
across command instances. This override prevents the deepcopying of
CompletionItems stored within a parser's 'choices' list.

Since the 'value' and 'table_data' fields may contain complex objects which
should not be deep copied, a shallow copy ensures the original object
references are preserved.
"""
return copy.copy(self)
Comment thread
tleonhardt marked this conversation as resolved.

def __str__(self) -> str:
"""Return the completion text."""
return self.text
Expand Down
31 changes: 31 additions & 0 deletions tests/test_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""

import argparse
import copy
import dataclasses
import enum
import os
Expand Down Expand Up @@ -919,6 +920,36 @@ def test_remove_duplicates() -> None:
assert new_meta in completions


def test_completion_item_deepcopy() -> None:
"""Test that deepcopy of a CompletionItem preserves identity of its members."""

class ComplexValue:
pass

value = ComplexValue()
table_data = (ComplexValue(),)
orig_item = CompletionItem(
value=value,
text="my_text",
display="my_display",
display_meta="my_meta",
table_data=table_data,
)

# Perform deepcopy
copied_item = copy.deepcopy(orig_item)

# We should have a new object
assert copied_item is not orig_item

# But its member should be the same objects
assert copied_item.value is orig_item.value
assert copied_item.text is orig_item.text
assert copied_item.display is orig_item.display
assert copied_item.display_meta is orig_item.display_meta
assert copied_item.table_data is orig_item.table_data


def test_plain_fields() -> None:
"""Test the plain text fields in CompletionItem."""
display = "\x1b[31mApple\x1b[0m"
Expand Down
Loading