Skip to content

Commit

Permalink
Add option to compile bytecode during installation (similar to defaul…
Browse files Browse the repository at this point in the history
…t behavior of old installer)
  • Loading branch information
radoering committed Feb 3, 2023
1 parent d858d62 commit 7f159dc
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 5 deletions.
16 changes: 16 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,21 @@ If you want to skip this installation, use the `--no-root` option.
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

* `--without`: The dependency groups to ignore.
Expand All @@ -236,6 +251,7 @@ poetry install --no-root
* `--dry-run`: Output the operations but do not execute anything (implicitly enables --verbose).
* `--extras (-E)`: Features to install (multiple values allowed).
* `--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)
* `--remove-untracked`: Remove dependencies not presented in the lock file. (**Deprecated**, use `--sync` instead)

Expand Down
12 changes: 8 additions & 4 deletions src/poetry/console/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,15 @@ class InstallCommand(InstallerCommand):
multiple=True,
),
option("all-extras", None, "Install all extra dependencies."),
option("only-root", None, "Exclude all dependencies."),
option(
"only-root",
"compile",
None,
"Exclude all dependencies.",
flag=True,
multiple=False,
(
"Compile Python source files to bytecode."
" (This option has no effect if modern-installation is disabled"
" because the old installer always compiles.)"
),
),
]

Expand Down Expand Up @@ -146,6 +149,7 @@ def handle(self) -> int:
self.installer.only_groups(self.activated_groups)
self.installer.dry_run(self.option("dry-run"))
self.installer.requires_synchronization(with_synchronization)
self.installer.executor.enable_bytecode_compilation(self.option("compile"))
self.installer.verbose(self.io.is_verbose())

return_code = self.installer.run()
Expand Down
3 changes: 3 additions & 0 deletions src/poetry/installation/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ def verbose(self, verbose: bool = True) -> Executor:

return self

def enable_bytecode_compilation(self, enable: bool = True) -> None:
self._wheel_installer.enable_bytecode_compilation(enable)

def pip_install(
self, req: Path, upgrade: bool = False, editable: bool = False
) -> int:
Expand Down
8 changes: 7 additions & 1 deletion src/poetry/installation/wheel_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ def for_source(self, source: WheelFile) -> WheelDestination:
scheme_dict["headers"] = str(Path(scheme_dict["headers"]) / source.distribution)

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,
)


Expand All @@ -88,6 +91,9 @@ def __init__(self, env: Env) -> None:
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:
with WheelFile.open(Path(wheel.as_posix())) as source:
install(
Expand Down
18 changes: 18 additions & 0 deletions tests/console/commands/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,24 @@ def test_sync_option_is_passed_to_the_installer(
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(
tester: CommandTester, mocker: MockerFixture
):
Expand Down
20 changes: 20 additions & 0 deletions tests/installation/test_wheel_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,23 @@ def test_installer_file_contains_valid_version(default_installation: Path) -> No
match = re.match(r"Poetry (?P<version>.*)", installer_content)
assert match
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()

0 comments on commit 7f159dc

Please sign in to comment.