Skip to content

Commit 3f4bb75

Browse files
authored
Merge pull request #7499 from chrahunt/feature/add-old-entrypoints
Add old pip entrypoints
2 parents c06874c + 38585ad commit 3f4bb75

File tree

11 files changed

+190
-72
lines changed

11 files changed

+190
-72
lines changed

news/7498.feature

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Define all old pip console script entrypoints to prevent import issues in
2+
stale wrapper scripts.

setup.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,11 @@ def get_version(rel_path):
7070
},
7171
entry_points={
7272
"console_scripts": [
73-
"pip=pip._internal.main:main",
74-
"pip{}=pip._internal.main:main".format(sys.version_info[0]),
75-
"pip{}.{}=pip._internal.main:main".format(*sys.version_info[:2]),
73+
"pip=pip._internal.cli.main:main",
74+
"pip{}=pip._internal.cli.main:main".format(sys.version_info[0]),
75+
"pip{}.{}=pip._internal.cli.main:main".format(
76+
*sys.version_info[:2]
77+
),
7678
],
7779
},
7880

src/pip/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,18 @@
1+
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
2+
3+
if MYPY_CHECK_RUNNING:
4+
from typing import List, Optional
5+
6+
17
__version__ = "20.0.dev0"
8+
9+
10+
def main(args=None):
11+
# type: (Optional[List[str]]) -> int
12+
"""This is an internal API only meant for use by pip's own console scripts.
13+
14+
For additional details, see https://github.com/pypa/pip/issues/7498.
15+
"""
16+
from pip._internal.utils.entrypoints import _wrapper
17+
18+
return _wrapper(args)

src/pip/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
path = os.path.dirname(os.path.dirname(__file__))
1414
sys.path.insert(0, path)
1515

16-
from pip._internal.main import main as _main # isort:skip # noqa
16+
from pip._internal.cli.main import main as _main # isort:skip # noqa
1717

1818
if __name__ == '__main__':
1919
sys.exit(_main())

src/pip/_internal/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,18 @@
11
#!/usr/bin/env python
22
import pip._internal.utils.inject_securetransport # noqa
3+
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
4+
5+
if MYPY_CHECK_RUNNING:
6+
from typing import Optional, List
7+
8+
9+
def main(args=None):
10+
# type: (Optional[List[str]]) -> int
11+
"""This is preserved for old console scripts that may still be referencing
12+
it.
13+
14+
For additional details, see https://github.com/pypa/pip/issues/7498.
15+
"""
16+
from pip._internal.utils.entrypoints import _wrapper
17+
18+
return _wrapper(args)

src/pip/_internal/cli/main.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""Primary application entrypoint.
2+
"""
3+
from __future__ import absolute_import
4+
5+
import locale
6+
import logging
7+
import os
8+
import sys
9+
10+
from pip._internal.cli.autocompletion import autocomplete
11+
from pip._internal.cli.main_parser import parse_command
12+
from pip._internal.commands import create_command
13+
from pip._internal.exceptions import PipError
14+
from pip._internal.utils import deprecation
15+
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
16+
17+
if MYPY_CHECK_RUNNING:
18+
from typing import List, Optional
19+
20+
logger = logging.getLogger(__name__)
21+
22+
23+
# Do not import and use main() directly! Using it directly is actively
24+
# discouraged by pip's maintainers. The name, location and behavior of
25+
# this function is subject to change, so calling it directly is not
26+
# portable across different pip versions.
27+
28+
# In addition, running pip in-process is unsupported and unsafe. This is
29+
# elaborated in detail at
30+
# https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program.
31+
# That document also provides suggestions that should work for nearly
32+
# all users that are considering importing and using main() directly.
33+
34+
# However, we know that certain users will still want to invoke pip
35+
# in-process. If you understand and accept the implications of using pip
36+
# in an unsupported manner, the best approach is to use runpy to avoid
37+
# depending on the exact location of this entry point.
38+
39+
# The following example shows how to use runpy to invoke pip in that
40+
# case:
41+
#
42+
# sys.argv = ["pip", your, args, here]
43+
# runpy.run_module("pip", run_name="__main__")
44+
#
45+
# Note that this will exit the process after running, unlike a direct
46+
# call to main. As it is not safe to do any processing after calling
47+
# main, this should not be an issue in practice.
48+
49+
def main(args=None):
50+
# type: (Optional[List[str]]) -> int
51+
if args is None:
52+
args = sys.argv[1:]
53+
54+
# Configure our deprecation warnings to be sent through loggers
55+
deprecation.install_warning_logger()
56+
57+
autocomplete()
58+
59+
try:
60+
cmd_name, cmd_args = parse_command(args)
61+
except PipError as exc:
62+
sys.stderr.write("ERROR: %s" % exc)
63+
sys.stderr.write(os.linesep)
64+
sys.exit(1)
65+
66+
# Needed for locale.getpreferredencoding(False) to work
67+
# in pip._internal.utils.encoding.auto_decode
68+
try:
69+
locale.setlocale(locale.LC_ALL, '')
70+
except locale.Error as e:
71+
# setlocale can apparently crash if locale are uninitialized
72+
logger.debug("Ignoring error %s when setting locale", e)
73+
command = create_command(cmd_name, isolated=("--isolated" in cmd_args))
74+
75+
return command.main(cmd_args)

src/pip/_internal/main.py

Lines changed: 7 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,16 @@
1-
"""Primary application entrypoint.
2-
"""
3-
from __future__ import absolute_import
4-
5-
import locale
6-
import logging
7-
import os
8-
import sys
9-
10-
from pip._internal.cli.autocompletion import autocomplete
11-
from pip._internal.cli.main_parser import parse_command
12-
from pip._internal.commands import create_command
13-
from pip._internal.exceptions import PipError
14-
from pip._internal.utils import deprecation
151
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
162

173
if MYPY_CHECK_RUNNING:
18-
from typing import List, Optional
19-
20-
logger = logging.getLogger(__name__)
21-
22-
23-
# Do not import and use main() directly! Using it directly is actively
24-
# discouraged by pip's maintainers. The name, location and behavior of
25-
# this function is subject to change, so calling it directly is not
26-
# portable across different pip versions.
4+
from typing import Optional, List
275

28-
# In addition, running pip in-process is unsupported and unsafe. This is
29-
# elaborated in detail at
30-
# https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program.
31-
# That document also provides suggestions that should work for nearly
32-
# all users that are considering importing and using main() directly.
33-
34-
# However, we know that certain users will still want to invoke pip
35-
# in-process. If you understand and accept the implications of using pip
36-
# in an unsupported manner, the best approach is to use runpy to avoid
37-
# depending on the exact location of this entry point.
38-
39-
# The following example shows how to use runpy to invoke pip in that
40-
# case:
41-
#
42-
# sys.argv = ["pip", your, args, here]
43-
# runpy.run_module("pip", run_name="__main__")
44-
#
45-
# Note that this will exit the process after running, unlike a direct
46-
# call to main. As it is not safe to do any processing after calling
47-
# main, this should not be an issue in practice.
486

497
def main(args=None):
508
# type: (Optional[List[str]]) -> int
51-
if args is None:
52-
args = sys.argv[1:]
53-
54-
# Configure our deprecation warnings to be sent through loggers
55-
deprecation.install_warning_logger()
56-
57-
autocomplete()
58-
59-
try:
60-
cmd_name, cmd_args = parse_command(args)
61-
except PipError as exc:
62-
sys.stderr.write("ERROR: %s" % exc)
63-
sys.stderr.write(os.linesep)
64-
sys.exit(1)
9+
"""This is preserved for old console scripts that may still be referencing
10+
it.
6511
66-
# Needed for locale.getpreferredencoding(False) to work
67-
# in pip._internal.utils.encoding.auto_decode
68-
try:
69-
locale.setlocale(locale.LC_ALL, '')
70-
except locale.Error as e:
71-
# setlocale can apparently crash if locale are uninitialized
72-
logger.debug("Ignoring error %s when setting locale", e)
73-
command = create_command(cmd_name, isolated=("--isolated" in cmd_args))
12+
For additional details, see https://github.com/pypa/pip/issues/7498.
13+
"""
14+
from pip._internal.utils.entrypoints import _wrapper
7415

75-
return command.main(cmd_args)
16+
return _wrapper(args)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import sys
2+
3+
from pip._internal.cli.main import main
4+
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
5+
6+
if MYPY_CHECK_RUNNING:
7+
from typing import Optional, List
8+
9+
10+
def _wrapper(args=None):
11+
# type: (Optional[List[str]]) -> int
12+
"""Central wrapper for all old entrypoints.
13+
14+
Historically pip has had several entrypoints defined. Because of issues
15+
arising from PATH, sys.path, multiple Pythons, their interactions, and most
16+
of them having a pip installed, users suffer every time an entrypoint gets
17+
moved.
18+
19+
To alleviate this pain, and provide a mechanism for warning users and
20+
directing them to an appropriate place for help, we now define all of
21+
our old entrypoints as wrappers for the current one.
22+
"""
23+
sys.stderr.write(
24+
"WARNING: pip is being invoked by an old script wrapper. This will "
25+
"fail in a future version of pip.\n"
26+
"Please see https://github.com/pypa/pip/issues/5599 for advice on "
27+
"fixing the underlying issue.\n"
28+
"To avoid this problem you can invoke Python with '-m pip' instead of "
29+
"running pip directly.\n"
30+
)
31+
return main(args)

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from pip._vendor.contextlib2 import ExitStack
1414
from setuptools.wheel import Wheel
1515

16-
from pip._internal.main import main as pip_entry_point
16+
from pip._internal.cli.main import main as pip_entry_point
1717
from pip._internal.utils.temp_dir import global_tempdir_manager
1818
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
1919
from tests.lib import DATA_DIR, SRC_DIR, TestData

tests/functional/test_cli.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Basic CLI functionality checks.
2+
"""
3+
from textwrap import dedent
4+
5+
import pytest
6+
7+
8+
@pytest.mark.parametrize("entrypoint", [
9+
("fake_pip = pip._internal.main:main",),
10+
("fake_pip = pip._internal:main",),
11+
("fake_pip = pip:main",),
12+
])
13+
def test_entrypoints_work(entrypoint, script):
14+
fake_pkg = script.temp_path / "fake_pkg"
15+
fake_pkg.mkdir()
16+
fake_pkg.joinpath("setup.py").write_text(dedent("""
17+
from setuptools import setup
18+
19+
setup(
20+
name="fake-pip",
21+
version="0.1.0",
22+
entry_points={{
23+
"console_scripts": [
24+
{!r}
25+
]
26+
}}
27+
)
28+
""".format(entrypoint)))
29+
30+
script.pip("install", "-vvv", str(fake_pkg))
31+
result = script.pip("-V")
32+
result2 = script.run("fake_pip", "-V", allow_stderr_warning=True)
33+
assert result.stdout == result2.stdout
34+
assert "old script wrapper" in result2.stderr

tests/unit/test_options.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import pytest
55

66
import pip._internal.configuration
7+
from pip._internal.cli.main import main
78
from pip._internal.commands import create_command
89
from pip._internal.exceptions import PipError
9-
from pip._internal.main import main
1010
from tests.lib.options_helpers import AddFakeCommandMixin
1111

1212

0 commit comments

Comments
 (0)