Also support a file descriptor in os.chmod().
.. audit-event:: os.chmod path,mode,dir_fd os.fchmod
- .. availability:: Unix.
+ .. availability:: Unix, Windows.
The function is limited on Emscripten and WASI, see
:ref:`wasm-availability` for more information.
+ .. versionchanged:: 3.13
+ Added support on Windows.
+
.. function:: fchown(fd, uid, gid)
Accepts a :term:`path-like object`.
.. versionchanged:: 3.13
- Added support for the *follow_symlinks* argument on Windows.
+ Added support for a file descriptor and the *follow_symlinks* argument
+ on Windows.
.. function:: chown(path, uid, gid, *, dir_fd=None, follow_symlinks=True)
``False`` on Windows.
(Contributed by Serhiy Storchaka in :gh:`59616`)
+* Add support of :func:`os.fchmod` and a file descriptor
+ in :func:`os.chmod` on Windows.
+ (Contributed by Serhiy Storchaka in :gh:`113191`)
+
* :func:`os.posix_spawn` now accepts ``env=None``, which makes the newly spawned
process use the current process environment.
(Contributed by Jakub Kulik in :gh:`113119`.)
_set = set()
_add("HAVE_FCHDIR", "chdir")
_add("HAVE_FCHMOD", "chmod")
+ _add("MS_WINDOWS", "chmod")
_add("HAVE_FCHOWN", "chown")
_add("HAVE_FDOPENDIR", "listdir")
_add("HAVE_FDOPENDIR", "scandir")
posix.utime(os_helper.TESTFN, (now, now))
def check_chmod(self, chmod_func, target, **kwargs):
+ closefd = not isinstance(target, int)
mode = os.stat(target).st_mode
try:
new_mode = mode & ~(stat.S_IWOTH | stat.S_IWGRP | stat.S_IWUSR)
self.assertEqual(os.stat(target).st_mode, new_mode)
if stat.S_ISREG(mode):
try:
- with open(target, 'wb+'):
+ with open(target, 'wb+', closefd=closefd):
pass
except PermissionError:
pass
chmod_func(target, new_mode, **kwargs)
self.assertEqual(os.stat(target).st_mode, new_mode)
if stat.S_ISREG(mode):
- with open(target, 'wb+'):
+ with open(target, 'wb+', closefd=closefd):
pass
finally:
- posix.chmod(target, mode)
+ chmod_func(target, mode)
@os_helper.skip_unless_working_chmod
def test_chmod_file(self):
target = self.tempdir()
self.check_chmod(posix.chmod, target)
+ @os_helper.skip_unless_working_chmod
+ def test_fchmod_file(self):
+ with open(os_helper.TESTFN, 'wb+') as f:
+ self.check_chmod(posix.fchmod, f.fileno())
+ self.check_chmod(posix.chmod, f.fileno())
+
@unittest.skipUnless(hasattr(posix, 'lchmod'), 'test needs os.lchmod()')
def test_lchmod_file(self):
self.check_chmod(posix.lchmod, os_helper.TESTFN)
--- /dev/null
+Add support of :func:`os.fchmod` and a file descriptor in :func:`os.chmod`
+on Windows.
return return_value;
}
-#if defined(HAVE_FCHMOD)
+#if (defined(HAVE_FCHMOD) || defined(MS_WINDOWS))
PyDoc_STRVAR(os_fchmod__doc__,
"fchmod($module, /, fd, mode)\n"
return return_value;
}
-#endif /* defined(HAVE_FCHMOD) */
+#endif /* (defined(HAVE_FCHMOD) || defined(MS_WINDOWS)) */
#if (defined(HAVE_LCHMOD) || defined(MS_WINDOWS))
#ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF
#define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF
#endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */
-/*[clinic end generated code: output=1be15e60a553b40d input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b82391c4f58231b6 input=a9049054013a1b77]*/
#ifdef MS_WINDOWS
#undef PATH_HAVE_FTRUNCATE
#define PATH_HAVE_FTRUNCATE 1
+ #undef PATH_HAVE_FCHMOD
+ #define PATH_HAVE_FCHMOD 1
#endif
/*[python input]
}
return SetFileAttributesW(path, attr);
}
-#endif
+
+static int
+win32_hchmod(HANDLE hfile, int mode)
+{
+ FILE_BASIC_INFO info;
+ if (!GetFileInformationByHandleEx(hfile, FileBasicInfo,
+ &info, sizeof(info)))
+ {
+ return 0;
+ }
+ if (mode & _S_IWRITE) {
+ info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
+ }
+ else {
+ info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
+ }
+ return SetFileInformationByHandle(hfile, FileBasicInfo,
+ &info, sizeof(info));
+}
+
+static int
+win32_fchmod(int fd, int mode)
+{
+ HANDLE hfile = _Py_get_osfhandle_noraise(fd);
+ if (hfile == INVALID_HANDLE_VALUE) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return 0;
+ }
+ return win32_hchmod(hfile, mode);
+}
+
+#endif /* MS_WINDOWS */
/*[clinic input]
os.chmod
#ifdef MS_WINDOWS
result = 0;
Py_BEGIN_ALLOW_THREADS
- if (follow_symlinks) {
- HANDLE hfile;
- FILE_BASIC_INFO info;
-
- hfile = CreateFileW(path->wide,
- FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES,
- 0, NULL,
- OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (path->fd != -1) {
+ result = win32_fchmod(path->fd, mode);
+ }
+ else if (follow_symlinks) {
+ HANDLE hfile = CreateFileW(path->wide,
+ FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES,
+ 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hfile != INVALID_HANDLE_VALUE) {
- if (GetFileInformationByHandleEx(hfile, FileBasicInfo,
- &info, sizeof(info)))
- {
- if (mode & _S_IWRITE) {
- info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
- }
- else {
- info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
- }
- result = SetFileInformationByHandle(hfile, FileBasicInfo,
- &info, sizeof(info));
- }
+ result = win32_hchmod(hfile, mode);
(void)CloseHandle(hfile);
}
}
}
-#ifdef HAVE_FCHMOD
+#if defined(HAVE_FCHMOD) || defined(MS_WINDOWS)
/*[clinic input]
os.fchmod
/*[clinic end generated code: output=afd9bc05b4e426b3 input=b5594618bbbc22df]*/
{
int res;
- int async_err = 0;
if (PySys_Audit("os.chmod", "iii", fd, mode, -1) < 0) {
return NULL;
}
+#ifdef MS_WINDOWS
+ res = 0;
+ Py_BEGIN_ALLOW_THREADS
+ res = win32_fchmod(fd, mode);
+ Py_END_ALLOW_THREADS
+ if (!res) {
+ return PyErr_SetFromWindowsErr(0);
+ }
+#else /* MS_WINDOWS */
+ int async_err = 0;
do {
Py_BEGIN_ALLOW_THREADS
res = fchmod(fd, mode);
} while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (res != 0)
return (!async_err) ? posix_error() : NULL;
+#endif /* MS_WINDOWS */
Py_RETURN_NONE;
}
-#endif /* HAVE_FCHMOD */
+#endif /* HAVE_FCHMOD || MS_WINDOWS */
#if defined(HAVE_LCHMOD) || defined(MS_WINDOWS)