From 16684fbc28b17eff558e20813e06dc546780eea6 Mon Sep 17 00:00:00 2001 From: Chris Hunt Date: Mon, 4 Nov 2019 17:41:32 -0500 Subject: Create Scheme model and wrap distutils_scheme Now we have a nicely-typed interface to the calculated scheme, and some more documentation for the same. --- src/pip/_internal/locations.py | 39 ++++++++++++++++++++++++++++++++++++++ src/pip/_internal/models/scheme.py | 25 ++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/pip/_internal/models/scheme.py diff --git a/src/pip/_internal/locations.py b/src/pip/_internal/locations.py index 5dd10e814..b54b46bbe 100644 --- a/src/pip/_internal/locations.py +++ b/src/pip/_internal/locations.py @@ -16,6 +16,7 @@ from distutils import sysconfig as distutils_sysconfig from distutils.command.install import SCHEME_KEYS # type: ignore from distutils.command.install import install as distutils_install_command +from pip._internal.models.scheme import Scheme from pip._internal.utils import appdirs from pip._internal.utils.compat import WINDOWS from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast @@ -153,3 +154,41 @@ def distutils_scheme( ) return scheme + + +def get_scheme( + dist_name, # type: str + user=False, # type: bool + home=None, # type: Optional[str] + root=None, # type: Optional[str] + isolated=False, # type: bool + prefix=None, # type: Optional[str] +): + # type: (...) -> Scheme + """ + Get the "scheme" corresponding to the input parameters. The distutils + documentation provides the context for the available schemes: + https://docs.python.org/3/install/index.html#alternate-installation + + :param dist_name: the name of the package to retrieve the scheme for, used + in the headers scheme path + :param user: indicates to use the "user" scheme + :param home: indicates to use the "home" scheme and provides the base + directory for the same + :param root: root under which other directories are re-based + :param isolated: equivalent to --no-user-cfg, i.e. do not consider + ~/.pydistutils.cfg (posix) or ~/pydistutils.cfg (non-posix) for + scheme paths + :param prefix: indicates to use the "prefix" scheme and provides the + base directory for the same + """ + scheme = distutils_scheme( + dist_name, user, home, root, isolated, prefix + ) + return Scheme( + platlib=scheme["platlib"], + purelib=scheme["purelib"], + headers=scheme["headers"], + scripts=scheme["scripts"], + data=scheme["data"], + ) diff --git a/src/pip/_internal/models/scheme.py b/src/pip/_internal/models/scheme.py new file mode 100644 index 000000000..af07b4078 --- /dev/null +++ b/src/pip/_internal/models/scheme.py @@ -0,0 +1,25 @@ +""" +For types associated with installation schemes. + +For a general overview of available schemes and their context, see +https://docs.python.org/3/install/index.html#alternate-installation. +""" + + +class Scheme(object): + """A Scheme holds paths which are used as the base directories for + artifacts associated with a Python package. + """ + def __init__( + self, + platlib, # type: str + purelib, # type: str + headers, # type: str + scripts, # type: str + data, # type: str + ): + self.platlib = platlib + self.purelib = purelib + self.headers = headers + self.scripts = scripts + self.data = data -- cgit v1.2.1 From 9de8a6d598b100071e212c7d8b40643223cf05ec Mon Sep 17 00:00:00 2001 From: Chris Hunt Date: Wed, 6 Nov 2019 20:54:47 -0500 Subject: Create scheme unconditionally on install --- src/pip/_internal/req/req_install.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 8033c69a4..fb378d28f 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -852,6 +852,15 @@ class InstallRequirement(object): pycompile=True # type: bool ): # type: (...) -> None + scheme = distutils_scheme( + self.name, + user=use_user_site, + home=home, + root=root, + isolated=self.isolated, + prefix=prefix, + ) + global_options = global_options if global_options is not None else [] if self.editable: self.install_editable( @@ -862,14 +871,11 @@ class InstallRequirement(object): use_user_site=use_user_site, ) return + if self.is_wheel: version = wheel.wheel_version(self.source_dir) wheel.check_compatibility(version, self.name) - scheme = distutils_scheme( - self.name, user=use_user_site, home=home, root=root, - isolated=self.isolated, prefix=prefix, - ) self.move_wheel_files( self.source_dir, scheme=scheme, -- cgit v1.2.1 From e03a71aff8c1b6dfaaacdd4abc14386a43f6b52a Mon Sep 17 00:00:00 2001 From: Chris Hunt Date: Wed, 6 Nov 2019 21:10:11 -0500 Subject: Use new Scheme model for wheel installation --- src/pip/_internal/req/req_install.py | 9 +++++---- src/pip/_internal/wheel.py | 14 ++++++++------ tests/unit/test_wheel.py | 25 ++++++++++++++----------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index fb378d28f..5a86532cc 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -22,7 +22,7 @@ from pip._vendor.pep517.wrappers import Pep517HookCaller from pip._internal import pep425tags, wheel from pip._internal.build_env import NoOpBuildEnvironment from pip._internal.exceptions import InstallationError -from pip._internal.locations import distutils_scheme +from pip._internal.locations import get_scheme from pip._internal.models.link import Link from pip._internal.operations.build.metadata import generate_metadata from pip._internal.operations.build.metadata_legacy import \ @@ -65,11 +65,12 @@ from pip._internal.vcs import vcs if MYPY_CHECK_RUNNING: from typing import ( - Any, Dict, Iterable, List, Mapping, Optional, Sequence, Union, + Any, Dict, Iterable, List, Optional, Sequence, Union, ) from pip._internal.build_env import BuildEnvironment from pip._internal.cache import WheelCache from pip._internal.index.package_finder import PackageFinder + from pip._internal.models.scheme import Scheme from pip._vendor.pkg_resources import Distribution from pip._vendor.packaging.specifiers import SpecifierSet from pip._vendor.packaging.markers import Marker @@ -540,7 +541,7 @@ class InstallRequirement(object): def move_wheel_files( self, wheeldir, # type: str - scheme, # type: Mapping[str, str] + scheme, # type: Scheme warn_script_location=True, # type: bool pycompile=True # type: bool ): @@ -852,7 +853,7 @@ class InstallRequirement(object): pycompile=True # type: bool ): # type: (...) -> None - scheme = distutils_scheme( + scheme = get_scheme( self.name, user=use_user_site, home=home, diff --git a/src/pip/_internal/wheel.py b/src/pip/_internal/wheel.py index 85a9e2b60..682a37e49 100644 --- a/src/pip/_internal/wheel.py +++ b/src/pip/_internal/wheel.py @@ -36,9 +36,11 @@ from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: from typing import ( - Dict, List, Optional, Sequence, Mapping, Tuple, IO, Text, Any, + Dict, List, Optional, Sequence, Tuple, IO, Text, Any, Iterable, Callable, Set, ) + + from pip._internal.models.scheme import Scheme from pip._internal.pep425tags import Pep425Tag InstalledCSVRow = Tuple[str, ...] @@ -289,7 +291,7 @@ class PipScriptMaker(ScriptMaker): def install_unpacked_wheel( name, # type: str wheeldir, # type: str - scheme, # type: Mapping[str, str] + scheme, # type: Scheme req_description, # type: str pycompile=True, # type: bool warn_script_location=True # type: bool @@ -311,9 +313,9 @@ def install_unpacked_wheel( # installation. if root_is_purelib(name, wheeldir): - lib_dir = scheme['purelib'] + lib_dir = scheme.purelib else: - lib_dir = scheme['platlib'] + lib_dir = scheme.platlib info_dir = [] # type: List[str] data_dirs = [] @@ -458,10 +460,10 @@ def install_unpacked_wheel( fixer = fix_script filter = is_entrypoint_wrapper source = os.path.join(wheeldir, datadir, subdir) - dest = scheme[subdir] + dest = getattr(scheme, subdir) clobber(source, dest, False, fixer=fixer, filter=filter) - maker = PipScriptMaker(None, scheme['scripts']) + maker = PipScriptMaker(None, scheme.scripts) # Ensure old scripts are overwritten. # See https://github.com/pypa/pip/issues/1800 diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py index 3153f2a82..ea145d722 100644 --- a/tests/unit/test_wheel.py +++ b/tests/unit/test_wheel.py @@ -11,8 +11,9 @@ from pip._vendor.packaging.requirements import Requirement from pip._internal import pep425tags, wheel from pip._internal.commands.wheel import WheelCommand from pip._internal.exceptions import InvalidWheelFilename, UnsupportedWheel -from pip._internal.locations import distutils_scheme +from pip._internal.locations import get_scheme from pip._internal.models.link import Link +from pip._internal.models.scheme import Scheme from pip._internal.req.req_install import InstallRequirement from pip._internal.utils.compat import WINDOWS from pip._internal.utils.misc import hash_file @@ -482,29 +483,31 @@ class TestInstallUnpackedWheel(object): self.src = os.path.join(tmpdir, 'src') self.dest = os.path.join(tmpdir, 'dest') unpack_file(self.wheelpath, self.src) - self.scheme = { - 'scripts': os.path.join(self.dest, 'bin'), - 'purelib': os.path.join(self.dest, 'lib'), - 'data': os.path.join(self.dest, 'data'), - } + self.scheme = Scheme( + purelib=os.path.join(self.dest, 'lib'), + platlib=os.path.join(self.dest, 'lib'), + headers=os.path.join(self.dest, 'headers'), + scripts=os.path.join(self.dest, 'bin'), + data=os.path.join(self.dest, 'data'), + ) self.src_dist_info = os.path.join( self.src, 'sample-1.2.0.dist-info') self.dest_dist_info = os.path.join( - self.scheme['purelib'], 'sample-1.2.0.dist-info') + self.scheme.purelib, 'sample-1.2.0.dist-info') def assert_installed(self): # lib assert os.path.isdir( - os.path.join(self.scheme['purelib'], 'sample')) + os.path.join(self.scheme.purelib, 'sample')) # dist-info metadata = os.path.join(self.dest_dist_info, 'METADATA') assert os.path.isfile(metadata) # data files - data_file = os.path.join(self.scheme['data'], 'my_data', 'data_file') + data_file = os.path.join(self.scheme.data, 'my_data', 'data_file') assert os.path.isfile(data_file) # package data pkg_data = os.path.join( - self.scheme['purelib'], 'sample', 'package_data.dat') + self.scheme.purelib, 'sample', 'package_data.dat') assert os.path.isfile(pkg_data) def test_std_install(self, data, tmpdir): @@ -520,7 +523,7 @@ class TestInstallUnpackedWheel(object): def test_install_prefix(self, data, tmpdir): prefix = os.path.join(os.path.sep, 'some', 'path') self.prep(data, tmpdir) - scheme = distutils_scheme( + scheme = get_scheme( self.name, user=False, home=None, -- cgit v1.2.1 From 6fa64a6b285ae8e5daaccbf346fb922bd023a494 Mon Sep 17 00:00:00 2001 From: Chris Hunt Date: Wed, 6 Nov 2019 21:10:19 -0500 Subject: Calculate header directories in one place This was already happening in locations.distutils_scheme, we're just reusing the existing work instead of doing it again. --- src/pip/_internal/req/req_install.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 5a86532cc..674545c9f 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -8,7 +8,6 @@ import logging import os import shutil import sys -import sysconfig import zipfile from distutils.util import change_root @@ -896,12 +895,7 @@ class InstallRequirement(object): install_options = list(install_options) + \ self.options.get('install_options', []) - header_dir = None # type: Optional[str] - if running_under_virtualenv(): - py_ver_str = 'python' + sysconfig.get_python_version() - header_dir = os.path.join( - sys.prefix, 'include', 'site', py_ver_str, self.name - ) + header_dir = scheme.headers with TempDirectory(kind="record") as temp_dir: record_filename = os.path.join(temp_dir.path, 'install-record.txt') -- cgit v1.2.1