[BUG] When source tree contains a directory symlink, the directory is randomly packaged either as the original or symlink path

Open
#4,937 1 comment 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
45/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Stale
Tech stack
python
Domain
build-system

Research direction

Start with the pyproject.toml and MANIFEST.in reproduction, then run python -m build -s and inspect the generated archive and SOURCES.txt. Trace how the sdist handles the foo/a directory and foo/b symlink, including both creation orders. Done means the original directory remains at its original path consistently, with regression coverage for the reported case.

Written by the indexing model from the issue text.

Description

bug Needs Triage
setuptools version

78.1.0

Python version

3.13.2

OS

Gentoo Linux amd64

Additional environment information

No response

Description

When the source tree contains a directory symlink such as b -> a, then setuptools randomly includes the directory as either a or b (and no symlink). The actual result seems to depend on the filesystem order: if the symlink is created first, then the original directory (i.e. a) is included in the sdist. However, if the symlink is created after the directory, the directory is included as b instead of a.

Expected behavior

I expected the original directory to be preserved in the source distribution at its original location. I don't have a hard opinion whether the symlink should be replaced by a copy of the directory, or be ignored — but it definitely shouldn't cause the original directory to disappear.

How to Reproduce
cat > pyproject.toml <<EOF
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "test"
version = "0"
EOF
echo "graft foo" > MANIFEST.in
mkdir -p foo/a
touch foo/a/{a,b,c}
ln -s a foo/b
python -m build -s
tar -tf dist/test-0.tar.gz
Output
$ python -m build -s
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
  - setuptools
* Getting build dependencies for sdist...
running egg_info
writing test.egg-info/PKG-INFO
writing dependency_links to test.egg-info/dependency_links.txt
writing top-level names to test.egg-info/top_level.txt
reading manifest file 'test.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'test.egg-info/SOURCES.txt'
* Building sdist...
running sdist
running egg_info
writing test.egg-info/PKG-INFO
writing dependency_links to test.egg-info/dependency_links.txt
writing top-level names to test.egg-info/top_level.txt
reading manifest file 'test.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'test.egg-info/SOURCES.txt'
warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md

running check
creating test-0
creating test-0/foo/b
creating test-0/test.egg-info
copying files to test-0...
copying MANIFEST.in -> test-0
copying pyproject.toml -> test-0
copying foo/b/a -> test-0/foo/b
copying foo/b/b -> test-0/foo/b
copying foo/b/c -> test-0/foo/b
copying test.egg-info/PKG-INFO -> test-0/test.egg-info
copying test.egg-info/SOURCES.txt -> test-0/test.egg-info
copying test.egg-info/dependency_links.txt -> test-0/test.egg-info
copying test.egg-info/top_level.txt -> test-0/test.egg-info
copying test.egg-info/SOURCES.txt -> test-0/test.egg-info
Writing test-0/setup.cfg
Creating tar archive
removing 'test-0' (and everything under it)
Successfully built test-0.tar.gz
$ tar -tf dist/test-0.tar.gz 
test-0/
test-0/MANIFEST.in
test-0/PKG-INFO
test-0/foo/
test-0/foo/b/
test-0/foo/b/a
test-0/foo/b/b
test-0/foo/b/c
test-0/pyproject.toml
test-0/setup.cfg
test-0/test.egg-info/
test-0/test.egg-info/PKG-INFO
test-0/test.egg-info/SOURCES.txt
test-0/test.egg-info/dependency_links.txt
test-0/test.egg-info/top_level.txt
Dominant language
Python
Stars
2.9k
Forks
1.4k
Avg merge
1d 1h
Merged PRs (30d)
1

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from pypa/setuptools

All issues in pypa/setuptools

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.