diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index f43f8aa51c9..fce06b435d6 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -31,11 +31,11 @@ jobs:
python-version: ["3.7", "3.8", "3.9", "3.10"]
include:
- os: Ubuntu
- image: ubuntu-latest
+ image: ubuntu-22.04
- os: Windows
image: windows-2022
- os: macOS
- image: macos-11
+ image: macos-12
fail-fast: false
defaults:
run:
@@ -81,15 +81,15 @@ jobs:
- name: Install dependencies
run: poetry install
+ - name: Run mypy
+ run: poetry run mypy
+
- name: Install pytest plugin
run: poetry run pip install pytest-github-actions-annotate-failures
- name: Run pytest
run: poetry run python -m pytest -p no:sugar -q tests/
- - name: Run mypy
- run: poetry run mypy
-
- name: Run pytest (integration suite)
env:
POETRY_TEST_INTEGRATION_GIT_USERNAME: ${GITHUB_ACTOR}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6e6713a837e..f462275abab 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -52,6 +52,12 @@ repos:
args: [--py37-plus]
exclude: ^(install|get)-poetry.py$
+ - repo: https://github.com/hadialqattan/pycln
+ rev: v1.3.2
+ hooks:
+ - id: pycln
+ args: [--all]
+
- repo: https://github.com/pycqa/isort
rev: 5.10.1
hooks:
diff --git a/docs/_index.md b/docs/_index.md
index 016c9f8d98d..3fc000fc248 100644
--- a/docs/_index.md
+++ b/docs/_index.md
@@ -43,9 +43,14 @@ curl -sSL https://install.python-poetry.org | python3 -
**windows powershell install instructions**
```powershell
-(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
+(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -
```
+{{% note %}}
+If you have installed Python through the Microsoft Store, replace `py` with `python` in the command
+above.
+{{% /note %}}
+
{{% note %}}
Note that the installer does not support Python < 3.7.
{{% /note %}}
diff --git a/docs/cli.md b/docs/cli.md
index 11cfc05751b..81768824df0 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -357,11 +357,15 @@ If the package(s) you want to install provide extras, you can specify them
when adding the package:
```bash
-poetry add requests[security,socks]
+poetry add "requests[security,socks]"
poetry add "requests[security,socks]~=2.22.0"
poetry add "git+https://github.com/pallets/flask.git@1.1.1[dotenv,dev]"
```
+{{% warning %}}
+Some shells may treat square braces (`[` and `]`) as special characters. It is suggested to always quote arguments containing these characters to prevent unexpected shell expansion.
+{{% /warning %}}
+
If you want to add a package to a specific group of dependencies, you can use the `--group (-G)` option:
```bash
@@ -509,6 +513,7 @@ See [Configuration]({{< relref "configuration" >}}) for all available settings.
* `--unset`: Remove the configuration element named by `setting-key`.
* `--list`: Show the list of current config variables.
+* `--local`: Set/Get settings that are specific to a project (in the local configuration file `poetry.toml`).
## run
@@ -623,6 +628,7 @@ The table below illustrates the effect of these rules with concrete examples.
### Options
* `--short (-s)`: Output the version number only.
+* `--dry-run`: Do not update pyproject.toml file.
## export
diff --git a/docs/plugins.md b/docs/plugins.md
index 96ca48e191b..761b4d68f19 100644
--- a/docs/plugins.md
+++ b/docs/plugins.md
@@ -67,11 +67,8 @@ from poetry.poetry import Poetry
class MyPlugin(Plugin):
def activate(self, poetry: Poetry, io: IO):
- version = self.get_custom_version()
- io.write_line(f"Setting package version to {version}")
- poetry.package.set_version(version)
-
- def get_custom_version(self) -> str:
+ io.write_line("Setting readme")
+ poetry.package.readme = "README.md"
...
```
diff --git a/docs/pyproject.md b/docs/pyproject.md
index a318d357e60..ec75e222e1e 100644
--- a/docs/pyproject.md
+++ b/docs/pyproject.md
@@ -17,12 +17,20 @@ The `tool.poetry` section of the `pyproject.toml` file is composed of multiple s
The name of the package. **Required**
+```toml
+name = "my-package"
+```
+
## version
The version of the package. **Required**
This should be a valid [PEP 440](https://peps.python.org/pep-0440/) string.
+```toml
+version = "0.1.0"
+```
+
{{% note %}}
If you would like to use semantic versioning for your project, please see
@@ -34,6 +42,10 @@ If you would like to use semantic versioning for your project, please see
A short description of the package. **Required**
+```toml
+description = "A short description of the package."
+```
+
## license
The license of the package.
@@ -61,40 +73,76 @@ More identifiers are listed at the [SPDX Open Source License Registry](https://s
If your project is proprietary and does not use a specific licence, you can set this value as `Proprietary`.
{{% /note %}}
+```toml
+license = "MIT"
+```
+
## authors
The authors of the package. **Required**
This is a list of authors and should contain at least one author. Authors must be in the form `name `.
+```toml
+authors = [
+ "Sébastien Eustace ",
+]
+```
+
## maintainers
The maintainers of the package. **Optional**
This is a list of maintainers and should be distinct from authors. Maintainers may contain an email and be in the form `name `.
+```toml
+maintainers = [
+ "Richard Brave ",
+]
+```
+
## readme
The readme file of the package. **Optional**
The file can be either `README.rst` or `README.md`.
+```toml
+readme = "README.md" # or "README.rst"
+```
+
## homepage
An URL to the website of the project. **Optional**
+```toml
+homepage = "https://python-poetry.org/"
+```
+
## repository
An URL to the repository of the project. **Optional**
+```toml
+repository = "https://github.com/python-poetry/poetry"
+```
+
## documentation
An URL to the documentation of the project. **Optional**
+```toml
+documentation = "https://python-poetry.org/docs/"
+```
+
## keywords
A list of keywords that the package is related to. **Optional**
+```toml
+keywords = ["packaging", "poetry"]
+```
+
## classifiers
A list of PyPI [trove classifiers](https://pypi.org/classifiers/) that describe the project. **Optional**
diff --git a/poetry.lock b/poetry.lock
index fde7774cd30..c773098d0b9 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -116,6 +116,9 @@ category = "dev"
optional = false
python-versions = ">=3.7"
+[package.dependencies]
+tomli = {version = "*", optional = true, markers = "extra == \"toml\""}
+
[package.extras]
toml = ["tomli"]
@@ -170,7 +173,7 @@ python-versions = "*"
[[package]]
name = "dulwich"
-version = "0.20.35"
+version = "0.20.38"
description = "Python Git Library"
category = "main"
optional = false
@@ -183,6 +186,7 @@ urllib3 = ">=1.24.1"
[package.extras]
fastimport = ["fastimport"]
https = ["urllib3[secure] (>=1.24.1)"]
+paramiko = ["paramiko"]
pgp = ["gpg"]
watch = ["pyinotify"]
@@ -520,11 +524,11 @@ diagrams = ["railroad-diagrams", "jinja2"]
[[package]]
name = "pytest"
-version = "6.2.5"
+version = "7.1.2"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.dependencies]
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
@@ -535,23 +539,22 @@ iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
py = ">=1.8.2"
-toml = "*"
+tomli = ">=1.0.0"
[package.extras]
-testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
+testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
[[package]]
name = "pytest-cov"
-version = "2.12.1"
+version = "3.0.0"
description = "Pytest plugin for measuring coverage."
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.6"
[package.dependencies]
-coverage = ">=5.2.1"
+coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
-toml = "*"
[package.extras]
testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"]
@@ -721,7 +724,7 @@ python-versions = ">=3.6"
[[package]]
name = "types-requests"
-version = "2.27.25"
+version = "2.27.26"
description = "Typing stubs for requests"
category = "dev"
optional = false
@@ -801,7 +804,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "2faf18664a94b0dc94c937c24fe8fdbf95aefe3e6bc85e2346fe935047bca353"
+content-hash = "5421b8901d8d4589152de8dafcb79827eaefa00ae7c3af53092be08a0caed970"
[metadata.files]
atomicwrites = [
@@ -972,27 +975,7 @@ distlib = [
{file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
]
dulwich = [
- {file = "dulwich-0.20.35-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:428b5fbb79f8cfba2f5ac6826cc813d1903b44b0780e9ec57e54cbd0f44feb61"},
- {file = "dulwich-0.20.35-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:581c6aa825c9267794747c5cc5ec3831960d96ca7fd9eb0158989e9a4099cbb1"},
- {file = "dulwich-0.20.35-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e11cc7a30b42dbbe5a0b6ebbfbfbb07138a5ffd6175bab2ddbabc9882a1c0438"},
- {file = "dulwich-0.20.35-cp310-cp310-win_amd64.whl", hash = "sha256:22c61a24edb699564b49a9701b723a08fa773f5d3322e8a0cabda897ae86816e"},
- {file = "dulwich-0.20.35-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:9759cf611503681bcdd2950c9d2db04d1c057ecbb62d6fccd095b13771864f1c"},
- {file = "dulwich-0.20.35-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d683b4f30b1dae6b1668336f62f10ff57ebf2a1252c7cc76ad3eeff973879eb"},
- {file = "dulwich-0.20.35-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9d85b6b41c4be6df9ecdc4014d3cbe78a5a44a73c97bccbefac3e5de83bb74be"},
- {file = "dulwich-0.20.35-cp36-cp36m-win_amd64.whl", hash = "sha256:6dc9b082f6ace9890de572260a575a09a996d617f5930edd2858c6f8fedfd7fb"},
- {file = "dulwich-0.20.35-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:28ac2374f09487b02a8cb9b2fad083c358fc927bcfe9803d971614bc00e25076"},
- {file = "dulwich-0.20.35-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:195b21c7a8f85cb2de8938d54fcc6d589d1ccbceaa63bb117796b531065bb68b"},
- {file = "dulwich-0.20.35-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9bdea3a4e8e5e3b1dbd513d9ab8a692f8a9a6f4760633e25c006446bce56fc5e"},
- {file = "dulwich-0.20.35-cp37-cp37m-win_amd64.whl", hash = "sha256:3d3d07b5aa51e6b7d08707c62932da86adbbaaa62552a0129b37d413735c7786"},
- {file = "dulwich-0.20.35-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:5d94cd182fb0da4ec2f182be977b27b9cc1d7dbd0ee9bbf991e101a95fdcd3d8"},
- {file = "dulwich-0.20.35-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f563e9f51e83c47a7df2f3cea79919f700e50d1e5556b6b753730b9cd2be1f47"},
- {file = "dulwich-0.20.35-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f221c3c2fd10260419905bb673cd00129d491e3ed38c7a8d3ac2c7662682dd9b"},
- {file = "dulwich-0.20.35-cp38-cp38-win_amd64.whl", hash = "sha256:c4f4c59445dc5c2341e9cb2fe35e51a890e8a5f42178abec0a96044811c558a9"},
- {file = "dulwich-0.20.35-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:3616a949053eb6bdf34581f57d1f6cb7192a4bb635be1a02c37f6f6dda032277"},
- {file = "dulwich-0.20.35-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134a2f586847c2c58569959a784d7a875b551df4226b639267302217799e4234"},
- {file = "dulwich-0.20.35-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c008b6b562af76cf011d3b5450a0d30edc96feeee7856b081d7400bc7cf42653"},
- {file = "dulwich-0.20.35-cp39-cp39-win_amd64.whl", hash = "sha256:bf228800785754d7a55d52c5f122c26c3ced51f0f3df727fde2c9fefb71d5d76"},
- {file = "dulwich-0.20.35.tar.gz", hash = "sha256:953f6301a9df8a091fa88d55eed394a88bf9988cde8be341775354910918c196"},
+ {file = "dulwich-0.20.38.tar.gz", hash = "sha256:7346790d8735c86fbbc5b70b674f0ef94096c1e5099ba7273491628239817fc8"},
]
entrypoints = [
{file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"},
@@ -1166,12 +1149,12 @@ pyparsing = [
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
]
pytest = [
- {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
- {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
+ {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"},
+ {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"},
]
pytest-cov = [
- {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"},
- {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"},
+ {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
+ {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
]
pytest-mock = [
{file = "pytest-mock-3.7.0.tar.gz", hash = "sha256:5112bd92cc9f186ee96e1a92efc84969ea494939c3aead39c50f421c4cc69534"},
@@ -1285,8 +1268,8 @@ typed-ast = [
{file = "typed_ast-1.5.3.tar.gz", hash = "sha256:27f25232e2dd0edfe1f019d6bfaaf11e86e657d9bdb7b0956db95f560cceb2b3"},
]
types-requests = [
- {file = "types-requests-2.27.25.tar.gz", hash = "sha256:805ae7e38fd9d157153066dc4381cf585fd34dfa212f2fc1fece248c05aac571"},
- {file = "types_requests-2.27.25-py3-none-any.whl", hash = "sha256:2444905c89731dbcb6bbcd6d873a04252445df7623917c640e463b2b28d2a708"},
+ {file = "types-requests-2.27.26.tar.gz", hash = "sha256:a6a04c0274c0949fd0525f35d8b53ac34e77afecbeb3c4932ddc6ce675ac009c"},
+ {file = "types_requests-2.27.26-py3-none-any.whl", hash = "sha256:302137cb5bd482357398a155faf3ed095855fbc6994e952d0496c7fd50f44125"},
]
types-urllib3 = [
{file = "types-urllib3-1.26.14.tar.gz", hash = "sha256:2a2578e4b36341ccd240b00fccda9826988ff0589a44ba4a664bbd69ef348d27"},
diff --git a/pyproject.toml b/pyproject.toml
index 88ede12c255..d7a1db7b42e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -61,8 +61,8 @@ dulwich = "^0.20.35"
[tool.poetry.dev-dependencies]
tox = "^3.18"
-pytest = "^6.2"
-pytest-cov = "^2.8"
+pytest = "^7.1"
+pytest-cov = "^3.0"
pytest-mock = "^3.5"
pytest-sugar = "^0.9"
pre-commit = "^2.6"
@@ -116,18 +116,23 @@ enable_error_code = ["ignore-without-code"]
[[tool.mypy.overrides]]
module = [
- 'poetry.console.commands.init',
- 'poetry.inspection.info',
- 'poetry.installation.chef',
- 'poetry.installation.chooser',
- '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.
+#
+# , meanwhile suppress that
+# warning.
+[[tool.mypy.overrides]]
+module = [
+ 'poetry.installation.executor',
+ 'poetry.repositories.installed_repository',
+]
+warn_unused_ignores = false
+
[[tool.mypy.overrides]]
module = [
'cachecontrol.*',
@@ -138,6 +143,7 @@ module = [
'html5lib.*',
'jsonschema.*',
'pexpect.*',
+ 'pkginfo.*',
'poetry.core.*',
'requests_toolbelt.*',
'shellingham.*',
diff --git a/src/poetry/__version__.py b/src/poetry/__version__.py
index 90c132aaa90..13fa08fc202 100644
--- a/src/poetry/__version__.py
+++ b/src/poetry/__version__.py
@@ -1,10 +1,14 @@
from __future__ import annotations
-from typing import Callable
+from typing import TYPE_CHECKING
from poetry.utils._compat import metadata
+if TYPE_CHECKING:
+ from collections.abc import Callable
+
+
# The metadata.version that we import for Python 3.7 is untyped, work around
# that.
version: Callable[[str], str] = metadata.version
diff --git a/src/poetry/config/config.py b/src/poetry/config/config.py
index 667438243f3..f748386321e 100644
--- a/src/poetry/config/config.py
+++ b/src/poetry/config/config.py
@@ -9,7 +9,6 @@
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
-from typing import Callable
from poetry.core.toml import TOMLFile
from poetry.core.utils.helpers import canonicalize_name
@@ -21,6 +20,8 @@
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from poetry.config.config_source import ConfigSource
diff --git a/src/poetry/console/application.py b/src/poetry/console/application.py
index c4a330b2143..c37cd5a677e 100644
--- a/src/poetry/console/application.py
+++ b/src/poetry/console/application.py
@@ -7,7 +7,6 @@
from importlib import import_module
from typing import TYPE_CHECKING
from typing import Any
-from typing import Callable
from typing import cast
from cleo.application import Application as BaseApplication
@@ -24,6 +23,8 @@
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from cleo.events.console_command_event import ConsoleCommandEvent
from cleo.io.inputs.definition import Definition
from cleo.io.inputs.input import Input
diff --git a/src/poetry/console/command_loader.py b/src/poetry/console/command_loader.py
index 562bd94fd3b..40f6b7f31bd 100644
--- a/src/poetry/console/command_loader.py
+++ b/src/poetry/console/command_loader.py
@@ -1,13 +1,14 @@
from __future__ import annotations
from typing import TYPE_CHECKING
-from typing import Callable
from cleo.exceptions import LogicException
from cleo.loaders.factory_command_loader import FactoryCommandLoader
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from poetry.console.commands.command import Command
diff --git a/src/poetry/console/commands/about.py b/src/poetry/console/commands/about.py
index 94c9721365c..1841c5448c7 100644
--- a/src/poetry/console/commands/about.py
+++ b/src/poetry/console/commands/about.py
@@ -1,10 +1,14 @@
from __future__ import annotations
-from typing import Callable
+from typing import TYPE_CHECKING
from poetry.console.commands.command import Command
+if TYPE_CHECKING:
+ from collections.abc import Callable
+
+
class AboutCommand(Command):
name = "about"
diff --git a/src/poetry/console/commands/init.py b/src/poetry/console/commands/init.py
index d59bfe672f2..7079a52da21 100644
--- a/src/poetry/console/commands/init.py
+++ b/src/poetry/console/commands/init.py
@@ -5,7 +5,9 @@
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
+from typing import Dict
from typing import Mapping
+from typing import Union
from cleo.helpers import option
from tomlkit import inline_table
@@ -22,6 +24,8 @@
from poetry.repositories import Pool
+Requirements = Dict[str, Union[str, Mapping[str, Any]]]
+
class InitCommand(Command):
name = "init"
@@ -162,7 +166,7 @@ def handle(self) -> int:
if self.io.is_interactive():
self.line("")
- requirements = {}
+ requirements: Requirements = {}
if self.option("dependency"):
requirements = self._format_requirements(
self._determine_requirements(self.option("dependency"))
@@ -192,7 +196,7 @@ def handle(self) -> int:
if self.io.is_interactive():
self.line("")
- dev_requirements: dict[str, str] = {}
+ dev_requirements: Requirements = {}
if self.option("dev-dependency"):
dev_requirements = self._format_requirements(
self._determine_requirements(self.option("dev-dependency"))
@@ -264,9 +268,9 @@ def _determine_requirements(
requires: list[str],
allow_prereleases: bool = False,
source: str | None = None,
- ) -> list[dict[str, str | list[str]]]:
+ ) -> list[dict[str, Any]]:
if not requires:
- requires = []
+ result = []
package = self.ask(
"Search for package to add (or leave blank to continue):"
@@ -280,7 +284,7 @@ def _determine_requirements(
or "version" in constraint
):
self.line(f"Adding {package}")
- requires.append(constraint)
+ result.append(constraint)
package = self.ask("\nAdd a package:")
continue
@@ -337,16 +341,15 @@ def _determine_requirements(
constraint["version"] = package_constraint
if package is not False:
- requires.append(constraint)
+ result.append(constraint)
if self.io.is_interactive():
package = self.ask("\nAdd a package:")
- return requires
+ return result
- requires = self._parse_requirements(requires)
result = []
- for requirement in requires:
+ for requirement in self._parse_requirements(requires):
if "git" in requirement or "url" in requirement or "path" in requirement:
result.append(requirement)
continue
@@ -414,10 +417,8 @@ def _parse_requirements(self, requirements: list[str]) -> list[dict[str, Any]]:
for requirement in requirements
]
- def _format_requirements(
- self, requirements: list[dict[str, str]]
- ) -> Mapping[str, str | Mapping[str, str]]:
- requires = {}
+ def _format_requirements(self, requirements: list[dict[str, str]]) -> Requirements:
+ requires: Requirements = {}
for requirement in requirements:
name = requirement.pop("name")
constraint: str | InlineTable
diff --git a/src/poetry/console/commands/plugin/add.py b/src/poetry/console/commands/plugin/add.py
index 70418500b97..ea41fbb1f71 100644
--- a/src/poetry/console/commands/plugin/add.py
+++ b/src/poetry/console/commands/plugin/add.py
@@ -62,11 +62,11 @@ def handle(self) -> int:
from cleo.io.inputs.string_input import StringInput
from cleo.io.io import IO
+ from poetry.core.packages.project_package import ProjectPackage
from poetry.core.pyproject.toml import PyProjectTOML
from poetry.core.semver.helpers import parse_constraint
from poetry.factory import Factory
- from poetry.packages.project_package import ProjectPackage
from poetry.repositories.installed_repository import InstalledRepository
from poetry.utils.env import EnvManager
diff --git a/src/poetry/console/commands/version.py b/src/poetry/console/commands/version.py
index 7b5744d6171..28b3c6815f3 100644
--- a/src/poetry/console/commands/version.py
+++ b/src/poetry/console/commands/version.py
@@ -29,7 +29,14 @@ class VersionCommand(Command):
optional=True,
)
]
- options = [option("short", "s", "Output the version number only")]
+ options = [
+ option("short", "s", "Output the version number only"),
+ option(
+ "dry-run",
+ None,
+ "Do not update pyproject.toml file",
+ ),
+ ]
help = """\
The version command shows the current version of the project or bumps the version of
@@ -66,12 +73,13 @@ def handle(self) -> None:
f" to {version}>"
)
- content: dict[str, Any] = self.poetry.file.read()
- poetry_content = content["tool"]["poetry"]
- poetry_content["version"] = version.text
+ if not self.option("dry-run"):
+ content: dict[str, Any] = self.poetry.file.read()
+ poetry_content = content["tool"]["poetry"]
+ poetry_content["version"] = version.text
- assert isinstance(content, TOMLDocument)
- self.poetry.file.write(content)
+ assert isinstance(content, TOMLDocument)
+ self.poetry.file.write(content)
else:
if self.option("short"):
self.line(self.poetry.package.pretty_version)
diff --git a/src/poetry/factory.py b/src/poetry/factory.py
index 51d560c8abb..3057729641d 100644
--- a/src/poetry/factory.py
+++ b/src/poetry/factory.py
@@ -11,12 +11,12 @@
from cleo.io.null_io import NullIO
from poetry.core.factory import Factory as BaseFactory
+from poetry.core.packages.project_package import ProjectPackage
from poetry.core.toml.file import TOMLFile
from tomlkit.toml_document import TOMLDocument
from poetry.config.config import Config
from poetry.packages.locker import Locker
-from poetry.packages.project_package import ProjectPackage
from poetry.plugins.plugin import Plugin
from poetry.plugins.plugin_manager import PluginManager
from poetry.poetry import Poetry
diff --git a/src/poetry/inspection/info.py b/src/poetry/inspection/info.py
index fd999a50f05..ea4c56f88c9 100644
--- a/src/poetry/inspection/info.py
+++ b/src/poetry/inspection/info.py
@@ -9,7 +9,10 @@
from pathlib import Path
from typing import TYPE_CHECKING
+from typing import Any
+from typing import ContextManager
from typing import Iterator
+from typing import cast
import pkginfo
@@ -27,6 +30,8 @@
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from poetry.core.packages.project_package import ProjectPackage
@@ -81,9 +86,9 @@ def __init__(
self.requires_python = requires_python
self.files = files or []
self._cache_version = cache_version
- self._source_type = None
- self._source_url = None
- self._source_reference = None
+ self._source_type: str | None = None
+ self._source_url: str | None = None
+ self._source_reference: str | None = None
@property
def cache_version(self) -> str | None:
@@ -100,7 +105,7 @@ def update(self, other: PackageInfo) -> PackageInfo:
self._cache_version = other.cache_version or self._cache_version
return self
- def asdict(self) -> dict[str, str | list[str] | None]:
+ def asdict(self) -> dict[str, Any]:
"""
Helper method to convert package info into a dictionary used for caching.
"""
@@ -116,7 +121,7 @@ def asdict(self) -> dict[str, str | list[str] | None]:
}
@classmethod
- def load(cls, data: dict[str, str | list[str] | None]) -> PackageInfo:
+ def load(cls, data: dict[str, Any]) -> PackageInfo:
"""
Helper method to load data from a dictionary produced by `PackageInfo.asdict()`.
@@ -169,7 +174,9 @@ def to_package(
if root_dir or (self._source_type in {"directory"} and self._source_url):
# this is a local poetry project, this means we can extract "richer"
# requirement information, eg: development requirements etc.
- poetry_package = self._get_poetry_package(path=root_dir or self._source_url)
+ poetry_package = self._get_poetry_package(
+ path=root_dir or Path(cast(str, self._source_url))
+ )
if poetry_package:
package.extras = poetry_package.extras
for dependency in poetry_package.requires:
@@ -274,6 +281,7 @@ def _from_sdist_file(cls, path: Path) -> PackageInfo:
# So, we unpack and introspect
suffix = path.suffix
+ context: Callable[[str], ContextManager[zipfile.ZipFile | tarfile.TarFile]]
if suffix == ".zip":
context = zipfile.ZipFile
else:
@@ -286,8 +294,8 @@ def _from_sdist_file(cls, path: Path) -> PackageInfo:
context = tarfile.open
- with temporary_directory() as tmp:
- tmp = Path(tmp)
+ with temporary_directory() as tmp_str:
+ tmp = Path(tmp_str)
with context(path.as_posix()) as archive:
archive.extractall(tmp.as_posix())
@@ -394,7 +402,7 @@ def from_metadata(cls, path: Path) -> PackageInfo | None:
if path.suffix in {".dist-info", ".egg-info"}:
directories = [path]
else:
- directories = cls._find_dist_info(path=path)
+ directories = list(cls._find_dist_info(path=path))
for directory in directories:
try:
@@ -463,6 +471,7 @@ def from_directory(cls, path: Path, disable_build: bool = False) -> PackageInfo:
build is attempted in order to gather metadata.
"""
project_package = cls._get_poetry_package(path)
+ info: PackageInfo | None
if project_package:
info = cls.from_package(project_package)
else:
@@ -480,6 +489,7 @@ def from_directory(cls, path: Path, disable_build: bool = False) -> PackageInfo:
# we discovered PkgInfo but no requirements were listed
+ assert info
info._source_type = "directory"
info._source_url = path.as_posix()
diff --git a/src/poetry/installation/__init__.py b/src/poetry/installation/__init__.py
index b7bc1c52e31..42ff15e3a35 100644
--- a/src/poetry/installation/__init__.py
+++ b/src/poetry/installation/__init__.py
@@ -1,3 +1,6 @@
from __future__ import annotations
from poetry.installation.installer import Installer
+
+
+__all__ = ["Installer"]
diff --git a/src/poetry/installation/chef.py b/src/poetry/installation/chef.py
index 0cf7b54e3b5..51cad799ab7 100644
--- a/src/poetry/installation/chef.py
+++ b/src/poetry/installation/chef.py
@@ -51,7 +51,7 @@ def get_cached_archive_for_link(self, link: Link) -> Link | None:
if not archives:
return link
- candidates = []
+ candidates: list[tuple[float | None, Link]] = []
for archive in archives:
if not archive.is_wheel:
candidates.append((float("inf"), archive))
diff --git a/src/poetry/installation/chooser.py b/src/poetry/installation/chooser.py
index 82659d444eb..ebfbbdaa765 100644
--- a/src/poetry/installation/chooser.py
+++ b/src/poetry/installation/chooser.py
@@ -4,6 +4,7 @@
import re
from typing import TYPE_CHECKING
+from typing import Any
from packaging.tags import Tag
@@ -15,6 +16,7 @@
if TYPE_CHECKING:
from poetry.core.packages.package import Package
from poetry.core.packages.utils.link import Link
+ from poetry.core.semver.version import Version
from poetry.repositories.pool import Pool
from poetry.utils.env import Env
@@ -144,7 +146,9 @@ def _get_links(self, package: Package) -> list[Link]:
return selected_links
- def _sort_key(self, package: Package, link: Link) -> tuple:
+ def _sort_key(
+ self, package: Package, link: Link
+ ) -> tuple[int, int, int, Version, tuple[Any, ...], int]:
"""
Function to pass as the `key` argument to a call to sorted() to sort
InstallationCandidates by preference.
@@ -168,7 +172,7 @@ def _sort_key(self, package: Package, link: Link) -> tuple:
comparison operators, but then different sdist links
with the same version, would have to be considered equal
"""
- build_tag = ()
+ build_tag: tuple[Any, ...] = ()
binary_preference = 0
if link.is_wheel:
wheel = Wheel(link.filename)
@@ -179,9 +183,11 @@ def _sort_key(self, package: Package, link: Link) -> tuple:
)
# TODO: Binary preference
- pri = -(wheel.get_minimum_supported_index(self._env.supported_tags))
+ pri = -(wheel.get_minimum_supported_index(self._env.supported_tags) or 0)
if wheel.build_tag is not None:
match = re.match(r"^(\d+)(.*)$", wheel.build_tag)
+ if not match:
+ raise ValueError(f"Unable to parse build tag: {wheel.build_tag}")
build_tag_groups = match.groups()
build_tag = (int(build_tag_groups[0]), build_tag_groups[1])
else: # sdist
diff --git a/src/poetry/installation/executor.py b/src/poetry/installation/executor.py
index 8e1f18f78fa..3d64507a191 100644
--- a/src/poetry/installation/executor.py
+++ b/src/poetry/installation/executor.py
@@ -12,6 +12,7 @@
from subprocess import CalledProcessError
from typing import TYPE_CHECKING
from typing import Any
+from typing import cast
from cleo.io.null_io import NullIO
from poetry.core.packages.file_dependency import FileDependency
@@ -21,6 +22,9 @@
from poetry.installation.chef import Chef
from poetry.installation.chooser import Chooser
+from poetry.installation.operations import Install
+from poetry.installation.operations import Uninstall
+from poetry.installation.operations import Update
from poetry.utils._compat import decode
from poetry.utils.authenticator import Authenticator
from poetry.utils.env import EnvCommandError
@@ -31,12 +35,10 @@
if TYPE_CHECKING:
from cleo.io.io import IO
+ from cleo.io.outputs.section_output import SectionOutput
from poetry.core.packages.package import Package
from poetry.config.config import Config
- from poetry.installation.operations import Install
- from poetry.installation.operations import Uninstall
- from poetry.installation.operations import Update
from poetry.installation.operations.operation import Operation
from poetry.repositories import Pool
from poetry.utils.env import Env
@@ -49,7 +51,7 @@ def __init__(
pool: Pool,
config: Config,
io: IO,
- parallel: bool = None,
+ parallel: bool | None = None,
) -> None:
self._env = env
self._io = io
@@ -75,7 +77,7 @@ def __init__(
self._executed_operations = 0
self._executed = {"install": 0, "update": 0, "uninstall": 0}
self._skipped = {"install": 0, "update": 0, "uninstall": 0}
- self._sections = {}
+ self._sections: dict[int, SectionOutput] = {}
self._lock = threading.Lock()
self._shutdown = False
self._hashes: dict[str, str] = {}
@@ -186,7 +188,7 @@ def _get_max_workers(desired_max_workers: int | None = None) -> int:
# (it raises a NotImplementedError), so, in this case, we assume
# that the system only has one CPU.
try:
- default_max_workers = os.cpu_count() + 4
+ default_max_workers = (os.cpu_count() or 1) + 4
except NotImplementedError:
default_max_workers = 5
@@ -312,7 +314,7 @@ def _do_execute_operation(self, operation: Operation) -> int:
return 0
- result = getattr(self, f"_execute_{method}")(operation)
+ result: int = getattr(self, f"_execute_{method}")(operation)
if result != 0:
return result
@@ -373,21 +375,21 @@ def get_operation_message(
source_operation_color += "_dark"
package_color += "_dark"
- if operation.job_type == "install":
+ if isinstance(operation, Install):
return (
f"<{base_tag}>Installing"
f" <{package_color}>{operation.package.name}{package_color}>"
f" (<{operation_color}>{operation.package.full_pretty_version}>)>"
)
- if operation.job_type == "uninstall":
+ if isinstance(operation, Uninstall):
return (
f"<{base_tag}>Removing"
f" <{package_color}>{operation.package.name}{package_color}>"
f" (<{operation_color}>{operation.package.full_pretty_version}>)>"
)
- if operation.job_type == "update":
+ if isinstance(operation, Update):
return (
f"<{base_tag}>Updating"
f" <{package_color}>{operation.initial_package.name}{package_color}> "
@@ -643,7 +645,7 @@ def _validate_archive_hash(archive: Path | Link, package: Package) -> str:
package.name,
archive_path,
)
- archive_hash = "sha256:" + file_dep.hash()
+ archive_hash: str = "sha256:" + file_dep.hash()
known_hashes = {f["hash"] for f in package.files}
if archive_hash not in known_hashes:
@@ -681,7 +683,7 @@ def _download_archive(self, operation: Install | Update, link: Link) -> Path:
progress.start()
done = 0
- archive = self._chef.get_cache_directory_for_link(link) / link.filename
+ archive: Path = self._chef.get_cache_directory_for_link(link) / link.filename
archive.parent.mkdir(parents=True, exist_ok=True)
with archive.open("wb") as f:
for chunk in response.iter_content(chunk_size=4096):
@@ -730,7 +732,7 @@ def _save_url_reference(self, operation: Operation) -> None:
direct_url_json.unlink()
return
- url_reference = None
+ url_reference: dict[str, Any] | None = None
if package.source_type == "git":
url_reference = self._create_git_url_reference(package)
@@ -745,26 +747,16 @@ def _save_url_reference(self, operation: Operation) -> None:
for dist in self._env.site_packages.distributions(
name=package.name, writable_only=True
):
- dist._path.joinpath("direct_url.json").write_text(
- json.dumps(url_reference),
- encoding="utf-8",
- )
+ dist_path = cast(Path, dist._path) # type: ignore[attr-defined]
+ url = dist_path / "direct_url.json"
+ url.write_text(json.dumps(url_reference), encoding="utf-8")
- record = dist._path.joinpath("RECORD")
+ record = dist_path / "RECORD"
if record.exists():
with record.open(mode="a", encoding="utf-8") as f:
writer = csv.writer(f)
- writer.writerow(
- [
- str(
- dist._path.joinpath("direct_url.json").relative_to(
- record.parent.parent
- )
- ),
- "",
- "",
- ]
- )
+ path = url.relative_to(record.parent.parent)
+ writer.writerow([str(path), "", ""])
def _create_git_url_reference(
self, package: Package
@@ -800,24 +792,20 @@ def _create_file_url_reference(
if package.name in self._hashes:
archive_info["hash"] = self._hashes[package.name]
- reference = {
+ return {
"url": Path(package.source_url).as_uri(),
"archive_info": archive_info,
}
- return reference
-
def _create_directory_url_reference(
self, package: Package
- ) -> dict[str, str | dict[str, str]]:
+ ) -> dict[str, str | dict[str, bool]]:
dir_info = {}
if package.develop:
dir_info["editable"] = True
- reference = {
+ return {
"url": Path(package.source_url).as_uri(),
"dir_info": dir_info,
}
-
- return reference
diff --git a/src/poetry/installation/installer.py b/src/poetry/installation/installer.py
index 25b13092673..b033e71f53c 100644
--- a/src/poetry/installation/installer.py
+++ b/src/poetry/installation/installer.py
@@ -58,9 +58,9 @@ def __init__(
self._execute_operations = True
self._lock = False
- self._whitelist = []
+ self._whitelist: list[str] = []
- self._extras = []
+ self._extras: list[str] = []
if executor is None:
executor = Executor(self._env, self._pool, config, self._io)
@@ -171,7 +171,7 @@ def whitelist(self, packages: Iterable[str]) -> Installer:
return self
- def extras(self, extras: list) -> Installer:
+ def extras(self, extras: list[str]) -> Installer:
self._extras = extras
return self
@@ -182,7 +182,7 @@ def use_executor(self, use_executor: bool = True) -> Installer:
return self
def _do_refresh(self) -> int:
- from poetry.puzzle import Solver
+ from poetry.puzzle.solver import Solver
# Checking extras
for extra in self._extras:
@@ -211,7 +211,7 @@ def _do_refresh(self) -> int:
return 0
def _do_install(self, local_repo: Repository) -> int:
- from poetry.puzzle import Solver
+ from poetry.puzzle.solver import Solver
locked_repository = Repository()
if self._update:
@@ -475,9 +475,9 @@ def _populate_local_repo(
def _get_operations_from_lock(
self, locked_repository: Repository
- ) -> Sequence[Operation]:
+ ) -> list[Operation]:
installed_repo = self._installed_repository
- ops = []
+ ops: list[Operation] = []
extra_packages = self._get_extra_packages(locked_repository)
for locked in locked_repository.packages:
diff --git a/src/poetry/installation/operations/__init__.py b/src/poetry/installation/operations/__init__.py
index 68127d928c3..d579ac6b8a0 100644
--- a/src/poetry/installation/operations/__init__.py
+++ b/src/poetry/installation/operations/__init__.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from typing import Union
-
from poetry.installation.operations.install import Install
from poetry.installation.operations.uninstall import Uninstall
from poetry.installation.operations.update import Update
diff --git a/src/poetry/installation/pip_installer.py b/src/poetry/installation/pip_installer.py
index da01d507a16..ae54ff73af5 100644
--- a/src/poetry/installation/pip_installer.py
+++ b/src/poetry/installation/pip_installer.py
@@ -84,6 +84,7 @@ def install(self, package: Package, update: bool = False) -> None:
if update:
args.append("-U")
+ req: str | list[str]
if package.files and not package.source_url:
# Format as a requirements.txt
# We need to create a requirements.txt file
@@ -136,10 +137,10 @@ def remove(self, package: Package) -> None:
if src_dir.exists():
remove_directory(src_dir, force=True)
- def run(self, *args: Any, **kwargs: Any) -> str:
+ def run(self, *args: Any, **kwargs: Any) -> int | str:
return self._env.run_pip(*args, **kwargs)
- def requirement(self, package: Package, formatted: bool = False) -> str:
+ def requirement(self, package: Package, formatted: bool = False) -> str | list[str]:
if formatted and not package.source_type:
req = f"{package.name}=={package.version}"
for f in package.files:
@@ -161,7 +162,7 @@ def requirement(self, package: Package, formatted: bool = False) -> str:
req = os.path.realpath(package.source_url)
if package.develop and package.source_type == "directory":
- req = ["-e", req]
+ return ["-e", req]
return req
@@ -172,7 +173,7 @@ def requirement(self, package: Package, formatted: bool = False) -> str:
)
if package.develop:
- req = ["-e", req]
+ return ["-e", req]
return req
@@ -183,9 +184,12 @@ def requirement(self, package: Package, formatted: bool = False) -> str:
def create_temporary_requirement(self, package: Package) -> str:
fd, name = tempfile.mkstemp("reqs.txt", f"{package.name}-{package.version}")
+ req = self.requirement(package, formatted=True)
+ if isinstance(req, list):
+ req = " ".join(req)
try:
- os.write(fd, encode(self.requirement(package, formatted=True)))
+ os.write(fd, encode(req))
finally:
os.close(fd)
@@ -237,7 +241,7 @@ def install_directory(self, package: Package) -> str | int:
with builder.setup_py():
if package.develop:
return pip_install(
- directory=req,
+ path=req,
environment=self._env,
upgrade=True,
editable=True,
@@ -248,7 +252,7 @@ def install_directory(self, package: Package) -> str | int:
if package.develop:
return pip_install(
- directory=req, environment=self._env, upgrade=True, editable=True
+ path=req, environment=self._env, upgrade=True, editable=True
)
return pip_install(path=req, environment=self._env, deps=False, upgrade=True)
diff --git a/src/poetry/layouts/layout.py b/src/poetry/layouts/layout.py
index f5dfd28e950..c4a91506a35 100644
--- a/src/poetry/layouts/layout.py
+++ b/src/poetry/layouts/layout.py
@@ -3,6 +3,7 @@
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
+from typing import Mapping
from tomlkit import dumps
from tomlkit import inline_table
@@ -50,8 +51,8 @@ def __init__(
author: str | None = None,
license: str | None = None,
python: str = "*",
- dependencies: dict[str, str] | None = None,
- dev_dependencies: dict[str, str] | None = None,
+ dependencies: dict[str, str | Mapping[str, Any]] | None = None,
+ dev_dependencies: dict[str, str | Mapping[str, Any]] | None = None,
) -> None:
self._project = canonicalize_name(project).replace(".", "-")
self._package_path_relative = Path(
@@ -90,7 +91,14 @@ def package_path(self) -> Path:
def get_package_include(self) -> InlineTable | None:
package = inline_table()
- include = self._package_path_relative.parts[0]
+ # If a project is created in the root directory (this is reasonable inside a
+ # docker container, eg )
+ # then parts will be empty.
+ parts = self._package_path_relative.parts
+ if not parts:
+ return None
+
+ include = parts[0]
package.append("include", include) # type: ignore[no-untyped-call]
if self.basedir != Path():
diff --git a/src/poetry/locations.py b/src/poetry/locations.py
index 9ab526fd491..33c5aa8c0ad 100644
--- a/src/poetry/locations.py
+++ b/src/poetry/locations.py
@@ -29,7 +29,7 @@
auth_toml = _LEGACY_CONFIG_DIR / "auth.toml"
if any(file.exists() for file in (auth_toml, config_toml)):
- logger.warn(
+ logger.warning(
"Configuration file exists at %s, reusing this directory.\n\nConsider"
" moving configuration to %s, as support for the legacy directory will be"
" removed in an upcoming release.",
diff --git a/src/poetry/masonry/builders/__init__.py b/src/poetry/masonry/builders/__init__.py
index ea5ca59a998..61662422c39 100644
--- a/src/poetry/masonry/builders/__init__.py
+++ b/src/poetry/masonry/builders/__init__.py
@@ -1,3 +1,6 @@
from __future__ import annotations
from poetry.masonry.builders.editable import EditableBuilder
+
+
+__all__ = ["EditableBuilder"]
diff --git a/src/poetry/mixology/incompatibility.py b/src/poetry/mixology/incompatibility.py
index 83e2391b6ff..4c08f7cef57 100644
--- a/src/poetry/mixology/incompatibility.py
+++ b/src/poetry/mixology/incompatibility.py
@@ -1,7 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
-from typing import Callable
from typing import Iterator
from poetry.mixology.incompatibility_cause import ConflictCause
@@ -14,6 +13,8 @@
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from poetry.mixology.incompatibility_cause import IncompatibilityCause
from poetry.mixology.term import Term
diff --git a/src/poetry/mixology/solutions/providers/__init__.py b/src/poetry/mixology/solutions/providers/__init__.py
index 9470041fd57..cfbd1873848 100644
--- a/src/poetry/mixology/solutions/providers/__init__.py
+++ b/src/poetry/mixology/solutions/providers/__init__.py
@@ -3,3 +3,6 @@
from poetry.mixology.solutions.providers.python_requirement_solution_provider import (
PythonRequirementSolutionProvider,
)
+
+
+__all__ = ["PythonRequirementSolutionProvider"]
diff --git a/src/poetry/mixology/solutions/solutions/__init__.py b/src/poetry/mixology/solutions/solutions/__init__.py
index 51b8449071b..e78e9a53361 100644
--- a/src/poetry/mixology/solutions/solutions/__init__.py
+++ b/src/poetry/mixology/solutions/solutions/__init__.py
@@ -3,3 +3,6 @@
from poetry.mixology.solutions.solutions.python_requirement_solution import (
PythonRequirementSolution,
)
+
+
+__all__ = ["PythonRequirementSolution"]
diff --git a/src/poetry/mixology/version_solver.py b/src/poetry/mixology/version_solver.py
index 6fd225a4958..d39591b4b8f 100644
--- a/src/poetry/mixology/version_solver.py
+++ b/src/poetry/mixology/version_solver.py
@@ -41,18 +41,26 @@ class DependencyCache:
def __init__(self, provider: Provider) -> None:
self.provider = provider
- self.cache: dict[str, list[DependencyPackage]] = {}
+ self.cache: dict[
+ tuple[str, str | None, str | None, str | None], list[DependencyPackage]
+ ] = {}
@functools.lru_cache(maxsize=128)
def search_for(self, dependency: Dependency) -> list[DependencyPackage]:
- complete_name = dependency.complete_name
- packages = self.cache.get(complete_name)
+ key = (
+ dependency.complete_name,
+ dependency.source_type,
+ dependency.source_url,
+ dependency.source_reference,
+ )
+
+ packages = self.cache.get(key)
if packages is None:
packages = self.provider.search_for(dependency)
else:
packages = [p for p in packages if dependency.constraint.allows(p.version)]
- self.cache[complete_name] = packages
+ self.cache[key] = packages
return packages
diff --git a/src/poetry/packages/project_package.py b/src/poetry/packages/project_package.py
deleted file mode 100644
index f2801fc5bdf..00000000000
--- a/src/poetry/packages/project_package.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from poetry.core.packages.project_package import ProjectPackage as _ProjectPackage
-
-
-if TYPE_CHECKING:
- from poetry.core.semver.version import Version
-
-
-class ProjectPackage(_ProjectPackage): # type: ignore[misc]
- def set_version(
- self, version: str | Version, pretty_version: str | None = None
- ) -> None:
- from poetry.core.semver.version import Version
-
- if not isinstance(version, Version):
- self._version = Version.parse(version)
- self._pretty_version = pretty_version or version
- else:
- self._version = version
- self._pretty_version = pretty_version or version.text
diff --git a/src/poetry/publishing/__init__.py b/src/poetry/publishing/__init__.py
index c7aa27edb1c..2cb619a2acd 100644
--- a/src/poetry/publishing/__init__.py
+++ b/src/poetry/publishing/__init__.py
@@ -1,3 +1,6 @@
from __future__ import annotations
from poetry.publishing.publisher import Publisher
+
+
+__all__ = ["Publisher"]
diff --git a/src/poetry/puzzle/__init__.py b/src/poetry/puzzle/__init__.py
index 48280ac9bec..d5bc659574a 100644
--- a/src/poetry/puzzle/__init__.py
+++ b/src/poetry/puzzle/__init__.py
@@ -1,3 +1,6 @@
from __future__ import annotations
from poetry.puzzle.solver import Solver
+
+
+__all__ = ["Solver"]
diff --git a/src/poetry/puzzle/provider.py b/src/poetry/puzzle/provider.py
index e4cefee244e..9fcee1265f0 100644
--- a/src/poetry/puzzle/provider.py
+++ b/src/poetry/puzzle/provider.py
@@ -42,6 +42,8 @@
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from poetry.core.packages.dependency import Dependency
from poetry.core.packages.package import Package
from poetry.core.semver.version_constraint import VersionConstraint
@@ -55,6 +57,24 @@
class Indicator(ProgressIndicator): # type: ignore[misc]
+ CONTEXT: str | None = None
+
+ @staticmethod
+ @contextmanager
+ def context() -> Iterator[Callable[[str | None], None]]:
+ def _set_context(context: str | None) -> None:
+ Indicator.CONTEXT = context
+
+ yield _set_context
+
+ _set_context(None)
+
+ def _formatter_context(self) -> str:
+ if Indicator.CONTEXT is None:
+ return " "
+ else:
+ return f" {Indicator.CONTEXT}> "
+
def _formatter_elapsed(self) -> str:
elapsed = time.time() - self._start_time
@@ -530,19 +550,33 @@ def complete_package(self, package: DependencyPackage) -> DependencyPackage:
# An example of this is:
# - pypiwin32 (220); sys_platform == "win32" and python_version >= "3.6"
# - pypiwin32 (219); sys_platform == "win32" and python_version < "3.6"
- duplicates: dict[str, list[Dependency]] = {}
+ #
+ # Additional care has to be taken to ensure that hidden constraints like that
+ # of source type, reference etc. are taking into consideration when duplicates
+ # are identified.
+ duplicates: dict[
+ tuple[str, str | None, str | None, str | None], list[Dependency]
+ ] = {}
for dep in dependencies:
- if dep.complete_name not in duplicates:
- duplicates[dep.complete_name] = []
-
- duplicates[dep.complete_name].append(dep)
+ key = (
+ dep.complete_name,
+ dep.source_type,
+ dep.source_url,
+ dep.source_reference,
+ )
+ if key not in duplicates:
+ duplicates[key] = []
+ duplicates[key].append(dep)
dependencies = []
- for dep_name, deps in duplicates.items():
+ for key, deps in duplicates.items():
if len(deps) == 1:
dependencies.append(deps[0])
continue
+ extra_keys = ", ".join(k for k in key[1:] if k is not None)
+ dep_name = f"{key[0]} ({extra_keys})" if extra_keys else key[0]
+
self.debug(f"Duplicate dependencies for {dep_name}")
deps = self._merge_dependencies_by_marker(deps)
@@ -631,7 +665,9 @@ def fmt_warning(d: Dependency) -> str:
dep_other.set_constraint(
dep_other.constraint.intersect(dep_any.constraint)
)
- elif not inverted_marker.is_empty():
+ elif not inverted_marker.is_empty() and self._python_constraint.allows_any(
+ get_python_constraint_from_marker(inverted_marker)
+ ):
# if there is no any marker dependency
# and the inverted marker is not empty,
# a dependency with the inverted union of all markers is required
@@ -794,7 +830,9 @@ def progress(self) -> Iterator[None]:
self._io.write_line("Resolving dependencies...")
yield
else:
- indicator = Indicator(self._io, "{message} ({elapsed:2s})")
+ indicator = Indicator(
+ self._io, "{message}{context}({elapsed:2s})"
+ )
with indicator.auto(
"Resolving dependencies...",
diff --git a/src/poetry/repositories/http.py b/src/poetry/repositories/http.py
index bbfcb8b5c0f..f6cc6fea204 100644
--- a/src/poetry/repositories/http.py
+++ b/src/poetry/repositories/http.py
@@ -15,6 +15,8 @@
from poetry.core.packages.dependency import Dependency
from poetry.core.packages.utils.link import Link
+from poetry.core.semver.helpers import parse_constraint
+from poetry.core.utils.helpers import temporary_directory
from poetry.core.version.markers import parse_marker
from poetry.repositories.cached import CachedRepository
@@ -23,7 +25,6 @@
from poetry.repositories.link_sources.html import HTMLPage
from poetry.utils.authenticator import Authenticator
from poetry.utils.helpers import download_file
-from poetry.utils.helpers import temporary_directory
from poetry.utils.patterns import wheel_file_re
@@ -148,6 +149,14 @@ def _get_info_from_urls(self, urls: dict[str, list[str]]) -> PackageInfo:
info = self._get_info_from_wheel(universal_python2_wheel)
py3_info = self._get_info_from_wheel(universal_python3_wheel)
+
+ if info.requires_python or py3_info.requires_python:
+ info.requires_python = str(
+ parse_constraint(info.requires_python or "^2.7").union(
+ parse_constraint(py3_info.requires_python or "^3")
+ )
+ )
+
if py3_info.requires_dist:
if not info.requires_dist:
info.requires_dist = py3_info.requires_dist
diff --git a/src/poetry/repositories/installed_repository.py b/src/poetry/repositories/installed_repository.py
index d7bea9ed652..d7a6d9abb6e 100644
--- a/src/poetry/repositories/installed_repository.py
+++ b/src/poetry/repositories/installed_repository.py
@@ -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")
@@ -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
@@ -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
@@ -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)
diff --git a/src/poetry/repositories/repository.py b/src/poetry/repositories/repository.py
index d2e624f45bd..63e169b9e3f 100644
--- a/src/poetry/repositories/repository.py
+++ b/src/poetry/repositories/repository.py
@@ -118,7 +118,7 @@ def _get_constraints_from_dependency(
def _log(self, msg: str, level: str = "info") -> None:
logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
- getattr(logger, level)(f"{self.name}: {msg}")
+ getattr(logger, level)(f"Source ({self.name}): {msg}")
def __len__(self) -> int:
return len(self._packages)
diff --git a/src/poetry/utils/env.py b/src/poetry/utils/env.py
index 1b6e9c96763..987a89dfcb7 100644
--- a/src/poetry/utils/env.py
+++ b/src/poetry/utils/env.py
@@ -10,6 +10,7 @@
import subprocess
import sys
import sysconfig
+import warnings
from contextlib import contextmanager
from copy import deepcopy
@@ -17,7 +18,6 @@
from subprocess import CalledProcessError
from typing import TYPE_CHECKING
from typing import Any
-from typing import ContextManager
from typing import Iterable
from typing import Iterator
from typing import TypeVar
@@ -35,6 +35,7 @@
from poetry.core.semver.helpers import parse_constraint
from poetry.core.semver.version import Version
from poetry.core.toml.file import TOMLFile
+from poetry.core.utils.helpers import temporary_directory
from virtualenv.seed.wheels.embed import get_embed_wheel
from poetry.locations import CACHE_DIR
@@ -45,7 +46,6 @@
from poetry.utils.helpers import is_dir_writable
from poetry.utils.helpers import paths_csv
from poetry.utils.helpers import remove_directory
-from poetry.utils.helpers import temporary_directory
if TYPE_CHECKING:
@@ -1550,7 +1550,9 @@ def get_paths(self) -> dict[str, str]:
d = Distribution()
d.parse_config_files()
- obj = d.get_command_obj("install", create=True)
+ with warnings.catch_warnings():
+ warnings.filterwarnings("ignore", "setup.py install is deprecated")
+ obj = d.get_command_obj("install", create=True)
obj.finalize_options()
paths = sysconfig.get_paths().copy()
@@ -1841,7 +1843,7 @@ def _bin(self, bin: str) -> str:
def ephemeral_environment(
executable: str | Path | None = None,
flags: dict[str, bool] = None,
-) -> ContextManager[VirtualEnv]:
+) -> Iterator[VirtualEnv]:
with temporary_directory() as tmp_dir:
# TODO: cache PEP 517 build environment corresponding to each project venv
venv_dir = Path(tmp_dir) / ".venv"
diff --git a/src/poetry/utils/helpers.py b/src/poetry/utils/helpers.py
index c76a49285ce..a0d670c4201 100644
--- a/src/poetry/utils/helpers.py
+++ b/src/poetry/utils/helpers.py
@@ -7,15 +7,14 @@
import tempfile
from collections.abc import Mapping
-from contextlib import contextmanager
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
-from typing import Callable
-from typing import Iterator
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from poetry.core.packages.package import Package
from requests import Session
@@ -34,15 +33,6 @@ def module_name(name: str) -> str:
return canonicalize_name(name).replace(".", "_").replace("-", "_")
-@contextmanager
-def temporary_directory(*args: Any, **kwargs: Any) -> Iterator[str]:
- name = tempfile.mkdtemp(*args, **kwargs)
-
- yield name
-
- remove_directory(name, force=True)
-
-
def get_cert(config: Config, repository_name: str) -> Path | None:
cert = config.get(f"certificates.{repository_name}.cert")
if cert:
@@ -100,15 +90,41 @@ def download_file(
) -> None:
import requests
+ from poetry.puzzle.provider import Indicator
+
get = requests.get if not session else session.get
response = get(url, stream=True)
response.raise_for_status()
- with open(dest, "wb") as f:
- for chunk in response.iter_content(chunk_size=chunk_size):
- if chunk:
- f.write(chunk)
+ set_indicator = False
+ with Indicator.context() as update_context:
+ update_context(f"Downloading {url}")
+
+ if "Content-Length" in response.headers:
+ try:
+ total_size = int(response.headers["Content-Length"])
+ except ValueError:
+ total_size = 0
+
+ fetched_size = 0
+ last_percent = 0
+
+ # if less than 1MB, we simply show that we're downloading
+ # but skip the updating
+ set_indicator = total_size > 1024 * 1024
+
+ with open(dest, "wb") as f:
+ for chunk in response.iter_content(chunk_size=chunk_size):
+ if chunk:
+ f.write(chunk)
+
+ if set_indicator:
+ fetched_size += len(chunk)
+ percent = (fetched_size * 100) // total_size
+ if percent > last_percent:
+ last_percent = percent
+ update_context(f"Downloading {url} {percent:3}%")
def get_package_version_display_string(
diff --git a/src/poetry/vcs/git/backend.py b/src/poetry/vcs/git/backend.py
index 4b7d987240d..70f8dd8ae46 100644
--- a/src/poetry/vcs/git/backend.py
+++ b/src/poetry/vcs/git/backend.py
@@ -149,7 +149,7 @@ def __post_init__(self, repo: Repo | Path | str) -> None:
class Git:
@staticmethod
def as_repo(repo: Path | str) -> Repo:
- return Repo(str(repo)) # type: ignore[no-untyped-call]
+ return Repo(str(repo))
@staticmethod
def get_remote_url(repo: Repo, remote: str = "origin") -> str:
@@ -158,8 +158,8 @@ def get_remote_url(repo: Repo, remote: str = "origin") -> str:
section = (b"remote", remote.encode("utf-8"))
url = ""
- if config.has_section(section): # type: ignore[no-untyped-call]
- value = config.get(section, b"url") # type: ignore[no-untyped-call]
+ if config.has_section(section):
+ value = config.get(section, b"url")
assert value is not None
url = value.decode("utf-8")
@@ -195,12 +195,10 @@ def _fetch_remote_refs(cls, url: str, local: Repo) -> FetchPackResult:
kwargs["username"] = credentials.username
kwargs["password"] = credentials.password
- client, path = get_transport_and_path( # type: ignore[no-untyped-call]
- url, **kwargs
- )
+ client, path = get_transport_and_path(url, **kwargs)
with local:
- result: FetchPackResult = client.fetch( # type: ignore[no-untyped-call]
+ result: FetchPackResult = client.fetch(
path,
local,
determine_wants=local.object_store.determine_wants_all,
@@ -241,7 +239,7 @@ def _clone_legacy(url: str, refspec: GitRefSpec, target: Path) -> Repo:
f"Failed to checkout {url} at '{revision}'"
)
- repo = Repo(str(target)) # type: ignore[no-untyped-call]
+ repo = Repo(str(target))
return repo
@classmethod
@@ -252,10 +250,10 @@ def _clone(cls, url: str, refspec: GitRefSpec, target: Path) -> Repo:
"""
local: Repo
if not target.exists():
- local = Repo.init(str(target), mkdir=True) # type: ignore[no-untyped-call]
- porcelain.remote_add(local, "origin", url) # type: ignore[no-untyped-call]
+ local = Repo.init(str(target), mkdir=True)
+ porcelain.remote_add(local, "origin", url)
else:
- local = Repo(str(target)) # type: ignore[no-untyped-call]
+ local = Repo(str(target))
remote_refs = cls._fetch_remote_refs(url=url, local=local)
@@ -282,7 +280,7 @@ def _clone(cls, url: str, refspec: GitRefSpec, target: Path) -> Repo:
(b"refs/remotes/origin", b"refs/heads/"),
(b"refs/tags", b"refs/tags"),
}:
- local.refs.import_refs( # type: ignore[no-untyped-call]
+ local.refs.import_refs(
base=base,
other={
n[len(prefix) :]: v
@@ -293,7 +291,7 @@ def _clone(cls, url: str, refspec: GitRefSpec, target: Path) -> Repo:
try:
with local:
- local.reset_index() # type: ignore[no-untyped-call]
+ local.reset_index()
except (AssertionError, KeyError) as e:
# this implies the ref we need does not exist or is invalid
if isinstance(e, KeyError):
@@ -335,7 +333,7 @@ def _clone_submodules(cls, repo: Repo) -> None:
url: bytes
path: bytes
- submodules = parse_submodules(config) # type: ignore[no-untyped-call]
+ submodules = parse_submodules(config)
for path, url, _ in submodules:
path_relative = Path(path.decode("utf-8"))
path_absolute = repo_root.joinpath(path_relative)
@@ -395,7 +393,7 @@ def clone(
else:
# check if the current local copy matches the requested ref spec
try:
- current_repo = Repo(str(target)) # type: ignore[no-untyped-call]
+ current_repo = Repo(str(target))
with current_repo:
current_sha = current_repo.head().decode("utf-8")
diff --git a/src/poetry/vcs/git/system.py b/src/poetry/vcs/git/system.py
index 559ad7e6c7c..c1520539462 100644
--- a/src/poetry/vcs/git/system.py
+++ b/src/poetry/vcs/git/system.py
@@ -48,7 +48,7 @@ def run(*args: Any, **kwargs: Any) -> str:
folder.as_posix(),
) + args
- git_command = find_git_command() # type: ignore[no-untyped-call]
+ git_command = find_git_command()
return (
subprocess.check_output(git_command + list(args), stderr=subprocess.STDOUT)
.decode()
diff --git a/tests/compat.py b/tests/compat.py
index a778a76f87f..1e140560366 100644
--- a/tests/compat.py
+++ b/tests/compat.py
@@ -2,12 +2,12 @@
try:
- import zipp
+ import zipp # nopycln: import
except ImportError:
import zipfile as zipp # noqa: F401, TC002
try:
- from typing import Protocol
+ from typing import Protocol # nopycln: import
except ImportError:
from typing_extensions import Protocol # noqa: F401, TC002
diff --git a/tests/config/test_config.py b/tests/config/test_config.py
index 985a7666ecb..68794108dd2 100644
--- a/tests/config/test_config.py
+++ b/tests/config/test_config.py
@@ -4,7 +4,6 @@
import re
from typing import TYPE_CHECKING
-from typing import Callable
from typing import Iterator
import pytest
@@ -17,6 +16,7 @@
if TYPE_CHECKING:
+ from collections.abc import Callable
from pathlib import Path
diff --git a/tests/console/commands/env/helpers.py b/tests/console/commands/env/helpers.py
index 97058c796cb..c9dc0a7a41a 100644
--- a/tests/console/commands/env/helpers.py
+++ b/tests/console/commands/env/helpers.py
@@ -3,12 +3,13 @@
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
-from typing import Callable
from poetry.core.semver.version import Version
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from poetry.core.version.pep440.version import PEP440Version
VERSION_3_7_1 = Version.parse("3.7.1")
diff --git a/tests/console/commands/test_version.py b/tests/console/commands/test_version.py
index bd93589412b..36487a3f38a 100644
--- a/tests/console/commands/test_version.py
+++ b/tests/console/commands/test_version.py
@@ -76,3 +76,12 @@ def test_version_update(tester: CommandTester):
def test_short_version_update(tester: CommandTester):
tester.execute("--short 2.0.0")
assert tester.io.fetch_output() == "2.0.0\n"
+
+
+def test_dry_run(tester: CommandTester):
+ old_pyproject = tester.command.poetry.file.path.read_text()
+ tester.execute("--dry-run major")
+
+ new_pyproject = tester.command.poetry.file.path.read_text()
+ assert tester.io.fetch_output() == "Bumping version from 1.2.3 to 2.0.0\n"
+ assert old_pyproject == new_pyproject
diff --git a/tests/installation/test_installer.py b/tests/installation/test_installer.py
index 9c2d234588c..8d49a4faaf0 100644
--- a/tests/installation/test_installer.py
+++ b/tests/installation/test_installer.py
@@ -1186,8 +1186,15 @@ def test_run_installs_with_local_file(
package: ProjectPackage,
fixture_dir: FixtureDirGetter,
):
+ root_dir = Path(__file__).parent.parent.parent
+ package.root_dir = root_dir
+ locker.set_lock_path(root_dir)
file_path = fixture_dir("distributions/demo-0.1.0-py2.py3-none-any.whl")
- package.add_dependency(Factory.create_dependency("demo", {"file": str(file_path)}))
+ package.add_dependency(
+ Factory.create_dependency(
+ "demo", {"file": str(file_path.relative_to(root_dir))}, root_dir=root_dir
+ )
+ )
repo.add_package(get_package("pendulum", "1.4.4"))
@@ -1206,10 +1213,17 @@ def test_run_installs_wheel_with_no_requires_dist(
package: ProjectPackage,
fixture_dir: FixtureDirGetter,
):
+ root_dir = Path(__file__).parent.parent.parent
+ package.root_dir = root_dir
+ locker.set_lock_path(root_dir)
file_path = fixture_dir(
"wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl"
)
- package.add_dependency(Factory.create_dependency("demo", {"file": str(file_path)}))
+ package.add_dependency(
+ Factory.create_dependency(
+ "demo", {"file": str(file_path.relative_to(root_dir))}, root_dir=root_dir
+ )
+ )
installer.run()
@@ -1228,10 +1242,15 @@ def test_run_installs_with_local_poetry_directory_and_extras(
tmpdir: Path,
fixture_dir: FixtureDirGetter,
):
+ root_dir = Path(__file__).parent.parent.parent
+ package.root_dir = root_dir
+ locker.set_lock_path(root_dir)
file_path = fixture_dir("project_with_extras")
package.add_dependency(
Factory.create_dependency(
- "project-with-extras", {"path": str(file_path), "extras": ["extras_a"]}
+ "project-with-extras",
+ {"path": str(file_path.relative_to(root_dir)), "extras": ["extras_a"]},
+ root_dir=root_dir,
)
)
@@ -1319,9 +1338,16 @@ def test_run_installs_with_local_setuptools_directory(
tmpdir: Path,
fixture_dir: FixtureDirGetter,
):
+ root_dir = Path(__file__).parent.parent.parent
+ package.root_dir = root_dir
+ locker.set_lock_path(root_dir)
file_path = fixture_dir("project_with_setup/")
package.add_dependency(
- Factory.create_dependency("project-with-setup", {"path": str(file_path)})
+ Factory.create_dependency(
+ "project-with-setup",
+ {"path": str(file_path.relative_to(root_dir))},
+ root_dir=root_dir,
+ )
)
repo.add_package(get_package("pendulum", "1.4.4"))
diff --git a/tests/mixology/helpers.py b/tests/mixology/helpers.py
index 544def12bd0..0120bdb72ff 100644
--- a/tests/mixology/helpers.py
+++ b/tests/mixology/helpers.py
@@ -11,7 +11,8 @@
if TYPE_CHECKING:
- from poetry.packages.project_package import ProjectPackage
+ from poetry.core.packages.project_package import ProjectPackage
+
from poetry.repositories import Repository
from tests.mixology.version_solver.conftest import Provider
diff --git a/tests/mixology/version_solver/test_backtracking.py b/tests/mixology/version_solver/test_backtracking.py
index bdbbfba11cc..6354d4c6a48 100644
--- a/tests/mixology/version_solver/test_backtracking.py
+++ b/tests/mixology/version_solver/test_backtracking.py
@@ -8,7 +8,8 @@
if TYPE_CHECKING:
- from poetry.packages.project_package import ProjectPackage
+ from poetry.core.packages.project_package import ProjectPackage
+
from poetry.repositories import Repository
from tests.mixology.version_solver.conftest import Provider
diff --git a/tests/mixology/version_solver/test_basic_graph.py b/tests/mixology/version_solver/test_basic_graph.py
index 9f392b2f26f..f8758f00c3e 100644
--- a/tests/mixology/version_solver/test_basic_graph.py
+++ b/tests/mixology/version_solver/test_basic_graph.py
@@ -8,7 +8,8 @@
if TYPE_CHECKING:
- from poetry.packages.project_package import ProjectPackage
+ from poetry.core.packages.project_package import ProjectPackage
+
from poetry.repositories import Repository
from tests.mixology.version_solver.conftest import Provider
diff --git a/tests/mixology/version_solver/test_dependency_cache.py b/tests/mixology/version_solver/test_dependency_cache.py
new file mode 100644
index 00000000000..469a1e569db
--- /dev/null
+++ b/tests/mixology/version_solver/test_dependency_cache.py
@@ -0,0 +1,59 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from poetry.factory import Factory
+from poetry.mixology.version_solver import DependencyCache
+from tests.mixology.helpers import add_to_repo
+
+
+if TYPE_CHECKING:
+ from poetry.core.packages.project_package import ProjectPackage
+
+ from poetry.repositories import Repository
+ from tests.mixology.version_solver.conftest import Provider
+
+
+def test_solver_dependency_cache_respects_source_type(
+ root: ProjectPackage, provider: Provider, repo: Repository
+):
+ dependency_pypi = Factory.create_dependency("demo", ">=0.1.0")
+ dependency_git = Factory.create_dependency(
+ "demo", {"git": "https://github.com/demo/demo.git"}, groups=["dev"]
+ )
+ root.add_dependency(dependency_pypi)
+ root.add_dependency(dependency_git)
+
+ add_to_repo(repo, "demo", "1.0.0")
+
+ cache = DependencyCache(provider)
+ cache.search_for.cache_clear()
+
+ # ensure cache was never hit for both calls
+ cache.search_for(dependency_pypi)
+ cache.search_for(dependency_git)
+ assert not cache.search_for.cache_info().hits
+
+ packages_pypi = cache.search_for(dependency_pypi)
+ packages_git = cache.search_for(dependency_git)
+
+ assert cache.search_for.cache_info().hits == 2
+ assert cache.search_for.cache_info().currsize == 2
+
+ assert len(packages_pypi) == len(packages_git) == 1
+ assert packages_pypi != packages_git
+
+ package_pypi = packages_pypi[0]
+ package_git = packages_git[0]
+
+ assert package_pypi.package.name == dependency_pypi.name
+ assert package_pypi.package.version.text == "1.0.0"
+
+ assert package_git.package.name == dependency_git.name
+ assert package_git.package.version.text == "0.1.2"
+ assert package_git.package.source_type == dependency_git.source_type
+ assert package_git.package.source_url == dependency_git.source_url
+ assert (
+ package_git.package.source_resolved_reference
+ == "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24"
+ )
diff --git a/tests/mixology/version_solver/test_python_constraint.py b/tests/mixology/version_solver/test_python_constraint.py
index f60e3a49293..52bbdd7bdab 100644
--- a/tests/mixology/version_solver/test_python_constraint.py
+++ b/tests/mixology/version_solver/test_python_constraint.py
@@ -8,7 +8,8 @@
if TYPE_CHECKING:
- from poetry.packages.project_package import ProjectPackage
+ from poetry.core.packages.project_package import ProjectPackage
+
from poetry.repositories import Repository
from tests.mixology.version_solver.conftest import Provider
diff --git a/tests/mixology/version_solver/test_unsolvable.py b/tests/mixology/version_solver/test_unsolvable.py
index 072925e5507..578fcfe688f 100644
--- a/tests/mixology/version_solver/test_unsolvable.py
+++ b/tests/mixology/version_solver/test_unsolvable.py
@@ -8,7 +8,8 @@
if TYPE_CHECKING:
- from poetry.packages.project_package import ProjectPackage
+ from poetry.core.packages.project_package import ProjectPackage
+
from poetry.repositories import Repository
from tests.mixology.version_solver.conftest import Provider
diff --git a/tests/mixology/version_solver/test_with_lock.py b/tests/mixology/version_solver/test_with_lock.py
index cc9e28f12d6..e9d07d21d7b 100644
--- a/tests/mixology/version_solver/test_with_lock.py
+++ b/tests/mixology/version_solver/test_with_lock.py
@@ -9,7 +9,8 @@
if TYPE_CHECKING:
- from poetry.packages.project_package import ProjectPackage
+ from poetry.core.packages.project_package import ProjectPackage
+
from poetry.repositories import Repository
from tests.mixology.version_solver.conftest import Provider
diff --git a/tests/plugins/test_plugin_manager.py b/tests/plugins/test_plugin_manager.py
index 965d04499b5..7332566f14b 100644
--- a/tests/plugins/test_plugin_manager.py
+++ b/tests/plugins/test_plugin_manager.py
@@ -7,9 +7,9 @@
from cleo.io.buffered_io import BufferedIO
from entrypoints import EntryPoint
+from poetry.core.packages.project_package import ProjectPackage
from poetry.packages.locker import Locker
-from poetry.packages.project_package import ProjectPackage
from poetry.plugins import ApplicationPlugin
from poetry.plugins import Plugin
from poetry.plugins.plugin_manager import PluginManager
@@ -32,8 +32,8 @@ def __call__(self, group: str = Plugin.group) -> PluginManager:
class MyPlugin(Plugin):
def activate(self, poetry: Poetry, io: BufferedIO) -> None:
- io.write_line("Updating version")
- poetry.package.set_version("9.9.9")
+ io.write_line("Setting readmes")
+ poetry.package.readmes = ("README.md",)
class MyCommandPlugin(ApplicationPlugin):
@@ -95,8 +95,8 @@ def test_load_plugins_and_activate(
manager.load_plugins()
manager.activate(poetry, io)
- assert poetry.package.version.text == "9.9.9"
- assert io.fetch_output() == "Updating version\n"
+ assert poetry.package.readmes == ("README.md",)
+ assert io.fetch_output() == "Setting readmes\n"
def test_load_plugins_with_invalid_plugin(
diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py
index c86f48d0df2..fe47e8bd220 100644
--- a/tests/puzzle/test_solver.py
+++ b/tests/puzzle/test_solver.py
@@ -14,6 +14,7 @@
from poetry.core.version.markers import parse_marker
from poetry.factory import Factory
+from poetry.packages import DependencyPackage
from poetry.puzzle import Solver
from poetry.puzzle.exceptions import SolverProblemError
from poetry.puzzle.provider import Provider as BaseProvider
@@ -32,7 +33,7 @@
if TYPE_CHECKING:
import httpretty
- from poetry.installation.operation.operation import Operation
+ from poetry.installation.operations.operation import Operation
from poetry.puzzle.transaction import Transaction
DEFAULT_SOURCE_REF = (
@@ -1337,6 +1338,67 @@ def test_solver_duplicate_dependencies_different_constraints_merge_by_marker(
)
+def test_solver_duplicate_dependencies_different_sources_types_are_preserved(
+ solver: Solver, repo: Repository, package: Package
+):
+ pendulum = get_package("pendulum", "2.0.3")
+ repo.add_package(pendulum)
+ repo.add_package(get_package("cleo", "1.0.0"))
+ repo.add_package(get_package("demo", "0.1.0"))
+
+ dependency_pypi = Factory.create_dependency("demo", ">=0.1.0")
+ dependency_git = Factory.create_dependency(
+ "demo", {"git": "https://github.com/demo/demo.git"}, groups=["dev"]
+ )
+ package.add_dependency(dependency_git)
+ package.add_dependency(dependency_pypi)
+
+ demo = Package(
+ "demo",
+ "0.1.2",
+ source_type="git",
+ source_url="https://github.com/demo/demo.git",
+ source_reference=DEFAULT_SOURCE_REF,
+ source_resolved_reference="9cf87a285a2d3fbb0b9fa621997b3acc3631ed24",
+ )
+
+ transaction = solver.solve()
+
+ ops = check_solver_result(
+ transaction,
+ [{"job": "install", "package": pendulum}, {"job": "install", "package": demo}],
+ )
+
+ op = ops[1]
+
+ assert op.package.source_type == demo.source_type
+ assert op.package.source_reference == DEFAULT_SOURCE_REF
+ assert op.package.source_resolved_reference.startswith(
+ demo.source_resolved_reference
+ )
+
+ complete_package = solver.provider.complete_package(
+ DependencyPackage(package.to_dependency(), package)
+ )
+
+ assert len(complete_package.all_requires) == 2
+
+ pypi, git = complete_package.all_requires
+
+ assert isinstance(pypi, Dependency)
+ assert pypi == dependency_pypi
+
+ assert isinstance(git, VCSDependency)
+ assert git.constraint
+ assert git.constraint != dependency_git.constraint
+ assert (git.name, git.source_type, git.source_url, git.source_reference) == (
+ dependency_git.name,
+ dependency_git.source_type,
+ dependency_git.source_url,
+ DEFAULT_SOURCE_REF,
+ )
+
+
def test_solver_duplicate_dependencies_different_constraints_merge_no_markers(
solver: Solver, repo: Repository, package: Package
):
diff --git a/tests/repositories/fixtures/legacy/poetry-test-py2-py3-metadata-merge.html b/tests/repositories/fixtures/legacy/poetry-test-py2-py3-metadata-merge.html
new file mode 100644
index 00000000000..7b43db0f21e
--- /dev/null
+++ b/tests/repositories/fixtures/legacy/poetry-test-py2-py3-metadata-merge.html
@@ -0,0 +1,11 @@
+
+
+
+ Links for poetry-test-py2-py3-metadata-merge
+
+
+ Links for ipython
+ poetry_test_py2_py3_metadata_merge-0.1.0-py2-none-any.whl
+ poetry_test_py2_py3_metadata_merge-0.1.0-py3-none-any.whl
+
+
diff --git a/tests/repositories/fixtures/pypi.org/dists/poetry_test_py2_py3_metadata_merge-0.1.0-py2-none-any.whl b/tests/repositories/fixtures/pypi.org/dists/poetry_test_py2_py3_metadata_merge-0.1.0-py2-none-any.whl
new file mode 100644
index 00000000000..255fcf7dab7
Binary files /dev/null and b/tests/repositories/fixtures/pypi.org/dists/poetry_test_py2_py3_metadata_merge-0.1.0-py2-none-any.whl differ
diff --git a/tests/repositories/fixtures/pypi.org/dists/poetry_test_py2_py3_metadata_merge-0.1.0-py3-none-any.whl b/tests/repositories/fixtures/pypi.org/dists/poetry_test_py2_py3_metadata_merge-0.1.0-py3-none-any.whl
new file mode 100644
index 00000000000..c4f890baafb
Binary files /dev/null and b/tests/repositories/fixtures/pypi.org/dists/poetry_test_py2_py3_metadata_merge-0.1.0-py3-none-any.whl differ
diff --git a/tests/repositories/test_legacy_repository.py b/tests/repositories/test_legacy_repository.py
index f1f44841fda..bb0c33f6a76 100644
--- a/tests/repositories/test_legacy_repository.py
+++ b/tests/repositories/test_legacy_repository.py
@@ -295,6 +295,16 @@ def test_get_package_from_both_py2_and_py3_specific_wheels():
assert str(required[5].marker) == 'sys_platform != "win32"'
+def test_get_package_from_both_py2_and_py3_specific_wheels_python_constraint():
+ repo = MockRepository()
+
+ package = repo.package("poetry-test-py2-py3-metadata-merge", "0.1.0")
+
+ assert package.name == "poetry-test-py2-py3-metadata-merge"
+ assert package.version.text == "0.1.0"
+ assert package.python_versions == ">=2.7,<2.8 || >=3.7,<4.0"
+
+
def test_get_package_with_dist_and_universal_py3_wheel():
repo = MockRepository()
diff --git a/tests/test_factory.py b/tests/test_factory.py
index ab360c14576..a281ef78b4d 100644
--- a/tests/test_factory.py
+++ b/tests/test_factory.py
@@ -28,8 +28,8 @@
class MyPlugin(Plugin):
def activate(self, poetry: Poetry, io: IO) -> None:
- io.write_line("Updating version")
- poetry.package.set_version("9.9.9")
+ io.write_line("Setting readmes")
+ poetry.package.readmes = ("README.md",)
def test_create_poetry():
@@ -343,4 +343,4 @@ def test_create_poetry_with_plugins(mocker: MockerFixture):
poetry = Factory().create_poetry(fixtures_dir / "sample_project")
- assert poetry.package.version.text == "9.9.9"
+ assert poetry.package.readmes == ("README.md",)
diff --git a/tests/utils/fixtures/setups/pendulum/setup.py b/tests/utils/fixtures/setups/pendulum/setup.py
index d0af694c887..705bd404289 100644
--- a/tests/utils/fixtures/setups/pendulum/setup.py
+++ b/tests/utils/fixtures/setups/pendulum/setup.py
@@ -2,7 +2,7 @@
from distutils.core import setup
-from build import *
+from build import * # nopycln: import
packages = [
diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py
index bf8fe8dfe9b..7d83c57a91f 100644
--- a/tests/utils/test_env.py
+++ b/tests/utils/test_env.py
@@ -7,7 +7,6 @@
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
-from typing import Callable
from typing import Iterator
import pytest
@@ -34,6 +33,8 @@
if TYPE_CHECKING:
+ from collections.abc import Callable
+
from pytest_mock import MockerFixture
from poetry.poetry import Poetry
diff --git a/tests/utils/test_setup_reader.py b/tests/utils/test_setup_reader.py
index 7dbf2ba91ae..d72e5386275 100644
--- a/tests/utils/test_setup_reader.py
+++ b/tests/utils/test_setup_reader.py
@@ -2,7 +2,7 @@
import os
-from typing import Callable
+from typing import TYPE_CHECKING
import pytest
@@ -11,6 +11,10 @@
from poetry.utils.setup_reader import SetupReader
+if TYPE_CHECKING:
+ from collections.abc import Callable
+
+
@pytest.fixture()
def setup() -> Callable[[str], str]:
def _setup(name: str) -> str: