Commit feb11b13 by Arun Babu Neelicattu

install: add --no-binary option

parent 7fcb6439
......@@ -223,6 +223,7 @@ option is used.
* `--default`: Only include the main dependencies. (**Deprecated**)
* `--sync`: Synchronize the environment with the locked packages and the specified groups.
* `--no-root`: Do not install the root package (your project).
* `--no-binary`: Do not use binary distributions for packages matching given policy. Use package name to disallow a specific package; or `:all:` to disallow and `:none:` to force binary for all packages.
* `--dry-run`: Output the operations but do not execute anything (implicitly enables --verbose).
* `--extras (-E)`: Features to install (multiple values allowed).
* `--no-dev`: Do not install dev dependencies. (**Deprecated**)
......@@ -233,6 +234,12 @@ option is used.
When `--only` is specified, `--with` and `--without` options are ignored.
{{% /note %}}
{{% note %}}
The `--no-binary` option will only work with the new installer. For the old installer,
this is ignored.
{{% /note %}}
## update
In order to get the latest versions of the dependencies and to update the `poetry.lock` file,
......
......@@ -34,6 +34,16 @@ class InstallCommand(InstallerCommand):
"no-root", None, "Do not install the root package (the current project)."
),
option(
"no-binary",
None,
"Do not use binary distributions for packages matching given policy.\n"
"Use package name to disallow a specific package; or <b>:all:</b> to\n"
"disallow and <b>:none:</b> to force binary for all packages. Multiple\n"
"packages can be specified separated by commas.",
flag=False,
multiple=True,
),
option(
"dry-run",
None,
"Output the operations but do not execute anything "
......@@ -98,6 +108,17 @@ dependencies and not including the current project, run the command with the
with_synchronization = True
if self.option("no-binary"):
policy = ",".join(self.option("no-binary", []))
try:
self._installer.no_binary(policy=policy)
except ValueError as e:
self.line_error(
f"<warning>Invalid value (<c1>{policy}</>) for"
f" `<b>--no-binary</b>`</>.\n\n<error>{e}</>"
)
return 1
self._installer.only_groups(self.activated_groups)
self._installer.dry_run(self.option("dry-run"))
self._installer.requires_synchronization(with_synchronization)
......
......@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING
from packaging.tags import Tag
from poetry.utils.helpers import canonicalize_name
from poetry.utils.patterns import wheel_file_re
......@@ -60,6 +61,28 @@ class Chooser:
def __init__(self, pool: Pool, env: Env) -> None:
self._pool = pool
self._env = env
self._no_binary_policy: set[str] = set()
def set_no_binary_policy(self, policy: str) -> None:
self._no_binary_policy = {
name.strip() if re.match(r":(all|none):", name) else canonicalize_name(name)
for name in policy.split(",")
}
if {":all:", ":none:"} <= self._no_binary_policy:
raise ValueError(
"Ambiguous binary policy containing :all: and :none: given."
)
def allow_binary(self, package_name: str) -> bool:
if ":all:" in self._no_binary_policy:
return False
return (
not self._no_binary_policy
or ":none:" in self._no_binary_policy
or canonicalize_name(package_name) not in self._no_binary_policy
)
def choose_for(self, package: Package) -> Link:
"""
......@@ -67,9 +90,17 @@ class Chooser:
"""
links = []
for link in self._get_links(package):
if link.is_wheel and not Wheel(link.filename).is_supported_by_environment(
self._env
):
if link.is_wheel:
if not self.allow_binary(package.name):
logger.debug(
"Skipping wheel for %s as requested in no binary policy for"
" package (%s)",
link.filename,
package.name,
)
continue
if not Wheel(link.filename).is_supported_by_environment(self._env):
logger.debug(
"Skipping wheel %s as this is not supported by the current"
" environment",
......
......@@ -92,6 +92,9 @@ class Executor:
def removals_count(self) -> int:
return self._executed["uninstall"]
def set_no_binary_policy(self, policy: str) -> None:
self._chooser.set_no_binary_policy(policy)
def supports_fancy_output(self) -> bool:
return self._io.output.is_decorated() and not self._dry_run
......
......@@ -135,6 +135,11 @@ class Installer:
def is_verbose(self) -> bool:
return self._verbose
def no_binary(self, policy: str) -> Installer:
if self._executor:
self._executor.set_no_binary_policy(policy=policy)
return self
def only_groups(self, groups: Iterable[str]) -> Installer:
self._groups = groups
......
......@@ -136,3 +136,29 @@ def test_sync_option_is_passed_to_the_installer(
tester.execute("--sync")
assert tester.command.installer._requires_synchronization
@pytest.mark.parametrize(
("options", "policy"),
[
(
"--no-binary :all:",
{":all:"},
),
("--no-binary :none:", {":none:"}),
("--no-binary pytest", {"pytest"}),
("--no-binary pytest,black", {"black", "pytest"}),
("--no-binary pytest --no-binary black", {"black", "pytest"}),
],
)
def test_no_binary_option_is_passed_to_the_installer(
tester: CommandTester, mocker: MockerFixture, options: str, policy: set[str]
) -> None:
"""
The --no-binary option is passed properly to the installer.
"""
mocker.patch.object(tester.command.installer, "run", return_value=1)
tester.execute(options)
assert tester.command.installer.executor._chooser._no_binary_policy == policy
......@@ -121,6 +121,44 @@ def test_chooser_chooses_universal_wheel_link_if_available(
assert link.filename == "pytest-3.5.0-py2.py3-none-any.whl"
@pytest.mark.parametrize(
("policy", "filename"),
[
(":all:", "pytest-3.5.0.tar.gz"),
(":none:", "pytest-3.5.0-py2.py3-none-any.whl"),
("black", "pytest-3.5.0-py2.py3-none-any.whl"),
("pytest", "pytest-3.5.0.tar.gz"),
("pytest,black", "pytest-3.5.0.tar.gz"),
],
)
@pytest.mark.parametrize("source_type", ["", "legacy"])
def test_chooser_no_binary_policy(
env: MockEnv,
mock_pypi: None,
mock_legacy: None,
source_type: str,
pool: Pool,
policy: str,
filename: str,
):
chooser = Chooser(pool, env)
chooser.set_no_binary_policy(policy)
package = Package("pytest", "3.5.0")
if source_type == "legacy":
package = Package(
package.name,
package.version.text,
source_type="legacy",
source_reference="foo",
source_url="https://foo.bar/simple/",
)
link = chooser.choose_for(package)
assert link.filename == filename
@pytest.mark.parametrize("source_type", ["", "legacy"])
def test_chooser_chooses_specific_python_universal_wheel_link_if_available(
env: MockEnv, mock_pypi: None, mock_legacy: None, source_type: str, pool: Pool
......
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