Skip to content

Commit ee3eaa7

Browse files
committed
Fix code for missing fcntl on non-POSIX systems.
1 parent 188aa12 commit ee3eaa7

1 file changed

Lines changed: 28 additions & 5 deletions

File tree

Lib/shutil.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
"""
66

77
import os
8-
import fcntl
98
import sys
109
import stat
1110
import fnmatch
1211
import collections
1312
import errno
13+
try:
14+
import fcntl
15+
except ImportError:
16+
fcntl = None
1417

1518
try:
1619
import zlib
@@ -84,6 +87,11 @@ class SpecialFileError(OSError):
8487
not supported on a special file (e.g. a named pipe)"""
8588

8689

90+
class MissingFeatureError(Error):
91+
"""Raised when a platform does not have all of the features it needs to
92+
perform an operation."""
93+
94+
8795
class ReadError(OSError):
8896
"""Raised when an archive cannot be read"""
8997

@@ -299,6 +307,8 @@ def copyfile(src, dst, *, follow_symlinks=True):
299307
raise SameFileError("{!r} and {!r} are the same file".format(src, dst))
300308

301309
file_size = 0
310+
311+
### Move this into the opener?
302312
for i, fn in enumerate([src, dst]):
303313
try:
304314
st = _stat(fn)
@@ -322,6 +332,17 @@ def copyfile(src, dst, *, follow_symlinks=True):
322332
# In Unix, we must open the files non-blocking initially as opening a
323333
# FIFO without data present in it will block.
324334
if os.name == 'posix':
335+
# We will need these features in order to set the filehandle back to
336+
# blocking mode later.
337+
if not fcntl or not hasattr(fcntl, 'F_SETFL') or not hasattr(fcntl, 'F_GETFL'):
338+
# Which exception to raise?
339+
# The needed module or feature does not exist
340+
# We would be unable to set the file to non-blocking later
341+
# Cannot reset the file handle to defaults
342+
raise MissingFeatureError("Cannot use copyfiles on systems that support"
343+
" socket files unless `fcntl`, `fcntl.F_SETFL`,"
344+
" and `fcntl.F_GETFL` are available.")
345+
325346
src_flags = os.O_RDONLY | os.O_NONBLOCK
326347
### Do we need os.O_EXCL?
327348
dst_flags = os.O_CREAT | os.O_WRONLY | os.O_NONBLOCK
@@ -330,7 +351,7 @@ def copyfile(src, dst, *, follow_symlinks=True):
330351
dst_flags = os.O_WRONLY
331352

332353
def _src_opener(path, flags):
333-
return os.open(path, flags)
354+
return os.open(path, flags | src_flags)
334355

335356
# dst's opener is more complex. We need to atomically check and open the
336357
# destination if it doesn't exist. If it does exist, we need to detect so
@@ -344,8 +365,10 @@ def _dst_opener(path, flags):
344365
except FileExistsError:
345366
dst_was_created = False
346367
try:
347-
# Open the already existing file in as non-blocking
348-
### We have O_CREATE and O_NONBLOCK in dst_flags. Is that intended?
368+
# Open the already existing file as non-blocking
369+
### We have O_CREAT and O_NONBLOCK in dst_flags. Even though
370+
### we know that we are not creating the file at this point. Is
371+
### that intended?
349372
fd = os.open(path, flags | dst_flags)
350373
# If the destination is already a FIFO for some reason, we'll get an
351374
# OSError here as we've opened a FIFO for a non-blocking write with
@@ -379,7 +402,7 @@ def _dst_opener(path, flags):
379402
if dst_was_created:
380403
os.unlink(dst)
381404
raise SpecialFileError("`%s` is a named pipe" % src)
382-
if _WINDOWS and i == 0:
405+
if _WINDOWS:
383406
file_size = st.st_size
384407

385408
# In Unix, we must set the file descriptors back to blocking as that's

0 commit comments

Comments
 (0)