Skip to content

Commit 4704da4

Browse files
authored
Merge pull request #10082 from uranusjr/setup-py-freeze
Unify Python project root detection logic
2 parents 7c3abcc + 288bffc commit 4704da4

File tree

8 files changed

+42
-33
lines changed

8 files changed

+42
-33
lines changed

news/10080.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Correctly allow PEP 517 projects to be detected without warnings in ``pip freeze``.

src/pip/_internal/operations/prepare.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from pip._internal.utils.filesystem import copy2_fixed
4040
from pip._internal.utils.hashes import Hashes, MissingHashes
4141
from pip._internal.utils.logging import indent_log
42-
from pip._internal.utils.misc import display_path, hide_url, rmtree
42+
from pip._internal.utils.misc import display_path, hide_url, is_installable_dir, rmtree
4343
from pip._internal.utils.temp_dir import TempDirectory
4444
from pip._internal.utils.unpacking import unpack_file
4545
from pip._internal.vcs import vcs
@@ -376,7 +376,7 @@ def _ensure_link_req_src_dir(self, req, parallel_builds):
376376
# installation.
377377
# FIXME: this won't upgrade when there's an existing
378378
# package unpacked in `req.source_dir`
379-
if os.path.exists(os.path.join(req.source_dir, 'setup.py')):
379+
if is_installable_dir(req.source_dir):
380380
raise PreviousBuildDirError(
381381
"pip can't proceed with requirements '{}' due to a"
382382
"pre-existing build directory ({}). This is likely "

src/pip/_internal/req/constructors.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ def _looks_like_path(name):
248248
def _get_url_from_path(path, name):
249249
# type: (str, str) -> Optional[str]
250250
"""
251-
First, it checks whether a provided path is an installable directory
252-
(e.g. it has a setup.py). If it is, returns the path.
251+
First, it checks whether a provided path is an installable directory. If it
252+
is, returns the path.
253253
254254
If false, check if the path is an archive file (such as a .whl).
255255
The function checks if the path is a file. If false, if the path has

src/pip/_internal/utils/misc.py

+12-5
Original file line numberDiff line numberDiff line change
@@ -270,13 +270,20 @@ def tabulate(rows):
270270

271271

272272
def is_installable_dir(path: str) -> bool:
273-
"""Is path is a directory containing pyproject.toml, setup.cfg or setup.py?"""
273+
"""Is path is a directory containing pyproject.toml or setup.py?
274+
275+
If pyproject.toml exists, this is a PEP 517 project. Otherwise we look for
276+
a legacy setuptools layout by identifying setup.py. We don't check for the
277+
setup.cfg because using it without setup.py is only available for PEP 517
278+
projects, which are already covered by the pyproject.toml check.
279+
"""
274280
if not os.path.isdir(path):
275281
return False
276-
return any(
277-
os.path.isfile(os.path.join(path, signifier))
278-
for signifier in ("pyproject.toml", "setup.cfg", "setup.py")
279-
)
282+
if os.path.isfile(os.path.join(path, "pyproject.toml")):
283+
return True
284+
if os.path.isfile(os.path.join(path, "setup.py")):
285+
return True
286+
return False
280287

281288

282289
def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE):

src/pip/_internal/vcs/git.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
RemoteNotValidError,
1919
RevOptions,
2020
VersionControl,
21-
find_path_to_setup_from_repo_root,
21+
find_path_to_project_root_from_repo_root,
2222
vcs,
2323
)
2424

@@ -410,8 +410,8 @@ def get_revision(cls, location, rev=None):
410410
def get_subdirectory(cls, location):
411411
# type: (str) -> Optional[str]
412412
"""
413-
Return the path to setup.py, relative to the repo root.
414-
Return None if setup.py is in the repo root.
413+
Return the path to Python project root, relative to the repo root.
414+
Return None if the project root is in the repo root.
415415
"""
416416
# find the repo root
417417
git_dir = cls.run_command(
@@ -423,7 +423,7 @@ def get_subdirectory(cls, location):
423423
if not os.path.isabs(git_dir):
424424
git_dir = os.path.join(location, git_dir)
425425
repo_root = os.path.abspath(os.path.join(git_dir, '..'))
426-
return find_path_to_setup_from_repo_root(location, repo_root)
426+
return find_path_to_project_root_from_repo_root(location, repo_root)
427427

428428
@classmethod
429429
def get_url_rev_and_auth(cls, url):

src/pip/_internal/vcs/mercurial.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from pip._internal.vcs.versioncontrol import (
1111
RevOptions,
1212
VersionControl,
13-
find_path_to_setup_from_repo_root,
13+
find_path_to_project_root_from_repo_root,
1414
vcs,
1515
)
1616

@@ -120,16 +120,16 @@ def is_commit_id_equal(cls, dest, name):
120120
def get_subdirectory(cls, location):
121121
# type: (str) -> Optional[str]
122122
"""
123-
Return the path to setup.py, relative to the repo root.
124-
Return None if setup.py is in the repo root.
123+
Return the path to Python project root, relative to the repo root.
124+
Return None if the project root is in the repo root.
125125
"""
126126
# find the repo root
127127
repo_root = cls.run_command(
128128
['root'], show_stdout=False, stdout_only=True, cwd=location
129129
).strip()
130130
if not os.path.isabs(repo_root):
131131
repo_root = os.path.abspath(os.path.join(location, repo_root))
132-
return find_path_to_setup_from_repo_root(location, repo_root)
132+
return find_path_to_project_root_from_repo_root(location, repo_root)
133133

134134
@classmethod
135135
def get_repository_root(cls, location):

src/pip/_internal/vcs/subversion.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
HiddenText,
88
display_path,
99
is_console_interactive,
10+
is_installable_dir,
1011
split_auth_from_netloc,
1112
)
1213
from pip._internal.utils.subprocess import CommandArgs, make_command
@@ -111,18 +112,17 @@ def make_rev_args(username, password):
111112
@classmethod
112113
def get_remote_url(cls, location):
113114
# type: (str) -> str
114-
# In cases where the source is in a subdirectory, not alongside
115-
# setup.py we have to look up in the location until we find a real
116-
# setup.py
115+
# In cases where the source is in a subdirectory, we have to look up in
116+
# the location until we find a valid project root.
117117
orig_location = location
118-
while not os.path.exists(os.path.join(location, 'setup.py')):
118+
while not is_installable_dir(location):
119119
last_location = location
120120
location = os.path.dirname(location)
121121
if location == last_location:
122122
# We've traversed up to the root of the filesystem without
123-
# finding setup.py
123+
# finding a Python project.
124124
logger.warning(
125-
"Could not find setup.py for directory %s (tried all "
125+
"Could not find Python project for directory %s (tried all "
126126
"parent directories)",
127127
orig_location,
128128
)

src/pip/_internal/vcs/versioncontrol.py

+11-10
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
display_path,
2828
hide_url,
2929
hide_value,
30+
is_installable_dir,
3031
rmtree,
3132
)
3233
from pip._internal.utils.subprocess import CommandArgs, call_subprocess, make_command
@@ -68,23 +69,23 @@ def make_vcs_requirement_url(repo_url, rev, project_name, subdir=None):
6869
return req
6970

7071

71-
def find_path_to_setup_from_repo_root(location, repo_root):
72+
def find_path_to_project_root_from_repo_root(location, repo_root):
7273
# type: (str, str) -> Optional[str]
7374
"""
74-
Find the path to `setup.py` by searching up the filesystem from `location`.
75-
Return the path to `setup.py` relative to `repo_root`.
76-
Return None if `setup.py` is in `repo_root` or cannot be found.
75+
Find the the Python project's root by searching up the filesystem from
76+
`location`. Return the path to project root relative to `repo_root`.
77+
Return None if the project root is `repo_root`, or cannot be found.
7778
"""
78-
# find setup.py
79+
# find project root.
7980
orig_location = location
80-
while not os.path.exists(os.path.join(location, 'setup.py')):
81+
while not is_installable_dir(location):
8182
last_location = location
8283
location = os.path.dirname(location)
8384
if location == last_location:
8485
# We've traversed up to the root of the filesystem without
85-
# finding setup.py
86+
# finding a Python project.
8687
logger.warning(
87-
"Could not find setup.py for directory %s (tried all "
88+
"Could not find a Python project for directory %s (tried all "
8889
"parent directories)",
8990
orig_location,
9091
)
@@ -296,8 +297,8 @@ def should_add_vcs_url_prefix(cls, remote_url):
296297
def get_subdirectory(cls, location):
297298
# type: (str) -> Optional[str]
298299
"""
299-
Return the path to setup.py, relative to the repo root.
300-
Return None if setup.py is in the repo root.
300+
Return the path to Python project root, relative to the repo root.
301+
Return None if the project root is in the repo root.
301302
"""
302303
return None
303304

0 commit comments

Comments
 (0)