diff --git a/src/pip/_internal/commands/install.py b/src/pip/_internal/commands/install.py index 88c5e9aaba9..63e34da26eb 100644 --- a/src/pip/_internal/commands/install.py +++ b/src/pip/_internal/commands/install.py @@ -24,7 +24,7 @@ from pip._internal.cli.req_command import RequirementCommand, with_cleanup from pip._internal.cli.status_codes import ERROR, SUCCESS from pip._internal.exceptions import CommandError, InstallationError -from pip._internal.locations import distutils_scheme +from pip._internal.locations import distutils_scheme, get_scheme from pip._internal.operations.check import check_install_conflicts from pip._internal.req import install_given_reqs from pip._internal.req.req_tracker import get_requirement_tracker @@ -46,6 +46,7 @@ from optparse import Values from typing import Any, Iterable, List, Optional + from pip._internal.locations import Scheme from pip._internal.models.format_control import FormatControl from pip._internal.req.req_install import InstallRequirement from pip._internal.wheel_builder import BinaryAllowedPredicate @@ -351,12 +352,24 @@ def run(self, options, args): ) ] + def get_scheme_for_req(name): + # type: (str) -> Scheme + return get_scheme( + name, + user=options.use_user_site, + home=target_temp_dir_path, + root=options.root_path, + isolated=options.isolated_mode, + prefix=options.prefix_path, + ) + _, build_failures = build( reqs_to_build, wheel_cache=wheel_cache, build_options=[], global_options=[], allow_editable=True, + get_scheme_for_editable_req=get_scheme_for_req, ) # If we're using PEP 517, we cannot do a direct install diff --git a/src/pip/_internal/commands/wheel.py b/src/pip/_internal/commands/wheel.py index d9c817869cc..2c409cdb1aa 100644 --- a/src/pip/_internal/commands/wheel.py +++ b/src/pip/_internal/commands/wheel.py @@ -168,6 +168,7 @@ def run(self, options, args): build_options=options.build_options or [], global_options=options.global_options or [], allow_editable=False, + get_scheme_for_editable_req=None, ) for req in build_successes: assert req.link and req.link.is_wheel diff --git a/src/pip/_internal/locations.py b/src/pip/_internal/locations.py index 0c115531911..05bd6ff590c 100644 --- a/src/pip/_internal/locations.py +++ b/src/pip/_internal/locations.py @@ -22,10 +22,12 @@ from pip._internal.utils.virtualenv import running_under_virtualenv if MYPY_CHECK_RUNNING: - from typing import Dict, List, Optional, Union + from typing import Callable, Dict, List, Optional, Union from distutils.cmd import Command as DistutilsCommand + GetSchemePredicate = Callable[[str], Scheme] + # Application Directories USER_CACHE_DIR = appdirs.user_cache_dir("pip") diff --git a/src/pip/_internal/models/scheme.py b/src/pip/_internal/models/scheme.py index b9d0ea68a41..915e9a08ac7 100644 --- a/src/pip/_internal/models/scheme.py +++ b/src/pip/_internal/models/scheme.py @@ -4,6 +4,10 @@ For a general overview of available schemes and their context, see https://docs.python.org/3/install/index.html#alternate-installation. """ +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict class Scheme(object): @@ -26,3 +30,13 @@ def __init__( self.headers = headers self.scripts = scripts self.data = data + + def as_dict(self): + # type: () -> Dict[str, str] + return dict( + platlib=self.platlib, + purelib=self.purelib, + headers=self.headers, + scripts=self.scripts, + data=self.data, + ) diff --git a/src/pip/_internal/operations/build/wheel.py b/src/pip/_internal/operations/build/wheel.py index 8cca67c44af..4c4a3a573d8 100644 --- a/src/pip/_internal/operations/build/wheel.py +++ b/src/pip/_internal/operations/build/wheel.py @@ -8,6 +8,7 @@ if MYPY_CHECK_RUNNING: from typing import List, Optional + from pip._internal.locations import Scheme from pip._vendor.pep517.wrappers import Pep517HookCaller logger = logging.getLogger(__name__) @@ -20,6 +21,7 @@ def build_wheel_pep517( build_options, # type: List[str] tempd, # type: str editable, # type: bool + scheme, # type: Scheme ): # type: (...) -> Optional[str] """Build one InstallRequirement using the PEP 517 build process. @@ -43,6 +45,7 @@ def build_wheel_pep517( try: wheel_name = backend.build_wheel_for_editable( tempd, + scheme=scheme.as_dict(), metadata_directory=metadata_directory, ) except HookMissing: diff --git a/src/pip/_internal/wheel_builder.py b/src/pip/_internal/wheel_builder.py index 7c40cfcca40..965a5241eaa 100644 --- a/src/pip/_internal/wheel_builder.py +++ b/src/pip/_internal/wheel_builder.py @@ -24,6 +24,7 @@ ) from pip._internal.cache import WheelCache + from pip._internal.locations import GetSchemePredicate, Scheme from pip._internal.req.req_install import InstallRequirement BinaryAllowedPredicate = Callable[[InstallRequirement], bool] @@ -172,6 +173,7 @@ def _build_one( build_options, # type: List[str] global_options, # type: List[str] editable, # type: bool + scheme, # type: Scheme ): # type: (...) -> Optional[str] """Build one wheel. @@ -190,7 +192,7 @@ def _build_one( # Install build deps into temporary directory (PEP 518) with req.build_env: return _build_one_inside_env( - req, output_dir, build_options, global_options, editable + req, output_dir, build_options, global_options, editable, scheme ) @@ -200,6 +202,7 @@ def _build_one_inside_env( build_options, # type: List[str] global_options, # type: List[str] editable, # type: bool + scheme, # type: Scheme ): # type: (...) -> Optional[str] with TempDirectory(kind="wheel") as temp_dir: @@ -213,6 +216,7 @@ def _build_one_inside_env( build_options=build_options, tempd=temp_dir.path, editable=editable, + scheme=scheme, ) else: wheel_path = build_wheel_legacy( @@ -269,6 +273,7 @@ def build( build_options, # type: List[str] global_options, # type: List[str] allow_editable, # type: bool + get_scheme_for_editable_req, # type: Optional[GetSchemePredicate] ): # type: (...) -> BuildResult """Build wheels. @@ -276,6 +281,8 @@ def build( :return: The list of InstallRequirement that succeeded to build and the list of InstallRequirement that failed to build. """ + assert allow_editable == bool(get_scheme_for_editable_req) + if not requirements: return [], [] @@ -289,11 +296,15 @@ def build( build_successes, build_failures = [], [] for req in requirements: cache_dir = _get_cache_dir(req, wheel_cache) + scheme = None + if get_scheme_for_editable_req: + scheme = get_scheme_for_editable_req(req.name) wheel_file = _build_one( req, cache_dir, build_options, global_options, allow_editable and req.editable, + scheme=scheme, ) if wheel_file: # Update the link for this. diff --git a/src/pip/_vendor/pep517/_in_process.py b/src/pip/_vendor/pep517/_in_process.py index 1822f034499..7652133803b 100644 --- a/src/pip/_vendor/pep517/_in_process.py +++ b/src/pip/_vendor/pep517/_in_process.py @@ -206,7 +206,7 @@ def build_wheel(wheel_directory, config_settings, metadata_directory=None): def build_wheel_for_editable( - wheel_directory, config_settings, metadata_directory=None + wheel_directory, scheme, config_settings, metadata_directory=None ): """Invoke the optional build_wheel_for_editable hook. """ @@ -216,7 +216,9 @@ def build_wheel_for_editable( except AttributeError: raise HookMissing() else: - return hook(wheel_directory, config_settings, metadata_directory) + return hook( + wheel_directory, scheme, config_settings, metadata_directory + ) def get_requires_for_build_sdist(config_settings): diff --git a/src/pip/_vendor/pep517/wrappers.py b/src/pip/_vendor/pep517/wrappers.py index 315bba19a6d..8fe09425c10 100644 --- a/src/pip/_vendor/pep517/wrappers.py +++ b/src/pip/_vendor/pep517/wrappers.py @@ -200,16 +200,20 @@ def build_wheel( }) def build_wheel_for_editable( - self, wheel_directory, config_settings=None, + self, wheel_directory, scheme, config_settings=None, metadata_directory=None): """Build a wheel for editable install from this project. Returns the name of the newly created file. + + 'scheme' is a dictionary with the following keys: + 'purelib', 'platlib', 'headers', 'scripts', 'data'. """ if metadata_directory is not None: metadata_directory = abspath(metadata_directory) return self._call_hook('build_wheel_for_editable', { 'wheel_directory': abspath(wheel_directory), + 'scheme': scheme, 'config_settings': config_settings, 'metadata_directory': metadata_directory, })