From 53bea44f358b5a5c9b76e7b52f97c2aa3e93a0e4 Mon Sep 17 00:00:00 2001
From: Cody Maloney <cmaloney@theoreticalchaos.com>
Date: Tue, 13 May 2025 11:14:41 -0700
Subject: [PATCH 1/4] gh-71253: Match _io exception in _pyio

Test was only testing _io, expanded to cover _pyio.
---
 Lib/_pyio.py        | 3 ++-
 Lib/test/test_io.py | 4 ++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index a870de5b532542..f79674fb7a9f5e 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -1563,7 +1563,8 @@ def __init__(self, file, mode='r', closefd=True, opener=None):
                     if not isinstance(fd, int):
                         raise TypeError('expected integer from opener')
                     if fd < 0:
-                        raise OSError('Negative file descriptor')
+                        # bpo-27066: Raise a ValueError for bad value.
+                        raise ValueError(f'opener returned {fd}')
                 owned_fd = fd
                 if not noinherit_flag:
                     os.set_inheritable(fd, False)
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index 5a8f1949baaa98..a12bd82fea9cd6 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -918,7 +918,7 @@ def test_bad_opener_negative_1(self):
         def badopener(fname, flags):
             return -1
         with self.assertRaises(ValueError) as cm:
-            open('non-existent', 'r', opener=badopener)
+            self.open('non-existent', 'r', opener=badopener)
         self.assertEqual(str(cm.exception), 'opener returned -1')
 
     def test_bad_opener_other_negative(self):
@@ -926,7 +926,7 @@ def test_bad_opener_other_negative(self):
         def badopener(fname, flags):
             return -2
         with self.assertRaises(ValueError) as cm:
-            open('non-existent', 'r', opener=badopener)
+            self.open('non-existent', 'r', opener=badopener)
         self.assertEqual(str(cm.exception), 'opener returned -2')
 
     def test_opener_invalid_fd(self):

From 28d198e63d66e7c4b38a3eebc966675d4a7a9809 Mon Sep 17 00:00:00 2001
From: Cody Maloney <cmaloney@theoreticalchaos.com>
Date: Tue, 13 May 2025 18:22:03 -0400
Subject: [PATCH 2/4] Add blurb

---
 .../next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst  | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst

diff --git a/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst b/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst
new file mode 100644
index 00000000000000..9fedaec06af721
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst
@@ -0,0 +1,2 @@
+Raise ValueError if an opener returns a negative fd in ``_pyio``
+:func:`open` to match ``_io``

From f15a811e0445acd16dec7cbd00ec3aa75a5e9a86 Mon Sep 17 00:00:00 2001
From: Cody Maloney <cmaloney@users.noreply.github.com>
Date: Thu, 15 May 2025 09:38:50 -0400
Subject: [PATCH 3/4] Update
 Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst

Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
---
 .../next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst b/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst
index 9fedaec06af721..a92cdc7017ea05 100644
--- a/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst
+++ b/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst
@@ -1,2 +1,2 @@
-Raise ValueError if an opener returns a negative fd in ``_pyio``
+Raise :exc:`ValueError` if an opener returns a negative file-descriptor in ``_pyio``
 :func:`open` to match ``_io``

From 2ecd0267535b30939629326e180df0b89fa73c7b Mon Sep 17 00:00:00 2001
From: Cody Maloney <cmaloney@theoreticalchaos.com>
Date: Thu, 15 May 2025 09:49:03 -0400
Subject: [PATCH 4/4] Tweak blurb with review comments

---
 .../Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst    | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst b/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst
index a92cdc7017ea05..714d707f488709 100644
--- a/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst
+++ b/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst
@@ -1,2 +1,3 @@
-Raise :exc:`ValueError` if an opener returns a negative file-descriptor in ``_pyio``
-:func:`open` to match ``_io``
+Raise :exc:`ValueError` in :func:`open` if *opener* returns a negative
+file-descriptor in the Python implementation of :mod:`io` to match the
+C implementation.