Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Lib/pathlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1328,8 +1328,13 @@ def _copy_from(self, source, follow_symlinks=True, preserve_metadata=False):
child, follow_symlinks, preserve_metadata)
if preserve_metadata:
_copy_info(source.info, self)
else:
elif source.info.is_file():
self._copy_from_file(source, preserve_metadata)
elif not source.info.exists(follow_symlinks=follow_symlinks):
self._copy_from_file(source, preserve_metadata)
else:
raise io.UnsupportedOperation(
f"{source!r} is a special file and cannot be copied")

def _copy_from_file(self, source, preserve_metadata=False):
ensure_different_files(source, self)
Expand Down
29 changes: 29 additions & 0 deletions Lib/test/test_pathlib/test_copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,35 @@ class LocalToLocalPathCopyTest(CopyTestBase, unittest.TestCase):
source_ground = LocalPathGround(Path)
target_ground = LocalPathGround(Path)

def test_copy_fifo(self):
import io
import os
if not hasattr(os, 'mkfifo'):
self.skipTest('os.mkfifo() required')

source = self.source_root / 'test.fifo'
try:
os.mkfifo(source)
except OSError:
self.skipTest("cannot create fifo")

target = self.target_root / 'copy_fifo'
with self.assertRaises(io.UnsupportedOperation):
source.copy(target)

def test_copy_char_device(self):
import io
# /dev/null is a character device on POSIX
source = Path('/dev/null')
if not source.exists() or not source.is_char_device():
self.skipTest('/dev/null required')

target = self.target_root / 'copy_null'
# This should fail immediately with UnsupportedOperation
# If it were buggy, it might loop infinitely or copy empty file depending on implementation
with self.assertRaises(io.UnsupportedOperation):
source.copy(target)


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Prevent :meth:`pathlib.Path.copy` from attempting to copy special files like FIFOs or character devices, avoiding hangs or infinite loops.
Loading