Skip to content

Commit 89e49bf

Browse files
committed
Stop using samefile(), add _file_id() and _device_id()
1 parent 0871a39 commit 89e49bf

File tree

2 files changed

+51
-36
lines changed

2 files changed

+51
-36
lines changed

Lib/pathlib/_local.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
except ImportError:
2020
grp = None
2121

22-
from pathlib._os import LocalCopyWriter, PathInfo, DirEntryInfo
22+
from pathlib._os import LocalCopyWriter, PathInfo, DirEntryInfo, ensure_different_files
2323
from pathlib._abc import JoinablePath, ReadablePath, WritablePath
2424

2525

@@ -1069,7 +1069,7 @@ def move(self, target):
10691069
else:
10701070
if not hasattr(target, '_copy_writer'):
10711071
target = self.with_segments(target_str)
1072-
target._copy_writer._ensure_different_file(self)
1072+
ensure_different_files(self, target)
10731073
try:
10741074
os.replace(self, target_str)
10751075
return target

Lib/pathlib/_os.py

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ def _create_metadata(self, source, follow_symlinks=True):
216216
pass
217217

218218
def _create(self, source, follow_symlinks, dirs_exist_ok, preserve_metadata):
219-
self._ensure_distinct_path(source)
219+
ensure_distinct_paths(source, self._path)
220220
if not follow_symlinks and source.is_symlink():
221221
self._create_symlink(source, preserve_metadata)
222222
elif source.is_dir():
@@ -243,7 +243,7 @@ def _create_dir(self, source, follow_symlinks, dirs_exist_ok, preserve_metadata)
243243

244244
def _create_file(self, source, preserve_metadata):
245245
"""Copy the given file to our path."""
246-
self._ensure_different_file(source)
246+
ensure_different_files(source, self._path)
247247
with magic_open(source, 'rb') as source_f:
248248
try:
249249
with magic_open(self._path, 'wb') as target_f:
@@ -263,30 +263,25 @@ def _create_symlink(self, source, preserve_metadata):
263263
if preserve_metadata:
264264
self._create_metadata(source, follow_symlinks=False)
265265

266-
def _ensure_different_file(self, source):
267-
"""
268-
Raise OSError(EINVAL) if both paths refer to the same file.
269-
"""
270-
pass
271266

272-
def _ensure_distinct_path(self, source):
273-
"""
274-
Raise OSError(EINVAL) if the other path is within this path.
275-
"""
276-
# Note: there is no straightforward, foolproof algorithm to determine
277-
# if one directory is within another (a particularly perverse example
278-
# would be a single network share mounted in one location via NFS, and
279-
# in another location via CIFS), so we simply checks whether the
280-
# other path is lexically equal to, or within, this path.
281-
if source == self._path:
282-
err = OSError(EINVAL, "Source and target are the same path")
283-
elif source in self._path.parents:
284-
err = OSError(EINVAL, "Source path is a parent of target path")
285-
else:
286-
return
287-
err.filename = str(source)
288-
err.filename2 = str(self._path)
289-
raise err
267+
def ensure_distinct_paths(source, target):
268+
"""
269+
Raise OSError(EINVAL) if the other path is within this path.
270+
"""
271+
# Note: there is no straightforward, foolproof algorithm to determine
272+
# if one directory is within another (a particularly perverse example
273+
# would be a single network share mounted in one location via NFS, and
274+
# in another location via CIFS), so we simply checks whether the
275+
# other path is lexically equal to, or within, this path.
276+
if source == target:
277+
err = OSError(EINVAL, "Source and target are the same path")
278+
elif source in target.parents:
279+
err = OSError(EINVAL, "Source path is a parent of target path")
280+
else:
281+
return
282+
err.filename = str(source)
283+
err.filename2 = str(target)
284+
raise err
290285

291286

292287
class LocalCopyWriter(CopyWriter):
@@ -376,19 +371,31 @@ def _create_symlink(self, source, preserve_metadata):
376371
if preserve_metadata:
377372
self._create_metadata(source, follow_symlinks=False)
378373

379-
def _ensure_different_file(self, source):
380-
"""
381-
Raise OSError(EINVAL) if both paths refer to the same file.
382-
"""
374+
375+
def ensure_different_files(source, target):
376+
"""
377+
Raise OSError(EINVAL) if both paths refer to the same file.
378+
"""
379+
try:
380+
source_file_id = source.info._file_id
381+
target_file_id = target.info._file_id
382+
source_device_id = source.info._device_id
383+
target_device_id = target.info._device_id
384+
except AttributeError:
385+
if source != target:
386+
return
387+
else:
383388
try:
384-
if not self._path.samefile(source):
389+
if source_file_id() != target_file_id():
390+
return
391+
if source_device_id() != target_device_id():
385392
return
386393
except (OSError, ValueError):
387394
return
388-
err = OSError(EINVAL, "Source and target are the same file")
389-
err.filename = str(source)
390-
err.filename2 = str(self._path)
391-
raise err
395+
err = OSError(EINVAL, "Source and target are the same file")
396+
err.filename = str(source)
397+
err.filename2 = str(target)
398+
raise err
392399

393400

394401
class _PathInfoBase:
@@ -439,6 +446,14 @@ def _posix_permissions(self, *, follow_symlinks=True):
439446
"""Return the POSIX file permissions."""
440447
return S_IMODE(self._stat(follow_symlinks=follow_symlinks).st_mode)
441448

449+
def _file_id(self, *, follow_symlinks=True):
450+
"""Returns the identifier of the file (unique for a device ID)."""
451+
return self._stat(follow_symlinks=follow_symlinks).st_ino
452+
453+
def _device_id(self, *, follow_symlinks=True):
454+
"""Returns the identifier of the device on which the file resides."""
455+
return self._stat(follow_symlinks=follow_symlinks).st_dev
456+
442457
def _access_time_ns(self, *, follow_symlinks=True):
443458
"""Return the access time in nanoseconds."""
444459
return self._stat(follow_symlinks=follow_symlinks).st_atime_ns

0 commit comments

Comments
 (0)