From 08dc580cd08bdeae121d7cde65fbc4c6c1a4aa36 Mon Sep 17 00:00:00 2001 From: Henk-Jaap Wagenaar Date: Sat, 11 Nov 2017 02:49:57 +0000 Subject: [PATCH 1/8] Add requests_kerberos as vendored package. --- src/pip/_vendor/README.rst | 2 + src/pip/_vendor/requests_kerberos/__init__.py | 25 ++ src/pip/_vendor/requests_kerberos/compat.py | 14 + .../_vendor/requests_kerberos/exceptions.py | 15 + .../_vendor/requests_kerberos/kerberos_.py | 323 ++++++++++++++++++ src/pip/_vendor/vendor.txt | 1 + 6 files changed, 380 insertions(+) create mode 100644 src/pip/_vendor/requests_kerberos/__init__.py create mode 100644 src/pip/_vendor/requests_kerberos/compat.py create mode 100644 src/pip/_vendor/requests_kerberos/exceptions.py create mode 100644 src/pip/_vendor/requests_kerberos/kerberos_.py diff --git a/src/pip/_vendor/README.rst b/src/pip/_vendor/README.rst index 2f304b1a39a..1034b186801 100644 --- a/src/pip/_vendor/README.rst +++ b/src/pip/_vendor/README.rst @@ -97,6 +97,8 @@ Modifications * ``pkg_resources`` has been modified to import its dependencies from ``pip._vendor`` * ``CacheControl`` has been modified to import its dependencies from ``pip._vendor`` * ``packaging`` has been modified to import its dependencies from ``pip._vendor`` +* ``requests_kerberos`` has been modified to import its dependencies from ``pip +._vendor`` * ``requests`` has been modified *not* to optionally load any C dependencies * Modified distro to delay importing ``argparse`` to avoid errors on 2.6 diff --git a/src/pip/_vendor/requests_kerberos/__init__.py b/src/pip/_vendor/requests_kerberos/__init__.py new file mode 100644 index 00000000000..d8dea418e7b --- /dev/null +++ b/src/pip/_vendor/requests_kerberos/__init__.py @@ -0,0 +1,25 @@ +""" +requests Kerberos/GSSAPI authentication library +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Requests is an HTTP library, written in Python, for human beings. This library +adds optional Kerberos/GSSAPI authentication support and supports mutual +authentication. Basic GET usage: + + >>> import pip._vendor.requests + >>> from pip._vendor.requests_kerberos import HTTPKerberosAuth + >>> r = pip._vendor.requests.get("http://example.org", auth=HTTPKerberosAuth()) + +The entire `requests.api` should be supported. +""" +import logging + +from .kerberos_ import HTTPKerberosAuth, REQUIRED, OPTIONAL, DISABLED +from .exceptions import MutualAuthenticationError +from .compat import NullHandler + +logging.getLogger(__name__).addHandler(NullHandler()) + +__all__ = ('HTTPKerberosAuth', 'MutualAuthenticationError', 'REQUIRED', + 'OPTIONAL', 'DISABLED') +__version__ = '0.11.0' diff --git a/src/pip/_vendor/requests_kerberos/compat.py b/src/pip/_vendor/requests_kerberos/compat.py new file mode 100644 index 00000000000..01b75009805 --- /dev/null +++ b/src/pip/_vendor/requests_kerberos/compat.py @@ -0,0 +1,14 @@ +""" +Compatibility library for older versions of python +""" +import sys + +# python 2.7 introduced a NullHandler which we want to use, but to support +# older versions, we implement our own if needed. +if sys.version_info[:2] > (2, 6): + from logging import NullHandler +else: + from logging import Handler + class NullHandler(Handler): + def emit(self, record): + pass diff --git a/src/pip/_vendor/requests_kerberos/exceptions.py b/src/pip/_vendor/requests_kerberos/exceptions.py new file mode 100644 index 00000000000..db1ca771495 --- /dev/null +++ b/src/pip/_vendor/requests_kerberos/exceptions.py @@ -0,0 +1,15 @@ +""" +requests_kerberos.exceptions +~~~~~~~~~~~~~~~~~~~ + +This module contains the set of exceptions. + +""" +from pip._vendor.requests.exceptions import RequestException + + +class MutualAuthenticationError(RequestException): + """Mutual Authentication Error""" + +class KerberosExchangeError(RequestException): + """Kerberos Exchange Failed Error""" diff --git a/src/pip/_vendor/requests_kerberos/kerberos_.py b/src/pip/_vendor/requests_kerberos/kerberos_.py new file mode 100644 index 00000000000..353187af234 --- /dev/null +++ b/src/pip/_vendor/requests_kerberos/kerberos_.py @@ -0,0 +1,323 @@ +try: + import kerberos +except ImportError: + import winkerberos as kerberos +import re +import logging + +from pip._vendor.requests.auth import AuthBase +from pip._vendor.requests.models import Response +from pip._vendor.requests.compat import urlparse, StringIO +from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.requests.cookies import cookiejar_from_dict + +from .exceptions import MutualAuthenticationError, KerberosExchangeError + +log = logging.getLogger(__name__) + +# Different types of mutual authentication: +# with mutual_authentication set to REQUIRED, all responses will be +# authenticated with the exception of errors. Errors will have their contents +# and headers stripped. If a non-error response cannot be authenticated, a +# MutualAuthenticationError exception will be raised. +# with mutual_authentication set to OPTIONAL, mutual authentication will be +# attempted if supported, and if supported and failed, a +# MutualAuthenticationError exception will be raised. Responses which do not +# support mutual authentication will be returned directly to the user. +# with mutual_authentication set to DISABLED, mutual authentication will not be +# attempted, even if supported. +REQUIRED = 1 +OPTIONAL = 2 +DISABLED = 3 + +class SanitizedResponse(Response): + """The :class:`Response ` object, which contains a server's + response to an HTTP request. + + This differs from `requests.models.Response` in that it's headers and + content have been sanitized. This is only used for HTTP Error messages + which do not support mutual authentication when mutual authentication is + required.""" + + def __init__(self, response): + super(SanitizedResponse, self).__init__() + self.status_code = response.status_code + self.encoding = response.encoding + self.raw = response.raw + self.reason = response.reason + self.url = response.url + self.request = response.request + self.connection = response.connection + self._content_consumed = True + + self._content = "" + self.cookies = cookiejar_from_dict({}) + self.headers = CaseInsensitiveDict() + self.headers['content-length'] = '0' + for header in ('date', 'server'): + if header in response.headers: + self.headers[header] = response.headers[header] + + +def _negotiate_value(response): + """Extracts the gssapi authentication token from the appropriate header""" + if hasattr(_negotiate_value, 'regex'): + regex = _negotiate_value.regex + else: + # There's no need to re-compile this EVERY time it is called. Compile + # it once and you won't have the performance hit of the compilation. + regex = re.compile('(?:.*,)*\s*Negotiate\s*([^,]*),?', re.I) + _negotiate_value.regex = regex + + authreq = response.headers.get('www-authenticate', None) + + if authreq: + match_obj = regex.search(authreq) + if match_obj: + return match_obj.group(1) + + return None + + +class HTTPKerberosAuth(AuthBase): + """Attaches HTTP GSSAPI/Kerberos Authentication to the given Request + object.""" + def __init__( + self, mutual_authentication=REQUIRED, + service="HTTP", delegate=False, force_preemptive=False, + principal=None, hostname_override=None, sanitize_mutual_error_response=True): + self.context = {} + self.mutual_authentication = mutual_authentication + self.delegate = delegate + self.pos = None + self.service = service + self.force_preemptive = force_preemptive + self.principal = principal + self.hostname_override = hostname_override + self.sanitize_mutual_error_response = sanitize_mutual_error_response + + def generate_request_header(self, response, host, is_preemptive=False): + """ + Generates the GSSAPI authentication token with kerberos. + + If any GSSAPI step fails, raise KerberosExchangeError + with failure detail. + + """ + + # Flags used by kerberos module. + gssflags = kerberos.GSS_C_MUTUAL_FLAG | kerberos.GSS_C_SEQUENCE_FLAG + if self.delegate: + gssflags |= kerberos.GSS_C_DELEG_FLAG + + try: + kerb_stage = "authGSSClientInit()" + # contexts still need to be stored by host, but hostname_override + # allows use of an arbitrary hostname for the kerberos exchange + # (eg, in cases of aliased hosts, internal vs external, CNAMEs + # w/ name-based HTTP hosting) + kerb_host = self.hostname_override if self.hostname_override is not None else host + kerb_spn = "{0}@{1}".format(self.service, kerb_host) + + result, self.context[host] = kerberos.authGSSClientInit(kerb_spn, + gssflags=gssflags, principal=self.principal) + + if result < 1: + raise EnvironmentError(result, kerb_stage) + + # if we have a previous response from the server, use it to continue + # the auth process, otherwise use an empty value + negotiate_resp_value = '' if is_preemptive else _negotiate_value(response) + + kerb_stage = "authGSSClientStep()" + result = kerberos.authGSSClientStep(self.context[host], + negotiate_resp_value) + + if result < 0: + raise EnvironmentError(result, kerb_stage) + + kerb_stage = "authGSSClientResponse()" + gss_response = kerberos.authGSSClientResponse(self.context[host]) + + return "Negotiate {0}".format(gss_response) + + except kerberos.GSSError as error: + log.exception( + "generate_request_header(): {0} failed:".format(kerb_stage)) + log.exception(error) + raise KerberosExchangeError("%s failed: %s" % (kerb_stage, str(error.args))) + + except EnvironmentError as error: + # ensure we raised this for translation to KerberosExchangeError + # by comparing errno to result, re-raise if not + if error.errno != result: + raise + message = "{0} failed, result: {1}".format(kerb_stage, result) + log.error("generate_request_header(): {0}".format(message)) + raise KerberosExchangeError(message) + + def authenticate_user(self, response, **kwargs): + """Handles user authentication with gssapi/kerberos""" + + host = urlparse(response.url).hostname + + try: + auth_header = self.generate_request_header(response, host) + except KerberosExchangeError: + # GSS Failure, return existing response + return response + + log.debug("authenticate_user(): Authorization header: {0}".format( + auth_header)) + response.request.headers['Authorization'] = auth_header + + # Consume the content so we can reuse the connection for the next + # request. + response.content + response.raw.release_conn() + + _r = response.connection.send(response.request, **kwargs) + _r.history.append(response) + + log.debug("authenticate_user(): returning {0}".format(_r)) + return _r + + def handle_401(self, response, **kwargs): + """Handles 401's, attempts to use gssapi/kerberos authentication""" + + log.debug("handle_401(): Handling: 401") + if _negotiate_value(response) is not None: + _r = self.authenticate_user(response, **kwargs) + log.debug("handle_401(): returning {0}".format(_r)) + return _r + else: + log.debug("handle_401(): Kerberos is not supported") + log.debug("handle_401(): returning {0}".format(response)) + return response + + def handle_other(self, response): + """Handles all responses with the exception of 401s. + + This is necessary so that we can authenticate responses if requested""" + + log.debug("handle_other(): Handling: %d" % response.status_code) + + if self.mutual_authentication in (REQUIRED, OPTIONAL): + + is_http_error = response.status_code >= 400 + + if _negotiate_value(response) is not None: + log.debug("handle_other(): Authenticating the server") + if not self.authenticate_server(response): + # Mutual authentication failure when mutual auth is wanted, + # raise an exception so the user doesn't use an untrusted + # response. + log.error("handle_other(): Mutual authentication failed") + raise MutualAuthenticationError("Unable to authenticate " + "{0}".format(response)) + + # Authentication successful + log.debug("handle_other(): returning {0}".format(response)) + return response + + elif is_http_error or self.mutual_authentication == OPTIONAL: + if not response.ok: + log.error("handle_other(): Mutual authentication unavailable " + "on {0} response".format(response.status_code)) + + if(self.mutual_authentication == REQUIRED and + self.sanitize_mutual_error_response): + return SanitizedResponse(response) + else: + return response + else: + # Unable to attempt mutual authentication when mutual auth is + # required, raise an exception so the user doesnt use an + # untrusted response. + log.error("handle_other(): Mutual authentication failed") + raise MutualAuthenticationError("Unable to authenticate " + "{0}".format(response)) + else: + log.debug("handle_other(): returning {0}".format(response)) + return response + + def authenticate_server(self, response): + """ + Uses GSSAPI to authenticate the server. + + Returns True on success, False on failure. + """ + + log.debug("authenticate_server(): Authenticate header: {0}".format( + _negotiate_value(response))) + + host = urlparse(response.url).hostname + + try: + result = kerberos.authGSSClientStep(self.context[host], + _negotiate_value(response)) + except kerberos.GSSError: + log.exception("authenticate_server(): authGSSClientStep() failed:") + return False + + if result < 1: + log.error("authenticate_server(): authGSSClientStep() failed: " + "{0}".format(result)) + return False + + log.debug("authenticate_server(): returning {0}".format(response)) + return True + + def handle_response(self, response, **kwargs): + """Takes the given response and tries kerberos-auth, as needed.""" + num_401s = kwargs.pop('num_401s', 0) + + if self.pos is not None: + # Rewind the file position indicator of the body to where + # it was to resend the request. + response.request.body.seek(self.pos) + + if response.status_code == 401 and num_401s < 2: + # 401 Unauthorized. Handle it, and if it still comes back as 401, + # that means authentication failed. + _r = self.handle_401(response, **kwargs) + log.debug("handle_response(): returning %s", _r) + log.debug("handle_response() has seen %d 401 responses", num_401s) + num_401s += 1 + return self.handle_response(_r, num_401s=num_401s, **kwargs) + elif response.status_code == 401 and num_401s >= 2: + # Still receiving 401 responses after attempting to handle them. + # Authentication has failed. Return the 401 response. + log.debug("handle_response(): returning 401 %s", response) + return response + else: + _r = self.handle_other(response) + log.debug("handle_response(): returning %s", _r) + return _r + + def deregister(self, response): + """Deregisters the response handler""" + response.request.deregister_hook('response', self.handle_response) + + def __call__(self, request): + if self.force_preemptive: + # add Authorization header before we receive a 401 + # by the 401 handler + host = urlparse(request.url).hostname + + auth_header = self.generate_request_header(None, host, is_preemptive=True) + + log.debug("HTTPKerberosAuth: Preemptive Authorization header: {0}".format(auth_header)) + + request.headers['Authorization'] = auth_header + + request.register_hook('response', self.handle_response) + try: + self.pos = request.body.tell() + except AttributeError: + # In the case of HTTPKerberosAuth being reused and the body + # of the previous request was a file-like object, pos has + # the file position of the previous body. Ensure it's set to + # None. + self.pos = None + return request diff --git a/src/pip/_vendor/vendor.txt b/src/pip/_vendor/vendor.txt index 82594b141be..dc4ebd957a9 100644 --- a/src/pip/_vendor/vendor.txt +++ b/src/pip/_vendor/vendor.txt @@ -20,3 +20,4 @@ requests==2.18.4 certifi==2017.7.27.1 setuptools==36.4.0 webencodings==0.5.1 +requests_kerberos==0.11.0 From c936e14135dd9f4a6dd834466b843815f3bf160b Mon Sep 17 00:00:00 2001 From: Henk-Jaap Wagenaar Date: Sat, 11 Nov 2017 03:25:28 +0000 Subject: [PATCH 2/8] Add kerberos support for authentication in sending requests. --- src/pip/_internal/basecommand.py | 13 +++++-- src/pip/_internal/download.py | 63 +++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/pip/_internal/basecommand.py b/src/pip/_internal/basecommand.py index 21aa8d950f9..ec3b257518d 100644 --- a/src/pip/_internal/basecommand.py +++ b/src/pip/_internal/basecommand.py @@ -79,6 +79,7 @@ def _build_session(self, options, retries=None, timeout=None): ), retries=retries if retries is not None else options.retries, insecure_hosts=options.trusted_hosts, + prompting=not options.no_input ) # Handle custom ca-bundles from the user @@ -102,9 +103,6 @@ def _build_session(self, options, retries=None, timeout=None): "https": options.proxy, } - # Determine if we can prompt the user for authentication or not - session.auth.prompting = not options.no_input - return session def parse_args(self, args): @@ -195,6 +193,15 @@ def main(self, args): ), }) + if level in ["INFO", "ERROR"]: + log_level_kerberos = logging.CRITICAL + 1 + else: + log_level_kerberos = logging.DEBUG + + logging.getLogger('pip._vendor.requests_kerberos.kerberos_').setLevel( + log_level_kerberos + ) + if sys.version_info[:2] == (3, 3): warnings.warn( "Python 3.3 supported has been deprecated and support for it " diff --git a/src/pip/_internal/download.py b/src/pip/_internal/download.py index 180ea77bfaf..e5a60d63551 100644 --- a/src/pip/_internal/download.py +++ b/src/pip/_internal/download.py @@ -52,6 +52,14 @@ except ImportError: HAS_TLS = False +try: + from pip._vendor.requests_kerberos import HTTPKerberosAuth + from pip._vendor.requests_kerberos import kerberos_ as ik + _KERBEROS_AVAILABLE = True + +except ImportError: + _KERBEROS_AVAILABLE = False + __all__ = ['get_file_content', 'is_url', 'url_to_path', 'path_to_url', 'is_archive_file', 'unpack_vcs_link', @@ -214,6 +222,47 @@ def parse_credentials(self, netloc): return None, None +class MultiAuth(AuthBase): + def __init__(self, initial_auth=None, *auths): + if initial_auth is None: + self.initial_auth = MultiDomainBasicAuth(prompting=False) + else: + self.initial_auth = initial_auth + + self.auths = auths + + def __call__(self, req): + req = self.initial_auth(req) + self._register_hook(req, 0) # register hook after auth itself + return req + + def _register_hook(self, req, i): + if i >= len(self.auths): + return + + def hook(resp, **kwargs): + self.handle_response(resp, i, **kwargs) + + req.register_hook("response", hook) + + def handle_response(self, resp, i, **kwargs): + if resp.status_code != 401: # authorization required + return resp + + # clear response + resp.content + resp.raw.release_conn() + + req = self.auths[i](resp.request) # deletegate to ith auth + logger.info('registering hook {}'.format(i + 1)) + self._register_hook(req, i + 1) # register hook after auth itself + + new_resp = resp.connection.send(req, **kwargs) + new_resp.history.append(resp) + + return new_resp + + class LocalFSAdapter(BaseAdapter): def send(self, request, stream=None, timeout=None, verify=None, cert=None, @@ -328,6 +377,7 @@ def __init__(self, *args, **kwargs): retries = kwargs.pop("retries", 0) cache = kwargs.pop("cache", None) insecure_hosts = kwargs.pop("insecure_hosts", []) + prompting = kwargs.pop("prompting", True) super(PipSession, self).__init__(*args, **kwargs) @@ -335,7 +385,18 @@ def __init__(self, *args, **kwargs): self.headers["User-Agent"] = user_agent() # Attach our Authentication handler to the session - self.auth = MultiDomainBasicAuth() + no_prompt = MultiDomainBasicAuth(prompting=False) + prompt = MultiDomainBasicAuth(prompting=True) + prompt.passwords = no_prompt.passwords # share same dict of passwords + + if _KERBEROS_AVAILABLE and prompting: + auths = [no_prompt, HTTPKerberosAuth(ik.REQUIRED), prompt] + elif _KERBEROS_AVAILABLE and not prompting: + auths = [no_prompt, HTTPKerberosAuth(ik.REQUIRED)] + else: + auths = [MultiDomainBasicAuth(prompting=prompting)] + + self.auth = MultiAuth(*auths) # Create our urllib3.Retry instance which will allow us to customize # how we handle retries. From 87086bde9acf49277a3606250c8cf71906ad706f Mon Sep 17 00:00:00 2001 From: Henk-Jaap Wagenaar Date: Sat, 11 Nov 2017 03:40:45 +0000 Subject: [PATCH 3/8] Add news entry for kerberos authentication --- news/4854.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/4854.feature diff --git a/news/4854.feature b/news/4854.feature new file mode 100644 index 00000000000..ddd5c6ecaa5 --- /dev/null +++ b/news/4854.feature @@ -0,0 +1 @@ +Add kerberos support to possible authenticators, when available. Vendor in requests_kerberos 0.11.0. From e6ce81424ca7dbc88def729a152bc392f408fb94 Mon Sep 17 00:00:00 2001 From: Henk-Jaap Wagenaar Date: Sat, 11 Nov 2017 03:42:30 +0000 Subject: [PATCH 4/8] Add news item vendoring requests_kerberos. --- news/requests_kerberos.vendor | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/requests_kerberos.vendor diff --git a/news/requests_kerberos.vendor b/news/requests_kerberos.vendor new file mode 100644 index 00000000000..cf17078720b --- /dev/null +++ b/news/requests_kerberos.vendor @@ -0,0 +1 @@ +Vendored requests_kerberos at requests_kerberos==0.11.0 From f9391d3c17b0dcc01cf883b66ea8022ae4c07a19 Mon Sep 17 00:00:00 2001 From: Henk-Jaap Wagenaar Date: Tue, 14 Nov 2017 09:05:18 +0000 Subject: [PATCH 5/8] Add documentation on Kerberos authentication feature. --- docs/reference/pip_install.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/reference/pip_install.rst b/docs/reference/pip_install.rst index a1fba2c2638..9b0eea8cb9a 100644 --- a/docs/reference/pip_install.rst +++ b/docs/reference/pip_install.rst @@ -459,6 +459,21 @@ SSL Certificate Verification Starting with v1.3, pip provides SSL certificate verification over https, to prevent man-in-the-middle attacks against PyPI downloads. +.. _`Kerberos Authentication`: + +Kerberos Authentication +++++++++++++++++++++++++++++ + +Starting with v10.0, pip supports using a Kerberos ticket to authenticate +with servers. This feature requires that ``pykerberos`` or ``winkerberos`` +is installed in the same environment as pip. + +If you wish to ignore Kerberos authenticated (index) servers for bootstrapping +the installation of ``pykerberos`` or ``winkerberos`` or are not authenticated +for all servers by default pip will ask for input. To change this behaviour +to ignore those servers use the ``--no-input`` command line option. Your system +administrator can also set this in the config files or an environment variable, +see :ref:`Configuration`. .. _`Caching`: From 5965c29ffdd7996e6ca148bf9fe1d9fdf2615c45 Mon Sep 17 00:00:00 2001 From: Henk-Jaap Wagenaar Date: Wed, 23 Jan 2019 12:42:08 +0000 Subject: [PATCH 6/8] Fix a few oopsies in last commit --- src/pip/_internal/utils/logging.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/pip/_internal/utils/logging.py b/src/pip/_internal/utils/logging.py index 1bd85913b05..b0bed785099 100644 --- a/src/pip/_internal/utils/logging.py +++ b/src/pip/_internal/utils/logging.py @@ -232,11 +232,6 @@ def setup_logging(verbosity, no_color, user_log_file): else: level = "INFO" - if level in ["INFO", "ERROR"]: - log_level_kerberos = logging.CRITICAL + 1 - else: - log_level_kerberos = logging.DEBUG - level_number = getattr(logging, level) # The "root" logger should match the "console" level *unless* we also need @@ -255,7 +250,10 @@ def setup_logging(verbosity, no_color, user_log_file): # Similar for vendored Kerberos, which is a bit trigger happy. logging.addLevelName(logging.CRITICAL + 1, "SUPERCRITICAL") - kerberos_log_level = "SUPERCRITICAL" if level in ["INFO", "ERROR"] else "DEBUG" + kerberos_log_level = ( + "SUPERCRITICAL" if level in ["INFO", "ERROR"] else + "DEBUG" + ) # Shorthands for clarity log_streams = { @@ -320,9 +318,7 @@ def setup_logging(verbosity, no_color, user_log_file): "loggers": { "pip._vendor": { "level": vendored_log_level - } - }, - "loggers": { + }, "pip._vendor.requests_kerberos.kerberos_": { "level": kerberos_log_level } From dc3936bf443e00a9c7cfeac24a99d8cd7730cebf Mon Sep 17 00:00:00 2001 From: Henk-Jaap Wagenaar Date: Wed, 23 Jan 2019 13:12:28 +0000 Subject: [PATCH 7/8] remove linebreak introduced by hard word-wrapping --- src/pip/_vendor/README.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pip/_vendor/README.rst b/src/pip/_vendor/README.rst index 41b17910b69..a55bb6d44d4 100644 --- a/src/pip/_vendor/README.rst +++ b/src/pip/_vendor/README.rst @@ -103,8 +103,7 @@ Modifications * ``CacheControl`` has been modified to import its dependencies from ``pip._vendor`` * ``requests`` has been modified to import its other dependencies from ``pip._vendor`` and to *not* load ``simplejson`` (all platforms) and ``pyopenssl`` (Windows). -* ``requests_kerberos`` has been modified to import its dependencies from ``pip -._vendor`` +* ``requests_kerberos`` has been modified to import its dependencies from ``pip._vendor`` Automatic Vendoring From 84f62ad30feb237f89513df9342a0337da1e10f8 Mon Sep 17 00:00:00 2001 From: Henk-Jaap Wagenaar Date: Mon, 26 Aug 2019 14:06:21 +0100 Subject: [PATCH 8/8] Fix linter --- src/pip/_internal/download.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pip/_internal/download.py b/src/pip/_internal/download.py index 3513b67ea1d..567b3874569 100644 --- a/src/pip/_internal/download.py +++ b/src/pip/_internal/download.py @@ -652,7 +652,10 @@ def __init__(self, *args, **kwargs): elif _KERBEROS_AVAILABLE and not prompting: auths = [no_prompt, HTTPKerberosAuth(ik.REQUIRED)] else: - auths = [MultiDomainBasicAuth(prompting=prompting, index_urls=index_urls)] + auths = [MultiDomainBasicAuth( + prompting=prompting, + index_urls=index_urls + )] self.auth = MultiAuth(*auths)