Commit 0768be1d by Sébastien Eustace Committed by GitHub

Merge pull request #4260 from python-poetry/dependency-groups

Dependency groups
parents 52788238 5c5af424
...@@ -48,8 +48,8 @@ authors = ["Sébastien Eustace <sebastien@eustace.io>"] ...@@ -48,8 +48,8 @@ authors = ["Sébastien Eustace <sebastien@eustace.io>"]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "*" python = "*"
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "^3.4" pytest = "^6.0"
``` ```
### Initialising a pre-existing project ### Initialising a pre-existing project
...@@ -68,7 +68,7 @@ If you want to add dependencies to your project, you can specify them in the `to ...@@ -68,7 +68,7 @@ If you want to add dependencies to your project, you can specify them in the `to
```toml ```toml
[tool.poetry.dependencies] [tool.poetry.dependencies]
pendulum = "^1.4" pendulum = "^2.1"
``` ```
As you can see, it takes a mapping of **package names** and **version constraints**. As you can see, it takes a mapping of **package names** and **version constraints**.
...@@ -82,7 +82,7 @@ Also, instead of modifying the `pyproject.toml` file by hand, you can use the `a ...@@ -82,7 +82,7 @@ Also, instead of modifying the `pyproject.toml` file by hand, you can use the `a
$ poetry add pendulum $ poetry add pendulum
``` ```
It will automatically find a suitable version constraint **and install** the package and subdependencies. It will automatically find a suitable version constraint **and install** the package and sub-dependencies.
## Using your virtual environment ## Using your virtual environment
...@@ -137,8 +137,8 @@ To deactivate this virtual environment simply use `deactivate`. ...@@ -137,8 +137,8 @@ To deactivate this virtual environment simply use `deactivate`.
### Version constraints ### Version constraints
In our example, we are requesting the `pendulum` package with the version constraint `^1.4`. In our example, we are requesting the `pendulum` package with the version constraint `^2.1`.
This means any version greater or equal to 1.4.0 and less than 2.0.0 (`>=1.4.0 <2.0.0`). This means any version greater or equal to 2.1.0 and less than 3.0.0 (`>=2.1.0 <3.0.0`).
Please read [Dependency specification]({{< relref "dependency-specification" >}} "Dependency specification documentation") for more in-depth information on versions, Please read [Dependency specification]({{< relref "dependency-specification" >}} "Dependency specification documentation") for more in-depth information on versions,
how versions relate to each other, and on the different ways you can specify dependencies. how versions relate to each other, and on the different ways you can specify dependencies.
...@@ -159,7 +159,7 @@ for the version constraint you have specified. ...@@ -159,7 +159,7 @@ for the version constraint you have specified.
## Installing dependencies ## Installing dependencies
To install the defined dependencies for your project, just run the `install` command. To install the defined dependencies for your project, just run the [`install`]({{< relref "cli#install" >}}) command.
```bash ```bash
poetry install poetry install
...@@ -172,7 +172,7 @@ When you run this command, one of two things may happen: ...@@ -172,7 +172,7 @@ When you run this command, one of two things may happen:
If you have never run the command before and there is also no `poetry.lock` file present, If you have never run the command before and there is also no `poetry.lock` file present,
Poetry simply resolves all dependencies listed in your `pyproject.toml` file and downloads the latest version of their files. Poetry simply resolves all dependencies listed in your `pyproject.toml` file and downloads the latest version of their files.
When Poetry has finished installing, it writes all of the packages and the exact versions of them that it downloaded to the `poetry.lock` file, When Poetry has finished installing, it writes all the packages and their exact versions that it downloaded to the `poetry.lock` file,
locking the project to those specific versions. locking the project to those specific versions.
You should commit the `poetry.lock` file to your project repo so that all people working on the project are locked to the same versions of dependencies (more below). You should commit the `poetry.lock` file to your project repo so that all people working on the project are locked to the same versions of dependencies (more below).
...@@ -187,7 +187,7 @@ Either way, running `install` when a `poetry.lock` file is present resolves and ...@@ -187,7 +187,7 @@ Either way, running `install` when a `poetry.lock` file is present resolves and
but Poetry uses the exact versions listed in `poetry.lock` to ensure that the package versions are consistent for everyone working on your project. but Poetry uses the exact versions listed in `poetry.lock` to ensure that the package versions are consistent for everyone working on your project.
As a result you will have all dependencies requested by your `pyproject.toml` file, As a result you will have all dependencies requested by your `pyproject.toml` file,
but they may not all be at the very latest available versions but they may not all be at the very latest available versions
(some of the dependencies listed in the `poetry.lock` file may have released newer versions since the file was created). (some dependencies listed in the `poetry.lock` file may have released newer versions since the file was created).
This is by design, it ensures that your project does not break because of unexpected changes in dependencies. This is by design, it ensures that your project does not break because of unexpected changes in dependencies.
### Commit your `poetry.lock` file to version control ### Commit your `poetry.lock` file to version control
......
...@@ -133,20 +133,36 @@ This ensures that everyone using the library will get the same versions of the d ...@@ -133,20 +133,36 @@ This ensures that everyone using the library will get the same versions of the d
If there is no `poetry.lock` file, Poetry will create one after dependency resolution. If there is no `poetry.lock` file, Poetry will create one after dependency resolution.
You can specify to the command that you do not want the development dependencies installed by passing If you want to exclude one or more dependency group for the installation, you can use
the `--no-dev` option. the `--without` option.
```bash ```bash
poetry install --no-dev poetry install --without test,docs
``` ```
Conversely, you can specify to the command that you only want to install the development dependencies {{% note %}}
by passing the `--dev-only` option. Note that `--no-dev` takes priority if both options are passed. The `--no-dev` option is now deprecated. You should use the `--without dev` notation instead.
{{% /note %}}
You can also select optional dependency groups with the `--with` option.
```bash ```bash
poetry install --dev-only poetry install --with test,docs
``` ```
It's also possible to only install specific dependency groups by using the `only` option.
```bash
poetry install --only test,docs
```
{{% note %}}
The `--dev-only` option is now deprecated. You should use the `--only dev` notation instead.
{{% /note %}}
See [Dependency groups]({{< relref "managing-dependencies#dependency-groups" >}}) for more information
about dependency groups.
If you want to remove old dependencies no longer present in the lock file, use the If you want to remove old dependencies no longer present in the lock file, use the
`--remove-untracked` option. `--remove-untracked` option.
...@@ -179,13 +195,17 @@ If you want to skip this installation, use the `--no-root` option. ...@@ -179,13 +195,17 @@ If you want to skip this installation, use the `--no-root` option.
poetry install --no-root poetry install --no-root
``` ```
Installation of your project's package is also skipped when the `--dev-only` Installation of your project's package is also skipped when the `--only`
option is passed. option is used.
### Options ### Options
* `--no-dev`: Do not install dev dependencies. * `--without`: The dependency groups to ignore for installation.
* `--dev-only`: Only install dev dependencies. * `--with`: The optional dependency groups to include for installation.
* `--only`: The only dependency groups to install.
* `--default`: Only install the default dependencies.
* `--no-dev`: Do not install dev dependencies. (**Deprecated**)
* `--dev-only`: Only install dev dependencies. (**Deprecated**)
* `--no-root`: Do not install the root package (your project). * `--no-root`: Do not install the root package (your project).
* `--dry-run`: Output the operations but do not execute anything (implicitly enables --verbose). * `--dry-run`: Output the operations but do not execute anything (implicitly enables --verbose).
* `--remove-untracked`: Remove dependencies not presented in the lock file * `--remove-untracked`: Remove dependencies not presented in the lock file
...@@ -311,9 +331,19 @@ poetry add "requests[security,socks]~=2.22.0" ...@@ -311,9 +331,19 @@ poetry add "requests[security,socks]~=2.22.0"
poetry add "git+https://github.com/pallets/flask.git@1.1.1[dotenv,dev]" poetry add "git+https://github.com/pallets/flask.git@1.1.1[dotenv,dev]"
``` ```
If you want to add a package to a specific group of dependencies, you can use the `--group (-G)` option:
```bash
poetry add mkdocs --group docs
```
See [Dependency groups]({{< relref "managing-dependencies#dependency-groups" >}}) for more information
about dependency groups.
### Options ### Options
* `--dev (-D)`: Add package as development dependency. * `--group (-D)`: The group to add the dependency to.
* `--dev (-D)`: Add package as development dependency. (**Deprecated**)
* `--editable (-e)`: Add vcs/path dependencies as editable. * `--editable (-e)`: Add vcs/path dependencies as editable.
* `--extras (-E)`: Extras to activate for the dependency. (multiple values allowed) * `--extras (-E)`: Extras to activate for the dependency. (multiple values allowed)
* `--optional`: Add as an optional dependency. * `--optional`: Add as an optional dependency.
...@@ -334,9 +364,19 @@ list of installed packages. ...@@ -334,9 +364,19 @@ list of installed packages.
poetry remove pendulum poetry remove pendulum
``` ```
If you want to remove a package from a specific group of dependencies, you can use the `--group (-G)` option:
```bash
poetry remove mkdocs --group docs
```
See [Dependency groups]({{< relref "managing-dependencies#dependency-groups" >}}) for more information
about dependency groups.
### Options ### Options
* `--dev (-D)`: Removes a package from the development dependencies. * `--group (-D)`: The group to remove the dependency from.
* `--dev (-D)`: Removes a package from the development dependencies. (**Deprecated**)
* `--dry-run` : Outputs the operations but will not execute anything (implicitly enables --verbose). * `--dry-run` : Outputs the operations but will not execute anything (implicitly enables --verbose).
...@@ -365,6 +405,10 @@ dependencies: ...@@ -365,6 +405,10 @@ dependencies:
### Options ### Options
* `--without`: Do not show the information of the specified groups' dependencies.
* `--with`: Show the information of the specified optional groups' dependencies as well.
* `--only`: Only show the information of dependencies belonging to the specified groups.
* `--default`: Only show the information of the default dependencies.
* `--no-dev`: Do not list the dev dependencies. * `--no-dev`: Do not list the dev dependencies.
* `--tree`: List the dependencies as a tree. * `--tree`: List the dependencies as a tree.
* `--latest (-l)`: Show the latest version. * `--latest (-l)`: Show the latest version.
......
...@@ -210,21 +210,21 @@ you can shift from using "inline table" syntax, to the "standard table" syntax. ...@@ -210,21 +210,21 @@ you can shift from using "inline table" syntax, to the "standard table" syntax.
An example where this might be useful is the following: An example where this might be useful is the following:
```toml ```toml
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
black = {version = "19.10b0", allow-prereleases = true, python = "^3.6", markers = "platform_python_implementation == 'CPython'"} black = {version = "19.10b0", allow-prereleases = true, python = "^3.6", markers = "platform_python_implementation == 'CPython'"}
``` ```
As a single line, this is a lot to digest. To make this a little bit easier to As a single line, this is a lot to digest. To make this a bit easier to
work with, you can do the following: work with, you can do the following:
```toml ```toml
[tool.poetry.dev-dependencies.black] [tool.poetry.group.dev.dependencies.black]
version = "19.10b0" version = "19.10b0"
allow-prereleases = true allow-prereleases = true
python = "^3.6" python = "^3.6"
markers = "platform_python_implementation == 'CPython'" markers = "platform_python_implementation == 'CPython'"
``` ```
All of the same information is still present, and ends up providing the exact The same information is still present, and ends up providing the exact
same specification. It's simply split into multiple, slightly more readable, same specification. It's simply split into multiple, slightly more readable,
lines. lines.
---
draft: false
layout: single
menu:
docs:
weight: 11
title: Managing dependencies
type: docs
---
# Managing dependencies
## Dependency groups
Poetry provides a way to **organize** your dependencies by **groups**. For instance, you might have
dependencies that are only needed to test your project or to build the documentation.
To declare a new dependency group, use a `tool.poetry.group.<group>` section
where `<group>` is the name of your dependency group (for instance, `test`):
```toml
[tool.poetry.group.test] # This part can be left out
[tool.poetry.group.test.dependencies]
pytest = "^6.0.0"
pytest-mock = "*"
```
{{% note %}}
All dependencies **must be compatible with each other** across groups since they will
be resolved regardless of whether they are required for installation or not (see [Installing group dependencies]({{< relref "#installing-group-dependencies" >}})).
Think of dependency groups as **labels** associated with your dependencies: they don't have any bearings
on whether their dependencies will be resolved and installed **by default**, they are simply a way to organize
the dependencies logically.
{{% /note %}}
The dependencies declared in `tool.poetry.dependencies` are part of an implicit `default` group.
```toml
[tool.poetry.dependencies] # Default dependency group
httpx = "*"
pendulum = "*"
[tool.poetry.group.test.dependencies]
pytest = "^6.0.0"
pytest-mock = "*"
```
{{% note %}}
**A note about the `dev-dependencies` section**
Any dependency declared in the `dev-dependencies` section will automatically be added to a `dev` group.
So the two following notations are equivalent:
```toml
[tool.poetry.dev-dependencies]
pytest = "^6.0.0"
pytest-mock = "*"
```
```toml
[tool.poetry.group.dev.dependencies]
pytest = "^6.0.0"
pytest-mock = "*"
```
Poetry will slowly transition away from the `dev-dependencies` notation which will soon be deprecated,
so it's advised to migrate your existing development dependencies to the new `group` notation.
{{% /note %}}
### Optional groups
A dependency group can be declared as optional. This makes sense when you have
a group of dependencies that are only required in a particular environment or for
a specific purpose.
```toml
[tool.poetry.group.docs]
optional = true
[tool.poetry.group.docs.dependencies]
mkdocs = "*"
```
Optional groups can be installed in addition to the **default** dependencies by using the `--with`
option of the [`install`]({{< relref "cli#install" >}}) command.
```bash
poetry install --with docs
```
{{% warning %}}
Optional group dependencies will **still** be resolved alongside other dependencies, so
special care should be taken to ensure they are compatible with each other.
{{% /warning %}}
### Adding a dependency to a group
The [`add`]({{< relref "cli#add" >}}) command is the preferred way to add dependencies
to a group. This is done by using the `--group (-G)` option.
```bash
poetry add pytest --group test
```
If the group does not already exist, it will be created automatically.
### Installing group dependencies
**By default**, dependencies across **all groups** will be installed when executing `poetry install`.
You can **exclude** one or more groups with the `--without` option:
```bash
poetry install --without test,docs
```
You can also opt in [optional groups]({{< relref "#optional-groups" >}}) by using the `--with` option:
```bash
poetry install --with docs
```
If you only want to install the **default**, non-grouped, dependencies, you can do so
with the `--default` option:
```bash
poetry install --default
```
Finally, in some case you might want to install **only specific groups** of dependencies
without installing the default dependencies. For that purpose, you can use
the `--only` option.
```bash
poetry install --only docs
```
### Removing dependencies from a group
The [`remove`]({{< relref "cli#remove" >}}) command supports a `--group` option
to remove packages from a specific group:
```bash
poetry remove mkdocs --group docs
```
...@@ -201,7 +201,7 @@ If no format is specified, it will default to include both `sdist` and `wheel`. ...@@ -201,7 +201,7 @@ If no format is specified, it will default to include both `sdist` and `wheel`.
exclude = ["my_package/excluded.py"] exclude = ["my_package/excluded.py"]
``` ```
## `dependencies` and `dev-dependencies` ## dependencies and dependency groups
Poetry is configured to look for dependencies on [PyPi](https://pypi.org) by default. Poetry is configured to look for dependencies on [PyPi](https://pypi.org) by default.
Only the name and a version string are required in this case. Only the name and a version string are required in this case.
...@@ -211,7 +211,8 @@ Only the name and a version string are required in this case. ...@@ -211,7 +211,8 @@ Only the name and a version string are required in this case.
requests = "^2.13.0" requests = "^2.13.0"
``` ```
If you want to use a private repository, you can add it to your `pyproject.toml` file, like so: If you want to use a [private repository]({{< relref "repositories#using-a-private-repository" >}}),
you can add it to your `pyproject.toml` file, like so:
```toml ```toml
[[tool.poetry.source]] [[tool.poetry.source]]
...@@ -229,6 +230,20 @@ python = "^3.6" ...@@ -229,6 +230,20 @@ python = "^3.6"
``` ```
{{% /note %}} {{% /note %}}
You can organize your dependencies in [groups]({{< relref "managing-dependencies#dependency-groups" >}})
to manage them in a more granular way.
```toml
[tool.poetry.group.test.dependencies]
pytest = "*"
[tool.poetry.group.docs.dependencies]
mkdocs = "*"
```
See [Dependency groups]({{< relref "managing-dependencies#dependency-groups" >}}) for a more in-depth look
at how to manage dependency groups.
## `scripts` ## `scripts`
This section describes the scripts or executables that will be installed when installing the package This section describes the scripts or executables that will be installed when installing the package
...@@ -290,7 +305,7 @@ pip install awesome[databases] ...@@ -290,7 +305,7 @@ pip install awesome[databases]
{{% note %}} {{% note %}}
The dependencies specified for each `extra` must already be defined as project dependencies. The dependencies specified for each `extra` must already be defined as project dependencies.
Dependencies listed in the `dev-dependencies` section cannot be specified as extras. Dependencies listed in [dependency groups]({{< relref "managing-dependencies#dependency-groups" >}}) cannot be specified as extras.
{{% /note %}} {{% /note %}}
......
...@@ -397,12 +397,19 @@ version = "1.1.0a5" ...@@ -397,12 +397,19 @@ version = "1.1.0a5"
description = "Poetry PEP 517 Build Backend" description = "Poetry PEP 517 Build Backend"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6,<4.0" python-versions = "^3.6"
develop = false
[package.dependencies] [package.dependencies]
dataclasses = {version = ">=0.8", markers = "python_version >= \"3.6\" and python_version < \"3.7\""} dataclasses = {version = ">=0.8", markers = "python_version >= \"3.6\" and python_version < \"3.7\""}
importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""}
[package.source]
type = "git"
url = "https://github.com/python-poetry/poetry-core.git"
reference = "master"
resolved_reference = "afaa6903f654b695d9411fb548ad10630287c19f"
[[package]] [[package]]
name = "pre-commit" name = "pre-commit"
version = "2.12.1" version = "2.12.1"
...@@ -701,7 +708,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt ...@@ -701,7 +708,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.6" python-versions = "^3.6"
content-hash = "ac67bc6eacbb6b633f9568d69533d80456f632c7bfb9a2aa61aa9dd98e862473" content-hash = "e38d34da68dcd63ef9f9d999f60b8e19d8da7f988031c3a6e6865d51c1fadeda"
[metadata.files] [metadata.files]
appdirs = [ appdirs = [
...@@ -967,10 +974,7 @@ pluggy = [ ...@@ -967,10 +974,7 @@ pluggy = [
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
] ]
poetry-core = [ poetry-core = []
{file = "poetry-core-1.1.0a5.tar.gz", hash = "sha256:1b886de26026865325eae86a5d12eb154b80c0add8067c106eb706757594d85f"},
{file = "poetry_core-1.1.0a5-py3-none-any.whl", hash = "sha256:b347525c1417e9b5c6aee52967eff98c0886853a9e8ab1b9dfb2659913dd37bc"},
]
pre-commit = [ pre-commit = [
{file = "pre_commit-2.12.1-py2.py3-none-any.whl", hash = "sha256:70c5ec1f30406250b706eda35e868b87e3e4ba099af8787e3e8b4b01e84f4712"}, {file = "pre_commit-2.12.1-py2.py3-none-any.whl", hash = "sha256:70c5ec1f30406250b706eda35e868b87e3e4ba099af8787e3e8b4b01e84f4712"},
{file = "pre_commit-2.12.1.tar.gz", hash = "sha256:900d3c7e1bf4cf0374bb2893c24c23304952181405b4d88c9c40b72bda1bb8a9"}, {file = "pre_commit-2.12.1.tar.gz", hash = "sha256:900d3c7e1bf4cf0374bb2893c24c23304952181405b4d88c9c40b72bda1bb8a9"},
......
# -*- coding: utf-8 -*-
from typing import Dict from typing import Dict
from typing import List from typing import List
...@@ -16,6 +15,13 @@ class AddCommand(InstallerCommand, InitCommand): ...@@ -16,6 +15,13 @@ class AddCommand(InstallerCommand, InitCommand):
arguments = [argument("name", "The packages to add.", multiple=True)] arguments = [argument("name", "The packages to add.", multiple=True)]
options = [ options = [
option(
"group",
"-G",
"The group to add the dependency to.",
flag=False,
default="default",
),
option("dev", "D", "Add as a development dependency."), option("dev", "D", "Add as a development dependency."),
option("editable", "e", "Add vcs/path dependencies as editable."), option("editable", "e", "Add vcs/path dependencies as editable."),
option( option(
...@@ -71,31 +77,55 @@ class AddCommand(InstallerCommand, InitCommand): ...@@ -71,31 +77,55 @@ class AddCommand(InstallerCommand, InitCommand):
def handle(self) -> int: def handle(self) -> int:
from tomlkit import inline_table from tomlkit import inline_table
from tomlkit import parse as parse_toml
from tomlkit import table
from poetry.core.semver.helpers import parse_constraint from poetry.core.semver.helpers import parse_constraint
from poetry.factory import Factory
packages = self.argument("name") packages = self.argument("name")
is_dev = self.option("dev") if self.option("dev"):
self.line(
"<warning>The --dev option is deprecated, "
"use the `--group dev` notation instead.</warning>"
)
self.line("")
group = "dev"
else:
group = self.option("group")
if self.option("extras") and len(packages) > 1: if self.option("extras") and len(packages) > 1:
raise ValueError( raise ValueError(
"You can only specify one package " "when using the --extras option" "You can only specify one package when using the --extras option"
) )
section = "dependencies"
if is_dev:
section = "dev-dependencies"
original_content = self.poetry.file.read()
content = self.poetry.file.read() content = self.poetry.file.read()
poetry_content = content["tool"]["poetry"] poetry_content = content["tool"]["poetry"]
if section not in poetry_content: if group == "default":
poetry_content[section] = {} if "dependencies" not in poetry_content:
poetry_content["dependencies"] = table()
existing_packages = self.get_existing_packages_from_input( section = poetry_content["dependencies"]
packages, poetry_content, section else:
) if "group" not in poetry_content:
group_table = table()
group_table._is_super_table = True
poetry_content.value._insert_after("dependencies", "group", group_table)
groups = poetry_content["group"]
if group not in groups:
group_table = parse_toml(
f"[tool.poetry.group.{group}.dependencies]\n\n"
)["tool"]["poetry"]["group"][group]
poetry_content["group"][group] = group_table
if "dependencies" not in poetry_content["group"][group]:
poetry_content["group"][group]["dependencies"] = table()
section = poetry_content["group"][group]["dependencies"]
existing_packages = self.get_existing_packages_from_input(packages, section)
if existing_packages: if existing_packages:
self.notify_about_existing_packages(existing_packages) self.notify_about_existing_packages(existing_packages)
...@@ -165,18 +195,25 @@ class AddCommand(InstallerCommand, InitCommand): ...@@ -165,18 +195,25 @@ class AddCommand(InstallerCommand, InitCommand):
if len(constraint) == 1 and "version" in constraint: if len(constraint) == 1 and "version" in constraint:
constraint = constraint["version"] constraint = constraint["version"]
poetry_content[section][_constraint["name"]] = constraint section[_constraint["name"]] = constraint
self.poetry.package.add_dependency(
Factory.create_dependency(
_constraint["name"],
constraint,
groups=[group],
root_dir=self.poetry.file.parent,
)
)
try: # Refresh the locker
# Write new content self.poetry.set_locker(
self.poetry.file.write(content) self.poetry.locker.__class__(self.poetry.locker.lock.path, poetry_content)
)
self._installer.set_locker(self.poetry.locker)
# Cosmetic new line # Cosmetic new line
self.line("") self.line("")
# Update packages
self.reset_poetry()
self._installer.set_package(self.poetry.package) self._installer.set_package(self.poetry.package)
self._installer.dry_run(self.option("dry-run")) self._installer.dry_run(self.option("dry-run"))
self._installer.verbose(self._io.is_verbose()) self._installer.verbose(self._io.is_verbose())
...@@ -187,31 +224,19 @@ class AddCommand(InstallerCommand, InitCommand): ...@@ -187,31 +224,19 @@ class AddCommand(InstallerCommand, InitCommand):
self._installer.whitelist([r["name"] for r in requirements]) self._installer.whitelist([r["name"] for r in requirements])
status = self._installer.run() status = self._installer.run()
except BaseException:
# Using BaseException here as some exceptions, eg: KeyboardInterrupt, do not inherit from Exception
self.poetry.file.write(original_content)
raise
if status != 0 or self.option("dry-run"):
# Revert changes
if not self.option("dry-run"):
self.line_error(
"\n"
"<error>Failed to add packages, reverting the pyproject.toml file "
"to its original content.</error>"
)
self.poetry.file.write(original_content) if status == 0 and not self.option("dry-run"):
self.poetry.file.write(content)
return status return status
def get_existing_packages_from_input( def get_existing_packages_from_input(
self, packages: List[str], poetry_content: Dict, target_section: str self, packages: List[str], section: Dict
) -> List[str]: ) -> List[str]:
existing_packages = [] existing_packages = []
for name in packages: for name in packages:
for key in poetry_content[target_section]: for key in section:
if key.lower() == name.lower(): if key.lower() == name.lower():
existing_packages.append(name) existing_packages.append(name)
......
...@@ -99,7 +99,7 @@ class DebugResolveCommand(InitCommand): ...@@ -99,7 +99,7 @@ class DebugResolveCommand(InitCommand):
packages = [op.package for op in ops] packages = [op.package for op in ops]
repo = Repository(packages) repo = Repository(packages)
requires = package.requires + package.dev_requires requires = package.all_requires
for pkg in repo.packages: for pkg in repo.packages:
for require in requires: for require in requires:
if pkg.name == require.name: if pkg.name == require.name:
......
...@@ -9,8 +9,38 @@ class InstallCommand(InstallerCommand): ...@@ -9,8 +9,38 @@ class InstallCommand(InstallerCommand):
description = "Installs the project dependencies." description = "Installs the project dependencies."
options = [ options = [
option("no-dev", None, "Do not install the development dependencies."), option(
option("dev-only", None, "Only install the development dependencies."), "without",
None,
"The dependency groups to ignore for installation.",
flag=False,
multiple=True,
),
option(
"with",
None,
"The optional dependency groups to include for installation.",
flag=False,
multiple=True,
),
option("default", None, "Only install the default dependencies."),
option(
"only",
None,
"The only dependency groups to install.",
flag=False,
multiple=True,
),
option(
"no-dev",
None,
"Do not install the development dependencies. (<warning>Deprecated</warning>)",
),
option(
"dev-only",
None,
"Only install the development dependencies. (<warning>Deprecated</warning>)",
),
option( option(
"no-root", None, "Do not install the root package (the current project)." "no-root", None, "Do not install the root package (the current project)."
), ),
...@@ -66,8 +96,51 @@ dependencies and not including the current project, run the command with the ...@@ -66,8 +96,51 @@ dependencies and not including the current project, run the command with the
extras.append(extra) extras.append(extra)
self._installer.extras(extras) self._installer.extras(extras)
self._installer.dev_mode(not self.option("no-dev"))
self._installer.dev_only(self.option("dev-only")) excluded_groups = []
included_groups = []
only_groups = []
if self.option("no-dev"):
self.line(
"<warning>The `<fg=yellow;options=bold>--no-dev</>` option is deprecated,"
"use the `<fg=yellow;options=bold>--without dev</>` notation instead.</warning>"
)
excluded_groups.append("dev")
elif self.option("dev-only"):
self.line(
"<warning>The `<fg=yellow;options=bold>--dev-only</>` option is deprecated,"
"use the `<fg=yellow;options=bold>--only dev</>` notation instead.</warning>"
)
only_groups.append("dev")
excluded_groups.extend(
[
group.strip()
for groups in self.option("without")
for group in groups.split(",")
]
)
included_groups.extend(
[
group.strip()
for groups in self.option("with")
for group in groups.split(",")
]
)
only_groups.extend(
[
group.strip()
for groups in self.option("only")
for group in groups.split(",")
]
)
if self.option("default"):
only_groups.append("default")
self._installer.only_groups(only_groups)
self._installer.without_groups(excluded_groups)
self._installer.with_groups(included_groups)
self._installer.dry_run(self.option("dry-run")) self._installer.dry_run(self.option("dry-run"))
self._installer.remove_untracked(self.option("remove-untracked")) self._installer.remove_untracked(self.option("remove-untracked"))
self._installer.verbose(self._io.is_verbose()) self._installer.verbose(self._io.is_verbose())
...@@ -77,7 +150,7 @@ dependencies and not including the current project, run the command with the ...@@ -77,7 +150,7 @@ dependencies and not including the current project, run the command with the
if return_code != 0: if return_code != 0:
return return_code return return_code
if self.option("no-root") or self.option("dev-only"): if self.option("no-root") or self.option("only"):
return 0 return 0
try: try:
......
from typing import Any
from typing import Dict
from typing import List
from cleo.helpers import argument from cleo.helpers import argument
from cleo.helpers import option from cleo.helpers import option
from ...utils.helpers import canonicalize_name
from .installer_command import InstallerCommand from .installer_command import InstallerCommand
...@@ -12,6 +15,7 @@ class RemoveCommand(InstallerCommand): ...@@ -12,6 +15,7 @@ class RemoveCommand(InstallerCommand):
arguments = [argument("packages", "The packages to remove.", multiple=True)] arguments = [argument("packages", "The packages to remove.", multiple=True)]
options = [ options = [
option("group", "G", "The group to remove the dependency from.", flag=False),
option("dev", "D", "Remove a package from the development dependencies."), option("dev", "D", "Remove a package from the development dependencies."),
option( option(
"dry-run", "dry-run",
...@@ -30,39 +34,70 @@ list of installed packages ...@@ -30,39 +34,70 @@ list of installed packages
def handle(self) -> int: def handle(self) -> int:
packages = self.argument("packages") packages = self.argument("packages")
is_dev = self.option("dev")
if self.option("dev"):
self.line(
"<warning>The --dev option is deprecated, "
"use the `--group dev` notation instead.</warning>"
)
self.line("")
group = "dev"
else:
group = self.option("group")
content = self.poetry.file.read() content = self.poetry.file.read()
poetry_content = content["tool"]["poetry"] poetry_content = content["tool"]["poetry"]
section = "dependencies"
if is_dev: if group is None:
section = "dev-dependencies" removed = []
group_sections = []
# Deleting entries for group_name, group_section in poetry_content.get("group", {}).items():
requirements = {} group_sections.append(
for name in packages: (group_name, group_section.get("dependencies", {}))
found = False )
for key in poetry_content[section]:
if key.lower() == name.lower(): for group_name, section in [
found = True ("default", poetry_content["dependencies"])
requirements[key] = poetry_content[section][key] ] + group_sections:
break removed += self._remove_packages(packages, section, group_name)
if group_name != "default":
if not found: if not section:
raise ValueError("Package {} not found".format(name)) del poetry_content["group"][group_name]
else:
for key in requirements: poetry_content["group"][group_name]["dependencies"] = section
del poetry_content[section][key] elif group == "dev" and "dev-dependencies" in poetry_content:
# We need to account for the old `dev-dependencies` section
dependencies = ( removed = self._remove_packages(
self.poetry.package.requires packages, poetry_content["dev-dependencies"], "dev"
if section == "dependencies" )
else self.poetry.package.dev_requires
if not poetry_content["dev-dependencies"]:
del poetry_content["dev-dependencies"]
else:
removed = self._remove_packages(
packages, poetry_content["group"][group].get("dependencies", {}), group
) )
for i, dependency in enumerate(reversed(dependencies)): if not poetry_content["group"][group]:
if dependency.name == canonicalize_name(key): del poetry_content["group"][group]
del dependencies[-i]
if "group" in poetry_content and not poetry_content["group"]:
del poetry_content["group"]
removed = set(removed)
not_found = set(packages).difference(removed)
if not_found:
raise ValueError(
"The following packages were not found: {}".format(
", ".join(sorted(not_found))
)
)
# Refresh the locker
self.poetry.set_locker(
self.poetry.locker.__class__(self.poetry.locker.lock.path, poetry_content)
)
self._installer.set_locker(self.poetry.locker)
# Update packages # Update packages
self._installer.use_executor( self._installer.use_executor(
...@@ -72,7 +107,7 @@ list of installed packages ...@@ -72,7 +107,7 @@ list of installed packages
self._installer.dry_run(self.option("dry-run")) self._installer.dry_run(self.option("dry-run"))
self._installer.verbose(self._io.is_verbose()) self._installer.verbose(self._io.is_verbose())
self._installer.update(True) self._installer.update(True)
self._installer.whitelist(requirements) self._installer.whitelist(removed)
status = self._installer.run() status = self._installer.run()
...@@ -80,3 +115,19 @@ list of installed packages ...@@ -80,3 +115,19 @@ list of installed packages
self.poetry.file.write(content) self.poetry.file.write(content)
return status return status
def _remove_packages(
self, packages: List[str], section: Dict[str, Any], group_name: str
) -> List[str]:
removed = []
group = self.poetry.package.dependency_group(group_name)
section_keys = list(section.keys())
for package in packages:
for existing_package in section_keys:
if existing_package.lower() == package.lower():
del section[existing_package]
removed.append(package)
group.remove_dependency(package)
return removed
...@@ -14,6 +14,7 @@ if TYPE_CHECKING: ...@@ -14,6 +14,7 @@ if TYPE_CHECKING:
from poetry.core.packages.dependency import Dependency from poetry.core.packages.dependency import Dependency
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.packages.project_package import ProjectPackage
from poetry.repositories import Repository from poetry.repositories import Repository
from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.installed_repository import InstalledRepository
...@@ -25,7 +26,35 @@ class ShowCommand(EnvCommand): ...@@ -25,7 +26,35 @@ class ShowCommand(EnvCommand):
arguments = [argument("package", "The package to inspect", optional=True)] arguments = [argument("package", "The package to inspect", optional=True)]
options = [ options = [
option("no-dev", None, "Do not list the development dependencies."), option(
"without",
None,
"Do not show the information of the specified groups' dependencies.",
flag=False,
multiple=True,
),
option(
"with",
None,
"Show the information of the specified optional groups' dependencies as well.",
flag=False,
multiple=True,
),
option(
"default", None, "Only show the information of the default dependencies."
),
option(
"only",
None,
"Only show the information of dependencies belonging to the specified groups.",
flag=False,
multiple=True,
),
option(
"no-dev",
None,
"Do not list the development dependencies. (<warning>Deprecated</warning>)",
),
option("tree", "t", "List the dependencies as a tree."), option("tree", "t", "List the dependencies as a tree."),
option("latest", "l", "Show the latest version."), option("latest", "l", "Show the latest version."),
option( option(
...@@ -63,19 +92,58 @@ lists all packages available.""" ...@@ -63,19 +92,58 @@ lists all packages available."""
if self.option("outdated"): if self.option("outdated"):
self._io.input.set_option("latest", True) self._io.input.set_option("latest", True)
include_dev = not self.option("no-dev") excluded_groups = []
included_groups = []
only_groups = []
if self.option("no-dev"):
self.line(
"<warning>The `<fg=yellow;options=bold>--no-dev</>` option is deprecated,"
"use the `<fg=yellow;options=bold>--without dev</>` notation instead.</warning>"
)
excluded_groups.append("dev")
excluded_groups.extend(
[
group.strip()
for groups in self.option("without")
for group in groups.split(",")
]
)
included_groups.extend(
[
group.strip()
for groups in self.option("with")
for group in groups.split(",")
]
)
only_groups.extend(
[
group.strip()
for groups in self.option("only")
for group in groups.split(",")
]
)
if self.option("default"):
only_groups.append("default")
locked_repo = self.poetry.locker.locked_repository(True) locked_repo = self.poetry.locker.locked_repository(True)
if only_groups:
root = self.poetry.package.with_dependency_groups(only_groups, only=True)
else:
root = self.poetry.package.with_dependency_groups(
included_groups
).without_dependency_groups(excluded_groups)
# Show tree view if requested # Show tree view if requested
if self.option("tree") and not package: if self.option("tree") and not package:
requires = self.poetry.package.requires requires = root.all_requires
if include_dev:
requires += self.poetry.package.dev_requires
packages = locked_repo.packages packages = locked_repo.packages
for package in packages: for pkg in packages:
for require in requires: for require in requires:
if package.name == require.name: if pkg.name == require.name:
self.display_package_tree(self._io, package, locked_repo) self.display_package_tree(self._io, pkg, locked_repo)
break break
return 0 return 0
...@@ -85,7 +153,7 @@ lists all packages available.""" ...@@ -85,7 +153,7 @@ lists all packages available."""
pool = Pool(ignore_repository_names=True) pool = Pool(ignore_repository_names=True)
pool.add_repository(locked_repo) pool.add_repository(locked_repo)
solver = Solver( solver = Solver(
self.poetry.package, root,
pool=pool, pool=pool,
installed=Repository(), installed=Repository(),
locked=locked_repo, locked=locked_repo,
...@@ -97,11 +165,6 @@ lists all packages available.""" ...@@ -97,11 +165,6 @@ lists all packages available."""
required_locked_packages = set([op.package for op in ops if not op.skipped]) required_locked_packages = set([op.package for op in ops if not op.skipped])
if self.option("no-dev"):
required_locked_packages = [
p for p in locked_packages if p.category == "main"
]
if package: if package:
pkg = None pkg = None
for locked in locked_packages: for locked in locked_packages:
...@@ -160,7 +223,7 @@ lists all packages available.""" ...@@ -160,7 +223,7 @@ lists all packages available."""
current_length += 4 current_length += 4
if show_latest: if show_latest:
latest = self.find_latest_package(locked, include_dev) latest = self.find_latest_package(locked, root)
if not latest: if not latest:
latest = locked latest = locked
...@@ -377,7 +440,7 @@ lists all packages available.""" ...@@ -377,7 +440,7 @@ lists all packages available."""
io.error_output.formatter.set_style(color, style) io.error_output.formatter.set_style(color, style)
def find_latest_package( def find_latest_package(
self, package: "Package", include_dev: bool self, package: "Package", root: "ProjectPackage"
) -> Union["Package", bool]: ) -> Union["Package", bool]:
from cleo.io.null_io import NullIO from cleo.io.null_io import NullIO
...@@ -386,13 +449,11 @@ lists all packages available.""" ...@@ -386,13 +449,11 @@ lists all packages available."""
# find the latest version allowed in this pool # find the latest version allowed in this pool
if package.source_type in ("git", "file", "directory"): if package.source_type in ("git", "file", "directory"):
requires = self.poetry.package.requires requires = root.all_requires
if include_dev:
requires = requires + self.poetry.package.dev_requires
for dep in requires: for dep in requires:
if dep.name == package.name: if dep.name == package.name:
provider = Provider(self.poetry.package, self.poetry.pool, NullIO()) provider = Provider(root, self.poetry.pool, NullIO())
if dep.is_vcs(): if dep.is_vcs():
return provider.search_for_vcs(dep)[0] return provider.search_for_vcs(dep)[0]
......
...@@ -37,7 +37,9 @@ class UpdateCommand(InstallerCommand): ...@@ -37,7 +37,9 @@ class UpdateCommand(InstallerCommand):
if packages: if packages:
self._installer.whitelist({name: "*" for name in packages}) self._installer.whitelist({name: "*" for name in packages})
self._installer.dev_mode(not self.option("no-dev")) if self.option("no-dev"):
self._installer.with_groups(["dev"])
self._installer.dry_run(self.option("dry-run")) self._installer.dry_run(self.option("dry-run"))
self._installer.execute_operations(not self.option("lock")) self._installer.execute_operations(not self.option("lock"))
......
...@@ -157,7 +157,9 @@ class PackageInfo: ...@@ -157,7 +157,9 @@ class PackageInfo:
poetry_package = self._get_poetry_package(path=root_dir or self._source_url) poetry_package = self._get_poetry_package(path=root_dir or self._source_url)
if poetry_package: if poetry_package:
package.extras = poetry_package.extras package.extras = poetry_package.extras
package.requires = poetry_package.requires for dependency in poetry_package.requires:
package.add_dependency(dependency)
return package return package
seen_requirements = set() seen_requirements = set()
...@@ -191,7 +193,7 @@ class PackageInfo: ...@@ -191,7 +193,7 @@ class PackageInfo:
req = dependency.to_pep_508(with_extras=True) req = dependency.to_pep_508(with_extras=True)
if req not in seen_requirements: if req not in seen_requirements:
package.requires.append(dependency) package.add_dependency(dependency)
seen_requirements.add(req) seen_requirements.add(req)
return package return package
......
...@@ -54,8 +54,10 @@ class Installer: ...@@ -54,8 +54,10 @@ class Installer:
self._update = False self._update = False
self._verbose = False self._verbose = False
self._write_lock = True self._write_lock = True
self._dev_mode = True self._without_groups = None
self._dev_only = False self._with_groups = None
self._only_groups = None
self._execute_operations = True self._execute_operations = True
self._lock = False self._lock = False
...@@ -137,21 +139,20 @@ class Installer: ...@@ -137,21 +139,20 @@ class Installer:
def is_verbose(self) -> bool: def is_verbose(self) -> bool:
return self._verbose return self._verbose
def dev_mode(self, dev_mode: bool = True) -> "Installer": def without_groups(self, groups: List[str]) -> "Installer":
self._dev_mode = dev_mode self._without_groups = groups
return self return self
def is_dev_mode(self) -> bool: def with_groups(self, groups: List[str]) -> "Installer":
return self._dev_mode self._with_groups = groups
def dev_only(self, dev_only: bool = False) -> "Installer":
self._dev_only = dev_only
return self return self
def is_dev_only(self) -> bool: def only_groups(self, groups: List[str]) -> "Installer":
return self._dev_only self._only_groups = groups
return self
def update(self, update: bool = True) -> "Installer": def update(self, update: bool = True) -> "Installer":
self._update = update self._update = update
...@@ -283,13 +284,20 @@ class Installer: ...@@ -283,13 +284,20 @@ class Installer:
# If we are only in lock mode, no need to go any further # If we are only in lock mode, no need to go any further
return 0 return 0
root = self._package if self._without_groups or self._with_groups or self._only_groups:
if not self.is_dev_mode(): if self._with_groups:
root = root.clone() # Default dependencies and opted-in optional dependencies
del root.dev_requires[:] root = self._package.with_dependency_groups(self._with_groups)
elif self.is_dev_only(): elif self._without_groups:
root = root.clone() # Default dependencies without selected groups
del root.requires[:] root = self._package.without_dependency_groups(self._without_groups)
else:
# Only selected groups
root = self._package.with_dependency_groups(
self._only_groups, only=True
)
else:
root = self._package.without_optional_dependency_groups()
if self._io.is_verbose(): if self._io.is_verbose():
self._io.write_line("") self._io.write_line("")
...@@ -502,9 +510,7 @@ class Installer: ...@@ -502,9 +510,7 @@ class Installer:
for installed in installed_repo.packages: for installed in installed_repo.packages:
if locked.name == installed.name: if locked.name == installed.name:
is_installed = True is_installed = True
if locked.category == "dev" and not self.is_dev_mode(): if locked.optional and locked.name not in extra_packages:
ops.append(Uninstall(locked))
elif locked.optional and locked.name not in extra_packages:
# Installed but optional and not requested in extras # Installed but optional and not requested in extras
ops.append(Uninstall(locked)) ops.append(Uninstall(locked))
elif locked.version != installed.version: elif locked.version != installed.version:
...@@ -553,11 +559,6 @@ class Installer: ...@@ -553,11 +559,6 @@ class Installer:
if package.name not in extra_packages: if package.name not in extra_packages:
op.skip("Not required") op.skip("Not required")
# If the package is a dev package and dev packages
# are not requested, we skip it
if package.category == "dev" and not self.is_dev_mode():
op.skip("Dev dependencies not requested")
def _get_extra_packages(self, repo: Repository) -> List[str]: def _get_extra_packages(self, repo: Repository) -> List[str]:
""" """
Returns all package names required by extras. Returns all package names required by extras.
......
...@@ -30,7 +30,7 @@ packages = [] ...@@ -30,7 +30,7 @@ packages = []
[tool.poetry.dependencies] [tool.poetry.dependencies]
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
""" """
BUILD_SYSTEM_MIN_VERSION: Optional[str] = None BUILD_SYSTEM_MIN_VERSION: Optional[str] = None
...@@ -143,8 +143,13 @@ class Layout: ...@@ -143,8 +143,13 @@ class Layout:
for dep_name, dep_constraint in self._dependencies.items(): for dep_name, dep_constraint in self._dependencies.items():
poetry_content["dependencies"][dep_name] = dep_constraint poetry_content["dependencies"][dep_name] = dep_constraint
if self._dev_dependencies:
for dep_name, dep_constraint in self._dev_dependencies.items(): for dep_name, dep_constraint in self._dev_dependencies.items():
poetry_content["dev-dependencies"][dep_name] = dep_constraint poetry_content["group"]["dev"]["dependencies"][
dep_name
] = dep_constraint
else:
del poetry_content["group"]
# Add build system # Add build system
build_system = table() build_system = table()
......
...@@ -47,7 +47,7 @@ class Locker: ...@@ -47,7 +47,7 @@ class Locker:
_VERSION = "1.1" _VERSION = "1.1"
_relevant_keys = ["dependencies", "dev-dependencies", "source", "extras"] _relevant_keys = ["dependencies", "group", "source", "extras"]
def __init__(self, lock: Union[str, Path], local_config: dict) -> None: def __init__(self, lock: Union[str, Path], local_config: dict) -> None:
self._lock = TOMLFile(lock) self._lock = TOMLFile(lock)
...@@ -128,7 +128,8 @@ class Locker: ...@@ -128,7 +128,8 @@ class Locker:
source_resolved_reference=source.get("resolved_reference"), source_resolved_reference=source.get("resolved_reference"),
) )
package.description = info.get("description", "") package.description = info.get("description", "")
package.category = info["category"] package.category = info.get("category", "main")
package.groups = info.get("groups", ["default"])
package.optional = info["optional"] package.optional = info["optional"]
if "hashes" in lock_data["metadata"]: if "hashes" in lock_data["metadata"]:
# Old lock so we create dummy files from the hashes # Old lock so we create dummy files from the hashes
......
...@@ -21,3 +21,5 @@ class ProjectPackage(_ProjectPackage): ...@@ -21,3 +21,5 @@ class ProjectPackage(_ProjectPackage):
else: else:
self._version = version self._version = version
self._pretty_version = pretty_version or version.text self._pretty_version = pretty_version or version.text
return self
...@@ -330,7 +330,8 @@ class Provider: ...@@ -330,7 +330,8 @@ class Provider:
for dep in package.extras[extra]: for dep in package.extras[extra]:
dep.activate() dep.activate()
package.requires += package.extras[extra] for extra_dep in package.extras[extra]:
package.add_dependency(extra_dep)
dependency._constraint = package.version dependency._constraint = package.version
dependency._pretty_constraint = package.version.text dependency._pretty_constraint = package.version.text
...@@ -535,10 +536,10 @@ class Provider: ...@@ -535,10 +536,10 @@ class Provider:
# - pypiwin32 (219); sys_platform == "win32" and python_version < "3.6" # - pypiwin32 (219); sys_platform == "win32" and python_version < "3.6"
duplicates = dict() duplicates = dict()
for dep in dependencies: for dep in dependencies:
if dep.name not in duplicates: if dep.complete_name not in duplicates:
duplicates[dep.name] = [] duplicates[dep.complete_name] = []
duplicates[dep.name].append(dep) duplicates[dep.complete_name].append(dep)
dependencies = [] dependencies = []
for dep_name, deps in duplicates.items(): for dep_name, deps in duplicates.items():
...@@ -699,7 +700,12 @@ class Provider: ...@@ -699,7 +700,12 @@ class Provider:
clean_dependencies.append(dep) clean_dependencies.append(dep)
package.requires = clean_dependencies package = DependencyPackage(
package.dependency, package.with_dependency_groups([], only=True)
)
for dep in clean_dependencies:
package.add_dependency(dep)
return package return package
......
...@@ -6,6 +6,7 @@ from contextlib import contextmanager ...@@ -6,6 +6,7 @@ from contextlib import contextmanager
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Callable from typing import Callable
from typing import Dict from typing import Dict
from typing import FrozenSet
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import Tuple from typing import Tuple
...@@ -254,7 +255,7 @@ class Solver: ...@@ -254,7 +255,7 @@ class Solver:
for dep in package.requires: for dep in package.requires:
if dep not in pkg.requires: if dep not in pkg.requires:
pkg.requires.append(dep) pkg.add_dependency(dep)
return packages, depths return packages, depths
...@@ -300,7 +301,7 @@ class Solver: ...@@ -300,7 +301,7 @@ class Solver:
continue continue
if dep not in _package.requires: if dep not in _package.requires:
_package.requires.append(dep) _package.add_dependency(dep)
continue continue
...@@ -312,7 +313,9 @@ class Solver: ...@@ -312,7 +313,9 @@ class Solver:
class DFSNode: class DFSNode:
def __init__(self, id: Tuple[str, str, bool], name: str, base_name: str) -> None: def __init__(
self, id: Tuple[str, FrozenSet[str], bool], name: str, base_name: str
) -> None:
self.id = id self.id = id
self.name = name self.name = name
self.base_name = base_name self.base_name = base_name
...@@ -423,13 +426,15 @@ class PackageNode(DFSNode): ...@@ -423,13 +426,15 @@ class PackageNode(DFSNode):
if not previous: if not previous:
self.category = "dev" self.category = "dev"
self.groups = frozenset()
self.optional = True self.optional = True
else: else:
self.category = dep.category self.category = "main" if "default" in dep.groups else "dev"
self.groups = dep.groups
self.optional = dep.is_optional() self.optional = dep.is_optional()
super().__init__( super().__init__(
(package.complete_name, self.category, self.optional), (package.complete_name, self.groups, self.optional),
package.complete_name, package.complete_name,
package.name, package.name,
) )
...@@ -470,7 +475,7 @@ class PackageNode(DFSNode): ...@@ -470,7 +475,7 @@ class PackageNode(DFSNode):
# we merge the requirements # we merge the requirements
if any( if any(
child.package.name == pkg.name child.package.name == pkg.name
and child.category == dependency.category and child.groups == dependency.groups
for child in children for child in children
): ):
continue continue
...@@ -505,14 +510,20 @@ def aggregate_package_nodes( ...@@ -505,14 +510,20 @@ def aggregate_package_nodes(
) -> Tuple[Package, int]: ) -> Tuple[Package, int]:
package = nodes[0].package package = nodes[0].package
depth = max(node.depth for node in nodes) depth = max(node.depth for node in nodes)
groups = []
for node in nodes:
groups.extend(node.groups)
category = ( category = (
"main" if any(node.category == "main" for node in children + nodes) else "dev" "main" if any("default" in node.groups for node in children + nodes) else "dev"
) )
optional = all(node.optional for node in children + nodes) optional = all(node.optional for node in children + nodes)
for node in nodes: for node in nodes:
node.depth = depth node.depth = depth
node.category = category node.category = category
node.optional = optional node.optional = optional
package.category = category package.category = category
package.optional = optional package.optional = optional
return package, depth return package, depth
...@@ -31,7 +31,7 @@ generate-setup-file = false ...@@ -31,7 +31,7 @@ generate-setup-file = false
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.6" python = "^3.6"
poetry-core = "~1.1.0a5" poetry-core = { git = "https://github.com/python-poetry/poetry-core.git", branch = "master" }
cleo = "^1.0.0a1" cleo = "^1.0.0a1"
crashtest = "^0.3.0" crashtest = "^0.3.0"
requests = "^2.18" requests = "^2.18"
......
...@@ -116,7 +116,7 @@ def test_add_constraint_with_extras(app, repo, tester): ...@@ -116,7 +116,7 @@ def test_add_constraint_with_extras(app, repo, tester):
cachy1 = get_package("cachy", "0.1.0") cachy1 = get_package("cachy", "0.1.0")
cachy1.extras = {"msgpack": [get_dependency("msgpack-python")]} cachy1.extras = {"msgpack": [get_dependency("msgpack-python")]}
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True) msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True)
cachy1.requires = [msgpack_dep] cachy1.add_dependency(msgpack_dep)
repo.add_package(get_package("cachy", "0.2.0")) repo.add_package(get_package("cachy", "0.2.0"))
repo.add_package(cachy1) repo.add_package(cachy1)
...@@ -144,7 +144,7 @@ Package operations: 2 installs, 0 updates, 0 removals ...@@ -144,7 +144,7 @@ Package operations: 2 installs, 0 updates, 0 removals
def test_add_constraint_dependencies(app, repo, tester): def test_add_constraint_dependencies(app, repo, tester):
cachy2 = get_package("cachy", "0.2.0") cachy2 = get_package("cachy", "0.2.0")
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6") msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6")
cachy2.requires = [msgpack_dep] cachy2.add_dependency(msgpack_dep)
repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2) repo.add_package(cachy2)
...@@ -443,7 +443,7 @@ def test_add_constraint_with_extras_option(app, repo, tester): ...@@ -443,7 +443,7 @@ def test_add_constraint_with_extras_option(app, repo, tester):
cachy2 = get_package("cachy", "0.2.0") cachy2 = get_package("cachy", "0.2.0")
cachy2.extras = {"msgpack": [get_dependency("msgpack-python")]} cachy2.extras = {"msgpack": [get_dependency("msgpack-python")]}
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True) msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True)
cachy2.requires = [msgpack_dep] cachy2.add_dependency(msgpack_dep)
repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2) repo.add_package(cachy2)
...@@ -667,13 +667,52 @@ def test_add_constraint_not_found_with_source(app, poetry, mocker, tester): ...@@ -667,13 +667,52 @@ def test_add_constraint_not_found_with_source(app, poetry, mocker, tester):
assert "Could not find a matching version of package cachy" == str(e.value) assert "Could not find a matching version of package cachy" == str(e.value)
def test_add_to_section_that_does_no_exist_yet(app, repo, tester): def test_add_to_section_that_does_not_exist_yet(app, repo, tester):
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))
tester.execute("cachy --group dev")
expected = """\
Using version ^0.2.0 for cachy
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 1 install, 0 updates, 0 removals
• Installing cachy (0.2.0)
"""
assert expected == tester.io.fetch_output()
assert 1 == tester.command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
assert "cachy" in content["group"]["dev"]["dependencies"]
assert content["group"]["dev"]["dependencies"]["cachy"] == "^0.2.0"
expected = """\
[tool.poetry.group.dev.dependencies]
cachy = "^0.2.0"
"""
assert expected in content.as_string()
def test_add_to_dev_section_deprecated(app, repo, tester):
repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0")) repo.add_package(get_package("cachy", "0.2.0"))
tester.execute("cachy --dev") tester.execute("cachy --dev")
expected = """\ expected = """\
The --dev option is deprecated, use the `--group dev` notation instead.
Using version ^0.2.0 for cachy Using version ^0.2.0 for cachy
Updating dependencies Updating dependencies
...@@ -691,8 +730,8 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -691,8 +730,8 @@ Package operations: 1 install, 0 updates, 0 removals
content = app.poetry.file.read()["tool"]["poetry"] content = app.poetry.file.read()["tool"]["poetry"]
assert "cachy" in content["dev-dependencies"] assert "cachy" in content["group"]["dev"]["dependencies"]
assert content["dev-dependencies"]["cachy"] == "^0.2.0" assert content["group"]["dev"]["dependencies"]["cachy"] == "^0.2.0"
def test_add_should_not_select_prereleases(app, repo, tester): def test_add_should_not_select_prereleases(app, repo, tester):
...@@ -919,7 +958,7 @@ def test_add_constraint_with_extras_old_installer(app, repo, installer, old_test ...@@ -919,7 +958,7 @@ def test_add_constraint_with_extras_old_installer(app, repo, installer, old_test
cachy1 = get_package("cachy", "0.1.0") cachy1 = get_package("cachy", "0.1.0")
cachy1.extras = {"msgpack": [get_dependency("msgpack-python")]} cachy1.extras = {"msgpack": [get_dependency("msgpack-python")]}
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True) msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True)
cachy1.requires = [msgpack_dep] cachy1.add_dependency(msgpack_dep)
repo.add_package(get_package("cachy", "0.2.0")) repo.add_package(get_package("cachy", "0.2.0"))
repo.add_package(cachy1) repo.add_package(cachy1)
...@@ -948,7 +987,7 @@ Package operations: 2 installs, 0 updates, 0 removals ...@@ -948,7 +987,7 @@ Package operations: 2 installs, 0 updates, 0 removals
def test_add_constraint_dependencies_old_installer(app, repo, installer, old_tester): def test_add_constraint_dependencies_old_installer(app, repo, installer, old_tester):
cachy2 = get_package("cachy", "0.2.0") cachy2 = get_package("cachy", "0.2.0")
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6") msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6")
cachy2.requires = [msgpack_dep] cachy2.add_dependency(msgpack_dep)
repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2) repo.add_package(cachy2)
...@@ -1245,7 +1284,7 @@ def test_add_constraint_with_extras_option_old_installer( ...@@ -1245,7 +1284,7 @@ def test_add_constraint_with_extras_option_old_installer(
cachy2 = get_package("cachy", "0.2.0") cachy2 = get_package("cachy", "0.2.0")
cachy2.extras = {"msgpack": [get_dependency("msgpack-python")]} cachy2.extras = {"msgpack": [get_dependency("msgpack-python")]}
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True) msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True)
cachy2.requires = [msgpack_dep] cachy2.add_dependency(msgpack_dep)
repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2) repo.add_package(cachy2)
...@@ -1487,7 +1526,7 @@ def test_add_to_section_that_does_no_exist_yet_old_installer( ...@@ -1487,7 +1526,7 @@ def test_add_to_section_that_does_no_exist_yet_old_installer(
repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0")) repo.add_package(get_package("cachy", "0.2.0"))
old_tester.execute("cachy --dev") old_tester.execute("cachy --group dev")
expected = """\ expected = """\
Using version ^0.2.0 for cachy Using version ^0.2.0 for cachy
...@@ -1508,8 +1547,8 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -1508,8 +1547,8 @@ Package operations: 1 install, 0 updates, 0 removals
content = app.poetry.file.read()["tool"]["poetry"] content = app.poetry.file.read()["tool"]["poetry"]
assert "cachy" in content["dev-dependencies"] assert "cachy" in content["group"]["dev"]["dependencies"]
assert content["dev-dependencies"]["cachy"] == "^0.2.0" assert content["group"]["dev"]["dependencies"]["cachy"] == "^0.2.0"
def test_add_should_not_select_prereleases_old_installer( def test_add_should_not_select_prereleases_old_installer(
......
...@@ -71,8 +71,6 @@ packages = [{include = "my_package"}] ...@@ -71,8 +71,6 @@ packages = [{include = "my_package"}]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
[tool.poetry.dev-dependencies]
""" """
...@@ -143,7 +141,7 @@ packages = [{include = "my_package"}] ...@@ -143,7 +141,7 @@ packages = [{include = "my_package"}]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
pendulum = "^2.0.0" pendulum = "^2.0.0"
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "^3.6.0" pytest = "^3.6.0"
""" """
...@@ -175,8 +173,6 @@ packages = [{{include = "my_package"}}] ...@@ -175,8 +173,6 @@ packages = [{{include = "my_package"}}]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^{python}" python = "^{python}"
[tool.poetry.dev-dependencies]
""".format( """.format(
python=".".join(str(c) for c in sys.version_info[:2]) python=".".join(str(c) for c in sys.version_info[:2])
) )
...@@ -220,7 +216,7 @@ packages = [{include = "my_package"}] ...@@ -220,7 +216,7 @@ packages = [{include = "my_package"}]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
demo = {git = "https://github.com/demo/demo.git"} demo = {git = "https://github.com/demo/demo.git"}
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "^3.6.0" pytest = "^3.6.0"
""" """
...@@ -264,7 +260,7 @@ packages = [{include = "my_package"}] ...@@ -264,7 +260,7 @@ packages = [{include = "my_package"}]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
demo = {git = "https://github.com/demo/demo.git", rev = "develop"} demo = {git = "https://github.com/demo/demo.git", rev = "develop"}
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "^3.6.0" pytest = "^3.6.0"
""" """
...@@ -308,7 +304,7 @@ packages = [{include = "my_package"}] ...@@ -308,7 +304,7 @@ packages = [{include = "my_package"}]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
demo = {git = "https://github.com/demo/pyproject-demo.git"} demo = {git = "https://github.com/demo/pyproject-demo.git"}
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "^3.6.0" pytest = "^3.6.0"
""" """
...@@ -355,7 +351,7 @@ packages = [{include = "my_package"}] ...@@ -355,7 +351,7 @@ packages = [{include = "my_package"}]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
demo = {path = "demo"} demo = {path = "demo"}
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "^3.6.0" pytest = "^3.6.0"
""" """
assert expected in tester.io.fetch_output() assert expected in tester.io.fetch_output()
...@@ -403,7 +399,7 @@ packages = [{include = "my_package"}] ...@@ -403,7 +399,7 @@ packages = [{include = "my_package"}]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
demo = {path = "pyproject-demo"} demo = {path = "pyproject-demo"}
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "^3.6.0" pytest = "^3.6.0"
""" """
...@@ -450,7 +446,7 @@ packages = [{include = "my_package"}] ...@@ -450,7 +446,7 @@ packages = [{include = "my_package"}]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
demo = {path = "demo-0.1.0-py2.py3-none-any.whl"} demo = {path = "demo-0.1.0-py2.py3-none-any.whl"}
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "^3.6.0" pytest = "^3.6.0"
""" """
...@@ -482,8 +478,6 @@ packages = [{include = "my_package"}] ...@@ -482,8 +478,6 @@ packages = [{include = "my_package"}]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
[tool.poetry.dev-dependencies]
""" """
assert expected in tester.io.fetch_output() assert expected in tester.io.fetch_output()
...@@ -518,8 +512,6 @@ packages = [{include = "my_package"}] ...@@ -518,8 +512,6 @@ packages = [{include = "my_package"}]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
pendulum = "^2.0.0" pendulum = "^2.0.0"
[tool.poetry.dev-dependencies]
""" """
assert expected in tester.io.fetch_output() assert expected in tester.io.fetch_output()
...@@ -596,7 +588,7 @@ packages = [{include = "my_package"}] ...@@ -596,7 +588,7 @@ packages = [{include = "my_package"}]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "^3.6.0" pytest = "^3.6.0"
""" """
...@@ -638,7 +630,7 @@ packages = [{include = "my_package"}] ...@@ -638,7 +630,7 @@ packages = [{include = "my_package"}]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "~2.7 || ^3.6" python = "~2.7 || ^3.6"
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "^3.6.0" pytest = "^3.6.0"
pytest-requests = "^0.2.0" pytest-requests = "^0.2.0"
""" """
...@@ -705,8 +697,6 @@ packages = [{include = "my_package"}] ...@@ -705,8 +697,6 @@ packages = [{include = "my_package"}]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.6" python = "^3.6"
foo = "^1.19.2" foo = "^1.19.2"
[tool.poetry.dev-dependencies]
""" """
assert "{}\n{}".format(existing_section, expected) in pyproject_file.read_text() assert "{}\n{}".format(existing_section, expected) in pyproject_file.read_text()
......
import pytest
@pytest.fixture
def tester(command_tester_factory):
return command_tester_factory("install")
def test_group_options_are_passed_to_the_installer(tester, mocker):
"""
Group options are passed properly to the installer.
"""
mocker.patch.object(tester.command.installer, "run", return_value=1)
tester.execute("--with foo,bar --without baz --without bim --only bam")
assert tester.command.installer._with_groups == ["foo", "bar"]
assert tester.command.installer._without_groups == ["baz", "bim"]
assert tester.command.installer._only_groups == ["bam"]
import pytest import pytest
import tomlkit
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.factory import Factory
@pytest.fixture() @pytest.fixture()
...@@ -8,6 +10,152 @@ def tester(command_tester_factory): ...@@ -8,6 +10,152 @@ def tester(command_tester_factory):
return command_tester_factory("remove") return command_tester_factory("remove")
def test_remove_without_specific_group_removes_from_all_groups(
tester, app, repo, command_tester_factory, installed
):
"""
Removing without specifying a group removes packages from all groups.
"""
installed.add_package(Package("foo", "2.0.0"))
repo.add_package(Package("foo", "2.0.0"))
repo.add_package(Package("baz", "1.0.0"))
content = app.poetry.file.read()
groups_content = tomlkit.parse(
"""\
[tool.poetry.group.bar.dependencies]
foo = "^2.0.0"
baz = "^1.0.0"
"""
)
content["tool"]["poetry"]["dependencies"]["foo"] = "^2.0.0"
content["tool"]["poetry"].value._insert_after(
"dependencies", "group", groups_content["tool"]["poetry"]["group"]
)
app.poetry.file.write(content)
app.poetry.package.add_dependency(Factory.create_dependency("foo", "^2.0.0"))
app.poetry.package.add_dependency(
Factory.create_dependency("foo", "^2.0.0", groups=["bar"])
)
app.poetry.package.add_dependency(
Factory.create_dependency("baz", "^1.0.0", groups=["bar"])
)
tester.execute("foo")
content = app.poetry.file.read()["tool"]["poetry"]
assert "foo" not in content["dependencies"]
assert "foo" not in content["group"]["bar"]["dependencies"]
assert "baz" in content["group"]["bar"]["dependencies"]
expected = """\
[tool.poetry.group.bar.dependencies]
baz = "^1.0.0"
"""
assert expected in content.as_string()
def test_remove_without_specific_group_removes_from_specific_groups(
tester, app, repo, command_tester_factory, installed
):
"""
Removing with a specific group given removes packages only from this group.
"""
installed.add_package(Package("foo", "2.0.0"))
repo.add_package(Package("foo", "2.0.0"))
repo.add_package(Package("baz", "1.0.0"))
content = app.poetry.file.read()
groups_content = tomlkit.parse(
"""\
[tool.poetry.group.bar.dependencies]
foo = "^2.0.0"
baz = "^1.0.0"
"""
)
content["tool"]["poetry"]["dependencies"]["foo"] = "^2.0.0"
content["tool"]["poetry"].value._insert_after(
"dependencies", "group", groups_content["tool"]["poetry"]["group"]
)
app.poetry.file.write(content)
app.poetry.package.add_dependency(Factory.create_dependency("foo", "^2.0.0"))
app.poetry.package.add_dependency(
Factory.create_dependency("foo", "^2.0.0", groups=["bar"])
)
app.poetry.package.add_dependency(
Factory.create_dependency("baz", "^1.0.0", groups=["bar"])
)
tester.execute("foo --group bar")
content = app.poetry.file.read()["tool"]["poetry"]
assert "foo" in content["dependencies"]
assert "foo" not in content["group"]["bar"]["dependencies"]
assert "baz" in content["group"]["bar"]["dependencies"]
expected = """\
[tool.poetry.group.bar.dependencies]
baz = "^1.0.0"
"""
assert expected in content.as_string()
def test_remove_does_not_live_empty_groups(
tester, app, repo, command_tester_factory, installed
):
"""
Empty groups are automatically discarded after package removal.
"""
installed.add_package(Package("foo", "2.0.0"))
repo.add_package(Package("foo", "2.0.0"))
repo.add_package(Package("baz", "1.0.0"))
content = app.poetry.file.read()
groups_content = tomlkit.parse(
"""\
[tool.poetry.group.bar.dependencies]
foo = "^2.0.0"
baz = "^1.0.0"
"""
)
content["tool"]["poetry"]["dependencies"]["foo"] = "^2.0.0"
content["tool"]["poetry"].value._insert_after(
"dependencies", "group", groups_content["tool"]["poetry"]["group"]
)
app.poetry.file.write(content)
app.poetry.package.add_dependency(Factory.create_dependency("foo", "^2.0.0"))
app.poetry.package.add_dependency(
Factory.create_dependency("foo", "^2.0.0", groups=["bar"])
)
app.poetry.package.add_dependency(
Factory.create_dependency("baz", "^1.0.0", groups=["bar"])
)
tester.execute("foo baz --group bar")
content = app.poetry.file.read()["tool"]["poetry"]
assert "foo" in content["dependencies"]
assert "foo" not in content["group"]["bar"]["dependencies"]
assert "baz" not in content["group"]["bar"]["dependencies"]
assert "[tool.poetry.group.bar]" not in content.as_string()
assert "[tool.poetry.group]" not in content.as_string()
def test_remove_command_should_not_write_changes_upon_installer_errors( def test_remove_command_should_not_write_changes_upon_installer_errors(
tester, app, repo, command_tester_factory, mocker tester, app, repo, command_tester_factory, mocker
): ):
......
import pytest import pytest
from poetry.core.packages.dependency_group import DependencyGroup
from poetry.factory import Factory from poetry.factory import Factory
from tests.helpers import get_package from tests.helpers import get_package
...@@ -13,7 +14,7 @@ def test_show_basic_with_installed_packages(tester, poetry, installed): ...@@ -13,7 +14,7 @@ def test_show_basic_with_installed_packages(tester, poetry, installed):
poetry.package.add_dependency(Factory.create_dependency("cachy", "^0.1.0")) poetry.package.add_dependency(Factory.create_dependency("cachy", "^0.1.0"))
poetry.package.add_dependency(Factory.create_dependency("pendulum", "^2.0.0")) poetry.package.add_dependency(Factory.create_dependency("pendulum", "^2.0.0"))
poetry.package.add_dependency( poetry.package.add_dependency(
Factory.create_dependency("pytest", "^3.7.3", category="dev") Factory.create_dependency("pytest", "^3.7.3", groups=["dev"])
) )
cachy_010 = get_package("cachy", "0.1.0") cachy_010 = get_package("cachy", "0.1.0")
...@@ -952,7 +953,7 @@ def test_show_outdated_no_dev_git_dev_dependency(tester, poetry, installed, repo ...@@ -952,7 +953,7 @@ def test_show_outdated_no_dev_git_dev_dependency(tester, poetry, installed, repo
} }
) )
tester.execute("--outdated --no-dev") tester.execute("--outdated --without dev")
expected = """\ expected = """\
cachy 0.1.0 0.2.0 Cachy package cachy 0.1.0 0.2.0 Cachy package
...@@ -1071,6 +1072,159 @@ pendulum 2.0.0 Pendulum package ...@@ -1071,6 +1072,159 @@ pendulum 2.0.0 Pendulum package
def test_show_non_dev_with_basic_installed_packages(tester, poetry, installed): def test_show_non_dev_with_basic_installed_packages(tester, poetry, installed):
poetry.package.add_dependency(Factory.create_dependency("cachy", "^0.1.0"))
poetry.package.add_dependency(Factory.create_dependency("pendulum", "^2.0.0"))
poetry.package.add_dependency(
Factory.create_dependency("pytest", "*", groups=["dev"])
)
cachy_010 = get_package("cachy", "0.1.0")
cachy_010.description = "Cachy package"
pendulum_200 = get_package("pendulum", "2.0.0")
pendulum_200.description = "Pendulum package"
pytest_373 = get_package("pytest", "3.7.3")
pytest_373.description = "Pytest package"
pytest_373.category = "dev"
installed.add_package(cachy_010)
installed.add_package(pendulum_200)
installed.add_package(pytest_373)
poetry.locker.mock_lock_data(
{
"package": [
{
"name": "cachy",
"version": "0.1.0",
"description": "Cachy package",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "pendulum",
"version": "2.0.0",
"description": "Pendulum package",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "pytest",
"version": "3.7.3",
"description": "Pytest package",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"cachy": [], "pendulum": [], "pytest": []},
},
}
)
tester.execute("--without dev")
expected = """\
cachy 0.1.0 Cachy package
pendulum 2.0.0 Pendulum package
"""
assert expected == tester.io.fetch_output()
def test_show_with_group_only(tester, poetry, installed):
poetry.package.add_dependency(Factory.create_dependency("cachy", "^0.1.0"))
poetry.package.add_dependency(Factory.create_dependency("pendulum", "^2.0.0"))
poetry.package.add_dependency(
Factory.create_dependency("pytest", "*", groups=["dev"])
)
cachy_010 = get_package("cachy", "0.1.0")
cachy_010.description = "Cachy package"
pendulum_200 = get_package("pendulum", "2.0.0")
pendulum_200.description = "Pendulum package"
pytest_373 = get_package("pytest", "3.7.3")
pytest_373.description = "Pytest package"
pytest_373.category = "dev"
installed.add_package(cachy_010)
installed.add_package(pendulum_200)
installed.add_package(pytest_373)
poetry.locker.mock_lock_data(
{
"package": [
{
"name": "cachy",
"version": "0.1.0",
"description": "Cachy package",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "pendulum",
"version": "2.0.0",
"description": "Pendulum package",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "pytest",
"version": "3.7.3",
"description": "Pytest package",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"cachy": [], "pendulum": [], "pytest": []},
},
}
)
tester.execute("--only dev")
expected = """\
pytest 3.7.3 Pytest package
"""
assert expected == tester.io.fetch_output()
def test_show_with_optional_group(tester, poetry, installed):
poetry.package.add_dependency(Factory.create_dependency("cachy", "^0.1.0"))
poetry.package.add_dependency(Factory.create_dependency("pendulum", "^2.0.0"))
group = DependencyGroup("dev", optional=True)
group.add_dependency(Factory.create_dependency("pytest", "*", groups=["dev"]))
poetry.package.add_dependency_group(group)
cachy_010 = get_package("cachy", "0.1.0") cachy_010 = get_package("cachy", "0.1.0")
cachy_010.description = "Cachy package" cachy_010.description = "Cachy package"
...@@ -1128,7 +1282,7 @@ def test_show_non_dev_with_basic_installed_packages(tester, poetry, installed): ...@@ -1128,7 +1282,7 @@ def test_show_non_dev_with_basic_installed_packages(tester, poetry, installed):
} }
) )
tester.execute("--no-dev") tester.execute()
expected = """\ expected = """\
cachy 0.1.0 Cachy package cachy 0.1.0 Cachy package
...@@ -1137,6 +1291,16 @@ pendulum 2.0.0 Pendulum package ...@@ -1137,6 +1291,16 @@ pendulum 2.0.0 Pendulum package
assert expected == tester.io.fetch_output() assert expected == tester.io.fetch_output()
tester.execute("--with dev")
expected = """\
cachy 0.1.0 Cachy package
pendulum 2.0.0 Pendulum package
pytest 3.7.3 Pytest package
"""
assert expected == tester.io.fetch_output()
def test_show_tree(tester, poetry, installed): def test_show_tree(tester, poetry, installed):
poetry.package.add_dependency(Factory.create_dependency("cachy", "^0.2.0")) poetry.package.add_dependency(Factory.create_dependency("cachy", "^0.2.0"))
...@@ -1193,7 +1357,7 @@ cachy 0.2.0 ...@@ -1193,7 +1357,7 @@ cachy 0.2.0
def test_show_tree_no_dev(tester, poetry, installed): def test_show_tree_no_dev(tester, poetry, installed):
poetry.package.add_dependency(Factory.create_dependency("cachy", "^0.2.0")) poetry.package.add_dependency(Factory.create_dependency("cachy", "^0.2.0"))
poetry.package.add_dependency( poetry.package.add_dependency(
Factory.create_dependency("pytest", "^6.1.0", category="dev") Factory.create_dependency("pytest", "^6.1.0", groups=["dev"])
) )
cachy2 = get_package("cachy", "0.2.0") cachy2 = get_package("cachy", "0.2.0")
...@@ -1247,7 +1411,7 @@ def test_show_tree_no_dev(tester, poetry, installed): ...@@ -1247,7 +1411,7 @@ def test_show_tree_no_dev(tester, poetry, installed):
} }
) )
tester.execute("--tree --no-dev") tester.execute("--tree --without dev")
expected = """\ expected = """\
cachy 0.2.0 cachy 0.2.0
......
...@@ -46,7 +46,7 @@ functools32 = { version = "^3.2.3", markers = "python_version ~= '2.7' and sys_p ...@@ -46,7 +46,7 @@ functools32 = { version = "^3.2.3", markers = "python_version ~= '2.7' and sys_p
[tool.poetry.extras] [tool.poetry.extras]
db = [ "orator" ] db = [ "orator" ]
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
pytest = "~3.4" pytest = "~3.4"
......
...@@ -101,7 +101,7 @@ six = "*" ...@@ -101,7 +101,7 @@ six = "*"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.8" python-versions = "^3.8"
content-hash = "0cd068218f235c162f7b74bc8faf4ce3387b82daee1c1bb7a97af034f27ee116" content-hash = "ae61bd854548e88c090780099edd400d58e6944ce9f3fc086d2f9aa5ac487f14"
[metadata.files] [metadata.files]
certifi = [ certifi = [
......
...@@ -7,7 +7,6 @@ from pathlib import Path ...@@ -7,7 +7,6 @@ from pathlib import Path
from poetry.console.application import Application from poetry.console.application import Application
from poetry.core.masonry.utils.helpers import escape_name from poetry.core.masonry.utils.helpers import escape_name
from poetry.core.masonry.utils.helpers import escape_version from poetry.core.masonry.utils.helpers import escape_version
from poetry.core.packages.dependency import Dependency
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.core.packages.utils.link import Link from poetry.core.packages.utils.link import Link
from poetry.core.toml.file import TOMLFile from poetry.core.toml.file import TOMLFile
...@@ -28,15 +27,18 @@ def get_package(name, version): ...@@ -28,15 +27,18 @@ def get_package(name, version):
def get_dependency( def get_dependency(
name, constraint=None, category="main", optional=False, allows_prereleases=False name, constraint=None, groups=None, optional=False, allows_prereleases=False
): ):
return Dependency( if constraint is None:
name, constraint = "*"
constraint or "*",
category=category, if isinstance(constraint, str):
optional=optional, constraint = {"version": constraint}
allows_prereleases=allows_prereleases,
) constraint["optional"] = optional
constraint["allow_prereleases"] = allows_prereleases
return Factory.create_dependency(name, constraint or "*", groups=groups)
def fixture(path=None): def fixture(path=None):
......
...@@ -15,6 +15,7 @@ from cleo.io.outputs.buffered_output import BufferedOutput ...@@ -15,6 +15,7 @@ from cleo.io.outputs.buffered_output import BufferedOutput
from cleo.io.outputs.output import Verbosity from cleo.io.outputs.output import Verbosity
from deepdiff import DeepDiff from deepdiff import DeepDiff
from poetry.core.packages.dependency_group import DependencyGroup
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.core.packages.project_package import ProjectPackage from poetry.core.packages.project_package import ProjectPackage
from poetry.core.toml.file import TOMLFile from poetry.core.toml.file import TOMLFile
...@@ -277,7 +278,9 @@ def test_run_update_after_removing_dependencies( ...@@ -277,7 +278,9 @@ def test_run_update_after_removing_dependencies(
assert 1 == installer.executor.removals_count assert 1 == installer.executor.removals_count
def _configure_run_install_dev(locker, repo, package, installed): def _configure_run_install_dev(
locker, repo, package, installed, with_optional_group=False
):
""" """
Perform common test setup for `test_run_install_*dev*()` methods. Perform common test setup for `test_run_install_*dev*()` methods.
""" """
...@@ -334,13 +337,16 @@ def _configure_run_install_dev(locker, repo, package, installed): ...@@ -334,13 +337,16 @@ def _configure_run_install_dev(locker, repo, package, installed):
package.add_dependency(Factory.create_dependency("A", "~1.0")) package.add_dependency(Factory.create_dependency("A", "~1.0"))
package.add_dependency(Factory.create_dependency("B", "~1.1")) package.add_dependency(Factory.create_dependency("B", "~1.1"))
package.add_dependency(Factory.create_dependency("C", "~1.2", category="dev"))
group = DependencyGroup("dev", optional=with_optional_group)
group.add_dependency(Factory.create_dependency("C", "~1.2", groups=["dev"]))
package.add_dependency_group(group)
def test_run_install_no_dev(installer, locker, repo, package, installed): def test_run_install_no_group(installer, locker, repo, package, installed):
_configure_run_install_dev(locker, repo, package, installed) _configure_run_install_dev(locker, repo, package, installed)
installer.dev_mode(False) installer.without_groups(["dev"])
installer.run() installer.run()
assert 0 == installer.executor.installations_count assert 0 == installer.executor.installations_count
...@@ -348,10 +354,10 @@ def test_run_install_no_dev(installer, locker, repo, package, installed): ...@@ -348,10 +354,10 @@ def test_run_install_no_dev(installer, locker, repo, package, installed):
assert 1 == installer.executor.removals_count assert 1 == installer.executor.removals_count
def test_run_install_dev_only(installer, locker, repo, package, installed): def test_run_install_group_only(installer, locker, repo, package, installed):
_configure_run_install_dev(locker, repo, package, installed) _configure_run_install_dev(locker, repo, package, installed)
installer.dev_only(True) installer.only_groups(["dev"])
installer.run() installer.run()
assert 0 == installer.executor.installations_count assert 0 == installer.executor.installations_count
...@@ -359,11 +365,13 @@ def test_run_install_dev_only(installer, locker, repo, package, installed): ...@@ -359,11 +365,13 @@ def test_run_install_dev_only(installer, locker, repo, package, installed):
assert 2 == installer.executor.removals_count assert 2 == installer.executor.removals_count
def test_run_install_no_dev_and_dev_only(installer, locker, repo, package, installed): def test_run_install_with_optional_group_not_selected(
_configure_run_install_dev(locker, repo, package, installed) installer, locker, repo, package, installed
):
_configure_run_install_dev(
locker, repo, package, installed, with_optional_group=True
)
installer.dev_mode(False)
installer.dev_only(True)
installer.run() installer.run()
assert 0 == installer.executor.installations_count assert 0 == installer.executor.installations_count
...@@ -371,6 +379,21 @@ def test_run_install_no_dev_and_dev_only(installer, locker, repo, package, insta ...@@ -371,6 +379,21 @@ def test_run_install_no_dev_and_dev_only(installer, locker, repo, package, insta
assert 1 == installer.executor.removals_count assert 1 == installer.executor.removals_count
def test_run_install_with_optional_group_selected(
installer, locker, repo, package, installed
):
_configure_run_install_dev(
locker, repo, package, installed, with_optional_group=True
)
installer.with_groups(["dev"])
installer.run()
assert 0 == installer.executor.installations_count
assert 0 == installer.executor.updates_count
assert 0 == installer.executor.removals_count
@pytest.mark.parametrize( @pytest.mark.parametrize(
"managed_reserved_package_names", "managed_reserved_package_names",
[ [
...@@ -439,7 +462,7 @@ def test_run_install_remove_untracked( ...@@ -439,7 +462,7 @@ def test_run_install_remove_untracked(
} }
) )
installer.dev_mode(True).remove_untracked(True) installer.remove_untracked(True)
installer.run() installer.run()
assert 0 == installer.executor.installations_count assert 0 == installer.executor.installations_count
...@@ -863,7 +886,7 @@ def test_installer_with_pypi_repository(package, locker, installed, config): ...@@ -863,7 +886,7 @@ def test_installer_with_pypi_repository(package, locker, installed, config):
NullIO(), NullEnv(), package, locker, pool, config, installed=installed NullIO(), NullEnv(), package, locker, pool, config, installed=installed
) )
package.add_dependency(Factory.create_dependency("pytest", "^3.5", category="dev")) package.add_dependency(Factory.create_dependency("pytest", "^3.5", groups=["dev"]))
installer.run() installer.run()
expected = fixture("with-pypi-repository") expected = fixture("with-pypi-repository")
...@@ -1069,7 +1092,7 @@ def test_run_changes_category_if_needed(installer, locker, repo, package): ...@@ -1069,7 +1092,7 @@ def test_run_changes_category_if_needed(installer, locker, repo, package):
package.add_dependency( package.add_dependency(
Factory.create_dependency( Factory.create_dependency(
"A", {"version": "^1.0", "optional": True}, category="dev" "A", {"version": "^1.0", "optional": True}, groups=["dev"]
) )
) )
package.add_dependency(Factory.create_dependency("B", "^1.1")) package.add_dependency(Factory.create_dependency("B", "^1.1"))
...@@ -1169,8 +1192,8 @@ def test_run_update_with_locked_extras(installer, locker, repo, package): ...@@ -1169,8 +1192,8 @@ def test_run_update_with_locked_extras(installer, locker, repo, package):
b_dependency.in_extras.append("foo") b_dependency.in_extras.append("foo")
c_dependency = get_dependency("C", "^1.0") c_dependency = get_dependency("C", "^1.0")
c_dependency.python_versions = "~2.7" c_dependency.python_versions = "~2.7"
package_a.requires.append(b_dependency) package_a.add_dependency(b_dependency)
package_a.requires.append(c_dependency) package_a.add_dependency(c_dependency)
repo.add_package(package_a) repo.add_package(package_a)
repo.add_package(get_package("B", "1.0")) repo.add_package(get_package("B", "1.0"))
...@@ -1869,7 +1892,7 @@ def test_installer_can_handle_old_lock_files( ...@@ -1869,7 +1892,7 @@ def test_installer_can_handle_old_lock_files(
pool = Pool() pool = Pool()
pool.add_repository(MockRepository()) pool.add_repository(MockRepository())
package.add_dependency(Factory.create_dependency("pytest", "^3.5", category="dev")) package.add_dependency(Factory.create_dependency("pytest", "^3.5", groups=["dev"]))
locker.locked() locker.locked()
locker.mock_lock_data(fixture("old-lock")) locker.mock_lock_data(fixture("old-lock"))
......
...@@ -227,7 +227,7 @@ def test_run_update_after_removing_dependencies( ...@@ -227,7 +227,7 @@ def test_run_update_after_removing_dependencies(
assert len(removals) == 1 assert len(removals) == 1
def test_run_install_no_dev(installer, locker, repo, package, installed): def test_run_install_no_group(installer, locker, repo, package, installed):
locker.locked(True) locker.locked(True)
locker.mock_lock_data( locker.mock_lock_data(
{ {
...@@ -281,9 +281,9 @@ def test_run_install_no_dev(installer, locker, repo, package, installed): ...@@ -281,9 +281,9 @@ def test_run_install_no_dev(installer, locker, repo, package, installed):
package.add_dependency(Factory.create_dependency("A", "~1.0")) package.add_dependency(Factory.create_dependency("A", "~1.0"))
package.add_dependency(Factory.create_dependency("B", "~1.1")) package.add_dependency(Factory.create_dependency("B", "~1.1"))
package.add_dependency(Factory.create_dependency("C", "~1.2", category="dev")) package.add_dependency(Factory.create_dependency("C", "~1.2", groups=["dev"]))
installer.dev_mode(False) installer.without_groups(["dev"])
installer.run() installer.run()
installs = installer.installer.installs installs = installer.installer.installs
...@@ -364,7 +364,7 @@ def test_run_install_remove_untracked( ...@@ -364,7 +364,7 @@ def test_run_install_remove_untracked(
} }
) )
installer.dev_mode(True).remove_untracked(True) installer.remove_untracked(True)
installer.run() installer.run()
installs = installer.installer.installs installs = installer.installer.installs
...@@ -767,7 +767,7 @@ def test_installer_with_pypi_repository(package, locker, installed, config): ...@@ -767,7 +767,7 @@ def test_installer_with_pypi_repository(package, locker, installed, config):
NullIO(), NullEnv(), package, locker, pool, config, installed=installed NullIO(), NullEnv(), package, locker, pool, config, installed=installed
) )
package.add_dependency(Factory.create_dependency("pytest", "^3.5", category="dev")) package.add_dependency(Factory.create_dependency("pytest", "^3.5", groups=["dev"]))
installer.run() installer.run()
expected = fixture("with-pypi-repository") expected = fixture("with-pypi-repository")
...@@ -974,7 +974,7 @@ def test_run_changes_category_if_needed(installer, locker, repo, package): ...@@ -974,7 +974,7 @@ def test_run_changes_category_if_needed(installer, locker, repo, package):
package.add_dependency( package.add_dependency(
Factory.create_dependency( Factory.create_dependency(
"A", {"version": "^1.0", "optional": True}, category="dev" "A", {"version": "^1.0", "optional": True}, groups=["dev"]
) )
) )
package.add_dependency(Factory.create_dependency("B", "^1.1")) package.add_dependency(Factory.create_dependency("B", "^1.1"))
...@@ -1074,8 +1074,8 @@ def test_run_update_with_locked_extras(installer, locker, repo, package): ...@@ -1074,8 +1074,8 @@ def test_run_update_with_locked_extras(installer, locker, repo, package):
b_dependency.in_extras.append("foo") b_dependency.in_extras.append("foo")
c_dependency = get_dependency("C", "^1.0") c_dependency = get_dependency("C", "^1.0")
c_dependency.python_versions = "~2.7" c_dependency.python_versions = "~2.7"
package_a.requires.append(b_dependency) package_a.add_dependency(b_dependency)
package_a.requires.append(c_dependency) package_a.add_dependency(c_dependency)
repo.add_package(package_a) repo.add_package(package_a)
repo.add_package(get_package("B", "1.0")) repo.add_package(get_package("B", "1.0"))
...@@ -1739,7 +1739,7 @@ def test_installer_can_handle_old_lock_files( ...@@ -1739,7 +1739,7 @@ def test_installer_can_handle_old_lock_files(
pool = Pool() pool = Pool()
pool.add_repository(MockRepository()) pool.add_repository(MockRepository())
package.add_dependency(Factory.create_dependency("pytest", "^3.5", category="dev")) package.add_dependency(Factory.create_dependency("pytest", "^3.5", groups=["dev"]))
locker.locked() locker.locked()
locker.mock_lock_data(fixture("old-lock")) locker.mock_lock_data(fixture("old-lock"))
......
...@@ -86,7 +86,7 @@ resolved_reference = "123456" ...@@ -86,7 +86,7 @@ resolved_reference = "123456"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "*" python-versions = "*"
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831"
[metadata.files] [metadata.files]
A = [ A = [
...@@ -295,7 +295,7 @@ python-versions = "*" ...@@ -295,7 +295,7 @@ python-versions = "*"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "*" python-versions = "*"
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831"
[metadata.files] [metadata.files]
A = [] A = []
...@@ -335,7 +335,7 @@ foo = ["B (>=1.0.0)"] ...@@ -335,7 +335,7 @@ foo = ["B (>=1.0.0)"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "*" python-versions = "*"
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831"
[metadata.files] [metadata.files]
A = [] A = []
...@@ -365,7 +365,7 @@ foo = ["bar"] ...@@ -365,7 +365,7 @@ foo = ["bar"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "*" python-versions = "*"
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831"
[metadata.files] [metadata.files]
A = [] A = []
...@@ -410,7 +410,7 @@ reference = "legacy" ...@@ -410,7 +410,7 @@ reference = "legacy"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "*" python-versions = "*"
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831"
[metadata.files] [metadata.files]
A = [] A = []
...@@ -497,7 +497,7 @@ B = {version = "^1.0.0", extras = ["a", "b", "c"], optional = true} ...@@ -497,7 +497,7 @@ B = {version = "^1.0.0", extras = ["a", "b", "c"], optional = true}
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "*" python-versions = "*"
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831"
[metadata.files] [metadata.files]
A = [] A = []
...@@ -591,7 +591,7 @@ F = {git = "https://github.com/python-poetry/poetry.git", branch = "foo"} ...@@ -591,7 +591,7 @@ F = {git = "https://github.com/python-poetry/poetry.git", branch = "foo"}
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "*" python-versions = "*"
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831"
[metadata.files] [metadata.files]
A = [] A = []
......
...@@ -148,7 +148,7 @@ def test_solver_with_deps(solver, repo, package): ...@@ -148,7 +148,7 @@ def test_solver_with_deps(solver, repo, package):
repo.add_package(package_b) repo.add_package(package_b)
repo.add_package(new_package_b) repo.add_package(new_package_b)
package_a.requires.append(get_dependency("B", "<1.1")) package_a.add_dependency(get_dependency("B", "<1.1"))
ops = solver.solve() ops = solver.solve()
...@@ -176,7 +176,7 @@ def test_install_honours_not_equal(solver, repo, package): ...@@ -176,7 +176,7 @@ def test_install_honours_not_equal(solver, repo, package):
repo.add_package(new_package_b12) repo.add_package(new_package_b12)
repo.add_package(new_package_b13) repo.add_package(new_package_b13)
package_a.requires.append(get_dependency("B", "<=1.3,!=1.3,!=1.2")) package_a.add_dependency(get_dependency("B", "<=1.3,!=1.3,!=1.2"))
ops = solver.solve() ops = solver.solve()
...@@ -201,10 +201,10 @@ def test_install_with_deps_in_order(solver, repo, package): ...@@ -201,10 +201,10 @@ def test_install_with_deps_in_order(solver, repo, package):
repo.add_package(package_b) repo.add_package(package_b)
repo.add_package(package_c) repo.add_package(package_c)
package_b.requires.append(get_dependency("A", ">=1.0")) package_b.add_dependency(get_dependency("A", ">=1.0"))
package_b.requires.append(get_dependency("C", ">=1.0")) package_b.add_dependency(get_dependency("C", ">=1.0"))
package_c.requires.append(get_dependency("A", ">=1.0")) package_c.add_dependency(get_dependency("A", ">=1.0"))
ops = solver.solve() ops = solver.solve()
...@@ -278,9 +278,9 @@ def test_update_with_use_latest(solver, repo, installed, package, locked): ...@@ -278,9 +278,9 @@ def test_update_with_use_latest(solver, repo, installed, package, locked):
) )
def test_solver_sets_categories(solver, repo, package): def test_solver_sets_groups(solver, repo, package):
package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("A", "*"))
package.add_dependency(Factory.create_dependency("B", "*", category="dev")) package.add_dependency(Factory.create_dependency("B", "*", groups=["dev"]))
package_a = get_package("A", "1.0") package_a = get_package("A", "1.0")
package_b = get_package("B", "1.0") package_b = get_package("B", "1.0")
...@@ -429,7 +429,7 @@ def test_solver_returns_extras_if_requested(solver, repo, package): ...@@ -429,7 +429,7 @@ def test_solver_returns_extras_if_requested(solver, repo, package):
dep = get_dependency("C", "^1.0", optional=True) dep = get_dependency("C", "^1.0", optional=True)
dep.marker = parse_marker("extra == 'foo'") dep.marker = parse_marker("extra == 'foo'")
package_b.extras = {"foo": [dep]} package_b.extras = {"foo": [dep]}
package_b.requires.append(dep) package_b.add_dependency(dep)
repo.add_package(package_a) repo.add_package(package_a)
repo.add_package(package_b) repo.add_package(package_b)
...@@ -474,8 +474,8 @@ def test_solver_returns_extras_only_requested(solver, repo, package, enabled_ext ...@@ -474,8 +474,8 @@ def test_solver_returns_extras_only_requested(solver, repo, package, enabled_ext
package_b.extras = {"one": [dep10], "two": [dep20]} package_b.extras = {"one": [dep10], "two": [dep20]}
package_b.requires.append(dep10) package_b.add_dependency(dep10)
package_b.requires.append(dep20) package_b.add_dependency(dep20)
repo.add_package(package_a) repo.add_package(package_a)
repo.add_package(package_b) repo.add_package(package_b)
...@@ -523,7 +523,7 @@ def test_solver_returns_extras_when_multiple_extras_use_same_dependency( ...@@ -523,7 +523,7 @@ def test_solver_returns_extras_when_multiple_extras_use_same_dependency(
package_b.extras = {"one": [dep], "two": [dep]} package_b.extras = {"one": [dep], "two": [dep]}
package_b.requires.append(dep) package_b.add_dependency(dep)
extras = [enabled_extra] if enabled_extra is not None else [] extras = [enabled_extra] if enabled_extra is not None else []
package_a.add_dependency( package_a.add_dependency(
...@@ -574,8 +574,8 @@ def test_solver_returns_extras_only_requested_nested( ...@@ -574,8 +574,8 @@ def test_solver_returns_extras_only_requested_nested(
package_b.extras = {"one": [dep10], "two": [dep20]} package_b.extras = {"one": [dep10], "two": [dep20]}
package_b.requires.append(dep10) package_b.add_dependency(dep10)
package_b.requires.append(dep20) package_b.add_dependency(dep20)
extras = [enabled_extra] if enabled_extra is not None else [] extras = [enabled_extra] if enabled_extra is not None else []
package_a.add_dependency( package_a.add_dependency(
...@@ -824,14 +824,14 @@ def test_solver_sub_dependencies_with_not_supported_python_version_transitive( ...@@ -824,14 +824,14 @@ def test_solver_sub_dependencies_with_not_supported_python_version_transitive(
) )
def test_solver_with_dependency_in_both_main_and_dev_dependencies( def test_solver_with_dependency_in_both_default_and_dev_dependencies(
solver, repo, package solver, repo, package
): ):
solver.provider.set_package_python_versions("^3.5") solver.provider.set_package_python_versions("^3.5")
package.add_dependency(Factory.create_dependency("A", "*")) package.add_dependency(Factory.create_dependency("A", "*"))
package.add_dependency( package.add_dependency(
Factory.create_dependency( Factory.create_dependency(
"A", {"version": "*", "extras": ["foo"]}, category="dev" "A", {"version": "*", "extras": ["foo"]}, groups=["dev"]
) )
) )
...@@ -884,7 +884,7 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies_with_one_more_ ...@@ -884,7 +884,7 @@ def test_solver_with_dependency_in_both_main_and_dev_dependencies_with_one_more_
package.add_dependency(Factory.create_dependency("E", "*")) package.add_dependency(Factory.create_dependency("E", "*"))
package.add_dependency( package.add_dependency(
Factory.create_dependency( Factory.create_dependency(
"A", {"version": "*", "extras": ["foo"]}, category="dev" "A", {"version": "*", "extras": ["foo"]}, groups=["dev"]
) )
) )
...@@ -1417,8 +1417,8 @@ def test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_onl ...@@ -1417,8 +1417,8 @@ def test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_onl
package_a = get_package("A", "1.0.0") package_a = get_package("A", "1.0.0")
package_a.extras = {"foo": [dep1], "bar": [dep2]} package_a.extras = {"foo": [dep1], "bar": [dep2]}
package_a.requires.append(dep1) package_a.add_dependency(dep1)
package_a.requires.append(dep2) package_a.add_dependency(dep2)
package_b2 = get_package("B", "2.0.0") package_b2 = get_package("B", "2.0.0")
package_b1 = get_package("B", "1.0.0") package_b1 = get_package("B", "1.0.0")
...@@ -1539,7 +1539,7 @@ def test_solver_ignores_dependencies_with_incompatible_python_full_version_marke ...@@ -1539,7 +1539,7 @@ def test_solver_ignores_dependencies_with_incompatible_python_full_version_marke
package.add_dependency(Factory.create_dependency("B", "^2.0")) package.add_dependency(Factory.create_dependency("B", "^2.0"))
package_a = get_package("A", "1.0.0") package_a = get_package("A", "1.0.0")
package_a.requires.append( package_a.add_dependency(
Dependency.create_from_pep_508( Dependency.create_from_pep_508(
'B (<2.0); platform_python_implementation == "PyPy" and python_full_version < "2.7.9"' 'B (<2.0); platform_python_implementation == "PyPy" and python_full_version < "2.7.9"'
) )
...@@ -2286,12 +2286,12 @@ def test_solver_does_not_raise_conflict_for_conditional_dev_dependencies( ...@@ -2286,12 +2286,12 @@ def test_solver_does_not_raise_conflict_for_conditional_dev_dependencies(
solver.provider.set_package_python_versions("~2.7 || ^3.5") solver.provider.set_package_python_versions("~2.7 || ^3.5")
package.add_dependency( package.add_dependency(
Factory.create_dependency( Factory.create_dependency(
"A", {"version": "^1.0", "python": "~2.7"}, category="dev" "A", {"version": "^1.0", "python": "~2.7"}, groups=["dev"]
) )
) )
package.add_dependency( package.add_dependency(
Factory.create_dependency( Factory.create_dependency(
"A", {"version": "^2.0", "python": "^3.5"}, category="dev" "A", {"version": "^2.0", "python": "^3.5"}, groups=["dev"]
) )
) )
......
...@@ -62,16 +62,12 @@ def poetry(fixture_dir, locker): ...@@ -62,16 +62,12 @@ def poetry(fixture_dir, locker):
def set_package_requires(poetry, skip=None): def set_package_requires(poetry, skip=None):
skip = skip or set() skip = skip or set()
packages = poetry.locker.locked_repository(with_dev_reqs=True).packages packages = poetry.locker.locked_repository(with_dev_reqs=True).packages
poetry.package.requires = [ package = poetry.package.with_dependency_groups([], only=True)
pkg.to_dependency() for pkg in packages:
for pkg in packages if pkg.name not in skip:
if pkg.category == "main" and pkg.name not in skip package.add_dependency(pkg.to_dependency())
]
poetry.package.dev_requires = [ poetry._package = package
pkg.to_dependency()
for pkg in packages
if pkg.category == "dev" and pkg.name not in skip
]
def test_exporter_can_export_requirements_txt_with_standard_packages( def test_exporter_can_export_requirements_txt_with_standard_packages(
...@@ -483,16 +479,18 @@ def test_exporter_can_export_requirements_txt_with_nested_packages_and_markers_a ...@@ -483,16 +479,18 @@ def test_exporter_can_export_requirements_txt_with_nested_packages_and_markers_a
} }
) )
poetry.package.requires = [ root = poetry.package.with_dependency_groups([], only=True)
root.add_dependency(
Factory.create_dependency( Factory.create_dependency(
name="a", constraint=dict(version="^1.2.3", python="<3.8") name="a", constraint=dict(version="^1.2.3", python="<3.8")
), )
] )
poetry.package.dev_requires = [ root.add_dependency(
Factory.create_dependency( Factory.create_dependency(
name="b", constraint=dict(version="^4.5.6"), category="dev" name="b", constraint=dict(version="^4.5.6"), groups=["dev"]
), )
] )
poetry._package = root
exporter = Exporter(poetry) exporter = Exporter(poetry)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment