diff --git a/pyproject.toml b/pyproject.toml index cb5199e5d6d..8726a0d40cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,11 +129,7 @@ module = [ 'poetry.mixology.term', 'poetry.mixology.version_solver', 'poetry.repositories.installed_repository', - 'poetry.utils.appdirs', - 'poetry.utils.authenticator', 'poetry.utils.env', - 'poetry.utils.exporter', - 'poetry.utils.setup_reader', ] ignore_errors = true diff --git a/src/poetry/utils/appdirs.py b/src/poetry/utils/appdirs.py index ca6bb9a432d..2d2f3f21985 100644 --- a/src/poetry/utils/appdirs.py +++ b/src/poetry/utils/appdirs.py @@ -7,17 +7,11 @@ import os import sys -from typing import TYPE_CHECKING - - -if TYPE_CHECKING: - from pathlib import Path - WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt") -def expanduser(path: str | Path) -> str: +def expanduser(path: str) -> str: """ Expand ~ and ~user constructions. @@ -214,14 +208,15 @@ def _get_win_folder_with_ctypes(csidl_name: str) -> str: }[csidl_name] buf = ctypes.create_unicode_buffer(1024) - ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + windll = ctypes.windll # type: ignore[attr-defined] + windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) # Downgrade to short path name if have highbit chars. See # . has_high_char = any(ord(c) > 255 for c in buf) if has_high_char: buf2 = ctypes.create_unicode_buffer(1024) - if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): buf = buf2 return buf.value diff --git a/src/poetry/utils/authenticator.py b/src/poetry/utils/authenticator.py index 6c7758a1503..eb25805e855 100644 --- a/src/poetry/utils/authenticator.py +++ b/src/poetry/utils/authenticator.py @@ -33,9 +33,9 @@ class Authenticator: def __init__(self, config: Config, io: IO | None = None) -> None: self._config = config self._io = io - self._session = None - self._credentials = {} - self._certs = {} + self._session: requests.Session | None = None + self._credentials: dict[str, tuple[str, str]] = {} + self._certs: dict[str, dict[str, Path | None]] = {} self._password_manager = PasswordManager(self._config) def _log(self, message: str, level: str = "debug") -> None: @@ -118,7 +118,9 @@ def get_credentials_for_url(self, url: str) -> tuple[str | None, str | None]: netloc = parsed_url.netloc - credentials = self._credentials.get(netloc, (None, None)) + credentials: tuple[str | None, str | None] = self._credentials.get( + netloc, (None, None) + ) if credentials == (None, None): if "@" not in netloc: @@ -131,25 +133,27 @@ def get_credentials_for_url(self, url: str) -> tuple[str | None, str | None]: # Split from the left because that's how urllib.parse.urlsplit() # behaves if more than one : is present (which again can be checked # using the password attribute of the return value) - credentials = auth.split(":", 1) if ":" in auth else (auth, None) - credentials = tuple( - None if x is None else urllib.parse.unquote(x) for x in credentials + user, password = auth.split(":", 1) if ":" in auth else (auth, "") + credentials = ( + urllib.parse.unquote(user), + urllib.parse.unquote(password), ) - if credentials[0] is not None or credentials[1] is not None: + if any(credential is not None for credential in credentials): credentials = (credentials[0] or "", credentials[1] or "") - self._credentials[netloc] = credentials - return credentials[0], credentials[1] + return credentials - def get_pypi_token(self, name: str) -> str: + def get_pypi_token(self, name: str) -> str | None: return self._password_manager.get_pypi_token(name) - def get_http_auth(self, name: str) -> dict[str, str] | None: + def get_http_auth(self, name: str) -> dict[str, str | None] | None: return self._get_http_auth(name, None) - def _get_http_auth(self, name: str, netloc: str | None) -> dict[str, str] | None: + def _get_http_auth( + self, name: str, netloc: str | None + ) -> dict[str, str | None] | None: if name == "pypi": url = "https://upload.pypi.org/legacy/" else: @@ -161,15 +165,18 @@ def _get_http_auth(self, name: str, netloc: str | None) -> dict[str, str] | None if netloc is None or netloc == parsed_url.netloc: auth = self._password_manager.get_http_auth(name) + auth = auth or {} - if auth is None or auth["password"] is None: - username = auth["username"] if auth else None + if auth.get("password") is None: + username = auth.get("username") auth = self._get_credentials_for_netloc_from_keyring( url, parsed_url.netloc, username ) return auth + return None + def _get_credentials_for_netloc(self, netloc: str) -> tuple[str | None, str | None]: for repository_name, _ in self._get_repository_netlocs(): auth = self._get_http_auth(repository_name, netloc) @@ -177,7 +184,7 @@ def _get_credentials_for_netloc(self, netloc: str) -> tuple[str | None, str | No if auth is None: continue - return auth["username"], auth["password"] + return auth.get("username"), auth.get("password") return None, None @@ -199,7 +206,7 @@ def _get_repository_netlocs(self) -> Iterator[tuple[str, str]]: def _get_credentials_for_netloc_from_keyring( self, url: str, netloc: str, username: str | None - ) -> dict[str, str] | None: + ) -> dict[str, str | None] | None: import keyring cred = keyring.get_credential(url, username) @@ -225,7 +232,7 @@ def _get_credentials_for_netloc_from_keyring( return None def _get_certs_for_netloc_from_config(self, netloc: str) -> dict[str, Path | None]: - certs = {"cert": None, "verify": None} + certs: dict[str, Path | None] = {"cert": None, "verify": None} for repository_name, repository_netloc in self._get_repository_netlocs(): if netloc == repository_netloc: diff --git a/src/poetry/utils/password_manager.py b/src/poetry/utils/password_manager.py index 2d45a45b1b9..b525bdbae45 100644 --- a/src/poetry/utils/password_manager.py +++ b/src/poetry/utils/password_manager.py @@ -152,7 +152,7 @@ def delete_pypi_token(self, name: str) -> None: self.keyring.delete_password(name, "__token__") - def get_http_auth(self, name: str) -> dict[str, str] | None: + def get_http_auth(self, name: str) -> dict[str, str | None] | None: auth = self._config.get(f"http-basic.{name}") if not auth: username = self._config.get(f"http-basic.{name}.username") @@ -181,10 +181,14 @@ def set_http_password(self, name: str, username: str, password: str) -> None: def delete_http_password(self, name: str) -> None: auth = self.get_http_auth(name) - if not auth or "username" not in auth: + if not auth: + return + + username = auth.get("username") + if username is None: return with suppress(KeyRingError): - self.keyring.delete_password(name, auth["username"]) + self.keyring.delete_password(name, username) self._config.auth_config_source.remove_property(f"http-basic.{name}") diff --git a/src/poetry/utils/setup_reader.py b/src/poetry/utils/setup_reader.py index baac42c12ff..1bc84450c63 100644 --- a/src/poetry/utils/setup_reader.py +++ b/src/poetry/utils/setup_reader.py @@ -5,7 +5,6 @@ from configparser import ConfigParser from pathlib import Path from typing import Any -from typing import Iterable from poetry.core.semver.version import Version @@ -15,7 +14,7 @@ class SetupReader: Class that reads a setup.py file without executing it. """ - DEFAULT = { + DEFAULT: dict[str, Any] = { "name": None, "version": None, "install_requires": [], @@ -52,26 +51,27 @@ def read_setup_py(self, filepath: str | Path) -> dict[str, list | dict]: with filepath.open(encoding="utf-8") as f: content = f.read() - result = {} + result: dict[str, Any] = {} body = ast.parse(content).body - setup_call, body = self._find_setup_call(body) - if not setup_call: + setup_call = self._find_setup_call(body) + if setup_call is None: return self.DEFAULT # Inspecting keyword arguments - result["name"] = self._find_single_string(setup_call, body, "name") - result["version"] = self._find_single_string(setup_call, body, "version") - result["install_requires"] = self._find_install_requires(setup_call, body) - result["extras_require"] = self._find_extras_require(setup_call, body) + call, body = setup_call + result["name"] = self._find_single_string(call, body, "name") + result["version"] = self._find_single_string(call, body, "version") + result["install_requires"] = self._find_install_requires(call, body) + result["extras_require"] = self._find_extras_require(call, body) result["python_requires"] = self._find_single_string( - setup_call, body, "python_requires" + call, body, "python_requires" ) return result - def read_setup_cfg(self, filepath: str | Path) -> dict[str, list | dict]: + def read_setup_cfg(self, filepath: str | Path) -> dict[str, Any]: parser = ConfigParser() parser.read(str(filepath)) @@ -85,7 +85,7 @@ def read_setup_cfg(self, filepath: str | Path) -> dict[str, list | dict]: version = Version.parse(parser.get("metadata", "version")).text install_requires = [] - extras_require = {} + extras_require: dict[str, list[str]] = {} python_requires = None if parser.has_section("options"): if parser.has_option("options", "install_requires"): @@ -119,9 +119,9 @@ def read_setup_cfg(self, filepath: str | Path) -> dict[str, list | dict]: } def _find_setup_call( - self, elements: list[Any] - ) -> tuple[ast.Call | None, list[Any] | None]: - funcdefs = [] + self, elements: list[ast.stmt] + ) -> tuple[ast.Call, list[ast.stmt]] | None: + funcdefs: list[ast.stmt] = [] for i, element in enumerate(elements): if isinstance(element, ast.If) and i == len(elements) - 1: # Checking if the last element is an if statement @@ -138,11 +138,13 @@ def _find_setup_call( if left.id != "__name__": continue - setup_call, body = self._find_sub_setup_call([element]) - if not setup_call: + setup_call = self._find_sub_setup_call([element]) + if setup_call is None: continue - return setup_call, body + elements + call, body = setup_call + return call, body + elements + if not isinstance(element, ast.Expr): if isinstance(element, ast.FunctionDef): funcdefs.append(element) @@ -156,8 +158,7 @@ def _find_setup_call( func = value.func if not (isinstance(func, ast.Name) and func.id == "setup") and not ( isinstance(func, ast.Attribute) - and hasattr(func.value, "id") - and func.value.id == "setuptools" + and getattr(func.value, "id", None) == "setuptools" and func.attr == "setup" ): continue @@ -168,24 +169,24 @@ def _find_setup_call( return self._find_sub_setup_call(funcdefs) def _find_sub_setup_call( - self, elements: list[Any] - ) -> tuple[ast.Call | None, list[Any] | None]: + self, elements: list[ast.stmt] + ) -> tuple[ast.Call, list[ast.stmt]] | None: for element in elements: if not isinstance(element, (ast.FunctionDef, ast.If)): continue setup_call = self._find_setup_call(element.body) - if setup_call != (None, None): - setup_call, body = setup_call + if setup_call is not None: + sub_call, body = setup_call body = elements + body - return setup_call, body + return sub_call, body - return None, None + return None - def _find_install_requires(self, call: ast.Call, body: Iterable[Any]) -> list[str]: - install_requires = [] + def _find_install_requires(self, call: ast.Call, body: list[ast.stmt]) -> list[str]: + install_requires: list[str] = [] value = self._find_in_call(call, "install_requires") if value is None: # Trying to find in kwargs @@ -214,20 +215,22 @@ def _find_install_requires(self, call: ast.Call, body: Iterable[Any]) -> list[st if isinstance(value, ast.List): for el in value.elts: - install_requires.append(el.s) + if isinstance(el, ast.Str): + install_requires.append(el.s) elif isinstance(value, ast.Name): variable = self._find_variable_in_body(body, value.id) if variable is not None and isinstance(variable, ast.List): for el in variable.elts: - install_requires.append(el.s) + if isinstance(el, ast.Str): + install_requires.append(el.s) return install_requires def _find_extras_require( - self, call: ast.Call, body: Iterable[Any] - ) -> dict[str, list]: - extras_require = {} + self, call: ast.Call, body: list[ast.stmt] + ) -> dict[str, list[str]]: + extras_require: dict[str, list[str]] = {} value = self._find_in_call(call, "extras_require") if value is None: # Trying to find in kwargs @@ -255,12 +258,18 @@ def _find_extras_require( return extras_require if isinstance(value, ast.Dict): + val: ast.expr | None for key, val in zip(value.keys, value.values): + if not isinstance(key, ast.Str): + continue + if isinstance(val, ast.Name): val = self._find_variable_in_body(body, val.id) if isinstance(val, ast.List): - extras_require[key.s] = [e.s for e in val.elts] + extras_require[key.s] = [ + e.s for e in val.elts if isinstance(e, ast.Str) + ] elif isinstance(value, ast.Name): variable = self._find_variable_in_body(body, value.id) @@ -268,16 +277,21 @@ def _find_extras_require( return extras_require for key, val in zip(variable.keys, variable.values): + if not isinstance(key, ast.Str): + continue + if isinstance(val, ast.Name): val = self._find_variable_in_body(body, val.id) if isinstance(val, ast.List): - extras_require[key.s] = [e.s for e in val.elts] + extras_require[key.s] = [ + e.s for e in val.elts if isinstance(e, ast.Str) + ] return extras_require def _find_single_string( - self, call: ast.Call, body: list[Any], name: str + self, call: ast.Call, body: list[ast.stmt], name: str ) -> str | None: value = self._find_in_call(call, name) if value is None: @@ -313,6 +327,8 @@ def _find_single_string( if variable is not None and isinstance(variable, ast.Str): return variable.s + return None + def _find_in_call(self, call: ast.Call, name: str) -> Any | None: for keyword in call.keywords: if keyword.arg == name: @@ -327,12 +343,10 @@ def _find_call_kwargs(self, call: ast.Call) -> Any | None: return kwargs - def _find_variable_in_body(self, body: Iterable[Any], name: str) -> Any | None: - found = None + def _find_variable_in_body( + self, body: list[ast.stmt], name: str + ) -> ast.expr | None: for elem in body: - if found: - break - if not isinstance(elem, ast.Assign): continue @@ -343,8 +357,11 @@ def _find_variable_in_body(self, body: Iterable[Any], name: str) -> Any | None: if target.id == name: return elem.value - def _find_in_dict(self, dict_: ast.Dict | ast.Call, name: str) -> Any | None: + return None + + def _find_in_dict(self, dict_: ast.Dict, name: str) -> ast.expr | None: for key, val in zip(dict_.keys, dict_.values): if isinstance(key, ast.Str) and key.s == name: return val + return None