mirror of https://github.com/pypa/pip
Merge pull request #4545 from pradyunsg/mypy/infrastructure
This commit is contained in:
commit
28cca11e28
|
@ -5,6 +5,7 @@ pip.egg-info/
|
|||
MANIFEST
|
||||
.tox
|
||||
.cache
|
||||
.mypy_cache
|
||||
*.egg
|
||||
*.eggs
|
||||
*.py[cod]
|
||||
|
|
|
@ -7,6 +7,7 @@ matrix:
|
|||
- env: TOXENV=docs
|
||||
- env: TOXENV=lint-py2
|
||||
- env: TOXENV=lint-py3
|
||||
- env: TOXENV=mypy
|
||||
- env: TOXENV=packaging
|
||||
# PyPy jobs start first -- they are the slowest
|
||||
- env: TOXENV=pypy
|
||||
|
|
|
@ -18,6 +18,9 @@ exclude appveyor.yml
|
|||
recursive-include src/pip/_vendor *.pem
|
||||
recursive-include docs Makefile *.rst *.py *.bat
|
||||
|
||||
exclude src/pip/_vendor/six
|
||||
recursive-exclude src/pip/_vendor *.pyi
|
||||
|
||||
prune .github
|
||||
prune .travis
|
||||
prune docs/_build
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Integrate with mypy for utilizing static typing.
|
10
setup.cfg
10
setup.cfg
|
@ -11,9 +11,19 @@ known_first_party =
|
|||
default_section = THIRDPARTY
|
||||
|
||||
[flake8]
|
||||
# Ignoring unused imports since mypy would warn of that.
|
||||
ignore = F401
|
||||
exclude = .tox,.idea,*.egg,build,_vendor,data
|
||||
select = E,W,F
|
||||
|
||||
[mypy]
|
||||
follow_imports = silent
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-pip/_vendor/*]
|
||||
follow_imports = skip
|
||||
ignore_errors = True
|
||||
|
||||
[tool:pytest]
|
||||
addopts = --ignore src/pip/_vendor --ignore tests/tests_cache
|
||||
|
||||
|
|
|
@ -55,15 +55,6 @@ from pip._vendor.requests.packages.urllib3.exceptions import (
|
|||
InsecureRequestWarning,
|
||||
)
|
||||
|
||||
|
||||
# assignment for flake8 to be happy
|
||||
|
||||
# This fixes a peculiarity when importing via __import__ - as we are
|
||||
# initialising the pip module, "from pip import cmdoptions" is recursive
|
||||
# and appears not to work properly in that situation.
|
||||
# import pip._internal.cmdoptions
|
||||
# cmdoptions = pip._internal.cmdoptions
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Hide the InsecureRequestWarning from urllib3
|
||||
|
@ -241,7 +232,7 @@ def main(args=None):
|
|||
sys.exit(1)
|
||||
|
||||
# Needed for locale.getpreferredencoding(False) to work
|
||||
# in pip.utils.encoding.auto_decode
|
||||
# in pip._internal.utils.encoding.auto_decode
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
except locale.Error as e:
|
||||
|
|
|
@ -20,7 +20,8 @@ from pip._internal.exceptions import (
|
|||
)
|
||||
from pip._internal.index import PackageFinder
|
||||
from pip._internal.locations import running_under_virtualenv
|
||||
from pip._internal.req import InstallRequirement, parse_requirements
|
||||
from pip._internal.req.req_file import parse_requirements
|
||||
from pip._internal.req.req_install import InstallRequirement
|
||||
from pip._internal.status_codes import (
|
||||
ERROR, PREVIOUS_BUILD_DIR_ERROR, SUCCESS, UNKNOWN_ERROR,
|
||||
VIRTUALENV_NOT_FOUND
|
||||
|
@ -29,18 +30,21 @@ from pip._internal.utils import deprecation
|
|||
from pip._internal.utils.logging import IndentingFormatter
|
||||
from pip._internal.utils.misc import get_prog, normalize_path
|
||||
from pip._internal.utils.outdated import pip_version_check
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Optional
|
||||
|
||||
__all__ = ['Command']
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(object):
|
||||
name = None
|
||||
usage = None
|
||||
hidden = False
|
||||
ignore_require_venv = False
|
||||
name = None # type: Optional[str]
|
||||
usage = None # type: Optional[str]
|
||||
hidden = False # type: bool
|
||||
ignore_require_venv = False # type: bool
|
||||
log_streams = ("ext://sys.stdout", "ext://sys.stderr")
|
||||
|
||||
def __init__(self, isolated=False):
|
||||
|
|
|
@ -19,8 +19,12 @@ from pip._internal.index import (
|
|||
from pip._internal.locations import USER_CACHE_DIR, src_prefix
|
||||
from pip._internal.models import PyPI
|
||||
from pip._internal.utils.hashes import STRONG_HASHES
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
from pip._internal.utils.ui import BAR_TYPES
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Any
|
||||
|
||||
|
||||
def make_option_group(group, parser):
|
||||
"""
|
||||
|
@ -64,7 +68,8 @@ help_ = partial(
|
|||
'-h', '--help',
|
||||
dest='help',
|
||||
action='help',
|
||||
help='Show help.')
|
||||
help='Show help.',
|
||||
) # type: Any
|
||||
|
||||
isolated_mode = partial(
|
||||
Option,
|
||||
|
@ -85,7 +90,8 @@ require_virtualenv = partial(
|
|||
dest='require_venv',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=SUPPRESS_HELP)
|
||||
help=SUPPRESS_HELP
|
||||
) # type: Any
|
||||
|
||||
verbose = partial(
|
||||
Option,
|
||||
|
@ -101,7 +107,8 @@ version = partial(
|
|||
'-V', '--version',
|
||||
dest='version',
|
||||
action='store_true',
|
||||
help='Show version and exit.')
|
||||
help='Show version and exit.',
|
||||
) # type: Any
|
||||
|
||||
quiet = partial(
|
||||
Option,
|
||||
|
@ -109,10 +116,12 @@ quiet = partial(
|
|||
dest='quiet',
|
||||
action='count',
|
||||
default=0,
|
||||
help=('Give less output. Option is additive, and can be used up to 3'
|
||||
' times (corresponding to WARNING, ERROR, and CRITICAL logging'
|
||||
' levels).')
|
||||
)
|
||||
help=(
|
||||
'Give less output. Option is additive, and can be used up to 3'
|
||||
' times (corresponding to WARNING, ERROR, and CRITICAL logging'
|
||||
' levels).'
|
||||
),
|
||||
) # type: Any
|
||||
|
||||
progress_bar = partial(
|
||||
Option,
|
||||
|
@ -121,8 +130,11 @@ progress_bar = partial(
|
|||
type='choice',
|
||||
choices=list(BAR_TYPES.keys()),
|
||||
default='on',
|
||||
help='Specify type of progress to be displayed [' +
|
||||
'|'.join(BAR_TYPES.keys()) + '] (default: %default)')
|
||||
help=(
|
||||
'Specify type of progress to be displayed [' +
|
||||
'|'.join(BAR_TYPES.keys()) + '] (default: %default)'
|
||||
),
|
||||
) # type: Any
|
||||
|
||||
log = partial(
|
||||
Option,
|
||||
|
@ -130,7 +142,7 @@ log = partial(
|
|||
dest="log",
|
||||
metavar="path",
|
||||
help="Path to a verbose appending log."
|
||||
)
|
||||
) # type: Any
|
||||
|
||||
no_input = partial(
|
||||
Option,
|
||||
|
@ -139,7 +151,8 @@ no_input = partial(
|
|||
dest='no_input',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=SUPPRESS_HELP)
|
||||
help=SUPPRESS_HELP
|
||||
) # type: Any
|
||||
|
||||
proxy = partial(
|
||||
Option,
|
||||
|
@ -147,7 +160,8 @@ proxy = partial(
|
|||
dest='proxy',
|
||||
type='str',
|
||||
default='',
|
||||
help="Specify a proxy in the form [user:passwd@]proxy.server:port.")
|
||||
help="Specify a proxy in the form [user:passwd@]proxy.server:port."
|
||||
) # type: Any
|
||||
|
||||
retries = partial(
|
||||
Option,
|
||||
|
@ -156,7 +170,8 @@ retries = partial(
|
|||
type='int',
|
||||
default=5,
|
||||
help="Maximum number of retries each connection should attempt "
|
||||
"(default %default times).")
|
||||
"(default %default times).",
|
||||
) # type: Any
|
||||
|
||||
timeout = partial(
|
||||
Option,
|
||||
|
@ -165,7 +180,8 @@ timeout = partial(
|
|||
dest='timeout',
|
||||
type='float',
|
||||
default=15,
|
||||
help='Set the socket timeout (default %default seconds).')
|
||||
help='Set the socket timeout (default %default seconds).',
|
||||
) # type: Any
|
||||
|
||||
skip_requirements_regex = partial(
|
||||
Option,
|
||||
|
@ -174,7 +190,8 @@ skip_requirements_regex = partial(
|
|||
dest='skip_requirements_regex',
|
||||
type='str',
|
||||
default='',
|
||||
help=SUPPRESS_HELP)
|
||||
help=SUPPRESS_HELP,
|
||||
) # type: Any
|
||||
|
||||
|
||||
def exists_action():
|
||||
|
@ -188,7 +205,8 @@ def exists_action():
|
|||
action='append',
|
||||
metavar='action',
|
||||
help="Default action when a path already exists: "
|
||||
"(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.")
|
||||
"(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort).",
|
||||
)
|
||||
|
||||
|
||||
cert = partial(
|
||||
|
@ -197,7 +215,8 @@ cert = partial(
|
|||
dest='cert',
|
||||
type='str',
|
||||
metavar='path',
|
||||
help="Path to alternate CA bundle.")
|
||||
help="Path to alternate CA bundle.",
|
||||
) # type: Any
|
||||
|
||||
client_cert = partial(
|
||||
Option,
|
||||
|
@ -207,7 +226,8 @@ client_cert = partial(
|
|||
default=None,
|
||||
metavar='path',
|
||||
help="Path to SSL client certificate, a single file containing the "
|
||||
"private key and the certificate in PEM format.")
|
||||
"private key and the certificate in PEM format.",
|
||||
) # type: Any
|
||||
|
||||
index_url = partial(
|
||||
Option,
|
||||
|
@ -218,7 +238,8 @@ index_url = partial(
|
|||
help="Base URL of Python Package Index (default %default). "
|
||||
"This should point to a repository compliant with PEP 503 "
|
||||
"(the simple repository API) or a local directory laid out "
|
||||
"in the same format.")
|
||||
"in the same format.",
|
||||
) # type: Any
|
||||
|
||||
|
||||
def extra_index_url():
|
||||
|
@ -230,7 +251,7 @@ def extra_index_url():
|
|||
default=[],
|
||||
help="Extra URLs of package indexes to use in addition to "
|
||||
"--index-url. Should follow the same rules as "
|
||||
"--index-url."
|
||||
"--index-url.",
|
||||
)
|
||||
|
||||
|
||||
|
@ -240,7 +261,8 @@ no_index = partial(
|
|||
dest='no_index',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Ignore package index (only looking at --find-links URLs instead).')
|
||||
help='Ignore package index (only looking at --find-links URLs instead).',
|
||||
) # type: Any
|
||||
|
||||
|
||||
def find_links():
|
||||
|
@ -252,7 +274,8 @@ def find_links():
|
|||
metavar='url',
|
||||
help="If a url or path to an html file, then parse for links to "
|
||||
"archives. If a local path or file:// url that's a directory, "
|
||||
"then look for archives in the directory listing.")
|
||||
"then look for archives in the directory listing.",
|
||||
)
|
||||
|
||||
|
||||
def trusted_host():
|
||||
|
@ -275,7 +298,7 @@ process_dependency_links = partial(
|
|||
action="store_true",
|
||||
default=False,
|
||||
help="Enable the processing of dependency links.",
|
||||
)
|
||||
) # type: Any
|
||||
|
||||
|
||||
def constraints():
|
||||
|
@ -286,7 +309,8 @@ def constraints():
|
|||
default=[],
|
||||
metavar='file',
|
||||
help='Constrain versions using the given constraints file. '
|
||||
'This option can be used multiple times.')
|
||||
'This option can be used multiple times.'
|
||||
)
|
||||
|
||||
|
||||
def requirements():
|
||||
|
@ -297,7 +321,8 @@ def requirements():
|
|||
default=[],
|
||||
metavar='file',
|
||||
help='Install from the given requirements file. '
|
||||
'This option can be used multiple times.')
|
||||
'This option can be used multiple times.'
|
||||
)
|
||||
|
||||
|
||||
def editable():
|
||||
|
@ -321,7 +346,7 @@ src = partial(
|
|||
help='Directory to check out editable projects into. '
|
||||
'The default in a virtualenv is "<venv path>/src". '
|
||||
'The default for global installs is "<current dir>/src".'
|
||||
)
|
||||
) # type: Any
|
||||
|
||||
|
||||
def _get_format_control(values, option):
|
||||
|
@ -351,7 +376,8 @@ def no_binary():
|
|||
"disable all binary packages, :none: to empty the set, or one or "
|
||||
"more package names with commas between them. Note that some "
|
||||
"packages are tricky to compile and may fail to install when "
|
||||
"this option is used on them.")
|
||||
"this option is used on them.",
|
||||
)
|
||||
|
||||
|
||||
def only_binary():
|
||||
|
@ -364,7 +390,8 @@ def only_binary():
|
|||
"disable all source packages, :none: to empty the set, or one or "
|
||||
"more package names with commas between them. Packages without "
|
||||
"binary distributions will fail to install when this option is "
|
||||
"used on them.")
|
||||
"used on them.",
|
||||
)
|
||||
|
||||
|
||||
cache_dir = partial(
|
||||
|
@ -390,7 +417,8 @@ no_deps = partial(
|
|||
dest='ignore_dependencies',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help="Don't install package dependencies.")
|
||||
help="Don't install package dependencies)."
|
||||
) # type: Any
|
||||
|
||||
build_dir = partial(
|
||||
Option,
|
||||
|
@ -398,14 +426,15 @@ build_dir = partial(
|
|||
dest='build_dir',
|
||||
metavar='dir',
|
||||
help='Directory to unpack packages into and build in.'
|
||||
)
|
||||
) # type: Any
|
||||
|
||||
ignore_requires_python = partial(
|
||||
Option,
|
||||
'--ignore-requires-python',
|
||||
dest='ignore_requires_python',
|
||||
action='store_true',
|
||||
help='Ignore the Requires-Python information.')
|
||||
help='Ignore the Requires-Python information.'
|
||||
) # type: Any
|
||||
|
||||
install_options = partial(
|
||||
Option,
|
||||
|
@ -417,7 +446,8 @@ install_options = partial(
|
|||
"command (use like --install-option=\"--install-scripts=/usr/local/"
|
||||
"bin\"). Use multiple --install-option options to pass multiple "
|
||||
"options to setup.py install. If you are using an option with a "
|
||||
"directory path, be sure to use absolute path.")
|
||||
"directory path, be sure to use absolute path.",
|
||||
) # type: Any
|
||||
|
||||
global_options = partial(
|
||||
Option,
|
||||
|
@ -426,14 +456,16 @@ global_options = partial(
|
|||
action='append',
|
||||
metavar='options',
|
||||
help="Extra global options to be supplied to the setup.py "
|
||||
"call before the install command.")
|
||||
"call before the install command.",
|
||||
) # type: Any
|
||||
|
||||
no_clean = partial(
|
||||
Option,
|
||||
'--no-clean',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help="Don't clean up build directories.")
|
||||
help="Don't clean up build directories)."
|
||||
) # type: Any
|
||||
|
||||
pre = partial(
|
||||
Option,
|
||||
|
@ -441,7 +473,8 @@ pre = partial(
|
|||
action='store_true',
|
||||
default=False,
|
||||
help="Include pre-release and development versions. By default, "
|
||||
"pip only finds stable versions.")
|
||||
"pip only finds stable versions.",
|
||||
) # type: Any
|
||||
|
||||
disable_pip_version_check = partial(
|
||||
Option,
|
||||
|
@ -450,7 +483,8 @@ disable_pip_version_check = partial(
|
|||
action="store_true",
|
||||
default=False,
|
||||
help="Don't periodically check PyPI to determine whether a new version "
|
||||
"of pip is available for download. Implied with --no-index.")
|
||||
"of pip is available for download. Implied with --no-index.",
|
||||
) # type: Any
|
||||
|
||||
|
||||
# Deprecated, Remove later
|
||||
|
@ -460,7 +494,7 @@ always_unzip = partial(
|
|||
dest='always_unzip',
|
||||
action='store_true',
|
||||
help=SUPPRESS_HELP,
|
||||
)
|
||||
) # type: Any
|
||||
|
||||
|
||||
def _merge_hash(option, opt_str, value, parser):
|
||||
|
@ -490,7 +524,8 @@ hash = partial(
|
|||
callback=_merge_hash,
|
||||
type='string',
|
||||
help="Verify that the package's archive matches this "
|
||||
'hash before installing. Example: --hash=sha256:abcdef...')
|
||||
'hash before installing. Example: --hash=sha256:abcdef...',
|
||||
) # type: Any
|
||||
|
||||
|
||||
require_hashes = partial(
|
||||
|
@ -501,7 +536,8 @@ require_hashes = partial(
|
|||
default=False,
|
||||
help='Require a hash to check each requirement against, for '
|
||||
'repeatable installs. This option is implied when any package in a '
|
||||
'requirements file has a --hash option.')
|
||||
'requirements file has a --hash option.',
|
||||
) # type: Any
|
||||
|
||||
|
||||
##########
|
||||
|
|
|
@ -17,6 +17,12 @@ from pip._internal.commands.install import InstallCommand
|
|||
from pip._internal.commands.uninstall import UninstallCommand
|
||||
from pip._internal.commands.wheel import WheelCommand
|
||||
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import List, Type
|
||||
from pip._internal.basecommand import Command
|
||||
|
||||
commands_order = [
|
||||
InstallCommand,
|
||||
DownloadCommand,
|
||||
|
@ -31,7 +37,7 @@ commands_order = [
|
|||
HashCommand,
|
||||
CompletionCommand,
|
||||
HelpCommand,
|
||||
]
|
||||
] # type: List[Type[Command]]
|
||||
|
||||
commands_dict = {c.name: c for c in commands_order}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import logging
|
|||
import warnings
|
||||
|
||||
from pip._vendor import six
|
||||
from pip._vendor.six.moves import zip_longest
|
||||
|
||||
from pip._internal.basecommand import Command
|
||||
from pip._internal.cmdoptions import index_group, make_option_group
|
||||
|
@ -16,12 +17,6 @@ from pip._internal.utils.misc import (
|
|||
)
|
||||
from pip._internal.utils.packaging import get_installer
|
||||
|
||||
try:
|
||||
from itertools import zip_longest
|
||||
except ImportError:
|
||||
from itertools import izip_longest as zip_longest
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@ from collections import OrderedDict
|
|||
|
||||
from pip._vendor import pkg_resources
|
||||
from pip._vendor.packaging.version import parse as parse_version
|
||||
from pip._vendor.six.moves import xmlrpc_client
|
||||
# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is
|
||||
# why we ignore the type on this import
|
||||
from pip._vendor.six.moves import xmlrpc_client # type: ignore
|
||||
|
||||
from pip._internal.basecommand import SUCCESS, Command
|
||||
from pip._internal.download import PipXmlrpcTransport
|
||||
|
|
|
@ -14,9 +14,9 @@ try:
|
|||
import ipaddress
|
||||
except ImportError:
|
||||
try:
|
||||
from pip._vendor import ipaddress
|
||||
from pip._vendor import ipaddress # type: ignore
|
||||
except ImportError:
|
||||
import ipaddr as ipaddress
|
||||
import ipaddr as ipaddress # type: ignore
|
||||
ipaddress.ip_address = ipaddress.IPAddress
|
||||
ipaddress.ip_network = ipaddress.IPNetwork
|
||||
|
||||
|
@ -34,12 +34,15 @@ if sys.version_info >= (3, 4):
|
|||
from importlib.util import cache_from_source
|
||||
else:
|
||||
import imp
|
||||
uses_pycache = hasattr(imp, 'cache_from_source')
|
||||
if uses_pycache:
|
||||
cache_from_source = imp.cache_from_source
|
||||
else:
|
||||
|
||||
try:
|
||||
cache_from_source = imp.cache_from_source # type: ignore
|
||||
except AttributeError:
|
||||
# does not use __pycache__
|
||||
cache_from_source = None
|
||||
|
||||
uses_pycache = cache_from_source is not None
|
||||
|
||||
|
||||
if sys.version_info >= (3, 5):
|
||||
backslashreplace_decode = "backslashreplace"
|
||||
|
|
|
@ -14,7 +14,7 @@ Some terminology:
|
|||
import logging
|
||||
import os
|
||||
|
||||
from pip._vendor.six import next
|
||||
from pip._vendor import six
|
||||
from pip._vendor.six.moves import configparser
|
||||
|
||||
from pip._internal.exceptions import ConfigurationError
|
||||
|
@ -23,12 +23,20 @@ from pip._internal.locations import (
|
|||
site_config_files, venv_config_file
|
||||
)
|
||||
from pip._internal.utils.misc import ensure_dir, enum
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Any, Dict, Iterable, List, NewType, Optional, Tuple
|
||||
|
||||
RawConfigParser = configparser.RawConfigParser # Shorthand
|
||||
Kind = NewType("Kind", str)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# NOTE: Maybe use the optionx attribute to normalize keynames.
|
||||
def _normalize_name(name):
|
||||
# type: (str) -> str
|
||||
"""Make a name consistent regardless of source (environment or file)
|
||||
"""
|
||||
name = name.lower().replace('_', '-')
|
||||
|
@ -38,6 +46,7 @@ def _normalize_name(name):
|
|||
|
||||
|
||||
def _disassemble_key(name):
|
||||
# type: (str) -> List[str]
|
||||
return name.split(".", 1)
|
||||
|
||||
|
||||
|
@ -66,6 +75,7 @@ class Configuration(object):
|
|||
"""
|
||||
|
||||
def __init__(self, isolated, load_only=None):
|
||||
# type: (bool, Kind) -> None
|
||||
super(Configuration, self).__init__()
|
||||
|
||||
_valid_load_only = [kinds.USER, kinds.GLOBAL, kinds.VENV, None]
|
||||
|
@ -75,8 +85,8 @@ class Configuration(object):
|
|||
", ".join(map(repr, _valid_load_only[:-1]))
|
||||
)
|
||||
)
|
||||
self.isolated = isolated
|
||||
self.load_only = load_only
|
||||
self.isolated = isolated # type: bool
|
||||
self.load_only = load_only # type: Optional[Kind]
|
||||
|
||||
# The order here determines the override order.
|
||||
self._override_order = [
|
||||
|
@ -86,11 +96,16 @@ class Configuration(object):
|
|||
self._ignore_env_names = ["version", "help"]
|
||||
|
||||
# Because we keep track of where we got the data from
|
||||
self._parsers = {variant: [] for variant in self._override_order}
|
||||
self._config = {variant: {} for variant in self._override_order}
|
||||
self._modified_parsers = []
|
||||
self._parsers = {
|
||||
variant: [] for variant in self._override_order
|
||||
} # type: Dict[Kind, List[Tuple[str, RawConfigParser]]]
|
||||
self._config = {
|
||||
variant: {} for variant in self._override_order
|
||||
} # type: Dict[Kind, Dict[str, Any]]
|
||||
self._modified_parsers = [] # type: List[Tuple[str, RawConfigParser]]
|
||||
|
||||
def load(self):
|
||||
# type: () -> None
|
||||
"""Loads configuration from configuration files and environment
|
||||
"""
|
||||
self._load_config_files()
|
||||
|
@ -98,6 +113,7 @@ class Configuration(object):
|
|||
self._load_environment_vars()
|
||||
|
||||
def get_file_to_edit(self):
|
||||
# type: () -> Optional[str]
|
||||
"""Returns the file with highest priority in configuration
|
||||
"""
|
||||
assert self.load_only is not None, \
|
||||
|
@ -109,12 +125,14 @@ class Configuration(object):
|
|||
return None
|
||||
|
||||
def items(self):
|
||||
# type: () -> Iterable[Tuple[str, Any]]
|
||||
"""Returns key-value pairs like dict.items() representing the loaded
|
||||
configuration
|
||||
"""
|
||||
return self._dictionary.items()
|
||||
|
||||
def get_value(self, key):
|
||||
# type: (str) -> Any
|
||||
"""Get a value from the configuration.
|
||||
"""
|
||||
try:
|
||||
|
@ -123,6 +141,7 @@ class Configuration(object):
|
|||
raise ConfigurationError("No such key - {}".format(key))
|
||||
|
||||
def set_value(self, key, value):
|
||||
# type: (str, Any) -> None
|
||||
"""Modify a value in the configuration.
|
||||
"""
|
||||
self._ensure_have_load_only()
|
||||
|
@ -141,6 +160,7 @@ class Configuration(object):
|
|||
self._mark_as_modified(fname, parser)
|
||||
|
||||
def unset_value(self, key):
|
||||
# type: (str) -> None
|
||||
"""Unset a value in the configuration.
|
||||
"""
|
||||
self._ensure_have_load_only()
|
||||
|
@ -161,7 +181,13 @@ class Configuration(object):
|
|||
|
||||
if modified_something:
|
||||
# name removed from parser, section may now be empty
|
||||
if next(iter(parser.items(section)), None) is None:
|
||||
section_iter = iter(parser.items(section))
|
||||
try:
|
||||
val = six.next(section_iter)
|
||||
except StopIteration:
|
||||
val = None
|
||||
|
||||
if val is None:
|
||||
parser.remove_section(section)
|
||||
|
||||
self._mark_as_modified(fname, parser)
|
||||
|
@ -173,6 +199,7 @@ class Configuration(object):
|
|||
del self._config[self.load_only][key]
|
||||
|
||||
def save(self):
|
||||
# type: () -> None
|
||||
"""Save the currentin-memory state.
|
||||
"""
|
||||
self._ensure_have_load_only()
|
||||
|
@ -184,19 +211,21 @@ class Configuration(object):
|
|||
ensure_dir(os.path.dirname(fname))
|
||||
|
||||
with open(fname, "w") as f:
|
||||
parser.write(f)
|
||||
parser.write(f) # type: ignore
|
||||
|
||||
#
|
||||
# Private routines
|
||||
#
|
||||
|
||||
def _ensure_have_load_only(self):
|
||||
# type: () -> None
|
||||
if self.load_only is None:
|
||||
raise ConfigurationError("Needed a specific file to be modifying.")
|
||||
logger.debug("Will be working with %s variant only", self.load_only)
|
||||
|
||||
@property
|
||||
def _dictionary(self):
|
||||
# type: () -> Dict[str, Any]
|
||||
"""A dictionary representing the loaded configuration.
|
||||
"""
|
||||
# NOTE: Dictionaries are not populated if not loaded. So, conditionals
|
||||
|
@ -209,6 +238,7 @@ class Configuration(object):
|
|||
return retval
|
||||
|
||||
def _load_config_files(self):
|
||||
# type: () -> None
|
||||
"""Loads configuration from configuration files
|
||||
"""
|
||||
config_files = dict(self._iter_config_files())
|
||||
|
@ -235,6 +265,7 @@ class Configuration(object):
|
|||
self._parsers[variant].append((fname, parser))
|
||||
|
||||
def _load_file(self, variant, fname):
|
||||
# type: (Kind, str) -> RawConfigParser
|
||||
logger.debug("For variant '%s', will try loading '%s'", variant, fname)
|
||||
parser = self._construct_parser(fname)
|
||||
|
||||
|
@ -245,6 +276,7 @@ class Configuration(object):
|
|||
return parser
|
||||
|
||||
def _construct_parser(self, fname):
|
||||
# type: (str) -> RawConfigParser
|
||||
parser = configparser.RawConfigParser()
|
||||
# If there is no such file, don't bother reading it but create the
|
||||
# parser anyway, to hold the data.
|
||||
|
@ -256,6 +288,7 @@ class Configuration(object):
|
|||
return parser
|
||||
|
||||
def _load_environment_vars(self):
|
||||
# type: () -> None
|
||||
"""Loads configuration from environment variables
|
||||
"""
|
||||
self._config[kinds.ENV_VAR].update(
|
||||
|
@ -263,6 +296,7 @@ class Configuration(object):
|
|||
)
|
||||
|
||||
def _normalized_keys(self, section, items):
|
||||
# type: (str, Iterable[Tuple[str, Any]]) -> Dict[str, Any]
|
||||
"""Normalizes items to construct a dictionary with normalized keys.
|
||||
|
||||
This routine is where the names become keys and are made the same
|
||||
|
@ -275,6 +309,7 @@ class Configuration(object):
|
|||
return normalized
|
||||
|
||||
def _get_environ_vars(self):
|
||||
# type: () -> Iterable[Tuple[str, str]]
|
||||
"""Returns a generator with all environmental vars with prefix PIP_"""
|
||||
for key, val in os.environ.items():
|
||||
should_be_yielded = (
|
||||
|
@ -286,6 +321,7 @@ class Configuration(object):
|
|||
|
||||
# XXX: This is patched in the tests.
|
||||
def _iter_config_files(self):
|
||||
# type: () -> Iterable[Tuple[Kind, List[str]]]
|
||||
"""Yields variant and configuration files associated with it.
|
||||
|
||||
This should be treated like items of a dictionary.
|
||||
|
@ -315,6 +351,7 @@ class Configuration(object):
|
|||
yield kinds.VENV, [venv_config_file]
|
||||
|
||||
def _get_parser_to_modify(self):
|
||||
# type: () -> Tuple[str, RawConfigParser]
|
||||
# Determine which parser to modify
|
||||
parsers = self._parsers[self.load_only]
|
||||
if not parsers:
|
||||
|
@ -328,6 +365,7 @@ class Configuration(object):
|
|||
|
||||
# XXX: This is patched in the tests.
|
||||
def _mark_as_modified(self, fname, parser):
|
||||
# type: (str, RawConfigParser) -> None
|
||||
file_parser_tuple = (fname, parser)
|
||||
if file_parser_tuple not in self._modified_parsers:
|
||||
self._modified_parsers.append(file_parser_tuple)
|
||||
|
|
|
@ -22,7 +22,9 @@ from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response
|
|||
from pip._vendor.requests.packages import urllib3
|
||||
from pip._vendor.requests.structures import CaseInsensitiveDict
|
||||
from pip._vendor.requests.utils import get_netrc_auth
|
||||
from pip._vendor.six.moves import xmlrpc_client
|
||||
# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is
|
||||
# why we ignore the type on this import
|
||||
from pip._vendor.six.moves import xmlrpc_client # type: ignore
|
||||
from pip._vendor.six.moves.urllib import parse as urllib_parse
|
||||
from pip._vendor.six.moves.urllib import request as urllib_request
|
||||
from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote
|
||||
|
|
|
@ -8,7 +8,7 @@ import site
|
|||
import sys
|
||||
import sysconfig
|
||||
from distutils import sysconfig as distutils_sysconfig
|
||||
from distutils.command.install import SCHEME_KEYS, install # noqa
|
||||
from distutils.command.install import SCHEME_KEYS, install # type: ignore
|
||||
|
||||
from pip._internal.compat import WINDOWS, expanduser
|
||||
from pip._internal.utils import appdirs
|
||||
|
|
|
@ -6,6 +6,11 @@ from __future__ import absolute_import
|
|||
import logging
|
||||
import warnings
|
||||
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Any
|
||||
|
||||
|
||||
class PipDeprecationWarning(Warning):
|
||||
pass
|
||||
|
@ -26,7 +31,7 @@ class RemovedInPip12Warning(PipDeprecationWarning, Pending):
|
|||
# Warnings <-> Logging Integration
|
||||
|
||||
|
||||
_warnings_showwarning = None
|
||||
_warnings_showwarning = None # type: Any
|
||||
|
||||
|
||||
def _showwarning(message, category, filename, lineno, file=None, line=None):
|
||||
|
|
|
@ -11,7 +11,7 @@ from pip._internal.utils.misc import ensure_dir
|
|||
try:
|
||||
import threading
|
||||
except ImportError:
|
||||
import dummy_threading as threading
|
||||
import dummy_threading as threading # type: ignore
|
||||
|
||||
|
||||
try:
|
||||
|
|
|
@ -19,7 +19,9 @@ import zipfile
|
|||
from collections import deque
|
||||
|
||||
from pip._vendor import pkg_resources
|
||||
from pip._vendor.retrying import retry
|
||||
# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is
|
||||
# why we ignore the type on this import.
|
||||
from pip._vendor.retrying import retry # type: ignore
|
||||
from pip._vendor.six import PY2
|
||||
from pip._vendor.six.moves import input
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
"""For neatly implementing static typing in pip.
|
||||
|
||||
`mypy` - the static type analysis tool we use - uses the `typing` module, which
|
||||
provides core functionality fundamental to mypy's functioning.
|
||||
|
||||
Generally, `typing` would be imported at runtime and used in that fashion -
|
||||
it acts as a no-op at runtime and does not have any run-time overhead by
|
||||
design.
|
||||
|
||||
As it turns out, `typing` is not vendorable - it uses separate sources for
|
||||
Python 2/Python 3. Thus, this codebase can not expect it to be present.
|
||||
To work around this, mypy allows the typing import to be behind a False-y
|
||||
optional to prevent it from running at runtime and type-comments can be used
|
||||
to remove the need for the types to be accessible directly during runtime.
|
||||
|
||||
This module provides the False-y guard in a nicely named fashion so that a
|
||||
curious maintainer can reach here to read this.
|
||||
|
||||
In pip, all static-typing related imports should be guarded as follows:
|
||||
|
||||
from pip.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import ...
|
||||
|
||||
Ref: https://github.com/python/mypy/issues/3216
|
||||
"""
|
||||
|
||||
MYPY_CHECK_RUNNING = False
|
|
@ -18,6 +18,10 @@ from pip._vendor.progress.spinner import Spinner
|
|||
from pip._internal.compat import WINDOWS
|
||||
from pip._internal.utils.logging import get_indentation
|
||||
from pip._internal.utils.misc import format_size
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Any
|
||||
|
||||
try:
|
||||
from pip._vendor import colorama
|
||||
|
@ -56,7 +60,7 @@ def _select_progress_class(preferred, fallback):
|
|||
return preferred
|
||||
|
||||
|
||||
_BaseBar = _select_progress_class(IncrementalBar, Bar)
|
||||
_BaseBar = _select_progress_class(IncrementalBar, Bar) # type: Any
|
||||
|
||||
|
||||
class InterruptibleMixin(object):
|
||||
|
@ -125,7 +129,7 @@ class BlueEmojiBar(IncrementalBar):
|
|||
suffix = "%(percent)d%%"
|
||||
bar_prefix = " "
|
||||
bar_suffix = " "
|
||||
phases = (u"\U0001F539", u"\U0001F537", u"\U0001F535")
|
||||
phases = (u"\U0001F539", u"\U0001F537", u"\U0001F535") # type: Any
|
||||
|
||||
|
||||
class DownloadProgressMixin(object):
|
||||
|
@ -194,36 +198,45 @@ class BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin,
|
|||
message = "%(percent)d%%"
|
||||
suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s"
|
||||
|
||||
# NOTE: The "type: ignore" comments on the following classes are there to
|
||||
# work around https://github.com/python/typing/issues/241
|
||||
|
||||
class DefaultDownloadProgressBar(BaseDownloadProgressBar, _BaseBar):
|
||||
|
||||
class DefaultDownloadProgressBar(BaseDownloadProgressBar,
|
||||
_BaseBar): # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
class DownloadSilentBar(BaseDownloadProgressBar, SilentBar):
|
||||
class DownloadSilentBar(BaseDownloadProgressBar, SilentBar): # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
class DownloadIncrementalBar(BaseDownloadProgressBar, IncrementalBar):
|
||||
class DownloadIncrementalBar(BaseDownloadProgressBar, # type: ignore
|
||||
IncrementalBar):
|
||||
pass
|
||||
|
||||
|
||||
class DownloadChargingBar(BaseDownloadProgressBar, ChargingBar):
|
||||
class DownloadChargingBar(BaseDownloadProgressBar, # type: ignore
|
||||
ChargingBar):
|
||||
pass
|
||||
|
||||
|
||||
class DownloadShadyBar(BaseDownloadProgressBar, ShadyBar):
|
||||
class DownloadShadyBar(BaseDownloadProgressBar, ShadyBar): # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
class DownloadFillingSquaresBar(BaseDownloadProgressBar, FillingSquaresBar):
|
||||
class DownloadFillingSquaresBar(BaseDownloadProgressBar, # type: ignore
|
||||
FillingSquaresBar):
|
||||
pass
|
||||
|
||||
|
||||
class DownloadFillingCirclesBar(BaseDownloadProgressBar, FillingCirclesBar):
|
||||
class DownloadFillingCirclesBar(BaseDownloadProgressBar, # type: ignore
|
||||
FillingCirclesBar):
|
||||
pass
|
||||
|
||||
|
||||
class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, BlueEmojiBar):
|
||||
class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, # type: ignore
|
||||
BlueEmojiBar):
|
||||
pass
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,11 @@ from pip._internal.exceptions import BadCommand
|
|||
from pip._internal.utils.misc import (
|
||||
display_path, backup_dir, call_subprocess, rmtree, ask_path_exists,
|
||||
)
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Dict, Tuple
|
||||
from pip._internal.basecommand import Command
|
||||
|
||||
__all__ = ['vcs', 'get_src_requirement']
|
||||
|
||||
|
@ -22,7 +26,7 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class VcsSupport(object):
|
||||
_registry = {}
|
||||
_registry = {} # type: Dict[str, Command]
|
||||
schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn']
|
||||
|
||||
def __init__(self):
|
||||
|
@ -99,7 +103,7 @@ class VersionControl(object):
|
|||
name = ''
|
||||
dirname = ''
|
||||
# List of supported schemes for this Version Control
|
||||
schemes = ()
|
||||
schemes = () # type: Tuple[str, ...]
|
||||
|
||||
def __init__(self, url=None, *args, **kwargs):
|
||||
self.url = url
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from appdirs import *
|
|
@ -0,0 +1 @@
|
|||
from cachecontrol import *
|
|
@ -0,0 +1 @@
|
|||
from colorama import *
|
|
@ -0,0 +1 @@
|
|||
from distlib import *
|
|
@ -0,0 +1 @@
|
|||
from distro import *
|
|
@ -0,0 +1 @@
|
|||
from html5lib import *
|
|
@ -0,0 +1 @@
|
|||
from ipaddress import *
|
|
@ -0,0 +1 @@
|
|||
from lockfile import *
|
|
@ -0,0 +1 @@
|
|||
from msgpack import *
|
|
@ -0,0 +1 @@
|
|||
from packaging import *
|
|
@ -0,0 +1 @@
|
|||
from pkg_resources import *
|
|
@ -0,0 +1 @@
|
|||
from progress import *
|
|
@ -0,0 +1 @@
|
|||
from pyparsing import *
|
|
@ -0,0 +1 @@
|
|||
from pytoml import *
|
|
@ -0,0 +1 @@
|
|||
from requests import *
|
|
@ -0,0 +1 @@
|
|||
from retrying import *
|
|
@ -0,0 +1 @@
|
|||
from six import *
|
|
@ -0,0 +1 @@
|
|||
from six.moves import *
|
|
@ -0,0 +1 @@
|
|||
from webencodings import *
|
|
@ -1,6 +1,7 @@
|
|||
""""Vendoring script, python 3.5 needed"""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
|
||||
|
@ -32,6 +33,11 @@ def log(msg):
|
|||
print('[vendoring.%s] %s' % (TASK_NAME, msg))
|
||||
|
||||
|
||||
def _get_vendor_dir(ctx):
|
||||
git_root = ctx.run('git rev-parse --show-toplevel', hide=True).stdout
|
||||
return Path(git_root.strip()) / 'src' / 'pip' / '_vendor'
|
||||
|
||||
|
||||
def clean_vendor(ctx, vendor_dir):
|
||||
# Old _vendor cleanup
|
||||
remove_all(vendor_dir.glob('*.pyc'))
|
||||
|
@ -45,6 +51,18 @@ def clean_vendor(ctx, vendor_dir):
|
|||
log('Skipping %s' % item)
|
||||
|
||||
|
||||
def detect_vendored_libs(vendor_dir):
|
||||
retval = []
|
||||
for item in vendor_dir.iterdir():
|
||||
if item.is_dir():
|
||||
retval.append(item.name)
|
||||
elif item.name.endswith(".pyi"):
|
||||
continue
|
||||
elif item.name not in FILE_WHITE_LIST:
|
||||
retval.append(item.name[:-3])
|
||||
return retval
|
||||
|
||||
|
||||
def rewrite_imports(package_dir, vendored_libs):
|
||||
for item in package_dir.iterdir():
|
||||
if item.is_dir():
|
||||
|
@ -98,12 +116,7 @@ def vendor(ctx, vendor_dir):
|
|||
remove_all(vendor_dir.glob('msgpack/*.so'))
|
||||
|
||||
# Detect the vendored packages/modules
|
||||
vendored_libs = []
|
||||
for item in vendor_dir.iterdir():
|
||||
if item.is_dir():
|
||||
vendored_libs.append(item.name)
|
||||
elif item.name not in FILE_WHITE_LIST:
|
||||
vendored_libs.append(item.name[:-3])
|
||||
vendored_libs = detect_vendored_libs(vendor_dir)
|
||||
log("Detected vendored libraries: %s" % ", ".join(vendored_libs))
|
||||
|
||||
# Global import rewrites
|
||||
|
@ -121,12 +134,37 @@ def vendor(ctx, vendor_dir):
|
|||
apply_patch(ctx, patch)
|
||||
|
||||
|
||||
@invoke.task(name=TASK_NAME)
|
||||
@invoke.task
|
||||
def update_stubs(ctx):
|
||||
vendor_dir = _get_vendor_dir(ctx)
|
||||
vendored_libs = detect_vendored_libs(vendor_dir)
|
||||
|
||||
print("[vendoring.update_stubs] Add mypy stubs")
|
||||
|
||||
# Some projects need stubs other than a simple <name>.pyi
|
||||
extra_stubs_needed = {
|
||||
"six": ["six.__init__", "six.moves"]
|
||||
}
|
||||
|
||||
for lib in vendored_libs:
|
||||
if lib not in extra_stubs_needed:
|
||||
(vendor_dir / (lib + ".pyi")).write_text("from %s import *" % lib)
|
||||
continue
|
||||
|
||||
for selector in extra_stubs_needed[lib]:
|
||||
fname = selector.replace(".", os.sep) + ".pyi"
|
||||
if selector.endswith(".__init__"):
|
||||
selector = selector[:-9]
|
||||
|
||||
f_path = vendor_dir / fname
|
||||
if not f_path.parent.exists():
|
||||
f_path.parent.mkdir()
|
||||
f_path.write_text("from %s import *" % selector)
|
||||
|
||||
|
||||
@invoke.task(name=TASK_NAME, post=[update_stubs])
|
||||
def main(ctx):
|
||||
git_root = Path(
|
||||
ctx.run('git rev-parse --show-toplevel', hide=True).stdout.strip()
|
||||
)
|
||||
vendor_dir = git_root / 'src' / 'pip' / '_vendor'
|
||||
vendor_dir = _get_vendor_dir(ctx)
|
||||
log('Using vendor dir: %s' % vendor_dir)
|
||||
clean_vendor(ctx, vendor_dir)
|
||||
vendor(ctx, vendor_dir)
|
||||
|
|
9
tox.ini
9
tox.ini
|
@ -1,6 +1,6 @@
|
|||
[tox]
|
||||
envlist =
|
||||
docs, packaging, lint-py2, lint-py3,
|
||||
docs, packaging, lint-py2, lint-py3, mypy,
|
||||
py27, py33, py34, py35, py36, py37, pypy
|
||||
|
||||
[testenv]
|
||||
|
@ -44,3 +44,10 @@ commands = {[lint]commands}
|
|||
basepython = python3
|
||||
deps = {[lint]deps}
|
||||
commands = {[lint]commands}
|
||||
|
||||
[testenv:mypy]
|
||||
basepython = python3
|
||||
deps = mypy
|
||||
commands =
|
||||
mypy src/pip
|
||||
mypy src/pip -2
|
||||
|
|
Loading…
Reference in New Issue