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)**
|
**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)**
|
**9.0.1 (2016-11-06)**
|
||||||
|
|
||||||
|
@ -111,7 +113,6 @@
|
||||||
* Restore the ability to use inline comments in requirements files passed to
|
* Restore the ability to use inline comments in requirements files passed to
|
||||||
``pip freeze`` (:issue:`3680`).
|
``pip freeze`` (:issue:`3680`).
|
||||||
|
|
||||||
|
|
||||||
**8.1.2 (2016-05-10)**
|
**8.1.2 (2016-05-10)**
|
||||||
|
|
||||||
* Fix a regression on systems with uninitialized locale (:issue:`3575`).
|
* 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.requirements import InvalidRequirement, Requirement
|
||||||
from pip._vendor.packaging.utils import canonicalize_name
|
from pip._vendor.packaging.utils import canonicalize_name
|
||||||
from pip._vendor.packaging.version import Version, parse as parse_version
|
from pip._vendor.packaging.version import Version, parse as parse_version
|
||||||
from pip._vendor.six.moves import configparser
|
|
||||||
|
|
||||||
import pip.wheel
|
import pip.wheel
|
||||||
|
|
||||||
|
@ -35,7 +34,7 @@ from pip.locations import (
|
||||||
from pip.utils import (
|
from pip.utils import (
|
||||||
display_path, rmtree, ask_path_exists, backup_dir, is_installable_dir,
|
display_path, rmtree, ask_path_exists, backup_dir, is_installable_dir,
|
||||||
dist_in_usersite, dist_in_site_packages, egg_link_path,
|
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,
|
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')
|
paths_to_remove.add(os.path.join(bin_dir, script) + '.bat')
|
||||||
|
|
||||||
# find console_scripts
|
# find console_scripts
|
||||||
if dist.has_metadata('entry_points.txt'):
|
_scripts_to_remove = []
|
||||||
if six.PY2:
|
console_scripts = dist.get_entry_map(group='console_scripts')
|
||||||
options = {}
|
for name in console_scripts.keys():
|
||||||
else:
|
_scripts_to_remove.extend(self._script_names(dist, name, False))
|
||||||
options = {"delimiters": ('=', )}
|
# find gui_scripts
|
||||||
config = configparser.SafeConfigParser(**options)
|
gui_scripts = dist.get_entry_map(group='gui_scripts')
|
||||||
config.readfp(
|
for name in gui_scripts.keys():
|
||||||
FakeFile(dist.get_metadata_lines('entry_points.txt'))
|
_scripts_to_remove.extend(self._script_names(dist, name, True))
|
||||||
)
|
|
||||||
if config.has_section('console_scripts'):
|
for s in _scripts_to_remove:
|
||||||
for name, value in config.items('console_scripts'):
|
paths_to_remove.add(s)
|
||||||
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'
|
|
||||||
)
|
|
||||||
|
|
||||||
paths_to_remove.remove(auto_confirm)
|
paths_to_remove.remove(auto_confirm)
|
||||||
self.uninstalled = paths_to_remove
|
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):
|
def rollback_uninstall(self):
|
||||||
if self.uninstalled:
|
if self.uninstalled:
|
||||||
self.uninstalled.rollback()
|
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.distlib.scripts import ScriptMaker
|
||||||
from pip._vendor import pkg_resources
|
from pip._vendor import pkg_resources
|
||||||
from pip._vendor.packaging.utils import canonicalize_name
|
from pip._vendor.packaging.utils import canonicalize_name
|
||||||
from pip._vendor.six.moves import configparser
|
|
||||||
|
|
||||||
|
|
||||||
wheel_ext = '.whl'
|
wheel_ext = '.whl'
|
||||||
|
@ -224,16 +223,19 @@ def get_entrypoints(filename):
|
||||||
data.write("\n")
|
data.write("\n")
|
||||||
data.seek(0)
|
data.seek(0)
|
||||||
|
|
||||||
cp = configparser.RawConfigParser()
|
# get the entry points and then the script names
|
||||||
cp.optionxform = lambda option: option
|
entry_points = pkg_resources.EntryPoint.parse_map(data)
|
||||||
cp.readfp(data)
|
console = entry_points.get('console_scripts', {})
|
||||||
|
gui = entry_points.get('gui_scripts', {})
|
||||||
|
|
||||||
console = {}
|
def _split_ep(s):
|
||||||
gui = {}
|
"""get the string representation of EntryPoint, remove space and split
|
||||||
if cp.has_section('console_scripts'):
|
on '='"""
|
||||||
console = dict(cp.items('console_scripts'))
|
return str(s).replace(" ", "").split("=")
|
||||||
if cp.has_section('gui_scripts'):
|
|
||||||
gui = dict(cp.items('gui_scripts'))
|
# 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
|
return console, gui
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import pretend
|
||||||
from os.path import join, normpath
|
from os.path import join, normpath
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
from tests.lib import assert_all_changes, pyversion
|
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 tests.lib.local_repos import local_repo, local_checkout
|
||||||
|
|
||||||
from pip.req import InstallRequirement
|
from pip.req import InstallRequirement
|
||||||
|
@ -154,32 +155,54 @@ def test_uninstall_overlapping_package(script, data):
|
||||||
assert_all_changes(result2, result3, [])
|
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,
|
Test uninstall package with two or more entry points in the same section,
|
||||||
whose name contain a colon.
|
whose name contain a colon.
|
||||||
"""
|
"""
|
||||||
script.scratch_path.join("ep_install").mkdir()
|
pkg_name = 'ep_install'
|
||||||
pkg_path = script.scratch_path / 'ep_install'
|
pkg_path = create_test_package_with_setup(
|
||||||
pkg_path.join("setup.py").write(textwrap.dedent("""
|
script,
|
||||||
from setuptools import setup
|
name=pkg_name,
|
||||||
setup(
|
version='0.1',
|
||||||
name='ep-install',
|
entry_points={"console_scripts": [console_scripts, ],
|
||||||
version='0.1',
|
"pip_test.ep":
|
||||||
entry_points={"pip_test.ep":
|
["ep:name1 = distutils_install",
|
||||||
["ep:name1 = distutils_install",
|
"ep:name2 = distutils_install"]
|
||||||
"ep:name2 = distutils_install"]
|
}
|
||||||
}
|
)
|
||||||
)
|
script_name = script.bin_path.join(console_scripts.split('=')[0].strip())
|
||||||
"""))
|
|
||||||
result = script.pip('install', pkg_path)
|
result = script.pip('install', pkg_path)
|
||||||
|
assert script_name.exists
|
||||||
result = script.pip('list', '--format=legacy')
|
result = script.pip('list', '--format=legacy')
|
||||||
assert "ep-install (0.1)" in result.stdout
|
assert "ep-install (0.1)" in result.stdout
|
||||||
script.pip('uninstall', 'ep_install', '-y')
|
script.pip('uninstall', 'ep_install', '-y')
|
||||||
|
assert not script_name.exists
|
||||||
result2 = script.pip('list', '--format=legacy')
|
result2 = script.pip('list', '--format=legacy')
|
||||||
assert "ep-install (0.1)" not in result2.stdout
|
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
|
@pytest.mark.network
|
||||||
def test_uninstall_console_scripts(script):
|
def test_uninstall_console_scripts(script):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -11,15 +11,21 @@ from pip.exceptions import InvalidWheelFilename, UnsupportedWheel
|
||||||
from pip.utils import unpack_file
|
from pip.utils import unpack_file
|
||||||
|
|
||||||
|
|
||||||
def test_get_entrypoints(tmpdir):
|
@pytest.mark.parametrize("console_scripts",
|
||||||
with open(str(tmpdir.join("entry_points.txt")), "w") as fp:
|
["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("""
|
fp.write("""
|
||||||
[console_scripts]
|
[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"))) == (
|
assert wheel.get_entrypoints(str(entry_points)) == (
|
||||||
{"pip": "pip.main:pip"},
|
dict([console_scripts.split(' = ')]),
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue