mirror of https://github.com/pypa/pip
Refactor wheel.move_wheel_files to use updated distlib (#6763)
This commit is contained in:
commit
e0239735a3
|
@ -0,0 +1,2 @@
|
||||||
|
Switch to new ``distlib`` wheel script template. This should be functionally
|
||||||
|
equivalent for end users.
|
|
@ -23,6 +23,7 @@ from email.parser import Parser
|
||||||
|
|
||||||
from pip._vendor import pkg_resources
|
from pip._vendor import pkg_resources
|
||||||
from pip._vendor.distlib.scripts import ScriptMaker
|
from pip._vendor.distlib.scripts import ScriptMaker
|
||||||
|
from pip._vendor.distlib.util import get_export_entry
|
||||||
from pip._vendor.packaging.utils import canonicalize_name
|
from pip._vendor.packaging.utils import canonicalize_name
|
||||||
from pip._vendor.six import StringIO
|
from pip._vendor.six import StringIO
|
||||||
|
|
||||||
|
@ -313,6 +314,22 @@ def get_csv_rows_for_installed(
|
||||||
return installed_rows
|
return installed_rows
|
||||||
|
|
||||||
|
|
||||||
|
class MissingCallableSuffix(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _raise_for_invalid_entrypoint(specification):
|
||||||
|
entry = get_export_entry(specification)
|
||||||
|
if entry is not None and entry.suffix is None:
|
||||||
|
raise MissingCallableSuffix(str(entry))
|
||||||
|
|
||||||
|
|
||||||
|
class PipScriptMaker(ScriptMaker):
|
||||||
|
def make(self, specification, options=None):
|
||||||
|
_raise_for_invalid_entrypoint(specification)
|
||||||
|
return super(PipScriptMaker, self).make(specification, options)
|
||||||
|
|
||||||
|
|
||||||
def move_wheel_files(
|
def move_wheel_files(
|
||||||
name, # type: str
|
name, # type: str
|
||||||
req, # type: Requirement
|
req, # type: Requirement
|
||||||
|
@ -475,7 +492,7 @@ def move_wheel_files(
|
||||||
dest = scheme[subdir]
|
dest = scheme[subdir]
|
||||||
clobber(source, dest, False, fixer=fixer, filter=filter)
|
clobber(source, dest, False, fixer=fixer, filter=filter)
|
||||||
|
|
||||||
maker = ScriptMaker(None, scheme['scripts'])
|
maker = PipScriptMaker(None, scheme['scripts'])
|
||||||
|
|
||||||
# Ensure old scripts are overwritten.
|
# Ensure old scripts are overwritten.
|
||||||
# See https://github.com/pypa/pip/issues/1800
|
# See https://github.com/pypa/pip/issues/1800
|
||||||
|
@ -491,36 +508,7 @@ def move_wheel_files(
|
||||||
# See https://bitbucket.org/pypa/distlib/issue/32/
|
# See https://bitbucket.org/pypa/distlib/issue/32/
|
||||||
maker.set_mode = True
|
maker.set_mode = True
|
||||||
|
|
||||||
# Simplify the script and fix the fact that the default script swallows
|
scripts_to_generate = []
|
||||||
# every single stack trace.
|
|
||||||
# See https://bitbucket.org/pypa/distlib/issue/34/
|
|
||||||
# See https://bitbucket.org/pypa/distlib/issue/33/
|
|
||||||
def _get_script_text(entry):
|
|
||||||
if entry.suffix is None:
|
|
||||||
raise InstallationError(
|
|
||||||
"Invalid script entry point: %s for req: %s - A callable "
|
|
||||||
"suffix is required. Cf https://packaging.python.org/en/"
|
|
||||||
"latest/distributing.html#console-scripts for more "
|
|
||||||
"information." % (entry, req)
|
|
||||||
)
|
|
||||||
return maker.script_template % {
|
|
||||||
"module": entry.prefix,
|
|
||||||
"import_name": entry.suffix.split(".")[0],
|
|
||||||
"func": entry.suffix,
|
|
||||||
}
|
|
||||||
# ignore type, because mypy disallows assigning to a method,
|
|
||||||
# see https://github.com/python/mypy/issues/2427
|
|
||||||
maker._get_script_text = _get_script_text # type: ignore
|
|
||||||
maker.script_template = r"""# -*- coding: utf-8 -*-
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from %(module)s import %(import_name)s
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
|
||||||
sys.exit(%(func)s())
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Special case pip and setuptools to generate versioned wrappers
|
# Special case pip and setuptools to generate versioned wrappers
|
||||||
#
|
#
|
||||||
|
@ -558,15 +546,16 @@ if __name__ == '__main__':
|
||||||
pip_script = console.pop('pip', None)
|
pip_script = console.pop('pip', None)
|
||||||
if pip_script:
|
if pip_script:
|
||||||
if "ENSUREPIP_OPTIONS" not in os.environ:
|
if "ENSUREPIP_OPTIONS" not in os.environ:
|
||||||
spec = 'pip = ' + pip_script
|
scripts_to_generate.append('pip = ' + pip_script)
|
||||||
generated.extend(maker.make(spec))
|
|
||||||
|
|
||||||
if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall":
|
if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall":
|
||||||
spec = 'pip%s = %s' % (sys.version_info[0], pip_script)
|
scripts_to_generate.append(
|
||||||
generated.extend(maker.make(spec))
|
'pip%s = %s' % (sys.version_info[0], pip_script)
|
||||||
|
)
|
||||||
|
|
||||||
spec = 'pip%s = %s' % (get_major_minor_version(), pip_script)
|
scripts_to_generate.append(
|
||||||
generated.extend(maker.make(spec))
|
'pip%s = %s' % (get_major_minor_version(), pip_script)
|
||||||
|
)
|
||||||
# Delete any other versioned pip entry points
|
# Delete any other versioned pip entry points
|
||||||
pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)]
|
pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)]
|
||||||
for k in pip_ep:
|
for k in pip_ep:
|
||||||
|
@ -574,13 +563,15 @@ if __name__ == '__main__':
|
||||||
easy_install_script = console.pop('easy_install', None)
|
easy_install_script = console.pop('easy_install', None)
|
||||||
if easy_install_script:
|
if easy_install_script:
|
||||||
if "ENSUREPIP_OPTIONS" not in os.environ:
|
if "ENSUREPIP_OPTIONS" not in os.environ:
|
||||||
spec = 'easy_install = ' + easy_install_script
|
scripts_to_generate.append(
|
||||||
generated.extend(maker.make(spec))
|
'easy_install = ' + easy_install_script
|
||||||
|
)
|
||||||
|
|
||||||
spec = 'easy_install-%s = %s' % (
|
scripts_to_generate.append(
|
||||||
get_major_minor_version(), easy_install_script,
|
'easy_install-%s = %s' % (
|
||||||
|
get_major_minor_version(), easy_install_script
|
||||||
|
)
|
||||||
)
|
)
|
||||||
generated.extend(maker.make(spec))
|
|
||||||
# Delete any other versioned easy_install entry points
|
# Delete any other versioned easy_install entry points
|
||||||
easy_install_ep = [
|
easy_install_ep = [
|
||||||
k for k in console if re.match(r'easy_install(-\d\.\d)?$', k)
|
k for k in console if re.match(r'easy_install(-\d\.\d)?$', k)
|
||||||
|
@ -589,24 +580,36 @@ if __name__ == '__main__':
|
||||||
del console[k]
|
del console[k]
|
||||||
|
|
||||||
# Generate the console and GUI entry points specified in the wheel
|
# Generate the console and GUI entry points specified in the wheel
|
||||||
if len(console) > 0:
|
scripts_to_generate.extend(
|
||||||
generated_console_scripts = maker.make_multiple(
|
'%s = %s' % kv for kv in console.items()
|
||||||
['%s = %s' % kv for kv in console.items()]
|
)
|
||||||
)
|
|
||||||
|
gui_scripts_to_generate = [
|
||||||
|
'%s = %s' % kv for kv in gui.items()
|
||||||
|
]
|
||||||
|
|
||||||
|
generated_console_scripts = [] # type: List[str]
|
||||||
|
|
||||||
|
try:
|
||||||
|
generated_console_scripts = maker.make_multiple(scripts_to_generate)
|
||||||
generated.extend(generated_console_scripts)
|
generated.extend(generated_console_scripts)
|
||||||
|
|
||||||
if warn_script_location:
|
|
||||||
msg = message_about_scripts_not_on_PATH(generated_console_scripts)
|
|
||||||
if msg is not None:
|
|
||||||
logger.warning(msg)
|
|
||||||
|
|
||||||
if len(gui) > 0:
|
|
||||||
generated.extend(
|
generated.extend(
|
||||||
maker.make_multiple(
|
maker.make_multiple(gui_scripts_to_generate, {'gui': True})
|
||||||
['%s = %s' % kv for kv in gui.items()],
|
|
||||||
{'gui': True}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
except MissingCallableSuffix as e:
|
||||||
|
entry = e.args[0]
|
||||||
|
raise InstallationError(
|
||||||
|
"Invalid script entry point: {} for req: {} - A callable "
|
||||||
|
"suffix is required. Cf https://packaging.python.org/en/"
|
||||||
|
"latest/distributing.html#console-scripts for more "
|
||||||
|
"information.".format(entry, req)
|
||||||
|
)
|
||||||
|
|
||||||
|
if warn_script_location:
|
||||||
|
msg = message_about_scripts_not_on_PATH(generated_console_scripts)
|
||||||
|
if msg is not None:
|
||||||
|
logger.warning(msg)
|
||||||
|
|
||||||
# Record pip as the installer
|
# Record pip as the installer
|
||||||
installer = os.path.join(info_dir[0], 'INSTALLER')
|
installer = os.path.join(info_dir[0], 'INSTALLER')
|
||||||
|
|
|
@ -14,6 +14,10 @@ from pip._internal.models.link import Link
|
||||||
from pip._internal.req.req_install import InstallRequirement
|
from pip._internal.req.req_install import InstallRequirement
|
||||||
from pip._internal.utils.compat import WINDOWS
|
from pip._internal.utils.compat import WINDOWS
|
||||||
from pip._internal.utils.misc import unpack_file
|
from pip._internal.utils.misc import unpack_file
|
||||||
|
from pip._internal.wheel import (
|
||||||
|
MissingCallableSuffix,
|
||||||
|
_raise_for_invalid_entrypoint,
|
||||||
|
)
|
||||||
from tests.lib import DATA_DIR, assert_paths_equal
|
from tests.lib import DATA_DIR, assert_paths_equal
|
||||||
|
|
||||||
|
|
||||||
|
@ -263,6 +267,19 @@ def test_get_entrypoints(tmpdir, console_scripts):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_raise_for_invalid_entrypoint_ok():
|
||||||
|
_raise_for_invalid_entrypoint("hello = hello:main")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("entrypoint", [
|
||||||
|
"hello = hello",
|
||||||
|
"hello = hello:",
|
||||||
|
])
|
||||||
|
def test_raise_for_invalid_entrypoint_fail(entrypoint):
|
||||||
|
with pytest.raises(MissingCallableSuffix):
|
||||||
|
_raise_for_invalid_entrypoint(entrypoint)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("outrows, expected", [
|
@pytest.mark.parametrize("outrows, expected", [
|
||||||
([
|
([
|
||||||
('', '', 'a'),
|
('', '', 'a'),
|
||||||
|
|
Loading…
Reference in New Issue