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
2 changes: 2 additions & 0 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ class _ColorlessTheme:
def __getattr__(self, name):
# _colorize's no_color themes are just all empty strings
# by directly using empty strings the import is avoided
if name.startswith("_"):
Comment thread
savannahostrowski marked this conversation as resolved.
raise AttributeError(name)
return ""

_colorless_theme = _ColorlessTheme()
Expand Down
55 changes: 55 additions & 0 deletions Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,48 @@ def test_parse_args(self):
)


class TestArgumentParserCopiable(unittest.TestCase):
Comment thread
DavidCEllis marked this conversation as resolved.
def _get_parser(self):
parser = argparse.ArgumentParser(exit_on_error=False)
parser.add_argument('--foo', type=int, default=42)
parser.add_argument('bar', nargs='?', default='baz')
return parser

@force_not_colorized
def test_copiable(self):
Comment thread
savannahostrowski marked this conversation as resolved.
import copy
parser = self._get_parser()
parser2 = copy.copy(parser)
ns = parser2.parse_args(['--foo', '123', 'quux'])
self.assertEqual(ns.foo, 123)
self.assertEqual(ns.bar, 'quux')
ns2 = parser2.parse_args([])
self.assertEqual(ns2.foo, 42)
self.assertEqual(ns2.bar, 'baz')

# Test shallow copy also gets new arguments
parser.add_argument("--extra")
ns3 = parser2.parse_args(["--extra", "bar"])
self.assertEqual(ns3.extra, "bar")

@force_not_colorized
def test_deepcopiable(self):
import copy
parser = self._get_parser()
parser2 = copy.deepcopy(parser)
ns = parser2.parse_args(['--foo', '123', 'quux'])
Comment thread
savannahostrowski marked this conversation as resolved.
self.assertEqual(ns.foo, 123)
self.assertEqual(ns.bar, 'quux')
ns2 = parser2.parse_args([])
self.assertEqual(ns2.foo, 42)
self.assertEqual(ns2.bar, 'baz')

# Test deep copy does not get new arguments
parser.add_argument("--extra")
with self.assertRaises(argparse.ArgumentError):
parser2.parse_args(["--extra", "bar"])


class TestArgumentParserPickleable(unittest.TestCase):

@force_not_colorized
Expand Down Expand Up @@ -7863,12 +7905,25 @@ def fake_can_colorize(*, file=None):

def test_fake_color_theme_matches_real(self):
from argparse import _colorless_theme

# Check the attributes match those of the 'real' theme
_colorize_nocolor = _colorize.get_theme(force_no_color=True).argparse
for k in _colorize_nocolor:
self.assertEqual(
getattr(_colorless_theme, k), getattr(_colorize_nocolor, k)
)

def test_fake_color_theme_raises(self):
from argparse import _colorless_theme

# Make sure the _colorless_theme doesn't return empty strings
# for magic methods or private attributes
with self.assertRaises(AttributeError):
_colorless_theme.__unknown_dunder__

with self.assertRaises(AttributeError):
_colorless_theme._private_attribute


class TestModule(unittest.TestCase):
def test_deprecated__version__(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a regression that broke the ability to deepcopy :class:`argparse.ArgumentParser` instances.
Loading