Is it valid to pass .fileno() to shutil.copymode(), .copystat()?

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

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

Cameron Simpson

Well, I’ve been pointed out that it breaks with follow_symlinks=False:

Traceback (most recent call last):
  File "/tmp/", line 4, in <module>
    shutil.copymode(inf.fileno(), outf.fileno(), follow_symlinks=False)
  File "/usr/lib/python3.11/", line 300, in copymode
    if not follow_symlinks and _islink(src) and os.path.islink(dst):
  File "/usr/lib/python3.11/", 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.