From c469e11d475ecf8c5131148dce28f9e08ab635c6 Mon Sep 17 00:00:00 2001 From: Devesh Kumar Singh Date: Wed, 20 May 2020 23:12:23 +0530 Subject: [PATCH 1/6] Type annotations for pip._internal.network.auth --- src/pip/_internal/network/auth.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/pip/_internal/network/auth.py b/src/pip/_internal/network/auth.py index ab8ac0701..19c75aa9d 100644 --- a/src/pip/_internal/network/auth.py +++ b/src/pip/_internal/network/auth.py @@ -4,9 +4,6 @@ Contains interface (MultiDomainBasicAuth) and associated glue code for providing credentials in the context of network requests. """ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - import logging from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth @@ -23,11 +20,10 @@ from pip._internal.utils.misc import ( from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: - from optparse import Values - from typing import Dict, Optional, Tuple + from typing import Dict, Optional, Tuple, List, Any from pip._internal.vcs.versioncontrol import AuthInfo - + from pip._vendor.requests.models import Response, Request Credentials = Tuple[str, str, str] logger = logging.getLogger(__name__) @@ -44,6 +40,7 @@ except Exception as exc: def get_keyring_auth(url, username): + # type: (str, str) -> Optional[AuthInfo] """Return the tuple auth for a given url from keyring.""" if not url or not keyring: return None @@ -70,12 +67,13 @@ def get_keyring_auth(url, username): logger.warning( "Keyring is skipped due to an exception: %s", str(exc), ) + return None class MultiDomainBasicAuth(AuthBase): def __init__(self, prompting=True, index_urls=None): - # type: (bool, Optional[Values]) -> None + # type: (bool, Optional[List[str]]) -> None self.prompting = prompting self.index_urls = index_urls self.passwords = {} # type: Dict[str, AuthInfo] @@ -87,6 +85,7 @@ class MultiDomainBasicAuth(AuthBase): self._credentials_to_save = None # type: Optional[Credentials] def _get_index_url(self, url): + # type: (str) -> Optional[str] """Return the original index URL matching the requested URL. Cached or dynamically generated credentials may work against @@ -106,9 +105,11 @@ class MultiDomainBasicAuth(AuthBase): prefix = remove_auth_from_url(u).rstrip("/") + "/" if url.startswith(prefix): return u + return None def _get_new_credentials(self, original_url, allow_netrc=True, allow_keyring=True): + # type: (str, bool, bool) -> AuthInfo """Find and return credentials for the specified URL.""" # Split the credentials and netloc from the url. url, netloc, url_user_password = split_auth_netloc_from_url( @@ -158,6 +159,7 @@ class MultiDomainBasicAuth(AuthBase): return username, password def _get_url_and_credentials(self, original_url): + # type: (str) -> Tuple[str, Optional[str], Optional[str]] """Return the credentials to use for the provided URL. If allowed, netrc and keyring may be used to obtain the @@ -178,7 +180,7 @@ class MultiDomainBasicAuth(AuthBase): username, password = self._get_new_credentials(original_url) if username is not None or password is not None: - # Convert the username and password if they're None, so that + # Convert the username and password if they 're None, so that # this netloc will show up as "cached" in the conditional above. # Further, HTTPBasicAuth doesn't accept None, so it makes sense to # cache the value that is going to be used. @@ -198,6 +200,7 @@ class MultiDomainBasicAuth(AuthBase): return url, username, password def __call__(self, req): + # type: (Request) -> Request # Get credentials for this request url, username, password = self._get_url_and_credentials(req.url) @@ -215,9 +218,10 @@ class MultiDomainBasicAuth(AuthBase): # Factored out to allow for easy patching in tests def _prompt_for_password(self, netloc): + # type: (str) -> Tuple[Optional[str], Optional[str], bool] username = ask_input("User for {}: ".format(netloc)) if not username: - return None, None + return None, None, False auth = get_keyring_auth(netloc, username) if auth and auth[0] is not None and auth[1] is not None: return auth[0], auth[1], False @@ -226,11 +230,13 @@ class MultiDomainBasicAuth(AuthBase): # Factored out to allow for easy patching in tests def _should_save_password_to_keyring(self): + # type: () -> bool if not keyring: return False return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y" def handle_401(self, resp, **kwargs): + # type: (Response, **Any) -> None # We only care about 401 responses, anything else we want to just # pass through the actual response if resp.status_code != 401: @@ -276,6 +282,7 @@ class MultiDomainBasicAuth(AuthBase): return new_resp def warn_on_401(self, resp, **kwargs): + # type: (Response, **Any) -> None """Response callback to warn about incorrect credentials.""" if resp.status_code == 401: logger.warning( @@ -283,6 +290,7 @@ class MultiDomainBasicAuth(AuthBase): ) def save_credentials(self, resp, **kwargs): + # type: (Response, **Any) -> None """Response callback to save credentials on success.""" assert keyring is not None, "should never reach here without keyring" if not keyring: From e9901982bfe36da0e8956c0ab79ca597256c03c7 Mon Sep 17 00:00:00 2001 From: Devesh Kumar Singh Date: Wed, 20 May 2020 23:12:49 +0530 Subject: [PATCH 2/6] Type annotations for pip._internal.utils.encoding --- src/pip/_internal/utils/encoding.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/pip/_internal/utils/encoding.py b/src/pip/_internal/utils/encoding.py index ab4d4b98e..823e7fc04 100644 --- a/src/pip/_internal/utils/encoding.py +++ b/src/pip/_internal/utils/encoding.py @@ -1,6 +1,3 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import codecs import locale import re @@ -35,8 +32,10 @@ def auto_decode(data): # Lets check the first two lines as in PEP263 for line in data.split(b'\n')[:2]: if line[0:1] == b'#' and ENCODING_RE.search(line): - encoding = ENCODING_RE.search(line).groups()[0].decode('ascii') - return data.decode(encoding) + result = ENCODING_RE.search(line) + if result: + encoding = result.groups()[0].decode('ascii') + return data.decode(encoding) return data.decode( locale.getpreferredencoding(False) or sys.getdefaultencoding(), ) From d84c14013ab761d89b01724e8f50f7d701965d38 Mon Sep 17 00:00:00 2001 From: Devesh Kumar Singh Date: Wed, 20 May 2020 23:13:00 +0530 Subject: [PATCH 3/6] Type annotations for pip._internal.utils.unpacking --- src/pip/_internal/utils/unpacking.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pip/_internal/utils/unpacking.py b/src/pip/_internal/utils/unpacking.py index 7252dc217..93a8b15ca 100644 --- a/src/pip/_internal/utils/unpacking.py +++ b/src/pip/_internal/utils/unpacking.py @@ -1,10 +1,6 @@ """Utilities related archives. """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - from __future__ import absolute_import import logging @@ -48,6 +44,7 @@ except ImportError: def current_umask(): + # type: () -> int """Get the current umask which involves having to set it temporarily.""" mask = os.umask(0) os.umask(mask) @@ -208,6 +205,7 @@ def untar_file(filename, location): ) continue else: + fp = None try: fp = tar.extractfile(member) except (KeyError, AttributeError) as exc: @@ -220,8 +218,10 @@ def untar_file(filename, location): continue ensure_dir(os.path.dirname(path)) with open(path, 'wb') as destfp: - shutil.copyfileobj(fp, destfp) - fp.close() + if fp: + shutil.copyfileobj(fp, destfp) + if fp: + fp.close() # Update the timestamp (useful for cython compiled files) # https://github.com/python/typeshed/issues/2673 tar.utime(member, path) # type: ignore From ea7a7d26fc430fc663e05807c7aa799e70ade847 Mon Sep 17 00:00:00 2001 From: Devesh Kumar Singh Date: Wed, 20 May 2020 23:13:43 +0530 Subject: [PATCH 4/6] Add NEWS file --- news/EBB1CF12-70ED-405F-90C0-BEA7CF25DCE4.trivial | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 news/EBB1CF12-70ED-405F-90C0-BEA7CF25DCE4.trivial diff --git a/news/EBB1CF12-70ED-405F-90C0-BEA7CF25DCE4.trivial b/news/EBB1CF12-70ED-405F-90C0-BEA7CF25DCE4.trivial new file mode 100644 index 000000000..e69de29bb From 50cbd6a03245d6315da74c470cf1c481ef554cba Mon Sep 17 00:00:00 2001 From: Devesh Kumar Singh Date: Thu, 21 May 2020 22:46:56 +0530 Subject: [PATCH 5/6] Assert not None instead of if check --- src/pip/_internal/utils/encoding.py | 6 +++--- src/pip/_internal/utils/unpacking.py | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/pip/_internal/utils/encoding.py b/src/pip/_internal/utils/encoding.py index 823e7fc04..5b83d61bb 100644 --- a/src/pip/_internal/utils/encoding.py +++ b/src/pip/_internal/utils/encoding.py @@ -33,9 +33,9 @@ def auto_decode(data): for line in data.split(b'\n')[:2]: if line[0:1] == b'#' and ENCODING_RE.search(line): result = ENCODING_RE.search(line) - if result: - encoding = result.groups()[0].decode('ascii') - return data.decode(encoding) + assert result is not None + encoding = result.groups()[0].decode('ascii') + return data.decode(encoding) return data.decode( locale.getpreferredencoding(False) or sys.getdefaultencoding(), ) diff --git a/src/pip/_internal/utils/unpacking.py b/src/pip/_internal/utils/unpacking.py index 93a8b15ca..fe71d26e3 100644 --- a/src/pip/_internal/utils/unpacking.py +++ b/src/pip/_internal/utils/unpacking.py @@ -205,7 +205,6 @@ def untar_file(filename, location): ) continue else: - fp = None try: fp = tar.extractfile(member) except (KeyError, AttributeError) as exc: @@ -217,11 +216,10 @@ def untar_file(filename, location): ) continue ensure_dir(os.path.dirname(path)) + assert fp is not None with open(path, 'wb') as destfp: - if fp: - shutil.copyfileobj(fp, destfp) - if fp: - fp.close() + shutil.copyfileobj(fp, destfp) + fp.close() # Update the timestamp (useful for cython compiled files) # https://github.com/python/typeshed/issues/2673 tar.utime(member, path) # type: ignore From e8aa5e73c556b1b2e56d0f5485edeb4a535748e0 Mon Sep 17 00:00:00 2001 From: Devesh Kumar Singh Date: Thu, 21 May 2020 22:49:58 +0530 Subject: [PATCH 6/6] Fix return type annotation for handle_401 --- src/pip/_internal/network/auth.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pip/_internal/network/auth.py b/src/pip/_internal/network/auth.py index 19c75aa9d..ca729fcdf 100644 --- a/src/pip/_internal/network/auth.py +++ b/src/pip/_internal/network/auth.py @@ -23,7 +23,9 @@ if MYPY_CHECK_RUNNING: from typing import Dict, Optional, Tuple, List, Any from pip._internal.vcs.versioncontrol import AuthInfo + from pip._vendor.requests.models import Response, Request + Credentials = Tuple[str, str, str] logger = logging.getLogger(__name__) @@ -180,7 +182,7 @@ class MultiDomainBasicAuth(AuthBase): username, password = self._get_new_credentials(original_url) if username is not None or password is not None: - # Convert the username and password if they 're None, so that + # Convert the username and password if they're None, so that # this netloc will show up as "cached" in the conditional above. # Further, HTTPBasicAuth doesn't accept None, so it makes sense to # cache the value that is going to be used. @@ -236,7 +238,7 @@ class MultiDomainBasicAuth(AuthBase): return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y" def handle_401(self, resp, **kwargs): - # type: (Response, **Any) -> None + # type: (Response, **Any) -> Response # We only care about 401 responses, anything else we want to just # pass through the actual response if resp.status_code != 401: