See also :func:`set_blocking` and :meth:`socket.socket.setblocking`.
- .. availability:: Unix.
+ .. availability:: Unix, Windows.
The function is limited on Emscripten and WASI, see
:ref:`wasm-availability` for more information.
+ On Windows, this function is limited to pipes.
+
.. versionadded:: 3.5
+ .. versionchanged:: 3.12
+ Added support for pipes on Windows.
.. function:: isatty(fd, /)
See also :func:`get_blocking` and :meth:`socket.socket.setblocking`.
- .. availability:: Unix.
+ .. availability:: Unix, Windows.
The function is limited on Emscripten and WASI, see
:ref:`wasm-availability` for more information.
+ On Windows, this function is limited to pipes.
+
.. versionadded:: 3.5
+ .. versionchanged:: 3.12
+ Added support for pipes on Windows.
.. data:: SF_NODISKIO
SF_MNOWAIT
PyAPI_FUNC(int) _Py_dup(int fd);
-#ifndef MS_WINDOWS
PyAPI_FUNC(int) _Py_get_blocking(int fd);
PyAPI_FUNC(int) _Py_set_blocking(int fd, int blocking);
-#else /* MS_WINDOWS */
+
+#ifdef MS_WINDOWS
PyAPI_FUNC(void*) _Py_get_osfhandle_noraise(int fd);
PyAPI_FUNC(void*) _Py_get_osfhandle(int fd);
@unittest.skipUnless(hasattr(os, 'get_blocking'),
'needs os.get_blocking() and os.set_blocking()')
@unittest.skipIf(support.is_emscripten, "Cannot unset blocking flag")
+@unittest.skipIf(sys.platform == 'win32', 'Windows only supports blocking on pipes')
class BlockingTests(unittest.TestCase):
def test_blocking(self):
fd = os.open(__file__, os.O_RDONLY)
--- /dev/null
+Add support for the os.get_blocking() and os.set_blocking() functions on Windows.
--- /dev/null
+Handle read and write operations on non-blocking pipes properly on Windows.
#endif /* defined(MS_WINDOWS) */
-#if !defined(MS_WINDOWS)
-
PyDoc_STRVAR(os_get_blocking__doc__,
"get_blocking($module, fd, /)\n"
"--\n"
return return_value;
}
-#endif /* !defined(MS_WINDOWS) */
-
-#if !defined(MS_WINDOWS)
-
PyDoc_STRVAR(os_set_blocking__doc__,
"set_blocking($module, fd, blocking, /)\n"
"--\n"
return return_value;
}
-#endif /* !defined(MS_WINDOWS) */
-
PyDoc_STRVAR(os_DirEntry_is_symlink__doc__,
"is_symlink($self, /)\n"
"--\n"
#define OS_SET_HANDLE_INHERITABLE_METHODDEF
#endif /* !defined(OS_SET_HANDLE_INHERITABLE_METHODDEF) */
-#ifndef OS_GET_BLOCKING_METHODDEF
- #define OS_GET_BLOCKING_METHODDEF
-#endif /* !defined(OS_GET_BLOCKING_METHODDEF) */
-
-#ifndef OS_SET_BLOCKING_METHODDEF
- #define OS_SET_BLOCKING_METHODDEF
-#endif /* !defined(OS_SET_BLOCKING_METHODDEF) */
-
#ifndef OS_GETRANDOM_METHODDEF
#define OS_GETRANDOM_METHODDEF
#endif /* !defined(OS_GETRANDOM_METHODDEF) */
#ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF
#define OS_WAITSTATUS_TO_EXITCODE_METHODDEF
#endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */
-/*[clinic end generated code: output=a3f76228b549e8ec input=a9049054013a1b77]*/
+/*[clinic end generated code: output=1b0eb6a76b1a0e28 input=a9049054013a1b77]*/
}
#endif /* MS_WINDOWS */
-#ifndef MS_WINDOWS
/*[clinic input]
os.get_blocking -> bool
fd: int
return NULL;
Py_RETURN_NONE;
}
-#endif /* !MS_WINDOWS */
/*[clinic input]
Py_BEGIN_ALLOW_THREADS
errno = 0;
#ifdef MS_WINDOWS
+ _doserrno = 0;
n = read(fd, buf, (int)count);
+ // read() on a non-blocking empty pipe fails with EINVAL, which is
+ // mapped from the Windows error code ERROR_NO_DATA.
+ if (n < 0 && errno == EINVAL) {
+ if (_doserrno == ERROR_NO_DATA) {
+ errno = EAGAIN;
+ }
+ }
#else
n = read(fd, buf, count);
#endif
}
}
}
+
#endif
if (count > _PY_WRITE_MAX) {
count = _PY_WRITE_MAX;
Py_BEGIN_ALLOW_THREADS
errno = 0;
#ifdef MS_WINDOWS
- n = write(fd, buf, (int)count);
+ // write() on a non-blocking pipe fails with ENOSPC on Windows if
+ // the pipe lacks available space for the entire buffer.
+ int c = (int)count;
+ do {
+ _doserrno = 0;
+ n = write(fd, buf, c);
+ if (n >= 0 || errno != ENOSPC || _doserrno != 0) {
+ break;
+ }
+ errno = EAGAIN;
+ c /= 2;
+ } while (c > 0);
#else
n = write(fd, buf, count);
#endif
do {
errno = 0;
#ifdef MS_WINDOWS
- n = write(fd, buf, (int)count);
+ // write() on a non-blocking pipe fails with ENOSPC on Windows if
+ // the pipe lacks available space for the entire buffer.
+ int c = (int)count;
+ do {
+ _doserrno = 0;
+ n = write(fd, buf, c);
+ if (n >= 0 || errno != ENOSPC || _doserrno != 0) {
+ break;
+ }
+ errno = EAGAIN;
+ c /= 2;
+ } while (c > 0);
#else
n = write(fd, buf, count);
#endif
return -1;
}
#else /* MS_WINDOWS */
+int
+_Py_get_blocking(int fd)
+{
+ HANDLE handle;
+ DWORD mode;
+ BOOL success;
+
+ handle = _Py_get_osfhandle(fd);
+ if (handle == INVALID_HANDLE_VALUE) {
+ return -1;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ success = GetNamedPipeHandleStateW(handle, &mode,
+ NULL, NULL, NULL, NULL, 0);
+ Py_END_ALLOW_THREADS
+
+ if (!success) {
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+
+ return !(mode & PIPE_NOWAIT);
+}
+
+int
+_Py_set_blocking(int fd, int blocking)
+{
+ HANDLE handle;
+ DWORD mode;
+ BOOL success;
+
+ handle = _Py_get_osfhandle(fd);
+ if (handle == INVALID_HANDLE_VALUE) {
+ return -1;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ success = GetNamedPipeHandleStateW(handle, &mode,
+ NULL, NULL, NULL, NULL, 0);
+ if (success) {
+ if (blocking) {
+ mode &= ~PIPE_NOWAIT;
+ }
+ else {
+ mode |= PIPE_NOWAIT;
+ }
+ success = SetNamedPipeHandleState(handle, &mode, NULL, NULL);
+ }
+ Py_END_ALLOW_THREADS
+
+ if (!success) {
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+ return 0;
+}
+
void*
_Py_get_osfhandle_noraise(int fd)
{