Skip to content

Commit

Permalink
Merge branch 'master' into document-poetry-badge
Browse files Browse the repository at this point in the history
  • Loading branch information
Secrus authored Jul 6, 2023
2 parents 18942a2 + 79f2668 commit 5cbf87e
Show file tree
Hide file tree
Showing 21 changed files with 156 additions and 87 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ repos:
- id: black

- repo: https://github.com/pre-commit/pre-commit
rev: v3.3.2
rev: v3.3.3
hooks:
- id: validate_manifest

Expand Down
4 changes: 4 additions & 0 deletions docs/basic-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ Even if you develop alone, in six months when reinstalling the project you can f
the dependencies installed are still working even if your dependencies released many new versions since then.
(See note below about using the update command.)

{{% warning %}} If you have added the recommended [`[build-system]`]({{< relref "pyproject#poetry-and-pep-517" >}}) section to your project's pyproject.toml then you _can_ successfully install your project and its dependencies into a virtual environment using a command like `pip install -e .`. However, pip will not use the lock file to determine dependency versions as the poetry-core build system is intended for library developers (see next section).
{{% /warning %}}


#### As a library developer

Library developers have more to consider. Your users are application developers, and your library will run in a Python environment you don't control.
Expand Down
13 changes: 11 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,12 +309,21 @@ might contain additional Python packages as well.

Create the virtualenv inside the project's root directory.

If not set explicitly, `poetry` by default will create virtual environment under
`{cache-dir}/virtualenvs` or use the `{project-dir}/.venv` directory when one is available.
If not set explicitly, `poetry` by default will create a virtual environment under
`{cache-dir}/virtualenvs` or use the `{project-dir}/.venv` directory if one already exists.

If set to `true`, the virtualenv will be created and expected in a folder named
`.venv` within the root directory of the project.

{{% note %}}
If a virtual environment has already been created for the project under `{cache-dir}/virtualenvs`, setting this variable to `true` will not cause `poetry` to create or use a local virtual environment.

In order for this setting to take effect for a project already in that state, you must delete the virtual environment folder located in `{cache-dir}/virtualenvs`.

You can find out where the current project's virtual environment (if there is one) is stored
with the command `poetry env info --path`.
{{% /note %}}

If set to `false`, `poetry` will ignore any existing `.venv` directory.

### `virtualenvs.options.always-copy`
Expand Down
14 changes: 7 additions & 7 deletions docs/pre-commit-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,16 @@ repos:
`pre-commit autoupdate` updates the `rev` for each repository defined in your `.pre-commit-config.yaml`
to the latest available tag in the default branch.

Poetry follows a branching strategy, where the default branch is the active development branch
and fixes gets back ported to stable branches. New tags are assigned in these stable branches.
Poetry follows a branching strategy where the default branch is the active development branch,
and fixes get backported to stable branches. New tags are assigned in these stable branches.

`pre-commit` does not support such a branching strategy and has decided to not implement
an option, either on the [user side](https://github.com/pre-commit/pre-commit/issues/2512)
or [hook author side](https://github.com/pre-commit/pre-commit/issues/2508), to define a branch for lookup the latest
available tag.
an option, either on the [user's side](https://github.com/pre-commit/pre-commit/issues/2512)
or the [hook author's side](https://github.com/pre-commit/pre-commit/issues/2508), to define a branch for looking
up the latest available tag.

Thus, `pre-commit autoupdate` is not usable for the hooks described here.

You can avoid changing the `rev` to an unexpected value, by using the `--repo` parameter (may be specified multiple
times), to explicit list repositories that should be updated. An option to explicit exclude
You can avoid changing the `rev` to an unexpected value by using the `--repo` parameter (may be specified multiple
times), to explicitly list repositories that should be updated. An option to explicitly exclude
repositories [will not be implemented](https://github.com/pre-commit/pre-commit/issues/1959) into `pre-commit`.
6 changes: 6 additions & 0 deletions docs/pyproject.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ The file(s) can be of any format, but if you intend to publish to PyPI keep the
https://packaging.python.org/en/latest/guides/making-a-pypi-friendly-readme/) in
mind. README paths are implicitly relative to `pyproject.toml`.

{{% note %}}
Whether paths are case-sensitive follows platform defaults, but it is recommended to keep cases.

To be specific, you can set `readme = "rEaDmE.mD"` for `README.md` on macOS and Windows, but Linux users can't `poetry install` after cloning your repo. This is because macOS and Windows are case-insensitive and case-preserving.
{{% /note %}}

The contents of the README file(s) are used to populate the [Description
field](https://packaging.python.org/en/latest/specifications/core-metadata/#description-optional)
of your distribution's metadata (similar to `long_description` in setuptools).
Expand Down
2 changes: 1 addition & 1 deletion docs/repositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ poetry source add --priority=default foo https://foo.bar/simple/
{{% warning %}}

In a future version of Poetry, PyPI will be disabled automatically
if there is at least one custom source configured with another priority than `explicit`.
if at least one custom primary source is configured.
If you are using custom sources in addition to PyPI, you should configure PyPI explicitly
with a certain priority, e.g.

Expand Down
26 changes: 24 additions & 2 deletions src/poetry/console/commands/check.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from cleo.helpers import option

from poetry.console.commands.command import Command


if TYPE_CHECKING:
from pathlib import Path


class CheckCommand(Command):
name = "check"
description = (
Expand All @@ -23,7 +29,7 @@ class CheckCommand(Command):
),
]

def validate_classifiers(
def _validate_classifiers(
self, project_classifiers: set[str]
) -> tuple[list[str], list[str]]:
"""Identify unrecognized and deprecated trove classifiers.
Expand Down Expand Up @@ -73,6 +79,17 @@ def validate_classifiers(

return errors, warnings

def _validate_readme(self, readme: str | list[str], poetry_file: Path) -> list[str]:
"""Check existence of referenced readme files"""

readmes = [readme] if isinstance(readme, str) else readme

errors = []
for name in readmes:
if not (poetry_file.parent / name).exists():
errors.append(f"Declared README file does not exist: {name}")
return errors

def handle(self) -> int:
from poetry.factory import Factory
from poetry.pyproject.toml import PyProjectTOML
Expand All @@ -84,10 +101,15 @@ def handle(self) -> int:

# Validate trove classifiers
project_classifiers = set(config.get("classifiers", []))
errors, warnings = self.validate_classifiers(project_classifiers)
errors, warnings = self._validate_classifiers(project_classifiers)
check_result["errors"].extend(errors)
check_result["warnings"].extend(warnings)

# Validate readme (files must exist)
if "readme" in config:
errors = self._validate_readme(config["readme"], poetry_file)
check_result["errors"].extend(errors)

# Verify that lock file is consistent
if self.option("lock") and not self.poetry.locker.is_locked():
check_result["errors"] += ["poetry.lock was not found."]
Expand Down
6 changes: 3 additions & 3 deletions src/poetry/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,12 @@ def create_pool(
else:
from poetry.repositories.pypi_repository import PyPiRepository

if pool.repositories:
if pool.has_primary_repositories():
io.write_error_line(
"<warning>"
"Warning: In a future version of Poetry, PyPI will be disabled"
" automatically if at least one custom source is configured"
" with another priority than 'explicit'. In order to avoid"
" automatically if at least one custom primary source is"
" configured. In order to avoid"
" a breaking change and make your pyproject.toml forward"
" compatible, add PyPI explicitly via 'poetry source add pypi'."
" By the way, this has the advantage that you can set the"
Expand Down
7 changes: 7 additions & 0 deletions src/poetry/installation/chooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from poetry.config.config import Config
from poetry.config.config import PackageFilterPolicy
from poetry.repositories.http_repository import HTTPRepository
from poetry.utils.wheel import Wheel


Expand Down Expand Up @@ -103,6 +104,12 @@ def _get_links(self, package: Package) -> list[Link]:

assert link.hash_name is not None
h = link.hash_name + ":" + link.hash
if (
h not in hashes
and link.hash_name not in ("sha256", "sha384", "sha512")
and isinstance(repository, HTTPRepository)
):
h = repository.calculate_sha256(link) or h
if h not in hashes:
logger.debug(
"Skipping %s as %s checksum does not match expected value",
Expand Down
38 changes: 20 additions & 18 deletions src/poetry/repositories/http_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,24 +226,7 @@ def _links_to_data(self, links: list[Link], data: PackageInfo) -> dict[str, Any]
and link.hash_name not in ("sha256", "sha384", "sha512")
and hasattr(hashlib, link.hash_name)
):
with self._cached_or_downloaded_file(link) as filepath:
known_hash = (
getattr(hashlib, link.hash_name)() if link.hash_name else None
)
required_hash = hashlib.sha256()

chunksize = 4096
with filepath.open("rb") as f:
while True:
chunk = f.read(chunksize)
if not chunk:
break
if known_hash:
known_hash.update(chunk)
required_hash.update(chunk)

if not known_hash or known_hash.hexdigest() == link.hash:
file_hash = f"{required_hash.name}:{required_hash.hexdigest()}"
file_hash = self.calculate_sha256(link) or file_hash

files.append({"file": link.filename, "hash": file_hash})

Expand All @@ -257,6 +240,25 @@ def _links_to_data(self, links: list[Link], data: PackageInfo) -> dict[str, Any]

return data.asdict()

def calculate_sha256(self, link: Link) -> str | None:
with self._cached_or_downloaded_file(link) as filepath:
known_hash = getattr(hashlib, link.hash_name)() if link.hash_name else None
required_hash = hashlib.sha256()

chunksize = 4096
with filepath.open("rb") as f:
while True:
chunk = f.read(chunksize)
if not chunk:
break
if known_hash:
known_hash.update(chunk)
required_hash.update(chunk)

if not known_hash or known_hash.hexdigest() == link.hash:
return f"{required_hash.name}:{required_hash.hexdigest()}"
return None

def _get_response(self, endpoint: str) -> requests.Response | None:
url = self._url + endpoint
try:
Expand Down
13 changes: 7 additions & 6 deletions src/poetry/utils/env/base_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@

from virtualenv.seed.wheels.embed import get_embed_wheel

from poetry.utils._compat import decode
from poetry.utils._compat import encode
from poetry.utils.env.exceptions import EnvCommandError
from poetry.utils.env.site_packages import SitePackages
from poetry.utils.helpers import get_real_windows_path
Expand Down Expand Up @@ -343,25 +341,28 @@ def _run(self, cmd: list[str], **kwargs: Any) -> str:

try:
if input_:
output = subprocess.run(
output: str = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=stderr,
input=encode(input_),
input=input_,
check=True,
env=env,
text=True,
**kwargs,
).stdout
elif call:
assert stderr != subprocess.PIPE
subprocess.check_call(cmd, stderr=stderr, env=env, **kwargs)
output = ""
else:
output = subprocess.check_output(cmd, stderr=stderr, env=env, **kwargs)
output = subprocess.check_output(
cmd, stderr=stderr, env=env, text=True, **kwargs
)
except CalledProcessError as e:
raise EnvCommandError(e, input=input_)

return decode(output)
return output

def execute(self, bin: str, *args: str, **kwargs: Any) -> int:
command = self.get_command_from_bin(bin) + list(args)
Expand Down
54 changes: 20 additions & 34 deletions src/poetry/utils/env/env_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

from poetry.toml.file import TOMLFile
from poetry.utils._compat import WINDOWS
from poetry.utils._compat import decode
from poetry.utils._compat import encode
from poetry.utils.env.exceptions import EnvCommandError
from poetry.utils.env.exceptions import IncorrectEnvError
Expand Down Expand Up @@ -67,11 +66,9 @@ def _full_python_path(python: str) -> Path | None:
return None

try:
executable = decode(
subprocess.check_output(
[path_python, "-c", "import sys; print(sys.executable)"],
).strip()
)
executable = subprocess.check_output(
[path_python, "-c", "import sys; print(sys.executable)"], text=True
).strip()
return Path(executable)

except CalledProcessError:
Expand Down Expand Up @@ -115,11 +112,9 @@ def get_python_version(
executable = EnvManager._detect_active_python(io)

if executable:
python_patch = decode(
subprocess.check_output(
[executable, "-c", GET_PYTHON_VERSION_ONELINER],
).strip()
)
python_patch = subprocess.check_output(
[executable, "-c", GET_PYTHON_VERSION_ONELINER], text=True
).strip()

version = ".".join(str(v) for v in python_patch.split(".")[:precision])

Expand Down Expand Up @@ -150,10 +145,8 @@ def activate(self, python: str) -> Env:
raise PythonVersionNotFound(python)

try:
python_version_string = decode(
subprocess.check_output(
[python_path, "-c", GET_PYTHON_VERSION_ONELINER],
)
python_version_string = subprocess.check_output(
[python_path, "-c", GET_PYTHON_VERSION_ONELINER], text=True
)
except CalledProcessError as e:
raise EnvCommandError(e)
Expand Down Expand Up @@ -334,10 +327,8 @@ def remove(self, python: str) -> Env:
if python_path.is_file():
# Validate env name if provided env is a full path to python
try:
env_dir = decode(
subprocess.check_output(
[python, "-c", GET_ENV_PATH_ONELINER],
)
env_dir = subprocess.check_output(
[python, "-c", GET_ENV_PATH_ONELINER], text=True
).strip("\n")
env_name = Path(env_dir).name
if not self.check_env_is_for_current_project(env_name, base_env_name):
Expand Down Expand Up @@ -393,10 +384,8 @@ def remove(self, python: str) -> Env:
pass

try:
python_version_string = decode(
subprocess.check_output(
[python, "-c", GET_PYTHON_VERSION_ONELINER],
)
python_version_string = subprocess.check_output(
[python, "-c", GET_PYTHON_VERSION_ONELINER], text=True
)
except CalledProcessError as e:
raise EnvCommandError(e)
Expand Down Expand Up @@ -485,11 +474,9 @@ def create_venv(
python_patch = ".".join([str(v) for v in sys.version_info[:3]])
python_minor = ".".join([str(v) for v in sys.version_info[:2]])
if executable:
python_patch = decode(
subprocess.check_output(
[executable, "-c", GET_PYTHON_VERSION_ONELINER],
).strip()
)
python_patch = subprocess.check_output(
[executable, "-c", GET_PYTHON_VERSION_ONELINER], text=True
).strip()
python_minor = ".".join(python_patch.split(".")[:2])

supported_python = self._poetry.package.python_constraint
Expand Down Expand Up @@ -533,12 +520,11 @@ def create_venv(
continue

try:
python_patch = decode(
subprocess.check_output(
[python, "-c", GET_PYTHON_VERSION_ONELINER],
stderr=subprocess.STDOUT,
).strip()
)
python_patch = subprocess.check_output(
[python, "-c", GET_PYTHON_VERSION_ONELINER],
stderr=subprocess.STDOUT,
text=True,
).strip()
except CalledProcessError:
continue

Expand Down
Loading

0 comments on commit 5cbf87e

Please sign in to comment.