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)
| 383 | |
| 384 | |
| 385 | def link_or_copy(src, dst): |
| 386 | """Attempts to hardlink ``src`` to ``dst``, copying if the link fails. |
| 387 | |
| 388 | Attempts to maintain the semantics of ``shutil.copy``. |
| 389 | |
| 390 | Because ``os.link`` does not overwrite files, a unique temporary file |
| 391 | will be used if the target already exists, then that file will be moved |
| 392 | into place. |
| 393 | """ |
| 394 | |
| 395 | if os.path.isdir(dst): |
| 396 | dst = os.path.join(dst, os.path.basename(src)) |
| 397 | |
| 398 | link_errno = link(src, dst) |
| 399 | if link_errno == errno.EEXIST: |
| 400 | if os.stat(src).st_ino == os.stat(dst).st_ino: |
| 401 | # dst is already a hard link to the correct file, so we don't need |
| 402 | # to do anything else. If we try to link and rename the file |
| 403 | # anyway, we get duplicate files - see http://bugs.python.org/issue21876 |
| 404 | return |
| 405 | |
| 406 | new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), ) |
| 407 | try: |
| 408 | link_or_copy(src, new_dst) |
| 409 | except: |
| 410 | try: |
| 411 | os.remove(new_dst) |
| 412 | except OSError: |
| 413 | pass |
| 414 | raise |
| 415 | os.rename(new_dst, dst) |
| 416 | elif link_errno != 0: |
| 417 | # Either link isn't supported, or the filesystem doesn't support |
| 418 | # linking, or 'src' and 'dst' are on different filesystems. |
| 419 | shutil.copy(src, dst) |
| 420 | |
| 421 | def ensure_dir_exists(path, mode=0o755): |
| 422 | """ensure that a directory exists |