mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Refactor wheel.move_wheel_files to use updated distlib.
This commit is contained in:
parent
d764181da3
commit
b0a7d2b503
2 changed files with 73 additions and 56 deletions
|
@ -23,6 +23,7 @@ from email.parser import Parser
|
|||
|
||||
from pip._vendor import pkg_resources
|
||||
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.six import StringIO
|
||||
|
||||
|
@ -311,6 +312,22 @@ def get_csv_rows_for_installed(
|
|||
return installed_rows
|
||||
|
||||
|
||||
class MissingCallableSuffix(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _assert_valid_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):
|
||||
_assert_valid_entrypoint(specification)
|
||||
return super(PipScriptMaker, self).make(specification, options)
|
||||
|
||||
|
||||
def move_wheel_files(
|
||||
name, # type: str
|
||||
req, # type: Requirement
|
||||
|
@ -473,7 +490,7 @@ def move_wheel_files(
|
|||
dest = scheme[subdir]
|
||||
clobber(source, dest, False, fixer=fixer, filter=filter)
|
||||
|
||||
maker = ScriptMaker(None, scheme['scripts'])
|
||||
maker = PipScriptMaker(None, scheme['scripts'])
|
||||
|
||||
# Ensure old scripts are overwritten.
|
||||
# See https://github.com/pypa/pip/issues/1800
|
||||
|
@ -489,36 +506,7 @@ def move_wheel_files(
|
|||
# See https://bitbucket.org/pypa/distlib/issue/32/
|
||||
maker.set_mode = True
|
||||
|
||||
# Simplify the script and fix the fact that the default script swallows
|
||||
# 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())
|
||||
"""
|
||||
scripts_to_generate = []
|
||||
|
||||
# Special case pip and setuptools to generate versioned wrappers
|
||||
#
|
||||
|
@ -556,15 +544,16 @@ if __name__ == '__main__':
|
|||
pip_script = console.pop('pip', None)
|
||||
if pip_script:
|
||||
if "ENSUREPIP_OPTIONS" not in os.environ:
|
||||
spec = 'pip = ' + pip_script
|
||||
generated.extend(maker.make(spec))
|
||||
scripts_to_generate.append('pip = ' + pip_script)
|
||||
|
||||
if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall":
|
||||
spec = 'pip%s = %s' % (sys.version_info[0], pip_script)
|
||||
generated.extend(maker.make(spec))
|
||||
scripts_to_generate.append(
|
||||
'pip%s = %s' % (sys.version_info[0], pip_script)
|
||||
)
|
||||
|
||||
spec = 'pip%s = %s' % (get_major_minor_version(), pip_script)
|
||||
generated.extend(maker.make(spec))
|
||||
scripts_to_generate.append(
|
||||
'pip%s = %s' % (get_major_minor_version(), pip_script)
|
||||
)
|
||||
# Delete any other versioned pip entry points
|
||||
pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)]
|
||||
for k in pip_ep:
|
||||
|
@ -572,13 +561,15 @@ if __name__ == '__main__':
|
|||
easy_install_script = console.pop('easy_install', None)
|
||||
if easy_install_script:
|
||||
if "ENSUREPIP_OPTIONS" not in os.environ:
|
||||
spec = 'easy_install = ' + easy_install_script
|
||||
generated.extend(maker.make(spec))
|
||||
|
||||
spec = 'easy_install-%s = %s' % (
|
||||
get_major_minor_version(), easy_install_script,
|
||||
scripts_to_generate.append(
|
||||
'easy_install = ' + easy_install_script
|
||||
)
|
||||
|
||||
scripts_to_generate.append(
|
||||
'easy_install-%s = %s' % (
|
||||
get_major_minor_version(), easy_install_script
|
||||
)
|
||||
)
|
||||
generated.extend(maker.make(spec))
|
||||
# Delete any other versioned easy_install entry points
|
||||
easy_install_ep = [
|
||||
k for k in console if re.match(r'easy_install(-\d\.\d)?$', k)
|
||||
|
@ -587,25 +578,37 @@ if __name__ == '__main__':
|
|||
del console[k]
|
||||
|
||||
# Generate the console and GUI entry points specified in the wheel
|
||||
if len(console) > 0:
|
||||
generated_console_scripts = maker.make_multiple(
|
||||
['%s = %s' % kv for kv in console.items()]
|
||||
scripts_to_generate.extend(
|
||||
'%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(
|
||||
maker.make_multiple(gui_scripts_to_generate, {'gui': True})
|
||||
)
|
||||
except MissingCallableSuffix as e:
|
||||
entry = e.args[0]
|
||||
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)
|
||||
)
|
||||
|
||||
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(
|
||||
maker.make_multiple(
|
||||
['%s = %s' % kv for kv in gui.items()],
|
||||
{'gui': True}
|
||||
)
|
||||
)
|
||||
|
||||
# Record pip as the installer
|
||||
installer = os.path.join(info_dir[0], 'INSTALLER')
|
||||
temp_installer = os.path.join(info_dir[0], 'INSTALLER.pip')
|
||||
|
|
|
@ -15,6 +15,7 @@ from pip._internal.models.link import Link
|
|||
from pip._internal.req.req_install import InstallRequirement
|
||||
from pip._internal.utils.compat import WINDOWS
|
||||
from pip._internal.utils.misc import unpack_file
|
||||
from pip._internal.wheel import MissingCallableSuffix, _assert_valid_entrypoint
|
||||
from tests.lib import DATA_DIR, assert_paths_equal
|
||||
|
||||
|
||||
|
@ -265,6 +266,19 @@ def test_get_entrypoints(tmpdir, console_scripts):
|
|||
)
|
||||
|
||||
|
||||
def test_assert_valid_entrypoint_ok():
|
||||
_assert_valid_entrypoint("hello = hello:main")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entrypoint", [
|
||||
"hello = hello",
|
||||
"hello = hello:",
|
||||
])
|
||||
def test_assert_valid_entrypoint_fail(entrypoint):
|
||||
with pytest.raises(MissingCallableSuffix):
|
||||
_assert_valid_entrypoint(entrypoint)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("outrows, expected", [
|
||||
([
|
||||
('', '', 'a'),
|
||||
|
|
Loading…
Reference in a new issue