Skip to content

Commit c2c6a97

Browse files
committed
draft: use poetry native dep management for PEP517
1 parent 9cda043 commit c2c6a97

File tree

8 files changed

+312
-214
lines changed

8 files changed

+312
-214
lines changed

src/poetry/inspection/info.py

+11-42
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import functools
55
import glob
66
import logging
7+
import tempfile
78

89
from pathlib import Path
910
from typing import TYPE_CHECKING
@@ -13,7 +14,7 @@
1314

1415
import pkginfo
1516

16-
from poetry.core.factory import Factory
17+
from build import BuildBackendException
1718
from poetry.core.packages.dependency import Dependency
1819
from poetry.core.packages.package import Package
1920
from poetry.core.pyproject.toml import PyProjectTOML
@@ -22,9 +23,9 @@
2223
from poetry.core.version.markers import InvalidMarker
2324
from poetry.core.version.requirements import InvalidRequirement
2425

25-
from poetry.utils.env import EnvCommandError
26-
from poetry.utils.env import ephemeral_environment
26+
from poetry.factory import Factory
2727
from poetry.utils.helpers import extractall
28+
from poetry.utils.pep517_build import pep517_builder
2829
from poetry.utils.setup_reader import SetupReader
2930

3031

@@ -38,25 +39,6 @@
3839

3940
logger = logging.getLogger(__name__)
4041

41-
PEP517_META_BUILD = """\
42-
import build
43-
import build.env
44-
import pyproject_hooks
45-
46-
source = '{source}'
47-
dest = '{dest}'
48-
49-
with build.env.DefaultIsolatedEnv() as env:
50-
builder = build.ProjectBuilder.from_isolated_env(
51-
env, source, runner=pyproject_hooks.quiet_subprocess_runner
52-
)
53-
env.install(builder.build_system_requires)
54-
env.install(builder.get_requires_for_build('wheel'))
55-
builder.metadata_path(dest)
56-
"""
57-
58-
PEP517_META_BUILD_DEPS = ["build==1.1.1", "pyproject_hooks==1.0.0"]
59-
6042

6143
class PackageInfoError(ValueError):
6244
def __init__(self, path: Path, *reasons: BaseException | str) -> None:
@@ -577,28 +559,15 @@ def get_pep517_metadata(path: Path) -> PackageInfo:
577559
if all(x is not None for x in (info.version, info.name, info.requires_dist)):
578560
return info
579561

580-
with ephemeral_environment(
581-
flags={"no-pip": False, "no-setuptools": True, "no-wheel": True}
582-
) as venv:
583-
# TODO: cache PEP 517 build environment corresponding to each project venv
584-
dest_dir = venv.path.parent / "dist"
585-
dest_dir.mkdir()
562+
with tempfile.TemporaryDirectory() as dist:
563+
try:
564+
dest = Path(dist)
586565

587-
pep517_meta_build_script = PEP517_META_BUILD.format(
588-
source=path.as_posix(), dest=dest_dir.as_posix()
589-
)
566+
with pep517_builder(path, "wheel") as builder:
567+
builder.metadata_path(dest)
590568

591-
try:
592-
venv.run_pip(
593-
"install",
594-
"--disable-pip-version-check",
595-
"--ignore-installed",
596-
"--no-input",
597-
*PEP517_META_BUILD_DEPS,
598-
)
599-
venv.run_python_script(pep517_meta_build_script)
600-
info = PackageInfo.from_metadata_directory(dest_dir)
601-
except EnvCommandError as e:
569+
info = PackageInfo.from_metadata_directory(dest)
570+
except BuildBackendException as e:
602571
logger.debug("PEP517 build failed: %s", e)
603572
raise PackageInfoError(path, e, "PEP517 build failed")
604573

src/poetry/installation/chef.py

+28-117
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,20 @@
11
from __future__ import annotations
22

3-
import os
43
import tempfile
54

6-
from contextlib import redirect_stdout
7-
from io import StringIO
85
from pathlib import Path
96
from typing import TYPE_CHECKING
107

118
from build import BuildBackendException
12-
from build import ProjectBuilder
13-
from build.env import IsolatedEnv as BaseIsolatedEnv
149
from poetry.core.utils.helpers import temporary_directory
15-
from pyproject_hooks import quiet_subprocess_runner # type: ignore[import-untyped]
1610

1711
from poetry.utils._compat import decode
18-
from poetry.utils.env import ephemeral_environment
1912
from poetry.utils.helpers import extractall
13+
from poetry.utils.pep517_build import PEP517BuildError
14+
from poetry.utils.pep517_build import pep517_builder
2015

2116

2217
if TYPE_CHECKING:
23-
from collections.abc import Collection
24-
2518
from poetry.repositories import RepositoryPool
2619
from poetry.utils.cache import ArtifactCache
2720
from poetry.utils.env import Env
@@ -30,78 +23,6 @@
3023
class ChefError(Exception): ...
3124

3225

33-
class ChefBuildError(ChefError): ...
34-
35-
36-
class ChefInstallError(ChefError):
37-
def __init__(self, requirements: Collection[str], output: str, error: str) -> None:
38-
message = "\n\n".join(
39-
(
40-
f"Failed to install {', '.join(requirements)}.",
41-
f"Output:\n{output}",
42-
f"Error:\n{error}",
43-
)
44-
)
45-
super().__init__(message)
46-
self._requirements = requirements
47-
48-
@property
49-
def requirements(self) -> Collection[str]:
50-
return self._requirements
51-
52-
53-
class IsolatedEnv(BaseIsolatedEnv):
54-
def __init__(self, env: Env, pool: RepositoryPool) -> None:
55-
self._env = env
56-
self._pool = pool
57-
58-
@property
59-
def python_executable(self) -> str:
60-
return str(self._env.python)
61-
62-
def make_extra_environ(self) -> dict[str, str]:
63-
path = os.environ.get("PATH")
64-
scripts_dir = str(self._env._bin_dir)
65-
return {
66-
"PATH": (
67-
os.pathsep.join([scripts_dir, path])
68-
if path is not None
69-
else scripts_dir
70-
)
71-
}
72-
73-
def install(self, requirements: Collection[str]) -> None:
74-
from cleo.io.buffered_io import BufferedIO
75-
from poetry.core.packages.dependency import Dependency
76-
from poetry.core.packages.project_package import ProjectPackage
77-
78-
from poetry.config.config import Config
79-
from poetry.installation.installer import Installer
80-
from poetry.packages.locker import Locker
81-
from poetry.repositories.installed_repository import InstalledRepository
82-
83-
# We build Poetry dependencies from the requirements
84-
package = ProjectPackage("__root__", "0.0.0")
85-
package.python_versions = ".".join(str(v) for v in self._env.version_info[:3])
86-
for requirement in requirements:
87-
dependency = Dependency.create_from_pep_508(requirement)
88-
package.add_dependency(dependency)
89-
90-
io = BufferedIO()
91-
installer = Installer(
92-
io,
93-
self._env,
94-
package,
95-
Locker(self._env.path.joinpath("poetry.lock"), {}),
96-
self._pool,
97-
Config.create(),
98-
InstalledRepository.load(self._env),
99-
)
100-
installer.update(True)
101-
if installer.run() != 0:
102-
raise ChefInstallError(requirements, io.fetch_output(), io.fetch_error())
103-
104-
10526
class Chef:
10627
def __init__(
10728
self, artifact_cache: ArtifactCache, env: Env, pool: RepositoryPool
@@ -127,46 +48,36 @@ def _prepare(
12748
) -> Path:
12849
from subprocess import CalledProcessError
12950

130-
with ephemeral_environment(
131-
self._env.python,
132-
flags={"no-pip": True, "no-setuptools": True, "no-wheel": True},
133-
) as venv:
134-
env = IsolatedEnv(venv, self._pool)
135-
builder = ProjectBuilder.from_isolated_env(
136-
env, directory, runner=quiet_subprocess_runner
137-
)
138-
env.install(builder.build_system_requires)
139-
140-
stdout = StringIO()
141-
error: Exception | None = None
142-
try:
143-
with redirect_stdout(stdout):
144-
dist_format = "wheel" if not editable else "editable"
145-
env.install(
146-
builder.build_system_requires
147-
| builder.get_requires_for_build(dist_format)
148-
)
149-
path = Path(
150-
builder.build(
151-
dist_format,
152-
destination.as_posix(),
153-
)
51+
distribution = "wheel" if not editable else "editable"
52+
error: Exception | None = None
53+
54+
try:
55+
with pep517_builder(
56+
source=directory,
57+
distribution=distribution,
58+
python_executable=self._env.python,
59+
pool=self._pool,
60+
) as builder:
61+
return Path(
62+
builder.build(
63+
distribution,
64+
destination.as_posix(),
15465
)
155-
except BuildBackendException as e:
156-
message_parts = [str(e)]
157-
if isinstance(e.exception, CalledProcessError):
158-
text = e.exception.stderr or e.exception.stdout
159-
if text is not None:
160-
message_parts.append(decode(text))
161-
else:
162-
message_parts.append(str(e.exception))
66+
)
67+
except BuildBackendException as e:
68+
message_parts = [str(e)]
16369

164-
error = ChefBuildError("\n\n".join(message_parts))
70+
if isinstance(e.exception, CalledProcessError):
71+
text = e.exception.stderr or e.exception.stdout
72+
if text is not None:
73+
message_parts.append(decode(text))
74+
else:
75+
message_parts.append(str(e.exception))
16576

166-
if error is not None:
167-
raise error from None
77+
error = PEP517BuildError("\n\n".join(message_parts))
16878

169-
return path
79+
if error is not None:
80+
raise error from None
17081

17182
def _prepare_sdist(self, archive: Path, destination: Path | None = None) -> Path:
17283
from poetry.core.packages.utils.link import Link

src/poetry/installation/executor.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
from poetry.core.packages.utils.link import Link
1919

2020
from poetry.installation.chef import Chef
21-
from poetry.installation.chef import ChefBuildError
22-
from poetry.installation.chef import ChefInstallError
2321
from poetry.installation.chooser import Chooser
2422
from poetry.installation.operations import Install
2523
from poetry.installation.operations import Uninstall
@@ -34,6 +32,8 @@
3432
from poetry.utils.helpers import get_highest_priority_hash_type
3533
from poetry.utils.helpers import pluralize
3634
from poetry.utils.helpers import remove_directory
35+
from poetry.utils.pep517_build import PEP517BuildError
36+
from poetry.utils.pep517_build import PEP517InstallError
3737
from poetry.utils.pip import pip_install
3838

3939

@@ -310,7 +310,7 @@ def _execute_operation(self, operation: Operation) -> None:
310310
trace = ExceptionTrace(e)
311311
trace.render(io)
312312
pkg = operation.package
313-
if isinstance(e, ChefBuildError):
313+
if isinstance(e, PEP517BuildError):
314314
pip_command = "pip wheel --no-cache-dir --use-pep517"
315315
if pkg.develop:
316316
requirement = pkg.source_url
@@ -328,7 +328,7 @@ def _execute_operation(self, operation: Operation) -> None:
328328
f" running '{pip_command} \"{requirement}\"'."
329329
"</info>"
330330
)
331-
elif isinstance(e, ChefInstallError):
331+
elif isinstance(e, PEP517InstallError):
332332
message = (
333333
"<error>"
334334
"Cannot install build-system.requires"

0 commit comments

Comments
 (0)