Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.password_manager',
'poetry.utils.setup_reader',
]
ignore_errors = true
Expand Down
12 changes: 3 additions & 9 deletions src/poetry/utils/appdirs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -214,14 +208,14 @@ 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)
ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) # type: ignore[attr-defined] # noqa: E501

# Downgrade to short path name if have highbit chars. See
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
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 ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): # type: ignore[attr-defined] # noqa: E501
buf = buf2

return buf.value
Expand Down
23 changes: 14 additions & 9 deletions src/poetry/utils/authenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from cleo.io.io import IO

from poetry.config.config import Config
from poetry.utils.types import Auth


logger = logging.getLogger()
Expand All @@ -28,8 +29,8 @@ class Authenticator:
def __init__(self, config: Config, io: IO | None = None) -> None:
self._config = config
self._io = io
self._session = None
self._credentials = {}
self._session: requests.Session | None = None
self._credentials: dict[str, tuple[str | None, str | None]] = {}
self._password_manager = PasswordManager(self._config)

def _log(self, message: str, level: str = "debug") -> None:
Expand Down Expand Up @@ -69,7 +70,7 @@ def request(self, method: str, url: str, **kwargs: Any) -> requests.Response:
)

# Send the request.
send_kwargs = {
send_kwargs: Any = {
"timeout": kwargs.get("timeout"),
"allow_redirects": kwargs.get("allow_redirects", True),
}
Expand Down Expand Up @@ -117,11 +118,14 @@ 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
credentials_tmp = auth.split(":", 1) if ":" in auth else (auth, None)
credentials_tmp = tuple(
None if x is None else urllib.parse.unquote(x)
for x in credentials_tmp
)

credentials = credentials_tmp[0], credentials_tmp[1]

if credentials[0] is not None or credentials[1] is not None:
credentials = (credentials[0] or "", credentials[1] or "")

Expand All @@ -132,10 +136,10 @@ def get_credentials_for_url(self, url: str) -> tuple[str | None, str | None]:
def get_pypi_token(self, name: str) -> str:
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) -> Auth | 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) -> Auth | None:
if name == "pypi":
url = "https://upload.pypi.org/legacy/"
else:
Expand All @@ -155,6 +159,7 @@ def _get_http_auth(self, name: str, netloc: str | None) -> dict[str, str] | None
)

return auth
return None

def _get_credentials_for_netloc(self, netloc: str) -> tuple[str | None, str | None]:
for repository_name in self._config.get("repositories", []):
Expand All @@ -169,7 +174,7 @@ def _get_credentials_for_netloc(self, netloc: str) -> tuple[str | None, str | No

def _get_credentials_for_netloc_from_keyring(
self, url: str, netloc: str, username: str | None
) -> dict[str, str] | None:
) -> Auth | None:
import keyring

cred = keyring.get_credential(url, username)
Expand Down
21 changes: 11 additions & 10 deletions src/poetry/utils/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

from poetry.core.packages.utils.utils import path_to_url

from poetry.repositories.remote_repository import RemoteRepository
from poetry.utils._compat import decode


if TYPE_CHECKING:
from pathlib import Path

from cleo.io.io import IO

from poetry.poetry import Poetry
from poetry.utils.types import Writer


class Exporter:
Expand All @@ -36,7 +36,7 @@ def export(
self,
fmt: str,
cwd: Path,
output: IO | str,
output: Writer | str,
with_hashes: bool = True,
dev: bool = False,
extras: bool | Sequence[str] | None = None,
Expand All @@ -59,7 +59,7 @@ def export(
def _export_requirements_txt(
self,
cwd: Path,
output: IO | str,
output: Writer | str,
with_hashes: bool = True,
dev: bool = False,
extras: bool | Sequence[str] | None = None,
Expand Down Expand Up @@ -143,14 +143,15 @@ def _export_requirements_txt(
repositories = [
r
for r in self._poetry.pool.repositories
if r.url == index.rstrip("/")
if (isinstance(r, RemoteRepository) and r.url == index.rstrip("/"))
]
if not repositories:
continue
repository = repositories[0]
if (
self._poetry.pool.has_default()
and repository is self._poetry.pool.repositories[0]
and isinstance(repository, RemoteRepository)
):
url = (
repository.authenticated_url
Expand All @@ -170,13 +171,13 @@ def _export_requirements_txt(

content = indexes_header + "\n" + content

self._output(content, cwd, output)
self._write(content, cwd, output)

def _output(self, content: str, cwd: Path, output: IO | str) -> None:
def _write(self, content: str, cwd: Path, output: Writer | str) -> None:
decoded = decode(content)
try:
output.write(decoded)
except AttributeError:
if isinstance(output, str):
filepath = cwd / output
with filepath.open("w", encoding="utf-8") as f:
f.write(decoded)
return
output.write(decoded)
21 changes: 12 additions & 9 deletions src/poetry/utils/password_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

if TYPE_CHECKING:
from poetry.config.config import Config
from poetry.utils.types import Auth


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -121,10 +123,10 @@ def _check(self) -> None:
class PasswordManager:
def __init__(self, config: Config) -> None:
self._config = config
self._keyring = None
self._keyring: KeyRing | None = None

@property
def keyring(self) -> KeyRing | None:
def keyring(self) -> KeyRing:
if self._keyring is None:
self._keyring = KeyRing("poetry-repository")
if not self._keyring.is_available():
Expand All @@ -141,18 +143,19 @@ def set_pypi_token(self, name: str, token: str) -> None:
self.keyring.set_password(name, "__token__", token)

def get_pypi_token(self, name: str) -> str:
if not self.keyring.is_available():
return self._config.get(f"pypi-token.{name}")

return self.keyring.get_password(name, "__token__")
token = self.keyring.get_password(name, "__token__")
if token is not None:
return token
return self._config.get(f"pypi-token.{name}")

def delete_pypi_token(self, name: str) -> None:
if not self.keyring.is_available():
return self._config.auth_config_source.remove_property(f"pypi-token.{name}")
self._config.auth_config_source.remove_property(f"pypi-token.{name}")
return

self.keyring.delete_password(name, "__token__")

def get_http_auth(self, name: str) -> dict[str, str] | None:
def get_http_auth(self, name: str) -> Auth | None:
auth = self._config.get(f"http-basic.{name}")
if not auth:
username = self._config.get(f"http-basic.{name}.username")
Expand Down Expand Up @@ -181,7 +184,7 @@ 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 auth is None or "username" not in auth:
return

with suppress(KeyRingError):
Expand Down
4 changes: 3 additions & 1 deletion src/poetry/utils/setup_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ 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:
Expand Down Expand Up @@ -342,8 +343,9 @@ def _find_variable_in_body(self, body: Iterable[Any], name: str) -> Any | None:

if target.id == name:
return elem.value
return None

def _find_in_dict(self, dict_: ast.Dict | ast.Call, name: str) -> Any | None:
def _find_in_dict(self, dict_: ast.Dict, name: str) -> Any | None:
for key, val in zip(dict_.keys, dict_.values):
if isinstance(key, ast.Str) and key.s == name:
return val
Expand Down
14 changes: 14 additions & 0 deletions src/poetry/utils/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from __future__ import annotations

from typing import Protocol
from typing import TypedDict


class Auth(TypedDict):
username: str
password: str | None


class Writer(Protocol):
def write(self, text: str) -> None:
pass