From 8eb006ebb4e39f324f3fb20f01409c4a2c6414cb Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Fri, 13 Feb 2026 14:20:27 +0100 Subject: [PATCH 1/8] Make sure that ArgumentParser instances are pickleable --- Lib/argparse.py | 11 +++++++---- Lib/test/test_argparse.py | 19 +++++++++++++++++++ ...-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst | 10 ++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst diff --git a/Lib/argparse.py b/Lib/argparse.py index 0494b545f2f1d3..25a3e977681e18 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -148,6 +148,11 @@ def _copy_items(items): return copy.copy(items) +def _identity(value): + # Defined at module scope so that ArgumentParser instances are pickleable. + return value + + # =============== # Formatting Help # =============== @@ -199,7 +204,7 @@ def _set_color(self, color, *, file=None): self._decolor = decolor else: self._theme = get_theme(force_no_color=True).argparse - self._decolor = lambda text: text + self._decolor = _identity # =============================== # Section and indentation methods @@ -1981,9 +1986,7 @@ def __init__(self, self._subparsers = None # register types - def identity(string): - return string - self.register('type', None, identity) + self.register('type', None, _identity) # add help argument if necessary # (using explicit default to override global argument_default) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 78f02f70b9f0fc..1ed8da82be4063 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -80,6 +80,25 @@ def test_skip_invalid_stdout(self): self.assertRegex(mocked_stderr.getvalue(), r'usage:') +class TestArgumentParserPickleable(unittest.TestCase): + + @mock.patch.dict(os.environ, {'NO_COLOR': 'true'}) + def test_pickle_roundtrip(self): + import pickle + parser = argparse.ArgumentParser() + parser.add_argument('--foo', type=int, default=42) + parser.add_argument('bar', nargs='?', default='baz') + # Try to pickle and unpickle the parser + parser2 = pickle.loads(pickle.dumps(parser)) + # Check that the round-tripped parser still works + 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') + + class TestCase(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst b/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst new file mode 100644 index 00000000000000..d63dfe9502ae7c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst @@ -0,0 +1,10 @@ +Make sure that ``argparse.ArgumentParser`` is pickleable. + +#.. section: Documentation #.. section: Tests #.. section: Build #.. +section: Windows #.. section: macOS #.. section: IDLE #.. section: +Tools/Demos #.. section: C API + +# Write your Misc/NEWS.d entry below. It should be a simple ReST paragraph. +# Don't start with "- Issue #: " or "- gh-issue-: " or that sort of +stuff. +########################################################################### From 262f062b2ef4b1c49bf211c647c6dc87a6ea9974 Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Fri, 13 Feb 2026 15:02:55 +0100 Subject: [PATCH 2/8] Apply suggestion from @johnslavik MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartosz Sławecki --- .../2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst b/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst index d63dfe9502ae7c..0bfe7faef36af2 100644 --- a/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst +++ b/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst @@ -1,10 +1,2 @@ Make sure that ``argparse.ArgumentParser`` is pickleable. -#.. section: Documentation #.. section: Tests #.. section: Build #.. -section: Windows #.. section: macOS #.. section: IDLE #.. section: -Tools/Demos #.. section: C API - -# Write your Misc/NEWS.d entry below. It should be a simple ReST paragraph. -# Don't start with "- Issue #: " or "- gh-issue-: " or that sort of -stuff. -########################################################################### From eb8024802f699ac3269ec240c04842778779952a Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Fri, 13 Feb 2026 22:14:55 +0100 Subject: [PATCH 3/8] Update Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst Co-authored-by: AN Long --- .../Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst b/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst index 0bfe7faef36af2..d8dd9ccc649c97 100644 --- a/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst +++ b/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst @@ -1,2 +1 @@ -Make sure that ``argparse.ArgumentParser`` is pickleable. - +Make sure that :class:`argparse.ArgumentParser` is pickleable. From f1f2876262d433894a82efe129124fd0eb84872d Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Sat, 14 Feb 2026 07:49:11 +0100 Subject: [PATCH 4/8] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartosz Sławecki --- Lib/test/test_argparse.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 1ed8da82be4063..e93016c5c77e8f 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -82,10 +82,10 @@ def test_skip_invalid_stdout(self): class TestArgumentParserPickleable(unittest.TestCase): - @mock.patch.dict(os.environ, {'NO_COLOR': 'true'}) + @force_not_colorized def test_pickle_roundtrip(self): import pickle - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(exit_on_error=False) parser.add_argument('--foo', type=int, default=42) parser.add_argument('bar', nargs='?', default='baz') # Try to pickle and unpickle the parser From 37b0a2b18985de806bd6233edec5410711c0e27e Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Sat, 14 Feb 2026 07:49:43 +0100 Subject: [PATCH 5/8] Update Lib/argparse.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartosz Sławecki --- Lib/argparse.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index 25a3e977681e18..296a210ad832da 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -149,7 +149,6 @@ def _copy_items(items): def _identity(value): - # Defined at module scope so that ArgumentParser instances are pickleable. return value From d19672688e84340492dd2dd70fa04f98e8548551 Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:51:40 +0100 Subject: [PATCH 6/8] Test all protocols --- Lib/test/test_argparse.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index e93016c5c77e8f..4526efe4b80ef4 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -88,15 +88,17 @@ def test_pickle_roundtrip(self): parser = argparse.ArgumentParser(exit_on_error=False) parser.add_argument('--foo', type=int, default=42) parser.add_argument('bar', nargs='?', default='baz') - # Try to pickle and unpickle the parser - parser2 = pickle.loads(pickle.dumps(parser)) - # Check that the round-tripped parser still works - 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') + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + # Try to pickle and unpickle the parser + parser2 = pickle.loads(pickle.dumps(parser, protocol=proto)) + # Check that the round-tripped parser still works + 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') class TestCase(unittest.TestCase): From 2f28bdce215513f3798e73e8365340231886a514 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Sat, 14 Feb 2026 17:35:24 +0100 Subject: [PATCH 7/8] Add some documentation suggestions --- Doc/library/argparse.rst | 3 +++ .../Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index 60411b0a0c9748..5058290878d358 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -137,6 +137,9 @@ ArgumentParser objects .. versionchanged:: 3.15 *suggest_on_error* default changed to ``True``. + .. versionchanged:: next + :class:`~argparse.ArgumentParser` objects are now :mod:`pickleable `. + The following sections describe how each of these are used. diff --git a/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst b/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst index d8dd9ccc649c97..6209bea9eacaba 100644 --- a/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst +++ b/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst @@ -1 +1 @@ -Make sure that :class:`argparse.ArgumentParser` is pickleable. +Make :class:`argparse.ArgumentParser` is :mod:`pickleable `. From 858c44ef6f8c78ceff0e067a27ff92dd2be49ad9 Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Sat, 14 Feb 2026 20:09:47 +0100 Subject: [PATCH 8/8] Apply suggestion from @mauvilsa --- .../next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst b/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst index 6209bea9eacaba..95f047474b7ee9 100644 --- a/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst +++ b/Misc/NEWS.d/next/Library/2026-02-13-14-20-10.gh-issue-144782.0Y8TKj.rst @@ -1 +1 @@ -Make :class:`argparse.ArgumentParser` is :mod:`pickleable `. +Make :class:`argparse.ArgumentParser` :mod:`pickleable `.