diff --git a/docs/html/cli/pip_install.rst b/docs/html/cli/pip_install.rst index d9af1b1e8..8f5a31914 100644 --- a/docs/html/cli/pip_install.rst +++ b/docs/html/cli/pip_install.rst @@ -490,18 +490,20 @@ You can install local projects by specifying the project path to pip: py -m pip install path/to/SomeProject -During regular installation, pip will copy the entire project directory to a -temporary location and install from there. The exception is that pip will -exclude .tox and .nox directories present in the top level of the project from -being copied. This approach is the cause of several performance and correctness -issues, so it is planned that pip 21.3 will change to install directly from the -local project directory. Depending on the build backend used by the project, -this may generate secondary build artifacts in the project directory, such as -the ``.egg-info`` and ``build`` directories in the case of the setuptools -backend. +.. note:: -To opt in to the future behavior, specify the ``--use-feature=in-tree-build`` -option in pip's command line. + Depending on the build backend used by the project, this may generate + secondary build artifacts in the project directory, such as the + ``.egg-info`` and ``build`` directories in the case of the setuptools + backend. + + Pip has a legacy behaviour that copies the entire project directory to a + temporary location and installs from there. This approach was the cause of + several performance and correctness issues, so it is now disabled by + default, and it is planned that pip 22.1 will remove it. + + To opt in to the legacy behavior, specify the + ``--use-deprecated=out-of-tree-build`` option in pip's command line. .. _`editable-installs`: diff --git a/news/10495.removal.rst b/news/10495.removal.rst new file mode 100644 index 000000000..9b5d22564 --- /dev/null +++ b/news/10495.removal.rst @@ -0,0 +1,3 @@ +In-tree builds are now the default. ``--use-feature=in-tree-build`` is now +ignored. ``--use-deprecated=out-of-tree-build`` may be used temporarily to ease +the transition. diff --git a/src/pip/_internal/cli/cmdoptions.py b/src/pip/_internal/cli/cmdoptions.py index e24037800..c15c68b6d 100644 --- a/src/pip/_internal/cli/cmdoptions.py +++ b/src/pip/_internal/cli/cmdoptions.py @@ -973,7 +973,7 @@ use_deprecated_feature: Callable[..., Option] = partial( metavar="feature", action="append", default=[], - choices=["legacy-resolver"], + choices=["legacy-resolver", "out-of-tree-build"], help=("Enable deprecated functionality, that will be removed in the future."), ) diff --git a/src/pip/_internal/cli/req_command.py b/src/pip/_internal/cli/req_command.py index 4129bf7e1..c224bd276 100644 --- a/src/pip/_internal/cli/req_command.py +++ b/src/pip/_internal/cli/req_command.py @@ -34,6 +34,7 @@ from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_tracker import RequirementTracker from pip._internal.resolution.base import BaseResolver from pip._internal.self_outdated_check import pip_self_version_check +from pip._internal.utils.deprecation import deprecated from pip._internal.utils.temp_dir import ( TempDirectory, TempDirectoryTypeRegistry, @@ -260,6 +261,20 @@ class RequirementCommand(IndexGroupCommand): "fast-deps has no effect when used with the legacy resolver." ) + in_tree_build = "out-of-tree-build" not in options.deprecated_features_enabled + if "in-tree-build" in options.features_enabled: + deprecated( + reason="In-tree builds are now the default.", + replacement="to remove the --use-feature=in-tree-build flag", + gone_in="22.1", + ) + if "out-of-tree-build" in options.deprecated_features_enabled: + deprecated( + reason="Out-of-tree builds are deprecated.", + replacement=None, + gone_in="22.1", + ) + return RequirementPreparer( build_dir=temp_build_dir_path, src_dir=options.src_dir, @@ -272,7 +287,7 @@ class RequirementCommand(IndexGroupCommand): require_hashes=options.require_hashes, use_user_site=use_user_site, lazy_wheel=lazy_wheel, - in_tree_build="in-tree-build" in options.features_enabled, + in_tree_build=in_tree_build, ) @classmethod diff --git a/src/pip/_internal/operations/prepare.py b/src/pip/_internal/operations/prepare.py index 67ef9dd83..ccf034eb5 100644 --- a/src/pip/_internal/operations/prepare.py +++ b/src/pip/_internal/operations/prepare.py @@ -35,7 +35,6 @@ from pip._internal.network.lazy_wheel import ( from pip._internal.network.session import PipSession from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_tracker import RequirementTracker -from pip._internal.utils.deprecation import deprecated from pip._internal.utils.filesystem import copy2_fixed from pip._internal.utils.hashes import Hashes, MissingHashes from pip._internal.utils.logging import indent_log @@ -197,19 +196,9 @@ def unpack_url( # # As further cleanup, _copy_source_tree and accompanying tests can # be removed. + # + # TODO when use-deprecated=out-of-tree-build is removed if link.is_existing_dir(): - deprecated( - reason=( - "pip copied the source tree into a temporary directory " - "before building it. This is changing so that packages " - "are built in-place " - 'within the original source tree ("in-tree build").' - ), - replacement=None, - gone_in="21.3", - feature_flag="in-tree-build", - issue=7555, - ) if os.path.isdir(location): rmtree(location) _copy_source_tree(link.file_path, location) diff --git a/tests/functional/test_cli.py b/tests/functional/test_cli.py index 1df4836f9..e9d4157b9 100644 --- a/tests/functional/test_cli.py +++ b/tests/functional/test_cli.py @@ -36,7 +36,8 @@ def test_entrypoints_work(entrypoint, script): ) ) - script.pip("install", "-vvv", str(fake_pkg)) + # expect_temp because pip install will generate fake_pkg.egg-info + script.pip("install", "-vvv", str(fake_pkg), expect_temp=True) result = script.pip("-V") result2 = script.run("fake_pip", "-V", allow_stderr_warning=True) assert result.stdout == result2.stdout diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 368973d9b..355113e37 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -587,7 +587,12 @@ def test_install_from_local_directory_with_symlinks_to_directories(script, data) Test installing from a local directory containing symlinks to directories. """ to_install = data.packages.joinpath("symlinks") - result = script.pip("install", to_install) + result = script.pip( + "install", + "--use-deprecated=out-of-tree-build", + to_install, + allow_stderr_warning=True, # TODO: set to False when removing out-of-tree-build + ) pkg_folder = script.site_packages / "symlinks" dist_info_folder = script.site_packages / "symlinks-0.1.dev0.dist-info" result.did_create(pkg_folder) @@ -597,10 +602,10 @@ def test_install_from_local_directory_with_symlinks_to_directories(script, data) @pytest.mark.usefixtures("with_wheel") def test_install_from_local_directory_with_in_tree_build(script, data): """ - Test installing from a local directory with --use-feature=in-tree-build. + Test installing from a local directory with default in tree build. """ to_install = data.packages.joinpath("FSPkg") - args = ["install", "--use-feature=in-tree-build", to_install] + args = ["install", to_install] in_tree_build_dir = to_install / "build" assert not in_tree_build_dir.exists() @@ -618,6 +623,8 @@ def test_install_from_local_directory_with_socket_file(script, data, tmpdir): """ Test installing from a local directory containing a socket file. """ + # TODO: remove this test when removing out-of-tree-build support, + # it is only meant to test the copy of socket files dist_info_folder = script.site_packages / "FSPkg-0.1.dev0.dist-info" package_folder = script.site_packages / "fspkg" to_copy = data.packages.joinpath("FSPkg") @@ -628,7 +635,13 @@ def test_install_from_local_directory_with_socket_file(script, data, tmpdir): socket_file_path = os.path.join(to_install, "example") make_socket_file(socket_file_path) - result = script.pip("install", "--verbose", to_install) + result = script.pip( + "install", + "--use-deprecated=out-of-tree-build", + "--verbose", + to_install, + allow_stderr_warning=True, # because of the out-of-tree deprecation warning + ) result.did_create(package_folder) result.did_create(dist_info_folder) assert str(socket_file_path) in result.stderr diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py index 57bb527da..3adaf438b 100644 --- a/tests/functional/test_uninstall.py +++ b/tests/functional/test_uninstall.py @@ -12,6 +12,7 @@ from pip._internal.req.constructors import install_req_from_line from pip._internal.utils.misc import rmtree from tests.lib import assert_all_changes, create_test_package_with_setup, need_svn from tests.lib.local_repos import local_checkout, local_repo +from tests.lib.path import Path @pytest.mark.network @@ -279,7 +280,15 @@ def test_uninstall_console_scripts(script): result = script.pip("install", pkg_path) result.did_create(script.bin / "discover" + script.exe) result2 = script.pip("uninstall", "discover", "-y") - assert_all_changes(result, result2, [script.venv / "build", "cache"]) + assert_all_changes( + result, + result2, + [ + script.venv / "build", + "cache", + Path("scratch") / "discover" / "discover.egg-info", + ], + ) def test_uninstall_console_scripts_uppercase_name(script):