Commit c7055be9 by Sébastien Eustace Committed by GitHub

New and faster installer implementation (#2595)

* Improve the way packages are installed

* Add support for parallelized operations

* Improve the Chooser class

* Make the new installer the default

* Add an Authenticator class to be able to download from protected urls

* Adapt code and tests to latest changes

* Update lock file and some dependencies

* Improve installations information and caching

* Make the final adjustments and tests for the new installer

* Use the preview version of Poetry for the CI

* Rename Executor.run() to Executor.run_pip()

* Gracefully handle errors when executing operations
parent d6289470
...@@ -16,7 +16,8 @@ test_task: ...@@ -16,7 +16,8 @@ test_task:
- pkg install -y git-lite $PYPACKAGE $SQLPACKAGE - pkg install -y git-lite $PYPACKAGE $SQLPACKAGE
pip_script: pip_script:
- $PYTHON -m ensurepip - $PYTHON -m ensurepip
- $PYTHON -m pip install -U pip tox poetry - $PYTHON -m pip install -U pip tox
- $PYTHON -m pip install -U --pre poetry
- poetry config virtualenvs.in-project true - poetry config virtualenvs.in-project true
tox_script: $PYTHON -m tox -e py -- -q --junitxml=junit.xml tests tox_script: $PYTHON -m tox -e py -- -q --junitxml=junit.xml tests
on_failure: on_failure:
......
...@@ -16,6 +16,7 @@ exclude = ...@@ -16,6 +16,7 @@ exclude =
.vscode .vscode
.github .github
poetry/utils/_compat.py poetry/utils/_compat.py
poetry/utils/env_scripts/tags.py
tests/fixtures/ tests/fixtures/
tests/repositories/fixtures/ tests/repositories/fixtures/
tests/utils/fixtures/ tests/utils/fixtures/
...@@ -49,7 +49,7 @@ jobs: ...@@ -49,7 +49,7 @@ jobs:
shell: bash shell: bash
run: | run: |
curl -fsS -o get-poetry.py https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py curl -fsS -o get-poetry.py https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py
python get-poetry.py -y python get-poetry.py -y --preview
echo "::set-env name=PATH::$HOME/.poetry/bin:$PATH" echo "::set-env name=PATH::$HOME/.poetry/bin:$PATH"
- name: Configure poetry - name: Configure poetry
......
...@@ -9,7 +9,6 @@ version = "1.4.4" ...@@ -9,7 +9,6 @@ version = "1.4.4"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Atomic file writes." description = "Atomic file writes."
marker = "python_version >= \"3.5\" and sys_platform == \"win32\" or sys_platform == \"win32\" or python_version < \"3.5\""
name = "atomicwrites" name = "atomicwrites"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
...@@ -32,7 +31,6 @@ tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.i ...@@ -32,7 +31,6 @@ tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.i
[[package]] [[package]]
category = "dev" category = "dev"
description = "Backport of functools.lru_cache" description = "Backport of functools.lru_cache"
marker = "python_version < \"3.2\""
name = "backports.functools-lru-cache" name = "backports.functools-lru-cache"
optional = false optional = false
python-versions = ">=2.6" python-versions = ">=2.6"
...@@ -45,7 +43,6 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pyt ...@@ -45,7 +43,6 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pyt
[[package]] [[package]]
category = "dev" category = "dev"
description = "The uncompromising code formatter." description = "The uncompromising code formatter."
marker = "python_version >= \"3.6\" and python_version < \"4.0\""
name = "black" name = "black"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
...@@ -74,10 +71,7 @@ version = "0.12.6" ...@@ -74,10 +71,7 @@ version = "0.12.6"
[package.dependencies] [package.dependencies]
msgpack = ">=0.5.2" msgpack = ">=0.5.2"
requests = "*" requests = "*"
lockfile = {version = ">=0.9", optional = true, markers = "extra == \"filecache\""}
[package.dependencies.lockfile]
optional = true
version = ">=0.9"
[package.extras] [package.extras]
filecache = ["lockfile (>=0.9)"] filecache = ["lockfile (>=0.9)"]
...@@ -107,7 +101,6 @@ version = "2020.6.20" ...@@ -107,7 +101,6 @@ version = "2020.6.20"
[[package]] [[package]]
category = "main" category = "main"
description = "Foreign Function Interface for Python calling C code." description = "Foreign Function Interface for Python calling C code."
marker = "python_version >= \"2.7\" and python_version < \"2.8\" and (sys_platform == \"linux2\" or sys_platform == \"linux\") or python_version >= \"3.5\" and python_version < \"3.6\" and sys_platform == \"linux\" or python_version >= \"3.6\" and python_version < \"4.0\" and sys_platform == \"linux\""
name = "cffi" name = "cffi"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -119,11 +112,10 @@ pycparser = "*" ...@@ -119,11 +112,10 @@ pycparser = "*"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Validate configuration and produce human readable error messages." description = "Validate configuration and produce human readable error messages."
marker = "python_version >= \"3.6.1\" and python_version < \"4.0.0\""
name = "cfgv" name = "cfgv"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6.1"
version = "3.0.0" version = "3.1.0"
[[package]] [[package]]
category = "main" category = "main"
...@@ -147,7 +139,6 @@ clikit = ">=0.6.0,<0.7.0" ...@@ -147,7 +139,6 @@ clikit = ">=0.6.0,<0.7.0"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Composable command line interface toolkit" description = "Composable command line interface toolkit"
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\" or python_version >= \"3.6\" and python_version < \"4.0\""
name = "click" name = "click"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
...@@ -164,27 +155,14 @@ version = "0.6.2" ...@@ -164,27 +155,14 @@ version = "0.6.2"
[package.dependencies] [package.dependencies]
pastel = ">=0.2.0,<0.3.0" pastel = ">=0.2.0,<0.3.0"
pylev = ">=1.3,<2.0" pylev = ">=1.3,<2.0"
crashtest = {version = ">=0.3.0,<0.4.0", markers = "python_version >= \"3.6\" and python_version < \"4.0\""}
[package.dependencies.crashtest] enum34 = {version = ">=1.1,<2.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""}
python = ">=3.6,<4.0" typing = {version = ">=3.6,<4.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\""}
version = ">=0.3.0,<0.4.0" typing-extensions = {version = ">=3.6,<4.0", markers = "python_version >= \"3.5\" and python_full_version < \"3.5.4\""}
[package.dependencies.enum34]
python = ">=2.7,<2.8"
version = ">=1.1,<2.0"
[package.dependencies.typing]
python = ">=2.7,<2.8 || >=3.4,<3.5"
version = ">=3.6,<4.0"
[package.dependencies.typing-extensions]
python = ">=3.5,<3.5.4"
version = ">=3.6,<4.0"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Cross-platform colored terminal text." description = "Cross-platform colored terminal text."
marker = "sys_platform == \"win32\" and python_version != \"3.4\" and python_version < \"3.5\" or platform_system == \"Windows\" or python_version >= \"3.5\" and sys_platform == \"win32\" or sys_platform == \"win32\""
name = "colorama" name = "colorama"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
...@@ -193,7 +171,6 @@ version = "0.4.3" ...@@ -193,7 +171,6 @@ version = "0.4.3"
[[package]] [[package]]
category = "main" category = "main"
description = "Updated configparser from Python 3.7 for Python 2.6+." description = "Updated configparser from Python 3.7 for Python 2.6+."
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version == \"2.7\" and python_version == \"2.7\" or python_version < \"3\""
name = "configparser" name = "configparser"
optional = false optional = false
python-versions = ">=2.6" python-versions = ">=2.6"
...@@ -206,7 +183,6 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2)", "pytes ...@@ -206,7 +183,6 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2)", "pytes
[[package]] [[package]]
category = "main" category = "main"
description = "Backports and enhancements for the contextlib module" description = "Backports and enhancements for the contextlib module"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version < \"3.4\""
name = "contextlib2" name = "contextlib2"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
...@@ -226,7 +202,6 @@ toml = ["toml"] ...@@ -226,7 +202,6 @@ toml = ["toml"]
[[package]] [[package]]
category = "main" category = "main"
description = "Manage Python errors with ease" description = "Manage Python errors with ease"
marker = "python_version >= \"3.6\" and python_version < \"4.0\""
name = "crashtest" name = "crashtest"
optional = false optional = false
python-versions = ">=3.6,<4.0" python-versions = ">=3.6,<4.0"
...@@ -235,7 +210,6 @@ version = "0.3.0" ...@@ -235,7 +210,6 @@ version = "0.3.0"
[[package]] [[package]]
category = "main" category = "main"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
marker = "python_version >= \"2.7\" and python_version < \"2.8\" and (sys_platform == \"linux2\" or sys_platform == \"linux\") or python_version >= \"3.5\" and python_version < \"3.6\" and sys_platform == \"linux\" or python_version >= \"3.6\" and python_version < \"4.0\" and sys_platform == \"linux\""
name = "cryptography" name = "cryptography"
optional = false optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
...@@ -244,14 +218,8 @@ version = "2.9.2" ...@@ -244,14 +218,8 @@ version = "2.9.2"
[package.dependencies] [package.dependencies]
cffi = ">=1.8,<1.11.3 || >1.11.3" cffi = ">=1.8,<1.11.3 || >1.11.3"
six = ">=1.4.1" six = ">=1.4.1"
enum34 = {version = "*", markers = "python_version < \"3\""}
[package.dependencies.enum34] ipaddress = {version = "*", markers = "python_version < \"3\""}
python = "<3"
version = "*"
[package.dependencies.ipaddress]
python = "<3"
version = "*"
[package.extras] [package.extras]
docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0)", "sphinx-rtd-theme"] docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0)", "sphinx-rtd-theme"]
...@@ -266,29 +234,22 @@ description = "Distribution utilities" ...@@ -266,29 +234,22 @@ description = "Distribution utilities"
name = "distlib" name = "distlib"
optional = false optional = false
python-versions = "*" python-versions = "*"
version = "0.3.0" version = "0.3.1"
[package.dependencies]
nut = "*"
[[package]] [[package]]
category = "main" category = "main"
description = "Discover and load entry points from installed packages." description = "Discover and load entry points from installed packages."
marker = "python_version >= \"2.7\" and python_version < \"2.8\""
name = "entrypoints" name = "entrypoints"
optional = false optional = false
python-versions = ">=2.7" python-versions = ">=2.7"
version = "0.3" version = "0.3"
[package.dependencies] [package.dependencies]
[package.dependencies.configparser] configparser = {version = ">=3.5", markers = "python_version == \"2.7\""}
python = ">=2.7,<2.8"
version = ">=3.5"
[[package]] [[package]]
category = "main" category = "main"
description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"2.7\" and python_version < \"2.8\" and (sys_platform == \"linux2\" or sys_platform == \"linux\")"
name = "enum34" name = "enum34"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -305,7 +266,6 @@ version = "3.0.12" ...@@ -305,7 +266,6 @@ version = "3.0.12"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+" description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+"
marker = "python_version < \"3.0\""
name = "funcsigs" name = "funcsigs"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -314,25 +274,22 @@ version = "1.0.2" ...@@ -314,25 +274,22 @@ version = "1.0.2"
[[package]] [[package]]
category = "main" category = "main"
description = "Backport of the functools module from Python 3.2.3 for use on 2.7 and PyPy." description = "Backport of the functools module from Python 3.2.3 for use on 2.7 and PyPy."
marker = "python_version >= \"2.7\" and python_version < \"2.8\""
name = "functools32" name = "functools32"
optional = false optional = false
python-versions = "*" python-versions = "*"
version = "3.2.3-2" version = "3.2.3-2"
[[package]] [[package]]
category = "dev" category = "main"
description = "Clean single-source support for Python 3 and 2" description = "Backport of the concurrent.futures package from Python 3"
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\"" name = "futures"
name = "future"
optional = false optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" python-versions = ">=2.6, <3"
version = "0.18.2" version = "3.3.0"
[[package]] [[package]]
category = "main" category = "main"
description = "Version of the glob module that can capture patterns and supports recursive wildcards" description = "Version of the glob module that can capture patterns and supports recursive wildcards"
marker = "python_version >= \"2.7\" and python_version < \"2.8\""
name = "glob2" name = "glob2"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -370,11 +327,10 @@ six = "*" ...@@ -370,11 +327,10 @@ six = "*"
[[package]] [[package]]
category = "dev" category = "dev"
description = "File identification library for Python" description = "File identification library for Python"
marker = "python_version >= \"3.6.1\" and python_version < \"4.0.0\""
name = "identify" name = "identify"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
version = "1.4.20" version = "1.4.21"
[package.extras] [package.extras]
license = ["editdistance"] license = ["editdistance"]
...@@ -385,31 +341,21 @@ description = "Internationalized Domain Names in Applications (IDNA)" ...@@ -385,31 +341,21 @@ description = "Internationalized Domain Names in Applications (IDNA)"
name = "idna" name = "idna"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "2.9" version = "2.10"
[[package]] [[package]]
category = "main" category = "main"
description = "Read metadata from Python packages" description = "Read metadata from Python packages"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version < \"3.8\" or python_version >= \"3.5\" and python_version < \"3.6\" or python_version >= \"3.5\" and python_version < \"3.8\" or python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"3.8\" or python_version >= \"3.6\" and python_version < \"3.8\""
name = "importlib-metadata" name = "importlib-metadata"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
version = "1.6.1" version = "1.7.0"
[package.dependencies] [package.dependencies]
zipp = ">=0.5" zipp = ">=0.5"
configparser = {version = ">=3.5", markers = "python_version < \"3\""}
[package.dependencies.configparser] contextlib2 = {version = "*", markers = "python_version < \"3\""}
python = "<3" pathlib2 = {version = "*", markers = "python_version < \"3\""}
version = ">=3.5"
[package.dependencies.contextlib2]
python = "<3"
version = "*"
[package.dependencies.pathlib2]
python = "<3"
version = "*"
[package.extras] [package.extras]
docs = ["sphinx", "rst.linker"] docs = ["sphinx", "rst.linker"]
...@@ -418,36 +364,17 @@ testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] ...@@ -418,36 +364,17 @@ testing = ["packaging", "pep517", "importlib-resources (>=1.3)"]
[[package]] [[package]]
category = "main" category = "main"
description = "Read resources from Python packages" description = "Read resources from Python packages"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version < \"3.7\" or python_version >= \"3.6.1\" and python_version < \"3.7\""
name = "importlib-resources" name = "importlib-resources"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
version = "2.0.1" version = "3.0.0"
[package.dependencies] [package.dependencies]
[package.dependencies.contextlib2] contextlib2 = {version = "*", markers = "python_version < \"3\""}
python = "<3" pathlib2 = {version = "*", markers = "python_version < \"3\""}
version = "*" singledispatch = {version = "*", markers = "python_version < \"3.4\""}
typing = {version = "*", markers = "python_version < \"3.5\""}
[package.dependencies.importlib-metadata] zipp = {version = ">=0.4", markers = "python_version < \"3.8\""}
python = "<3.8"
version = "*"
[package.dependencies.pathlib2]
python = "<3"
version = "*"
[package.dependencies.singledispatch]
python = "<3.4"
version = "*"
[package.dependencies.typing]
python = "<3.5"
version = "*"
[package.dependencies.zipp]
python = "<3.8"
version = ">=0.4"
[package.extras] [package.extras]
docs = ["sphinx", "rst.linker", "jaraco.packaging"] docs = ["sphinx", "rst.linker", "jaraco.packaging"]
...@@ -455,7 +382,6 @@ docs = ["sphinx", "rst.linker", "jaraco.packaging"] ...@@ -455,7 +382,6 @@ docs = ["sphinx", "rst.linker", "jaraco.packaging"]
[[package]] [[package]]
category = "main" category = "main"
description = "IPv4/IPv6 manipulation library" description = "IPv4/IPv6 manipulation library"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" and (sys_platform == \"linux2\" or sys_platform == \"linux\")"
name = "ipaddress" name = "ipaddress"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -464,7 +390,6 @@ version = "1.0.23" ...@@ -464,7 +390,6 @@ version = "1.0.23"
[[package]] [[package]]
category = "main" category = "main"
description = "Low-level, pure Python DBus protocol wrapper." description = "Low-level, pure Python DBus protocol wrapper."
marker = "python_version >= \"3.5\" and python_version < \"3.6\" and sys_platform == \"linux\" or python_version >= \"3.6\" and python_version < \"4.0\" and sys_platform == \"linux\""
name = "jeepney" name = "jeepney"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
...@@ -476,7 +401,6 @@ dev = ["testpath"] ...@@ -476,7 +401,6 @@ dev = ["testpath"]
[[package]] [[package]]
category = "dev" category = "dev"
description = "A very fast and expressive template engine." description = "A very fast and expressive template engine."
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "jinja2" name = "jinja2"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
...@@ -489,27 +413,8 @@ MarkupSafe = ">=0.23" ...@@ -489,27 +413,8 @@ MarkupSafe = ">=0.23"
i18n = ["Babel (>=0.8)"] i18n = ["Babel (>=0.8)"]
[[package]] [[package]]
category = "dev"
description = "Lightweight pipelining: using Python functions as pipeline jobs."
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "joblib"
optional = false
python-versions = "*"
version = "0.14.1"
[[package]]
category = "dev"
description = "Lightweight pipelining: using Python functions as pipeline jobs."
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "joblib"
optional = false
python-versions = ">=3.6"
version = "0.15.1"
[[package]]
category = "main" category = "main"
description = "Store and access your passwords safely." description = "Store and access your passwords safely."
marker = "python_version >= \"2.7\" and python_version < \"2.8\""
name = "keyring" name = "keyring"
optional = false optional = false
python-versions = ">=2.7" python-versions = ">=2.7"
...@@ -517,11 +422,8 @@ version = "18.0.1" ...@@ -517,11 +422,8 @@ version = "18.0.1"
[package.dependencies] [package.dependencies]
entrypoints = "*" entrypoints = "*"
pywin32-ctypes = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1" pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""}
secretstorage = {version = "<3", markers = "(sys_platform == \"linux2\" or sys_platform == \"linux\") and python_version < \"3.5\""}
[package.dependencies.secretstorage]
python = "<3.5"
version = "<3"
[package.extras] [package.extras]
docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
...@@ -530,19 +432,15 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs", "pytest-flake8 ...@@ -530,19 +432,15 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs", "pytest-flake8
[[package]] [[package]]
category = "main" category = "main"
description = "Store and access your passwords safely." description = "Store and access your passwords safely."
marker = "python_version >= \"3.5\" and python_version < \"3.6\""
name = "keyring" name = "keyring"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
version = "20.0.1" version = "20.0.1"
[package.dependencies] [package.dependencies]
pywin32-ctypes = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1" importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
secretstorage = "*" pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""}
secretstorage = {version = "*", markers = "sys_platform == \"linux\""}
[package.dependencies.importlib-metadata]
python = "<3.8"
version = "*"
[package.extras] [package.extras]
docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
...@@ -551,20 +449,16 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pyt ...@@ -551,20 +449,16 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pyt
[[package]] [[package]]
category = "main" category = "main"
description = "Store and access your passwords safely." description = "Store and access your passwords safely."
marker = "python_version >= \"3.6\" and python_version < \"4.0\""
name = "keyring" name = "keyring"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
version = "21.2.1" version = "21.2.1"
[package.dependencies] [package.dependencies]
SecretStorage = ">=3" SecretStorage = {version = ">=3", markers = "sys_platform == \"linux\""}
jeepney = ">=0.4.2" importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
pywin32-ctypes = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1" jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""}
pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""}
[package.dependencies.importlib-metadata]
python = "<3.8"
version = "*"
[package.extras] [package.extras]
docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
...@@ -573,7 +467,6 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pyt ...@@ -573,7 +467,6 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pyt
[[package]] [[package]]
category = "dev" category = "dev"
description = "Python LiveReload is an awesome tool for web developers" description = "Python LiveReload is an awesome tool for web developers"
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "livereload" name = "livereload"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -582,8 +475,12 @@ version = "2.6.2" ...@@ -582,8 +475,12 @@ version = "2.6.2"
[package.dependencies] [package.dependencies]
six = "*" six = "*"
[package.dependencies.tornado] [[package.dependencies.tornado]]
python = ">=2.8" markers = "python_version == \"2.7\""
version = "<6"
[[package.dependencies.tornado]]
markers = "python_version > \"2.7\""
version = "*" version = "*"
[[package]] [[package]]
...@@ -596,52 +493,12 @@ version = "0.12.2" ...@@ -596,52 +493,12 @@ version = "0.12.2"
[[package]] [[package]]
category = "dev" category = "dev"
description = "A Python implementation of Lunr.js"
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "lunr"
optional = false
python-versions = "*"
version = "0.5.8"
[package.dependencies]
future = ">=0.16.0"
six = ">=1.11.0"
[package.dependencies.nltk]
optional = true
python = ">=2.8"
version = ">=3.2.5"
[package.extras]
languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"]
[[package]]
category = "dev"
description = "Python implementation of Markdown." description = "Python implementation of Markdown."
name = "markdown" name = "markdown"
optional = false optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
version = "3.1.1" version = "3.1.1"
[package.dependencies]
setuptools = ">=36"
[package.extras]
testing = ["coverage", "pyyaml"]
[[package]]
category = "dev"
description = "Python implementation of Markdown."
name = "markdown"
optional = false
python-versions = ">=3.5"
version = "3.2.2"
[package.dependencies]
[package.dependencies.importlib-metadata]
python = "<3.8"
version = "*"
[package.extras] [package.extras]
testing = ["coverage", "pyyaml"] testing = ["coverage", "pyyaml"]
...@@ -659,7 +516,6 @@ markdown = "*" ...@@ -659,7 +516,6 @@ markdown = "*"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Safely add untrusted strings to HTML/XML markup." description = "Safely add untrusted strings to HTML/XML markup."
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "markupsafe" name = "markupsafe"
optional = false optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
...@@ -668,7 +524,6 @@ version = "1.1.1" ...@@ -668,7 +524,6 @@ version = "1.1.1"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Project documentation with Markdown." description = "Project documentation with Markdown."
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "mkdocs" name = "mkdocs"
optional = false optional = false
python-versions = ">=2.7.9,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" python-versions = ">=2.7.9,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
...@@ -684,29 +539,7 @@ tornado = ">=5.0" ...@@ -684,29 +539,7 @@ tornado = ">=5.0"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Project documentation with Markdown."
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "mkdocs"
optional = false
python-versions = ">=3.5"
version = "1.1.2"
[package.dependencies]
Jinja2 = ">=2.10.1"
Markdown = ">=3.2.1"
PyYAML = ">=3.10"
click = ">=3.3"
livereload = ">=2.5.1"
tornado = ">=5.0"
[package.dependencies.lunr]
extras = ["languages"]
version = "0.5.8"
[[package]]
category = "dev"
description = "Rolling backport of unittest.mock for all Pythons" description = "Rolling backport of unittest.mock for all Pythons"
marker = "python_version < \"3.0\""
name = "mock" name = "mock"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
...@@ -714,10 +547,7 @@ version = "3.0.5" ...@@ -714,10 +547,7 @@ version = "3.0.5"
[package.dependencies] [package.dependencies]
six = "*" six = "*"
funcsigs = {version = ">=1", markers = "python_version < \"3.3\""}
[package.dependencies.funcsigs]
python = "<3.3"
version = ">=1"
[package.extras] [package.extras]
build = ["twine", "wheel", "blurb"] build = ["twine", "wheel", "blurb"]
...@@ -727,7 +557,6 @@ test = ["pytest", "pytest-cov"] ...@@ -727,7 +557,6 @@ test = ["pytest", "pytest-cov"]
[[package]] [[package]]
category = "dev" category = "dev"
description = "More routines for operating on iterables, beyond itertools" description = "More routines for operating on iterables, beyond itertools"
marker = "python_version <= \"2.7\""
name = "more-itertools" name = "more-itertools"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -754,31 +583,7 @@ version = "1.0.0" ...@@ -754,31 +583,7 @@ version = "1.0.0"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Natural Language Toolkit"
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "nltk"
optional = false
python-versions = "*"
version = "3.5"
[package.dependencies]
click = "*"
joblib = "*"
regex = "*"
tqdm = "*"
[package.extras]
all = ["requests", "numpy", "python-crfsuite", "scikit-learn", "twython", "pyparsing", "scipy", "matplotlib", "gensim"]
corenlp = ["requests"]
machine_learning = ["gensim", "numpy", "python-crfsuite", "scikit-learn", "scipy"]
plot = ["matplotlib"]
tgrep = ["pyparsing"]
twitter = ["twython"]
[[package]]
category = "dev"
description = "Node.js virtual environment builder" description = "Node.js virtual environment builder"
marker = "python_version >= \"3.6.1\" and python_version < \"4.0.0\""
name = "nodeenv" name = "nodeenv"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -786,14 +591,6 @@ version = "1.4.0" ...@@ -786,14 +591,6 @@ version = "1.4.0"
[[package]] [[package]]
category = "main" category = "main"
description = "Network utility... things like a UDP/TCP relay."
name = "nut"
optional = false
python-versions = "*"
version = "0.2.0"
[[package]]
category = "dev"
description = "Core utilities for Python packages" description = "Core utilities for Python packages"
name = "packaging" name = "packaging"
optional = false optional = false
...@@ -815,7 +612,6 @@ version = "0.2.0" ...@@ -815,7 +612,6 @@ version = "0.2.0"
[[package]] [[package]]
category = "main" category = "main"
description = "Object-oriented filesystem paths" description = "Object-oriented filesystem paths"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"2.7\" and python_version < \"2.8\" and sys_platform != \"win32\" or python_version < \"3.5\" or python_version >= \"3.5\" and python_version < \"3.6\""
name = "pathlib2" name = "pathlib2"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -823,15 +619,11 @@ version = "2.3.5" ...@@ -823,15 +619,11 @@ version = "2.3.5"
[package.dependencies] [package.dependencies]
six = "*" six = "*"
scandir = {version = "*", markers = "python_version < \"3.5\""}
[package.dependencies.scandir]
python = "<3.5"
version = "*"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Utility library for gitignore style pattern matching of file paths." description = "Utility library for gitignore style pattern matching of file paths."
marker = "python_version >= \"3.6\" and python_version < \"4.0\""
name = "pathspec" name = "pathspec"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
...@@ -876,9 +668,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" ...@@ -876,9 +668,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.13.1" version = "0.13.1"
[package.dependencies] [package.dependencies]
[package.dependencies.importlib-metadata] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
python = "<3.8"
version = ">=0.12"
[package.extras] [package.extras]
dev = ["pre-commit", "tox"] dev = ["pre-commit", "tox"]
...@@ -892,30 +682,18 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" ...@@ -892,30 +682,18 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "1.0.0a8" version = "1.0.0a8"
[package.dependencies] [package.dependencies]
[package.dependencies.enum34] enum34 = {version = ">=1.1.10,<2.0.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""}
python = ">=2.7,<2.8" functools32 = {version = ">=3.2.3-2,<4.0.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""}
version = ">=1.1.10,<2.0.0" pathlib2 = {version = ">=2.3.5,<3.0.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""}
typing = {version = ">=3.7.4.1,<4.0.0.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""}
[package.dependencies.functools32]
python = ">=2.7,<2.8"
version = ">=3.2.3-2,<4.0.0"
[package.dependencies.pathlib2]
python = ">=2.7,<2.8"
version = ">=2.3.5,<3.0.0"
[package.dependencies.typing]
python = ">=2.7,<2.8"
version = ">=3.7.4.1,<4.0.0.0"
[[package]] [[package]]
category = "dev" category = "dev"
description = "A framework for managing and maintaining multi-language pre-commit hooks." description = "A framework for managing and maintaining multi-language pre-commit hooks."
marker = "python_version >= \"3.6.1\" and python_version < \"4.0.0\""
name = "pre-commit" name = "pre-commit"
optional = false optional = false
python-versions = ">=3.6.1" python-versions = ">=3.6.1"
version = "2.5.1" version = "2.6.0"
[package.dependencies] [package.dependencies]
cfgv = ">=2.0.0" cfgv = ">=2.0.0"
...@@ -924,14 +702,8 @@ nodeenv = ">=0.11.1" ...@@ -924,14 +702,8 @@ nodeenv = ">=0.11.1"
pyyaml = ">=5.1" pyyaml = ">=5.1"
toml = "*" toml = "*"
virtualenv = ">=20.0.8" virtualenv = ">=20.0.8"
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
[package.dependencies.importlib-metadata] importlib-resources = {version = "*", markers = "python_version < \"3.7\""}
python = "<3.8"
version = "*"
[package.dependencies.importlib-resources]
python = "<3.7"
version = "*"
[[package]] [[package]]
category = "main" category = "main"
...@@ -952,7 +724,6 @@ version = "1.9.0" ...@@ -952,7 +724,6 @@ version = "1.9.0"
[[package]] [[package]]
category = "main" category = "main"
description = "C parser in Python" description = "C parser in Python"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" and (sys_platform == \"linux2\" or sys_platform == \"linux\") or python_version >= \"3.5\" and python_version < \"3.6\" and sys_platform == \"linux\" or python_version >= \"3.6\" and python_version < \"4.0\" and sys_platform == \"linux\""
name = "pycparser" name = "pycparser"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
...@@ -968,14 +739,6 @@ version = "2.5.2" ...@@ -968,14 +739,6 @@ version = "2.5.2"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Pygments is a syntax highlighting package written in Python."
name = "pygments"
optional = false
python-versions = ">=3.5"
version = "2.6.1"
[[package]]
category = "dev"
description = "Pygments Github custom lexers." description = "Pygments Github custom lexers."
name = "pygments-github-lexers" name = "pygments-github-lexers"
optional = false optional = false
...@@ -1006,18 +769,7 @@ Markdown = ">=3.0.1" ...@@ -1006,18 +769,7 @@ Markdown = ">=3.0.1"
pep562 = "*" pep562 = "*"
[[package]] [[package]]
category = "dev" category = "main"
description = "Extension pack for Python Markdown."
name = "pymdown-extensions"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
version = "6.3"
[package.dependencies]
Markdown = ">=3.2"
[[package]]
category = "dev"
description = "Python parsing module" description = "Python parsing module"
name = "pyparsing" name = "pyparsing"
optional = false optional = false
...@@ -1040,26 +792,18 @@ pluggy = ">=0.12,<1.0" ...@@ -1040,26 +792,18 @@ pluggy = ">=0.12,<1.0"
py = ">=1.5.0" py = ">=1.5.0"
six = ">=1.10.0" six = ">=1.10.0"
wcwidth = "*" wcwidth = "*"
colorama = {version = "*", markers = "sys_platform == \"win32\" and python_version != \"3.4\""}
funcsigs = {version = ">=1.0", markers = "python_version < \"3.0\""}
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
pathlib2 = {version = ">=2.2.0", markers = "python_version < \"3.6\""}
[package.dependencies.colorama] [[package.dependencies.more-itertools]]
python = "<3.4.0 || >=3.5.0" markers = "python_version <= \"2.7\""
version = "*"
[package.dependencies.funcsigs]
python = "<3.0"
version = ">=1.0"
[package.dependencies.importlib-metadata]
python = "<3.8"
version = ">=0.12"
[package.dependencies.more-itertools]
python = "<2.8"
version = ">=4.0.0,<6.0.0" version = ">=4.0.0,<6.0.0"
[package.dependencies.pathlib2] [[package.dependencies.more-itertools]]
python = "<3.6" markers = "python_version > \"2.7\""
version = ">=2.2.0" version = ">=4.0.0"
[package.extras] [package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"] testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"]
...@@ -1073,22 +817,16 @@ python-versions = ">=3.5" ...@@ -1073,22 +817,16 @@ python-versions = ">=3.5"
version = "5.4.3" version = "5.4.3"
[package.dependencies] [package.dependencies]
atomicwrites = ">=1.0"
attrs = ">=17.4.0" attrs = ">=17.4.0"
colorama = "*"
more-itertools = ">=4.0.0" more-itertools = ">=4.0.0"
packaging = "*" packaging = "*"
pluggy = ">=0.12,<1.0" pluggy = ">=0.12,<1.0"
py = ">=1.5.0" py = ">=1.5.0"
wcwidth = "*" wcwidth = "*"
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
[package.dependencies.importlib-metadata] colorama = {version = "*", markers = "sys_platform == \"win32\""}
python = "<3.8" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
version = ">=0.12" pathlib2 = {version = ">=2.2.0", markers = "python_version < \"3.6\""}
[package.dependencies.pathlib2]
python = "<3.6"
version = ">=2.2.0"
[package.extras] [package.extras]
checkqa-mypy = ["mypy (v0.761)"] checkqa-mypy = ["mypy (v0.761)"]
...@@ -1119,10 +857,7 @@ version = "1.13.0" ...@@ -1119,10 +857,7 @@ version = "1.13.0"
[package.dependencies] [package.dependencies]
pytest = ">=2.7" pytest = ">=2.7"
mock = {version = "*", markers = "python_version < \"3.0\""}
[package.dependencies.mock]
python = "<3.0"
version = "*"
[package.extras] [package.extras]
dev = ["pre-commit", "tox"] dev = ["pre-commit", "tox"]
...@@ -1143,7 +878,6 @@ termcolor = ">=1.1.0" ...@@ -1143,7 +878,6 @@ termcolor = ">=1.1.0"
[[package]] [[package]]
category = "main" category = "main"
description = "" description = ""
marker = "python_version >= \"2.7\" and python_version < \"2.8\" and sys_platform == \"win32\" or python_version >= \"3.5\" and python_version < \"3.6\" and sys_platform == \"win32\" or python_version >= \"3.6\" and python_version < \"4.0\" and sys_platform == \"win32\""
name = "pywin32-ctypes" name = "pywin32-ctypes"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -1152,7 +886,6 @@ version = "0.2.0" ...@@ -1152,7 +886,6 @@ version = "0.2.0"
[[package]] [[package]]
category = "dev" category = "dev"
description = "YAML parser and emitter for Python" description = "YAML parser and emitter for Python"
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\" or python_version >= \"3.6.1\" and python_version < \"4.0.0\""
name = "pyyaml" name = "pyyaml"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
...@@ -1161,7 +894,6 @@ version = "5.3.1" ...@@ -1161,7 +894,6 @@ version = "5.3.1"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Alternative regular expression module, to replace re." description = "Alternative regular expression module, to replace re."
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\" or python_version >= \"3.6\" and python_version < \"4.0\""
name = "regex" name = "regex"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -1199,7 +931,6 @@ requests = ">=2.0.1,<3.0.0" ...@@ -1199,7 +931,6 @@ requests = ">=2.0.1,<3.0.0"
[[package]] [[package]]
category = "main" category = "main"
description = "scandir, a better directory iterator and faster os.walk()" description = "scandir, a better directory iterator and faster os.walk()"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"2.7\" and python_version < \"2.8\" and sys_platform != \"win32\""
name = "scandir" name = "scandir"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -1208,7 +939,6 @@ version = "1.10.0" ...@@ -1208,7 +939,6 @@ version = "1.10.0"
[[package]] [[package]]
category = "main" category = "main"
description = "Python bindings to FreeDesktop.org Secret Service API" description = "Python bindings to FreeDesktop.org Secret Service API"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" and (sys_platform == \"linux2\" or sys_platform == \"linux\")"
name = "secretstorage" name = "secretstorage"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -1223,7 +953,6 @@ dbus-python = ["dbus-python"] ...@@ -1223,7 +953,6 @@ dbus-python = ["dbus-python"]
[[package]] [[package]]
category = "main" category = "main"
description = "Python bindings to FreeDesktop.org Secret Service API" description = "Python bindings to FreeDesktop.org Secret Service API"
marker = "python_version >= \"3.5\" and python_version < \"3.6\" and sys_platform == \"linux\" or python_version >= \"3.6\" and python_version < \"4.0\" and sys_platform == \"linux\""
name = "secretstorage" name = "secretstorage"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
...@@ -1244,7 +973,6 @@ version = "1.3.2" ...@@ -1244,7 +973,6 @@ version = "1.3.2"
[[package]] [[package]]
category = "main" category = "main"
description = "This library brings functools.singledispatch from Python 3.4 to Python 2.6-3.3." description = "This library brings functools.singledispatch from Python 3.4 to Python 2.6-3.3."
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version < \"3.4\" or python_version >= \"2.7\" and python_version < \"2.8\" and (python_version >= \"2.7\" and python_version < \"2.8\" or python_version < \"3.4\")"
name = "singledispatch" name = "singledispatch"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -1264,7 +992,6 @@ version = "1.15.0" ...@@ -1264,7 +992,6 @@ version = "1.15.0"
[[package]] [[package]]
category = "main" category = "main"
description = "A backport of the subprocess module from Python 3 for use on 2.x." description = "A backport of the subprocess module from Python 3 for use on 2.x."
marker = "python_version >= \"2.7\" and python_version < \"2.8\""
name = "subprocess32" name = "subprocess32"
optional = false optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4" python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4"
...@@ -1295,22 +1022,13 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" ...@@ -1295,22 +1022,13 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.5.11" version = "0.5.11"
[package.dependencies] [package.dependencies]
[package.dependencies.enum34] enum34 = {version = ">=1.1,<2.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""}
python = ">=2.7,<2.8" functools32 = {version = ">=3.2.3,<4.0.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""}
version = ">=1.1,<2.0" typing = {version = ">=3.6,<4.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\""}
[package.dependencies.functools32]
python = ">=2.7,<2.8"
version = ">=3.2.3,<4.0.0"
[package.dependencies.typing]
python = ">=2.7,<2.8 || >=3.4,<3.5"
version = ">=3.6,<4.0"
[[package]] [[package]]
category = "dev" category = "dev"
description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "tornado" name = "tornado"
optional = false optional = false
python-versions = ">= 2.7, !=3.0.*, !=3.1.*, !=3.2.*, != 3.3.*" python-versions = ">= 2.7, !=3.0.*, !=3.1.*, !=3.2.*, != 3.3.*"
...@@ -1322,10 +1040,9 @@ description = "tox is a generic virtualenv management and test command line tool ...@@ -1322,10 +1040,9 @@ description = "tox is a generic virtualenv management and test command line tool
name = "tox" name = "tox"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
version = "3.15.2" version = "3.16.1"
[package.dependencies] [package.dependencies]
colorama = ">=0.4.1"
filelock = ">=3.0.0" filelock = ">=3.0.0"
packaging = ">=14" packaging = ">=14"
pluggy = ">=0.12.0" pluggy = ">=0.12.0"
...@@ -1333,10 +1050,8 @@ py = ">=1.4.17" ...@@ -1333,10 +1050,8 @@ py = ">=1.4.17"
six = ">=1.14.0" six = ">=1.14.0"
toml = ">=0.9.4" toml = ">=0.9.4"
virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7"
colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""}
[package.dependencies.importlib-metadata] importlib-metadata = {version = ">=0.12,<2", markers = "python_version < \"3.8\""}
python = "<3.8"
version = ">=0.12,<2"
[package.extras] [package.extras]
docs = ["sphinx (>=2.0.0)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"] docs = ["sphinx (>=2.0.0)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"]
...@@ -1344,20 +1059,7 @@ testing = ["freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "pytest (>=4.0.0)", "py ...@@ -1344,20 +1059,7 @@ testing = ["freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "pytest (>=4.0.0)", "py
[[package]] [[package]]
category = "dev" category = "dev"
description = "Fast, Extensible Progress Meter"
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "tqdm"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*"
version = "4.46.1"
[package.extras]
dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"]
[[package]]
category = "dev"
description = "a fork of Python 2 and 3 ast modules with type comment support" description = "a fork of Python 2 and 3 ast modules with type comment support"
marker = "python_version >= \"3.6\" and python_version < \"4.0\""
name = "typed-ast" name = "typed-ast"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -1366,7 +1068,6 @@ version = "1.4.1" ...@@ -1366,7 +1068,6 @@ version = "1.4.1"
[[package]] [[package]]
category = "main" category = "main"
description = "Type Hints for Python" description = "Type Hints for Python"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\" or python_version >= \"2.7\" and python_version < \"2.8\" and (python_version >= \"2.7\" and python_version < \"2.8\" or python_version < \"3.5\") or python_version < \"3.5\""
name = "typing" name = "typing"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -1375,7 +1076,6 @@ version = "3.7.4.1" ...@@ -1375,7 +1076,6 @@ version = "3.7.4.1"
[[package]] [[package]]
category = "main" category = "main"
description = "Backported and Experimental Type Hints for Python 3.5+" description = "Backported and Experimental Type Hints for Python 3.5+"
marker = "python_version >= \"3.5\" and python_full_version < \"3.5.4\""
name = "typing-extensions" name = "typing-extensions"
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -1407,18 +1107,9 @@ appdirs = ">=1.4.3,<2" ...@@ -1407,18 +1107,9 @@ appdirs = ">=1.4.3,<2"
distlib = ">=0.3.0,<1" distlib = ">=0.3.0,<1"
filelock = ">=3.0.0,<4" filelock = ">=3.0.0,<4"
six = ">=1.9.0,<2" six = ">=1.9.0,<2"
importlib-metadata = {version = ">=0.12,<2", markers = "python_version < \"3.8\""}
[package.dependencies.importlib-metadata] importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""}
python = "<3.8" pathlib2 = {version = ">=2.3.3,<3", markers = "python_version < \"3.4\" and sys_platform != \"win32\""}
version = ">=0.12,<2"
[package.dependencies.importlib-resources]
python = "<3.7"
version = ">=1.0"
[package.dependencies.pathlib2]
python = "<3.4"
version = ">=2.3.3,<3"
[package.extras] [package.extras]
docs = ["sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)", "proselint (>=0.10.2)"] docs = ["sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)", "proselint (>=0.10.2)"]
...@@ -1433,9 +1124,7 @@ python-versions = "*" ...@@ -1433,9 +1124,7 @@ python-versions = "*"
version = "0.2.5" version = "0.2.5"
[package.dependencies] [package.dependencies]
[package.dependencies."backports.functools-lru-cache"] "backports.functools-lru-cache" = {version = ">=1.2.1", markers = "python_version < \"3.2\""}
python = "<3.2"
version = ">=1.2.1"
[[package]] [[package]]
category = "main" category = "main"
...@@ -1448,36 +1137,20 @@ version = "0.5.1" ...@@ -1448,36 +1137,20 @@ version = "0.5.1"
[[package]] [[package]]
category = "main" category = "main"
description = "Backport of pathlib-compatible object wrapper for zip files" description = "Backport of pathlib-compatible object wrapper for zip files"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version < \"3.8\" or python_version >= \"3.5\" and python_version < \"3.6\" or python_version >= \"3.5\" and python_version < \"3.8\" or python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"3.8\""
name = "zipp" name = "zipp"
optional = false optional = false
python-versions = ">=2.7" python-versions = ">=2.7"
version = "1.2.0" version = "1.2.0"
[package.dependencies] [package.dependencies]
[package.dependencies.contextlib2] contextlib2 = {version = "*", markers = "python_version < \"3.4\""}
python = "<3.4"
version = "*"
[package.extras] [package.extras]
docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["pathlib2", "unittest2", "jaraco.itertools", "func-timeout"] testing = ["pathlib2", "unittest2", "jaraco.itertools", "func-timeout"]
[[package]]
category = "main"
description = "Backport of pathlib-compatible object wrapper for zip files"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.6\" and python_version < \"3.8\" or python_version < \"3.8\" or python_version >= \"3.5\" and python_version < \"3.8\" or python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"3.8\""
name = "zipp"
optional = false
python-versions = ">=3.6"
version = "3.1.0"
[package.extras]
docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["jaraco.itertools", "func-timeout"]
[metadata] [metadata]
content-hash = "78c1880171165781164556d08eb2cdd58ed2c708b73dfa6a92acd2dd1df3739c" content-hash = "363cbb8480f9bbda72b318c6edfcaa1de130ceecba7c7d0e774005a3bca91df5"
python-versions = "~2.7 || ^3.5" python-versions = "~2.7 || ^3.5"
[metadata.files] [metadata.files]
...@@ -1544,8 +1217,8 @@ cffi = [ ...@@ -1544,8 +1217,8 @@ cffi = [
{file = "cffi-1.14.0.tar.gz", hash = "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6"}, {file = "cffi-1.14.0.tar.gz", hash = "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6"},
] ]
cfgv = [ cfgv = [
{file = "cfgv-3.0.0-py2.py3-none-any.whl", hash = "sha256:f22b426ed59cd2ab2b54ff96608d846c33dfb8766a67f0b4a6ce130ce244414f"}, {file = "cfgv-3.1.0-py2.py3-none-any.whl", hash = "sha256:1ccf53320421aeeb915275a196e23b3b8ae87dea8ac6698b1638001d4a486d53"},
{file = "cfgv-3.0.0.tar.gz", hash = "sha256:04b093b14ddf9fd4d17c53ebfd55582d27b76ed30050193c14e560770c5360eb"}, {file = "cfgv-3.1.0.tar.gz", hash = "sha256:c8e8f552ffcc6194f4e18dd4f68d9aef0c0d58ae7e7be8c82bee3c5e9edfa513"},
] ]
chardet = [ chardet = [
{file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
...@@ -1634,7 +1307,8 @@ cryptography = [ ...@@ -1634,7 +1307,8 @@ cryptography = [
{file = "cryptography-2.9.2.tar.gz", hash = "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229"}, {file = "cryptography-2.9.2.tar.gz", hash = "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229"},
] ]
distlib = [ distlib = [
{file = "distlib-0.3.0.zip", hash = "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21"}, {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
{file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"},
] ]
entrypoints = [ entrypoints = [
{file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"},
...@@ -1657,8 +1331,9 @@ functools32 = [ ...@@ -1657,8 +1331,9 @@ functools32 = [
{file = "functools32-3.2.3-2.tar.gz", hash = "sha256:f6253dfbe0538ad2e387bd8fdfd9293c925d63553f5813c4e587745416501e6d"}, {file = "functools32-3.2.3-2.tar.gz", hash = "sha256:f6253dfbe0538ad2e387bd8fdfd9293c925d63553f5813c4e587745416501e6d"},
{file = "functools32-3.2.3-2.zip", hash = "sha256:89d824aa6c358c421a234d7f9ee0bd75933a67c29588ce50aaa3acdf4d403fa0"}, {file = "functools32-3.2.3-2.zip", hash = "sha256:89d824aa6c358c421a234d7f9ee0bd75933a67c29588ce50aaa3acdf4d403fa0"},
] ]
future = [ futures = [
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, {file = "futures-3.3.0-py2-none-any.whl", hash = "sha256:49b3f5b064b6e3afc3316421a3f25f66c137ae88f068abbf72830170033c5e16"},
{file = "futures-3.3.0.tar.gz", hash = "sha256:7e033af76a5e35f58e56da7a91e687706faf4e7bdfb2cbc3f2cca6b9bcda9794"},
] ]
glob2 = [ glob2 = [
{file = "glob2-0.6.tar.gz", hash = "sha256:f5b0a686ff21f820c4d3f0c4edd216704cea59d79d00fa337e244a2f2ff83ed6"}, {file = "glob2-0.6.tar.gz", hash = "sha256:f5b0a686ff21f820c4d3f0c4edd216704cea59d79d00fa337e244a2f2ff83ed6"},
...@@ -1671,20 +1346,20 @@ httpretty = [ ...@@ -1671,20 +1346,20 @@ httpretty = [
{file = "httpretty-0.9.7.tar.gz", hash = "sha256:66216f26b9d2c52e81808f3e674a6fb65d4bf719721394a1a9be926177e55fbe"}, {file = "httpretty-0.9.7.tar.gz", hash = "sha256:66216f26b9d2c52e81808f3e674a6fb65d4bf719721394a1a9be926177e55fbe"},
] ]
identify = [ identify = [
{file = "identify-1.4.20-py2.py3-none-any.whl", hash = "sha256:acf0712ab4042642e8f44e9532d95c26fbe60c0ab8b6e5b654dd1bc6512810e0"}, {file = "identify-1.4.21-py2.py3-none-any.whl", hash = "sha256:dac33eff90d57164e289fb20bf4e131baef080947ee9bf45efcd0da8d19064bf"},
{file = "identify-1.4.20.tar.gz", hash = "sha256:b2cd24dece806707e0b50517c1b3bcf3044e0b1cb13a72e7d34aa31c91f2a55a"}, {file = "identify-1.4.21.tar.gz", hash = "sha256:c4d07f2b979e3931894170a9e0d4b8281e6905ea6d018c326f7ffefaf20db680"},
] ]
idna = [ idna = [
{file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"}, {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
{file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
] ]
importlib-metadata = [ importlib-metadata = [
{file = "importlib_metadata-1.6.1-py2.py3-none-any.whl", hash = "sha256:15ec6c0fd909e893e3a08b3a7c76ecb149122fb14b7efe1199ddd4c7c57ea958"}, {file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"},
{file = "importlib_metadata-1.6.1.tar.gz", hash = "sha256:0505dd08068cfec00f53a74a0ad927676d7757da81b7436a6eefe4c7cf75c545"}, {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"},
] ]
importlib-resources = [ importlib-resources = [
{file = "importlib_resources-2.0.1-py2.py3-none-any.whl", hash = "sha256:83985739b3a6679702f9ab33f0ad016ad564664d0568a31ac14d7c64789453e6"}, {file = "importlib_resources-3.0.0-py2.py3-none-any.whl", hash = "sha256:d028f66b66c0d5732dae86ba4276999855e162a749c92620a38c1d779ed138a7"},
{file = "importlib_resources-2.0.1.tar.gz", hash = "sha256:f5edfcece1cc9435d0979c19e08739521f4cf1aa1adaf6e571f732df6f568962"}, {file = "importlib_resources-3.0.0.tar.gz", hash = "sha256:19f745a6eca188b490b1428c8d1d4a0d2368759f32370ea8fb89cad2ab1106c3"},
] ]
ipaddress = [ ipaddress = [
{file = "ipaddress-1.0.23-py2.py3-none-any.whl", hash = "sha256:6e0f4a39e66cb5bb9a137b00276a2eff74f93b71dcbdad6f10ff7df9d3557fcc"}, {file = "ipaddress-1.0.23-py2.py3-none-any.whl", hash = "sha256:6e0f4a39e66cb5bb9a137b00276a2eff74f93b71dcbdad6f10ff7df9d3557fcc"},
...@@ -1698,12 +1373,6 @@ jinja2 = [ ...@@ -1698,12 +1373,6 @@ jinja2 = [
{file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
{file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
] ]
joblib = [
{file = "joblib-0.14.1-py2.py3-none-any.whl", hash = "sha256:bdb4fd9b72915ffb49fde2229ce482dd7ae79d842ed8c2b4c932441495af1403"},
{file = "joblib-0.14.1.tar.gz", hash = "sha256:0630eea4f5664c463f23fbf5dcfc54a2bc6168902719fa8e19daf033022786c8"},
{file = "joblib-0.15.1-py3-none-any.whl", hash = "sha256:6825784ffda353cc8a1be573118085789e5b5d29401856b35b756645ab5aecb5"},
{file = "joblib-0.15.1.tar.gz", hash = "sha256:61e49189c84b3c5d99a969d314853f4d1d263316cc694bec17548ebaa9c47b6e"},
]
keyring = [ keyring = [
{file = "keyring-18.0.1-py2.py3-none-any.whl", hash = "sha256:7b29ebfcf8678c4da531b2478a912eea01e80007e5ddca9ee0c7038cb3489ec6"}, {file = "keyring-18.0.1-py2.py3-none-any.whl", hash = "sha256:7b29ebfcf8678c4da531b2478a912eea01e80007e5ddca9ee0c7038cb3489ec6"},
{file = "keyring-18.0.1.tar.gz", hash = "sha256:67d6cc0132bd77922725fae9f18366bb314fd8f95ff4d323a4df41890a96a838"}, {file = "keyring-18.0.1.tar.gz", hash = "sha256:67d6cc0132bd77922725fae9f18366bb314fd8f95ff4d323a4df41890a96a838"},
...@@ -1719,15 +1388,9 @@ lockfile = [ ...@@ -1719,15 +1388,9 @@ lockfile = [
{file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"}, {file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"},
{file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"}, {file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"},
] ]
lunr = [
{file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"},
{file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"},
]
markdown = [ markdown = [
{file = "Markdown-3.1.1-py2.py3-none-any.whl", hash = "sha256:56a46ac655704b91e5b7e6326ce43d5ef72411376588afa1dd90e881b83c7e8c"}, {file = "Markdown-3.1.1-py2.py3-none-any.whl", hash = "sha256:56a46ac655704b91e5b7e6326ce43d5ef72411376588afa1dd90e881b83c7e8c"},
{file = "Markdown-3.1.1.tar.gz", hash = "sha256:2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a"}, {file = "Markdown-3.1.1.tar.gz", hash = "sha256:2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a"},
{file = "Markdown-3.2.2-py3-none-any.whl", hash = "sha256:c467cd6233885534bf0fe96e62e3cf46cfc1605112356c4f9981512b8174de59"},
{file = "Markdown-3.2.2.tar.gz", hash = "sha256:1fafe3f1ecabfb514a5285fca634a53c1b32a81cb0feb154264d55bf2ff22c17"},
] ]
markdown-include = [ markdown-include = [
{file = "markdown-include-0.5.1.tar.gz", hash = "sha256:72a45461b589489a088753893bc95c5fa5909936186485f4ed55caa57d10250f"}, {file = "markdown-include-0.5.1.tar.gz", hash = "sha256:72a45461b589489a088753893bc95c5fa5909936186485f4ed55caa57d10250f"},
...@@ -1760,18 +1423,11 @@ markupsafe = [ ...@@ -1760,18 +1423,11 @@ markupsafe = [
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"},
{file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"},
{file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"},
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"},
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"},
{file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"},
{file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"},
{file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
] ]
mkdocs = [ mkdocs = [
{file = "mkdocs-1.0.4-py2.py3-none-any.whl", hash = "sha256:8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"}, {file = "mkdocs-1.0.4-py2.py3-none-any.whl", hash = "sha256:8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"},
{file = "mkdocs-1.0.4.tar.gz", hash = "sha256:17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939"}, {file = "mkdocs-1.0.4.tar.gz", hash = "sha256:17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939"},
{file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"},
{file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"},
] ]
mock = [ mock = [
{file = "mock-3.0.5-py2.py3-none-any.whl", hash = "sha256:d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"}, {file = "mock-3.0.5-py2.py3-none-any.whl", hash = "sha256:d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"},
...@@ -1804,15 +1460,9 @@ msgpack = [ ...@@ -1804,15 +1460,9 @@ msgpack = [
{file = "msgpack-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:39c54fdebf5fa4dda733369012c59e7d085ebdfe35b6cf648f09d16708f1be5d"}, {file = "msgpack-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:39c54fdebf5fa4dda733369012c59e7d085ebdfe35b6cf648f09d16708f1be5d"},
{file = "msgpack-1.0.0.tar.gz", hash = "sha256:9534d5cc480d4aff720233411a1f765be90885750b07df772380b34c10ecb5c0"}, {file = "msgpack-1.0.0.tar.gz", hash = "sha256:9534d5cc480d4aff720233411a1f765be90885750b07df772380b34c10ecb5c0"},
] ]
nltk = [
{file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"},
]
nodeenv = [ nodeenv = [
{file = "nodeenv-1.4.0-py2.py3-none-any.whl", hash = "sha256:4b0b77afa3ba9b54f4b6396e60b0c83f59eaeb2d63dc3cc7a70f7f4af96c82bc"}, {file = "nodeenv-1.4.0-py2.py3-none-any.whl", hash = "sha256:4b0b77afa3ba9b54f4b6396e60b0c83f59eaeb2d63dc3cc7a70f7f4af96c82bc"},
] ]
nut = [
{file = "nut-0.2.0.tar.gz", hash = "sha256:427bd08d2b47d0c1c8f0a76783586df8b31c3427a0800d7930f010c06ff81e85"},
]
packaging = [ packaging = [
{file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"},
{file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"},
...@@ -1850,8 +1500,8 @@ poetry-core = [ ...@@ -1850,8 +1500,8 @@ poetry-core = [
{file = "poetry_core-1.0.0a8-py2.py3-none-any.whl", hash = "sha256:d7d9e702fbae06c05abb1ada63678e940d746b2696f4601b951b27ac7c2c5337"}, {file = "poetry_core-1.0.0a8-py2.py3-none-any.whl", hash = "sha256:d7d9e702fbae06c05abb1ada63678e940d746b2696f4601b951b27ac7c2c5337"},
] ]
pre-commit = [ pre-commit = [
{file = "pre_commit-2.5.1-py2.py3-none-any.whl", hash = "sha256:c5c8fd4d0e1c363723aaf0a8f9cba0f434c160b48c4028f4bae6d219177945b3"}, {file = "pre_commit-2.6.0-py2.py3-none-any.whl", hash = "sha256:e8b1315c585052e729ab7e99dcca5698266bedce9067d21dc909c23e3ceed626"},
{file = "pre_commit-2.5.1.tar.gz", hash = "sha256:da463cf8f0e257f9af49047ba514f6b90dbd9b4f92f4c8847a3ccd36834874c7"}, {file = "pre_commit-2.6.0.tar.gz", hash = "sha256:1657663fdd63a321a4a739915d7d03baedd555b25054449090f97bb0cb30a915"},
] ]
ptyprocess = [ ptyprocess = [
{file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"},
...@@ -1868,8 +1518,6 @@ pycparser = [ ...@@ -1868,8 +1518,6 @@ pycparser = [
pygments = [ pygments = [
{file = "Pygments-2.5.2-py2.py3-none-any.whl", hash = "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b"}, {file = "Pygments-2.5.2-py2.py3-none-any.whl", hash = "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b"},
{file = "Pygments-2.5.2.tar.gz", hash = "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"}, {file = "Pygments-2.5.2.tar.gz", hash = "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"},
{file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"},
{file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"},
] ]
pygments-github-lexers = [ pygments-github-lexers = [
{file = "pygments-github-lexers-0.0.5.tar.gz", hash = "sha256:aaca57e77cd6fcfce8d6ee97a998962eebf7fbb810519a8ebde427c62823e133"}, {file = "pygments-github-lexers-0.0.5.tar.gz", hash = "sha256:aaca57e77cd6fcfce8d6ee97a998962eebf7fbb810519a8ebde427c62823e133"},
...@@ -1882,8 +1530,6 @@ pylev = [ ...@@ -1882,8 +1530,6 @@ pylev = [
pymdown-extensions = [ pymdown-extensions = [
{file = "pymdown-extensions-6.2.1.tar.gz", hash = "sha256:3bbe6048275f8a0d13a0fe44e0ea201e67268aa7bb40c2544eef16abbf168f7b"}, {file = "pymdown-extensions-6.2.1.tar.gz", hash = "sha256:3bbe6048275f8a0d13a0fe44e0ea201e67268aa7bb40c2544eef16abbf168f7b"},
{file = "pymdown_extensions-6.2.1-py2.py3-none-any.whl", hash = "sha256:dce5e17b93be0572322b7d06c9a13c13a9d98694d6468277911d50ca87d26f29"}, {file = "pymdown_extensions-6.2.1-py2.py3-none-any.whl", hash = "sha256:dce5e17b93be0572322b7d06c9a13c13a9d98694d6468277911d50ca87d26f29"},
{file = "pymdown-extensions-6.3.tar.gz", hash = "sha256:cb879686a586b22292899771f5e5bc3382808e92aa938f71b550ecdea709419f"},
{file = "pymdown_extensions-6.3-py2.py3-none-any.whl", hash = "sha256:66fae2683c7a1dac53184f7de57f51f8dad73f9ead2f453e94e85096cb811335"},
] ]
pyparsing = [ pyparsing = [
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
...@@ -1986,7 +1632,6 @@ six = [ ...@@ -1986,7 +1632,6 @@ six = [
] ]
subprocess32 = [ subprocess32 = [
{file = "subprocess32-3.5.4-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:88e37c1aac5388df41cc8a8456bb49ebffd321a3ad4d70358e3518176de3a56b"}, {file = "subprocess32-3.5.4-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:88e37c1aac5388df41cc8a8456bb49ebffd321a3ad4d70358e3518176de3a56b"},
{file = "subprocess32-3.5.4-cp27-cp27mu-manylinux2014_x86_64.whl", hash = "sha256:e45d985aef903c5b7444d34350b05da91a9e0ea015415ab45a21212786c649d0"},
{file = "subprocess32-3.5.4.tar.gz", hash = "sha256:eb2937c80497978d181efa1b839ec2d9622cf9600a039a79d0e108d1f9aec79d"}, {file = "subprocess32-3.5.4.tar.gz", hash = "sha256:eb2937c80497978d181efa1b839ec2d9622cf9600a039a79d0e108d1f9aec79d"},
] ]
termcolor = [ termcolor = [
...@@ -2010,12 +1655,8 @@ tornado = [ ...@@ -2010,12 +1655,8 @@ tornado = [
{file = "tornado-5.1.1.tar.gz", hash = "sha256:4e5158d97583502a7e2739951553cbd88a72076f152b4b11b64b9a10c4c49409"}, {file = "tornado-5.1.1.tar.gz", hash = "sha256:4e5158d97583502a7e2739951553cbd88a72076f152b4b11b64b9a10c4c49409"},
] ]
tox = [ tox = [
{file = "tox-3.15.2-py2.py3-none-any.whl", hash = "sha256:50a188b8e17580c1fb931f494a754e6507d4185f54fb18aca5ba3e12d2ffd55e"}, {file = "tox-3.16.1-py2.py3-none-any.whl", hash = "sha256:60c3793f8ab194097ec75b5a9866138444f63742b0f664ec80be1222a40687c5"},
{file = "tox-3.15.2.tar.gz", hash = "sha256:c696d36cd7c6a28ada2da780400e44851b20ee19ef08cfe73344a1dcebbbe9f3"}, {file = "tox-3.16.1.tar.gz", hash = "sha256:9a746cda9cadb9e1e05c7ab99f98cfcea355140d2ecac5f97520be94657c3bc7"},
]
tqdm = [
{file = "tqdm-4.46.1-py2.py3-none-any.whl", hash = "sha256:07c06493f1403c1380b630ae3dcbe5ae62abcf369a93bbc052502279f189ab8c"},
{file = "tqdm-4.46.1.tar.gz", hash = "sha256:cd140979c2bebd2311dfb14781d8f19bd5a9debb92dcab9f6ef899c987fcf71f"},
] ]
typed-ast = [ typed-ast = [
{file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"},
...@@ -2069,6 +1710,4 @@ webencodings = [ ...@@ -2069,6 +1710,4 @@ webencodings = [
zipp = [ zipp = [
{file = "zipp-1.2.0-py2.py3-none-any.whl", hash = "sha256:e0d9e63797e483a30d27e09fffd308c59a700d365ec34e93cc100844168bf921"}, {file = "zipp-1.2.0-py2.py3-none-any.whl", hash = "sha256:e0d9e63797e483a30d27e09fffd308c59a700d365ec34e93cc100844168bf921"},
{file = "zipp-1.2.0.tar.gz", hash = "sha256:c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1"}, {file = "zipp-1.2.0.tar.gz", hash = "sha256:c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1"},
{file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"},
{file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"},
] ]
...@@ -37,6 +37,7 @@ class Config(object): ...@@ -37,6 +37,7 @@ class Config(object):
"in-project": False, "in-project": False,
"path": os.path.join("{cache-dir}", "virtualenvs"), "path": os.path.join("{cache-dir}", "virtualenvs"),
}, },
"experimental": {"new-installer": True},
} }
def __init__( def __init__(
......
from cleo import argument from cleo import argument
from cleo import option from cleo import option
from .env_command import EnvCommand
from .init import InitCommand from .init import InitCommand
from .installer_command import InstallerCommand
class AddCommand(EnvCommand, InitCommand): class AddCommand(InstallerCommand, InitCommand):
name = "add" name = "add"
description = "Adds a new dependency to <comment>pyproject.toml</>." description = "Adds a new dependency to <comment>pyproject.toml</>."
...@@ -56,7 +56,6 @@ If you do not specify a version constraint, poetry will choose a suitable one ba ...@@ -56,7 +56,6 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
loggers = ["poetry.repositories.pypi_repository"] loggers = ["poetry.repositories.pypi_repository"]
def handle(self): def handle(self):
from poetry.installation.installer import Installer
from poetry.core.semver import parse_constraint from poetry.core.semver import parse_constraint
from tomlkit import inline_table from tomlkit import inline_table
...@@ -149,18 +148,17 @@ If you do not specify a version constraint, poetry will choose a suitable one ba ...@@ -149,18 +148,17 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
# Update packages # Update packages
self.reset_poetry() self.reset_poetry()
installer = Installer( self._installer.set_package(self.poetry.package)
self.io, self.env, self.poetry.package, self.poetry.locker, self.poetry.pool self._installer.dry_run(self.option("dry-run"))
) self._installer.verbose(self._io.is_verbose())
self._installer.update(True)
installer.dry_run(self.option("dry-run"))
installer.update(True)
if self.option("lock"): if self.option("lock"):
installer.lock() self._installer.lock()
installer.whitelist([r["name"] for r in requirements])
self._installer.whitelist([r["name"] for r in requirements])
try: try:
status = installer.run() status = self._installer.run()
except Exception: except Exception:
self.poetry.file.write(original_content) self.poetry.file.write(original_content)
...@@ -169,10 +167,10 @@ If you do not specify a version constraint, poetry will choose a suitable one ba ...@@ -169,10 +167,10 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
if status != 0 or self.option("dry-run"): if status != 0 or self.option("dry-run"):
# Revert changes # Revert changes
if not self.option("dry-run"): if not self.option("dry-run"):
self.error( self.line_error(
"\n" "\n"
"Addition failed, reverting pyproject.toml " "<error>Failed to add packages, reverting the pyproject.toml file "
"to its original content." "to its original content.</error>"
) )
self.poetry.file.write(original_content) self.poetry.file.write(original_content)
......
...@@ -57,6 +57,11 @@ To remove a repository (repo is a short alias for repositories): ...@@ -57,6 +57,11 @@ To remove a repository (repo is a short alias for repositories):
lambda val: str(Path(val)), lambda val: str(Path(val)),
str(Path(CACHE_DIR) / "virtualenvs"), str(Path(CACHE_DIR) / "virtualenvs"),
), ),
"experimental.new-installer": (
boolean_validator,
boolean_normalizer,
True,
),
} }
return unique_config_values return unique_config_values
......
from cleo import option from cleo import option
from .env_command import EnvCommand from .installer_command import InstallerCommand
class InstallCommand(EnvCommand): class InstallCommand(InstallerCommand):
name = "install" name = "install"
description = "Installs the project dependencies." description = "Installs the project dependencies."
...@@ -48,12 +48,11 @@ dependencies and not including the current project, run the command with the ...@@ -48,12 +48,11 @@ dependencies and not including the current project, run the command with the
_loggers = ["poetry.repositories.pypi_repository"] _loggers = ["poetry.repositories.pypi_repository"]
def handle(self): def handle(self):
from poetry.installation.installer import Installer
from poetry.masonry.builders import EditableBuilder from poetry.masonry.builders import EditableBuilder
from poetry.core.masonry.utils.module import ModuleOrPackageNotFound from poetry.core.masonry.utils.module import ModuleOrPackageNotFound
installer = Installer( self._installer.use_executor(
self.io, self.env, self.poetry.package, self.poetry.locker, self.poetry.pool self.poetry.config.get("experimental.new-installer", False)
) )
extras = [] extras = []
...@@ -63,13 +62,13 @@ dependencies and not including the current project, run the command with the ...@@ -63,13 +62,13 @@ dependencies and not including the current project, run the command with the
else: else:
extras.append(extra) extras.append(extra)
installer.extras(extras) self._installer.extras(extras)
installer.dev_mode(not self.option("no-dev")) self._installer.dev_mode(not self.option("no-dev"))
installer.dry_run(self.option("dry-run")) self._installer.dry_run(self.option("dry-run"))
installer.remove_untracked(self.option("remove-untracked")) self._installer.remove_untracked(self.option("remove-untracked"))
installer.verbose(self.option("verbose")) self._installer.verbose(self._io.is_verbose())
return_code = installer.run() return_code = self._installer.run()
if return_code != 0: if return_code != 0:
return return_code return return_code
...@@ -85,15 +84,32 @@ dependencies and not including the current project, run the command with the ...@@ -85,15 +84,32 @@ dependencies and not including the current project, run the command with the
# If this is a true error it will be picked up later by build anyway. # If this is a true error it will be picked up later by build anyway.
return 0 return 0
self.line("")
if not self._io.supports_ansi() or self.io.is_debug():
self.line( self.line(
" - Installing <c1>{}</c1> (<c2>{}</c2>)".format( "<b>Installing</> the current project: <c1>{}</c1> (<c2>{}</c2>)".format(
self.poetry.package.pretty_name, self.poetry.package.pretty_version
)
)
else:
self.write(
"<b>Installing</> the current project: <c1>{}</c1> (<c2>{}</c2>)".format(
self.poetry.package.pretty_name, self.poetry.package.pretty_version self.poetry.package.pretty_name, self.poetry.package.pretty_version
) )
) )
if self.option("dry-run"): if self.option("dry-run"):
self.line("")
return 0 return 0
builder.build() builder.build()
if self._io.supports_ansi() and not self.io.is_debug():
self.overwrite(
"<b>Installing</> the current project: <c1>{}</c1> (<success>{}</success>)".format(
self.poetry.package.pretty_name, self.poetry.package.pretty_version
)
)
self.line("")
return 0 return 0
from typing import TYPE_CHECKING
from .env_command import EnvCommand
if TYPE_CHECKING:
from poetry.installation.installer import Installer
class InstallerCommand(EnvCommand):
def __init__(self):
self._installer = None
super(InstallerCommand, self).__init__()
@property
def installer(self): # type: () -> Installer
return self._installer
def set_installer(self, installer): # type: (Installer) -> None
self._installer = installer
from .env_command import EnvCommand from .installer_command import InstallerCommand
class LockCommand(EnvCommand): class LockCommand(InstallerCommand):
name = "lock" name = "lock"
description = "Locks the project dependencies." description = "Locks the project dependencies."
...@@ -17,12 +17,10 @@ file. ...@@ -17,12 +17,10 @@ file.
loggers = ["poetry.repositories.pypi_repository"] loggers = ["poetry.repositories.pypi_repository"]
def handle(self): def handle(self):
from poetry.installation.installer import Installer self._installer.use_executor(
self.poetry.config.get("experimental.new-installer", False)
installer = Installer(
self.io, self.env, self.poetry.package, self.poetry.locker, self.poetry.pool
) )
installer.lock() self._installer.lock()
return installer.run() return self._installer.run()
from cleo import argument from cleo import argument
from cleo import option from cleo import option
from .env_command import EnvCommand from .installer_command import InstallerCommand
class RemoveCommand(EnvCommand): class RemoveCommand(InstallerCommand):
name = "remove" name = "remove"
description = "Removes a package from the project dependencies." description = "Removes a package from the project dependencies."
...@@ -28,8 +28,6 @@ list of installed packages ...@@ -28,8 +28,6 @@ list of installed packages
loggers = ["poetry.repositories.pypi_repository"] loggers = ["poetry.repositories.pypi_repository"]
def handle(self): def handle(self):
from poetry.installation.installer import Installer
packages = self.argument("packages") packages = self.argument("packages")
is_dev = self.option("dev") is_dev = self.option("dev")
...@@ -62,16 +60,18 @@ list of installed packages ...@@ -62,16 +60,18 @@ list of installed packages
# Update packages # Update packages
self.reset_poetry() self.reset_poetry()
installer = Installer( self._installer.set_package(self.poetry.package)
self.io, self.env, self.poetry.package, self.poetry.locker, self.poetry.pool self._installer.use_executor(
self.poetry.config.get("experimental.new-installer", False)
) )
installer.dry_run(self.option("dry-run")) self._installer.dry_run(self.option("dry-run"))
installer.update(True) self._installer.verbose(self._io.is_verbose())
installer.whitelist(requirements) self._installer.update(True)
self._installer.whitelist(requirements)
try: try:
status = installer.run() status = self._installer.run()
except Exception: except Exception:
self.poetry.file.write(original_content) self.poetry.file.write(original_content)
...@@ -80,7 +80,7 @@ list of installed packages ...@@ -80,7 +80,7 @@ list of installed packages
if status != 0 or self.option("dry-run"): if status != 0 or self.option("dry-run"):
# Revert changes # Revert changes
if not self.option("dry-run"): if not self.option("dry-run"):
self.error( self.line_error(
"\n" "\n"
"Removal failed, reverting pyproject.toml " "Removal failed, reverting pyproject.toml "
"to its original content." "to its original content."
......
from cleo import argument from cleo import argument
from cleo import option from cleo import option
from .env_command import EnvCommand from .installer_command import InstallerCommand
class UpdateCommand(EnvCommand): class UpdateCommand(InstallerCommand):
name = "update" name = "update"
description = ( description = (
...@@ -28,22 +28,20 @@ class UpdateCommand(EnvCommand): ...@@ -28,22 +28,20 @@ class UpdateCommand(EnvCommand):
loggers = ["poetry.repositories.pypi_repository"] loggers = ["poetry.repositories.pypi_repository"]
def handle(self): def handle(self):
from poetry.installation.installer import Installer
packages = self.argument("packages") packages = self.argument("packages")
installer = Installer( self._installer.use_executor(
self.io, self.env, self.poetry.package, self.poetry.locker, self.poetry.pool self.poetry.config.get("experimental.new-installer", False)
) )
if packages: if packages:
installer.whitelist({name: "*" for name in packages}) self._installer.whitelist({name: "*" for name in packages})
installer.dev_mode(not self.option("no-dev")) self._installer.dev_mode(not self.option("no-dev"))
installer.dry_run(self.option("dry-run")) self._installer.dry_run(self.option("dry-run"))
installer.execute_operations(not self.option("lock")) self._installer.execute_operations(not self.option("lock"))
# Force update # Force update
installer.update(True) self._installer.update(True)
return installer.run() return self._installer.run()
...@@ -27,6 +27,7 @@ from clikit.io.output_stream import StandardOutputStream ...@@ -27,6 +27,7 @@ from clikit.io.output_stream import StandardOutputStream
from poetry.console.commands.command import Command from poetry.console.commands.command import Command
from poetry.console.commands.env_command import EnvCommand from poetry.console.commands.env_command import EnvCommand
from poetry.console.commands.installer_command import InstallerCommand
from poetry.console.logging.io_formatter import IOFormatter from poetry.console.logging.io_formatter import IOFormatter
from poetry.console.logging.io_handler import IOHandler from poetry.console.logging.io_handler import IOHandler
from poetry.utils._compat import PY36 from poetry.utils._compat import PY36
...@@ -37,15 +38,22 @@ class ApplicationConfig(BaseApplicationConfig): ...@@ -37,15 +38,22 @@ class ApplicationConfig(BaseApplicationConfig):
super(ApplicationConfig, self).configure() super(ApplicationConfig, self).configure()
self.add_style(Style("c1").fg("cyan")) self.add_style(Style("c1").fg("cyan"))
self.add_style(Style("c2").fg("green")) self.add_style(Style("c2").fg("default").bold())
self.add_style(Style("info").fg("blue")) self.add_style(Style("info").fg("blue"))
self.add_style(Style("comment").fg("green")) self.add_style(Style("comment").fg("green"))
self.add_style(Style("error").fg("red").bold()) self.add_style(Style("error").fg("red").bold())
self.add_style(Style("warning").fg("yellow").bold()) self.add_style(Style("warning").fg("yellow").bold())
self.add_style(Style("debug").fg("default").dark()) self.add_style(Style("debug").fg("default").dark())
self.add_style(Style("success").fg("green"))
# Dark variants
self.add_style(Style("c1_dark").fg("cyan").dark())
self.add_style(Style("c2_dark").fg("default").bold().dark())
self.add_style(Style("success_dark").fg("green").dark())
self.add_event_listener(PRE_HANDLE, self.register_command_loggers) self.add_event_listener(PRE_HANDLE, self.register_command_loggers)
self.add_event_listener(PRE_HANDLE, self.set_env) self.add_event_listener(PRE_HANDLE, self.set_env)
self.add_event_listener(PRE_HANDLE, self.set_installer)
if PY36: if PY36:
from poetry.mixology.solutions.providers import ( from poetry.mixology.solutions.providers import (
...@@ -93,6 +101,9 @@ class ApplicationConfig(BaseApplicationConfig): ...@@ -93,6 +101,9 @@ class ApplicationConfig(BaseApplicationConfig):
if not isinstance(command, EnvCommand): if not isinstance(command, EnvCommand):
return return
if command.env is not None:
return
io = event.io io = event.io
poetry = command.poetry poetry = command.poetry
...@@ -104,6 +115,32 @@ class ApplicationConfig(BaseApplicationConfig): ...@@ -104,6 +115,32 @@ class ApplicationConfig(BaseApplicationConfig):
command.set_env(env) command.set_env(env)
def set_installer(
self, event, event_name, _
): # type: (PreHandleEvent, str, Any) -> None
command = event.command.config.handler # type: InstallerCommand
if not isinstance(command, InstallerCommand):
return
# If the command already has an installer
# we skip this step
if command.installer is not None:
return
from poetry.installation.installer import Installer
poetry = command.poetry
installer = Installer(
event.io,
command.env,
poetry.package,
poetry.locker,
poetry.pool,
poetry.config,
)
installer.use_executor(poetry.config.get("experimental.new-installer", False))
command.set_installer(installer)
def resolve_help_command( def resolve_help_command(
self, event, event_name, dispatcher self, event, event_name, dispatcher
): # type: (PreResolveEvent, str, EventDispatcher) -> None ): # type: (PreResolveEvent, str, EventDispatcher) -> None
......
from typing import TYPE_CHECKING
from poetry.utils._compat import urlparse
from poetry.utils.password_manager import PasswordManager
if TYPE_CHECKING:
from typing import Any
from typing import Optional
from typing import Tuple
from clikit.api.io import IO
from requests import Request # noqa
from requests import Response # noqa
from requests import Session # noqa
from poetry.config.config import Config
class Authenticator(object):
def __init__(self, config, io): # type: (Config, IO) -> None
self._config = config
self._io = io
self._session = None
self._credentials = {}
self._password_manager = PasswordManager(self._config)
@property
def session(self): # type: () -> Session
from requests import Session # noqa
if self._session is None:
self._session = Session()
return self._session
def request(self, method, url, **kwargs): # type: (str, str, Any) -> Response
from requests import Request # noqa
from requests.auth import HTTPBasicAuth
request = Request(method, url)
username, password = self._get_credentials_for_url(url)
if username is not None and password is not None:
request = HTTPBasicAuth(username, password)(request)
session = self.session
prepared_request = session.prepare_request(request)
proxies = kwargs.get("proxies", {})
stream = kwargs.get("stream")
verify = kwargs.get("verify")
cert = kwargs.get("cert")
settings = session.merge_environment_settings(
prepared_request.url, proxies, stream, verify, cert
)
# Send the request.
send_kwargs = {
"timeout": kwargs.get("timeout"),
"allow_redirects": kwargs.get("allow_redirects", True),
}
send_kwargs.update(settings)
resp = session.send(prepared_request, **send_kwargs)
resp.raise_for_status()
return resp
def _get_credentials_for_url(
self, url
): # type: (str) -> Tuple[Optional[str], Optional[str]]
parsed_url = urlparse.urlsplit(url)
netloc = parsed_url.netloc
credentials = self._credentials.get(netloc, (None, None))
if credentials == (None, None):
if "@" not in netloc:
credentials = self._get_credentials_for_netloc_from_config(netloc)
else:
# Split from the right because that's how urllib.parse.urlsplit()
# behaves if more than one @ is present (which can be checked using
# the password attribute of urlsplit()'s return value).
auth, netloc = netloc.rsplit("@", 1)
if ":" in auth:
# Split from the left because that's how urllib.parse.urlsplit()
# behaves if more than one : is present (which again can be checked
# using the password attribute of the return value)
credentials = auth.split(":", 1)
else:
credentials = auth, None
credentials = tuple(
None if x is None else urlparse.unquote(x) for x in credentials
)
if credentials[0] is not None or credentials[1] is not None:
credentials = (credentials[0] or "", credentials[1] or "")
self._credentials[netloc] = credentials
return credentials[0], credentials[1]
def _get_credentials_for_netloc_from_config(
self, netloc
): # type: (str) -> Tuple[Optional[str], Optional[str]]
credentials = (None, None)
for repository_name in self._config.get("http-basic", {}):
repository_config = self._config.get(
"repositories.{}".format(repository_name)
)
if not repository_config:
continue
url = repository_config.get("url")
if not url:
continue
parsed_url = urlparse.urlsplit(url)
if netloc == parsed_url.netloc:
auth = self._password_manager.get_http_auth(repository_name)
if auth is None:
continue
return auth["username"], auth["password"]
return credentials
import hashlib
import json
from typing import TYPE_CHECKING
from poetry.core.packages.utils.link import Link
from poetry.utils._compat import Path
from .chooser import InvalidWheelName
from .chooser import Wheel
if TYPE_CHECKING:
from typing import List
from typing import Optional
from poetry.config.config import Config
from poetry.utils.env import Env
class Chef:
def __init__(self, config, env): # type: (Config, Env) -> None
self._config = config
self._env = env
self._cache_dir = (
Path(config.get("cache-dir")).expanduser().joinpath("artifacts")
)
def prepare(self, archive): # type: (Path) -> Path
return archive
def prepare_sdist(self, archive): # type: (Path) -> Path
return archive
def prepare_wheel(self, archive): # type: (Path) -> Path
return archive
def should_prepare(self, archive): # type: (Path) -> bool
return not self.is_wheel(archive)
def is_wheel(self, archive): # type: (Path) -> bool
return archive.suffix == ".whl"
def get_cached_archive_for_link(self, link): # type: (Link) -> Optional[Link]
# If the archive is already a wheel, there is no need to cache it.
if link.is_wheel:
pass
archives = self.get_cached_archives_for_link(link)
if not archives:
return link
candidates = []
for archive in archives:
if not archive.is_wheel:
candidates.append((float("inf"), archive))
continue
try:
wheel = Wheel(archive.filename)
except InvalidWheelName:
continue
if not wheel.is_supported_by_environment(self._env):
continue
candidates.append(
(wheel.get_minimum_supported_index(self._env.supported_tags), archive),
)
if not candidates:
return link
return min(candidates)[1]
def get_cached_archives_for_link(self, link): # type: (Link) -> List[Link]
cache_dir = self.get_cache_directory_for_link(link)
archive_types = ["whl", "tar.gz", "tar.bz2", "bz2", "zip"]
links = []
for archive_type in archive_types:
for archive in cache_dir.glob("*.{}".format(archive_type)):
links.append(Link("file://{}".format(str(archive))))
return links
def get_cache_directory_for_link(self, link): # type: (Link) -> Path
key_parts = {"url": link.url_without_fragment}
if link.hash_name is not None and link.hash is not None:
key_parts[link.hash_name] = link.hash
if link.subdirectory_fragment:
key_parts["subdirectory"] = link.subdirectory_fragment
key_parts["interpreter_name"] = self._env.marker_env["interpreter_name"]
key_parts["interpreter_version"] = "".join(
self._env.marker_env["interpreter_version"].split(".")[:2]
)
key = hashlib.sha256(
json.dumps(
key_parts, sort_keys=True, separators=(",", ":"), ensure_ascii=True
).encode("ascii")
).hexdigest()
split_key = [key[:2], key[2:4], key[4:6], key[6:]]
return self._cache_dir.joinpath(*split_key)
import re
from typing import List
from typing import Tuple
from packaging.tags import Tag
from poetry.core.packages.package import Package
from poetry.core.packages.utils.link import Link
from poetry.repositories.pool import Pool
from poetry.utils.env import Env
from poetry.utils.patterns import wheel_file_re
class InvalidWheelName(Exception):
pass
class Wheel(object):
def __init__(self, filename): # type: (str) -> None
wheel_info = wheel_file_re.match(filename)
if not wheel_info:
raise InvalidWheelName("{} is not a valid wheel filename.".format(filename))
self.filename = filename
self.name = wheel_info.group("name").replace("_", "-")
self.version = wheel_info.group("ver").replace("_", "-")
self.build_tag = wheel_info.group("build")
self.pyversions = wheel_info.group("pyver").split(".")
self.abis = wheel_info.group("abi").split(".")
self.plats = wheel_info.group("plat").split(".")
self.tags = {
Tag(x, y, z) for x in self.pyversions for y in self.abis for z in self.plats
}
def get_minimum_supported_index(self, tags):
indexes = [tags.index(t) for t in self.tags if t in tags]
return min(indexes) if indexes else None
def is_supported_by_environment(self, env):
return bool(set(env.supported_tags).intersection(self.tags))
class Chooser:
"""
A Chooser chooses an appropriate release archive for packages.
"""
def __init__(self, pool, env): # type: (Pool, Env) -> None
self._pool = pool
self._env = env
def choose_for(self, package): # type: (Package) -> Link
"""
Return the url of the selected archive for a given package.
"""
links = []
for link in self._get_links(package):
if link.is_wheel and not Wheel(link.filename).is_supported_by_environment(
self._env
):
continue
if link.ext == ".egg":
continue
links.append(link)
if not links:
raise RuntimeError(
"Unable to find installation candidates for {}".format(package)
)
# Get the best link
chosen = max(links, key=lambda link: self._sort_key(package, link))
if not chosen:
raise RuntimeError(
"Unable to find installation candidates for {}".format(package)
)
return chosen
def _get_links(self, package): # type: (Package) -> List[Link]
if not package.source_type:
if not self._pool.has_repository("pypi"):
repository = self._pool.repositories[0]
else:
repository = self._pool.repository("pypi")
else:
repository = self._pool.repository(package.source_reference)
links = repository.find_links_for_package(package)
hashes = [f["hash"] for f in package.files]
if not hashes:
return links
selected_links = []
for link in links:
if not link.hash:
selected_links.append(link)
continue
h = link.hash_name + ":" + link.hash
if h not in hashes:
continue
selected_links.append(link)
return selected_links
def _sort_key(self, package, link): # type: (Package, Link) -> Tuple
"""
Function to pass as the `key` argument to a call to sorted() to sort
InstallationCandidates by preference.
Returns a tuple such that tuples sorting as greater using Python's
default comparison operator are more preferred.
The preference is as follows:
First and foremost, candidates with allowed (matching) hashes are
always preferred over candidates without matching hashes. This is
because e.g. if the only candidate with an allowed hash is yanked,
we still want to use that candidate.
Second, excepting hash considerations, candidates that have been
yanked (in the sense of PEP 592) are always less preferred than
candidates that haven't been yanked. Then:
If not finding wheels, they are sorted by version only.
If finding wheels, then the sort order is by version, then:
1. existing installs
2. wheels ordered via Wheel.support_index_min(self._supported_tags)
3. source archives
If prefer_binary was set, then all wheels are sorted above sources.
Note: it was considered to embed this logic into the Link
comparison operators, but then different sdist links
with the same version, would have to be considered equal
"""
support_num = len(self._env.supported_tags)
build_tag = ()
binary_preference = 0
if link.is_wheel:
wheel = Wheel(link.filename)
if not wheel.is_supported_by_environment(self._env):
raise RuntimeError(
"{} is not a supported wheel for this platform. It "
"can't be sorted.".format(wheel.filename)
)
# TODO: Binary preference
pri = -(wheel.get_minimum_supported_index(self._env.supported_tags))
if wheel.build_tag is not None:
match = re.match(r"^(\d+)(.*)$", wheel.build_tag)
build_tag_groups = match.groups()
build_tag = (int(build_tag_groups[0]), build_tag_groups[1])
else: # sdist
pri = -support_num
has_allowed_hash = int(self._is_link_hash_allowed_for_package(link, package))
# TODO: Proper yank value
yank_value = 0
return (
has_allowed_hash,
yank_value,
binary_preference,
package.version,
build_tag,
pri,
)
def _is_link_hash_allowed_for_package(
self, link, package
): # type: (Link, Package) -> bool
if not link.hash:
return True
h = link.hash_name + ":" + link.hash
return h in {f["hash"] for f in package.files}
# -*- coding: utf-8 -*-
from __future__ import division
import itertools
import os
import threading
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import wait
from subprocess import CalledProcessError
from poetry.core.packages.file_dependency import FileDependency
from poetry.core.packages.utils.link import Link
from poetry.io.null_io import NullIO
from poetry.utils._compat import OrderedDict
from poetry.utils._compat import Path
from poetry.utils._compat import cpu_count
from poetry.utils._compat import decode
from poetry.utils.env import EnvCommandError
from poetry.utils.helpers import safe_rmtree
from .authenticator import Authenticator
from .chef import Chef
from .chooser import Chooser
from .operations.install import Install
from .operations.operation import Operation
from .operations.uninstall import Uninstall
from .operations.update import Update
class Executor(object):
def __init__(self, env, pool, config, io, parallel=None):
self._env = env
self._io = io
self._dry_run = False
self._enabled = True
self._verbose = False
self._authenticator = Authenticator(config, self._io)
self._chef = Chef(config, self._env)
self._chooser = Chooser(pool, self._env)
if parallel is None:
parallel = self.supports_fancy_output()
if parallel:
# This should be directly handled by ThreadPoolExecutor
# however, on some systems the number of CPUs cannot be determined
# (it raises a NotImplementedError), so, in this case, we assume
# that the system only has one CPU.
try:
self._max_workers = cpu_count() + 4
except NotImplementedError:
self._max_workers = 5
else:
self._max_workers = 1
self._executor = ThreadPoolExecutor(max_workers=self._max_workers)
self._total_operations = 0
self._executed_operations = 0
self._executed = {"install": 0, "update": 0, "uninstall": 0}
self._skipped = {"install": 0, "update": 0, "uninstall": 0}
self._sections = OrderedDict()
self._lock = threading.Lock()
self._shutdown = False
@property
def installations_count(self): # type: () -> int
return self._executed["install"]
@property
def updates_count(self): # type: () -> int
return self._executed["update"]
@property
def removals_count(self): # type: () -> int
return self._executed["uninstall"]
def supports_fancy_output(self): # type: () -> bool
return self._io.supports_ansi() and not self._dry_run
def disable(self):
self._enabled = False
return self
def dry_run(self, dry_run=True):
self._dry_run = dry_run
return self
def verbose(self, verbose=True):
self._verbose = verbose
return self
def execute(self, operations): # type: (Operation) -> int
self._total_operations = len(operations)
for job_type in self._executed:
self._executed[job_type] = 0
self._skipped[job_type] = 0
if operations and (self._enabled or self._dry_run):
self._display_summary(operations)
# We group operations by priority
groups = itertools.groupby(operations, key=lambda o: -o.priority)
self._sections = OrderedDict()
for _, group in groups:
tasks = []
for operation in group:
if self._shutdown:
break
tasks.append(self._executor.submit(self._execute_operation, operation))
try:
wait(tasks)
except KeyboardInterrupt:
self._shutdown = True
if self._shutdown:
self._executor.shutdown(wait=True)
break
return self._shutdown
def _write(self, operation, line):
if not self.supports_fancy_output() or not self._should_write_operation(
operation
):
return
if self._io.is_debug():
self._lock.acquire()
section = self._sections[id(operation)]
section.write_line(line)
self._lock.release()
return
self._lock.acquire()
section = self._sections[id(operation)]
section.output.clear()
section.write(line)
self._lock.release()
def _execute_operation(self, operation):
try:
if self.supports_fancy_output():
if id(operation) not in self._sections:
if self._should_write_operation(operation):
self._lock.acquire()
self._sections[id(operation)] = self._io.section()
self._sections[id(operation)].write_line(
" <fg=blue;options=bold>•</> {message}: <fg=blue>Pending...</>".format(
message=self.get_operation_message(operation),
),
)
self._lock.release()
else:
if self._should_write_operation(operation):
if not operation.skipped:
self._io.write_line(
" <fg=blue;options=bold>•</> {message}".format(
message=self.get_operation_message(operation),
),
)
else:
self._io.write_line(
" <fg=default;options=bold,dark>•</> {message}: "
"<fg=default;options=bold,dark>Skipped</> "
"<fg=default;options=dark>for the following reason:</> "
"<fg=default;options=bold,dark>{reason}</>".format(
message=self.get_operation_message(operation),
reason=operation.skip_reason,
)
)
try:
result = self._do_execute_operation(operation)
except EnvCommandError as e:
if e.e.returncode == -2:
result = -2
else:
raise
# If we have a result of -2 it means a KeyboardInterrupt
# in the any python subprocess, so we raise a KeyboardInterrupt
# error to be picked up by the error handler.
if result == -2:
raise KeyboardInterrupt
except Exception as e:
from clikit.ui.components.exception_trace import ExceptionTrace
if not self.supports_fancy_output():
io = self._io
else:
message = " <error>•</error> {message}: <error>Failed</error>".format(
message=self.get_operation_message(operation, error=True),
)
self._write(operation, message)
io = self._sections.get(id(operation), self._io)
self._lock.acquire()
trace = ExceptionTrace(e)
trace.render(io)
io.write_line("")
self._shutdown = True
self._lock.release()
except KeyboardInterrupt:
message = " <warning>•</warning> {message}: <warning>Cancelled</warning>".format(
message=self.get_operation_message(operation, warning=True),
)
if not self.supports_fancy_output():
self._io.write_line(message)
else:
self._write(operation, message)
self._lock.acquire()
self._shutdown = True
self._lock.release()
def _do_execute_operation(self, operation):
method = operation.job_type
operation_message = self.get_operation_message(operation)
if operation.skipped:
if self.supports_fancy_output():
self._write(
operation,
" <fg=default;options=bold,dark>•</> {message}: "
"<fg=default;options=bold,dark>Skipped</> "
"<fg=default;options=dark>for the following reason:</> "
"<fg=default;options=bold,dark>{reason}</>".format(
message=operation_message, reason=operation.skip_reason,
),
)
self._skipped[operation.job_type] += 1
return 0
if not self._enabled or self._dry_run:
self._io.write_line(
" <fg=blue;options=bold>•</> {message}".format(
message=operation_message,
)
)
return 0
result = getattr(self, "_execute_{}".format(method))(operation)
if result != 0:
return result
message = " <fg=green;options=bold>•</> {message}".format(
message=self.get_operation_message(operation, done=True),
)
self._write(operation, message)
self._increment_operations_count(operation, True)
return result
def _increment_operations_count(self, operation, executed):
self._lock.acquire()
if executed:
self._executed_operations += 1
self._executed[operation.job_type] += 1
else:
self._skipped[operation.job_type] += 1
self._lock.release()
def run_pip(self, *args, **kwargs): # type: (...) -> int
try:
self._env.run("python", "-m", "pip", *args, **kwargs)
except EnvCommandError as e:
output = decode(e.e.output)
if (
"KeyboardInterrupt" in output
or "ERROR: Operation cancelled by user" in output
):
return -2
raise
return 0
def get_operation_message(self, operation, done=False, error=False, warning=False):
base_tag = "fg=default"
operation_color = "c2"
source_operation_color = "c2"
package_color = "c1"
if error:
operation_color = "error"
elif warning:
operation_color = "warning"
elif done:
operation_color = "success"
if operation.skipped:
base_tag = "fg=default;options=dark"
operation_color += "_dark"
source_operation_color += "_dark"
package_color += "_dark"
if operation.job_type == "install":
return "<{}>Installing <{}>{}</{}> (<{}>{}</>)</>".format(
base_tag,
package_color,
operation.package.name,
package_color,
operation_color,
operation.package.full_pretty_version,
)
if operation.job_type == "uninstall":
return "<{}>Removing <{}>{}</{}> (<{}>{}</>)</>".format(
base_tag,
package_color,
operation.package.name,
package_color,
operation_color,
operation.package.full_pretty_version,
)
if operation.job_type == "update":
return "<{}>Updating <{}>{}</{}> (<{}>{}</{}> -> <{}>{}</>)</>".format(
base_tag,
package_color,
operation.initial_package.name,
package_color,
source_operation_color,
operation.initial_package.full_pretty_version,
source_operation_color,
operation_color,
operation.target_package.full_pretty_version,
)
return ""
def _display_summary(self, operations):
installs = 0
updates = 0
uninstalls = 0
skipped = 0
for op in operations:
if op.skipped:
skipped += 1
continue
if op.job_type == "install":
installs += 1
elif op.job_type == "update":
updates += 1
elif op.job_type == "uninstall":
uninstalls += 1
if not installs and not updates and not uninstalls and not self._verbose:
self._io.write_line("")
self._io.write_line("No dependencies to install or update")
return
self._io.write_line("")
self._io.write_line(
"<b>Package operations</b>: "
"<info>{}</> install{}, "
"<info>{}</> update{}, "
"<info>{}</> removal{}"
"{}".format(
installs,
"" if installs == 1 else "s",
updates,
"" if updates == 1 else "s",
uninstalls,
"" if uninstalls == 1 else "s",
", <info>{}</> skipped".format(skipped)
if skipped and self._verbose
else "",
)
)
self._io.write_line("")
def _execute_install(self, operation): # type: (Install) -> None
return self._install(operation)
def _execute_update(self, operation): # type: (Update) -> None
return self._update(operation)
def _execute_uninstall(self, operation): # type: (Uninstall) -> None
message = " <fg=blue;options=bold>•</> {message}: <info>Removing...</info>".format(
message=self.get_operation_message(operation),
)
self._write(operation, message)
return self._remove(operation)
def _install(self, operation):
package = operation.package
if package.source_type == "directory":
return self._install_directory(operation)
if package.source_type == "git":
return self._install_git(operation)
if package.source_type == "file":
archive = self._prepare_file(operation)
elif package.source_type == "url":
archive = self._download_link(operation, Link(package.source_url))
else:
archive = self._download(operation)
operation_message = self.get_operation_message(operation)
message = " <fg=blue;options=bold>•</> {message}: <info>Installing...</info>".format(
message=operation_message,
)
self._write(operation, message)
args = ["install", "--no-deps", str(archive)]
if operation.job_type == "update":
args.insert(2, "-U")
return self.run_pip(*args)
def _update(self, operation):
return self._install(operation)
def _remove(self, operation):
package = operation.package
# If we have a VCS package, remove its source directory
if package.source_type == "git":
src_dir = self._env.path / "src" / package.name
if src_dir.exists():
safe_rmtree(str(src_dir))
try:
return self.run_pip("uninstall", package.name, "-y")
except CalledProcessError as e:
if "not installed" in str(e):
return 0
raise
def _prepare_file(self, operation):
package = operation.package
message = " <fg=blue;options=bold>•</> {message}: <info>Preparing...</info>".format(
message=self.get_operation_message(operation),
)
self._write(operation, message)
archive = Path(package.source_url)
if not Path(package.source_url).is_absolute() and package.root_dir:
archive = package.root_dir / archive
archive = self._chef.prepare(archive)
return archive
def _install_directory(self, operation):
from poetry.factory import Factory
from poetry.utils.toml_file import TomlFile
package = operation.package
operation_message = self.get_operation_message(operation)
message = " <fg=blue;options=bold>•</> {message}: <info>Building...</info>".format(
message=operation_message,
)
self._write(operation, message)
if package.root_dir:
req = os.path.join(str(package.root_dir), package.source_url)
else:
req = os.path.realpath(package.source_url)
args = ["install", "--no-deps", "-U"]
pyproject = TomlFile(os.path.join(req, "pyproject.toml"))
has_poetry = False
has_build_system = False
if pyproject.exists():
pyproject_content = pyproject.read()
has_poetry = (
"tool" in pyproject_content and "poetry" in pyproject_content["tool"]
)
# Even if there is a build system specified
# some versions of pip (< 19.0.0) don't understand it
# so we need to check the version of pip to know
# if we can rely on the build system
pip_version = self._env.pip_version
pip_version_with_build_system_support = pip_version.__class__(19, 0, 0)
has_build_system = (
"build-system" in pyproject_content
and pip_version >= pip_version_with_build_system_support
)
if has_poetry:
package_poetry = Factory().create_poetry(pyproject.parent)
if package.develop and not package_poetry.package.build_script:
from poetry.masonry.builders.editable import EditableBuilder
# This is a Poetry package in editable mode
# we can use the EditableBuilder without going through pip
# to install it, unless it has a build script.
builder = EditableBuilder(package_poetry, self._env, NullIO())
builder.build()
return 0
elif not has_build_system or package_poetry.package.build_script:
from poetry.core.masonry.builders.sdist import SdistBuilder
# We need to rely on creating a temporary setup.py
# file since the version of pip does not support
# build-systems
# We also need it for non-PEP-517 packages
builder = SdistBuilder(package_poetry)
with builder.setup_py():
if package.develop:
args.append("-e")
args.append(req)
return self.run_pip(*args)
if package.develop:
args.append("-e")
args.append(req)
return self.run_pip(*args)
def _install_git(self, operation):
from poetry.core.vcs import Git
package = operation.package
operation_message = self.get_operation_message(operation)
message = " <fg=blue;options=bold>•</> {message}: <info>Cloning...</info>".format(
message=operation_message,
)
self._write(operation, message)
src_dir = self._env.path / "src" / package.name
if src_dir.exists():
safe_rmtree(str(src_dir))
src_dir.parent.mkdir(exist_ok=True)
git = Git()
git.clone(package.source_url, src_dir)
git.checkout(package.source_reference, src_dir)
# Now we just need to install from the source directory
package.source_url = str(src_dir)
return self._install_directory(operation)
def _download(self, operation): # type: (Operation) -> Path
link = self._chooser.choose_for(operation.package)
return self._download_link(operation, link)
def _download_link(self, operation, link):
package = operation.package
archive = self._chef.get_cached_archive_for_link(link)
if archive is link:
# No cached distributions was found, so we download and prepare it
try:
archive = self._download_archive(operation, link)
except BaseException:
cache_directory = self._chef.get_cache_directory_for_link(link)
cache_directory.joinpath(link.filename).unlink(missing_ok=True)
raise
# TODO: Check readability of the created archive
if not link.is_wheel:
archive = self._chef.prepare(archive)
if package.files:
archive_hash = "sha256:" + FileDependency(package.name, archive).hash()
if archive_hash not in {f["hash"] for f in package.files}:
raise RuntimeError(
"Invalid hash for {} using archive {}".format(package, archive.name)
)
return archive
def _download_archive(self, operation, link): # type: (Operation, Link) -> Path
response = self._authenticator.request("get", link.url, stream=True)
wheel_size = response.headers.get("content-length")
operation_message = self.get_operation_message(operation)
message = " <fg=blue;options=bold>•</> {message}: <info>Downloading...</>".format(
message=operation_message,
)
progress = None
if self.supports_fancy_output():
if wheel_size is None:
self._write(operation, message)
else:
from clikit.ui.components.progress_bar import ProgressBar
progress = ProgressBar(
self._sections[id(operation)].output, max=int(wheel_size)
)
progress.set_format(message + " <b>%percent%%</b>")
if progress:
self._lock.acquire()
progress.start()
self._lock.release()
done = 0
archive = self._chef.get_cache_directory_for_link(link) / link.filename
archive.parent.mkdir(parents=True, exist_ok=True)
with archive.open("wb") as f:
for chunk in response.iter_content(chunk_size=4096):
if not chunk:
break
done += len(chunk)
if progress:
self._lock.acquire()
progress.set_progress(done)
self._lock.release()
f.write(chunk)
if progress:
self._lock.acquire()
progress.finish()
self._lock.release()
return archive
def _should_write_operation(self, operation): # type: (Operation) -> bool
if not operation.skipped:
return True
return self._dry_run or self._verbose
from typing import List from typing import List
from typing import Optional
from typing import Union from typing import Union
from clikit.api.io import IO from clikit.api.io import IO
from poetry.config.config import Config
from poetry.core.packages.project_package import ProjectPackage from poetry.core.packages.project_package import ProjectPackage
from poetry.io.null_io import NullIO from poetry.io.null_io import NullIO
from poetry.packages import Locker from poetry.packages import Locker
from poetry.puzzle import Solver
from poetry.puzzle.operations import Install
from poetry.puzzle.operations import Uninstall
from poetry.puzzle.operations import Update
from poetry.puzzle.operations.operation import Operation
from poetry.repositories import Pool from poetry.repositories import Pool
from poetry.repositories import Repository from poetry.repositories import Repository
from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.installed_repository import InstalledRepository
...@@ -18,6 +15,11 @@ from poetry.utils.extras import get_extra_package_names ...@@ -18,6 +15,11 @@ from poetry.utils.extras import get_extra_package_names
from poetry.utils.helpers import canonicalize_name from poetry.utils.helpers import canonicalize_name
from .base_installer import BaseInstaller from .base_installer import BaseInstaller
from .executor import Executor
from .operations import Install
from .operations import Uninstall
from .operations import Update
from .operations.operation import Operation
from .pip_installer import PipInstaller from .pip_installer import PipInstaller
...@@ -29,7 +31,9 @@ class Installer: ...@@ -29,7 +31,9 @@ class Installer:
package, # type: ProjectPackage package, # type: ProjectPackage
locker, # type: Locker locker, # type: Locker
pool, # type: Pool pool, # type: Pool
installed=None, # type: (Union[InstalledRepository, None]) config, # type: Config
installed=None, # type: Union[InstalledRepository, None]
executor=None, # type: Optional[Executor]
): ):
self._io = io self._io = io
self._env = env self._env = env
...@@ -50,6 +54,12 @@ class Installer: ...@@ -50,6 +54,12 @@ class Installer:
self._extras = [] self._extras = []
if executor is None:
executor = Executor(self._env, self._pool, config, self._io)
self._executor = executor
self._use_executor = False
self._installer = self._get_installer() self._installer = self._get_installer()
if installed is None: if installed is None:
installed = self._get_installed() installed = self._get_installed()
...@@ -57,9 +67,18 @@ class Installer: ...@@ -57,9 +67,18 @@ class Installer:
self._installed_repository = installed self._installed_repository = installed
@property @property
def executor(self):
return self._executor
@property
def installer(self): def installer(self):
return self._installer return self._installer
def set_package(self, package): # type: (ProjectPackage) -> Installer
self._package = package
return self
def run(self): def run(self):
# Force update if there is no lock file present # Force update if there is no lock file present
if not self._update and not self._locker.is_locked(): if not self._update and not self._locker.is_locked():
...@@ -71,12 +90,12 @@ class Installer: ...@@ -71,12 +90,12 @@ class Installer:
self._execute_operations = False self._execute_operations = False
local_repo = Repository() local_repo = Repository()
self._do_install(local_repo)
return 0 return self._do_install(local_repo)
def dry_run(self, dry_run=True): # type: (bool) -> Installer def dry_run(self, dry_run=True): # type: (bool) -> Installer
self._dry_run = dry_run self._dry_run = dry_run
self._executor.dry_run(dry_run)
return self return self
...@@ -93,6 +112,7 @@ class Installer: ...@@ -93,6 +112,7 @@ class Installer:
def verbose(self, verbose=True): # type: (bool) -> Installer def verbose(self, verbose=True): # type: (bool) -> Installer
self._verbose = verbose self._verbose = verbose
self._executor.verbose(verbose)
return self return self
...@@ -128,6 +148,9 @@ class Installer: ...@@ -128,6 +148,9 @@ class Installer:
def execute_operations(self, execute=True): # type: (bool) -> Installer def execute_operations(self, execute=True): # type: (bool) -> Installer
self._execute_operations = execute self._execute_operations = execute
if not execute:
self._executor.disable()
return self return self
def whitelist(self, packages): # type: (dict) -> Installer def whitelist(self, packages): # type: (dict) -> Installer
...@@ -140,7 +163,14 @@ class Installer: ...@@ -140,7 +163,14 @@ class Installer:
return self return self
def use_executor(self, use_executor=True): # type: (bool) -> Installer
self._use_executor = use_executor
return self
def _do_install(self, local_repo): def _do_install(self, local_repo):
from poetry.puzzle import Solver
locked_repository = Repository() locked_repository = Repository()
if self._update: if self._update:
if self._locker.is_locked() and not self._lock: if self._locker.is_locked() and not self._lock:
...@@ -247,19 +277,30 @@ class Installer: ...@@ -247,19 +277,30 @@ class Installer:
# or optional and not requested, are dropped # or optional and not requested, are dropped
self._filter_operations(ops, local_repo) self._filter_operations(ops, local_repo)
# Execute operations
return self._execute(ops)
def _write_lock_file(self, repo): # type: (Repository) -> None
if self._update and self._write_lock:
updated_lock = self._locker.set_lock_data(self._package, repo.packages)
if updated_lock:
self._io.write_line("") self._io.write_line("")
self._io.write_line("<info>Writing lock file</>")
# Execute operations def _execute(self, operations):
actual_ops = [op for op in ops if not op.skipped] if self._use_executor:
if not actual_ops and (self._execute_operations or self._dry_run): return self._executor.execute(operations)
if not operations and (self._execute_operations or self._dry_run):
self._io.write_line("No dependencies to install or update") self._io.write_line("No dependencies to install or update")
if actual_ops and (self._execute_operations or self._dry_run): if operations and (self._execute_operations or self._dry_run):
installs = 0 installs = 0
updates = 0 updates = 0
uninstalls = 0 uninstalls = 0
skipped = 0 skipped = 0
for op in ops: for op in operations:
if op.skipped: if op.skipped:
skipped += 1 skipped += 1
elif op.job_type == "install": elif op.job_type == "install":
...@@ -289,18 +330,13 @@ class Installer: ...@@ -289,18 +330,13 @@ class Installer:
) )
self._io.write_line("") self._io.write_line("")
for op in ops:
self._execute(op)
def _write_lock_file(self, repo): # type: (Repository) -> None for op in operations:
if self._update and self._write_lock: self._execute_operation(op)
updated_lock = self._locker.set_lock_data(self._package, repo.packages)
if updated_lock: return 0
self._io.write_line("")
self._io.write_line("<info>Writing lock file</>")
def _execute(self, operation): # type: (Operation) -> None def _execute_operation(self, operation): # type: (Operation) -> None
""" """
Execute a given operation. Execute a given operation.
""" """
......
...@@ -2,8 +2,8 @@ from .operation import Operation ...@@ -2,8 +2,8 @@ from .operation import Operation
class Install(Operation): class Install(Operation):
def __init__(self, package, reason=None): def __init__(self, package, reason=None, priority=0):
super(Install, self).__init__(reason) super(Install, self).__init__(reason, priority=priority)
self._package = package self._package = package
......
...@@ -4,11 +4,14 @@ from typing import Union ...@@ -4,11 +4,14 @@ from typing import Union
class Operation(object): class Operation(object):
def __init__(self, reason=None): # type: (Union[str, None]) -> None def __init__(
self, reason=None, priority=0
): # type: (Union[str, None], int) -> None
self._reason = reason self._reason = reason
self._skipped = False self._skipped = False
self._skip_reason = None self._skip_reason = None
self._priority = priority
@property @property
def job_type(self): # type: () -> str def job_type(self): # type: () -> str
...@@ -27,6 +30,10 @@ class Operation(object): ...@@ -27,6 +30,10 @@ class Operation(object):
return self._skip_reason return self._skip_reason
@property @property
def priority(self): # type: () -> int
return self._priority
@property
def package(self): def package(self):
raise NotImplementedError() raise NotImplementedError()
......
...@@ -2,8 +2,8 @@ from .operation import Operation ...@@ -2,8 +2,8 @@ from .operation import Operation
class Uninstall(Operation): class Uninstall(Operation):
def __init__(self, package, reason=None): def __init__(self, package, reason=None, priority=float("inf")):
super(Uninstall, self).__init__(reason) super(Uninstall, self).__init__(reason, priority=priority)
self._package = package self._package = package
......
...@@ -2,11 +2,11 @@ from .operation import Operation ...@@ -2,11 +2,11 @@ from .operation import Operation
class Update(Operation): class Update(Operation):
def __init__(self, initial, target, reason=None): def __init__(self, initial, target, reason=None, priority=0):
self._initial_package = initial self._initial_package = initial
self._target_package = target self._target_package = target
super(Update, self).__init__(reason) super(Update, self).__init__(reason, priority=priority)
@property @property
def initial_package(self): def initial_package(self):
......
...@@ -10,6 +10,10 @@ from clikit.io import ConsoleIO ...@@ -10,6 +10,10 @@ from clikit.io import ConsoleIO
from poetry.core.packages import Package from poetry.core.packages import Package
from poetry.core.packages.project_package import ProjectPackage from poetry.core.packages.project_package import ProjectPackage
from poetry.installation.operations import Install
from poetry.installation.operations import Uninstall
from poetry.installation.operations import Update
from poetry.installation.operations.operation import Operation
from poetry.mixology import resolve_version from poetry.mixology import resolve_version
from poetry.mixology.failure import SolveFailure from poetry.mixology.failure import SolveFailure
from poetry.packages import DependencyPackage from poetry.packages import DependencyPackage
...@@ -19,10 +23,6 @@ from poetry.utils.env import Env ...@@ -19,10 +23,6 @@ from poetry.utils.env import Env
from .exceptions import OverrideNeeded from .exceptions import OverrideNeeded
from .exceptions import SolverProblemError from .exceptions import SolverProblemError
from .operations import Install
from .operations import Uninstall
from .operations import Update
from .operations.operation import Operation
from .provider import Provider from .provider import Provider
...@@ -78,7 +78,7 @@ class Solver: ...@@ -78,7 +78,7 @@ class Solver:
) )
operations = [] operations = []
for package in packages: for i, package in enumerate(packages):
installed = False installed = False
for pkg in self._installed.packages: for pkg in self._installed.packages:
if package.name == pkg.name: if package.name == pkg.name:
...@@ -113,23 +113,27 @@ class Solver: ...@@ -113,23 +113,27 @@ class Solver:
package.source_reference package.source_reference
) )
): ):
operations.append(Update(pkg, package)) operations.append(Update(pkg, package, priority=depths[i]))
else: else:
operations.append( operations.append(
Install(package).skip("Already installed") Install(package).skip("Already installed")
) )
elif package.version != pkg.version: elif package.version != pkg.version:
# Checking version # Checking version
operations.append(Update(pkg, package)) operations.append(Update(pkg, package, priority=depths[i]))
elif pkg.source_type and package.source_type != pkg.source_type: elif pkg.source_type and package.source_type != pkg.source_type:
operations.append(Update(pkg, package)) operations.append(Update(pkg, package, priority=depths[i]))
else: else:
operations.append(Install(package).skip("Already installed")) operations.append(
Install(package, priority=depths[i]).skip(
"Already installed"
)
)
break break
if not installed: if not installed:
operations.append(Install(package)) operations.append(Install(package, priority=depths[i]))
# Checking for removals # Checking for removals
for pkg in self._locked.packages: for pkg in self._locked.packages:
...@@ -165,15 +169,7 @@ class Solver: ...@@ -165,15 +169,7 @@ class Solver:
operations.append(Uninstall(installed)) operations.append(Uninstall(installed))
return sorted( return sorted(
operations, operations, key=lambda o: (-o.priority, o.package.name, o.package.version,),
key=lambda o: (
o.job_type == "uninstall",
# Packages to be uninstalled have no depth so we default to 0
# since it actually doesn't matter since removals are always on top.
-depths[packages.index(o.package)] if o.job_type != "uninstall" else 0,
o.package.name,
o.package.version,
),
) )
def solve_in_compatibility_mode(self, overrides, use_latest=None): def solve_in_compatibility_mode(self, overrides, use_latest=None):
......
...@@ -90,6 +90,7 @@ class InstalledRepository(Repository): ...@@ -90,6 +90,7 @@ class InstalledRepository(Repository):
# TODO: handle multiple source directories? # TODO: handle multiple source directories?
package.source_type = "directory" package.source_type = "directory"
package.source_url = paths.pop().as_posix() package.source_url = paths.pop().as_posix()
continue continue
src_path = env.path / "src" src_path = env.path / "src"
......
...@@ -304,6 +304,13 @@ class LegacyRepository(PyPiRepository): ...@@ -304,6 +304,13 @@ class LegacyRepository(PyPiRepository):
return package return package
def find_links_for_package(self, package):
page = self._get("/{}/".format(package.name.replace(".", "-")))
if page is None:
return []
return list(page.links_for_version(package.version))
def _get_release_info(self, name, version): # type: (str, str) -> dict def _get_release_info(self, name, version): # type: (str, str) -> dict
page = self._get("/{}/".format(canonicalize_name(name).replace(".", "-"))) page = self._get("/{}/".format(canonicalize_name(name).replace(".", "-")))
if page is None: if page is None:
......
...@@ -33,7 +33,15 @@ class Pool(BaseRepository): ...@@ -33,7 +33,15 @@ class Pool(BaseRepository):
def has_default(self): # type: () -> bool def has_default(self): # type: () -> bool
return self._default return self._default
def has_repository(self, name): # type: (str) -> bool
name = name.lower() if name is not None else None
return name in self._lookup
def repository(self, name): # type: (str) -> Repository def repository(self, name): # type: (str) -> Repository
if name is not None:
name = name.lower()
if name in self._lookup: if name in self._lookup:
return self._repositories[self._lookup[name]] return self._repositories[self._lookup[name]]
...@@ -45,6 +53,9 @@ class Pool(BaseRepository): ...@@ -45,6 +53,9 @@ class Pool(BaseRepository):
""" """
Adds a repository to the pool. Adds a repository to the pool.
""" """
repository_name = (
repository.name.lower() if repository.name is not None else None
)
if default: if default:
if self.has_default(): if self.has_default():
raise ValueError("Only one repository can be the default") raise ValueError("Only one repository can be the default")
...@@ -57,17 +68,17 @@ class Pool(BaseRepository): ...@@ -57,17 +68,17 @@ class Pool(BaseRepository):
if self._secondary_start_idx is not None: if self._secondary_start_idx is not None:
self._secondary_start_idx += 1 self._secondary_start_idx += 1
self._lookup[repository.name] = 0 self._lookup[repository_name] = 0
elif secondary: elif secondary:
if self._secondary_start_idx is None: if self._secondary_start_idx is None:
self._secondary_start_idx = len(self._repositories) self._secondary_start_idx = len(self._repositories)
self._repositories.append(repository) self._repositories.append(repository)
self._lookup[repository.name] = len(self._repositories) - 1 self._lookup[repository_name] = len(self._repositories) - 1
else: else:
if self._secondary_start_idx is None: if self._secondary_start_idx is None:
self._repositories.append(repository) self._repositories.append(repository)
self._lookup[repository.name] = len(self._repositories) - 1 self._lookup[repository_name] = len(self._repositories) - 1
else: else:
self._repositories.insert(self._secondary_start_idx, repository) self._repositories.insert(self._secondary_start_idx, repository)
...@@ -77,12 +88,15 @@ class Pool(BaseRepository): ...@@ -77,12 +88,15 @@ class Pool(BaseRepository):
self._lookup[name] += 1 self._lookup[name] += 1
self._lookup[repository.name] = self._secondary_start_idx self._lookup[repository_name] = self._secondary_start_idx
self._secondary_start_idx += 1 self._secondary_start_idx += 1
return self return self
def remove_repository(self, repository_name): # type: (str) -> Pool def remove_repository(self, repository_name): # type: (str) -> Pool
if repository_name is not None:
repository_name = repository_name.lower()
idx = self._lookup.get(repository_name) idx = self._lookup.get(repository_name)
if idx is not None: if idx is not None:
del self._repositories[idx] del self._repositories[idx]
...@@ -95,6 +109,9 @@ class Pool(BaseRepository): ...@@ -95,6 +109,9 @@ class Pool(BaseRepository):
def package( def package(
self, name, version, extras=None, repository=None self, name, version, extras=None, repository=None
): # type: (str, str, List[str], str) -> "Package" ): # type: (str, str, List[str], str) -> "Package"
if repository is not None:
repository = repository.lower()
if ( if (
repository is not None repository is not None
and repository not in self._lookup and repository not in self._lookup
...@@ -104,9 +121,7 @@ class Pool(BaseRepository): ...@@ -104,9 +121,7 @@ class Pool(BaseRepository):
if repository is not None and not self._ignore_repository_names: if repository is not None and not self._ignore_repository_names:
try: try:
return self._repositories[self._lookup[repository]].package( return self.repository(repository).package(name, version, extras=extras)
name, version, extras=extras
)
except PackageNotFound: except PackageNotFound:
pass pass
else: else:
...@@ -131,6 +146,9 @@ class Pool(BaseRepository): ...@@ -131,6 +146,9 @@ class Pool(BaseRepository):
allow_prereleases=False, allow_prereleases=False,
repository=None, repository=None,
): ):
if repository is not None:
repository = repository.lower()
if ( if (
repository is not None repository is not None
and repository not in self._lookup and repository not in self._lookup
...@@ -139,7 +157,7 @@ class Pool(BaseRepository): ...@@ -139,7 +157,7 @@ class Pool(BaseRepository):
raise ValueError('Repository "{}" does not exist.'.format(repository)) raise ValueError('Repository "{}" does not exist.'.format(repository))
if repository is not None and not self._ignore_repository_names: if repository is not None and not self._ignore_repository_names:
return self._repositories[self._lookup[repository]].find_packages( return self.repository(repository).find_packages(
name, constraint, extras=extras, allow_prereleases=allow_prereleases name, constraint, extras=extras, allow_prereleases=allow_prereleases
) )
......
...@@ -241,6 +241,18 @@ class PyPiRepository(RemoteRepository): ...@@ -241,6 +241,18 @@ class PyPiRepository(RemoteRepository):
return PackageInfo.load(cached) return PackageInfo.load(cached)
def find_links_for_package(self, package):
json_data = self._get("pypi/{}/{}/json".format(package.name, package.version))
if json_data is None:
return []
links = []
for url in json_data["urls"]:
h = "sha256={}".format(url["digests"]["sha256"])
links.append(Link(url["url"] + "#" + h))
return links
def _get_release_info(self, name, version): # type: (str, str) -> dict def _get_release_info(self, name, version): # type: (str, str) -> dict
self._log("Getting info for {} ({}) from PyPI".format(name, version), "debug") self._log("Getting info for {} ({}) from PyPI".format(name, version), "debug")
......
...@@ -6,10 +6,10 @@ from .base_repository import BaseRepository ...@@ -6,10 +6,10 @@ from .base_repository import BaseRepository
class Repository(BaseRepository): class Repository(BaseRepository):
def __init__(self, packages=None): def __init__(self, packages=None, name=None):
super(Repository, self).__init__() super(Repository, self).__init__()
self._name = None self._name = name
if packages is None: if packages is None:
packages = [] packages = []
...@@ -115,6 +115,9 @@ class Repository(BaseRepository): ...@@ -115,6 +115,9 @@ class Repository(BaseRepository):
if index is not None: if index is not None:
del self._packages[index] del self._packages[index]
def find_links_for_package(self, package):
return []
def search(self, query): def search(self, query):
results = [] results = []
......
...@@ -23,6 +23,11 @@ try: ...@@ -23,6 +23,11 @@ try:
except ImportError: except ImportError:
import urlparse import urlparse
try:
from os import cpu_count
except ImportError: # Python 2
from multiprocessing import cpu_count
try: # Python 2 try: # Python 2
long = long long = long
unicode = unicode unicode = unicode
...@@ -50,6 +55,14 @@ else: ...@@ -50,6 +55,14 @@ else:
shell_quote = shlex.quote shell_quote = shlex.quote
if PY34:
from importlib.machinery import EXTENSION_SUFFIXES
else:
from imp import get_suffixes
EXTENSION_SUFFIXES = [suffix[0] for suffix in get_suffixes()]
if PY35: if PY35:
from pathlib import Path from pathlib import Path
else: else:
......
...@@ -7,7 +7,7 @@ import re ...@@ -7,7 +7,7 @@ import re
import shutil import shutil
import sys import sys
import sysconfig import sysconfig
import warnings import textwrap
from contextlib import contextmanager from contextlib import contextmanager
from typing import Any from typing import Any
...@@ -20,6 +20,12 @@ import tomlkit ...@@ -20,6 +20,12 @@ import tomlkit
from clikit.api.io import IO from clikit.api.io import IO
import packaging.tags
from packaging.tags import Tag
from packaging.tags import interpreter_name
from packaging.tags import interpreter_version
from packaging.tags import sys_tags
from poetry.core.semver import parse_constraint from poetry.core.semver import parse_constraint
from poetry.core.semver.version import Version from poetry.core.semver.version import Version
from poetry.core.version.markers import BaseMarker from poetry.core.version.markers import BaseMarker
...@@ -39,6 +45,36 @@ import json ...@@ -39,6 +45,36 @@ import json
import os import os
import platform import platform
import sys import sys
import sysconfig
INTERPRETER_SHORT_NAMES = {
"python": "py",
"cpython": "cp",
"pypy": "pp",
"ironpython": "ip",
"jython": "jy",
}
def interpreter_version():
version = sysconfig.get_config_var("interpreter_version")
if version:
version = str(version)
else:
version = _version_nodot(sys.version_info[:2])
return version
def _version_nodot(version):
# type: (PythonVersion) -> str
if any(v >= 10 for v in version):
sep = "_"
else:
sep = ""
return sep.join(map(str, version))
if hasattr(sys, "implementation"): if hasattr(sys, "implementation"):
info = sys.implementation.version info = sys.implementation.version
...@@ -50,7 +86,7 @@ if hasattr(sys, "implementation"): ...@@ -50,7 +86,7 @@ if hasattr(sys, "implementation"):
implementation_name = sys.implementation.name implementation_name = sys.implementation.name
else: else:
iver = "0" iver = "0"
implementation_name = "" implementation_name = platform.python_implementation().lower()
env = { env = {
"implementation_name": implementation_name, "implementation_name": implementation_name,
...@@ -65,6 +101,9 @@ env = { ...@@ -65,6 +101,9 @@ env = {
"python_version": platform.python_version()[:3], "python_version": platform.python_version()[:3],
"sys_platform": sys.platform, "sys_platform": sys.platform,
"version_info": tuple(sys.version_info), "version_info": tuple(sys.version_info),
# Extra information
"interpreter_name": INTERPRETER_SHORT_NAMES.get(implementation_name, implementation_name),
"interpreter_version": interpreter_version(),
} }
print(json.dumps(env)) print(json.dumps(env))
...@@ -82,12 +121,6 @@ else: ...@@ -82,12 +121,6 @@ else:
print(sys.prefix) print(sys.prefix)
""" """
GET_CONFIG_VAR = """\
import sysconfig
print(sysconfig.get_config_var("{config_var}")),
"""
GET_PYTHON_VERSION = """\ GET_PYTHON_VERSION = """\
import sys import sys
...@@ -742,6 +775,7 @@ class Env(object): ...@@ -742,6 +775,7 @@ class Env(object):
self._pip_version = None self._pip_version = None
self._site_packages = None self._site_packages = None
self._paths = None self._paths = None
self._supported_tags = None
@property @property
def path(self): # type: () -> Path def path(self): # type: () -> Path
...@@ -813,6 +847,13 @@ class Env(object): ...@@ -813,6 +847,13 @@ class Env(object):
return self._paths return self._paths
@property
def supported_tags(self): # type: () -> List[Tag]
if self._supported_tags is None:
self._supported_tags = self.get_supported_tags()
return self._supported_tags
@classmethod @classmethod
def get_base_prefix(cls): # type: () -> Path def get_base_prefix(cls): # type: () -> Path
if hasattr(sys, "real_prefix"): if hasattr(sys, "real_prefix"):
...@@ -835,7 +876,7 @@ class Env(object): ...@@ -835,7 +876,7 @@ class Env(object):
def get_pip_command(self): # type: () -> List[str] def get_pip_command(self): # type: () -> List[str]
raise NotImplementedError() raise NotImplementedError()
def config_var(self, var): # type: (str) -> Any def get_supported_tags(self): # type: () -> List[Tag]
raise NotImplementedError() raise NotImplementedError()
def get_pip_version(self): # type: () -> Version def get_pip_version(self): # type: () -> Version
...@@ -987,6 +1028,9 @@ class SystemEnv(Env): ...@@ -987,6 +1028,9 @@ class SystemEnv(Env):
return paths return paths
def get_supported_tags(self): # type: () -> List[Tag]
return list(sys_tags())
def get_marker_env(self): # type: () -> Dict[str, Any] def get_marker_env(self): # type: () -> Dict[str, Any]
if hasattr(sys, "implementation"): if hasattr(sys, "implementation"):
info = sys.implementation.version info = sys.implementation.version
...@@ -1015,16 +1059,11 @@ class SystemEnv(Env): ...@@ -1015,16 +1059,11 @@ class SystemEnv(Env):
), ),
"sys_platform": sys.platform, "sys_platform": sys.platform,
"version_info": sys.version_info, "version_info": sys.version_info,
# Extra information
"interpreter_name": interpreter_name(),
"interpreter_version": interpreter_version(),
} }
def config_var(self, var): # type: (str) -> Any
try:
return sysconfig.get_config_var(var)
except IOError as e:
warnings.warn("{0}".format(e), RuntimeWarning)
return
def get_pip_version(self): # type: () -> Version def get_pip_version(self): # type: () -> Version
from pip import __version__ from pip import __version__
...@@ -1068,28 +1107,40 @@ class VirtualEnv(Env): ...@@ -1068,28 +1107,40 @@ class VirtualEnv(Env):
# so assume that we have a functional pip # so assume that we have a functional pip
return [self._bin("pip")] return [self._bin("pip")]
def get_marker_env(self): # type: () -> Dict[str, Any] def get_supported_tags(self): # type: () -> List[Tag]
output = self.run("python", "-", input_=GET_ENVIRONMENT_INFO) file_path = Path(packaging.tags.__file__)
if file_path.suffix == ".pyc":
# Python 2
file_path = file_path.with_suffix(".py")
return json.loads(output) with file_path.open(encoding="utf-8") as f:
script = decode(f.read())
def config_var(self, var): # type: (str) -> Any script = script.replace(
try: "from ._typing import TYPE_CHECKING, cast",
value = self.run( "TYPE_CHECKING = False\ncast = lambda type_, value: value",
"python", "-", input_=GET_CONFIG_VAR.format(config_var=var) )
).strip() script = script.replace(
except EnvCommandError as e: "from ._typing import MYPY_CHECK_RUNNING, cast",
warnings.warn("{0}".format(e), RuntimeWarning) "MYPY_CHECK_RUNNING = False\ncast = lambda type_, value: value",
return None )
script += textwrap.dedent(
"""
import json
print(json.dumps([(t.interpreter, t.abi, t.platform) for t in sys_tags()]))
"""
)
output = self.run("python", "-", input_=script)
return [Tag(*t) for t in json.loads(output)]
if value == "None": def get_marker_env(self): # type: () -> Dict[str, Any]
value = None output = self.run("python", "-", input_=GET_ENVIRONMENT_INFO)
elif value == "1":
value = 1
elif value == "0":
value = 0
return value return json.loads(output)
def get_pip_version(self): # type: () -> Version def get_pip_version(self): # type: () -> Version
output = self.run_pip("--version").strip() output = self.run_pip("--version").strip()
...@@ -1188,7 +1239,7 @@ class MockEnv(NullEnv): ...@@ -1188,7 +1239,7 @@ class MockEnv(NullEnv):
pip_version="19.1", pip_version="19.1",
sys_path=None, sys_path=None,
marker_env=None, marker_env=None,
config_vars=None, supported_tags=None,
**kwargs **kwargs
): ):
super(MockEnv, self).__init__(**kwargs) super(MockEnv, self).__init__(**kwargs)
...@@ -1201,15 +1252,7 @@ class MockEnv(NullEnv): ...@@ -1201,15 +1252,7 @@ class MockEnv(NullEnv):
self._pip_version = Version.parse(pip_version) self._pip_version = Version.parse(pip_version)
self._sys_path = sys_path self._sys_path = sys_path
self._mock_marker_env = marker_env self._mock_marker_env = marker_env
self._config_vars = config_vars self._supported_tags = supported_tags
@property
def version_info(self): # type: () -> Tuple[int]
return self._version_info
@property
def python_implementation(self): # type: () -> str
return self._python_implementation
@property @property
def platform(self): # type: () -> str def platform(self): # type: () -> str
...@@ -1240,17 +1283,12 @@ class MockEnv(NullEnv): ...@@ -1240,17 +1283,12 @@ class MockEnv(NullEnv):
marker_env["python_version"] = ".".join(str(v) for v in self._version_info[:2]) marker_env["python_version"] = ".".join(str(v) for v in self._version_info[:2])
marker_env["python_full_version"] = ".".join(str(v) for v in self._version_info) marker_env["python_full_version"] = ".".join(str(v) for v in self._version_info)
marker_env["sys_platform"] = self._platform marker_env["sys_platform"] = self._platform
marker_env["interpreter_name"] = self._python_implementation.lower()
marker_env["interpreter_version"] = "cp" + "".join(
str(v) for v in self._version_info[:2]
)
return marker_env return marker_env
def is_venv(self): # type: () -> bool def is_venv(self): # type: () -> bool
return self._is_venv return self._is_venv
def config_var(self, var): # type: (str) -> Any
if self._config_vars is None:
return super().config_var(var)
else:
try:
return self._config_vars[var]
except KeyError:
return None
...@@ -37,12 +37,15 @@ html5lib = "^1.0" ...@@ -37,12 +37,15 @@ html5lib = "^1.0"
shellingham = "^1.1" shellingham = "^1.1"
tomlkit = "^0.5.11" tomlkit = "^0.5.11"
pexpect = "^4.7.0" pexpect = "^4.7.0"
packaging = "^20.4"
# The typing module is not in the stdlib in Python 2.7 # The typing module is not in the stdlib in Python 2.7
typing = { version = "^3.6", python = "~2.7" } typing = { version = "^3.6", python = "~2.7" }
# Use pathlib2 for Python 2.7 # Use pathlib2 for Python 2.7
pathlib2 = { version = "^2.3", python = "~2.7" } pathlib2 = { version = "^2.3", python = "~2.7" }
# Use futures on Python 2.7
futures = { version = "^3.3.0", python = "~2.7" }
# Use glob2 for Python 2.7 and 3.4 # Use glob2 for Python 2.7 and 3.4
glob2 = { version = "^0.6", python = "~2.7" } glob2 = { version = "^0.6", python = "~2.7" }
# Use virtualenv for Python 2.7 since venv does not exist # Use virtualenv for Python 2.7 since venv does not exist
......
...@@ -104,11 +104,13 @@ def git_mock(mocker): ...@@ -104,11 +104,13 @@ def git_mock(mocker):
@pytest.fixture @pytest.fixture
def http(): def http():
httpretty.enable() httpretty.reset()
httpretty.enable(allow_net_connect=False)
yield httpretty yield httpretty
httpretty.disable() httpretty.activate()
httpretty.reset()
@pytest.fixture @pytest.fixture
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import sys import sys
import pytest import pytest
from cleo.testers import CommandTester
from poetry.core.semver import Version
from poetry.installation.installer import Installer
from poetry.repositories.legacy_repository import LegacyRepository
from poetry.utils._compat import Path
from tests.helpers import get_dependency
from tests.helpers import get_package
@pytest.fixture()
def tester(app, poetry, config, executor, env):
tester = CommandTester(app.find("add"))
executor._io = tester.io
installer = Installer(
tester.io,
env,
poetry.package,
poetry.locker,
poetry.pool,
config,
executor=executor,
)
installer.use_executor(True)
tester._command.set_installer(installer)
tester._command.set_env(env)
return tester
@pytest.fixture()
def old_tester(tester):
tester._command.installer.use_executor(False)
return tester
def test_add_no_constraint(app, repo, tester):
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))
tester.execute("cachy")
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["dependencies"]
assert content["dependencies"]["cachy"] == "^0.2.0"
def test_add_equal_constraint(app, repo, tester):
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))
tester.execute("cachy==0.1.0")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 1 install, 0 updates, 0 removals
• Installing cachy (0.1.0)
"""
assert expected == tester.io.fetch_output()
assert 1 == tester._command.installer.executor.installations_count
def test_add_greater_constraint(app, repo, tester):
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))
tester.execute("cachy>=0.1.0")
expected = """\
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
def test_add_constraint_with_extras(app, repo, tester):
cachy1 = get_package("cachy", "0.1.0")
cachy1.extras = {"msgpack": [get_dependency("msgpack-python")]}
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True)
cachy1.requires = [msgpack_dep]
repo.add_package(get_package("cachy", "0.2.0"))
repo.add_package(cachy1)
repo.add_package(get_package("msgpack-python", "0.5.3"))
tester.execute("cachy[msgpack]>=0.1.0,<0.2.0")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing msgpack-python (0.5.3)
• Installing cachy (0.1.0)
"""
assert expected == tester.io.fetch_output()
assert 2 == tester._command.installer.executor.installations_count
def test_add_constraint_dependencies(app, repo, tester):
cachy2 = get_package("cachy", "0.2.0")
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6")
cachy2.requires = [msgpack_dep]
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2)
repo.add_package(get_package("msgpack-python", "0.5.3"))
tester.execute("cachy=0.2.0")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing msgpack-python (0.5.3)
• Installing cachy (0.2.0)
"""
assert expected == tester.io.fetch_output()
assert 2 == tester._command.installer.executor.installations_count
def test_add_git_constraint(app, repo, tester, tmp_venv):
tester._command.set_env(tmp_venv)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
tester.execute("git+https://github.com/demo/demo.git")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing pendulum (1.4.4)
• Installing demo (0.1.2 9cf87a2)
"""
assert expected == tester.io.fetch_output()
assert 2 == tester._command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"git": "https://github.com/demo/demo.git"
}
def test_add_git_constraint_with_poetry(app, repo, tester, tmp_venv):
tester._command.set_env(tmp_venv)
repo.add_package(get_package("pendulum", "1.4.4"))
tester.execute("git+https://github.com/demo/pyproject-demo.git")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing pendulum (1.4.4)
• Installing demo (0.1.2 9cf87a2)
"""
assert expected == tester.io.fetch_output()
assert 2 == tester._command.installer.executor.installations_count
def test_add_git_constraint_with_extras(app, repo, tester, tmp_venv):
tester._command.set_env(tmp_venv)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
repo.add_package(get_package("tomlkit", "0.5.5"))
tester.execute("git+https://github.com/demo/demo.git[foo,bar]")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 4 installs, 0 updates, 0 removals
• Installing cleo (0.6.5)
• Installing pendulum (1.4.4)
• Installing tomlkit (0.5.5)
• Installing demo (0.1.2 9cf87a2)
"""
assert expected == tester.io.fetch_output()
assert 4 == tester._command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"git": "https://github.com/demo/demo.git",
"extras": ["foo", "bar"],
}
def test_add_git_ssh_constraint(app, repo, tester, tmp_venv):
tester._command.set_env(tmp_venv)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
tester.execute("git+ssh://git@github.com/demo/demo.git@develop")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing pendulum (1.4.4)
• Installing demo (0.1.2 9cf87a2)
"""
assert expected == tester.io.fetch_output()
assert 2 == tester._command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"git": "ssh://git@github.com/demo/demo.git",
"rev": "develop",
}
def test_add_directory_constraint(app, repo, tester, mocker):
p = mocker.patch("poetry.utils._compat.Path.cwd")
p.return_value = Path(__file__) / ".."
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
tester.execute("../git/github.com/demo/demo")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing pendulum (1.4.4)
• Installing demo (0.1.2 ../git/github.com/demo/demo)
"""
assert expected == tester.io.fetch_output()
assert 2 == tester._command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {"path": "../git/github.com/demo/demo"}
def test_add_directory_with_poetry(app, repo, tester, mocker):
p = mocker.patch("poetry.utils._compat.Path.cwd")
p.return_value = Path(__file__) / ".."
repo.add_package(get_package("pendulum", "1.4.4"))
tester.execute("../git/github.com/demo/pyproject-demo")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing pendulum (1.4.4)
• Installing demo (0.1.2 ../git/github.com/demo/pyproject-demo)
"""
assert expected == tester.io.fetch_output()
assert 2 == tester._command.installer.executor.installations_count
def test_add_file_constraint_wheel(app, repo, tester, mocker, poetry):
p = mocker.patch("poetry.utils._compat.Path.cwd")
p.return_value = poetry.file.parent
repo.add_package(get_package("pendulum", "1.4.4"))
tester.execute("../distributions/demo-0.1.0-py2.py3-none-any.whl")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing pendulum (1.4.4)
• Installing demo (0.1.0 ../distributions/demo-0.1.0-py2.py3-none-any.whl)
"""
assert expected == tester.io.fetch_output()
assert 2 == tester._command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"path": "../distributions/demo-0.1.0-py2.py3-none-any.whl"
}
def test_add_file_constraint_sdist(app, repo, tester, mocker):
p = mocker.patch("poetry.utils._compat.Path.cwd")
p.return_value = Path(__file__) / ".."
repo.add_package(get_package("pendulum", "1.4.4"))
tester.execute("../distributions/demo-0.1.0.tar.gz")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing pendulum (1.4.4)
• Installing demo (0.1.0 ../distributions/demo-0.1.0.tar.gz)
"""
assert expected == tester.io.fetch_output()
assert 2 == tester._command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"path": "../distributions/demo-0.1.0.tar.gz"
}
def test_add_constraint_with_extras_option(app, repo, tester):
cachy2 = get_package("cachy", "0.2.0")
cachy2.extras = {"msgpack": [get_dependency("msgpack-python")]}
msgpack_dep = get_dependency("msgpack-python", ">=0.5 <0.6", optional=True)
cachy2.requires = [msgpack_dep]
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2)
repo.add_package(get_package("msgpack-python", "0.5.3"))
tester.execute("cachy=0.2.0 --extras msgpack")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing msgpack-python (0.5.3)
• Installing cachy (0.2.0)
"""
assert expected == tester.io.fetch_output()
assert 2 == tester._command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
assert "cachy" in content["dependencies"]
assert content["dependencies"]["cachy"] == {
"version": "0.2.0",
"extras": ["msgpack"],
}
def test_add_url_constraint_wheel(app, repo, tester, mocker):
p = mocker.patch("poetry.utils._compat.Path.cwd")
p.return_value = Path(__file__) / ".."
repo.add_package(get_package("pendulum", "1.4.4"))
tester.execute(
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
)
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
• Installing pendulum (1.4.4)
• Installing demo (0.1.0 https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl)
"""
assert expected == tester.io.fetch_output()
assert 2 == tester._command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"url": "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
}
def test_add_url_constraint_wheel_with_extras(app, repo, tester, mocker):
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
repo.add_package(get_package("tomlkit", "0.5.5"))
tester.execute(
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl[foo,bar]"
)
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 4 installs, 0 updates, 0 removals
• Installing cleo (0.6.5)
• Installing pendulum (1.4.4)
• Installing tomlkit (0.5.5)
• Installing demo (0.1.0 https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl)
"""
assert expected == tester.io.fetch_output()
assert 4 == tester._command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"url": "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl",
"extras": ["foo", "bar"],
}
def test_add_constraint_with_python(app, repo, tester):
cachy2 = get_package("cachy", "0.2.0")
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2)
tester.execute("cachy=0.2.0 --python >=2.7")
expected = """\
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["dependencies"]
assert content["dependencies"]["cachy"] == {"version": "0.2.0", "python": ">=2.7"}
def test_add_constraint_with_platform(app, repo, tester, env):
platform = sys.platform
env._platform = platform
cachy2 = get_package("cachy", "0.2.0")
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2)
tester.execute("cachy=0.2.0 --platform {} -vvv".format(platform))
expected = """\
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["dependencies"]
assert content["dependencies"]["cachy"] == {
"version": "0.2.0",
"platform": platform,
}
def test_add_constraint_with_source(app, poetry, tester):
repo = LegacyRepository(name="my-index", url="https://my-index.fake")
repo.add_package(get_package("cachy", "0.2.0"))
repo._cache.store("matches").put("cachy:0.2.0", [Version.parse("0.2.0")], 5)
poetry.pool.add_repository(repo)
tester.execute("cachy=0.2.0 --source my-index")
expected = """\
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["dependencies"]
assert content["dependencies"]["cachy"] == {
"version": "0.2.0",
"source": "my-index",
}
def test_add_constraint_with_source_that_does_not_exist(app, tester):
with pytest.raises(ValueError) as e:
tester.execute("foo --source i-dont-exist")
assert 'Repository "i-dont-exist" does not exist.' == str(e.value)
def test_add_constraint_not_found_with_source(app, poetry, mocker, tester):
repo = LegacyRepository(name="my-index", url="https://my-index.fake")
mocker.patch.object(repo, "find_packages", return_value=[])
poetry.pool.add_repository(repo)
pypi = poetry.pool.repositories[0]
pypi.add_package(get_package("cachy", "0.2.0"))
with pytest.raises(ValueError) as e:
tester.execute("cachy --source my-index")
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):
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))
tester.execute("cachy --dev")
expected = """\
Using version ^0.2.0 for cachy
Updating dependencies
Resolving dependencies...
Writing lock file
from cleo.testers import CommandTester Package operations: 1 install, 0 updates, 0 removals
from poetry.core.semver import Version • Installing cachy (0.2.0)
from poetry.repositories.legacy_repository import LegacyRepository """
from poetry.utils._compat import Path
from tests.helpers import get_dependency assert expected == tester.io.fetch_output()
from tests.helpers import get_package assert 1 == tester._command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
def test_add_no_constraint(app, repo, installer): assert "cachy" in content["dev-dependencies"]
command = app.find("add") assert content["dev-dependencies"]["cachy"] == "^0.2.0"
tester = CommandTester(command)
repo.add_package(get_package("cachy", "0.1.0"))
def test_add_should_not_select_prereleases(app, repo, tester):
repo.add_package(get_package("pyyaml", "3.13"))
repo.add_package(get_package("pyyaml", "4.2b2"))
tester.execute("pyyaml")
expected = """\
Using version ^3.13 for pyyaml
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 1 install, 0 updates, 0 removals
• Installing pyyaml (3.13)
"""
assert expected == tester.io.fetch_output()
assert 1 == tester._command.installer.executor.installations_count
content = app.poetry.file.read()["tool"]["poetry"]
assert "pyyaml" in content["dependencies"]
assert content["dependencies"]["pyyaml"] == "^3.13"
def test_add_should_display_an_error_when_adding_existing_package_with_no_constraint(
app, repo, tester
):
content = app.poetry.file.read()
content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0"
app.poetry.file.write(content)
repo.add_package(get_package("foo", "1.1.2"))
with pytest.raises(ValueError) as e:
tester.execute("foo")
assert "Package foo is already present" == str(e.value)
def test_add_should_work_when_adding_existing_package_with_latest_constraint(
app, repo, tester
):
content = app.poetry.file.read()
content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0"
app.poetry.file.write(content)
repo.add_package(get_package("foo", "1.1.2"))
tester.execute("foo@latest")
expected = """\
Using version ^1.1.2 for foo
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 1 install, 0 updates, 0 removals
• Installing foo (1.1.2)
"""
assert expected in tester.io.fetch_output()
content = app.poetry.file.read()["tool"]["poetry"]
assert "foo" in content["dependencies"]
assert content["dependencies"]["foo"] == "^1.1.2"
def test_add_chooses_prerelease_if_only_prereleases_are_available(app, repo, tester):
repo.add_package(get_package("foo", "1.2.3b0"))
repo.add_package(get_package("foo", "1.2.3b1"))
tester.execute("foo")
expected = """\
Using version ^1.2.3-beta.1 for foo
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 1 install, 0 updates, 0 removals
• Installing foo (1.2.3b1)
"""
assert expected in tester.io.fetch_output()
def test_add_prefers_stable_releases(app, repo, tester):
repo.add_package(get_package("foo", "1.2.3"))
repo.add_package(get_package("foo", "1.2.4b1"))
tester.execute("foo")
expected = """\
Using version ^1.2.3 for foo
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 1 install, 0 updates, 0 removals
• Installing foo (1.2.3)
"""
assert expected in tester.io.fetch_output()
def test_add_with_lock(app, repo, tester):
repo.add_package(get_package("cachy", "0.2.0")) repo.add_package(get_package("cachy", "0.2.0"))
tester.execute("cachy") tester.execute("cachy --lock")
expected = """\ expected = """\
Using version ^0.2.0 for cachy Using version ^0.2.0 for cachy
...@@ -27,14 +803,31 @@ Updating dependencies ...@@ -27,14 +803,31 @@ Updating dependencies
Resolving dependencies... Resolving dependencies...
Writing lock file Writing lock file
"""
assert expected == tester.io.fetch_output()
def test_add_no_constraint_old_installer(app, repo, installer, old_tester):
repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(get_package("cachy", "0.2.0"))
old_tester.execute("cachy")
expected = """\
Using version ^0.2.0 for cachy
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0) - Installing cachy (0.2.0)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 1 assert len(installer.installs) == 1
...@@ -44,14 +837,11 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -44,14 +837,11 @@ Package operations: 1 install, 0 updates, 0 removals
assert content["dependencies"]["cachy"] == "^0.2.0" assert content["dependencies"]["cachy"] == "^0.2.0"
def test_add_equal_constraint(app, repo, installer): def test_add_equal_constraint_old_installer(app, repo, installer, old_tester):
command = app.find("add")
tester = CommandTester(command)
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==0.1.0") old_tester.execute("cachy==0.1.0")
expected = """\ expected = """\
...@@ -60,25 +850,21 @@ Resolving dependencies... ...@@ -60,25 +850,21 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.1.0) - Installing cachy (0.1.0)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 1 assert len(installer.installs) == 1
def test_add_greater_constraint(app, repo, installer): def test_add_greater_constraint_old_installer(app, repo, installer, old_tester):
command = app.find("add")
tester = CommandTester(command)
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>=0.1.0") old_tester.execute("cachy>=0.1.0")
expected = """\ expected = """\
...@@ -87,21 +873,17 @@ Resolving dependencies... ...@@ -87,21 +873,17 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0) - Installing cachy (0.2.0)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 1 assert len(installer.installs) == 1
def test_add_constraint_with_extras(app, repo, installer): def test_add_constraint_with_extras_old_installer(app, repo, installer, old_tester):
command = app.find("add")
tester = CommandTester(command)
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)
...@@ -111,7 +893,7 @@ def test_add_constraint_with_extras(app, repo, installer): ...@@ -111,7 +893,7 @@ def test_add_constraint_with_extras(app, repo, installer):
repo.add_package(cachy1) repo.add_package(cachy1)
repo.add_package(get_package("msgpack-python", "0.5.3")) repo.add_package(get_package("msgpack-python", "0.5.3"))
tester.execute("cachy[msgpack]>=0.1.0,<0.2.0") old_tester.execute("cachy[msgpack]>=0.1.0,<0.2.0")
expected = """\ expected = """\
...@@ -120,22 +902,18 @@ Resolving dependencies... ...@@ -120,22 +902,18 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 2 installs, 0 updates, 0 removals Package operations: 2 installs, 0 updates, 0 removals
- Installing msgpack-python (0.5.3) - Installing msgpack-python (0.5.3)
- Installing cachy (0.1.0) - Installing cachy (0.1.0)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 2 assert len(installer.installs) == 2
def test_add_constraint_dependencies(app, repo, installer): def test_add_constraint_dependencies_old_installer(app, repo, installer, old_tester):
command = app.find("add")
tester = CommandTester(command)
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.requires = [msgpack_dep]
...@@ -144,7 +922,7 @@ def test_add_constraint_dependencies(app, repo, installer): ...@@ -144,7 +922,7 @@ def test_add_constraint_dependencies(app, repo, installer):
repo.add_package(cachy2) repo.add_package(cachy2)
repo.add_package(get_package("msgpack-python", "0.5.3")) repo.add_package(get_package("msgpack-python", "0.5.3"))
tester.execute("cachy=0.2.0") old_tester.execute("cachy=0.2.0")
expected = """\ expected = """\
...@@ -153,26 +931,22 @@ Resolving dependencies... ...@@ -153,26 +931,22 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 2 installs, 0 updates, 0 removals Package operations: 2 installs, 0 updates, 0 removals
- Installing msgpack-python (0.5.3) - Installing msgpack-python (0.5.3)
- Installing cachy (0.2.0) - Installing cachy (0.2.0)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 2 assert len(installer.installs) == 2
def test_add_git_constraint(app, repo, installer): def test_add_git_constraint_old_installer(app, repo, installer, old_tester):
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5")) repo.add_package(get_package("cleo", "0.6.5"))
tester.execute("git+https://github.com/demo/demo.git") old_tester.execute("git+https://github.com/demo/demo.git")
expected = """\ expected = """\
...@@ -181,14 +955,13 @@ Resolving dependencies... ...@@ -181,14 +955,13 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 2 installs, 0 updates, 0 removals Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4) - Installing pendulum (1.4.4)
- Installing demo (0.1.2 9cf87a2) - Installing demo (0.1.2 9cf87a2)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 2 assert len(installer.installs) == 2
...@@ -200,13 +973,10 @@ Package operations: 2 installs, 0 updates, 0 removals ...@@ -200,13 +973,10 @@ Package operations: 2 installs, 0 updates, 0 removals
} }
def test_add_git_constraint_with_poetry(app, repo, installer): def test_add_git_constraint_with_poetry_old_installer(app, repo, installer, old_tester):
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
tester.execute("git+https://github.com/demo/pyproject-demo.git") old_tester.execute("git+https://github.com/demo/pyproject-demo.git")
expected = """\ expected = """\
...@@ -215,27 +985,23 @@ Resolving dependencies... ...@@ -215,27 +985,23 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 2 installs, 0 updates, 0 removals Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4) - Installing pendulum (1.4.4)
- Installing demo (0.1.2 9cf87a2) - Installing demo (0.1.2 9cf87a2)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 2 assert len(installer.installs) == 2
def test_add_git_constraint_with_extras(app, repo, installer): def test_add_git_constraint_with_extras_old_installer(app, repo, installer, old_tester):
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5")) repo.add_package(get_package("cleo", "0.6.5"))
repo.add_package(get_package("tomlkit", "0.5.5")) repo.add_package(get_package("tomlkit", "0.5.5"))
tester.execute("git+https://github.com/demo/demo.git[foo,bar]") old_tester.execute("git+https://github.com/demo/demo.git[foo,bar]")
expected = """\ expected = """\
...@@ -244,7 +1010,6 @@ Resolving dependencies... ...@@ -244,7 +1010,6 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 4 installs, 0 updates, 0 removals Package operations: 4 installs, 0 updates, 0 removals
- Installing cleo (0.6.5) - Installing cleo (0.6.5)
...@@ -253,7 +1018,7 @@ Package operations: 4 installs, 0 updates, 0 removals ...@@ -253,7 +1018,7 @@ Package operations: 4 installs, 0 updates, 0 removals
- Installing demo (0.1.2 9cf87a2) - Installing demo (0.1.2 9cf87a2)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 4 assert len(installer.installs) == 4
...@@ -266,14 +1031,11 @@ Package operations: 4 installs, 0 updates, 0 removals ...@@ -266,14 +1031,11 @@ Package operations: 4 installs, 0 updates, 0 removals
} }
def test_add_git_ssh_constraint(app, repo, installer): def test_add_git_ssh_constraint_old_installer(app, repo, installer, old_tester):
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5")) repo.add_package(get_package("cleo", "0.6.5"))
tester.execute("git+ssh://git@github.com/demo/demo.git@develop") old_tester.execute("git+ssh://git@github.com/demo/demo.git@develop")
expected = """\ expected = """\
...@@ -282,14 +1044,13 @@ Resolving dependencies... ...@@ -282,14 +1044,13 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 2 installs, 0 updates, 0 removals Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4) - Installing pendulum (1.4.4)
- Installing demo (0.1.2 9cf87a2) - Installing demo (0.1.2 9cf87a2)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 2 assert len(installer.installs) == 2
...@@ -302,17 +1063,16 @@ Package operations: 2 installs, 0 updates, 0 removals ...@@ -302,17 +1063,16 @@ Package operations: 2 installs, 0 updates, 0 removals
} }
def test_add_directory_constraint(app, repo, installer, mocker): def test_add_directory_constraint_old_installer(
app, repo, installer, mocker, old_tester
):
p = mocker.patch("poetry.utils._compat.Path.cwd") p = mocker.patch("poetry.utils._compat.Path.cwd")
p.return_value = Path(__file__) / ".." p.return_value = Path(__file__) / ".."
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5")) repo.add_package(get_package("cleo", "0.6.5"))
tester.execute("../git/github.com/demo/demo") old_tester.execute("../git/github.com/demo/demo")
expected = """\ expected = """\
...@@ -321,14 +1081,13 @@ Resolving dependencies... ...@@ -321,14 +1081,13 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 2 installs, 0 updates, 0 removals Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4) - Installing pendulum (1.4.4)
- Installing demo (0.1.2 ../git/github.com/demo/demo) - Installing demo (0.1.2 ../git/github.com/demo/demo)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 2 assert len(installer.installs) == 2
...@@ -338,16 +1097,15 @@ Package operations: 2 installs, 0 updates, 0 removals ...@@ -338,16 +1097,15 @@ Package operations: 2 installs, 0 updates, 0 removals
assert content["dependencies"]["demo"] == {"path": "../git/github.com/demo/demo"} assert content["dependencies"]["demo"] == {"path": "../git/github.com/demo/demo"}
def test_add_directory_with_poetry(app, repo, installer, mocker): def test_add_directory_with_poetry_old_installer(
app, repo, installer, mocker, old_tester
):
p = mocker.patch("poetry.utils._compat.Path.cwd") p = mocker.patch("poetry.utils._compat.Path.cwd")
p.return_value = Path(__file__) / ".." p.return_value = Path(__file__) / ".."
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
tester.execute("../git/github.com/demo/pyproject-demo") old_tester.execute("../git/github.com/demo/pyproject-demo")
expected = """\ expected = """\
...@@ -356,28 +1114,26 @@ Resolving dependencies... ...@@ -356,28 +1114,26 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 2 installs, 0 updates, 0 removals Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4) - Installing pendulum (1.4.4)
- Installing demo (0.1.2 ../git/github.com/demo/pyproject-demo) - Installing demo (0.1.2 ../git/github.com/demo/pyproject-demo)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 2 assert len(installer.installs) == 2
def test_add_file_constraint_wheel(app, repo, installer, mocker): def test_add_file_constraint_wheel_old_installer(
app, repo, installer, mocker, old_tester
):
p = mocker.patch("poetry.utils._compat.Path.cwd") p = mocker.patch("poetry.utils._compat.Path.cwd")
p.return_value = Path(__file__) / ".." p.return_value = Path(__file__) / ".."
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
tester.execute("../distributions/demo-0.1.0-py2.py3-none-any.whl") old_tester.execute("../distributions/demo-0.1.0-py2.py3-none-any.whl")
expected = """\ expected = """\
...@@ -386,14 +1142,13 @@ Resolving dependencies... ...@@ -386,14 +1142,13 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 2 installs, 0 updates, 0 removals Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4) - Installing pendulum (1.4.4)
- Installing demo (0.1.0 ../distributions/demo-0.1.0-py2.py3-none-any.whl) - Installing demo (0.1.0 ../distributions/demo-0.1.0-py2.py3-none-any.whl)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 2 assert len(installer.installs) == 2
...@@ -405,16 +1160,15 @@ Package operations: 2 installs, 0 updates, 0 removals ...@@ -405,16 +1160,15 @@ Package operations: 2 installs, 0 updates, 0 removals
} }
def test_add_file_constraint_sdist(app, repo, installer, mocker): def test_add_file_constraint_sdist_old_installer(
app, repo, installer, mocker, old_tester
):
p = mocker.patch("poetry.utils._compat.Path.cwd") p = mocker.patch("poetry.utils._compat.Path.cwd")
p.return_value = Path(__file__) / ".." p.return_value = Path(__file__) / ".."
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
tester.execute("../distributions/demo-0.1.0.tar.gz") old_tester.execute("../distributions/demo-0.1.0.tar.gz")
expected = """\ expected = """\
...@@ -423,14 +1177,13 @@ Resolving dependencies... ...@@ -423,14 +1177,13 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 2 installs, 0 updates, 0 removals Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4) - Installing pendulum (1.4.4)
- Installing demo (0.1.0 ../distributions/demo-0.1.0.tar.gz) - Installing demo (0.1.0 ../distributions/demo-0.1.0.tar.gz)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 2 assert len(installer.installs) == 2
...@@ -442,10 +1195,9 @@ Package operations: 2 installs, 0 updates, 0 removals ...@@ -442,10 +1195,9 @@ Package operations: 2 installs, 0 updates, 0 removals
} }
def test_add_constraint_with_extras_option(app, repo, installer): def test_add_constraint_with_extras_option_old_installer(
command = app.find("add") app, repo, installer, old_tester
tester = CommandTester(command) ):
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)
...@@ -455,7 +1207,7 @@ def test_add_constraint_with_extras_option(app, repo, installer): ...@@ -455,7 +1207,7 @@ def test_add_constraint_with_extras_option(app, repo, installer):
repo.add_package(cachy2) repo.add_package(cachy2)
repo.add_package(get_package("msgpack-python", "0.5.3")) repo.add_package(get_package("msgpack-python", "0.5.3"))
tester.execute("cachy=0.2.0 --extras msgpack") old_tester.execute("cachy=0.2.0 --extras msgpack")
expected = """\ expected = """\
...@@ -464,14 +1216,13 @@ Resolving dependencies... ...@@ -464,14 +1216,13 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 2 installs, 0 updates, 0 removals Package operations: 2 installs, 0 updates, 0 removals
- Installing msgpack-python (0.5.3) - Installing msgpack-python (0.5.3)
- Installing cachy (0.2.0) - Installing cachy (0.2.0)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 2 assert len(installer.installs) == 2
...@@ -484,16 +1235,15 @@ Package operations: 2 installs, 0 updates, 0 removals ...@@ -484,16 +1235,15 @@ Package operations: 2 installs, 0 updates, 0 removals
} }
def test_add_url_constraint_wheel(app, repo, installer, mocker): def test_add_url_constraint_wheel_old_installer(
app, repo, installer, mocker, old_tester
):
p = mocker.patch("poetry.utils._compat.Path.cwd") p = mocker.patch("poetry.utils._compat.Path.cwd")
p.return_value = Path(__file__) / ".." p.return_value = Path(__file__) / ".."
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
tester.execute( old_tester.execute(
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl" "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
) )
...@@ -504,14 +1254,13 @@ Resolving dependencies... ...@@ -504,14 +1254,13 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 2 installs, 0 updates, 0 removals Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4) - Installing pendulum (1.4.4)
- Installing demo (0.1.0 https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl) - Installing demo (0.1.0 https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 2 assert len(installer.installs) == 2
...@@ -523,15 +1272,14 @@ Package operations: 2 installs, 0 updates, 0 removals ...@@ -523,15 +1272,14 @@ Package operations: 2 installs, 0 updates, 0 removals
} }
def test_add_url_constraint_wheel_with_extras(app, repo, installer, mocker): def test_add_url_constraint_wheel_with_extras_old_installer(
command = app.find("add") app, repo, installer, old_tester
tester = CommandTester(command) ):
repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5")) repo.add_package(get_package("cleo", "0.6.5"))
repo.add_package(get_package("tomlkit", "0.5.5")) repo.add_package(get_package("tomlkit", "0.5.5"))
tester.execute( old_tester.execute(
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl[foo,bar]" "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl[foo,bar]"
) )
...@@ -542,7 +1290,6 @@ Resolving dependencies... ...@@ -542,7 +1290,6 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 4 installs, 0 updates, 0 removals Package operations: 4 installs, 0 updates, 0 removals
- Installing cleo (0.6.5) - Installing cleo (0.6.5)
...@@ -551,7 +1298,7 @@ Package operations: 4 installs, 0 updates, 0 removals ...@@ -551,7 +1298,7 @@ Package operations: 4 installs, 0 updates, 0 removals
- Installing demo (0.1.0 https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl) - Installing demo (0.1.0 https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 4 assert len(installer.installs) == 4
...@@ -564,16 +1311,13 @@ Package operations: 4 installs, 0 updates, 0 removals ...@@ -564,16 +1311,13 @@ Package operations: 4 installs, 0 updates, 0 removals
} }
def test_add_constraint_with_python(app, repo, installer): def test_add_constraint_with_python_old_installer(app, repo, installer, old_tester):
command = app.find("add")
tester = CommandTester(command)
cachy2 = get_package("cachy", "0.2.0") cachy2 = get_package("cachy", "0.2.0")
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)
tester.execute("cachy=0.2.0 --python >=2.7") old_tester.execute("cachy=0.2.0 --python >=2.7")
expected = """\ expected = """\
...@@ -582,13 +1326,12 @@ Resolving dependencies... ...@@ -582,13 +1326,12 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0) - Installing cachy (0.2.0)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 1 assert len(installer.installs) == 1
...@@ -598,18 +1341,18 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -598,18 +1341,18 @@ Package operations: 1 install, 0 updates, 0 removals
assert content["dependencies"]["cachy"] == {"version": "0.2.0", "python": ">=2.7"} assert content["dependencies"]["cachy"] == {"version": "0.2.0", "python": ">=2.7"}
def test_add_constraint_with_platform(app, repo, installer, env): def test_add_constraint_with_platform_old_installer(
app, repo, installer, env, old_tester
):
platform = sys.platform platform = sys.platform
env._platform = platform env._platform = platform
command = app.find("add")
tester = CommandTester(command)
cachy2 = get_package("cachy", "0.2.0") cachy2 = get_package("cachy", "0.2.0")
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)
tester.execute("cachy=0.2.0 --platform {} -vvv".format(platform)) old_tester.execute("cachy=0.2.0 --platform {} -vvv".format(platform))
expected = """\ expected = """\
...@@ -618,13 +1361,12 @@ Resolving dependencies... ...@@ -618,13 +1361,12 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0) - Installing cachy (0.2.0)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 1 assert len(installer.installs) == 1
...@@ -637,17 +1379,14 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -637,17 +1379,14 @@ Package operations: 1 install, 0 updates, 0 removals
} }
def test_add_constraint_with_source(app, poetry, installer): def test_add_constraint_with_source_old_installer(app, poetry, installer, old_tester):
repo = LegacyRepository(name="my-index", url="https://my-index.fake") repo = LegacyRepository(name="my-index", url="https://my-index.fake")
repo.add_package(get_package("cachy", "0.2.0")) repo.add_package(get_package("cachy", "0.2.0"))
repo._cache.store("matches").put("cachy:0.2.0", [Version.parse("0.2.0")], 5) repo._cache.store("matches").put("cachy:0.2.0", [Version.parse("0.2.0")], 5)
poetry.pool.add_repository(repo) poetry.pool.add_repository(repo)
command = app.find("add") old_tester.execute("cachy=0.2.0 --source my-index")
tester = CommandTester(command)
tester.execute("cachy=0.2.0 --source my-index")
expected = """\ expected = """\
...@@ -656,13 +1395,12 @@ Resolving dependencies... ...@@ -656,13 +1395,12 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0) - Installing cachy (0.2.0)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 1 assert len(installer.installs) == 1
...@@ -675,17 +1413,16 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -675,17 +1413,16 @@ Package operations: 1 install, 0 updates, 0 removals
} }
def test_add_constraint_with_source_that_does_not_exist(app): def test_add_constraint_with_source_that_does_not_exist_old_installer(app, old_tester):
command = app.find("add")
tester = CommandTester(command)
with pytest.raises(ValueError) as e: with pytest.raises(ValueError) as e:
tester.execute("foo --source i-dont-exist") old_tester.execute("foo --source i-dont-exist")
assert 'Repository "i-dont-exist" does not exist.' == str(e.value) assert 'Repository "i-dont-exist" does not exist.' == str(e.value)
def test_add_constraint_not_found_with_source(app, poetry, mocker): def test_add_constraint_not_found_with_source_old_installer(
app, poetry, mocker, old_tester
):
repo = LegacyRepository(name="my-index", url="https://my-index.fake") repo = LegacyRepository(name="my-index", url="https://my-index.fake")
mocker.patch.object(repo, "find_packages", return_value=[]) mocker.patch.object(repo, "find_packages", return_value=[])
...@@ -694,23 +1431,19 @@ def test_add_constraint_not_found_with_source(app, poetry, mocker): ...@@ -694,23 +1431,19 @@ def test_add_constraint_not_found_with_source(app, poetry, mocker):
pypi = poetry.pool.repositories[0] pypi = poetry.pool.repositories[0]
pypi.add_package(get_package("cachy", "0.2.0")) pypi.add_package(get_package("cachy", "0.2.0"))
command = app.find("add")
tester = CommandTester(command)
with pytest.raises(ValueError) as e: with pytest.raises(ValueError) as e:
tester.execute("cachy --source my-index") old_tester.execute("cachy --source my-index")
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, installer): def test_add_to_section_that_does_no_exist_yet_old_installer(
command = app.find("add") app, repo, installer, old_tester
tester = CommandTester(command) ):
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") old_tester.execute("cachy --dev")
expected = """\ expected = """\
Using version ^0.2.0 for cachy Using version ^0.2.0 for cachy
...@@ -720,13 +1453,12 @@ Resolving dependencies... ...@@ -720,13 +1453,12 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing cachy (0.2.0) - Installing cachy (0.2.0)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 1 assert len(installer.installs) == 1
...@@ -736,14 +1468,13 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -736,14 +1468,13 @@ Package operations: 1 install, 0 updates, 0 removals
assert content["dev-dependencies"]["cachy"] == "^0.2.0" assert content["dev-dependencies"]["cachy"] == "^0.2.0"
def test_add_should_not_select_prereleases(app, repo, installer): def test_add_should_not_select_prereleases_old_installer(
command = app.find("add") app, repo, installer, old_tester
tester = CommandTester(command) ):
repo.add_package(get_package("pyyaml", "3.13")) repo.add_package(get_package("pyyaml", "3.13"))
repo.add_package(get_package("pyyaml", "4.2b2")) repo.add_package(get_package("pyyaml", "4.2b2"))
tester.execute("pyyaml") old_tester.execute("pyyaml")
expected = """\ expected = """\
Using version ^3.13 for pyyaml Using version ^3.13 for pyyaml
...@@ -753,13 +1484,12 @@ Resolving dependencies... ...@@ -753,13 +1484,12 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing pyyaml (3.13) - Installing pyyaml (3.13)
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
assert len(installer.installs) == 1 assert len(installer.installs) == 1
...@@ -769,35 +1499,31 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -769,35 +1499,31 @@ Package operations: 1 install, 0 updates, 0 removals
assert content["dependencies"]["pyyaml"] == "^3.13" assert content["dependencies"]["pyyaml"] == "^3.13"
def test_add_should_display_an_error_when_adding_existing_package_with_no_constraint( def test_add_should_display_an_error_when_adding_existing_package_with_no_constraint_old_installer(
app, repo, installer app, repo, installer, old_tester
): ):
content = app.poetry.file.read() content = app.poetry.file.read()
content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0" content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0"
app.poetry.file.write(content) app.poetry.file.write(content)
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("foo", "1.1.2")) repo.add_package(get_package("foo", "1.1.2"))
with pytest.raises(ValueError) as e: with pytest.raises(ValueError) as e:
tester.execute("foo") old_tester.execute("foo")
assert "Package foo is already present" == str(e.value) assert "Package foo is already present" == str(e.value)
def test_add_should_work_when_adding_existing_package_with_latest_constraint( def test_add_should_work_when_adding_existing_package_with_latest_constraint_old_installer(
app, repo, installer app, repo, installer, old_tester
): ):
content = app.poetry.file.read() content = app.poetry.file.read()
content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0" content["tool"]["poetry"]["dependencies"]["foo"] = "^1.0"
app.poetry.file.write(content) app.poetry.file.write(content)
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("foo", "1.1.2")) repo.add_package(get_package("foo", "1.1.2"))
tester.execute("foo@latest") old_tester.execute("foo@latest")
expected = """\ expected = """\
Using version ^1.1.2 for foo Using version ^1.1.2 for foo
...@@ -807,13 +1533,12 @@ Resolving dependencies... ...@@ -807,13 +1533,12 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing foo (1.1.2) - Installing foo (1.1.2)
""" """
assert expected in tester.io.fetch_output() assert expected in old_tester.io.fetch_output()
content = app.poetry.file.read()["tool"]["poetry"] content = app.poetry.file.read()["tool"]["poetry"]
...@@ -821,14 +1546,13 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -821,14 +1546,13 @@ Package operations: 1 install, 0 updates, 0 removals
assert content["dependencies"]["foo"] == "^1.1.2" assert content["dependencies"]["foo"] == "^1.1.2"
def test_add_chooses_prerelease_if_only_prereleases_are_available(app, repo, installer): def test_add_chooses_prerelease_if_only_prereleases_are_available_old_installer(
command = app.find("add") app, repo, installer, old_tester
tester = CommandTester(command) ):
repo.add_package(get_package("foo", "1.2.3b0")) repo.add_package(get_package("foo", "1.2.3b0"))
repo.add_package(get_package("foo", "1.2.3b1")) repo.add_package(get_package("foo", "1.2.3b1"))
tester.execute("foo") old_tester.execute("foo")
expected = """\ expected = """\
Using version ^1.2.3-beta.1 for foo Using version ^1.2.3-beta.1 for foo
...@@ -838,23 +1562,19 @@ Resolving dependencies... ...@@ -838,23 +1562,19 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing foo (1.2.3b1) - Installing foo (1.2.3b1)
""" """
assert expected in tester.io.fetch_output() assert expected in old_tester.io.fetch_output()
def test_add_preferes_stable_releases(app, repo, installer):
command = app.find("add")
tester = CommandTester(command)
def test_add_preferes_stable_releases_old_installer(app, repo, installer, old_tester):
repo.add_package(get_package("foo", "1.2.3")) repo.add_package(get_package("foo", "1.2.3"))
repo.add_package(get_package("foo", "1.2.4b1")) repo.add_package(get_package("foo", "1.2.4b1"))
tester.execute("foo") old_tester.execute("foo")
expected = """\ expected = """\
Using version ^1.2.3 for foo Using version ^1.2.3 for foo
...@@ -864,22 +1584,18 @@ Resolving dependencies... ...@@ -864,22 +1584,18 @@ Resolving dependencies...
Writing lock file Writing lock file
Package operations: 1 install, 0 updates, 0 removals Package operations: 1 install, 0 updates, 0 removals
- Installing foo (1.2.3) - Installing foo (1.2.3)
""" """
assert expected in tester.io.fetch_output() assert expected in old_tester.io.fetch_output()
def test_add_with_lock(app, repo, installer):
command = app.find("add")
tester = CommandTester(command)
def test_add_with_lock_old_installer(app, repo, installer, old_tester):
repo.add_package(get_package("cachy", "0.2.0")) repo.add_package(get_package("cachy", "0.2.0"))
tester.execute("cachy --lock") old_tester.execute("cachy --lock")
expected = """\ expected = """\
Using version ^0.2.0 for cachy Using version ^0.2.0 for cachy
...@@ -890,4 +1606,4 @@ Resolving dependencies... ...@@ -890,4 +1606,4 @@ Resolving dependencies...
Writing lock file Writing lock file
""" """
assert expected == tester.io.fetch_output() assert expected == old_tester.io.fetch_output()
...@@ -13,6 +13,7 @@ def test_list_displays_default_value_if_not_set(app, config): ...@@ -13,6 +13,7 @@ def test_list_displays_default_value_if_not_set(app, config):
tester.execute("--list") tester.execute("--list")
expected = """cache-dir = "/foo" expected = """cache-dir = "/foo"
experimental.new-installer = true
virtualenvs.create = true virtualenvs.create = true
virtualenvs.in-project = false virtualenvs.in-project = false
virtualenvs.path = {path} # /foo{sep}virtualenvs virtualenvs.path = {path} # /foo{sep}virtualenvs
...@@ -32,6 +33,7 @@ def test_list_displays_set_get_setting(app, config): ...@@ -32,6 +33,7 @@ def test_list_displays_set_get_setting(app, config):
tester.execute("--list") tester.execute("--list")
expected = """cache-dir = "/foo" expected = """cache-dir = "/foo"
experimental.new-installer = true
virtualenvs.create = false virtualenvs.create = false
virtualenvs.in-project = false virtualenvs.in-project = false
virtualenvs.path = {path} # /foo{sep}virtualenvs virtualenvs.path = {path} # /foo{sep}virtualenvs
...@@ -79,6 +81,7 @@ def test_list_displays_set_get_local_setting(app, config): ...@@ -79,6 +81,7 @@ def test_list_displays_set_get_local_setting(app, config):
tester.execute("--list") tester.execute("--list")
expected = """cache-dir = "/foo" expected = """cache-dir = "/foo"
experimental.new-installer = true
virtualenvs.create = false virtualenvs.create = false
virtualenvs.in-project = false virtualenvs.in-project = false
virtualenvs.path = {path} # /foo{sep}virtualenvs virtualenvs.path = {path} # /foo{sep}virtualenvs
......
import os import os
import re
import pytest import pytest
from cleo import ApplicationTester from cleo import ApplicationTester
from poetry.console import Application as BaseApplication from poetry.console import Application as BaseApplication
from poetry.core.masonry.utils.helpers import escape_name
from poetry.core.masonry.utils.helpers import escape_version
from poetry.core.packages.utils.link import Link
from poetry.factory import Factory from poetry.factory import Factory
from poetry.installation.executor import Executor as BaseExecutor
from poetry.installation.noop_installer import NoopInstaller from poetry.installation.noop_installer import NoopInstaller
from poetry.io.null_io import NullIO
from poetry.packages import Locker as BaseLocker from poetry.packages import Locker as BaseLocker
from poetry.poetry import Poetry as BasePoetry from poetry.poetry import Poetry as BasePoetry
from poetry.repositories import Pool from poetry.repositories import Pool
...@@ -18,6 +24,42 @@ from poetry.utils.toml_file import TomlFile ...@@ -18,6 +24,42 @@ from poetry.utils.toml_file import TomlFile
from tests.helpers import mock_clone from tests.helpers import mock_clone
class Executor(BaseExecutor):
def __init__(self, *args, **kwargs):
super(Executor, self).__init__(*args, **kwargs)
self._installs = []
self._updates = []
self._uninstalls = []
@property
def installations(self):
return self._installs
@property
def updates(self):
return self._updates
@property
def removals(self):
return self._uninstalls
def _do_execute_operation(self, operation):
super(Executor, self)._do_execute_operation(operation)
if not operation.skipped:
getattr(self, "_{}s".format(operation.job_type)).append(operation.package)
def _execute_install(self, operation):
return 0
def _execute_update(self, operation):
return 0
def _execute_remove(self, operation):
return 0
@pytest.fixture() @pytest.fixture()
def installer(): def installer():
return NoopInstaller() return NoopInstaller()
...@@ -39,6 +81,9 @@ def setup(mocker, installer, installed, config, env): ...@@ -39,6 +81,9 @@ def setup(mocker, installer, installed, config, env):
p = mocker.patch("poetry.installation.installer.Installer._get_installer") p = mocker.patch("poetry.installation.installer.Installer._get_installer")
p.return_value = installer p.return_value = installer
# Do not run pip commands of the executor
mocker.patch("poetry.installation.executor.Executor.run_pip")
p = mocker.patch("poetry.installation.installer.Installer._get_installed") p = mocker.patch("poetry.installation.installer.Installer._get_installed")
p.return_value = installed p.return_value = installed
...@@ -144,10 +189,22 @@ class Repository(BaseRepository): ...@@ -144,10 +189,22 @@ class Repository(BaseRepository):
raise PackageNotFound("Package [{}] not found.".format(name)) raise PackageNotFound("Package [{}] not found.".format(name))
return packages return packages
def find_links_for_package(self, package):
return [
Link(
"https://foo.bar/files/{}-{}-py2.py3-none-any.whl".format(
escape_name(package.name), escape_version(package.version.text)
)
)
]
@pytest.fixture @pytest.fixture
def repo(): def repo(http):
return Repository() http.register_uri(
http.GET, re.compile("^https?://foo.bar/(.+?)$"),
)
return Repository(name="foo")
@pytest.fixture @pytest.fixture
...@@ -188,3 +245,13 @@ def app(poetry): ...@@ -188,3 +245,13 @@ def app(poetry):
@pytest.fixture @pytest.fixture
def app_tester(app): def app_tester(app):
return ApplicationTester(app) return ApplicationTester(app)
@pytest.fixture
def new_installer_disabled(config):
config.merge({"experimental": {"new-installer": False}})
@pytest.fixture()
def executor(poetry, config, env):
return Executor(env, poetry.pool, config, NullIO())
import re
import pytest
from poetry.installation.authenticator import Authenticator
from poetry.io.null_io import NullIO
@pytest.fixture()
def mock_remote(http):
http.register_uri(
http.GET, re.compile("^https?://foo.bar/(.+?)$"),
)
def test_authenticator_uses_url_provided_credentials(config, mock_remote, http):
config.merge(
{
"repositories": {"foo": {"url": "https://foo.bar/simple/"}},
"http-basic": {"foo": {"username": "bar", "password": "baz"}},
}
)
authenticator = Authenticator(config, NullIO())
authenticator.request("get", "https://foo001:bar002@foo.bar/files/foo-0.1.0.tar.gz")
request = http.last_request()
assert "Basic Zm9vMDAxOmJhcjAwMg==" == request.headers["Authorization"]
def test_authenticator_uses_credentials_from_config_if_not_provided(
config, mock_remote, http
):
config.merge(
{
"repositories": {"foo": {"url": "https://foo.bar/simple/"}},
"http-basic": {"foo": {"username": "bar", "password": "baz"}},
}
)
authenticator = Authenticator(config, NullIO())
authenticator.request("get", "https://foo.bar/files/foo-0.1.0.tar.gz")
request = http.last_request()
assert "Basic YmFyOmJheg==" == request.headers["Authorization"]
def test_authenticator_uses_username_only_credentials(config, mock_remote, http):
config.merge(
{
"repositories": {"foo": {"url": "https://foo.bar/simple/"}},
"http-basic": {"foo": {"username": "bar", "password": "baz"}},
}
)
authenticator = Authenticator(config, NullIO())
authenticator.request("get", "https://foo001@foo.bar/files/foo-0.1.0.tar.gz")
request = http.last_request()
assert "Basic Zm9vMDAxOg==" == request.headers["Authorization"]
def test_authenticator_uses_password_only_credentials(config, mock_remote, http):
config.merge(
{
"repositories": {"foo": {"url": "https://foo.bar/simple/"}},
"http-basic": {"foo": {"username": "bar", "password": "baz"}},
}
)
authenticator = Authenticator(config, NullIO())
authenticator.request("get", "https://:bar002@foo.bar/files/foo-0.1.0.tar.gz")
request = http.last_request()
assert "Basic OmJhcjAwMg==" == request.headers["Authorization"]
def test_authenticator_uses_empty_strings_as_default_password(
config, mock_remote, http
):
config.merge(
{
"repositories": {"foo": {"url": "https://foo.bar/simple/"}},
"http-basic": {"foo": {"username": "bar"}},
}
)
authenticator = Authenticator(config, NullIO())
authenticator.request("get", "https://foo.bar/files/foo-0.1.0.tar.gz")
request = http.last_request()
assert "Basic YmFyOg==" == request.headers["Authorization"]
def test_authenticator_uses_empty_strings_as_default_username(
config, mock_remote, http
):
config.merge(
{
"repositories": {"foo": {"url": "https://foo.bar/simple/"}},
"http-basic": {"foo": {"username": None, "password": "bar"}},
}
)
authenticator = Authenticator(config, NullIO())
authenticator.request("get", "https://foo.bar/files/foo-0.1.0.tar.gz")
request = http.last_request()
assert "Basic OmJhcg==" == request.headers["Authorization"]
from packaging.tags import Tag
from poetry.core.packages.utils.link import Link
from poetry.installation.chef import Chef
from poetry.utils._compat import Path
from poetry.utils.env import MockEnv
def test_get_cached_archive_for_link(config, mocker):
chef = Chef(
config,
MockEnv(
version_info=(3, 8, 3),
marker_env={"interpreter_name": "cpython", "interpreter_version": "3.8.3"},
supported_tags=[
Tag("cp38", "cp38", "macosx_10_15_x86_64"),
Tag("py3", "none", "any"),
],
),
)
mocker.patch.object(
chef,
"get_cached_archives_for_link",
return_value=[
Link("file:///foo/demo-0.1.0-py2.py3-none-any"),
Link("file:///foo/demo-0.1.0.tar.gz"),
Link("file:///foo/demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl"),
Link("file:///foo/demo-0.1.0-cp37-cp37-macosx_10_15_x86_64.whl"),
],
)
archive = chef.get_cached_archive_for_link(
Link("https://files.python-poetry.org/demo-0.1.0.tar.gz")
)
assert Link("file:///foo/demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl") == archive
def test_get_cached_archives_for_link(config, mocker):
chef = Chef(
config,
MockEnv(
marker_env={"interpreter_name": "cpython", "interpreter_version": "3.8.3"}
),
)
mocker.patch.object(
chef,
"get_cache_directory_for_link",
return_value=Path(__file__).parent.parent.joinpath("fixtures/distributions"),
)
archives = chef.get_cached_archives_for_link(
Link("https://files.python-poetry.org/demo-0.1.0.tar.gz")
)
assert 2 == len(archives)
def test_get_cache_directory_for_link(config):
chef = Chef(
config,
MockEnv(
marker_env={"interpreter_name": "cpython", "interpreter_version": "3.8.3"}
),
)
directory = chef.get_cache_directory_for_link(
Link("https://files.python-poetry.org/poetry-1.1.0.tar.gz")
)
expected = Path(
"/foo/artifacts/ba/63/13/283a3b3b7f95f05e9e6f84182d276f7bb0951d5b0cc24422b33f7a4648"
)
assert expected == directory
import re
import pytest
from packaging.tags import Tag
from poetry.core.packages.package import Package
from poetry.installation.chooser import Chooser
from poetry.repositories.legacy_repository import LegacyRepository
from poetry.repositories.pool import Pool
from poetry.repositories.pypi_repository import PyPiRepository
from poetry.utils._compat import Path
from poetry.utils.env import MockEnv
JSON_FIXTURES = (
Path(__file__).parent.parent / "repositories" / "fixtures" / "pypi.org" / "json"
)
LEGACY_FIXTURES = Path(__file__).parent.parent / "repositories" / "fixtures" / "legacy"
@pytest.fixture()
def env():
return MockEnv(
supported_tags=[
Tag("cp37", "cp37", "macosx_10_15_x86_64"),
Tag("py3", "none", "any"),
]
)
@pytest.fixture()
def mock_pypi(http):
def callback(request, uri, headers):
parts = uri.rsplit("/")
name = parts[-3]
version = parts[-2]
fixture = JSON_FIXTURES / name / (version + ".json")
if not fixture.exists():
fixture = JSON_FIXTURES / (name + ".json")
if not fixture.exists():
return
with fixture.open(encoding="utf-8") as f:
return [200, headers, f.read()]
http.register_uri(
http.GET, re.compile("^https://pypi.org/(.+?)/(.+?)/json$"), body=callback,
)
@pytest.fixture()
def mock_legacy(http):
def callback(request, uri, headers):
parts = uri.rsplit("/")
name = parts[-2]
fixture = LEGACY_FIXTURES / (name + ".html")
with fixture.open(encoding="utf-8") as f:
return [200, headers, f.read()]
http.register_uri(
http.GET, re.compile("^https://foo.bar/simple/(.+?)$"), body=callback,
)
@pytest.fixture()
def pool():
pool = Pool()
pool.add_repository(PyPiRepository(disable_cache=True))
pool.add_repository(
LegacyRepository("foo", "https://foo.bar/simple/", disable_cache=True)
)
return pool
@pytest.mark.parametrize("source_type", ["", "legacy"])
def test_chooser_chooses_universal_wheel_link_if_available(
env, mock_pypi, mock_legacy, source_type, pool
):
chooser = Chooser(pool, env)
package = Package("pytest", "3.5.0")
if source_type == "legacy":
package.source_type = "legacy"
package.source_reference = "foo"
package.source_url = "https://foo.bar/simple/"
link = chooser.choose_for(package)
assert "pytest-3.5.0-py2.py3-none-any.whl" == link.filename
@pytest.mark.parametrize("source_type", ["", "legacy"])
def test_chooser_chooses_specific_python_universal_wheel_link_if_available(
env, mock_pypi, mock_legacy, source_type, pool
):
chooser = Chooser(pool, env)
package = Package("isort", "4.3.4")
if source_type == "legacy":
package.source_type = "legacy"
package.source_reference = "foo"
package.source_url = "https://foo.bar/simple/"
link = chooser.choose_for(package)
assert "isort-4.3.4-py3-none-any.whl" == link.filename
@pytest.mark.parametrize("source_type", ["", "legacy"])
def test_chooser_chooses_system_specific_wheel_link_if_available(
mock_pypi, mock_legacy, source_type, pool
):
env = MockEnv(
supported_tags=[Tag("cp37", "cp37m", "win32"), Tag("py3", "none", "any")]
)
chooser = Chooser(pool, env)
package = Package("pyyaml", "3.13.0")
if source_type == "legacy":
package.source_type = "legacy"
package.source_reference = "foo"
package.source_url = "https://foo.bar/simple/"
link = chooser.choose_for(package)
assert "PyYAML-3.13-cp37-cp37m-win32.whl" == link.filename
@pytest.mark.parametrize("source_type", ["", "legacy"])
def test_chooser_chooses_sdist_if_no_compatible_wheel_link_is_available(
env, mock_pypi, mock_legacy, source_type, pool,
):
chooser = Chooser(pool, env)
package = Package("pyyaml", "3.13.0")
if source_type == "legacy":
package.source_type = "legacy"
package.source_reference = "foo"
package.source_url = "https://foo.bar/simple/"
link = chooser.choose_for(package)
assert "PyYAML-3.13.tar.gz" == link.filename
@pytest.mark.parametrize("source_type", ["", "legacy"])
def test_chooser_chooses_distributions_that_match_the_package_hashes(
env, mock_pypi, mock_legacy, source_type, pool,
):
chooser = Chooser(pool, env)
package = Package("isort", "4.3.4")
package.files = [
{
"hash": "sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8",
"filename": "isort-4.3.4.tar.gz",
}
]
if source_type == "legacy":
package.source_type = "legacy"
package.source_reference = "foo"
package.source_url = "https://foo.bar/simple/"
link = chooser.choose_for(package)
assert "isort-4.3.4.tar.gz" == link.filename
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import re
import pytest
from clikit.api.formatter.style import Style
from clikit.io.buffered_io import BufferedIO
from poetry.config.config import Config
from poetry.core.packages.package import Package
from poetry.installation.executor import Executor
from poetry.installation.operations import Install
from poetry.installation.operations import Uninstall
from poetry.installation.operations import Update
from poetry.repositories.pool import Pool
from poetry.utils._compat import PY36
from poetry.utils._compat import Path
from poetry.utils.env import MockEnv
from tests.repositories.test_pypi_repository import MockRepository
@pytest.fixture()
def io():
io = BufferedIO()
io.formatter.add_style(Style("c1_dark").fg("cyan").dark())
io.formatter.add_style(Style("c2_dark").fg("default").bold().dark())
io.formatter.add_style(Style("success_dark").fg("green").dark())
io.formatter.add_style(Style("warning").fg("yellow"))
return io
@pytest.fixture()
def pool():
pool = Pool()
pool.add_repository(MockRepository())
return pool
@pytest.fixture()
def mock_file_downloads(http):
def callback(request, uri, headers):
fixture = Path(__file__).parent.parent.joinpath(
"fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl"
)
with fixture.open("rb") as f:
return [200, headers, f.read()]
http.register_uri(
http.GET, re.compile("^https://files.pythonhosted.org/.*$"), body=callback,
)
def test_execute_executes_a_batch_of_operations(
config, pool, io, tmp_dir, mock_file_downloads
):
config = Config()
config.merge({"cache-dir": tmp_dir})
env = MockEnv(path=Path(tmp_dir))
executor = Executor(env, pool, config, io)
file_package = Package("demo", "0.1.0")
file_package.source_type = "file"
file_package.source_url = str(
Path(__file__)
.parent.parent.joinpath(
"fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl"
)
.resolve()
)
directory_package = Package("simple-project", "1.2.3")
directory_package.source_type = "directory"
directory_package.source_url = str(
Path(__file__).parent.parent.joinpath("fixtures/simple_project").resolve()
)
git_package = Package("demo", "0.1.0")
git_package.source_type = "git"
git_package.source_reference = "master"
git_package.source_url = "https://github.com/demo/demo.git"
assert 0 == executor.execute(
[
Install(Package("pytest", "3.5.2")),
Uninstall(Package("attrs", "17.4.0")),
Update(Package("requests", "2.18.3"), Package("requests", "2.18.4")),
Uninstall(Package("clikit", "0.2.3")).skip("Not currently installed"),
Install(file_package),
Install(directory_package),
Install(git_package),
]
)
expected = """
Package operations: 4 installs, 1 update, 1 removal
• Installing pytest (3.5.2)
• Removing attrs (17.4.0)
• Updating requests (2.18.3 -> 2.18.4)
• Installing demo (0.1.0 {})
• Installing simple-project (1.2.3 {})
• Installing demo (0.1.0 master)
""".format(
file_package.source_url, directory_package.source_url
)
assert expected == io.fetch_output()
assert 5 == len(env.executed)
def test_execute_shows_skipped_operations_if_verbose(config, pool, io):
config = Config()
config.merge({"cache-dir": "/foo"})
env = MockEnv()
executor = Executor(env, pool, config, io)
executor.verbose()
assert 0 == executor.execute(
[Uninstall(Package("clikit", "0.2.3")).skip("Not currently installed")]
)
expected = """
Package operations: 0 installs, 0 updates, 0 removals, 1 skipped
• Removing clikit (0.2.3): Skipped for the following reason: Not currently installed
"""
assert expected == io.fetch_output()
assert 0 == len(env.executed)
@pytest.mark.skipif(
not PY36, reason="Improved error rendering is only available on Python >=3.6"
)
def test_execute_should_show_errors(config, mocker, io):
env = MockEnv()
executor = Executor(env, pool, config, io)
executor.verbose()
mocker.patch.object(executor, "_install", side_effect=Exception("It failed!"))
assert 1 == executor.execute([Install(Package("clikit", "0.2.3"))])
expected = """
Package operations: 1 install, 0 updates, 0 removals
• Installing clikit (0.2.3)
Exception
It failed!
"""
assert expected in io.fetch_output()
def test_execute_should_show_operation_as_cancelled_on_subprocess_keyboard_interrupt(
config, mocker, io
):
env = MockEnv()
executor = Executor(env, pool, config, io)
executor.verbose()
# A return code of -2 means KeyboardInterrupt in the pip subprocess
mocker.patch.object(executor, "_install", return_value=-2)
assert 1 == executor.execute([Install(Package("clikit", "0.2.3"))])
expected = """
Package operations: 1 install, 0 updates, 0 removals
• Installing clikit (0.2.3)
• Installing clikit (0.2.3): Cancelled
"""
assert expected == io.fetch_output()
...@@ -8,6 +8,7 @@ from clikit.io import NullIO ...@@ -8,6 +8,7 @@ from clikit.io import NullIO
from poetry.core.packages import ProjectPackage from poetry.core.packages import ProjectPackage
from poetry.installation import Installer as BaseInstaller from poetry.installation import Installer as BaseInstaller
from poetry.installation.executor import Executor as BaseExecutor
from poetry.installation.noop_installer import NoopInstaller from poetry.installation.noop_installer import NoopInstaller
from poetry.packages import Locker as BaseLocker from poetry.packages import Locker as BaseLocker
from poetry.repositories import Pool from poetry.repositories import Pool
...@@ -34,6 +35,42 @@ class Installer(BaseInstaller): ...@@ -34,6 +35,42 @@ class Installer(BaseInstaller):
return NoopInstaller() return NoopInstaller()
class Executor(BaseExecutor):
def __init__(self, *args, **kwargs):
super(Executor, self).__init__(*args, **kwargs)
self._installs = []
self._updates = []
self._uninstalls = []
@property
def installations(self):
return self._installs
@property
def updates(self):
return self._updates
@property
def removals(self):
return self._uninstalls
def _do_execute_operation(self, operation):
super(Executor, self)._do_execute_operation(operation)
if not operation.skipped:
getattr(self, "_{}s".format(operation.job_type)).append(operation.package)
def _execute_install(self, operation):
return 0
def _execute_update(self, operation):
return 0
def _execute_uninstall(self, operation):
return 0
class CustomInstalledRepository(InstalledRepository): class CustomInstalledRepository(InstalledRepository):
@classmethod @classmethod
def load(cls, env): def load(cls, env):
...@@ -122,8 +159,20 @@ def env(): ...@@ -122,8 +159,20 @@ def env():
@pytest.fixture() @pytest.fixture()
def installer(package, pool, locker, env, installed): def installer(package, pool, locker, env, installed, config):
return Installer(NullIO(), env, package, locker, pool, installed=installed) installer = Installer(
NullIO(),
env,
package,
locker,
pool,
config,
installed=installed,
executor=Executor(env, pool, config, NullIO()),
)
installer.use_executor(True)
return installer
def fixture(name): def fixture(name):
...@@ -217,14 +266,9 @@ def test_run_update_after_removing_dependencies( ...@@ -217,14 +266,9 @@ def test_run_update_after_removing_dependencies(
assert locker.written_data == expected assert locker.written_data == expected
installs = installer.installer.installs assert 0 == installer.executor.installations_count
assert len(installs) == 0 assert 0 == installer.executor.updates_count
assert 1 == installer.executor.removals_count
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 1
def test_run_install_no_dev(installer, locker, repo, package, installed): def test_run_install_no_dev(installer, locker, repo, package, installed):
...@@ -286,14 +330,9 @@ def test_run_install_no_dev(installer, locker, repo, package, installed): ...@@ -286,14 +330,9 @@ def test_run_install_no_dev(installer, locker, repo, package, installed):
installer.dev_mode(False) installer.dev_mode(False)
installer.run() installer.run()
installs = installer.installer.installs assert 0 == installer.executor.installations_count
assert len(installs) == 0 assert 0 == installer.executor.updates_count
assert 1 == installer.executor.removals_count
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 1
def test_run_install_remove_untracked(installer, locker, repo, package, installed): def test_run_install_remove_untracked(installer, locker, repo, package, installed):
...@@ -339,14 +378,10 @@ def test_run_install_remove_untracked(installer, locker, repo, package, installe ...@@ -339,14 +378,10 @@ def test_run_install_remove_untracked(installer, locker, repo, package, installe
installer.dev_mode(True).remove_untracked(True) installer.dev_mode(True).remove_untracked(True)
installer.run() installer.run()
installs = installer.installer.installs assert 0 == installer.executor.installations_count
assert len(installs) == 0 assert 0 == installer.executor.updates_count
assert 2 == installer.executor.removals_count
updates = installer.installer.updates assert {"b", "c"} == set(r.name for r in installer.executor.removals)
assert len(updates) == 0
removals = installer.installer.removals
assert set(r.name for r in removals) == {"b", "c"}
def test_run_whitelist_add(installer, locker, repo, package): def test_run_whitelist_add(installer, locker, repo, package):
...@@ -438,9 +473,9 @@ def test_run_whitelist_remove(installer, locker, repo, package, installed): ...@@ -438,9 +473,9 @@ def test_run_whitelist_remove(installer, locker, repo, package, installed):
expected = fixture("remove") expected = fixture("remove")
assert locker.written_data == expected assert locker.written_data == expected
assert len(installer.installer.installs) == 1 assert 1 == installer.executor.installations_count
assert len(installer.installer.updates) == 0 assert 0 == installer.executor.updates_count
assert len(installer.installer.removals) == 1 assert 1 == installer.executor.removals_count
def test_add_with_sub_dependencies(installer, locker, repo, package): def test_add_with_sub_dependencies(installer, locker, repo, package):
...@@ -518,13 +553,12 @@ def test_run_with_optional_and_python_restricted_dependencies( ...@@ -518,13 +553,12 @@ def test_run_with_optional_and_python_restricted_dependencies(
assert locker.written_data == expected assert locker.written_data == expected
installer = installer.installer
# We should only have 2 installs: # We should only have 2 installs:
# C,D since python version is not compatible # C,D since python version is not compatible
# with B's python constraint and A is optional # with B's python constraint and A is optional
assert len(installer.installs) == 2 assert 2 == installer.executor.installations_count
assert installer.installs[0].name == "d" assert "d" == installer.executor.installations[0].name
assert installer.installs[1].name == "c" assert "c" == installer.executor.installations[1].name
def test_run_with_optional_and_platform_restricted_dependencies( def test_run_with_optional_and_platform_restricted_dependencies(
...@@ -555,13 +589,12 @@ def test_run_with_optional_and_platform_restricted_dependencies( ...@@ -555,13 +589,12 @@ def test_run_with_optional_and_platform_restricted_dependencies(
assert locker.written_data == expected assert locker.written_data == expected
installer = installer.installer
# We should only have 2 installs: # We should only have 2 installs:
# C,D since the mocked python version is not compatible # C,D since the mocked python version is not compatible
# with B's python constraint and A is optional # with B's python constraint and A is optional
assert len(installer.installs) == 2 assert 2 == installer.executor.installations_count
assert installer.installs[0].name == "d" assert "d" == installer.executor.installations[0].name
assert installer.installs[1].name == "c" assert "c" == installer.executor.installations[1].name
def test_run_with_dependencies_extras(installer, locker, repo, package): def test_run_with_dependencies_extras(installer, locker, repo, package):
...@@ -609,8 +642,7 @@ def test_run_does_not_install_extras_if_not_requested(installer, locker, repo, p ...@@ -609,8 +642,7 @@ def test_run_does_not_install_extras_if_not_requested(installer, locker, repo, p
assert locker.written_data == expected assert locker.written_data == expected
# But should not be installed # But should not be installed
installer = installer.installer assert 3 == installer.executor.installations_count # A, B, C
assert len(installer.installs) == 3 # A, B, C
def test_run_installs_extras_if_requested(installer, locker, repo, package): def test_run_installs_extras_if_requested(installer, locker, repo, package):
...@@ -638,8 +670,7 @@ def test_run_installs_extras_if_requested(installer, locker, repo, package): ...@@ -638,8 +670,7 @@ def test_run_installs_extras_if_requested(installer, locker, repo, package):
assert locker.written_data == expected assert locker.written_data == expected
# But should not be installed # But should not be installed
installer = installer.installer assert 4 == installer.executor.installations_count # A, B, C, D
assert len(installer.installs) == 4 # A, B, C, D
def test_run_installs_extras_with_deps_if_requested(installer, locker, repo, package): def test_run_installs_extras_with_deps_if_requested(installer, locker, repo, package):
...@@ -668,8 +699,7 @@ def test_run_installs_extras_with_deps_if_requested(installer, locker, repo, pac ...@@ -668,8 +699,7 @@ def test_run_installs_extras_with_deps_if_requested(installer, locker, repo, pac
assert locker.written_data == expected assert locker.written_data == expected
# But should not be installed # But should not be installed
installer = installer.installer assert 4 == installer.executor.installations_count # A, B, C, D
assert len(installer.installs) == 4 # A, B, C, D
def test_run_installs_extras_with_deps_if_requested_locked( def test_run_installs_extras_with_deps_if_requested_locked(
...@@ -698,16 +728,15 @@ def test_run_installs_extras_with_deps_if_requested_locked( ...@@ -698,16 +728,15 @@ def test_run_installs_extras_with_deps_if_requested_locked(
installer.run() installer.run()
# But should not be installed # But should not be installed
installer = installer.installer assert 4 == installer.executor.installations_count # A, B, C, D
assert len(installer.installs) == 4 # A, B, C, D
def test_installer_with_pypi_repository(package, locker, installed): def test_installer_with_pypi_repository(package, locker, installed, config):
pool = Pool() pool = Pool()
pool.add_repository(MockRepository()) pool.add_repository(MockRepository())
installer = Installer( installer = Installer(
NullIO(), NullEnv(), package, locker, pool, installed=installed NullIO(), NullEnv(), package, locker, pool, config, installed=installed
) )
package.add_dependency("pytest", "^3.5", category="dev") package.add_dependency("pytest", "^3.5", category="dev")
...@@ -730,7 +759,7 @@ def test_run_installs_with_local_file(installer, locker, repo, package): ...@@ -730,7 +759,7 @@ def test_run_installs_with_local_file(installer, locker, repo, package):
assert locker.written_data == expected assert locker.written_data == expected
assert len(installer.installer.installs) == 2 assert 2 == installer.executor.installations_count
def test_run_installs_wheel_with_no_requires_dist(installer, locker, repo, package): def test_run_installs_wheel_with_no_requires_dist(installer, locker, repo, package):
...@@ -745,7 +774,7 @@ def test_run_installs_wheel_with_no_requires_dist(installer, locker, repo, packa ...@@ -745,7 +774,7 @@ def test_run_installs_wheel_with_no_requires_dist(installer, locker, repo, packa
assert locker.written_data == expected assert locker.written_data == expected
assert len(installer.installer.installs) == 1 assert 1 == installer.executor.installations_count
def test_run_installs_with_local_poetry_directory_and_extras( def test_run_installs_with_local_poetry_directory_and_extras(
...@@ -764,7 +793,7 @@ def test_run_installs_with_local_poetry_directory_and_extras( ...@@ -764,7 +793,7 @@ def test_run_installs_with_local_poetry_directory_and_extras(
assert locker.written_data == expected assert locker.written_data == expected
assert len(installer.installer.installs) == 2 assert 2 == installer.executor.installations_count
def test_run_installs_with_local_poetry_directory_transitive( def test_run_installs_with_local_poetry_directory_transitive(
...@@ -788,7 +817,7 @@ def test_run_installs_with_local_poetry_directory_transitive( ...@@ -788,7 +817,7 @@ def test_run_installs_with_local_poetry_directory_transitive(
assert locker.written_data == expected assert locker.written_data == expected
assert len(installer.installer.installs) == 6 assert 6 == installer.executor.installations_count
def test_run_installs_with_local_poetry_file_transitive( def test_run_installs_with_local_poetry_file_transitive(
...@@ -812,7 +841,7 @@ def test_run_installs_with_local_poetry_file_transitive( ...@@ -812,7 +841,7 @@ def test_run_installs_with_local_poetry_file_transitive(
assert locker.written_data == expected assert locker.written_data == expected
assert len(installer.installer.installs) == 4 assert 4 == installer.executor.installations_count
def test_run_installs_with_local_setuptools_directory( def test_run_installs_with_local_setuptools_directory(
...@@ -830,7 +859,7 @@ def test_run_installs_with_local_setuptools_directory( ...@@ -830,7 +859,7 @@ def test_run_installs_with_local_setuptools_directory(
assert locker.written_data == expected assert locker.written_data == expected
assert len(installer.installer.installs) == 3 assert 3 == installer.executor.installations_count
def test_run_with_prereleases(installer, locker, repo, package): def test_run_with_prereleases(installer, locker, repo, package):
...@@ -1049,16 +1078,14 @@ def test_run_install_duplicate_dependencies_different_constraints( ...@@ -1049,16 +1078,14 @@ def test_run_install_duplicate_dependencies_different_constraints(
assert locker.written_data == expected assert locker.written_data == expected
installs = installer.installer.installs installs = installer.executor.installations
assert len(installs) == 3 assert 3 == installer.executor.installations_count
assert installs[0] == package_c12 assert installs[0] == package_c12
assert installs[1] == package_b10 assert installs[1] == package_b10
assert installs[2] == package_a assert installs[2] == package_a
updates = installer.installer.updates assert 0 == installer.executor.updates_count
assert len(updates) == 0 assert 0 == installer.executor.removals_count
removals = installer.installer.removals
assert len(removals) == 0
def test_run_install_duplicate_dependencies_different_constraints_with_lock( def test_run_install_duplicate_dependencies_different_constraints_with_lock(
...@@ -1159,12 +1186,9 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock( ...@@ -1159,12 +1186,9 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock(
assert locker.written_data == expected assert locker.written_data == expected
installs = installer.installer.installs assert 3 == installer.executor.installations_count
assert len(installs) == 3 assert 0 == installer.executor.updates_count
updates = installer.installer.updates assert 0 == installer.executor.removals_count
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 0
def test_run_update_uninstalls_after_removal_transient_dependency( def test_run_update_uninstalls_after_removal_transient_dependency(
...@@ -1218,12 +1242,9 @@ def test_run_update_uninstalls_after_removal_transient_dependency( ...@@ -1218,12 +1242,9 @@ def test_run_update_uninstalls_after_removal_transient_dependency(
installer.update(True) installer.update(True)
installer.run() installer.run()
installs = installer.installer.installs assert 0 == installer.executor.installations_count
assert len(installs) == 0 assert 0 == installer.executor.updates_count
updates = installer.installer.updates assert 1 == installer.executor.removals_count
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 1
def test_run_install_duplicate_dependencies_different_constraints_with_lock_update( def test_run_install_duplicate_dependencies_different_constraints_with_lock_update(
...@@ -1326,12 +1347,9 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock_upda ...@@ -1326,12 +1347,9 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock_upda
assert locker.written_data == expected assert locker.written_data == expected
installs = installer.installer.installs assert 2 == installer.executor.installations_count
assert len(installs) == 2 assert 1 == installer.executor.updates_count
updates = installer.installer.updates assert 0 == installer.executor.removals_count
assert len(updates) == 1
removals = installer.installer.removals
assert len(removals) == 0
@pytest.mark.skip( @pytest.mark.skip(
...@@ -1357,16 +1375,14 @@ def test_installer_test_solver_finds_compatible_package_for_dependency_python_no ...@@ -1357,16 +1375,14 @@ def test_installer_test_solver_finds_compatible_package_for_dependency_python_no
expected = fixture("with-conditional-dependency") expected = fixture("with-conditional-dependency")
assert locker.written_data == expected assert locker.written_data == expected
installs = installer.installer.installs
if sys.version_info >= (3, 5, 0): if sys.version_info >= (3, 5, 0):
assert len(installs) == 1 assert 1 == installer.executor.installations_count
else: else:
assert len(installs) == 0 assert 0 == installer.executor.installations_count
def test_installer_required_extras_should_not_be_removed_when_updating_single_dependency( def test_installer_required_extras_should_not_be_removed_when_updating_single_dependency(
installer, locker, repo, package, installed, env, pool installer, locker, repo, package, installed, env, pool, config
): ):
package.add_dependency("A", {"version": "^1.0"}) package.add_dependency("A", {"version": "^1.0"})
...@@ -1388,9 +1404,9 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de ...@@ -1388,9 +1404,9 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de
installer.update(True) installer.update(True)
installer.run() installer.run()
assert len(installer.installer.installs) == 3 assert 3 == installer.executor.installations_count
assert len(installer.installer.updates) == 0 assert 0 == installer.executor.updates_count
assert len(installer.installer.removals) == 0 assert 0 == installer.executor.removals_count
package.add_dependency("D", "^1.0") package.add_dependency("D", "^1.0")
locker.locked(True) locker.locked(True)
...@@ -1400,62 +1416,102 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de ...@@ -1400,62 +1416,102 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de
installed.add_package(package_b) installed.add_package(package_b)
installed.add_package(package_c) installed.add_package(package_c)
installer = Installer(NullIO(), env, package, locker, pool, installed=installed) installer = Installer(
NullIO(),
env,
package,
locker,
pool,
config,
installed=installed,
executor=Executor(env, pool, config, NullIO()),
)
installer.use_executor()
installer.update(True) installer.update(True)
installer.whitelist(["D"]) installer.whitelist(["D"])
installer.run() installer.run()
assert len(installer.installer.installs) == 1 assert 1 == installer.executor.installations_count
assert len(installer.installer.updates) == 0 assert 0 == installer.executor.updates_count
assert len(installer.installer.removals) == 0 assert 0 == installer.executor.removals_count
def test_installer_required_extras_should_not_be_removed_when_updating_single_dependency_pypi_repository( def test_installer_required_extras_should_not_be_removed_when_updating_single_dependency_pypi_repository(
locker, repo, package, installed, env, mocker locker, repo, package, installed, env, mocker, config
): ):
mocker.patch("sys.platform", "darwin") mocker.patch("sys.platform", "darwin")
pool = Pool() pool = Pool()
pool.add_repository(MockRepository()) pool.add_repository(MockRepository())
installer = Installer(NullIO(), env, package, locker, pool, installed=installed) installer = Installer(
NullIO(),
env,
package,
locker,
pool,
config,
installed=installed,
executor=Executor(env, pool, config, NullIO()),
)
installer.use_executor()
package.add_dependency("poetry", {"version": "^0.12.0"}) package.add_dependency("poetry", {"version": "^0.12.0"})
installer.update(True) installer.update(True)
installer.run() installer.run()
assert len(installer.installer.installs) == 3 assert 3 == installer.executor.installations_count
assert len(installer.installer.updates) == 0 assert 0 == installer.executor.updates_count
assert len(installer.installer.removals) == 0 assert 0 == installer.executor.removals_count
package.add_dependency("pytest", "^3.5") package.add_dependency("pytest", "^3.5")
locker.locked(True) locker.locked(True)
locker.mock_lock_data(locker.written_data) locker.mock_lock_data(locker.written_data)
for pkg in installer.installer.installs: for pkg in installer.executor.installations:
installed.add_package(pkg) installed.add_package(pkg)
installer = Installer(NullIO(), env, package, locker, pool, installed=installed) installer = Installer(
NullIO(),
env,
package,
locker,
pool,
config,
installed=installed,
executor=Executor(env, pool, config, NullIO()),
)
installer.use_executor()
installer.update(True) installer.update(True)
installer.whitelist(["pytest"]) installer.whitelist(["pytest"])
installer.run() installer.run()
assert len(installer.installer.installs) == 6 if not PY2 else 7 assert (6 if not PY2 else 7) == installer.executor.installations_count
assert len(installer.installer.updates) == 0 assert 0 == installer.executor.updates_count
assert len(installer.installer.removals) == 0 assert 0 == installer.executor.removals_count
def test_installer_required_extras_should_be_installed( def test_installer_required_extras_should_be_installed(
locker, repo, package, installed, env, mocker locker, repo, package, installed, env, config
): ):
pool = Pool() pool = Pool()
pool.add_repository(MockRepository()) pool.add_repository(MockRepository())
installer = Installer(NullIO(), env, package, locker, pool, installed=installed) installer = Installer(
NullIO(),
env,
package,
locker,
pool,
config,
installed=installed,
executor=Executor(env, pool, config, NullIO()),
)
installer.use_executor()
package.add_dependency( package.add_dependency(
"cachecontrol", {"version": "^0.12.5", "extras": ["filecache"]} "cachecontrol", {"version": "^0.12.5", "extras": ["filecache"]}
...@@ -1464,21 +1520,31 @@ def test_installer_required_extras_should_be_installed( ...@@ -1464,21 +1520,31 @@ def test_installer_required_extras_should_be_installed(
installer.update(True) installer.update(True)
installer.run() installer.run()
assert len(installer.installer.installs) == 2 assert 2 == installer.executor.installations_count
assert len(installer.installer.updates) == 0 assert 0 == installer.executor.updates_count
assert len(installer.installer.removals) == 0 assert 0 == installer.executor.removals_count
locker.locked(True) locker.locked(True)
locker.mock_lock_data(locker.written_data) locker.mock_lock_data(locker.written_data)
installer = Installer(NullIO(), env, package, locker, pool, installed=installed) installer = Installer(
NullIO(),
env,
package,
locker,
pool,
config,
installed=installed,
executor=Executor(env, pool, config, NullIO()),
)
installer.use_executor()
installer.update(True) installer.update(True)
installer.run() installer.run()
assert len(installer.installer.installs) == 2 assert 2 == installer.executor.installations_count
assert len(installer.installer.updates) == 0 assert 0 == installer.executor.updates_count
assert len(installer.installer.removals) == 0 assert 0 == installer.executor.removals_count
def test_update_multiple_times_with_split_dependencies_is_idempotent( def test_update_multiple_times_with_split_dependencies_is_idempotent(
...@@ -1557,7 +1623,7 @@ def test_update_multiple_times_with_split_dependencies_is_idempotent( ...@@ -1557,7 +1623,7 @@ def test_update_multiple_times_with_split_dependencies_is_idempotent(
def test_installer_can_install_dependencies_from_forced_source( def test_installer_can_install_dependencies_from_forced_source(
locker, package, installed, env locker, package, installed, env, config
): ):
package.python_versions = "^3.7" package.python_versions = "^3.7"
package.add_dependency("tomlkit", {"version": "^0.5", "source": "legacy"}) package.add_dependency("tomlkit", {"version": "^0.5", "source": "legacy"})
...@@ -1566,14 +1632,24 @@ def test_installer_can_install_dependencies_from_forced_source( ...@@ -1566,14 +1632,24 @@ def test_installer_can_install_dependencies_from_forced_source(
pool.add_repository(MockLegacyRepository()) pool.add_repository(MockLegacyRepository())
pool.add_repository(MockRepository()) pool.add_repository(MockRepository())
installer = Installer(NullIO(), env, package, locker, pool, installed=installed) installer = Installer(
NullIO(),
env,
package,
locker,
pool,
config,
installed=installed,
executor=Executor(env, pool, config, NullIO()),
)
installer.use_executor()
installer.update(True) installer.update(True)
installer.run() installer.run()
assert len(installer.installer.installs) == 1 assert 1 == installer.executor.installations_count
assert len(installer.installer.updates) == 0 assert 0 == installer.executor.updates_count
assert len(installer.installer.removals) == 0 assert 0 == installer.executor.removals_count
def test_run_installs_with_url_file(installer, locker, repo, package): def test_run_installs_with_url_file(installer, locker, repo, package):
...@@ -1588,7 +1664,7 @@ def test_run_installs_with_url_file(installer, locker, repo, package): ...@@ -1588,7 +1664,7 @@ def test_run_installs_with_url_file(installer, locker, repo, package):
assert locker.written_data == expected assert locker.written_data == expected
assert len(installer.installer.installs) == 2 assert 2 == installer.executor.installations_count
def test_installer_uses_prereleases_if_they_are_compatible( def test_installer_uses_prereleases_if_they_are_compatible(
...@@ -1616,11 +1692,11 @@ def test_installer_uses_prereleases_if_they_are_compatible( ...@@ -1616,11 +1692,11 @@ def test_installer_uses_prereleases_if_they_are_compatible(
installer.update(True) installer.update(True)
installer.run() installer.run()
assert len(installer.installer.installs) == 2 assert 2 == installer.executor.installations_count
def test_installer_can_handle_old_lock_files( def test_installer_can_handle_old_lock_files(
installer, locker, package, repo, installed installer, locker, package, repo, installed, config
): ):
pool = Pool() pool = Pool()
pool.add_repository(MockRepository()) pool.add_repository(MockRepository())
...@@ -1631,12 +1707,20 @@ def test_installer_can_handle_old_lock_files( ...@@ -1631,12 +1707,20 @@ def test_installer_can_handle_old_lock_files(
locker.mock_lock_data(fixture("old-lock")) locker.mock_lock_data(fixture("old-lock"))
installer = Installer( installer = Installer(
NullIO(), MockEnv(), package, locker, pool, installed=installed NullIO(),
MockEnv(),
package,
locker,
pool,
config,
installed=installed,
executor=Executor(MockEnv(), pool, config, NullIO(),),
) )
installer.use_executor()
installer.run() installer.run()
assert 6 == len(installer.installer.installs) assert 6 == installer.executor.installations_count
installer = Installer( installer = Installer(
NullIO(), NullIO(),
...@@ -1644,13 +1728,16 @@ def test_installer_can_handle_old_lock_files( ...@@ -1644,13 +1728,16 @@ def test_installer_can_handle_old_lock_files(
package, package,
locker, locker,
pool, pool,
config,
installed=installed, installed=installed,
executor=Executor(MockEnv(version_info=(2, 7, 18)), pool, config, NullIO(),),
) )
installer.use_executor()
installer.run() installer.run()
# funcsigs will be added # funcsigs will be added
assert 7 == len(installer.installer.installs) assert 7 == installer.executor.installations_count
installer = Installer( installer = Installer(
NullIO(), NullIO(),
...@@ -1658,10 +1745,15 @@ def test_installer_can_handle_old_lock_files( ...@@ -1658,10 +1745,15 @@ def test_installer_can_handle_old_lock_files(
package, package,
locker, locker,
pool, pool,
config,
installed=installed, installed=installed,
executor=Executor(
MockEnv(version_info=(2, 7, 18), platform="win32"), pool, config, NullIO(),
),
) )
installer.use_executor()
installer.run() installer.run()
# colorama will be added # colorama will be added
assert 8 == len(installer.installer.installs) assert 8 == installer.executor.installations_count
from __future__ import unicode_literals
import sys
import pytest
from clikit.io import NullIO
from poetry.core.packages import ProjectPackage
from poetry.installation import Installer as BaseInstaller
from poetry.installation.noop_installer import NoopInstaller
from poetry.packages import Locker as BaseLocker
from poetry.repositories import Pool
from poetry.repositories import Repository
from poetry.repositories.installed_repository import InstalledRepository
from poetry.utils._compat import PY2
from poetry.utils._compat import Path
from poetry.utils.env import MockEnv
from poetry.utils.env import NullEnv
from poetry.utils.toml_file import TomlFile
from tests.helpers import get_dependency
from tests.helpers import get_package
from tests.repositories.test_legacy_repository import (
MockRepository as MockLegacyRepository,
)
from tests.repositories.test_pypi_repository import MockRepository
fixtures_dir = Path("tests/fixtures")
class Installer(BaseInstaller):
def _get_installer(self):
return NoopInstaller()
class CustomInstalledRepository(InstalledRepository):
@classmethod
def load(cls, env):
return cls()
class Locker(BaseLocker):
def __init__(self):
self._written_data = None
self._locked = False
self._content_hash = self._get_content_hash()
@property
def written_data(self):
return self._written_data
def locked(self, is_locked=True):
self._locked = is_locked
return self
def mock_lock_data(self, data):
self._lock_data = data
def is_locked(self):
return self._locked
def is_fresh(self):
return True
def _get_content_hash(self):
return "123456789"
def _write_lock_data(self, data):
for package in data["package"]:
python_versions = str(package["python-versions"])
if PY2:
python_versions = python_versions.decode()
if "requirements" in package:
requirements = {}
for key, value in package["requirements"].items():
requirements[key.decode()] = value.decode()
package["requirements"] = requirements
package["python-versions"] = python_versions
self._written_data = data
self._lock_data = data
@pytest.fixture()
def package():
p = ProjectPackage("root", "1.0")
p.root_dir = Path.cwd()
return p
@pytest.fixture()
def repo():
return Repository()
@pytest.fixture()
def pool(repo):
pool = Pool()
pool.add_repository(repo)
return pool
@pytest.fixture()
def installed():
return CustomInstalledRepository()
@pytest.fixture()
def locker():
return Locker()
@pytest.fixture()
def env():
return NullEnv()
@pytest.fixture()
def installer(package, pool, locker, env, installed, config):
return Installer(NullIO(), env, package, locker, pool, config, installed=installed)
def fixture(name):
file = TomlFile(Path(__file__).parent / "fixtures" / "{}.test".format(name))
return file.read()
def test_run_no_dependencies(installer, locker):
installer.run()
expected = fixture("no-dependencies")
assert locker.written_data == expected
def test_run_with_dependencies(installer, locker, repo, package):
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
package.add_dependency("A", "~1.0")
package.add_dependency("B", "^1.0")
installer.run()
expected = fixture("with-dependencies")
assert locker.written_data == expected
def test_run_update_after_removing_dependencies(
installer, locker, repo, package, installed
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "B",
"version": "1.1",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "C",
"version": "1.2",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": [], "B": [], "C": []},
},
}
)
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c = get_package("C", "1.2")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
installed.add_package(package_a)
installed.add_package(package_b)
installed.add_package(package_c)
package.add_dependency("A", "~1.0")
package.add_dependency("B", "~1.1")
installer.update(True)
installer.run()
expected = fixture("with-dependencies")
assert locker.written_data == expected
installs = installer.installer.installs
assert len(installs) == 0
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 1
def test_run_install_no_dev(installer, locker, repo, package, installed):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "B",
"version": "1.1",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "C",
"version": "1.2",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": [], "B": [], "C": []},
},
}
)
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c = get_package("C", "1.2")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
installed.add_package(package_a)
installed.add_package(package_b)
installed.add_package(package_c)
package.add_dependency("A", "~1.0")
package.add_dependency("B", "~1.1")
package.add_dependency("C", "~1.2", category="dev")
installer.dev_mode(False)
installer.run()
installs = installer.installer.installs
assert len(installs) == 0
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 1
def test_run_install_remove_untracked(installer, locker, repo, package, installed):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "a",
"version": "1.0",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
}
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"a": []},
},
}
)
package_a = get_package("a", "1.0")
package_b = get_package("b", "1.1")
package_c = get_package("c", "1.2")
package_pip = get_package("pip", "20.0.0")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_pip)
installed.add_package(package_a)
installed.add_package(package_b)
installed.add_package(package_c)
installed.add_package(package_pip) # Always required and never removed.
installed.add_package(package) # Root package never removed.
package.add_dependency("A", "~1.0")
installer.dev_mode(True).remove_untracked(True)
installer.run()
installs = installer.installer.installs
assert len(installs) == 0
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert set(r.name for r in removals) == {"b", "c"}
def test_run_whitelist_add(installer, locker, repo, package):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
}
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": []},
},
}
)
package_a = get_package("A", "1.0")
package_a_new = get_package("A", "1.1")
package_b = get_package("B", "1.1")
repo.add_package(package_a)
repo.add_package(package_a_new)
repo.add_package(package_b)
package.add_dependency("A", "~1.0")
package.add_dependency("B", "^1.0")
installer.update(True)
installer.whitelist(["B"])
installer.run()
expected = fixture("with-dependencies")
assert locker.written_data == expected
def test_run_whitelist_remove(installer, locker, repo, package, installed):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "B",
"version": "1.1",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": [], "B": []},
},
}
)
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
installed.add_package(package_b)
package.add_dependency("A", "~1.0")
installer.update(True)
installer.whitelist(["B"])
installer.run()
expected = fixture("remove")
assert locker.written_data == expected
assert len(installer.installer.installs) == 1
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 1
def test_add_with_sub_dependencies(installer, locker, repo, package):
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c = get_package("C", "1.2")
package_d = get_package("D", "1.3")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
package.add_dependency("A", "~1.0")
package.add_dependency("B", "^1.0")
package_a.add_dependency("D", "^1.0")
package_b.add_dependency("C", "~1.2")
installer.run()
expected = fixture("with-sub-dependencies")
assert locker.written_data == expected
def test_run_with_python_versions(installer, locker, repo, package):
package.python_versions = "~2.7 || ^3.4"
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c12 = get_package("C", "1.2")
package_c12.python_versions = "~2.7 || ^3.3"
package_c13 = get_package("C", "1.3")
package_c13.python_versions = "~3.3"
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c12)
repo.add_package(package_c13)
package.add_dependency("A", "~1.0")
package.add_dependency("B", "^1.0")
package.add_dependency("C", "^1.0")
installer.run()
expected = fixture("with-python-versions")
assert locker.written_data == expected
def test_run_with_optional_and_python_restricted_dependencies(
installer, locker, repo, package
):
package.python_versions = "~2.7 || ^3.4"
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c12 = get_package("C", "1.2")
package_c13 = get_package("C", "1.3")
package_d = get_package("D", "1.4")
package_c13.add_dependency("D", "^1.2")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c12)
repo.add_package(package_c13)
repo.add_package(package_d)
package.extras = {"foo": [get_dependency("A", "~1.0")]}
package.add_dependency("A", {"version": "~1.0", "optional": True})
package.add_dependency("B", {"version": "^1.0", "python": "~2.4"})
package.add_dependency("C", {"version": "^1.0", "python": "~2.7 || ^3.4"})
installer.run()
expected = fixture("with-optional-dependencies")
assert locker.written_data == expected
installer = installer.installer
# We should only have 2 installs:
# C,D since python version is not compatible
# with B's python constraint and A is optional
assert len(installer.installs) == 2
assert installer.installs[0].name == "d"
assert installer.installs[1].name == "c"
def test_run_with_optional_and_platform_restricted_dependencies(
installer, locker, repo, package, mocker
):
mocker.patch("sys.platform", "darwin")
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c12 = get_package("C", "1.2")
package_c13 = get_package("C", "1.3")
package_d = get_package("D", "1.4")
package_c13.add_dependency("D", "^1.2")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c12)
repo.add_package(package_c13)
repo.add_package(package_d)
package.extras = {"foo": [get_dependency("A", "~1.0")]}
package.add_dependency("A", {"version": "~1.0", "optional": True})
package.add_dependency("B", {"version": "^1.0", "platform": "custom"})
package.add_dependency("C", {"version": "^1.0", "platform": "darwin"})
installer.run()
expected = fixture("with-platform-dependencies")
assert locker.written_data == expected
installer = installer.installer
# We should only have 2 installs:
# C,D since the mocked python version is not compatible
# with B's python constraint and A is optional
assert len(installer.installs) == 2
assert installer.installs[0].name == "d"
assert installer.installs[1].name == "c"
def test_run_with_dependencies_extras(installer, locker, repo, package):
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.0")
package_c = get_package("C", "1.0")
package_b.extras = {"foo": [get_dependency("C", "^1.0")]}
package_b.add_dependency("C", {"version": "^1.0", "optional": True})
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
package.add_dependency("A", "^1.0")
package.add_dependency("B", {"version": "^1.0", "extras": ["foo"]})
installer.run()
expected = fixture("with-dependencies-extras")
assert locker.written_data == expected
def test_run_does_not_install_extras_if_not_requested(installer, locker, repo, package):
package.extras["foo"] = [get_dependency("D")]
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.0")
package_c = get_package("C", "1.0")
package_d = get_package("D", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
package.add_dependency("A", "^1.0")
package.add_dependency("B", "^1.0")
package.add_dependency("C", "^1.0")
package.add_dependency("D", {"version": "^1.0", "optional": True})
installer.run()
expected = fixture("extras")
# Extras are pinned in lock
assert locker.written_data == expected
# But should not be installed
installer = installer.installer
assert len(installer.installs) == 3 # A, B, C
def test_run_installs_extras_if_requested(installer, locker, repo, package):
package.extras["foo"] = [get_dependency("D")]
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.0")
package_c = get_package("C", "1.0")
package_d = get_package("D", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
package.add_dependency("A", "^1.0")
package.add_dependency("B", "^1.0")
package.add_dependency("C", "^1.0")
package.add_dependency("D", {"version": "^1.0", "optional": True})
installer.extras(["foo"])
installer.run()
expected = fixture("extras")
# Extras are pinned in lock
assert locker.written_data == expected
# But should not be installed
installer = installer.installer
assert len(installer.installs) == 4 # A, B, C, D
def test_run_installs_extras_with_deps_if_requested(installer, locker, repo, package):
package.extras["foo"] = [get_dependency("C")]
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.0")
package_c = get_package("C", "1.0")
package_d = get_package("D", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
package.add_dependency("A", "^1.0")
package.add_dependency("B", "^1.0")
package.add_dependency("C", {"version": "^1.0", "optional": True})
package_c.add_dependency("D", "^1.0")
installer.extras(["foo"])
installer.run()
expected = fixture("extras-with-dependencies")
# Extras are pinned in lock
assert locker.written_data == expected
# But should not be installed
installer = installer.installer
assert len(installer.installs) == 4 # A, B, C, D
def test_run_installs_extras_with_deps_if_requested_locked(
installer, locker, repo, package
):
locker.locked(True)
locker.mock_lock_data(fixture("extras-with-dependencies"))
package.extras["foo"] = [get_dependency("C")]
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.0")
package_c = get_package("C", "1.0")
package_d = get_package("D", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
package.add_dependency("A", "^1.0")
package.add_dependency("B", "^1.0")
package.add_dependency("C", {"version": "^1.0", "optional": True})
package_c.add_dependency("D", "^1.0")
installer.extras(["foo"])
installer.run()
# But should not be installed
installer = installer.installer
assert len(installer.installs) == 4 # A, B, C, D
def test_installer_with_pypi_repository(package, locker, installed, config):
pool = Pool()
pool.add_repository(MockRepository())
installer = Installer(
NullIO(), NullEnv(), package, locker, pool, config, installed=installed
)
package.add_dependency("pytest", "^3.5", category="dev")
installer.run()
expected = fixture("with-pypi-repository")
assert locker.written_data == expected
def test_run_installs_with_local_file(installer, locker, repo, package):
file_path = fixtures_dir / "distributions/demo-0.1.0-py2.py3-none-any.whl"
package.add_dependency("demo", {"file": str(file_path)})
repo.add_package(get_package("pendulum", "1.4.4"))
installer.run()
expected = fixture("with-file-dependency")
assert locker.written_data == expected
assert len(installer.installer.installs) == 2
def test_run_installs_wheel_with_no_requires_dist(installer, locker, repo, package):
file_path = (
fixtures_dir / "wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl"
)
package.add_dependency("demo", {"file": str(file_path)})
installer.run()
expected = fixture("with-wheel-dependency-no-requires-dist")
assert locker.written_data == expected
assert len(installer.installer.installs) == 1
def test_run_installs_with_local_poetry_directory_and_extras(
installer, locker, repo, package, tmpdir
):
file_path = fixtures_dir / "project_with_extras"
package.add_dependency(
"project-with-extras", {"path": str(file_path), "extras": ["extras_a"]}
)
repo.add_package(get_package("pendulum", "1.4.4"))
installer.run()
expected = fixture("with-directory-dependency-poetry")
assert locker.written_data == expected
assert len(installer.installer.installs) == 2
def test_run_installs_with_local_poetry_directory_transitive(
installer, locker, repo, package, tmpdir
):
package.root_dir = fixtures_dir.joinpath("directory")
directory = fixtures_dir.joinpath("directory").joinpath(
"project_with_transitive_directory_dependencies"
)
package.add_dependency(
"project-with-transitive-directory-dependencies",
{"path": str(directory.relative_to(fixtures_dir.joinpath("directory")))},
)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
installer.run()
expected = fixture("with-directory-dependency-poetry-transitive")
assert locker.written_data == expected
assert len(installer.installer.installs) == 6
def test_run_installs_with_local_poetry_file_transitive(
installer, locker, repo, package, tmpdir
):
package.root_dir = fixtures_dir.joinpath("directory")
directory = fixtures_dir.joinpath("directory").joinpath(
"project_with_transitive_file_dependencies"
)
package.add_dependency(
"project-with-transitive-file-dependencies",
{"path": str(directory.relative_to(fixtures_dir.joinpath("directory")))},
)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
installer.run()
expected = fixture("with-file-dependency-transitive")
assert locker.written_data == expected
assert len(installer.installer.installs) == 4
def test_run_installs_with_local_setuptools_directory(
installer, locker, repo, package, tmpdir
):
file_path = fixtures_dir / "project_with_setup/"
package.add_dependency("my-package", {"path": str(file_path)})
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
installer.run()
expected = fixture("with-directory-dependency-setuptools")
assert locker.written_data == expected
assert len(installer.installer.installs) == 3
def test_run_with_prereleases(installer, locker, repo, package):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0a2",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
}
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": []},
},
}
)
package_a = get_package("A", "1.0a2")
package_b = get_package("B", "1.1")
repo.add_package(package_a)
repo.add_package(package_b)
package.add_dependency("A", {"version": "*", "allows-prereleases": True})
package.add_dependency("B", "^1.1")
installer.update(True)
installer.whitelist({"B": "^1.1"})
installer.run()
expected = fixture("with-prereleases")
assert locker.written_data == expected
def test_run_changes_category_if_needed(installer, locker, repo, package):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"category": "dev",
"optional": True,
"platform": "*",
"python-versions": "*",
"checksum": [],
}
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": []},
},
}
)
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_b.add_dependency("A", "^1.0")
repo.add_package(package_a)
repo.add_package(package_b)
package.add_dependency("A", {"version": "^1.0", "optional": True}, category="dev")
package.add_dependency("B", "^1.1")
installer.update(True)
installer.whitelist(["B"])
installer.run()
expected = fixture("with-category-change")
assert locker.written_data == expected
def test_run_update_all_with_lock(installer, locker, repo, package):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"category": "dev",
"optional": True,
"platform": "*",
"python-versions": "*",
"checksum": [],
}
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": []},
},
}
)
package_a = get_package("A", "1.1")
repo.add_package(get_package("A", "1.0"))
repo.add_package(package_a)
package.add_dependency("A")
installer.update(True)
installer.run()
expected = fixture("update-with-lock")
assert locker.written_data == expected
def test_run_update_with_locked_extras(installer, locker, repo, package):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"B": "^1.0", "C": "^1.0"},
},
{
"name": "B",
"version": "1.0",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "C",
"version": "1.1",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"requirements": {"python": "~2.7"},
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": [], "B": [], "C": []},
},
}
)
package_a = get_package("A", "1.0")
package_a.extras["foo"] = [get_dependency("B")]
b_dependency = get_dependency("B", "^1.0", optional=True)
b_dependency.in_extras.append("foo")
c_dependency = get_dependency("C", "^1.0")
c_dependency.python_versions = "~2.7"
package_a.requires.append(b_dependency)
package_a.requires.append(c_dependency)
repo.add_package(package_a)
repo.add_package(get_package("B", "1.0"))
repo.add_package(get_package("C", "1.1"))
repo.add_package(get_package("D", "1.1"))
package.add_dependency("A", {"version": "^1.0", "extras": ["foo"]})
package.add_dependency("D", "^1.0")
installer.update(True)
installer.whitelist("D")
installer.run()
expected = fixture("update-with-locked-extras")
assert locker.written_data == expected
def test_run_install_duplicate_dependencies_different_constraints(
installer, locker, repo, package
):
package.add_dependency("A")
package_a = get_package("A", "1.0")
package_a.add_dependency("B", {"version": "^1.0", "python": "<4.0"})
package_a.add_dependency("B", {"version": "^2.0", "python": ">=4.0"})
package_b10 = get_package("B", "1.0")
package_b20 = get_package("B", "2.0")
package_b10.add_dependency("C", "1.2")
package_b20.add_dependency("C", "1.5")
package_c12 = get_package("C", "1.2")
package_c15 = get_package("C", "1.5")
repo.add_package(package_a)
repo.add_package(package_b10)
repo.add_package(package_b20)
repo.add_package(package_c12)
repo.add_package(package_c15)
installer.run()
expected = fixture("with-duplicate-dependencies")
assert locker.written_data == expected
installs = installer.installer.installs
assert len(installs) == 3
assert installs[0] == package_c12
assert installs[1] == package_b10
assert installs[2] == package_a
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 0
def test_run_install_duplicate_dependencies_different_constraints_with_lock(
installer, locker, repo, package
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {
"B": [
{"version": "^1.0", "python": "<4.0"},
{"version": "^2.0", "python": ">=4.0"},
]
},
},
{
"name": "B",
"version": "1.0",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"C": "1.2"},
"requirements": {"python": "<4.0"},
},
{
"name": "B",
"version": "2.0",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"C": "1.5"},
"requirements": {"python": ">=4.0"},
},
{
"name": "C",
"version": "1.2",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "C",
"version": "1.5",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": [], "B": [], "C": []},
},
}
)
package.add_dependency("A")
package_a = get_package("A", "1.0")
package_a.add_dependency("B", {"version": "^1.0", "python": "<4.0"})
package_a.add_dependency("B", {"version": "^2.0", "python": ">=4.0"})
package_b10 = get_package("B", "1.0")
package_b20 = get_package("B", "2.0")
package_b10.add_dependency("C", "1.2")
package_b20.add_dependency("C", "1.5")
package_c12 = get_package("C", "1.2")
package_c15 = get_package("C", "1.5")
repo.add_package(package_a)
repo.add_package(package_b10)
repo.add_package(package_b20)
repo.add_package(package_c12)
repo.add_package(package_c15)
installer.update(True)
installer.run()
expected = fixture("with-duplicate-dependencies")
assert locker.written_data == expected
installs = installer.installer.installs
assert len(installs) == 3
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 0
def test_run_update_uninstalls_after_removal_transient_dependency(
installer, locker, repo, package, installed
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"B": {"version": "^1.0", "python": "<2.0"}},
},
{
"name": "B",
"version": "1.0",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": [], "B": []},
},
}
)
package.add_dependency("A")
package_a = get_package("A", "1.0")
package_a.add_dependency("B", {"version": "^1.0", "python": "<2.0"})
package_b10 = get_package("B", "1.0")
repo.add_package(package_a)
repo.add_package(package_b10)
installed.add_package(get_package("A", "1.0"))
installed.add_package(get_package("B", "1.0"))
installer.update(True)
installer.run()
installs = installer.installer.installs
assert len(installs) == 0
updates = installer.installer.updates
assert len(updates) == 0
removals = installer.installer.removals
assert len(removals) == 1
def test_run_install_duplicate_dependencies_different_constraints_with_lock_update(
installer, locker, repo, package, installed
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {
"B": [
{"version": "^1.0", "python": "<2.7"},
{"version": "^2.0", "python": ">=2.7"},
]
},
},
{
"name": "B",
"version": "1.0",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"C": "1.2"},
"requirements": {"python": "<2.7"},
},
{
"name": "B",
"version": "2.0",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"C": "1.5"},
"requirements": {"python": ">=2.7"},
},
{
"name": "C",
"version": "1.2",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "C",
"version": "1.5",
"category": "dev",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": [], "B": [], "C": []},
},
}
)
package.add_dependency("A")
package_a = get_package("A", "1.1")
package_a.add_dependency("B", "^2.0")
package_b10 = get_package("B", "1.0")
package_b20 = get_package("B", "2.0")
package_b10.add_dependency("C", "1.2")
package_b20.add_dependency("C", "1.5")
package_c12 = get_package("C", "1.2")
package_c15 = get_package("C", "1.5")
repo.add_package(package_a)
repo.add_package(package_b10)
repo.add_package(package_b20)
repo.add_package(package_c12)
repo.add_package(package_c15)
installed.add_package(get_package("A", "1.0"))
installer.update(True)
installer.whitelist(["A"])
installer.run()
expected = fixture("with-duplicate-dependencies-update")
assert locker.written_data == expected
installs = installer.installer.installs
assert len(installs) == 2
updates = installer.installer.updates
assert len(updates) == 1
removals = installer.installer.removals
assert len(removals) == 0
@pytest.mark.skip(
"This is not working at the moment due to limitations in the resolver"
)
def test_installer_test_solver_finds_compatible_package_for_dependency_python_not_fully_compatible_with_package_python(
installer, locker, repo, package, installed
):
package.python_versions = "~2.7 || ^3.4"
package.add_dependency("A", {"version": "^1.0", "python": "^3.5"})
package_a101 = get_package("A", "1.0.1")
package_a101.python_versions = ">=3.6"
package_a100 = get_package("A", "1.0.0")
package_a100.python_versions = ">=3.5"
repo.add_package(package_a100)
repo.add_package(package_a101)
installer.run()
expected = fixture("with-conditional-dependency")
assert locker.written_data == expected
installs = installer.installer.installs
if sys.version_info >= (3, 5, 0):
assert len(installs) == 1
else:
assert len(installs) == 0
def test_installer_required_extras_should_not_be_removed_when_updating_single_dependency(
installer, locker, repo, package, installed, env, pool, config
):
package.add_dependency("A", {"version": "^1.0"})
package_a = get_package("A", "1.0.0")
package_a.add_dependency("B", {"version": "^1.0", "extras": ["foo"]})
package_b = get_package("B", "1.0.0")
package_b.add_dependency("C", {"version": "^1.0", "optional": True})
package_b.extras = {"foo": [get_dependency("C")]}
package_c = get_package("C", "1.0.0")
package_d = get_package("D", "1.0.0")
repo.add_package(package_a)
repo.add_package(package_b)
repo.add_package(package_c)
repo.add_package(package_d)
installer.update(True)
installer.run()
assert len(installer.installer.installs) == 3
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
package.add_dependency("D", "^1.0")
locker.locked(True)
locker.mock_lock_data(locker.written_data)
installed.add_package(package_a)
installed.add_package(package_b)
installed.add_package(package_c)
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
installer.update(True)
installer.whitelist(["D"])
installer.run()
assert len(installer.installer.installs) == 1
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
def test_installer_required_extras_should_not_be_removed_when_updating_single_dependency_pypi_repository(
locker, repo, package, installed, env, mocker, config
):
mocker.patch("sys.platform", "darwin")
pool = Pool()
pool.add_repository(MockRepository())
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
package.add_dependency("poetry", {"version": "^0.12.0"})
installer.update(True)
installer.run()
assert len(installer.installer.installs) == 3
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
package.add_dependency("pytest", "^3.5")
locker.locked(True)
locker.mock_lock_data(locker.written_data)
for pkg in installer.installer.installs:
installed.add_package(pkg)
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
installer.update(True)
installer.whitelist(["pytest"])
installer.run()
assert len(installer.installer.installs) == 6 if not PY2 else 7
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
def test_installer_required_extras_should_be_installed(
locker, repo, package, installed, env, config
):
pool = Pool()
pool.add_repository(MockRepository())
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
package.add_dependency(
"cachecontrol", {"version": "^0.12.5", "extras": ["filecache"]}
)
installer.update(True)
installer.run()
assert len(installer.installer.installs) == 2
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
locker.locked(True)
locker.mock_lock_data(locker.written_data)
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
installer.update(True)
installer.run()
assert len(installer.installer.installs) == 2
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
def test_update_multiple_times_with_split_dependencies_is_idempotent(
installer, locker, repo, package
):
locker.locked(True)
locker.mock_lock_data(
{
"package": [
{
"name": "A",
"version": "1.0",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
"dependencies": {"B": ">=1.0"},
},
{
"name": "B",
"version": "1.0.1",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*",
"checksum": [],
"dependencies": {},
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"A": [], "B": []},
},
}
)
package.python_versions = "~2.7 || ^3.4"
package.add_dependency("A", "^1.0")
a10 = get_package("A", "1.0")
a11 = get_package("A", "1.1")
a11.add_dependency("B", ">=1.0.1")
a11.add_dependency("C", {"version": "^1.0", "python": "~2.7"})
a11.add_dependency("C", {"version": "^2.0", "python": "^3.4"})
b101 = get_package("B", "1.0.1")
b110 = get_package("B", "1.1.0")
repo.add_package(a10)
repo.add_package(a11)
repo.add_package(b101)
repo.add_package(b110)
repo.add_package(get_package("C", "1.0"))
repo.add_package(get_package("C", "2.0"))
expected = fixture("with-multiple-updates")
installer.update(True)
installer.run()
assert expected == locker.written_data
locker.mock_lock_data(locker.written_data)
installer.update(True)
installer.run()
assert expected == locker.written_data
locker.mock_lock_data(locker.written_data)
installer.update(True)
installer.run()
assert expected == locker.written_data
def test_installer_can_install_dependencies_from_forced_source(
locker, package, installed, env, config
):
package.python_versions = "^3.7"
package.add_dependency("tomlkit", {"version": "^0.5", "source": "legacy"})
pool = Pool()
pool.add_repository(MockLegacyRepository())
pool.add_repository(MockRepository())
installer = Installer(
NullIO(), env, package, locker, pool, config, installed=installed
)
installer.update(True)
installer.run()
assert len(installer.installer.installs) == 1
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 0
def test_run_installs_with_url_file(installer, locker, repo, package):
url = "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
package.add_dependency("demo", {"url": url})
repo.add_package(get_package("pendulum", "1.4.4"))
installer.run()
expected = fixture("with-url-dependency")
assert locker.written_data == expected
assert len(installer.installer.installs) == 2
def test_installer_uses_prereleases_if_they_are_compatible(
installer, locker, package, repo
):
package.python_versions = "~2.7 || ^3.4"
package.add_dependency(
"prerelease", {"git": "https://github.com/demo/prerelease.git"}
)
package_b = get_package("b", "2.0.0")
package_b.add_dependency("prerelease", ">=0.19")
repo.add_package(package_b)
installer.run()
del installer.installer.installs[:]
locker.locked(True)
locker.mock_lock_data(locker.written_data)
package.add_dependency("b", "^2.0.0")
installer.whitelist(["b"])
installer.update(True)
installer.run()
assert len(installer.installer.installs) == 2
def test_installer_can_handle_old_lock_files(
installer, locker, package, repo, installed, config
):
pool = Pool()
pool.add_repository(MockRepository())
package.add_dependency("pytest", "^3.5", category="dev")
locker.locked()
locker.mock_lock_data(fixture("old-lock"))
installer = Installer(
NullIO(), MockEnv(), package, locker, pool, config, installed=installed
)
installer.run()
assert 6 == len(installer.installer.installs)
installer = Installer(
NullIO(),
MockEnv(version_info=(2, 7, 18)),
package,
locker,
pool,
config,
installed=installed,
)
installer.run()
# funcsigs will be added
assert 7 == len(installer.installer.installs)
installer = Installer(
NullIO(),
MockEnv(version_info=(2, 7, 18), platform="win32"),
package,
locker,
pool,
config,
installed=installed,
)
installer.run()
# colorama will be added
assert 8 == len(installer.installer.installs)
<!DOCTYPE html>
<html>
<head>
<title>Links for pytest</title>
</head>
<body>
<h1>Links for pytest</h1><a href="https://files.pythonhosted.org/packages/ed/96/271c93f75212c06e2a7ec3e2fa8a9c90acee0a4838dc05bf379ea09aae31/pytest-3.5.0-py2.py3-none-any.whl#sha256=6266f87ab64692112e5477eba395cfedda53b1933ccd29478e671e73b420c19c" data-requires-python="&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*">pytest-3.5.0-py2.py3-none-any.whl</a><br/>
<a href="https://files.pythonhosted.org/packages/2d/56/6019153cdd743300c5688ab3b07702355283e53c83fbf922242c053ffb7b/pytest-3.5.0.tar.gz#sha256=fae491d1874f199537fd5872b5e1f0e74a009b979df9d53d1553fd03da1703e1" data-requires-python="&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*">pytest-3.5.0.tar.gz</a><br/>
</body>
</html>
<!--SERIAL 7198641-->
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
</head> </head>
<body> <body>
<h1>Links for python-language-server</h1> <h1>Links for python-language-server</h1>
<a href="https://files.pythonhosted.org/packages/5c/ed/d6557f70daaaab6ee5cd2f8ccf7bedd63081e522e38679c03840e1acc114/PyYAML-3.13-cp37-cp37m-win32.whl#sha256=e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531">PyYAML-3.13-cp37-cp37m-win32.whl</a><br/>
<a href="https://files.pythonhosted.org/packages/9e/a3/1d13970c3f36777c583f136c136f804d70f500168edc1edea6daa7200769/PyYAML-3.13.tar.gz#sha256=3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf">PyYAML-3.13.tar.gz</a><br/> <a href="https://files.pythonhosted.org/packages/9e/a3/1d13970c3f36777c583f136c136f804d70f500168edc1edea6daa7200769/PyYAML-3.13.tar.gz#sha256=3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf">PyYAML-3.13.tar.gz</a><br/>
<a href="https://files.pythonhosted.org/packages/0f/9d/f98ed0a460dc540f720bbe5c6e076f025595cdfa3e318fad27165db13cf9/PyYAML-4.2b2.tar.gz#sha256=406b717f739e2d00c49873068b71f5454c2420157db51b082d4d2beb17ffffb6">PyYAML-4.2b2.tar.gz</a><br/> <a href="https://files.pythonhosted.org/packages/0f/9d/f98ed0a460dc540f720bbe5c6e076f025595cdfa3e318fad27165db13cf9/PyYAML-4.2b2.tar.gz#sha256=406b717f739e2d00c49873068b71f5454c2420157db51b082d4d2beb17ffffb6">PyYAML-4.2b2.tar.gz</a><br/>
</body> </body>
......
...@@ -7,5 +7,5 @@ envlist = py27, py35, py36, py37, py38 ...@@ -7,5 +7,5 @@ envlist = py27, py35, py36, py37, py38
whitelist_externals = poetry whitelist_externals = poetry
skip_install = true skip_install = true
commands = commands =
poetry install -vvv poetry install -vv
poetry run pytest {posargs} tests/ poetry run pytest {posargs} tests/
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