Original file line numberDiff line numberDiff line change@@ -828,6 +828,13 @@ that may require changes to your code.
828828:exc:`TypeError`.
829829 (Contributed by Serhiy Storchaka in :gh:`152587`.)
830830831+* On Windows, seeking a pipe now fails instead of silently appearing to
832+ succeed: :func:`os.lseek` and :meth:`~io.IOBase.seek` raise :exc:`OSError`,
833+ and :meth:`~io.IOBase.seekable` returns ``False``. As a consequence,
834+ opening a pipe in a read-write binary mode (``'r+b'`` or ``'w+b'``) now
835+ raises :exc:`io.UnsupportedOperation` unless buffering is disabled.
836+ (Contributed by An Long in :gh:`86768`.)
837+831838832839Build changes
833840=============
Original file line numberDiff line numberDiff line change@@ -2993,6 +2993,14 @@ def test_ftruncate(self):
29932993deftest_lseek(self):
29942994self.check(os.lseek, 0, 0)
[email protected](hasattr(os, 'lseek'), 'test needs os.lseek()')
[email protected](hasattr(os, 'pipe'), "need os.pipe()")
2998+deftest_lseek_on_pipe(self):
2999+rfd, wfd=os.pipe()
3000+self.addCleanup(os.close, rfd)
3001+self.addCleanup(os.close, wfd)
3002+self.assertRaises(OSError, os.lseek, rfd, 123, os.SEEK_END)
[email protected](hasattr(os, 'read'), 'test needs os.read()')
29973005deftest_read(self):
29983006self.check(os.read, 1)
Original file line numberDiff line numberDiff line change@@ -152,7 +152,7 @@ def test_namedpipe(self):
152152# Pipe instance is available, so this passes
153153_winapi.WaitNamedPipe(pipe_name, 0)
154154155-withopen(pipe_name, 'w+b') aspipe2:
155+withopen(pipe_name, 'w+b', buffering=0) aspipe2:
156156# No instances available, so this times out
157157# (WinError 121 does not get mapped to TimeoutError)
158158withself.assertRaises(OSError):
Original file line numberDiff line numberDiff line change@@ -0,0 +1,6 @@
1+:func:`os.lseek` and :meth:`~io.IOBase.seek` of file objects now raise
2+:exc:`OSError` for pipes on Windows, and :meth:`~io.IOBase.seekable` now
3+returns ``False`` for them. Previously seeking a pipe silently appeared to
4+succeed. As a consequence, opening a pipe in a read-write binary mode
5+(``'r+b'`` or ``'w+b'``) now raises :exc:`io.UnsupportedOperation` unless
6+buffering is disabled.
Original file line numberDiff line numberDiff line change@@ -992,7 +992,14 @@ portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_er
992992Py_BEGIN_ALLOW_THREADS
993993_Py_BEGIN_SUPPRESS_IPH
994994#ifdefMS_WINDOWS
995-res=_lseeki64(fd, pos, whence);
995+HANDLEh= (HANDLE)_get_osfhandle(fd);
996+if (h!=INVALID_HANDLE_VALUE&&GetFileType(h) ==FILE_TYPE_PIPE) {
997+res=-1;
998+errno=ESPIPE;
999+ }
1000+else {
1001+res=_lseeki64(fd, pos, whence);
1002+ }
9961003#else
9971004res=lseek(fd, pos, whence);
9981005#endif
Original file line numberDiff line numberDiff line change@@ -12037,7 +12037,7 @@ static Py_off_t
1203712037os_lseek_impl(PyObject *module, int fd, Py_off_t position, int how)
1203812038/*[clinic end generated code: output=971e1efb6b30bd2f input=32ea0788da7cb44b]*/
1203912039{
12040- Py_off_t result;
12040+ Py_off_t result = -1;
12041120411204212042#ifdef SEEK_SET
1204312043 /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */
@@ -12051,14 +12051,21 @@ os_lseek_impl(PyObject *module, int fd, Py_off_t position, int how)
1205112051 Py_BEGIN_ALLOW_THREADS
1205212052 _Py_BEGIN_SUPPRESS_IPH
1205312053#ifdef MS_WINDOWS
12054- result = _lseeki64(fd, position, how);
12054+ HANDLE h = (HANDLE)_get_osfhandle(fd);
12055+ if (h != INVALID_HANDLE_VALUE && GetFileType(h) == FILE_TYPE_PIPE) {
12056+ errno = ESPIPE;
12057+ }
12058+ else {
12059+ result = _lseeki64(fd, position, how);
12060+ }
1205512061#else
1205612062 result = lseek(fd, position, how);
1205712063#endif
1205812064 _Py_END_SUPPRESS_IPH
1205912065 Py_END_ALLOW_THREADS
12060- if (result < 0)
12066+ if (result < 0) {
1206112067 posix_error();
12068+ }
12062120691206312070 return result;
1206412071}