Skip to content

Commit

Permalink
Misc fixes (#8253)
Browse files Browse the repository at this point in the history
* typo
* unnecessary special case
* tighten typechecking
* mypy 1.4
* handle ast.Str deprecation
* keyring 24.0.0
* use requests.utils.atomic_open directly

poetry is already using requests, and the docstring for requests.utils
indicates that "this module provides utility functions ... that are also
useful for external consumption".

ie there's no reason copy the code, just use it.
  • Loading branch information
dimbleby authored Aug 4, 2023
1 parent 9df40a4 commit 88096ac
Show file tree
Hide file tree
Showing 11 changed files with 72 additions and 86 deletions.
70 changes: 35 additions & 35 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ importlib-metadata = { version = ">=4.4", python = "<3.10" }
installer = "^0.7.0"
# jsonschema 4.18 uses Rust-based libraries which causes issues when building from source
jsonschema = ">=4.10.0,<4.18.0"
keyring = "^23.9.0"
keyring = "^24.0.0"
# packaging uses calver, so version is unclamped
packaging = ">=20.4"
pexpect = "^4.7.0"
Expand Down
2 changes: 1 addition & 1 deletion src/poetry/console/commands/group_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def project_with_activated_groups_only(self) -> ProjectPackage:

def _validate_group_options(self, group_options: dict[str, set[str]]) -> None:
"""
Raises en error if it detects that a group is not part of pyproject.toml
Raises an error if it detects that a group is not part of pyproject.toml
"""
invalid_options = defaultdict(set)
for opt, groups in group_options.items():
Expand Down
2 changes: 1 addition & 1 deletion src/poetry/installation/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from cleo.io.null_io import NullIO
from poetry.core.packages.utils.link import Link
from requests.utils import atomic_open

from poetry.installation.chef import Chef
from poetry.installation.chef import ChefBuildError
Expand All @@ -29,7 +30,6 @@
from poetry.utils.authenticator import Authenticator
from poetry.utils.cache import ArtifactCache
from poetry.utils.env import EnvCommandError
from poetry.utils.helpers import atomic_open
from poetry.utils.helpers import get_file_hash
from poetry.utils.helpers import pluralize
from poetry.utils.helpers import remove_directory
Expand Down
5 changes: 1 addition & 4 deletions src/poetry/json/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ def validate_object(obj: dict[str, Any]) -> list[str]:
schema = json.loads(schema_file.read_text(encoding="utf-8"))

validator = jsonschema.Draft7Validator(schema)
validation_errors = sorted(
validator.iter_errors(obj),
key=lambda e: e.path, # type: ignore[no-any-return]
)
validation_errors = sorted(validator.iter_errors(obj), key=lambda e: e.path)

errors = []

Expand Down
5 changes: 1 addition & 4 deletions src/poetry/mixology/version_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,10 +474,7 @@ def _get_min(dependency: Dependency) -> tuple[bool, int, int]:
preference = Preference.DEFAULT
return is_specific_marker, preference, num_packages

if len(unsatisfied) == 1:
dependency = unsatisfied[0]
else:
dependency = min(*unsatisfied, key=_get_min)
dependency = min(unsatisfied, key=_get_min)

locked = self._provider.get_locked(dependency)
if locked is None:
Expand Down
5 changes: 1 addition & 4 deletions src/poetry/packages/locker.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,10 +436,7 @@ def _dump_package(self, package: Package) -> dict[str, Any]:
"description": package.description or "",
"optional": package.optional,
"python-versions": package.python_versions,
"files": sorted(
package.files,
key=lambda x: x["file"], # type: ignore[no-any-return]
),
"files": sorted(package.files, key=lambda x: x["file"]),
}

if dependencies:
Expand Down
2 changes: 1 addition & 1 deletion src/poetry/repositories/pypi_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def _find_packages(

return [Package(name, version, yanked=yanked) for version, yanked in versions]

def _get_package_info(self, name: str) -> dict[str, Any]:
def _get_package_info(self, name: NormalizedName) -> dict[str, Any]:
headers = {"Accept": "application/vnd.pypi.simple.v1+json"}
info = self._get(f"simple/{name}/", headers=headers)
if info is None:
Expand Down
21 changes: 2 additions & 19 deletions src/poetry/utils/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
from typing import TYPE_CHECKING
from typing import Any

from requests.utils import atomic_open

from poetry.utils.constants import REQUESTS_TIMEOUT


if TYPE_CHECKING:
from collections.abc import Callable
from collections.abc import Iterator
from io import BufferedWriter

from poetry.core.packages.package import Package
from requests import Session
Expand All @@ -38,24 +39,6 @@ def directory(path: Path) -> Iterator[Path]:
os.chdir(cwd)


@contextmanager
def atomic_open(filename: str | os.PathLike[str]) -> Iterator[BufferedWriter]:
"""
write a file to the disk in an atomic fashion
Taken from requests.utils
(https://github.com/psf/requests/blob/7104ad4b135daab0ed19d8e41bd469874702342b/requests/utils.py#L296)
"""
tmp_descriptor, tmp_name = tempfile.mkstemp(dir=os.path.dirname(filename))
try:
with os.fdopen(tmp_descriptor, "wb") as tmp_handler:
yield tmp_handler
os.replace(tmp_name, filename)
except BaseException:
os.remove(tmp_name)
raise


def _on_rm_error(func: Callable[[str], None], path: str, exc_info: Exception) -> None:
if not os.path.exists(path):
return
Expand Down
42 changes: 27 additions & 15 deletions src/poetry/utils/setup_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,15 @@ def _find_install_requires(self, call: ast.Call, body: list[ast.stmt]) -> list[s

if isinstance(value, ast.List):
for el in value.elts:
if isinstance(el, ast.Str):
install_requires.append(el.s)
if isinstance(el, ast.Constant) and isinstance(el.value, str):
install_requires.append(el.value)
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:
if isinstance(el, ast.Str):
install_requires.append(el.s)
if isinstance(el, ast.Constant) and isinstance(el.value, str):
install_requires.append(el.value)

return install_requires

Expand Down Expand Up @@ -259,15 +259,17 @@ def _find_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):
if not isinstance(key, ast.Constant) or not isinstance(key.value, 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 if isinstance(e, ast.Str)
extras_require[key.value] = [
e.value
for e in val.elts
if isinstance(e, ast.Constant) and isinstance(e.value, str)
]
elif isinstance(value, ast.Name):
variable = self._find_variable_in_body(body, value.id)
Expand All @@ -276,15 +278,17 @@ def _find_extras_require(
return extras_require

for key, val in zip(variable.keys, variable.values):
if not isinstance(key, ast.Str):
if not isinstance(key, ast.Constant) or not isinstance(key.value, 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 if isinstance(e, ast.Str)
extras_require[key.value] = [
e.value
for e in val.elts
if isinstance(e, ast.Constant) and isinstance(e.value, str)
]

return extras_require
Expand Down Expand Up @@ -318,13 +322,17 @@ def _find_single_string(
if value is None:
return None

if isinstance(value, ast.Str):
return value.s
if isinstance(value, ast.Constant) and isinstance(value.value, str):
return value.value
elif isinstance(value, ast.Name):
variable = self._find_variable_in_body(body, value.id)

if variable is not None and isinstance(variable, ast.Str):
return variable.s
if (
variable is not None
and isinstance(variable, ast.Constant)
and isinstance(variable.value, str)
):
return variable.value

return None

Expand Down Expand Up @@ -360,7 +368,11 @@ def _find_variable_in_body(

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:
if (
isinstance(key, ast.Constant)
and isinstance(key.value, str)
and key.value == name
):
return val

return None
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def dummy_keyring() -> DummyBackend:
def with_simple_keyring(dummy_keyring: DummyBackend) -> None:
import keyring

keyring.set_keyring(dummy_keyring) # type: ignore[no-untyped-call]
keyring.set_keyring(dummy_keyring)


@pytest.fixture()
Expand Down

0 comments on commit 88096ac

Please # to comment.