Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faster build and installation of packages #6205

Merged
merged 8 commits into from
Feb 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
13 changes: 13 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,19 @@ the number of maximum workers is still limited at `number_of_cores + 4`.
This configuration is ignored when `installer.parallel` is set to `false`.
{{% /note %}}

### `installer.modern-installation`

**Type**: `boolean`

**Default**: `true`

*Introduced in 1.4.0*

Use a more modern and faster method for package installation.

If this causes issues, you can disable it by setting it to `false` and report the problems
you encounter on the [issue tracker](https://github.com/python-poetry/poetry/issues).

### `installer.no-binary`

**Type**: `string | boolean`
Expand Down
56 changes: 54 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 14 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@ python = "^3.7"
poetry-core = "1.5.0"
poetry-plugin-export = "^1.3.0"
"backports.cached-property" = { version = "^1.0.2", python = "<3.8" }
build = "^0.10.0"
cachecontrol = { version = "^0.12.9", extras = ["filecache"] }
cleo = "^2.0.0"
crashtest = "^0.4.1"
dulwich = "^0.21.2"
filelock = "^3.8.0"
html5lib = "^1.0"
importlib-metadata = { version = ">=4.4", python = "<3.10" }
installer = "^0.6.0"
jsonschema = "^4.10.0"
keyring = "^23.9.0"
lockfile = "^0.12.2"
Expand All @@ -65,6 +67,7 @@ packaging = ">=20.4"
pexpect = "^4.7.0"
pkginfo = "^1.9.4"
platformdirs = "^2.5.2"
pyproject-hooks = "^1.0.0"
requests = "^2.18"
requests-toolbelt = ">=0.9.1,<0.11.0"
shellingham = "^1.5"
Expand Down Expand Up @@ -166,22 +169,22 @@ enable_error_code = [
# warning.
[[tool.mypy.overrides]]
module = [
'poetry.console.commands.self.show.plugins',
'poetry.plugins.plugin_manager',
'poetry.repositories.installed_repository',
'poetry.utils.env',
'poetry.console.commands.self.show.plugins',
'poetry.plugins.plugin_manager',
'poetry.repositories.installed_repository',
'poetry.utils.env',
]
warn_unused_ignores = false

[[tool.mypy.overrides]]
module = [
'cachecontrol.*',
'lockfile.*',
'pexpect.*',
'requests_toolbelt.*',
'shellingham.*',
'virtualenv.*',
'xattr.*',
'cachecontrol.*',
'lockfile.*',
'pexpect.*',
'requests_toolbelt.*',
'shellingham.*',
'virtualenv.*',
'xattr.*',
]
ignore_missing_imports = true

Expand Down
14 changes: 11 additions & 3 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,8 +123,16 @@ class Config:
"prefer-active-python": False,
"prompt": "{project_name}-py{python_version}",
},
"experimental": {"new-installer": True, "system-git-client": False},
"installer": {"parallel": True, "max-workers": None, "no-binary": None},
"experimental": {
"new-installer": True,
"system-git-client": False,
},
"installer": {
"modern-installation": True,
"parallel": True,
"max-workers": None,
"no-binary": None,
},
}

def __init__(
Expand Down Expand Up @@ -267,6 +274,7 @@ def _get_normalizer(name: str) -> Callable[[str], Any]:
"virtualenvs.options.prefer-active-python",
"experimental.new-installer",
"experimental.system-git-client",
"installer.modern-installation",
"installer.parallel",
}:
return boolean_normalizer
Expand Down
1 change: 1 addition & 0 deletions src/poetry/console/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def unique_config_values(self) -> dict[str, tuple[Any, Any]]:
"virtualenvs.prefer-active-python": (boolean_validator, boolean_normalizer),
"experimental.new-installer": (boolean_validator, boolean_normalizer),
"experimental.system-git-client": (boolean_validator, boolean_normalizer),
"installer.modern-installation": (boolean_validator, boolean_normalizer),
"installer.parallel": (boolean_validator, boolean_normalizer),
"installer.max-workers": (lambda val: int(val) > 0, int_normalizer),
"virtualenvs.prompt": (str, lambda val: str(val)),
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
12 changes: 6 additions & 6 deletions src/poetry/console/commands/source/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class SourceAddCommand(Command):

def handle(self) -> int:
from poetry.factory import Factory
from poetry.repositories import RepositoryPool
from poetry.utils.source import source_to_table

name = self.argument("name")
Expand Down Expand Up @@ -84,13 +83,14 @@ def handle(self) -> int:
self.line(f"Adding source with name <c1>{name}</c1>.")
sources.append(source_to_table(new_source))

self.poetry.config.merge(
{"sources": {source["name"]: source for source in sources}}
)

# ensure new source is valid. eg: invalid name etc.
self.poetry._pool = RepositoryPool()
try:
Factory.configure_sources(
self.poetry, sources, self.poetry.config, NullIO()
)
self.poetry.pool.repository(name)
pool = Factory.create_pool(self.poetry.config, NullIO())
pool.repository(name)
except ValueError as e:
self.line_error(
f"<error>Failed to validate addition of <c1>{name}</c1>: {e}</error>"
Expand Down
47 changes: 28 additions & 19 deletions src/poetry/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
from poetry.core.packages.package import Package
from tomlkit.toml_document import TOMLDocument

from poetry.repositories import RepositoryPool
from poetry.repositories.legacy_repository import LegacyRepository
from poetry.utils.dependency_specification import DependencySpec


logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -90,13 +90,15 @@ def create_poetry(
)

# Configuring sources
self.configure_sources(
poetry,
poetry.local_config.get("source", []),
config,
io,
disable_cache=disable_cache,
config.merge(
{
"sources": {
source["name"]: source
for source in poetry.local_config.get("source", [])
}
}
)
poetry.set_pool(self.create_pool(config, io, disable_cache=disable_cache))

plugin_manager = PluginManager(Plugin.group, disable_plugins=disable_plugins)
plugin_manager.load_plugins()
Expand All @@ -110,23 +112,28 @@ def get_package(cls, name: str, version: str) -> ProjectPackage:
return ProjectPackage(name, version)

@classmethod
def configure_sources(
def create_pool(
cls,
poetry: Poetry,
sources: list[dict[str, str]],
config: Config,
io: IO,
io: IO | None = None,
disable_cache: bool = False,
) -> None:
) -> RepositoryPool:
from poetry.repositories import RepositoryPool

if io is None:
io = NullIO()

if disable_cache:
logger.debug("Disabling source caches")

for source in sources:
pool = RepositoryPool()

for source in config.get("sources", {}).values():
repository = cls.create_package_source(
source, config, disable_cache=disable_cache
)
is_default = bool(source.get("default", False))
is_secondary = bool(source.get("secondary", False))
is_default = source.get("default", False)
is_secondary = source.get("secondary", False)
if io.is_debug():
message = f"Adding repository {repository.name} ({repository.url})"
if is_default:
Expand All @@ -136,22 +143,24 @@ def configure_sources(

io.write_line(message)

poetry.pool.add_repository(repository, is_default, secondary=is_secondary)
pool.add_repository(repository, is_default, secondary=is_secondary)

# Put PyPI last to prefer private repositories
# unless we have no default source AND no primary sources
# (default = false, secondary = false)
if poetry.pool.has_default():
if pool.has_default():
if io.is_debug():
io.write_line("Deactivating the PyPI repository")
else:
from poetry.repositories.pypi_repository import PyPiRepository

default = not poetry.pool.has_primary_repositories()
poetry.pool.add_repository(
default = not pool.has_primary_repositories()
pool.add_repository(
PyPiRepository(disable_cache=disable_cache), default, not default
)

return pool

@classmethod
def create_package_source(
cls, source: dict[str, str], auth_config: Config, disable_cache: bool = False
Expand Down
Loading