From aad61ac85c49d5d7e62e86192bd1daa7b1d2974a Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Wed, 3 Jun 2026 19:18:20 -0400 Subject: [PATCH] Remove colorless theme monkeypatchfor argparse bug in 3.15.0b1 3.15.0b2 is out and available now in both uv and GitHub Actions. --- cmd2/argparse_utils.py | 24 ------------------------ tests/test_argparse_utils.py | 19 ------------------- 2 files changed, 43 deletions(-) diff --git a/cmd2/argparse_utils.py b/cmd2/argparse_utils.py index 7bdec4403..e4e7a69d7 100644 --- a/cmd2/argparse_utils.py +++ b/cmd2/argparse_utils.py @@ -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 diff --git a/tests/test_argparse_utils.py b/tests/test_argparse_utils.py index 7e10d4c25..4ab5dcff4 100644 --- a/tests/test_argparse_utils.py +++ b/tests/test_argparse_utils.py @@ -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