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
24 changes: 0 additions & 24 deletions cmd2/argparse_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,30 +423,6 @@ def _validate_completion_callable(self: argparse.Action, value: Any) -> Any:
register_argparse_argument_parameter("nargs_range")
register_argparse_argument_parameter("suppress_tab_hint")

############################################################################################################
# Workaround for Python 3.15.0b1 argparse bug
# _ColorlessTheme.__getattr__ incorrectly returns "" for non-public attributes, which breaks
# protocols like copy.deepcopy().
############################################################################################################

if sys.version_info >= (3, 15):

def _ColorlessTheme_getattr( # noqa: N802
_self: argparse._ColorlessTheme, # type: ignore[name-defined]
name: str,
) -> Any:
"""Patched __getattr__ that allows non-public lookups to fail correctly.

This matches the implementation in CPython for their next release.
"""
if name.startswith("_"):
raise AttributeError(name)
return ""

# If the bug still exists, then install the patch.
if getattr(argparse._ColorlessTheme(), "__deepcopy__", None) == "": # type: ignore[attr-defined]
argparse._ColorlessTheme.__getattr__ = _ColorlessTheme_getattr # type: ignore[attr-defined]


############################################################################################################
# Patch _ActionsContainer.add_argument to support more arguments
Expand Down
19 changes: 0 additions & 19 deletions tests/test_argparse_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -863,22 +863,3 @@ def test_deprecated_subcommand() -> None:
# Verify it was removed from _deprecated set
assert "old" not in subparsers_action._deprecated # type: ignore[attr-defined]
assert "old_alias" not in subparsers_action._deprecated # type: ignore[attr-defined]


@pytest.mark.skipif(
sys.version_info < (3, 15),
reason="_ColorlessTheme only exists in 3.15+",
)
def test_colorless_theme_monkeypatch() -> None:
"""Test the _ColorlessTheme.__getattr__ monkey patch."""

# If this assertion fails, then the bug no longer exists and our patch wasn't installed.
# We can remove the patch function and this test.
assert argparse._ColorlessTheme.__getattr__ == argparse_utils._ColorlessTheme_getattr

# Our patch raises an Attribute error for non-public.
with pytest.raises(AttributeError):
getattr(argparse._ColorlessTheme(), "_fake") # noqa: B009

with pytest.raises(AttributeError):
getattr(argparse._ColorlessTheme(), "__deepcopy__") # noqa: B009
Loading