gh-86768: Raise OSError when seeking a pipe on Windows (GH-133137) · python/cpython@d65bf51

GitHub

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}