From 8d8c7da39c98d663d28c027561af16035e351f4a Mon Sep 17 00:00:00 2001 From: Bulat Date: Tue, 23 Jul 2024 17:07:51 +0200 Subject: [PATCH] feat: compile flag for installer (#88) --- .../bundlers/venv_bundler.py | 8 ++++ .../console/commands/bundle/venv.py | 9 +++++ tests/bundlers/test_venv_bundler.py | 37 +++++++++++++++++++ tests/console/commands/bundle/test_venv.py | 17 ++++++++- 4 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/poetry_plugin_bundle/bundlers/venv_bundler.py b/src/poetry_plugin_bundle/bundlers/venv_bundler.py index 1dbac51..b860876 100644 --- a/src/poetry_plugin_bundle/bundlers/venv_bundler.py +++ b/src/poetry_plugin_bundle/bundlers/venv_bundler.py @@ -26,6 +26,7 @@ def __init__(self) -> None: self._executable: str | None = None self._remove: bool = False self._activated_groups: set[str] | None = None + self._compile: bool = False def set_path(self, path: Path) -> VenvBundler: self._path = path @@ -47,6 +48,11 @@ def set_remove(self, remove: bool = True) -> VenvBundler: return self + def set_compile(self, compile: bool = False) -> VenvBundler: + self._compile = compile + + return self + def bundle(self, poetry: Poetry, io: IO) -> bool: from pathlib import Path from tempfile import TemporaryDirectory @@ -141,6 +147,8 @@ def locked_repository(self) -> LockfileRepository: installer.only_groups(self._activated_groups) installer.requires_synchronization() + installer.executor.enable_bytecode_compilation(self._compile) + return_code = installer.run() if return_code: self._write( diff --git a/src/poetry_plugin_bundle/console/commands/bundle/venv.py b/src/poetry_plugin_bundle/console/commands/bundle/venv.py index 514f8a7..15fb9a4 100644 --- a/src/poetry_plugin_bundle/console/commands/bundle/venv.py +++ b/src/poetry_plugin_bundle/console/commands/bundle/venv.py @@ -37,6 +37,14 @@ class BundleVenvCommand(BundleCommand): "Clear the existing virtual environment if it exists. ", flag=True, ), + option( + "compile", + None, + "Compile Python source files to bytecode." + " (This option has no effect if modern-installation is disabled" + " because the old installer always compiles.)", + flag=True, + ), ] bundler_name = "venv" @@ -45,4 +53,5 @@ def configure_bundler(self, bundler: VenvBundler) -> None: # type: ignore[overr bundler.set_path(Path(self.argument("path"))) bundler.set_executable(self.option("python")) bundler.set_remove(self.option("clear")) + bundler.set_compile(self.option("compile")) bundler.set_activated_groups(self.activated_groups) diff --git a/tests/bundlers/test_venv_bundler.py b/tests/bundlers/test_venv_bundler.py index 4fc1111..1921317 100644 --- a/tests/bundlers/test_venv_bundler.py +++ b/tests/bundlers/test_venv_bundler.py @@ -271,6 +271,43 @@ def test_bundler_can_filter_dependency_groups( assert expected == io.fetch_output() +@pytest.mark.parametrize("compile", [True, False]) +def test_bundler_passes_compile_flag( + io: BufferedIO, + tmp_venv: VirtualEnv, + poetry: Poetry, + mocker: MockerFixture, + compile: bool, +) -> None: + mocker.patch("poetry.installation.executor.Executor._execute_operation") + + bundler = VenvBundler() + bundler.set_path(tmp_venv.path) + bundler.set_remove(True) + bundler.set_compile(compile) + + # bundle passes the flag from set_compile to enable_bytecode_compilation method + mocker = mocker.patch( + "poetry.installation.executor.Executor.enable_bytecode_compilation" + ) + + assert bundler.bundle(poetry, io) + + mocker.assert_called_once_with(compile) + + path = str(tmp_venv.path) + python_version = ".".join(str(v) for v in sys.version_info[:3]) + expected = f"""\ + • Bundling simple-project (1.2.3) into {path} + • Bundling simple-project (1.2.3) into {path}: Removing existing virtual environment + • Bundling simple-project (1.2.3) into {path}: Creating a virtual environment using Python {python_version} + • Bundling simple-project (1.2.3) into {path}: Installing dependencies + • Bundling simple-project (1.2.3) into {path}: Installing simple-project (1.2.3) + • Bundled simple-project (1.2.3) into {path} +""" + assert expected == io.fetch_output() + + def test_bundler_editable_deps( io: BufferedIO, tmpdir: str, poetry: Poetry, mocker: MockerFixture, config: Config ) -> None: diff --git a/tests/console/commands/bundle/test_venv.py b/tests/console/commands/bundle/test_venv.py index 84f22f0..2f84d8d 100644 --- a/tests/console/commands/bundle/test_venv.py +++ b/tests/console/commands/bundle/test_venv.py @@ -18,12 +18,13 @@ def test_venv_calls_venv_bundler( ) -> None: mock = mocker.patch( "poetry_plugin_bundle.bundlers.venv_bundler.VenvBundler.bundle", - side_effect=[True, False, False, False], + side_effect=[True, False, False, False, False], ) set_path = mocker.spy(VenvBundler, "set_path") set_executable = mocker.spy(VenvBundler, "set_executable") set_remove = mocker.spy(VenvBundler, "set_remove") set_activated_groups = mocker.spy(VenvBundler, "set_activated_groups") + set_compile = mocker.spy(VenvBundler, "set_compile") app_tester.application.catch_exceptions(False) assert app_tester.execute("bundle venv /foo") == 0 @@ -33,6 +34,7 @@ def test_venv_calls_venv_bundler( ) assert app_tester.execute("bundle venv /foo --only dev") == 1 assert app_tester.execute("bundle venv /foo --without main --with dev") == 1 + assert app_tester.execute("bundle venv /foo --compile") == 1 assert isinstance(app_tester.application, Application) assert [ @@ -40,6 +42,7 @@ def test_venv_calls_venv_bundler( mocker.call(app_tester.application.poetry, mocker.ANY), mocker.call(app_tester.application.poetry, mocker.ANY), mocker.call(app_tester.application.poetry, mocker.ANY), + mocker.call(app_tester.application.poetry, mocker.ANY), ] == mock.call_args_list assert set_path.call_args_list == [ @@ -47,22 +50,34 @@ def test_venv_calls_venv_bundler( mocker.call(mocker.ANY, Path("/foo")), mocker.call(mocker.ANY, Path("/foo")), mocker.call(mocker.ANY, Path("/foo")), + mocker.call(mocker.ANY, Path("/foo")), ] assert set_executable.call_args_list == [ mocker.call(mocker.ANY, None), mocker.call(mocker.ANY, "python3.8"), mocker.call(mocker.ANY, None), mocker.call(mocker.ANY, None), + mocker.call(mocker.ANY, None), ] assert set_remove.call_args_list == [ mocker.call(mocker.ANY, False), mocker.call(mocker.ANY, True), mocker.call(mocker.ANY, False), mocker.call(mocker.ANY, False), + mocker.call(mocker.ANY, False), ] assert set_activated_groups.call_args_list == [ mocker.call(mocker.ANY, {"main"}), mocker.call(mocker.ANY, {"main", "dev"}), mocker.call(mocker.ANY, {"dev"}), mocker.call(mocker.ANY, {"dev"}), + mocker.call(mocker.ANY, {"main"}), + ] + + assert set_compile.call_args_list == [ + mocker.call(mocker.ANY, False), + mocker.call(mocker.ANY, False), + mocker.call(mocker.ANY, False), + mocker.call(mocker.ANY, False), + mocker.call(mocker.ANY, True), ]