Attempts to hardlink ``src`` to ``dst``, copying if the link fails. Attempts to maintain the semantics of ``shutil.copy``. Because ``os.link`` does not overwrite files, a unique temporary file will be used if the target already exists, then that file will be moved into place.
(src, dst)
| 301 | |
| 302 | |
| 303 | def link_or_copy(src, dst): |
| 304 | """Attempts to hardlink ``src`` to ``dst``, copying if the link fails. |
| 305 | |
| 306 | Attempts to maintain the semantics of ``shutil.copy``. |
| 307 | |
| 308 | Because ``os.link`` does not overwrite files, a unique temporary file |
| 309 | will be used if the target already exists, then that file will be moved |
| 310 | into place. |
| 311 | """ |
| 312 | |
| 313 | if os.path.isdir(dst): |
| 314 | dst = os.path.join(dst, os.path.basename(src)) |
| 315 | |
| 316 | link_errno = link(src, dst) |
| 317 | if link_errno == errno.EEXIST: |
| 318 | if os.stat(src).st_ino == os.stat(dst).st_ino: |
| 319 | # dst is already a hard link to the correct file, so we don't need |
| 320 | # to do anything else. If we try to link and rename the file |
| 321 | # anyway, we get duplicate files - see http://bugs.python.org/issue21876 |
| 322 | return |
| 323 | |
| 324 | new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), ) |
| 325 | try: |
| 326 | link_or_copy(src, new_dst) |
| 327 | except: |
| 328 | try: |
| 329 | os.remove(new_dst) |
| 330 | except OSError: |
| 331 | pass |
| 332 | raise |
| 333 | os.rename(new_dst, dst) |
| 334 | elif link_errno != 0: |
| 335 | # Either link isn't supported, or the filesystem doesn't support |
| 336 | # linking, or 'src' and 'dst' are on different filesystems. |
| 337 | shutil.copy(src, dst) |
| 338 | |
| 339 | def ensure_dir_exists(path: str, mode: int=0o755): |
| 340 | """ensure that a directory exists |