`shutil.copytree()` incorrectly classifies relative symlinks as dangling
まだ誰も着手していません。
- 主要言語
- Python
- スター
- 77.2k
- フォーク
- 35.9k
- PR マージ指標
- PR 指標を取得中
説明
Bug report
Bug description:
Summary
shutil.copytree(..., symlinks=False, ignore_dangling_symlinks=True) tests a relative symlink target against the process current working directory rather than against the directory containing the symlink. Consequently, a valid relative symlink can be silently omitted from a successful copy, and a truly dangling symlink can still cause shutil.Error when the current working directory happens to contain a file with the same name as its target.
Affected code
Lib/shutil.py, _copytree():
linkto = os.readlink(srcname)
...
if not os.path.exists(linkto) and ignore_dangling_symlinks:
continue
os.readlink() returns the raw target. A relative target is interpreted by the operating system relative to the symlink's parent directory, but os.path.exists(linkto) interprets it relative to the process current working
directory.
Reproducer
import os
import shutil
import tempfile
with tempfile.TemporaryDirectory() as root:
src = os.path.join(root, "src")
dst = os.path.join(root, "dst")
os.mkdir(src)
with open(os.path.join(src, "target"), "w") as f:
f.write("data")
os.symlink("target", os.path.join(src, "link"))
old_cwd = os.getcwd()
try:
os.chdir(root) # root/target does not exist
shutil.copytree(src, dst, ignore_dangling_symlinks=True)
print("exception: none")
finally:
os.chdir(old_cwd)
print("source link exists?", os.path.lexists(os.path.join(src, "link")))
print("destination entries:", sorted(os.listdir(dst)))
print("destination link exists?", os.path.lexists(os.path.join(dst, "link")))
Actual output:
exception: none
source link exists? True
destination entries: ['target']
destination link exists? False
The source symlink is valid: src/link -> target resolves to src/target. With symlinks=False, the destination should contain a regular link file with the contents of src/target, in addition to target. Instead, the operation returns successfully with an incomplete destination tree.
The inverse case also fails: if src/link -> target is actually dangling but the current working directory contains target, it is not ignored and copytree() raises shutil.Error after creating the destination directory.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
- gh-156214
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
Lib/shutil.py の _copytree() から始め、変更を加える前にリンク先の PR gh-156214 を確認してください。Issue にある相対 symlink のケースを再現し、その後、既存の shutil テストに回帰テストのカバレッジを追加または更新して、現在の作業ディレクトリに関係なく、有効なリンクがコピーされ、壊れたリンクが無視されるようにしてください。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- tooling
- issue の種類
- バグ
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 活発さ
- 停滞
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 25/100