Skip to content

Commit cf2bda6

Browse files
committed
Allow symlinked Extension.depends to be auto-included by sdist
1 parent 77a1295 commit cf2bda6

File tree

2 files changed

+29
-3
lines changed

2 files changed

+29
-3
lines changed

setuptools/_path.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import sys
33
from typing import Union
4+
from pathlib import Path
45

56
_Path = Union[str, os.PathLike]
67

@@ -11,6 +12,14 @@ def ensure_directory(path):
1112
os.makedirs(dirname, exist_ok=True)
1213

1314

15+
def safe_samefile(path1: _Path, path2: _Path) -> bool:
16+
"""Similar to os.path.samefile but returns False instead of raising exception"""
17+
try:
18+
return os.path.samefile(path1, path2)
19+
except OSError:
20+
return False
21+
22+
1423
def same_path(p1: _Path, p2: _Path) -> bool:
1524
"""Differs from os.path.samefile because it does not require paths to exist.
1625
Purely string based (no comparison between i-nodes).
@@ -35,3 +44,21 @@ def normpath(filename: _Path) -> str:
3544
# See pkg_resources.normalize_path for notes about cygwin
3645
file = os.path.abspath(filename) if sys.platform == 'cygwin' else filename
3746
return os.path.normcase(os.path.realpath(os.path.normpath(file)))
47+
48+
49+
def besteffort_internal_path(root: _Path, file: _Path) -> str:
50+
"""Process ``file`` and return an equivalent relative path contained into root
51+
(POSIX style, with no ``..`` segments).
52+
It may raise an ``ValueError`` if that is not possible.
53+
If ``file`` is not absolute, it will be assumed to be relative to ``root``.
54+
"""
55+
path = os.path.join(root, file) # will ignore root if file is absolute
56+
resolved = Path(path).resolve()
57+
logical = Path(os.path.abspath(path))
58+
59+
# Prefer logical paths, since a parent directory can be symlinked inside root
60+
if same_path(resolved, logical) or safe_samefile(resolved, logical):
61+
return logical.relative_to(root).as_posix()
62+
63+
# Since ``resolved`` is used, it makes sense to resolve root too.
64+
return resolved.relative_to(Path(root).resolve()).as_posix()

setuptools/command/build_ext.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from distutils.sysconfig import customize_compiler, get_config_var
1212
from distutils import log
1313

14+
from setuptools import _path
1415
from setuptools.errors import BaseError
1516
from setuptools.extension import Extension, Library
1617

@@ -271,9 +272,7 @@ def _get_internal_depends(self) -> Iterator[str]:
271272
depends = (dep for ext in self.extensions for dep in ext.depends)
272273
for dep in depends:
273274
try:
274-
abs_path = (project_root / dep).resolve()
275-
# if dep is absolute, Path will ignore project_root
276-
yield abs_path.relative_to(project_root).as_posix()
275+
yield _path.besteffort_internal_path(project_root, dep)
277276
except ValueError:
278277
log.warn(f"ignoring {dep} for distribution: outside of project dir")
279278

0 commit comments

Comments
 (0)