Skip to content

Commit 7bf78f0

Browse files
authored
Merge pull request #8283 from uranusjr/pre-existing-build-directory-fix
Pre-existing build directory fix
2 parents 19e739c + daf454b commit 7bf78f0

File tree

7 files changed

+62
-26
lines changed

7 files changed

+62
-26
lines changed

src/pip/_internal/operations/prepare.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ def _download_should_save(self):
379379
def prepare_linked_requirement(
380380
self,
381381
req, # type: InstallRequirement
382+
parallel_builds=False, # type: bool
382383
):
383384
# type: (...) -> AbstractDistribution
384385
"""Prepare a requirement that would be obtained from req.link
@@ -415,7 +416,11 @@ def prepare_linked_requirement(
415416
with indent_log():
416417
# Since source_dir is only set for editable requirements.
417418
assert req.source_dir is None
418-
req.ensure_has_source_dir(self.build_dir, autodelete_unpacked)
419+
req.ensure_has_source_dir(
420+
self.build_dir,
421+
autodelete=autodelete_unpacked,
422+
parallel_builds=parallel_builds,
423+
)
419424
# If a checkout exists, it's unwise to keep going. version
420425
# inconsistencies are logged later, but do not fail the
421426
# installation.

src/pip/_internal/req/req_install.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import shutil
99
import sys
10+
import uuid
1011
import zipfile
1112

1213
from pip._vendor import pkg_resources, six
@@ -339,8 +340,8 @@ def from_path(self):
339340
s += '->' + comes_from
340341
return s
341342

342-
def ensure_build_location(self, build_dir, autodelete):
343-
# type: (str, bool) -> str
343+
def ensure_build_location(self, build_dir, autodelete, parallel_builds):
344+
# type: (str, bool, bool) -> str
344345
assert build_dir is not None
345346
if self._temp_build_dir is not None:
346347
assert self._temp_build_dir.path
@@ -354,16 +355,19 @@ def ensure_build_location(self, build_dir, autodelete):
354355
)
355356

356357
return self._temp_build_dir.path
357-
if self.editable:
358-
name = self.name.lower()
359-
else:
360-
name = self.name
358+
359+
# When parallel builds are enabled, add a UUID to the build directory
360+
# name so multiple builds do not interfere with each other.
361+
dir_name = canonicalize_name(self.name)
362+
if parallel_builds:
363+
dir_name = "{}_{}".format(dir_name, uuid.uuid4().hex)
364+
361365
# FIXME: Is there a better place to create the build_dir? (hg and bzr
362366
# need this)
363367
if not os.path.exists(build_dir):
364368
logger.debug('Creating directory %s', build_dir)
365369
os.makedirs(build_dir)
366-
actual_build_dir = os.path.join(build_dir, name)
370+
actual_build_dir = os.path.join(build_dir, dir_name)
367371
# `None` indicates that we respect the globally-configured deletion
368372
# settings, which is what we actually want when auto-deleting.
369373
delete_arg = None if autodelete else False
@@ -588,8 +592,13 @@ def assert_source_matches_version(self):
588592
)
589593

590594
# For both source distributions and editables
591-
def ensure_has_source_dir(self, parent_dir, autodelete=False):
592-
# type: (str, bool) -> None
595+
def ensure_has_source_dir(
596+
self,
597+
parent_dir,
598+
autodelete=False,
599+
parallel_builds=False,
600+
):
601+
# type: (str, bool, bool) -> None
593602
"""Ensure that a source_dir is set.
594603
595604
This will create a temporary build dir if the name of the requirement
@@ -601,7 +610,9 @@ def ensure_has_source_dir(self, parent_dir, autodelete=False):
601610
"""
602611
if self.source_dir is None:
603612
self.source_dir = self.ensure_build_location(
604-
parent_dir, autodelete
613+
parent_dir,
614+
autodelete=autodelete,
615+
parallel_builds=parallel_builds,
605616
)
606617

607618
# For editable installations

src/pip/_internal/resolution/resolvelib/candidates.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,9 @@ def __init__(
255255

256256
def _prepare_abstract_distribution(self):
257257
# type: () -> AbstractDistribution
258-
return self._factory.preparer.prepare_linked_requirement(self._ireq)
258+
return self._factory.preparer.prepare_linked_requirement(
259+
self._ireq, parallel_builds=True,
260+
)
259261

260262

261263
class EditableCandidate(_InstallRequirementBackedCandidate):

tests/functional/test_install_cleanup.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ def test_no_clean_option_blocks_cleaning_after_install(script, data):
2020

2121

2222
@pytest.mark.network
23-
def test_cleanup_prevented_upon_build_dir_exception(script, data):
23+
def test_cleanup_prevented_upon_build_dir_exception(
24+
script,
25+
data,
26+
use_new_resolver,
27+
):
2428
"""
2529
Test no cleanup occurs after a PreviousBuildDirError
2630
"""
@@ -31,12 +35,14 @@ def test_cleanup_prevented_upon_build_dir_exception(script, data):
3135
result = script.pip(
3236
'install', '-f', data.find_links, '--no-index', 'simple',
3337
'--build', build,
34-
expect_error=True, expect_temp=True,
38+
expect_error=(not use_new_resolver),
39+
expect_temp=(not use_new_resolver),
3540
)
3641

37-
assert result.returncode == PREVIOUS_BUILD_DIR_ERROR, str(result)
38-
assert "pip can't proceed" in result.stderr, str(result)
39-
assert exists(build_simple), str(result)
42+
if not use_new_resolver:
43+
assert result.returncode == PREVIOUS_BUILD_DIR_ERROR, str(result)
44+
assert "pip can't proceed" in result.stderr, str(result)
45+
assert exists(build_simple), str(result)
4046

4147

4248
@pytest.mark.network

tests/functional/test_new_resolver.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,6 @@ def test_new_resolver_extra_merge_in_package(
863863
assert_installed(script, pkg="1.0.0", dep="1.0.0", depdev="1.0.0")
864864

865865

866-
@pytest.mark.xfail(reason="pre-existing build directory")
867866
def test_new_resolver_build_directory_error_zazo_19(script):
868867
"""https://github.com/pradyunsg/zazo/issues/19#issuecomment-631615674
869868

tests/functional/test_wheel.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,11 @@ def test_pip_wheel_fail(script, data):
191191
assert result.returncode != 0
192192

193193

194-
def test_no_clean_option_blocks_cleaning_after_wheel(script, data):
194+
def test_no_clean_option_blocks_cleaning_after_wheel(
195+
script,
196+
data,
197+
use_new_resolver,
198+
):
195199
"""
196200
Test --no-clean option blocks cleaning after wheel build
197201
"""
@@ -202,9 +206,11 @@ def test_no_clean_option_blocks_cleaning_after_wheel(script, data):
202206
'simple',
203207
expect_temp=True,
204208
)
205-
build = build / 'simple'
206-
assert exists(build), \
207-
"build/simple should still exist {result}".format(**locals())
209+
210+
if not use_new_resolver:
211+
build = build / 'simple'
212+
message = "build/simple should still exist {}".format(result)
213+
assert exists(build), message
208214

209215

210216
def test_pip_wheel_source_deps(script, data):
@@ -224,7 +230,11 @@ def test_pip_wheel_source_deps(script, data):
224230
assert "Successfully built source" in result.stdout, result.stdout
225231

226232

227-
def test_pip_wheel_fail_cause_of_previous_build_dir(script, data):
233+
def test_pip_wheel_fail_cause_of_previous_build_dir(
234+
script,
235+
data,
236+
use_new_resolver,
237+
):
228238
"""
229239
Test when 'pip wheel' tries to install a package that has a previous build
230240
directory
@@ -240,11 +250,14 @@ def test_pip_wheel_fail_cause_of_previous_build_dir(script, data):
240250
'wheel', '--no-index',
241251
'--find-links={data.find_links}'.format(**locals()),
242252
'--build', script.venv_path / 'build',
243-
'simple==3.0', expect_error=True, expect_temp=True,
253+
'simple==3.0',
254+
expect_error=(not use_new_resolver),
255+
expect_temp=(not use_new_resolver),
244256
)
245257

246258
# Then I see that the error code is the right one
247-
assert result.returncode == PREVIOUS_BUILD_DIR_ERROR, result
259+
if not use_new_resolver:
260+
assert result.returncode == PREVIOUS_BUILD_DIR_ERROR, result
248261

249262

250263
def test_wheel_package_with_latin1_setup(script, data):

tests/unit/test_req_install.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def test_tmp_build_directory(self):
2121
requirement = InstallRequirement(None, None)
2222
tmp_dir = tempfile.mkdtemp('-build', 'pip-')
2323
tmp_build_dir = requirement.ensure_build_location(
24-
tmp_dir, autodelete=False
24+
tmp_dir, autodelete=False, parallel_builds=False,
2525
)
2626
assert (
2727
os.path.dirname(tmp_build_dir) ==

0 commit comments

Comments
 (0)