mirror of https://github.com/pypa/pip
Allow pip install with entrypoint names with colons (#3901)
This commit is contained in:
parent
0964c9797c
commit
d64b871d97
|
@ -1,5 +1,7 @@
|
|||
**9.1.0 (UNRELEASED)**
|
||||
|
||||
* Use pkg_resources to parse the entry points file to allow names with
|
||||
colons (:pull:`3901`)
|
||||
|
||||
**9.0.1 (2016-11-06)**
|
||||
|
||||
|
@ -111,7 +113,6 @@
|
|||
* Restore the ability to use inline comments in requirements files passed to
|
||||
``pip freeze`` (:issue:`3680`).
|
||||
|
||||
|
||||
**8.1.2 (2016-05-10)**
|
||||
|
||||
* Fix a regression on systems with uninitialized locale (:issue:`3575`).
|
||||
|
|
|
@ -20,7 +20,6 @@ from pip._vendor.packaging.markers import Marker
|
|||
from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
|
||||
from pip._vendor.packaging.utils import canonicalize_name
|
||||
from pip._vendor.packaging.version import Version, parse as parse_version
|
||||
from pip._vendor.six.moves import configparser
|
||||
|
||||
import pip.wheel
|
||||
|
||||
|
@ -35,7 +34,7 @@ from pip.locations import (
|
|||
from pip.utils import (
|
||||
display_path, rmtree, ask_path_exists, backup_dir, is_installable_dir,
|
||||
dist_in_usersite, dist_in_site_packages, egg_link_path,
|
||||
call_subprocess, read_text_file, FakeFile, _make_build_dir, ensure_dir,
|
||||
call_subprocess, read_text_file, _make_build_dir, ensure_dir,
|
||||
get_installed_version, normalize_path, dist_is_local,
|
||||
)
|
||||
|
||||
|
@ -724,36 +723,41 @@ class InstallRequirement(object):
|
|||
paths_to_remove.add(os.path.join(bin_dir, script) + '.bat')
|
||||
|
||||
# find console_scripts
|
||||
if dist.has_metadata('entry_points.txt'):
|
||||
if six.PY2:
|
||||
options = {}
|
||||
else:
|
||||
options = {"delimiters": ('=', )}
|
||||
config = configparser.SafeConfigParser(**options)
|
||||
config.readfp(
|
||||
FakeFile(dist.get_metadata_lines('entry_points.txt'))
|
||||
)
|
||||
if config.has_section('console_scripts'):
|
||||
for name, value in config.items('console_scripts'):
|
||||
if dist_in_usersite(dist):
|
||||
bin_dir = bin_user
|
||||
else:
|
||||
bin_dir = bin_py
|
||||
paths_to_remove.add(os.path.join(bin_dir, name))
|
||||
if WINDOWS:
|
||||
paths_to_remove.add(
|
||||
os.path.join(bin_dir, name) + '.exe'
|
||||
)
|
||||
paths_to_remove.add(
|
||||
os.path.join(bin_dir, name) + '.exe.manifest'
|
||||
)
|
||||
paths_to_remove.add(
|
||||
os.path.join(bin_dir, name) + '-script.py'
|
||||
)
|
||||
_scripts_to_remove = []
|
||||
console_scripts = dist.get_entry_map(group='console_scripts')
|
||||
for name in console_scripts.keys():
|
||||
_scripts_to_remove.extend(self._script_names(dist, name, False))
|
||||
# find gui_scripts
|
||||
gui_scripts = dist.get_entry_map(group='gui_scripts')
|
||||
for name in gui_scripts.keys():
|
||||
_scripts_to_remove.extend(self._script_names(dist, name, True))
|
||||
|
||||
for s in _scripts_to_remove:
|
||||
paths_to_remove.add(s)
|
||||
|
||||
paths_to_remove.remove(auto_confirm)
|
||||
self.uninstalled = paths_to_remove
|
||||
|
||||
def _script_names(self, dist, name, is_gui):
|
||||
'''Create the fully qualified name of the files created by
|
||||
{console,gui}_scripts for the given ``dist``. Returns the list of file
|
||||
names'''
|
||||
if dist_in_usersite(dist):
|
||||
bin_dir = bin_user
|
||||
else:
|
||||
bin_dir = bin_py
|
||||
exe_name = os.path.join(bin_dir, name)
|
||||
paths_to_remove = [exe_name, ]
|
||||
if WINDOWS:
|
||||
paths_to_remove.add(exe_name + '.exe')
|
||||
paths_to_remove.add(exe_name + '.exe.manifest')
|
||||
if is_gui:
|
||||
paths_to_remove.add(exe_name + '-script.pyw')
|
||||
else:
|
||||
paths_to_remove.add(exe_name + '-script.py')
|
||||
|
||||
return paths_to_remove
|
||||
|
||||
def rollback_uninstall(self):
|
||||
if self.uninstalled:
|
||||
self.uninstalled.rollback()
|
||||
|
|
22
pip/wheel.py
22
pip/wheel.py
|
@ -39,7 +39,6 @@ from pip.utils.setuptools_build import SETUPTOOLS_SHIM
|
|||
from pip._vendor.distlib.scripts import ScriptMaker
|
||||
from pip._vendor import pkg_resources
|
||||
from pip._vendor.packaging.utils import canonicalize_name
|
||||
from pip._vendor.six.moves import configparser
|
||||
|
||||
|
||||
wheel_ext = '.whl'
|
||||
|
@ -224,16 +223,19 @@ def get_entrypoints(filename):
|
|||
data.write("\n")
|
||||
data.seek(0)
|
||||
|
||||
cp = configparser.RawConfigParser()
|
||||
cp.optionxform = lambda option: option
|
||||
cp.readfp(data)
|
||||
# get the entry points and then the script names
|
||||
entry_points = pkg_resources.EntryPoint.parse_map(data)
|
||||
console = entry_points.get('console_scripts', {})
|
||||
gui = entry_points.get('gui_scripts', {})
|
||||
|
||||
console = {}
|
||||
gui = {}
|
||||
if cp.has_section('console_scripts'):
|
||||
console = dict(cp.items('console_scripts'))
|
||||
if cp.has_section('gui_scripts'):
|
||||
gui = dict(cp.items('gui_scripts'))
|
||||
def _split_ep(s):
|
||||
"""get the string representation of EntryPoint, remove space and split
|
||||
on '='"""
|
||||
return str(s).replace(" ", "").split("=")
|
||||
|
||||
# convert the EntryPoint objects into strings with module:function
|
||||
console = dict(_split_ep(v) for v in console.values())
|
||||
gui = dict(_split_ep(v) for v in gui.values())
|
||||
return console, gui
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import pretend
|
|||
from os.path import join, normpath
|
||||
from tempfile import mkdtemp
|
||||
from tests.lib import assert_all_changes, pyversion
|
||||
from tests.lib import create_test_package_with_setup
|
||||
from tests.lib.local_repos import local_repo, local_checkout
|
||||
|
||||
from pip.req import InstallRequirement
|
||||
|
@ -154,32 +155,54 @@ def test_uninstall_overlapping_package(script, data):
|
|||
assert_all_changes(result2, result3, [])
|
||||
|
||||
|
||||
def test_uninstall_entry_point(script):
|
||||
@pytest.mark.parametrize("console_scripts",
|
||||
["test_ = distutils_install",
|
||||
"test_:test_ = distutils_install"])
|
||||
def test_uninstall_entry_point(script, console_scripts):
|
||||
"""
|
||||
Test uninstall package with two or more entry points in the same section,
|
||||
whose name contain a colon.
|
||||
"""
|
||||
script.scratch_path.join("ep_install").mkdir()
|
||||
pkg_path = script.scratch_path / 'ep_install'
|
||||
pkg_path.join("setup.py").write(textwrap.dedent("""
|
||||
from setuptools import setup
|
||||
setup(
|
||||
name='ep-install',
|
||||
version='0.1',
|
||||
entry_points={"pip_test.ep":
|
||||
["ep:name1 = distutils_install",
|
||||
"ep:name2 = distutils_install"]
|
||||
}
|
||||
)
|
||||
"""))
|
||||
pkg_name = 'ep_install'
|
||||
pkg_path = create_test_package_with_setup(
|
||||
script,
|
||||
name=pkg_name,
|
||||
version='0.1',
|
||||
entry_points={"console_scripts": [console_scripts, ],
|
||||
"pip_test.ep":
|
||||
["ep:name1 = distutils_install",
|
||||
"ep:name2 = distutils_install"]
|
||||
}
|
||||
)
|
||||
script_name = script.bin_path.join(console_scripts.split('=')[0].strip())
|
||||
result = script.pip('install', pkg_path)
|
||||
assert script_name.exists
|
||||
result = script.pip('list', '--format=legacy')
|
||||
assert "ep-install (0.1)" in result.stdout
|
||||
script.pip('uninstall', 'ep_install', '-y')
|
||||
assert not script_name.exists
|
||||
result2 = script.pip('list', '--format=legacy')
|
||||
assert "ep-install (0.1)" not in result2.stdout
|
||||
|
||||
|
||||
def test_uninstall_gui_scripts(script):
|
||||
"""
|
||||
Make sure that uninstall removes gui scripts
|
||||
"""
|
||||
pkg_name = "gui_pkg"
|
||||
pkg_path = create_test_package_with_setup(
|
||||
script,
|
||||
name=pkg_name,
|
||||
version='0.1',
|
||||
entry_points={"gui_scripts": ["test_ = distutils_install", ], }
|
||||
)
|
||||
script_name = script.bin_path.join('test_')
|
||||
script.pip('install', pkg_path)
|
||||
assert script_name.exists
|
||||
script.pip('uninstall', pkg_name, '-y')
|
||||
assert not script_name.exists
|
||||
|
||||
|
||||
@pytest.mark.network
|
||||
def test_uninstall_console_scripts(script):
|
||||
"""
|
||||
|
|
|
@ -11,15 +11,21 @@ from pip.exceptions import InvalidWheelFilename, UnsupportedWheel
|
|||
from pip.utils import unpack_file
|
||||
|
||||
|
||||
def test_get_entrypoints(tmpdir):
|
||||
with open(str(tmpdir.join("entry_points.txt")), "w") as fp:
|
||||
@pytest.mark.parametrize("console_scripts",
|
||||
["pip = pip.main:pip", "pip:pip = pip.main:pip"])
|
||||
def test_get_entrypoints(tmpdir, console_scripts):
|
||||
entry_points = tmpdir.join("entry_points.txt")
|
||||
with open(str(entry_points), "w") as fp:
|
||||
fp.write("""
|
||||
[console_scripts]
|
||||
pip = pip.main:pip
|
||||
""")
|
||||
{0}
|
||||
[section]
|
||||
common:one = module:func
|
||||
common:two = module:other_func
|
||||
""".format(console_scripts))
|
||||
|
||||
assert wheel.get_entrypoints(str(tmpdir.join("entry_points.txt"))) == (
|
||||
{"pip": "pip.main:pip"},
|
||||
assert wheel.get_entrypoints(str(entry_points)) == (
|
||||
dict([console_scripts.split(' = ')]),
|
||||
{},
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue