Skip to content

Commit

Permalink
Use the new wheel builder and installer
Browse files Browse the repository at this point in the history
  • Loading branch information
sdispater committed Aug 20, 2022
1 parent 771e40c commit 455e638
Show file tree
Hide file tree
Showing 14 changed files with 452 additions and 47 deletions.
8 changes: 6 additions & 2 deletions src/poetry/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ def validator(cls, policy: str) -> bool:

logger = logging.getLogger(__name__)


_default_config: Config | None = None


Expand All @@ -124,7 +123,11 @@ class Config:
"prefer-active-python": False,
"prompt": "{project_name}-py{python_version}",
},
"experimental": {"new-installer": True, "system-git-client": False},
"experimental": {
"new-installer": True,
"system-git-client": False,
"wheel-installer": True,
},
"installer": {"parallel": True, "max-workers": None, "no-binary": None},
}

Expand Down Expand Up @@ -267,6 +270,7 @@ def _get_normalizer(name: str) -> Callable[[str], Any]:
"virtualenvs.options.prefer-active-python",
"experimental.new-installer",
"experimental.system-git-client",
"experimental.wheel-installer",
"installer.parallel",
}:
return boolean_normalizer
Expand Down
3 changes: 2 additions & 1 deletion src/poetry/installation/chef.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def install(self, requirements: list[str]) -> None:
from poetry.core.packages.dependency import Dependency
from poetry.core.packages.project_package import ProjectPackage

from poetry.config.config import Config
from poetry.factory import Factory
from poetry.installation.installer import Installer
from poetry.packages.locker import NullLocker
Expand All @@ -74,7 +75,7 @@ def install(self, requirements: list[str]) -> None:
package,
NullLocker(self._env.path.joinpath("poetry.lock"), {}),
pool,
Factory.create_config(NullIO()),
Config.create(),
InstalledRepository.load(self._env),
)
installer.update(True)
Expand Down
86 changes: 76 additions & 10 deletions src/poetry/installation/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
from cleo.io.null_io import NullIO
from poetry.core.packages.file_dependency import FileDependency
from poetry.core.packages.utils.link import Link
from poetry.core.pyproject.toml import PyProjectTOML

from poetry.installation.chef import Chef
from poetry.installation.chooser import Chooser
from poetry.installation.operations import Install
from poetry.installation.operations import Uninstall
from poetry.installation.operations import Update
from poetry.installation.wheel_installer import WheelInstaller
from poetry.utils._compat import decode
from poetry.utils.authenticator import Authenticator
from poetry.utils.env import EnvCommandError
Expand Down Expand Up @@ -61,6 +61,8 @@ def __init__(
self._authenticator = Authenticator(config, self._io)
self._chef = Chef(config, self._env)
self._chooser = Chooser(pool, self._env, config)
self._wheel_installer = WheelInstaller(self._env)
self._use_wheel_installer = config.get("experimental.wheel-installer", True)

if parallel is None:
parallel = config.get("installer.parallel", True)
Expand All @@ -82,6 +84,11 @@ def __init__(
self._shutdown = False
self._hashes: dict[str, str] = {}

def set_chef(self, chef: Chef) -> Executor:
self._chef = chef

return self

@property
def installations_count(self) -> int:
return self._executed["install"]
Expand Down Expand Up @@ -453,19 +460,22 @@ def _execute_uninstall(self, operation: Uninstall) -> int:
message = f" <fg=blue;options=bold>•</> {op_msg}: <info>Removing...</info>"
self._write(operation, message)

return self._remove(operation)
return self._remove(operation.package)

def _install(self, operation: Install | Update) -> int:
package = operation.package
if package.source_type == "directory":
if not self._use_wheel_installer:
return self._install_directory_without_wheel_installer(operation)

return self._install_directory(operation)

if package.source_type == "git":
return self._install_git(operation)

archive: Path
if package.source_type == "file":
archive = self._prepare_file(operation)
archive = self._prepare_archive(operation)
elif package.source_type == "url":
assert package.source_url is not None
archive = self._download_link(operation, Link(package.source_url))
Expand All @@ -478,14 +488,18 @@ def _install(self, operation: Install | Update) -> int:
" <info>Installing...</info>"
)
self._write(operation, message)
return self.pip_install(archive, upgrade=operation.job_type == "update")

if not self._use_wheel_installer:
return self.pip_install(archive, upgrade=operation.job_type == "update")

self._wheel_installer.install(archive)

return 0

def _update(self, operation: Install | Update) -> int:
return self._install(operation)

def _remove(self, operation: Uninstall) -> int:
package = operation.package

def _remove(self, package: Package) -> int:
# If we have a VCS package, remove its source directory
if package.source_type == "git":
src_dir = self._env.path / "src" / package.name
Expand All @@ -500,7 +514,7 @@ def _remove(self, operation: Uninstall) -> int:

raise

def _prepare_file(self, operation: Install | Update) -> Path:
def _prepare_archive(self, operation: Install | Update) -> Path:
package = operation.package
operation_message = self.get_operation_message(operation)

Expand All @@ -515,9 +529,51 @@ def _prepare_file(self, operation: Install | Update) -> Path:
if not Path(package.source_url).is_absolute() and package.root_dir:
archive = package.root_dir / archive

return archive
return self._chef.prepare(archive)

def _install_directory(self, operation: Install | Update) -> int:
package = operation.package
operation_message = self.get_operation_message(operation)

message = (
f" <fg=blue;options=bold>•</> {operation_message}:"
" <info>Building...</info>"
)
self._write(operation, message)

assert package.source_url is not None
if package.root_dir:
req = package.root_dir / package.source_url
else:
req = Path(package.source_url).resolve(strict=False)

if package.source_subdirectory:
req /= package.source_subdirectory

if package.develop:
# Editable installations are currently not supported
# for PEP-517 build systems so we defer to pip.
# TODO: Remove this workaround once either PEP-660 or PEP-662 is accepted
return self.pip_install(req, editable=True)

archive = self._prepare_archive(operation)

try:
if operation.job_type == "update":
# Uninstall first
# TODO: Make an uninstaller and find a way to rollback in case
# the new package can't be installed
self._remove(operation.initial_package)

self._wheel_installer.install(archive)
finally:
archive.unlink()

def _install_directory_without_wheel_installer(
self, operation: Install | Update
) -> int:
from poetry.core.pyproject.toml import PyProjectTOML

from poetry.factory import Factory

package = operation.package
Expand Down Expand Up @@ -618,6 +674,7 @@ def _download_link(self, operation: Install | Update, link: Link) -> Path:
package = operation.package

archive: Path | None
output_dir = self._chef.get_cache_directory_for_link(link)
archive = self._chef.get_cached_archive_for_link(link)
if archive is None:
# No cached distributions was found, so we download and prepare it
Expand All @@ -633,7 +690,16 @@ def _download_link(self, operation: Install | Update, link: Link) -> Path:

raise

if package.files:
if archive.suffix != ".whl":
message = (
f" <fg=blue;options=bold>•</> {self.get_operation_message(operation)}:"
" <info>Preparing...</info>"
)
self._write(operation, message)

archive = self._chef.prepare(archive, output_dir=output_dir)

if package.files and archive.name in {f["file"] for f in package.files}:
archive_hash = self._validate_archive_hash(archive, package)

self._hashes[package.name] = archive_hash
Expand Down
16 changes: 16 additions & 0 deletions tests/console/commands/self/test_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,33 @@

from poetry.__version__ import __version__
from poetry.factory import Factory
from poetry.installation.executor import Executor
from poetry.installation.wheel_installer import WheelInstaller


if TYPE_CHECKING:
from cleo.testers.command_tester import CommandTester
from pytest_mock import MockerFixture

from tests.helpers import TestRepository
from tests.types import CommandTesterFactory

FIXTURES = Path(__file__).parent.joinpath("fixtures")


@pytest.fixture()
def setup(mocker: MockerFixture, fixture_dir: Path):
mocker.patch.object(
Executor,
"_download",
return_value=fixture_dir("distributions").joinpath(
"demo-0.1.2-py2.py3-none-any.whl"
),
)

mocker.patch.object(WheelInstaller, "install")


@pytest.fixture()
def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("self update")
Expand Down
3 changes: 3 additions & 0 deletions tests/console/commands/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def test_list_displays_default_value_if_not_set(
expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false
experimental.wheel-installer = true
installer.max-workers = null
installer.no-binary = null
installer.parallel = true
Expand Down Expand Up @@ -82,6 +83,7 @@ def test_list_displays_set_get_setting(
expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false
experimental.wheel-installer = true
installer.max-workers = null
installer.no-binary = null
installer.parallel = true
Expand Down Expand Up @@ -135,6 +137,7 @@ def test_list_displays_set_get_local_setting(
expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false
experimental.wheel-installer = true
installer.max-workers = null
installer.no-binary = null
installer.parallel = true
Expand Down
Binary file modified tests/fixtures/distributions/demo-0.1.0.tar.gz
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/fixtures/extended_with_no_setup/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ script = "build.py"
generate-setup-file = false

[build-system]
requires = ["poetry-core>=1.0.0"]
requires = ["poetry-core>=1.1.0rc1"]
build-backend = "poetry.core.masonry.api"
19 changes: 18 additions & 1 deletion tests/installation/test_chef.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
from packaging.tags import Tag
from poetry.core.packages.utils.link import Link

from poetry.factory import Factory
from poetry.installation.chef import Chef
from poetry.repositories import Pool
from poetry.utils.env import EnvManager
from poetry.utils.env import MockEnv
from tests.repositories.test_pypi_repository import MockRepository


if TYPE_CHECKING:
Expand All @@ -19,6 +22,20 @@
from tests.conftest import Config


@pytest.fixture()
def pool() -> Pool:
pool = Pool()

pool.add_repository(MockRepository())

return pool


@pytest.fixture(autouse=True)
def setup(mocker: MockerFixture, pool: Pool) -> None:
mocker.patch.object(Factory, "create_pool", return_value=pool)


@pytest.mark.parametrize(
("link", "cached"),
[
Expand Down Expand Up @@ -83,7 +100,7 @@ def test_get_cached_archives_for_link(config: Config, mocker: MockerFixture):
)

assert archives
assert set(archives) == {Path(path) for path in distributions.glob("demo-0.1.0*")}
assert set(archives) == set(distributions.glob("demo-0.1.*"))


def test_get_cache_directory_for_link(config: Config, config_cache_dir: Path):
Expand Down
Loading

0 comments on commit 455e638

Please sign in to comment.