Skip to content

Commit

Permalink
Merge branch 'python-poetry:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
estyxx authored May 16, 2022
2 parents c4a9d9f + f0f5ae2 commit a0b1132
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 21 deletions.
1 change: 1 addition & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ option is used.
When `--only` is specified, `--with` and `--without` options are ignored.
{{% /note %}}


## update

In order to get the latest versions of the dependencies and to update the `poetry.lock` file,
Expand Down
48 changes: 48 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,54 @@ the number of maximum workers is still limited at `number_of_cores + 4`.
This configuration will be ignored when `installer.parallel` is set to false.
{{% /note %}}

### `installer.no-binary`

**Type**: string | bool

*Introduced in 1.2.0*

When set this configuration allows users to configure package distribution format policy for all or
specific packages.

| Configuration | Description |
|------------------------|------------------------------------------------------------|
| `:all:` or `true` | Disallow binary distributions for all packages. |
| `:none:` or `false` | Allow binary distributions for all packages. |
| `package[,package,..]` | Disallow binary distributions for specified packages only. |

{{% note %}}
This configuration is only respected when using the new installer. If you have disabled it please
consider re-enabling it.

As with all configurations described here, this is a user specific configuration. This means that this
is not taken into consideration when a lockfile is generated or dependencies are resolved. This is
applied only when selecting which distribution for dependency should be installed into a Poetry managed
environment.
{{% /note %}}

{{% note %}}
For project specific usage, it is recommended that this be configured with the `--local`.

```bash
poetry config --local installer.no-binary :all:
```
{{% /note %}}

{{% note %}}
For CI or container environments using [environment variable](#using-environment-variables)
to configure this might be useful.

```bash
export POETRY_INSTALLER_NO_BINARY=:all:
```
{{% /note %}}

{{% warning %}}
Unless this is required system-wide, if configured globally, you could encounter slower install times
across all your projects if incorrectly set.
{{% /warning %}}


### `virtualenvs.create`

**Type**: boolean
Expand Down
13 changes: 12 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,22 @@ module = [
'poetry.installation.executor',
'poetry.installation.installer',
'poetry.installation.pip_installer',
'poetry.repositories.installed_repository',
'poetry.utils.env',
]
ignore_errors = true

# use of importlib-metadata backport at python3.7 makes it impossible to
# satisfy mypy without some ignores: but we get a different set of ignores at
# different python versions.
#
# <https://github.com/python/mypy/issues/8823>, meanwhile suppress that
# warning.
[[tool.mypy.overrides]]
module = [
'poetry.repositories.installed_repository',
]
warn_unused_ignores = false

[[tool.mypy.overrides]]
module = [
'cachecontrol.*',
Expand Down
68 changes: 67 additions & 1 deletion src/poetry/config/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import dataclasses
import logging
import os
import re
Expand All @@ -11,6 +12,7 @@
from typing import Callable

from poetry.core.toml import TOMLFile
from poetry.core.utils.helpers import canonicalize_name

from poetry.config.dict_config_source import DictConfigSource
from poetry.config.file_config_source import FileConfigSource
Expand All @@ -34,6 +36,67 @@ def int_normalizer(val: str) -> int:
return int(val)


@dataclasses.dataclass
class PackageFilterPolicy:
policy: dataclasses.InitVar[str | list[str] | None]
packages: list[str] = dataclasses.field(init=False)

def __post_init__(self, policy: str | list[str] | None) -> None:
if not policy:
policy = []
elif isinstance(policy, str):
policy = self.normalize(policy)
self.packages = policy

def allows(self, package_name: str) -> bool:
if ":all:" in self.packages:
return False

return (
not self.packages
or ":none:" in self.packages
or canonicalize_name(package_name) not in self.packages
)

@classmethod
def is_reserved(cls, name: str) -> bool:
return bool(re.match(r":(all|none):", name))

@classmethod
def normalize(cls, policy: str) -> list[str]:
if boolean_validator(policy):
if boolean_normalizer(policy):
return [":all:"]
else:
return [":none:"]

return list(
{
name.strip() if cls.is_reserved(name) else canonicalize_name(name)
for name in policy.strip().split(",")
if name
}
)

@classmethod
def validator(cls, policy: str) -> bool:
if boolean_validator(policy):
return True

names = policy.strip().split(",")

for name in names:
if (
not name
or (cls.is_reserved(name) and len(names) == 1)
or re.match(r"^[a-zA-Z\d_-]+$", name)
):
continue
return False

return True


logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -61,7 +124,7 @@ class Config:
"prefer-active-python": False,
},
"experimental": {"new-installer": True, "system-git-client": False},
"installer": {"parallel": True, "max-workers": None},
"installer": {"parallel": True, "max-workers": None, "no-binary": None},
}

def __init__(
Expand Down Expand Up @@ -196,6 +259,9 @@ def _get_normalizer(name: str) -> Callable[[str], Any]:
if name == "installer.max-workers":
return int_normalizer

if name == "installer.no-binary":
return PackageFilterPolicy.normalize

return lambda val: val

@classmethod
Expand Down
6 changes: 6 additions & 0 deletions src/poetry/console/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from cleo.helpers import argument
from cleo.helpers import option

from poetry.config.config import PackageFilterPolicy
from poetry.console.commands.command import Command


Expand Down Expand Up @@ -107,6 +108,11 @@ def unique_config_values(self) -> dict[str, tuple[Any, Any, Any]]:
int_normalizer,
None,
),
"installer.no-binary": (
PackageFilterPolicy.validator,
PackageFilterPolicy.normalize,
None,
),
}

return unique_config_values
Expand Down
34 changes: 24 additions & 10 deletions src/poetry/installation/chooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

from packaging.tags import Tag

from poetry.config.config import Config
from poetry.config.config import PackageFilterPolicy
from poetry.utils.patterns import wheel_file_re


Expand Down Expand Up @@ -57,25 +59,37 @@ class Chooser:
A Chooser chooses an appropriate release archive for packages.
"""

def __init__(self, pool: Pool, env: Env) -> None:
def __init__(self, pool: Pool, env: Env, config: Config | None = None) -> None:
self._pool = pool
self._env = env
self._config = config or Config.create()
self._no_binary_policy: PackageFilterPolicy = PackageFilterPolicy(
self._config.get("installer.no-binary", [])
)

def choose_for(self, package: Package) -> Link:
"""
Return the url of the selected archive for a given package.
"""
links = []
for link in self._get_links(package):
if link.is_wheel and not Wheel(link.filename).is_supported_by_environment(
self._env
):
logger.debug(
"Skipping wheel %s as this is not supported by the current"
" environment",
link.filename,
)
continue
if link.is_wheel:
if not self._no_binary_policy.allows(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",
link.filename,
)
continue

if link.ext in {".egg", ".exe", ".msi", ".rpm", ".srpm"}:
logger.debug("Skipping unsupported distribution %s", link.filename)
Expand Down
2 changes: 1 addition & 1 deletion src/poetry/installation/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def __init__(
self._verbose = False
self._authenticator = Authenticator(config, self._io)
self._chef = Chef(config, self._env)
self._chooser = Chooser(pool, self._env)
self._chooser = Chooser(pool, self._env, config)

if parallel is None:
parallel = config.get("installer.parallel", True)
Expand Down
23 changes: 16 additions & 7 deletions src/poetry/repositories/installed_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def create_package_from_distribution(
) -> Package:
# We first check for a direct_url.json file to determine
# the type of package.
path = Path(str(distribution._path))
path = Path(str(distribution._path)) # type: ignore[attr-defined]

if (
path.name.endswith(".dist-info")
Expand Down Expand Up @@ -163,13 +163,17 @@ def create_package_from_distribution(
source_reference=source_reference,
source_resolved_reference=source_resolved_reference,
)
package.description = distribution.metadata.get("summary", "")

package.description = distribution.metadata.get( # type: ignore[attr-defined]
"summary",
"",
)

return package

@classmethod
def create_package_from_pep610(cls, distribution: metadata.Distribution) -> Package:
path = Path(str(distribution._path))
path = Path(str(distribution._path)) # type: ignore[attr-defined]
source_type = None
source_url = None
source_reference = None
Expand Down Expand Up @@ -213,7 +217,10 @@ def create_package_from_pep610(cls, distribution: metadata.Distribution) -> Pack
develop=develop,
)

package.description = distribution.metadata.get("summary", "")
package.description = distribution.metadata.get( # type: ignore[attr-defined]
"summary",
"",
)

return package

Expand All @@ -229,15 +236,17 @@ def load(cls, env: Env, with_dependencies: bool = False) -> InstalledRepository:

for entry in reversed(env.sys_path):
for distribution in sorted(
metadata.distributions(path=[entry]),
key=lambda d: str(d._path),
metadata.distributions( # type: ignore[no-untyped-call]
path=[entry],
),
key=lambda d: str(d._path), # type: ignore[attr-defined]
):
name = canonicalize_name(distribution.metadata["name"])

if name in seen:
continue

path = Path(str(distribution._path))
path = Path(str(distribution._path)) # type: ignore[attr-defined]

try:
path.relative_to(_VENDORS)
Expand Down
Loading

0 comments on commit a0b1132

Please sign in to comment.