Skip to content

Commit e679df4

Browse files
authored
Merge pull request #11001 from pradyunsg/remove-out-of-tree
Drop out-of-tree/in-tree build transition flags
2 parents eacc739 + 428e886 commit e679df4

File tree

11 files changed

+13
-373
lines changed

11 files changed

+13
-373
lines changed

docs/html/topics/local-project-installs.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,17 @@ There are two advantages over using `setup.py develop` directly:
5151
## Build artifacts
5252

5353
```{versionchanged} 21.3
54-
The project being installed is no longer copied to a temporary directory before invoking the build system.
54+
The project being installed is no longer copied to a temporary directory before invoking the build system, by default. A `--use-deprecated=out-of-tree-build` option is provided as a temporary fallback to aid user migrations.
5555
```
5656

57-
This behaviour change has several consequences:
57+
```{versionchanged} 22.1
58+
The `--use-deprecated=out-of-tree-build` option has been removed.
59+
```
60+
61+
When provided with a project that's in a local directory, pip will invoke the build system "in place". This behaviour has several consequences:
5862

5963
- Local project builds will now be significantly faster, for certain kinds of projects and on systems with slow I/O (eg: via network attached storage or overly aggressive antivirus software).
6064
- Certain build backends (eg: `setuptools`) will litter the project directory with secondary build artifacts (eg: `.egg-info` directories).
6165
- Certain build backends (eg: `setuptools`) may not be able to perform with parallel builds anymore, since they previously relied on the fact that pip invoked them in a separate directory for each build.
6266

63-
A `--use-deprecated=out-of-tree-build` option is available, until pip 22.1, as a mechanism to aid users with transitioning to the newer model of in-tree-builds.
64-
6567
[^1]: Specifically, the current machine's filesystem.

news/11001.removal.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Drop ``--use-deprecated=out-of-tree-build``, according to deprecation message.

src/pip/_internal/cli/cmdoptions.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,7 @@ def check_list_path_option(options: Values) -> None:
957957
metavar="feature",
958958
action="append",
959959
default=[],
960-
choices=["2020-resolver", "fast-deps", "in-tree-build"],
960+
choices=["2020-resolver", "fast-deps"],
961961
help="Enable new functionality, that may be backward incompatible.",
962962
)
963963

@@ -970,7 +970,6 @@ def check_list_path_option(options: Values) -> None:
970970
default=[],
971971
choices=[
972972
"legacy-resolver",
973-
"out-of-tree-build",
974973
"backtrack-on-build-failures",
975974
"html5lib",
976975
],

src/pip/_internal/cli/req_command.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -288,20 +288,6 @@ def make_requirement_preparer(
288288
"fast-deps has no effect when used with the legacy resolver."
289289
)
290290

291-
in_tree_build = "out-of-tree-build" not in options.deprecated_features_enabled
292-
if "in-tree-build" in options.features_enabled:
293-
deprecated(
294-
reason="In-tree builds are now the default.",
295-
replacement="to remove the --use-feature=in-tree-build flag",
296-
gone_in="22.1",
297-
)
298-
if "out-of-tree-build" in options.deprecated_features_enabled:
299-
deprecated(
300-
reason="Out-of-tree builds are deprecated.",
301-
replacement=None,
302-
gone_in="22.1",
303-
)
304-
305291
return RequirementPreparer(
306292
build_dir=temp_build_dir_path,
307293
src_dir=options.src_dir,
@@ -315,7 +301,6 @@ def make_requirement_preparer(
315301
use_user_site=use_user_site,
316302
lazy_wheel=lazy_wheel,
317303
verbosity=verbosity,
318-
in_tree_build=in_tree_build,
319304
)
320305

321306
@classmethod

src/pip/_internal/operations/prepare.py

Lines changed: 4 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@
3535
from pip._internal.network.session import PipSession
3636
from pip._internal.operations.build.build_tracker import BuildTracker
3737
from pip._internal.req.req_install import InstallRequirement
38-
from pip._internal.utils.filesystem import copy2_fixed
3938
from pip._internal.utils.hashes import Hashes, MissingHashes
4039
from pip._internal.utils.logging import indent_log
41-
from pip._internal.utils.misc import display_path, hide_url, is_installable_dir, rmtree
40+
from pip._internal.utils.misc import display_path, hide_url, is_installable_dir
4241
from pip._internal.utils.temp_dir import TempDirectory
4342
from pip._internal.utils.unpacking import unpack_file
4443
from pip._internal.vcs import vcs
@@ -98,55 +97,6 @@ def get_http_url(
9897
return File(from_path, content_type)
9998

10099

101-
def _copy2_ignoring_special_files(src: str, dest: str) -> None:
102-
"""Copying special files is not supported, but as a convenience to users
103-
we skip errors copying them. This supports tools that may create e.g.
104-
socket files in the project source directory.
105-
"""
106-
try:
107-
copy2_fixed(src, dest)
108-
except shutil.SpecialFileError as e:
109-
# SpecialFileError may be raised due to either the source or
110-
# destination. If the destination was the cause then we would actually
111-
# care, but since the destination directory is deleted prior to
112-
# copy we ignore all of them assuming it is caused by the source.
113-
logger.warning(
114-
"Ignoring special file error '%s' encountered copying %s to %s.",
115-
str(e),
116-
src,
117-
dest,
118-
)
119-
120-
121-
def _copy_source_tree(source: str, target: str) -> None:
122-
target_abspath = os.path.abspath(target)
123-
target_basename = os.path.basename(target_abspath)
124-
target_dirname = os.path.dirname(target_abspath)
125-
126-
def ignore(d: str, names: List[str]) -> List[str]:
127-
skipped: List[str] = []
128-
if d == source:
129-
# Pulling in those directories can potentially be very slow,
130-
# exclude the following directories if they appear in the top
131-
# level dir (and only it).
132-
# See discussion at https://github.com/pypa/pip/pull/6770
133-
skipped += [".tox", ".nox"]
134-
if os.path.abspath(d) == target_dirname:
135-
# Prevent an infinite recursion if the target is in source.
136-
# This can happen when TMPDIR is set to ${PWD}/...
137-
# and we copy PWD to TMPDIR.
138-
skipped += [target_basename]
139-
return skipped
140-
141-
shutil.copytree(
142-
source,
143-
target,
144-
ignore=ignore,
145-
symlinks=True,
146-
copy_function=_copy2_ignoring_special_files,
147-
)
148-
149-
150100
def get_file_url(
151101
link: Link, download_dir: Optional[str] = None, hashes: Optional[Hashes] = None
152102
) -> File:
@@ -191,19 +141,7 @@ def unpack_url(
191141
unpack_vcs_link(link, location, verbosity=verbosity)
192142
return None
193143

194-
# Once out-of-tree-builds are no longer supported, could potentially
195-
# replace the below condition with `assert not link.is_existing_dir`
196-
# - unpack_url does not need to be called for in-tree-builds.
197-
#
198-
# As further cleanup, _copy_source_tree and accompanying tests can
199-
# be removed.
200-
#
201-
# TODO when use-deprecated=out-of-tree-build is removed
202-
if link.is_existing_dir():
203-
if os.path.isdir(location):
204-
rmtree(location)
205-
_copy_source_tree(link.file_path, location)
206-
return None
144+
assert not link.is_existing_dir()
207145

208146
# file urls
209147
if link.is_file:
@@ -269,7 +207,6 @@ def __init__(
269207
use_user_site: bool,
270208
lazy_wheel: bool,
271209
verbosity: int,
272-
in_tree_build: bool,
273210
) -> None:
274211
super().__init__()
275212

@@ -300,9 +237,6 @@ def __init__(
300237
# How verbose should underlying tooling be?
301238
self.verbosity = verbosity
302239

303-
# Should in-tree builds be used for local paths?
304-
self.in_tree_build = in_tree_build
305-
306240
# Memoized downloaded files, as mapping of url: path.
307241
self._downloaded: Dict[str, str] = {}
308242

@@ -336,7 +270,7 @@ def _ensure_link_req_src_dir(
336270
# directory.
337271
return
338272
assert req.source_dir is None
339-
if req.link.is_existing_dir() and self.in_tree_build:
273+
if req.link.is_existing_dir():
340274
# build local directories in-tree
341275
req.source_dir = req.link.file_path
342276
return
@@ -525,7 +459,7 @@ def _prepare_linked_requirement(
525459
self._ensure_link_req_src_dir(req, parallel_builds)
526460
hashes = self._get_linked_req_hashes(req)
527461

528-
if link.is_existing_dir() and self.in_tree_build:
462+
if link.is_existing_dir():
529463
local_file = None
530464
elif link.url not in self._downloaded:
531465
try:

src/pip/_internal/utils/filesystem.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
import os
33
import os.path
44
import random
5-
import shutil
6-
import stat
75
import sys
86
from contextlib import contextmanager
97
from tempfile import NamedTemporaryFile
@@ -42,33 +40,6 @@ def check_path_owner(path: str) -> bool:
4240
return False # assume we don't own the path
4341

4442

45-
def copy2_fixed(src: str, dest: str) -> None:
46-
"""Wrap shutil.copy2() but map errors copying socket files to
47-
SpecialFileError as expected.
48-
49-
See also https://bugs.python.org/issue37700.
50-
"""
51-
try:
52-
shutil.copy2(src, dest)
53-
except OSError:
54-
for f in [src, dest]:
55-
try:
56-
is_socket_file = is_socket(f)
57-
except OSError:
58-
# An error has already occurred. Another error here is not
59-
# a problem and we can ignore it.
60-
pass
61-
else:
62-
if is_socket_file:
63-
raise shutil.SpecialFileError(f"`{f}` is a socket")
64-
65-
raise
66-
67-
68-
def is_socket(path: str) -> bool:
69-
return stat.S_ISSOCK(os.lstat(path).st_mode)
70-
71-
7243
@contextmanager
7344
def adjacent_tmp_file(path: str, **kwargs: Any) -> Iterator[BinaryIO]:
7445
"""Return a file-like object pointing to a tmp file next to path.

tests/functional/test_install.py

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import glob
33
import os
44
import re
5-
import shutil
65
import ssl
76
import sys
87
import textwrap
@@ -30,7 +29,6 @@
3029
pyversion,
3130
requirements_file,
3231
)
33-
from tests.lib.filesystem import make_socket_file
3432
from tests.lib.local_repos import local_checkout
3533
from tests.lib.path import Path
3634
from tests.lib.server import (
@@ -648,26 +646,6 @@ def test_hashed_install_failure_later_flag(
648646
)
649647

650648

651-
@pytest.mark.usefixtures("with_wheel")
652-
def test_install_from_local_directory_with_symlinks_to_directories(
653-
script: PipTestEnvironment, data: TestData
654-
) -> None:
655-
"""
656-
Test installing from a local directory containing symlinks to directories.
657-
"""
658-
to_install = data.packages.joinpath("symlinks")
659-
result = script.pip(
660-
"install",
661-
"--use-deprecated=out-of-tree-build",
662-
to_install,
663-
allow_stderr_warning=True, # TODO: set to False when removing out-of-tree-build
664-
)
665-
pkg_folder = script.site_packages / "symlinks"
666-
dist_info_folder = script.site_packages / "symlinks-0.1.dev0.dist-info"
667-
result.did_create(pkg_folder)
668-
result.did_create(dist_info_folder)
669-
670-
671649
@pytest.mark.usefixtures("with_wheel")
672650
def test_install_from_local_directory_with_in_tree_build(
673651
script: PipTestEnvironment, data: TestData
@@ -688,38 +666,6 @@ def test_install_from_local_directory_with_in_tree_build(
688666
assert in_tree_build_dir.exists()
689667

690668

691-
@pytest.mark.skipif("sys.platform == 'win32'")
692-
@pytest.mark.usefixtures("with_wheel")
693-
def test_install_from_local_directory_with_socket_file(
694-
script: PipTestEnvironment, data: TestData, tmpdir: Path
695-
) -> None:
696-
"""
697-
Test installing from a local directory containing a socket file.
698-
"""
699-
# TODO: remove this test when removing out-of-tree-build support,
700-
# it is only meant to test the copy of socket files
701-
dist_info_folder = script.site_packages / "FSPkg-0.1.dev0.dist-info"
702-
package_folder = script.site_packages / "fspkg"
703-
to_copy = data.packages.joinpath("FSPkg")
704-
to_install = tmpdir.joinpath("src")
705-
706-
shutil.copytree(to_copy, to_install)
707-
# Socket file, should be ignored.
708-
socket_file_path = os.path.join(to_install, "example")
709-
make_socket_file(socket_file_path)
710-
711-
result = script.pip(
712-
"install",
713-
"--use-deprecated=out-of-tree-build",
714-
"--verbose",
715-
to_install,
716-
allow_stderr_warning=True, # because of the out-of-tree deprecation warning
717-
)
718-
result.did_create(package_folder)
719-
result.did_create(dist_info_folder)
720-
assert str(socket_file_path) in result.stderr
721-
722-
723669
def test_install_from_local_directory_with_no_setup_py(
724670
script: PipTestEnvironment, data: TestData
725671
) -> None:

tests/lib/filesystem.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,10 @@
11
"""Helpers for filesystem-dependent tests.
22
"""
33
import os
4-
import socket
5-
import subprocess
6-
import sys
74
from functools import partial
85
from itertools import chain
96
from typing import Iterator, List, Set
107

11-
from .path import Path
12-
13-
14-
def make_socket_file(path: str) -> None:
15-
# Socket paths are limited to 108 characters (sometimes less) so we
16-
# chdir before creating it and use a relative path name.
17-
cwd = os.getcwd()
18-
os.chdir(os.path.dirname(path))
19-
try:
20-
sock = socket.socket(socket.AF_UNIX)
21-
sock.bind(os.path.basename(path))
22-
finally:
23-
os.chdir(cwd)
24-
25-
26-
def make_unreadable_file(path: str) -> None:
27-
Path(path).touch()
28-
os.chmod(path, 0o000)
29-
if sys.platform == "win32":
30-
username = os.getlogin()
31-
# Remove "Read Data/List Directory" permission for current user, but
32-
# leave everything else.
33-
args = ["icacls", path, "/deny", username + ":(RD)"]
34-
subprocess.check_call(args)
35-
368

379
def get_filelist(base: str) -> Set[str]:
3810
def join(dirpath: str, dirnames: List[str], filenames: List[str]) -> Iterator[str]:

0 commit comments

Comments
 (0)