From 78860838e7a999adbae4ef1f8ea770706c021a75 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 1 Aug 2026 19:27:41 +0300 Subject: [PATCH 1/2] gh-155042: Support copy.replace() for email.policy.Policy Co-Authored-By: Claude Opus 5 (1M context) --- Doc/library/email.policy.rst | 5 +++++ Lib/email/_policybase.py | 2 ++ Lib/test/test_email/test_policy.py | 11 +++++++++++ .../2026-08-01-19-03-00.gh-issue-155042.Em1Pol.rst | 1 + 4 files changed, 19 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-08-01-19-03-00.gh-issue-155042.Em1Pol.rst diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst index 816d02d86f4fc4b..caec568407a0ad4 100644 --- a/Doc/library/email.policy.rst +++ b/Doc/library/email.policy.rst @@ -254,6 +254,11 @@ added matters. To illustrate:: values as the current instance, except where those attributes are given new values by the keyword arguments. + This method is also used by :func:`copy.replace`. + + .. versionchanged:: next + Added support of :func:`copy.replace`. + The remaining :class:`Policy` methods are called by the email package code, and are not intended to be called by an application using the email package. diff --git a/Lib/email/_policybase.py b/Lib/email/_policybase.py index e23843df44881f3..3e87a28c3fef1f8 100644 --- a/Lib/email/_policybase.py +++ b/Lib/email/_policybase.py @@ -84,6 +84,8 @@ def clone(self, **kw): object.__setattr__(newpolicy, attr, value) return newpolicy + __replace__ = clone + def __setattr__(self, name, value): if hasattr(self, name): msg = "{!r} object attribute {!r} is read-only" diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py index 90e8e5580295f9b..3625896fd23b84b 100644 --- a/Lib/test/test_email/test_policy.py +++ b/Lib/test/test_email/test_policy.py @@ -1,3 +1,4 @@ +import copy import io import types import textwrap @@ -254,6 +255,16 @@ def test_clone_copies_factory(self): h = policy2.header_factory('foo', 'test') self.assertIsInstance(h, self.Foo) + def test_copy_replace(self): + policy = email.policy.default + new = copy.replace(policy, max_line_length=100) + self.assertIsInstance(new, type(policy)) + self.assertEqual(new.max_line_length, 100) + self.assertEqual(new.linesep, policy.linesep) + self.assertEqual(policy.max_line_length, 78) + with self.assertRaises(TypeError): + copy.replace(policy, no_such_attribute=1) + def test_new_factory_overrides_default(self): mypolicy = email.policy.EmailPolicy() myfactory = mypolicy.header_factory diff --git a/Misc/NEWS.d/next/Library/2026-08-01-19-03-00.gh-issue-155042.Em1Pol.rst b/Misc/NEWS.d/next/Library/2026-08-01-19-03-00.gh-issue-155042.Em1Pol.rst new file mode 100644 index 000000000000000..c31ccdef29de39a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-01-19-03-00.gh-issue-155042.Em1Pol.rst @@ -0,0 +1 @@ +:class:`email.policy.Policy` objects now support :func:`copy.replace`. From 6987efb2bc6243d95da3a54b414f381ab29bbe86 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 1 Aug 2026 21:48:09 +0300 Subject: [PATCH 2/2] Say "support for" instead of "support of" Co-Authored-By: Claude Opus 5 (1M context) --- Doc/library/email.policy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst index caec568407a0ad4..a179acfa4c31f1b 100644 --- a/Doc/library/email.policy.rst +++ b/Doc/library/email.policy.rst @@ -257,7 +257,7 @@ added matters. To illustrate:: This method is also used by :func:`copy.replace`. .. versionchanged:: next - Added support of :func:`copy.replace`. + Added support for :func:`copy.replace`. The remaining :class:`Policy` methods are called by the email package code,