According to the documentation of shutil.copymode(), src and dst should be paths. However, on Unix it seems to accept a file descriptors most of the time as well, e.g.:
import shutil
with open("foo", "rb") as inf, open("bar", "wb") as outf:
shutil.copymode(inf.fileno(), outf.fileno())
According to the documentation for os.chmod() and os.stat(), both of these functions support specifying a file descriptor.
Can I rely on shutil.copymode() accepting the file descriptor in my program, or should I use os.chmod() and os.stat() (or os.fchmod() and os.fstat()) directly instead?
Good question. I’d expect copymode to be a shim for these calls. So I
would be reasonably confident that what you’re doing would be
reliable.
The advantage of copymode is that it does the fiddly bits for you
(extracting the modes from fstat’s st_mode field, making a correct chmod() call). That’s easy enough for chmod, but gets more fiddly
for copystat().
And you’ll lose ACLs and xattrs and ownerships anyway.
It looks like a documentation oversight to me. I’d be happy to use copymode with file descriptors until you find a circumstance where it
breaks.
Well, I’ve been pointed out that it breaks with follow_symlinks=False:
Traceback (most recent call last):
File "/tmp/test.py", line 4, in <module>
shutil.copymode(inf.fileno(), outf.fileno(), follow_symlinks=False)
File "/usr/lib/python3.11/shutil.py", line 300, in copymode
if not follow_symlinks and _islink(src) and os.path.islink(dst):
^^^^^^^^^^^^
File "/usr/lib/python3.11/shutil.py", line 224, in _islink
return fn.is_symlink() if isinstance(fn, os.DirEntry) else os.path.islink(fn)
^^^^^^^^^^^^^^^^^^
File "<frozen posixpath>", line 167, in islink
TypeError: lstat: path should be string, bytes or os.PathLike, not int
Of course, if you pass file descriptors, then following symlinks is not really a problem but it suggests that it may not have been expected.