Commit 53cdce0d by Randy Döring

Add option to compile bytecode during installation (similar to default behavior of old installer)

parent f127bd27
...@@ -225,6 +225,21 @@ If you want to skip this installation, use the `--no-root` option. ...@@ -225,6 +225,21 @@ If you want to skip this installation, use the `--no-root` option.
poetry install --no-root poetry install --no-root
``` ```
By default `poetry` does not compile Python source files to bytecode during installation.
This speeds up the installation process, but the first execution may take a little more
time because Python then compiles source files to bytecode automatically.
If you want to compile source files to bytecode during installation,
you can use the `--compile` option:
```bash
poetry install --compile
```
{{% note %}}
The `--compile` option has no effect if `installer.modern-installation`
is set to `false` because the old installer always compiles source files to bytecode.
{{% /note %}}
### Options ### Options
* `--without`: The dependency groups to ignore. * `--without`: The dependency groups to ignore.
...@@ -236,6 +251,7 @@ poetry install --no-root ...@@ -236,6 +251,7 @@ poetry install --no-root
* `--dry-run`: Output the operations but do not execute anything (implicitly enables --verbose). * `--dry-run`: Output the operations but do not execute anything (implicitly enables --verbose).
* `--extras (-E)`: Features to install (multiple values allowed). * `--extras (-E)`: Features to install (multiple values allowed).
* `--all-extras`: Install all extra features (conflicts with --extras). * `--all-extras`: Install all extra features (conflicts with --extras).
* `--compile`: Compile Python source files to bytecode.
* `--no-dev`: Do not install dev dependencies. (**Deprecated**, use `--without dev` or `--only main` instead) * `--no-dev`: Do not install dev dependencies. (**Deprecated**, use `--without dev` or `--only main` instead)
* `--remove-untracked`: Remove dependencies not presented in the lock file. (**Deprecated**, use `--sync` instead) * `--remove-untracked`: Remove dependencies not presented in the lock file. (**Deprecated**, use `--sync` instead)
......
...@@ -54,12 +54,15 @@ class InstallCommand(InstallerCommand): ...@@ -54,12 +54,15 @@ class InstallCommand(InstallerCommand):
multiple=True, multiple=True,
), ),
option("all-extras", None, "Install all extra dependencies."), option("all-extras", None, "Install all extra dependencies."),
option("only-root", None, "Exclude all dependencies."),
option( option(
"only-root", "compile",
None, None,
"Exclude all dependencies.", (
flag=True, "Compile Python source files to bytecode."
multiple=False, " (This option has no effect if modern-installation is disabled"
" because the old installer always compiles.)"
),
), ),
] ]
...@@ -146,6 +149,7 @@ dependencies and not including the current project, run the command with the ...@@ -146,6 +149,7 @@ dependencies and not including the current project, run the command with the
self.installer.only_groups(self.activated_groups) self.installer.only_groups(self.activated_groups)
self.installer.dry_run(self.option("dry-run")) self.installer.dry_run(self.option("dry-run"))
self.installer.requires_synchronization(with_synchronization) self.installer.requires_synchronization(with_synchronization)
self.installer.executor.enable_bytecode_compilation(self.option("compile"))
self.installer.verbose(self.io.is_verbose()) self.installer.verbose(self.io.is_verbose())
return_code = self.installer.run() return_code = self.installer.run()
......
...@@ -123,6 +123,9 @@ class Executor: ...@@ -123,6 +123,9 @@ class Executor:
return self return self
def enable_bytecode_compilation(self, enable: bool = True) -> None:
self._wheel_installer.enable_bytecode_compilation(enable)
def pip_install( def pip_install(
self, req: Path, upgrade: bool = False, editable: bool = False self, req: Path, upgrade: bool = False, editable: bool = False
) -> int: ) -> int:
......
...@@ -66,7 +66,10 @@ class WheelDestination(SchemeDictionaryDestination): ...@@ -66,7 +66,10 @@ class WheelDestination(SchemeDictionaryDestination):
scheme_dict["headers"] = str(Path(scheme_dict["headers"]) / source.distribution) scheme_dict["headers"] = str(Path(scheme_dict["headers"]) / source.distribution)
return self.__class__( return self.__class__(
scheme_dict, interpreter=self.interpreter, script_kind=self.script_kind scheme_dict,
interpreter=self.interpreter,
script_kind=self.script_kind,
bytecode_optimization_levels=self.bytecode_optimization_levels,
) )
...@@ -90,6 +93,9 @@ class WheelInstaller: ...@@ -90,6 +93,9 @@ class WheelInstaller:
schemes, interpreter=self._env.python, script_kind=script_kind schemes, interpreter=self._env.python, script_kind=script_kind
) )
def enable_bytecode_compilation(self, enable: bool = True) -> None:
self._destination.bytecode_optimization_levels = (1,) if enable else ()
def install(self, wheel: Path) -> None: def install(self, wheel: Path) -> None:
with WheelFile.open(Path(wheel.as_posix())) as source: with WheelFile.open(Path(wheel.as_posix())) as source:
install( install(
......
...@@ -162,6 +162,24 @@ def test_sync_option_is_passed_to_the_installer( ...@@ -162,6 +162,24 @@ def test_sync_option_is_passed_to_the_installer(
assert tester.command.installer._requires_synchronization assert tester.command.installer._requires_synchronization
@pytest.mark.parametrize("compile", [False, True])
def test_compile_option_is_passed_to_the_installer(
tester: CommandTester, mocker: MockerFixture, compile: bool
):
"""
The --compile option is passed properly to the installer.
"""
mocker.patch.object(tester.command.installer, "run", return_value=1)
enable_bytecode_compilation_mock = mocker.patch.object(
tester.command.installer.executor._wheel_installer,
"enable_bytecode_compilation",
)
tester.execute("--compile" if compile else "")
enable_bytecode_compilation_mock.assert_called_once_with(compile)
def test_no_all_extras_doesnt_populate_installer( def test_no_all_extras_doesnt_populate_installer(
tester: CommandTester, mocker: MockerFixture tester: CommandTester, mocker: MockerFixture
): ):
......
...@@ -59,3 +59,23 @@ def test_installer_file_contains_valid_version(default_installation: Path) -> No ...@@ -59,3 +59,23 @@ def test_installer_file_contains_valid_version(default_installation: Path) -> No
match = re.match(r"Poetry (?P<version>.*)", installer_content) match = re.match(r"Poetry (?P<version>.*)", installer_content)
assert match assert match
parse_constraint(match.group("version")) # must not raise an error parse_constraint(match.group("version")) # must not raise an error
def test_default_installation_no_bytecode(default_installation: Path) -> None:
cache_dir = default_installation / "demo" / "__pycache__"
assert not cache_dir.exists()
@pytest.mark.parametrize("compile", [True, False])
def test_enable_bytecode_compilation(
env: MockEnv, demo_wheel: Path, compile: bool
) -> None:
installer = WheelInstaller(env)
installer.enable_bytecode_compilation(compile)
installer.install(demo_wheel)
cache_dir = Path(env.paths["purelib"]) / "demo" / "__pycache__"
if compile:
assert cache_dir.exists()
assert list(cache_dir.glob("*.pyc"))
else:
assert not cache_dir.exists()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment