From 134aef1b8e5d335311feeab6de6415c934d157da Mon Sep 17 00:00:00 2001 From: Tom Milligan Date: Thu, 10 Oct 2019 14:07:14 +0100 Subject: [PATCH 01/18] export: fix exporting extras sub-dependencies (#1294) --- poetry/installation/installer.py | 30 ++++--------------- poetry/utils/exporter.py | 23 +++++++-------- poetry/utils/extras.py | 48 ++++++++++++++++++++++++++++++ tests/utils/test_exporter.py | 14 +++++++-- tests/utils/test_extras.py | 50 ++++++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 39 deletions(-) create mode 100644 poetry/utils/extras.py create mode 100644 tests/utils/test_extras.py diff --git a/poetry/installation/installer.py b/poetry/installation/installer.py index afc7b668602..704cc3e133b 100644 --- a/poetry/installation/installer.py +++ b/poetry/installation/installer.py @@ -17,6 +17,7 @@ from poetry.repositories.installed_repository import InstalledRepository from poetry.semver import parse_constraint from poetry.utils.helpers import canonicalize_name +from poetry.utils.extras import get_extra_package_names from .base_installer import BaseInstaller from .pip_installer import PipInstaller @@ -399,7 +400,7 @@ def _get_operations_from_lock( installed_repo = self._installed_repository ops = [] - extra_packages = [p.name for p in self._get_extra_packages(locked_repository)] + extra_packages = self._get_extra_packages(locked_repository) for locked in locked_repository.packages: is_installed = False for installed in installed_repo.packages: @@ -429,7 +430,7 @@ def _get_operations_from_lock( def _filter_operations( self, ops, repo ): # type: (List[Operation], Repository) -> None - extra_packages = [p.name for p in self._get_extra_packages(repo)] + extra_packages = self._get_extra_packages(repo) for op in ops: if isinstance(op, Update): package = op.target_package @@ -468,9 +469,9 @@ def _filter_operations( if package.category == "dev" and not self.is_dev_mode(): op.skip("Dev dependencies not requested") - def _get_extra_packages(self, repo): + def _get_extra_packages(self, repo): # type: (Repository) -> List[str] """ - Returns all packages required by extras. + Returns all package names required by extras. Maybe we just let the solver handle it? """ @@ -479,26 +480,7 @@ def _get_extra_packages(self, repo): else: extras = self._locker.lock_data.get("extras", {}) - extra_packages = [] - for extra_name, packages in extras.items(): - if extra_name not in self._extras: - continue - - extra_packages += [Dependency(p, "*") for p in packages] - - def _extra_packages(packages): - pkgs = [] - for package in packages: - for pkg in repo.packages: - if pkg.name == package.name: - pkgs.append(package) - pkgs += _extra_packages(pkg.requires) - - break - - return pkgs - - return _extra_packages(extra_packages) + return list(get_extra_package_names(repo.packages, extras, self._extras)) def _get_installer(self): # type: () -> BaseInstaller return PipInstaller(self._env, self._io, self._pool) diff --git a/poetry/utils/exporter.py b/poetry/utils/exporter.py index 3c05a4da864..94b3c797111 100644 --- a/poetry/utils/exporter.py +++ b/poetry/utils/exporter.py @@ -9,6 +9,7 @@ from poetry.poetry import Poetry from poetry.utils._compat import Path from poetry.utils._compat import decode +from poetry.utils.extras import get_extra_package_names class Exporter(object): @@ -55,20 +56,16 @@ def _export_requirements_txt( ): # type: (Path, Union[IO, str], bool, bool, bool) -> None indexes = [] content = "" + packages = self._poetry.locker.locked_repository(dev).packages - # Generate a list of package names we have opted into via `extras` - extras_set = frozenset(extras or ()) - extra_package_names = set() - if extras: - for extra_name, extra_packages in self._poetry.locker.lock_data.get( - "extras", {} - ).items(): - if extra_name in extras_set: - extra_package_names.update(extra_packages) - - for package in sorted( - self._poetry.locker.locked_repository(dev).packages, key=lambda p: p.name - ): + # Build a set of all packages required by our selected extras + extra_package_names = set( + get_extra_package_names( + packages, self._poetry.locker.lock_data.get("extras", {}), extras or () + ) + ) + + for package in sorted(packages, key=lambda p: p.name): # If a package is optional and we haven't opted in to it, continue if package.optional and package.name not in extra_package_names: continue diff --git a/poetry/utils/extras.py b/poetry/utils/extras.py new file mode 100644 index 00000000000..7a1b24ad42e --- /dev/null +++ b/poetry/utils/extras.py @@ -0,0 +1,48 @@ +from typing import Iterator, Mapping, Sequence + +from poetry.packages import Package +from poetry.utils.helpers import canonicalize_name + + +def get_extra_package_names( + packages, # type: Sequence[Package] + extras, # type: Mapping[str, Collection[str]] + extra_names, # type: Sequence[str] +): # type: (...) -> Iterator[str] + """ + Returns all package names required by the given extras. + + :param packages: A collection of packages, such as from Repository.packages + :param extras: A mapping of `extras` names to lists of package names, as defined + in the `extras` section of `poetry.lock`. + :param extra_names: A list of strings specifying names of extra groups to resolve. + """ + if not extra_names: + return [] + + # lookup for packages by name, faster than looping over packages repeatedly + packages_by_name = {package.name: package for package in packages} + + # get and flatten names of packages we've opted into as extras + extra_package_names = [ + canonicalize_name(extra_package_name) + for extra_name in extra_names + for extra_package_name in extras.get(extra_name, ()) + ] + + def _extra_packages(package_names): + """Recursively find dependencies for packages names""" + # for each extra pacakge name + for package_name in package_names: + # Find the actual Package object. A missing key indicates an implicit + # dependency (like setuptools), which should be ignored + package = packages_by_name.get(canonicalize_name(package_name)) + if package: + yield package.name + # Recurse for dependencies + for dependency_package_name in _extra_packages( + dependency.name for dependency in package.requires + ): + yield dependency_package_name + + return _extra_packages(extra_package_names) diff --git a/tests/utils/test_exporter.py b/tests/utils/test_exporter.py index ad5be296bf4..ccb8ecf248b 100644 --- a/tests/utils/test_exporter.py +++ b/tests/utils/test_exporter.py @@ -367,7 +367,15 @@ def test_exporter_exports_requirements_txt_with_optional_packages_if_opted_in( { "name": "bar", "version": "4.5.6", - "category": "dev", + "category": "main", + "optional": True, + "python-versions": "*", + "dependencies": {"spam": ">=0.1"}, + }, + { + "name": "spam", + "version": "0.1.0", + "category": "main", "optional": True, "python-versions": "*", }, @@ -375,7 +383,7 @@ def test_exporter_exports_requirements_txt_with_optional_packages_if_opted_in( "metadata": { "python-versions": "*", "content-hash": "123456789", - "hashes": {"foo": ["12345"], "bar": ["67890"]}, + "hashes": {"foo": ["12345"], "bar": ["67890"], "spam": ["abcde"]}, }, "extras": {"feature_bar": ["bar"]}, } @@ -398,6 +406,8 @@ def test_exporter_exports_requirements_txt_with_optional_packages_if_opted_in( --hash=sha256:67890 foo==1.2.3 \\ --hash=sha256:12345 +spam==0.1.0 \\ + --hash=sha256:abcde """ assert expected == content diff --git a/tests/utils/test_extras.py b/tests/utils/test_extras.py new file mode 100644 index 00000000000..4bb03d19035 --- /dev/null +++ b/tests/utils/test_extras.py @@ -0,0 +1,50 @@ +import pytest + +from poetry.utils.extras import get_extra_package_names + +from poetry.packages import Package + +_PACKAGE_FOO = Package("foo", "0.1.0") +_PACKAGE_SPAM = Package("spam", "0.2.0") +_PACKAGE_BAR = Package("bar", "0.3.0") +_PACKAGE_BAR.add_dependency("foo") + + +@pytest.mark.parametrize( + "packages,extras,extra_names,expected_extra_package_names", + [ + # Empty edge case + ([], {}, [], []), + # Selecting no extras is fine + ([_PACKAGE_FOO], {}, [], []), + # An empty extras group should return an empty list + ([_PACKAGE_FOO], {"group0": []}, ["group0"], []), + # Selecting an extras group should return the contained packages + ( + [_PACKAGE_FOO, _PACKAGE_SPAM, _PACKAGE_BAR], + {"group0": ["foo"]}, + ["group0"], + ["foo"], + ), + # If a package has dependencies, we should also get their names + ( + [_PACKAGE_FOO, _PACKAGE_SPAM, _PACKAGE_BAR], + {"group0": ["bar"], "group1": ["spam"]}, + ["group0"], + ["bar", "foo"], + ), + # Selecting multpile extras should get us the union of all package names + ( + [_PACKAGE_FOO, _PACKAGE_SPAM, _PACKAGE_BAR], + {"group0": ["bar"], "group1": ["spam"]}, + ["group0", "group1"], + ["bar", "foo", "spam"], + ), + ], +) +def test_get_extra_package_names( + packages, extras, extra_names, expected_extra_package_names +): + assert expected_extra_package_names == list( + get_extra_package_names(packages, extras, extra_names) + ) From 2966ab177206e8f8f888f6050a5f39b18cbda16b Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 10 Oct 2019 10:36:51 -0400 Subject: [PATCH 02/18] Support POETRY_HOME for install (#794) Allow the `POETRY_HOME` environment variable to be passed during installation to change the default installation directory of `~/.poetry`: ``` POETRY_HOME=/etc/poetry python get-poetry.py ``` --- CHANGELOG.md | 1 + docs/docs/index.md | 6 ++++++ get-poetry.py | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e4f35cf2ee..fc28d7e1a93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Added a `env use` command to control the Python version used by the project. - Added a `env list` command to list the virtualenvs associated with the current project. - Added a `env remove` command to delete virtualenvs associated with the current project. +- Added support for `POETRY_HOME` declaration within `get-poetry.py`. - Added support for declaring a specific source for dependencies. - Added support for disabling PyPI and making another repository the default one. - Added support for declaring private repositories as secondary. diff --git a/docs/docs/index.md b/docs/docs/index.md index 2d4d12a0bf6..66fb0ade021 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -46,6 +46,12 @@ python get-poetry.py --uninstall POETRY_UNINSTALL=1 python get-poetry.py ``` +By default, Poetry is installed into the user's platform-specific home directory. If you wish to change this, you may define the `POETRY_HOME` environment variable: + +```bash +POETRY_HOME=/etc/poetry python get-poetry.py +``` + If you want to install prerelease versions, you can do so by passing `--preview` to `get-poetry.py` or by using the `POETRY_PREVIEW` environment variable: diff --git a/get-poetry.py b/get-poetry.py index e369c866d32..e6cbe6db8fb 100644 --- a/get-poetry.py +++ b/get-poetry.py @@ -187,7 +187,7 @@ def expanduser(path): HOME = expanduser("~") -POETRY_HOME = os.path.join(HOME, ".poetry") +POETRY_HOME = os.environ.get("POETRY_HOME") or os.path.join(HOME, ".poetry") POETRY_BIN = os.path.join(POETRY_HOME, "bin") POETRY_ENV = os.path.join(POETRY_HOME, "env") POETRY_LIB = os.path.join(POETRY_HOME, "lib") From 3622f8e458d7958d298b043b4bbefb39d6dc71c5 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Fri, 11 Oct 2019 15:40:53 +0200 Subject: [PATCH 03/18] * check if relative filename is in excluded file list (#1459) * * check if relative filename is in excluded file list * removed find_excluded_files() method from wheel.py * added test for excluding files in wheels * creating an own test data folder, for testing excluding files by pyproject.toml * use as_posix() to respect windows file path delimiters --- poetry/masonry/builders/wheel.py | 6 +-- .../default_with_excluded_data_toml/LICENSE | 20 +++++++++ .../README.rst | 2 + .../my_package/__init__.py | 0 .../my_package/data/data1.txt | 0 .../my_package/data/sub_data/data2.txt | 0 .../my_package/data/sub_data/data3.txt | 0 .../pyproject.toml | 41 +++++++++++++++++++ tests/masonry/builders/test_wheel.py | 15 +++++++ 9 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 tests/masonry/builders/fixtures/default_with_excluded_data_toml/LICENSE create mode 100644 tests/masonry/builders/fixtures/default_with_excluded_data_toml/README.rst create mode 100644 tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/__init__.py create mode 100644 tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/data/data1.txt create mode 100644 tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/data/sub_data/data2.txt create mode 100644 tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/data/sub_data/data3.txt create mode 100644 tests/masonry/builders/fixtures/default_with_excluded_data_toml/pyproject.toml diff --git a/poetry/masonry/builders/wheel.py b/poetry/masonry/builders/wheel.py index 71423fefeb6..9b7ce4834ef 100644 --- a/poetry/masonry/builders/wheel.py +++ b/poetry/masonry/builders/wheel.py @@ -153,7 +153,7 @@ def _copy_module(self, wheel): else: rel_file = file.relative_to(self._path) - if file in excluded: + if rel_file.as_posix() in excluded: continue if file.suffix == ".pyc": @@ -199,10 +199,6 @@ def _write_record(self, wheel): # RECORD itself is recorded with no hash or size f.write(self.dist_info + "/RECORD,,\n") - def find_excluded_files(self): # type: () -> Set - # Checking VCS - return set() - @property def dist_info(self): # type: () -> str return self.dist_info_name(self._package.name, self._meta.version) diff --git a/tests/masonry/builders/fixtures/default_with_excluded_data_toml/LICENSE b/tests/masonry/builders/fixtures/default_with_excluded_data_toml/LICENSE new file mode 100644 index 00000000000..44cf2b30e68 --- /dev/null +++ b/tests/masonry/builders/fixtures/default_with_excluded_data_toml/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2018 Sébastien Eustace + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/masonry/builders/fixtures/default_with_excluded_data_toml/README.rst b/tests/masonry/builders/fixtures/default_with_excluded_data_toml/README.rst new file mode 100644 index 00000000000..f7fe15470f9 --- /dev/null +++ b/tests/masonry/builders/fixtures/default_with_excluded_data_toml/README.rst @@ -0,0 +1,2 @@ +My Package +========== diff --git a/tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/__init__.py b/tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/data/data1.txt b/tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/data/data1.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/data/sub_data/data2.txt b/tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/data/sub_data/data2.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/data/sub_data/data3.txt b/tests/masonry/builders/fixtures/default_with_excluded_data_toml/my_package/data/sub_data/data3.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/default_with_excluded_data_toml/pyproject.toml b/tests/masonry/builders/fixtures/default_with_excluded_data_toml/pyproject.toml new file mode 100644 index 00000000000..80c284ecee1 --- /dev/null +++ b/tests/masonry/builders/fixtures/default_with_excluded_data_toml/pyproject.toml @@ -0,0 +1,41 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Sébastien Eustace " +] +license = "MIT" + +readme = "README.rst" + +exclude = ["my_package/data/data1.txt"] + +homepage = "https://poetry.eustace.io/" +repository = "https://github.com/sdispater/poetry" +documentation = "https://poetry.eustace.io/docs" + +keywords = ["packaging", "dependency", "poetry"] + +classifiers = [ + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules" +] + +# Requirements +[tool.poetry.dependencies] +python = "^3.6" +cleo = "^0.6" +cachy = { version = "^0.2.0", extras = ["msgpack"] } + +pendulum = { version = "^1.4", optional = true } + +[tool.poetry.dev-dependencies] +pytest = "~3.4" + +[tool.poetry.extras] +time = ["pendulum"] + +[tool.poetry.scripts] +my-script = "my_package:main" +my-2nd-script = "my_package:main2" diff --git a/tests/masonry/builders/test_wheel.py b/tests/masonry/builders/test_wheel.py index 6344a7f1435..7b5ec8611ea 100644 --- a/tests/masonry/builders/test_wheel.py +++ b/tests/masonry/builders/test_wheel.py @@ -62,6 +62,21 @@ def test_wheel_prerelease(): assert whl.exists() +def test_wheel_excluded_data(): + module_path = fixtures_dir / "default_with_excluded_data_toml" + WheelBuilder.make(Factory().create_poetry(module_path), NullEnv(), NullIO()) + + whl = module_path / "dist" / "my_package-1.2.3-py3-none-any.whl" + + assert whl.exists() + + with zipfile.ZipFile(str(whl)) as z: + assert "my_package/__init__.py" in z.namelist() + assert "my_package/data/sub_data/data2.txt" in z.namelist() + assert "my_package/data/sub_data/data3.txt" in z.namelist() + assert "my_package/data/data1.txt" not in z.namelist() + + def test_wheel_localversionlabel(): module_path = fixtures_dir / "localversionlabel" WheelBuilder.make(Factory().create_poetry(module_path), NullEnv(), NullIO()) From 3a39e5ac2afd65c17182c033b353bf4599088a8b Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sat, 19 Oct 2019 22:06:57 +0200 Subject: [PATCH 04/18] Exclude nested items (#784) (#1464) * This PR impliments the feature request #784. When a folder is explicit defined in `pyproject.toml` as excluded, all nested data, including subfolder, are excluded. It is no longer neccessary to use the glob `folder/**/*` * use `Path` instead of `os.path.join` to create string for globbing * try to fix linting error * create glob pattern string by concatenating and not using Path * using `os.path.isdir()`` for checking of explicit excluded name is a folder, because pathlib's `is_dir()` raises in exception under windows of name contains globing characters * Remove nested data when wildcards where used. Steps to do this are: 1. expand any wildcard used 2. if expanded path is a folder append **/* and expand again * fix linting * only glob a second time if path is dir * implement @sdispater 's suggestion for better readability * fix glob for windows? * On Windows, testing if a path with a glob is a directory will raise an OSError * pathlibs glob function doesn't return the correct case (https://bugs.python.org/issue26655). So switching back to glob.glob() * removing obsolete imports --- poetry/masonry/builders/builder.py | 17 +++++--- .../fixtures/exclude_nested_data_toml/LICENSE | 20 +++++++++ .../exclude_nested_data_toml/README.rst | 2 + .../my_package/__init__.py | 0 .../my_package/data/data1.txt | 0 .../my_package/data/sub_data/data2.txt | 0 .../my_package/data/sub_data/data3.txt | 0 .../my_package/puplic/item1/itemdata1.txt | 0 .../puplic/item1/subitem/subitemdata.txt | 0 .../my_package/puplic/item2/itemdata2.txt | 0 .../my_package/puplic/publicdata.txt | 0 .../exclude_nested_data_toml/pyproject.toml | 41 +++++++++++++++++++ tests/masonry/builders/test_sdist.py | 32 +++++++++++++++ tests/masonry/builders/test_wheel.py | 20 +++++++++ 14 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 tests/masonry/builders/fixtures/exclude_nested_data_toml/LICENSE create mode 100644 tests/masonry/builders/fixtures/exclude_nested_data_toml/README.rst create mode 100644 tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/__init__.py create mode 100644 tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/data/data1.txt create mode 100644 tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/data/sub_data/data2.txt create mode 100644 tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/data/sub_data/data3.txt create mode 100644 tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/item1/itemdata1.txt create mode 100644 tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/item1/subitem/subitemdata.txt create mode 100644 tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/item2/itemdata2.txt create mode 100644 tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/publicdata.txt create mode 100644 tests/masonry/builders/fixtures/exclude_nested_data_toml/pyproject.toml diff --git a/poetry/masonry/builders/builder.py b/poetry/masonry/builders/builder.py index ef38540743f..14ca2de7db6 100644 --- a/poetry/masonry/builders/builder.py +++ b/poetry/masonry/builders/builder.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -import os import re import shutil import tempfile - from collections import defaultdict from contextlib import contextmanager from typing import Set @@ -17,12 +15,10 @@ from poetry.utils._compat import lru_cache from poetry.utils._compat import to_str from poetry.vcs import get_vcs - from ..metadata import Metadata from ..utils.module import Module from ..utils.package_include import PackageInclude - AUTHOR_REGEX = re.compile(r"(?u)^(?P[- .,\w\d'’\"()]+) <(?P.+?)>$") METADATA_BASE = """\ @@ -34,7 +30,6 @@ class Builder(object): - AVAILABLE_PYTHONS = {"2", "2.7", "3", "3.4", "3.5", "3.6", "3.7"} format = None @@ -86,8 +81,18 @@ def find_excluded_files(self): # type: () -> Set[str] explicitely_excluded = set() for excluded_glob in self._package.exclude: + excluded_path = Path(self._path, excluded_glob) + + try: + is_dir = excluded_path.is_dir() + except OSError: + # On Windows, testing if a path with a glob is a directory will raise an OSError + is_dir = False + if is_dir: + excluded_glob = Path(excluded_glob, "**/*") + for excluded in glob( - os.path.join(self._path.as_posix(), str(excluded_glob)), recursive=True + Path(self._path, excluded_glob).as_posix(), recursive=True ): explicitely_excluded.add( Path(excluded).relative_to(self._path).as_posix() diff --git a/tests/masonry/builders/fixtures/exclude_nested_data_toml/LICENSE b/tests/masonry/builders/fixtures/exclude_nested_data_toml/LICENSE new file mode 100644 index 00000000000..44cf2b30e68 --- /dev/null +++ b/tests/masonry/builders/fixtures/exclude_nested_data_toml/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2018 Sébastien Eustace + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/masonry/builders/fixtures/exclude_nested_data_toml/README.rst b/tests/masonry/builders/fixtures/exclude_nested_data_toml/README.rst new file mode 100644 index 00000000000..f7fe15470f9 --- /dev/null +++ b/tests/masonry/builders/fixtures/exclude_nested_data_toml/README.rst @@ -0,0 +1,2 @@ +My Package +========== diff --git a/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/__init__.py b/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/data/data1.txt b/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/data/data1.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/data/sub_data/data2.txt b/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/data/sub_data/data2.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/data/sub_data/data3.txt b/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/data/sub_data/data3.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/item1/itemdata1.txt b/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/item1/itemdata1.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/item1/subitem/subitemdata.txt b/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/item1/subitem/subitemdata.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/item2/itemdata2.txt b/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/item2/itemdata2.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/publicdata.txt b/tests/masonry/builders/fixtures/exclude_nested_data_toml/my_package/puplic/publicdata.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/exclude_nested_data_toml/pyproject.toml b/tests/masonry/builders/fixtures/exclude_nested_data_toml/pyproject.toml new file mode 100644 index 00000000000..21efcba30eb --- /dev/null +++ b/tests/masonry/builders/fixtures/exclude_nested_data_toml/pyproject.toml @@ -0,0 +1,41 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Sébastien Eustace " +] +license = "MIT" + +readme = "README.rst" + +exclude = ["my_package/data/", "**/*/item*"] + +homepage = "https://poetry.eustace.io/" +repository = "https://github.com/sdispater/poetry" +documentation = "https://poetry.eustace.io/docs" + +keywords = ["packaging", "dependency", "poetry"] + +classifiers = [ + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules" +] + +# Requirements +[tool.poetry.dependencies] +python = "^3.6" +cleo = "^0.6" +cachy = { version = "^0.2.0", extras = ["msgpack"] } + +pendulum = { version = "^1.4", optional = true } + +[tool.poetry.dev-dependencies] +pytest = "~3.4" + +[tool.poetry.extras] +time = ["pendulum"] + +[tool.poetry.scripts] +my-script = "my_package:main" +my-2nd-script = "my_package:main2" diff --git a/tests/masonry/builders/test_sdist.py b/tests/masonry/builders/test_sdist.py index 281016a3653..b67dc5ee229 100644 --- a/tests/masonry/builders/test_sdist.py +++ b/tests/masonry/builders/test_sdist.py @@ -410,6 +410,38 @@ def test_default_with_excluded_data(mocker): assert "my-package-1.2.3/PKG-INFO" in names +def test_src_excluded_nested_data(): + module_path = fixtures_dir / "exclude_nested_data_toml" + poetry = Factory().create_poetry(module_path) + + builder = SdistBuilder(poetry, NullEnv(), NullIO()) + builder.build() + + sdist = module_path / "dist" / "my-package-1.2.3.tar.gz" + + assert sdist.exists() + + with tarfile.open(str(sdist), "r") as tar: + names = tar.getnames() + assert len(names) == len(set(names)) + assert "my-package-1.2.3/LICENSE" in names + assert "my-package-1.2.3/README.rst" in names + assert "my-package-1.2.3/pyproject.toml" in names + assert "my-package-1.2.3/setup.py" in names + assert "my-package-1.2.3/PKG-INFO" in names + assert "my-package-1.2.3/my_package/__init__.py" in names + assert "my-package-1.2.3/my_package/data/sub_data/data2.txt" not in names + assert "my-package-1.2.3/my_package/data/sub_data/data3.txt" not in names + assert "my-package-1.2.3/my_package/data/data1.txt" not in names + assert "my-package-1.2.3/my_package/puplic/publicdata.txt" in names + assert "my-package-1.2.3/my_package/public/item1/itemdata1.txt" not in names + assert ( + "my-package-1.2.3/my_package/public/item1/subitem/subitemdata.txt" + not in names + ) + assert "my-package-1.2.3/my_package/public/item2/itemdata2.txt" not in names + + def test_proper_python_requires_if_two_digits_precision_version_specified(): poetry = Factory().create_poetry(project("simple_version")) diff --git a/tests/masonry/builders/test_wheel.py b/tests/masonry/builders/test_wheel.py index 7b5ec8611ea..33c50a5d6a7 100644 --- a/tests/masonry/builders/test_wheel.py +++ b/tests/masonry/builders/test_wheel.py @@ -77,6 +77,26 @@ def test_wheel_excluded_data(): assert "my_package/data/data1.txt" not in z.namelist() +def test_wheel_excluded_nested_data(): + module_path = fixtures_dir / "exclude_nested_data_toml" + poetry = Factory().create_poetry(module_path) + WheelBuilder.make(poetry, NullEnv(), NullIO()) + + whl = module_path / "dist" / "my_package-1.2.3-py3-none-any.whl" + + assert whl.exists() + + with zipfile.ZipFile(str(whl)) as z: + assert "my_package/__init__.py" in z.namelist() + assert "my_package/data/sub_data/data2.txt" not in z.namelist() + assert "my_package/data/sub_data/data3.txt" not in z.namelist() + assert "my_package/data/data1.txt" not in z.namelist() + assert "my_package/puplic/publicdata.txt" in z.namelist() + assert "my_package/public/item1/itemdata1.txt" not in z.namelist() + assert "my_package/public/item1/subitem/subitemdata.txt" not in z.namelist() + assert "my_package/public/item2/itemdata2.txt" not in z.namelist() + + def test_wheel_localversionlabel(): module_path = fixtures_dir / "localversionlabel" WheelBuilder.make(Factory().create_poetry(module_path), NullEnv(), NullIO()) From c64459e6740f5ca6dc9f1171e8b705af2913a470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Wed, 30 Oct 2019 20:27:28 +0100 Subject: [PATCH 05/18] Update dependencies --- poetry.lock | 106 +++++++++++++++++++++++++++++++++++++++++++++++-- pyproject.toml | 2 +- 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index d3866ab2f9a..91271254590 100644 --- a/poetry.lock +++ b/poetry.lock @@ -47,13 +47,16 @@ marker = "python_version >= \"3.6\" and python_version < \"4.0\"" name = "black" optional = false python-versions = ">=3.6" -version = "19.3b0" +version = "19.10b0" [package.dependencies] appdirs = "*" attrs = ">=18.1.0" click = ">=6.5" +pathspec = ">=0.6,<1" +regex = "*" toml = ">=0.9.4" +typed-ast = ">=1.4.0" [package.extras] d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] @@ -653,6 +656,15 @@ six = "*" python = "<3.5" version = "*" +[[package]] +category = "dev" +description = "Utility library for gitignore style pattern matching of file paths." +marker = "python_version >= \"3.6\" and python_version < \"4.0\"" +name = "pathspec" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.6.0" + [[package]] category = "dev" description = "Backport of PEP 562." @@ -718,10 +730,36 @@ six = "*" toml = "*" virtualenv = ">=15.2" +[package.dependencies.importlib-resources] +python = "<3.7" +version = "*" + +[[package]] +category = "dev" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +name = "pre-commit" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "1.20.0" + +[package.dependencies] +"aspy.yaml" = "*" +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = "*" +six = "*" +toml = "*" +virtualenv = ">=15.2" + [package.dependencies.futures] python = "<3.2" version = "*" +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" + [package.dependencies.importlib-resources] python = "<3.7" version = "*" @@ -932,6 +970,15 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "5.1.2" +[[package]] +category = "dev" +description = "Alternative regular expression module, to replace re." +marker = "python_version >= \"3.6\" and python_version < \"4.0\"" +name = "regex" +optional = false +python-versions = "*" +version = "2019.08.19" + [[package]] category = "main" description = "Python HTTP for Humans." @@ -1121,6 +1168,15 @@ version = ">=0.12,<1" docs = ["sphinx (>=2.0.0,<3)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"] testing = ["freezegun (>=0.3.11,<1)", "pathlib2 (>=2.3.3,<3)", "pytest (>=4.0.0,<6)", "pytest-cov (>=2.5.1,<3)", "pytest-mock (>=1.10.0,<2)", "pytest-xdist (>=1.22.2,<2)", "pytest-randomly (>=1.2.3,<2)", "flaky (>=3.4.0,<4)", "psutil (>=5.6.1,<6)"] +[[package]] +category = "dev" +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" +optional = false +python-versions = "*" +version = "1.4.0" + [[package]] category = "main" description = "Type Hints for Python" @@ -1199,7 +1255,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["pathlib2", "contextlib2", "unittest2"] [metadata] -content-hash = "402f356e484839dbb4361c25f986325cc08f1d441d777a15950cb28ce71ac005" +content-hash = "0a019fe9f27e3e3fc226c506b4c9de219d7b9d021e998ce88563f7b9c945aa35" python-versions = "~2.7 || ^3.4" [metadata.files] @@ -1220,8 +1276,8 @@ attrs = [ {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, ] black = [ - {file = "black-19.3b0-py36-none-any.whl", hash = "sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf"}, - {file = "black-19.3b0.tar.gz", hash = "sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"}, + {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"}, + {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, ] cachecontrol = [ {file = "CacheControl-0.12.5.tar.gz", hash = "sha256:cef77effdf51b43178f6a2d3b787e3734f98ade253fa3187f3bb7315aaa42ff7"}, @@ -1245,6 +1301,8 @@ cffi = [ {file = "cffi-1.13.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:6fd58366747debfa5e6163ada468a90788411f10c92597d3b0a912d07e580c36"}, {file = "cffi-1.13.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:9c77564a51d4d914ed5af096cd9843d90c45b784b511723bd46a8a9d09cf16fc"}, {file = "cffi-1.13.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:728ec653964655d65408949b07f9b2219df78badd601d6c49e28d604efe40599"}, + {file = "cffi-1.13.1-cp34-cp34m-win32.whl", hash = "sha256:b8f09f21544b9899defb09afbdaeb200e6a87a2b8e604892940044cf94444644"}, + {file = "cffi-1.13.1-cp34-cp34m-win_amd64.whl", hash = "sha256:8a2bcae2258d00fcfc96a9bde4a6177bc4274fe033f79311c5dd3d3148c26518"}, {file = "cffi-1.13.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:a19089fa74ed19c4fe96502a291cfdb89223a9705b1d73b3005df4256976142e"}, {file = "cffi-1.13.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:e22a00c0c81ffcecaf07c2bfb3672fa372c50e2bd1024ffee0da191c1b27fc71"}, {file = "cffi-1.13.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:bb75ba21d5716abc41af16eac1145ab2e471deedde1f22c6f99bd9f995504df0"}, @@ -1526,6 +1584,9 @@ pathlib2 = [ {file = "pathlib2-2.3.5-py2.py3-none-any.whl", hash = "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db"}, {file = "pathlib2-2.3.5.tar.gz", hash = "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868"}, ] +pathspec = [ + {file = "pathspec-0.6.0.tar.gz", hash = "sha256:e285ccc8b0785beadd4c18e5708b12bb8fcf529a1e61215b3feff1d1e559ea5c"}, +] pep562 = [ {file = "pep562-1.0-py2.py3-none-any.whl", hash = "sha256:d2a48b178ebf5f8dd31709cc26a19808ef794561fa2fe50ea01ea2bad4d667ef"}, {file = "pep562-1.0.tar.gz", hash = "sha256:58cb1cc9ee63d93e62b4905a50357618d526d289919814bea1f0da8f53b79395"}, @@ -1545,6 +1606,8 @@ pluggy = [ pre-commit = [ {file = "pre_commit-1.18.3-py2.py3-none-any.whl", hash = "sha256:fa78ff96e8e9ac94c748388597693f18b041a181c94a4f039ad20f45287ba44a"}, {file = "pre_commit-1.18.3.tar.gz", hash = "sha256:1d3c0587bda7c4e537a46c27f2c84aa006acc18facf9970bf947df596ce91f3f"}, + {file = "pre_commit-1.20.0-py2.py3-none-any.whl", hash = "sha256:c2e4810d2d3102d354947907514a78c5d30424d299dc0fe48f5aa049826e9b50"}, + {file = "pre_commit-1.20.0.tar.gz", hash = "sha256:9f152687127ec90642a2cc3e4d9e1e6240c4eb153615cb02aa1ad41d331cbb6e"}, ] ptyprocess = [ {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, @@ -1619,6 +1682,19 @@ pyyaml = [ {file = "PyYAML-5.1.2-cp38-cp38m-win_amd64.whl", hash = "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681"}, {file = "PyYAML-5.1.2.tar.gz", hash = "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4"}, ] +regex = [ + {file = "regex-2019.08.19-cp27-none-win32.whl", hash = "sha256:1e9f9bc44ca195baf0040b1938e6801d2f3409661c15fe57f8164c678cfc663f"}, + {file = "regex-2019.08.19-cp27-none-win_amd64.whl", hash = "sha256:93f6c9da57e704e128d90736430c5c59dd733327882b371b0cae8833106c2a21"}, + {file = "regex-2019.08.19-cp35-none-win32.whl", hash = "sha256:c5c8999b3a341b21ac2c6ec704cfcccbc50f1fedd61b6a8ee915ca7fd4b0a557"}, + {file = "regex-2019.08.19-cp35-none-win_amd64.whl", hash = "sha256:f2b37b5b2c2a9d56d9e88efef200ec09c36c7f323f9d58d0b985a90923df386d"}, + {file = "regex-2019.08.19-cp36-none-win32.whl", hash = "sha256:d96479257e8e4d1d7800adb26bf9c5ca5bab1648a1eddcac84d107b73dc68327"}, + {file = "regex-2019.08.19-cp36-none-win_amd64.whl", hash = "sha256:a46f27d267665016acb3ec8c6046ec5eae8cf80befe85ba47f43c6f5ec636dcd"}, + {file = "regex-2019.08.19-cp37-none-win32.whl", hash = "sha256:d4d1829cf97632673aa49f378b0a2c3925acd795148c5ace8ef854217abbee89"}, + {file = "regex-2019.08.19-cp37-none-win_amd64.whl", hash = "sha256:fe765b809a1f7ce642c2edeee351e7ebd84391640031ba4b60af8d91a9045890"}, + {file = "regex-2019.08.19-cp38-none-win32.whl", hash = "sha256:f20f4912daf443220436759858f96fefbfc6c6ba9e67835fd6e4e9b73582791a"}, + {file = "regex-2019.08.19-cp38-none-win_amd64.whl", hash = "sha256:835ccdcdc612821edf132c20aef3eaaecfb884c9454fdc480d5887562594ac61"}, + {file = "regex-2019.08.19.tar.gz", hash = "sha256:587b62d48ca359d2d4f02d486f1f0aa9a20fbaf23a9d4198c4bed72ab2f6c849"}, +] requests = [ {file = "requests-2.21.0-py2.py3-none-any.whl", hash = "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"}, {file = "requests-2.21.0.tar.gz", hash = "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e"}, @@ -1691,6 +1767,28 @@ tox = [ {file = "tox-3.14.0-py2.py3-none-any.whl", hash = "sha256:0bc216b6a2e6afe764476b4a07edf2c1dab99ed82bb146a1130b2e828f5bff5e"}, {file = "tox-3.14.0.tar.gz", hash = "sha256:c4f6b319c20ba4913dbfe71ebfd14ff95d1853c4231493608182f66e566ecfe1"}, ] +typed-ast = [ + {file = "typed_ast-1.4.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e"}, + {file = "typed_ast-1.4.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b"}, + {file = "typed_ast-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4"}, + {file = "typed_ast-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"}, + {file = "typed_ast-1.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631"}, + {file = "typed_ast-1.4.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233"}, + {file = "typed_ast-1.4.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1"}, + {file = "typed_ast-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a"}, + {file = "typed_ast-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c"}, + {file = "typed_ast-1.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a"}, + {file = "typed_ast-1.4.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e"}, + {file = "typed_ast-1.4.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d"}, + {file = "typed_ast-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36"}, + {file = "typed_ast-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0"}, + {file = "typed_ast-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66"}, + {file = "typed_ast-1.4.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2"}, + {file = "typed_ast-1.4.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47"}, + {file = "typed_ast-1.4.0-cp38-cp38-win32.whl", hash = "sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161"}, + {file = "typed_ast-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e"}, + {file = "typed_ast-1.4.0.tar.gz", hash = "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34"}, +] typing = [ {file = "typing-3.7.4.1-py2-none-any.whl", hash = "sha256:c8cabb5ab8945cd2f54917be357d134db9cc1eb039e59d1606dc1e60cb1d9d36"}, {file = "typing-3.7.4.1-py3-none-any.whl", hash = "sha256:f38d83c5a7a7086543a0f649564d661859c5146a85775ab90c0d2f93ffaa9714"}, diff --git a/pyproject.toml b/pyproject.toml index 3d873178725..b7d4fee2c2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,7 @@ pymdown-extensions = "^6.0" pygments = "^2.2" pytest-mock = "^1.9" pygments-github-lexers = "^0.0.5" -black = { version = "^19.3b0", python = "^3.6" } +black = { version = "^19.10b0", python = "^3.6" } pre-commit = "^1.10" tox = "^3.0" pytest-sugar = "^0.9.2" From 380ac4c260bbcbcd7fc399425860e8719e6a8cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Wed, 30 Oct 2019 21:20:41 +0100 Subject: [PATCH 06/18] Deprecate allows-prereleases in favor of allow-prereleases for consistency --- poetry/console/commands/add.py | 2 +- poetry/console/config/application_config.py | 7 ++++--- poetry/console/logging/io_handler.py | 2 +- poetry/factory.py | 11 +++++++++++ poetry/json/schemas/poetry-schema.json | 8 ++++++++ poetry/packages/package.py | 17 ++++++++++++++++- tests/console/commands/test_check.py | 1 + tests/fixtures/invalid_pyproject/pyproject.toml | 1 + tests/puzzle/test_solver.py | 2 +- 9 files changed, 44 insertions(+), 7 deletions(-) diff --git a/poetry/console/commands/add.py b/poetry/console/commands/add.py index 3fcc8a075f6..276369cffff 100644 --- a/poetry/console/commands/add.py +++ b/poetry/console/commands/add.py @@ -105,7 +105,7 @@ def handle(self): constraint["optional"] = True if self.option("allow-prereleases"): - constraint["allows-prereleases"] = True + constraint["allow-prereleases"] = True if self.option("extras"): extras = [] diff --git a/poetry/console/config/application_config.py b/poetry/console/config/application_config.py index f71a75c9c5f..b28a7f40c36 100644 --- a/poetry/console/config/application_config.py +++ b/poetry/console/config/application_config.py @@ -46,13 +46,14 @@ def register_command_loggers( io = event.io - if not command.loggers: - return + loggers = ["poetry.packages.package"] + + loggers += command.loggers handler = IOHandler(io) handler.setFormatter(IOFormatter()) - for logger in command.loggers: + for logger in loggers: logger = logging.getLogger(logger) logger.handlers = [handler] diff --git a/poetry/console/logging/io_handler.py b/poetry/console/logging/io_handler.py index fb869129a35..3a8f886260f 100644 --- a/poetry/console/logging/io_handler.py +++ b/poetry/console/logging/io_handler.py @@ -19,7 +19,7 @@ def emit(self, record): level = record.levelname.lower() err = level in ("warning", "error", "exception", "critical") if err: - self._io.error(msg, newline=True) + self._io.error_line(msg) else: self._io.write_line(msg) except Exception: diff --git a/poetry/factory.py b/poetry/factory.py index 2c95743bbcd..93361f24dd0 100644 --- a/poetry/factory.py +++ b/poetry/factory.py @@ -290,6 +290,17 @@ def validate( "Consider specifying a more explicit one." ) + for name, constraint in config["dependencies"].items(): + if not isinstance(constraint, dict): + continue + + if "allows-prereleases" in constraint: + result["warnings"].append( + 'The "{}" dependency specifies ' + 'the "allows-prereleases" property, which is deprecated. ' + 'Use "allow-preleases" instead.'.format(name) + ) + # Checking for scripts with extras if "scripts" in config: scripts = config["scripts"] diff --git a/poetry/json/schemas/poetry-schema.json b/poetry/json/schemas/poetry-schema.json index c02c93aff79..10aff39e502 100644 --- a/poetry/json/schemas/poetry-schema.json +++ b/poetry/json/schemas/poetry-schema.json @@ -247,6 +247,10 @@ "type": "string", "description": "The PEP 508 compliant environment markers for which the dependency should be installed." }, + "allow-prereleases": { + "type": "boolean", + "description": "Whether the dependency allows prereleases or not." + }, "allows-prereleases": { "type": "boolean", "description": "Whether the dependency allows prereleases or not." @@ -304,6 +308,10 @@ "type": "string", "description": "The PEP 508 compliant environment markers for which the dependency should be installed." }, + "allow-prereleases": { + "type": "boolean", + "description": "Whether the dependency allows prereleases or not." + }, "allows-prereleases": { "type": "boolean", "description": "Whether the dependency allows prereleases or not." diff --git a/poetry/packages/package.py b/poetry/packages/package.py index 562788a2de4..b42aec2ef37 100644 --- a/poetry/packages/package.py +++ b/poetry/packages/package.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- import copy +import logging import re from contextlib import contextmanager from typing import Union +from warnings import warn from poetry.semver import Version from poetry.semver import parse_constraint @@ -24,6 +26,8 @@ AUTHOR_REGEX = re.compile(r"(?u)^(?P[- .,\w\d'’\"()]+)(?: <(?P.+?)>)?$") +logger = logging.getLogger(__name__) + class Package(object): @@ -270,7 +274,18 @@ def add_dependency( python_versions = constraint.get("python") platform = constraint.get("platform") markers = constraint.get("markers") - allows_prereleases = constraint.get("allows-prereleases", False) + if "allows-prereleases" in constraint: + message = ( + 'The "{}" dependency specifies ' + 'the "allows-prereleases" property, which is deprecated. ' + 'Use "allow-preleases" instead.'.format(name) + ) + warn(message, DeprecationWarning) + logger.warning(message) + + allows_prereleases = constraint.get( + "allow-prereleases", constraint.get("allows-prereleases", False) + ) if "git" in constraint: # VCS dependency diff --git a/tests/console/commands/test_check.py b/tests/console/commands/test_check.py index bfc4ce9d0f2..74082cc37d4 100644 --- a/tests/console/commands/test_check.py +++ b/tests/console/commands/test_check.py @@ -42,6 +42,7 @@ def test_check_invalid(app, mocker): Error: 'description' is a required property Error: INVALID is not a valid license Warning: A wildcard Python dependency is ambiguous. Consider specifying a more explicit one. +Warning: The "pendulum" dependency specifies the "allows-prereleases" property, which is deprecated. Use "allow-preleases" instead. """ assert expected == tester.io.fetch_output() diff --git a/tests/fixtures/invalid_pyproject/pyproject.toml b/tests/fixtures/invalid_pyproject/pyproject.toml index d5dc17d38f5..06a8e27dae8 100644 --- a/tests/fixtures/invalid_pyproject/pyproject.toml +++ b/tests/fixtures/invalid_pyproject/pyproject.toml @@ -8,3 +8,4 @@ license = "INVALID" [tool.poetry.dependencies] python = "*" +pendulum = {"version" = "^2.0.5", allows-prereleases = true} diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 7235c283d94..322ccdf3b59 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -430,7 +430,7 @@ def test_solver_returns_extras_if_requested(solver, repo, package): def test_solver_returns_prereleases_if_requested(solver, repo, package): package.add_dependency("A") package.add_dependency("B") - package.add_dependency("C", {"version": "*", "allows-prereleases": True}) + package.add_dependency("C", {"version": "*", "allow-prereleases": True}) package_a = get_package("A", "1.0") package_b = get_package("B", "1.0") From 8fd63110b8de2479f3d65a86209ade65c788d96a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Wed, 30 Oct 2019 21:59:22 +0100 Subject: [PATCH 07/18] Fix tests for Python 2.7 --- tests/console/commands/test_check.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/console/commands/test_check.py b/tests/console/commands/test_check.py index 74082cc37d4..26ba9b29753 100644 --- a/tests/console/commands/test_check.py +++ b/tests/console/commands/test_check.py @@ -36,6 +36,7 @@ def test_check_invalid(app, mocker): Error: u'description' is a required property Error: INVALID is not a valid license Warning: A wildcard Python dependency is ambiguous. Consider specifying a more explicit one. +Warning: The "pendulum" dependency specifies the "allows-prereleases" property, which is deprecated. Use "allow-preleases" instead. """ else: expected = """\ From 3bfed009cdf1a75555bc65c74f2608140728346e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Sat, 2 Nov 2019 00:09:34 +0100 Subject: [PATCH 08/18] Fix linting --- poetry/installation/installer.py | 3 +-- poetry/masonry/builders/builder.py | 3 +++ poetry/masonry/builders/wheel.py | 1 - poetry/utils/extras.py | 5 ++++- tests/utils/test_extras.py | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/poetry/installation/installer.py b/poetry/installation/installer.py index 335ce6a9bce..df19b7e3359 100644 --- a/poetry/installation/installer.py +++ b/poetry/installation/installer.py @@ -4,7 +4,6 @@ from clikit.api.io import IO from clikit.io import NullIO -from poetry.packages import Dependency from poetry.packages import Locker from poetry.packages import Package from poetry.puzzle import Solver @@ -16,8 +15,8 @@ from poetry.repositories import Repository from poetry.repositories.installed_repository import InstalledRepository from poetry.semver import parse_constraint -from poetry.utils.helpers import canonicalize_name from poetry.utils.extras import get_extra_package_names +from poetry.utils.helpers import canonicalize_name from .base_installer import BaseInstaller from .pip_installer import PipInstaller diff --git a/poetry/masonry/builders/builder.py b/poetry/masonry/builders/builder.py index a0844939ee0..af1a05a29d4 100644 --- a/poetry/masonry/builders/builder.py +++ b/poetry/masonry/builders/builder.py @@ -2,6 +2,7 @@ import re import shutil import tempfile + from collections import defaultdict from contextlib import contextmanager from typing import Set @@ -15,10 +16,12 @@ from poetry.utils._compat import lru_cache from poetry.utils._compat import to_str from poetry.vcs import get_vcs + from ..metadata import Metadata from ..utils.module import Module from ..utils.package_include import PackageInclude + AUTHOR_REGEX = re.compile(r"(?u)^(?P[- .,\w\d'’\"()]+) <(?P.+?)>$") METADATA_BASE = """\ diff --git a/poetry/masonry/builders/wheel.py b/poetry/masonry/builders/wheel.py index 1f5bfb6239e..058679edb4e 100644 --- a/poetry/masonry/builders/wheel.py +++ b/poetry/masonry/builders/wheel.py @@ -10,7 +10,6 @@ from base64 import urlsafe_b64encode from io import StringIO -from typing import Set from clikit.api.io.flags import VERY_VERBOSE diff --git a/poetry/utils/extras.py b/poetry/utils/extras.py index 7a1b24ad42e..4b6b729eccc 100644 --- a/poetry/utils/extras.py +++ b/poetry/utils/extras.py @@ -1,4 +1,7 @@ -from typing import Iterator, Mapping, Sequence +from typing import Collection +from typing import Iterator +from typing import Mapping +from typing import Sequence from poetry.packages import Package from poetry.utils.helpers import canonicalize_name diff --git a/tests/utils/test_extras.py b/tests/utils/test_extras.py index 4bb03d19035..83a184d97ae 100644 --- a/tests/utils/test_extras.py +++ b/tests/utils/test_extras.py @@ -1,8 +1,8 @@ import pytest +from poetry.packages import Package from poetry.utils.extras import get_extra_package_names -from poetry.packages import Package _PACKAGE_FOO = Package("foo", "0.1.0") _PACKAGE_SPAM = Package("spam", "0.2.0") From e717aa1fe120081cc57217f0e160317cf2babfe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Sat, 2 Nov 2019 00:12:43 +0100 Subject: [PATCH 09/18] Fix linting --- tests/utils/fixtures/setups/pendulum/setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/utils/fixtures/setups/pendulum/setup.py b/tests/utils/fixtures/setups/pendulum/setup.py index 702e08f4ce1..3a6323fbbdf 100644 --- a/tests/utils/fixtures/setups/pendulum/setup.py +++ b/tests/utils/fixtures/setups/pendulum/setup.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from distutils.core import setup +from build import * + packages = [ "pendulum", "pendulum._extensions", @@ -45,7 +47,6 @@ "extras_require": extras_require, "python_requires": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", } -from build import * build(setup_kwargs) From 2e7d4b33462dcad1aa5383c2961e5583475972e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Sat, 2 Nov 2019 00:16:55 +0100 Subject: [PATCH 10/18] Fix linting --- tests/utils/fixtures/setups/with-setup-cfg/setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/utils/fixtures/setups/with-setup-cfg/setup.py b/tests/utils/fixtures/setups/with-setup-cfg/setup.py index b024da80e9c..606849326a4 100644 --- a/tests/utils/fixtures/setups/with-setup-cfg/setup.py +++ b/tests/utils/fixtures/setups/with-setup-cfg/setup.py @@ -1,4 +1,3 @@ from setuptools import setup - setup() From f6e439490591cf0c05b9756d42413cf7b2fb6600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Sat, 2 Nov 2019 00:46:57 +0100 Subject: [PATCH 11/18] Fix typing import --- poetry/utils/extras.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/poetry/utils/extras.py b/poetry/utils/extras.py index 4b6b729eccc..2181f03966b 100644 --- a/poetry/utils/extras.py +++ b/poetry/utils/extras.py @@ -1,5 +1,5 @@ -from typing import Collection from typing import Iterator +from typing import List from typing import Mapping from typing import Sequence @@ -9,7 +9,7 @@ def get_extra_package_names( packages, # type: Sequence[Package] - extras, # type: Mapping[str, Collection[str]] + extras, # type: Mapping[str, List[str]] extra_names, # type: Sequence[str] ): # type: (...) -> Iterator[str] """ From a53390568bea9c9d4e5fcd1fa0b4359243572822 Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Wed, 13 Nov 2019 11:31:29 -0500 Subject: [PATCH 12/18] Correct a couple typos in get-poetry.py (#573) From 39c57cc37d2a3b010539774f96137b8ef721097b Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 17 Nov 2019 12:44:19 -0800 Subject: [PATCH 13/18] Docs: `self:update` changed to `self update` (#1588) --- docs/docs/index.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/docs/index.md b/docs/docs/index.md index 66fb0ade021..9cbf2d60736 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -117,28 +117,28 @@ pipx uninstall poetry ## Updating `poetry` -Updating poetry to the latest stable version is as simple as calling the `self:update` command. +Updating Poetry to the latest stable version is as simple as calling the `self update` command. ```bash -poetry self:update +poetry self update ``` -If you want to install prerelease versions, you can use the `--preview` option. +If you want to install pre-release versions, you can use the `--preview` option. ```bash -poetry self:update --preview +poetry self update --preview ``` -And finally, if you want to install a specific version you can pass it as an argument -to `self:update`. +And finally, if you want to install a specific version, you can pass it as an argument +to `self update`. ```bash -poetry self:update 0.8.0 +poetry self update 0.8.0 ``` !!!note - The `self:update` command will only work if you used the recommended + The `self update` command will only work if you used the recommended installer to install Poetry. From 65ab92dec6ae618bab539cd9bdc2df28d3f73e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Tue, 21 Jan 2020 22:45:14 +0100 Subject: [PATCH 14/18] Fix GitHub actions cache issues on develop (#1918) * Fix Github actions cache issues * Fix Github Actions cache issues (#1928) --- .github/workflows/main.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0167d8f660a..fd09899b96d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,6 +30,10 @@ jobs: uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} + - name: Get full python version + id: full-python-version + run: | + echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info[:3]))") - name: Install and set up Poetry run: | python get-poetry.py --preview -y @@ -39,7 +43,7 @@ jobs: uses: actions/cache@v1 with: path: .venv - key: ${{ runner.os }}-venv-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }} + key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies run: | source $HOME/.poetry/env @@ -62,6 +66,10 @@ jobs: uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} + - name: Get full python version + id: full-python-version + run: | + echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info[:3]))") - name: Install and set up Poetry run: | python get-poetry.py --preview -y @@ -71,7 +79,7 @@ jobs: uses: actions/cache@v1 with: path: .venv - key: ${{ runner.os }}-venv-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }} + key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies run: | source $HOME/.poetry/env @@ -94,6 +102,11 @@ jobs: uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} + - name: Get full python version + id: full-python-version + shell: bash + run: | + echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info[:3]))") - name: Install and setup Poetry run: | python get-poetry.py --preview -y @@ -103,7 +116,7 @@ jobs: uses: actions/cache@v1 with: path: .venv - key: ${{ runner.os }}-venv-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }} + key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies run: | $env:Path += ";$env:Userprofile\.poetry\bin" From 4d05c156f2b372ef1b879e276880ea00a8d28761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yannick=20P=C3=89ROUX?= Date: Fri, 31 Jan 2020 17:09:57 +0100 Subject: [PATCH 15/18] Add --source option to "poetry add" (#1912) * Add --source option to 'poetry add' * Add tests for 'poetry add --source' --- poetry/console/commands/add.py | 13 +++++- poetry/console/commands/init.py | 11 +++-- poetry/version/version_selector.py | 3 +- tests/console/commands/test_add.py | 68 ++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 6 deletions(-) diff --git a/poetry/console/commands/add.py b/poetry/console/commands/add.py index 275fc44f5f1..bb210265703 100644 --- a/poetry/console/commands/add.py +++ b/poetry/console/commands/add.py @@ -33,6 +33,12 @@ class AddCommand(EnvCommand, InitCommand): "Platforms for which the dependency must be installed.", flag=False, ), + option( + "source", + None, + "Name of the source to use to install the package.", + flag=False, + ), option("allow-prereleases", None, "Accept prereleases."), option( "dry-run", @@ -86,7 +92,9 @@ def handle(self): raise ValueError("Package {} is already present".format(name)) requirements = self._determine_requirements( - packages, allow_prereleases=self.option("allow-prereleases") + packages, + allow_prereleases=self.option("allow-prereleases"), + source=self.option("source"), ) for _constraint in requirements: @@ -123,6 +131,9 @@ def handle(self): if self.option("platform"): constraint["platform"] = self.option("platform") + if self.option("source"): + constraint["source"] = self.option("source") + if len(constraint) == 1 and "version" in constraint: constraint = constraint["version"] diff --git a/poetry/console/commands/init.py b/poetry/console/commands/init.py index 3691dfebed0..5813dee6551 100644 --- a/poetry/console/commands/init.py +++ b/poetry/console/commands/init.py @@ -204,7 +204,7 @@ def handle(self): f.write(content) def _determine_requirements( - self, requires, allow_prereleases=False + self, requires, allow_prereleases=False, source=None ): # type: (List[str], bool) -> List[Dict[str, str]] if not requires: requires = [] @@ -300,7 +300,9 @@ def _determine_requirements( elif "version" not in requirement: # determine the best version automatically name, version = self._find_best_version_for_package( - requirement["name"], allow_prereleases=allow_prereleases + requirement["name"], + allow_prereleases=allow_prereleases, + source=source, ) requirement["version"] = version requirement["name"] = name @@ -315,6 +317,7 @@ def _determine_requirements( requirement["name"], requirement["version"], allow_prereleases=allow_prereleases, + source=source, ) requirement["name"] = name @@ -324,13 +327,13 @@ def _determine_requirements( return result def _find_best_version_for_package( - self, name, required_version=None, allow_prereleases=False + self, name, required_version=None, allow_prereleases=False, source=None ): # type: (...) -> Tuple[str, str] from poetry.version.version_selector import VersionSelector selector = VersionSelector(self._get_pool()) package = selector.find_best_candidate( - name, required_version, allow_prereleases=allow_prereleases + name, required_version, allow_prereleases=allow_prereleases, source=source ) if not package: diff --git a/poetry/version/version_selector.py b/poetry/version/version_selector.py index 8c71daec87b..2077e322699 100644 --- a/poetry/version/version_selector.py +++ b/poetry/version/version_selector.py @@ -15,6 +15,7 @@ def find_best_candidate( package_name, # type: str target_package_version=None, # type: Union[str, None] allow_prereleases=False, # type: bool + source=None, # type: str ): # type: (...) -> Union[Package, bool] """ Given a package name and optional version, @@ -26,7 +27,7 @@ def find_best_candidate( constraint = parse_constraint("*") candidates = self._pool.find_packages( - package_name, constraint, allow_prereleases=True + package_name, constraint, allow_prereleases=True, repository=source ) only_prereleases = all([c.version.is_prerelease() for c in candidates]) diff --git a/tests/console/commands/test_add.py b/tests/console/commands/test_add.py index 6a64ba7e4eb..8e7f4a870c8 100644 --- a/tests/console/commands/test_add.py +++ b/tests/console/commands/test_add.py @@ -4,6 +4,8 @@ from cleo.testers import CommandTester +from poetry.repositories.legacy_repository import LegacyRepository +from poetry.semver import Version from poetry.utils._compat import Path from tests.helpers import get_dependency from tests.helpers import get_package @@ -634,6 +636,72 @@ def test_add_constraint_with_platform(app, repo, installer): } +def test_add_constraint_with_source(app, poetry, installer): + 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) + + command = app.find("add") + tester = CommandTester(command) + + 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 len(installer.installs) == 1 + + 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): + command = app.find("add") + tester = CommandTester(command) + + 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): + 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")) + + command = app.find("add") + tester = CommandTester(command) + + 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, installer): command = app.find("add") tester = CommandTester(command) From 7b1fd0cd0888f0f97db647fddf3d86052bbabd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 21 Feb 2020 15:46:03 +0100 Subject: [PATCH 16/18] Merge master into develop (#2070) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix Github actions cache issues (#1908) * Fix case of `-f` flag * Make it clearer what options to pass to `--format` * fix (masonry.api): `get_requires_for_build_wheel` must return additional list of requirements for building a package, not listed in `pyproject.toml` and not dependencies for the package itself (#1875) fix (tests): adopted tests * Lazy Keyring intialization for PasswordManager (#1892) * Fix Github Actions cache issues (#1928) * Avoid nested quantifiers with overlapping character space on git url parsing (#1902 (#1913) * fix (git): match for `\w` instead of `.` for getting user * change (vcs.git): hold pattern of the regex parts in a dictionary to be consistent over all regexs * new (vcs.git): test for `parse_url` and some fixes for the regex pattern * new (vcs.git): test for `parse_url` with string that should fail * fix (test.vcs.git): make flake8 happy * fix: correct parsing of wheel version with regex. (#1932) The previous regexp was only taking the first integer of the version number, this presented problems when the major version number reached double digits. Poetry would determine that the version of the dependency is '1', rather than, ie: '14'. This caused failures to solve versions. * Fix errors when using the --help option (#1910) * Fix how repository credentials are retrieved from env vars (#1909) # Conflicts: # poetry/utils/password_manager.py * Fix downloading packages from Simplepypi (#1851) * fix downloading packages from simplepypi * unused code removed * remove unused imports * Upgrade dependencies for the 1.0.3 release (#1965) * Bump version to 1.0.3 (#1966) * Fix non-compliant Git URL matching RFC 3986 § 2.3 permits more characters in a URL than were matched. This corrects that, though there may be other deficiencies. This was a regression from v1.0.2, where at least “.” was matched without error. * Update README.md "Updating Poetry" Currently the note in "Updating Poetry" is different from the one below in "Enable tab completion for Bash, Fish, or Zsh". This MR is to make them more consistent. * init: change dev dependency prompt * Fix CI issues (#2069) Co-authored-by: brandonaut Co-authored-by: finswimmer Co-authored-by: Yannick PÉROUX Co-authored-by: Edward George Co-authored-by: Jan Škoda Co-authored-by: Andrew Marshall Co-authored-by: Andrew Selzer Co-authored-by: Andrii Maletskyi --- .github/workflows/main.yml | 8 +- CHANGELOG.md | 16 +- README.md | 2 +- docs/docs/cli.md | 2 +- poetry.lock | 190 +++++++++++--------- poetry/__version__.py | 2 +- poetry/console/commands/init.py | 3 +- poetry/console/config/application_config.py | 12 +- poetry/masonry/api.py | 11 +- poetry/packages/__init__.py | 3 +- poetry/utils/inspector.py | 10 +- poetry/utils/password_manager.py | 42 +++-- poetry/vcs/git.py | 102 +++++++---- pyproject.toml | 2 +- tests/console/commands/env/test_list.py | 4 +- tests/console/commands/env/test_use.py | 3 + tests/console/commands/test_run.py | 6 +- tests/console/conftest.py | 11 +- tests/masonry/test_api.py | 8 +- tests/packages/test_main.py | 11 ++ tests/utils/test_password_manager.py | 16 ++ tests/vcs/test_git.py | 185 +++++++++++++++++++ 22 files changed, 477 insertions(+), 172 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fd09899b96d..cb541c562b7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,7 +33,7 @@ jobs: - name: Get full python version id: full-python-version run: | - echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info[:3]))") + echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - name: Install and set up Poetry run: | python get-poetry.py --preview -y @@ -69,7 +69,7 @@ jobs: - name: Get full python version id: full-python-version run: | - echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info[:3]))") + echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - name: Install and set up Poetry run: | python get-poetry.py --preview -y @@ -87,7 +87,7 @@ jobs: - name: Test run: | source $HOME/.poetry/env - poetry run pytest -q tests + .venv/bin/pytest -q tests Windows: needs: Linting @@ -106,7 +106,7 @@ jobs: id: full-python-version shell: bash run: | - echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info[:3]))") + echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - name: Install and setup Poetry run: | python get-poetry.py --preview -y diff --git a/CHANGELOG.md b/CHANGELOG.md index 225ad1590eb..e70d1c8effd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Change Log +## [1.0.3] - 2020-01-31 + +### Fixed + +- Fixed an error which caused the configuration environment variables (like `POETRY_HTTP_BASIC_XXX_PASSWORD`) to not be used ([#1909](https://github.com/python-poetry/poetry/pull/1909)). +- Fixed an error where the `--help` option was not working ([#1910](https://github.com/python-poetry/poetry/pull/1910)). +- Fixed an error where packages from private indices were not decompressed properly ([#1851](https://github.com/python-poetry/poetry/pull/1851)). +- Fixed an error where the version of some PEP-508-formatted wheel dependencies was not properly retrieved ([#1932](https://github.com/python-poetry/poetry/pull/1932)). +- Fixed internal regexps to avoid potential catastrophic backtracking errors ([#1913](https://github.com/python-poetry/poetry/pull/1913)). +- Fixed performance issues when custom indices were defined in the `pyproject.toml` file ([#1892](https://github.com/python-poetry/poetry/pull/1892)). +- Fixed the `get_requires_for_build_wheel()` function of `masonry.api` which wasn't returning the proper result ([#1875](https://github.com/python-poetry/poetry/pull/1875)). + + ## [1.0.2] - 2020-01-10 ### Fixed @@ -783,7 +796,8 @@ Initial release -[Unreleased]: https://github.com/python-poetry/poetry/compare/1.0.2...master +[Unreleased]: https://github.com/python-poetry/poetry/compare/1.0.3...master +[1.0.3]: https://github.com/python-poetry/poetry/releases/tag/1.0.3 [1.0.2]: https://github.com/python-poetry/poetry/releases/tag/1.0.2 [1.0.1]: https://github.com/python-poetry/poetry/releases/tag/1.0.1 [1.0.0]: https://github.com/python-poetry/poetry/releases/tag/1.0.0 diff --git a/README.md b/README.md index cf8317294a2..7bea3955c96 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ to `self update`. poetry self update 1.0.0 ``` -!!!note +*Note:* If you are still on poetry version < 1.0 use `poetry self:update` instead. diff --git a/docs/docs/cli.md b/docs/docs/cli.md index 611a8ed4475..89d19a8cc18 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -305,7 +305,7 @@ Note that, at the moment, only pure python wheels are supported. ### Options -* `--format (-F)`: Limit the format to either wheel or sdist. +* `--format (-f)`: Limit the format to either `wheel` or `sdist`. ## publish diff --git a/poetry.lock b/poetry.lock index 182f389363d..8cc55799405 100644 --- a/poetry.lock +++ b/poetry.lock @@ -207,7 +207,7 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2)", "pytes [[package]] category = "main" description = "Backports and enhancements for the contextlib module" -marker = "python_version < \"3\"" +marker = "python_version < \"3.4\"" name = "contextlib2" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" @@ -227,7 +227,7 @@ description = "Code coverage measurement for Python" name = "coverage" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "5.0.2" +version = "5.0.3" [package.extras] toml = ["toml"] @@ -363,7 +363,7 @@ description = "File identification library for Python" name = "identify" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "1.4.9" +version = "1.4.11" [package.extras] license = ["editdistance"] @@ -453,6 +453,21 @@ MarkupSafe = ">=0.23" [package.extras] i18n = ["Babel (>=0.8)"] +[[package]] +category = "dev" +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" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.11.1" + +[package.dependencies] +MarkupSafe = ">=0.23" + +[package.extras] +i18n = ["Babel (>=0.8)"] + [[package]] category = "main" description = "An implementation of JSON Schema validation for Python" @@ -623,9 +638,9 @@ docs = ["sphinx"] test = ["pytest", "pytest-cov"] [[package]] -category = "main" +category = "dev" description = "More routines for operating on iterables, beyond itertools" -marker = "python_version < \"3.8\"" +marker = "python_version <= \"2.7\"" name = "more-itertools" optional = false python-versions = "*" @@ -635,22 +650,22 @@ version = "5.0.0" six = ">=1.0.0,<2.0.0" [[package]] -category = "main" +category = "dev" description = "More routines for operating on iterables, beyond itertools" -marker = "python_version < \"3.8\" or python_version > \"2.7\"" +marker = "python_version > \"2.7\"" name = "more-itertools" optional = false python-versions = ">=3.4" version = "7.2.0" [[package]] -category = "main" +category = "dev" description = "More routines for operating on iterables, beyond itertools" -marker = "python_version >= \"3.5\" and python_version < \"3.8\" or python_version < \"3.8\" or python_version > \"2.7\"" +marker = "python_version > \"2.7\"" name = "more-itertools" optional = false python-versions = ">=3.5" -version = "8.0.2" +version = "8.2.0" [[package]] category = "main" @@ -674,7 +689,7 @@ description = "Core utilities for Python packages" name = "packaging" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.0" +version = "20.1" [package.dependencies] pyparsing = ">=2.0.2" @@ -727,7 +742,7 @@ description = "Pexpect allows easy control of interactive console applications." name = "pexpect" optional = false python-versions = "*" -version = "4.7.0" +version = "4.8.0" [package.dependencies] ptyprocess = ">=0.5" @@ -1139,8 +1154,8 @@ category = "main" description = "Python 2 and 3 compatibility utilities" name = "six" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "1.13.0" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.14.0" [[package]] category = "main" @@ -1260,7 +1275,7 @@ marker = "python_version >= \"3.6\" and python_version < \"4.0\"" name = "typed-ast" optional = false python-versions = "*" -version = "1.4.0" +version = "1.4.1" [[package]] category = "main" @@ -1288,8 +1303,8 @@ category = "main" description = "HTTP library with thread-safe connection pooling, file post, and more." name = "urllib3" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" -version = "1.25.7" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "1.25.8" [package.extras] brotli = ["brotlipy (>=0.6.0)"] @@ -1331,14 +1346,16 @@ marker = "python_version >= \"3.5\" and python_version < \"3.8\" or python_versi name = "zipp" optional = false python-versions = ">=2.7" -version = "0.6.0" +version = "1.1.0" [package.dependencies] -more-itertools = "*" +[package.dependencies.contextlib2] +python = "<3.4" +version = "*" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pathlib2", "contextlib2", "unittest2"] +testing = ["pathlib2", "unittest2", "jaraco.itertools"] [metadata] content-hash = "e0b632d8363fdf9f70d93901ff537714611bfc31705a897f6d2fb3bc010bca0a" @@ -1479,37 +1496,37 @@ coverage = [ {file = "coverage-4.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351"}, {file = "coverage-4.5.4-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5"}, {file = "coverage-4.5.4.tar.gz", hash = "sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c"}, - {file = "coverage-5.0.2-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:511ec0c00840e12fb4e852e4db58fa6a01ca4da72f36a9766fae344c3d502033"}, - {file = "coverage-5.0.2-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:d22b4297e7e4225ccf01f1aa55e7a96412ea0796b532dd614c3fcbafa341128e"}, - {file = "coverage-5.0.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:593853aa1ac6dcc6405324d877544c596c9d948ef20d2e9512a0f5d2d3202356"}, - {file = "coverage-5.0.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:e65a5aa1670db6263f19fdc03daee1d7dbbadb5cb67fd0a1f16033659db13c1d"}, - {file = "coverage-5.0.2-cp27-cp27m-win32.whl", hash = "sha256:d4a2b578a7a70e0c71f662705262f87a456f1e6c1e40ada7ea699abaf070a76d"}, - {file = "coverage-5.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:28f7f73b34a05e23758e860a89a7f649b85c6749e252eff60ebb05532d180e86"}, - {file = "coverage-5.0.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7d1cc7acc9ce55179616cf72154f9e648136ea55987edf84addbcd9886ffeba2"}, - {file = "coverage-5.0.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:2d0cb9b1fe6ad0d915d45ad3d87f03a38e979093a98597e755930db1f897afae"}, - {file = "coverage-5.0.2-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:bfe102659e2ec13b86c7f3b1db6c9a4e7beea4255058d006351339e6b342d5d2"}, - {file = "coverage-5.0.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:23688ff75adfa8bfa2a67254d889f9bdf9302c27241d746e17547c42c732d3f4"}, - {file = "coverage-5.0.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1bf7ba2af1d373a1750888724f84cffdfc697738f29a353c98195f98fc011509"}, - {file = "coverage-5.0.2-cp35-cp35m-win32.whl", hash = "sha256:569f9ee3025682afda6e9b0f5bb14897c0db03f1a1dc088b083dd36e743f92bb"}, - {file = "coverage-5.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:cf908840896f7aa62d0ec693beb53264b154f972eb8226fb864ac38975590c4f"}, - {file = "coverage-5.0.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:eaad65bd20955131bcdb3967a4dea66b4e4d4ca488efed7c00d91ee0173387e8"}, - {file = "coverage-5.0.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:225e79a5d485bc1642cb7ba02281419c633c216cdc6b26c26494ba959f09e69f"}, - {file = "coverage-5.0.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bd82b684bb498c60ef47bb1541a50e6d006dde8579934dcbdbc61d67d1ea70d9"}, - {file = "coverage-5.0.2-cp36-cp36m-win32.whl", hash = "sha256:7ca3db38a61f3655a2613ee2c190d63639215a7a736d3c64cc7bbdb002ce6310"}, - {file = "coverage-5.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:47874b4711c5aeb295c31b228a758ce3d096be83dc37bd56da48ed99efb8813b"}, - {file = "coverage-5.0.2-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:955ec084f549128fa2702f0b2dc696392001d986b71acd8fd47424f28289a9c3"}, - {file = "coverage-5.0.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1f4ee8e2e4243971618bc16fcc4478317405205f135e95226c2496e2a3b8dbbf"}, - {file = "coverage-5.0.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f45fba420b94165c17896861bb0e8b27fb7abdcedfeb154895d8553df90b7b00"}, - {file = "coverage-5.0.2-cp37-cp37m-win32.whl", hash = "sha256:cca38ded59105f7705ef6ffe1e960b8db6c7d8279c1e71654a4775ab4454ca15"}, - {file = "coverage-5.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:cb2b74c123f65e8166f7e1265829a6c8ed755c3cd16d7f50e75a83456a5f3fd7"}, - {file = "coverage-5.0.2-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:53e7438fef0c97bc248f88ba1edd10268cd94d5609970aaf87abbe493691af87"}, - {file = "coverage-5.0.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c1e4e39e43057396a5e9d069bfbb6ffeee892e40c5d2effbd8cd71f34ee66c4d"}, - {file = "coverage-5.0.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5b0a07158360d22492f9abd02a0f2ee7981b33f0646bf796598b7673f6bbab14"}, - {file = "coverage-5.0.2-cp38-cp38m-win32.whl", hash = "sha256:88b51153657612aea68fa684a5b88037597925260392b7bb4509d4f9b0bdd889"}, - {file = "coverage-5.0.2-cp38-cp38m-win_amd64.whl", hash = "sha256:189aac76d6e0d7af15572c51892e7326ee451c076c5a50a9d266406cd6c49708"}, - {file = "coverage-5.0.2-cp39-cp39m-win32.whl", hash = "sha256:d095a7b473f8a95f7efe821f92058c8a2ecfb18f8db6677ae3819e15dc11aaae"}, - {file = "coverage-5.0.2-cp39-cp39m-win_amd64.whl", hash = "sha256:ddeb42a3d5419434742bf4cc71c9eaa22df3b76808e23a82bd0b0bd360f1a9f1"}, - {file = "coverage-5.0.2.tar.gz", hash = "sha256:b251c7092cbb6d789d62dc9c9e7c4fb448c9138b51285c36aeb72462cad3600e"}, + {file = "coverage-5.0.3-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f"}, + {file = "coverage-5.0.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc"}, + {file = "coverage-5.0.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a"}, + {file = "coverage-5.0.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52"}, + {file = "coverage-5.0.3-cp27-cp27m-win32.whl", hash = "sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c"}, + {file = "coverage-5.0.3-cp27-cp27m-win_amd64.whl", hash = "sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73"}, + {file = "coverage-5.0.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68"}, + {file = "coverage-5.0.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691"}, + {file = "coverage-5.0.3-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301"}, + {file = "coverage-5.0.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf"}, + {file = "coverage-5.0.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3"}, + {file = "coverage-5.0.3-cp35-cp35m-win32.whl", hash = "sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0"}, + {file = "coverage-5.0.3-cp35-cp35m-win_amd64.whl", hash = "sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0"}, + {file = "coverage-5.0.3-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2"}, + {file = "coverage-5.0.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894"}, + {file = "coverage-5.0.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf"}, + {file = "coverage-5.0.3-cp36-cp36m-win32.whl", hash = "sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477"}, + {file = "coverage-5.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc"}, + {file = "coverage-5.0.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8"}, + {file = "coverage-5.0.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987"}, + {file = "coverage-5.0.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea"}, + {file = "coverage-5.0.3-cp37-cp37m-win32.whl", hash = "sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc"}, + {file = "coverage-5.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e"}, + {file = "coverage-5.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb"}, + {file = "coverage-5.0.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37"}, + {file = "coverage-5.0.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d"}, + {file = "coverage-5.0.3-cp38-cp38m-win32.whl", hash = "sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954"}, + {file = "coverage-5.0.3-cp38-cp38m-win_amd64.whl", hash = "sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e"}, + {file = "coverage-5.0.3-cp39-cp39m-win32.whl", hash = "sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40"}, + {file = "coverage-5.0.3-cp39-cp39m-win_amd64.whl", hash = "sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af"}, + {file = "coverage-5.0.3.tar.gz", hash = "sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef"}, ] cryptography = [ {file = "cryptography-2.8-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"}, @@ -1571,8 +1588,8 @@ httpretty = [ {file = "httpretty-0.9.7.tar.gz", hash = "sha256:66216f26b9d2c52e81808f3e674a6fb65d4bf719721394a1a9be926177e55fbe"}, ] identify = [ - {file = "identify-1.4.9-py2.py3-none-any.whl", hash = "sha256:72e9c4ed3bc713c7045b762b0d2e2115c572b85abfc1f4604f5a4fd4c6642b71"}, - {file = "identify-1.4.9.tar.gz", hash = "sha256:6f44e637caa40d1b4cb37f6ed3b262ede74901d28b1cc5b1fc07360871edd65d"}, + {file = "identify-1.4.11-py2.py3-none-any.whl", hash = "sha256:1222b648251bdcb8deb240b294f450fbf704c7984e08baa92507e4ea10b436d5"}, + {file = "identify-1.4.11.tar.gz", hash = "sha256:d824ebe21f38325c771c41b08a95a761db1982f1fc0eee37c6c97df3f1636b96"}, ] idna = [ {file = "idna-2.8-py2.py3-none-any.whl", hash = "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"}, @@ -1597,6 +1614,8 @@ jeepney = [ jinja2 = [ {file = "Jinja2-2.10.3-py2.py3-none-any.whl", hash = "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f"}, {file = "Jinja2-2.10.3.tar.gz", hash = "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"}, + {file = "Jinja2-2.11.1-py2.py3-none-any.whl", hash = "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"}, + {file = "Jinja2-2.11.1.tar.gz", hash = "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250"}, ] jsonschema = [ {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, @@ -1669,8 +1688,8 @@ more-itertools = [ {file = "more_itertools-5.0.0-py3-none-any.whl", hash = "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"}, {file = "more-itertools-7.2.0.tar.gz", hash = "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832"}, {file = "more_itertools-7.2.0-py3-none-any.whl", hash = "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"}, - {file = "more-itertools-8.0.2.tar.gz", hash = "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d"}, - {file = "more_itertools-8.0.2-py3-none-any.whl", hash = "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564"}, + {file = "more-itertools-8.2.0.tar.gz", hash = "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"}, + {file = "more_itertools-8.2.0-py3-none-any.whl", hash = "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c"}, ] msgpack = [ {file = "msgpack-0.6.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:774f5edc3475917cd95fe593e625d23d8580f9b48b570d8853d06cac171cd170"}, @@ -1699,8 +1718,8 @@ nodeenv = [ {file = "nodeenv-1.3.4-py2.py3-none-any.whl", hash = "sha256:561057acd4ae3809e665a9aaaf214afff110bbb6a6d5c8a96121aea6878408b3"}, ] packaging = [ - {file = "packaging-20.0-py2.py3-none-any.whl", hash = "sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb"}, - {file = "packaging-20.0.tar.gz", hash = "sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8"}, + {file = "packaging-20.1-py2.py3-none-any.whl", hash = "sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73"}, + {file = "packaging-20.1.tar.gz", hash = "sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334"}, ] pastel = [ {file = "pastel-0.1.1-py2.py3-none-any.whl", hash = "sha256:a904e1659512cc9880a028f66de77cc813a4c32f7ceb68725cbc8afad57ef7ef"}, @@ -1719,8 +1738,8 @@ pep562 = [ {file = "pep562-1.0.tar.gz", hash = "sha256:58cb1cc9ee63d93e62b4905a50357618d526d289919814bea1f0da8f53b79395"}, ] pexpect = [ - {file = "pexpect-4.7.0-py2.py3-none-any.whl", hash = "sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1"}, - {file = "pexpect-4.7.0.tar.gz", hash = "sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"}, + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, ] pkginfo = [ {file = "pkginfo-1.5.0.1-py2.py3-none-any.whl", hash = "sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32"}, @@ -1874,8 +1893,8 @@ shellingham = [ {file = "shellingham-1.3.1.tar.gz", hash = "sha256:985b23bbd1feae47ca6a6365eacd314d93d95a8a16f8f346945074c28fe6f3e0"}, ] six = [ - {file = "six-1.13.0-py2.py3-none-any.whl", hash = "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd"}, - {file = "six-1.13.0.tar.gz", hash = "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"}, + {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, + {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, ] subprocess32 = [ {file = "subprocess32-3.5.4-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:88e37c1aac5388df41cc8a8456bb49ebffd321a3ad4d70358e3518176de3a56b"}, @@ -1916,26 +1935,27 @@ tox = [ {file = "tox-3.14.3.tar.gz", hash = "sha256:06ba73b149bf838d5cd25dc30c2dd2671ae5b2757cf98e5c41a35fe449f131b3"}, ] typed-ast = [ - {file = "typed_ast-1.4.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e"}, - {file = "typed_ast-1.4.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b"}, - {file = "typed_ast-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4"}, - {file = "typed_ast-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"}, - {file = "typed_ast-1.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631"}, - {file = "typed_ast-1.4.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233"}, - {file = "typed_ast-1.4.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1"}, - {file = "typed_ast-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a"}, - {file = "typed_ast-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c"}, - {file = "typed_ast-1.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a"}, - {file = "typed_ast-1.4.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e"}, - {file = "typed_ast-1.4.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d"}, - {file = "typed_ast-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36"}, - {file = "typed_ast-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0"}, - {file = "typed_ast-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66"}, - {file = "typed_ast-1.4.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2"}, - {file = "typed_ast-1.4.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47"}, - {file = "typed_ast-1.4.0-cp38-cp38-win32.whl", hash = "sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161"}, - {file = "typed_ast-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e"}, - {file = "typed_ast-1.4.0.tar.gz", hash = "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34"}, + {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, + {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, + {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, + {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, + {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, + {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, + {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, + {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, + {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, + {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, + {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, + {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, + {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, + {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, + {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, ] typing = [ {file = "typing-3.7.4.1-py2-none-any.whl", hash = "sha256:c8cabb5ab8945cd2f54917be357d134db9cc1eb039e59d1606dc1e60cb1d9d36"}, @@ -1945,8 +1965,8 @@ typing = [ urllib3 = [ {file = "urllib3-1.24.3-py2.py3-none-any.whl", hash = "sha256:a637e5fae88995b256e3409dc4d52c2e2e0ba32c42a6365fee8bbd2238de3cfb"}, {file = "urllib3-1.24.3.tar.gz", hash = "sha256:2393a695cd12afedd0dcb26fe5d50d0cf248e5a66f75dbd89a3d4eb333a61af4"}, - {file = "urllib3-1.25.7-py2.py3-none-any.whl", hash = "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293"}, - {file = "urllib3-1.25.7.tar.gz", hash = "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745"}, + {file = "urllib3-1.25.8-py2.py3-none-any.whl", hash = "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc"}, + {file = "urllib3-1.25.8.tar.gz", hash = "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"}, ] virtualenv = [ {file = "virtualenv-16.7.9-py2.py3-none-any.whl", hash = "sha256:55059a7a676e4e19498f1aad09b8313a38fcc0cdbe4fdddc0e9b06946d21b4bb"}, @@ -1961,6 +1981,6 @@ webencodings = [ {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] zipp = [ - {file = "zipp-0.6.0-py2.py3-none-any.whl", hash = "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"}, - {file = "zipp-0.6.0.tar.gz", hash = "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e"}, + {file = "zipp-1.1.0-py2.py3-none-any.whl", hash = "sha256:15428d652e993b6ce86694c3cccf0d71aa7afdc6ef1807fa25a920e9444e0281"}, + {file = "zipp-1.1.0.tar.gz", hash = "sha256:d9d2efe11d3a3fb9184da550d35bd1319dc8e30a63255927c82bb42fca1f4f7c"}, ] diff --git a/poetry/__version__.py b/poetry/__version__.py index 7863915fa5f..976498ab9ca 100644 --- a/poetry/__version__.py +++ b/poetry/__version__.py @@ -1 +1 @@ -__version__ = "1.0.2" +__version__ = "1.0.3" diff --git a/poetry/console/commands/init.py b/poetry/console/commands/init.py index 5813dee6551..40ed33a17f1 100644 --- a/poetry/console/commands/init.py +++ b/poetry/console/commands/init.py @@ -165,8 +165,7 @@ def handle(self): dev_requirements = {} question = ( - "Would you like to define your dev dependencies" - " (require-dev) interactively" + "Would you like to define your development dependencies interactively?" ) if self.confirm(question, True): if not help_displayed: diff --git a/poetry/console/config/application_config.py b/poetry/console/config/application_config.py index 36b72e9969e..97570956b09 100644 --- a/poetry/console/config/application_config.py +++ b/poetry/console/config/application_config.py @@ -9,6 +9,7 @@ from clikit.api.event import PreHandleEvent from clikit.api.event import PreResolveEvent from clikit.api.event.event_dispatcher import EventDispatcher +from clikit.api.exceptions import CliKitException from clikit.api.formatter import Style from clikit.api.io import Input from clikit.api.io import InputStream @@ -101,7 +102,16 @@ def resolve_help_command( if args.has_option_token("-h") or args.has_option_token("--help"): from clikit.api.resolver import ResolvedCommand - resolved_command = self.command_resolver.resolve(args, application) + try: + resolved_command = self.command_resolver.resolve(args, application) + except CliKitException: + # We weren't able to resolve the command, + # due to a parse error most likely, + # so we fall back on the default behavior + return super(ApplicationConfig, self).resolve_help_command( + event, event_name, dispatcher + ) + # If the current command is the run one, skip option # check and interpret them as part of the executed command if resolved_command.command.name == "run": diff --git a/poetry/masonry/api.py b/poetry/masonry/api.py index 539637df8f8..b6df04b0168 100644 --- a/poetry/masonry/api.py +++ b/poetry/masonry/api.py @@ -20,13 +20,14 @@ def get_requires_for_build_wheel(config_settings=None): """ - Returns a list of requirements for building, as strings - """ - poetry = Factory().create_poetry(Path(".")) + Returns an additional list of requirements for building, as PEP508 strings, + above and beyond those specified in the pyproject.toml file. - main, _ = SdistBuilder.convert_dependencies(poetry.package, poetry.package.requires) + This implementation is optional. At the moment it only returns an empty list, which would be the same as if + not define. So this is just for completeness for future implementation. + """ - return main + return [] # For now, we require all dependencies to build either a wheel or an sdist. diff --git a/poetry/packages/__init__.py b/poetry/packages/__init__.py index caa9c1c28a6..e0a2203c9d3 100644 --- a/poetry/packages/__init__.py +++ b/poetry/packages/__init__.py @@ -2,6 +2,7 @@ import re from poetry.semver import Version +from poetry.utils.patterns import wheel_file_re from poetry.version.requirements import Requirement from .dependency import Dependency @@ -70,7 +71,7 @@ def dependency_from_pep_508(name): link = Link(path_to_url(os.path.normpath(os.path.abspath(link.path)))) # wheel file if link.is_wheel: - m = re.match(r"^(?P(?P.+?)-(?P\d.*?))", link.filename) + m = wheel_file_re.match(link.filename) if not m: raise ValueError("Invalid wheel name: {}".format(link.filename)) diff --git a/poetry/utils/inspector.py b/poetry/utils/inspector.py index 767f0946542..f1675273b47 100644 --- a/poetry/utils/inspector.py +++ b/poetry/utils/inspector.py @@ -3,8 +3,6 @@ import tarfile import zipfile -from bz2 import BZ2File -from gzip import GzipFile from typing import Dict from typing import List from typing import Union @@ -114,27 +112,21 @@ def inspect_sdist( # Still not dependencies found # So, we unpack and introspect suffix = file_path.suffix - gz = None if suffix == ".zip": tar = zipfile.ZipFile(str(file_path)) else: if suffix == ".bz2": - gz = BZ2File(str(file_path)) suffixes = file_path.suffixes if len(suffixes) > 1 and suffixes[-2] == ".tar": suffix = ".tar.bz2" else: - gz = GzipFile(str(file_path)) suffix = ".tar.gz" - tar = tarfile.TarFile(str(file_path), fileobj=gz) + tar = tarfile.open(str(file_path)) try: tar.extractall(os.path.join(str(file_path.parent), "unpacked")) finally: - if gz: - gz.close() - tar.close() unpacked = file_path.parent / "unpacked" diff --git a/poetry/utils/password_manager.py b/poetry/utils/password_manager.py index e992b0562e9..24a615a46bf 100644 --- a/poetry/utils/password_manager.py +++ b/poetry/utils/password_manager.py @@ -117,44 +117,52 @@ def _check(self): class PasswordManager: def __init__(self, config): self._config = config - self._keyring = KeyRing("poetry-repository") - if not self._keyring.is_available(): - logger.warning("Using a plaintext file to store and retrieve credentials") + self._keyring = None @property def keyring(self): + if self._keyring is None: + self._keyring = KeyRing("poetry-repository") + if not self._keyring.is_available(): + logger.warning( + "Using a plaintext file to store and retrieve credentials" + ) + return self._keyring def set_pypi_token(self, name, token): - if not self._keyring.is_available(): + if not self.keyring.is_available(): self._config.auth_config_source.add_property( "pypi-token.{}".format(name), token ) else: - self._keyring.set_password(name, "__token__", token) + self.keyring.set_password(name, "__token__", token) def get_pypi_token(self, name): - if not self._keyring.is_available(): + if not self.keyring.is_available(): return self._config.get("pypi-token.{}".format(name)) - return self._keyring.get_password(name, "__token__") + return self.keyring.get_password(name, "__token__") def delete_pypi_token(self, name): - if not self._keyring.is_available(): + if not self.keyring.is_available(): return self._config.auth_config_source.remove_property( "pypi-token.{}".format(name) ) - self._keyring.delete_password(name, "__token__") + self.keyring.delete_password(name, "__token__") def get_http_auth(self, name): auth = self._config.get("http-basic.{}".format(name)) if not auth: - return None - - username, password = auth["username"], auth.get("password") - if password is None: - password = self._keyring.get_password(name, username) + username = self._config.get("http-basic.{}.username".format(name)) + password = self._config.get("http-basic.{}.password".format(name)) + if not username and not password: + return None + else: + username, password = auth["username"], auth.get("password") + if password is None: + password = self.keyring.get_password(name, username) return { "username": username, @@ -164,10 +172,10 @@ def get_http_auth(self, name): def set_http_password(self, name, username, password): auth = {"username": username} - if not self._keyring.is_available(): + if not self.keyring.is_available(): auth["password"] = password else: - self._keyring.set_password(name, username, password) + self.keyring.set_password(name, username, password) self._config.auth_config_source.add_property("http-basic.{}".format(name), auth) @@ -177,7 +185,7 @@ def delete_http_password(self, name): return try: - self._keyring.delete_password(name, auth["username"]) + self.keyring.delete_password(name, auth["username"]) except KeyRingError: pass diff --git a/poetry/vcs/git.py b/poetry/vcs/git.py index 780a687f40b..791e68dd1af 100644 --- a/poetry/vcs/git.py +++ b/poetry/vcs/git.py @@ -7,46 +7,84 @@ from poetry.utils._compat import decode +pattern_formats = { + "protocol": r"\w+", + "user": r"[a-zA-Z0-9_.-]+", + "resource": r"[a-zA-Z0-9_.-]+", + "port": r"\d+", + "path": r"[\w~.\-/\\]+", + "name": r"[\w~.\-]+", + "rev": r"[^@#]+", +} + PATTERNS = [ - re.compile( - r"(git\+)?" - r"((?P\w+)://)" - r"((?P\w+)@)?" - r"(?P[\w.\-]+)" - r"(:(?P\d+))?" - r"(?P(/(?P\w+)/)" - r"((?P([\w\-/]+)/)?(?P[\w\-]+)(\.git|/)?)?)" - r"([@#](?P[^@#]+))?" - r"$" - ), re.compile( r"^(git\+)?" r"(?Phttps?|git|ssh|rsync|file)://" - r"(?:(?P.+)@)*" - r"(?P[a-z0-9_.-]*)" - r"(:?P[\d]+)?" - r"(?P[:/]((?P[\w\-]+)/(?P([\w\-/]+)/)?)?" - r"((?P[\w\-.]+?)(\.git|/)?)?)" - r"([@#](?P[^@#]+))?" - r"$" + r"(?:(?P{user})@)?" + r"(?P{resource})?" + r"(:(?P{port}))?" + r"(?P[:/\\]({path}[/\\])?" + r"((?P{name}?)(\.git|[/\\])?)?)" + r"([@#](?P{rev}))?" + r"$".format( + user=pattern_formats["user"], + resource=pattern_formats["resource"], + port=pattern_formats["port"], + path=pattern_formats["path"], + name=pattern_formats["name"], + rev=pattern_formats["rev"], + ) ), re.compile( - r"^(?:(?P.+)@)*" - r"(?P[a-z0-9_.-]*)[:]*" - r"(?P[\d]+)?" - r"(?P/?(?P.+)/(?P([\w\-/]+)/)?(?P.+).git)" - r"([@#](?P[^@#]+))?" - r"$" + r"(git\+)?" + r"((?P{protocol})://)" + r"(?:(?P{user})@)?" + r"(?P{resource}:?)" + r"(:(?P{port}))?" + r"(?P({path})" + r"(?P{name})(\.git|/)?)" + r"([@#](?P{rev}))?" + r"$".format( + protocol=pattern_formats["protocol"], + user=pattern_formats["user"], + resource=pattern_formats["resource"], + port=pattern_formats["port"], + path=pattern_formats["path"], + name=pattern_formats["name"], + rev=pattern_formats["rev"], + ) ), re.compile( - r"((?P\w+)@)?" - r"(?P[\w.\-]+)" - r"[:/]{1,2}" - r"(?P((?P\w+)/)?" - r"(?P([\w\-/]+)/)?" - r"((?P[\w\-]+)(\.git|/)?)?)" - r"([@#](?P[^@#]+))?" - r"$" + r"^(?:(?P{user})@)?" + r"(?P{resource})" + r"(:(?P{port}))?" + r"(?P([:/]{path}/)" + r"(?P{name})(\.git|/)?)" + r"([@#](?P{rev}))?" + r"$".format( + user=pattern_formats["user"], + resource=pattern_formats["resource"], + port=pattern_formats["port"], + path=pattern_formats["path"], + name=pattern_formats["name"], + rev=pattern_formats["rev"], + ) + ), + re.compile( + r"((?P{user})@)?" + r"(?P{resource})" + r"[:/]{{1,2}}" + r"(?P({path})" + r"(?P{name})(\.git|/)?)" + r"([@#](?P{rev}))?" + r"$".format( + user=pattern_formats["user"], + resource=pattern_formats["resource"], + path=pattern_formats["path"], + name=pattern_formats["name"], + rev=pattern_formats["rev"], + ) ), ] diff --git a/pyproject.toml b/pyproject.toml index 283091dfbc1..13e8199331b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "poetry" -version = "1.0.2" +version = "1.0.3" description = "Python dependency management and packaging made easy." authors = [ "Sébastien Eustace " diff --git a/tests/console/commands/env/test_list.py b/tests/console/commands/env/test_list.py index 1d79b8e57a7..e9112449307 100644 --- a/tests/console/commands/env/test_list.py +++ b/tests/console/commands/env/test_list.py @@ -7,7 +7,9 @@ from poetry.utils.toml_file import TomlFile -def test_none_activated(app, tmp_dir): +def test_none_activated(app, tmp_dir, mocker, env): + mocker.patch("poetry.utils.env.EnvManager.get", return_value=env) + app.poetry.config.merge({"virtualenvs": {"path": str(tmp_dir)}}) venv_name = EnvManager.generate_env_name( diff --git a/tests/console/commands/env/test_use.py b/tests/console/commands/env/test_use.py index 7f848494755..2b1967bf3b1 100644 --- a/tests/console/commands/env/test_use.py +++ b/tests/console/commands/env/test_use.py @@ -37,6 +37,7 @@ def check_output(cmd, *args, **kwargs): def test_activate_activates_non_existing_virtualenv_no_envs_file(app, tmp_dir, mocker): + mocker.stopall() if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -85,6 +86,7 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file(app, tmp_dir, m def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( app, tmp_dir, mocker ): + mocker.stopall() os.environ["VIRTUAL_ENV"] = "/environment/prefix" venv_name = EnvManager.generate_env_name( @@ -127,6 +129,7 @@ def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( def test_get_prefers_explicitly_activated_non_existing_virtualenvs_over_env_var( app, tmp_dir, mocker ): + mocker.stopall() os.environ["VIRTUAL_ENV"] = "/environment/prefix" venv_name = EnvManager.generate_env_name( diff --git a/tests/console/commands/test_run.py b/tests/console/commands/test_run.py index 45848222841..e8ae40aa606 100644 --- a/tests/console/commands/test_run.py +++ b/tests/console/commands/test_run.py @@ -1,11 +1,7 @@ from cleo.testers import CommandTester -from poetry.utils._compat import Path -from poetry.utils.env import MockEnv - -def test_run_passes_all_args(app, mocker): - env = MockEnv(path=Path("/prefix"), base=Path("/base/prefix"), is_venv=True) +def test_run_passes_all_args(app, mocker, env): mocker.patch("poetry.utils.env.EnvManager.get", return_value=env) command = app.find("run") diff --git a/tests/console/conftest.py b/tests/console/conftest.py index 6750fbbcfdd..58b8834aa61 100644 --- a/tests/console/conftest.py +++ b/tests/console/conftest.py @@ -13,6 +13,7 @@ from poetry.repositories import Repository as BaseRepository from poetry.repositories.exceptions import PackageNotFound from poetry.utils._compat import Path +from poetry.utils.env import MockEnv from poetry.utils.toml_file import TomlFile from tests.helpers import mock_clone from tests.helpers import mock_download @@ -28,8 +29,13 @@ def installed(): return BaseRepository() +@pytest.fixture +def env(): + return MockEnv(path=Path("/prefix"), base=Path("/base/prefix"), is_venv=True) + + @pytest.fixture(autouse=True) -def setup(mocker, installer, installed, config): +def setup(mocker, installer, installed, config, env): # Set Installer's installer p = mocker.patch("poetry.installation.installer.Installer._get_installer") p.return_value = installer @@ -51,6 +57,9 @@ def setup(mocker, installer, installed, config): # Patch download to not download anything but to just copy from fixtures mocker.patch("poetry.utils.inspector.Inspector.download", new=mock_download) + # Patch the virtual environment creation do actually do nothing + mocker.patch("poetry.utils.env.EnvManager.create_venv", return_value=env) + # Setting terminal width environ = dict(os.environ) os.environ["COLUMNS"] = "80" diff --git a/tests/masonry/test_api.py b/tests/masonry/test_api.py index c455d568296..2019a08212a 100644 --- a/tests/masonry/test_api.py +++ b/tests/masonry/test_api.py @@ -28,15 +28,15 @@ def cwd(directory): def test_get_requires_for_build_wheel(): - expected = ["cleo>=0.6.0,<0.7.0", "cachy[msgpack]>=0.2.0,<0.3.0"] + expected = [] with cwd(os.path.join(fixtures, "complete")): - api.get_requires_for_build_wheel() == expected + assert api.get_requires_for_build_wheel() == expected def test_get_requires_for_build_sdist(): - expected = ["cleo>=0.6.0,<0.7.0", "cachy[msgpack]>=0.2.0,<0.3.0"] + expected = [] with cwd(os.path.join(fixtures, "complete")): - api.get_requires_for_build_sdist() == expected + assert api.get_requires_for_build_sdist() == expected def test_build_wheel(): diff --git a/tests/packages/test_main.py b/tests/packages/test_main.py index 7cc93397f0d..586be5a31b8 100644 --- a/tests/packages/test_main.py +++ b/tests/packages/test_main.py @@ -191,3 +191,14 @@ def test_dependency_from_pep_508_with_url(): assert "django-utils" == dep.name assert dep.is_url() assert "https://example.com/django-utils-1.0.0.tar.gz" == dep.url + + +def test_dependency_from_pep_508_with_wheel_url(): + name = ( + "example_wheel @ https://example.com/example_wheel-14.0.2-py2.py3-none-any.whl" + ) + + dep = dependency_from_pep_508(name) + + assert "example-wheel" == dep.name + assert str(dep.constraint) == "14.0.2" diff --git a/tests/utils/test_password_manager.py b/tests/utils/test_password_manager.py index a5f89eb028e..675d38b7bbc 100644 --- a/tests/utils/test_password_manager.py +++ b/tests/utils/test_password_manager.py @@ -1,3 +1,5 @@ +import os + import pytest from keyring.backend import KeyringBackend @@ -208,3 +210,17 @@ def test_keyring_with_chainer_backend_and_not_compatible_only_should_be_unavaila key_ring = KeyRing("poetry") assert not key_ring.is_available() + + +def test_get_http_auth_from_environment_variables( + environ, config, mock_available_backend +): + os.environ["POETRY_HTTP_BASIC_FOO_USERNAME"] = "bar" + os.environ["POETRY_HTTP_BASIC_FOO_PASSWORD"] = "baz" + + manager = PasswordManager(config) + + auth = manager.get_http_auth("foo") + + assert "bar" == auth["username"] + assert "baz" == auth["password"] diff --git a/tests/vcs/test_git.py b/tests/vcs/test_git.py index 667294ee614..062272151fb 100644 --- a/tests/vcs/test_git.py +++ b/tests/vcs/test_git.py @@ -2,6 +2,7 @@ from poetry.vcs.git import Git from poetry.vcs.git import GitUrl +from poetry.vcs.git import ParsedUrl @pytest.mark.parametrize( @@ -19,6 +20,10 @@ "git+https://user@hostname/project/blah.git", GitUrl("https://user@hostname/project/blah.git", None), ), + ( + "git+https://user@hostname/project~_-.foo/blah~_-.bar.git", + GitUrl("https://user@hostname/project~_-.foo/blah~_-.bar.git", None), + ), ( "git+https://user@hostname:project/blah.git", GitUrl("https://user@hostname/project/blah.git", None), @@ -74,3 +79,183 @@ ) def test_normalize_url(url, normalized): assert normalized == Git.normalize_url(url) + + +@pytest.mark.parametrize( + "url, parsed", + [ + ( + "git+ssh://user@hostname:project.git#commit", + ParsedUrl( + "ssh", "hostname", ":project.git", "user", None, "project", "commit" + ), + ), + ( + "git+http://user@hostname/project/blah.git@commit", + ParsedUrl( + "http", "hostname", "/project/blah.git", "user", None, "blah", "commit" + ), + ), + ( + "git+https://user@hostname/project/blah.git", + ParsedUrl( + "https", "hostname", "/project/blah.git", "user", None, "blah", None + ), + ), + ( + "git+https://user@hostname/project~_-.foo/blah~_-.bar.git", + ParsedUrl( + "https", + "hostname", + "/project~_-.foo/blah~_-.bar.git", + "user", + None, + "blah~_-.bar", + None, + ), + ), + ( + "git+https://user@hostname:project/blah.git", + ParsedUrl( + "https", "hostname", ":project/blah.git", "user", None, "blah", None + ), + ), + ( + "git+ssh://git@github.com:sdispater/poetry.git#v1.0.27", + ParsedUrl( + "ssh", + "github.com", + ":sdispater/poetry.git", + "git", + None, + "poetry", + "v1.0.27", + ), + ), + ( + "git+ssh://git@github.com:/sdispater/poetry.git", + ParsedUrl( + "ssh", + "github.com", + ":/sdispater/poetry.git", + "git", + None, + "poetry", + None, + ), + ), + ( + "git+ssh://git@github.com:org/repo", + ParsedUrl("ssh", "github.com", ":org/repo", "git", None, "repo", None), + ), + ( + "git+ssh://git@github.com/org/repo", + ParsedUrl("ssh", "github.com", "/org/repo", "git", None, "repo", None), + ), + ( + "git+ssh://foo:22/some/path", + ParsedUrl("ssh", "foo", "/some/path", None, "22", "path", None), + ), + ( + "git@github.com:org/repo", + ParsedUrl(None, "github.com", ":org/repo", "git", None, "repo", None), + ), + ( + "git+https://github.com/sdispater/pendulum", + ParsedUrl( + "https", + "github.com", + "/sdispater/pendulum", + None, + None, + "pendulum", + None, + ), + ), + ( + "git+https://github.com/sdispater/pendulum#7a018f2d075b03a73409e8356f9b29c9ad4ea2c5", + ParsedUrl( + "https", + "github.com", + "/sdispater/pendulum", + None, + None, + "pendulum", + "7a018f2d075b03a73409e8356f9b29c9ad4ea2c5", + ), + ), + ( + "git+ssh://git@git.example.com:b/b.git#v1.0.0", + ParsedUrl("ssh", "git.example.com", ":b/b.git", "git", None, "b", "v1.0.0"), + ), + ( + "git+ssh://git@github.com:sdispater/pendulum.git#foo/bar", + ParsedUrl( + "ssh", + "github.com", + ":sdispater/pendulum.git", + "git", + None, + "pendulum", + "foo/bar", + ), + ), + ( + "git+file:///foo/bar.git", + ParsedUrl("file", None, "/foo/bar.git", None, None, "bar", None), + ), + ( + "git+file://C:\\Users\\hello\\testing.git#zkat/windows-files", + ParsedUrl( + "file", + "C", + ":\\Users\\hello\\testing.git", + None, + None, + "testing", + "zkat/windows-files", + ), + ), + ( + "git+https://git.example.com/sdispater/project/my_repo.git", + ParsedUrl( + "https", + "git.example.com", + "/sdispater/project/my_repo.git", + None, + None, + "my_repo", + None, + ), + ), + ( + "git+ssh://git@git.example.com:sdispater/project/my_repo.git", + ParsedUrl( + "ssh", + "git.example.com", + ":sdispater/project/my_repo.git", + "git", + None, + "my_repo", + None, + ), + ), + ], +) +def test_parse_url(url, parsed): + result = ParsedUrl.parse(url) + assert parsed.name == result.name + assert parsed.pathname == result.pathname + assert parsed.port == result.port + assert parsed.protocol == result.protocol + assert parsed.resource == result.resource + assert parsed.rev == result.rev + assert parsed.url == result.url + assert parsed.user == result.user + + +def test_parse_url_should_fail(): + url = "https://" + "@" * 64 + "!" + + with pytest.raises(ValueError): + ParsedUrl.parse(url) From 9e3f606454db3e1e19866cf2f94f0cc211a7e7e7 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Sun, 8 Mar 2020 20:52:54 +0100 Subject: [PATCH 17/18] pre-commit: replace isort mirror with isort upstream (#2118) The isort pre-commit mirror has been deprecated. This change updates configuration to use the upstream package repository instead of the mirror. --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b296497d29f..426692139b4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,8 +9,8 @@ repos: hooks: - id: flake8 - - repo: https://github.com/pre-commit/mirrors-isort - rev: v4.3.21 + - repo: https://github.com/timothycrosley/isort + rev: 4.3.21 hooks: - id: isort additional_dependencies: [toml] From 54701a1defda652d7a8246c7801964e8f9449e54 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Sun, 8 Mar 2020 20:58:06 +0100 Subject: [PATCH 18/18] Add cache list command (#1187) * Add poetry.locations.REPOSITORY_CACHE_DIR The repository cache directory is used in multiple places in the codebase. This change ensures that the value is reused. * Add cache list command This introduces a new cache sub-command that lists all available caches. Relates-to: #1162 --- docs/docs/cli.md | 12 +++++ poetry/console/commands/cache/cache.py | 4 +- poetry/console/commands/cache/clear.py | 8 ++-- poetry/console/commands/cache/list.py | 21 +++++++++ poetry/locations.py | 3 ++ poetry/repositories/legacy_repository.py | 4 +- poetry/repositories/pypi_repository.py | 4 +- tests/console/commands/test_cache.py | 60 ++++++++++++++++++++++++ 8 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 poetry/console/commands/cache/list.py create mode 100644 tests/console/commands/test_cache.py diff --git a/docs/docs/cli.md b/docs/docs/cli.md index 89d19a8cc18..63d63fbc990 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -452,3 +452,15 @@ The `env` command regroups sub commands to interact with the virtualenvs associated with a specific project. See [Managing environments](./managing-environments.md) for more information about these commands. + +## cache + +The `cache` command regroups sub commands to interact with Poetry's cache. + +### cache list + +The `cache list` command lists Poetry's available caches. + +```bash +poetry cache list +``` diff --git a/poetry/console/commands/cache/cache.py b/poetry/console/commands/cache/cache.py index 469eb56be05..695e27e0af7 100644 --- a/poetry/console/commands/cache/cache.py +++ b/poetry/console/commands/cache/cache.py @@ -1,3 +1,5 @@ +from poetry.console.commands.cache.list import CacheListCommand + from ..command import Command from .clear import CacheClearCommand @@ -7,7 +9,7 @@ class CacheCommand(Command): name = "cache" description = "Interact with Poetry's cache" - commands = [CacheClearCommand()] + commands = [CacheClearCommand(), CacheListCommand()] def handle(self): return self.call("help", self._config.name) diff --git a/poetry/console/commands/cache/clear.py b/poetry/console/commands/cache/clear.py index 347f534afc0..c46dab01281 100644 --- a/poetry/console/commands/cache/clear.py +++ b/poetry/console/commands/cache/clear.py @@ -16,19 +16,17 @@ class CacheClearCommand(Command): def handle(self): from cachy import CacheManager - from poetry.locations import CACHE_DIR - from poetry.utils._compat import Path + from poetry.locations import REPOSITORY_CACHE_DIR cache = self.argument("cache") parts = cache.split(":") root = parts[0] - base_cache = Path(CACHE_DIR) / "cache" / "repositories" - cache_dir = base_cache / root + cache_dir = REPOSITORY_CACHE_DIR / root try: - cache_dir.relative_to(base_cache) + cache_dir.relative_to(REPOSITORY_CACHE_DIR) except ValueError: raise ValueError("{} is not a valid repository cache".format(root)) diff --git a/poetry/console/commands/cache/list.py b/poetry/console/commands/cache/list.py new file mode 100644 index 00000000000..6a030fa2eba --- /dev/null +++ b/poetry/console/commands/cache/list.py @@ -0,0 +1,21 @@ +import os + +from ..command import Command + + +class CacheListCommand(Command): + + name = "list" + description = "List Poetry's caches." + + def handle(self): + from poetry.locations import REPOSITORY_CACHE_DIR + + if os.path.exists(str(REPOSITORY_CACHE_DIR)): + caches = list(sorted(REPOSITORY_CACHE_DIR.iterdir())) + if caches: + for cache in caches: + self.line("{}".format(cache.name)) + return 0 + + self.line("No caches found") diff --git a/poetry/locations.py b/poetry/locations.py index 17fb4d42a99..003950d500d 100644 --- a/poetry/locations.py +++ b/poetry/locations.py @@ -1,6 +1,9 @@ +from .utils._compat import Path from .utils.appdirs import user_cache_dir from .utils.appdirs import user_config_dir CACHE_DIR = user_cache_dir("pypoetry") CONFIG_DIR = user_config_dir("pypoetry") + +REPOSITORY_CACHE_DIR = Path(CACHE_DIR) / "cache" / "repositories" diff --git a/poetry/repositories/legacy_repository.py b/poetry/repositories/legacy_repository.py index 56748911fe1..b228fc78764 100644 --- a/poetry/repositories/legacy_repository.py +++ b/poetry/repositories/legacy_repository.py @@ -15,7 +15,7 @@ import poetry.packages -from poetry.locations import CACHE_DIR +from poetry.locations import REPOSITORY_CACHE_DIR from poetry.packages import Package from poetry.packages import dependency_from_pep_508 from poetry.packages.utils.link import Link @@ -174,7 +174,7 @@ def __init__( self._client_cert = client_cert self._cert = cert self._inspector = Inspector() - self._cache_dir = Path(CACHE_DIR) / "cache" / "repositories" / name + self._cache_dir = REPOSITORY_CACHE_DIR / name self._cache = CacheManager( { "default": "releases", diff --git a/poetry/repositories/pypi_repository.py b/poetry/repositories/pypi_repository.py index cdfaf84cebd..9410f124299 100644 --- a/poetry/repositories/pypi_repository.py +++ b/poetry/repositories/pypi_repository.py @@ -15,7 +15,7 @@ from requests import session from requests.exceptions import TooManyRedirects -from poetry.locations import CACHE_DIR +from poetry.locations import REPOSITORY_CACHE_DIR from poetry.packages import Package from poetry.packages import dependency_from_pep_508 from poetry.packages.utils.link import Link @@ -55,7 +55,7 @@ def __init__(self, url="https://pypi.org/", disable_cache=False, fallback=True): self._disable_cache = disable_cache self._fallback = fallback - release_cache_dir = Path(CACHE_DIR) / "cache" / "repositories" / "pypi" + release_cache_dir = REPOSITORY_CACHE_DIR / "pypi" self._cache = CacheManager( { "default": "releases", diff --git a/tests/console/commands/test_cache.py b/tests/console/commands/test_cache.py new file mode 100644 index 00000000000..8a22eee7f1b --- /dev/null +++ b/tests/console/commands/test_cache.py @@ -0,0 +1,60 @@ +import uuid + +import pytest + +from cleo.testers import CommandTester + + +@pytest.fixture +def repository_cache_dir(monkeypatch, tmpdir): + import poetry.locations + from poetry.utils._compat import Path + + path = Path(str(tmpdir)) + monkeypatch.setattr(poetry.locations, "REPOSITORY_CACHE_DIR", path) + return path + + +@pytest.fixture +def repository_one(): + return "01_{}".format(uuid.uuid4()) + + +@pytest.fixture +def repository_two(): + return "02_{}".format(uuid.uuid4()) + + +@pytest.fixture +def mock_caches(repository_cache_dir, repository_one, repository_two): + (repository_cache_dir / repository_one).mkdir() + (repository_cache_dir / repository_two).mkdir() + + +def test_cache_list(app, mock_caches, repository_one, repository_two): + command = app.find("cache list") + tester = CommandTester(command) + + tester.execute() + + expected = """\ +{} +{} +""".format( + repository_one, repository_two + ) + + assert expected == tester.io.fetch_output() + + +def test_cache_list_empty(app, repository_cache_dir): + command = app.find("cache list") + tester = CommandTester(command) + + tester.execute() + + expected = """\ +No caches found +""" + + assert expected == tester.io.fetch_output()