From 0db07bc81b989c5c4933cf019fec7a4f93fbd744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 6 Oct 2020 00:12:18 +0200 Subject: [PATCH 01/77] Remove executable bits from Python files without shebangs --- poetry/core/factory.py | 0 poetry/core/packages/__init__.py | 0 poetry/core/packages/dependency.py | 0 poetry/core/packages/directory_dependency.py | 0 poetry/core/packages/file_dependency.py | 0 poetry/core/packages/package.py | 0 poetry/core/packages/specification.py | 0 poetry/core/packages/url_dependency.py | 0 poetry/core/packages/vcs_dependency.py | 0 9 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 poetry/core/factory.py mode change 100755 => 100644 poetry/core/packages/__init__.py mode change 100755 => 100644 poetry/core/packages/dependency.py mode change 100755 => 100644 poetry/core/packages/directory_dependency.py mode change 100755 => 100644 poetry/core/packages/file_dependency.py mode change 100755 => 100644 poetry/core/packages/package.py mode change 100755 => 100644 poetry/core/packages/specification.py mode change 100755 => 100644 poetry/core/packages/url_dependency.py mode change 100755 => 100644 poetry/core/packages/vcs_dependency.py diff --git a/poetry/core/factory.py b/poetry/core/factory.py old mode 100755 new mode 100644 diff --git a/poetry/core/packages/__init__.py b/poetry/core/packages/__init__.py old mode 100755 new mode 100644 diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py old mode 100755 new mode 100644 diff --git a/poetry/core/packages/directory_dependency.py b/poetry/core/packages/directory_dependency.py old mode 100755 new mode 100644 diff --git a/poetry/core/packages/file_dependency.py b/poetry/core/packages/file_dependency.py old mode 100755 new mode 100644 diff --git a/poetry/core/packages/package.py b/poetry/core/packages/package.py old mode 100755 new mode 100644 diff --git a/poetry/core/packages/specification.py b/poetry/core/packages/specification.py old mode 100755 new mode 100644 diff --git a/poetry/core/packages/url_dependency.py b/poetry/core/packages/url_dependency.py old mode 100755 new mode 100644 diff --git a/poetry/core/packages/vcs_dependency.py b/poetry/core/packages/vcs_dependency.py old mode 100755 new mode 100644 From b0d90868f832e6af241e0adb91191a99f5d52847 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 13 Oct 2020 23:26:48 +0200 Subject: [PATCH 02/77] utils: fix canonical package name regex Relates-to: python-poetry/poetry#3132 Relates-to: python-poetry/poetry#3200 --- poetry/core/utils/helpers.py | 2 +- tests/utils/test_helpers.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/poetry/core/utils/helpers.py b/poetry/core/utils/helpers.py index b4bf7902f..920e14288 100644 --- a/poetry/core/utils/helpers.py +++ b/poetry/core/utils/helpers.py @@ -17,7 +17,7 @@ from collections import Mapping -_canonicalize_regex = re.compile("[-_]+") +_canonicalize_regex = re.compile(r"[-_.]+") def canonicalize_name(name): # type: (str) -> str diff --git a/tests/utils/test_helpers.py b/tests/utils/test_helpers.py index a2e423c1d..267e83296 100644 --- a/tests/utils/test_helpers.py +++ b/tests/utils/test_helpers.py @@ -1,3 +1,6 @@ +import pytest + +from poetry.core.utils.helpers import canonicalize_name from poetry.core.utils.helpers import parse_requires @@ -52,3 +55,10 @@ def test_parse_requires(): 'isort@ git+git://github.com/timothycrosley/isort.git@e63ae06ec7d70b06df9e528357650281a3d3ec22#egg=isort ; extra == "dev"', ] assert result == expected + + +@pytest.mark.parametrize( + "raw", ["a-b-c", "a.b-c", "a.b.c", "a_b-c", "a_b_c", "a-b_c", "a.b_c", "a-b.c"] +) +def test_utils_helpers_canonical_names(raw): + assert canonicalize_name(raw) == "a-b-c" From fa522603bc2aff99127adaf585046f0ad50db973 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Fri, 16 Oct 2020 21:47:11 +0200 Subject: [PATCH 03/77] Fix PEP 508 representation of dependency w/o extras --- poetry/core/packages/dependency.py | 4 +++- tests/packages/test_dependency.py | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py index c648a4b6a..1ffe0450b 100644 --- a/poetry/core/packages/dependency.py +++ b/poetry/core/packages/dependency.py @@ -224,7 +224,9 @@ def to_pep_508(self, with_extras=True): # type: (bool) -> str if not with_extras: marker = marker.without_extras() - if not marker.is_empty(): + # we re-check for any marker here since the without extra marker might + # return an any marker again + if not marker.is_empty() and not marker.is_any(): markers.append(str(marker)) has_extras = "extra" in convert_markers(marker) diff --git a/tests/packages/test_dependency.py b/tests/packages/test_dependency.py index b903c6fbf..8591e72f5 100644 --- a/tests/packages/test_dependency.py +++ b/tests/packages/test_dependency.py @@ -2,6 +2,7 @@ from poetry.core.packages import Dependency from poetry.core.packages import Package +from poetry.core.packages import dependency_from_pep_508 def test_accepts(): @@ -88,6 +89,9 @@ def test_to_pep_508_in_extras(): result = dependency.to_pep_508() assert result == 'Django (>=1.23,<2.0); extra == "foo"' + result = dependency.to_pep_508(with_extras=False) + assert result == "Django (>=1.23,<2.0)" + dependency.in_extras.append("bar") result = dependency.to_pep_508() @@ -105,6 +109,23 @@ def test_to_pep_508_in_extras(): 'and (extra == "foo" or extra == "bar")' ) + result = dependency.to_pep_508(with_extras=False) + assert result == ( + "Django (>=1.23,<2.0); " + 'python_version >= "2.7" and python_version < "2.8" ' + 'or python_version >= "3.6" and python_version < "4.0"' + ) + + +def test_to_pep_508_in_extras_parsed(): + dependency = dependency_from_pep_508('foo[bar] (>=1.23,<2.0) ; extra == "baz"') + + result = dependency.to_pep_508() + assert result == 'foo[bar] (>=1.23,<2.0); extra == "baz"' + + result = dependency.to_pep_508(with_extras=False) + assert result == "foo[bar] (>=1.23,<2.0)" + def test_to_pep_508_with_single_version_excluded(): dependency = Dependency("foo", "!=1.2.3") From 727907df37016c1451b68b00d3dc2268956c3cc4 Mon Sep 17 00:00:00 2001 From: Victor Tso Date: Thu, 22 Oct 2020 12:36:26 -0400 Subject: [PATCH 04/77] Ignore dev dependencies for PEP 517 builds This change allows for development dependencies to be ignored creating `Poetry` instances. This is used when PEP 517 artifacts are built as they are not required in this scenario. Relates-to: python-poetry/poetry#2174 --- poetry/core/factory.py | 6 ++- poetry/core/masonry/api.py | 6 +-- .../pyproject.toml | 13 ++++++ .../fixtures/with_bad_path_dep/pyproject.toml | 9 ++++ .../with_bad_path_dep/__init__.py | 0 .../with_bad_path_dev_dep/pyproject.toml | 11 +++++ .../with_bad_path_dev_dep/__init__.py | 0 tests/masonry/test_api.py | 45 +++++++++++++++++++ tests/test_factory.py | 18 ++++++++ 9 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 tests/fixtures/project_with_invalid_dev_deps/pyproject.toml create mode 100644 tests/masonry/builders/fixtures/with_bad_path_dep/pyproject.toml create mode 100644 tests/masonry/builders/fixtures/with_bad_path_dep/with_bad_path_dep/__init__.py create mode 100644 tests/masonry/builders/fixtures/with_bad_path_dev_dep/pyproject.toml create mode 100644 tests/masonry/builders/fixtures/with_bad_path_dev_dep/with_bad_path_dev_dep/__init__.py diff --git a/poetry/core/factory.py b/poetry/core/factory.py index f14aea7ba..d248dbb74 100644 --- a/poetry/core/factory.py +++ b/poetry/core/factory.py @@ -27,7 +27,9 @@ class Factory(object): Factory class to create various elements needed by Poetry. """ - def create_poetry(self, cwd=None): # type: (Optional[Path]) -> Poetry + def create_poetry( + self, cwd=None, with_dev=True + ): # type: (Optional[Path]. bool) -> Poetry poetry_file = self.locate(cwd) local_config = PyProjectTOML(path=poetry_file).poetry_config @@ -91,7 +93,7 @@ def create_poetry(self, cwd=None): # type: (Optional[Path]) -> Poetry self.create_dependency(name, constraint, root_dir=package.root_dir) ) - if "dev-dependencies" in local_config: + if with_dev and "dev-dependencies" in local_config: for name, constraint in local_config["dev-dependencies"].items(): if isinstance(constraint, list): for _constraint in constraint: diff --git a/poetry/core/masonry/api.py b/poetry/core/masonry/api.py index aae519789..4907e10d4 100644 --- a/poetry/core/masonry/api.py +++ b/poetry/core/masonry/api.py @@ -31,7 +31,7 @@ def get_requires_for_build_wheel(config_settings=None): def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None): - poetry = Factory().create_poetry(Path(".").resolve()) + poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False) builder = WheelBuilder(poetry) dist_info = Path(metadata_directory, builder.dist_info) @@ -52,14 +52,14 @@ def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None): def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): """Builds a wheel, places it in wheel_directory""" - poetry = Factory().create_poetry(Path(".").resolve()) + poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False) return unicode(WheelBuilder.make_in(poetry, Path(wheel_directory))) def build_sdist(sdist_directory, config_settings=None): """Builds an sdist, places it in sdist_directory""" - poetry = Factory().create_poetry(Path(".").resolve()) + poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False) path = SdistBuilder(poetry).build(Path(sdist_directory)) diff --git a/tests/fixtures/project_with_invalid_dev_deps/pyproject.toml b/tests/fixtures/project_with_invalid_dev_deps/pyproject.toml new file mode 100644 index 000000000..1d0b5a846 --- /dev/null +++ b/tests/fixtures/project_with_invalid_dev_deps/pyproject.toml @@ -0,0 +1,13 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = ["Awesome Hacker "] +license = "MIT" + +[tool.poetry.dependencies] + +[tool.poetry.extras] + +[tool.poetry.dev-dependencies] +mylib = { path = "../mylib", develop = true} diff --git a/tests/masonry/builders/fixtures/with_bad_path_dep/pyproject.toml b/tests/masonry/builders/fixtures/with_bad_path_dep/pyproject.toml new file mode 100644 index 000000000..3c7edf13a --- /dev/null +++ b/tests/masonry/builders/fixtures/with_bad_path_dep/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "with_bad_path_dep" +version = "1.2.3" +description = "Some description." +authors = ["Awesome Hacker "] + +[tool.poetry.dependencies] +python = "^3.6" +bogus = { path = "../only/in/dev", develop = true } diff --git a/tests/masonry/builders/fixtures/with_bad_path_dep/with_bad_path_dep/__init__.py b/tests/masonry/builders/fixtures/with_bad_path_dep/with_bad_path_dep/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/masonry/builders/fixtures/with_bad_path_dev_dep/pyproject.toml b/tests/masonry/builders/fixtures/with_bad_path_dev_dep/pyproject.toml new file mode 100644 index 000000000..921d93af5 --- /dev/null +++ b/tests/masonry/builders/fixtures/with_bad_path_dev_dep/pyproject.toml @@ -0,0 +1,11 @@ +[tool.poetry] +name = "with_bad_path_dev_dep" +version = "1.2.3" +description = "Some description." +authors = ["Awesome Hacker "] + +[tool.poetry.dependencies] +python = "^3.6" + +[tool.poetry.dev-dependencies] +bogus = { path = "../only/in/dev", develop = true } diff --git a/tests/masonry/builders/fixtures/with_bad_path_dev_dep/with_bad_path_dev_dep/__init__.py b/tests/masonry/builders/fixtures/with_bad_path_dev_dep/with_bad_path_dev_dep/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/masonry/test_api.py b/tests/masonry/test_api.py index 9f8cfc95c..adbbddeba 100644 --- a/tests/masonry/test_api.py +++ b/tests/masonry/test_api.py @@ -65,6 +65,21 @@ def test_build_wheel_with_include(): ) +def test_build_wheel_with_bad_path_dev_dep_succeeds(): + with temporary_directory() as tmp_dir, cwd( + os.path.join(fixtures, "with_bad_path_dev_dep") + ): + api.build_wheel(tmp_dir) + + +def test_build_wheel_with_bad_path_dep_fails(): + with pytest.raises(ValueError) as err, temporary_directory() as tmp_dir, cwd( + os.path.join(fixtures, "with_bad_path_dep") + ): + api.build_wheel(tmp_dir) + assert "does not exist" in str(err.value) + + @pytest.mark.skipif( sys.platform == "win32" and sys.version_info <= (3, 6) @@ -101,6 +116,21 @@ def test_build_sdist_with_include(): ) +def test_build_sdist_with_bad_path_dev_dep_succeeds(): + with temporary_directory() as tmp_dir, cwd( + os.path.join(fixtures, "with_bad_path_dev_dep") + ): + api.build_sdist(tmp_dir) + + +def test_build_sdist_with_bad_path_dep_fails(): + with pytest.raises(ValueError) as err, temporary_directory() as tmp_dir, cwd( + os.path.join(fixtures, "with_bad_path_dep") + ): + api.build_sdist(tmp_dir) + assert "does not exist" in str(err.value) + + def test_prepare_metadata_for_build_wheel(): entry_points = """\ [console_scripts] @@ -170,3 +200,18 @@ def test_prepare_metadata_for_build_wheel(): with (dist_info / "METADATA").open(encoding="utf-8") as f: assert metadata == decode(f.read()) + + +def test_prepare_metadata_for_build_wheel_with_bad_path_dev_dep_succeeds(): + with temporary_directory() as tmp_dir, cwd( + os.path.join(fixtures, "with_bad_path_dev_dep") + ): + api.prepare_metadata_for_build_wheel(tmp_dir) + + +def test_prepare_metadata_for_build_wheel_with_bad_path_dep_succeeds(): + with pytest.raises(ValueError) as err, temporary_directory() as tmp_dir, cwd( + os.path.join(fixtures, "with_bad_path_dep") + ): + api.prepare_metadata_for_build_wheel(tmp_dir) + assert "does not exist" in str(err.value) diff --git a/tests/test_factory.py b/tests/test_factory.py index 4c4e5da02..525fadb49 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -199,3 +199,21 @@ def test_create_poetry_fails_on_invalid_configuration(): - 'description' is a required property """ assert expected == str(e.value) + + +def test_create_poetry_omits_dev_dependencies_iff_with_dev_is_false(): + poetry = Factory().create_poetry(fixtures_dir / "sample_project", with_dev=False) + assert not any(r for r in poetry.package.dev_requires if "pytest" in str(r)) + + poetry = Factory().create_poetry(fixtures_dir / "sample_project") + assert any(r for r in poetry.package.dev_requires if "pytest" in str(r)) + + +def test_create_poetry_fails_with_invalid_dev_dependencies_iff_with_dev_is_true(): + with pytest.raises(ValueError) as err: + Factory().create_poetry(fixtures_dir / "project_with_invalid_dev_deps") + assert "does not exist" in str(err.value) + + Factory().create_poetry( + fixtures_dir / "project_with_invalid_dev_deps", with_dev=False + ) From 7004040c64afb47c4fff1baa4fa07e73d3822d28 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Sat, 24 Oct 2020 13:03:01 -0400 Subject: [PATCH 05/77] Make sdist deterministic by setting gzip mtime to 0 --- poetry/core/masonry/builders/sdist.py | 2 +- tests/masonry/builders/test_sdist.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/poetry/core/masonry/builders/sdist.py b/poetry/core/masonry/builders/sdist.py index 981b63b2a..5e954cafa 100644 --- a/poetry/core/masonry/builders/sdist.py +++ b/poetry/core/masonry/builders/sdist.py @@ -66,7 +66,7 @@ def build(self, target_dir=None): # type: (Path) -> Path target = target_dir / "{}-{}.tar.gz".format( self._package.pretty_name, self._meta.version ) - gz = GzipFile(target.as_posix(), mode="wb") + gz = GzipFile(target.as_posix(), mode="wb", mtime=0) tar = tarfile.TarFile( target.as_posix(), mode="w", fileobj=gz, format=tarfile.PAX_FORMAT ) diff --git a/tests/masonry/builders/test_sdist.py b/tests/masonry/builders/test_sdist.py index 02f0a65ca..5c39eb57a 100644 --- a/tests/masonry/builders/test_sdist.py +++ b/tests/masonry/builders/test_sdist.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import ast +import gzip import shutil import tarfile @@ -587,3 +588,18 @@ def test_sdist_disable_setup_py(): "my-package-1.2.3/PKG-INFO", "my-package-1.2.3/my_package/__init__.py", } + + +def test_sdist_mtime_zero(): + poetry = Factory().create_poetry(project("module1")) + + builder = SdistBuilder(poetry) + builder.build() + + sdist = fixtures_dir / "module1" / "dist" / "module1-0.1.tar.gz" + + assert sdist.exists() + + with gzip.open(str(sdist), "rb") as gz: + gz.read(100) + assert gz.mtime == 0 From d61b964dc9c113c400ed80eaea27676cc1943af6 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Wed, 18 Nov 2020 18:01:46 +0100 Subject: [PATCH 06/77] actions: remove use of set-env command Ref: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/ --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1f38ebd01..9bff305de 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -42,7 +42,7 @@ jobs: run: | curl -fsS -o get-poetry.py https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py python get-poetry.py --preview -y - echo "::set-env name=PATH::$HOME/.poetry/bin:$PATH" + echo "$HOME/.poetry/bin" >> $GITHUB_PATH - name: Configure poetry shell: bash From abe9fe59fd7a0b181463461273c16af8a258a969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 18 Nov 2020 11:10:26 +0100 Subject: [PATCH 07/77] Extend the wheel Tag regex in tests to match cp3_10 and cp310 Fixes https://github.com/python-poetry/poetry-core/issues/110 --- tests/masonry/builders/test_complete.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/masonry/builders/test_complete.py b/tests/masonry/builders/test_complete.py index 467ffdd43..4248a87a2 100644 --- a/tests/masonry/builders/test_complete.py +++ b/tests/masonry/builders/test_complete.py @@ -79,7 +79,7 @@ def test_wheel_c_extension(): Wheel-Version: 1.0 Generator: poetry {} Root-Is-Purelib: false -Tag: cp[23]\\d-cp[23]\\dm?u?-.+ +Tag: cp[23]_?\\d+-cp[23]_?\\d+m?u?-.+ $""".format( __version__ ), @@ -136,7 +136,7 @@ def test_wheel_c_extension_with_no_setup(): Wheel-Version: 1.0 Generator: poetry {} Root-Is-Purelib: false -Tag: cp[23]\\d-cp[23]\\dm?u?-.+ +Tag: cp[23]_?\\d+-cp[23]_?\\d+m?u?-.+ $""".format( __version__ ), @@ -193,7 +193,7 @@ def test_wheel_c_extension_src_layout(): Wheel-Version: 1.0 Generator: poetry {} Root-Is-Purelib: false -Tag: cp[23]\\d-cp[23]\\dm?u?-.+ +Tag: cp[23]_?\\d+-cp[23]_?\\d+m?u?-.+ $""".format( __version__ ), From 79bc4cb632df299d6604f866b1a96ad25a58ac52 Mon Sep 17 00:00:00 2001 From: Joshua Cannon Date: Thu, 31 Dec 2020 07:54:48 -0600 Subject: [PATCH 08/77] Simplify temporary_directory and use safe_rmtree (#119) * Simplify temporary_directory and use safe_rmtree --- poetry/core/utils/helpers.py | 14 +++----------- tests/utils/test_helpers.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/poetry/core/utils/helpers.py b/poetry/core/utils/helpers.py index 920e14288..f2de92592 100644 --- a/poetry/core/utils/helpers.py +++ b/poetry/core/utils/helpers.py @@ -34,17 +34,9 @@ def normalize_version(version): # type: (str) -> str @contextmanager def temporary_directory(*args, **kwargs): - try: - from tempfile import TemporaryDirectory - - with TemporaryDirectory(*args, **kwargs) as name: - yield name - except ImportError: - name = tempfile.mkdtemp(*args, **kwargs) - try: - yield name - finally: - shutil.rmtree(name) + name = tempfile.mkdtemp(*args, **kwargs) + yield name + safe_rmtree(name) def parse_requires(requires): # type: (str) -> List[str] diff --git a/tests/utils/test_helpers.py b/tests/utils/test_helpers.py index 267e83296..8ca49b9a5 100644 --- a/tests/utils/test_helpers.py +++ b/tests/utils/test_helpers.py @@ -1,7 +1,12 @@ +import os + +from stat import S_IREAD + import pytest from poetry.core.utils.helpers import canonicalize_name from poetry.core.utils.helpers import parse_requires +from poetry.core.utils.helpers import temporary_directory def test_parse_requires(): @@ -62,3 +67,14 @@ def test_parse_requires(): ) def test_utils_helpers_canonical_names(raw): assert canonicalize_name(raw) == "a-b-c" + + +def test_utils_helpers_temporary_directory_readonly_file(): + with temporary_directory() as temp_dir: + readonly_filename = os.path.join(temp_dir, "file.txt") + with open(readonly_filename, "w+") as readonly_file: + readonly_file.write("Poetry rocks!") + os.chmod(str(readonly_filename), S_IREAD) + + assert not os.path.exists(temp_dir) + assert not os.path.exists(readonly_filename) From 8997e3a49da6e07c96812535e7c1e6c73104bf3d Mon Sep 17 00:00:00 2001 From: Kurt McKee Date: Wed, 6 Jan 2021 20:32:43 -0600 Subject: [PATCH 09/77] Demonstrate that the wheel builder does not close wheels explicitly Related to python-poetry/poetry#3545 --- tests/masonry/builders/test_wheel.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/masonry/builders/test_wheel.py b/tests/masonry/builders/test_wheel.py index a8bbdd527..37543779b 100644 --- a/tests/masonry/builders/test_wheel.py +++ b/tests/masonry/builders/test_wheel.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import os import shutil import zipfile @@ -270,3 +271,24 @@ def test_default_src_with_excluded_data(mocker): assert "my_package/data/data1.txt" in names assert "my_package/data/sub_data/data2.txt" not in names assert "my_package/data/sub_data/data3.txt" in names + + +def test_wheel_file_is_closed(monkeypatch): + """Confirm that wheel zip files are explicitly closed.""" + + # Using a list is a hack for Python 2.7 compatibility. + fd_file = [None] + + real_fdopen = os.fdopen + + def capturing_fdopen(*args, **kwargs): + fd_file[0] = real_fdopen(*args, **kwargs) + return fd_file[0] + + monkeypatch.setattr(os, "fdopen", capturing_fdopen) + + module_path = fixtures_dir / "module1" + WheelBuilder.make(Factory().create_poetry(module_path)) + + assert fd_file[0] is not None + assert fd_file[0].closed From 124a879e722d94d49cf5b4ea8a326eeec10737b2 Mon Sep 17 00:00:00 2001 From: Kurt McKee Date: Wed, 6 Jan 2021 20:53:25 -0600 Subject: [PATCH 10/77] Explicitly close the wheel temp file after it's written This resolves a crash that occurs on PyPy3 on Windows Closes python-poetry/poetry#3545 --- poetry/core/masonry/builders/wheel.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/poetry/core/masonry/builders/wheel.py b/poetry/core/masonry/builders/wheel.py index 1d0b13e18..969de059e 100644 --- a/poetry/core/masonry/builders/wheel.py +++ b/poetry/core/masonry/builders/wheel.py @@ -78,18 +78,19 @@ def build(self): new_mode = normalize_file_permissions(st_mode) os.chmod(temp_path, new_mode) - with zipfile.ZipFile( - os.fdopen(fd, "w+b"), mode="w", compression=zipfile.ZIP_DEFLATED - ) as zip_file: - if not self._poetry.package.build_should_generate_setup(): - self._build(zip_file) - self._copy_module(zip_file) - else: - self._copy_module(zip_file) - self._build(zip_file) - - self._write_metadata(zip_file) - self._write_record(zip_file) + with os.fdopen(fd, "w+b") as fd_file: + with zipfile.ZipFile( + fd_file, mode="w", compression=zipfile.ZIP_DEFLATED + ) as zip_file: + if not self._poetry.package.build_should_generate_setup(): + self._build(zip_file) + self._copy_module(zip_file) + else: + self._copy_module(zip_file) + self._build(zip_file) + + self._write_metadata(zip_file) + self._write_record(zip_file) wheel_path = dist_dir / self.wheel_filename if wheel_path.exists(): From 832b10b03ac08dc96531002e99f2cd0682ea45c1 Mon Sep 17 00:00:00 2001 From: Audun Skaugen Date: Wed, 13 Jan 2021 19:46:29 +0100 Subject: [PATCH 11/77] Fix #3258 Paths for generated code that's excluded by vcs, but then included explicitly, were wrong because they appared in the exclude list during Builder.find_excluded_files(). I changed find_excluded_files to not exclude files which are explicitly included, by taking a set difference. Added a test for this case. --- poetry/core/masonry/builders/builder.py | 10 +++++++++- .../lib/my_package/__init__.py | 0 .../lib/my_package/generated.py | 0 .../include_excluded_code/pyproject.toml | 20 +++++++++++++++++++ tests/masonry/builders/test_wheel.py | 13 ++++++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/masonry/builders/fixtures/include_excluded_code/lib/my_package/__init__.py create mode 100644 tests/masonry/builders/fixtures/include_excluded_code/lib/my_package/generated.py create mode 100644 tests/masonry/builders/fixtures/include_excluded_code/pyproject.toml diff --git a/poetry/core/masonry/builders/builder.py b/poetry/core/masonry/builders/builder.py index c6f6d9cd6..b439b4616 100644 --- a/poetry/core/masonry/builders/builder.py +++ b/poetry/core/masonry/builders/builder.py @@ -111,7 +111,15 @@ def find_excluded_files(self): # type: () -> Set[str] Path(excluded).relative_to(self._path).as_posix() ) - ignored = vcs_ignored_files | explicitely_excluded + explicitely_included = set() + for inc in self._package.include: + included_glob = inc['path'] + for included in self._path.glob(str(included_glob)): + explicitely_included.add( + Path(included).relative_to(self._path).as_posix() + ) + + ignored = (vcs_ignored_files | explicitely_excluded) - explicitely_included result = set() for file in ignored: result.add(file) diff --git a/tests/masonry/builders/fixtures/include_excluded_code/lib/my_package/__init__.py b/tests/masonry/builders/fixtures/include_excluded_code/lib/my_package/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/masonry/builders/fixtures/include_excluded_code/lib/my_package/generated.py b/tests/masonry/builders/fixtures/include_excluded_code/lib/my_package/generated.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/masonry/builders/fixtures/include_excluded_code/pyproject.toml b/tests/masonry/builders/fixtures/include_excluded_code/pyproject.toml new file mode 100644 index 000000000..379b7dbc1 --- /dev/null +++ b/tests/masonry/builders/fixtures/include_excluded_code/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "my_package" +version = "0.1.0" +description = "" +authors = ["Audun Skaugen "] + +packages = [{include='my_package', from='lib'}] +# Simulate excluding due to .gitignore +exclude = ['lib/my_package/generated.py'] +# Include again +include = ['lib/my_package/generated.py'] + +[tool.poetry.dependencies] +python = "^3.8" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/masonry/builders/test_wheel.py b/tests/masonry/builders/test_wheel.py index a8bbdd527..c0cc8c1ce 100644 --- a/tests/masonry/builders/test_wheel.py +++ b/tests/masonry/builders/test_wheel.py @@ -96,6 +96,18 @@ def test_wheel_excluded_nested_data(): 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_include_excluded_code(): + module_path = fixtures_dir / "include_excluded_code" + poetry = Factory().create_poetry(module_path) + wb = WheelBuilder(poetry) + wb.build() + whl = module_path / "dist" / wb.wheel_filename + assert whl.exists() + + with zipfile.ZipFile(str(whl)) as z: + assert "my_package/__init__.py" in z.namelist() + assert "my_package/generated.py" in z.namelist() + assert "lib/my_package/generated.py" not in z.namelist() def test_wheel_localversionlabel(): module_path = fixtures_dir / "localversionlabel" @@ -270,3 +282,4 @@ def test_default_src_with_excluded_data(mocker): assert "my_package/data/data1.txt" in names assert "my_package/data/sub_data/data2.txt" not in names assert "my_package/data/sub_data/data3.txt" in names + From b2d24fd7fbf7f7b7b4123067618b03228b15cd0b Mon Sep 17 00:00:00 2001 From: Audun Skaugen Date: Wed, 13 Jan 2021 19:58:09 +0100 Subject: [PATCH 12/77] Black formatting --- poetry/core/masonry/builders/builder.py | 2 +- tests/masonry/builders/test_wheel.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/poetry/core/masonry/builders/builder.py b/poetry/core/masonry/builders/builder.py index b439b4616..78ef49de4 100644 --- a/poetry/core/masonry/builders/builder.py +++ b/poetry/core/masonry/builders/builder.py @@ -113,7 +113,7 @@ def find_excluded_files(self): # type: () -> Set[str] explicitely_included = set() for inc in self._package.include: - included_glob = inc['path'] + included_glob = inc["path"] for included in self._path.glob(str(included_glob)): explicitely_included.add( Path(included).relative_to(self._path).as_posix() diff --git a/tests/masonry/builders/test_wheel.py b/tests/masonry/builders/test_wheel.py index c0cc8c1ce..530680d16 100644 --- a/tests/masonry/builders/test_wheel.py +++ b/tests/masonry/builders/test_wheel.py @@ -96,6 +96,7 @@ def test_wheel_excluded_nested_data(): 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_include_excluded_code(): module_path = fixtures_dir / "include_excluded_code" poetry = Factory().create_poetry(module_path) @@ -109,6 +110,7 @@ def test_include_excluded_code(): assert "my_package/generated.py" in z.namelist() assert "lib/my_package/generated.py" not in z.namelist() + def test_wheel_localversionlabel(): module_path = fixtures_dir / "localversionlabel" project = Factory().create_poetry(module_path) @@ -282,4 +284,3 @@ def test_default_src_with_excluded_data(mocker): assert "my_package/data/data1.txt" in names assert "my_package/data/sub_data/data2.txt" not in names assert "my_package/data/sub_data/data3.txt" in names - From 7e304051b216b316722e8e0fa2fd198c7eed31cd Mon Sep 17 00:00:00 2001 From: finswimmer Date: Sun, 18 Oct 2020 21:28:58 +0200 Subject: [PATCH 13/77] fix incorrect type hints and add missing ones --- poetry/core/factory.py | 2 +- poetry/core/masonry/api.py | 21 ++- poetry/core/masonry/builder.py | 7 +- poetry/core/masonry/builders/builder.py | 25 +-- poetry/core/masonry/builders/sdist.py | 26 ++- poetry/core/masonry/builders/wheel.py | 51 ++++-- poetry/core/masonry/metadata.py | 8 +- poetry/core/masonry/utils/helpers.py | 6 +- poetry/core/masonry/utils/include.py | 4 +- poetry/core/masonry/utils/module.py | 7 +- poetry/core/masonry/utils/package_include.py | 13 +- poetry/core/packages/constraints/__init__.py | 13 +- .../packages/constraints/any_constraint.py | 26 +-- .../packages/constraints/base_constraint.py | 25 +-- .../core/packages/constraints/constraint.py | 38 +++-- .../packages/constraints/empty_constraint.py | 22 ++- .../packages/constraints/multi_constraint.py | 24 ++- .../packages/constraints/union_constraint.py | 30 ++-- poetry/core/packages/dependency.py | 73 +++++---- poetry/core/packages/directory_dependency.py | 31 ++-- poetry/core/packages/file_dependency.py | 27 ++-- poetry/core/packages/package.py | 85 ++++++---- poetry/core/packages/project_package.py | 30 +++- poetry/core/packages/specification.py | 16 +- poetry/core/packages/url_dependency.py | 19 ++- poetry/core/packages/utils/link.py | 65 ++++---- poetry/core/packages/utils/utils.py | 36 +++-- poetry/core/packages/vcs_dependency.py | 46 +++--- poetry/core/pyproject/tables.py | 7 +- poetry/core/pyproject/toml.py | 7 +- poetry/core/semver/__init__.py | 9 +- poetry/core/semver/empty_constraint.py | 25 +-- poetry/core/semver/version.py | 61 ++++--- poetry/core/semver/version_constraint.py | 2 +- poetry/core/semver/version_range.py | 54 ++++--- poetry/core/semver/version_union.py | 39 ++--- poetry/core/spdx/__init__.py | 8 +- poetry/core/spdx/license.py | 5 +- poetry/core/spdx/updater.py | 9 +- poetry/core/toml/file.py | 10 +- poetry/core/utils/_compat.py | 23 ++- poetry/core/utils/helpers.py | 11 +- poetry/core/utils/toml_file.py | 4 +- poetry/core/vcs/git.py | 38 +++-- poetry/core/version/base.py | 23 ++- poetry/core/version/helpers.py | 10 +- poetry/core/version/legacy_version.py | 22 +-- poetry/core/version/markers.py | 152 ++++++++++-------- poetry/core/version/requirements.py | 6 +- poetry/core/version/utils.py | 39 ++--- poetry/core/version/version.py | 36 +++-- 51 files changed, 858 insertions(+), 518 deletions(-) diff --git a/poetry/core/factory.py b/poetry/core/factory.py index d248dbb74..be157f00a 100644 --- a/poetry/core/factory.py +++ b/poetry/core/factory.py @@ -29,7 +29,7 @@ class Factory(object): def create_poetry( self, cwd=None, with_dev=True - ): # type: (Optional[Path]. bool) -> Poetry + ): # type: (Optional[Path], bool) -> Poetry poetry_file = self.locate(cwd) local_config = PyProjectTOML(path=poetry_file).poetry_config diff --git a/poetry/core/masonry/api.py b/poetry/core/masonry/api.py index 4907e10d4..30a9185b3 100644 --- a/poetry/core/masonry/api.py +++ b/poetry/core/masonry/api.py @@ -3,6 +3,11 @@ """ import logging +from typing import Any +from typing import Dict +from typing import List +from typing import Optional + from poetry.core.factory import Factory from poetry.core.utils._compat import Path from poetry.core.utils._compat import unicode @@ -14,7 +19,9 @@ log = logging.getLogger(__name__) -def get_requires_for_build_wheel(config_settings=None): +def get_requires_for_build_wheel( + config_settings=None, +): # type: (Optional[Dict[str, Any]]) -> List[str] """ Returns an additional list of requirements for building, as PEP508 strings, above and beyond those specified in the pyproject.toml file. @@ -30,7 +37,9 @@ def get_requires_for_build_wheel(config_settings=None): get_requires_for_build_sdist = get_requires_for_build_wheel -def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None): +def prepare_metadata_for_build_wheel( + metadata_directory, config_settings=None +): # type: (str, Optional[Dict[str, Any]]) -> str poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False) builder = WheelBuilder(poetry) @@ -50,14 +59,18 @@ def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None): return dist_info.name -def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): +def build_wheel( + wheel_directory, config_settings=None, metadata_directory=None +): # type: (str, Optional[Dict[str, Any]], Optional[str]) -> str """Builds a wheel, places it in wheel_directory""" poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False) return unicode(WheelBuilder.make_in(poetry, Path(wheel_directory))) -def build_sdist(sdist_directory, config_settings=None): +def build_sdist( + sdist_directory, config_settings=None +): # type: (str, Optional[Dict[str, Any]]) -> str """Builds an sdist, places it in sdist_directory""" poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False) diff --git a/poetry/core/masonry/builder.py b/poetry/core/masonry/builder.py index eed6fb5fe..21e3bb154 100644 --- a/poetry/core/masonry/builder.py +++ b/poetry/core/masonry/builder.py @@ -1,3 +1,4 @@ +from typing import TYPE_CHECKING from typing import Optional from typing import Union @@ -7,13 +8,17 @@ from .builders.wheel import WheelBuilder +if TYPE_CHECKING: + from poetry.core.poetry import Poetry # noqa + + class Builder: _FORMATS = { "sdist": SdistBuilder, "wheel": WheelBuilder, } - def __init__(self, poetry): + def __init__(self, poetry): # type: ("Poetry") -> None self._poetry = poetry def build( diff --git a/poetry/core/masonry/builders/builder.py b/poetry/core/masonry/builders/builder.py index c6f6d9cd6..769d9e497 100644 --- a/poetry/core/masonry/builders/builder.py +++ b/poetry/core/masonry/builders/builder.py @@ -8,6 +8,9 @@ from collections import defaultdict from contextlib import contextmanager from typing import TYPE_CHECKING +from typing import Any +from typing import Dict +from typing import List from typing import Optional from typing import Set from typing import Union @@ -22,7 +25,7 @@ if TYPE_CHECKING: - from poetry.core.poetry import Poetry + from poetry.core.poetry import Poetry # noqa AUTHOR_REGEX = re.compile(r"(?u)^(?P[- .,\w\d'’\"()]+) <(?P.+?)>$") @@ -38,7 +41,7 @@ class Builder(object): - format = None + format = None # type: Optional[str] def __init__( self, poetry, ignore_packages_formats=False, executable=None @@ -46,8 +49,8 @@ def __init__( self._poetry = poetry self._package = poetry.package self._path = poetry.file.parent - self._excluded_files = None - self._executable = Path(executable or sys.executable) # type: Path + self._excluded_files = None # type: Optional[Set[str]] + self._executable = Path(executable or sys.executable) packages = [] for p in self._package.packages: @@ -92,7 +95,7 @@ def __init__( def executable(self): # type: () -> Path return self._executable - def build(self): + def build(self): # type: () -> None raise NotImplementedError() def find_excluded_files(self): # type: () -> Set[str] @@ -264,7 +267,7 @@ def get_metadata_content(self): # type: () -> str return content - def convert_entry_points(self): # type: () -> dict + def convert_entry_points(self): # type: () -> Dict[str, List[str]] result = defaultdict(list) # Scripts -> Entry points @@ -288,7 +291,7 @@ def convert_entry_points(self): # type: () -> dict return dict(result) @classmethod - def convert_author(cls, author): # type: (...) -> dict + def convert_author(cls, author): # type: (str) -> Dict[str, str] m = AUTHOR_REGEX.match(author) name = m.group("name") @@ -298,7 +301,7 @@ def convert_author(cls, author): # type: (...) -> dict @classmethod @contextmanager - def temporary_directory(cls, *args, **kwargs): + def temporary_directory(cls, *args, **kwargs): # type: (*Any, **Any) -> None try: from tempfile import TemporaryDirectory @@ -348,16 +351,16 @@ def __eq__(self, other): # type: (Union[BuildIncludeFile, Path]) -> bool def __ne__(self, other): # type: (Union[BuildIncludeFile, Path]) -> bool return not self.__eq__(other) - def __hash__(self): + def __hash__(self): # type: () -> int return hash(self.path) def __repr__(self): # type: () -> str return str(self.path) - def relative_to_project_root(self): # type(): -> Path + def relative_to_project_root(self): # type: () -> Path return self.path.relative_to(self.project_root) - def relative_to_source_root(self): # type(): -> Path + def relative_to_source_root(self): # type: () -> Path if self.source_root is not None: return self.path.relative_to(self.source_root) return self.path diff --git a/poetry/core/masonry/builders/sdist.py b/poetry/core/masonry/builders/sdist.py index 981b63b2a..96781f781 100644 --- a/poetry/core/masonry/builders/sdist.py +++ b/poetry/core/masonry/builders/sdist.py @@ -12,8 +12,14 @@ from io import BytesIO from posixpath import join as pjoin from pprint import pformat +from tarfile import TarInfo +from typing import TYPE_CHECKING +from typing import Dict from typing import Iterator +from typing import List +from typing import Optional from typing import Set +from typing import Tuple from poetry.core.utils._compat import Path from poetry.core.utils._compat import decode @@ -26,6 +32,10 @@ from .builder import BuildIncludeFile +if TYPE_CHECKING: + from poetry.core.packages import Dependency # noqa + from poetry.core.packages import ProjectPackage # noqa + SETUP = """\ # -*- coding: utf-8 -*- from setuptools import setup @@ -55,7 +65,7 @@ class SdistBuilder(Builder): format = "sdist" - def build(self, target_dir=None): # type: (Path) -> Path + def build(self, target_dir=None): # type: (Optional[Path]) -> Path logger.info("Building sdist") if target_dir is None: target_dir = self._path / "dist" @@ -219,10 +229,12 @@ def setup_py(self): # type: () -> Iterator[Path] if not has_setup: setup.unlink() - def build_pkg_info(self): + def build_pkg_info(self): # type: () -> bytes return encode(self.get_metadata_content()) - def find_packages(self, include): + def find_packages( + self, include + ): # type: (PackageInclude) -> Tuple[str, List[str], dict] """ Discover subpackages and data. @@ -242,7 +254,7 @@ def find_packages(self, include): packages = [pkg_name] subpkg_paths = set() - def find_nearest_pkg(rel_path): + def find_nearest_pkg(rel_path): # type: (str) -> Tuple[str, str] parts = rel_path.split(os.sep) for i in reversed(range(1, len(parts))): ancestor = "/".join(parts[:i]) @@ -329,7 +341,9 @@ def find_files_to_add( return to_add @classmethod - def convert_dependencies(cls, package, dependencies): + def convert_dependencies( + cls, package, dependencies + ): # type: ("ProjectPackage", List["Dependency"]) -> Tuple[List[str], Dict[str, List[str]]] main = [] extras = defaultdict(list) req_regex = re.compile(r"^(.+) \((.+)\)$") @@ -386,7 +400,7 @@ def convert_dependencies(cls, package, dependencies): return main, dict(extras) @classmethod - def clean_tarinfo(cls, tar_info): + def clean_tarinfo(cls, tar_info): # type: (TarInfo) -> TarInfo """ Clean metadata from a TarInfo object to make it more reproducible. diff --git a/poetry/core/masonry/builders/wheel.py b/poetry/core/masonry/builders/wheel.py index 1d0b13e18..04a1a9b99 100644 --- a/poetry/core/masonry/builders/wheel.py +++ b/poetry/core/masonry/builders/wheel.py @@ -14,12 +14,18 @@ from base64 import urlsafe_b64encode from io import BytesIO from io import StringIO +from typing import TYPE_CHECKING +from typing import Iterator +from typing import Optional +from typing import TextIO +from typing import Union from packaging.tags import sys_tags from poetry.core import __version__ from poetry.core.semver import parse_constraint from poetry.core.utils._compat import PY2 +from poetry.core.utils._compat import Path from poetry.core.utils._compat import decode from ..utils.helpers import escape_name @@ -29,6 +35,9 @@ from .sdist import SdistBuilder +if TYPE_CHECKING: + from poetry.core.poetry import Poetry # noqa + wheel_file_template = """\ Wheel-Version: 1.0 Generator: poetry {version} @@ -42,7 +51,9 @@ class WheelBuilder(Builder): format = "wheel" - def __init__(self, poetry, target_dir=None, original=None, executable=None): + def __init__( + self, poetry, target_dir=None, original=None, executable=None + ): # type: ("Poetry", Optional[Path], Optional[Path], Optional[str]) -> None super(WheelBuilder, self).__init__(poetry, executable=executable) self._records = [] @@ -52,7 +63,9 @@ def __init__(self, poetry, target_dir=None, original=None, executable=None): self._original_path = original.file.parent @classmethod - def make_in(cls, poetry, directory=None, original=None, executable=None): + def make_in( + cls, poetry, directory=None, original=None, executable=None + ): # type: ("Poetry", Path, Path, str) -> str wb = WheelBuilder( poetry, target_dir=directory, original=original, executable=executable ) @@ -61,11 +74,11 @@ def make_in(cls, poetry, directory=None, original=None, executable=None): return wb.wheel_filename @classmethod - def make(cls, poetry, executable=None): + def make(cls, poetry, executable=None): # type: ("Poetry", Optional[str]) -> None """Build a wheel in the dist/ directory, and optionally upload it.""" cls.make_in(poetry, executable=executable) - def build(self): + def build(self): # type: () -> None logger.info("Building wheel") dist_dir = self._target_dir @@ -98,7 +111,7 @@ def build(self): logger.info("Built {}".format(self.wheel_filename)) - def _build(self, wheel): + def _build(self, wheel): # type: (zipfile.ZipFile) -> None if self._package.build_script: if not self._poetry.package.build_should_generate_setup(): # Since we have a build script but no setup.py generation is required, @@ -145,7 +158,7 @@ def _build(self, wheel): self._add_file(wheel, pkg, rel_path) - def _run_build_command(self, setup): + def _run_build_command(self, setup): # type: (Path) -> None subprocess.check_call( [ self.executable.as_posix(), @@ -156,7 +169,7 @@ def _run_build_command(self, setup): ] ) - def _run_build_script(self, build_script): + def _run_build_script(self, build_script): # type: (str) -> None logger.debug("Executing build script: {}".format(build_script)) subprocess.check_call([self.executable.as_posix(), build_script]) @@ -168,7 +181,7 @@ def _copy_module(self, wheel): # type: (zipfile.ZipFile) -> None for file in sorted(list(to_add), key=lambda x: x.path): self._add_file(wheel, file.path, file.relative_to_source_root()) - def _write_metadata(self, wheel): + def _write_metadata(self, wheel): # type: (zipfile.ZipFile) -> None if ( "scripts" in self._poetry.local_config or "plugins" in self._poetry.local_config @@ -196,7 +209,7 @@ def _write_metadata(self, wheel): with self._write_to_zip(wheel, self.dist_info + "/METADATA") as f: self._write_metadata_file(f) - def _write_record(self, wheel): + def _write_record(self, wheel): # type: (zipfile.ZipFile) -> None # Write a record of the files in the wheel with self._write_to_zip(wheel, self.dist_info + "/RECORD") as f: record = StringIO() if not PY2 else BytesIO() @@ -227,19 +240,19 @@ def wheel_filename(self): # type: () -> str self.tag, ) - def supports_python2(self): + def supports_python2(self): # type: () -> bool return self._package.python_constraint.allows_any( parse_constraint(">=2.0.0 <3.0.0") ) - def dist_info_name(self, distribution, version): # type: (...) -> str + def dist_info_name(self, distribution, version): # type: (str, str) -> str escaped_name = escape_name(distribution) escaped_version = escape_version(version) return "{}-{}.dist-info".format(escaped_name, escaped_version) @property - def tag(self): + def tag(self): # type: () -> str if self._package.build_script: tag = next(sys_tags()) tag = (tag.interpreter, tag.abi, tag.platform) @@ -254,7 +267,9 @@ def tag(self): return "-".join(tag) - def _add_file(self, wheel, full_path, rel_path): + def _add_file( + self, wheel, full_path, rel_path + ): # type: (zipfile.ZipFile, Union[Path, str], Union[Path, str]) -> None full_path, rel_path = str(full_path), str(rel_path) if os.sep != "/": # We always want to have /-separated paths in the zip file and in @@ -288,7 +303,9 @@ def _add_file(self, wheel, full_path, rel_path): self._records.append((rel_path, hash_digest, size)) @contextlib.contextmanager - def _write_to_zip(self, wheel, rel_path): + def _write_to_zip( + self, wheel, rel_path + ): # type: (zipfile.ZipFile, str) -> Iterator[StringIO] sio = StringIO() yield sio @@ -305,7 +322,7 @@ def _write_to_zip(self, wheel, rel_path): wheel.writestr(zi, b, compress_type=zipfile.ZIP_DEFLATED) self._records.append((rel_path, hash_digest, len(b))) - def _write_entry_points(self, fp): + def _write_entry_points(self, fp): # type: (TextIO) -> None """ Write entry_points.txt. """ @@ -318,7 +335,7 @@ def _write_entry_points(self, fp): fp.write("\n") - def _write_wheel_file(self, fp): + def _write_wheel_file(self, fp): # type: (TextIO) -> None fp.write( wheel_file_template.format( version=__version__, @@ -327,7 +344,7 @@ def _write_wheel_file(self, fp): ) ) - def _write_metadata_file(self, fp): + def _write_metadata_file(self, fp): # type: (TextIO) -> None """ Write out metadata in the 2.x format (email like) """ diff --git a/poetry/core/masonry/metadata.py b/poetry/core/masonry/metadata.py index 1a7fe559a..48bf6033c 100644 --- a/poetry/core/masonry/metadata.py +++ b/poetry/core/masonry/metadata.py @@ -1,8 +1,14 @@ +from typing import TYPE_CHECKING + from poetry.core.utils.helpers import canonicalize_name from poetry.core.utils.helpers import normalize_version from poetry.core.version.helpers import format_python_constraint +if TYPE_CHECKING: + from poetry.core.packages import Package # noqa + + class Metadata: metadata_version = "2.1" @@ -39,7 +45,7 @@ class Metadata: provides_extra = [] @classmethod - def from_package(cls, package): # type: (...) -> Metadata + def from_package(cls, package): # type: ("Package") -> Metadata meta = cls() meta.name = canonicalize_name(package.name) diff --git a/poetry/core/masonry/utils/helpers.py b/poetry/core/masonry/utils/helpers.py index e69ebc248..3a515f425 100644 --- a/poetry/core/masonry/utils/helpers.py +++ b/poetry/core/masonry/utils/helpers.py @@ -1,7 +1,7 @@ import re -def normalize_file_permissions(st_mode): +def normalize_file_permissions(st_mode): # type: (int) -> int """ Normalizes the permission bits in the st_mode field from stat to 644/755 @@ -17,7 +17,7 @@ def normalize_file_permissions(st_mode): return new_mode -def escape_version(version): +def escape_version(version): # type: (str) -> str """ Escaped version in wheel filename. Doesn't exactly follow the escaping specification in :pep:`427#escaping-and-unicode` @@ -26,6 +26,6 @@ def escape_version(version): return re.sub(r"[^\w\d.+]+", "_", version, flags=re.UNICODE) -def escape_name(name): +def escape_name(name): # type: (str) -> str """Escaped wheel name as specified in :pep:`427#escaping-and-unicode`.""" return re.sub(r"[^\w\d.]+", "_", name, flags=re.UNICODE) diff --git a/poetry/core/masonry/utils/include.py b/poetry/core/masonry/utils/include.py index 0bc5ed68d..f8f2b8f11 100644 --- a/poetry/core/masonry/utils/include.py +++ b/poetry/core/masonry/utils/include.py @@ -25,7 +25,9 @@ def __init__( self._include = str(include) self._formats = formats - self._elements = sorted(list(self._base.glob(str(self._include)))) + self._elements = sorted( + list(self._base.glob(str(self._include))) + ) # type: List[Path] @property def base(self): # type: () -> Path diff --git a/poetry/core/masonry/utils/module.py b/poetry/core/masonry/utils/module.py index c1de7c530..2e2a7539f 100644 --- a/poetry/core/masonry/utils/module.py +++ b/poetry/core/masonry/utils/module.py @@ -1,4 +1,7 @@ +from typing import Any +from typing import Dict from typing import List +from typing import Optional from poetry.core.utils._compat import Path from poetry.core.utils.helpers import module_name @@ -13,7 +16,9 @@ class ModuleOrPackageNotFound(ValueError): class Module: - def __init__(self, name, directory=".", packages=None, includes=None): + def __init__( + self, name, directory=".", packages=None, includes=None + ): # type: (str, str, Optional[List[Dict[str, Any]]], Optional[List[Dict[str, Any]]]) -> None self._name = module_name(name) self._in_src = False self._is_package = False diff --git a/poetry/core/masonry/utils/package_include.py b/poetry/core/masonry/utils/package_include.py index 47345765f..d409f5d33 100644 --- a/poetry/core/masonry/utils/package_include.py +++ b/poetry/core/masonry/utils/package_include.py @@ -1,8 +1,15 @@ +from typing import List +from typing import Optional + +from poetry.core.utils._compat import Path + from .include import Include class PackageInclude(Include): - def __init__(self, base, include, formats=None, source=None): + def __init__( + self, base, include, formats=None, source=None + ): # type: (Path, str, Optional[List[str]], Optional[str]) -> None self._package = None self._is_package = False self._is_module = False @@ -19,7 +26,7 @@ def package(self): # type: () -> str return self._package @property - def source(self): # type: () -> str + def source(self): # type: () -> Optional[str] return self._source def is_package(self): # type: () -> bool @@ -67,7 +74,7 @@ def check_elements(self): # type: () -> PackageInclude if root.is_dir(): # If it's a directory, we include everything inside it self._package = root.name - self._elements = sorted(list(root.glob("**/*"))) + self._elements = sorted(list(root.glob("**/*"))) # type: List[Path] if not self.is_stub_only() and not self.has_modules(): raise ValueError("{} is not a package.".format(root.name)) diff --git a/poetry/core/packages/constraints/__init__.py b/poetry/core/packages/constraints/__init__.py index bd5c2fbc9..33acb85a7 100644 --- a/poetry/core/packages/constraints/__init__.py +++ b/poetry/core/packages/constraints/__init__.py @@ -1,15 +1,24 @@ import re +from typing import Union + from .any_constraint import AnyConstraint from .base_constraint import BaseConstraint from .constraint import Constraint +from .empty_constraint import EmptyConstraint +from .multi_constraint import MultiConstraint from .union_constraint import UnionConstraint BASIC_CONSTRAINT = re.compile(r"^(!?==?)?\s*([^\s]+?)\s*$") +ConstraintTypes = Union[ + AnyConstraint, Constraint, UnionConstraint, EmptyConstraint, MultiConstraint +] -def parse_constraint(constraints): +def parse_constraint( + constraints, +): # type: (str) -> Union[AnyConstraint, UnionConstraint, Constraint] if constraints == "*": return AnyConstraint() @@ -42,7 +51,7 @@ def parse_constraint(constraints): return UnionConstraint(*or_groups) -def parse_single_constraint(constraint): # type: (str) -> BaseConstraint +def parse_single_constraint(constraint): # type: (str) -> Constraint # Basic comparator m = BASIC_CONSTRAINT.match(constraint) if m: diff --git a/poetry/core/packages/constraints/any_constraint.py b/poetry/core/packages/constraints/any_constraint.py index c0003725e..88945a119 100644 --- a/poetry/core/packages/constraints/any_constraint.py +++ b/poetry/core/packages/constraints/any_constraint.py @@ -1,37 +1,43 @@ +from typing import TYPE_CHECKING + from .base_constraint import BaseConstraint from .empty_constraint import EmptyConstraint +if TYPE_CHECKING: + from . import ConstraintTypes # noqa + + class AnyConstraint(BaseConstraint): - def allows(self, other): + def allows(self, other): # type: ("ConstraintTypes") -> bool return True - def allows_all(self, other): + def allows_all(self, other): # type: ("ConstraintTypes") -> bool return True - def allows_any(self, other): + def allows_any(self, other): # type: ("ConstraintTypes") -> bool return True - def difference(self, other): + def difference(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" if other.is_any(): return EmptyConstraint() return other - def intersect(self, other): + def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" return other - def union(self, other): + def union(self, other): # type: ("ConstraintTypes") -> AnyConstraint return AnyConstraint() - def is_any(self): + def is_any(self): # type: () -> bool return True - def is_empty(self): + def is_empty(self): # type: () -> bool return False - def __str__(self): + def __str__(self): # type: () -> str return "*" - def __eq__(self, other): + def __eq__(self, other): # type: ("ConstraintTypes") -> bool return other.is_any() diff --git a/poetry/core/packages/constraints/base_constraint.py b/poetry/core/packages/constraints/base_constraint.py index c8076fe7b..8cb95c138 100644 --- a/poetry/core/packages/constraints/base_constraint.py +++ b/poetry/core/packages/constraints/base_constraint.py @@ -1,27 +1,34 @@ +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from . import ConstraintTypes # noqa + + class BaseConstraint(object): - def allows_all(self, other): + def allows_all(self, other): # type: ("ConstraintTypes") -> bool raise NotImplementedError() - def allows_any(self, other): + def allows_any(self, other): # type: ("ConstraintTypes") -> bool raise NotImplementedError() - def difference(self, other): + def difference(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" raise NotImplementedError() - def intersect(self, other): + def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" raise NotImplementedError() - def union(self, other): + def union(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" raise NotImplementedError() - def is_any(self): + def is_any(self): # type: () -> bool return False - def is_empty(self): + def is_empty(self): # type: () -> bool return False - def __repr__(self): + def __repr__(self): # type: () -> str return "<{} {}>".format(self.__class__.__name__, str(self)) - def __eq__(self, other): + def __eq__(self, other): # type: ("ConstraintTypes") -> bool raise NotImplementedError() diff --git a/poetry/core/packages/constraints/constraint.py b/poetry/core/packages/constraints/constraint.py index f05bf54d5..1ebe915f5 100644 --- a/poetry/core/packages/constraints/constraint.py +++ b/poetry/core/packages/constraints/constraint.py @@ -1,9 +1,17 @@ import operator +from typing import TYPE_CHECKING +from typing import Any +from typing import Union + from .base_constraint import BaseConstraint from .empty_constraint import EmptyConstraint +if TYPE_CHECKING: + from . import ConstraintTypes # noqa + + class Constraint(BaseConstraint): OP_EQ = operator.eq @@ -13,7 +21,7 @@ class Constraint(BaseConstraint): _trans_op_int = {OP_EQ: "==", OP_NE: "!="} - def __init__(self, version, operator="=="): + def __init__(self, version, operator="=="): # type: (str, str) -> None if operator == "=": operator = "==" @@ -22,14 +30,14 @@ def __init__(self, version, operator="=="): self._op = self._trans_op_str[operator] @property - def version(self): + def version(self): # type: () -> str return self._version @property - def operator(self): + def operator(self): # type: () -> str return self._operator - def allows(self, other): + def allows(self, other): # type: ("ConstraintTypes") -> bool is_equal_op = self._operator == "==" is_non_equal_op = self._operator == "!=" is_other_equal_op = other.operator == "==" @@ -50,13 +58,13 @@ def allows(self, other): return False - def allows_all(self, other): + def allows_all(self, other): # type: ("ConstraintTypes") -> bool if not isinstance(other, Constraint): return other.is_empty() return other == self - def allows_any(self, other): + def allows_any(self, other): # type: ("ConstraintTypes") -> bool if isinstance(other, Constraint): is_non_equal_op = self._operator == "!=" is_other_non_equal_op = other.operator == "!=" @@ -66,13 +74,15 @@ def allows_any(self, other): return other.allows(self) - def difference(self, other): + def difference( + self, other + ): # type: ("ConstraintTypes") -> Union[Constraint, "EmptyConstraint"] if other.allows(self): return EmptyConstraint() return self - def intersect(self, other): + def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" from .multi_constraint import MultiConstraint if isinstance(other, Constraint): @@ -92,7 +102,7 @@ def intersect(self, other): return other.intersect(self) - def union(self, other): + def union(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" if isinstance(other, Constraint): from .union_constraint import UnionConstraint @@ -100,22 +110,22 @@ def union(self, other): return other.union(self) - def is_any(self): + def is_any(self): # type: () -> bool return False - def is_empty(self): + def is_empty(self): # type: () -> bool return False - def __eq__(self, other): + def __eq__(self, other): # type: (Any) -> bool if not isinstance(other, Constraint): return NotImplemented return (self.version, self.operator) == (other.version, other.operator) - def __hash__(self): + def __hash__(self): # type: () -> int return hash((self._operator, self._version)) - def __str__(self): + def __str__(self): # type: () -> str return "{}{}".format( self._operator if self._operator != "==" else "", self._version ) diff --git a/poetry/core/packages/constraints/empty_constraint.py b/poetry/core/packages/constraints/empty_constraint.py index 0a1c45ecc..eb29e4ad3 100644 --- a/poetry/core/packages/constraints/empty_constraint.py +++ b/poetry/core/packages/constraints/empty_constraint.py @@ -1,30 +1,36 @@ +from typing import TYPE_CHECKING + from .base_constraint import BaseConstraint +if TYPE_CHECKING: + from . import ConstraintTypes # noqa + + class EmptyConstraint(BaseConstraint): pretty_string = None - def matches(self, _): + def matches(self, _): # type: ("ConstraintTypes") -> bool return True - def is_empty(self): + def is_empty(self): # type: () -> bool return True - def allows_all(self, other): + def allows_all(self, other): # type: ("ConstraintTypes") -> bool return True - def allows_any(self, other): + def allows_any(self, other): # type: ("ConstraintTypes") -> bool return True - def intersect(self, other): + def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" return other - def difference(self, other): + def difference(self, other): # type: ("ConstraintTypes") -> None return - def __eq__(self, other): + def __eq__(self, other): # type: ("ConstraintTypes") -> bool return other.is_empty() - def __str__(self): + def __str__(self): # type: () -> str return "" diff --git a/poetry/core/packages/constraints/multi_constraint.py b/poetry/core/packages/constraints/multi_constraint.py index 0bb63dd27..33fc9e4a5 100644 --- a/poetry/core/packages/constraints/multi_constraint.py +++ b/poetry/core/packages/constraints/multi_constraint.py @@ -1,9 +1,17 @@ +from typing import TYPE_CHECKING +from typing import Any +from typing import Tuple + from .base_constraint import BaseConstraint from .constraint import Constraint +if TYPE_CHECKING: + from . import ConstraintTypes # noqa + + class MultiConstraint(BaseConstraint): - def __init__(self, *constraints): + def __init__(self, *constraints): # type: (*Constraint) -> None if any(c.operator == "==" for c in constraints): raise ValueError( "A multi-constraint can only be comprised of negative constraints" @@ -12,17 +20,17 @@ def __init__(self, *constraints): self._constraints = constraints @property - def constraints(self): + def constraints(self): # type: () -> Tuple[Constraint] return self._constraints - def allows(self, other): + def allows(self, other): # type: ("ConstraintTypes") -> bool for constraint in self._constraints: if not constraint.allows(other): return False return True - def allows_all(self, other): + def allows_all(self, other): # type: ("ConstraintTypes") -> bool if other.is_any(): return False @@ -45,7 +53,7 @@ def allows_all(self, other): return their_constraint is None - def allows_any(self, other): + def allows_any(self, other): # type: ("ConstraintTypes") -> bool if other.is_any(): return True @@ -63,7 +71,7 @@ def allows_any(self, other): return False - def intersect(self, other): + def intersect(self, other): # type: (Constraint) -> MultiConstraint if isinstance(other, Constraint): constraints = self._constraints if other not in constraints: @@ -76,7 +84,7 @@ def intersect(self, other): return MultiConstraint(*constraints) - def __eq__(self, other): + def __eq__(self, other): # type: (Any) -> bool if not isinstance(other, MultiConstraint): return False @@ -84,7 +92,7 @@ def __eq__(self, other): self._constraints, key=lambda c: (c.operator, c.version) ) == sorted(other.constraints, key=lambda c: (c.operator, c.version)) - def __str__(self): + def __str__(self): # type: () -> str constraints = [] for constraint in self._constraints: constraints.append(str(constraint)) diff --git a/poetry/core/packages/constraints/union_constraint.py b/poetry/core/packages/constraints/union_constraint.py index 805315499..ec0330c2d 100644 --- a/poetry/core/packages/constraints/union_constraint.py +++ b/poetry/core/packages/constraints/union_constraint.py @@ -1,24 +1,35 @@ +from typing import TYPE_CHECKING +from typing import Tuple +from typing import Union + from .base_constraint import BaseConstraint from .constraint import Constraint from .empty_constraint import EmptyConstraint +from .multi_constraint import MultiConstraint + + +if TYPE_CHECKING: + from . import ConstraintTypes # noqa class UnionConstraint(BaseConstraint): - def __init__(self, *constraints): + def __init__(self, *constraints): # type: (*Constraint) -> None self._constraints = constraints @property - def constraints(self): + def constraints(self): # type: () -> Tuple[Constraint] return self._constraints - def allows(self, other): + def allows( + self, other + ): # type: (Union[Constraint, MultiConstraint, UnionConstraint]) -> bool for constraint in self._constraints: if constraint.allows(other): return True return False - def allows_any(self, other): + def allows_any(self, other): # type: ("ConstraintTypes") -> bool if other.is_empty(): return False @@ -37,7 +48,7 @@ def allows_any(self, other): return False - def allows_all(self, other): + def allows_all(self, other): # type: ("ConstraintTypes") -> bool if other.is_any(): return False @@ -62,7 +73,7 @@ def allows_all(self, other): return their_constraint is None - def intersect(self, other): + def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" if other.is_any(): return self @@ -88,7 +99,7 @@ def intersect(self, other): return UnionConstraint(*new_constraints) - def union(self, other): + def union(self, other): # type: (Constraint) -> UnionConstraint if isinstance(other, Constraint): constraints = self._constraints if other not in self._constraints: @@ -96,7 +107,8 @@ def union(self, other): return UnionConstraint(*constraints) - def __eq__(self, other): + def __eq__(self, other): # type: ("ConstraintTypes") -> bool + if not isinstance(other, UnionConstraint): return False @@ -104,7 +116,7 @@ def __eq__(self, other): self._constraints, key=lambda c: (c.operator, c.version) ) == sorted(other.constraints, key=lambda c: (c.operator, c.version)) - def __str__(self): + def __str__(self): # type: () -> str constraints = [] for constraint in self._constraints: constraints.append(str(constraint)) diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py index 1ffe0450b..5432e8bb3 100644 --- a/poetry/core/packages/dependency.py +++ b/poetry/core/packages/dependency.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING +from typing import Any from typing import FrozenSet from typing import List from typing import Optional @@ -21,6 +23,13 @@ from .utils.utils import convert_markers +if TYPE_CHECKING: + from poetry.core.version.markers import BaseMarker # noqa + from poetry.core.version.markers import VersionTypes # noqa + + from .constraints import BaseConstraint # noqa + + class Dependency(PackageSpecification): def __init__( self, @@ -74,14 +83,14 @@ def __init__( self.source_name = None @property - def name(self): + def name(self): # type: () -> str return self._name @property - def constraint(self): + def constraint(self): # type: () -> "VersionTypes" return self._constraint - def set_constraint(self, constraint): + def set_constraint(self, constraint): # type: (Union[str, "VersionTypes"]) -> None try: if not isinstance(constraint, VersionConstraint): self._constraint = parse_constraint(constraint) @@ -91,23 +100,23 @@ def set_constraint(self, constraint): self._constraint = parse_constraint("*") @property - def pretty_constraint(self): + def pretty_constraint(self): # type: () -> str return self._pretty_constraint @property - def pretty_name(self): + def pretty_name(self): # type: () -> str return self._pretty_name @property - def category(self): + def category(self): # type: () -> str return self._category @property - def python_versions(self): + def python_versions(self): # type: () -> str return self._python_versions @python_versions.setter - def python_versions(self, value): + def python_versions(self, value): # type: (str) -> None self._python_versions = value self._python_constraint = parse_constraint(value) if not self._python_constraint.is_any(): @@ -120,34 +129,34 @@ def python_versions(self, value): ) @property - def transitive_python_versions(self): + def transitive_python_versions(self): # type: () -> str if self._transitive_python_versions is None: return self._python_versions return self._transitive_python_versions @transitive_python_versions.setter - def transitive_python_versions(self, value): + def transitive_python_versions(self, value): # type: (str) -> None self._transitive_python_versions = value self._transitive_python_constraint = parse_constraint(value) @property - def transitive_marker(self): + def transitive_marker(self): # type: () -> "BaseMarker" if self._transitive_marker is None: return self.marker return self._transitive_marker @transitive_marker.setter - def transitive_marker(self, value): + def transitive_marker(self, value): # type: ("BaseMarker") -> None self._transitive_marker = value @property - def python_constraint(self): + def python_constraint(self): # type: () -> "VersionTypes" return self._python_constraint @property - def transitive_python_constraint(self): + def transitive_python_constraint(self): # type: () -> "VersionTypes" if self._transitive_python_constraint is None: return self._python_constraint @@ -183,25 +192,25 @@ def base_pep_508_name(self): # type: () -> str return requirement - def allows_prereleases(self): + def allows_prereleases(self): # type: () -> bool return self._allows_prereleases - def is_optional(self): + def is_optional(self): # type: () -> bool return self._optional - def is_activated(self): + def is_activated(self): # type: () -> bool return self._activated - def is_vcs(self): + def is_vcs(self): # type: () -> bool return False - def is_file(self): + def is_file(self): # type: () -> bool return False - def is_directory(self): + def is_directory(self): # type: () -> bool return False - def is_url(self): + def is_url(self): # type: () -> bool return False def accepts(self, package): # type: (poetry.core.packages.Package) -> bool @@ -257,7 +266,9 @@ def to_pep_508(self, with_extras=True): # type: (bool) -> str return requirement - def _create_nested_marker(self, name, constraint): + def _create_nested_marker( + self, name, constraint + ): # type: (str, Union["BaseConstraint", Version, VersionConstraint]) -> str if isinstance(constraint, (MultiConstraint, UnionConstraint)): parts = [] for c in constraint.constraints: @@ -339,13 +350,13 @@ def _create_nested_marker(self, name, constraint): return marker - def activate(self): + def activate(self): # type: () -> None """ Set the dependency as mandatory. """ self._activated = True - def deactivate(self): + def deactivate(self): # type: () -> None """ Set the dependency as optional. """ @@ -354,7 +365,9 @@ def deactivate(self): self._activated = False - def with_constraint(self, constraint): + def with_constraint( + self, constraint + ): # type: (Union[str, VersionConstraint]) -> Dependency new = Dependency( self.pretty_name, constraint, @@ -375,7 +388,7 @@ def with_constraint(self, constraint): return new - def __eq__(self, other): + def __eq__(self, other): # type: (Any) -> bool if not isinstance(other, Dependency): return NotImplemented @@ -385,17 +398,17 @@ def __eq__(self, other): and self._extras == other.extras ) - def __ne__(self, other): + def __ne__(self, other): # type: (Any) -> bool return not self == other - def __hash__(self): + def __hash__(self): # type: () -> int return ( super(Dependency, self).__hash__() ^ hash(self._constraint) ^ hash(self._extras) ) - def __str__(self): + def __str__(self): # type: () -> str if self.is_root: return self._pretty_name @@ -406,5 +419,5 @@ def __str__(self): return "{} ({})".format(name, self._pretty_constraint) - def __repr__(self): + def __repr__(self): # type: () -> str return "<{} {}>".format(self.__class__.__name__, str(self)) diff --git a/poetry/core/packages/directory_dependency.py b/poetry/core/packages/directory_dependency.py index 0e7c920ee..ac1193904 100644 --- a/poetry/core/packages/directory_dependency.py +++ b/poetry/core/packages/directory_dependency.py @@ -1,23 +1,28 @@ +from typing import TYPE_CHECKING +from typing import FrozenSet from typing import List -from typing import Set from typing import Union from poetry.core.pyproject import PyProjectTOML from poetry.core.utils._compat import Path + +if TYPE_CHECKING: + from .constraints import BaseConstraint # noqa + from .dependency import Dependency class DirectoryDependency(Dependency): def __init__( self, - name, + name, # type: str path, # type: Path category="main", # type: str optional=False, # type: bool base=None, # type: Path develop=False, # type: bool - extras=None, # type: Union[List[str], Set[str]] + extras=None, # type: Union[List[str], FrozenSet[str]] ): self._path = path self._base = base or Path.cwd() @@ -63,28 +68,30 @@ def __init__( ) @property - def path(self): + def path(self): # type: () -> Path return self._path @property - def full_path(self): + def full_path(self): # type: () -> Path return self._full_path @property - def base(self): + def base(self): # type: () -> Path return self._base @property - def develop(self): + def develop(self): # type: () -> bool return self._develop - def supports_poetry(self): + def supports_poetry(self): # type: () -> bool return self._supports_poetry - def is_directory(self): + def is_directory(self): # type: () -> bool return True - def with_constraint(self, constraint): + def with_constraint( + self, constraint + ): # type: ("BaseConstraint") -> DirectoryDependency new = DirectoryDependency( self.pretty_name, path=self.path, @@ -119,7 +126,7 @@ def base_pep_508_name(self): # type: () -> str return requirement - def __str__(self): + def __str__(self): # type: () -> str if self.is_root: return self._pretty_name @@ -127,5 +134,5 @@ def __str__(self): self._pretty_name, self._pretty_constraint, self._path.as_posix() ) - def __hash__(self): + def __hash__(self): # type: () -> int return hash((self._name, self._full_path.as_posix())) diff --git a/poetry/core/packages/file_dependency.py b/poetry/core/packages/file_dependency.py index 5eba72085..939520acc 100644 --- a/poetry/core/packages/file_dependency.py +++ b/poetry/core/packages/file_dependency.py @@ -1,8 +1,9 @@ import hashlib import io +from typing import TYPE_CHECKING +from typing import FrozenSet from typing import List -from typing import Set from typing import Union from poetry.core.packages.utils.utils import path_to_url @@ -11,15 +12,19 @@ from .dependency import Dependency +if TYPE_CHECKING: + from .constraints import BaseConstraint + + class FileDependency(Dependency): def __init__( self, - name, + name, # type: str path, # type: Path category="main", # type: str optional=False, # type: bool base=None, # type: Path - extras=None, # type: Union[List[str], Set[str]] + extras=None, # type: Union[List[str], FrozenSet[str]] ): self._path = path self._base = base or Path.cwd() @@ -49,21 +54,21 @@ def __init__( ) @property - def base(self): + def base(self): # type: () -> Path return self._base @property - def path(self): + def path(self): # type: () -> Path return self._path @property - def full_path(self): + def full_path(self): # type: () -> Path return self._full_path - def is_file(self): + def is_file(self): # type: () -> bool return True - def hash(self): + def hash(self): # type: () -> str h = hashlib.sha256() with self._full_path.open("rb") as fp: for content in iter(lambda: fp.read(io.DEFAULT_BUFFER_SIZE), b""): @@ -71,7 +76,7 @@ def hash(self): return h.hexdigest() - def with_constraint(self, constraint): + def with_constraint(self, constraint): # type: ("BaseConstraint") -> FileDependency new = FileDependency( self.pretty_name, path=self.path, @@ -106,7 +111,7 @@ def base_pep_508_name(self): # type: () -> str return requirement - def __str__(self): + def __str__(self): # type: () -> str if self.is_root: return self._pretty_name @@ -114,5 +119,5 @@ def __str__(self): self._pretty_name, self._pretty_constraint, self._path ) - def __hash__(self): + def __hash__(self): # type: () -> int return hash((self._name, self._full_path)) diff --git a/poetry/core/packages/package.py b/poetry/core/packages/package.py index aac39afa7..c99c17e1d 100644 --- a/poetry/core/packages/package.py +++ b/poetry/core/packages/package.py @@ -3,7 +3,11 @@ import re from contextlib import contextmanager +from typing import TYPE_CHECKING +from typing import Dict from typing import List +from typing import Optional +from typing import Union from poetry.core.semver import Version from poetry.core.semver import parse_constraint @@ -12,11 +16,20 @@ from poetry.core.version.markers import AnyMarker from poetry.core.version.markers import parse_marker -from .dependency import Dependency from .specification import PackageSpecification from .utils.utils import create_nested_marker +if TYPE_CHECKING: + from poetry.core.semver import VersionTypes # noqa + from poetry.core.version.markers import BaseMarker # noqa + + from .dependency import Dependency + from .directory_dependency import DirectoryDependency + from .file_dependency import FileDependency + from .url_dependency import URLDependency + from .vcs_dependency import VCSDependency + AUTHOR_REGEX = re.compile(r"(?u)^(?P[- .,\w\d'’\"()]+)(?: <(?P.+?)>)?$") @@ -36,14 +49,14 @@ class Package(PackageSpecification): def __init__( self, - name, - version, - pretty_version=None, - source_type=None, - source_url=None, - source_reference=None, - source_resolved_reference=None, - features=None, + name, # type: str + version, # type: Union[str, Version] + pretty_version=None, # type: Optional[str] + source_type=None, # type: Optional[str] + source_url=None, # type: Optional[str] + source_reference=None, # type: Optional[str] + source_resolved_reference=None, # type: Optional[str] + features=None, # type: Optional[List[str]] ): """ Creates a new in memory package. @@ -99,34 +112,34 @@ def __init__( self.develop = True @property - def name(self): + def name(self): # type: () -> str return self._name @property - def pretty_name(self): + def pretty_name(self): # type: () -> str return self._pretty_name @property - def version(self): + def version(self): # type: () -> "Version" return self._version @property - def pretty_version(self): + def pretty_version(self): # type: () -> str return self._pretty_version @property - def unique_name(self): + def unique_name(self): # type: () -> str if self.is_root(): return self._name return self.complete_name + "-" + self._version.text @property - def pretty_string(self): + def pretty_string(self): # type: () -> str return self.pretty_name + " " + self.pretty_version @property - def full_pretty_version(self): + def full_pretty_version(self): # type: () -> str if self.source_type in ["file", "directory", "url"]: return "{} {}".format(self._pretty_version, self.source_url) @@ -173,7 +186,9 @@ def maintainer_email(self): # type: () -> str return self._get_maintainer()["email"] @property - def all_requires(self): + def all_requires( + self, + ): # type: () -> List[Union["DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency", "Dependency"]] return self.requires + self.dev_requires def _get_author(self): # type: () -> dict @@ -211,11 +226,11 @@ def _get_maintainer(self): # type: () -> dict return {"name": name, "email": email} @property - def python_versions(self): + def python_versions(self): # type: () -> str return self._python_versions @python_versions.setter - def python_versions(self, value): + def python_versions(self, value): # type: (str) -> None self._python_versions = value self._python_constraint = parse_constraint(value) self._python_marker = parse_marker( @@ -223,19 +238,19 @@ def python_versions(self, value): ) @property - def python_constraint(self): + def python_constraint(self): # type: () -> "VersionTypes" return self._python_constraint @property - def python_marker(self): + def python_marker(self): # type: () -> "BaseMarker" return self._python_marker @property - def license(self): + def license(self): # type: () -> License return self._license @license.setter - def license(self, value): + def license(self, value): # type: (Optional[str, License]) -> None if value is None: self._license = value elif isinstance(value, License): @@ -244,7 +259,7 @@ def license(self, value): self._license = license_by_id(value) @property - def all_classifiers(self): + def all_classifiers(self): # type: () -> List[str] classifiers = copy.copy(self.classifiers) # Automatically set python classifiers @@ -273,7 +288,7 @@ def all_classifiers(self): return sorted(classifiers) @property - def urls(self): + def urls(self): # type: () -> Dict[str, str] urls = {} if self.homepage: @@ -287,15 +302,15 @@ def urls(self): return urls - def is_prerelease(self): + def is_prerelease(self): # type: () -> bool return self._version.is_prerelease() - def is_root(self): + def is_root(self): # type: () -> bool return False def add_dependency( self, dependency, - ): # type: (Dependency) -> "Package" + ): # type: ("Dependency") -> "Dependency" if dependency.category == "dev": self.dev_requires.append(dependency) else: @@ -303,7 +318,9 @@ def add_dependency( return dependency - def to_dependency(self): + def to_dependency( + self, + ): # type: () -> Union["Dependency", "DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency"] from poetry.core.utils._compat import Path from .dependency import Dependency @@ -366,7 +383,7 @@ def to_dependency(self): return dep.with_constraint(self._version) @contextmanager - def with_python_versions(self, python_versions): + def with_python_versions(self, python_versions): # type: (str) -> None original_python_versions = self.python_versions self.python_versions = python_versions @@ -415,19 +432,19 @@ def clone(self): # type: () -> "Package" return clone - def __hash__(self): + def __hash__(self): # type: () -> int return super(Package, self).__hash__() ^ hash(self._version) - def __eq__(self, other): + def __eq__(self, other): # type: (Package) -> bool if not isinstance(other, Package): return NotImplemented return self.is_same_package_as(other) and self._version == other.version - def __str__(self): + def __str__(self): # type: () -> str return "{} ({})".format(self.complete_name, self.full_pretty_version) - def __repr__(self): + def __repr__(self): # type: () -> str args = [repr(self._name), repr(self._version.text)] if self._features: diff --git a/poetry/core/packages/project_package.py b/poetry/core/packages/project_package.py index bcc5799e0..aabde6418 100644 --- a/poetry/core/packages/project_package.py +++ b/poetry/core/packages/project_package.py @@ -1,15 +1,31 @@ +from typing import TYPE_CHECKING +from typing import Any +from typing import Dict from typing import Optional +from typing import Union from poetry.core.semver import VersionRange from poetry.core.semver import parse_constraint from poetry.core.version.markers import parse_marker + +if TYPE_CHECKING: + from . import ( + DirectoryDependency, + FileDependency, + URLDependency, + VCSDependency, + Dependency, + ) + from .package import Package from .utils.utils import create_nested_marker class ProjectPackage(Package): - def __init__(self, name, version, pretty_version=None): + def __init__( + self, name, version, pretty_version=None + ): # type: (str, Union[str, VersionRange], Optional[str]) -> None super(ProjectPackage, self).__init__(name, version, pretty_version) self.build_config = dict() @@ -25,10 +41,12 @@ def __init__(self, name, version, pretty_version=None): def build_script(self): # type: () -> Optional[str] return self.build_config.get("script") - def is_root(self): + def is_root(self): # type: () -> bool return True - def to_dependency(self): + def to_dependency( + self, + ): # type: () -> Union["DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency", "Dependency"] dependency = super(ProjectPackage, self).to_dependency() dependency.is_root = True @@ -36,11 +54,11 @@ def to_dependency(self): return dependency @property - def python_versions(self): + def python_versions(self): # type: () -> Union[str, VersionRange] return self._python_versions @python_versions.setter - def python_versions(self, value): + def python_versions(self, value): # type: (Union[str, VersionRange]) -> None self._python_versions = value if value == "*" or value == VersionRange(): @@ -52,7 +70,7 @@ def python_versions(self, value): ) @property - def urls(self): + def urls(self): # type: () -> Dict[str, Any] urls = super(ProjectPackage, self).urls urls.update(self.custom_urls) diff --git a/poetry/core/packages/specification.py b/poetry/core/packages/specification.py index 86faaa939..70b88f193 100644 --- a/poetry/core/packages/specification.py +++ b/poetry/core/packages/specification.py @@ -8,13 +8,13 @@ class PackageSpecification(object): def __init__( self, - name, - source_type=None, - source_url=None, - source_reference=None, - source_resolved_reference=None, - features=None, - ): # type: (str, Optional[str], Optional[str], Optional[str], Optional[str], Optional[List[str]]) -> None + name, # type: str + source_type=None, # type: Optional[str] + source_url=None, # type: Optional[str] + source_reference=None, # type: Optional[str] + source_resolved_reference=None, # type: Optional[str] + features=None, # type: Optional[List[str]] + ): self._pretty_name = name self._name = canonicalize_name(name) self._source_type = source_type @@ -36,7 +36,7 @@ def pretty_name(self): # type: () -> str return self._pretty_name @property - def complete_name(self): # type () -> str + def complete_name(self): # type: () -> str name = self._name if self._features: diff --git a/poetry/core/packages/url_dependency.py b/poetry/core/packages/url_dependency.py index c8b5d0641..2d4ce5dd4 100644 --- a/poetry/core/packages/url_dependency.py +++ b/poetry/core/packages/url_dependency.py @@ -1,5 +1,6 @@ +from typing import TYPE_CHECKING +from typing import FrozenSet from typing import List -from typing import Set from typing import Union from poetry.core.utils._compat import urlparse @@ -7,14 +8,18 @@ from .dependency import Dependency +if TYPE_CHECKING: + from .constraints import BaseConstraint + + class URLDependency(Dependency): def __init__( self, - name, + name, # type: str url, # type: str category="main", # type: str optional=False, # type: bool - extras=None, # type: Union(List[str], Set[str]) + extras=None, # type: Union[List[str], FrozenSet[str]] ): self._url = url @@ -34,7 +39,7 @@ def __init__( ) @property - def url(self): + def url(self): # type: () -> str return self._url @property @@ -51,7 +56,7 @@ def base_pep_508_name(self): # type: () -> str def is_url(self): # type: () -> bool return True - def with_constraint(self, constraint): + def with_constraint(self, constraint): # type: ("BaseConstraint") -> URLDependency new = URLDependency( self.pretty_name, url=self._url, @@ -73,8 +78,8 @@ def with_constraint(self, constraint): return new - def __str__(self): + def __str__(self): # type: () -> str return "{} ({} url)".format(self._pretty_name, self._pretty_constraint) - def __hash__(self): + def __hash__(self): # type: () -> int return hash((self._name, self._url)) diff --git a/poetry/core/packages/utils/link.py b/poetry/core/packages/utils/link.py index 7621718f7..76f6c1c78 100644 --- a/poetry/core/packages/utils/link.py +++ b/poetry/core/packages/utils/link.py @@ -1,6 +1,15 @@ import posixpath import re +from typing import TYPE_CHECKING +from typing import Any +from typing import Optional +from typing import Tuple + + +if TYPE_CHECKING: + from pip._internal.index.collector import HTMLPage # noqa + from .utils import path_to_url from .utils import splitext @@ -12,7 +21,9 @@ class Link: - def __init__(self, url, comes_from=None, requires_python=None): + def __init__( + self, url, comes_from=None, requires_python=None + ): # type: (str, Optional["HTMLPage"], Optional[str]) -> None """ Object representing a parsed link from https://pypi.python.org/simple/* @@ -34,7 +45,7 @@ def __init__(self, url, comes_from=None, requires_python=None): self.comes_from = comes_from self.requires_python = requires_python if requires_python else None - def __str__(self): + def __str__(self): # type: () -> str if self.requires_python: rp = " (requires-python:%s)" % self.requires_python else: @@ -44,44 +55,44 @@ def __str__(self): else: return str(self.url) - def __repr__(self): + def __repr__(self): # type: () -> str return "" % self - def __eq__(self, other): + def __eq__(self, other): # type: (Any) -> bool if not isinstance(other, Link): return NotImplemented return self.url == other.url - def __ne__(self, other): + def __ne__(self, other): # type: (Any) -> bool if not isinstance(other, Link): return NotImplemented return self.url != other.url - def __lt__(self, other): + def __lt__(self, other): # type: (Any) -> bool if not isinstance(other, Link): return NotImplemented return self.url < other.url - def __le__(self, other): + def __le__(self, other): # type: (Any) -> bool if not isinstance(other, Link): return NotImplemented return self.url <= other.url - def __gt__(self, other): + def __gt__(self, other): # type: (Any) -> bool if not isinstance(other, Link): return NotImplemented return self.url > other.url - def __ge__(self, other): + def __ge__(self, other): # type: (Any) -> bool if not isinstance(other, Link): return NotImplemented return self.url >= other.url - def __hash__(self): + def __hash__(self): # type: () -> int return hash(self.url) @property - def filename(self): + def filename(self): # type: () -> str _, netloc, path, _, _ = urlparse.urlsplit(self.url) name = posixpath.basename(path.rstrip("/")) or netloc name = urlparse.unquote(name) @@ -89,33 +100,33 @@ def filename(self): return name @property - def scheme(self): + def scheme(self): # type: () -> str return urlparse.urlsplit(self.url)[0] @property - def netloc(self): + def netloc(self): # type: () -> str return urlparse.urlsplit(self.url)[1] @property - def path(self): + def path(self): # type: () -> str return urlparse.unquote(urlparse.urlsplit(self.url)[2]) - def splitext(self): + def splitext(self): # type: () -> Tuple[str, str] return splitext(posixpath.basename(self.path.rstrip("/"))) @property - def ext(self): + def ext(self): # type: () -> str return self.splitext()[1] @property - def url_without_fragment(self): + def url_without_fragment(self): # type: () -> str scheme, netloc, path, query, fragment = urlparse.urlsplit(self.url) return urlparse.urlunsplit((scheme, netloc, path, query, None)) _egg_fragment_re = re.compile(r"[#&]egg=([^&]*)") @property - def egg_fragment(self): + def egg_fragment(self): # type: () -> Optional[str] match = self._egg_fragment_re.search(self.url) if not match: return None @@ -124,7 +135,7 @@ def egg_fragment(self): _subdirectory_fragment_re = re.compile(r"[#&]subdirectory=([^&]*)") @property - def subdirectory_fragment(self): + def subdirectory_fragment(self): # type: () -> Optional[str] match = self._subdirectory_fragment_re.search(self.url) if not match: return None @@ -133,41 +144,41 @@ def subdirectory_fragment(self): _hash_re = re.compile(r"(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)") @property - def hash(self): + def hash(self): # type: () -> Optional[str] match = self._hash_re.search(self.url) if match: return match.group(2) return None @property - def hash_name(self): + def hash_name(self): # type: () -> Optional[str] match = self._hash_re.search(self.url) if match: return match.group(1) return None @property - def show_url(self): + def show_url(self): # type: () -> str return posixpath.basename(self.url.split("#", 1)[0].split("?", 1)[0]) @property - def is_wheel(self): + def is_wheel(self): # type: () -> bool return self.ext == ".whl" @property - def is_wininst(self): + def is_wininst(self): # type: () -> bool return self.ext == ".exe" @property - def is_egg(self): + def is_egg(self): # type: () -> bool return self.ext == ".egg" @property - def is_sdist(self): + def is_sdist(self): # type: () -> bool return self.ext in {".tar.bz2", ".tar.gz", ".zip"} @property - def is_artifact(self): + def is_artifact(self): # type: () -> bool """ Determines if this points to an actual artifact (e.g. a tarball) or if it points to an "abstract" thing like a path or a VCS location. diff --git a/poetry/core/packages/utils/utils.py b/poetry/core/packages/utils/utils.py index e0dd09c54..21c64bb60 100644 --- a/poetry/core/packages/utils/utils.py +++ b/poetry/core/packages/utils/utils.py @@ -3,6 +3,10 @@ import re import sys +from typing import TYPE_CHECKING +from typing import Dict +from typing import List +from typing import Tuple from typing import Union from six.moves.urllib.parse import unquote # noqa @@ -25,6 +29,10 @@ from poetry.core.version.markers import SingleMarker +if TYPE_CHECKING: + from poetry.core.packages.constraints import BaseConstraint # noqa + from poetry.core.semver import VersionTypes # noqa + BZ2_EXTENSIONS = (".tar.bz2", ".tbz") XZ_EXTENSIONS = (".tar.xz", ".txz", ".tlz", ".tar.lz", ".tar.lzma") ZIP_EXTENSIONS = (".zip", ".whl") @@ -82,7 +90,7 @@ def url_to_path(url): # type: (str) -> Path return Path(url2pathname(netloc + unquote(path))) -def is_url(name): +def is_url(name): # type: (str) -> bool if ":" not in name: return False scheme = name.split(":", 1)[0].lower() @@ -102,7 +110,7 @@ def is_url(name): ] -def strip_extras(path): +def strip_extras(path): # type: (str) -> Tuple[str, str] m = re.match(r"^(.+)(\[[^\]]+\])$", path) extras = None if m: @@ -114,7 +122,7 @@ def strip_extras(path): return path_no_extras, extras -def is_installable_dir(path): +def is_installable_dir(path): # type: (str) -> bool """Return True if `path` is a directory containing a setup.py file.""" if not os.path.isdir(path): return False @@ -124,7 +132,7 @@ def is_installable_dir(path): return False -def is_archive_file(name): +def is_archive_file(name): # type: (str) -> bool """Return True if `name` is a considered as an archive file.""" ext = splitext(name)[1].lower() if ext in ARCHIVE_EXTENSIONS: @@ -132,7 +140,7 @@ def is_archive_file(name): return False -def splitext(path): +def splitext(path): # type: (str) -> Tuple[str, str] """Like os.path.splitext, but take off .tar too""" base, ext = posixpath.splitext(path) if base.lower().endswith(".tar"): @@ -141,7 +149,9 @@ def splitext(path): return base, ext -def group_markers(markers, or_=False): +def group_markers( + markers, or_=False +): # type: (List[BaseMarker], bool) -> List[Union[Tuple[str, str, str], List[Tuple[str, str, str]]]] groups = [[]] for marker in markers: @@ -160,12 +170,14 @@ def group_markers(markers, or_=False): return groups -def convert_markers(marker): +def convert_markers(marker): # type: (BaseMarker) -> Dict[str, List[Tuple[str, str]]] groups = group_markers([marker]) requirements = {} - def _group(_groups, or_=False): + def _group( + _groups, or_=False + ): # type: (List[Union[Tuple[str, str, str], List[Tuple[str, str, str]]]], bool) -> None ors = {} for group in _groups: if isinstance(group, list): @@ -197,7 +209,9 @@ def _group(_groups, or_=False): return requirements -def create_nested_marker(name, constraint): +def create_nested_marker( + name, constraint +): # type: (str, Union["BaseConstraint", VersionUnion, Version, VersionConstraint]) -> str if constraint.is_any(): return "" @@ -264,9 +278,7 @@ def create_nested_marker(name, constraint): return marker -def get_python_constraint_from_marker( - marker, -): # type: (BaseMarker) -> VersionConstraint +def get_python_constraint_from_marker(marker,): # type: (BaseMarker) -> "VersionTypes" python_marker = marker.only("python_version", "python_full_version") if python_marker.is_any(): return VersionRange() diff --git a/poetry/core/packages/vcs_dependency.py b/poetry/core/packages/vcs_dependency.py index 6e4248668..2800644aa 100644 --- a/poetry/core/packages/vcs_dependency.py +++ b/poetry/core/packages/vcs_dependency.py @@ -1,5 +1,7 @@ +from typing import TYPE_CHECKING +from typing import FrozenSet from typing import List -from typing import Set +from typing import Optional from typing import Union from poetry.core.vcs import git @@ -7,6 +9,10 @@ from .dependency import Dependency +if TYPE_CHECKING: + from .constraints import BaseConstraint + + class VCSDependency(Dependency): """ Represents a VCS dependency @@ -14,17 +20,17 @@ class VCSDependency(Dependency): def __init__( self, - name, - vcs, - source, - branch=None, - tag=None, - rev=None, - resolved_rev=None, - category="main", - optional=False, - develop=False, - extras=None, # type: Union[List[str], Set[str]] + name, # type: str + vcs, # type: str + source, # type: str + branch=None, # type: Optional[str] + tag=None, # type: Optional[str] + rev=None, # type: Optional[str] + resolved_rev=None, # type: Optional[str] + category="main", # type: str + optional=False, # type: bool + develop=False, # type: bool + extras=None, # type: Union[List[str], FrozenSet[str]] ): self._vcs = vcs self._source = source @@ -52,23 +58,23 @@ def __init__( ) @property - def vcs(self): + def vcs(self): # type: () -> str return self._vcs @property - def source(self): + def source(self): # type: () -> str return self._source @property - def branch(self): + def branch(self): # type: () -> Optional[str] return self._branch @property - def tag(self): + def tag(self): # type: () -> Optional[str] return self._tag @property - def rev(self): + def rev(self): # type: () -> Optional[str] return self._rev @property @@ -116,7 +122,7 @@ def is_vcs(self): # type: () -> bool def accepts_prereleases(self): # type: () -> bool return True - def with_constraint(self, constraint): + def with_constraint(self, constraint): # type: ("BaseConstraint") -> VCSDependency new = VCSDependency( self.pretty_name, self._vcs, @@ -144,7 +150,7 @@ def with_constraint(self, constraint): return new - def __str__(self): + def __str__(self): # type: () -> str reference = self._vcs if self._branch: reference += " branch {}".format(self._branch) @@ -155,5 +161,5 @@ def __str__(self): return "{} ({} {})".format(self._pretty_name, self._constraint, reference) - def __hash__(self): + def __hash__(self): # type: () -> int return hash((self._name, self._vcs, self._branch, self._tag, self._rev)) diff --git a/poetry/core/pyproject/tables.py b/poetry/core/pyproject/tables.py index f69b42ffe..1f3686220 100644 --- a/poetry/core/pyproject/tables.py +++ b/poetry/core/pyproject/tables.py @@ -1,3 +1,4 @@ +from typing import TYPE_CHECKING from typing import List from typing import Optional @@ -5,6 +6,10 @@ from poetry.core.utils.helpers import canonicalize_name +if TYPE_CHECKING: + from poetry.core.packages import Dependency # noqa + + # TODO: Convert to dataclass once python 2.7, 3.5 is dropped class BuildSystem: def __init__( @@ -19,7 +24,7 @@ def __init__( self._dependencies = None @property - def dependencies(self): + def dependencies(self): # type: () -> List["Dependency"] if self._dependencies is None: # avoid circular dependency when loading DirectoryDependency from poetry.core.packages import DirectoryDependency diff --git a/poetry/core/pyproject/toml.py b/poetry/core/pyproject/toml.py index 5beab9221..a223dc1f6 100644 --- a/poetry/core/pyproject/toml.py +++ b/poetry/core/pyproject/toml.py @@ -1,3 +1,4 @@ +from typing import Any from typing import Optional from typing import Union @@ -66,10 +67,10 @@ def is_poetry_project(self): # type: () -> bool pass return False - def __getattr__(self, item): + def __getattr__(self, item): # type: (str) -> Any return getattr(self.data, item) - def save(self): + def save(self): # type: () -> None data = self.data if self._poetry_config is not None: @@ -83,7 +84,7 @@ def save(self): self.file.write(data=data) - def reload(self): + def reload(self): # type: () -> None self._data = None self._build_system = None self._poetry_config = None diff --git a/poetry/core/semver/__init__.py b/poetry/core/semver/__init__.py index e782e3df8..27d4a08af 100644 --- a/poetry/core/semver/__init__.py +++ b/poetry/core/semver/__init__.py @@ -1,5 +1,7 @@ import re +from typing import Union + from .empty_constraint import EmptyConstraint from .exceptions import ParseConstraintError from .patterns import BASIC_CONSTRAINT @@ -13,7 +15,10 @@ from .version_union import VersionUnion -def parse_constraint(constraints): # type: (str) -> VersionConstraint +VersionTypes = Union[Version, VersionRange, VersionUnion, EmptyConstraint] + + +def parse_constraint(constraints): # type: (str) -> VersionTypes if constraints == "*": return VersionRange() @@ -46,7 +51,7 @@ def parse_constraint(constraints): # type: (str) -> VersionConstraint return VersionUnion.of(*or_groups) -def parse_single_constraint(constraint): # type: (str) -> VersionConstraint +def parse_single_constraint(constraint): # type: (str) -> VersionTypes m = re.match(r"(?i)^v?[xX*](\.[xX*])*$", constraint) if m: return VersionRange() diff --git a/poetry/core/semver/empty_constraint.py b/poetry/core/semver/empty_constraint.py index 3c5e27e10..c463fa586 100644 --- a/poetry/core/semver/empty_constraint.py +++ b/poetry/core/semver/empty_constraint.py @@ -1,30 +1,37 @@ +from typing import TYPE_CHECKING + from .version_constraint import VersionConstraint +if TYPE_CHECKING: + from . import VersionTypes # noqa + from .version import Version # noqa + + class EmptyConstraint(VersionConstraint): - def is_empty(self): + def is_empty(self): # type: () -> bool return True - def is_any(self): + def is_any(self): # type: () -> bool return False - def allows(self, version): + def allows(self, version): # type: ("Version") -> bool return False - def allows_all(self, other): + def allows_all(self, other): # type: ("VersionTypes") -> bool return other.is_empty() - def allows_any(self, other): + def allows_any(self, other): # type: ("VersionTypes") -> bool return False - def intersect(self, other): + def intersect(self, other): # type: ("VersionTypes") -> EmptyConstraint return self - def union(self, other): + def union(self, other): # type: ("VersionTypes") -> "VersionTypes" return other - def difference(self, other): + def difference(self, other): # type: ("VersionTypes") -> EmptyConstraint return self - def __str__(self): + def __str__(self): # type: () -> str return "" diff --git a/poetry/core/semver/version.py b/poetry/core/semver/version.py index 143dbd2e4..acd5f3e84 100644 --- a/poetry/core/semver/version.py +++ b/poetry/core/semver/version.py @@ -1,5 +1,6 @@ import re +from typing import TYPE_CHECKING from typing import List from typing import Optional from typing import Union @@ -12,6 +13,10 @@ from .version_union import VersionUnion +if TYPE_CHECKING: + from . import VersionTypes # noqa + + class Version(VersionRange): """ A parsed semantic version number. @@ -119,7 +124,7 @@ def build(self): # type: () -> List[str] return self._build @property - def text(self): + def text(self): # type: () -> str return self._text @property @@ -127,7 +132,7 @@ def precision(self): # type: () -> int return self._precision @property - def stable(self): + def stable(self): # type: () -> Version if not self.is_prerelease(): return self @@ -176,23 +181,23 @@ def first_prerelease(self): # type: () -> Version ) @property - def min(self): + def min(self): # type: () -> Version return self @property - def max(self): + def max(self): # type: () -> Version return self @property - def full_max(self): + def full_max(self): # type: () -> Version return self @property - def include_min(self): + def include_min(self): # type: () -> bool return True @property - def include_max(self): + def include_max(self): # type: () -> bool return True @classmethod @@ -220,10 +225,10 @@ def parse(cls, text): # type: (str) -> Version return Version(major, minor, patch, rest, pre, build, text) - def is_any(self): + def is_any(self): # type: () -> bool return False - def is_empty(self): + def is_empty(self): # type: () -> bool return False def is_prerelease(self): # type: () -> bool @@ -232,19 +237,21 @@ def is_prerelease(self): # type: () -> bool def allows(self, version): # type: (Version) -> bool return self == version - def allows_all(self, other): # type: (VersionConstraint) -> bool + def allows_all(self, other): # type: ("VersionTypes") -> bool return other.is_empty() or other == self - def allows_any(self, other): # type: (VersionConstraint) -> bool + def allows_any(self, other): # type: ("VersionTypes") -> bool return other.allows(self) - def intersect(self, other): # type: (VersionConstraint) -> VersionConstraint + def intersect( + self, other + ): # type: ("VersionTypes") -> Union[Version, EmptyConstraint] if other.allows(self): return self return EmptyConstraint() - def union(self, other): # type: (VersionConstraint) -> VersionConstraint + def union(self, other): # type: ("VersionTypes") -> "VersionTypes" from .version_range import VersionRange if other.allows(self): @@ -269,7 +276,9 @@ def union(self, other): # type: (VersionConstraint) -> VersionConstraint return VersionUnion.of(self, other) - def difference(self, other): # type: (VersionConstraint) -> VersionConstraint + def difference( + self, other + ): # type: ("VersionTypes") -> Union[Version, EmptyConstraint] if other.allows(self): return EmptyConstraint() @@ -293,7 +302,7 @@ def _increment_patch(self): # type: () -> Version self.major, self.minor, self.patch + 1, precision=self._precision ) - def _normalize_prerelease(self, pre): # type: (str) -> str + def _normalize_prerelease(self, pre): # type: (str) -> Optional[str] if not pre: return @@ -318,7 +327,7 @@ def _normalize_prerelease(self, pre): # type: (str) -> str return "{}.{}".format(modifier, number) - def _normalize_build(self, build): # type: (str) -> str + def _normalize_build(self, build): # type: (str) -> Optional[str] if not build: return @@ -341,19 +350,19 @@ def _split_parts(self, text): # type: (str) -> List[Union[str, int]] return parts - def __lt__(self, other): + def __lt__(self, other): # type: (Version) -> int return self._cmp(other) < 0 - def __le__(self, other): + def __le__(self, other): # type: (Version) -> int return self._cmp(other) <= 0 - def __gt__(self, other): + def __gt__(self, other): # type: (Version) -> int return self._cmp(other) > 0 - def __ge__(self, other): + def __ge__(self, other): # type: (Version) -> int return self._cmp(other) >= 0 - def _cmp(self, other): + def _cmp(self, other): # type: (Version) -> int if not isinstance(other, VersionConstraint): return NotImplemented @@ -392,7 +401,7 @@ def _cmp(self, other): return self._cmp_lists(self.build, other.build) - def _cmp_parts(self, a, b): + def _cmp_parts(self, a, b): # type: (Optional[int], Optional[int]) -> int if a < b: return -1 elif a > b: @@ -446,16 +455,16 @@ def __eq__(self, other): # type: (Version) -> bool and self._build == other.build ) - def __ne__(self, other): + def __ne__(self, other): # type: ("VersionTypes") -> bool return not self == other - def __str__(self): + def __str__(self): # type: () -> str return self._text - def __repr__(self): + def __repr__(self): # type: () -> str return "".format(str(self)) - def __hash__(self): + def __hash__(self): # type: () -> int return hash( ( self.major, diff --git a/poetry/core/semver/version_constraint.py b/poetry/core/semver/version_constraint.py index 5016c88ab..343efc8f7 100644 --- a/poetry/core/semver/version_constraint.py +++ b/poetry/core/semver/version_constraint.py @@ -2,7 +2,7 @@ if TYPE_CHECKING: - from poetry.core.semver.version import Version + from poetry.core.semver import Version # noqa class VersionConstraint: diff --git a/poetry/core/semver/version_range.py b/poetry/core/semver/version_range.py index f8e00324e..ae9be3ecf 100644 --- a/poetry/core/semver/version_range.py +++ b/poetry/core/semver/version_range.py @@ -1,5 +1,7 @@ from typing import TYPE_CHECKING +from typing import Any from typing import List +from typing import Optional from .empty_constraint import EmptyConstraint from .version_constraint import VersionConstraint @@ -9,15 +11,17 @@ if TYPE_CHECKING: from poetry.core.semver.version import Version + from . import VersionTypes # noqa + class VersionRange(VersionConstraint): def __init__( self, - min=None, - max=None, - include_min=False, - include_max=False, - always_include_max_prerelease=False, + min=None, # type: Optional["Version"] + max=None, # type: Optional["Version"] + include_min=False, # type: bool + include_max=False, # type: bool + always_include_max_prerelease=False, # type: bool ): full_max = max if ( @@ -40,29 +44,29 @@ def __init__( self._include_max = include_max @property - def min(self): + def min(self): # type: () -> "Version" return self._min @property - def max(self): + def max(self): # type: () -> "Version" return self._max @property - def full_max(self): + def full_max(self): # type: () -> "Version" return self._full_max @property - def include_min(self): + def include_min(self): # type: () -> bool return self._include_min @property - def include_max(self): + def include_max(self): # type: () -> bool return self._include_max - def is_empty(self): + def is_empty(self): # type: () -> bool return False - def is_any(self): + def is_any(self): # type: () -> bool return self._min is None and self._max is None def allows(self, other): # type: ("Version") -> bool @@ -82,7 +86,7 @@ def allows(self, other): # type: ("Version") -> bool return True - def allows_all(self, other): # type: (VersionConstraint) -> bool + def allows_all(self, other): # type: ("VersionTypes") -> bool from .version import Version if other.is_empty(): @@ -99,7 +103,7 @@ def allows_all(self, other): # type: (VersionConstraint) -> bool raise ValueError("Unknown VersionConstraint type {}.".format(other)) - def allows_any(self, other): # type: (VersionConstraint) -> bool + def allows_any(self, other): # type: ("VersionTypes") -> bool from .version import Version if other.is_empty(): @@ -118,7 +122,7 @@ def allows_any(self, other): # type: (VersionConstraint) -> bool raise ValueError("Unknown VersionConstraint type {}.".format(other)) - def intersect(self, other): # type: (VersionConstraint) -> VersionConstraint + def intersect(self, other): # type: ("VersionTypes") -> "VersionTypes" from .version import Version if other.is_empty(): @@ -173,7 +177,7 @@ def intersect(self, other): # type: (VersionConstraint) -> VersionConstraint intersect_min, intersect_max, intersect_include_min, intersect_include_max ) - def union(self, other): # type: (VersionConstraint) -> VersionConstraint + def union(self, other): # type: ("VersionTypes") -> "VersionTypes" from .version import Version if isinstance(other, Version): @@ -225,7 +229,7 @@ def union(self, other): # type: (VersionConstraint) -> VersionConstraint return VersionUnion.of(self, other) - def difference(self, other): # type: (VersionConstraint) -> VersionConstraint + def difference(self, other): # type: ("VersionTypes") -> "VersionTypes" from .version import Version if other.is_empty(): @@ -371,7 +375,7 @@ def is_adjacent_to(self, other): # type: (VersionRange) -> bool and other.include_min ) - def __eq__(self, other): + def __eq__(self, other): # type: (Any) -> int if not isinstance(other, VersionRange): return False @@ -382,16 +386,16 @@ def __eq__(self, other): and self._include_max == other.include_max ) - def __lt__(self, other): + def __lt__(self, other): # type: (VersionRange) -> int return self._cmp(other) < 0 - def __le__(self, other): + def __le__(self, other): # type: (VersionRange) -> int return self._cmp(other) <= 0 - def __gt__(self, other): + def __gt__(self, other): # type: (VersionRange) -> int return self._cmp(other) > 0 - def __ge__(self, other): + def __ge__(self, other): # type: (VersionRange) -> int return self._cmp(other) >= 0 def _cmp(self, other): # type: (VersionRange) -> int @@ -430,7 +434,7 @@ def _compare_max(self, other): # type: (VersionRange) -> int return 0 - def __str__(self): + def __str__(self): # type: () -> str text = "" if self.min is not None: @@ -448,10 +452,10 @@ def __str__(self): return text - def __repr__(self): + def __repr__(self): # type: () -> str return "".format(str(self)) - def __hash__(self): + def __hash__(self): # type: () -> int return ( hash(self.min) ^ hash(self.max) diff --git a/poetry/core/semver/version_union.py b/poetry/core/semver/version_union.py index d60fff647..50a597db6 100644 --- a/poetry/core/semver/version_union.py +++ b/poetry/core/semver/version_union.py @@ -1,4 +1,5 @@ from typing import TYPE_CHECKING +from typing import Any from typing import List from .empty_constraint import EmptyConstraint @@ -6,7 +7,9 @@ if TYPE_CHECKING: - from poetry.core.semver.version import Version + from . import VersionTypes # noqa + from .version import Version + from .version_range import VersionRange class VersionUnion(VersionConstraint): @@ -18,15 +21,15 @@ class VersionUnion(VersionConstraint): as a non-compound value. """ - def __init__(self, *ranges): + def __init__(self, *ranges): # type: (*"VersionRange") -> None self._ranges = list(ranges) @property - def ranges(self): + def ranges(self): # type: () -> List["VersionRange"] return self._ranges @classmethod - def of(cls, *ranges): + def of(cls, *ranges): # type: (*"VersionTypes") -> "VersionTypes" from .version_range import VersionRange flattened = [] @@ -73,16 +76,16 @@ def of(cls, *ranges): return VersionUnion(*merged) - def is_empty(self): + def is_empty(self): # type: () -> bool return False - def is_any(self): + def is_any(self): # type: () -> bool return False def allows(self, version): # type: ("Version") -> bool return any([constraint.allows(version) for constraint in self._ranges]) - def allows_all(self, other): # type: (VersionConstraint) -> bool + def allows_all(self, other): # type: ("VersionTypes") -> bool our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) @@ -97,7 +100,7 @@ def allows_all(self, other): # type: (VersionConstraint) -> bool return their_current_range is None - def allows_any(self, other): # type: (VersionConstraint) -> bool + def allows_any(self, other): # type: ("VersionTypes") -> bool our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) @@ -115,7 +118,7 @@ def allows_any(self, other): # type: (VersionConstraint) -> bool return False - def intersect(self, other): # type: (VersionConstraint) -> VersionConstraint + def intersect(self, other): # type: ("VersionTypes") -> "VersionTypes" our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) new_ranges = [] @@ -136,10 +139,10 @@ def intersect(self, other): # type: (VersionConstraint) -> VersionConstraint return VersionUnion.of(*new_ranges) - def union(self, other): # type: (VersionConstraint) -> VersionConstraint + def union(self, other): # type: ("VersionTypes") -> "VersionTypes" return VersionUnion.of(self, other) - def difference(self, other): # type: (VersionConstraint) -> VersionConstraint + def difference(self, other): # type: ("VersionTypes") -> "VersionTypes" our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) new_ranges = [] @@ -149,7 +152,7 @@ def difference(self, other): # type: (VersionConstraint) -> VersionConstraint "their_range": next(their_ranges, None), } - def their_next_range(): + def their_next_range(): # type: () -> bool state["their_range"] = next(their_ranges, None) if state["their_range"]: return True @@ -162,7 +165,7 @@ def their_next_range(): return False - def our_next_range(include_current=True): + def our_next_range(include_current=True): # type: (bool) -> bool if include_current: new_ranges.append(state["current"]) @@ -219,9 +222,7 @@ def our_next_range(include_current=True): return VersionUnion.of(*new_ranges) - def _ranges_for( - self, constraint - ): # type: (VersionConstraint) -> List["VersionRange"] + def _ranges_for(self, constraint): # type: ("VersionTypes") -> List["VersionRange"] from .version_range import VersionRange if constraint.is_empty(): @@ -241,7 +242,7 @@ def excludes_single_version(self): # type: () -> bool return isinstance(VersionRange().difference(self), Version) - def __eq__(self, other): + def __eq__(self, other): # type: (Any) -> bool if not isinstance(other, VersionUnion): return False @@ -255,7 +256,7 @@ def __hash__(self): # type: () -> int return h - def __str__(self): + def __str__(self): # type: () -> str from .version_range import VersionRange if self.excludes_single_version(): @@ -263,5 +264,5 @@ def __str__(self): return " || ".join([str(r) for r in self._ranges]) - def __repr__(self): + def __repr__(self): # type: () -> str return "".format(str(self)) diff --git a/poetry/core/spdx/__init__.py b/poetry/core/spdx/__init__.py index 6bf6e38c0..713aa30df 100644 --- a/poetry/core/spdx/__init__.py +++ b/poetry/core/spdx/__init__.py @@ -2,15 +2,17 @@ import os from io import open +from typing import Dict +from typing import Optional from .license import License from .updater import Updater -_licenses = None +_licenses = None # type: Optional[Dict[str, License]] -def license_by_id(identifier): +def license_by_id(identifier): # type: (str) -> License if _licenses is None: load_licenses() @@ -24,7 +26,7 @@ def license_by_id(identifier): return _licenses[id] -def load_licenses(): +def load_licenses(): # type: () -> None global _licenses _licenses = {} diff --git a/poetry/core/spdx/license.py b/poetry/core/spdx/license.py index 11bd2942a..f5a9fb6d6 100644 --- a/poetry/core/spdx/license.py +++ b/poetry/core/spdx/license.py @@ -1,4 +1,5 @@ from collections import namedtuple +from typing import Optional class License(namedtuple("License", "id name is_osi_approved is_deprecated")): @@ -130,7 +131,7 @@ class License(namedtuple("License", "id name is_osi_approved is_deprecated")): } @property - def classifier(self): + def classifier(self): # type: () -> str parts = ["License"] if self.is_osi_approved: @@ -143,7 +144,7 @@ def classifier(self): return " :: ".join(parts) @property - def classifier_name(self): + def classifier_name(self): # type: () -> Optional[str] if self.id not in self.CLASSIFIER_SUPPORTED: if self.is_osi_approved: return None diff --git a/poetry/core/spdx/updater.py b/poetry/core/spdx/updater.py index dca4d7223..30c3a5190 100644 --- a/poetry/core/spdx/updater.py +++ b/poetry/core/spdx/updater.py @@ -2,6 +2,9 @@ import os from io import open +from typing import Any +from typing import Dict +from typing import Optional try: @@ -14,10 +17,10 @@ class Updater: BASE_URL = "https://raw.githubusercontent.com/spdx/license-list-data/master/json/" - def __init__(self, base_url=BASE_URL): + def __init__(self, base_url=BASE_URL): # type: (str) -> None self._base_url = base_url - def dump(self, file=None): + def dump(self, file=None): # type: (Optional[str]) -> None if file is None: file = os.path.join(os.path.dirname(__file__), "data", "licenses.json") @@ -28,7 +31,7 @@ def dump(self, file=None): json.dumps(self.get_licenses(licenses_url), indent=2, sort_keys=True) ) - def get_licenses(self, url): + def get_licenses(self, url): # type: (str) -> Dict[str, Any] licenses = {} with urlopen(url) as r: data = json.loads(r.read().decode()) diff --git a/poetry/core/toml/file.py b/poetry/core/toml/file.py index 0c81007fb..574bdcb80 100644 --- a/poetry/core/toml/file.py +++ b/poetry/core/toml/file.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING +from typing import Any from typing import Union from tomlkit.exceptions import TOMLKitError @@ -7,6 +9,10 @@ from poetry.core.utils._compat import Path +if TYPE_CHECKING: + from tomlkit.toml_document import TOMLDocument # noqa + + class TOMLFile(BaseTOMLFile): def __init__(self, path): # type: (Union[str, Path]) -> None if isinstance(path, str): @@ -21,13 +27,13 @@ def path(self): # type: () -> Path def exists(self): # type: () -> bool return self.__path.exists() - def read(self): + def read(self): # type: () -> "TOMLDocument" try: return super(TOMLFile, self).read() except (ValueError, TOMLKitError) as e: raise TOMLError("Invalid TOML file {}: {}".format(self.path.as_posix(), e)) - def __getattr__(self, item): + def __getattr__(self, item): # type: (str) -> Any return getattr(self.__path, item) def __str__(self): # type: () -> str diff --git a/poetry/core/utils/_compat.py b/poetry/core/utils/_compat.py index c94d9f624..7c5daa9f6 100644 --- a/poetry/core/utils/_compat.py +++ b/poetry/core/utils/_compat.py @@ -1,5 +1,10 @@ import sys +from typing import AnyStr +from typing import List +from typing import Optional +from typing import Union + import six.moves.urllib.parse as urllib_parse @@ -34,12 +39,12 @@ shell_quote = shlex.quote if PY35: - from pathlib import Path + from pathlib import Path # noqa else: - from pathlib2 import Path + from pathlib2 import Path # noqa if not PY36: - from collections import OrderedDict + from collections import OrderedDict # noqa else: OrderedDict = dict @@ -50,7 +55,9 @@ FileNotFoundError = IOError # noqa -def decode(string, encodings=None): +def decode( + string, encodings=None +): # type: (Union[AnyStr, unicode], Optional[str]) -> Union[str, bytes] if not PY2 and not isinstance(string, bytes): return string @@ -68,7 +75,9 @@ def decode(string, encodings=None): return string.decode(encodings[0], errors="ignore") -def encode(string, encodings=None): +def encode( + string, encodings=None +): # type: (AnyStr, Optional[str]) -> Union[str, bytes] if not PY2 and isinstance(string, bytes): return string @@ -86,7 +95,7 @@ def encode(string, encodings=None): return string.encode(encodings[0], errors="ignore") -def to_str(string): +def to_str(string): # type: (AnyStr) -> str if isinstance(string, str) or not isinstance(string, (unicode, bytes)): return string @@ -106,7 +115,7 @@ def to_str(string): return getattr(string, method)(encodings[0], errors="ignore") -def list_to_shell_command(cmd): +def list_to_shell_command(cmd): # type: (List[str]) -> str executable = cmd[0] if " " in executable: diff --git a/poetry/core/utils/helpers.py b/poetry/core/utils/helpers.py index f2de92592..b0526011a 100644 --- a/poetry/core/utils/helpers.py +++ b/poetry/core/utils/helpers.py @@ -5,7 +5,10 @@ import tempfile from contextlib import contextmanager +from typing import Any +from typing import Iterator from typing import List +from typing import Union from poetry.core.utils._compat import Path from poetry.core.version import Version @@ -33,7 +36,7 @@ def normalize_version(version): # type: (str) -> str @contextmanager -def temporary_directory(*args, **kwargs): +def temporary_directory(*args, **kwargs): # type: (*Any, **Any) -> Iterator[str] name = tempfile.mkdtemp(*args, **kwargs) yield name safe_rmtree(name) @@ -80,7 +83,7 @@ def parse_requires(requires): # type: (str) -> List[str] return requires_dist -def _on_rm_error(func, path, exc_info): +def _on_rm_error(func, path, exc_info): # type: (Any, Union[str, Path], Any) -> None if not os.path.exists(path): return @@ -88,14 +91,14 @@ def _on_rm_error(func, path, exc_info): func(path) -def safe_rmtree(path): +def safe_rmtree(path): # type: (Union[str, Path]) -> None if Path(path).is_symlink(): return os.unlink(str(path)) shutil.rmtree(path, onerror=_on_rm_error) -def merge_dicts(d1, d2): +def merge_dicts(d1, d2): # type: (dict, dict) -> None for k, v in d2.items(): if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], Mapping): merge_dicts(d1[k], d2[k]) diff --git a/poetry/core/utils/toml_file.py b/poetry/core/utils/toml_file.py index 4ac333afe..7abf9a8a3 100644 --- a/poetry/core/utils/toml_file.py +++ b/poetry/core/utils/toml_file.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- +from typing import Any + from poetry.core.toml import TOMLFile class TomlFile(TOMLFile): @classmethod - def __new__(cls, *args, **kwargs): + def __new__(cls, *args, **kwargs): # type: (*Any, **Any) -> TOMLFile import warnings warnings.warn( diff --git a/poetry/core/vcs/git.py b/poetry/core/vcs/git.py index c060ec060..e1b7a8711 100644 --- a/poetry/core/vcs/git.py +++ b/poetry/core/vcs/git.py @@ -3,7 +3,10 @@ import subprocess from collections import namedtuple +from typing import Any +from typing import Optional +from poetry.core.utils._compat import Path from poetry.core.utils._compat import decode @@ -90,7 +93,16 @@ class ParsedUrl: - def __init__(self, protocol, resource, pathname, user, port, name, rev): + def __init__( + self, + protocol, # type: Optional[str] + resource, # type: Optional[str] + pathname, # type: Optional[str] + user, # type: Optional[str] + port, # type: Optional[str] + name, # type: Optional[str] + rev, # type: Optional[str] + ): self.protocol = protocol self.resource = resource self.pathname = pathname @@ -100,7 +112,7 @@ def __init__(self, protocol, resource, pathname, user, port, name, rev): self.rev = rev @classmethod - def parse(cls, url): # type: () -> ParsedUrl + def parse(cls, url): # type: (str) -> ParsedUrl for pattern in PATTERNS: m = pattern.match(url) if m: @@ -138,7 +150,7 @@ def __str__(self): # type: () -> str class GitConfig: - def __init__(self, requires_git_presence=False): + def __init__(self, requires_git_presence=False): # type: (bool) -> None self._config = {} try: @@ -156,15 +168,15 @@ def __init__(self, requires_git_presence=False): if requires_git_presence: raise - def get(self, key, default=None): + def get(self, key, default=None): # type: (Any, Optional[Any]) -> Any return self._config.get(key, default) - def __getitem__(self, item): + def __getitem__(self, item): # type: (Any) -> Any return self._config[item] class Git: - def __init__(self, work_dir=None): + def __init__(self, work_dir=None): # type: (Optional[Path]) -> None self._config = GitConfig(requires_git_presence=True) self._work_dir = work_dir @@ -196,10 +208,10 @@ def normalize_url(cls, url): # type: (str) -> GitUrl def config(self): # type: () -> GitConfig return self._config - def clone(self, repository, dest): # type: (...) -> str + def clone(self, repository, dest): # type: (str, Path) -> str return self.run("clone", "--recurse-submodules", repository, str(dest)) - def checkout(self, rev, folder=None): # type: (...) -> str + def checkout(self, rev, folder=None): # type: (str, Optional[Path]) -> str args = [] if folder is None and self._work_dir: folder = self._work_dir @@ -216,7 +228,7 @@ def checkout(self, rev, folder=None): # type: (...) -> str return self.run(*args) - def rev_parse(self, rev, folder=None): # type: (...) -> str + def rev_parse(self, rev, folder=None): # type: (str, Optional[Path]) -> str args = [] if folder is None and self._work_dir: folder = self._work_dir @@ -241,7 +253,7 @@ def rev_parse(self, rev, folder=None): # type: (...) -> str return self.run(*args) - def get_ignored_files(self, folder=None): # type: (...) -> list + def get_ignored_files(self, folder=None): # type: (Optional[Path]) -> list args = [] if folder is None and self._work_dir: folder = self._work_dir @@ -259,7 +271,7 @@ def get_ignored_files(self, folder=None): # type: (...) -> list return output.strip().split("\n") - def remote_urls(self, folder=None): # type: (...) -> dict + def remote_urls(self, folder=None): # type: (Optional[Path]) -> dict output = self.run( "config", "--get-regexp", r"remote\..*\.url", folder=folder ).strip() @@ -271,12 +283,12 @@ def remote_urls(self, folder=None): # type: (...) -> dict return urls - def remote_url(self, folder=None): # type: (...) -> str + def remote_url(self, folder=None): # type: (Optional[Path]) -> str urls = self.remote_urls(folder=folder) return urls.get("remote.origin.url", urls[list(urls.keys())[0]]) - def run(self, *args, **kwargs): # type: (...) -> str + def run(self, *args, **kwargs): # type: (*Any, **Any) -> str folder = kwargs.pop("folder", None) if folder: args = ( diff --git a/poetry/core/version/base.py b/poetry/core/version/base.py index b2281c90b..826f86226 100644 --- a/poetry/core/version/base.py +++ b/poetry/core/version/base.py @@ -1,26 +1,33 @@ +from typing import Callable + + class BaseVersion: - def __hash__(self): + def __init__(self, version): # type: (str) -> None + self._version = str(version) + self._key = None + + def __hash__(self): # type: () -> int return hash(self._key) - def __lt__(self, other): + def __lt__(self, other): # type: (BaseVersion) -> bool return self._compare(other, lambda s, o: s < o) - def __le__(self, other): + def __le__(self, other): # type: (BaseVersion) -> bool return self._compare(other, lambda s, o: s <= o) - def __eq__(self, other): + def __eq__(self, other): # type: (BaseVersion) -> bool return self._compare(other, lambda s, o: s == o) - def __ge__(self, other): + def __ge__(self, other): # type: (BaseVersion) -> bool return self._compare(other, lambda s, o: s >= o) - def __gt__(self, other): + def __gt__(self, other): # type: (BaseVersion) -> bool return self._compare(other, lambda s, o: s > o) - def __ne__(self, other): + def __ne__(self, other): # type: (BaseVersion) -> bool return self._compare(other, lambda s, o: s != o) - def _compare(self, other, method): + def _compare(self, other, method): # type: (BaseVersion, Callable) -> bool if not isinstance(other, BaseVersion): return NotImplemented diff --git a/poetry/core/version/helpers.py b/poetry/core/version/helpers.py index f194fcdcc..6ca22228e 100644 --- a/poetry/core/version/helpers.py +++ b/poetry/core/version/helpers.py @@ -1,8 +1,14 @@ +from typing import TYPE_CHECKING +from typing import Union + from poetry.core.semver import Version from poetry.core.semver import VersionUnion from poetry.core.semver import parse_constraint +if TYPE_CHECKING: + from poetry.core.semver import VersionConstraint # noqa + PYTHON_VERSION = [ "2.7.*", "3.0.*", @@ -17,7 +23,9 @@ ] -def format_python_constraint(constraint): +def format_python_constraint( + constraint, +): # type: (Union[Version, VersionUnion, "VersionConstraint"]) -> str """ This helper will help in transforming disjunctive constraint into proper constraint. diff --git a/poetry/core/version/legacy_version.py b/poetry/core/version/legacy_version.py index db6a11b62..adaa53d7e 100644 --- a/poetry/core/version/legacy_version.py +++ b/poetry/core/version/legacy_version.py @@ -1,37 +1,39 @@ import re +from typing import Tuple + from .base import BaseVersion class LegacyVersion(BaseVersion): - def __init__(self, version): + def __init__(self, version): # type: (str) -> None self._version = str(version) self._key = _legacy_cmpkey(self._version) - def __str__(self): + def __str__(self): # type: () -> str return self._version - def __repr__(self): + def __repr__(self): # type: () -> str return "".format(repr(str(self))) @property - def public(self): + def public(self): # type: () -> str return self._version @property - def base_version(self): + def base_version(self): # type: () -> str return self._version @property - def local(self): + def local(self): # type: () -> None return None @property - def is_prerelease(self): + def is_prerelease(self): # type: () -> bool return False @property - def is_postrelease(self): + def is_postrelease(self): # type: () -> bool return False @@ -46,7 +48,7 @@ def is_postrelease(self): } -def _parse_version_parts(s): +def _parse_version_parts(s): # type: (str) -> str for part in _legacy_version_component_re.split(s): part = _legacy_version_replacement_map.get(part, part) @@ -63,7 +65,7 @@ def _parse_version_parts(s): yield "*final" -def _legacy_cmpkey(version): +def _legacy_cmpkey(version): # type: (str) -> Tuple[int, Tuple[str]] # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch # greater than or equal to 0. This will effectively put the LegacyVersion, # which uses the defacto standard originally implemented by setuptools, diff --git a/poetry/core/version/markers.py b/poetry/core/version/markers.py index afc254aa5..894e1dc17 100644 --- a/poetry/core/version/markers.py +++ b/poetry/core/version/markers.py @@ -1,16 +1,26 @@ import os import re +from typing import TYPE_CHECKING from typing import Any from typing import Dict from typing import Iterator from typing import List +from typing import Union from lark import Lark from lark import Token from lark import Tree +if TYPE_CHECKING: + from poetry.core.semver import VersionTypes # noqa + +MarkerTypes = Union[ + "AnyMarker", "EmptyMarker", "SingleMarker", "MultiMarker", "MarkerUnion" +] + + class InvalidMarker(ValueError): """ An invalid marker was found, users should refer to PEP 508. @@ -71,48 +81,48 @@ def only(self, *marker_names): # type: (str) -> BaseMarker def invert(self): # type: () -> BaseMarker raise NotImplementedError() - def __repr__(self): + def __repr__(self): # type: () -> str return "<{} {}>".format(self.__class__.__name__, str(self)) class AnyMarker(BaseMarker): - def intersect(self, other): + def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes return other - def union(self, other): + def union(self, other): # type: (MarkerTypes) -> MarkerTypes return self - def is_any(self): + def is_any(self): # type: () -> bool return True def is_empty(self): # type: () -> bool return False - def validate(self, environment): + def validate(self, environment): # type: (Dict[str, Any]) -> bool return True - def without_extras(self): + def without_extras(self): # type: () -> MarkerTypes return self - def exclude(self, marker_name): # type: (str) -> AnyMarker + def exclude(self, marker_name): # type: (str) -> MarkerTypes return self - def only(self, *marker_names): # type: (str) -> AnyMarker + def only(self, *marker_names): # type: (*str) -> MarkerTypes return self def invert(self): # type: () -> EmptyMarker return EmptyMarker() - def __str__(self): + def __str__(self): # type: () -> str return "" - def __repr__(self): + def __repr__(self): # type: () -> str return "" - def __hash__(self): + def __hash__(self): # type: () -> int return hash(("", "")) - def __eq__(self, other): + def __eq__(self, other): # type: (MarkerTypes) -> bool if not isinstance(other, BaseMarker): return NotImplemented @@ -120,43 +130,43 @@ def __eq__(self, other): class EmptyMarker(BaseMarker): - def intersect(self, other): + def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes return self - def union(self, other): + def union(self, other): # type: (MarkerTypes) -> MarkerTypes return other - def is_any(self): + def is_any(self): # type: () -> bool return False def is_empty(self): # type: () -> bool return True - def validate(self, environment): + def validate(self, environment): # type: (Dict[str, Any]) -> bool return False - def without_extras(self): + def without_extras(self): # type: () -> BaseMarker return self def exclude(self, marker_name): # type: (str) -> EmptyMarker return self - def only(self, *marker_names): # type: (str) -> EmptyMarker + def only(self, *marker_names): # type: (*str) -> EmptyMarker return self def invert(self): # type: () -> AnyMarker return AnyMarker() - def __str__(self): + def __str__(self): # type: () -> str return "" - def __repr__(self): + def __repr__(self): # type: () -> str return "" - def __hash__(self): + def __hash__(self): # type: () -> int return hash(("", "")) - def __eq__(self, other): + def __eq__(self, other): # type: (MarkerTypes) -> bool if not isinstance(other, BaseMarker): return NotImplemented @@ -172,7 +182,9 @@ class SingleMarker(BaseMarker): "platform_release", } - def __init__(self, name, constraint): + def __init__( + self, name, constraint + ): # type: (str, Union[str, "VersionTypes"]) -> None from poetry.core.packages.constraints import ( parse_constraint as parse_generic_constraint, ) @@ -216,29 +228,29 @@ def __init__(self, name, constraint): self._constraint = self._parser(self._constraint_string) @property - def name(self): + def name(self): # type: () -> str return self._name @property - def constraint_string(self): + def constraint_string(self): # type: () -> str if self._operator in {"in", "not in"}: return "{} {}".format(self._operator, self._value) return self._constraint_string @property - def constraint(self): + def constraint(self): # type: () -> "VersionTypes" return self._constraint @property - def operator(self): + def operator(self): # type: () -> str return self._operator @property - def value(self): + def value(self): # type: () -> str return self._value - def intersect(self, other): + def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes if isinstance(other, SingleMarker): if other.name != self.name: return MultiMarker(self, other) @@ -260,7 +272,7 @@ def intersect(self, other): return other.intersect(self) - def union(self, other): + def union(self, other): # type: (MarkerTypes) -> MarkerTypes if isinstance(other, SingleMarker): if self == other: return self @@ -269,7 +281,7 @@ def union(self, other): return other.union(self) - def validate(self, environment): + def validate(self, environment): # type: (Dict[str, Any]) -> bool if environment is None: return True @@ -278,22 +290,22 @@ def validate(self, environment): return self._constraint.allows(self._parser(environment[self._name])) - def without_extras(self): + def without_extras(self): # type: () -> MarkerTypes return self.exclude("extra") - def exclude(self, marker_name): # type: (str) -> BaseMarker + def exclude(self, marker_name): # type: (str) -> MarkerTypes if self.name == marker_name: return AnyMarker() return self - def only(self, *marker_names): # type: (str) -> BaseMarker + def only(self, *marker_names): # type: (*str) -> Union[SingleMarker, EmptyMarker] if self.name not in marker_names: return EmptyMarker() return self - def invert(self): # type: () -> BaseMarker + def invert(self): # type: () -> MarkerTypes if self._operator in ("===", "=="): operator = "!=" elif self._operator == "!=": @@ -338,22 +350,22 @@ def invert(self): # type: () -> BaseMarker return parse_marker("{} {} '{}'".format(self._name, operator, self._value)) - def __eq__(self, other): + def __eq__(self, other): # type: (MarkerTypes) -> bool if not isinstance(other, SingleMarker): return False return self._name == other.name and self._constraint == other.constraint - def __hash__(self): + def __hash__(self): # type: () -> int return hash((self._name, self._constraint_string)) - def __str__(self): + def __str__(self): # type: () -> str return '{} {} "{}"'.format(self._name, self._operator, self._value) def _flatten_markers( markers, flatten_class -): # type: (Iterator[BaseMarker], Any) -> List[BaseMarker] +): # type: (Iterator[Union[MarkerUnion, MultiMarker]], Any) -> List[MarkerTypes] flattened = [] for marker in markers: @@ -366,7 +378,7 @@ def _flatten_markers( class MultiMarker(BaseMarker): - def __init__(self, *markers): + def __init__(self, *markers): # type: (*MarkerTypes) -> None self._markers = [] markers = _flatten_markers(markers, MultiMarker) @@ -375,7 +387,7 @@ def __init__(self, *markers): self._markers.append(m) @classmethod - def of(cls, *markers): + def of(cls, *markers): # type: (*MarkerTypes) -> MarkerTypes new_markers = [] markers = _flatten_markers(markers, MultiMarker) @@ -419,10 +431,10 @@ def of(cls, *markers): return MultiMarker(*new_markers) @property - def markers(self): + def markers(self): # type: () -> List[MarkerTypes] return self._markers - def intersect(self, other): + def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes if other.is_any(): return self @@ -433,23 +445,23 @@ def intersect(self, other): return MultiMarker.of(*new_markers) - def union(self, other): + def union(self, other): # type: (MarkerTypes) -> MarkerTypes if isinstance(other, (SingleMarker, MultiMarker)): return MarkerUnion.of(self, other) return other.union(self) - def validate(self, environment): + def validate(self, environment): # type: (Dict[str, Any]) -> bool for m in self._markers: if not m.validate(environment): return False return True - def without_extras(self): + def without_extras(self): # type: () -> MarkerTypes return self.exclude("extra") - def exclude(self, marker_name): # type: (str) -> BaseMarker + def exclude(self, marker_name): # type: (str) -> MarkerTypes new_markers = [] for m in self._markers: @@ -464,7 +476,7 @@ def exclude(self, marker_name): # type: (str) -> BaseMarker return self.of(*new_markers) - def only(self, *marker_names): # type: (str) -> BaseMarker + def only(self, *marker_names): # type: (*str) -> MarkerTypes new_markers = [] for m in self._markers: @@ -479,25 +491,25 @@ def only(self, *marker_names): # type: (str) -> BaseMarker return self.of(*new_markers) - def invert(self): # type: () -> MarkerUnion + def invert(self): # type: () -> MarkerTypes markers = [marker.invert() for marker in self._markers] return MarkerUnion.of(*markers) - def __eq__(self, other): + def __eq__(self, other): # type: (MarkerTypes) -> bool if not isinstance(other, MultiMarker): return False return set(self._markers) == set(other.markers) - def __hash__(self): + def __hash__(self): # type: () -> int h = hash("multi") for m in self._markers: h |= hash(m) return h - def __str__(self): + def __str__(self): # type: () -> str elements = [] for m in self._markers: if isinstance(m, SingleMarker): @@ -511,15 +523,15 @@ def __str__(self): class MarkerUnion(BaseMarker): - def __init__(self, *markers): + def __init__(self, *markers): # type: (*MarkerTypes) -> None self._markers = list(markers) @property - def markers(self): + def markers(self): # type: () -> List[MarkerTypes] return self._markers @classmethod - def of(cls, *markers): # type: (BaseMarker) -> MarkerUnion + def of(cls, *markers): # type: (*BaseMarker) -> MarkerTypes flattened_markers = _flatten_markers(markers, MarkerUnion) markers = [] @@ -562,13 +574,13 @@ def of(cls, *markers): # type: (BaseMarker) -> MarkerUnion return MarkerUnion(*markers) - def append(self, marker): + def append(self, marker): # type: (MarkerTypes) -> None if marker in self._markers: return self._markers.append(marker) - def intersect(self, other): + def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes if other.is_any(): return self @@ -592,7 +604,7 @@ def intersect(self, other): return MarkerUnion.of(*new_markers) - def union(self, other): + def union(self, other): # type: (MarkerTypes) -> MarkerTypes if other.is_any(): return other @@ -603,17 +615,17 @@ def union(self, other): return MarkerUnion.of(*new_markers) - def validate(self, environment): + def validate(self, environment): # type: (Dict[str, Any]) -> bool for m in self._markers: if m.validate(environment): return True return False - def without_extras(self): + def without_extras(self): # type: () -> MarkerTypes return self.exclude("extra") - def exclude(self, marker_name): # type: (str) -> BaseMarker + def exclude(self, marker_name): # type: (str) -> MarkerTypes new_markers = [] for m in self._markers: @@ -628,7 +640,7 @@ def exclude(self, marker_name): # type: (str) -> BaseMarker return self.of(*new_markers) - def only(self, *marker_names): # type: (str) -> BaseMarker + def only(self, *marker_names): # type: (*str) -> MarkerTypes new_markers = [] for m in self._markers: @@ -643,37 +655,37 @@ def only(self, *marker_names): # type: (str) -> BaseMarker return self.of(*new_markers) - def invert(self): # type: () -> MultiMarker + def invert(self): # type: () -> MarkerTypes markers = [marker.invert() for marker in self._markers] return MultiMarker.of(*markers) - def __eq__(self, other): + def __eq__(self, other): # type: (MarkerTypes) -> bool if not isinstance(other, MarkerUnion): return False return set(self._markers) == set(other.markers) - def __hash__(self): + def __hash__(self): # type: () -> int h = hash("union") for m in self._markers: h |= hash(m) return h - def __str__(self): + def __str__(self): # type: () -> str return " or ".join( str(m) for m in self._markers if not m.is_any() and not m.is_empty() ) - def is_any(self): + def is_any(self): # type: () -> bool return any(m.is_any() for m in self._markers) - def is_empty(self): + def is_empty(self): # type: () -> bool return all(m.is_empty() for m in self._markers) -def parse_marker(marker): +def parse_marker(marker): # type: (str) -> MarkerTypes if marker == "": return EmptyMarker() @@ -687,7 +699,7 @@ def parse_marker(marker): return markers -def _compact_markers(tree_elements, tree_prefix=""): # type: (Tree, str) -> BaseMarker +def _compact_markers(tree_elements, tree_prefix=""): # type: (Tree, str) -> MarkerTypes groups = [MultiMarker()] for token in tree_elements: if isinstance(token, Token): diff --git a/poetry/core/version/requirements.py b/poetry/core/version/requirements.py index a3cb3cf0f..c9601a07f 100644 --- a/poetry/core/version/requirements.py +++ b/poetry/core/version/requirements.py @@ -43,7 +43,7 @@ class Requirement(object): string. """ - def __init__(self, requirement_string): + def __init__(self, requirement_string): # type: (str) -> None try: parsed = _parser.parse(requirement_string) except (UnexpectedCharacters, UnexpectedToken) as e: @@ -101,7 +101,7 @@ def __init__(self, requirement_string): self.marker = marker - def __str__(self): + def __str__(self): # type: () -> str parts = [self.name] if self.extras: @@ -118,5 +118,5 @@ def __str__(self): return "".join(parts) - def __repr__(self): + def __repr__(self): # type: () -> str return "".format(str(self)) diff --git a/poetry/core/version/utils.py b/poetry/core/version/utils.py index 4e3d50feb..a81a9e7f2 100644 --- a/poetry/core/version/utils.py +++ b/poetry/core/version/utils.py @@ -1,29 +1,32 @@ +from typing import Any + + class Infinity(object): - def __repr__(self): + def __repr__(self): # type: () -> str return "Infinity" - def __hash__(self): + def __hash__(self): # type: () -> int return hash(repr(self)) - def __lt__(self, other): + def __lt__(self, other): # type: (Any) -> bool return False - def __le__(self, other): + def __le__(self, other): # type: (Any) -> bool return False - def __eq__(self, other): + def __eq__(self, other): # type: (Any) -> bool return isinstance(other, self.__class__) - def __ne__(self, other): + def __ne__(self, other): # type: (Any) -> bool return not isinstance(other, self.__class__) - def __gt__(self, other): + def __gt__(self, other): # type: (Any) -> bool return True - def __ge__(self, other): + def __ge__(self, other): # type: (Any) -> bool return True - def __neg__(self): + def __neg__(self): # type: () -> NegativeInfinity return NegativeInfinity @@ -31,31 +34,31 @@ def __neg__(self): class NegativeInfinity(object): - def __repr__(self): + def __repr__(self): # type: () -> str return "-Infinity" - def __hash__(self): + def __hash__(self): # type: () -> int return hash(repr(self)) - def __lt__(self, other): + def __lt__(self, other): # type: (Any) -> bool return True - def __le__(self, other): + def __le__(self, other): # type: (Any) -> bool return True - def __eq__(self, other): + def __eq__(self, other): # type: (Any) -> bool return isinstance(other, self.__class__) - def __ne__(self, other): + def __ne__(self, other): # type: (Any) -> bool return not isinstance(other, self.__class__) - def __gt__(self, other): + def __gt__(self, other): # type: (Any) -> bool return False - def __ge__(self, other): + def __ge__(self, other): # type: (Any) -> bool return False - def __neg__(self): + def __neg__(self): # type: () -> Infinity return Infinity diff --git a/poetry/core/version/version.py b/poetry/core/version/version.py index b85c6c5c7..0726d9439 100644 --- a/poetry/core/version/version.py +++ b/poetry/core/version/version.py @@ -2,10 +2,15 @@ from collections import namedtuple from itertools import dropwhile +from typing import Any +from typing import Optional +from typing import Tuple +from typing import Union from .base import BaseVersion from .exceptions import InvalidVersion from .utils import Infinity +from .utils import NegativeInfinity _Version = namedtuple("_Version", ["epoch", "release", "dev", "pre", "post", "local"]) @@ -49,7 +54,7 @@ class Version(BaseVersion): - def __init__(self, version): + def __init__(self, version): # type: (str) -> None # Validate the version and parse it into pieces match = VERSION_PATTERN.match(version) if not match: @@ -77,10 +82,10 @@ def __init__(self, version): self._version.local, ) - def __repr__(self): + def __repr__(self): # type: () -> str return "".format(repr(str(self))) - def __str__(self): + def __str__(self): # type: () -> str parts = [] # Epoch @@ -109,11 +114,11 @@ def __str__(self): return "".join(parts) @property - def public(self): + def public(self): # type: () -> str return str(self).split("+", 1)[0] @property - def base_version(self): + def base_version(self): # type: () -> str parts = [] # Epoch @@ -126,21 +131,23 @@ def base_version(self): return "".join(parts) @property - def local(self): + def local(self): # type: () -> str version_string = str(self) if "+" in version_string: return version_string.split("+", 1)[1] @property - def is_prerelease(self): + def is_prerelease(self): # type: () -> bool return bool(self._version.dev or self._version.pre) @property - def is_postrelease(self): + def is_postrelease(self): # type: () -> bool return bool(self._version.post) -def _parse_letter_version(letter, number): +def _parse_letter_version( + letter, number +): # type: (str, Optional[str]) -> Tuple[str, int] if letter: # We consider there to be an implicit 0 in a pre-release if there is # not a numeral associated with it. @@ -174,7 +181,7 @@ def _parse_letter_version(letter, number): _local_version_seperators = re.compile(r"[._-]") -def _parse_local_version(local): +def _parse_local_version(local): # type: (Optional[str]) -> Tuple[Union[str, int], ...] """ Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). """ @@ -185,7 +192,14 @@ def _parse_local_version(local): ) -def _cmpkey(epoch, release, pre, post, dev, local): +def _cmpkey( + epoch, # type: int + release, # type: Optional[Tuple[int, ...]] + pre, # type: Optional[Tuple[str, int]] + post, # type: Optional[Tuple[str, int]] + dev, # type: Optional[Tuple[str, int]] + local, # type: Optional[Tuple[Union[str, int], ...]] +): # type: (...) -> Tuple[int, Tuple[int, ...], Union[Union[Infinity, NegativeInfinity, Tuple[str, int]], Any], Union[NegativeInfinity, Tuple[str, int]], Union[Union[Infinity, Tuple[str, int]], Any], Union[NegativeInfinity, Tuple[Union[Tuple[int, str], Tuple[NegativeInfinity, Union[str, int]]], ...]]] # When we compare a release version, we want to compare it with all of the # trailing zeros removed. So we'll use a reverse the list, drop all the now # leading zeros until we come to something non zero, then take the rest From cdc45d19b4860dbf09c6cb5573bb5d2f956b58e0 Mon Sep 17 00:00:00 2001 From: Giovanni Barillari Date: Mon, 18 Jan 2021 18:16:52 +0100 Subject: [PATCH 14/77] Update packaging dep to v20.8 --- poetry.lock | 355 ++--- poetry/core/_vendor/_pyrsistent_version.py | 2 +- poetry/core/_vendor/attr/__init__.py | 16 +- poetry/core/_vendor/attr/_compat.py | 7 +- poetry/core/_vendor/attr/_funcs.py | 124 +- poetry/core/_vendor/attr/_make.py | 1245 ++++++++++++----- poetry/core/_vendor/attr/_next_gen.py | 160 +++ poetry/core/_vendor/attr/converters.py | 9 +- poetry/core/_vendor/attr/exceptions.py | 26 +- poetry/core/_vendor/attr/setters.py | 77 + poetry/core/_vendor/attr/validators.py | 5 +- poetry/core/_vendor/packaging/__about__.py | 4 +- poetry/core/_vendor/packaging/requirements.py | 20 +- poetry/core/_vendor/packaging/specifiers.py | 27 +- poetry/core/_vendor/packaging/tags.py | 225 ++- poetry/core/_vendor/packaging/utils.py | 18 +- poetry/core/_vendor/packaging/version.py | 41 +- .../pyrsistent/{LICENCE.mit => LICENSE.mit} | 0 poetry/core/_vendor/vendor.txt | 13 +- pyproject.toml | 3 +- vendors/poetry.lock | 77 +- vendors/pyproject.toml | 3 +- 22 files changed, 1792 insertions(+), 665 deletions(-) create mode 100644 poetry/core/_vendor/attr/_next_gen.py create mode 100644 poetry/core/_vendor/attr/setters.py rename poetry/core/_vendor/pyrsistent/{LICENCE.mit => LICENSE.mit} (100%) diff --git a/poetry.lock b/poetry.lock index 099974e5d..682e6ab72 100644 --- a/poetry.lock +++ b/poetry.lock @@ -27,17 +27,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "19.3.0" +version = "20.3.0" description = "Classes Without Boilerplate" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] -dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] -docs = ["sphinx", "zope.interface"] -tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] [[package]] name = "backports.functools-lru-cache" @@ -49,7 +49,7 @@ python-versions = ">=2.6" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-black-multipy", "pytest-cov"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-black-multipy", "pytest-cov"] [[package]] name = "backports.tempfile" @@ -72,7 +72,7 @@ python-versions = "*" [[package]] name = "certifi" -version = "2020.6.20" +version = "2020.12.5" description = "Python package for providing Mozilla's CA Bundle." category = "dev" optional = false @@ -91,11 +91,11 @@ six = "*" [[package]] name = "chardet" -version = "3.0.4" +version = "4.0.0" description = "Universal encoding detector for Python 2 and 3" category = "dev" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "click" @@ -107,7 +107,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "colorama" -version = "0.4.3" +version = "0.4.4" description = "Cross-platform colored terminal text." category = "dev" optional = false @@ -123,7 +123,7 @@ python-versions = ">=2.6" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2)", "pytest-flake8", "pytest-black-multipy"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2)", "pytest-flake8", "pytest-black-multipy"] [[package]] name = "contextlib2" @@ -135,7 +135,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "coverage" -version = "5.2.1" +version = "5.3.1" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -194,7 +194,7 @@ python-versions = ">=2.6, <3" [[package]] name = "identify" -version = "1.4.26" +version = "1.5.13" description = "File identification library for Python" category = "dev" optional = false @@ -219,27 +219,24 @@ category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -[package.extras] -docs = ["sphinx", "rst.linker"] -testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] - [package.dependencies] -zipp = ">=0.5" configparser = {version = ">=3.5", markers = "python_version < \"3\""} contextlib2 = {version = "*", markers = "python_version < \"3\""} pathlib2 = {version = "*", markers = "python_version < \"3\""} +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "rst.linker"] +testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] [[package]] name = "importlib-resources" -version = "3.0.0" +version = "3.2.1" description = "Read resources from Python packages" category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -[package.extras] -docs = ["sphinx", "rst.linker", "jaraco.packaging"] - [package.dependencies] contextlib2 = {version = "*", markers = "python_version < \"3\""} pathlib2 = {version = "*", markers = "python_version < \"3\""} @@ -247,6 +244,9 @@ singledispatch = {version = "*", markers = "python_version < \"3.4\""} typing = {version = "*", markers = "python_version < \"3.5\""} zipp = {version = ">=0.4", markers = "python_version < \"3.8\""} +[package.extras] +docs = ["sphinx", "rst.linker", "jaraco.packaging"] + [[package]] name = "jsonschema" version = "3.2.0" @@ -255,15 +255,15 @@ category = "dev" optional = false python-versions = "*" -[package.extras] -format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] -format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] - [package.dependencies] attrs = ">=17.4.0" pyrsistent = ">=0.14.0" six = ">=1.11.0" +[package.extras] +format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] +format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] + [[package]] name = "mock" version = "3.0.5" @@ -272,15 +272,15 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[package.dependencies] +funcsigs = {version = ">=1", markers = "python_version < \"3.3\""} +six = "*" + [package.extras] build = ["twine", "wheel", "blurb"] docs = ["sphinx"] test = ["pytest", "pytest-cov"] -[package.dependencies] -six = "*" -funcsigs = {version = ">=1", markers = "python_version < \"3.3\""} - [[package]] name = "more-itertools" version = "5.0.0" @@ -294,7 +294,7 @@ six = ">=1.0.0,<2.0.0" [[package]] name = "more-itertools" -version = "8.4.0" +version = "8.6.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false @@ -302,7 +302,7 @@ python-versions = ">=3.5" [[package]] name = "nodeenv" -version = "1.4.0" +version = "1.5.0" description = "Node.js virtual environment builder" category = "dev" optional = false @@ -310,7 +310,7 @@ python-versions = "*" [[package]] name = "packaging" -version = "20.4" +version = "20.8" description = "Core utilities for Python packages" category = "dev" optional = false @@ -318,7 +318,6 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] pyparsing = ">=2.0.2" -six = "*" [[package]] name = "pathlib2" @@ -329,8 +328,8 @@ optional = false python-versions = "*" [package.dependencies] -six = "*" scandir = {version = "*", markers = "python_version < \"3.5\""} +six = "*" [[package]] name = "pep517" @@ -341,8 +340,8 @@ optional = false python-versions = "*" [package.dependencies] -toml = "*" importlib_metadata = {version = "*", markers = "python_version < \"3.8\""} +toml = "*" zipp = {version = "*", markers = "python_version < \"3.8\""} [[package]] @@ -353,12 +352,12 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[package.extras] -dev = ["pre-commit", "tox"] - [package.dependencies] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +[package.extras] +dev = ["pre-commit", "tox"] + [[package]] name = "pre-commit" version = "1.21.0" @@ -370,19 +369,19 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] "aspy.yaml" = "*" cfgv = ">=2.0.0" +futures = {version = "*", markers = "python_version < \"3.2\""} identify = ">=1.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-resources = {version = "*", markers = "python_version < \"3.7\""} nodeenv = ">=0.11.1" pyyaml = "*" six = "*" toml = "*" virtualenv = ">=15.2" -futures = {version = "*", markers = "python_version < \"3.2\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -importlib-resources = {version = "*", markers = "python_version < \"3.7\""} [[package]] name = "py" -version = "1.9.0" +version = "1.10.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" category = "dev" optional = false @@ -398,11 +397,11 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pyrsistent" -version = "0.16.0" +version = "0.16.1" description = "Persistent/Functional/Immutable data structures" category = "dev" optional = false -python-versions = "*" +python-versions = ">=2.7" [package.dependencies] six = "*" @@ -415,45 +414,41 @@ category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -[package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"] - [package.dependencies] atomicwrites = ">=1.0" attrs = ">=17.4.0" +colorama = {version = "*", markers = "sys_platform == \"win32\" and python_version != \"3.4\""} +funcsigs = {version = ">=1.0", markers = "python_version < \"3.0\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +more-itertools = [ + {version = ">=4.0.0,<6.0.0", markers = "python_version <= \"2.7\""}, + {version = ">=4.0.0", markers = "python_version > \"2.7\""}, +] packaging = "*" +pathlib2 = {version = ">=2.2.0", markers = "python_version < \"3.6\""} pluggy = ">=0.12,<1.0" py = ">=1.5.0" six = ">=1.10.0" wcwidth = "*" -colorama = {version = "*", markers = "sys_platform == \"win32\" and python_version != \"3.4\""} -funcsigs = {version = ">=1.0", markers = "python_version < \"3.0\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -pathlib2 = {version = ">=2.2.0", markers = "python_version < \"3.6\""} - -[[package.dependencies.more-itertools]] -version = ">=4.0.0,<6.0.0" -markers = "python_version <= \"2.7\"" -[[package.dependencies.more-itertools]] -version = ">=4.0.0" -markers = "python_version > \"2.7\"" +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"] [[package]] name = "pytest-cov" -version = "2.10.1" +version = "2.11.0" description = "Pytest plugin for measuring coverage." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -[package.extras] -testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "pytest-xdist", "virtualenv"] - [package.dependencies] -coverage = ">=4.4" +coverage = ">=5.2.1" pytest = ">=4.6" +[package.extras] +testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] + [[package]] name = "pytest-mock" version = "2.0.0" @@ -462,12 +457,12 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[package.extras] -dev = ["pre-commit", "tox"] - [package.dependencies] -pytest = ">=2.7" mock = {version = "*", markers = "python_version < \"3.0\""} +pytest = ">=2.7" + +[package.extras] +dev = ["pre-commit", "tox"] [[package]] name = "pyyaml" @@ -479,21 +474,21 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "requests" -version = "2.24.0" +version = "2.25.1" description = "Python HTTP for Humans." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -[package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] - [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" +chardet = ">=3.0.2,<5" idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] [[package]] name = "scandir" @@ -524,34 +519,34 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "toml" -version = "0.10.1" +version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" category = "dev" optional = false -python-versions = "*" +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tox" -version = "3.19.0" +version = "3.21.1" description = "tox is a generic virtualenv management and test command line tool" category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -[package.extras] -docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] -testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)", "pytest-xdist (>=1.22.2)"] - [package.dependencies] +colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} filelock = ">=3.0.0" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} packaging = ">=14" pluggy = ">=0.12.0" py = ">=1.4.17" six = ">=1.14.0" toml = ">=0.9.4" virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" -colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = ">=0.12,<2", markers = "python_version < \"3.8\""} + +[package.extras] +docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] +testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)", "pytest-xdist (>=1.22.2)", "pathlib2 (>=2.3.3)"] [[package]] name = "typing" @@ -563,7 +558,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "urllib3" -version = "1.25.10" +version = "1.26.2" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false @@ -571,20 +566,16 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] brotli = ["brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "vendoring" -version = "0.3.2" +version = "0.3.3" description = "A command line tool, to simplify vendoring pure Python dependencies." category = "dev" optional = false -python-versions = "~= 3.8.0" - -[package.extras] -doc = ["sphinx"] -test = ["pytest", "pytest-xdist", "pytest-cov", "pytest-mock"] +python-versions = "~= 3.8" [package.dependencies] click = "*" @@ -593,26 +584,30 @@ packaging = "*" requests = "*" toml = "*" +[package.extras] +doc = ["sphinx"] +test = ["pytest", "pytest-xdist", "pytest-cov", "pytest-mock"] + [[package]] name = "virtualenv" -version = "20.0.30" +version = "20.3.1" description = "Virtual Python Environment builder" category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -[package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] -testing = ["coverage (>=5)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] - [package.dependencies] appdirs = ">=1.4.3,<2" distlib = ">=0.3.1,<1" filelock = ">=3.0.0,<4" -six = ">=1.9.0,<2" -importlib-metadata = {version = ">=0.12,<2", markers = "python_version < \"3.8\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} pathlib2 = {version = ">=2.3.3,<3", markers = "python_version < \"3.4\" and sys_platform != \"win32\""} +six = ">=1.9.0,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] [[package]] name = "wcwidth" @@ -633,17 +628,17 @@ category = "main" optional = false python-versions = ">=2.7" +[package.dependencies] +contextlib2 = {version = "*", markers = "python_version < \"3.4\""} + [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["pathlib2", "unittest2", "jaraco.itertools", "func-timeout"] -[package.dependencies] -contextlib2 = {version = "*", markers = "python_version < \"3.4\""} - [metadata] lock-version = "1.1" python-versions = "~2.7 || ^3.5" -content-hash = "6e56f24c806c05b85548fd96b962ed2b0d2b53e8723d382de9118e788b63323d" +content-hash = "73b0c1d12930e6381fb1a47e8c903603ac18a2981ee899f9f675c4ebcbbbe3ff" [metadata.files] appdirs = [ @@ -659,8 +654,8 @@ atomicwrites = [ {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, - {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] "backports.functools-lru-cache" = [ {file = "backports.functools_lru_cache-1.6.1-py2.py3-none-any.whl", hash = "sha256:0bada4c2f8a43d533e4ecb7a12214d9420e66eb206d54bf2d682581ca4b80848"}, @@ -675,24 +670,24 @@ attrs = [ {file = "backports.weakref-1.0.post1.tar.gz", hash = "sha256:bc4170a29915f8b22c9e7c4939701859650f2eb84184aee80da329ac0b9825c2"}, ] certifi = [ - {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, - {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, ] cfgv = [ {file = "cfgv-2.0.1-py2.py3-none-any.whl", hash = "sha256:fbd93c9ab0a523bf7daec408f3be2ed99a980e20b2d19b50fc184ca6b820d289"}, {file = "cfgv-2.0.1.tar.gz", hash = "sha256:edb387943b665bf9c434f717bf630fa78aecd53d5900d2e05da6ad6048553144"}, ] chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, ] colorama = [ - {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, - {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] configparser = [ {file = "configparser-4.0.2-py2.py3-none-any.whl", hash = "sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c"}, @@ -703,40 +698,55 @@ contextlib2 = [ {file = "contextlib2-0.6.0.post1.tar.gz", hash = "sha256:01f490098c18b19d2bd5bb5dc445b2054d2fa97f09a4280ba2c5f3c394c8162e"}, ] coverage = [ - {file = "coverage-5.2.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4"}, - {file = "coverage-5.2.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01"}, - {file = "coverage-5.2.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8"}, - {file = "coverage-5.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59"}, - {file = "coverage-5.2.1-cp27-cp27m-win32.whl", hash = "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3"}, - {file = "coverage-5.2.1-cp27-cp27m-win_amd64.whl", hash = "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f"}, - {file = "coverage-5.2.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd"}, - {file = "coverage-5.2.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651"}, - {file = "coverage-5.2.1-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b"}, - {file = "coverage-5.2.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d"}, - {file = "coverage-5.2.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3"}, - {file = "coverage-5.2.1-cp35-cp35m-win32.whl", hash = "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0"}, - {file = "coverage-5.2.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962"}, - {file = "coverage-5.2.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082"}, - {file = "coverage-5.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716"}, - {file = "coverage-5.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb"}, - {file = "coverage-5.2.1-cp36-cp36m-win32.whl", hash = "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d"}, - {file = "coverage-5.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546"}, - {file = "coverage-5.2.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811"}, - {file = "coverage-5.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258"}, - {file = "coverage-5.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034"}, - {file = "coverage-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46"}, - {file = "coverage-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8"}, - {file = "coverage-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0"}, - {file = "coverage-5.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd"}, - {file = "coverage-5.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b"}, - {file = "coverage-5.2.1-cp38-cp38-win32.whl", hash = "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd"}, - {file = "coverage-5.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d"}, - {file = "coverage-5.2.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3"}, - {file = "coverage-5.2.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4"}, - {file = "coverage-5.2.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4"}, - {file = "coverage-5.2.1-cp39-cp39-win32.whl", hash = "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89"}, - {file = "coverage-5.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b"}, - {file = "coverage-5.2.1.tar.gz", hash = "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b"}, + {file = "coverage-5.3.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fabeeb121735d47d8eab8671b6b031ce08514c86b7ad8f7d5490a7b6dcd6267d"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7e4d159021c2029b958b2363abec4a11db0ce8cd43abb0d9ce44284cb97217e7"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:378ac77af41350a8c6b8801a66021b52da8a05fd77e578b7380e876c0ce4f528"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e448f56cfeae7b1b3b5bcd99bb377cde7c4eb1970a525c770720a352bc4c8044"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:cc44e3545d908ecf3e5773266c487ad1877be718d9dc65fc7eb6e7d14960985b"}, + {file = "coverage-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:08b3ba72bd981531fd557f67beee376d6700fba183b167857038997ba30dd297"}, + {file = "coverage-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:8dacc4073c359f40fcf73aede8428c35f84639baad7e1b46fce5ab7a8a7be4bb"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ee2f1d1c223c3d2c24e3afbb2dd38be3f03b1a8d6a83ee3d9eb8c36a52bee899"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9a9d4ff06804920388aab69c5ea8a77525cf165356db70131616acd269e19b36"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:782a5c7df9f91979a7a21792e09b34a658058896628217ae6362088b123c8500"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:fda29412a66099af6d6de0baa6bd7c52674de177ec2ad2630ca264142d69c6c7"}, + {file = "coverage-5.3.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:f2c6888eada180814b8583c3e793f3f343a692fc802546eed45f40a001b1169f"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8f33d1156241c43755137288dea619105477961cfa7e47f48dbf96bc2c30720b"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b239711e774c8eb910e9b1ac719f02f5ae4bf35fa0420f438cdc3a7e4e7dd6ec"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:f54de00baf200b4539a5a092a759f000b5f45fd226d6d25a76b0dff71177a714"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:be0416074d7f253865bb67630cf7210cbc14eb05f4099cc0f82430135aaa7a3b"}, + {file = "coverage-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:c46643970dff9f5c976c6512fd35768c4a3819f01f61169d8cdac3f9290903b7"}, + {file = "coverage-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9a4f66259bdd6964d8cf26142733c81fb562252db74ea367d9beb4f815478e72"}, + {file = "coverage-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c6e5174f8ca585755988bc278c8bb5d02d9dc2e971591ef4a1baabdf2d99589b"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3911c2ef96e5ddc748a3c8b4702c61986628bb719b8378bf1e4a6184bbd48fe4"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c5ec71fd4a43b6d84ddb88c1df94572479d9a26ef3f150cef3dacefecf888105"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f51dbba78d68a44e99d484ca8c8f604f17e957c1ca09c3ebc2c7e3bbd9ba0448"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a2070c5affdb3a5e751f24208c5c4f3d5f008fa04d28731416e023c93b275277"}, + {file = "coverage-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:535dc1e6e68fad5355f9984d5637c33badbdc987b0c0d303ee95a6c979c9516f"}, + {file = "coverage-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:a4857f7e2bc6921dbd487c5c88b84f5633de3e7d416c4dc0bb70256775551a6c"}, + {file = "coverage-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fac3c432851038b3e6afe086f777732bcf7f6ebbfd90951fa04ee53db6d0bcdd"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cd556c79ad665faeae28020a0ab3bda6cd47d94bec48e36970719b0b86e4dcf4"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a66ca3bdf21c653e47f726ca57f46ba7fc1f260ad99ba783acc3e58e3ebdb9ff"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ab110c48bc3d97b4d19af41865e14531f300b482da21783fdaacd159251890e8"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e52d3d95df81c8f6b2a1685aabffadf2d2d9ad97203a40f8d61e51b70f191e4e"}, + {file = "coverage-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:fa10fee7e32213f5c7b0d6428ea92e3a3fdd6d725590238a3f92c0de1c78b9d2"}, + {file = "coverage-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ce6f3a147b4b1a8b09aae48517ae91139b1b010c5f36423fa2b866a8b23df879"}, + {file = "coverage-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:93a280c9eb736a0dcca19296f3c30c720cb41a71b1f9e617f341f0a8e791a69b"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3102bb2c206700a7d28181dbe04d66b30780cde1d1c02c5f3c165cf3d2489497"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8ffd4b204d7de77b5dd558cdff986a8274796a1e57813ed005b33fd97e29f059"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a607ae05b6c96057ba86c811d9c43423f35e03874ffb03fbdcd45e0637e8b631"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:3a3c3f8863255f3c31db3889f8055989527173ef6192a283eb6f4db3c579d830"}, + {file = "coverage-5.3.1-cp38-cp38-win32.whl", hash = "sha256:ff1330e8bc996570221b450e2d539134baa9465f5cb98aff0e0f73f34172e0ae"}, + {file = "coverage-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:3498b27d8236057def41de3585f317abae235dd3a11d33e01736ffedb2ef8606"}, + {file = "coverage-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ceb499d2b3d1d7b7ba23abe8bf26df5f06ba8c71127f188333dddcf356b4b63f"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:3b14b1da110ea50c8bcbadc3b82c3933974dbeea1832e814aab93ca1163cd4c1"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:76b2775dda7e78680d688daabcb485dc87cf5e3184a0b3e012e1d40e38527cc8"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:cef06fb382557f66d81d804230c11ab292d94b840b3cb7bf4450778377b592f4"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f61319e33222591f885c598e3e24f6a4be3533c1d70c19e0dc59e83a71ce27d"}, + {file = "coverage-5.3.1-cp39-cp39-win32.whl", hash = "sha256:cc6f8246e74dd210d7e2b56c76ceaba1cc52b025cd75dbe96eb48791e0250e98"}, + {file = "coverage-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:2757fa64e11ec12220968f65d086b7a29b6583d16e9a544c889b22ba98555ef1"}, + {file = "coverage-5.3.1-pp36-none-any.whl", hash = "sha256:723d22d324e7997a651478e9c5a3120a0ecbc9a7e94071f7e1954562a8806cf3"}, + {file = "coverage-5.3.1-pp37-none-any.whl", hash = "sha256:c89b558f8a9a5a6f2cfc923c304d49f0ce629c3bd85cb442ca258ec20366394c"}, + {file = "coverage-5.3.1.tar.gz", hash = "sha256:38f16b1317b8dd82df67ed5daa5f5e7c959e46579840d77a67a4ceb9cef0a50b"}, ] distlib = [ {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, @@ -764,8 +774,8 @@ futures = [ {file = "futures-3.3.0.tar.gz", hash = "sha256:7e033af76a5e35f58e56da7a91e687706faf4e7bdfb2cbc3f2cca6b9bcda9794"}, ] identify = [ - {file = "identify-1.4.26-py2.py3-none-any.whl", hash = "sha256:150e7c679d6c12e67d18a63808f59068e98576b9ab1fd0ebfcc013c885c5f2e7"}, - {file = "identify-1.4.26.tar.gz", hash = "sha256:1e9d119ad2aea3af2dec09cf8d4b89af11aa9069ea48240338b53f9dbeedc015"}, + {file = "identify-1.5.13-py2.py3-none-any.whl", hash = "sha256:9dfb63a2e871b807e3ba62f029813552a24b5289504f5b071dea9b041aee9fe4"}, + {file = "identify-1.5.13.tar.gz", hash = "sha256:70b638cf4743f33042bebb3b51e25261a0a10e80f978739f17e7fd4837664a66"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, @@ -776,8 +786,8 @@ importlib-metadata = [ {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, ] importlib-resources = [ - {file = "importlib_resources-3.0.0-py2.py3-none-any.whl", hash = "sha256:d028f66b66c0d5732dae86ba4276999855e162a749c92620a38c1d779ed138a7"}, - {file = "importlib_resources-3.0.0.tar.gz", hash = "sha256:19f745a6eca188b490b1428c8d1d4a0d2368759f32370ea8fb89cad2ab1106c3"}, + {file = "importlib_resources-3.2.1-py2.py3-none-any.whl", hash = "sha256:e2860cf0c4bc999947228d18be154fa3779c5dde0b882bd2d7b3f4d25e698bd6"}, + {file = "importlib_resources-3.2.1.tar.gz", hash = "sha256:a9fe213ab6452708ec1b3f4ec6f2881b8ab3645cb4e5efb7fea2bbf05a91db3b"}, ] jsonschema = [ {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, @@ -791,15 +801,16 @@ more-itertools = [ {file = "more-itertools-5.0.0.tar.gz", hash = "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4"}, {file = "more_itertools-5.0.0-py2-none-any.whl", hash = "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc"}, {file = "more_itertools-5.0.0-py3-none-any.whl", hash = "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"}, - {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"}, - {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"}, + {file = "more-itertools-8.6.0.tar.gz", hash = "sha256:b3a9005928e5bed54076e6e549c792b306fddfe72b2d1d22dd63d42d5d3899cf"}, + {file = "more_itertools-8.6.0-py3-none-any.whl", hash = "sha256:8e1a2a43b2f2727425f2b5839587ae37093f19153dc26c0927d1048ff6557330"}, ] nodeenv = [ - {file = "nodeenv-1.4.0-py2.py3-none-any.whl", hash = "sha256:4b0b77afa3ba9b54f4b6396e60b0c83f59eaeb2d63dc3cc7a70f7f4af96c82bc"}, + {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, + {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, ] packaging = [ - {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, - {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, + {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, + {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, ] pathlib2 = [ {file = "pathlib2-2.3.5-py2.py3-none-any.whl", hash = "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db"}, @@ -818,23 +829,23 @@ pre-commit = [ {file = "pre_commit-1.21.0.tar.gz", hash = "sha256:8f48d8637bdae6fa70cc97db9c1dd5aa7c5c8bf71968932a380628c25978b850"}, ] py = [ - {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, - {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, + {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pyrsistent = [ - {file = "pyrsistent-0.16.0.tar.gz", hash = "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3"}, + {file = "pyrsistent-0.16.1.tar.gz", hash = "sha256:aa2ae1c2e496f4d6777f869ea5de7166a8ccb9c2e06ebcf6c7ff1b670c98c5ef"}, ] pytest = [ {file = "pytest-4.6.11-py2.py3-none-any.whl", hash = "sha256:a00a7d79cbbdfa9d21e7d0298392a8dd4123316bfac545075e6f8f24c94d8c97"}, {file = "pytest-4.6.11.tar.gz", hash = "sha256:50fa82392f2120cc3ec2ca0a75ee615be4c479e66669789771f1758332be4353"}, ] pytest-cov = [ - {file = "pytest-cov-2.10.1.tar.gz", hash = "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"}, - {file = "pytest_cov-2.10.1-py2.py3-none-any.whl", hash = "sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191"}, + {file = "pytest-cov-2.11.0.tar.gz", hash = "sha256:e90e034cde61dacb1394639a33f449725c591025b182d69752c1dd0bfec639a7"}, + {file = "pytest_cov-2.11.0-py2.py3-none-any.whl", hash = "sha256:626a8a6ab188656c4f84b67d22436d6c494699d917e567e0048dda6e7f59e028"}, ] pytest-mock = [ {file = "pytest-mock-2.0.0.tar.gz", hash = "sha256:b35eb281e93aafed138db25c8772b95d3756108b601947f89af503f8c629413f"}, @@ -854,8 +865,8 @@ pyyaml = [ {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, ] scandir = [ {file = "scandir-1.10.0-cp27-cp27m-win32.whl", hash = "sha256:92c85ac42f41ffdc35b6da57ed991575bdbe69db895507af88b9f499b701c188"}, @@ -879,28 +890,28 @@ six = [ {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] toml = [ - {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, - {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tox = [ - {file = "tox-3.19.0-py2.py3-none-any.whl", hash = "sha256:3d94b6921a0b6dc90fd8128df83741f30bb41ccd6cd52d131a6a6944ca8f16e6"}, - {file = "tox-3.19.0.tar.gz", hash = "sha256:17e61a93afe5c49281fb969ab71f7a3f22d7586d1c56f9a74219910f356fe7d3"}, + {file = "tox-3.21.1-py2.py3-none-any.whl", hash = "sha256:8a28facf65275c84b3bfde102afe24541d2ce02fd83ea07ce906537c3a74ed2d"}, + {file = "tox-3.21.1.tar.gz", hash = "sha256:31379f2662393034203c5d6b2ebb7b86c67452cfc689784475c1c6e4a3cbc0cd"}, ] typing = [ {file = "typing-3.7.4.3-py2-none-any.whl", hash = "sha256:283d868f5071ab9ad873e5e52268d611e851c870a2ba354193026f2dfb29d8b5"}, {file = "typing-3.7.4.3.tar.gz", hash = "sha256:1187fb9c82fd670d10aa07bbb6cfcfe4bdda42d6fab8d5134f04e8c4d0b71cc9"}, ] urllib3 = [ - {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, - {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, + {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"}, + {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"}, ] vendoring = [ - {file = "vendoring-0.3.2-py2.py3-none-any.whl", hash = "sha256:cbe826d310aae559391ed593f5c8466312451705c822e38380e1d05702e0f6a2"}, - {file = "vendoring-0.3.2.tar.gz", hash = "sha256:952e851adbd5f3c080f06a19e9dc67c7ea91d85ec482c78758f794bfea4d23f1"}, + {file = "vendoring-0.3.3-py2.py3-none-any.whl", hash = "sha256:2b91c302116320f903fdb5e60c2b0805d807e2b87425ab0c86624028aa5ffa57"}, + {file = "vendoring-0.3.3.tar.gz", hash = "sha256:2bbfc0c8da2863f4638c854b91e80c5d0ca57db62fb979d4b0f52088eeab1162"}, ] virtualenv = [ - {file = "virtualenv-20.0.30-py2.py3-none-any.whl", hash = "sha256:8cd7b2a4850b003a11be2fc213e206419efab41115cc14bca20e69654f2ac08e"}, - {file = "virtualenv-20.0.30.tar.gz", hash = "sha256:7b54fd606a1b85f83de49ad8d80dbec08e983a2d2f96685045b262ebc7481ee5"}, + {file = "virtualenv-20.3.1-py2.py3-none-any.whl", hash = "sha256:14b34341e742bdca219e10708198e704e8a7064dd32f474fc16aca68ac53a306"}, + {file = "virtualenv-20.3.1.tar.gz", hash = "sha256:0c111a2236b191422b37fe8c28b8c828ced39aab4bf5627fa5c331aeffb570d9"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, diff --git a/poetry/core/_vendor/_pyrsistent_version.py b/poetry/core/_vendor/_pyrsistent_version.py index 8911e95ca..9513287c9 100644 --- a/poetry/core/_vendor/_pyrsistent_version.py +++ b/poetry/core/_vendor/_pyrsistent_version.py @@ -1 +1 @@ -__version__ = '0.16.0' +__version__ = '0.16.1' diff --git a/poetry/core/_vendor/attr/__init__.py b/poetry/core/_vendor/attr/__init__.py index 9ff4d47ff..bf329cad5 100644 --- a/poetry/core/_vendor/attr/__init__.py +++ b/poetry/core/_vendor/attr/__init__.py @@ -1,10 +1,12 @@ from __future__ import absolute_import, division, print_function +import sys + from functools import partial -from . import converters, exceptions, filters, validators +from . import converters, exceptions, filters, setters, validators from ._config import get_run_validators, set_run_validators -from ._funcs import asdict, assoc, astuple, evolve, has +from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types from ._make import ( NOTHING, Attribute, @@ -19,7 +21,7 @@ from ._version_info import VersionInfo -__version__ = "19.3.0" +__version__ = "20.3.0" __version_info__ = VersionInfo._from_version_string(__version__) __title__ = "attrs" @@ -39,7 +41,6 @@ ib = attr = attrib dataclass = partial(attrs, auto_attribs=True) # happy Easter ;) - __all__ = [ "Attribute", "Factory", @@ -61,8 +62,15 @@ "has", "ib", "make_class", + "resolve_types", "s", "set_run_validators", + "setters", "validate", "validators", ] + +if sys.version_info[:2] >= (3, 6): + from ._next_gen import define, field, frozen, mutable + + __all__.extend((define, field, frozen, mutable)) diff --git a/poetry/core/_vendor/attr/_compat.py b/poetry/core/_vendor/attr/_compat.py index a915db8eb..b0ead6e1c 100644 --- a/poetry/core/_vendor/attr/_compat.py +++ b/poetry/core/_vendor/attr/_compat.py @@ -19,9 +19,10 @@ if PY2: - from UserDict import IterableUserDict from collections import Mapping, Sequence + from UserDict import IterableUserDict + # We 'bundle' isclass instead of using inspect as importing inspect is # fairly expensive (order of 10-15 ms for a modern machine in 2016) def isclass(klass): @@ -90,7 +91,7 @@ def metadata_proxy(d): res.data.update(d) # We blocked update, so we have to do it like this. return res - def just_warn(*args, **kw): # pragma: nocover + def just_warn(*args, **kw): # pragma: no cover """ We only warn on Python 3 because we are not aware of any concrete consequences of not setting the cell on Python 2. @@ -131,7 +132,7 @@ def make_set_closure_cell(): """ # pypy makes this easy. (It also supports the logic below, but # why not do the easy/fast thing?) - if PYPY: # pragma: no cover + if PYPY: def set_closure_cell(cell, value): cell.__setstate__((value,)) diff --git a/poetry/core/_vendor/attr/_funcs.py b/poetry/core/_vendor/attr/_funcs.py index c077e4284..e6c930cbd 100644 --- a/poetry/core/_vendor/attr/_funcs.py +++ b/poetry/core/_vendor/attr/_funcs.py @@ -13,6 +13,7 @@ def asdict( filter=None, dict_factory=dict, retain_collection_types=False, + value_serializer=None, ): """ Return the ``attrs`` attribute values of *inst* as a dict. @@ -32,6 +33,10 @@ def asdict( :param bool retain_collection_types: Do not convert to ``list`` when encountering an attribute whose type is ``tuple`` or ``set``. Only meaningful if ``recurse`` is ``True``. + :param Optional[callable] value_serializer: A hook that is called for every + attribute or dict key/value. It receives the current instance, field + and value and must return the (updated) value. The hook is run *after* + the optional *filter* has been applied. :rtype: return type of *dict_factory* @@ -40,6 +45,7 @@ def asdict( .. versionadded:: 16.0.0 *dict_factory* .. versionadded:: 16.1.0 *retain_collection_types* + .. versionadded:: 20.3.0 *value_serializer* """ attrs = fields(inst.__class__) rv = dict_factory() @@ -47,17 +53,30 @@ def asdict( v = getattr(inst, a.name) if filter is not None and not filter(a, v): continue + + if value_serializer is not None: + v = value_serializer(inst, a, v) + if recurse is True: if has(v.__class__): rv[a.name] = asdict( - v, True, filter, dict_factory, retain_collection_types + v, + True, + filter, + dict_factory, + retain_collection_types, + value_serializer, ) - elif isinstance(v, (tuple, list, set)): + elif isinstance(v, (tuple, list, set, frozenset)): cf = v.__class__ if retain_collection_types is True else list rv[a.name] = cf( [ _asdict_anything( - i, filter, dict_factory, retain_collection_types + i, + filter, + dict_factory, + retain_collection_types, + value_serializer, ) for i in v ] @@ -67,10 +86,18 @@ def asdict( rv[a.name] = df( ( _asdict_anything( - kk, filter, df, retain_collection_types + kk, + filter, + df, + retain_collection_types, + value_serializer, ), _asdict_anything( - vv, filter, df, retain_collection_types + vv, + filter, + df, + retain_collection_types, + value_serializer, ), ) for kk, vv in iteritems(v) @@ -82,19 +109,36 @@ def asdict( return rv -def _asdict_anything(val, filter, dict_factory, retain_collection_types): +def _asdict_anything( + val, + filter, + dict_factory, + retain_collection_types, + value_serializer, +): """ ``asdict`` only works on attrs instances, this works on anything. """ if getattr(val.__class__, "__attrs_attrs__", None) is not None: # Attrs class. - rv = asdict(val, True, filter, dict_factory, retain_collection_types) - elif isinstance(val, (tuple, list, set)): + rv = asdict( + val, + True, + filter, + dict_factory, + retain_collection_types, + value_serializer, + ) + elif isinstance(val, (tuple, list, set, frozenset)): cf = val.__class__ if retain_collection_types is True else list rv = cf( [ _asdict_anything( - i, filter, dict_factory, retain_collection_types + i, + filter, + dict_factory, + retain_collection_types, + value_serializer, ) for i in val ] @@ -103,13 +147,20 @@ def _asdict_anything(val, filter, dict_factory, retain_collection_types): df = dict_factory rv = df( ( - _asdict_anything(kk, filter, df, retain_collection_types), - _asdict_anything(vv, filter, df, retain_collection_types), + _asdict_anything( + kk, filter, df, retain_collection_types, value_serializer + ), + _asdict_anything( + vv, filter, df, retain_collection_types, value_serializer + ), ) for kk, vv in iteritems(val) ) else: rv = val + if value_serializer is not None: + rv = value_serializer(None, None, rv) + return rv @@ -164,7 +215,7 @@ def astuple( retain_collection_types=retain, ) ) - elif isinstance(v, (tuple, list, set)): + elif isinstance(v, (tuple, list, set, frozenset)): cf = v.__class__ if retain is True else list rv.append( cf( @@ -209,6 +260,7 @@ def astuple( rv.append(v) else: rv.append(v) + return rv if tuple_factory is list else tuple_factory(rv) @@ -287,4 +339,52 @@ def evolve(inst, **changes): init_name = attr_name if attr_name[0] != "_" else attr_name[1:] if init_name not in changes: changes[init_name] = getattr(inst, attr_name) + return cls(**changes) + + +def resolve_types(cls, globalns=None, localns=None): + """ + Resolve any strings and forward annotations in type annotations. + + This is only required if you need concrete types in `Attribute`'s *type* + field. In other words, you don't need to resolve your types if you only + use them for static type checking. + + With no arguments, names will be looked up in the module in which the class + was created. If this is not what you want, e.g. if the name only exists + inside a method, you may pass *globalns* or *localns* to specify other + dictionaries in which to look up these names. See the docs of + `typing.get_type_hints` for more details. + + :param type cls: Class to resolve. + :param Optional[dict] globalns: Dictionary containing global variables. + :param Optional[dict] localns: Dictionary containing local variables. + + :raise TypeError: If *cls* is not a class. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + :raise NameError: If types cannot be resolved because of missing variables. + + :returns: *cls* so you can use this function also as a class decorator. + Please note that you have to apply it **after** `attr.s`. That means + the decorator has to come in the line **before** `attr.s`. + + .. versionadded:: 20.1.0 + """ + try: + # Since calling get_type_hints is expensive we cache whether we've + # done it already. + cls.__attrs_types_resolved__ + except AttributeError: + import typing + + hints = typing.get_type_hints(cls, globalns=globalns, localns=localns) + for field in fields(cls): + if field.name in hints: + # Since fields have been frozen we must work around it. + _obj_setattr(field, "type", hints[field.name]) + cls.__attrs_types_resolved__ = True + + # Return the class so you can use it as a decorator too. + return cls diff --git a/poetry/core/_vendor/attr/_make.py b/poetry/core/_vendor/attr/_make.py index 46f9c54ec..49484f935 100644 --- a/poetry/core/_vendor/attr/_make.py +++ b/poetry/core/_vendor/attr/_make.py @@ -9,9 +9,10 @@ from operator import itemgetter -from . import _config +from . import _config, setters from ._compat import ( PY2, + PYPY, isclass, iteritems, metadata_proxy, @@ -29,7 +30,7 @@ # This is used at least twice, so cache it here. _obj_setattr = object.__setattr__ -_init_converter_pat = "__attr_converter_{}" +_init_converter_pat = "__attr_converter_%s" _init_factory_pat = "__attr_factory_{}" _tuple_property_pat = ( " {attr_name} = _attrs_property(_attrs_itemgetter({index}))" @@ -70,6 +71,31 @@ def __repr__(self): """ +class _CacheHashWrapper(int): + """ + An integer subclass that pickles / copies as None + + This is used for non-slots classes with ``cache_hash=True``, to avoid + serializing a potentially (even likely) invalid hash value. Since ``None`` + is the default value for uncalculated hashes, whenever this is copied, + the copy's value for the hash should automatically reset. + + See GH #613 for more details. + """ + + if PY2: + # For some reason `type(None)` isn't callable in Python 2, but we don't + # actually need a constructor for None objects, we just need any + # available function that returns None. + def __reduce__(self, _none_constructor=getattr, _args=(0, "", None)): + return _none_constructor, _args + + else: + + def __reduce__(self, _none_constructor=type(None), _args=()): + return _none_constructor, _args + + def attrib( default=NOTHING, validator=None, @@ -84,6 +110,7 @@ def attrib( kw_only=False, eq=None, order=None, + on_setattr=None, ): """ Create a new attribute on a class. @@ -101,7 +128,7 @@ def attrib( used to construct a new value (useful for mutable data types like lists or dicts). - If a default is not set (or set manually to ``attr.NOTHING``), a value + If a default is not set (or set manually to `attr.NOTHING`), a value *must* be supplied when instantiating; otherwise a `TypeError` will be raised. @@ -110,7 +137,7 @@ def attrib( :type default: Any value :param callable factory: Syntactic sugar for - ``default=attr.Factory(callable)``. + ``default=attr.Factory(factory)``. :param validator: `callable` that is called by ``attrs``-generated ``__init__`` methods after the instance has been initialized. They @@ -120,7 +147,7 @@ def attrib( The return value is *not* inspected so the validator has to throw an exception itself. - If a ``list`` is passed, its items are treated as validators and must + If a `list` is passed, its items are treated as validators and must all pass. Validators can be globally disabled and re-enabled using @@ -128,7 +155,7 @@ def attrib( The validator can also be set using decorator notation as shown below. - :type validator: ``callable`` or a ``list`` of ``callable``\\ s. + :type validator: `callable` or a `list` of `callable`\\ s. :param repr: Include this attribute in the generated ``__repr__`` method. If ``True``, include the attribute; if ``False``, omit it. By @@ -137,7 +164,7 @@ def attrib( value and returns a string. Note that the resulting string is used as-is, i.e. it will be used directly *instead* of calling ``repr()`` (the default). - :type repr: a ``bool`` or a ``callable`` to use a custom function. + :type repr: a `bool` or a `callable` to use a custom function. :param bool eq: If ``True`` (default), include this attribute in the generated ``__eq__`` and ``__ne__`` methods that check two instances for equality. @@ -145,17 +172,16 @@ def attrib( generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. :param bool cmp: Setting to ``True`` is equivalent to setting ``eq=True, order=True``. Deprecated in favor of *eq* and *order*. - :param hash: Include this attribute in the generated ``__hash__`` - method. If ``None`` (default), mirror *eq*'s value. This is the - correct behavior according the Python spec. Setting this value to - anything else than ``None`` is *discouraged*. - :type hash: ``bool`` or ``None`` + :param Optional[bool] hash: Include this attribute in the generated + ``__hash__`` method. If ``None`` (default), mirror *eq*'s value. This + is the correct behavior according the Python spec. Setting this value + to anything else than ``None`` is *discouraged*. :param bool init: Include this attribute in the generated ``__init__`` method. It is possible to set this to ``False`` and set a default value. In that case this attributed is unconditionally initialized with the specified default value or factory. :param callable converter: `callable` that is called by - ``attrs``-generated ``__init__`` methods to converter attribute's value + ``attrs``-generated ``__init__`` methods to convert attribute's value to the desired format. It is given the passed-in value, and the returned value will be used as the new value of the attribute. The value is converted before being passed to the validator, if any. @@ -174,6 +200,12 @@ def attrib( :param kw_only: Make this attribute keyword-only (Python 3+) in the generated ``__init__`` (if ``init`` is ``False``, this parameter is ignored). + :param on_setattr: Allows to overwrite the *on_setattr* setting from + `attr.s`. If left `None`, the *on_setattr* value from `attr.s` is used. + Set to `attr.setters.NO_OP` to run **no** `setattr` hooks for this + attribute -- regardless of the setting in `attr.s`. + :type on_setattr: `callable`, or a list of callables, or `None`, or + `attr.setters.NO_OP` .. versionadded:: 15.2.0 *convert* .. versionadded:: 16.3.0 *metadata* @@ -191,8 +223,10 @@ def attrib( .. versionchanged:: 19.2.0 *repr* also accepts a custom callable. .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 """ - eq, order = _determine_eq_order(cmp, eq, order) + eq, order = _determine_eq_order(cmp, eq, order, True) if hash is not None and hash is not True and hash is not False: raise TypeError( @@ -212,6 +246,16 @@ def attrib( if metadata is None: metadata = {} + # Apply syntactic sugar by auto-wrapping. + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + if validator and isinstance(validator, (list, tuple)): + validator = and_(*validator) + + if converter and isinstance(converter, (list, tuple)): + converter = pipe(*converter) + return _CountingAttr( default=default, validator=validator, @@ -225,6 +269,7 @@ def attrib( kw_only=kw_only, eq=eq, order=order, + on_setattr=on_setattr, ) @@ -282,20 +327,32 @@ def _is_class_var(annot): return str(annot).startswith(_classvar_prefixes) -def _get_annotations(cls): +def _has_own_attribute(cls, attrib_name): """ - Get annotations for *cls*. + Check whether *cls* defines *attrib_name* (and doesn't just inherit it). + + Requires Python 3. """ - anns = getattr(cls, "__annotations__", None) - if anns is None: - return {} + attr = getattr(cls, attrib_name, _sentinel) + if attr is _sentinel: + return False - # Verify that the annotations aren't merely inherited. for base_cls in cls.__mro__[1:]: - if anns is getattr(base_cls, "__annotations__", None): - return {} + a = getattr(base_cls, attrib_name, None) + if attr is a: + return False - return anns + return True + + +def _get_annotations(cls): + """ + Get annotations for *cls*. + """ + if _has_own_attribute(cls, "__annotations__"): + return cls.__annotations__ + + return {} def _counter_getter(e): @@ -305,12 +362,76 @@ def _counter_getter(e): return e[1].counter -def _transform_attrs(cls, these, auto_attribs, kw_only): +def _collect_base_attrs(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in reversed(cls.__mro__[1:-1]): + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.inherited or a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + # For each name, only keep the freshest definition i.e. the furthest at the + # back. base_attr_map is fine because it gets overwritten with every new + # instance. + filtered = [] + seen = set() + for a in reversed(base_attrs): + if a.name in seen: + continue + filtered.insert(0, a) + seen.add(a.name) + + return filtered, base_attr_map + + +def _collect_base_attrs_broken(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + + N.B. *taken_attr_names* will be mutated. + + Adhere to the old incorrect behavior. + + Notably it collects from the front and considers inherited attributes which + leads to the buggy behavior reported in #428. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in cls.__mro__[1:-1]: + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) + taken_attr_names.add(a.name) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + return base_attrs, base_attr_map + + +def _transform_attrs( + cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer +): """ Transform all `_CountingAttr`s on a class into `Attribute`s. If *these* is passed, use that and don't look for them on the class. + *collect_by_mro* is True, collect them in the correct MRO order, otherwise + use the old -- incorrect -- order. See #428. + Return an `_Attributes`. """ cd = cls.__dict__ @@ -334,6 +455,7 @@ def _transform_attrs(cls, these, auto_attribs, kw_only): continue annot_names.add(attr_name) a = cd.get(attr_name, NOTHING) + if not isinstance(a, _CountingAttr): if a is NOTHING: a = attrib() @@ -367,30 +489,22 @@ def _transform_attrs(cls, these, auto_attribs, kw_only): for attr_name, ca in ca_list ] - base_attrs = [] - base_attr_map = {} # A dictionary of base attrs to their classes. - taken_attr_names = {a.name: a for a in own_attrs} - - # Traverse the MRO and collect attributes. - for base_cls in cls.__mro__[1:-1]: - sub_attrs = getattr(base_cls, "__attrs_attrs__", None) - if sub_attrs is not None: - for a in sub_attrs: - prev_a = taken_attr_names.get(a.name) - # Only add an attribute if it hasn't been defined before. This - # allows for overwriting attribute definitions by subclassing. - if prev_a is None: - base_attrs.append(a) - taken_attr_names[a.name] = a - base_attr_map[a.name] = base_cls + if collect_by_mro: + base_attrs, base_attr_map = _collect_base_attrs( + cls, {a.name for a in own_attrs} + ) + else: + base_attrs, base_attr_map = _collect_base_attrs_broken( + cls, {a.name for a in own_attrs} + ) attr_names = [a.name for a in base_attrs + own_attrs] AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) if kw_only: - own_attrs = [a._assoc(kw_only=True) for a in own_attrs] - base_attrs = [a._assoc(kw_only=True) for a in base_attrs] + own_attrs = [a.evolve(kw_only=True) for a in own_attrs] + base_attrs = [a.evolve(kw_only=True) for a in base_attrs] attrs = AttrsClass(base_attrs + own_attrs) @@ -409,14 +523,34 @@ def _transform_attrs(cls, these, auto_attribs, kw_only): if had_default is False and a.default is not NOTHING: had_default = True + if field_transformer is not None: + attrs = field_transformer(cls, attrs) return _Attributes((attrs, base_attrs, base_attr_map)) -def _frozen_setattrs(self, name, value): - """ - Attached to frozen classes as __setattr__. - """ - raise FrozenInstanceError() +if PYPY: + + def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + if isinstance(self, BaseException) and name in ( + "__cause__", + "__context__", + ): + BaseException.__setattr__(self, name, value) + return + + raise FrozenInstanceError() + + +else: + + def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + raise FrozenInstanceError() def _frozen_delattrs(self, name): @@ -432,19 +566,22 @@ class _ClassBuilder(object): """ __slots__ = ( - "_cls", - "_cls_dict", + "_attr_names", "_attrs", + "_base_attr_map", "_base_names", - "_attr_names", - "_slots", - "_frozen", - "_weakref_slot", "_cache_hash", - "_has_post_init", + "_cls", + "_cls_dict", "_delete_attribs", - "_base_attr_map", + "_frozen", + "_has_post_init", "_is_exc", + "_on_setattr", + "_slots", + "_weakref_slot", + "_has_own_setattr", + "_has_custom_setattr", ) def __init__( @@ -454,13 +591,23 @@ def __init__( slots, frozen, weakref_slot, + getstate_setstate, auto_attribs, kw_only, cache_hash, is_exc, + collect_by_mro, + on_setattr, + has_custom_setattr, + field_transformer, ): attrs, base_attrs, base_map = _transform_attrs( - cls, these, auto_attribs, kw_only + cls, + these, + auto_attribs, + kw_only, + collect_by_mro, + field_transformer, ) self._cls = cls @@ -470,12 +617,16 @@ def __init__( self._base_attr_map = base_map self._attr_names = tuple(a.name for a in attrs) self._slots = slots - self._frozen = frozen or _has_frozen_base_class(cls) + self._frozen = frozen self._weakref_slot = weakref_slot self._cache_hash = cache_hash self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) self._delete_attribs = not bool(these) self._is_exc = is_exc + self._on_setattr = on_setattr + + self._has_custom_setattr = has_custom_setattr + self._has_own_setattr = False self._cls_dict["__attrs_attrs__"] = self._attrs @@ -483,6 +634,14 @@ def __init__( self._cls_dict["__setattr__"] = _frozen_setattrs self._cls_dict["__delattr__"] = _frozen_delattrs + self._has_own_setattr = True + + if getstate_setstate: + ( + self._cls_dict["__getstate__"], + self._cls_dict["__setstate__"], + ) = self._make_getstate_setstate() + def __repr__(self): return "<_ClassBuilder(cls={cls})>".format(cls=self._cls.__name__) @@ -523,25 +682,15 @@ def _patch_original_class(self): for name, value in self._cls_dict.items(): setattr(cls, name, value) - # Attach __setstate__. This is necessary to clear the hash code - # cache on deserialization. See issue - # https://github.com/python-attrs/attrs/issues/482 . - # Note that this code only handles setstate for dict classes. - # For slotted classes, see similar code in _create_slots_class . - if self._cache_hash: - existing_set_state_method = getattr(cls, "__setstate__", None) - if existing_set_state_method: - raise NotImplementedError( - "Currently you cannot use hash caching if " - "you specify your own __setstate__ method." - "See https://github.com/python-attrs/attrs/issues/494 ." - ) - - def cache_hash_set_state(chss_self, _): - # clear hash code cache - setattr(chss_self, _hash_cache_field, None) + # If we've inherited an attrs __setattr__ and don't write our own, + # reset it to object's. + if not self._has_own_setattr and getattr( + cls, "__attrs_own_setattr__", False + ): + cls.__attrs_own_setattr__ = False - setattr(cls, "__setstate__", cache_hash_set_state) + if not self._has_custom_setattr: + cls.__setattr__ = object.__setattr__ return cls @@ -556,11 +705,27 @@ def _create_slots_class(self): if k not in tuple(self._attr_names) + ("__dict__", "__weakref__") } - weakref_inherited = False + # If our class doesn't have its own implementation of __setattr__ + # (either from the user or by us), check the bases, if one of them has + # an attrs-made __setattr__, that needs to be reset. We don't walk the + # MRO because we only care about our immediate base classes. + # XXX: This can be confused by subclassing a slotted attrs class with + # XXX: a non-attrs class and subclass the resulting class with an attrs + # XXX: class. See `test_slotted_confused` for details. For now that's + # XXX: OK with us. + if not self._has_own_setattr: + cd["__attrs_own_setattr__"] = False + + if not self._has_custom_setattr: + for base_cls in self._cls.__bases__: + if base_cls.__dict__.get("__attrs_own_setattr__", False): + cd["__setattr__"] = object.__setattr__ + break # Traverse the MRO to check for an existing __weakref__. + weakref_inherited = False for base_cls in self._cls.__mro__[1:-1]: - if "__weakref__" in getattr(base_cls, "__dict__", ()): + if base_cls.__dict__.get("__weakref__", None) is not None: weakref_inherited = True break @@ -574,7 +739,7 @@ def _create_slots_class(self): names += ("__weakref__",) # We only add the names of attributes that aren't inherited. - # Settings __slots__ to inherited attributes wastes memory. + # Setting __slots__ to inherited attributes wastes memory. slot_names = [name for name in names if name not in base_names] if self._cache_hash: slot_names.append(_hash_cache_field) @@ -584,38 +749,6 @@ def _create_slots_class(self): if qualname is not None: cd["__qualname__"] = qualname - # __weakref__ is not writable. - state_attr_names = tuple( - an for an in self._attr_names if an != "__weakref__" - ) - - def slots_getstate(self): - """ - Automatically created by attrs. - """ - return tuple(getattr(self, name) for name in state_attr_names) - - hash_caching_enabled = self._cache_hash - - def slots_setstate(self, state): - """ - Automatically created by attrs. - """ - __bound_setattr = _obj_setattr.__get__(self, Attribute) - for name, value in zip(state_attr_names, state): - __bound_setattr(name, value) - # Clearing the hash code cache on deserialization is needed - # because hash codes can change from run to run. See issue - # https://github.com/python-attrs/attrs/issues/482 . - # Note that this code only handles setstate for slotted classes. - # For dict classes, see similar code in _patch_original_class . - if hash_caching_enabled: - __bound_setattr(_hash_cache_field, None) - - # slots and frozen require __getstate__/__setstate__ to work - cd["__getstate__"] = slots_getstate - cd["__setstate__"] = slots_setstate - # Create new class based on old class and our methods. cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd) @@ -636,8 +769,13 @@ def slots_setstate(self, state): if not closure_cells: # Catch None or the empty list. continue for cell in closure_cells: - if cell.cell_contents is self._cls: - set_closure_cell(cell, cls) + try: + match = cell.cell_contents is self._cls + except ValueError: # ValueError: Cell is empty + pass + else: + if match: + set_closure_cell(cell, cls) return cls @@ -660,6 +798,40 @@ def __str__(self): self._cls_dict["__str__"] = self._add_method_dunders(__str__) return self + def _make_getstate_setstate(self): + """ + Create custom __setstate__ and __getstate__ methods. + """ + # __weakref__ is not writable. + state_attr_names = tuple( + an for an in self._attr_names if an != "__weakref__" + ) + + def slots_getstate(self): + """ + Automatically created by attrs. + """ + return tuple(getattr(self, name) for name in state_attr_names) + + hash_caching_enabled = self._cache_hash + + def slots_setstate(self, state): + """ + Automatically created by attrs. + """ + __bound_setattr = _obj_setattr.__get__(self, Attribute) + for name, value in zip(state_attr_names, state): + __bound_setattr(name, value) + + # The hash code cache is not included when the object is + # serialized, but it still needs to be initialized to None to + # indicate that the first call to __hash__ should be a cache + # miss. + if hash_caching_enabled: + __bound_setattr(_hash_cache_field, None) + + return slots_getstate, slots_setstate + def make_unhashable(self): self._cls_dict["__hash__"] = None return self @@ -687,6 +859,8 @@ def add_init(self): self._cache_hash, self._base_attr_map, self._is_exc, + self._on_setattr is not None + and self._on_setattr is not setters.NO_OP, ) ) @@ -695,10 +869,10 @@ def add_init(self): def add_eq(self): cd = self._cls_dict - cd["__eq__"], cd["__ne__"] = ( - self._add_method_dunders(meth) - for meth in _make_eq(self._cls, self._attrs) + cd["__eq__"] = self._add_method_dunders( + _make_eq(self._cls, self._attrs) ) + cd["__ne__"] = self._add_method_dunders(_make_ne()) return self @@ -712,6 +886,42 @@ def add_order(self): return self + def add_setattr(self): + if self._frozen: + return self + + sa_attrs = {} + for a in self._attrs: + on_setattr = a.on_setattr or self._on_setattr + if on_setattr and on_setattr is not setters.NO_OP: + sa_attrs[a.name] = a, on_setattr + + if not sa_attrs: + return self + + if self._has_custom_setattr: + # We need to write a __setattr__ but there already is one! + raise ValueError( + "Can't combine custom __setattr__ with on_setattr hooks." + ) + + # docstring comes from _add_method_dunders + def __setattr__(self, name, val): + try: + a, hook = sa_attrs[name] + except KeyError: + nval = val + else: + nval = hook(self, a, val) + + _obj_setattr(self, name, nval) + + self._cls_dict["__attrs_own_setattr__"] = True + self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__) + self._has_own_setattr = True + + return self + def _add_method_dunders(self, method): """ Add __module__ and __qualname__ to a *method* if possible. @@ -728,6 +938,13 @@ def _add_method_dunders(self, method): except AttributeError: pass + try: + method.__doc__ = "Method generated by attrs for class %s." % ( + self._cls.__qualname__, + ) + except AttributeError: + pass + return method @@ -737,10 +954,10 @@ def _add_method_dunders(self, method): ) -def _determine_eq_order(cmp, eq, order): +def _determine_eq_order(cmp, eq, order, default_eq): """ Validate the combination of *cmp*, *eq*, and *order*. Derive the effective - values of eq and order. + values of eq and order. If *eq* is None, set it to *default_eq*. """ if cmp is not None and any((eq is not None, order is not None)): raise ValueError("Don't mix `cmp` with `eq' and `order`.") @@ -751,9 +968,10 @@ def _determine_eq_order(cmp, eq, order): return cmp, cmp - # If left None, equality is on and ordering mirrors equality. + # If left None, equality is set to the specified default and ordering + # mirrors equality. if eq is None: - eq = True + eq = default_eq if order is None: order = eq @@ -764,14 +982,42 @@ def _determine_eq_order(cmp, eq, order): return eq, order +def _determine_whether_to_implement( + cls, flag, auto_detect, dunders, default=True +): + """ + Check whether we should implement a set of methods for *cls*. + + *flag* is the argument passed into @attr.s like 'init', *auto_detect* the + same as passed into @attr.s and *dunders* is a tuple of attribute names + whose presence signal that the user has implemented it themselves. + + Return *default* if no reason for either for or against is found. + + auto_detect must be False on Python 2. + """ + if flag is True or flag is False: + return flag + + if flag is None and auto_detect is False: + return default + + # Logically, flag is None and auto_detect is True here. + for dunder in dunders: + if _has_own_attribute(cls, dunder): + return False + + return default + + def attrs( maybe_cls=None, these=None, repr_ns=None, - repr=True, + repr=None, cmp=None, hash=None, - init=True, + init=None, slots=False, frozen=False, weakref_slot=True, @@ -782,6 +1028,11 @@ def attrs( auto_exc=False, eq=None, order=None, + auto_detect=False, + collect_by_mro=False, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, ): r""" A class decorator that adds `dunder @@ -806,28 +1057,52 @@ def attrs( :param str repr_ns: When using nested classes, there's no way in Python 2 to automatically detect that. Therefore it's possible to set the namespace explicitly for a more meaningful ``repr`` output. + :param bool auto_detect: Instead of setting the *init*, *repr*, *eq*, + *order*, and *hash* arguments explicitly, assume they are set to + ``True`` **unless any** of the involved methods for one of the + arguments is implemented in the *current* class (i.e. it is *not* + inherited from some base class). + + So for example by implementing ``__eq__`` on a class yourself, + ``attrs`` will deduce ``eq=False`` and won't create *neither* + ``__eq__`` *nor* ``__ne__`` (but Python classes come with a sensible + ``__ne__`` by default, so it *should* be enough to only implement + ``__eq__`` in most cases). + + .. warning:: + + If you prevent ``attrs`` from creating the ordering methods for you + (``order=False``, e.g. by implementing ``__le__``), it becomes + *your* responsibility to make sure its ordering is sound. The best + way is to use the `functools.total_ordering` decorator. + + + Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*, + *cmp*, or *hash* overrides whatever *auto_detect* would determine. + + *auto_detect* requires Python 3. Setting it ``True`` on Python 2 raises + a `PythonTooOldError`. + :param bool repr: Create a ``__repr__`` method with a human readable representation of ``attrs`` attributes.. :param bool str: Create a ``__str__`` method that is identical to ``__repr__``. This is usually not necessary except for `Exception`\ s. - :param bool eq: If ``True`` or ``None`` (default), add ``__eq__`` and - ``__ne__`` methods that check two instances for equality. + :param Optional[bool] eq: If ``True`` or ``None`` (default), add ``__eq__`` + and ``__ne__`` methods that check two instances for equality. They compare the instances as if they were tuples of their ``attrs`` - attributes, but only iff the types of both classes are *identical*! - :type eq: `bool` or `None` - :param bool order: If ``True``, add ``__lt__``, ``__le__``, ``__gt__``, - and ``__ge__`` methods that behave like *eq* above and allow instances - to be ordered. If ``None`` (default) mirror value of *eq*. - :type order: `bool` or `None` - :param cmp: Setting to ``True`` is equivalent to setting ``eq=True, - order=True``. Deprecated in favor of *eq* and *order*, has precedence - over them for backward-compatibility though. Must not be mixed with - *eq* or *order*. - :type cmp: `bool` or `None` - :param hash: If ``None`` (default), the ``__hash__`` method is generated - according how *eq* and *frozen* are set. + attributes if and only if the types of both classes are *identical*! + :param Optional[bool] order: If ``True``, add ``__lt__``, ``__le__``, + ``__gt__``, and ``__ge__`` methods that behave like *eq* above and + allow instances to be ordered. If ``None`` (default) mirror value of + *eq*. + :param Optional[bool] cmp: Setting to ``True`` is equivalent to setting + ``eq=True, order=True``. Deprecated in favor of *eq* and *order*, has + precedence over them for backward-compatibility though. Must not be + mixed with *eq* or *order*. + :param Optional[bool] hash: If ``None`` (default), the ``__hash__`` method + is generated according how *eq* and *frozen* are set. 1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you. 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to @@ -845,18 +1120,19 @@ def attrs( `object.__hash__`, and the `GitHub issue that led to the default \ behavior `_ for more details. - :type hash: ``bool`` or ``None`` :param bool init: Create a ``__init__`` method that initializes the ``attrs`` attributes. Leading underscores are stripped for the argument name. If a ``__attrs_post_init__`` method exists on the class, it will be called after the class is fully initialized. :param bool slots: Create a `slotted class ` that's more - memory-efficient. + memory-efficient. Slotted classes are generally superior to the default + dict classes, but have some gotchas you should know about, so we + encourage you to read the `glossary entry `. :param bool frozen: Make instances immutable after initialization. If someone attempts to modify a frozen instance, `attr.exceptions.FrozenInstanceError` is raised. - Please note: + .. note:: 1. This is achieved by installing a custom ``__setattr__`` method on your class, so you can't implement your own. @@ -872,10 +1148,12 @@ def attrs( circumvent that limitation by using ``object.__setattr__(self, "attribute_name", value)``. + 5. Subclasses of a frozen class are frozen too. + :param bool weakref_slot: Make instances weak-referenceable. This has no effect unless ``slots`` is also enabled. - :param bool auto_attribs: If True, collect `PEP 526`_-annotated attributes - (Python 3.6 and later only) from the class body. + :param bool auto_attribs: If ``True``, collect `PEP 526`_-annotated + attributes (Python 3.6 and later only) from the class body. In this case, you **must** annotate every field. If ``attrs`` encounters a field that is set to an `attr.ib` but lacks a type @@ -915,6 +1193,46 @@ def attrs( default value are additionally available as a tuple in the ``args`` attribute, - the value of *str* is ignored leaving ``__str__`` to base classes. + :param bool collect_by_mro: Setting this to `True` fixes the way ``attrs`` + collects attributes from base classes. The default behavior is + incorrect in certain cases of multiple inheritance. It should be on by + default but is kept off for backward-compatability. + + See issue `#428 `_ for + more details. + + :param Optional[bool] getstate_setstate: + .. note:: + This is usually only interesting for slotted classes and you should + probably just set *auto_detect* to `True`. + + If `True`, ``__getstate__`` and + ``__setstate__`` are generated and attached to the class. This is + necessary for slotted classes to be pickleable. If left `None`, it's + `True` by default for slotted classes and ``False`` for dict classes. + + If *auto_detect* is `True`, and *getstate_setstate* is left `None`, + and **either** ``__getstate__`` or ``__setstate__`` is detected directly + on the class (i.e. not inherited), it is set to `False` (this is usually + what you want). + + :param on_setattr: A callable that is run whenever the user attempts to set + an attribute (either by assignment like ``i.x = 42`` or by using + `setattr` like ``setattr(i, "x", 42)``). It receives the same arguments + as validators: the instance, the attribute that is being modified, and + the new value. + + If no exception is raised, the attribute is set to the return value of + the callable. + + If a list of callables is passed, they're automatically wrapped in an + `attr.setters.pipe`. + + :param Optional[callable] field_transformer: + A function that is called with the original class object and all + fields right before ``attrs`` finalizes the class. You can use + this, e.g., to automatically add converters or validators to + fields based on their types. See `transform-fields` for more details. .. versionadded:: 16.0.0 *slots* .. versionadded:: 16.1.0 *frozen* @@ -940,37 +1258,86 @@ def attrs( .. versionadded:: 19.1.0 *auto_exc* .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *auto_detect* + .. versionadded:: 20.1.0 *collect_by_mro* + .. versionadded:: 20.1.0 *getstate_setstate* + .. versionadded:: 20.1.0 *on_setattr* + .. versionadded:: 20.3.0 *field_transformer* """ - eq, order = _determine_eq_order(cmp, eq, order) + if auto_detect and PY2: + raise PythonTooOldError( + "auto_detect only works on Python 3 and later." + ) + + eq_, order_ = _determine_eq_order(cmp, eq, order, None) + hash_ = hash # work around the lack of nonlocal + + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) def wrap(cls): if getattr(cls, "__class__", None) is None: raise TypeError("attrs only works with new-style classes.") + is_frozen = frozen or _has_frozen_base_class(cls) is_exc = auto_exc is True and issubclass(cls, BaseException) + has_own_setattr = auto_detect and _has_own_attribute( + cls, "__setattr__" + ) + + if has_own_setattr and is_frozen: + raise ValueError("Can't freeze a class with a custom __setattr__.") builder = _ClassBuilder( cls, these, slots, - frozen, + is_frozen, weakref_slot, + _determine_whether_to_implement( + cls, + getstate_setstate, + auto_detect, + ("__getstate__", "__setstate__"), + default=slots, + ), auto_attribs, kw_only, cache_hash, is_exc, + collect_by_mro, + on_setattr, + has_own_setattr, + field_transformer, ) - - if repr is True: + if _determine_whether_to_implement( + cls, repr, auto_detect, ("__repr__",) + ): builder.add_repr(repr_ns) if str is True: builder.add_str() - if eq is True and not is_exc: + + eq = _determine_whether_to_implement( + cls, eq_, auto_detect, ("__eq__", "__ne__") + ) + if not is_exc and eq is True: builder.add_eq() - if order is True and not is_exc: + if not is_exc and _determine_whether_to_implement( + cls, order_, auto_detect, ("__lt__", "__le__", "__gt__", "__ge__") + ): builder.add_order() + builder.add_setattr() + + if ( + hash_ is None + and auto_detect is True + and _has_own_attribute(cls, "__hash__") + ): + hash = False + else: + hash = hash_ if hash is not True and hash is not False and hash is not None: # Can't use `hash in` because 1 == True for example. raise TypeError( @@ -985,7 +1352,9 @@ def wrap(cls): " hashing must be either explicitly or implicitly " "enabled." ) - elif hash is True or (hash is None and eq is True and frozen is True): + elif hash is True or ( + hash is None and eq is True and is_frozen is True + ): # Build a __hash__ if told so, or if it's safe. builder.add_hash() else: @@ -998,7 +1367,9 @@ def wrap(cls): ) builder.make_unhashable() - if init is True: + if _determine_whether_to_implement( + cls, init, auto_detect, ("__init__",) + ): builder.add_init() else: if cache_hash: @@ -1095,7 +1466,23 @@ def _make_hash(cls, attrs, frozen, cache_hash): unique_filename = _generate_unique_filename(cls, "hash") type_hash = hash(unique_filename) - method_lines = ["def __hash__(self):"] + hash_def = "def __hash__(self" + hash_func = "hash((" + closing_braces = "))" + if not cache_hash: + hash_def += "):" + else: + if not PY2: + hash_def += ", *" + + hash_def += ( + ", _cache_wrapper=" + + "__import__('attr._make')._make._CacheHashWrapper):" + ) + hash_func = "_cache_wrapper(" + hash_func + closing_braces += ")" + + method_lines = [hash_def] def append_hash_computation_lines(prefix, indent): """ @@ -1103,14 +1490,18 @@ def append_hash_computation_lines(prefix, indent): Below this will either be returned directly or used to compute a value which is then cached, depending on the value of cache_hash """ + method_lines.extend( - [indent + prefix + "hash((", indent + " %d," % (type_hash,)] + [ + indent + prefix + hash_func, + indent + " %d," % (type_hash,), + ] ) for a in attrs: method_lines.append(indent + " self.%s," % a.name) - method_lines.append(indent + " ))") + method_lines.append(indent + " " + closing_braces) if cache_hash: method_lines.append(tab + "if self.%s is None:" % _hash_cache_field) @@ -1153,19 +1544,29 @@ def _add_hash(cls, attrs): return cls -def __ne__(self, other): +def _make_ne(): """ - Check equality and either forward a NotImplemented or return the result - negated. + Create __ne__ method. """ - result = self.__eq__(other) - if result is NotImplemented: - return NotImplemented - return not result + def __ne__(self, other): + """ + Check equality and either forward a NotImplemented or + return the result negated. + """ + result = self.__eq__(other) + if result is NotImplemented: + return NotImplemented + + return not result + + return __ne__ def _make_eq(cls, attrs): + """ + Create __eq__ method for *cls* with *attrs*. + """ attrs = [a for a in attrs if a.eq] unique_filename = _generate_unique_filename(cls, "eq") @@ -1201,10 +1602,13 @@ def _make_eq(cls, attrs): script.splitlines(True), unique_filename, ) - return locs["__eq__"], __ne__ + return locs["__eq__"] def _make_order(cls, attrs): + """ + Create ordering methods for *cls* with *attrs*. + """ attrs = [a for a in attrs if a.order] def attrs_to_tuple(obj): @@ -1259,7 +1663,8 @@ def _add_eq(cls, attrs=None): if attrs is None: attrs = cls.__attrs_attrs__ - cls.__eq__, cls.__ne__ = _make_eq(cls, attrs) + cls.__eq__ = _make_eq(cls, attrs) + cls.__ne__ = _make_ne() return cls @@ -1337,43 +1742,6 @@ def _add_repr(cls, ns=None, attrs=None): return cls -def _make_init( - cls, attrs, post_init, frozen, slots, cache_hash, base_attr_map, is_exc -): - attrs = [a for a in attrs if a.init or a.default is not NOTHING] - - unique_filename = _generate_unique_filename(cls, "init") - - script, globs, annotations = _attrs_to_init_script( - attrs, frozen, slots, post_init, cache_hash, base_attr_map, is_exc - ) - locs = {} - bytecode = compile(script, unique_filename, "exec") - attr_dict = dict((a.name, a) for a in attrs) - globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) - - if frozen is True: - # Save the lookup overhead in __init__ if we need to circumvent - # immutability. - globs["_cached_setattr"] = _obj_setattr - - eval(bytecode, globs, locs) - - # In order of debuggers like PDB being able to step through the code, - # we add a fake linecache entry. - linecache.cache[unique_filename] = ( - len(script), - None, - script.splitlines(True), - unique_filename, - ) - - __init__ = locs["__init__"] - __init__.__annotations__ = annotations - - return __init__ - - def fields(cls): """ Return the tuple of ``attrs`` attributes for a class. @@ -1458,8 +1826,191 @@ def _is_slot_attr(a_name, base_attr_map): return a_name in base_attr_map and _is_slot_cls(base_attr_map[a_name]) +def _make_init( + cls, + attrs, + post_init, + frozen, + slots, + cache_hash, + base_attr_map, + is_exc, + has_global_on_setattr, +): + if frozen and has_global_on_setattr: + raise ValueError("Frozen classes can't use on_setattr.") + + needs_cached_setattr = cache_hash or frozen + filtered_attrs = [] + attr_dict = {} + for a in attrs: + if not a.init and a.default is NOTHING: + continue + + filtered_attrs.append(a) + attr_dict[a.name] = a + + if a.on_setattr is not None: + if frozen is True: + raise ValueError("Frozen classes can't use on_setattr.") + + needs_cached_setattr = True + elif ( + has_global_on_setattr and a.on_setattr is not setters.NO_OP + ) or _is_slot_attr(a.name, base_attr_map): + needs_cached_setattr = True + + unique_filename = _generate_unique_filename(cls, "init") + + script, globs, annotations = _attrs_to_init_script( + filtered_attrs, + frozen, + slots, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_global_on_setattr, + ) + locs = {} + bytecode = compile(script, unique_filename, "exec") + globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) + + if needs_cached_setattr: + # Save the lookup overhead in __init__ if we need to circumvent + # setattr hooks. + globs["_cached_setattr"] = _obj_setattr + + eval(bytecode, globs, locs) + + # In order of debuggers like PDB being able to step through the code, + # we add a fake linecache entry. + linecache.cache[unique_filename] = ( + len(script), + None, + script.splitlines(True), + unique_filename, + ) + + __init__ = locs["__init__"] + __init__.__annotations__ = annotations + + return __init__ + + +def _setattr(attr_name, value_var, has_on_setattr): + """ + Use the cached object.setattr to set *attr_name* to *value_var*. + """ + return "_setattr('%s', %s)" % (attr_name, value_var) + + +def _setattr_with_converter(attr_name, value_var, has_on_setattr): + """ + Use the cached object.setattr to set *attr_name* to *value_var*, but run + its converter first. + """ + return "_setattr('%s', %s(%s))" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + +def _assign(attr_name, value, has_on_setattr): + """ + Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise + relegate to _setattr. + """ + if has_on_setattr: + return _setattr(attr_name, value, True) + + return "self.%s = %s" % (attr_name, value) + + +def _assign_with_converter(attr_name, value_var, has_on_setattr): + """ + Unless *attr_name* has an on_setattr hook, use normal assignment after + conversion. Otherwise relegate to _setattr_with_converter. + """ + if has_on_setattr: + return _setattr_with_converter(attr_name, value_var, True) + + return "self.%s = %s(%s)" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + +if PY2: + + def _unpack_kw_only_py2(attr_name, default=None): + """ + Unpack *attr_name* from _kw_only dict. + """ + if default is not None: + arg_default = ", %s" % default + else: + arg_default = "" + return "%s = _kw_only.pop('%s'%s)" % ( + attr_name, + attr_name, + arg_default, + ) + + def _unpack_kw_only_lines_py2(kw_only_args): + """ + Unpack all *kw_only_args* from _kw_only dict and handle errors. + + Given a list of strings "{attr_name}" and "{attr_name}={default}" + generates list of lines of code that pop attrs from _kw_only dict and + raise TypeError similar to builtin if required attr is missing or + extra key is passed. + + >>> print("\n".join(_unpack_kw_only_lines_py2(["a", "b=42"]))) + try: + a = _kw_only.pop('a') + b = _kw_only.pop('b', 42) + except KeyError as _key_error: + raise TypeError( + ... + if _kw_only: + raise TypeError( + ... + """ + lines = ["try:"] + lines.extend( + " " + _unpack_kw_only_py2(*arg.split("=")) + for arg in kw_only_args + ) + lines += """\ +except KeyError as _key_error: + raise TypeError( + '__init__() missing required keyword-only argument: %s' % _key_error + ) +if _kw_only: + raise TypeError( + '__init__() got an unexpected keyword argument %r' + % next(iter(_kw_only)) + ) +""".split( + "\n" + ) + return lines + + def _attrs_to_init_script( - attrs, frozen, slots, post_init, cache_hash, base_attr_map, is_exc + attrs, + frozen, + slots, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_global_on_setattr, ): """ Return a script of an initializer for *attrs* and a dict of globals. @@ -1470,85 +2021,49 @@ def _attrs_to_init_script( a cached ``object.__setattr__``. """ lines = [] - any_slot_ancestors = any( - _is_slot_attr(a.name, base_attr_map) for a in attrs - ) + if needs_cached_setattr: + lines.append( + # Circumvent the __setattr__ descriptor to save one lookup per + # assignment. + # Note _setattr will be used again below if cache_hash is True + "_setattr = _cached_setattr.__get__(self, self.__class__)" + ) + if frozen is True: if slots is True: - lines.append( - # Circumvent the __setattr__ descriptor to save one lookup per - # assignment. - # Note _setattr will be used again below if cache_hash is True - "_setattr = _cached_setattr.__get__(self, self.__class__)" - ) - - def fmt_setter(attr_name, value_var): - return "_setattr('%(attr_name)s', %(value_var)s)" % { - "attr_name": attr_name, - "value_var": value_var, - } - - def fmt_setter_with_converter(attr_name, value_var): - conv_name = _init_converter_pat.format(attr_name) - return "_setattr('%(attr_name)s', %(conv)s(%(value_var)s))" % { - "attr_name": attr_name, - "value_var": value_var, - "conv": conv_name, - } - + fmt_setter = _setattr + fmt_setter_with_converter = _setattr_with_converter else: # Dict frozen classes assign directly to __dict__. # But only if the attribute doesn't come from an ancestor slot # class. # Note _inst_dict will be used again below if cache_hash is True lines.append("_inst_dict = self.__dict__") - if any_slot_ancestors: - lines.append( - # Circumvent the __setattr__ descriptor to save one lookup - # per assignment. - "_setattr = _cached_setattr.__get__(self, self.__class__)" - ) - def fmt_setter(attr_name, value_var): - if _is_slot_attr(attr_name, base_attr_map): - res = "_setattr('%(attr_name)s', %(value_var)s)" % { - "attr_name": attr_name, - "value_var": value_var, - } - else: - res = "_inst_dict['%(attr_name)s'] = %(value_var)s" % { - "attr_name": attr_name, - "value_var": value_var, - } - return res - - def fmt_setter_with_converter(attr_name, value_var): - conv_name = _init_converter_pat.format(attr_name) + def fmt_setter(attr_name, value_var, has_on_setattr): if _is_slot_attr(attr_name, base_attr_map): - tmpl = "_setattr('%(attr_name)s', %(c)s(%(value_var)s))" - else: - tmpl = "_inst_dict['%(attr_name)s'] = %(c)s(%(value_var)s)" - return tmpl % { - "attr_name": attr_name, - "value_var": value_var, - "c": conv_name, - } + return _setattr(attr_name, value_var, has_on_setattr) + + return "_inst_dict['%s'] = %s" % (attr_name, value_var) + + def fmt_setter_with_converter( + attr_name, value_var, has_on_setattr + ): + if has_on_setattr or _is_slot_attr(attr_name, base_attr_map): + return _setattr_with_converter( + attr_name, value_var, has_on_setattr + ) + + return "_inst_dict['%s'] = %s(%s)" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) else: # Not frozen. - def fmt_setter(attr_name, value): - return "self.%(attr_name)s = %(value)s" % { - "attr_name": attr_name, - "value": value, - } - - def fmt_setter_with_converter(attr_name, value_var): - conv_name = _init_converter_pat.format(attr_name) - return "self.%(attr_name)s = %(conv)s(%(value_var)s)" % { - "attr_name": attr_name, - "value_var": value_var, - "conv": conv_name, - } + fmt_setter = _assign + fmt_setter_with_converter = _assign_with_converter args = [] kw_only_args = [] @@ -1562,13 +2077,19 @@ def fmt_setter_with_converter(attr_name, value_var): for a in attrs: if a.validator: attrs_to_validate.append(a) + attr_name = a.name + has_on_setattr = a.on_setattr is not None or ( + a.on_setattr is not setters.NO_OP and has_global_on_setattr + ) arg_name = a.name.lstrip("_") + has_factory = isinstance(a.default, Factory) if has_factory and a.default.takes_self: maybe_self = "self" else: maybe_self = "" + if a.init is False: if has_factory: init_factory_name = _init_factory_pat.format(a.name) @@ -1576,16 +2097,18 @@ def fmt_setter_with_converter(attr_name, value_var): lines.append( fmt_setter_with_converter( attr_name, - init_factory_name + "({0})".format(maybe_self), + init_factory_name + "(%s)" % (maybe_self,), + has_on_setattr, ) ) - conv_name = _init_converter_pat.format(a.name) + conv_name = _init_converter_pat % (a.name,) names_for_globals[conv_name] = a.converter else: lines.append( fmt_setter( attr_name, - init_factory_name + "({0})".format(maybe_self), + init_factory_name + "(%s)" % (maybe_self,), + has_on_setattr, ) ) names_for_globals[init_factory_name] = a.default.factory @@ -1594,70 +2117,78 @@ def fmt_setter_with_converter(attr_name, value_var): lines.append( fmt_setter_with_converter( attr_name, - "attr_dict['{attr_name}'].default".format( - attr_name=attr_name - ), + "attr_dict['%s'].default" % (attr_name,), + has_on_setattr, ) ) - conv_name = _init_converter_pat.format(a.name) + conv_name = _init_converter_pat % (a.name,) names_for_globals[conv_name] = a.converter else: lines.append( fmt_setter( attr_name, - "attr_dict['{attr_name}'].default".format( - attr_name=attr_name - ), + "attr_dict['%s'].default" % (attr_name,), + has_on_setattr, ) ) elif a.default is not NOTHING and not has_factory: - arg = "{arg_name}=attr_dict['{attr_name}'].default".format( - arg_name=arg_name, attr_name=attr_name - ) + arg = "%s=attr_dict['%s'].default" % (arg_name, attr_name) if a.kw_only: kw_only_args.append(arg) else: args.append(arg) + if a.converter is not None: - lines.append(fmt_setter_with_converter(attr_name, arg_name)) + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) names_for_globals[ - _init_converter_pat.format(a.name) + _init_converter_pat % (a.name,) ] = a.converter else: - lines.append(fmt_setter(attr_name, arg_name)) + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + elif has_factory: - arg = "{arg_name}=NOTHING".format(arg_name=arg_name) + arg = "%s=NOTHING" % (arg_name,) if a.kw_only: kw_only_args.append(arg) else: args.append(arg) - lines.append( - "if {arg_name} is not NOTHING:".format(arg_name=arg_name) - ) + lines.append("if %s is not NOTHING:" % (arg_name,)) + init_factory_name = _init_factory_pat.format(a.name) if a.converter is not None: lines.append( - " " + fmt_setter_with_converter(attr_name, arg_name) + " " + + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) ) lines.append("else:") lines.append( " " + fmt_setter_with_converter( attr_name, - init_factory_name + "({0})".format(maybe_self), + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, ) ) names_for_globals[ - _init_converter_pat.format(a.name) + _init_converter_pat % (a.name,) ] = a.converter else: - lines.append(" " + fmt_setter(attr_name, arg_name)) + lines.append( + " " + fmt_setter(attr_name, arg_name, has_on_setattr) + ) lines.append("else:") lines.append( " " + fmt_setter( attr_name, - init_factory_name + "({0})".format(maybe_self), + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, ) ) names_for_globals[init_factory_name] = a.default.factory @@ -1666,13 +2197,18 @@ def fmt_setter_with_converter(attr_name, value_var): kw_only_args.append(arg_name) else: args.append(arg_name) + if a.converter is not None: - lines.append(fmt_setter_with_converter(attr_name, arg_name)) + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) names_for_globals[ - _init_converter_pat.format(a.name) + _init_converter_pat % (a.name,) ] = a.converter else: - lines.append(fmt_setter(attr_name, arg_name)) + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) if a.init is True and a.converter is None and a.type is not None: annotations[arg_name] = a.type @@ -1681,13 +2217,14 @@ def fmt_setter_with_converter(attr_name, value_var): names_for_globals["_config"] = _config lines.append("if _config._run_validators is True:") for a in attrs_to_validate: - val_name = "__attr_validator_{}".format(a.name) - attr_name = "__attr_{}".format(a.name) + val_name = "__attr_validator_" + a.name + attr_name = "__attr_" + a.name lines.append( - " {}(self, {}, self.{})".format(val_name, attr_name, a.name) + " %s(self, %s, self.%s)" % (val_name, attr_name, a.name) ) names_for_globals[val_name] = a.validator names_for_globals[attr_name] = a + if post_init: lines.append("self.__attrs_post_init__()") @@ -1718,14 +2255,14 @@ def fmt_setter_with_converter(attr_name, value_var): args = ", ".join(args) if kw_only_args: if PY2: - raise PythonTooOldError( - "Keyword-only arguments only work on Python 3 and later." - ) + lines = _unpack_kw_only_lines_py2(kw_only_args) + lines - args += "{leading_comma}*, {kw_only_args}".format( - leading_comma=", " if args else "", - kw_only_args=", ".join(kw_only_args), - ) + args += "%s**_kw_only" % (", " if args else "",) # leading comma + else: + args += "%s*, %s" % ( + ", " if args else "", # leading comma + ", ".join(kw_only_args), # kw_only args + ) return ( """\ def __init__(self, {args}): @@ -1742,12 +2279,26 @@ class Attribute(object): """ *Read-only* representation of an attribute. + Instances of this class are frequently used for introspection purposes + like: + + - `fields` returns a tuple of them. + - Validators get them passed as the first argument. + - The *field transformer* hook receives a list of them. + :attribute name: The name of the attribute. + :attribute inherited: Whether or not that attribute has been inherited from + a base class. Plus *all* arguments of `attr.ib` (except for ``factory`` which is only syntactic sugar for ``default=Factory(...)``. - For the version history of the fields, see `attr.ib`. + .. versionadded:: 20.1.0 *inherited* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.2.0 *inherited* is not taken into account for + equality checks and hashing anymore. + + For the full version history of the fields, see `attr.ib`. """ __slots__ = ( @@ -1763,6 +2314,8 @@ class Attribute(object): "type", "converter", "kw_only", + "inherited", + "on_setattr", ) def __init__( @@ -1774,14 +2327,16 @@ def __init__( cmp, # XXX: unused, remove along with other cmp code. hash, init, + inherited, metadata=None, type=None, converter=None, kw_only=False, eq=None, order=None, + on_setattr=None, ): - eq, order = _determine_eq_order(cmp, eq, order) + eq, order = _determine_eq_order(cmp, eq, order, True) # Cache this descriptor here to speed things up later. bound_setattr = _obj_setattr.__get__(self, Attribute) @@ -1807,6 +2362,8 @@ def __init__( ) bound_setattr("type", type) bound_setattr("kw_only", kw_only) + bound_setattr("inherited", inherited) + bound_setattr("on_setattr", on_setattr) def __setattr__(self, name, value): raise FrozenInstanceError() @@ -1829,6 +2386,7 @@ def from_counting_attr(cls, name, ca, type=None): "validator", "default", "type", + "inherited", ) # exclude methods and deprecated alias } return cls( @@ -1837,6 +2395,7 @@ def from_counting_attr(cls, name, ca, type=None): default=ca._default, type=type, cmp=None, + inherited=False, **inst_dict ) @@ -1849,10 +2408,17 @@ def cmp(self): return self.eq and self.order - # Don't use attr.assoc since fields(Attribute) doesn't work - def _assoc(self, **changes): + # Don't use attr.evolve since fields(Attribute) doesn't work + def evolve(self, **changes): """ Copy *self* and apply *changes*. + + This works similarly to `attr.evolve` but that function does not work + with ``Attribute``. + + It is mainly meant to be used for `transform-fields`. + + .. versionadded:: 20.3.0 """ new = copy.copy(self) @@ -1901,13 +2467,17 @@ def _setattrs(self, name_values_pairs): order=False, hash=(name != "metadata"), init=True, + inherited=False, ) for name in Attribute.__slots__ ] Attribute = _add_hash( - _add_eq(_add_repr(Attribute, attrs=_a), attrs=_a), - attrs=[a for a in _a if a.hash], + _add_eq( + _add_repr(Attribute, attrs=_a), + attrs=[a for a in _a if a.name != "inherited"], + ), + attrs=[a for a in _a if a.hash and a.name != "inherited"], ) @@ -1933,6 +2503,7 @@ class _CountingAttr(object): "converter", "type", "kw_only", + "on_setattr", ) __attrs_attrs__ = tuple( Attribute( @@ -1946,6 +2517,8 @@ class _CountingAttr(object): kw_only=False, eq=True, order=False, + inherited=False, + on_setattr=None, ) for name in ( "counter", @@ -1955,6 +2528,7 @@ class _CountingAttr(object): "order", "hash", "init", + "on_setattr", ) ) + ( Attribute( @@ -1968,6 +2542,8 @@ class _CountingAttr(object): kw_only=False, eq=True, order=False, + inherited=False, + on_setattr=None, ), ) cls_counter = 0 @@ -1986,24 +2562,22 @@ def __init__( kw_only, eq, order, + on_setattr, ): _CountingAttr.cls_counter += 1 self.counter = _CountingAttr.cls_counter self._default = default - # If validator is a list/tuple, wrap it using helper validator. - if validator and isinstance(validator, (list, tuple)): - self._validator = and_(*validator) - else: - self._validator = validator + self._validator = validator + self.converter = converter self.repr = repr self.eq = eq self.order = order self.hash = hash self.init = init - self.converter = converter self.metadata = metadata self.type = type self.kw_only = kw_only + self.on_setattr = on_setattr def validator(self, meth): """ @@ -2072,8 +2646,7 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments): """ A quick way to create a new class called *name* with *attrs*. - :param name: The name for the new class. - :type name: str + :param str name: The name for the new class. :param attrs: A list of names or a dictionary of mappings of names to attributes. @@ -2120,17 +2693,21 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments): # We do it here for proper warnings with meaningful stacklevel. cmp = attributes_arguments.pop("cmp", None) - attributes_arguments["eq"], attributes_arguments[ - "order" - ] = _determine_eq_order( - cmp, attributes_arguments.get("eq"), attributes_arguments.get("order") + ( + attributes_arguments["eq"], + attributes_arguments["order"], + ) = _determine_eq_order( + cmp, + attributes_arguments.get("eq"), + attributes_arguments.get("order"), + True, ) return _attrs(these=cls_dict, **attributes_arguments)(type_) # These are required by within this module so we define them here and merely -# import into .validators. +# import into .validators / .converters. @attrs(slots=True, hash=True) @@ -2152,8 +2729,7 @@ def and_(*validators): When called on a value, it runs all wrapped validators. - :param validators: Arbitrary number of validators. - :type validators: callables + :param callables validators: Arbitrary number of validators. .. versionadded:: 17.1.0 """ @@ -2166,3 +2742,24 @@ def and_(*validators): ) return _AndValidator(tuple(vals)) + + +def pipe(*converters): + """ + A converter that composes multiple converters into one. + + When called on a value, it runs all wrapped converters, returning the + *last* value. + + :param callables converters: Arbitrary number of converters. + + .. versionadded:: 20.1.0 + """ + + def pipe_converter(val): + for converter in converters: + val = converter(val) + + return val + + return pipe_converter diff --git a/poetry/core/_vendor/attr/_next_gen.py b/poetry/core/_vendor/attr/_next_gen.py new file mode 100644 index 000000000..2b5565c56 --- /dev/null +++ b/poetry/core/_vendor/attr/_next_gen.py @@ -0,0 +1,160 @@ +""" +This is a Python 3.6 and later-only, keyword-only, and **provisional** API that +calls `attr.s` with different default values. + +Provisional APIs that shall become "import attrs" one glorious day. +""" + +from functools import partial + +from attr.exceptions import UnannotatedAttributeError + +from . import setters +from ._make import NOTHING, _frozen_setattrs, attrib, attrs + + +def define( + maybe_cls=None, + *, + these=None, + repr=None, + hash=None, + init=None, + slots=True, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=None, + kw_only=False, + cache_hash=False, + auto_exc=True, + eq=None, + order=False, + auto_detect=True, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, +): + r""" + The only behavioral differences are the handling of the *auto_attribs* + option: + + :param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves + exactly like `attr.s`. If left `None`, `attr.s` will try to guess: + + 1. If all attributes are annotated and no `attr.ib` is found, it assumes + *auto_attribs=True*. + 2. Otherwise it assumes *auto_attribs=False* and tries to collect + `attr.ib`\ s. + + and that mutable classes (``frozen=False``) validate on ``__setattr__``. + + .. versionadded:: 20.1.0 + """ + + def do_it(cls, auto_attribs): + return attrs( + maybe_cls=cls, + these=these, + repr=repr, + hash=hash, + init=init, + slots=slots, + frozen=frozen, + weakref_slot=weakref_slot, + str=str, + auto_attribs=auto_attribs, + kw_only=kw_only, + cache_hash=cache_hash, + auto_exc=auto_exc, + eq=eq, + order=order, + auto_detect=auto_detect, + collect_by_mro=True, + getstate_setstate=getstate_setstate, + on_setattr=on_setattr, + field_transformer=field_transformer, + ) + + def wrap(cls): + """ + Making this a wrapper ensures this code runs during class creation. + + We also ensure that frozen-ness of classes is inherited. + """ + nonlocal frozen, on_setattr + + had_on_setattr = on_setattr not in (None, setters.NO_OP) + + # By default, mutable classes validate on setattr. + if frozen is False and on_setattr is None: + on_setattr = setters.validate + + # However, if we subclass a frozen class, we inherit the immutability + # and disable on_setattr. + for base_cls in cls.__bases__: + if base_cls.__setattr__ is _frozen_setattrs: + if had_on_setattr: + raise ValueError( + "Frozen classes can't use on_setattr " + "(frozen-ness was inherited)." + ) + + on_setattr = setters.NO_OP + break + + if auto_attribs is not None: + return do_it(cls, auto_attribs) + + try: + return do_it(cls, True) + except UnannotatedAttributeError: + return do_it(cls, False) + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but ``None`` if used as `@attrs()`. + if maybe_cls is None: + return wrap + else: + return wrap(maybe_cls) + + +mutable = define +frozen = partial(define, frozen=True, on_setattr=None) + + +def field( + *, + default=NOTHING, + validator=None, + repr=True, + hash=None, + init=True, + metadata=None, + converter=None, + factory=None, + kw_only=False, + eq=None, + order=None, + on_setattr=None, +): + """ + Identical to `attr.ib`, except keyword-only and with some arguments + removed. + + .. versionadded:: 20.1.0 + """ + return attrib( + default=default, + validator=validator, + repr=repr, + hash=hash, + init=init, + metadata=metadata, + converter=converter, + factory=factory, + kw_only=kw_only, + eq=eq, + order=order, + on_setattr=on_setattr, + ) diff --git a/poetry/core/_vendor/attr/converters.py b/poetry/core/_vendor/attr/converters.py index 859289784..715ce1785 100644 --- a/poetry/core/_vendor/attr/converters.py +++ b/poetry/core/_vendor/attr/converters.py @@ -4,7 +4,14 @@ from __future__ import absolute_import, division, print_function -from ._make import NOTHING, Factory +from ._make import NOTHING, Factory, pipe + + +__all__ = [ + "pipe", + "optional", + "default_if_none", +] def optional(converter): diff --git a/poetry/core/_vendor/attr/exceptions.py b/poetry/core/_vendor/attr/exceptions.py index d1b76185c..fcd89106f 100644 --- a/poetry/core/_vendor/attr/exceptions.py +++ b/poetry/core/_vendor/attr/exceptions.py @@ -1,20 +1,37 @@ from __future__ import absolute_import, division, print_function -class FrozenInstanceError(AttributeError): +class FrozenError(AttributeError): """ - A frozen/immutable instance has been attempted to be modified. + A frozen/immutable instance or attribute haave been attempted to be + modified. It mirrors the behavior of ``namedtuples`` by using the same error message and subclassing `AttributeError`. - .. versionadded:: 16.1.0 + .. versionadded:: 20.1.0 """ msg = "can't set attribute" args = [msg] +class FrozenInstanceError(FrozenError): + """ + A frozen instance has been attempted to be modified. + + .. versionadded:: 16.1.0 + """ + + +class FrozenAttributeError(FrozenError): + """ + A frozen attribute has been attempted to be modified. + + .. versionadded:: 20.1.0 + """ + + class AttrsAttributeNotFoundError(ValueError): """ An ``attrs`` function couldn't find an attribute that the user asked for. @@ -51,7 +68,8 @@ class UnannotatedAttributeError(RuntimeError): class PythonTooOldError(RuntimeError): """ - An ``attrs`` feature requiring a more recent python version has been used. + It was attempted to use an ``attrs`` feature that requires a newer Python + version. .. versionadded:: 18.2.0 """ diff --git a/poetry/core/_vendor/attr/setters.py b/poetry/core/_vendor/attr/setters.py new file mode 100644 index 000000000..240014b3c --- /dev/null +++ b/poetry/core/_vendor/attr/setters.py @@ -0,0 +1,77 @@ +""" +Commonly used hooks for on_setattr. +""" + +from __future__ import absolute_import, division, print_function + +from . import _config +from .exceptions import FrozenAttributeError + + +def pipe(*setters): + """ + Run all *setters* and return the return value of the last one. + + .. versionadded:: 20.1.0 + """ + + def wrapped_pipe(instance, attrib, new_value): + rv = new_value + + for setter in setters: + rv = setter(instance, attrib, rv) + + return rv + + return wrapped_pipe + + +def frozen(_, __, ___): + """ + Prevent an attribute to be modified. + + .. versionadded:: 20.1.0 + """ + raise FrozenAttributeError() + + +def validate(instance, attrib, new_value): + """ + Run *attrib*'s validator on *new_value* if it has one. + + .. versionadded:: 20.1.0 + """ + if _config._run_validators is False: + return new_value + + v = attrib.validator + if not v: + return new_value + + v(instance, attrib, new_value) + + return new_value + + +def convert(instance, attrib, new_value): + """ + Run *attrib*'s converter -- if it has one -- on *new_value* and return the + result. + + .. versionadded:: 20.1.0 + """ + c = attrib.converter + if c: + return c(new_value) + + return new_value + + +NO_OP = object() +""" +Sentinel for disabling class-wide *on_setattr* hooks for certain attributes. + +Does not work in `pipe` or within lists. + +.. versionadded:: 20.1.0 +""" diff --git a/poetry/core/_vendor/attr/validators.py b/poetry/core/_vendor/attr/validators.py index 839d310c3..b9a73054e 100644 --- a/poetry/core/_vendor/attr/validators.py +++ b/poetry/core/_vendor/attr/validators.py @@ -67,7 +67,7 @@ def instance_of(type): return _InstanceOfValidator(type) -@attrs(repr=False, frozen=True) +@attrs(repr=False, frozen=True, slots=True) class _MatchesReValidator(object): regex = attrib() flags = attrib() @@ -171,7 +171,8 @@ def provides(interface): performed using ``interface.providedBy(value)`` (see `zope.interface `_). - :param zope.interface.Interface interface: The interface to check for. + :param interface: The interface to check for. + :type interface: ``zope.interface.Interface`` :raises TypeError: With a human readable error message, the attribute (of type `attr.Attribute`), the expected interface, and the diff --git a/poetry/core/_vendor/packaging/__about__.py b/poetry/core/_vendor/packaging/__about__.py index 4d998578d..2d39193b0 100644 --- a/poetry/core/_vendor/packaging/__about__.py +++ b/poetry/core/_vendor/packaging/__about__.py @@ -18,10 +18,10 @@ __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" -__version__ = "20.4" +__version__ = "20.8" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" __license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = "Copyright 2014-2019 %s" % __author__ +__copyright__ = "2014-2019 %s" % __author__ diff --git a/poetry/core/_vendor/packaging/requirements.py b/poetry/core/_vendor/packaging/requirements.py index 91f81ede0..5ba8daf28 100644 --- a/poetry/core/_vendor/packaging/requirements.py +++ b/poetry/core/_vendor/packaging/requirements.py @@ -5,18 +5,24 @@ import string import re +import sys from pyparsing import stringStart, stringEnd, originalTextFor, ParseException from pyparsing import ZeroOrMore, Word, Optional, Regex, Combine from pyparsing import Literal as L # noqa -from six.moves.urllib import parse as urlparse from ._typing import TYPE_CHECKING from .markers import MARKER_EXPR, Marker from .specifiers import LegacySpecifier, Specifier, SpecifierSet +if sys.version_info[0] >= 3: + from urllib import parse as urlparse # pragma: no cover +else: # pragma: no cover + import urlparse + + if TYPE_CHECKING: # pragma: no cover - from typing import List + from typing import List, Optional as TOptional, Set class InvalidRequirement(ValueError): @@ -103,7 +109,7 @@ def __init__(self, requirement_string): ) ) - self.name = req.name + self.name = req.name # type: str if req.url: parsed_url = urlparse.urlparse(req.url) if parsed_url.scheme == "file": @@ -113,12 +119,12 @@ def __init__(self, requirement_string): not parsed_url.scheme and not parsed_url.netloc ): raise InvalidRequirement("Invalid URL: {0}".format(req.url)) - self.url = req.url + self.url = req.url # type: TOptional[str] else: self.url = None - self.extras = set(req.extras.asList() if req.extras else []) - self.specifier = SpecifierSet(req.specifier) - self.marker = req.marker if req.marker else None + self.extras = set(req.extras.asList() if req.extras else []) # type: Set[str] + self.specifier = SpecifierSet(req.specifier) # type: SpecifierSet + self.marker = req.marker if req.marker else None # type: TOptional[Marker] def __str__(self): # type: () -> str diff --git a/poetry/core/_vendor/packaging/specifiers.py b/poetry/core/_vendor/packaging/specifiers.py index fe09bb1db..a42cbfef3 100644 --- a/poetry/core/_vendor/packaging/specifiers.py +++ b/poetry/core/_vendor/packaging/specifiers.py @@ -7,6 +7,7 @@ import functools import itertools import re +import warnings from ._compat import string_types, with_metaclass from ._typing import TYPE_CHECKING @@ -14,17 +15,7 @@ from .version import Version, LegacyVersion, parse if TYPE_CHECKING: # pragma: no cover - from typing import ( - List, - Dict, - Union, - Iterable, - Iterator, - Optional, - Callable, - Tuple, - FrozenSet, - ) + from typing import List, Dict, Union, Iterable, Iterator, Optional, Callable, Tuple ParsedVersion = Union[Version, LegacyVersion] UnparsedVersion = Union[Version, LegacyVersion, str] @@ -285,6 +276,16 @@ class LegacySpecifier(_IndividualSpecifier): ">": "greater_than", } + def __init__(self, spec="", prereleases=None): + # type: (str, Optional[bool]) -> None + super(LegacySpecifier, self).__init__(spec, prereleases) + + warnings.warn( + "Creating a LegacyVersion has been deprecated and will be " + "removed in the next major release", + DeprecationWarning, + ) + def _coerce_version(self, version): # type: (Union[ParsedVersion, str]) -> LegacyVersion if not isinstance(version, LegacyVersion): @@ -317,7 +318,7 @@ def _compare_greater_than(self, prospective, spec): def _require_version_compare( - fn # type: (Callable[[Specifier, ParsedVersion, str], bool]) + fn, # type: (Callable[[Specifier, ParsedVersion, str], bool]) ): # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool] @functools.wraps(fn) @@ -750,7 +751,7 @@ def __len__(self): return len(self._specs) def __iter__(self): - # type: () -> Iterator[FrozenSet[_IndividualSpecifier]] + # type: () -> Iterator[_IndividualSpecifier] return iter(self._specs) @property diff --git a/poetry/core/_vendor/packaging/tags.py b/poetry/core/_vendor/packaging/tags.py index 9064910b8..13798e38b 100644 --- a/poetry/core/_vendor/packaging/tags.py +++ b/poetry/core/_vendor/packaging/tags.py @@ -13,6 +13,7 @@ EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] del imp +import collections import logging import os import platform @@ -57,6 +58,24 @@ _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 +_LEGACY_MANYLINUX_MAP = { + # CentOS 7 w/ glibc 2.17 (PEP 599) + (2, 17): "manylinux2014", + # CentOS 6 w/ glibc 2.12 (PEP 571) + (2, 12): "manylinux2010", + # CentOS 5 w/ glibc 2.5 (PEP 513) + (2, 5): "manylinux1", +} + +# If glibc ever changes its major version, we need to know what the last +# minor version was, so we can build the complete list of all versions. +# For now, guess what the highest minor version might be, assume it will +# be 50 for testing. Once this actually happens, update the dictionary +# with the actual value. +_LAST_GLIBC_MINOR = collections.defaultdict(lambda: 50) # type: Dict[int, int] +glibcVersion = collections.namedtuple("Version", ["major", "minor"]) + + class Tag(object): """ A representation of the tag triple for a wheel. @@ -65,13 +84,19 @@ class Tag(object): is also supported. """ - __slots__ = ["_interpreter", "_abi", "_platform"] + __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] def __init__(self, interpreter, abi, platform): # type: (str, str, str) -> None self._interpreter = interpreter.lower() self._abi = abi.lower() self._platform = platform.lower() + # The __hash__ of every single element in a Set[Tag] will be evaluated each time + # that a set calls its `.disjoint()` method, which may be called hundreds of + # times when scanning a page of links for packages with tags matching that + # Set[Tag]. Pre-computing the value here produces significant speedups for + # downstream consumers. + self._hash = hash((self._interpreter, self._abi, self._platform)) @property def interpreter(self): @@ -101,7 +126,7 @@ def __eq__(self, other): def __hash__(self): # type: () -> int - return hash((self._interpreter, self._abi, self._platform)) + return self._hash def __str__(self): # type: () -> str @@ -382,7 +407,12 @@ def _mac_binary_formats(version, cpu_arch): return [] formats.extend(["fat32", "fat"]) - formats.append("universal") + if cpu_arch in {"arm64", "x86_64"}: + formats.append("universal2") + + if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: + formats.append("universal") + return formats @@ -405,30 +435,73 @@ def mac_platforms(version=None, arch=None): arch = _mac_arch(cpu_arch) else: arch = arch - for minor_version in range(version[1], -1, -1): - compat_version = version[0], minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) + if (10, 0) <= version and version < (11, 0): + # Prior to Mac OS 11, each yearly release of Mac OS bumped the + # "minor" version number. The major version was always 10. + for minor_version in range(version[1], -1, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=10, minor=minor_version, binary_format=binary_format + ) + + if version >= (11, 0): + # Starting with Mac OS 11, each yearly release bumps the major version + # number. The minor versions are now the midyear updates. + for major_version in range(version[0], 10, -1): + compat_version = major_version, 0 + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=major_version, minor=0, binary_format=binary_format + ) -# From PEP 513. -def _is_manylinux_compatible(name, glibc_version): - # type: (str, GlibcVersion) -> bool + if version >= (11, 0) and arch == "x86_64": + # Mac OS 11 on x86_64 is compatible with binaries from previous releases. + # Arm64 support was introduced in 11.0, so no Arm binaries from previous + # releases exist. + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + + +# From PEP 513, PEP 600 +def _is_manylinux_compatible(name, arch, glibc_version): + # type: (str, str, GlibcVersion) -> bool + sys_glibc = _get_glibc_version() + if sys_glibc < glibc_version: + return False # Check for presence of _manylinux module. try: import _manylinux # noqa - - return bool(getattr(_manylinux, name + "_compatible")) - except (ImportError, AttributeError): - # Fall through to heuristic check below. + except ImportError: pass - - return _have_compatible_glibc(*glibc_version) + else: + if hasattr(_manylinux, "manylinux_compatible"): + result = _manylinux.manylinux_compatible( + glibc_version[0], glibc_version[1], arch + ) + if result is not None: + return bool(result) + else: + if glibc_version == (2, 5): + if hasattr(_manylinux, "manylinux1_compatible"): + return bool(_manylinux.manylinux1_compatible) + if glibc_version == (2, 12): + if hasattr(_manylinux, "manylinux2010_compatible"): + return bool(_manylinux.manylinux2010_compatible) + if glibc_version == (2, 17): + if hasattr(_manylinux, "manylinux2014_compatible"): + return bool(_manylinux.manylinux2014_compatible) + return True def _glibc_version_string(): @@ -474,8 +547,20 @@ def _glibc_version_string_ctypes(): # main program". This way we can let the linker do the work to figure out # which libc our process is actually using. # - # Note: typeshed is wrong here so we are ignoring this line. - process_namespace = ctypes.CDLL(None) # type: ignore + # We must also handle the special case where the executable is not a + # dynamically linked executable. This can occur when using musl libc, + # for example. In this situation, dlopen() will error, leading to an + # OSError. Interestingly, at least in the case of musl, there is no + # errno set on the OSError. The single string argument used to construct + # OSError comes from libc itself and is therefore not portable to + # hard code here. In any case, failure to call dlopen() means we + # can proceed, so we bail on our attempt. + try: + # Note: typeshed is wrong here so we are ignoring this line. + process_namespace = ctypes.CDLL(None) # type: ignore + except OSError: + return None + try: gnu_get_libc_version = process_namespace.gnu_get_libc_version except AttributeError: @@ -493,10 +578,9 @@ def _glibc_version_string_ctypes(): return version_str -# Separated out from have_compatible_glibc for easier unit testing. -def _check_glibc_version(version_str, required_major, minimum_minor): - # type: (str, int, int) -> bool - # Parse string and check against requested version. +def _parse_glibc_version(version_str): + # type: (str) -> Tuple[int, int] + # Parse glibc version. # # We use a regexp instead of str.split because we want to discard any # random junk that might come after the minor version -- this might happen @@ -509,19 +593,23 @@ def _check_glibc_version(version_str, required_major, minimum_minor): " got: %s" % version_str, RuntimeWarning, ) - return False - return ( - int(m.group("major")) == required_major - and int(m.group("minor")) >= minimum_minor - ) + return -1, -1 + return (int(m.group("major")), int(m.group("minor"))) + +_glibc_version = [] # type: List[Tuple[int, int]] -def _have_compatible_glibc(required_major, minimum_minor): - # type: (int, int) -> bool + +def _get_glibc_version(): + # type: () -> Tuple[int, int] + if _glibc_version: + return _glibc_version[0] version_str = _glibc_version_string() if version_str is None: - return False - return _check_glibc_version(version_str, required_major, minimum_minor) + _glibc_version.append((-1, -1)) + else: + _glibc_version.append(_parse_glibc_version(version_str)) + return _glibc_version[0] # Python does not provide platform information at sufficient granularity to @@ -639,7 +727,42 @@ def _have_compatible_manylinux_abi(arch): return _is_linux_armhf() if arch == "i686": return _is_linux_i686() - return True + return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"} + + +def _manylinux_tags(linux, arch): + # type: (str, str) -> Iterator[str] + # Oldest glibc to be supported regardless of architecture is (2, 17). + too_old_glibc2 = glibcVersion(2, 16) + if arch in {"x86_64", "i686"}: + # On x86/i686 also oldest glibc to be supported is (2, 5). + too_old_glibc2 = glibcVersion(2, 4) + current_glibc = glibcVersion(*_get_glibc_version()) + glibc_max_list = [current_glibc] + # We can assume compatibility across glibc major versions. + # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 + # + # Build a list of maximum glibc versions so that we can + # output the canonical list of all glibc from current_glibc + # down to too_old_glibc2, including all intermediary versions. + for glibc_major in range(current_glibc.major - 1, 1, -1): + glibc_max_list.append(glibcVersion(glibc_major, _LAST_GLIBC_MINOR[glibc_major])) + for glibc_max in glibc_max_list: + if glibc_max.major == too_old_glibc2.major: + min_minor = too_old_glibc2.minor + else: + # For other glibc major versions oldest supported is (x, 0). + min_minor = -1 + for glibc_minor in range(glibc_max.minor, min_minor, -1): + glibc_version = (glibc_max.major, glibc_minor) + tag = "manylinux_{}_{}".format(*glibc_version) + if _is_manylinux_compatible(tag, arch, glibc_version): + yield linux.replace("linux", tag) + # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. + if glibc_version in _LEGACY_MANYLINUX_MAP: + legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] + if _is_manylinux_compatible(legacy_tag, arch, glibc_version): + yield linux.replace("linux", legacy_tag) def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): @@ -650,28 +773,10 @@ def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): linux = "linux_i686" elif linux == "linux_aarch64": linux = "linux_armv7l" - manylinux_support = [] _, arch = linux.split("_", 1) if _have_compatible_manylinux_abi(arch): - if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}: - manylinux_support.append( - ("manylinux2014", (2, 17)) - ) # CentOS 7 w/ glibc 2.17 (PEP 599) - if arch in {"x86_64", "i686"}: - manylinux_support.append( - ("manylinux2010", (2, 12)) - ) # CentOS 6 w/ glibc 2.12 (PEP 571) - manylinux_support.append( - ("manylinux1", (2, 5)) - ) # CentOS 5 w/ glibc 2.5 (PEP 513) - manylinux_support_iter = iter(manylinux_support) - for name, glibc_version in manylinux_support_iter: - if _is_manylinux_compatible(name, glibc_version): - yield linux.replace("linux", name) - break - # Support for a later manylinux implies support for an earlier version. - for name, _ in manylinux_support_iter: - yield linux.replace("linux", name) + for tag in _manylinux_tags(linux, arch): + yield tag yield linux @@ -722,11 +827,7 @@ def interpreter_version(**kwargs): def _version_nodot(version): # type: (PythonVersion) -> str - if any(v >= 10 for v in version): - sep = "_" - else: - sep = "" - return sep.join(map(str, version)) + return "".join(map(str, version)) def sys_tags(**kwargs): diff --git a/poetry/core/_vendor/packaging/utils.py b/poetry/core/_vendor/packaging/utils.py index 19579c1a0..92c7b00b7 100644 --- a/poetry/core/_vendor/packaging/utils.py +++ b/poetry/core/_vendor/packaging/utils.py @@ -12,6 +12,8 @@ from typing import NewType, Union NormalizedName = NewType("NormalizedName", str) +else: + NormalizedName = str _canonicalize_regex = re.compile(r"[-_.]+") @@ -23,18 +25,18 @@ def canonicalize_name(name): return cast("NormalizedName", value) -def canonicalize_version(_version): - # type: (str) -> Union[Version, str] +def canonicalize_version(version): + # type: (Union[Version, str]) -> Union[Version, str] """ This is very similar to Version.__str__, but has one subtle difference with the way it handles the release segment. """ - - try: - version = Version(_version) - except InvalidVersion: - # Legacy versions cannot be normalized - return _version + if not isinstance(version, Version): + try: + version = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version parts = [] diff --git a/poetry/core/_vendor/packaging/version.py b/poetry/core/_vendor/packaging/version.py index 00371e86a..517d91f24 100644 --- a/poetry/core/_vendor/packaging/version.py +++ b/poetry/core/_vendor/packaging/version.py @@ -6,6 +6,7 @@ import collections import itertools import re +import warnings from ._structures import Infinity, NegativeInfinity from ._typing import TYPE_CHECKING @@ -71,36 +72,50 @@ def __hash__(self): # type: () -> int return hash(self._key) + # Please keep the duplicated `isinstance` check + # in the six comparisons hereunder + # unless you find a way to avoid adding overhead function calls. def __lt__(self, other): # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s < o) + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key < other._key def __le__(self, other): # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s <= o) + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key <= other._key def __eq__(self, other): # type: (object) -> bool - return self._compare(other, lambda s, o: s == o) + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key == other._key def __ge__(self, other): # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s >= o) + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key >= other._key def __gt__(self, other): # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s > o) + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key > other._key def __ne__(self, other): # type: (object) -> bool - return self._compare(other, lambda s, o: s != o) - - def _compare(self, other, method): - # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented] if not isinstance(other, _BaseVersion): return NotImplemented - return method(self._key, other._key) + return self._key != other._key class LegacyVersion(_BaseVersion): @@ -109,6 +124,12 @@ def __init__(self, version): self._version = str(version) self._key = _legacy_cmpkey(self._version) + warnings.warn( + "Creating a LegacyVersion has been deprecated and will be " + "removed in the next major release", + DeprecationWarning, + ) + def __str__(self): # type: () -> str return self._version diff --git a/poetry/core/_vendor/pyrsistent/LICENCE.mit b/poetry/core/_vendor/pyrsistent/LICENSE.mit similarity index 100% rename from poetry/core/_vendor/pyrsistent/LICENCE.mit rename to poetry/core/_vendor/pyrsistent/LICENSE.mit diff --git a/poetry/core/_vendor/vendor.txt b/poetry/core/_vendor/vendor.txt index a1c24ba96..6667b3d32 100644 --- a/poetry/core/_vendor/vendor.txt +++ b/poetry/core/_vendor/vendor.txt @@ -1,8 +1,9 @@ -attrs==19.3.0 +attrs==20.3.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" jsonschema==3.2.0 lark-parser==0.9.0 -packaging==20.4 -pyparsing==2.4.7 -pyrsistent==0.16.0 -six==1.15.0 -tomlkit==0.7.0 +packaging==20.8; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") +pyparsing==2.4.7; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" +pyrsistent==0.16.1; python_version >= "2.7" +six==1.15.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "2.7" +tomlkit==0.7.0; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") +typing-extensions==3.7.4.3; python_version >= "3.6" and python_version < "3.8" diff --git a/pyproject.toml b/pyproject.toml index 628cf484b..9948932ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,7 @@ functools32 = {version = "^3.2.3-2", python = "~2.7"} [tool.poetry.dev-dependencies] pre-commit = "^1.10" +pyrsistent = "^0.16.0" pytest = "^4.6" pytest-cov = "^2.8" pytest-mock = "^2.0" @@ -113,7 +114,7 @@ appdirs = [] [tool.vendoring.license.fallback-urls] -pyrsistent = "https://raw.githubusercontent.com/tobgu/pyrsistent/master/LICENCE.mit" +pyrsistent = "https://raw.githubusercontent.com/tobgu/pyrsistent/master/LICENSE.mit" [build-system] requires = [] diff --git a/vendors/poetry.lock b/vendors/poetry.lock index 3e6414c96..b6f1d79ad 100644 --- a/vendors/poetry.lock +++ b/vendors/poetry.lock @@ -1,32 +1,33 @@ [[package]] name = "attrs" -version = "19.3.0" +version = "20.3.0" description = "Classes Without Boilerplate" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] -dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] -docs = ["sphinx", "zope.interface"] -tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] [[package]] name = "importlib-metadata" -version = "1.7.0" +version = "3.4.0" description = "Read metadata from Python packages" category = "main" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.extras] -docs = ["sphinx", "rst.linker"] -testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] +python-versions = ">=3.6" [package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] + [[package]] name = "jsonschema" version = "3.2.0" @@ -35,15 +36,15 @@ category = "main" optional = false python-versions = "*" -[package.extras] -format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] -format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] - [package.dependencies] attrs = ">=17.4.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} pyrsistent = ">=0.14.0" six = ">=1.11.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] +format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] [[package]] name = "lark-parser" @@ -58,7 +59,7 @@ regex = ["regex"] [[package]] name = "packaging" -version = "20.4" +version = "20.8" description = "Core utilities for Python packages" category = "main" optional = false @@ -66,7 +67,6 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] pyparsing = ">=2.0.2" -six = "*" [[package]] name = "pyparsing" @@ -78,11 +78,11 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pyrsistent" -version = "0.16.0" +version = "0.16.1" description = "Persistent/Functional/Immutable data structures" category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7" [package.dependencies] six = "*" @@ -103,9 +103,17 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "typing-extensions" +version = "3.7.4.3" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "zipp" -version = "3.1.0" +version = "3.4.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false @@ -113,21 +121,21 @@ python-versions = ">=3.6" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["jaraco.itertools", "func-timeout"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "cf00c694f857a9caf5610127561a5cd3d571db9420f4e4ebb6c787cefa32af31" +content-hash = "f9770372f7c711e4c2941e30c28b26a58a8c76aae88ddc1e3ea8e175fcc6a534" [metadata.files] attrs = [ - {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, - {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] importlib-metadata = [ - {file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"}, - {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, + {file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"}, + {file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"}, ] jsonschema = [ {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, @@ -137,15 +145,15 @@ lark-parser = [ {file = "lark-parser-0.9.0.tar.gz", hash = "sha256:9e7589365d6b6de1cca40b0eaec31104a3fb96a37a11a9dfd5098e95b50aa6cd"}, ] packaging = [ - {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, - {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, + {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, + {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pyrsistent = [ - {file = "pyrsistent-0.16.0.tar.gz", hash = "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3"}, + {file = "pyrsistent-0.16.1.tar.gz", hash = "sha256:aa2ae1c2e496f4d6777f869ea5de7166a8ccb9c2e06ebcf6c7ff1b670c98c5ef"}, ] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, @@ -155,7 +163,12 @@ tomlkit = [ {file = "tomlkit-0.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"}, {file = "tomlkit-0.7.0.tar.gz", hash = "sha256:ac57f29693fab3e309ea789252fcce3061e19110085aa31af5446ca749325618"}, ] +typing-extensions = [ + {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, + {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, + {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, +] zipp = [ - {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, - {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, + {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, + {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, ] diff --git a/vendors/pyproject.toml b/vendors/pyproject.toml index 69ccbd0f0..ff8020cd5 100644 --- a/vendors/pyproject.toml +++ b/vendors/pyproject.toml @@ -23,5 +23,6 @@ python = "^3.6" jsonschema = "^3.2.0" lark-parser = "^0.9.0" -packaging = "^20.1" +packaging = "^20.8" +pyrsistent = "^0.16.0" tomlkit = ">=0.7.0,<1.0.0" From 38ba90048c992aaeba6ec58ee00a313c64024bf6 Mon Sep 17 00:00:00 2001 From: Giovanni Barillari Date: Mon, 18 Jan 2021 19:13:49 +0100 Subject: [PATCH 15/77] Update GH test workflow --- .github/workflows/tests.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9bff305de..e80838a5e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: os: [Ubuntu, MacOS, Windows] - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9.0-rc.1, pypy2, pypy3] + python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, pypy2, pypy3] exclude: - os: Ubuntu python-version: pypy2 @@ -48,6 +48,11 @@ jobs: shell: bash run: poetry config virtualenvs.in-project true + - name: Configure poetry installer + if: matrix.python-version == '2.7' + shell: bash + run: poetry config experimental.new-installer false + - name: Set up cache uses: actions/cache@v1 id: cache From 01602122f9b2a4cc927d94c543efb5020dfa3817 Mon Sep 17 00:00:00 2001 From: Shay Nehmad <70973922+ShayNehmad-RecoLabs@users.noreply.github.com> Date: Mon, 25 Jan 2021 17:30:38 +0200 Subject: [PATCH 16/77] Add & to author name regex, resolve #3485 (#120) Useful for R&D departments :) https://github.com/python-poetry/poetry/issues/3485 --- poetry/core/packages/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry/core/packages/package.py b/poetry/core/packages/package.py index aac39afa7..c7c19669c 100644 --- a/poetry/core/packages/package.py +++ b/poetry/core/packages/package.py @@ -17,7 +17,7 @@ from .utils.utils import create_nested_marker -AUTHOR_REGEX = re.compile(r"(?u)^(?P[- .,\w\d'’\"()]+)(?: <(?P.+?)>)?$") +AUTHOR_REGEX = re.compile(r"(?u)^(?P[- .,\w\d'’\"()&]+)(?: <(?P.+?)>)?$") class Package(PackageSpecification): From bbd545e9177eb59a256d3f68d795c672bda49c3c Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Sun, 31 Jan 2021 16:08:56 +0100 Subject: [PATCH 17/77] Ensure dependency string is PEP 508 compliant (#103) --- poetry/core/packages/dependency.py | 8 +------- tests/packages/test_dependency.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py index 5432e8bb3..d678947e2 100644 --- a/poetry/core/packages/dependency.py +++ b/poetry/core/packages/dependency.py @@ -411,13 +411,7 @@ def __hash__(self): # type: () -> int def __str__(self): # type: () -> str if self.is_root: return self._pretty_name - - name = self._pretty_name - - if self._features: - name = "{}[{}]".format(name, ",".join(sorted(self._features))) - - return "{} ({})".format(name, self._pretty_constraint) + return self.base_pep_508_name def __repr__(self): # type: () -> str return "<{} {}>".format(self.__class__.__name__, str(self)) diff --git a/tests/packages/test_dependency.py b/tests/packages/test_dependency.py index 8591e72f5..116c50880 100644 --- a/tests/packages/test_dependency.py +++ b/tests/packages/test_dependency.py @@ -205,3 +205,22 @@ def test_complete_name(): "foo[bar,baz]" == Dependency("foo", ">=1.2.3", extras=["baz", "bar"]).complete_name ) + + +@pytest.mark.parametrize( + "name,constraint,extras,expected", + [ + ("A", ">2.7,<3.0", None, "A (>2.7,<3.0)"), + ("A", ">2.7,<3.0", ["x"], "A[x] (>2.7,<3.0)"), + ("A", ">=1.6.5,<1.8.0 || >1.8.0,<3.1.0", None, "A (>=1.6.5,!=1.8.0,<3.1.0)"), + ( + "A", + ">=1.6.5,<1.8.0 || >1.8.0,<3.1.0", + ["x"], + "A[x] (>=1.6.5,!=1.8.0,<3.1.0)", + ), + ], +) +def test_dependency_string_representation(name, constraint, extras, expected): + dependency = Dependency(name=name, constraint=constraint, extras=extras) + assert str(dependency) == expected From c0d7e52f49b12092ba6829ecb02db7701cbff019 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Tue, 2 Feb 2021 11:13:50 +0100 Subject: [PATCH 18/77] fix (core.version.helpers): add python 3.9.* to list of PYTHON_VERSION (#127) --- poetry/core/version/helpers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/poetry/core/version/helpers.py b/poetry/core/version/helpers.py index 6ca22228e..bd46e8d2d 100644 --- a/poetry/core/version/helpers.py +++ b/poetry/core/version/helpers.py @@ -20,6 +20,7 @@ "3.6.*", "3.7.*", "3.8.*", + "3.9.*", ] From cb2ce46468db13b346b478809ada96995f0f003b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Wed, 3 Feb 2021 20:15:01 +0100 Subject: [PATCH 19/77] Fix constraint upper bound including prereleases (#128) Fixes python-poetry/poetry#2271 --- poetry/core/semver/__init__.py | 29 +++++------------------------ poetry/core/semver/version_range.py | 19 ++++++++++--------- tests/semver/test_version_range.py | 26 ++++++++++++++++++++------ 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/poetry/core/semver/__init__.py b/poetry/core/semver/__init__.py index 27d4a08af..2cff22d6e 100644 --- a/poetry/core/semver/__init__.py +++ b/poetry/core/semver/__init__.py @@ -65,9 +65,7 @@ def parse_single_constraint(constraint): # type: (str) -> VersionTypes if len(m.group(1).split(".")) == 1: high = version.stable.next_major - return VersionRange( - version, high, include_min=True, always_include_max_prerelease=True - ) + return VersionRange(version, high, include_min=True) # PEP 440 Tilde range (~=) m = TILDE_PEP440_CONSTRAINT.match(constraint) @@ -86,21 +84,14 @@ def parse_single_constraint(constraint): # type: (str) -> VersionTypes else: high = version.stable.next_minor - return VersionRange( - version, high, include_min=True, always_include_max_prerelease=True - ) + return VersionRange(version, high, include_min=True) # Caret range m = CARET_CONSTRAINT.match(constraint) if m: version = Version.parse(m.group(1)) - return VersionRange( - version, - version.next_breaking, - include_min=True, - always_include_max_prerelease=True, - ) + return VersionRange(version, version.next_breaking, include_min=True) # X Range m = X_CONSTRAINT.match(constraint) @@ -112,24 +103,14 @@ def parse_single_constraint(constraint): # type: (str) -> VersionTypes if minor is not None: version = Version(major, int(minor), 0) - result = VersionRange( - version, - version.next_minor, - include_min=True, - always_include_max_prerelease=True, - ) + result = VersionRange(version, version.next_minor, include_min=True) else: if major == 0: result = VersionRange(max=Version(1, 0, 0)) else: version = Version(major, 0, 0) - result = VersionRange( - version, - version.next_major, - include_min=True, - always_include_max_prerelease=True, - ) + result = VersionRange(version, version.next_major, include_min=True) if op == "!=": result = VersionRange().difference(result) diff --git a/poetry/core/semver/version_range.py b/poetry/core/semver/version_range.py index ae9be3ecf..26f42f5d0 100644 --- a/poetry/core/semver/version_range.py +++ b/poetry/core/semver/version_range.py @@ -25,8 +25,9 @@ def __init__( ): full_max = max if ( - always_include_max_prerelease + not always_include_max_prerelease and not include_max + and full_max is not None and not full_max.is_prerelease() and not full_max.build and ( @@ -77,11 +78,11 @@ def allows(self, other): # type: ("Version") -> bool if not self._include_min and other == self._min: return False - if self._max is not None: - if other > self._max: + if self.full_max is not None: + if other > self.full_max: return False - if not self._include_max and other == self._max: + if not self._include_max and other == self.full_max: return False return True @@ -335,22 +336,22 @@ def allows_lower(self, other): # type: (VersionRange) -> bool return self.include_min and not other.include_min def allows_higher(self, other): # type: (VersionRange) -> bool - if self.max is None: + if self.full_max is None: return other.max is not None - if other.max is None: + if other.full_max is None: return False - if self.max < other.max: + if self.full_max < other.full_max: return False - if self.max > other.max: + if self.full_max > other.full_max: return True return self.include_max and not other.include_max def is_strictly_lower(self, other): # type: (VersionRange) -> bool - if self.max is None or other.min is None: + if self.full_max is None or other.min is None: return False if self.full_max < other.min: diff --git a/tests/semver/test_version_range.py b/tests/semver/test_version_range.py index 662063938..3667a465f 100644 --- a/tests/semver/test_version_range.py +++ b/tests/semver/test_version_range.py @@ -70,6 +70,11 @@ def v300(): return Version.parse("3.0.0") +@pytest.fixture() +def v300b1(): + return Version.parse("3.0.0b1") + + def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250, v300): assert VersionRange(v123, v250).allows_all(EmptyConstraint()) @@ -173,12 +178,7 @@ def test_allows_any( # pre-release min does not allow lesser than itself range = VersionRange(Version.parse("1.9b1"), include_min=True) assert not range.allows_any( - VersionRange( - Version.parse("1.8.0"), - Version.parse("1.9.0"), - include_min=True, - always_include_max_prerelease=True, - ) + VersionRange(Version.parse("1.8.0"), Version.parse("1.9.0"), include_min=True) ) @@ -258,3 +258,17 @@ def test_union( assert result == VersionRange(v003, v200) result = VersionRange(v003, v114).union(VersionRange(v114, v200, include_min=True)) assert result == VersionRange(v003, v200) + + +def test_include_max_prerelease(v200, v300, v300b1): + result = VersionRange(v200, v300) + + assert not result.allows(v300b1) + assert not result.allows_any(VersionRange(v300b1)) + assert not result.allows_all(VersionRange(v200, v300b1)) + + result = VersionRange(v200, v300, always_include_max_prerelease=True) + + assert result.allows(v300b1) + assert result.allows_any(VersionRange(v300b1)) + assert result.allows_all(VersionRange(v200, v300b1)) From d7b168229ee27a1f08336261b07afa5f7df39a47 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 3 Feb 2021 16:02:44 -0500 Subject: [PATCH 20/77] chore: packaging 20.9, Universal2 wheels on Apple Silicon (#129) --- poetry/core/_vendor/packaging/__about__.py | 2 +- poetry/core/_vendor/packaging/markers.py | 16 +++- poetry/core/_vendor/packaging/requirements.py | 17 ++++- poetry/core/_vendor/packaging/specifiers.py | 4 +- poetry/core/_vendor/packaging/tags.py | 26 +++++-- poetry/core/_vendor/packaging/utils.py | 75 ++++++++++++++++++- poetry/core/_vendor/vendor.txt | 2 +- vendors/poetry.lock | 8 +- vendors/pyproject.toml | 2 +- 9 files changed, 127 insertions(+), 25 deletions(-) diff --git a/poetry/core/_vendor/packaging/__about__.py b/poetry/core/_vendor/packaging/__about__.py index 2d39193b0..4c43a968c 100644 --- a/poetry/core/_vendor/packaging/__about__.py +++ b/poetry/core/_vendor/packaging/__about__.py @@ -18,7 +18,7 @@ __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" -__version__ = "20.8" +__version__ = "20.9" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" diff --git a/poetry/core/_vendor/packaging/markers.py b/poetry/core/_vendor/packaging/markers.py index 87cd3f958..e0330ab6a 100644 --- a/poetry/core/_vendor/packaging/markers.py +++ b/poetry/core/_vendor/packaging/markers.py @@ -8,13 +8,21 @@ import platform import sys -from pyparsing import ParseException, ParseResults, stringStart, stringEnd -from pyparsing import ZeroOrMore, Group, Forward, QuotedString -from pyparsing import Literal as L # noqa +from pyparsing import ( # noqa: N817 + Forward, + Group, + Literal as L, + ParseException, + ParseResults, + QuotedString, + ZeroOrMore, + stringEnd, + stringStart, +) from ._compat import string_types from ._typing import TYPE_CHECKING -from .specifiers import Specifier, InvalidSpecifier +from .specifiers import InvalidSpecifier, Specifier if TYPE_CHECKING: # pragma: no cover from typing import Any, Callable, Dict, List, Optional, Tuple, Union diff --git a/poetry/core/_vendor/packaging/requirements.py b/poetry/core/_vendor/packaging/requirements.py index 5ba8daf28..aa69d50d1 100644 --- a/poetry/core/_vendor/packaging/requirements.py +++ b/poetry/core/_vendor/packaging/requirements.py @@ -3,13 +3,22 @@ # for complete details. from __future__ import absolute_import, division, print_function -import string import re +import string import sys -from pyparsing import stringStart, stringEnd, originalTextFor, ParseException -from pyparsing import ZeroOrMore, Word, Optional, Regex, Combine -from pyparsing import Literal as L # noqa +from pyparsing import ( # noqa: N817 + Combine, + Literal as L, + Optional, + ParseException, + Regex, + Word, + ZeroOrMore, + originalTextFor, + stringEnd, + stringStart, +) from ._typing import TYPE_CHECKING from .markers import MARKER_EXPR, Marker diff --git a/poetry/core/_vendor/packaging/specifiers.py b/poetry/core/_vendor/packaging/specifiers.py index a42cbfef3..a6a83c1fe 100644 --- a/poetry/core/_vendor/packaging/specifiers.py +++ b/poetry/core/_vendor/packaging/specifiers.py @@ -12,10 +12,10 @@ from ._compat import string_types, with_metaclass from ._typing import TYPE_CHECKING from .utils import canonicalize_version -from .version import Version, LegacyVersion, parse +from .version import LegacyVersion, Version, parse if TYPE_CHECKING: # pragma: no cover - from typing import List, Dict, Union, Iterable, Iterator, Optional, Callable, Tuple + from typing import Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union ParsedVersion = Union[Version, LegacyVersion] UnparsedVersion = Union[Version, LegacyVersion, str] diff --git a/poetry/core/_vendor/packaging/tags.py b/poetry/core/_vendor/packaging/tags.py index 13798e38b..d637f1b69 100644 --- a/poetry/core/_vendor/packaging/tags.py +++ b/poetry/core/_vendor/packaging/tags.py @@ -27,9 +27,9 @@ if TYPE_CHECKING: # pragma: no cover from typing import ( + IO, Dict, FrozenSet, - IO, Iterable, Iterator, List, @@ -458,14 +458,28 @@ def mac_platforms(version=None, arch=None): major=major_version, minor=0, binary_format=binary_format ) - if version >= (11, 0) and arch == "x86_64": + if version >= (11, 0): # Mac OS 11 on x86_64 is compatible with binaries from previous releases. # Arm64 support was introduced in 11.0, so no Arm binaries from previous # releases exist. - for minor_version in range(16, 3, -1): - compat_version = 10, minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: + # + # However, the "universal2" binary format can have a + # macOS version earlier than 11.0 when the x86_64 part of the binary supports + # that version of macOS. + if arch == "x86_64": + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + else: + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_format = "universal2" yield "macosx_{major}_{minor}_{binary_format}".format( major=compat_version[0], minor=compat_version[1], diff --git a/poetry/core/_vendor/packaging/utils.py b/poetry/core/_vendor/packaging/utils.py index 92c7b00b7..6e8c2a3e5 100644 --- a/poetry/core/_vendor/packaging/utils.py +++ b/poetry/core/_vendor/packaging/utils.py @@ -6,23 +6,41 @@ import re from ._typing import TYPE_CHECKING, cast +from .tags import Tag, parse_tag from .version import InvalidVersion, Version if TYPE_CHECKING: # pragma: no cover - from typing import NewType, Union + from typing import FrozenSet, NewType, Tuple, Union + BuildTag = Union[Tuple[()], Tuple[int, str]] NormalizedName = NewType("NormalizedName", str) else: + BuildTag = tuple NormalizedName = str + +class InvalidWheelFilename(ValueError): + """ + An invalid wheel filename was found, users should refer to PEP 427. + """ + + +class InvalidSdistFilename(ValueError): + """ + An invalid sdist filename was found, users should refer to the packaging user guide. + """ + + _canonicalize_regex = re.compile(r"[-_.]+") +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)") def canonicalize_name(name): # type: (str) -> NormalizedName # This is taken from PEP 503. value = _canonicalize_regex.sub("-", name).lower() - return cast("NormalizedName", value) + return cast(NormalizedName, value) def canonicalize_version(version): @@ -65,3 +83,56 @@ def canonicalize_version(version): parts.append("+{0}".format(version.local)) return "".join(parts) + + +def parse_wheel_filename(filename): + # type: (str) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]] + if not filename.endswith(".whl"): + raise InvalidWheelFilename( + "Invalid wheel filename (extension must be '.whl'): {0}".format(filename) + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + "Invalid wheel filename (wrong number of parts): {0}".format(filename) + ) + + parts = filename.split("-", dashes - 2) + name_part = parts[0] + # See PEP 427 for the rules on escaping the project name + if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: + raise InvalidWheelFilename("Invalid project name: {0}".format(filename)) + name = canonicalize_name(name_part) + version = Version(parts[1]) + if dashes == 5: + build_part = parts[2] + build_match = _build_tag_regex.match(build_part) + if build_match is None: + raise InvalidWheelFilename( + "Invalid build number: {0} in '{1}'".format(build_part, filename) + ) + build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) + else: + build = () + tags = parse_tag(parts[-1]) + return (name, version, build, tags) + + +def parse_sdist_filename(filename): + # type: (str) -> Tuple[NormalizedName, Version] + if not filename.endswith(".tar.gz"): + raise InvalidSdistFilename( + "Invalid sdist filename (extension must be '.tar.gz'): {0}".format(filename) + ) + + # We are requiring a PEP 440 version, which cannot contain dashes, + # so we split on the last dash. + name_part, sep, version_part = filename[:-7].rpartition("-") + if not sep: + raise InvalidSdistFilename("Invalid sdist filename: {0}".format(filename)) + + name = canonicalize_name(name_part) + version = Version(version_part) + return (name, version) diff --git a/poetry/core/_vendor/vendor.txt b/poetry/core/_vendor/vendor.txt index 6667b3d32..13de1ee1b 100644 --- a/poetry/core/_vendor/vendor.txt +++ b/poetry/core/_vendor/vendor.txt @@ -1,7 +1,7 @@ attrs==20.3.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" jsonschema==3.2.0 lark-parser==0.9.0 -packaging==20.8; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") +packaging==20.9; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") pyparsing==2.4.7; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" pyrsistent==0.16.1; python_version >= "2.7" six==1.15.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "2.7" diff --git a/vendors/poetry.lock b/vendors/poetry.lock index b6f1d79ad..ea3e4b636 100644 --- a/vendors/poetry.lock +++ b/vendors/poetry.lock @@ -59,7 +59,7 @@ regex = ["regex"] [[package]] name = "packaging" -version = "20.8" +version = "20.9" description = "Core utilities for Python packages" category = "main" optional = false @@ -126,7 +126,7 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "f9770372f7c711e4c2941e30c28b26a58a8c76aae88ddc1e3ea8e175fcc6a534" +content-hash = "6790cea1370c2296b96f05af2218de159499268429c0c81757de8e4e90bfa9b0" [metadata.files] attrs = [ @@ -145,8 +145,8 @@ lark-parser = [ {file = "lark-parser-0.9.0.tar.gz", hash = "sha256:9e7589365d6b6de1cca40b0eaec31104a3fb96a37a11a9dfd5098e95b50aa6cd"}, ] packaging = [ - {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, - {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, diff --git a/vendors/pyproject.toml b/vendors/pyproject.toml index ff8020cd5..2671380a7 100644 --- a/vendors/pyproject.toml +++ b/vendors/pyproject.toml @@ -23,6 +23,6 @@ python = "^3.6" jsonschema = "^3.2.0" lark-parser = "^0.9.0" -packaging = "^20.8" +packaging = "^20.9" pyrsistent = "^0.16.0" tomlkit = ">=0.7.0,<1.0.0" From 1034515a9d441c8dcab63bedfc30d5ad68d5ce7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 5 Feb 2021 16:45:01 +0100 Subject: [PATCH 21/77] Revert changes to canonicalize_name (#132) --- poetry/core/utils/helpers.py | 2 +- tests/utils/test_helpers.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/poetry/core/utils/helpers.py b/poetry/core/utils/helpers.py index b0526011a..6047e830c 100644 --- a/poetry/core/utils/helpers.py +++ b/poetry/core/utils/helpers.py @@ -20,7 +20,7 @@ from collections import Mapping -_canonicalize_regex = re.compile(r"[-_.]+") +_canonicalize_regex = re.compile(r"[-_]+") def canonicalize_name(name): # type: (str) -> str diff --git a/tests/utils/test_helpers.py b/tests/utils/test_helpers.py index 8ca49b9a5..b07e6730a 100644 --- a/tests/utils/test_helpers.py +++ b/tests/utils/test_helpers.py @@ -62,9 +62,7 @@ def test_parse_requires(): assert result == expected -@pytest.mark.parametrize( - "raw", ["a-b-c", "a.b-c", "a.b.c", "a_b-c", "a_b_c", "a-b_c", "a.b_c", "a-b.c"] -) +@pytest.mark.parametrize("raw", ["a-b-c", "a_b-c", "a_b_c", "a-b_c"]) def test_utils_helpers_canonical_names(raw): assert canonicalize_name(raw) == "a-b-c" From e5d09cd08f1a4d593bf5472964b7f513ed15d4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 5 Feb 2021 16:52:36 +0100 Subject: [PATCH 22/77] Update dependencies --- poetry.lock | 132 ++++++++++++++++++++++++++-------------------------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/poetry.lock b/poetry.lock index 682e6ab72..d79d5ee74 100644 --- a/poetry.lock +++ b/poetry.lock @@ -135,7 +135,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "coverage" -version = "5.3.1" +version = "5.4" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -310,7 +310,7 @@ python-versions = "*" [[package]] name = "packaging" -version = "20.8" +version = "20.9" description = "Core utilities for Python packages" category = "dev" optional = false @@ -436,7 +436,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"] [[package]] name = "pytest-cov" -version = "2.11.0" +version = "2.11.1" description = "Pytest plugin for measuring coverage." category = "dev" optional = false @@ -527,7 +527,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tox" -version = "3.21.1" +version = "3.21.4" description = "tox is a generic virtualenv management and test command line tool" category = "dev" optional = false @@ -558,7 +558,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "urllib3" -version = "1.26.2" +version = "1.26.3" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false @@ -590,7 +590,7 @@ test = ["pytest", "pytest-xdist", "pytest-cov", "pytest-mock"] [[package]] name = "virtualenv" -version = "20.3.1" +version = "20.4.2" description = "Virtual Python Environment builder" category = "dev" optional = false @@ -698,55 +698,55 @@ contextlib2 = [ {file = "contextlib2-0.6.0.post1.tar.gz", hash = "sha256:01f490098c18b19d2bd5bb5dc445b2054d2fa97f09a4280ba2c5f3c394c8162e"}, ] coverage = [ - {file = "coverage-5.3.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fabeeb121735d47d8eab8671b6b031ce08514c86b7ad8f7d5490a7b6dcd6267d"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7e4d159021c2029b958b2363abec4a11db0ce8cd43abb0d9ce44284cb97217e7"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:378ac77af41350a8c6b8801a66021b52da8a05fd77e578b7380e876c0ce4f528"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e448f56cfeae7b1b3b5bcd99bb377cde7c4eb1970a525c770720a352bc4c8044"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:cc44e3545d908ecf3e5773266c487ad1877be718d9dc65fc7eb6e7d14960985b"}, - {file = "coverage-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:08b3ba72bd981531fd557f67beee376d6700fba183b167857038997ba30dd297"}, - {file = "coverage-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:8dacc4073c359f40fcf73aede8428c35f84639baad7e1b46fce5ab7a8a7be4bb"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ee2f1d1c223c3d2c24e3afbb2dd38be3f03b1a8d6a83ee3d9eb8c36a52bee899"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9a9d4ff06804920388aab69c5ea8a77525cf165356db70131616acd269e19b36"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:782a5c7df9f91979a7a21792e09b34a658058896628217ae6362088b123c8500"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:fda29412a66099af6d6de0baa6bd7c52674de177ec2ad2630ca264142d69c6c7"}, - {file = "coverage-5.3.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:f2c6888eada180814b8583c3e793f3f343a692fc802546eed45f40a001b1169f"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8f33d1156241c43755137288dea619105477961cfa7e47f48dbf96bc2c30720b"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b239711e774c8eb910e9b1ac719f02f5ae4bf35fa0420f438cdc3a7e4e7dd6ec"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:f54de00baf200b4539a5a092a759f000b5f45fd226d6d25a76b0dff71177a714"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:be0416074d7f253865bb67630cf7210cbc14eb05f4099cc0f82430135aaa7a3b"}, - {file = "coverage-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:c46643970dff9f5c976c6512fd35768c4a3819f01f61169d8cdac3f9290903b7"}, - {file = "coverage-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9a4f66259bdd6964d8cf26142733c81fb562252db74ea367d9beb4f815478e72"}, - {file = "coverage-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c6e5174f8ca585755988bc278c8bb5d02d9dc2e971591ef4a1baabdf2d99589b"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3911c2ef96e5ddc748a3c8b4702c61986628bb719b8378bf1e4a6184bbd48fe4"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c5ec71fd4a43b6d84ddb88c1df94572479d9a26ef3f150cef3dacefecf888105"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f51dbba78d68a44e99d484ca8c8f604f17e957c1ca09c3ebc2c7e3bbd9ba0448"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a2070c5affdb3a5e751f24208c5c4f3d5f008fa04d28731416e023c93b275277"}, - {file = "coverage-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:535dc1e6e68fad5355f9984d5637c33badbdc987b0c0d303ee95a6c979c9516f"}, - {file = "coverage-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:a4857f7e2bc6921dbd487c5c88b84f5633de3e7d416c4dc0bb70256775551a6c"}, - {file = "coverage-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fac3c432851038b3e6afe086f777732bcf7f6ebbfd90951fa04ee53db6d0bcdd"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cd556c79ad665faeae28020a0ab3bda6cd47d94bec48e36970719b0b86e4dcf4"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a66ca3bdf21c653e47f726ca57f46ba7fc1f260ad99ba783acc3e58e3ebdb9ff"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ab110c48bc3d97b4d19af41865e14531f300b482da21783fdaacd159251890e8"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e52d3d95df81c8f6b2a1685aabffadf2d2d9ad97203a40f8d61e51b70f191e4e"}, - {file = "coverage-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:fa10fee7e32213f5c7b0d6428ea92e3a3fdd6d725590238a3f92c0de1c78b9d2"}, - {file = "coverage-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ce6f3a147b4b1a8b09aae48517ae91139b1b010c5f36423fa2b866a8b23df879"}, - {file = "coverage-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:93a280c9eb736a0dcca19296f3c30c720cb41a71b1f9e617f341f0a8e791a69b"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3102bb2c206700a7d28181dbe04d66b30780cde1d1c02c5f3c165cf3d2489497"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8ffd4b204d7de77b5dd558cdff986a8274796a1e57813ed005b33fd97e29f059"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a607ae05b6c96057ba86c811d9c43423f35e03874ffb03fbdcd45e0637e8b631"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:3a3c3f8863255f3c31db3889f8055989527173ef6192a283eb6f4db3c579d830"}, - {file = "coverage-5.3.1-cp38-cp38-win32.whl", hash = "sha256:ff1330e8bc996570221b450e2d539134baa9465f5cb98aff0e0f73f34172e0ae"}, - {file = "coverage-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:3498b27d8236057def41de3585f317abae235dd3a11d33e01736ffedb2ef8606"}, - {file = "coverage-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ceb499d2b3d1d7b7ba23abe8bf26df5f06ba8c71127f188333dddcf356b4b63f"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:3b14b1da110ea50c8bcbadc3b82c3933974dbeea1832e814aab93ca1163cd4c1"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:76b2775dda7e78680d688daabcb485dc87cf5e3184a0b3e012e1d40e38527cc8"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:cef06fb382557f66d81d804230c11ab292d94b840b3cb7bf4450778377b592f4"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f61319e33222591f885c598e3e24f6a4be3533c1d70c19e0dc59e83a71ce27d"}, - {file = "coverage-5.3.1-cp39-cp39-win32.whl", hash = "sha256:cc6f8246e74dd210d7e2b56c76ceaba1cc52b025cd75dbe96eb48791e0250e98"}, - {file = "coverage-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:2757fa64e11ec12220968f65d086b7a29b6583d16e9a544c889b22ba98555ef1"}, - {file = "coverage-5.3.1-pp36-none-any.whl", hash = "sha256:723d22d324e7997a651478e9c5a3120a0ecbc9a7e94071f7e1954562a8806cf3"}, - {file = "coverage-5.3.1-pp37-none-any.whl", hash = "sha256:c89b558f8a9a5a6f2cfc923c304d49f0ce629c3bd85cb442ca258ec20366394c"}, - {file = "coverage-5.3.1.tar.gz", hash = "sha256:38f16b1317b8dd82df67ed5daa5f5e7c959e46579840d77a67a4ceb9cef0a50b"}, + {file = "coverage-5.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135"}, + {file = "coverage-5.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c"}, + {file = "coverage-5.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9754a5c265f991317de2bac0c70a746efc2b695cf4d49f5d2cddeac36544fb44"}, + {file = "coverage-5.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3"}, + {file = "coverage-5.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:b7f7421841f8db443855d2854e25914a79a1ff48ae92f70d0a5c2f8907ab98c9"}, + {file = "coverage-5.4-cp27-cp27m-win32.whl", hash = "sha256:4a780807e80479f281d47ee4af2eb2df3e4ccf4723484f77da0bb49d027e40a1"}, + {file = "coverage-5.4-cp27-cp27m-win_amd64.whl", hash = "sha256:87c4b38288f71acd2106f5d94f575bc2136ea2887fdb5dfe18003c881fa6b370"}, + {file = "coverage-5.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c6809ebcbf6c1049002b9ac09c127ae43929042ec1f1dbd8bb1615f7cd9f70a0"}, + {file = "coverage-5.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ba7ca81b6d60a9f7a0b4b4e175dcc38e8fef4992673d9d6e6879fd6de00dd9b8"}, + {file = "coverage-5.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:89fc12c6371bf963809abc46cced4a01ca4f99cba17be5e7d416ed7ef1245d19"}, + {file = "coverage-5.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a8eb7785bd23565b542b01fb39115a975fefb4a82f23d407503eee2c0106247"}, + {file = "coverage-5.4-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:7e40d3f8eb472c1509b12ac2a7e24158ec352fc8567b77ab02c0db053927e339"}, + {file = "coverage-5.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1ccae21a076d3d5f471700f6d30eb486da1626c380b23c70ae32ab823e453337"}, + {file = "coverage-5.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:755c56beeacac6a24c8e1074f89f34f4373abce8b662470d3aa719ae304931f3"}, + {file = "coverage-5.4-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:322549b880b2d746a7672bf6ff9ed3f895e9c9f108b714e7360292aa5c5d7cf4"}, + {file = "coverage-5.4-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:60a3307a84ec60578accd35d7f0c71a3a971430ed7eca6567399d2b50ef37b8c"}, + {file = "coverage-5.4-cp35-cp35m-win32.whl", hash = "sha256:1375bb8b88cb050a2d4e0da901001347a44302aeadb8ceb4b6e5aa373b8ea68f"}, + {file = "coverage-5.4-cp35-cp35m-win_amd64.whl", hash = "sha256:16baa799ec09cc0dcb43a10680573269d407c159325972dd7114ee7649e56c66"}, + {file = "coverage-5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2f2cf7a42d4b7654c9a67b9d091ec24374f7c58794858bff632a2039cb15984d"}, + {file = "coverage-5.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b62046592b44263fa7570f1117d372ae3f310222af1fc1407416f037fb3af21b"}, + {file = "coverage-5.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:812eaf4939ef2284d29653bcfee9665f11f013724f07258928f849a2306ea9f9"}, + {file = "coverage-5.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:859f0add98707b182b4867359e12bde806b82483fb12a9ae868a77880fc3b7af"}, + {file = "coverage-5.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:04b14e45d6a8e159c9767ae57ecb34563ad93440fc1b26516a89ceb5b33c1ad5"}, + {file = "coverage-5.4-cp36-cp36m-win32.whl", hash = "sha256:ebfa374067af240d079ef97b8064478f3bf71038b78b017eb6ec93ede1b6bcec"}, + {file = "coverage-5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:84df004223fd0550d0ea7a37882e5c889f3c6d45535c639ce9802293b39cd5c9"}, + {file = "coverage-5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1b811662ecf72eb2d08872731636aee6559cae21862c36f74703be727b45df90"}, + {file = "coverage-5.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6b588b5cf51dc0fd1c9e19f622457cc74b7d26fe295432e434525f1c0fae02bc"}, + {file = "coverage-5.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3fe50f1cac369b02d34ad904dfe0771acc483f82a1b54c5e93632916ba847b37"}, + {file = "coverage-5.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:32ab83016c24c5cf3db2943286b85b0a172dae08c58d0f53875235219b676409"}, + {file = "coverage-5.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:68fb816a5dd901c6aff352ce49e2a0ffadacdf9b6fae282a69e7a16a02dad5fb"}, + {file = "coverage-5.4-cp37-cp37m-win32.whl", hash = "sha256:a636160680c6e526b84f85d304e2f0bb4e94f8284dd765a1911de9a40450b10a"}, + {file = "coverage-5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:bb32ca14b4d04e172c541c69eec5f385f9a075b38fb22d765d8b0ce3af3a0c22"}, + {file = "coverage-5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4d7165a4e8f41eca6b990c12ee7f44fef3932fac48ca32cecb3a1b2223c21f"}, + {file = "coverage-5.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a565f48c4aae72d1d3d3f8e8fb7218f5609c964e9c6f68604608e5958b9c60c3"}, + {file = "coverage-5.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786"}, + {file = "coverage-5.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a839e25f07e428a87d17d857d9935dd743130e77ff46524abb992b962eb2076c"}, + {file = "coverage-5.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:6625e52b6f346a283c3d563d1fd8bae8956daafc64bb5bbd2b8f8a07608e3994"}, + {file = "coverage-5.4-cp38-cp38-win32.whl", hash = "sha256:5bee3970617b3d74759b2d2df2f6a327d372f9732f9ccbf03fa591b5f7581e39"}, + {file = "coverage-5.4-cp38-cp38-win_amd64.whl", hash = "sha256:03ed2a641e412e42cc35c244508cf186015c217f0e4d496bf6d7078ebe837ae7"}, + {file = "coverage-5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14a9f1887591684fb59fdba8feef7123a0da2424b0652e1b58dd5b9a7bb1188c"}, + {file = "coverage-5.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9564ac7eb1652c3701ac691ca72934dd3009997c81266807aef924012df2f4b3"}, + {file = "coverage-5.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:0f48fc7dc82ee14aeaedb986e175a429d24129b7eada1b7e94a864e4f0644dde"}, + {file = "coverage-5.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:107d327071061fd4f4a2587d14c389a27e4e5c93c7cba5f1f59987181903902f"}, + {file = "coverage-5.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:0cdde51bfcf6b6bd862ee9be324521ec619b20590787d1655d005c3fb175005f"}, + {file = "coverage-5.4-cp39-cp39-win32.whl", hash = "sha256:c67734cff78383a1f23ceba3b3239c7deefc62ac2b05fa6a47bcd565771e5880"}, + {file = "coverage-5.4-cp39-cp39-win_amd64.whl", hash = "sha256:c669b440ce46ae3abe9b2d44a913b5fd86bb19eb14a8701e88e3918902ecd345"}, + {file = "coverage-5.4-pp36-none-any.whl", hash = "sha256:c0ff1c1b4d13e2240821ef23c1efb1f009207cb3f56e16986f713c2b0e7cd37f"}, + {file = "coverage-5.4-pp37-none-any.whl", hash = "sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b"}, + {file = "coverage-5.4.tar.gz", hash = "sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca"}, ] distlib = [ {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, @@ -809,8 +809,8 @@ nodeenv = [ {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, ] packaging = [ - {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, - {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] pathlib2 = [ {file = "pathlib2-2.3.5-py2.py3-none-any.whl", hash = "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db"}, @@ -844,8 +844,8 @@ pytest = [ {file = "pytest-4.6.11.tar.gz", hash = "sha256:50fa82392f2120cc3ec2ca0a75ee615be4c479e66669789771f1758332be4353"}, ] pytest-cov = [ - {file = "pytest-cov-2.11.0.tar.gz", hash = "sha256:e90e034cde61dacb1394639a33f449725c591025b182d69752c1dd0bfec639a7"}, - {file = "pytest_cov-2.11.0-py2.py3-none-any.whl", hash = "sha256:626a8a6ab188656c4f84b67d22436d6c494699d917e567e0048dda6e7f59e028"}, + {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, + {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, ] pytest-mock = [ {file = "pytest-mock-2.0.0.tar.gz", hash = "sha256:b35eb281e93aafed138db25c8772b95d3756108b601947f89af503f8c629413f"}, @@ -862,6 +862,8 @@ pyyaml = [ {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, + {file = "PyYAML-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a"}, + {file = "PyYAML-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e"}, {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] requests = [ @@ -894,24 +896,24 @@ toml = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tox = [ - {file = "tox-3.21.1-py2.py3-none-any.whl", hash = "sha256:8a28facf65275c84b3bfde102afe24541d2ce02fd83ea07ce906537c3a74ed2d"}, - {file = "tox-3.21.1.tar.gz", hash = "sha256:31379f2662393034203c5d6b2ebb7b86c67452cfc689784475c1c6e4a3cbc0cd"}, + {file = "tox-3.21.4-py2.py3-none-any.whl", hash = "sha256:65d0e90ceb816638a50d64f4b47b11da767b284c0addda2294cb3cd69bd72425"}, + {file = "tox-3.21.4.tar.gz", hash = "sha256:cf7fef81a3a2434df4d7af2a6d1bf606d2970220addfbe7dea2615bd4bb2c252"}, ] typing = [ {file = "typing-3.7.4.3-py2-none-any.whl", hash = "sha256:283d868f5071ab9ad873e5e52268d611e851c870a2ba354193026f2dfb29d8b5"}, {file = "typing-3.7.4.3.tar.gz", hash = "sha256:1187fb9c82fd670d10aa07bbb6cfcfe4bdda42d6fab8d5134f04e8c4d0b71cc9"}, ] urllib3 = [ - {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"}, - {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"}, + {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, + {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"}, ] vendoring = [ {file = "vendoring-0.3.3-py2.py3-none-any.whl", hash = "sha256:2b91c302116320f903fdb5e60c2b0805d807e2b87425ab0c86624028aa5ffa57"}, {file = "vendoring-0.3.3.tar.gz", hash = "sha256:2bbfc0c8da2863f4638c854b91e80c5d0ca57db62fb979d4b0f52088eeab1162"}, ] virtualenv = [ - {file = "virtualenv-20.3.1-py2.py3-none-any.whl", hash = "sha256:14b34341e742bdca219e10708198e704e8a7064dd32f474fc16aca68ac53a306"}, - {file = "virtualenv-20.3.1.tar.gz", hash = "sha256:0c111a2236b191422b37fe8c28b8c828ced39aab4bf5627fa5c331aeffb570d9"}, + {file = "virtualenv-20.4.2-py2.py3-none-any.whl", hash = "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3"}, + {file = "virtualenv-20.4.2.tar.gz", hash = "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, From fc95204980e204b34fa1892182f9b8d51e437516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 5 Feb 2021 17:14:09 +0100 Subject: [PATCH 23/77] Bump version to 1.0.1 --- CHANGELOG.md | 18 +++++++++++++++++- poetry/core/__init__.py | 2 +- pyproject.toml | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 285b8d5ef..04ff66018 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Change Log +## [1.0.1] - 2021-02-05 + +### Fixed + +- Fixed PEP 508 representation of dependency without extras ([#102](https://github.com/python-poetry/poetry-core/pull/102)). +- Fixed an error where development dependencies were being resolved when invoking the PEP-517 backend ([#101](https://github.com/python-poetry/poetry-core/pull/101)). +- Fixed source distribution not being deterministic ([#105](https://github.com/python-poetry/poetry-core/pull/105)). +- Fixed an error where zip files were left open when building wheels ([#122](https://github.com/python-poetry/poetry-core/pull/122)). +- Fixed an error where explicitly included files were still not present in final distributions ([#124](https://github.com/python-poetry/poetry-core/pull/124)). +- Fixed wheel filename matching for recent architecture ([#125](https://github.com/python-poetry/poetry-core/pull/125), [#129](https://github.com/python-poetry/poetry-core/pull/129)). +- Fixed an error where the `&` character was not accepted for author names ([#120](https://github.com/python-poetry/poetry-core/pull/120)). +- Fixed the PEP-508 representation of some dependencies ([#103](https://github.com/python-poetry/poetry-core/pull/103)). +- Fixed the `Requires-Python` metadata generation ([#127](https://github.com/python-poetry/poetry-core/pull/127)). + + ## [1.0.0] - 2020-09-30 No changes. @@ -116,7 +131,8 @@ No changes. - Fixed support for stub-only packages ([#28](https://github.com/python-poetry/core/pull/28)). -[Unreleased]: https://github.com/python-poetry/poetry-core/compare/1.0.0...master +[Unreleased]: https://github.com/python-poetry/poetry-core/compare/1.0.1...master +[1.0.1]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.1 [1.0.0]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.0 [1.0.0rc3]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.0rc3 [1.0.0rc2]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.0rc2 diff --git a/poetry/core/__init__.py b/poetry/core/__init__.py index 1a59fcbea..032a4c75b 100644 --- a/poetry/core/__init__.py +++ b/poetry/core/__init__.py @@ -7,7 +7,7 @@ # noinspection PyUnresolvedReferences from pathlib2 import Path -__version__ = "1.0.0" +__version__ = "1.0.1" __vendor_site__ = (Path(__file__).parent / "_vendor").as_posix() diff --git a/pyproject.toml b/pyproject.toml index 9948932ab..1de68970f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "poetry-core" -version = "1.0.0" +version = "1.0.1" description = "Poetry PEP 517 Build Backend" authors = ["Sébastien Eustace "] From 1b11c7842f2e6771ce450d3bc3b38d13851e09eb Mon Sep 17 00:00:00 2001 From: finswimmer Date: Fri, 5 Feb 2021 20:51:41 +0100 Subject: [PATCH 24/77] "Dependency" needs to be imported outside TYPE_CHECKING only section (#134) --- poetry/core/packages/package.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/poetry/core/packages/package.py b/poetry/core/packages/package.py index 982b556e2..211261c56 100644 --- a/poetry/core/packages/package.py +++ b/poetry/core/packages/package.py @@ -16,6 +16,9 @@ from poetry.core.version.markers import AnyMarker from poetry.core.version.markers import parse_marker +# Do not move to the TYPE_CHECKING only section, because Dependency get's imported +# by poetry/packages/locker.py from here +from .dependency import Dependency from .specification import PackageSpecification from .utils.utils import create_nested_marker @@ -24,7 +27,6 @@ from poetry.core.semver import VersionTypes # noqa from poetry.core.version.markers import BaseMarker # noqa - from .dependency import Dependency from .directory_dependency import DirectoryDependency from .file_dependency import FileDependency from .url_dependency import URLDependency @@ -188,7 +190,7 @@ def maintainer_email(self): # type: () -> str @property def all_requires( self, - ): # type: () -> List[Union["DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency", "Dependency"]] + ): # type: () -> List[Union["DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency", Dependency]] return self.requires + self.dev_requires def _get_author(self): # type: () -> dict @@ -310,7 +312,7 @@ def is_root(self): # type: () -> bool def add_dependency( self, dependency, - ): # type: ("Dependency") -> "Dependency" + ): # type: (Dependency) -> Dependency if dependency.category == "dev": self.dev_requires.append(dependency) else: @@ -320,7 +322,7 @@ def add_dependency( def to_dependency( self, - ): # type: () -> Union["Dependency", "DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency"] + ): # type: () -> Union[Dependency, "DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency"] from poetry.core.utils._compat import Path from .dependency import Dependency From e9580acff8a8d626b5e7c58560e22f06e3ecb4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 5 Feb 2021 20:54:40 +0100 Subject: [PATCH 25/77] Update 1.0.1 change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04ff66018..4fc2fefe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Fixed an error where the `&` character was not accepted for author names ([#120](https://github.com/python-poetry/poetry-core/pull/120)). - Fixed the PEP-508 representation of some dependencies ([#103](https://github.com/python-poetry/poetry-core/pull/103)). - Fixed the `Requires-Python` metadata generation ([#127](https://github.com/python-poetry/poetry-core/pull/127)). +- Fixed an error where pre-release versions were accepted in version constraints ([#128](https://github.com/python-poetry/poetry-core/pull/128)). ## [1.0.0] - 2020-09-30 From a706ee22017e2576b7297821321fadbf2ad2437d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 5 Feb 2021 20:56:46 +0100 Subject: [PATCH 26/77] Bump version to 1.0.2 --- CHANGELOG.md | 10 +++++++++- poetry/core/__init__.py | 2 +- pyproject.toml | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fc2fefe2..0056c56d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## [1.0.2] - 2021-02-05 + +### Fixed + +- Fixed a missing import causing an error in Poetry ([#134](https://github.com/python-poetry/poetry-core/pull/134)). + + ## [1.0.1] - 2021-02-05 ### Fixed @@ -132,7 +139,8 @@ No changes. - Fixed support for stub-only packages ([#28](https://github.com/python-poetry/core/pull/28)). -[Unreleased]: https://github.com/python-poetry/poetry-core/compare/1.0.1...master +[Unreleased]: https://github.com/python-poetry/poetry-core/compare/1.0.2...master +[1.0.2]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.2 [1.0.1]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.1 [1.0.0]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.0 [1.0.0rc3]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.0rc3 diff --git a/poetry/core/__init__.py b/poetry/core/__init__.py index 032a4c75b..eed5b1895 100644 --- a/poetry/core/__init__.py +++ b/poetry/core/__init__.py @@ -7,7 +7,7 @@ # noinspection PyUnresolvedReferences from pathlib2 import Path -__version__ = "1.0.1" +__version__ = "1.0.2" __vendor_site__ = (Path(__file__).parent / "_vendor").as_posix() diff --git a/pyproject.toml b/pyproject.toml index 1de68970f..d55b8444a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "poetry-core" -version = "1.0.1" +version = "1.0.2" description = "Poetry PEP 517 Build Backend" authors = ["Sébastien Eustace "] From b103125e4591a0c78361b0626880dbcf1dcb61e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Wed, 3 Mar 2021 11:29:50 +0100 Subject: [PATCH 27/77] Modernize code base (#131) * Update CI files for Python 3.6+ only * Modernize codebase * Reorganize imports to improve performance --- .github/workflows/tests.yml | 13 +- .pre-commit-config.yaml | 10 +- poetry.lock | 503 ++++++++---------- poetry/core/__init__.py | 8 +- poetry/core/factory.py | 41 +- poetry/core/json/__init__.py | 6 +- poetry/core/masonry/__init__.py | 2 - poetry/core/masonry/api.py | 25 +- poetry/core/masonry/builder.py | 29 +- poetry/core/masonry/builders/__init__.py | 2 - poetry/core/masonry/builders/builder.py | 85 +-- poetry/core/masonry/builders/sdist.py | 80 ++- poetry/core/masonry/builders/wheel.py | 76 +-- poetry/core/masonry/metadata.py | 10 +- poetry/core/masonry/utils/helpers.py | 6 +- poetry/core/masonry/utils/include.py | 21 +- poetry/core/masonry/utils/module.py | 32 +- poetry/core/masonry/utils/package_include.py | 29 +- poetry/core/packages/__init__.py | 224 -------- poetry/core/packages/constraints/__init__.py | 6 +- .../packages/constraints/any_constraint.py | 20 +- .../packages/constraints/base_constraint.py | 18 +- .../core/packages/constraints/constraint.py | 30 +- .../packages/constraints/empty_constraint.py | 16 +- .../packages/constraints/multi_constraint.py | 16 +- .../packages/constraints/union_constraint.py | 20 +- poetry/core/packages/dependency.py | 351 +++++++++--- poetry/core/packages/directory_dependency.py | 45 +- poetry/core/packages/file_dependency.py | 35 +- poetry/core/packages/package.py | 122 ++--- poetry/core/packages/project_package.py | 37 +- poetry/core/packages/specification.py | 38 +- poetry/core/packages/types.py | 14 + poetry/core/packages/url_dependency.py | 27 +- poetry/core/packages/utils/link.py | 73 ++- poetry/core/packages/utils/utils.py | 80 +-- poetry/core/packages/vcs_dependency.py | 54 +- poetry/core/poetry.py | 25 +- poetry/core/pyproject/__init__.py | 6 - poetry/core/pyproject/tables.py | 16 +- poetry/core/pyproject/toml.py | 55 +- poetry/core/semver/__init__.py | 151 ------ poetry/core/semver/empty_constraint.py | 18 +- poetry/core/semver/helpers.py | 162 ++++++ poetry/core/semver/patterns.py | 4 +- poetry/core/semver/version.py | 118 ++-- poetry/core/semver/version_constraint.py | 18 +- poetry/core/semver/version_range.py | 68 +-- poetry/core/semver/version_union.py | 38 +- poetry/core/spdx/__init__.py | 57 -- poetry/core/spdx/helpers.py | 66 +++ poetry/core/spdx/license.py | 4 +- poetry/core/spdx/updater.py | 6 +- poetry/core/toml/file.py | 21 +- poetry/core/utils/_compat.py | 102 +--- poetry/core/utils/helpers.py | 18 +- poetry/core/utils/toml_file.py | 8 +- poetry/core/vcs/__init__.py | 11 +- poetry/core/vcs/git.py | 67 ++- poetry/core/version/__init__.py | 6 +- poetry/core/version/base.py | 18 +- poetry/core/version/grammars/parser.py | 26 + poetry/core/version/helpers.py | 12 +- poetry/core/version/legacy_version.py | 20 +- poetry/core/version/markers.py | 198 +++---- poetry/core/version/requirements.py | 35 +- poetry/core/version/utils.py | 36 +- poetry/core/version/version.py | 53 +- pyproject.toml | 23 +- stanza | 10 +- tests/conftest.py | 2 +- tests/fixtures/project_with_setup/setup.py | 2 - tests/integration/test_pep517.py | 3 +- .../builders/fixtures/extended/setup.py | 25 + .../pep_561_stub_only/pkg-stubs/module.pyi | 1 + .../pkg-stubs/module.pyi | 1 + .../src/pkg-stubs/module.pyi | 1 + .../builders/fixtures/src_extended/setup.py | 28 + tests/masonry/builders/test_builder.py | 3 +- tests/masonry/builders/test_complete.py | 31 +- tests/masonry/builders/test_sdist.py | 19 +- tests/masonry/builders/test_wheel.py | 4 +- tests/masonry/test_api.py | 10 +- tests/masonry/utils/test_package_include.py | 3 +- tests/packages/test_dependency.py | 9 +- tests/packages/test_directory_dependency.py | 9 +- tests/packages/test_file_dependency.py | 14 +- tests/packages/test_main.py | 46 +- tests/packages/test_package.py | 6 +- tests/packages/test_url_dependency.py | 2 +- tests/packages/utils/test_utils.py | 2 +- tests/packages/utils/test_utils_link.py | 2 +- tests/packages/utils/test_utils_urls.py | 9 +- tests/pyproject/conftest.py | 9 +- tests/pyproject/test_pyproject_toml.py | 7 +- tests/pyproject/test_pyproject_toml_file.py | 3 +- .../semver/{test_main.py => test_helpers.py} | 8 +- tests/semver/test_parse_constraint.py | 8 +- tests/semver/test_version.py | 6 +- tests/semver/test_version_range.py | 6 +- tests/spdx/{test_main.py => test_helpers.py} | 2 +- tests/spdx/test_license.py | 2 +- tests/test_factory.py | 27 +- tests/testutils.py | 2 +- tests/vcs/test_vcs.py | 5 +- tests/version/test_requirements.py | 7 +- tox.ini | 2 +- 107 files changed, 1971 insertions(+), 2035 deletions(-) create mode 100644 poetry/core/packages/types.py create mode 100644 poetry/core/semver/helpers.py create mode 100644 poetry/core/spdx/helpers.py create mode 100644 poetry/core/version/grammars/parser.py create mode 100644 tests/masonry/builders/fixtures/extended/setup.py create mode 100644 tests/masonry/builders/fixtures/src_extended/setup.py rename tests/semver/{test_main.py => test_helpers.py} (96%) rename tests/spdx/{test_main.py => test_helpers.py} (96%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e80838a5e..2add7486d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,16 +12,10 @@ jobs: strategy: matrix: os: [Ubuntu, MacOS, Windows] - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, pypy2, pypy3] + python-version: [3.6, 3.7, 3.8, 3.9, pypy3] exclude: - - os: Ubuntu - python-version: pypy2 - - os: MacOS - python-version: pypy2 - os: MacOS python-version: pypy3 - - os: Windows - python-version: pypy2 - os: Windows python-version: pypy3 steps: @@ -48,11 +42,6 @@ jobs: shell: bash run: poetry config virtualenvs.in-project true - - name: Configure poetry installer - if: matrix.python-version == '2.7' - shell: bash - run: poetry config experimental.new-installer false - - name: Set up cache uses: actions/cache@v1 id: cache diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9779be83b..f7be580c1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ repos: - repo: https://github.com/psf/black - rev: 19.10b0 + rev: 20.8b1 hooks: - id: black exclude: ^poetry/core/_vendor - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.3 + rev: 3.8.4 hooks: - id: flake8 exclude: | @@ -16,8 +16,8 @@ repos: | ^poetry/core/_vendor ) - - repo: https://github.com/pre-commit/mirrors-isort - rev: v5.4.2 + - repo: https://github.com/timothycrosley/isort + rev: 5.7.0 hooks: - id: isort additional_dependencies: [toml] @@ -28,7 +28,7 @@ repos: ) - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v3.4.0 hooks: - id: trailing-whitespace exclude: | diff --git a/poetry.lock b/poetry.lock index d79d5ee74..27992c61c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,17 +6,6 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "aspy.yaml" -version = "1.3.0" -description = "A few extensions to pyyaml." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -pyyaml = "*" - [[package]] name = "atomicwrites" version = "1.4.0" @@ -40,35 +29,27 @@ tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)" tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] [[package]] -name = "backports.functools-lru-cache" -version = "1.6.1" -description = "Backport of functools.lru_cache" +name = "black" +version = "20.8b1" +description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=2.6" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-black-multipy", "pytest-cov"] - -[[package]] -name = "backports.tempfile" -version = "1.0" -description = "Backport of new features in Python's tempfile module" -category = "dev" -optional = false -python-versions = "*" +python-versions = ">=3.6" [package.dependencies] -"backports.weakref" = "*" +appdirs = "*" +click = ">=7.1.2" +dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} +mypy-extensions = ">=0.4.3" +pathspec = ">=0.6,<1" +regex = ">=2020.1.8" +toml = ">=0.10.1" +typed-ast = ">=1.4.0" +typing-extensions = ">=3.7.4" -[[package]] -name = "backports.weakref" -version = "1.0.post1" -description = "Backport of new features in Python's weakref module" -category = "dev" -optional = false -python-versions = "*" +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] name = "certifi" @@ -80,14 +61,11 @@ python-versions = "*" [[package]] name = "cfgv" -version = "2.0.1" +version = "3.0.0" description = "Validate configuration and produce human readable error messages." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -six = "*" +python-versions = ">=3.6" [[package]] name = "chardet" @@ -113,26 +91,6 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -[[package]] -name = "configparser" -version = "4.0.2" -description = "Updated configparser from Python 3.7 for Python 2.6+." -category = "main" -optional = false -python-versions = ">=2.6" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2)", "pytest-flake8", "pytest-black-multipy"] - -[[package]] -name = "contextlib2" -version = "0.6.0.post1" -description = "Backports and enhancements for the contextlib module" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - [[package]] name = "coverage" version = "5.4" @@ -145,18 +103,18 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" toml = ["toml"] [[package]] -name = "distlib" -version = "0.3.1" -description = "Distribution utilities" +name = "dataclasses" +version = "0.8" +description = "A backport of the dataclasses module for Python 3.6" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6, <3.7" [[package]] -name = "enum34" -version = "1.1.10" -description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" -category = "main" +name = "distlib" +version = "0.3.1" +description = "Distribution utilities" +category = "dev" optional = false python-versions = "*" @@ -168,30 +126,6 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "funcsigs" -version = "1.0.2" -description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "functools32" -version = "3.2.3-2" -description = "Backport of the functools module from Python 3.2.3 for use on 2.7 and PyPy." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "futures" -version = "3.3.0" -description = "Backport of the concurrent.futures package from Python 3" -category = "dev" -optional = false -python-versions = ">=2.6, <3" - [[package]] name = "identify" version = "1.5.13" @@ -220,9 +154,6 @@ optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] -configparser = {version = ">=3.5", markers = "python_version < \"3\""} -contextlib2 = {version = "*", markers = "python_version < \"3\""} -pathlib2 = {version = "*", markers = "python_version < \"3\""} zipp = ">=0.5" [package.extras] @@ -231,21 +162,31 @@ testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] [[package]] name = "importlib-resources" -version = "3.2.1" +version = "5.1.0" description = "Read resources from Python packages" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] -contextlib2 = {version = "*", markers = "python_version < \"3\""} -pathlib2 = {version = "*", markers = "python_version < \"3\""} -singledispatch = {version = "*", markers = "python_version < \"3.4\""} -typing = {version = "*", markers = "python_version < \"3.5\""} zipp = {version = ">=0.4", markers = "python_version < \"3.8\""} [package.extras] -docs = ["sphinx", "rst.linker", "jaraco.packaging"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[[package]] +name = "isort" +version = "5.7.0" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.extras] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] [[package]] name = "jsonschema" @@ -257,6 +198,7 @@ python-versions = "*" [package.dependencies] attrs = ">=17.4.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} pyrsistent = ">=0.14.0" six = ">=1.11.0" @@ -264,41 +206,21 @@ six = ">=1.11.0" format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] -[[package]] -name = "mock" -version = "3.0.5" -description = "Rolling backport of unittest.mock for all Pythons" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -funcsigs = {version = ">=1", markers = "python_version < \"3.3\""} -six = "*" - -[package.extras] -build = ["twine", "wheel", "blurb"] -docs = ["sphinx"] -test = ["pytest", "pytest-cov"] - [[package]] name = "more-itertools" -version = "5.0.0" +version = "8.6.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false -python-versions = "*" - -[package.dependencies] -six = ">=1.0.0,<2.0.0" +python-versions = ">=3.5" [[package]] -name = "more-itertools" -version = "8.6.0" -description = "More routines for operating on iterables, beyond itertools" +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." category = "dev" optional = false -python-versions = ">=3.5" +python-versions = "*" [[package]] name = "nodeenv" @@ -320,16 +242,12 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" pyparsing = ">=2.0.2" [[package]] -name = "pathlib2" -version = "2.3.5" -description = "Object-oriented filesystem paths" -category = "main" +name = "pathspec" +version = "0.8.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" optional = false -python-versions = "*" - -[package.dependencies] -scandir = {version = "*", markers = "python_version < \"3.5\""} -six = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pep517" @@ -360,24 +278,21 @@ dev = ["pre-commit", "tox"] [[package]] name = "pre-commit" -version = "1.21.0" +version = "2.10.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6.1" [package.dependencies] -"aspy.yaml" = "*" cfgv = ">=2.0.0" -futures = {version = "*", markers = "python_version < \"3.2\""} identify = ">=1.0.0" importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} importlib-resources = {version = "*", markers = "python_version < \"3.7\""} nodeenv = ">=0.11.1" -pyyaml = "*" -six = "*" +pyyaml = ">=5.1" toml = "*" -virtualenv = ">=15.2" +virtualenv = ">=20.0.8" [[package]] name = "py" @@ -418,14 +333,9 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" atomicwrites = ">=1.0" attrs = ">=17.4.0" colorama = {version = "*", markers = "sys_platform == \"win32\" and python_version != \"3.4\""} -funcsigs = {version = ">=1.0", markers = "python_version < \"3.0\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -more-itertools = [ - {version = ">=4.0.0,<6.0.0", markers = "python_version <= \"2.7\""}, - {version = ">=4.0.0", markers = "python_version > \"2.7\""}, -] +more-itertools = {version = ">=4.0.0", markers = "python_version > \"2.7\""} packaging = "*" -pathlib2 = {version = ">=2.2.0", markers = "python_version < \"3.6\""} pluggy = ">=0.12,<1.0" py = ">=1.5.0" six = ">=1.10.0" @@ -458,7 +368,6 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] -mock = {version = "*", markers = "python_version < \"3.0\""} pytest = ">=2.7" [package.extras] @@ -466,11 +375,19 @@ dev = ["pre-commit", "tox"] [[package]] name = "pyyaml" -version = "5.3.1" +version = "5.4.1" description = "YAML parser and emitter for Python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[[package]] +name = "regex" +version = "2020.11.13" +description = "Alternative regular expression module, to replace re." +category = "dev" +optional = false +python-versions = "*" [[package]] name = "requests" @@ -490,30 +407,11 @@ urllib3 = ">=1.21.1,<1.27" security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -[[package]] -name = "scandir" -version = "1.10.0" -description = "scandir, a better directory iterator and faster os.walk()" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "singledispatch" -version = "3.4.0.3" -description = "This library brings functools.singledispatch from Python 3.4 to Python 2.6-3.3." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" - [[package]] name = "six" version = "1.15.0" description = "Python 2 and 3 compatibility utilities" -category = "main" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" @@ -549,12 +447,20 @@ docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-a testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)", "pytest-xdist (>=1.22.2)", "pathlib2 (>=2.3.3)"] [[package]] -name = "typing" +name = "typed-ast" +version = "1.4.2" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "typing-extensions" version = "3.7.4.3" -description = "Type Hints for Python" -category = "main" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = "*" [[package]] name = "urllib3" @@ -602,7 +508,6 @@ distlib = ">=0.3.1,<1" filelock = ">=3.0.0,<4" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} -pathlib2 = {version = ">=2.3.3,<3", markers = "python_version < \"3.4\" and sys_platform != \"win32\""} six = ">=1.9.0,<2" [package.extras] @@ -617,38 +522,28 @@ category = "dev" optional = false python-versions = "*" -[package.dependencies] -"backports.functools-lru-cache" = {version = ">=1.2.1", markers = "python_version < \"3.2\""} - [[package]] name = "zipp" -version = "1.2.0" +version = "3.4.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false -python-versions = ">=2.7" - -[package.dependencies] -contextlib2 = {version = "*", markers = "python_version < \"3.4\""} +python-versions = ">=3.6" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pathlib2", "unittest2", "jaraco.itertools", "func-timeout"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [metadata] lock-version = "1.1" -python-versions = "~2.7 || ^3.5" -content-hash = "73b0c1d12930e6381fb1a47e8c903603ac18a2981ee899f9f675c4ebcbbbe3ff" +python-versions = "^3.6" +content-hash = "7d0b49fb8df307d8f6299be0bc1d2017281c338c0a9d9eb8e648cc1de45e4c4b" [metadata.files] appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] -"aspy.yaml" = [ - {file = "aspy.yaml-1.3.0-py2.py3-none-any.whl", hash = "sha256:463372c043f70160a9ec950c3f1e4c3a82db5fca01d334b6bc89c7164d744bdc"}, - {file = "aspy.yaml-1.3.0.tar.gz", hash = "sha256:e7c742382eff2caed61f87a39d13f99109088e5e93f04d76eb8d4b28aa143f45"}, -] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, @@ -657,30 +552,18 @@ attrs = [ {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] -"backports.functools-lru-cache" = [ - {file = "backports.functools_lru_cache-1.6.1-py2.py3-none-any.whl", hash = "sha256:0bada4c2f8a43d533e4ecb7a12214d9420e66eb206d54bf2d682581ca4b80848"}, - {file = "backports.functools_lru_cache-1.6.1.tar.gz", hash = "sha256:8fde5f188da2d593bd5bc0be98d9abc46c95bb8a9dde93429570192ee6cc2d4a"}, -] -"backports.tempfile" = [ - {file = "backports.tempfile-1.0-py2.py3-none-any.whl", hash = "sha256:05aa50940946f05759696156a8c39be118169a0e0f94a49d0bb106503891ff54"}, - {file = "backports.tempfile-1.0.tar.gz", hash = "sha256:1c648c452e8770d759bdc5a5e2431209be70d25484e1be24876cf2168722c762"}, -] -"backports.weakref" = [ - {file = "backports.weakref-1.0.post1-py2.py3-none-any.whl", hash = "sha256:81bc9b51c0abc58edc76aefbbc68c62a787918ffe943a37947e162c3f8e19e82"}, - {file = "backports.weakref-1.0.post1.tar.gz", hash = "sha256:bc4170a29915f8b22c9e7c4939701859650f2eb84184aee80da329ac0b9825c2"}, +black = [ + {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] certifi = [ {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, ] cfgv = [ - {file = "cfgv-2.0.1-py2.py3-none-any.whl", hash = "sha256:fbd93c9ab0a523bf7daec408f3be2ed99a980e20b2d19b50fc184ca6b820d289"}, - {file = "cfgv-2.0.1.tar.gz", hash = "sha256:edb387943b665bf9c434f717bf630fa78aecd53d5900d2e05da6ad6048553144"}, -] -chardet = [ - {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, - {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, + {file = "cfgv-3.0.0-py2.py3-none-any.whl", hash = "sha256:f22b426ed59cd2ab2b54ff96608d846c33dfb8766a67f0b4a6ce130ce244414f"}, + {file = "cfgv-3.0.0.tar.gz", hash = "sha256:04b093b14ddf9fd4d17c53ebfd55582d27b76ed30050193c14e560770c5360eb"}, ] +chardet = [] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, @@ -689,14 +572,6 @@ colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] -configparser = [ - {file = "configparser-4.0.2-py2.py3-none-any.whl", hash = "sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c"}, - {file = "configparser-4.0.2.tar.gz", hash = "sha256:c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df"}, -] -contextlib2 = [ - {file = "contextlib2-0.6.0.post1-py2.py3-none-any.whl", hash = "sha256:3355078a159fbb44ee60ea80abd0d87b80b78c248643b49aa6d94673b413609b"}, - {file = "contextlib2-0.6.0.post1.tar.gz", hash = "sha256:01f490098c18b19d2bd5bb5dc445b2054d2fa97f09a4280ba2c5f3c394c8162e"}, -] coverage = [ {file = "coverage-5.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135"}, {file = "coverage-5.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c"}, @@ -748,31 +623,18 @@ coverage = [ {file = "coverage-5.4-pp37-none-any.whl", hash = "sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b"}, {file = "coverage-5.4.tar.gz", hash = "sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca"}, ] +dataclasses = [ + {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, + {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, +] distlib = [ {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, ] -enum34 = [ - {file = "enum34-1.1.10-py2-none-any.whl", hash = "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53"}, - {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"}, - {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"}, -] filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, ] -funcsigs = [ - {file = "funcsigs-1.0.2-py2.py3-none-any.whl", hash = "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca"}, - {file = "funcsigs-1.0.2.tar.gz", hash = "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"}, -] -functools32 = [ - {file = "functools32-3.2.3-2.tar.gz", hash = "sha256:f6253dfbe0538ad2e387bd8fdfd9293c925d63553f5813c4e587745416501e6d"}, - {file = "functools32-3.2.3-2.zip", hash = "sha256:89d824aa6c358c421a234d7f9ee0bd75933a67c29588ce50aaa3acdf4d403fa0"}, -] -futures = [ - {file = "futures-3.3.0-py2-none-any.whl", hash = "sha256:49b3f5b064b6e3afc3316421a3f25f66c137ae88f068abbf72830170033c5e16"}, - {file = "futures-3.3.0.tar.gz", hash = "sha256:7e033af76a5e35f58e56da7a91e687706faf4e7bdfb2cbc3f2cca6b9bcda9794"}, -] identify = [ {file = "identify-1.5.13-py2.py3-none-any.whl", hash = "sha256:9dfb63a2e871b807e3ba62f029813552a24b5289504f5b071dea9b041aee9fe4"}, {file = "identify-1.5.13.tar.gz", hash = "sha256:70b638cf4743f33042bebb3b51e25261a0a10e80f978739f17e7fd4837664a66"}, @@ -786,24 +648,25 @@ importlib-metadata = [ {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, ] importlib-resources = [ - {file = "importlib_resources-3.2.1-py2.py3-none-any.whl", hash = "sha256:e2860cf0c4bc999947228d18be154fa3779c5dde0b882bd2d7b3f4d25e698bd6"}, - {file = "importlib_resources-3.2.1.tar.gz", hash = "sha256:a9fe213ab6452708ec1b3f4ec6f2881b8ab3645cb4e5efb7fea2bbf05a91db3b"}, + {file = "importlib_resources-5.1.0-py3-none-any.whl", hash = "sha256:885b8eae589179f661c909d699a546cf10d83692553e34dca1bf5eb06f7f6217"}, + {file = "importlib_resources-5.1.0.tar.gz", hash = "sha256:bfdad047bce441405a49cf8eb48ddce5e56c696e185f59147a8b79e75e9e6380"}, +] +isort = [ + {file = "isort-5.7.0-py3-none-any.whl", hash = "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"}, + {file = "isort-5.7.0.tar.gz", hash = "sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e"}, ] jsonschema = [ {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, ] -mock = [ - {file = "mock-3.0.5-py2.py3-none-any.whl", hash = "sha256:d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"}, - {file = "mock-3.0.5.tar.gz", hash = "sha256:83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3"}, -] more-itertools = [ - {file = "more-itertools-5.0.0.tar.gz", hash = "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4"}, - {file = "more_itertools-5.0.0-py2-none-any.whl", hash = "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc"}, - {file = "more_itertools-5.0.0-py3-none-any.whl", hash = "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"}, {file = "more-itertools-8.6.0.tar.gz", hash = "sha256:b3a9005928e5bed54076e6e549c792b306fddfe72b2d1d22dd63d42d5d3899cf"}, {file = "more_itertools-8.6.0-py3-none-any.whl", hash = "sha256:8e1a2a43b2f2727425f2b5839587ae37093f19153dc26c0927d1048ff6557330"}, ] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] nodeenv = [ {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, @@ -812,9 +675,9 @@ packaging = [ {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] -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.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, + {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, ] pep517 = [ {file = "pep517-0.8.2-py2.py3-none-any.whl", hash = "sha256:576c480be81f3e1a70a16182c762311eb80d1f8a7b0d11971e5234967d7a342c"}, @@ -825,13 +688,10 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] pre-commit = [ - {file = "pre_commit-1.21.0-py2.py3-none-any.whl", hash = "sha256:f92a359477f3252452ae2e8d3029de77aec59415c16ae4189bcfba40b757e029"}, - {file = "pre_commit-1.21.0.tar.gz", hash = "sha256:8f48d8637bdae6fa70cc97db9c1dd5aa7c5c8bf71968932a380628c25978b850"}, -] -py = [ - {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, - {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, + {file = "pre_commit-2.10.0-py2.py3-none-any.whl", hash = "sha256:391ed331fdd0a21d0be48c1b9919921e9d372dfd60f6dc77b8f01dd6b13161c1"}, + {file = "pre_commit-2.10.0.tar.gz", hash = "sha256:f413348d3a8464b77987e36ef6e02c3372dadb823edf0dfe6fb0c3dc2f378ef9"}, ] +py = [] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, @@ -852,41 +712,75 @@ pytest-mock = [ {file = "pytest_mock-2.0.0-py2.py3-none-any.whl", hash = "sha256:cb67402d87d5f53c579263d37971a164743dc33c159dfb4fb4a86f37c5552307"}, ] pyyaml = [ - {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, - {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, - {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, - {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, - {file = "PyYAML-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a"}, - {file = "PyYAML-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e"}, - {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, +] +regex = [ + {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"}, + {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"}, + {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"}, + {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"}, + {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"}, + {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"}, + {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"}, + {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"}, + {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"}, + {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"}, + {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"}, + {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"}, + {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"}, ] requests = [ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, ] -scandir = [ - {file = "scandir-1.10.0-cp27-cp27m-win32.whl", hash = "sha256:92c85ac42f41ffdc35b6da57ed991575bdbe69db895507af88b9f499b701c188"}, - {file = "scandir-1.10.0-cp27-cp27m-win_amd64.whl", hash = "sha256:cb925555f43060a1745d0a321cca94bcea927c50114b623d73179189a4e100ac"}, - {file = "scandir-1.10.0-cp34-cp34m-win32.whl", hash = "sha256:2c712840c2e2ee8dfaf36034080108d30060d759c7b73a01a52251cc8989f11f"}, - {file = "scandir-1.10.0-cp34-cp34m-win_amd64.whl", hash = "sha256:2586c94e907d99617887daed6c1d102b5ca28f1085f90446554abf1faf73123e"}, - {file = "scandir-1.10.0-cp35-cp35m-win32.whl", hash = "sha256:2b8e3888b11abb2217a32af0766bc06b65cc4a928d8727828ee68af5a967fa6f"}, - {file = "scandir-1.10.0-cp35-cp35m-win_amd64.whl", hash = "sha256:8c5922863e44ffc00c5c693190648daa6d15e7c1207ed02d6f46a8dcc2869d32"}, - {file = "scandir-1.10.0-cp36-cp36m-win32.whl", hash = "sha256:2ae41f43797ca0c11591c0c35f2f5875fa99f8797cb1a1fd440497ec0ae4b022"}, - {file = "scandir-1.10.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7d2d7a06a252764061a020407b997dd036f7bd6a175a5ba2b345f0a357f0b3f4"}, - {file = "scandir-1.10.0-cp37-cp37m-win32.whl", hash = "sha256:67f15b6f83e6507fdc6fca22fedf6ef8b334b399ca27c6b568cbfaa82a364173"}, - {file = "scandir-1.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b24086f2375c4a094a6b51e78b4cf7ca16c721dcee2eddd7aa6494b42d6d519d"}, - {file = "scandir-1.10.0.tar.gz", hash = "sha256:4d4631f6062e658e9007ab3149a9b914f3548cb38bfb021c64f39a025ce578ae"}, -] -singledispatch = [ - {file = "singledispatch-3.4.0.3-py2.py3-none-any.whl", hash = "sha256:833b46966687b3de7f438c761ac475213e53b306740f1abfaa86e1d1aae56aa8"}, - {file = "singledispatch-3.4.0.3.tar.gz", hash = "sha256:5b06af87df13818d14f08a028e42f566640aef80805c3b50c5056b086e3c2b9c"}, -] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, @@ -899,9 +793,42 @@ tox = [ {file = "tox-3.21.4-py2.py3-none-any.whl", hash = "sha256:65d0e90ceb816638a50d64f4b47b11da767b284c0addda2294cb3cd69bd72425"}, {file = "tox-3.21.4.tar.gz", hash = "sha256:cf7fef81a3a2434df4d7af2a6d1bf606d2970220addfbe7dea2615bd4bb2c252"}, ] -typing = [ - {file = "typing-3.7.4.3-py2-none-any.whl", hash = "sha256:283d868f5071ab9ad873e5e52268d611e851c870a2ba354193026f2dfb29d8b5"}, - {file = "typing-3.7.4.3.tar.gz", hash = "sha256:1187fb9c82fd670d10aa07bbb6cfcfe4bdda42d6fab8d5134f04e8c4d0b71cc9"}, +typed-ast = [ + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"}, + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487"}, + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412"}, + {file = "typed_ast-1.4.2-cp35-cp35m-win32.whl", hash = "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400"}, + {file = "typed_ast-1.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606"}, + {file = "typed_ast-1.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a"}, + {file = "typed_ast-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151"}, + {file = "typed_ast-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3"}, + {file = "typed_ast-1.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37"}, + {file = "typed_ast-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd"}, + {file = "typed_ast-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496"}, + {file = "typed_ast-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787"}, + {file = "typed_ast-1.4.2-cp38-cp38-win32.whl", hash = "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2"}, + {file = "typed_ast-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937"}, + {file = "typed_ast-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d"}, + {file = "typed_ast-1.4.2-cp39-cp39-win32.whl", hash = "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b"}, + {file = "typed_ast-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440"}, + {file = "typed_ast-1.4.2.tar.gz", hash = "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a"}, +] +typing-extensions = [ + {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, + {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, + {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, ] urllib3 = [ {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, @@ -920,6 +847,6 @@ wcwidth = [ {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] zipp = [ - {file = "zipp-1.2.0-py2.py3-none-any.whl", hash = "sha256:e0d9e63797e483a30d27e09fffd308c59a700d365ec34e93cc100844168bf921"}, - {file = "zipp-1.2.0.tar.gz", hash = "sha256:c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1"}, + {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, + {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, ] diff --git a/poetry/core/__init__.py b/poetry/core/__init__.py index eed5b1895..faa2c8436 100644 --- a/poetry/core/__init__.py +++ b/poetry/core/__init__.py @@ -1,13 +1,9 @@ import sys +from pathlib import Path -try: - from pathlib import Path -except ImportError: - # noinspection PyUnresolvedReferences - from pathlib2 import Path -__version__ = "1.0.2" +__version__ = "1.1.0a0" __vendor_site__ = (Path(__file__).parent / "_vendor").as_posix() diff --git a/poetry/core/factory.py b/poetry/core/factory.py index be157f00a..e670bdad9 100644 --- a/poetry/core/factory.py +++ b/poetry/core/factory.py @@ -3,6 +3,8 @@ import logging +from pathlib import Path +from typing import TYPE_CHECKING from typing import Any from typing import Dict from typing import List @@ -10,14 +12,10 @@ from typing import Union from warnings import warn -from .json import validate_object -from .packages.dependency import Dependency -from .packages.project_package import ProjectPackage -from .poetry import Poetry -from .pyproject import PyProjectTOML -from .spdx import license_by_id -from .utils._compat import Path +if TYPE_CHECKING: + from .packages.types import DependencyTypes + from .poetry import Poetry logger = logging.getLogger(__name__) @@ -28,8 +26,14 @@ class Factory(object): """ def create_poetry( - self, cwd=None, with_dev=True - ): # type: (Optional[Path], bool) -> Poetry + self, cwd: Optional[Path] = None, with_dev: bool = True + ) -> "Poetry": + from .packages.dependency import Dependency + from .packages.project_package import ProjectPackage + from .poetry import Poetry + from .pyproject.toml import PyProjectTOML + from .spdx.helpers import license_by_id + poetry_file = self.locate(cwd) local_config = PyProjectTOML(path=poetry_file).poetry_config @@ -164,12 +168,13 @@ def create_poetry( @classmethod def create_dependency( cls, - name, # type: str - constraint, # type: Union[str, Dict[str, Any]] - category="main", # type: str - root_dir=None, # type: Optional[Path] - ): # type: (...) -> Dependency + name: str, + constraint: Union[str, Dict[str, Any]], + category: str = "main", + root_dir: Optional[Path] = None, + ) -> "DependencyTypes": from .packages.constraints import parse_constraint as parse_generic_constraint + from .packages.dependency import Dependency from .packages.directory_dependency import DirectoryDependency from .packages.file_dependency import FileDependency from .packages.url_dependency import URLDependency @@ -303,12 +308,12 @@ def create_dependency( return dependency @classmethod - def validate( - cls, config, strict=False - ): # type: (dict, bool) -> Dict[str, List[str]] + def validate(cls, config: dict, strict: bool = False) -> Dict[str, List[str]]: """ Checks the validity of a configuration """ + from .json import validate_object + result = {"errors": [], "warnings": []} # Schema validation errors validation_errors = validate_object(config, "poetry-schema") @@ -355,7 +360,7 @@ def validate( return result @classmethod - def locate(cls, cwd): # type: (Path) -> Path + def locate(cls, cwd: Path) -> Path: candidates = [Path(cwd)] candidates.extend(Path(cwd).parents) diff --git a/poetry/core/json/__init__.py b/poetry/core/json/__init__.py index 83ecab77c..b01bfe95e 100644 --- a/poetry/core/json/__init__.py +++ b/poetry/core/json/__init__.py @@ -4,8 +4,6 @@ from io import open from typing import List -from jsonschema import Draft7Validator - SCHEMA_DIR = os.path.join(os.path.dirname(__file__), "schemas") @@ -15,7 +13,7 @@ class ValidationError(ValueError): pass -def validate_object(obj, schema_name): # type: (dict, str) -> List[str] +def validate_object(obj: dict, schema_name: str) -> List[str]: schema = os.path.join(SCHEMA_DIR, "{}.json".format(schema_name)) if not os.path.exists(schema): @@ -24,6 +22,8 @@ def validate_object(obj, schema_name): # type: (dict, str) -> List[str] with open(schema, encoding="utf-8") as f: schema = json.loads(f.read()) + from jsonschema import Draft7Validator + validator = Draft7Validator(schema) validation_errors = sorted(validator.iter_errors(obj), key=lambda e: e.path) diff --git a/poetry/core/masonry/__init__.py b/poetry/core/masonry/__init__.py index ddd3a14f1..943204ad1 100644 --- a/poetry/core/masonry/__init__.py +++ b/poetry/core/masonry/__init__.py @@ -6,5 +6,3 @@ `flit `__ and adapted to work with the poetry codebase, so kudos to them for showing the way. """ - -from .builder import Builder diff --git a/poetry/core/masonry/api.py b/poetry/core/masonry/api.py index 30a9185b3..e0a9be858 100644 --- a/poetry/core/masonry/api.py +++ b/poetry/core/masonry/api.py @@ -3,14 +3,13 @@ """ import logging +from pathlib import Path from typing import Any from typing import Dict from typing import List from typing import Optional from poetry.core.factory import Factory -from poetry.core.utils._compat import Path -from poetry.core.utils._compat import unicode from .builders.sdist import SdistBuilder from .builders.wheel import WheelBuilder @@ -20,8 +19,8 @@ def get_requires_for_build_wheel( - config_settings=None, -): # type: (Optional[Dict[str, Any]]) -> List[str] + config_settings: Optional[Dict[str, Any]] = None, +) -> List[str]: """ Returns an additional list of requirements for building, as PEP508 strings, above and beyond those specified in the pyproject.toml file. @@ -38,8 +37,8 @@ def get_requires_for_build_wheel( def prepare_metadata_for_build_wheel( - metadata_directory, config_settings=None -): # type: (str, Optional[Dict[str, Any]]) -> str + metadata_directory: str, config_settings: Optional[Dict[str, Any]] = None +) -> str: poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False) builder = WheelBuilder(poetry) @@ -60,20 +59,22 @@ def prepare_metadata_for_build_wheel( def build_wheel( - wheel_directory, config_settings=None, metadata_directory=None -): # type: (str, Optional[Dict[str, Any]], Optional[str]) -> str + wheel_directory: str, + config_settings: Optional[Dict[str, Any]] = None, + metadata_directory: Optional[str] = None, +) -> str: """Builds a wheel, places it in wheel_directory""" poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False) - return unicode(WheelBuilder.make_in(poetry, Path(wheel_directory))) + return WheelBuilder.make_in(poetry, Path(wheel_directory)) def build_sdist( - sdist_directory, config_settings=None -): # type: (str, Optional[Dict[str, Any]]) -> str + sdist_directory: str, config_settings: Optional[Dict[str, Any]] = None +) -> str: """Builds an sdist, places it in sdist_directory""" poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False) path = SdistBuilder(poetry).build(Path(sdist_directory)) - return unicode(path.name) + return path.name diff --git a/poetry/core/masonry/builder.py b/poetry/core/masonry/builder.py index 21e3bb154..d896e7bf7 100644 --- a/poetry/core/masonry/builder.py +++ b/poetry/core/masonry/builder.py @@ -1,33 +1,30 @@ +from pathlib import Path from typing import TYPE_CHECKING from typing import Optional from typing import Union -from poetry.core.utils._compat import Path - -from .builders.sdist import SdistBuilder -from .builders.wheel import WheelBuilder - if TYPE_CHECKING: from poetry.core.poetry import Poetry # noqa class Builder: - _FORMATS = { - "sdist": SdistBuilder, - "wheel": WheelBuilder, - } + def __init__(self, poetry: "Poetry") -> None: + from .builders.sdist import SdistBuilder + from .builders.wheel import WheelBuilder - def __init__(self, poetry): # type: ("Poetry") -> None self._poetry = poetry - def build( - self, fmt, executable=None - ): # type: (str, Optional[Union[str, Path]]) -> None - if fmt in self._FORMATS: - builders = [self._FORMATS[fmt]] + self._formats = { + "sdist": SdistBuilder, + "wheel": WheelBuilder, + } + + def build(self, fmt: str, executable: Optional[Union[str, Path]] = None) -> None: + if fmt in self._formats: + builders = [self._formats[fmt]] elif fmt == "all": - builders = self._FORMATS.values() + builders = self._formats.values() else: raise ValueError("Invalid format: {}".format(fmt)) diff --git a/poetry/core/masonry/builders/__init__.py b/poetry/core/masonry/builders/__init__.py index 20d725b77..e69de29bb 100644 --- a/poetry/core/masonry/builders/__init__.py +++ b/poetry/core/masonry/builders/__init__.py @@ -1,2 +0,0 @@ -from .sdist import SdistBuilder -from .wheel import WheelBuilder diff --git a/poetry/core/masonry/builders/builder.py b/poetry/core/masonry/builders/builder.py index f0f7e8a54..04f12252c 100644 --- a/poetry/core/masonry/builders/builder.py +++ b/poetry/core/masonry/builders/builder.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import logging import re import shutil @@ -7,22 +6,16 @@ from collections import defaultdict from contextlib import contextmanager +from pathlib import Path from typing import TYPE_CHECKING from typing import Any +from typing import ContextManager from typing import Dict from typing import List from typing import Optional from typing import Set from typing import Union -from poetry.core.utils._compat import Path -from poetry.core.utils._compat import to_str -from poetry.core.vcs import get_vcs - -from ..metadata import Metadata -from ..utils.module import Module -from ..utils.package_include import PackageInclude - if TYPE_CHECKING: from poetry.core.poetry import Poetry # noqa @@ -41,15 +34,21 @@ class Builder(object): - format = None # type: Optional[str] + format: Optional[str] = None def __init__( - self, poetry, ignore_packages_formats=False, executable=None - ): # type: ("Poetry", bool, Optional[Union[Path, str]]) -> None + self, + poetry: "Poetry", + ignore_packages_formats: bool = False, + executable: Optional[Union[Path, str]] = None, + ) -> None: + from poetry.core.masonry.metadata import Metadata + from poetry.core.masonry.utils.module import Module + self._poetry = poetry self._package = poetry.package self._path = poetry.file.parent - self._excluded_files = None # type: Optional[Set[str]] + self._excluded_files: Optional[Set[str]] = None self._executable = Path(executable or sys.executable) packages = [] @@ -92,14 +91,16 @@ def __init__( self._meta = Metadata.from_package(self._package) @property - def executable(self): # type: () -> Path + def executable(self) -> Path: return self._executable - def build(self): # type: () -> None + def build(self) -> None: raise NotImplementedError() - def find_excluded_files(self): # type: () -> Set[str] + def find_excluded_files(self) -> Set[str]: if self._excluded_files is None: + from poetry.core.vcs import get_vcs + # Checking VCS vcs = get_vcs(self._path) if not vcs: @@ -134,7 +135,7 @@ def find_excluded_files(self): # type: () -> Set[str] return self._excluded_files - def is_excluded(self, filepath): # type: (Union[str, Path]) -> bool + def is_excluded(self, filepath: Union[str, Path]) -> bool: exclude_path = Path(filepath) while True: @@ -148,12 +149,12 @@ def is_excluded(self, filepath): # type: (Union[str, Path]) -> bool return False - def find_files_to_add( - self, exclude_build=True - ): # type: (bool) -> Set[BuildIncludeFile] + def find_files_to_add(self, exclude_build: bool = True) -> Set["BuildIncludeFile"]: """ Finds all files to add to the tarball """ + from poetry.core.masonry.utils.package_include import PackageInclude + to_add = set() for include in self._module.includes: @@ -219,11 +220,11 @@ def find_files_to_add( return to_add - def get_metadata_content(self): # type: () -> str + def get_metadata_content(self) -> str: content = METADATA_BASE.format( name=self._meta.name, version=self._meta.version, - summary=to_str(self._meta.summary), + summary=str(self._meta.summary), ) # Optional fields @@ -237,18 +238,16 @@ def get_metadata_content(self): # type: () -> str content += "Keywords: {}\n".format(self._meta.keywords) if self._meta.author: - content += "Author: {}\n".format(to_str(self._meta.author)) + content += "Author: {}\n".format(str(self._meta.author)) if self._meta.author_email: - content += "Author-email: {}\n".format(to_str(self._meta.author_email)) + content += "Author-email: {}\n".format(str(self._meta.author_email)) if self._meta.maintainer: - content += "Maintainer: {}\n".format(to_str(self._meta.maintainer)) + content += "Maintainer: {}\n".format(str(self._meta.maintainer)) if self._meta.maintainer_email: - content += "Maintainer-email: {}\n".format( - to_str(self._meta.maintainer_email) - ) + content += "Maintainer-email: {}\n".format(str(self._meta.maintainer_email)) if self._meta.requires_python: content += "Requires-Python: {}\n".format(self._meta.requires_python) @@ -263,7 +262,7 @@ def get_metadata_content(self): # type: () -> str content += "Requires-Dist: {}\n".format(dep) for url in sorted(self._meta.project_urls, key=lambda u: u[0]): - content += "Project-URL: {}\n".format(to_str(url)) + content += "Project-URL: {}\n".format(str(url)) if self._meta.description_content_type: content += "Description-Content-Type: {}\n".format( @@ -271,11 +270,11 @@ def get_metadata_content(self): # type: () -> str ) if self._meta.description is not None: - content += "\n" + to_str(self._meta.description) + "\n" + content += "\n" + str(self._meta.description) + "\n" return content - def convert_entry_points(self): # type: () -> Dict[str, List[str]] + def convert_entry_points(self) -> Dict[str, List[str]]: result = defaultdict(list) # Scripts -> Entry points @@ -299,7 +298,7 @@ def convert_entry_points(self): # type: () -> Dict[str, List[str]] return dict(result) @classmethod - def convert_author(cls, author): # type: (str) -> Dict[str, str] + def convert_author(cls, author: str) -> Dict[str, str]: m = AUTHOR_REGEX.match(author) name = m.group("name") @@ -309,7 +308,7 @@ def convert_author(cls, author): # type: (str) -> Dict[str, str] @classmethod @contextmanager - def temporary_directory(cls, *args, **kwargs): # type: (*Any, **Any) -> None + def temporary_directory(cls, *args: Any, **kwargs: Any) -> ContextManager[str]: try: from tempfile import TemporaryDirectory @@ -326,9 +325,9 @@ def temporary_directory(cls, *args, **kwargs): # type: (*Any, **Any) -> None class BuildIncludeFile: def __init__( self, - path, # type: Union[Path, str] - project_root, # type: Union[Path, str] - source_root=None, # type: Optional[Union[Path, str]] + path: Union[Path, str], + project_root: Union[Path, str], + source_root: Optional[Union[Path, str]] = None, ): """ :param project_root: the full path of the project's root @@ -351,24 +350,26 @@ def __init__( # python 3.5 are dropped, until we can use resolve(strict=False). pass - def __eq__(self, other): # type: (Union[BuildIncludeFile, Path]) -> bool + def __eq__(self, other: Union["BuildIncludeFile", Path]) -> bool: if hasattr(other, "path"): return self.path == other.path + return self.path == other - def __ne__(self, other): # type: (Union[BuildIncludeFile, Path]) -> bool + def __ne__(self, other: Union["BuildIncludeFile", Path]) -> bool: return not self.__eq__(other) - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash(self.path) - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return str(self.path) - def relative_to_project_root(self): # type: () -> Path + def relative_to_project_root(self) -> Path: return self.path.relative_to(self.project_root) - def relative_to_source_root(self): # type: () -> Path + def relative_to_source_root(self) -> Path: if self.source_root is not None: return self.path.relative_to(self.source_root) + return self.path diff --git a/poetry/core/masonry/builders/sdist.py b/poetry/core/masonry/builders/sdist.py index 5e1f8b9c5..add8dcd27 100644 --- a/poetry/core/masonry/builders/sdist.py +++ b/poetry/core/masonry/builders/sdist.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import logging import os import re @@ -10,29 +9,24 @@ from copy import copy from gzip import GzipFile from io import BytesIO +from pathlib import Path from posixpath import join as pjoin from pprint import pformat from tarfile import TarInfo from typing import TYPE_CHECKING +from typing import ContextManager from typing import Dict -from typing import Iterator from typing import List from typing import Optional from typing import Set from typing import Tuple -from poetry.core.utils._compat import Path -from poetry.core.utils._compat import decode -from poetry.core.utils._compat import encode -from poetry.core.utils._compat import to_str - -from ..utils.helpers import normalize_file_permissions -from ..utils.package_include import PackageInclude from .builder import Builder from .builder import BuildIncludeFile if TYPE_CHECKING: + from poetry.core.masonry.utils.package_include import PackageInclude # noqa from poetry.core.packages import Dependency # noqa from poetry.core.packages import ProjectPackage # noqa @@ -65,7 +59,7 @@ class SdistBuilder(Builder): format = "sdist" - def build(self, target_dir=None): # type: (Optional[Path]) -> Path + def build(self, target_dir: Optional[Path] = None) -> Path: logger.info("Building sdist") if target_dir is None: target_dir = self._path / "dist" @@ -119,7 +113,9 @@ def build(self, target_dir=None): # type: (Optional[Path]) -> Path logger.info("Built {}".format(target.name)) return target - def build_setup(self): # type: () -> bytes + def build_setup(self) -> bytes: + from poetry.core.masonry.utils.package_include import PackageInclude + before, extra, after = [], [], [] package_dir = {} @@ -196,25 +192,23 @@ def build_setup(self): # type: () -> bytes extra.append("'python_requires': {!r},".format(python_requires)) - return encode( - SETUP.format( - before="\n".join(before), - name=to_str(self._meta.name), - version=to_str(self._meta.version), - description=to_str(self._meta.summary), - long_description=to_str(self._meta.description), - author=to_str(self._meta.author), - author_email=to_str(self._meta.author_email), - maintainer=to_str(self._meta.maintainer), - maintainer_email=to_str(self._meta.maintainer_email), - url=to_str(self._meta.home_page), - extra="\n ".join(extra), - after="\n".join(after), - ) - ) + return SETUP.format( + before="\n".join(before), + name=str(self._meta.name), + version=str(self._meta.version), + description=str(self._meta.summary), + long_description=str(self._meta.description), + author=str(self._meta.author), + author_email=str(self._meta.author_email), + maintainer=str(self._meta.maintainer), + maintainer_email=str(self._meta.maintainer_email), + url=str(self._meta.home_page), + extra="\n ".join(extra), + after="\n".join(after), + ).encode() @contextmanager - def setup_py(self): # type: () -> Iterator[Path] + def setup_py(self) -> ContextManager[Path]: setup = self._path / "setup.py" has_setup = setup.exists() @@ -222,19 +216,17 @@ def setup_py(self): # type: () -> Iterator[Path] logger.warning("A setup.py file already exists. Using it.") else: with setup.open("w", encoding="utf-8") as f: - f.write(decode(self.build_setup())) + f.write(self.build_setup().decode()) yield setup if not has_setup: setup.unlink() - def build_pkg_info(self): # type: () -> bytes - return encode(self.get_metadata_content()) + def build_pkg_info(self) -> bytes: + return self.get_metadata_content().encode() - def find_packages( - self, include - ): # type: (PackageInclude) -> Tuple[str, List[str], dict] + def find_packages(self, include: "PackageInclude") -> Tuple[str, List[str], dict]: """ Discover subpackages and data. @@ -254,7 +246,7 @@ def find_packages( packages = [pkg_name] subpkg_paths = set() - def find_nearest_pkg(rel_path): # type: (str) -> Tuple[str, str] + def find_nearest_pkg(rel_path: str) -> Tuple[str, str]: parts = rel_path.split(os.sep) for i in reversed(range(1, len(parts))): ancestor = "/".join(parts[:i]) @@ -313,9 +305,7 @@ def find_nearest_pkg(rel_path): # type: (str) -> Tuple[str, str] return pkgdir, sorted(packages), pkg_data - def find_files_to_add( - self, exclude_build=False - ): # type: (bool) -> Set[BuildIncludeFile] + def find_files_to_add(self, exclude_build: bool = False) -> Set[BuildIncludeFile]: to_add = super(SdistBuilder, self).find_files_to_add(exclude_build) # add any additional files, starting with all LICENSE files @@ -342,8 +332,8 @@ def find_files_to_add( @classmethod def convert_dependencies( - cls, package, dependencies - ): # type: ("ProjectPackage", List["Dependency"]) -> Tuple[List[str], Dict[str, List[str]]] + cls, package: "ProjectPackage", dependencies: List["Dependency"] + ) -> Tuple[List[str], Dict[str, List[str]]]: main = [] extras = defaultdict(list) req_regex = re.compile(r"^(.+) \((.+)\)$") @@ -353,9 +343,7 @@ def convert_dependencies( for extra_name, reqs in package.extras.items(): for req in reqs: if req.name == dependency.name: - requirement = to_str( - dependency.to_pep_508(with_extras=False) - ) + requirement = dependency.to_pep_508(with_extras=False) if ";" in requirement: requirement, conditions = requirement.split(";") @@ -379,7 +367,7 @@ def convert_dependencies( extras[extra_name].append(requirement) continue - requirement = to_str(dependency.to_pep_508()) + requirement = dependency.to_pep_508() if ";" in requirement: requirement, conditions = requirement.split(";") @@ -400,7 +388,7 @@ def convert_dependencies( return main, dict(extras) @classmethod - def clean_tarinfo(cls, tar_info): # type: (TarInfo) -> TarInfo + def clean_tarinfo(cls, tar_info: TarInfo) -> TarInfo: """ Clean metadata from a TarInfo object to make it more reproducible. @@ -409,6 +397,8 @@ def clean_tarinfo(cls, tar_info): # type: (TarInfo) -> TarInfo - Normalise permissions to 644 or 755 - Set mtime if not None """ + from poetry.core.masonry.utils.helpers import normalize_file_permissions + ti = copy(tar_info) ti.uid = 0 ti.gid = 0 diff --git a/poetry/core/masonry/builders/wheel.py b/poetry/core/masonry/builders/wheel.py index a2bb0d078..1f4c4af87 100644 --- a/poetry/core/masonry/builders/wheel.py +++ b/poetry/core/masonry/builders/wheel.py @@ -12,10 +12,10 @@ import zipfile from base64 import urlsafe_b64encode -from io import BytesIO from io import StringIO +from pathlib import Path from typing import TYPE_CHECKING -from typing import Iterator +from typing import ContextManager from typing import Optional from typing import TextIO from typing import Union @@ -23,10 +23,7 @@ from packaging.tags import sys_tags from poetry.core import __version__ -from poetry.core.semver import parse_constraint -from poetry.core.utils._compat import PY2 -from poetry.core.utils._compat import Path -from poetry.core.utils._compat import decode +from poetry.core.semver.helpers import parse_constraint from ..utils.helpers import escape_name from ..utils.helpers import escape_version @@ -52,20 +49,28 @@ class WheelBuilder(Builder): format = "wheel" def __init__( - self, poetry, target_dir=None, original=None, executable=None - ): # type: ("Poetry", Optional[Path], Optional[Path], Optional[str]) -> None + self, + poetry: "Poetry", + target_dir: Optional[Path] = None, + original: Optional[Path] = None, + executable: Optional[str] = None, + ) -> None: super(WheelBuilder, self).__init__(poetry, executable=executable) self._records = [] self._original_path = self._path self._target_dir = target_dir or (self._poetry.file.parent / "dist") if original: - self._original_path = original.file.parent + self._original_path = original.parent @classmethod def make_in( - cls, poetry, directory=None, original=None, executable=None - ): # type: ("Poetry", Path, Path, str) -> str + cls, + poetry: "Poetry", + directory: Optional[Path] = None, + original: Optional[Path] = None, + executable: Optional[str] = None, + ) -> str: wb = WheelBuilder( poetry, target_dir=directory, original=original, executable=executable ) @@ -74,11 +79,11 @@ def make_in( return wb.wheel_filename @classmethod - def make(cls, poetry, executable=None): # type: ("Poetry", Optional[str]) -> None + def make(cls, poetry: "Poetry", executable: Optional[str] = None) -> None: """Build a wheel in the dist/ directory, and optionally upload it.""" cls.make_in(poetry, executable=executable) - def build(self): # type: () -> None + def build(self) -> None: logger.info("Building wheel") dist_dir = self._target_dir @@ -112,7 +117,7 @@ def build(self): # type: () -> None logger.info("Built {}".format(self.wheel_filename)) - def _build(self, wheel): # type: (zipfile.ZipFile) -> None + def _build(self, wheel: zipfile.ZipFile) -> None: if self._package.build_script: if not self._poetry.package.build_should_generate_setup(): # Since we have a build script but no setup.py generation is required, @@ -159,7 +164,7 @@ def _build(self, wheel): # type: (zipfile.ZipFile) -> None self._add_file(wheel, pkg, rel_path) - def _run_build_command(self, setup): # type: (Path) -> None + def _run_build_command(self, setup: Path) -> None: subprocess.check_call( [ self.executable.as_posix(), @@ -170,11 +175,11 @@ def _run_build_command(self, setup): # type: (Path) -> None ] ) - def _run_build_script(self, build_script): # type: (str) -> None + def _run_build_script(self, build_script: str) -> None: logger.debug("Executing build script: {}".format(build_script)) subprocess.check_call([self.executable.as_posix(), build_script]) - def _copy_module(self, wheel): # type: (zipfile.ZipFile) -> None + def _copy_module(self, wheel: zipfile.ZipFile) -> None: to_add = self.find_files_to_add() # Walk the files and compress them, @@ -182,7 +187,7 @@ def _copy_module(self, wheel): # type: (zipfile.ZipFile) -> None for file in sorted(list(to_add), key=lambda x: x.path): self._add_file(wheel, file.path, file.relative_to_source_root()) - def _write_metadata(self, wheel): # type: (zipfile.ZipFile) -> None + def _write_metadata(self, wheel: zipfile.ZipFile) -> None: if ( "scripts" in self._poetry.local_config or "plugins" in self._poetry.local_config @@ -210,10 +215,10 @@ def _write_metadata(self, wheel): # type: (zipfile.ZipFile) -> None with self._write_to_zip(wheel, self.dist_info + "/METADATA") as f: self._write_metadata_file(f) - def _write_record(self, wheel): # type: (zipfile.ZipFile) -> None + def _write_record(self, wheel: zipfile.ZipFile) -> None: # Write a record of the files in the wheel with self._write_to_zip(wheel, self.dist_info + "/RECORD") as f: - record = StringIO() if not PY2 else BytesIO() + record = StringIO() csv_writer = csv.writer( record, @@ -227,33 +232,33 @@ def _write_record(self, wheel): # type: (zipfile.ZipFile) -> None # RECORD itself is recorded with no hash or size csv_writer.writerow((self.dist_info + "/RECORD", "", "")) - f.write(decode(record.getvalue())) + f.write(record.getvalue()) @property - def dist_info(self): # type: () -> str + def dist_info(self) -> str: return self.dist_info_name(self._package.name, self._meta.version) @property - def wheel_filename(self): # type: () -> str + def wheel_filename(self) -> str: return "{}-{}-{}.whl".format( escape_name(self._package.pretty_name), escape_version(self._meta.version), self.tag, ) - def supports_python2(self): # type: () -> bool + def supports_python2(self) -> bool: return self._package.python_constraint.allows_any( parse_constraint(">=2.0.0 <3.0.0") ) - def dist_info_name(self, distribution, version): # type: (str, str) -> str + def dist_info_name(self, distribution: str, version: str) -> str: escaped_name = escape_name(distribution) escaped_version = escape_version(version) return "{}-{}.dist-info".format(escaped_name, escaped_version) @property - def tag(self): # type: () -> str + def tag(self) -> str: if self._package.build_script: tag = next(sys_tags()) tag = (tag.interpreter, tag.abi, tag.platform) @@ -269,8 +274,11 @@ def tag(self): # type: () -> str return "-".join(tag) def _add_file( - self, wheel, full_path, rel_path - ): # type: (zipfile.ZipFile, Union[Path, str], Union[Path, str]) -> None + self, + wheel: zipfile.ZipFile, + full_path: Union[Path, str], + rel_path: Union[Path, str], + ) -> None: full_path, rel_path = str(full_path), str(rel_path) if os.sep != "/": # We always want to have /-separated paths in the zip file and in @@ -305,8 +313,8 @@ def _add_file( @contextlib.contextmanager def _write_to_zip( - self, wheel, rel_path - ): # type: (zipfile.ZipFile, str) -> Iterator[StringIO] + self, wheel: zipfile.ZipFile, rel_path: str + ) -> ContextManager[StringIO]: sio = StringIO() yield sio @@ -323,7 +331,7 @@ def _write_to_zip( wheel.writestr(zi, b, compress_type=zipfile.ZIP_DEFLATED) self._records.append((rel_path, hash_digest, len(b))) - def _write_entry_points(self, fp): # type: (TextIO) -> None + def _write_entry_points(self, fp: TextIO) -> None: """ Write entry_points.txt. """ @@ -336,7 +344,7 @@ def _write_entry_points(self, fp): # type: (TextIO) -> None fp.write("\n") - def _write_wheel_file(self, fp): # type: (TextIO) -> None + def _write_wheel_file(self, fp: TextIO) -> None: fp.write( wheel_file_template.format( version=__version__, @@ -345,8 +353,8 @@ def _write_wheel_file(self, fp): # type: (TextIO) -> None ) ) - def _write_metadata_file(self, fp): # type: (TextIO) -> None + def _write_metadata_file(self, fp: TextIO) -> None: """ Write out metadata in the 2.x format (email like) """ - fp.write(decode(self.get_metadata_content())) + fp.write(self.get_metadata_content()) diff --git a/poetry/core/masonry/metadata.py b/poetry/core/masonry/metadata.py index 48bf6033c..ebdf93f43 100644 --- a/poetry/core/masonry/metadata.py +++ b/poetry/core/masonry/metadata.py @@ -1,9 +1,5 @@ from typing import TYPE_CHECKING -from poetry.core.utils.helpers import canonicalize_name -from poetry.core.utils.helpers import normalize_version -from poetry.core.version.helpers import format_python_constraint - if TYPE_CHECKING: from poetry.core.packages import Package # noqa @@ -45,7 +41,11 @@ class Metadata: provides_extra = [] @classmethod - def from_package(cls, package): # type: ("Package") -> Metadata + def from_package(cls, package: "Package") -> "Metadata": + from poetry.core.utils.helpers import canonicalize_name + from poetry.core.utils.helpers import normalize_version + from poetry.core.version.helpers import format_python_constraint + meta = cls() meta.name = canonicalize_name(package.name) diff --git a/poetry/core/masonry/utils/helpers.py b/poetry/core/masonry/utils/helpers.py index 3a515f425..5dfe68b51 100644 --- a/poetry/core/masonry/utils/helpers.py +++ b/poetry/core/masonry/utils/helpers.py @@ -1,7 +1,7 @@ import re -def normalize_file_permissions(st_mode): # type: (int) -> int +def normalize_file_permissions(st_mode: int) -> int: """ Normalizes the permission bits in the st_mode field from stat to 644/755 @@ -17,7 +17,7 @@ def normalize_file_permissions(st_mode): # type: (int) -> int return new_mode -def escape_version(version): # type: (str) -> str +def escape_version(version: str) -> str: """ Escaped version in wheel filename. Doesn't exactly follow the escaping specification in :pep:`427#escaping-and-unicode` @@ -26,6 +26,6 @@ def escape_version(version): # type: (str) -> str return re.sub(r"[^\w\d.+]+", "_", version, flags=re.UNICODE) -def escape_name(name): # type: (str) -> str +def escape_name(name: str) -> str: """Escaped wheel name as specified in :pep:`427#escaping-and-unicode`.""" return re.sub(r"[^\w\d.]+", "_", name, flags=re.UNICODE) diff --git a/poetry/core/masonry/utils/include.py b/poetry/core/masonry/utils/include.py index f8f2b8f11..7547dcea9 100644 --- a/poetry/core/masonry/utils/include.py +++ b/poetry/core/masonry/utils/include.py @@ -1,8 +1,7 @@ +from pathlib import Path from typing import List from typing import Optional -from poetry.core.utils._compat import Path - class Include(object): """ @@ -19,32 +18,30 @@ class Include(object): """ def __init__( - self, base, include, formats=None - ): # type: (Path, str, Optional[List[str]]) -> None + self, base: Path, include: str, formats: Optional[List[str]] = None + ) -> None: self._base = base self._include = str(include) self._formats = formats - self._elements = sorted( - list(self._base.glob(str(self._include))) - ) # type: List[Path] + self._elements: List[Path] = sorted(list(self._base.glob(str(self._include)))) @property - def base(self): # type: () -> Path + def base(self) -> Path: return self._base @property - def elements(self): # type: () -> List[Path] + def elements(self) -> List[Path]: return self._elements @property - def formats(self): # type: () -> Optional[List[str]] + def formats(self) -> Optional[List[str]]: return self._formats - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return len(self._elements) == 0 - def refresh(self): # type: () -> Include + def refresh(self) -> "Include": self._elements = sorted(list(self._base.glob(self._include))) return self diff --git a/poetry/core/masonry/utils/module.py b/poetry/core/masonry/utils/module.py index 2e2a7539f..13d7dfd38 100644 --- a/poetry/core/masonry/utils/module.py +++ b/poetry/core/masonry/utils/module.py @@ -1,14 +1,9 @@ +from pathlib import Path from typing import Any from typing import Dict from typing import List from typing import Optional -from poetry.core.utils._compat import Path -from poetry.core.utils.helpers import module_name - -from .include import Include -from .package_include import PackageInclude - class ModuleOrPackageNotFound(ValueError): @@ -17,8 +12,17 @@ class ModuleOrPackageNotFound(ValueError): class Module: def __init__( - self, name, directory=".", packages=None, includes=None - ): # type: (str, str, Optional[List[Dict[str, Any]]], Optional[List[Dict[str, Any]]]) -> None + self, + name: str, + directory: str = ".", + packages: Optional[List[Dict[str, Any]]] = None, + includes: Optional[List[Dict[str, Any]]] = None, + ) -> None: + from poetry.core.utils.helpers import module_name + + from .include import Include + from .package_include import PackageInclude + self._name = module_name(name) self._in_src = False self._is_package = False @@ -84,26 +88,26 @@ def __init__( ) @property - def name(self): # type: () -> str + def name(self) -> str: return self._name @property - def path(self): # type: () -> Path + def path(self) -> Path: return self._path @property - def file(self): # type: () -> Path + def file(self) -> Path: if self._is_package: return self._path / "__init__.py" else: return self._path @property - def includes(self): # type: () -> List + def includes(self) -> List: return self._includes - def is_package(self): # type: () -> bool + def is_package(self) -> bool: return self._is_package - def is_in_src(self): # type: () -> bool + def is_in_src(self) -> bool: return self._in_src diff --git a/poetry/core/masonry/utils/package_include.py b/poetry/core/masonry/utils/package_include.py index d409f5d33..addbea59d 100644 --- a/poetry/core/masonry/utils/package_include.py +++ b/poetry/core/masonry/utils/package_include.py @@ -1,15 +1,18 @@ +from pathlib import Path from typing import List from typing import Optional -from poetry.core.utils._compat import Path - from .include import Include class PackageInclude(Include): def __init__( - self, base, include, formats=None, source=None - ): # type: (Path, str, Optional[List[str]], Optional[str]) -> None + self, + base: Path, + include: str, + formats: Optional[List[str]] = None, + source: Optional[str] = None, + ) -> None: self._package = None self._is_package = False self._is_module = False @@ -22,25 +25,25 @@ def __init__( self.check_elements() @property - def package(self): # type: () -> str + def package(self) -> str: return self._package @property - def source(self): # type: () -> Optional[str] + def source(self) -> Optional[str]: return self._source - def is_package(self): # type: () -> bool + def is_package(self) -> bool: return self._is_package - def is_module(self): # type: () -> bool + def is_module(self) -> bool: return self._is_module - def refresh(self): # type: () -> PackageInclude + def refresh(self) -> "PackageInclude": super(PackageInclude, self).refresh() return self.check_elements() - def is_stub_only(self): # type: () -> bool + def is_stub_only(self) -> bool: # returns `True` if this a PEP 561 stub-only package, # see [PEP 561](https://www.python.org/dev/peps/pep-0561/#stub-only-packages) return self.package.endswith("-stubs") and all( @@ -50,12 +53,12 @@ def is_stub_only(self): # type: () -> bool if el.is_file() ) - def has_modules(self): # type: () -> bool + def has_modules(self) -> bool: # Packages no longer need an __init__.py in python3, but there must # at least be one .py file for it to be considered a package return any(element.suffix == ".py" for element in self.elements) - def check_elements(self): # type: () -> PackageInclude + def check_elements(self) -> "PackageInclude": if not self._elements: raise ValueError( "{} does not contain any element".format(self._base / self._include) @@ -74,7 +77,7 @@ def check_elements(self): # type: () -> PackageInclude if root.is_dir(): # If it's a directory, we include everything inside it self._package = root.name - self._elements = sorted(list(root.glob("**/*"))) # type: List[Path] + self._elements: List[Path] = sorted(list(root.glob("**/*"))) if not self.is_stub_only() and not self.has_modules(): raise ValueError("{} is not a package.".format(root.name)) diff --git a/poetry/core/packages/__init__.py b/poetry/core/packages/__init__.py index f0819ebdc..e69de29bb 100644 --- a/poetry/core/packages/__init__.py +++ b/poetry/core/packages/__init__.py @@ -1,224 +0,0 @@ -import os -import re - -from typing import List -from typing import Optional -from typing import Union - -from poetry.core.semver import Version -from poetry.core.semver import parse_constraint -from poetry.core.utils._compat import Path -from poetry.core.utils.patterns import wheel_file_re -from poetry.core.version.requirements import Requirement - -from .dependency import Dependency -from .directory_dependency import DirectoryDependency -from .file_dependency import FileDependency -from .package import Package -from .project_package import ProjectPackage -from .url_dependency import URLDependency -from .utils.link import Link -from .utils.utils import convert_markers -from .utils.utils import group_markers -from .utils.utils import is_archive_file -from .utils.utils import is_installable_dir -from .utils.utils import is_url -from .utils.utils import path_to_url -from .utils.utils import strip_extras -from .utils.utils import url_to_path -from .vcs_dependency import VCSDependency - - -def _make_file_or_dir_dep( - name, # type: str - path, # type: Path - base=None, # type: Optional[Path] - extras=None, # type: Optional[List[str]] -): # type: (...) -> Optional[Union[FileDependency, DirectoryDependency]] - """ - Helper function to create a file or directoru dependency with the given arguments. If - path is not a file or directory that exists, `None` is returned. - """ - _path = path - if not path.is_absolute() and base: - # a base path was specified, so we should respect that - _path = Path(base) / path - - if _path.is_file(): - return FileDependency(name, path, base=base, extras=extras) - elif _path.is_dir(): - return DirectoryDependency(name, path, base=base, extras=extras) - - return None - - -def dependency_from_pep_508( - name, relative_to=None -): # type: (str, Optional[Path]) -> Dependency - """ - Resolve a PEP-508 requirement string to a `Dependency` instance. If a `relative_to` - path is specified, this is used as the base directory if the identified dependency is - of file or directory type. - """ - from poetry.core.vcs.git import ParsedUrl - - # Removing comments - parts = name.split("#", 1) - name = parts[0].strip() - if len(parts) > 1: - rest = parts[1] - if " ;" in rest: - name += " ;" + rest.split(" ;", 1)[1] - - req = Requirement(name) - - if req.marker: - markers = convert_markers(req.marker) - else: - markers = {} - - name = req.name - path = os.path.normpath(os.path.abspath(name)) - link = None - - if is_url(name): - link = Link(name) - elif req.url: - link = Link(req.url) - else: - p, extras = strip_extras(path) - if os.path.isdir(p) and (os.path.sep in name or name.startswith(".")): - - if not is_installable_dir(p): - raise ValueError( - "Directory {!r} is not installable. File 'setup.py' " - "not found.".format(name) - ) - link = Link(path_to_url(p)) - elif is_archive_file(p): - link = Link(path_to_url(p)) - - # it's a local file, dir, or url - if link: - is_file_uri = link.scheme == "file" - is_relative_uri = is_file_uri and re.search(r"\.\./", link.url) - - # Handle relative file URLs - if is_file_uri and is_relative_uri: - path = Path(link.path) - if relative_to: - path = relative_to / path - link = Link(path_to_url(path)) - - # wheel file - version = None - if link.is_wheel: - m = wheel_file_re.match(link.filename) - if not m: - raise ValueError("Invalid wheel name: {}".format(link.filename)) - name = m.group("name") - version = m.group("ver") - - name = req.name or link.egg_fragment - dep = None - - if link.scheme.startswith("git+"): - url = ParsedUrl.parse(link.url) - dep = VCSDependency(name, "git", url.url, rev=url.rev, extras=req.extras) - elif link.scheme == "git": - dep = VCSDependency( - name, "git", link.url_without_fragment, extras=req.extras - ) - elif link.scheme in ["http", "https"]: - dep = URLDependency(name, link.url) - elif is_file_uri: - # handle RFC 8089 references - path = url_to_path(req.url) - dep = _make_file_or_dir_dep( - name=name, path=path, base=relative_to, extras=req.extras - ) - else: - try: - # this is a local path not using the file URI scheme - dep = _make_file_or_dir_dep( - name=name, path=Path(req.url), base=relative_to, extras=req.extras, - ) - except ValueError: - pass - - if dep is None: - dep = Dependency(name, version or "*", extras=req.extras) - - if version: - dep._constraint = parse_constraint(version) - else: - if req.pretty_constraint: - constraint = req.constraint - else: - constraint = "*" - - dep = Dependency(name, constraint, extras=req.extras) - - if "extra" in markers: - # If we have extras, the dependency is optional - dep.deactivate() - - for or_ in markers["extra"]: - for _, extra in or_: - dep.in_extras.append(extra) - - if "python_version" in markers: - ors = [] - for or_ in markers["python_version"]: - ands = [] - for op, version in or_: - # Expand python version - if op == "==" and "*" not in version: - version = "~" + version - op = "" - elif op == "!=": - version += ".*" - elif op in ("<=", ">"): - parsed_version = Version.parse(version) - if parsed_version.precision == 1: - if op == "<=": - op = "<" - version = parsed_version.next_major.text - elif op == ">": - op = ">=" - version = parsed_version.next_major.text - elif parsed_version.precision == 2: - if op == "<=": - op = "<" - version = parsed_version.next_minor.text - elif op == ">": - op = ">=" - version = parsed_version.next_minor.text - elif op in ("in", "not in"): - versions = [] - for v in re.split("[ ,]+", version): - split = v.split(".") - if len(split) in [1, 2]: - split.append("*") - op_ = "" if op == "in" else "!=" - else: - op_ = "==" if op == "in" else "!=" - - versions.append(op_ + ".".join(split)) - - glue = " || " if op == "in" else ", " - if versions: - ands.append(glue.join(versions)) - - continue - - ands.append("{}{}".format(op, version)) - - ors.append(" ".join(ands)) - - dep.python_versions = " || ".join(ors) - - if req.marker: - dep.marker = req.marker - - return dep diff --git a/poetry/core/packages/constraints/__init__.py b/poetry/core/packages/constraints/__init__.py index 33acb85a7..c23cdcd23 100644 --- a/poetry/core/packages/constraints/__init__.py +++ b/poetry/core/packages/constraints/__init__.py @@ -17,8 +17,8 @@ def parse_constraint( - constraints, -): # type: (str) -> Union[AnyConstraint, UnionConstraint, Constraint] + constraints: str, +) -> Union[AnyConstraint, UnionConstraint, Constraint]: if constraints == "*": return AnyConstraint() @@ -51,7 +51,7 @@ def parse_constraint( return UnionConstraint(*or_groups) -def parse_single_constraint(constraint): # type: (str) -> Constraint +def parse_single_constraint(constraint: str) -> Constraint: # Basic comparator m = BASIC_CONSTRAINT.match(constraint) if m: diff --git a/poetry/core/packages/constraints/any_constraint.py b/poetry/core/packages/constraints/any_constraint.py index 88945a119..ad514ecee 100644 --- a/poetry/core/packages/constraints/any_constraint.py +++ b/poetry/core/packages/constraints/any_constraint.py @@ -9,35 +9,35 @@ class AnyConstraint(BaseConstraint): - def allows(self, other): # type: ("ConstraintTypes") -> bool + def allows(self, other: "ConstraintTypes") -> bool: return True - def allows_all(self, other): # type: ("ConstraintTypes") -> bool + def allows_all(self, other: "ConstraintTypes") -> bool: return True - def allows_any(self, other): # type: ("ConstraintTypes") -> bool + def allows_any(self, other: "ConstraintTypes") -> bool: return True - def difference(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" + def difference(self, other: "ConstraintTypes") -> "ConstraintTypes": if other.is_any(): return EmptyConstraint() return other - def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" + def intersect(self, other: "ConstraintTypes") -> "ConstraintTypes": return other - def union(self, other): # type: ("ConstraintTypes") -> AnyConstraint + def union(self, other: "ConstraintTypes") -> "AnyConstraint": return AnyConstraint() - def is_any(self): # type: () -> bool + def is_any(self) -> bool: return True - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return False - def __str__(self): # type: () -> str + def __str__(self) -> str: return "*" - def __eq__(self, other): # type: ("ConstraintTypes") -> bool + def __eq__(self, other: "ConstraintTypes") -> bool: return other.is_any() diff --git a/poetry/core/packages/constraints/base_constraint.py b/poetry/core/packages/constraints/base_constraint.py index 8cb95c138..a428b2e4b 100644 --- a/poetry/core/packages/constraints/base_constraint.py +++ b/poetry/core/packages/constraints/base_constraint.py @@ -6,29 +6,29 @@ class BaseConstraint(object): - def allows_all(self, other): # type: ("ConstraintTypes") -> bool + def allows_all(self, other: "ConstraintTypes") -> bool: raise NotImplementedError() - def allows_any(self, other): # type: ("ConstraintTypes") -> bool + def allows_any(self, other: "ConstraintTypes") -> bool: raise NotImplementedError() - def difference(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" + def difference(self, other: "ConstraintTypes") -> "ConstraintTypes": raise NotImplementedError() - def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" + def intersect(self, other: "ConstraintTypes") -> "ConstraintTypes": raise NotImplementedError() - def union(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" + def union(self, other: "ConstraintTypes") -> "ConstraintTypes": raise NotImplementedError() - def is_any(self): # type: () -> bool + def is_any(self) -> bool: return False - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return False - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "<{} {}>".format(self.__class__.__name__, str(self)) - def __eq__(self, other): # type: ("ConstraintTypes") -> bool + def __eq__(self, other: "ConstraintTypes") -> bool: raise NotImplementedError() diff --git a/poetry/core/packages/constraints/constraint.py b/poetry/core/packages/constraints/constraint.py index 1ebe915f5..ee40ff00a 100644 --- a/poetry/core/packages/constraints/constraint.py +++ b/poetry/core/packages/constraints/constraint.py @@ -21,7 +21,7 @@ class Constraint(BaseConstraint): _trans_op_int = {OP_EQ: "==", OP_NE: "!="} - def __init__(self, version, operator="=="): # type: (str, str) -> None + def __init__(self, version: str, operator: str = "==") -> None: if operator == "=": operator = "==" @@ -30,14 +30,14 @@ def __init__(self, version, operator="=="): # type: (str, str) -> None self._op = self._trans_op_str[operator] @property - def version(self): # type: () -> str + def version(self) -> str: return self._version @property - def operator(self): # type: () -> str + def operator(self) -> str: return self._operator - def allows(self, other): # type: ("ConstraintTypes") -> bool + def allows(self, other: "ConstraintTypes") -> bool: is_equal_op = self._operator == "==" is_non_equal_op = self._operator == "!=" is_other_equal_op = other.operator == "==" @@ -58,13 +58,13 @@ def allows(self, other): # type: ("ConstraintTypes") -> bool return False - def allows_all(self, other): # type: ("ConstraintTypes") -> bool + def allows_all(self, other: "ConstraintTypes") -> bool: if not isinstance(other, Constraint): return other.is_empty() return other == self - def allows_any(self, other): # type: ("ConstraintTypes") -> bool + def allows_any(self, other: "ConstraintTypes") -> bool: if isinstance(other, Constraint): is_non_equal_op = self._operator == "!=" is_other_non_equal_op = other.operator == "!=" @@ -75,14 +75,14 @@ def allows_any(self, other): # type: ("ConstraintTypes") -> bool return other.allows(self) def difference( - self, other - ): # type: ("ConstraintTypes") -> Union[Constraint, "EmptyConstraint"] + self, other: "ConstraintTypes" + ) -> Union["Constraint", "EmptyConstraint"]: if other.allows(self): return EmptyConstraint() return self - def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" + def intersect(self, other: "ConstraintTypes") -> "ConstraintTypes": from .multi_constraint import MultiConstraint if isinstance(other, Constraint): @@ -102,7 +102,7 @@ def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" return other.intersect(self) - def union(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" + def union(self, other: "ConstraintTypes") -> "ConstraintTypes": if isinstance(other, Constraint): from .union_constraint import UnionConstraint @@ -110,22 +110,22 @@ def union(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" return other.union(self) - def is_any(self): # type: () -> bool + def is_any(self) -> bool: return False - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return False - def __eq__(self, other): # type: (Any) -> bool + def __eq__(self, other: Any) -> bool: if not isinstance(other, Constraint): return NotImplemented return (self.version, self.operator) == (other.version, other.operator) - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash((self._operator, self._version)) - def __str__(self): # type: () -> str + def __str__(self) -> str: return "{}{}".format( self._operator if self._operator != "==" else "", self._version ) diff --git a/poetry/core/packages/constraints/empty_constraint.py b/poetry/core/packages/constraints/empty_constraint.py index eb29e4ad3..d8a789ded 100644 --- a/poetry/core/packages/constraints/empty_constraint.py +++ b/poetry/core/packages/constraints/empty_constraint.py @@ -11,26 +11,26 @@ class EmptyConstraint(BaseConstraint): pretty_string = None - def matches(self, _): # type: ("ConstraintTypes") -> bool + def matches(self, _: "ConstraintTypes") -> bool: return True - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return True - def allows_all(self, other): # type: ("ConstraintTypes") -> bool + def allows_all(self, other: "ConstraintTypes") -> bool: return True - def allows_any(self, other): # type: ("ConstraintTypes") -> bool + def allows_any(self, other: "ConstraintTypes") -> bool: return True - def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" + def intersect(self, other: "ConstraintTypes") -> "ConstraintTypes": return other - def difference(self, other): # type: ("ConstraintTypes") -> None + def difference(self, other: "ConstraintTypes") -> None: return - def __eq__(self, other): # type: ("ConstraintTypes") -> bool + def __eq__(self, other: "ConstraintTypes") -> bool: return other.is_empty() - def __str__(self): # type: () -> str + def __str__(self) -> str: return "" diff --git a/poetry/core/packages/constraints/multi_constraint.py b/poetry/core/packages/constraints/multi_constraint.py index 33fc9e4a5..3d54ece62 100644 --- a/poetry/core/packages/constraints/multi_constraint.py +++ b/poetry/core/packages/constraints/multi_constraint.py @@ -11,7 +11,7 @@ class MultiConstraint(BaseConstraint): - def __init__(self, *constraints): # type: (*Constraint) -> None + def __init__(self, *constraints: Constraint) -> None: if any(c.operator == "==" for c in constraints): raise ValueError( "A multi-constraint can only be comprised of negative constraints" @@ -20,17 +20,17 @@ def __init__(self, *constraints): # type: (*Constraint) -> None self._constraints = constraints @property - def constraints(self): # type: () -> Tuple[Constraint] + def constraints(self) -> Tuple[Constraint]: return self._constraints - def allows(self, other): # type: ("ConstraintTypes") -> bool + def allows(self, other: "ConstraintTypes") -> bool: for constraint in self._constraints: if not constraint.allows(other): return False return True - def allows_all(self, other): # type: ("ConstraintTypes") -> bool + def allows_all(self, other: "ConstraintTypes") -> bool: if other.is_any(): return False @@ -53,7 +53,7 @@ def allows_all(self, other): # type: ("ConstraintTypes") -> bool return their_constraint is None - def allows_any(self, other): # type: ("ConstraintTypes") -> bool + def allows_any(self, other: "ConstraintTypes") -> bool: if other.is_any(): return True @@ -71,7 +71,7 @@ def allows_any(self, other): # type: ("ConstraintTypes") -> bool return False - def intersect(self, other): # type: (Constraint) -> MultiConstraint + def intersect(self, other: Constraint) -> "MultiConstraint": if isinstance(other, Constraint): constraints = self._constraints if other not in constraints: @@ -84,7 +84,7 @@ def intersect(self, other): # type: (Constraint) -> MultiConstraint return MultiConstraint(*constraints) - def __eq__(self, other): # type: (Any) -> bool + def __eq__(self, other: Any) -> bool: if not isinstance(other, MultiConstraint): return False @@ -92,7 +92,7 @@ def __eq__(self, other): # type: (Any) -> bool self._constraints, key=lambda c: (c.operator, c.version) ) == sorted(other.constraints, key=lambda c: (c.operator, c.version)) - def __str__(self): # type: () -> str + def __str__(self) -> str: constraints = [] for constraint in self._constraints: constraints.append(str(constraint)) diff --git a/poetry/core/packages/constraints/union_constraint.py b/poetry/core/packages/constraints/union_constraint.py index ec0330c2d..d527ab48f 100644 --- a/poetry/core/packages/constraints/union_constraint.py +++ b/poetry/core/packages/constraints/union_constraint.py @@ -13,23 +13,23 @@ class UnionConstraint(BaseConstraint): - def __init__(self, *constraints): # type: (*Constraint) -> None + def __init__(self, *constraints: Constraint) -> None: self._constraints = constraints @property - def constraints(self): # type: () -> Tuple[Constraint] + def constraints(self) -> Tuple[Constraint]: return self._constraints def allows( - self, other - ): # type: (Union[Constraint, MultiConstraint, UnionConstraint]) -> bool + self, other: Union[Constraint, MultiConstraint, "UnionConstraint"] + ) -> bool: for constraint in self._constraints: if constraint.allows(other): return True return False - def allows_any(self, other): # type: ("ConstraintTypes") -> bool + def allows_any(self, other: "ConstraintTypes") -> bool: if other.is_empty(): return False @@ -48,7 +48,7 @@ def allows_any(self, other): # type: ("ConstraintTypes") -> bool return False - def allows_all(self, other): # type: ("ConstraintTypes") -> bool + def allows_all(self, other: "ConstraintTypes") -> bool: if other.is_any(): return False @@ -73,7 +73,7 @@ def allows_all(self, other): # type: ("ConstraintTypes") -> bool return their_constraint is None - def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" + def intersect(self, other: "ConstraintTypes") -> "ConstraintTypes": if other.is_any(): return self @@ -99,7 +99,7 @@ def intersect(self, other): # type: ("ConstraintTypes") -> "ConstraintTypes" return UnionConstraint(*new_constraints) - def union(self, other): # type: (Constraint) -> UnionConstraint + def union(self, other: Constraint) -> "UnionConstraint": if isinstance(other, Constraint): constraints = self._constraints if other not in self._constraints: @@ -107,7 +107,7 @@ def union(self, other): # type: (Constraint) -> UnionConstraint return UnionConstraint(*constraints) - def __eq__(self, other): # type: ("ConstraintTypes") -> bool + def __eq__(self, other: "ConstraintTypes") -> bool: if not isinstance(other, UnionConstraint): return False @@ -116,7 +116,7 @@ def __eq__(self, other): # type: ("ConstraintTypes") -> bool self._constraints, key=lambda c: (c.operator, c.version) ) == sorted(other.constraints, key=lambda c: (c.operator, c.version)) - def __str__(self): # type: () -> str + def __str__(self) -> str: constraints = [] for constraint in self._constraints: constraints.append(str(constraint)) diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py index d678947e2..75a89da16 100644 --- a/poetry/core/packages/dependency.py +++ b/poetry/core/packages/dependency.py @@ -1,3 +1,7 @@ +import os +import re + +from pathlib import Path from typing import TYPE_CHECKING from typing import Any from typing import FrozenSet @@ -5,45 +9,41 @@ from typing import Optional from typing import Union -import poetry.core.packages - -from poetry.core.semver import Version -from poetry.core.semver import VersionConstraint -from poetry.core.semver import VersionRange -from poetry.core.semver import VersionUnion -from poetry.core.semver import parse_constraint -from poetry.core.version.markers import AnyMarker +from poetry.core.semver.helpers import parse_constraint from poetry.core.version.markers import parse_marker from .constraints import parse_constraint as parse_generic_constraint -from .constraints.constraint import Constraint -from .constraints.multi_constraint import MultiConstraint -from .constraints.union_constraint import UnionConstraint from .specification import PackageSpecification -from .utils.utils import convert_markers if TYPE_CHECKING: + from poetry.core.semver.helpers import VersionTypes # noqa from poetry.core.version.markers import BaseMarker # noqa - from poetry.core.version.markers import VersionTypes # noqa from .constraints import BaseConstraint # noqa + from .directory_dependency import DirectoryDependency # noqa + from .file_dependency import FileDependency # noqa + from .package import Package + from .types import DependencyTypes class Dependency(PackageSpecification): def __init__( self, - name, # type: str - constraint, # type: Union[str, VersionConstraint] - optional=False, # type: bool - category="main", # type: str - allows_prereleases=False, # type: bool - extras=None, # type: Union[List[str], FrozenSet[str]] - source_type=None, # type: Optional[str] - source_url=None, # type: Optional[str] - source_reference=None, # type: Optional[str] - source_resolved_reference=None, # type: Optional[str] + name: str, + constraint: Union[str, "VersionTypes"], + optional: bool = False, + category: str = "main", + allows_prereleases: bool = False, + extras: Union[List[str], FrozenSet[str]] = None, + source_type: Optional[str] = None, + source_url: Optional[str] = None, + source_reference: Optional[str] = None, + source_resolved_reference: Optional[str] = None, ): + from poetry.core.semver.version_range import VersionRange + from poetry.core.version.markers import AnyMarker + super(Dependency, self).__init__( name, source_type=source_type, @@ -83,14 +83,16 @@ def __init__( self.source_name = None @property - def name(self): # type: () -> str + def name(self) -> str: return self._name @property - def constraint(self): # type: () -> "VersionTypes" + def constraint(self) -> "VersionTypes": return self._constraint - def set_constraint(self, constraint): # type: (Union[str, "VersionTypes"]) -> None + def set_constraint(self, constraint: Union[str, "VersionTypes"]) -> None: + from poetry.core.semver.version_constraint import VersionConstraint + try: if not isinstance(constraint, VersionConstraint): self._constraint = parse_constraint(constraint) @@ -100,23 +102,23 @@ def set_constraint(self, constraint): # type: (Union[str, "VersionTypes"]) -> N self._constraint = parse_constraint("*") @property - def pretty_constraint(self): # type: () -> str + def pretty_constraint(self) -> str: return self._pretty_constraint @property - def pretty_name(self): # type: () -> str + def pretty_name(self) -> str: return self._pretty_name @property - def category(self): # type: () -> str + def category(self) -> str: return self._category @property - def python_versions(self): # type: () -> str + def python_versions(self) -> str: return self._python_versions @python_versions.setter - def python_versions(self, value): # type: (str) -> None + def python_versions(self, value: str) -> None: self._python_versions = value self._python_constraint = parse_constraint(value) if not self._python_constraint.is_any(): @@ -129,49 +131,52 @@ def python_versions(self, value): # type: (str) -> None ) @property - def transitive_python_versions(self): # type: () -> str + def transitive_python_versions(self) -> str: if self._transitive_python_versions is None: return self._python_versions return self._transitive_python_versions @transitive_python_versions.setter - def transitive_python_versions(self, value): # type: (str) -> None + def transitive_python_versions(self, value: str) -> None: self._transitive_python_versions = value self._transitive_python_constraint = parse_constraint(value) @property - def transitive_marker(self): # type: () -> "BaseMarker" + def transitive_marker(self) -> "BaseMarker": if self._transitive_marker is None: return self.marker return self._transitive_marker @transitive_marker.setter - def transitive_marker(self, value): # type: ("BaseMarker") -> None + def transitive_marker(self, value: "BaseMarker") -> None: self._transitive_marker = value @property - def python_constraint(self): # type: () -> "VersionTypes" + def python_constraint(self) -> "VersionTypes": return self._python_constraint @property - def transitive_python_constraint(self): # type: () -> "VersionTypes" + def transitive_python_constraint(self) -> "VersionTypes": if self._transitive_python_constraint is None: return self._python_constraint return self._transitive_python_constraint @property - def extras(self): # type: () -> FrozenSet[str] + def extras(self) -> FrozenSet[str]: return self._extras @property - def in_extras(self): # type: () -> list + def in_extras(self) -> List[str]: return self._in_extras @property - def base_pep_508_name(self): # type: () -> str + def base_pep_508_name(self) -> str: + from poetry.core.semver.version import Version + from poetry.core.semver.version_union import VersionUnion + requirement = self.pretty_name if self.extras: @@ -192,28 +197,28 @@ def base_pep_508_name(self): # type: () -> str return requirement - def allows_prereleases(self): # type: () -> bool + def allows_prereleases(self) -> bool: return self._allows_prereleases - def is_optional(self): # type: () -> bool + def is_optional(self) -> bool: return self._optional - def is_activated(self): # type: () -> bool + def is_activated(self) -> bool: return self._activated - def is_vcs(self): # type: () -> bool + def is_vcs(self) -> bool: return False - def is_file(self): # type: () -> bool + def is_file(self) -> bool: return False - def is_directory(self): # type: () -> bool + def is_directory(self) -> bool: return False - def is_url(self): # type: () -> bool + def is_url(self) -> bool: return False - def accepts(self, package): # type: (poetry.core.packages.Package) -> bool + def accepts(self, package: "Package") -> bool: """ Determines if the given package matches this dependency. """ @@ -223,7 +228,9 @@ def accepts(self, package): # type: (poetry.core.packages.Package) -> bool and (not package.is_prerelease() or self.allows_prereleases()) ) - def to_pep_508(self, with_extras=True): # type: (bool) -> str + def to_pep_508(self, with_extras: bool = True) -> str: + from .utils.utils import convert_markers + requirement = self.base_pep_508_name markers = [] @@ -267,8 +274,15 @@ def to_pep_508(self, with_extras=True): # type: (bool) -> str return requirement def _create_nested_marker( - self, name, constraint - ): # type: (str, Union["BaseConstraint", Version, VersionConstraint]) -> str + self, name: str, constraint: Union["BaseConstraint", "VersionTypes"] + ) -> str: + from poetry.core.semver.version import Version + from poetry.core.semver.version_union import VersionUnion + + from .constraints.constraint import Constraint + from .constraints.multi_constraint import MultiConstraint + from .constraints.union_constraint import UnionConstraint + if isinstance(constraint, (MultiConstraint, UnionConstraint)): parts = [] for c in constraint.constraints: @@ -350,13 +364,13 @@ def _create_nested_marker( return marker - def activate(self): # type: () -> None + def activate(self) -> None: """ Set the dependency as mandatory. """ self._activated = True - def deactivate(self): # type: () -> None + def deactivate(self) -> None: """ Set the dependency as optional. """ @@ -365,9 +379,7 @@ def deactivate(self): # type: () -> None self._activated = False - def with_constraint( - self, constraint - ): # type: (Union[str, VersionConstraint]) -> Dependency + def with_constraint(self, constraint: Union[str, "VersionTypes"]) -> "Dependency": new = Dependency( self.pretty_name, constraint, @@ -388,7 +400,198 @@ def with_constraint( return new - def __eq__(self, other): # type: (Any) -> bool + @classmethod + def create_from_pep_508( + cls, name: str, relative_to: Optional[Path] = None + ) -> "DependencyTypes": + """ + Resolve a PEP-508 requirement string to a `Dependency` instance. If a `relative_to` + path is specified, this is used as the base directory if the identified dependency is + of file or directory type. + """ + from poetry.core.semver.version import Version + from poetry.core.utils.patterns import wheel_file_re + from poetry.core.vcs.git import ParsedUrl + from poetry.core.version.requirements import Requirement + + from .url_dependency import URLDependency + from .utils.link import Link + from .utils.utils import convert_markers + from .utils.utils import is_archive_file + from .utils.utils import is_installable_dir + from .utils.utils import is_url + from .utils.utils import path_to_url + from .utils.utils import strip_extras + from .utils.utils import url_to_path + from .vcs_dependency import VCSDependency + + # Removing comments + parts = name.split("#", 1) + name = parts[0].strip() + if len(parts) > 1: + rest = parts[1] + if " ;" in rest: + name += " ;" + rest.split(" ;", 1)[1] + + req = Requirement(name) + + if req.marker: + markers = convert_markers(req.marker) + else: + markers = {} + + name = req.name + path = os.path.normpath(os.path.abspath(name)) + link = None + + if is_url(name): + link = Link(name) + elif req.url: + link = Link(req.url) + else: + p, extras = strip_extras(path) + if os.path.isdir(p) and (os.path.sep in name or name.startswith(".")): + + if not is_installable_dir(p): + raise ValueError( + "Directory {!r} is not installable. File 'setup.py' " + "not found.".format(name) + ) + link = Link(path_to_url(p)) + elif is_archive_file(p): + link = Link(path_to_url(p)) + + # it's a local file, dir, or url + if link: + is_file_uri = link.scheme == "file" + is_relative_uri = is_file_uri and re.search(r"\.\./", link.url) + + # Handle relative file URLs + if is_file_uri and is_relative_uri: + path = Path(link.path) + if relative_to: + path = relative_to / path + link = Link(path_to_url(path)) + + # wheel file + version = None + if link.is_wheel: + m = wheel_file_re.match(link.filename) + if not m: + raise ValueError("Invalid wheel name: {}".format(link.filename)) + name = m.group("name") + version = m.group("ver") + + name = req.name or link.egg_fragment + dep = None + + if link.scheme.startswith("git+"): + url = ParsedUrl.parse(link.url) + dep = VCSDependency( + name, "git", url.url, rev=url.rev, extras=req.extras + ) + elif link.scheme == "git": + dep = VCSDependency( + name, "git", link.url_without_fragment, extras=req.extras + ) + elif link.scheme in ["http", "https"]: + dep = URLDependency(name, link.url) + elif is_file_uri: + # handle RFC 8089 references + path = url_to_path(req.url) + dep = _make_file_or_dir_dep( + name=name, path=path, base=relative_to, extras=req.extras + ) + else: + try: + # this is a local path not using the file URI scheme + dep = _make_file_or_dir_dep( + name=name, + path=Path(req.url), + base=relative_to, + extras=req.extras, + ) + except ValueError: + pass + + if dep is None: + dep = Dependency(name, version or "*", extras=req.extras) + + if version: + dep._constraint = parse_constraint(version) + else: + if req.pretty_constraint: + constraint = req.constraint + else: + constraint = "*" + + dep = Dependency(name, constraint, extras=req.extras) + + if "extra" in markers: + # If we have extras, the dependency is optional + dep.deactivate() + + for or_ in markers["extra"]: + for _, extra in or_: + dep.in_extras.append(extra) + + if "python_version" in markers: + ors = [] + for or_ in markers["python_version"]: + ands = [] + for op, version in or_: + # Expand python version + if op == "==" and "*" not in version: + version = "~" + version + op = "" + elif op == "!=": + version += ".*" + elif op in ("<=", ">"): + parsed_version = Version.parse(version) + if parsed_version.precision == 1: + if op == "<=": + op = "<" + version = parsed_version.next_major.text + elif op == ">": + op = ">=" + version = parsed_version.next_major.text + elif parsed_version.precision == 2: + if op == "<=": + op = "<" + version = parsed_version.next_minor.text + elif op == ">": + op = ">=" + version = parsed_version.next_minor.text + elif op in ("in", "not in"): + versions = [] + for v in re.split("[ ,]+", version): + split = v.split(".") + if len(split) in [1, 2]: + split.append("*") + op_ = "" if op == "in" else "!=" + else: + op_ = "==" if op == "in" else "!=" + + versions.append(op_ + ".".join(split)) + + glue = " || " if op == "in" else ", " + if versions: + ands.append(glue.join(versions)) + + continue + + ands.append("{}{}".format(op, version)) + + ors.append(" ".join(ands)) + + dep.python_versions = " || ".join(ors) + + if req.marker: + dep.marker = req.marker + + return dep + + def __eq__(self, other: Any) -> bool: if not isinstance(other, Dependency): return NotImplemented @@ -398,20 +601,46 @@ def __eq__(self, other): # type: (Any) -> bool and self._extras == other.extras ) - def __ne__(self, other): # type: (Any) -> bool + def __ne__(self, other: Any) -> bool: return not self == other - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return ( super(Dependency, self).__hash__() ^ hash(self._constraint) ^ hash(self._extras) ) - def __str__(self): # type: () -> str + def __str__(self) -> str: if self.is_root: return self._pretty_name return self.base_pep_508_name - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "<{} {}>".format(self.__class__.__name__, str(self)) + + +def _make_file_or_dir_dep( + name: str, + path: Path, + base: Optional[Path] = None, + extras: Optional[List[str]] = None, +) -> Optional[Union["FileDependency", "DirectoryDependency"]]: + """ + Helper function to create a file or directoru dependency with the given arguments. If + path is not a file or directory that exists, `None` is returned. + """ + from .directory_dependency import DirectoryDependency + from .file_dependency import FileDependency + + _path = path + if not path.is_absolute() and base: + # a base path was specified, so we should respect that + _path = Path(base) / path + + if _path.is_file(): + return FileDependency(name, path, base=base, extras=extras) + elif _path.is_dir(): + return DirectoryDependency(name, path, base=base, extras=extras) + + return None diff --git a/poetry/core/packages/directory_dependency.py b/poetry/core/packages/directory_dependency.py index ac1193904..d0cbf2523 100644 --- a/poetry/core/packages/directory_dependency.py +++ b/poetry/core/packages/directory_dependency.py @@ -1,11 +1,10 @@ +from pathlib import Path from typing import TYPE_CHECKING from typing import FrozenSet from typing import List +from typing import Optional from typing import Union -from poetry.core.pyproject import PyProjectTOML -from poetry.core.utils._compat import Path - if TYPE_CHECKING: from .constraints import BaseConstraint # noqa @@ -16,14 +15,16 @@ class DirectoryDependency(Dependency): def __init__( self, - name, # type: str - path, # type: Path - category="main", # type: str - optional=False, # type: bool - base=None, # type: Path - develop=False, # type: bool - extras=None, # type: Union[List[str], FrozenSet[str]] - ): + name: str, + path: Path, + category: str = "main", + optional: bool = False, + base: Optional[Path] = None, + develop: bool = False, + extras: Optional[Union[List[str], FrozenSet[str]]] = None, + ) -> None: + from poetry.core.pyproject.toml import PyProjectTOML + self._path = path self._base = base or Path.cwd() self._full_path = path @@ -68,30 +69,28 @@ def __init__( ) @property - def path(self): # type: () -> Path + def path(self) -> Path: return self._path @property - def full_path(self): # type: () -> Path + def full_path(self) -> Path: return self._full_path @property - def base(self): # type: () -> Path + def base(self) -> Path: return self._base @property - def develop(self): # type: () -> bool + def develop(self) -> bool: return self._develop - def supports_poetry(self): # type: () -> bool + def supports_poetry(self) -> bool: return self._supports_poetry - def is_directory(self): # type: () -> bool + def is_directory(self) -> bool: return True - def with_constraint( - self, constraint - ): # type: ("BaseConstraint") -> DirectoryDependency + def with_constraint(self, constraint: "BaseConstraint") -> "DirectoryDependency": new = DirectoryDependency( self.pretty_name, path=self.path, @@ -116,7 +115,7 @@ def with_constraint( return new @property - def base_pep_508_name(self): # type: () -> str + def base_pep_508_name(self) -> str: requirement = self.pretty_name if self.extras: @@ -126,7 +125,7 @@ def base_pep_508_name(self): # type: () -> str return requirement - def __str__(self): # type: () -> str + def __str__(self) -> str: if self.is_root: return self._pretty_name @@ -134,5 +133,5 @@ def __str__(self): # type: () -> str self._pretty_name, self._pretty_constraint, self._path.as_posix() ) - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash((self._name, self._full_path.as_posix())) diff --git a/poetry/core/packages/file_dependency.py b/poetry/core/packages/file_dependency.py index 939520acc..6485a5c5e 100644 --- a/poetry/core/packages/file_dependency.py +++ b/poetry/core/packages/file_dependency.py @@ -1,13 +1,14 @@ import hashlib import io +from pathlib import Path from typing import TYPE_CHECKING from typing import FrozenSet from typing import List +from typing import Optional from typing import Union from poetry.core.packages.utils.utils import path_to_url -from poetry.core.utils._compat import Path from .dependency import Dependency @@ -19,13 +20,13 @@ class FileDependency(Dependency): def __init__( self, - name, # type: str - path, # type: Path - category="main", # type: str - optional=False, # type: bool - base=None, # type: Path - extras=None, # type: Union[List[str], FrozenSet[str]] - ): + name: str, + path: Path, + category: str = "main", + optional: bool = False, + base: Optional[Path] = None, + extras: Optional[Union[List[str], FrozenSet[str]]] = None, + ) -> None: self._path = path self._base = base or Path.cwd() self._full_path = path @@ -54,21 +55,21 @@ def __init__( ) @property - def base(self): # type: () -> Path + def base(self) -> Path: return self._base @property - def path(self): # type: () -> Path + def path(self) -> Path: return self._path @property - def full_path(self): # type: () -> Path + def full_path(self) -> Path: return self._full_path - def is_file(self): # type: () -> bool + def is_file(self) -> bool: return True - def hash(self): # type: () -> str + def hash(self) -> str: h = hashlib.sha256() with self._full_path.open("rb") as fp: for content in iter(lambda: fp.read(io.DEFAULT_BUFFER_SIZE), b""): @@ -76,7 +77,7 @@ def hash(self): # type: () -> str return h.hexdigest() - def with_constraint(self, constraint): # type: ("BaseConstraint") -> FileDependency + def with_constraint(self, constraint: "BaseConstraint") -> "FileDependency": new = FileDependency( self.pretty_name, path=self.path, @@ -100,7 +101,7 @@ def with_constraint(self, constraint): # type: ("BaseConstraint") -> FileDepend return new @property - def base_pep_508_name(self): # type: () -> str + def base_pep_508_name(self) -> str: requirement = self.pretty_name if self.extras: @@ -111,7 +112,7 @@ def base_pep_508_name(self): # type: () -> str return requirement - def __str__(self): # type: () -> str + def __str__(self) -> str: if self.is_root: return self._pretty_name @@ -119,5 +120,5 @@ def __str__(self): # type: () -> str self._pretty_name, self._pretty_constraint, self._path ) - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash((self._name, self._full_path)) diff --git a/poetry/core/packages/package.py b/poetry/core/packages/package.py index 211261c56..cf972dae5 100644 --- a/poetry/core/packages/package.py +++ b/poetry/core/packages/package.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import copy import re @@ -9,28 +8,20 @@ from typing import Optional from typing import Union -from poetry.core.semver import Version -from poetry.core.semver import parse_constraint -from poetry.core.spdx import License -from poetry.core.spdx import license_by_id -from poetry.core.version.markers import AnyMarker +from poetry.core.semver.helpers import parse_constraint from poetry.core.version.markers import parse_marker -# Do not move to the TYPE_CHECKING only section, because Dependency get's imported -# by poetry/packages/locker.py from here -from .dependency import Dependency from .specification import PackageSpecification from .utils.utils import create_nested_marker if TYPE_CHECKING: - from poetry.core.semver import VersionTypes # noqa + from poetry.core.semver.helpers import VersionTypes # noqa + from poetry.core.semver.version import Version # noqa + from poetry.core.spdx.license import License # noqa from poetry.core.version.markers import BaseMarker # noqa - from .directory_dependency import DirectoryDependency - from .file_dependency import FileDependency - from .url_dependency import URLDependency - from .vcs_dependency import VCSDependency + from .types import DependencyTypes AUTHOR_REGEX = re.compile(r"(?u)^(?P[- .,\w\d'’\"()&]+)(?: <(?P.+?)>)?$") @@ -51,18 +42,21 @@ class Package(PackageSpecification): def __init__( self, - name, # type: str - version, # type: Union[str, Version] - pretty_version=None, # type: Optional[str] - source_type=None, # type: Optional[str] - source_url=None, # type: Optional[str] - source_reference=None, # type: Optional[str] - source_resolved_reference=None, # type: Optional[str] - features=None, # type: Optional[List[str]] - ): + name: str, + version: Union[str, "Version"], + pretty_version: Optional[str] = None, + source_type: Optional[str] = None, + source_url: Optional[str] = None, + source_reference: Optional[str] = None, + source_resolved_reference: Optional[str] = None, + features: Optional[List[str]] = None, # type + ) -> None: """ Creates a new in memory package. """ + from poetry.core.semver.version import Version + from poetry.core.version.markers import AnyMarker + super(Package, self).__init__( name, source_type=source_type, @@ -114,34 +108,34 @@ def __init__( self.develop = True @property - def name(self): # type: () -> str + def name(self) -> str: return self._name @property - def pretty_name(self): # type: () -> str + def pretty_name(self) -> str: return self._pretty_name @property - def version(self): # type: () -> "Version" + def version(self) -> "Version": return self._version @property - def pretty_version(self): # type: () -> str + def pretty_version(self) -> str: return self._pretty_version @property - def unique_name(self): # type: () -> str + def unique_name(self) -> str: if self.is_root(): return self._name return self.complete_name + "-" + self._version.text @property - def pretty_string(self): # type: () -> str + def pretty_string(self) -> str: return self.pretty_name + " " + self.pretty_version @property - def full_pretty_version(self): # type: () -> str + def full_pretty_version(self) -> str: if self.source_type in ["file", "directory", "url"]: return "{} {}".format(self._pretty_version, self.source_url) @@ -164,36 +158,36 @@ def full_pretty_version(self): # type: () -> str ) @property - def authors(self): # type: () -> list + def authors(self) -> List[str]: return self._authors @property - def author_name(self): # type: () -> str + def author_name(self) -> str: return self._get_author()["name"] @property - def author_email(self): # type: () -> str + def author_email(self) -> str: return self._get_author()["email"] @property - def maintainers(self): # type: () -> list + def maintainers(self) -> List[str]: return self._maintainers @property - def maintainer_name(self): # type: () -> str + def maintainer_name(self) -> str: return self._get_maintainer()["name"] @property - def maintainer_email(self): # type: () -> str + def maintainer_email(self) -> str: return self._get_maintainer()["email"] @property def all_requires( self, - ): # type: () -> List[Union["DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency", Dependency]] + ) -> List[Union["DependencyTypes"]]: return self.requires + self.dev_requires - def _get_author(self): # type: () -> dict + def _get_author(self) -> Dict[str, Optional[str]]: if not self._authors: return {"name": None, "email": None} @@ -210,7 +204,7 @@ def _get_author(self): # type: () -> dict return {"name": name, "email": email} - def _get_maintainer(self): # type: () -> dict + def _get_maintainer(self) -> Dict[str, Optional[str]]: if not self._maintainers: return {"name": None, "email": None} @@ -228,11 +222,11 @@ def _get_maintainer(self): # type: () -> dict return {"name": name, "email": email} @property - def python_versions(self): # type: () -> str + def python_versions(self) -> str: return self._python_versions @python_versions.setter - def python_versions(self, value): # type: (str) -> None + def python_versions(self, value: str) -> None: self._python_versions = value self._python_constraint = parse_constraint(value) self._python_marker = parse_marker( @@ -240,19 +234,22 @@ def python_versions(self, value): # type: (str) -> None ) @property - def python_constraint(self): # type: () -> "VersionTypes" + def python_constraint(self) -> "VersionTypes": return self._python_constraint @property - def python_marker(self): # type: () -> "BaseMarker" + def python_marker(self) -> "BaseMarker": return self._python_marker @property - def license(self): # type: () -> License + def license(self) -> "License": return self._license @license.setter - def license(self, value): # type: (Optional[str, License]) -> None + def license(self, value: Optional[Union[str, "License"]]) -> None: + from poetry.core.spdx.helpers import license_by_id + from poetry.core.spdx.license import License # noqa + if value is None: self._license = value elif isinstance(value, License): @@ -261,7 +258,9 @@ def license(self, value): # type: (Optional[str, License]) -> None self._license = license_by_id(value) @property - def all_classifiers(self): # type: () -> List[str] + def all_classifiers(self) -> List[str]: + from poetry.core.semver.version import Version # noqa + classifiers = copy.copy(self.classifiers) # Automatically set python classifiers @@ -290,7 +289,7 @@ def all_classifiers(self): # type: () -> List[str] return sorted(classifiers) @property - def urls(self): # type: () -> Dict[str, str] + def urls(self) -> Dict[str, str]: urls = {} if self.homepage: @@ -304,15 +303,16 @@ def urls(self): # type: () -> Dict[str, str] return urls - def is_prerelease(self): # type: () -> bool + def is_prerelease(self) -> bool: return self._version.is_prerelease() - def is_root(self): # type: () -> bool + def is_root(self) -> bool: return False def add_dependency( - self, dependency, - ): # type: (Dependency) -> Dependency + self, + dependency: "DependencyTypes", + ) -> "DependencyTypes": if dependency.category == "dev": self.dev_requires.append(dependency) else: @@ -322,8 +322,8 @@ def add_dependency( def to_dependency( self, - ): # type: () -> Union[Dependency, "DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency"] - from poetry.core.utils._compat import Path + ) -> Union["DependencyTypes"]: + from pathlib import Path from .dependency import Dependency from .directory_dependency import DirectoryDependency @@ -385,7 +385,7 @@ def to_dependency( return dep.with_constraint(self._version) @contextmanager - def with_python_versions(self, python_versions): # type: (str) -> None + def with_python_versions(self, python_versions: str) -> None: original_python_versions = self.python_versions self.python_versions = python_versions @@ -394,17 +394,17 @@ def with_python_versions(self, python_versions): # type: (str) -> None self.python_versions = original_python_versions - def with_features(self, features): # type: (List[str]) -> "Package" + def with_features(self, features: List[str]) -> "Package": package = self.clone() package._features = frozenset(features) return package - def without_features(self): # type: () -> "Package" + def without_features(self) -> "Package": return self.with_features([]) - def clone(self): # type: () -> "Package" + def clone(self) -> "Package": if self.is_root(): clone = self.__class__(self.pretty_name, self.version) else: @@ -434,19 +434,19 @@ def clone(self): # type: () -> "Package" return clone - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return super(Package, self).__hash__() ^ hash(self._version) - def __eq__(self, other): # type: (Package) -> bool + def __eq__(self, other: "Package") -> bool: if not isinstance(other, Package): return NotImplemented return self.is_same_package_as(other) and self._version == other.version - def __str__(self): # type: () -> str + def __str__(self) -> str: return "{} ({})".format(self.complete_name, self.full_pretty_version) - def __repr__(self): # type: () -> str + def __repr__(self) -> str: args = [repr(self._name), repr(self._version.text)] if self._features: diff --git a/poetry/core/packages/project_package.py b/poetry/core/packages/project_package.py index aabde6418..a1600619e 100644 --- a/poetry/core/packages/project_package.py +++ b/poetry/core/packages/project_package.py @@ -4,19 +4,13 @@ from typing import Optional from typing import Union -from poetry.core.semver import VersionRange -from poetry.core.semver import parse_constraint +from poetry.core.semver.helpers import parse_constraint from poetry.core.version.markers import parse_marker if TYPE_CHECKING: - from . import ( - DirectoryDependency, - FileDependency, - URLDependency, - VCSDependency, - Dependency, - ) + from .types import DependencyTypes + from poetry.core.semver.helpers import VersionTypes from .package import Package from .utils.utils import create_nested_marker @@ -24,8 +18,11 @@ class ProjectPackage(Package): def __init__( - self, name, version, pretty_version=None - ): # type: (str, Union[str, VersionRange], Optional[str]) -> None + self, + name: str, + version: Union[str, "VersionTypes"], + pretty_version: Optional[str] = None, + ) -> None: super(ProjectPackage, self).__init__(name, version, pretty_version) self.build_config = dict() @@ -38,15 +35,13 @@ def __init__( self._python_constraint = parse_constraint("~2.7 || >=3.4") @property - def build_script(self): # type: () -> Optional[str] + def build_script(self) -> Optional[str]: return self.build_config.get("script") - def is_root(self): # type: () -> bool + def is_root(self) -> bool: return True - def to_dependency( - self, - ): # type: () -> Union["DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency", "Dependency"] + def to_dependency(self) -> Union["DependencyTypes"]: dependency = super(ProjectPackage, self).to_dependency() dependency.is_root = True @@ -54,11 +49,13 @@ def to_dependency( return dependency @property - def python_versions(self): # type: () -> Union[str, VersionRange] + def python_versions(self) -> Union[str, "VersionTypes"]: return self._python_versions @python_versions.setter - def python_versions(self, value): # type: (Union[str, VersionRange]) -> None + def python_versions(self, value: Union[str, "VersionTypes"]) -> None: + from poetry.core.semver.version_range import VersionRange + self._python_versions = value if value == "*" or value == VersionRange(): @@ -70,12 +67,12 @@ def python_versions(self, value): # type: (Union[str, VersionRange]) -> None ) @property - def urls(self): # type: () -> Dict[str, Any] + def urls(self) -> Dict[str, Any]: urls = super(ProjectPackage, self).urls urls.update(self.custom_urls) return urls - def build_should_generate_setup(self): # type: () -> bool + def build_should_generate_setup(self) -> bool: return self.build_config.get("generate-setup-file", True) diff --git a/poetry/core/packages/specification.py b/poetry/core/packages/specification.py index 70b88f193..3137f7f51 100644 --- a/poetry/core/packages/specification.py +++ b/poetry/core/packages/specification.py @@ -2,19 +2,19 @@ from typing import List from typing import Optional -from poetry.core.utils.helpers import canonicalize_name - class PackageSpecification(object): def __init__( self, - name, # type: str - source_type=None, # type: Optional[str] - source_url=None, # type: Optional[str] - source_reference=None, # type: Optional[str] - source_resolved_reference=None, # type: Optional[str] - features=None, # type: Optional[List[str]] + name: str, + source_type: Optional[str] = None, + source_url: Optional[str] = None, + source_reference: Optional[str] = None, + source_resolved_reference: Optional[str] = None, + features: Optional[List[str]] = None, ): + from poetry.core.utils.helpers import canonicalize_name + self._pretty_name = name self._name = canonicalize_name(name) self._source_type = source_type @@ -28,15 +28,15 @@ def __init__( self._features = frozenset(features) @property - def name(self): # type: () -> str + def name(self) -> str: return self._name @property - def pretty_name(self): # type: () -> str + def pretty_name(self) -> str: return self._pretty_name @property - def complete_name(self): # type: () -> str + def complete_name(self) -> str: name = self._name if self._features: @@ -45,26 +45,26 @@ def complete_name(self): # type: () -> str return name @property - def source_type(self): # type: () -> Optional[str] + def source_type(self) -> Optional[str]: return self._source_type @property - def source_url(self): # type: () -> Optional[str] + def source_url(self) -> Optional[str]: return self._source_url @property - def source_reference(self): # type: () -> Optional[str] + def source_reference(self) -> Optional[str]: return self._source_reference @property - def source_resolved_reference(self): # type: () -> Optional[str] + def source_resolved_reference(self) -> Optional[str]: return self._source_resolved_reference @property - def features(self): # type: () -> FrozenSet[str] + def features(self) -> FrozenSet[str]: return self._features - def is_same_package_as(self, other): # type: ("PackageSpecification") -> bool + def is_same_package_as(self, other: "PackageSpecification") -> bool: if other.complete_name != self.complete_name: return False @@ -101,7 +101,7 @@ def is_same_package_as(self, other): # type: ("PackageSpecification") -> bool return True - def __hash__(self): # type: () -> int + def __hash__(self) -> int: if not self._source_type: return hash(self._name) @@ -114,5 +114,5 @@ def __hash__(self): # type: () -> int ^ hash(self._features) ) - def __str__(self): # type: () -> str + def __str__(self) -> str: raise NotImplementedError() diff --git a/poetry/core/packages/types.py b/poetry/core/packages/types.py new file mode 100644 index 000000000..5fbafc793 --- /dev/null +++ b/poetry/core/packages/types.py @@ -0,0 +1,14 @@ +from typing import TYPE_CHECKING +from typing import Union + + +if TYPE_CHECKING: + from .dependency import Dependency + from .directory_dependency import DirectoryDependency + from .file_dependency import FileDependency + from .url_dependency import URLDependency + from .vcs_dependency import VCSDependency + + DependencyTypes = Union[ + Dependency, DirectoryDependency, FileDependency, URLDependency, VCSDependency + ] diff --git a/poetry/core/packages/url_dependency.py b/poetry/core/packages/url_dependency.py index 2d4ce5dd4..e8770e815 100644 --- a/poetry/core/packages/url_dependency.py +++ b/poetry/core/packages/url_dependency.py @@ -2,8 +2,7 @@ from typing import FrozenSet from typing import List from typing import Union - -from poetry.core.utils._compat import urlparse +from urllib.parse import urlparse from .dependency import Dependency @@ -15,15 +14,15 @@ class URLDependency(Dependency): def __init__( self, - name, # type: str - url, # type: str - category="main", # type: str - optional=False, # type: bool - extras=None, # type: Union[List[str], FrozenSet[str]] + name: str, + url: str, + category: str = "main", + optional: bool = False, + extras: Union[List[str], FrozenSet[str]] = None, ): self._url = url - parsed = urlparse.urlparse(url) + parsed = urlparse(url) if not parsed.scheme or not parsed.netloc: raise ValueError("{} does not seem like a valid url".format(url)) @@ -39,11 +38,11 @@ def __init__( ) @property - def url(self): # type: () -> str + def url(self) -> str: return self._url @property - def base_pep_508_name(self): # type: () -> str + def base_pep_508_name(self) -> str: requirement = self.pretty_name if self.extras: @@ -53,10 +52,10 @@ def base_pep_508_name(self): # type: () -> str return requirement - def is_url(self): # type: () -> bool + def is_url(self) -> bool: return True - def with_constraint(self, constraint): # type: ("BaseConstraint") -> URLDependency + def with_constraint(self, constraint: "BaseConstraint") -> "URLDependency": new = URLDependency( self.pretty_name, url=self._url, @@ -78,8 +77,8 @@ def with_constraint(self, constraint): # type: ("BaseConstraint") -> URLDepende return new - def __str__(self): # type: () -> str + def __str__(self) -> str: return "{} ({} url)".format(self._pretty_name, self._pretty_constraint) - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash((self._name, self._url)) diff --git a/poetry/core/packages/utils/link.py b/poetry/core/packages/utils/link.py index 76f6c1c78..ae1319ce8 100644 --- a/poetry/core/packages/utils/link.py +++ b/poetry/core/packages/utils/link.py @@ -1,29 +1,22 @@ import posixpath import re +import urllib.parse as urlparse -from typing import TYPE_CHECKING from typing import Any from typing import Optional from typing import Tuple - -if TYPE_CHECKING: - from pip._internal.index.collector import HTMLPage # noqa - from .utils import path_to_url from .utils import splitext -try: - import urllib.parse as urlparse -except ImportError: - import urlparse - - class Link: def __init__( - self, url, comes_from=None, requires_python=None - ): # type: (str, Optional["HTMLPage"], Optional[str]) -> None + self, + url: str, + comes_from: Optional[Any] = None, + requires_python: Optional[str] = None, + ) -> None: """ Object representing a parsed link from https://pypi.python.org/simple/* @@ -45,7 +38,7 @@ def __init__( self.comes_from = comes_from self.requires_python = requires_python if requires_python else None - def __str__(self): # type: () -> str + def __str__(self) -> str: if self.requires_python: rp = " (requires-python:%s)" % self.requires_python else: @@ -55,78 +48,78 @@ def __str__(self): # type: () -> str else: return str(self.url) - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "" % self - def __eq__(self, other): # type: (Any) -> bool + def __eq__(self, other: Any) -> bool: if not isinstance(other, Link): return NotImplemented return self.url == other.url - def __ne__(self, other): # type: (Any) -> bool + def __ne__(self, other: Any) -> bool: if not isinstance(other, Link): return NotImplemented return self.url != other.url - def __lt__(self, other): # type: (Any) -> bool + def __lt__(self, other: Any) -> bool: if not isinstance(other, Link): return NotImplemented return self.url < other.url - def __le__(self, other): # type: (Any) -> bool + def __le__(self, other: Any) -> bool: if not isinstance(other, Link): return NotImplemented return self.url <= other.url - def __gt__(self, other): # type: (Any) -> bool + def __gt__(self, other: Any) -> bool: if not isinstance(other, Link): return NotImplemented return self.url > other.url - def __ge__(self, other): # type: (Any) -> bool + def __ge__(self, other: Any) -> bool: if not isinstance(other, Link): return NotImplemented return self.url >= other.url - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash(self.url) @property - def filename(self): # type: () -> str + def filename(self) -> str: _, netloc, path, _, _ = urlparse.urlsplit(self.url) name = posixpath.basename(path.rstrip("/")) or netloc name = urlparse.unquote(name) - assert name, "URL %r produced no filename" % self.url + return name @property - def scheme(self): # type: () -> str + def scheme(self) -> str: return urlparse.urlsplit(self.url)[0] @property - def netloc(self): # type: () -> str + def netloc(self) -> str: return urlparse.urlsplit(self.url)[1] @property - def path(self): # type: () -> str + def path(self) -> str: return urlparse.unquote(urlparse.urlsplit(self.url)[2]) - def splitext(self): # type: () -> Tuple[str, str] + def splitext(self) -> Tuple[str, str]: return splitext(posixpath.basename(self.path.rstrip("/"))) @property - def ext(self): # type: () -> str + def ext(self) -> str: return self.splitext()[1] @property - def url_without_fragment(self): # type: () -> str + def url_without_fragment(self) -> str: scheme, netloc, path, query, fragment = urlparse.urlsplit(self.url) return urlparse.urlunsplit((scheme, netloc, path, query, None)) _egg_fragment_re = re.compile(r"[#&]egg=([^&]*)") @property - def egg_fragment(self): # type: () -> Optional[str] + def egg_fragment(self) -> Optional[str]: match = self._egg_fragment_re.search(self.url) if not match: return None @@ -135,7 +128,7 @@ def egg_fragment(self): # type: () -> Optional[str] _subdirectory_fragment_re = re.compile(r"[#&]subdirectory=([^&]*)") @property - def subdirectory_fragment(self): # type: () -> Optional[str] + def subdirectory_fragment(self) -> Optional[str]: match = self._subdirectory_fragment_re.search(self.url) if not match: return None @@ -144,41 +137,41 @@ def subdirectory_fragment(self): # type: () -> Optional[str] _hash_re = re.compile(r"(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)") @property - def hash(self): # type: () -> Optional[str] + def hash(self) -> Optional[str]: match = self._hash_re.search(self.url) if match: return match.group(2) return None @property - def hash_name(self): # type: () -> Optional[str] + def hash_name(self) -> Optional[str]: match = self._hash_re.search(self.url) if match: return match.group(1) return None @property - def show_url(self): # type: () -> str + def show_url(self) -> str: return posixpath.basename(self.url.split("#", 1)[0].split("?", 1)[0]) @property - def is_wheel(self): # type: () -> bool + def is_wheel(self) -> bool: return self.ext == ".whl" @property - def is_wininst(self): # type: () -> bool + def is_wininst(self) -> bool: return self.ext == ".exe" @property - def is_egg(self): # type: () -> bool + def is_egg(self) -> bool: return self.ext == ".egg" @property - def is_sdist(self): # type: () -> bool + def is_sdist(self) -> bool: return self.ext in {".tar.bz2", ".tar.gz", ".zip"} @property - def is_artifact(self): # type: () -> bool + def is_artifact(self) -> bool: """ Determines if this points to an actual artifact (e.g. a tarball) or if it points to an "abstract" thing like a path or a VCS location. diff --git a/poetry/core/packages/utils/utils.py b/poetry/core/packages/utils/utils.py index 21c64bb60..f2514b1bf 100644 --- a/poetry/core/packages/utils/utils.py +++ b/poetry/core/packages/utils/utils.py @@ -3,35 +3,26 @@ import re import sys +from pathlib import Path from typing import TYPE_CHECKING from typing import Dict from typing import List from typing import Tuple from typing import Union - -from six.moves.urllib.parse import unquote # noqa -from six.moves.urllib.parse import urlsplit # noqa -from six.moves.urllib.request import url2pathname # noqa - -from poetry.core.packages.constraints.constraint import Constraint -from poetry.core.packages.constraints.multi_constraint import MultiConstraint -from poetry.core.packages.constraints.union_constraint import UnionConstraint -from poetry.core.semver import EmptyConstraint -from poetry.core.semver import Version -from poetry.core.semver import VersionConstraint -from poetry.core.semver import VersionRange -from poetry.core.semver import VersionUnion -from poetry.core.semver import parse_constraint -from poetry.core.utils._compat import Path -from poetry.core.version.markers import BaseMarker -from poetry.core.version.markers import MarkerUnion -from poetry.core.version.markers import MultiMarker -from poetry.core.version.markers import SingleMarker +from urllib.parse import unquote +from urllib.parse import urlsplit +from urllib.request import url2pathname if TYPE_CHECKING: from poetry.core.packages.constraints import BaseConstraint # noqa - from poetry.core.semver import VersionTypes # noqa + from poetry.core.semver.helpers import VersionTypes # noqa + from poetry.core.semver.version import Version # noqa + from poetry.core.semver.version_constraint import VersionConstraint # noqa + from poetry.core.semver.version_range import VersionRange # noqa + from poetry.core.semver.version_union import VersionUnion # noqa + from poetry.core.version.markers import BaseMarker # noqa + BZ2_EXTENSIONS = (".tar.bz2", ".tbz") XZ_EXTENSIONS = (".tar.xz", ".txz", ".tlz", ".tar.lz", ".tar.lzma") @@ -56,7 +47,7 @@ pass -def path_to_url(path): # type: (Union[str, Path]) -> str +def path_to_url(path: Union[str, Path]) -> str: """ Convert a path to a file: URL. The path will be made absolute unless otherwise specified and have quoted path parts. @@ -64,7 +55,7 @@ def path_to_url(path): # type: (Union[str, Path]) -> str return Path(path).absolute().as_uri() -def url_to_path(url): # type: (str) -> Path +def url_to_path(url: str) -> Path: """ Convert an RFC8089 file URI to path. @@ -90,7 +81,7 @@ def url_to_path(url): # type: (str) -> Path return Path(url2pathname(netloc + unquote(path))) -def is_url(name): # type: (str) -> bool +def is_url(name: str) -> bool: if ":" not in name: return False scheme = name.split(":", 1)[0].lower() @@ -110,7 +101,7 @@ def is_url(name): # type: (str) -> bool ] -def strip_extras(path): # type: (str) -> Tuple[str, str] +def strip_extras(path: str) -> Tuple[str, str]: m = re.match(r"^(.+)(\[[^\]]+\])$", path) extras = None if m: @@ -122,7 +113,7 @@ def strip_extras(path): # type: (str) -> Tuple[str, str] return path_no_extras, extras -def is_installable_dir(path): # type: (str) -> bool +def is_installable_dir(path: str) -> bool: """Return True if `path` is a directory containing a setup.py file.""" if not os.path.isdir(path): return False @@ -132,7 +123,7 @@ def is_installable_dir(path): # type: (str) -> bool return False -def is_archive_file(name): # type: (str) -> bool +def is_archive_file(name: str) -> bool: """Return True if `name` is a considered as an archive file.""" ext = splitext(name)[1].lower() if ext in ARCHIVE_EXTENSIONS: @@ -140,7 +131,7 @@ def is_archive_file(name): # type: (str) -> bool return False -def splitext(path): # type: (str) -> Tuple[str, str] +def splitext(path: str) -> Tuple[str, str]: """Like os.path.splitext, but take off .tar too""" base, ext = posixpath.splitext(path) if base.lower().endswith(".tar"): @@ -150,8 +141,12 @@ def splitext(path): # type: (str) -> Tuple[str, str] def group_markers( - markers, or_=False -): # type: (List[BaseMarker], bool) -> List[Union[Tuple[str, str, str], List[Tuple[str, str, str]]]] + markers: List["BaseMarker"], or_: bool = False +) -> List[Union[Tuple[str, str, str], List[Tuple[str, str, str]]]]: + from poetry.core.version.markers import MarkerUnion + from poetry.core.version.markers import MultiMarker + from poetry.core.version.markers import SingleMarker + groups = [[]] for marker in markers: @@ -170,14 +165,15 @@ def group_markers( return groups -def convert_markers(marker): # type: (BaseMarker) -> Dict[str, List[Tuple[str, str]]] +def convert_markers(marker: "BaseMarker") -> Dict[str, List[Tuple[str, str]]]: groups = group_markers([marker]) requirements = {} def _group( - _groups, or_=False - ): # type: (List[Union[Tuple[str, str, str], List[Tuple[str, str, str]]]], bool) -> None + _groups: List[Union[Tuple[str, str, str], List[Tuple[str, str, str]]]], + or_: bool = False, + ) -> None: ors = {} for group in _groups: if isinstance(group, list): @@ -210,8 +206,15 @@ def _group( def create_nested_marker( - name, constraint -): # type: (str, Union["BaseConstraint", VersionUnion, Version, VersionConstraint]) -> str + name: str, + constraint: Union["BaseConstraint", "VersionUnion", "Version", "VersionConstraint"], +) -> str: + from poetry.core.packages.constraints.constraint import Constraint + from poetry.core.packages.constraints.multi_constraint import MultiConstraint + from poetry.core.packages.constraints.union_constraint import UnionConstraint + from poetry.core.semver.version import Version + from poetry.core.semver.version_union import VersionUnion + if constraint.is_any(): return "" @@ -278,7 +281,14 @@ def create_nested_marker( return marker -def get_python_constraint_from_marker(marker,): # type: (BaseMarker) -> "VersionTypes" +def get_python_constraint_from_marker( + marker: "BaseMarker", +) -> "VersionTypes": + from poetry.core.semver.empty_constraint import EmptyConstraint + from poetry.core.semver.helpers import parse_constraint + from poetry.core.semver.version import Version + from poetry.core.semver.version_range import VersionRange # noqa + python_marker = marker.only("python_version", "python_full_version") if python_marker.is_any(): return VersionRange() diff --git a/poetry/core/packages/vcs_dependency.py b/poetry/core/packages/vcs_dependency.py index 2800644aa..932338c16 100644 --- a/poetry/core/packages/vcs_dependency.py +++ b/poetry/core/packages/vcs_dependency.py @@ -4,8 +4,6 @@ from typing import Optional from typing import Union -from poetry.core.vcs import git - from .dependency import Dependency @@ -20,17 +18,17 @@ class VCSDependency(Dependency): def __init__( self, - name, # type: str - vcs, # type: str - source, # type: str - branch=None, # type: Optional[str] - tag=None, # type: Optional[str] - rev=None, # type: Optional[str] - resolved_rev=None, # type: Optional[str] - category="main", # type: str - optional=False, # type: bool - develop=False, # type: bool - extras=None, # type: Union[List[str], FrozenSet[str]] + name: str, + vcs: str, + source: str, + branch: Optional[str] = None, + tag: Optional[str] = None, + rev: Optional[str] = None, + resolved_rev: Optional[str] = None, + category: str = "main", + optional: bool = False, + develop: bool = False, + extras: Union[List[str], FrozenSet[str]] = None, ): self._vcs = vcs self._source = source @@ -58,35 +56,35 @@ def __init__( ) @property - def vcs(self): # type: () -> str + def vcs(self) -> str: return self._vcs @property - def source(self): # type: () -> str + def source(self) -> str: return self._source @property - def branch(self): # type: () -> Optional[str] + def branch(self) -> Optional[str]: return self._branch @property - def tag(self): # type: () -> Optional[str] + def tag(self) -> Optional[str]: return self._tag @property - def rev(self): # type: () -> Optional[str] + def rev(self) -> Optional[str]: return self._rev @property - def develop(self): # type: () -> bool + def develop(self) -> bool: return self._develop @property - def reference(self): # type: () -> str + def reference(self) -> str: return self._branch or self._tag or self._rev @property - def pretty_constraint(self): # type: () -> str + def pretty_constraint(self) -> str: if self._branch: what = "branch" version = self._branch @@ -100,7 +98,9 @@ def pretty_constraint(self): # type: () -> str return "{} {}".format(what, version) @property - def base_pep_508_name(self): # type: () -> str + def base_pep_508_name(self) -> str: + from poetry.core.vcs import git + requirement = self.pretty_name parsed_url = git.ParsedUrl.parse(self._source) @@ -116,13 +116,13 @@ def base_pep_508_name(self): # type: () -> str return requirement - def is_vcs(self): # type: () -> bool + def is_vcs(self) -> bool: return True - def accepts_prereleases(self): # type: () -> bool + def accepts_prereleases(self) -> bool: return True - def with_constraint(self, constraint): # type: ("BaseConstraint") -> VCSDependency + def with_constraint(self, constraint: "BaseConstraint") -> "VCSDependency": new = VCSDependency( self.pretty_name, self._vcs, @@ -150,7 +150,7 @@ def with_constraint(self, constraint): # type: ("BaseConstraint") -> VCSDepende return new - def __str__(self): # type: () -> str + def __str__(self) -> str: reference = self._vcs if self._branch: reference += " branch {}".format(self._branch) @@ -161,5 +161,5 @@ def __str__(self): # type: () -> str return "{} ({} {})".format(self._pretty_name, self._constraint, reference) - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash((self._name, self._vcs, self._branch, self._tag, self._rev)) diff --git a/poetry/core/poetry.py b/poetry/core/poetry.py index af04a0dad..ed10c25d5 100644 --- a/poetry/core/poetry.py +++ b/poetry/core/poetry.py @@ -4,38 +4,43 @@ from typing import TYPE_CHECKING from typing import Any -from poetry.core.pyproject import PyProjectTOML -from poetry.core.utils._compat import Path # noqa - if TYPE_CHECKING: + from pathlib import Path + from poetry.core.packages import ProjectPackage # noqa + from poetry.core.pyproject.toml import PyProjectTOML # noqa from poetry.core.pyproject.toml import PyProjectTOMLFile # noqa class Poetry(object): def __init__( - self, file, local_config, package, - ): # type: (Path, dict, "ProjectPackage") -> None + self, + file: "Path", + local_config: dict, + package: "ProjectPackage", + ) -> None: + from poetry.core.pyproject.toml import PyProjectTOML # noqa + self._pyproject = PyProjectTOML(file) self._package = package self._local_config = local_config @property - def pyproject(self): # type: () -> PyProjectTOML + def pyproject(self) -> "PyProjectTOML": return self._pyproject @property - def file(self): # type: () -> "PyProjectTOMLFile" + def file(self) -> "PyProjectTOMLFile": return self._pyproject.file @property - def package(self): # type: () -> "ProjectPackage" + def package(self) -> "ProjectPackage": return self._package @property - def local_config(self): # type: () -> dict + def local_config(self) -> dict: return self._local_config - def get_project_config(self, config, default=None): # type: (str, Any) -> Any + def get_project_config(self, config: str, default: Any = None) -> Any: return self._local_config.get("config", {}).get(config, default) diff --git a/poetry/core/pyproject/__init__.py b/poetry/core/pyproject/__init__.py index 9f760fbe2..e69de29bb 100644 --- a/poetry/core/pyproject/__init__.py +++ b/poetry/core/pyproject/__init__.py @@ -1,6 +0,0 @@ -from poetry.core.pyproject.exceptions import PyProjectException -from poetry.core.pyproject.tables import BuildSystem -from poetry.core.pyproject.toml import PyProjectTOML - - -__all__ = [clazz.__name__ for clazz in {BuildSystem, PyProjectException, PyProjectTOML}] diff --git a/poetry/core/pyproject/tables.py b/poetry/core/pyproject/tables.py index 1f3686220..f8339c04f 100644 --- a/poetry/core/pyproject/tables.py +++ b/poetry/core/pyproject/tables.py @@ -1,8 +1,8 @@ +from pathlib import Path from typing import TYPE_CHECKING from typing import List from typing import Optional -from poetry.core.utils._compat import Path from poetry.core.utils.helpers import canonicalize_name @@ -13,8 +13,8 @@ # TODO: Convert to dataclass once python 2.7, 3.5 is dropped class BuildSystem: def __init__( - self, build_backend=None, requires=None - ): # type: (Optional[str], Optional[List[str]]) -> None + self, build_backend: Optional[str] = None, requires: Optional[List[str]] = None + ) -> None: self.build_backend = ( build_backend if build_backend is not None @@ -24,18 +24,18 @@ def __init__( self._dependencies = None @property - def dependencies(self): # type: () -> List["Dependency"] + def dependencies(self) -> List["Dependency"]: if self._dependencies is None: # avoid circular dependency when loading DirectoryDependency - from poetry.core.packages import DirectoryDependency - from poetry.core.packages import FileDependency - from poetry.core.packages import dependency_from_pep_508 + from poetry.core.packages.dependency import Dependency + from poetry.core.packages.directory_dependency import DirectoryDependency + from poetry.core.packages.file_dependency import FileDependency self._dependencies = [] for requirement in self.requires: dependency = None try: - dependency = dependency_from_pep_508(requirement) + dependency = Dependency.create_from_pep_508(requirement) except ValueError: # PEP 517 requires can be path if not PEP 508 path = Path(requirement) diff --git a/poetry/core/pyproject/toml.py b/poetry/core/pyproject/toml.py index a223dc1f6..cb6dddcf0 100644 --- a/poetry/core/pyproject/toml.py +++ b/poetry/core/pyproject/toml.py @@ -1,38 +1,47 @@ +from pathlib import Path +from typing import TYPE_CHECKING from typing import Any from typing import Optional from typing import Union -from tomlkit.container import Container -from tomlkit.toml_document import TOMLDocument -from poetry.core.pyproject.exceptions import PyProjectException -from poetry.core.pyproject.tables import BuildSystem -from poetry.core.toml import TOMLFile -from poetry.core.utils._compat import Path +if TYPE_CHECKING: + from tomlkit.toml_document import TOMLDocument + + from poetry.core.toml import TOMLFile + + from .tables import BuildSystem class PyProjectTOML: - def __init__(self, path): # type: (Union[str, Path]) -> None + def __init__(self, path: Union[str, Path]) -> None: + from poetry.core.toml import TOMLFile + self._file = TOMLFile(path=path) - self._data = None # type: Optional[TOMLDocument] - self._build_system = None # type: Optional[BuildSystem] - self._poetry_config = None # type: Optional[TOMLDocument] + self._data: Optional["TOMLDocument"] = None + self._build_system: Optional["BuildSystem"] = None + self._poetry_config: Optional["TOMLDocument"] = None @property - def file(self): # type: () -> TOMLFile + def file(self) -> "TOMLFile": return self._file @property - def data(self): # type: () -> TOMLDocument + def data(self) -> "TOMLDocument": + from tomlkit.toml_document import TOMLDocument + if self._data is None: if not self._file.exists(): self._data = TOMLDocument() else: self._data = self._file.read() + return self._data @property - def build_system(self): # type: () -> BuildSystem + def build_system(self) -> "BuildSystem": + from .tables import BuildSystem + if self._build_system is None: build_backend = None requires = None @@ -46,31 +55,40 @@ def build_system(self): # type: () -> BuildSystem build_backend=container.get("build-backend", build_backend), requires=container.get("requires", requires), ) + return self._build_system @property - def poetry_config(self): # type: () -> Optional[TOMLDocument] + def poetry_config(self) -> Optional["TOMLDocument"]: + from .exceptions import PyProjectException + if self._poetry_config is None: self._poetry_config = self.data.get("tool", {}).get("poetry") if self._poetry_config is None: raise PyProjectException( "[tool.poetry] section not found in {}".format(self._file) ) + return self._poetry_config - def is_poetry_project(self): # type: () -> bool + def is_poetry_project(self) -> bool: + from .exceptions import PyProjectException + if self.file.exists(): try: _ = self.poetry_config return True except PyProjectException: pass + return False - def __getattr__(self, item): # type: (str) -> Any + def __getattr__(self, item: str) -> Any: return getattr(self.data, item) - def save(self): # type: () -> None + def save(self) -> None: + from tomlkit.container import Container + data = self.data if self._poetry_config is not None: @@ -79,12 +97,13 @@ def save(self): # type: () -> None if self._build_system is not None: if "build-system" not in data: data["build-system"] = Container() + data["build-system"]["requires"] = self._build_system.requires data["build-system"]["build-backend"] = self._build_system.build_backend self.file.write(data=data) - def reload(self): # type: () -> None + def reload(self) -> None: self._data = None self._build_system = None self._poetry_config = None diff --git a/poetry/core/semver/__init__.py b/poetry/core/semver/__init__.py index 2cff22d6e..e69de29bb 100644 --- a/poetry/core/semver/__init__.py +++ b/poetry/core/semver/__init__.py @@ -1,151 +0,0 @@ -import re - -from typing import Union - -from .empty_constraint import EmptyConstraint -from .exceptions import ParseConstraintError -from .patterns import BASIC_CONSTRAINT -from .patterns import CARET_CONSTRAINT -from .patterns import TILDE_CONSTRAINT -from .patterns import TILDE_PEP440_CONSTRAINT -from .patterns import X_CONSTRAINT -from .version import Version -from .version_constraint import VersionConstraint -from .version_range import VersionRange -from .version_union import VersionUnion - - -VersionTypes = Union[Version, VersionRange, VersionUnion, EmptyConstraint] - - -def parse_constraint(constraints): # type: (str) -> VersionTypes - if constraints == "*": - return VersionRange() - - or_constraints = re.split(r"\s*\|\|?\s*", constraints.strip()) - or_groups = [] - for constraints in or_constraints: - and_constraints = re.split( - "(?< ,]) *(? 1: - for constraint in and_constraints: - constraint_objects.append(parse_single_constraint(constraint)) - else: - constraint_objects.append(parse_single_constraint(and_constraints[0])) - - if len(constraint_objects) == 1: - constraint = constraint_objects[0] - else: - constraint = constraint_objects[0] - for next_constraint in constraint_objects[1:]: - constraint = constraint.intersect(next_constraint) - - or_groups.append(constraint) - - if len(or_groups) == 1: - return or_groups[0] - else: - return VersionUnion.of(*or_groups) - - -def parse_single_constraint(constraint): # type: (str) -> VersionTypes - m = re.match(r"(?i)^v?[xX*](\.[xX*])*$", constraint) - if m: - return VersionRange() - - # Tilde range - m = TILDE_CONSTRAINT.match(constraint) - if m: - version = Version.parse(m.group(1)) - - high = version.stable.next_minor - if len(m.group(1).split(".")) == 1: - high = version.stable.next_major - - return VersionRange(version, high, include_min=True) - - # PEP 440 Tilde range (~=) - m = TILDE_PEP440_CONSTRAINT.match(constraint) - if m: - precision = 1 - if m.group(3): - precision += 1 - - if m.group(4): - precision += 1 - - version = Version.parse(m.group(1)) - - if precision == 2: - high = version.stable.next_major - else: - high = version.stable.next_minor - - return VersionRange(version, high, include_min=True) - - # Caret range - m = CARET_CONSTRAINT.match(constraint) - if m: - version = Version.parse(m.group(1)) - - return VersionRange(version, version.next_breaking, include_min=True) - - # X Range - m = X_CONSTRAINT.match(constraint) - if m: - op = m.group(1) - major = int(m.group(2)) - minor = m.group(3) - - if minor is not None: - version = Version(major, int(minor), 0) - - result = VersionRange(version, version.next_minor, include_min=True) - else: - if major == 0: - result = VersionRange(max=Version(1, 0, 0)) - else: - version = Version(major, 0, 0) - - result = VersionRange(version, version.next_major, include_min=True) - - if op == "!=": - result = VersionRange().difference(result) - - return result - - # Basic comparator - m = BASIC_CONSTRAINT.match(constraint) - if m: - op = m.group(1) - version = m.group(2) - - if version == "dev": - version = "0.0-dev" - - try: - version = Version.parse(version) - except ValueError: - raise ValueError( - "Could not parse version constraint: {}".format(constraint) - ) - - if op == "<": - return VersionRange(max=version) - elif op == "<=": - return VersionRange(max=version, include_max=True) - elif op == ">": - return VersionRange(min=version) - elif op == ">=": - return VersionRange(min=version, include_min=True) - elif op == "!=": - return VersionUnion(VersionRange(max=version), VersionRange(min=version)) - else: - return version - - raise ParseConstraintError( - "Could not parse version constraint: {}".format(constraint) - ) diff --git a/poetry/core/semver/empty_constraint.py b/poetry/core/semver/empty_constraint.py index c463fa586..208451cbb 100644 --- a/poetry/core/semver/empty_constraint.py +++ b/poetry/core/semver/empty_constraint.py @@ -9,29 +9,29 @@ class EmptyConstraint(VersionConstraint): - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return True - def is_any(self): # type: () -> bool + def is_any(self) -> bool: return False - def allows(self, version): # type: ("Version") -> bool + def allows(self, version: "Version") -> bool: return False - def allows_all(self, other): # type: ("VersionTypes") -> bool + def allows_all(self, other: "VersionTypes") -> bool: return other.is_empty() - def allows_any(self, other): # type: ("VersionTypes") -> bool + def allows_any(self, other: "VersionTypes") -> bool: return False - def intersect(self, other): # type: ("VersionTypes") -> EmptyConstraint + def intersect(self, other: "VersionTypes") -> "EmptyConstraint": return self - def union(self, other): # type: ("VersionTypes") -> "VersionTypes" + def union(self, other: "VersionTypes") -> "VersionTypes": return other - def difference(self, other): # type: ("VersionTypes") -> EmptyConstraint + def difference(self, other: "VersionTypes") -> "EmptyConstraint": return self - def __str__(self): # type: () -> str + def __str__(self) -> str: return "" diff --git a/poetry/core/semver/helpers.py b/poetry/core/semver/helpers.py new file mode 100644 index 000000000..e20008f2e --- /dev/null +++ b/poetry/core/semver/helpers.py @@ -0,0 +1,162 @@ +import re + +from typing import TYPE_CHECKING +from typing import Union + + +if TYPE_CHECKING: + from .empty_constraint import EmptyConstraint # noqa + from .version import Version # noqa + from .version_range import VersionRange # noqa + from .version_union import VersionUnion # noqa + + +VersionTypes = Union["Version", "VersionRange", "VersionUnion", "EmptyConstraint"] + + +def parse_constraint(constraints: str) -> VersionTypes: + if constraints == "*": + from .version_range import VersionRange + + return VersionRange() + + or_constraints = re.split(r"\s*\|\|?\s*", constraints.strip()) + or_groups = [] + for constraints in or_constraints: + and_constraints = re.split( + "(?< ,]) *(? 1: + for constraint in and_constraints: + constraint_objects.append(parse_single_constraint(constraint)) + else: + constraint_objects.append(parse_single_constraint(and_constraints[0])) + + if len(constraint_objects) == 1: + constraint = constraint_objects[0] + else: + constraint = constraint_objects[0] + for next_constraint in constraint_objects[1:]: + constraint = constraint.intersect(next_constraint) + + or_groups.append(constraint) + + if len(or_groups) == 1: + return or_groups[0] + else: + from .version_union import VersionUnion + + return VersionUnion.of(*or_groups) + + +def parse_single_constraint(constraint: str) -> VersionTypes: + from .patterns import BASIC_CONSTRAINT + from .patterns import CARET_CONSTRAINT + from .patterns import TILDE_CONSTRAINT + from .patterns import TILDE_PEP440_CONSTRAINT + from .patterns import X_CONSTRAINT + from .version import Version + from .version_range import VersionRange + from .version_union import VersionUnion + + m = re.match(r"(?i)^v?[xX*](\.[xX*])*$", constraint) + if m: + return VersionRange() + + # Tilde range + m = TILDE_CONSTRAINT.match(constraint) + if m: + version = Version.parse(m.group(1)) + + high = version.stable.next_minor + if len(m.group(1).split(".")) == 1: + high = version.stable.next_major + + return VersionRange(version, high, include_min=True) + + # PEP 440 Tilde range (~=) + m = TILDE_PEP440_CONSTRAINT.match(constraint) + if m: + precision = 1 + if m.group(3): + precision += 1 + + if m.group(4): + precision += 1 + + version = Version.parse(m.group(1)) + + if precision == 2: + high = version.stable.next_major + else: + high = version.stable.next_minor + + return VersionRange(version, high, include_min=True) + + # Caret range + m = CARET_CONSTRAINT.match(constraint) + if m: + version = Version.parse(m.group(1)) + + return VersionRange(version, version.next_breaking, include_min=True) + + # X Range + m = X_CONSTRAINT.match(constraint) + if m: + op = m.group(1) + major = int(m.group(2)) + minor = m.group(3) + + if minor is not None: + version = Version(major, int(minor), 0) + + result = VersionRange(version, version.next_minor, include_min=True) + else: + if major == 0: + result = VersionRange(max=Version(1, 0, 0)) + else: + version = Version(major, 0, 0) + + result = VersionRange(version, version.next_major, include_min=True) + + if op == "!=": + result = VersionRange().difference(result) + + return result + + # Basic comparator + m = BASIC_CONSTRAINT.match(constraint) + if m: + op = m.group(1) + version = m.group(2) + + if version == "dev": + version = "0.0-dev" + + try: + version = Version.parse(version) + except ValueError: + raise ValueError( + "Could not parse version constraint: {}".format(constraint) + ) + + if op == "<": + return VersionRange(max=version) + elif op == "<=": + return VersionRange(max=version, include_max=True) + elif op == ">": + return VersionRange(min=version) + elif op == ">=": + return VersionRange(min=version, include_min=True) + elif op == "!=": + return VersionUnion(VersionRange(max=version), VersionRange(min=version)) + else: + return version + + from .exceptions import ParseConstraintError + + raise ParseConstraintError( + "Could not parse version constraint: {}".format(constraint) + ) diff --git a/poetry/core/semver/patterns.py b/poetry/core/semver/patterns.py index 6cda2a305..dfcaf64e7 100644 --- a/poetry/core/semver/patterns.py +++ b/poetry/core/semver/patterns.py @@ -7,8 +7,8 @@ r"([+-]?([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?" ) -_COMPLETE_VERSION = r"v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?{}(?:\+[^\s]+)?".format( - MODIFIERS +_COMPLETE_VERSION = ( + r"v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?{}(?:\+[^\s]+)?".format(MODIFIERS) ) COMPLETE_VERSION = re.compile("(?i)" + _COMPLETE_VERSION) diff --git a/poetry/core/semver/version.py b/poetry/core/semver/version.py index acd5f3e84..e20764d8c 100644 --- a/poetry/core/semver/version.py +++ b/poetry/core/semver/version.py @@ -24,15 +24,15 @@ class Version(VersionRange): def __init__( self, - major, # type: int - minor=None, # type: Optional[int] - patch=None, # type: Optional[int] - rest=None, # type: Optional[int] - pre=None, # type: Optional[str] - build=None, # type: Optional[str] - text=None, # type: Optional[str] - precision=None, # type: Optional[int] - ): # type: (...) -> None + major: int, + minor: Optional[int] = None, + patch: Optional[int] = None, + rest: Optional[int] = None, + pre: Optional[str] = None, + build: Optional[str] = None, + text: Optional[str] = None, + precision: Optional[int] = None, + ) -> None: self._major = int(major) self._precision = None if precision is None: @@ -100,67 +100,67 @@ def __init__( self._build = self._split_parts(build) @property - def major(self): # type: () -> int + def major(self) -> int: return self._major @property - def minor(self): # type: () -> int + def minor(self) -> int: return self._minor @property - def patch(self): # type: () -> int + def patch(self) -> int: return self._patch @property - def rest(self): # type: () -> int + def rest(self) -> int: return self._rest @property - def prerelease(self): # type: () -> List[str] + def prerelease(self) -> List[str]: return self._prerelease @property - def build(self): # type: () -> List[str] + def build(self) -> List[str]: return self._build @property - def text(self): # type: () -> str + def text(self) -> str: return self._text @property - def precision(self): # type: () -> int + def precision(self) -> int: return self._precision @property - def stable(self): # type: () -> Version + def stable(self) -> "Version": if not self.is_prerelease(): return self return self.next_patch @property - def next_major(self): # type: () -> Version + def next_major(self) -> "Version": if self.is_prerelease() and self.minor == 0 and self.patch == 0: return Version(self.major, self.minor, self.patch) return self._increment_major() @property - def next_minor(self): # type: () -> Version + def next_minor(self) -> "Version": if self.is_prerelease() and self.patch == 0: return Version(self.major, self.minor, self.patch) return self._increment_minor() @property - def next_patch(self): # type: () -> Version + def next_patch(self) -> "Version": if self.is_prerelease(): return Version(self.major, self.minor, self.patch) return self._increment_patch() @property - def next_breaking(self): # type: () -> Version + def next_breaking(self) -> "Version": if self.major == 0: if self.minor != 0: return self._increment_minor() @@ -175,33 +175,33 @@ def next_breaking(self): # type: () -> Version return self._increment_major() @property - def first_prerelease(self): # type: () -> Version + def first_prerelease(self) -> "Version": return Version.parse( "{}.{}.{}-alpha.0".format(self.major, self.minor, self.patch) ) @property - def min(self): # type: () -> Version + def min(self) -> "Version": return self @property - def max(self): # type: () -> Version + def max(self) -> "Version": return self @property - def full_max(self): # type: () -> Version + def full_max(self) -> "Version": return self @property - def include_min(self): # type: () -> bool + def include_min(self) -> bool: return True @property - def include_max(self): # type: () -> bool + def include_max(self) -> bool: return True @classmethod - def parse(cls, text): # type: (str) -> Version + def parse(cls, text: str) -> "Version": try: match = COMPLETE_VERSION.match(text) except TypeError: @@ -225,33 +225,31 @@ def parse(cls, text): # type: (str) -> Version return Version(major, minor, patch, rest, pre, build, text) - def is_any(self): # type: () -> bool + def is_any(self) -> bool: return False - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return False - def is_prerelease(self): # type: () -> bool + def is_prerelease(self) -> bool: return len(self._prerelease) > 0 - def allows(self, version): # type: (Version) -> bool + def allows(self, version: "Version") -> bool: return self == version - def allows_all(self, other): # type: ("VersionTypes") -> bool + def allows_all(self, other: "VersionTypes") -> bool: return other.is_empty() or other == self - def allows_any(self, other): # type: ("VersionTypes") -> bool + def allows_any(self, other: "VersionTypes") -> bool: return other.allows(self) - def intersect( - self, other - ): # type: ("VersionTypes") -> Union[Version, EmptyConstraint] + def intersect(self, other: "VersionTypes") -> Union["Version", EmptyConstraint]: if other.allows(self): return self return EmptyConstraint() - def union(self, other): # type: ("VersionTypes") -> "VersionTypes" + def union(self, other: "VersionTypes") -> "VersionTypes": from .version_range import VersionRange if other.allows(self): @@ -276,33 +274,31 @@ def union(self, other): # type: ("VersionTypes") -> "VersionTypes" return VersionUnion.of(self, other) - def difference( - self, other - ): # type: ("VersionTypes") -> Union[Version, EmptyConstraint] + def difference(self, other: "VersionTypes") -> Union["Version", EmptyConstraint]: if other.allows(self): return EmptyConstraint() return self - def equals_without_prerelease(self, other): # type: (Version) -> bool + def equals_without_prerelease(self, other: "Version") -> bool: return ( self.major == other.major and self.minor == other.minor and self.patch == other.patch ) - def _increment_major(self): # type: () -> Version + def _increment_major(self) -> "Version": return Version(self.major + 1, 0, 0, precision=self._precision) - def _increment_minor(self): # type: () -> Version + def _increment_minor(self) -> "Version": return Version(self.major, self.minor + 1, 0, precision=self._precision) - def _increment_patch(self): # type: () -> Version + def _increment_patch(self) -> "Version": return Version( self.major, self.minor, self.patch + 1, precision=self._precision ) - def _normalize_prerelease(self, pre): # type: (str) -> Optional[str] + def _normalize_prerelease(self, pre: str) -> Optional[str]: if not pre: return @@ -327,7 +323,7 @@ def _normalize_prerelease(self, pre): # type: (str) -> Optional[str] return "{}.{}".format(modifier, number) - def _normalize_build(self, build): # type: (str) -> Optional[str] + def _normalize_build(self, build: str) -> Optional[str]: if not build: return @@ -339,7 +335,7 @@ def _normalize_build(self, build): # type: (str) -> Optional[str] return build - def _split_parts(self, text): # type: (str) -> List[Union[str, int]] + def _split_parts(self, text: str) -> List[Union[str, int]]: parts = text.split(".") for i, part in enumerate(parts): @@ -350,19 +346,19 @@ def _split_parts(self, text): # type: (str) -> List[Union[str, int]] return parts - def __lt__(self, other): # type: (Version) -> int + def __lt__(self, other: "Version") -> int: return self._cmp(other) < 0 - def __le__(self, other): # type: (Version) -> int + def __le__(self, other: "Version") -> int: return self._cmp(other) <= 0 - def __gt__(self, other): # type: (Version) -> int + def __gt__(self, other: "Version") -> int: return self._cmp(other) > 0 - def __ge__(self, other): # type: (Version) -> int + def __ge__(self, other: "Version") -> int: return self._cmp(other) >= 0 - def _cmp(self, other): # type: (Version) -> int + def _cmp(self, other: "Version") -> int: if not isinstance(other, VersionConstraint): return NotImplemented @@ -401,7 +397,7 @@ def _cmp(self, other): # type: (Version) -> int return self._cmp_lists(self.build, other.build) - def _cmp_parts(self, a, b): # type: (Optional[int], Optional[int]) -> int + def _cmp_parts(self, a: Optional[int], b: Optional[int]) -> int: if a < b: return -1 elif a > b: @@ -409,7 +405,7 @@ def _cmp_parts(self, a, b): # type: (Optional[int], Optional[int]) -> int return 0 - def _cmp_lists(self, a, b): # type: (List, List) -> int + def _cmp_lists(self, a: List, b: List) -> int: for i in range(max(len(a), len(b))): a_part = None if i < len(a): @@ -442,7 +438,7 @@ def _cmp_lists(self, a, b): # type: (List, List) -> int return 0 - def __eq__(self, other): # type: (Version) -> bool + def __eq__(self, other: "Version") -> bool: if not isinstance(other, Version): return NotImplemented @@ -455,16 +451,16 @@ def __eq__(self, other): # type: (Version) -> bool and self._build == other.build ) - def __ne__(self, other): # type: ("VersionTypes") -> bool + def __ne__(self, other: "VersionTypes") -> bool: return not self == other - def __str__(self): # type: () -> str + def __str__(self) -> str: return self._text - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "".format(str(self)) - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash( ( self.major, diff --git a/poetry/core/semver/version_constraint.py b/poetry/core/semver/version_constraint.py index 343efc8f7..127878779 100644 --- a/poetry/core/semver/version_constraint.py +++ b/poetry/core/semver/version_constraint.py @@ -2,30 +2,30 @@ if TYPE_CHECKING: - from poetry.core.semver import Version # noqa + from poetry.core.semver.version import Version # noqa class VersionConstraint: - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: raise NotImplementedError() - def is_any(self): # type: () -> bool + def is_any(self) -> bool: raise NotImplementedError() - def allows(self, version): # type: ("Version") -> bool + def allows(self, version: "Version") -> bool: raise NotImplementedError() - def allows_all(self, other): # type: (VersionConstraint) -> bool + def allows_all(self, other: "VersionConstraint") -> bool: raise NotImplementedError() - def allows_any(self, other): # type: (VersionConstraint) -> bool + def allows_any(self, other: "VersionConstraint") -> bool: raise NotImplementedError() - def intersect(self, other): # type: (VersionConstraint) -> VersionConstraint + def intersect(self, other: "VersionConstraint") -> "VersionConstraint": raise NotImplementedError() - def union(self, other): # type: (VersionConstraint) -> VersionConstraint + def union(self, other: "VersionConstraint") -> "VersionConstraint": raise NotImplementedError() - def difference(self, other): # type: (VersionConstraint) -> VersionConstraint + def difference(self, other: "VersionConstraint") -> "VersionConstraint": raise NotImplementedError() diff --git a/poetry/core/semver/version_range.py b/poetry/core/semver/version_range.py index 26f42f5d0..e07904f04 100644 --- a/poetry/core/semver/version_range.py +++ b/poetry/core/semver/version_range.py @@ -17,11 +17,11 @@ class VersionRange(VersionConstraint): def __init__( self, - min=None, # type: Optional["Version"] - max=None, # type: Optional["Version"] - include_min=False, # type: bool - include_max=False, # type: bool - always_include_max_prerelease=False, # type: bool + min: Optional["Version"] = None, + max: Optional["Version"] = None, + include_min: bool = False, + include_max: bool = False, + always_include_max_prerelease: bool = False, ): full_max = max if ( @@ -45,32 +45,32 @@ def __init__( self._include_max = include_max @property - def min(self): # type: () -> "Version" + def min(self) -> "Version": return self._min @property - def max(self): # type: () -> "Version" + def max(self) -> "Version": return self._max @property - def full_max(self): # type: () -> "Version" + def full_max(self) -> "Version": return self._full_max @property - def include_min(self): # type: () -> bool + def include_min(self) -> bool: return self._include_min @property - def include_max(self): # type: () -> bool + def include_max(self) -> bool: return self._include_max - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return False - def is_any(self): # type: () -> bool + def is_any(self) -> bool: return self._min is None and self._max is None - def allows(self, other): # type: ("Version") -> bool + def allows(self, other: "Version") -> bool: if self._min is not None: if other < self._min: return False @@ -87,7 +87,7 @@ def allows(self, other): # type: ("Version") -> bool return True - def allows_all(self, other): # type: ("VersionTypes") -> bool + def allows_all(self, other: "VersionTypes") -> bool: from .version import Version if other.is_empty(): @@ -104,7 +104,7 @@ def allows_all(self, other): # type: ("VersionTypes") -> bool raise ValueError("Unknown VersionConstraint type {}.".format(other)) - def allows_any(self, other): # type: ("VersionTypes") -> bool + def allows_any(self, other: "VersionTypes") -> bool: from .version import Version if other.is_empty(): @@ -123,7 +123,7 @@ def allows_any(self, other): # type: ("VersionTypes") -> bool raise ValueError("Unknown VersionConstraint type {}.".format(other)) - def intersect(self, other): # type: ("VersionTypes") -> "VersionTypes" + def intersect(self, other: "VersionTypes") -> "VersionTypes": from .version import Version if other.is_empty(): @@ -178,7 +178,7 @@ def intersect(self, other): # type: ("VersionTypes") -> "VersionTypes" intersect_min, intersect_max, intersect_include_min, intersect_include_max ) - def union(self, other): # type: ("VersionTypes") -> "VersionTypes" + def union(self, other: "VersionTypes") -> "VersionTypes": from .version import Version if isinstance(other, Version): @@ -230,7 +230,7 @@ def union(self, other): # type: ("VersionTypes") -> "VersionTypes" return VersionUnion.of(self, other) - def difference(self, other): # type: ("VersionTypes") -> "VersionTypes" + def difference(self, other: "VersionTypes") -> "VersionTypes": from .version import Version if other.is_empty(): @@ -289,7 +289,7 @@ def difference(self, other): # type: ("VersionTypes") -> "VersionTypes" return VersionUnion.of(before, after) elif isinstance(other, VersionUnion): - ranges = [] # type: List[VersionRange] + ranges: List[VersionRange] = [] current = self for range in other.ranges: @@ -320,7 +320,7 @@ def difference(self, other): # type: ("VersionTypes") -> "VersionTypes" raise ValueError("Unknown VersionConstraint type {}.".format(other)) - def allows_lower(self, other): # type: (VersionRange) -> bool + def allows_lower(self, other: "VersionRange") -> bool: if self.min is None: return other.min is not None @@ -335,7 +335,7 @@ def allows_lower(self, other): # type: (VersionRange) -> bool return self.include_min and not other.include_min - def allows_higher(self, other): # type: (VersionRange) -> bool + def allows_higher(self, other: "VersionRange") -> bool: if self.full_max is None: return other.max is not None @@ -350,7 +350,7 @@ def allows_higher(self, other): # type: (VersionRange) -> bool return self.include_max and not other.include_max - def is_strictly_lower(self, other): # type: (VersionRange) -> bool + def is_strictly_lower(self, other: "VersionRange") -> bool: if self.full_max is None or other.min is None: return False @@ -362,10 +362,10 @@ def is_strictly_lower(self, other): # type: (VersionRange) -> bool return not self.include_max or not other.include_min - def is_strictly_higher(self, other): # type: (VersionRange) -> bool + def is_strictly_higher(self, other: "VersionRange") -> bool: return other.is_strictly_lower(self) - def is_adjacent_to(self, other): # type: (VersionRange) -> bool + def is_adjacent_to(self, other: "VersionRange") -> bool: if self.max != other.min: return False @@ -376,7 +376,7 @@ def is_adjacent_to(self, other): # type: (VersionRange) -> bool and other.include_min ) - def __eq__(self, other): # type: (Any) -> int + def __eq__(self, other: Any) -> int: if not isinstance(other, VersionRange): return False @@ -387,19 +387,19 @@ def __eq__(self, other): # type: (Any) -> int and self._include_max == other.include_max ) - def __lt__(self, other): # type: (VersionRange) -> int + def __lt__(self, other: "VersionRange") -> int: return self._cmp(other) < 0 - def __le__(self, other): # type: (VersionRange) -> int + def __le__(self, other: "VersionRange") -> int: return self._cmp(other) <= 0 - def __gt__(self, other): # type: (VersionRange) -> int + def __gt__(self, other: "VersionRange") -> int: return self._cmp(other) > 0 - def __ge__(self, other): # type: (VersionRange) -> int + def __ge__(self, other: "VersionRange") -> int: return self._cmp(other) >= 0 - def _cmp(self, other): # type: (VersionRange) -> int + def _cmp(self, other: "VersionRange") -> int: if self.min is None: if other.min is None: return self._compare_max(other) @@ -417,7 +417,7 @@ def _cmp(self, other): # type: (VersionRange) -> int return self._compare_max(other) - def _compare_max(self, other): # type: (VersionRange) -> int + def _compare_max(self, other: "VersionRange") -> int: if self.max is None: if other.max is None: return 0 @@ -435,7 +435,7 @@ def _compare_max(self, other): # type: (VersionRange) -> int return 0 - def __str__(self): # type: () -> str + def __str__(self) -> str: text = "" if self.min is not None: @@ -453,10 +453,10 @@ def __str__(self): # type: () -> str return text - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "".format(str(self)) - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return ( hash(self.min) ^ hash(self.max) diff --git a/poetry/core/semver/version_union.py b/poetry/core/semver/version_union.py index 50a597db6..b37e99e8f 100644 --- a/poetry/core/semver/version_union.py +++ b/poetry/core/semver/version_union.py @@ -21,15 +21,15 @@ class VersionUnion(VersionConstraint): as a non-compound value. """ - def __init__(self, *ranges): # type: (*"VersionRange") -> None + def __init__(self, *ranges: "VersionRange") -> None: self._ranges = list(ranges) @property - def ranges(self): # type: () -> List["VersionRange"] + def ranges(self) -> List["VersionRange"]: return self._ranges @classmethod - def of(cls, *ranges): # type: (*"VersionTypes") -> "VersionTypes" + def of(cls, *ranges: "VersionTypes") -> "VersionTypes": from .version_range import VersionRange flattened = [] @@ -76,16 +76,16 @@ def of(cls, *ranges): # type: (*"VersionTypes") -> "VersionTypes" return VersionUnion(*merged) - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return False - def is_any(self): # type: () -> bool + def is_any(self) -> bool: return False - def allows(self, version): # type: ("Version") -> bool + def allows(self, version: "Version") -> bool: return any([constraint.allows(version) for constraint in self._ranges]) - def allows_all(self, other): # type: ("VersionTypes") -> bool + def allows_all(self, other: "VersionTypes") -> bool: our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) @@ -100,7 +100,7 @@ def allows_all(self, other): # type: ("VersionTypes") -> bool return their_current_range is None - def allows_any(self, other): # type: ("VersionTypes") -> bool + def allows_any(self, other: "VersionTypes") -> bool: our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) @@ -118,7 +118,7 @@ def allows_any(self, other): # type: ("VersionTypes") -> bool return False - def intersect(self, other): # type: ("VersionTypes") -> "VersionTypes" + def intersect(self, other: "VersionTypes") -> "VersionTypes": our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) new_ranges = [] @@ -139,10 +139,10 @@ def intersect(self, other): # type: ("VersionTypes") -> "VersionTypes" return VersionUnion.of(*new_ranges) - def union(self, other): # type: ("VersionTypes") -> "VersionTypes" + def union(self, other: "VersionTypes") -> "VersionTypes": return VersionUnion.of(self, other) - def difference(self, other): # type: ("VersionTypes") -> "VersionTypes" + def difference(self, other: "VersionTypes") -> "VersionTypes": our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) new_ranges = [] @@ -152,7 +152,7 @@ def difference(self, other): # type: ("VersionTypes") -> "VersionTypes" "their_range": next(their_ranges, None), } - def their_next_range(): # type: () -> bool + def their_next_range() -> bool: state["their_range"] = next(their_ranges, None) if state["their_range"]: return True @@ -165,7 +165,7 @@ def their_next_range(): # type: () -> bool return False - def our_next_range(include_current=True): # type: (bool) -> bool + def our_next_range(include_current: bool = True) -> bool: if include_current: new_ranges.append(state["current"]) @@ -222,7 +222,7 @@ def our_next_range(include_current=True): # type: (bool) -> bool return VersionUnion.of(*new_ranges) - def _ranges_for(self, constraint): # type: ("VersionTypes") -> List["VersionRange"] + def _ranges_for(self, constraint: "VersionTypes") -> List["VersionRange"]: from .version_range import VersionRange if constraint.is_empty(): @@ -236,19 +236,19 @@ def _ranges_for(self, constraint): # type: ("VersionTypes") -> List["VersionRan raise ValueError("Unknown VersionConstraint type {}".format(constraint)) - def excludes_single_version(self): # type: () -> bool + def excludes_single_version(self) -> bool: from .version import Version from .version_range import VersionRange return isinstance(VersionRange().difference(self), Version) - def __eq__(self, other): # type: (Any) -> bool + def __eq__(self, other: Any) -> bool: if not isinstance(other, VersionUnion): return False return self._ranges == other.ranges - def __hash__(self): # type: () -> int + def __hash__(self) -> int: h = hash(self._ranges[0]) for range in self._ranges[1:]: @@ -256,7 +256,7 @@ def __hash__(self): # type: () -> int return h - def __str__(self): # type: () -> str + def __str__(self) -> str: from .version_range import VersionRange if self.excludes_single_version(): @@ -264,5 +264,5 @@ def __str__(self): # type: () -> str return " || ".join([str(r) for r in self._ranges]) - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "".format(str(self)) diff --git a/poetry/core/spdx/__init__.py b/poetry/core/spdx/__init__.py index 713aa30df..e69de29bb 100644 --- a/poetry/core/spdx/__init__.py +++ b/poetry/core/spdx/__init__.py @@ -1,57 +0,0 @@ -import json -import os - -from io import open -from typing import Dict -from typing import Optional - -from .license import License -from .updater import Updater - - -_licenses = None # type: Optional[Dict[str, License]] - - -def license_by_id(identifier): # type: (str) -> License - if _licenses is None: - load_licenses() - - id = identifier.lower() - - if id not in _licenses: - if not identifier: - raise ValueError("A license identifier is required") - return License(identifier, identifier, False, False) - - return _licenses[id] - - -def load_licenses(): # type: () -> None - global _licenses - - _licenses = {} - - licenses_file = os.path.join(os.path.dirname(__file__), "data", "licenses.json") - - with open(licenses_file, encoding="utf-8") as f: - data = json.loads(f.read()) - - for name, license_info in data.items(): - license = License(name, license_info[0], license_info[1], license_info[2]) - _licenses[name.lower()] = license - - full_name = license_info[0].lower() - if full_name in _licenses: - existing_license = _licenses[full_name] - if not existing_license.is_deprecated: - continue - - _licenses[full_name] = license - - # Add a Proprietary license for non-standard licenses - _licenses["proprietary"] = License("Proprietary", "Proprietary", False, False) - - -if __name__ == "__main__": - updater = Updater() - updater.dump() diff --git a/poetry/core/spdx/helpers.py b/poetry/core/spdx/helpers.py new file mode 100644 index 000000000..a1a1d73c8 --- /dev/null +++ b/poetry/core/spdx/helpers.py @@ -0,0 +1,66 @@ +import json +import os + +from io import open +from typing import TYPE_CHECKING +from typing import Dict +from typing import Optional + + +if TYPE_CHECKING: + from .license import License # noqa + + +_licenses: Optional[Dict[str, "License"]] = None + + +def license_by_id(identifier: str) -> "License": + from .license import License # noqa + + if _licenses is None: + load_licenses() + + id = identifier.lower() + + if id not in _licenses: + if not identifier: + raise ValueError("A license identifier is required") + + return License(identifier, identifier, False, False) + + return _licenses[id] + + +def load_licenses() -> None: + from .license import License # noqa + + global _licenses + + _licenses = {} + + licenses_file = os.path.join(os.path.dirname(__file__), "data", "licenses.json") + + with open(licenses_file, encoding="utf-8") as f: + data = json.loads(f.read()) + + for name, license_info in data.items(): + license = License(name, license_info[0], license_info[1], license_info[2]) + _licenses[name.lower()] = license + + full_name = license_info[0].lower() + if full_name in _licenses: + existing_license = _licenses[full_name] + if not existing_license.is_deprecated: + continue + + _licenses[full_name] = license + + # Add a Proprietary license for non-standard licenses + _licenses["proprietary"] = License("Proprietary", "Proprietary", False, False) + + +if __name__ == "__main__": + from .updater import Updater + + updater = Updater() + updater.dump() diff --git a/poetry/core/spdx/license.py b/poetry/core/spdx/license.py index f5a9fb6d6..e37032cfe 100644 --- a/poetry/core/spdx/license.py +++ b/poetry/core/spdx/license.py @@ -131,7 +131,7 @@ class License(namedtuple("License", "id name is_osi_approved is_deprecated")): } @property - def classifier(self): # type: () -> str + def classifier(self) -> str: parts = ["License"] if self.is_osi_approved: @@ -144,7 +144,7 @@ def classifier(self): # type: () -> str return " :: ".join(parts) @property - def classifier_name(self): # type: () -> Optional[str] + def classifier_name(self) -> Optional[str]: if self.id not in self.CLASSIFIER_SUPPORTED: if self.is_osi_approved: return None diff --git a/poetry/core/spdx/updater.py b/poetry/core/spdx/updater.py index 30c3a5190..29adfaea3 100644 --- a/poetry/core/spdx/updater.py +++ b/poetry/core/spdx/updater.py @@ -17,10 +17,10 @@ class Updater: BASE_URL = "https://raw.githubusercontent.com/spdx/license-list-data/master/json/" - def __init__(self, base_url=BASE_URL): # type: (str) -> None + def __init__(self, base_url: str = BASE_URL) -> None: self._base_url = base_url - def dump(self, file=None): # type: (Optional[str]) -> None + def dump(self, file: Optional[str] = None) -> None: if file is None: file = os.path.join(os.path.dirname(__file__), "data", "licenses.json") @@ -31,7 +31,7 @@ def dump(self, file=None): # type: (Optional[str]) -> None json.dumps(self.get_licenses(licenses_url), indent=2, sort_keys=True) ) - def get_licenses(self, url): # type: (str) -> Dict[str, Any] + def get_licenses(self, url: str) -> Dict[str, Any]: licenses = {} with urlopen(url) as r: data = json.loads(r.read().decode()) diff --git a/poetry/core/toml/file.py b/poetry/core/toml/file.py index 574bdcb80..ca90b71d1 100644 --- a/poetry/core/toml/file.py +++ b/poetry/core/toml/file.py @@ -1,40 +1,41 @@ +from pathlib import Path from typing import TYPE_CHECKING from typing import Any from typing import Union -from tomlkit.exceptions import TOMLKitError from tomlkit.toml_file import TOMLFile as BaseTOMLFile -from poetry.core.toml import TOMLError -from poetry.core.utils._compat import Path - if TYPE_CHECKING: from tomlkit.toml_document import TOMLDocument # noqa class TOMLFile(BaseTOMLFile): - def __init__(self, path): # type: (Union[str, Path]) -> None + def __init__(self, path: Union[str, Path]) -> None: if isinstance(path, str): path = Path(path) super(TOMLFile, self).__init__(path.as_posix()) self.__path = path @property - def path(self): # type: () -> Path + def path(self) -> Path: return self.__path - def exists(self): # type: () -> bool + def exists(self) -> bool: return self.__path.exists() - def read(self): # type: () -> "TOMLDocument" + def read(self) -> "TOMLDocument": + from tomlkit.exceptions import TOMLKitError + + from poetry.core.toml import TOMLError + try: return super(TOMLFile, self).read() except (ValueError, TOMLKitError) as e: raise TOMLError("Invalid TOML file {}: {}".format(self.path.as_posix(), e)) - def __getattr__(self, item): # type: (str) -> Any + def __getattr__(self, item: str) -> Any: return getattr(self.__path, item) - def __str__(self): # type: () -> str + def __str__(self) -> str: return self.__path.as_posix() diff --git a/poetry/core/utils/_compat.py b/poetry/core/utils/_compat.py index 7c5daa9f6..4dd42bffd 100644 --- a/poetry/core/utils/_compat.py +++ b/poetry/core/utils/_compat.py @@ -1,53 +1,13 @@ import sys -from typing import AnyStr from typing import List -from typing import Optional -from typing import Union -import six.moves.urllib.parse as urllib_parse - -urlparse = urllib_parse - - -try: # Python 2 - long = long - unicode = unicode - basestring = basestring -except NameError: # Python 3 - long = int - unicode = str - basestring = str - - -PY2 = sys.version_info[0] == 2 -PY34 = sys.version_info >= (3, 4) -PY35 = sys.version_info >= (3, 5) PY36 = sys.version_info >= (3, 6) PY37 = sys.version_info >= (3, 7) WINDOWS = sys.platform == "win32" -if PY2: - import pipes - - shell_quote = pipes.quote -else: - import shlex - - shell_quote = shlex.quote - -if PY35: - from pathlib import Path # noqa -else: - from pathlib2 import Path # noqa - -if not PY36: - from collections import OrderedDict # noqa -else: - OrderedDict = dict - try: FileNotFoundError @@ -55,67 +15,7 @@ FileNotFoundError = IOError # noqa -def decode( - string, encodings=None -): # type: (Union[AnyStr, unicode], Optional[str]) -> Union[str, bytes] - if not PY2 and not isinstance(string, bytes): - return string - - if PY2 and isinstance(string, unicode): - return string - - encodings = encodings or ["utf-8", "latin1", "ascii"] - - for encoding in encodings: - try: - return string.decode(encoding) - except (UnicodeEncodeError, UnicodeDecodeError): - pass - - return string.decode(encodings[0], errors="ignore") - - -def encode( - string, encodings=None -): # type: (AnyStr, Optional[str]) -> Union[str, bytes] - if not PY2 and isinstance(string, bytes): - return string - - if PY2 and isinstance(string, str): - return string - - encodings = encodings or ["utf-8", "latin1", "ascii"] - - for encoding in encodings: - try: - return string.encode(encoding) - except (UnicodeEncodeError, UnicodeDecodeError): - pass - - return string.encode(encodings[0], errors="ignore") - - -def to_str(string): # type: (AnyStr) -> str - if isinstance(string, str) or not isinstance(string, (unicode, bytes)): - return string - - if PY2: - method = "encode" - else: - method = "decode" - - encodings = ["utf-8", "latin1", "ascii"] - - for encoding in encodings: - try: - return getattr(string, method)(encoding) - except (UnicodeEncodeError, UnicodeDecodeError): - pass - - return getattr(string, method)(encodings[0], errors="ignore") - - -def list_to_shell_command(cmd): # type: (List[str]) -> str +def list_to_shell_command(cmd: List[str]) -> str: executable = cmd[0] if " " in executable: diff --git a/poetry/core/utils/helpers.py b/poetry/core/utils/helpers.py index 6047e830c..22ccff21f 100644 --- a/poetry/core/utils/helpers.py +++ b/poetry/core/utils/helpers.py @@ -5,12 +5,12 @@ import tempfile from contextlib import contextmanager +from pathlib import Path from typing import Any from typing import Iterator from typing import List from typing import Union -from poetry.core.utils._compat import Path from poetry.core.version import Version @@ -23,26 +23,26 @@ _canonicalize_regex = re.compile(r"[-_]+") -def canonicalize_name(name): # type: (str) -> str +def canonicalize_name(name: str) -> str: return _canonicalize_regex.sub("-", name).lower() -def module_name(name): # type: (str) -> str +def module_name(name: str) -> str: return canonicalize_name(name).replace(".", "_").replace("-", "_") -def normalize_version(version): # type: (str) -> str +def normalize_version(version: str) -> str: return str(Version(version)) @contextmanager -def temporary_directory(*args, **kwargs): # type: (*Any, **Any) -> Iterator[str] +def temporary_directory(*args: Any, **kwargs: Any) -> Iterator[str]: name = tempfile.mkdtemp(*args, **kwargs) yield name safe_rmtree(name) -def parse_requires(requires): # type: (str) -> List[str] +def parse_requires(requires: str) -> List[str]: lines = requires.split("\n") requires_dist = [] @@ -83,7 +83,7 @@ def parse_requires(requires): # type: (str) -> List[str] return requires_dist -def _on_rm_error(func, path, exc_info): # type: (Any, Union[str, Path], Any) -> None +def _on_rm_error(func: Any, path: Union[str, Path], exc_info: Any) -> None: if not os.path.exists(path): return @@ -91,14 +91,14 @@ def _on_rm_error(func, path, exc_info): # type: (Any, Union[str, Path], Any) -> func(path) -def safe_rmtree(path): # type: (Union[str, Path]) -> None +def safe_rmtree(path: Union[str, Path]) -> None: if Path(path).is_symlink(): return os.unlink(str(path)) shutil.rmtree(path, onerror=_on_rm_error) -def merge_dicts(d1, d2): # type: (dict, dict) -> None +def merge_dicts(d1: dict, d2: dict) -> None: for k, v in d2.items(): if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], Mapping): merge_dicts(d1[k], d2[k]) diff --git a/poetry/core/utils/toml_file.py b/poetry/core/utils/toml_file.py index 7abf9a8a3..afb0f0c5d 100644 --- a/poetry/core/utils/toml_file.py +++ b/poetry/core/utils/toml_file.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from typing import Any from poetry.core.toml import TOMLFile @@ -6,12 +5,15 @@ class TomlFile(TOMLFile): @classmethod - def __new__(cls, *args, **kwargs): # type: (*Any, **Any) -> TOMLFile + def __new__(cls, *args: Any, **kwargs: Any) -> TOMLFile: import warnings warnings.warn( "Use of {}.{} has been deprecated, use {}.{} instead.".format( - cls.__module__, cls.__name__, TOMLFile.__module__, TOMLFile.__name__, + cls.__module__, + cls.__name__, + TOMLFile.__module__, + TOMLFile.__name__, ), category=DeprecationWarning, stacklevel=2, diff --git a/poetry/core/vcs/__init__.py b/poetry/core/vcs/__init__.py index d831b2a28..4c8beb7d7 100644 --- a/poetry/core/vcs/__init__.py +++ b/poetry/core/vcs/__init__.py @@ -1,22 +1,23 @@ import os import subprocess -from poetry.core.utils._compat import Path -from poetry.core.utils._compat import decode +from pathlib import Path from .git import Git -def get_vcs(directory): # type: (Path) -> Git +def get_vcs(directory: Path) -> Git: working_dir = Path.cwd() os.chdir(str(directory.resolve())) try: - git_dir = decode( + git_dir = ( subprocess.check_output( ["git", "rev-parse", "--show-toplevel"], stderr=subprocess.STDOUT ) - ).strip() + .decode() + .strip() + ) vcs = Git(Path(git_dir)) diff --git a/poetry/core/vcs/git.py b/poetry/core/vcs/git.py index e1b7a8711..1633ad090 100644 --- a/poetry/core/vcs/git.py +++ b/poetry/core/vcs/git.py @@ -1,14 +1,11 @@ -# -*- coding: utf-8 -*- import re import subprocess from collections import namedtuple +from pathlib import Path from typing import Any from typing import Optional -from poetry.core.utils._compat import Path -from poetry.core.utils._compat import decode - pattern_formats = { "protocol": r"\w+", @@ -95,13 +92,13 @@ class ParsedUrl: def __init__( self, - protocol, # type: Optional[str] - resource, # type: Optional[str] - pathname, # type: Optional[str] - user, # type: Optional[str] - port, # type: Optional[str] - name, # type: Optional[str] - rev, # type: Optional[str] + protocol: Optional[str], + resource: Optional[str], + pathname: Optional[str], + user: Optional[str], + port: Optional[str], + name: Optional[str], + rev: Optional[str], ): self.protocol = protocol self.resource = resource @@ -112,7 +109,7 @@ def __init__( self.rev = rev @classmethod - def parse(cls, url): # type: (str) -> ParsedUrl + def parse(cls, url: str) -> "ParsedUrl": for pattern in PATTERNS: m = pattern.match(url) if m: @@ -130,7 +127,7 @@ def parse(cls, url): # type: (str) -> ParsedUrl raise ValueError('Invalid git url "{}"'.format(url)) @property - def url(self): # type: () -> str + def url(self) -> str: return "{}{}{}{}{}".format( "{}://".format(self.protocol) if self.protocol else "", "{}@".format(self.user) if self.user else "", @@ -139,10 +136,10 @@ def url(self): # type: () -> str "/" + self.pathname.lstrip(":/"), ) - def format(self): # type: () -> str + def format(self) -> str: return self.url - def __str__(self): # type: () -> str + def __str__(self) -> str: return self.format() @@ -150,15 +147,13 @@ def __str__(self): # type: () -> str class GitConfig: - def __init__(self, requires_git_presence=False): # type: (bool) -> None + def __init__(self, requires_git_presence: bool = False) -> None: self._config = {} try: - config_list = decode( - subprocess.check_output( - ["git", "config", "-l"], stderr=subprocess.STDOUT - ) - ) + config_list = subprocess.check_output( + ["git", "config", "-l"], stderr=subprocess.STDOUT + ).decode() m = re.findall("(?ms)^([^=]+)=(.*?)$", config_list) if m: @@ -168,20 +163,20 @@ def __init__(self, requires_git_presence=False): # type: (bool) -> None if requires_git_presence: raise - def get(self, key, default=None): # type: (Any, Optional[Any]) -> Any + def get(self, key: Any, default: Optional[Any] = None) -> Any: return self._config.get(key, default) - def __getitem__(self, item): # type: (Any) -> Any + def __getitem__(self, item: Any) -> Any: return self._config[item] class Git: - def __init__(self, work_dir=None): # type: (Optional[Path]) -> None + def __init__(self, work_dir: Optional[Path] = None) -> None: self._config = GitConfig(requires_git_presence=True) self._work_dir = work_dir @classmethod - def normalize_url(cls, url): # type: (str) -> GitUrl + def normalize_url(cls, url: str) -> GitUrl: parsed = ParsedUrl.parse(url) formatted = re.sub(r"^git\+", "", url) @@ -205,13 +200,13 @@ def normalize_url(cls, url): # type: (str) -> GitUrl return GitUrl(re.sub(r"#[^#]*$", "", normalized), parsed.rev) @property - def config(self): # type: () -> GitConfig + def config(self) -> GitConfig: return self._config - def clone(self, repository, dest): # type: (str, Path) -> str + def clone(self, repository: str, dest: Path) -> str: return self.run("clone", "--recurse-submodules", repository, str(dest)) - def checkout(self, rev, folder=None): # type: (str, Optional[Path]) -> str + def checkout(self, rev: str, folder: Optional[Path] = None) -> str: args = [] if folder is None and self._work_dir: folder = self._work_dir @@ -228,7 +223,7 @@ def checkout(self, rev, folder=None): # type: (str, Optional[Path]) -> str return self.run(*args) - def rev_parse(self, rev, folder=None): # type: (str, Optional[Path]) -> str + def rev_parse(self, rev: str, folder: Optional[Path] = None) -> str: args = [] if folder is None and self._work_dir: folder = self._work_dir @@ -253,7 +248,7 @@ def rev_parse(self, rev, folder=None): # type: (str, Optional[Path]) -> str return self.run(*args) - def get_ignored_files(self, folder=None): # type: (Optional[Path]) -> list + def get_ignored_files(self, folder: Optional[Path] = None) -> list: args = [] if folder is None and self._work_dir: folder = self._work_dir @@ -271,7 +266,7 @@ def get_ignored_files(self, folder=None): # type: (Optional[Path]) -> list return output.strip().split("\n") - def remote_urls(self, folder=None): # type: (Optional[Path]) -> dict + def remote_urls(self, folder: Optional[Path] = None) -> dict: output = self.run( "config", "--get-regexp", r"remote\..*\.url", folder=folder ).strip() @@ -283,12 +278,12 @@ def remote_urls(self, folder=None): # type: (Optional[Path]) -> dict return urls - def remote_url(self, folder=None): # type: (Optional[Path]) -> str + def remote_url(self, folder: Optional[Path] = None) -> str: urls = self.remote_urls(folder=folder) return urls.get("remote.origin.url", urls[list(urls.keys())[0]]) - def run(self, *args, **kwargs): # type: (*Any, **Any) -> str + def run(self, *args: Any, **kwargs: Any) -> str: folder = kwargs.pop("folder", None) if folder: args = ( @@ -298,6 +293,8 @@ def run(self, *args, **kwargs): # type: (*Any, **Any) -> str folder.as_posix(), ) + args - return decode( + return ( subprocess.check_output(["git"] + list(args), stderr=subprocess.STDOUT) - ).strip() + .decode() + .strip() + ) diff --git a/poetry/core/version/__init__.py b/poetry/core/version/__init__.py index 62d0349fe..3b106577e 100644 --- a/poetry/core/version/__init__.py +++ b/poetry/core/version/__init__.py @@ -26,9 +26,9 @@ def parse( - version, # type: str - strict=False, # type: bool -): # type:(...) -> Union[Version, LegacyVersion] + version: str, + strict: bool = False, +) -> Union[Version, LegacyVersion]: """ Parse the given version string and return either a :class:`Version` object or a LegacyVersion object depending on if the given version is diff --git a/poetry/core/version/base.py b/poetry/core/version/base.py index 826f86226..78197da90 100644 --- a/poetry/core/version/base.py +++ b/poetry/core/version/base.py @@ -2,32 +2,32 @@ class BaseVersion: - def __init__(self, version): # type: (str) -> None + def __init__(self, version: str) -> None: self._version = str(version) self._key = None - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash(self._key) - def __lt__(self, other): # type: (BaseVersion) -> bool + def __lt__(self, other: "BaseVersion") -> bool: return self._compare(other, lambda s, o: s < o) - def __le__(self, other): # type: (BaseVersion) -> bool + def __le__(self, other: "BaseVersion") -> bool: return self._compare(other, lambda s, o: s <= o) - def __eq__(self, other): # type: (BaseVersion) -> bool + def __eq__(self, other: "BaseVersion") -> bool: return self._compare(other, lambda s, o: s == o) - def __ge__(self, other): # type: (BaseVersion) -> bool + def __ge__(self, other: "BaseVersion") -> bool: return self._compare(other, lambda s, o: s >= o) - def __gt__(self, other): # type: (BaseVersion) -> bool + def __gt__(self, other: "BaseVersion") -> bool: return self._compare(other, lambda s, o: s > o) - def __ne__(self, other): # type: (BaseVersion) -> bool + def __ne__(self, other: "BaseVersion") -> bool: return self._compare(other, lambda s, o: s != o) - def _compare(self, other, method): # type: (BaseVersion, Callable) -> bool + def _compare(self, other: "BaseVersion", method: Callable) -> bool: if not isinstance(other, BaseVersion): return NotImplemented diff --git a/poetry/core/version/grammars/parser.py b/poetry/core/version/grammars/parser.py new file mode 100644 index 000000000..13b8d76e3 --- /dev/null +++ b/poetry/core/version/grammars/parser.py @@ -0,0 +1,26 @@ +import os + +from typing import TYPE_CHECKING +from typing import Optional + + +if TYPE_CHECKING: + from lark import Lark # noqa + from lark import Tree # noqa + + +class Parser: + def __init__(self, grammar: str) -> None: + self._grammar = grammar + self._parser: Optional["Lark"] = None + + def parse(self, string: str) -> "Tree": + from lark import Lark + + if self._parser is None: + self._parser = Lark.open( + os.path.join(os.path.dirname(__file__), f"{self._grammar}.lark"), + parser="lalr", + ) + + return self._parser.parse(string) diff --git a/poetry/core/version/helpers.py b/poetry/core/version/helpers.py index bd46e8d2d..df64b8e0f 100644 --- a/poetry/core/version/helpers.py +++ b/poetry/core/version/helpers.py @@ -1,13 +1,13 @@ from typing import TYPE_CHECKING from typing import Union -from poetry.core.semver import Version -from poetry.core.semver import VersionUnion -from poetry.core.semver import parse_constraint +from poetry.core.semver.helpers import parse_constraint +from poetry.core.semver.version import Version +from poetry.core.semver.version_union import VersionUnion if TYPE_CHECKING: - from poetry.core.semver import VersionConstraint # noqa + from poetry.core.semver.version_constraint import VersionConstraint # noqa PYTHON_VERSION = [ "2.7.*", @@ -25,8 +25,8 @@ def format_python_constraint( - constraint, -): # type: (Union[Version, VersionUnion, "VersionConstraint"]) -> str + constraint: Union[Version, VersionUnion, "VersionConstraint"], +) -> str: """ This helper will help in transforming disjunctive constraint into proper constraint. diff --git a/poetry/core/version/legacy_version.py b/poetry/core/version/legacy_version.py index adaa53d7e..49b62aeb3 100644 --- a/poetry/core/version/legacy_version.py +++ b/poetry/core/version/legacy_version.py @@ -6,34 +6,34 @@ class LegacyVersion(BaseVersion): - def __init__(self, version): # type: (str) -> None + def __init__(self, version: str) -> None: self._version = str(version) self._key = _legacy_cmpkey(self._version) - def __str__(self): # type: () -> str + def __str__(self) -> str: return self._version - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "".format(repr(str(self))) @property - def public(self): # type: () -> str + def public(self) -> str: return self._version @property - def base_version(self): # type: () -> str + def base_version(self) -> str: return self._version @property - def local(self): # type: () -> None + def local(self) -> None: return None @property - def is_prerelease(self): # type: () -> bool + def is_prerelease(self) -> bool: return False @property - def is_postrelease(self): # type: () -> bool + def is_postrelease(self) -> bool: return False @@ -48,7 +48,7 @@ def is_postrelease(self): # type: () -> bool } -def _parse_version_parts(s): # type: (str) -> str +def _parse_version_parts(s: str) -> str: for part in _legacy_version_component_re.split(s): part = _legacy_version_replacement_map.get(part, part) @@ -65,7 +65,7 @@ def _parse_version_parts(s): # type: (str) -> str yield "*final" -def _legacy_cmpkey(version): # type: (str) -> Tuple[int, Tuple[str]] +def _legacy_cmpkey(version: str) -> Tuple[int, Tuple[str]]: # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch # greater than or equal to 0. This will effectively put the LegacyVersion, # which uses the defacto standard originally implemented by setuptools, diff --git a/poetry/core/version/markers.py b/poetry/core/version/markers.py index 894e1dc17..af7d86e46 100644 --- a/poetry/core/version/markers.py +++ b/poetry/core/version/markers.py @@ -1,4 +1,3 @@ -import os import re from typing import TYPE_CHECKING @@ -8,13 +7,13 @@ from typing import List from typing import Union -from lark import Lark -from lark import Token -from lark import Tree +from .grammars.parser import Parser if TYPE_CHECKING: - from poetry.core.semver import VersionTypes # noqa + from lark import Tree # noqa + + from poetry.core.semver.helpers import VersionTypes # noqa MarkerTypes = Union[ "AnyMarker", "EmptyMarker", "SingleMarker", "MultiMarker", "MarkerUnion" @@ -48,81 +47,81 @@ class UndefinedEnvironmentName(ValueError): "platform.python_implementation": "platform_python_implementation", "python_implementation": "platform_python_implementation", } -_parser = Lark.open( - os.path.join(os.path.dirname(__file__), "grammars", "markers.lark"), parser="lalr" -) + + +_parser = Parser("markers") class BaseMarker(object): - def intersect(self, other): # type: (BaseMarker) -> BaseMarker + def intersect(self, other: "BaseMarker") -> "BaseMarker": raise NotImplementedError() - def union(self, other): # type: (BaseMarker) -> BaseMarker + def union(self, other: "BaseMarker") -> "BaseMarker": raise NotImplementedError() - def is_any(self): # type: () -> bool + def is_any(self) -> bool: return False - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return False - def validate(self, environment): # type: (Dict[str, Any]) -> bool + def validate(self, environment: Dict[str, Any]) -> bool: raise NotImplementedError() - def without_extras(self): # type: () -> BaseMarker + def without_extras(self) -> "BaseMarker": raise NotImplementedError() - def exclude(self, marker_name): # type: (str) -> BaseMarker + def exclude(self, marker_name: str) -> "BaseMarker": raise NotImplementedError() - def only(self, *marker_names): # type: (str) -> BaseMarker + def only(self, *marker_names: str) -> "BaseMarker": raise NotImplementedError() - def invert(self): # type: () -> BaseMarker + def invert(self) -> "BaseMarker": raise NotImplementedError() - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "<{} {}>".format(self.__class__.__name__, str(self)) class AnyMarker(BaseMarker): - def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes + def intersect(self, other: MarkerTypes) -> MarkerTypes: return other - def union(self, other): # type: (MarkerTypes) -> MarkerTypes + def union(self, other: MarkerTypes) -> MarkerTypes: return self - def is_any(self): # type: () -> bool + def is_any(self) -> bool: return True - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return False - def validate(self, environment): # type: (Dict[str, Any]) -> bool + def validate(self, environment: Dict[str, Any]) -> bool: return True - def without_extras(self): # type: () -> MarkerTypes + def without_extras(self) -> MarkerTypes: return self - def exclude(self, marker_name): # type: (str) -> MarkerTypes + def exclude(self, marker_name: str) -> MarkerTypes: return self - def only(self, *marker_names): # type: (*str) -> MarkerTypes + def only(self, *marker_names: str) -> MarkerTypes: return self - def invert(self): # type: () -> EmptyMarker + def invert(self) -> "EmptyMarker": return EmptyMarker() - def __str__(self): # type: () -> str + def __str__(self) -> str: return "" - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "" - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash(("", "")) - def __eq__(self, other): # type: (MarkerTypes) -> bool + def __eq__(self, other: MarkerTypes) -> bool: if not isinstance(other, BaseMarker): return NotImplemented @@ -130,43 +129,43 @@ def __eq__(self, other): # type: (MarkerTypes) -> bool class EmptyMarker(BaseMarker): - def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes + def intersect(self, other: MarkerTypes) -> MarkerTypes: return self - def union(self, other): # type: (MarkerTypes) -> MarkerTypes + def union(self, other: MarkerTypes) -> MarkerTypes: return other - def is_any(self): # type: () -> bool + def is_any(self) -> bool: return False - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return True - def validate(self, environment): # type: (Dict[str, Any]) -> bool + def validate(self, environment: Dict[str, Any]) -> bool: return False - def without_extras(self): # type: () -> BaseMarker + def without_extras(self) -> BaseMarker: return self - def exclude(self, marker_name): # type: (str) -> EmptyMarker + def exclude(self, marker_name: str) -> "EmptyMarker": return self - def only(self, *marker_names): # type: (*str) -> EmptyMarker + def only(self, *marker_names: str) -> "EmptyMarker": return self - def invert(self): # type: () -> AnyMarker + def invert(self) -> AnyMarker: return AnyMarker() - def __str__(self): # type: () -> str + def __str__(self) -> str: return "" - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "" - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash(("", "")) - def __eq__(self, other): # type: (MarkerTypes) -> bool + def __eq__(self, other: MarkerTypes) -> bool: if not isinstance(other, BaseMarker): return NotImplemented @@ -182,13 +181,11 @@ class SingleMarker(BaseMarker): "platform_release", } - def __init__( - self, name, constraint - ): # type: (str, Union[str, "VersionTypes"]) -> None + def __init__(self, name: str, constraint: Union[str, "VersionTypes"]) -> None: from poetry.core.packages.constraints import ( parse_constraint as parse_generic_constraint, ) - from poetry.core.semver import parse_constraint + from poetry.core.semver.helpers import parse_constraint self._name = ALIASES.get(name, name) self._constraint_string = str(constraint) @@ -228,29 +225,29 @@ def __init__( self._constraint = self._parser(self._constraint_string) @property - def name(self): # type: () -> str + def name(self) -> str: return self._name @property - def constraint_string(self): # type: () -> str + def constraint_string(self) -> str: if self._operator in {"in", "not in"}: return "{} {}".format(self._operator, self._value) return self._constraint_string @property - def constraint(self): # type: () -> "VersionTypes" + def constraint(self) -> "VersionTypes": return self._constraint @property - def operator(self): # type: () -> str + def operator(self) -> str: return self._operator @property - def value(self): # type: () -> str + def value(self) -> str: return self._value - def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes + def intersect(self, other: MarkerTypes) -> MarkerTypes: if isinstance(other, SingleMarker): if other.name != self.name: return MultiMarker(self, other) @@ -272,7 +269,7 @@ def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes return other.intersect(self) - def union(self, other): # type: (MarkerTypes) -> MarkerTypes + def union(self, other: MarkerTypes) -> MarkerTypes: if isinstance(other, SingleMarker): if self == other: return self @@ -281,7 +278,7 @@ def union(self, other): # type: (MarkerTypes) -> MarkerTypes return other.union(self) - def validate(self, environment): # type: (Dict[str, Any]) -> bool + def validate(self, environment: Dict[str, Any]) -> bool: if environment is None: return True @@ -290,22 +287,22 @@ def validate(self, environment): # type: (Dict[str, Any]) -> bool return self._constraint.allows(self._parser(environment[self._name])) - def without_extras(self): # type: () -> MarkerTypes + def without_extras(self) -> MarkerTypes: return self.exclude("extra") - def exclude(self, marker_name): # type: (str) -> MarkerTypes + def exclude(self, marker_name: str) -> MarkerTypes: if self.name == marker_name: return AnyMarker() return self - def only(self, *marker_names): # type: (*str) -> Union[SingleMarker, EmptyMarker] + def only(self, *marker_names: str) -> Union["SingleMarker", EmptyMarker]: if self.name not in marker_names: return EmptyMarker() return self - def invert(self): # type: () -> MarkerTypes + def invert(self) -> MarkerTypes: if self._operator in ("===", "=="): operator = "!=" elif self._operator == "!=": @@ -326,7 +323,7 @@ def invert(self): # type: () -> MarkerTypes # This one is more tricky to handle # since it's technically a multi marker # so the inverse will be a union of inverse - from poetry.core.semver import VersionRange + from poetry.core.semver.version_range import VersionRange if not isinstance(self._constraint, VersionRange): # The constraint must be a version range, otherwise @@ -350,22 +347,22 @@ def invert(self): # type: () -> MarkerTypes return parse_marker("{} {} '{}'".format(self._name, operator, self._value)) - def __eq__(self, other): # type: (MarkerTypes) -> bool + def __eq__(self, other: MarkerTypes) -> bool: if not isinstance(other, SingleMarker): return False return self._name == other.name and self._constraint == other.constraint - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash((self._name, self._constraint_string)) - def __str__(self): # type: () -> str + def __str__(self) -> str: return '{} {} "{}"'.format(self._name, self._operator, self._value) def _flatten_markers( - markers, flatten_class -): # type: (Iterator[Union[MarkerUnion, MultiMarker]], Any) -> List[MarkerTypes] + markers: Iterator[Union["MarkerUnion", "MultiMarker"]], flatten_class: Any +) -> List[MarkerTypes]: flattened = [] for marker in markers: @@ -378,7 +375,7 @@ def _flatten_markers( class MultiMarker(BaseMarker): - def __init__(self, *markers): # type: (*MarkerTypes) -> None + def __init__(self, *markers: MarkerTypes) -> None: self._markers = [] markers = _flatten_markers(markers, MultiMarker) @@ -387,7 +384,7 @@ def __init__(self, *markers): # type: (*MarkerTypes) -> None self._markers.append(m) @classmethod - def of(cls, *markers): # type: (*MarkerTypes) -> MarkerTypes + def of(cls, *markers: MarkerTypes) -> MarkerTypes: new_markers = [] markers = _flatten_markers(markers, MultiMarker) @@ -431,10 +428,10 @@ def of(cls, *markers): # type: (*MarkerTypes) -> MarkerTypes return MultiMarker(*new_markers) @property - def markers(self): # type: () -> List[MarkerTypes] + def markers(self) -> List[MarkerTypes]: return self._markers - def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes + def intersect(self, other: MarkerTypes) -> MarkerTypes: if other.is_any(): return self @@ -445,23 +442,23 @@ def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes return MultiMarker.of(*new_markers) - def union(self, other): # type: (MarkerTypes) -> MarkerTypes + def union(self, other: MarkerTypes) -> MarkerTypes: if isinstance(other, (SingleMarker, MultiMarker)): return MarkerUnion.of(self, other) return other.union(self) - def validate(self, environment): # type: (Dict[str, Any]) -> bool + def validate(self, environment: Dict[str, Any]) -> bool: for m in self._markers: if not m.validate(environment): return False return True - def without_extras(self): # type: () -> MarkerTypes + def without_extras(self) -> MarkerTypes: return self.exclude("extra") - def exclude(self, marker_name): # type: (str) -> MarkerTypes + def exclude(self, marker_name: str) -> MarkerTypes: new_markers = [] for m in self._markers: @@ -476,7 +473,7 @@ def exclude(self, marker_name): # type: (str) -> MarkerTypes return self.of(*new_markers) - def only(self, *marker_names): # type: (*str) -> MarkerTypes + def only(self, *marker_names: str) -> MarkerTypes: new_markers = [] for m in self._markers: @@ -491,25 +488,25 @@ def only(self, *marker_names): # type: (*str) -> MarkerTypes return self.of(*new_markers) - def invert(self): # type: () -> MarkerTypes + def invert(self) -> MarkerTypes: markers = [marker.invert() for marker in self._markers] return MarkerUnion.of(*markers) - def __eq__(self, other): # type: (MarkerTypes) -> bool + def __eq__(self, other: MarkerTypes) -> bool: if not isinstance(other, MultiMarker): return False return set(self._markers) == set(other.markers) - def __hash__(self): # type: () -> int + def __hash__(self) -> int: h = hash("multi") for m in self._markers: h |= hash(m) return h - def __str__(self): # type: () -> str + def __str__(self) -> str: elements = [] for m in self._markers: if isinstance(m, SingleMarker): @@ -523,15 +520,15 @@ def __str__(self): # type: () -> str class MarkerUnion(BaseMarker): - def __init__(self, *markers): # type: (*MarkerTypes) -> None + def __init__(self, *markers: MarkerTypes) -> None: self._markers = list(markers) @property - def markers(self): # type: () -> List[MarkerTypes] + def markers(self) -> List[MarkerTypes]: return self._markers @classmethod - def of(cls, *markers): # type: (*BaseMarker) -> MarkerTypes + def of(cls, *markers: BaseMarker) -> MarkerTypes: flattened_markers = _flatten_markers(markers, MarkerUnion) markers = [] @@ -574,13 +571,13 @@ def of(cls, *markers): # type: (*BaseMarker) -> MarkerTypes return MarkerUnion(*markers) - def append(self, marker): # type: (MarkerTypes) -> None + def append(self, marker: MarkerTypes) -> None: if marker in self._markers: return self._markers.append(marker) - def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes + def intersect(self, other: MarkerTypes) -> MarkerTypes: if other.is_any(): return self @@ -604,7 +601,7 @@ def intersect(self, other): # type: (MarkerTypes) -> MarkerTypes return MarkerUnion.of(*new_markers) - def union(self, other): # type: (MarkerTypes) -> MarkerTypes + def union(self, other: MarkerTypes) -> MarkerTypes: if other.is_any(): return other @@ -615,17 +612,17 @@ def union(self, other): # type: (MarkerTypes) -> MarkerTypes return MarkerUnion.of(*new_markers) - def validate(self, environment): # type: (Dict[str, Any]) -> bool + def validate(self, environment: Dict[str, Any]) -> bool: for m in self._markers: if m.validate(environment): return True return False - def without_extras(self): # type: () -> MarkerTypes + def without_extras(self) -> MarkerTypes: return self.exclude("extra") - def exclude(self, marker_name): # type: (str) -> MarkerTypes + def exclude(self, marker_name: str) -> MarkerTypes: new_markers = [] for m in self._markers: @@ -640,7 +637,7 @@ def exclude(self, marker_name): # type: (str) -> MarkerTypes return self.of(*new_markers) - def only(self, *marker_names): # type: (*str) -> MarkerTypes + def only(self, *marker_names: str) -> MarkerTypes: new_markers = [] for m in self._markers: @@ -655,37 +652,37 @@ def only(self, *marker_names): # type: (*str) -> MarkerTypes return self.of(*new_markers) - def invert(self): # type: () -> MarkerTypes + def invert(self) -> MarkerTypes: markers = [marker.invert() for marker in self._markers] return MultiMarker.of(*markers) - def __eq__(self, other): # type: (MarkerTypes) -> bool + def __eq__(self, other: MarkerTypes) -> bool: if not isinstance(other, MarkerUnion): return False return set(self._markers) == set(other.markers) - def __hash__(self): # type: () -> int + def __hash__(self) -> int: h = hash("union") for m in self._markers: h |= hash(m) return h - def __str__(self): # type: () -> str + def __str__(self) -> str: return " or ".join( str(m) for m in self._markers if not m.is_any() and not m.is_empty() ) - def is_any(self): # type: () -> bool + def is_any(self) -> bool: return any(m.is_any() for m in self._markers) - def is_empty(self): # type: () -> bool + def is_empty(self) -> bool: return all(m.is_empty() for m in self._markers) -def parse_marker(marker): # type: (str) -> MarkerTypes +def parse_marker(marker: str) -> MarkerTypes: if marker == "": return EmptyMarker() @@ -699,7 +696,9 @@ def parse_marker(marker): # type: (str) -> MarkerTypes return markers -def _compact_markers(tree_elements, tree_prefix=""): # type: (Tree, str) -> MarkerTypes +def _compact_markers(tree_elements: "Tree", tree_prefix: str = "") -> MarkerTypes: + from lark import Token + groups = [MultiMarker()] for token in tree_elements: if isinstance(token, Token): @@ -715,7 +714,10 @@ def _compact_markers(tree_elements, tree_prefix=""): # type: (Tree, str) -> Mar elif token.data == "{}item".format(tree_prefix): name, op, value = token.children if value.type == "{}MARKER_NAME".format(tree_prefix): - name, value, = value, name + name, value, = ( + value, + name, + ) value = value[1:-1] groups[-1] = MultiMarker.of( diff --git a/poetry/core/version/requirements.py b/poetry/core/version/requirements.py index c9601a07f..1016bf612 100644 --- a/poetry/core/version/requirements.py +++ b/poetry/core/version/requirements.py @@ -1,37 +1,19 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function +import urllib.parse as urlparse -import os - -from lark import Lark -from lark import UnexpectedCharacters -from lark import UnexpectedToken - -from poetry.core.semver import parse_constraint from poetry.core.semver.exceptions import ParseConstraintError +from poetry.core.semver.helpers import parse_constraint +from .grammars.parser import Parser from .markers import _compact_markers -try: - import urllib.parse as urlparse -except ImportError: - import urlparse - - class InvalidRequirement(ValueError): """ An invalid requirement was found, users should refer to PEP 508. """ -_parser = Lark.open( - os.path.join(os.path.dirname(__file__), "grammars", "pep508.lark"), parser="lalr" -) +_parser = Parser("pep508") class Requirement(object): @@ -43,7 +25,10 @@ class Requirement(object): string. """ - def __init__(self, requirement_string): # type: (str) -> None + def __init__(self, requirement_string: str) -> None: + from lark import UnexpectedCharacters + from lark import UnexpectedToken + try: parsed = _parser.parse(requirement_string) except (UnexpectedCharacters, UnexpectedToken) as e: @@ -101,7 +86,7 @@ def __init__(self, requirement_string): # type: (str) -> None self.marker = marker - def __str__(self): # type: () -> str + def __str__(self) -> str: parts = [self.name] if self.extras: @@ -118,5 +103,5 @@ def __str__(self): # type: () -> str return "".join(parts) - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "".format(str(self)) diff --git a/poetry/core/version/utils.py b/poetry/core/version/utils.py index a81a9e7f2..761fe3d36 100644 --- a/poetry/core/version/utils.py +++ b/poetry/core/version/utils.py @@ -2,31 +2,31 @@ class Infinity(object): - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "Infinity" - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash(repr(self)) - def __lt__(self, other): # type: (Any) -> bool + def __lt__(self, other: Any) -> bool: return False - def __le__(self, other): # type: (Any) -> bool + def __le__(self, other: Any) -> bool: return False - def __eq__(self, other): # type: (Any) -> bool + def __eq__(self, other: Any) -> bool: return isinstance(other, self.__class__) - def __ne__(self, other): # type: (Any) -> bool + def __ne__(self, other: Any) -> bool: return not isinstance(other, self.__class__) - def __gt__(self, other): # type: (Any) -> bool + def __gt__(self, other: Any) -> bool: return True - def __ge__(self, other): # type: (Any) -> bool + def __ge__(self, other: Any) -> bool: return True - def __neg__(self): # type: () -> NegativeInfinity + def __neg__(self) -> "NegativeInfinity": return NegativeInfinity @@ -34,31 +34,31 @@ def __neg__(self): # type: () -> NegativeInfinity class NegativeInfinity(object): - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "-Infinity" - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash(repr(self)) - def __lt__(self, other): # type: (Any) -> bool + def __lt__(self, other: Any) -> bool: return True - def __le__(self, other): # type: (Any) -> bool + def __le__(self, other: Any) -> bool: return True - def __eq__(self, other): # type: (Any) -> bool + def __eq__(self, other: Any) -> bool: return isinstance(other, self.__class__) - def __ne__(self, other): # type: (Any) -> bool + def __ne__(self, other: Any) -> bool: return not isinstance(other, self.__class__) - def __gt__(self, other): # type: (Any) -> bool + def __gt__(self, other: Any) -> bool: return False - def __ge__(self, other): # type: (Any) -> bool + def __ge__(self, other: Any) -> bool: return False - def __neg__(self): # type: () -> Infinity + def __neg__(self) -> Infinity: return Infinity diff --git a/poetry/core/version/version.py b/poetry/core/version/version.py index 0726d9439..bbe38a406 100644 --- a/poetry/core/version/version.py +++ b/poetry/core/version/version.py @@ -5,6 +5,7 @@ from typing import Any from typing import Optional from typing import Tuple +from typing import Type from typing import Union from .base import BaseVersion @@ -54,7 +55,7 @@ class Version(BaseVersion): - def __init__(self, version): # type: (str) -> None + def __init__(self, version: str) -> None: # Validate the version and parse it into pieces match = VERSION_PATTERN.match(version) if not match: @@ -82,10 +83,10 @@ def __init__(self, version): # type: (str) -> None self._version.local, ) - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "".format(repr(str(self))) - def __str__(self): # type: () -> str + def __str__(self) -> str: parts = [] # Epoch @@ -114,11 +115,11 @@ def __str__(self): # type: () -> str return "".join(parts) @property - def public(self): # type: () -> str + def public(self) -> str: return str(self).split("+", 1)[0] @property - def base_version(self): # type: () -> str + def base_version(self) -> str: parts = [] # Epoch @@ -131,23 +132,21 @@ def base_version(self): # type: () -> str return "".join(parts) @property - def local(self): # type: () -> str + def local(self) -> str: version_string = str(self) if "+" in version_string: return version_string.split("+", 1)[1] @property - def is_prerelease(self): # type: () -> bool + def is_prerelease(self) -> bool: return bool(self._version.dev or self._version.pre) @property - def is_postrelease(self): # type: () -> bool + def is_postrelease(self) -> bool: return bool(self._version.post) -def _parse_letter_version( - letter, number -): # type: (str, Optional[str]) -> Tuple[str, int] +def _parse_letter_version(letter: str, number: Optional[str]) -> Tuple[str, int]: if letter: # We consider there to be an implicit 0 in a pre-release if there is # not a numeral associated with it. @@ -181,7 +180,7 @@ def _parse_letter_version( _local_version_seperators = re.compile(r"[._-]") -def _parse_local_version(local): # type: (Optional[str]) -> Tuple[Union[str, int], ...] +def _parse_local_version(local: Optional[str]) -> Tuple[Union[str, int], ...]: """ Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). """ @@ -193,13 +192,29 @@ def _parse_local_version(local): # type: (Optional[str]) -> Tuple[Union[str, in def _cmpkey( - epoch, # type: int - release, # type: Optional[Tuple[int, ...]] - pre, # type: Optional[Tuple[str, int]] - post, # type: Optional[Tuple[str, int]] - dev, # type: Optional[Tuple[str, int]] - local, # type: Optional[Tuple[Union[str, int], ...]] -): # type: (...) -> Tuple[int, Tuple[int, ...], Union[Union[Infinity, NegativeInfinity, Tuple[str, int]], Any], Union[NegativeInfinity, Tuple[str, int]], Union[Union[Infinity, Tuple[str, int]], Any], Union[NegativeInfinity, Tuple[Union[Tuple[int, str], Tuple[NegativeInfinity, Union[str, int]]], ...]]] + epoch: int, + release: Optional[Tuple[int, ...]], + pre: Optional[Tuple[str, int]], + post: Optional[Tuple[str, int]], + dev: Optional[Tuple[str, int]], + local: Optional[Tuple[Union[str, int], ...]], +) -> Tuple[ + int, + Tuple[int, ...], + Union[Infinity.__class__, NegativeInfinity.__class__, Tuple[str, int], Any], + Union[NegativeInfinity.__class__, Tuple[str, int]], + Union[Infinity.__class__, Tuple[str, int], Any], + Union[ + NegativeInfinity.__class__, + Tuple[ + Union[ + Tuple[int, str], + Tuple[Type[NegativeInfinity.__class__], Union[str, int]], + ], + ..., + ], + ], +]: # When we compare a release version, we want to compare it with all of the # trailing zeros removed. So we'll use a reverse the list, drop all the now # leading zeros until we come to something non zero, then take the rest diff --git a/pyproject.toml b/pyproject.toml index d55b8444a..43cdf7e68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "poetry-core" -version = "1.0.2" +version = "1.1.0a0" description = "Poetry PEP 517 Build Backend" authors = ["Sébastien Eustace "] @@ -30,21 +30,13 @@ exclude = [ "Bug Tracker" = "https://github.com/python-poetry/poetry/issues" [tool.poetry.dependencies] -python = "~2.7 || ^3.5" +python = "^3.6" # required for compatibility -importlib-metadata = {version = "^1.7.0", python = "~2.7 || >=3.5, <3.8"} -pathlib2 = {version = "^2.3.5", python = "~2.7"} -typing = {version = "^3.7.4.1", python = "~2.7"} - -# required by tomlkit -enum34 = {version = "^1.1.10", python = "~2.7"} - -# required by tomlkit, jsonschema -functools32 = {version = "^3.2.3-2", python = "~2.7"} +importlib-metadata = {version = "^1.7.0", python = ">=3.5, <3.8"} [tool.poetry.dev-dependencies] -pre-commit = "^1.10" +pre-commit = {version = "^2.10.0", python = "^3.6.1"} pyrsistent = "^0.16.0" pytest = "^4.6" pytest-cov = "^2.8" @@ -52,7 +44,8 @@ pytest-mock = "^2.0" tox = "^3.0" vendoring = {version = "^0.3", python = "~3.8"} pep517 = "^0.8.2" -"backports.tempfile" = {version = "^1.0", python = "~2.7"} +black = {version = "^20.8b1", markers = "python_version >= '3.6' and python_version < '4.0' and implementation_name != 'pypy'"} +isort = "^5.7.0" [tool.black] line-length = 88 @@ -74,15 +67,13 @@ exclude = ''' ''' [tool.isort] -line_length = 88 +profile = "black" force_single_line = true atomic = true include_trailing_comma = true lines_after_imports = 2 lines_between_types = 1 -multi_line_output = 3 use_parentheses = true -not_skip = "__init__.py" skip_glob = ["*/setup.py", "*/poetry/core/_vendor/*"] filter_files = true diff --git a/stanza b/stanza index a0469c38e..a42173ab0 100755 --- a/stanza +++ b/stanza @@ -34,13 +34,19 @@ def extract_license( ext = sdist.suffixes[-1][1:] with tarfile.open(sdist, mode="r:{}".format(ext)) as tar: return find_and_extract_license( - destination, tar, tar.getmembers(), license_directories, + destination, + tar, + tar.getmembers(), + license_directories, ) def extract_from_source_zipfile(sdist: Path) -> bool: with zipfile.ZipFile(sdist) as zip: return find_and_extract_license( - destination, zip, zip.infolist(), license_directories, + destination, + zip, + zip.infolist(), + license_directories, ) if sdist.suffixes[-2] == ".tar": diff --git a/tests/conftest.py b/tests/conftest.py index 44a4ff892..25bd23e23 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,12 +1,12 @@ import sys +from pathlib import Path from typing import Callable import pytest import virtualenv from poetry.core.factory import Factory -from poetry.core.utils._compat import Path from tests.testutils import tempfile diff --git a/tests/fixtures/project_with_setup/setup.py b/tests/fixtures/project_with_setup/setup.py index 71b3074d8..03c5a5396 100644 --- a/tests/fixtures/project_with_setup/setup.py +++ b/tests/fixtures/project_with_setup/setup.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from setuptools import setup diff --git a/tests/integration/test_pep517.py b/tests/integration/test_pep517.py index 321f8d713..d5805a7dd 100644 --- a/tests/integration/test_pep517.py +++ b/tests/integration/test_pep517.py @@ -1,9 +1,10 @@ +from pathlib import Path + import pytest from pep517.build import build from pep517.check import check -from poetry.core.utils._compat import Path from tests.testutils import subprocess_run from tests.testutils import temporary_project_directory diff --git a/tests/masonry/builders/fixtures/extended/setup.py b/tests/masonry/builders/fixtures/extended/setup.py new file mode 100644 index 000000000..932f2476a --- /dev/null +++ b/tests/masonry/builders/fixtures/extended/setup.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from setuptools import setup + +packages = ["extended"] + +package_data = {"": ["*"]} + +setup_kwargs = { + "name": "extended", + "version": "0.1", + "description": "Some description.", + "long_description": "Module 1\n========\n", + "author": "Sébastien Eustace", + "author_email": "sebastien@eustace.io", + "maintainer": "None", + "maintainer_email": "None", + "url": "https://python-poetry.org/", + "packages": packages, + "package_data": package_data, +} +from build import * + +build(setup_kwargs) + +setup(**setup_kwargs) diff --git a/tests/masonry/builders/fixtures/pep_561_stub_only/pkg-stubs/module.pyi b/tests/masonry/builders/fixtures/pep_561_stub_only/pkg-stubs/module.pyi index d79e6e39e..f85a07d46 100644 --- a/tests/masonry/builders/fixtures/pep_561_stub_only/pkg-stubs/module.pyi +++ b/tests/masonry/builders/fixtures/pep_561_stub_only/pkg-stubs/module.pyi @@ -1,4 +1,5 @@ """Example module""" from typing import Tuple + version_info = Tuple[int, int, int] diff --git a/tests/masonry/builders/fixtures/pep_561_stub_only_partial/pkg-stubs/module.pyi b/tests/masonry/builders/fixtures/pep_561_stub_only_partial/pkg-stubs/module.pyi index d79e6e39e..f85a07d46 100644 --- a/tests/masonry/builders/fixtures/pep_561_stub_only_partial/pkg-stubs/module.pyi +++ b/tests/masonry/builders/fixtures/pep_561_stub_only_partial/pkg-stubs/module.pyi @@ -1,4 +1,5 @@ """Example module""" from typing import Tuple + version_info = Tuple[int, int, int] diff --git a/tests/masonry/builders/fixtures/pep_561_stub_only_src/src/pkg-stubs/module.pyi b/tests/masonry/builders/fixtures/pep_561_stub_only_src/src/pkg-stubs/module.pyi index d79e6e39e..f85a07d46 100644 --- a/tests/masonry/builders/fixtures/pep_561_stub_only_src/src/pkg-stubs/module.pyi +++ b/tests/masonry/builders/fixtures/pep_561_stub_only_src/src/pkg-stubs/module.pyi @@ -1,4 +1,5 @@ """Example module""" from typing import Tuple + version_info = Tuple[int, int, int] diff --git a/tests/masonry/builders/fixtures/src_extended/setup.py b/tests/masonry/builders/fixtures/src_extended/setup.py new file mode 100644 index 000000000..d5ca286ad --- /dev/null +++ b/tests/masonry/builders/fixtures/src_extended/setup.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from setuptools import setup + +package_dir = {"": "src"} + +packages = ["extended"] + +package_data = {"": ["*"]} + +setup_kwargs = { + "name": "extended", + "version": "0.1", + "description": "Some description.", + "long_description": "Module 1\n========\n", + "author": "Sébastien Eustace", + "author_email": "sebastien@eustace.io", + "maintainer": "None", + "maintainer_email": "None", + "url": "https://python-poetry.org/", + "package_dir": package_dir, + "packages": packages, + "package_data": package_data, +} +from build import * + +build(setup_kwargs) + +setup(**setup_kwargs) diff --git a/tests/masonry/builders/test_builder.py b/tests/masonry/builders/test_builder.py index 209cde80b..86cf4bad5 100644 --- a/tests/masonry/builders/test_builder.py +++ b/tests/masonry/builders/test_builder.py @@ -1,14 +1,13 @@ -# -*- coding: utf-8 -*- import sys from email.parser import Parser +from pathlib import Path import pytest from poetry.core.factory import Factory from poetry.core.masonry.builders.builder import Builder from poetry.core.utils._compat import PY37 -from poetry.core.utils._compat import Path def test_builder_find_excluded_files(mocker): diff --git a/tests/masonry/builders/test_complete.py b/tests/masonry/builders/test_complete.py index 4248a87a2..01a8c114a 100644 --- a/tests/masonry/builders/test_complete.py +++ b/tests/masonry/builders/test_complete.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals import ast @@ -11,13 +10,13 @@ import tempfile import zipfile +from pathlib import Path + import pytest from poetry.core import __version__ from poetry.core.factory import Factory -from poetry.core.masonry import Builder -from poetry.core.utils._compat import Path -from poetry.core.utils._compat import decode +from poetry.core.masonry.builder import Builder fixtures_dir = Path(__file__).parent / "fixtures" @@ -71,7 +70,7 @@ def test_wheel_c_extension(): assert has_compiled_extension try: - wheel_data = decode(zip.read("extended-0.1.dist-info/WHEEL")) + wheel_data = zip.read("extended-0.1.dist-info/WHEEL").decode() assert ( re.match( @@ -88,7 +87,7 @@ def test_wheel_c_extension(): is not None ) - records = decode(zip.read("extended-0.1.dist-info/RECORD")) + records = zip.read("extended-0.1.dist-info/RECORD").decode() assert re.search(r"\s+extended/extended.*\.(so|pyd)", records) is not None finally: @@ -128,7 +127,7 @@ def test_wheel_c_extension_with_no_setup(): assert has_compiled_extension try: - wheel_data = decode(zip.read("extended-0.1.dist-info/WHEEL")) + wheel_data = zip.read("extended-0.1.dist-info/WHEEL").decode() assert ( re.match( @@ -145,7 +144,7 @@ def test_wheel_c_extension_with_no_setup(): is not None ) - records = decode(zip.read("extended-0.1.dist-info/RECORD")) + records = zip.read("extended-0.1.dist-info/RECORD").decode() assert re.search(r"\s+extended/extended.*\.(so|pyd)", records) is not None finally: @@ -185,7 +184,7 @@ def test_wheel_c_extension_src_layout(): assert has_compiled_extension try: - wheel_data = decode(zip.read("extended-0.1.dist-info/WHEEL")) + wheel_data = zip.read("extended-0.1.dist-info/WHEEL").decode() assert ( re.match( @@ -202,7 +201,7 @@ def test_wheel_c_extension_src_layout(): is not None ) - records = decode(zip.read("extended-0.1.dist-info/RECORD")) + records = zip.read("extended-0.1.dist-info/RECORD").decode() assert re.search(r"\s+extended/extended.*\.(so|pyd)", records) is not None finally: @@ -228,7 +227,7 @@ def test_complete(): entry_points = zip.read("my_package-1.2.3.dist-info/entry_points.txt") assert ( - decode(entry_points.decode()) + entry_points.decode() == """\ [console_scripts] extra-script=my_package.extra:main[time] @@ -237,7 +236,7 @@ def test_complete(): """ ) - wheel_data = decode(zip.read("my_package-1.2.3.dist-info/WHEEL")) + wheel_data = zip.read("my_package-1.2.3.dist-info/WHEEL").decode() assert ( wheel_data @@ -250,7 +249,7 @@ def test_complete(): __version__ ) ) - wheel_data = decode(zip.read("my_package-1.2.3.dist-info/METADATA")) + wheel_data = zip.read("my_package-1.2.3.dist-info/METADATA").decode() assert ( wheel_data @@ -331,7 +330,7 @@ def test_complete_no_vcs(): entry_points = zip.read("my_package-1.2.3.dist-info/entry_points.txt") assert ( - decode(entry_points.decode()) + entry_points.decode() == """\ [console_scripts] extra-script=my_package.extra:main[time] @@ -340,7 +339,7 @@ def test_complete_no_vcs(): """ ) - wheel_data = decode(zip.read("my_package-1.2.3.dist-info/WHEEL")) + wheel_data = zip.read("my_package-1.2.3.dist-info/WHEEL").decode() assert ( wheel_data @@ -353,7 +352,7 @@ def test_complete_no_vcs(): __version__ ) ) - wheel_data = decode(zip.read("my_package-1.2.3.dist-info/METADATA")) + wheel_data = zip.read("my_package-1.2.3.dist-info/METADATA").decode() assert ( wheel_data diff --git a/tests/masonry/builders/test_sdist.py b/tests/masonry/builders/test_sdist.py index 5c39eb57a..3cb2a3b12 100644 --- a/tests/masonry/builders/test_sdist.py +++ b/tests/masonry/builders/test_sdist.py @@ -1,22 +1,19 @@ -# -*- coding: utf-8 -*- import ast import gzip import shutil import tarfile from email.parser import Parser +from pathlib import Path import pytest from poetry.core.factory import Factory from poetry.core.masonry.builders.sdist import SdistBuilder from poetry.core.masonry.utils.package_include import PackageInclude -from poetry.core.packages import Package from poetry.core.packages.dependency import Dependency +from poetry.core.packages.package import Package from poetry.core.packages.vcs_dependency import VCSDependency -from poetry.core.utils._compat import Path -from poetry.core.utils._compat import encode -from poetry.core.utils._compat import to_str fixtures_dir = Path(__file__).parent / "fixtures" @@ -157,7 +154,7 @@ def test_make_pkg_info_any_python(): builder = SdistBuilder(poetry) pkg_info = builder.build_pkg_info() p = Parser() - parsed = p.parsestr(to_str(pkg_info)) + parsed = p.parsestr(pkg_info.decode()) assert "Requires-Python" not in parsed @@ -193,7 +190,7 @@ def test_make_pkg_info_multi_constraints_dependency(): builder = SdistBuilder(poetry) pkg_info = builder.build_pkg_info() p = Parser() - parsed = p.parsestr(to_str(pkg_info)) + parsed = p.parsestr(pkg_info.decode()) requires = parsed.get_all("Requires-Dist") assert requires == [ @@ -269,8 +266,8 @@ def test_setup_py_context(): with open(str(setup), "rb") as f: # we convert to string and replace line endings here for compatibility - data = to_str(encode(f.read())).replace("\r\n", "\n") - assert data == to_str(builder.build_setup()) + data = f.read().decode().replace("\r\n", "\n") + assert data == builder.build_setup().decode() assert not project_setup_py.exists() finally: @@ -483,7 +480,7 @@ def test_proper_python_requires_if_two_digits_precision_version_specified(): builder = SdistBuilder(poetry) pkg_info = builder.build_pkg_info() p = Parser() - parsed = p.parsestr(to_str(pkg_info)) + parsed = p.parsestr(pkg_info.decode()) assert parsed["Requires-Python"] == ">=3.6,<3.7" @@ -494,7 +491,7 @@ def test_proper_python_requires_if_three_digits_precision_version_specified(): builder = SdistBuilder(poetry) pkg_info = builder.build_pkg_info() p = Parser() - parsed = p.parsestr(to_str(pkg_info)) + parsed = p.parsestr(pkg_info.decode()) assert parsed["Requires-Python"] == "==2.7.15" diff --git a/tests/masonry/builders/test_wheel.py b/tests/masonry/builders/test_wheel.py index e300bb34c..3db7c04d4 100644 --- a/tests/masonry/builders/test_wheel.py +++ b/tests/masonry/builders/test_wheel.py @@ -1,13 +1,13 @@ -# -*- coding: utf-8 -*- import os import shutil import zipfile +from pathlib import Path + import pytest from poetry.core.factory import Factory from poetry.core.masonry.builders.wheel import WheelBuilder -from poetry.core.utils._compat import Path from tests.masonry.builders.test_sdist import project diff --git a/tests/masonry/test_api.py b/tests/masonry/test_api.py index adbbddeba..a649a197c 100644 --- a/tests/masonry/test_api.py +++ b/tests/masonry/test_api.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals import os @@ -6,13 +5,12 @@ import sys from contextlib import contextmanager +from pathlib import Path import pytest from poetry.core import __version__ from poetry.core.masonry import api -from poetry.core.utils._compat import Path -from poetry.core.utils._compat import decode from poetry.core.utils.helpers import temporary_directory from tests.testutils import validate_sdist_contents from tests.testutils import validate_wheel_contents @@ -193,13 +191,13 @@ def test_prepare_metadata_for_build_wheel(): assert (dist_info / "METADATA").exists() with (dist_info / "entry_points.txt").open(encoding="utf-8") as f: - assert entry_points == decode(f.read()) + assert entry_points == f.read() with (dist_info / "WHEEL").open(encoding="utf-8") as f: - assert wheel_data == decode(f.read()) + assert wheel_data == f.read() with (dist_info / "METADATA").open(encoding="utf-8") as f: - assert metadata == decode(f.read()) + assert metadata == f.read() def test_prepare_metadata_for_build_wheel_with_bad_path_dev_dep_succeeds(): diff --git a/tests/masonry/utils/test_package_include.py b/tests/masonry/utils/test_package_include.py index 0db1ff6da..f9bebf19c 100644 --- a/tests/masonry/utils/test_package_include.py +++ b/tests/masonry/utils/test_package_include.py @@ -1,7 +1,8 @@ +from pathlib import Path + import pytest from poetry.core.masonry.utils.package_include import PackageInclude -from poetry.core.utils._compat import Path fixtures_dir = Path(__file__).parent / "fixtures" diff --git a/tests/packages/test_dependency.py b/tests/packages/test_dependency.py index 116c50880..94ac58775 100644 --- a/tests/packages/test_dependency.py +++ b/tests/packages/test_dependency.py @@ -1,8 +1,7 @@ import pytest -from poetry.core.packages import Dependency -from poetry.core.packages import Package -from poetry.core.packages import dependency_from_pep_508 +from poetry.core.packages.dependency import Dependency +from poetry.core.packages.package import Package def test_accepts(): @@ -118,7 +117,9 @@ def test_to_pep_508_in_extras(): def test_to_pep_508_in_extras_parsed(): - dependency = dependency_from_pep_508('foo[bar] (>=1.23,<2.0) ; extra == "baz"') + dependency = Dependency.create_from_pep_508( + 'foo[bar] (>=1.23,<2.0) ; extra == "baz"' + ) result = dependency.to_pep_508() assert result == 'foo[bar] (>=1.23,<2.0); extra == "baz"' diff --git a/tests/packages/test_directory_dependency.py b/tests/packages/test_directory_dependency.py index 522935da1..a69364532 100644 --- a/tests/packages/test_directory_dependency.py +++ b/tests/packages/test_directory_dependency.py @@ -1,8 +1,9 @@ +from pathlib import Path + import pytest -from poetry.core.packages import dependency_from_pep_508 +from poetry.core.packages.dependency import Dependency from poetry.core.packages.directory_dependency import DirectoryDependency -from poetry.core.utils._compat import Path DIST_PATH = Path(__file__).parent.parent / "fixtures" / "git" / "github.com" / "demo" @@ -14,7 +15,9 @@ def test_directory_dependency_must_exist(): def _test_directory_dependency_pep_508(name, path, pep_508_input, pep_508_output=None): - dep = dependency_from_pep_508(pep_508_input, relative_to=Path(__file__).parent) + dep = Dependency.create_from_pep_508( + pep_508_input, relative_to=Path(__file__).parent + ) assert dep.is_directory() assert dep.name == name diff --git a/tests/packages/test_file_dependency.py b/tests/packages/test_file_dependency.py index 7760ab5ce..3abe20f88 100644 --- a/tests/packages/test_file_dependency.py +++ b/tests/packages/test_file_dependency.py @@ -1,9 +1,9 @@ +from pathlib import Path + import pytest -from poetry.core.packages import FileDependency -from poetry.core.packages import dependency_from_pep_508 -from poetry.core.utils._compat import PY36 -from poetry.core.utils._compat import Path +from poetry.core.packages.dependency import Dependency +from poetry.core.packages.file_dependency import FileDependency DIST_PATH = Path(__file__).parent.parent / "fixtures" / "distributions" @@ -24,10 +24,10 @@ def _test_file_dependency_pep_508( ): mocker.patch.object(Path, "exists").return_value = True mocker.patch.object(Path, "is_file").return_value = True - if not PY36: - mocker.patch.object(Path, "resolve").return_value = path - dep = dependency_from_pep_508(pep_508_input, relative_to=Path(__file__).parent) + dep = Dependency.create_from_pep_508( + pep_508_input, relative_to=Path(__file__).parent + ) assert dep.is_file() assert dep.name == name diff --git a/tests/packages/test_main.py b/tests/packages/test_main.py index a6e6e2ea9..d9cba1ac6 100644 --- a/tests/packages/test_main.py +++ b/tests/packages/test_main.py @@ -1,9 +1,9 @@ -from poetry.core.packages import dependency_from_pep_508 +from poetry.core.packages.dependency import Dependency def test_dependency_from_pep_508(): name = "requests" - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == name assert str(dep.constraint) == "*" @@ -11,7 +11,7 @@ def test_dependency_from_pep_508(): def test_dependency_from_pep_508_with_version(): name = "requests==2.18.0" - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -19,7 +19,7 @@ def test_dependency_from_pep_508_with_version(): def test_dependency_from_pep_508_with_parens(): name = "requests (==2.18.0)" - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -27,7 +27,7 @@ def test_dependency_from_pep_508_with_parens(): def test_dependency_from_pep_508_with_constraint(): name = "requests>=2.12.0,!=2.17.*,<3.0" - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == ">=2.12.0,<2.17.0 || >=2.18.0,<3.0" @@ -35,7 +35,7 @@ def test_dependency_from_pep_508_with_constraint(): def test_dependency_from_pep_508_with_extras(): name = 'requests==2.18.0; extra == "foo" or extra == "bar"' - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -45,7 +45,7 @@ def test_dependency_from_pep_508_with_extras(): def test_dependency_from_pep_508_with_python_version(): name = 'requests (==2.18.0); python_version == "2.7" or python_version == "2.6"' - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -56,7 +56,7 @@ def test_dependency_from_pep_508_with_python_version(): def test_dependency_from_pep_508_with_single_python_version(): name = 'requests (==2.18.0); python_version == "2.7"' - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -67,7 +67,7 @@ def test_dependency_from_pep_508_with_single_python_version(): def test_dependency_from_pep_508_with_platform(): name = 'requests (==2.18.0); sys_platform == "win32" or sys_platform == "darwin"' - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -83,7 +83,7 @@ def test_dependency_from_pep_508_complex(): 'and (sys_platform == "win32" or sys_platform == "darwin") ' 'and extra == "foo"' ) - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -98,7 +98,7 @@ def test_dependency_from_pep_508_complex(): def test_dependency_python_version_in(): name = "requests (==2.18.0); python_version in '3.3 3.4 3.5'" - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -108,7 +108,7 @@ def test_dependency_python_version_in(): def test_dependency_python_version_in_comma(): name = "requests (==2.18.0); python_version in '3.3, 3.4, 3.5'" - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -118,7 +118,7 @@ def test_dependency_python_version_in_comma(): def test_dependency_platform_in(): name = "requests (==2.18.0); sys_platform in 'win32 darwin'" - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -127,7 +127,7 @@ def test_dependency_platform_in(): def test_dependency_with_extra(): name = "requests[security] (==2.18.0)" - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -142,7 +142,7 @@ def test_dependency_from_pep_508_with_python_version_union_of_multi(): '(python_version >= "2.7" and python_version < "2.8") ' 'or (python_version >= "3.4" and python_version < "3.5")' ) - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -160,7 +160,7 @@ def test_dependency_from_pep_508_with_not_in_op_marker(): '; python_version not in "3.0,3.1,3.2" and extra == "export"' ) - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "jinja2" assert str(dep.constraint) == ">=2.7,<2.8" @@ -174,7 +174,7 @@ def test_dependency_from_pep_508_with_not_in_op_marker(): def test_dependency_from_pep_508_with_git_url(): name = "django-utils @ git+ssh://git@corp-gitlab.com/corp-utils.git@1.2" - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert "django-utils" == dep.name assert dep.is_vcs() @@ -189,7 +189,7 @@ def test_dependency_from_pep_508_with_git_url_and_comment_and_extra(): ' ; extra == "foo;"' ) - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert "poetry" == dep.name assert dep.is_vcs() @@ -202,7 +202,7 @@ def test_dependency_from_pep_508_with_git_url_and_comment_and_extra(): def test_dependency_from_pep_508_with_url(): name = "django-utils @ https://example.com/django-utils-1.0.0.tar.gz" - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert "django-utils" == dep.name assert dep.is_url() @@ -214,7 +214,7 @@ def test_dependency_from_pep_508_with_wheel_url(): "example_wheel @ https://example.com/example_wheel-14.0.2-py2.py3-none-any.whl" ) - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert "example-wheel" == dep.name assert str(dep.constraint) == "14.0.2" @@ -226,7 +226,7 @@ def test_dependency_from_pep_508_with_python_full_version(): '(python_version >= "2.7" and python_version < "2.8") ' 'or (python_full_version >= "3.4" and python_full_version < "3.5.4")' ) - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" @@ -240,7 +240,7 @@ def test_dependency_from_pep_508_with_python_full_version(): def test_dependency_from_pep_508_with_python_full_version_pep440_compatible_release_astrix(): name = 'pathlib2 ; python_version == "3.4.*" or python_version < "3"' - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "pathlib2" assert str(dep.constraint) == "*" @@ -249,7 +249,7 @@ def test_dependency_from_pep_508_with_python_full_version_pep440_compatible_rele def test_dependency_from_pep_508_with_python_full_version_pep440_compatible_release_tilde(): name = 'pathlib2 ; python_version ~= "3.4" or python_version < "3"' - dep = dependency_from_pep_508(name) + dep = Dependency.create_from_pep_508(name) assert dep.name == "pathlib2" assert str(dep.constraint) == "*" diff --git a/tests/packages/test_package.py b/tests/packages/test_package.py index 40b980795..41bf98398 100644 --- a/tests/packages/test_package.py +++ b/tests/packages/test_package.py @@ -1,10 +1,10 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals +from pathlib import Path + import pytest -from poetry.core.packages import Package -from poetry.core.utils._compat import Path +from poetry.core.packages.package import Package def test_package_authors(): diff --git a/tests/packages/test_url_dependency.py b/tests/packages/test_url_dependency.py index 93423d940..88ab2adce 100644 --- a/tests/packages/test_url_dependency.py +++ b/tests/packages/test_url_dependency.py @@ -1,4 +1,4 @@ -from poetry.core.packages import URLDependency +from poetry.core.packages.url_dependency import URLDependency from poetry.core.version.markers import SingleMarker diff --git a/tests/packages/utils/test_utils.py b/tests/packages/utils/test_utils.py index 7250e5f5d..7f8cfcd7e 100644 --- a/tests/packages/utils/test_utils.py +++ b/tests/packages/utils/test_utils.py @@ -2,7 +2,7 @@ from poetry.core.packages.utils.utils import convert_markers from poetry.core.packages.utils.utils import get_python_constraint_from_marker -from poetry.core.semver import parse_constraint +from poetry.core.semver.helpers import parse_constraint from poetry.core.version.markers import parse_marker diff --git a/tests/packages/utils/test_utils_link.py b/tests/packages/utils/test_utils_link.py index fb39f93f6..5751ed59a 100644 --- a/tests/packages/utils/test_utils_link.py +++ b/tests/packages/utils/test_utils_link.py @@ -2,7 +2,7 @@ from hashlib import sha256 -from poetry.core.packages import Link +from poetry.core.packages.utils.link import Link def make_url(ext): diff --git a/tests/packages/utils/test_utils_urls.py b/tests/packages/utils/test_utils_urls.py index e7fa0cc8d..bbbb610bc 100644 --- a/tests/packages/utils/test_utils_urls.py +++ b/tests/packages/utils/test_utils_urls.py @@ -3,13 +3,12 @@ import sys -import pytest +from pathlib import Path -from six.moves.urllib.request import pathname2url # noqa +import pytest -from poetry.core.packages import path_to_url -from poetry.core.packages import url_to_path -from poetry.core.utils._compat import Path +from poetry.core.packages.utils.utils import path_to_url +from poetry.core.packages.utils.utils import url_to_path @pytest.mark.skipif("sys.platform == 'win32'") diff --git a/tests/pyproject/conftest.py b/tests/pyproject/conftest.py index 4d42a1938..31df17f78 100644 --- a/tests/pyproject/conftest.py +++ b/tests/pyproject/conftest.py @@ -1,7 +1,6 @@ -import pytest +from pathlib import Path -from poetry.core.utils._compat import Path -from poetry.core.utils._compat import decode +import pytest @pytest.fixture @@ -20,7 +19,7 @@ def build_system_section(pyproject_toml): # type: (Path) -> str build-backend = "poetry.core.masonry.api" """ with pyproject_toml.open(mode="a") as f: - f.write(decode(content)) + f.write(content) return content @@ -34,5 +33,5 @@ def poetry_section(pyproject_toml): # type: (Path) -> str python = "^3.5" """ with pyproject_toml.open(mode="a") as f: - f.write(decode(content)) + f.write(content) return content diff --git a/tests/pyproject/test_pyproject_toml.py b/tests/pyproject/test_pyproject_toml.py index 4fc3eab75..7ca621ad8 100644 --- a/tests/pyproject/test_pyproject_toml.py +++ b/tests/pyproject/test_pyproject_toml.py @@ -1,13 +1,14 @@ import uuid +from pathlib import Path # noqa + import pytest from tomlkit.toml_document import TOMLDocument from tomlkit.toml_file import TOMLFile -from poetry.core.pyproject import PyProjectException -from poetry.core.pyproject import PyProjectTOML -from poetry.core.utils._compat import Path # noqa +from poetry.core.pyproject.exceptions import PyProjectException +from poetry.core.pyproject.toml import PyProjectTOML def test_pyproject_toml_simple(pyproject_toml, build_system_section, poetry_section): diff --git a/tests/pyproject/test_pyproject_toml_file.py b/tests/pyproject/test_pyproject_toml_file.py index 071dab28f..a146a79f2 100644 --- a/tests/pyproject/test_pyproject_toml_file.py +++ b/tests/pyproject/test_pyproject_toml_file.py @@ -2,7 +2,6 @@ from poetry.core.exceptions import PoetryCoreException from poetry.core.toml import TOMLFile -from poetry.core.utils._compat import decode def test_old_pyproject_toml_file_deprecation( @@ -19,7 +18,7 @@ def test_old_pyproject_toml_file_deprecation( def test_pyproject_toml_file_invalid(pyproject_toml): with pyproject_toml.open(mode="a") as f: - f.write(decode("<<<<<<<<<<<")) + f.write("<<<<<<<<<<<") with pytest.raises(PoetryCoreException) as excval: _ = TOMLFile(pyproject_toml).read() diff --git a/tests/semver/test_main.py b/tests/semver/test_helpers.py similarity index 96% rename from tests/semver/test_main.py rename to tests/semver/test_helpers.py index 85a46b3f5..a9f193b1a 100644 --- a/tests/semver/test_main.py +++ b/tests/semver/test_helpers.py @@ -1,9 +1,9 @@ import pytest -from poetry.core.semver import Version -from poetry.core.semver import VersionRange -from poetry.core.semver import VersionUnion -from poetry.core.semver import parse_constraint +from poetry.core.semver.helpers import parse_constraint +from poetry.core.semver.version import Version +from poetry.core.semver.version_range import VersionRange +from poetry.core.semver.version_union import VersionUnion @pytest.mark.parametrize( diff --git a/tests/semver/test_parse_constraint.py b/tests/semver/test_parse_constraint.py index f70c5741e..97e80dd1d 100644 --- a/tests/semver/test_parse_constraint.py +++ b/tests/semver/test_parse_constraint.py @@ -1,9 +1,9 @@ import pytest -from poetry.core.semver import Version -from poetry.core.semver import VersionRange -from poetry.core.semver import VersionUnion -from poetry.core.semver import parse_constraint +from poetry.core.semver.helpers import parse_constraint +from poetry.core.semver.version import Version +from poetry.core.semver.version_range import VersionRange +from poetry.core.semver.version_union import VersionUnion @pytest.mark.parametrize( diff --git a/tests/semver/test_version.py b/tests/semver/test_version.py index 9f26d4eb8..69bb6a6c1 100644 --- a/tests/semver/test_version.py +++ b/tests/semver/test_version.py @@ -1,9 +1,9 @@ import pytest -from poetry.core.semver import EmptyConstraint -from poetry.core.semver import Version -from poetry.core.semver import VersionRange +from poetry.core.semver.empty_constraint import EmptyConstraint from poetry.core.semver.exceptions import ParseVersionError +from poetry.core.semver.version import Version +from poetry.core.semver.version_range import VersionRange @pytest.mark.parametrize( diff --git a/tests/semver/test_version_range.py b/tests/semver/test_version_range.py index 3667a465f..e6e21e34c 100644 --- a/tests/semver/test_version_range.py +++ b/tests/semver/test_version_range.py @@ -1,8 +1,8 @@ import pytest -from poetry.core.semver import EmptyConstraint -from poetry.core.semver import Version -from poetry.core.semver import VersionRange +from poetry.core.semver.empty_constraint import EmptyConstraint +from poetry.core.semver.version import Version +from poetry.core.semver.version_range import VersionRange @pytest.fixture() diff --git a/tests/spdx/test_main.py b/tests/spdx/test_helpers.py similarity index 96% rename from tests/spdx/test_main.py rename to tests/spdx/test_helpers.py index 62448e353..bea44acd4 100644 --- a/tests/spdx/test_main.py +++ b/tests/spdx/test_helpers.py @@ -1,6 +1,6 @@ import pytest -from poetry.core.spdx import license_by_id +from poetry.core.spdx.helpers import license_by_id def test_license_by_id(): diff --git a/tests/spdx/test_license.py b/tests/spdx/test_license.py index 431dbacc0..55d0e8a16 100644 --- a/tests/spdx/test_license.py +++ b/tests/spdx/test_license.py @@ -1,4 +1,4 @@ -from poetry.core.spdx import license_by_id +from poetry.core.spdx.helpers import license_by_id def test_classifier_name(): diff --git a/tests/test_factory.py b/tests/test_factory.py index 525fadb49..da0329429 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -1,13 +1,12 @@ -# -*- coding: utf-8 -*- from __future__ import absolute_import from __future__ import unicode_literals +from pathlib import Path + import pytest from poetry.core.factory import Factory from poetry.core.toml import TOMLFile -from poetry.core.utils._compat import PY2 -from poetry.core.utils._compat import Path fixtures_dir = Path(__file__).parent / "fixtures" @@ -168,16 +167,10 @@ def test_validate_fails(): content = complete.read()["tool"]["poetry"] content["this key is not in the schema"] = "" - if PY2: - expected = ( - "Additional properties are not allowed " - "(u'this key is not in the schema' was unexpected)" - ) - else: - expected = ( - "Additional properties are not allowed " - "('this key is not in the schema' was unexpected)" - ) + expected = ( + "Additional properties are not allowed " + "('this key is not in the schema' was unexpected)" + ) assert Factory.validate(content) == {"errors": [expected], "warnings": []} @@ -188,13 +181,7 @@ def test_create_poetry_fails_on_invalid_configuration(): Path(__file__).parent / "fixtures" / "invalid_pyproject" / "pyproject.toml" ) - if PY2: - expected = """\ -The Poetry configuration is invalid: - - u'description' is a required property -""" - else: - expected = """\ + expected = """\ The Poetry configuration is invalid: - 'description' is a required property """ diff --git a/tests/testutils.py b/tests/testutils.py index 8e4fc266f..193b95cb6 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -4,6 +4,7 @@ import zipfile from contextlib import contextmanager +from pathlib import Path from typing import Any from typing import ContextManager from typing import Dict @@ -11,7 +12,6 @@ from typing import Optional from poetry.core.toml import TOMLFile -from poetry.core.utils._compat import Path try: diff --git a/tests/vcs/test_vcs.py b/tests/vcs/test_vcs.py index c2f888fbd..ebd518cc7 100644 --- a/tests/vcs/test_vcs.py +++ b/tests/vcs/test_vcs.py @@ -36,7 +36,10 @@ "git+ssh://git@github.com:/sdispater/poetry.git", GitUrl("git@github.com:/sdispater/poetry.git", None), ), - ("git+ssh://git@github.com:org/repo", GitUrl("git@github.com:org/repo", None),), + ( + "git+ssh://git@github.com:org/repo", + GitUrl("git@github.com:org/repo", None), + ), ( "git+ssh://git@github.com/org/repo", GitUrl("ssh://git@github.com/org/repo", None), diff --git a/tests/version/test_requirements.py b/tests/version/test_requirements.py index 9a779bd14..375294641 100644 --- a/tests/version/test_requirements.py +++ b/tests/version/test_requirements.py @@ -2,7 +2,7 @@ import pytest -from poetry.core.semver import parse_constraint +from poetry.core.semver.helpers import parse_constraint from poetry.core.version.requirements import InvalidRequirement from poetry.core.version.requirements import Requirement @@ -56,7 +56,10 @@ def assert_requirement(req, name, url=None, extras=None, constraint="*", marker= "name @ file:///absolute/path", {"name": "name", "url": "file:///absolute/path"}, ), - ("name @ file://.", {"name": "name", "url": "file://."},), + ( + "name @ file://.", + {"name": "name", "url": "file://."}, + ), ( "name [fred,bar] @ http://foo.com ; python_version=='2.7'", { diff --git a/tox.ini b/tox.ini index 822055fbe..2bcde871e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] minversion = 3.3.0 isolated_build = True -envlist = py27, py35, py36, py37, py38, pypy, pypy3, integration +envlist = py36, py37, py38, py39, pypy3, integration [testenv] whitelist_externals = poetry From d0b8f3ff1d2ec94e317d8ec20920f32a5d4992ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 3 Jul 2020 17:29:29 +0200 Subject: [PATCH 28/77] Make the Factory class more pluggable --- poetry/core/factory.py | 97 +++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 38 deletions(-) diff --git a/poetry/core/factory.py b/poetry/core/factory.py index e670bdad9..3462ff504 100644 --- a/poetry/core/factory.py +++ b/poetry/core/factory.py @@ -14,6 +14,7 @@ if TYPE_CHECKING: + from .packages.project_package import ProjectPackage from .packages.types import DependencyTypes from .poetry import Poetry @@ -28,11 +29,8 @@ class Factory(object): def create_poetry( self, cwd: Optional[Path] = None, with_dev: bool = True ) -> "Poetry": - from .packages.dependency import Dependency - from .packages.project_package import ProjectPackage from .poetry import Poetry from .pyproject.toml import PyProjectTOML - from .spdx.helpers import license_by_id poetry_file = self.locate(cwd) local_config = PyProjectTOML(path=poetry_file).poetry_config @@ -49,36 +47,59 @@ def create_poetry( # Load package name = local_config["name"] version = local_config["version"] - package = ProjectPackage(name, version, version) - package.root_dir = poetry_file.parent + package = self.get_package(name, version) + package = self.configure_package( + package, local_config, poetry_file.parent, with_dev=with_dev + ) + + return Poetry(poetry_file, local_config, package) + + @classmethod + def get_package(cls, name: str, version: str) -> "ProjectPackage": + from .packages.project_package import ProjectPackage - for author in local_config["authors"]: + return ProjectPackage(name, version, version) + + @classmethod + def configure_package( + cls, + package: "ProjectPackage", + config: Dict[str, Any], + root: Path, + with_dev: bool = True, + ) -> "ProjectPackage": + from .packages.dependency import Dependency + from .spdx.helpers import license_by_id + + package.root_dir = root + + for author in config["authors"]: package.authors.append(author) - for maintainer in local_config.get("maintainers", []): + for maintainer in config.get("maintainers", []): package.maintainers.append(maintainer) - package.description = local_config.get("description", "") - package.homepage = local_config.get("homepage") - package.repository_url = local_config.get("repository") - package.documentation_url = local_config.get("documentation") + package.description = config.get("description", "") + package.homepage = config.get("homepage") + package.repository_url = config.get("repository") + package.documentation_url = config.get("documentation") try: - license_ = license_by_id(local_config.get("license", "")) + license_ = license_by_id(config.get("license", "")) except ValueError: license_ = None package.license = license_ - package.keywords = local_config.get("keywords", []) - package.classifiers = local_config.get("classifiers", []) + package.keywords = config.get("keywords", []) + package.classifiers = config.get("classifiers", []) - if "readme" in local_config: - package.readme = Path(poetry_file.parent) / local_config["readme"] + if "readme" in config: + package.readme = root / config["readme"] - if "platform" in local_config: - package.platform = local_config["platform"] + if "platform" in config: + package.platform = config["platform"] - if "dependencies" in local_config: - for name, constraint in local_config["dependencies"].items(): + if "dependencies" in config: + for name, constraint in config["dependencies"].items(): if name.lower() == "python": package.python_versions = constraint continue @@ -86,7 +107,7 @@ def create_poetry( if isinstance(constraint, list): for _constraint in constraint: package.add_dependency( - self.create_dependency( + cls.create_dependency( name, _constraint, root_dir=package.root_dir ) ) @@ -94,15 +115,15 @@ def create_poetry( continue package.add_dependency( - self.create_dependency(name, constraint, root_dir=package.root_dir) + cls.create_dependency(name, constraint, root_dir=package.root_dir) ) - if with_dev and "dev-dependencies" in local_config: - for name, constraint in local_config["dev-dependencies"].items(): + if with_dev and "dev-dependencies" in config: + for name, constraint in config["dev-dependencies"].items(): if isinstance(constraint, list): for _constraint in constraint: package.add_dependency( - self.create_dependency( + cls.create_dependency( name, _constraint, category="dev", @@ -113,12 +134,12 @@ def create_poetry( continue package.add_dependency( - self.create_dependency( + cls.create_dependency( name, constraint, category="dev", root_dir=package.root_dir ) ) - extras = local_config.get("extras", {}) + extras = config.get("extras", {}) for extra_name, requirements in extras.items(): package.extras[extra_name] = [] @@ -133,16 +154,16 @@ def create_poetry( break - if "build" in local_config: - build = local_config["build"] + if "build" in config: + build = config["build"] if not isinstance(build, dict): build = {"script": build} package.build_config = build or {} - if "include" in local_config: + if "include" in config: package.include = [] - for include in local_config["include"]: + for include in config["include"]: if not isinstance(include, dict): include = {"path": include} @@ -153,17 +174,17 @@ def create_poetry( package.include.append(include) - if "exclude" in local_config: - package.exclude = local_config["exclude"] + if "exclude" in config: + package.exclude = config["exclude"] - if "packages" in local_config: - package.packages = local_config["packages"] + if "packages" in config: + package.packages = config["packages"] # Custom urls - if "urls" in local_config: - package.custom_urls = local_config["urls"] + if "urls" in config: + package.custom_urls = config["urls"] - return Poetry(poetry_file, local_config, package) + return package @classmethod def create_dependency( From 5924c574770a0d20dee2503dfa4feab5dcc6b0e3 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Sat, 26 Sep 2020 23:13:51 +0200 Subject: [PATCH 29/77] vendoring: split sync and update make targets The sync target previously regenerated the poetry.lock for vendored packages, causing larger than necessary change set for patches. This change introduces the update target to specifically handle lock file updates, lock target to regenerate the lock file and repurpose the sync target to only sync as expected. --- Makefile | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 36af0b13e..4f29b7906 100644 --- a/Makefile +++ b/Makefile @@ -3,24 +3,27 @@ ROOT_DIR := $(patsubst %/,%,$(dir $(MAKEFILE_PATH))) VENDOR_SRC := $(ROOT_DIR)/vendors VENDOR_DIR := $(ROOT_DIR)/poetry/core/_vendor +VENDOR_TXT := $(VENDOR_DIR)/vendor.txt POETRY_BIN ?= $(shell which poetry) - .PHONY: vendor/lock -vendor/lock: - # regenerate lock file - @pushd $(VENDOR_SRC) && $(POETRY_BIN) lock +vendor/lock: $(VENDOR_LOCK) + # regenerate lock file + @pushd $(VENDOR_SRC) && $(POETRY_BIN) lock --no-update +.PHONY: vendor/sync +vendor/sync: # regenerate vendor.txt file (exported from lockfile) - @pushd $(VENDOR_SRC) && $(POETRY_BIN) export --without-hashes \ + @pushd $(VENDOR_SRC) && $(POETRY_BIN) export --without-hashes 2> /dev/null \ | egrep -v "(importlib|zipp)" \ - | sort > $(VENDOR_DIR)/vendor.txt - + | sort > $(VENDOR_TXT) -.PHONY: vendor/sync -vendor/sync: | vendor/lock # vendor packages @vendoring sync # strip out *.pyi stubs @find "$(VENDOR_DIR)" -type f -name "*.pyi" -exec rm {} \; + +.PHONY: vendor/update +vendor/update: | vendor/lock vendor/sync + @: From 9a8cd00137d91e83c2c78534190864b9ca1a62bc Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Fri, 12 Mar 2021 01:38:51 +0100 Subject: [PATCH 30/77] deps: update vendoring python constraint --- poetry.lock | 169 ++++++++++++++++++++++++++----------------------- pyproject.toml | 2 +- 2 files changed, 90 insertions(+), 81 deletions(-) diff --git a/poetry.lock b/poetry.lock index 27992c61c..0b585234d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -61,11 +61,11 @@ python-versions = "*" [[package]] name = "cfgv" -version = "3.0.0" +version = "3.2.0" description = "Validate configuration and produce human readable error messages." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.1" [[package]] name = "chardet" @@ -93,7 +93,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "coverage" -version = "5.4" +version = "5.5" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -128,11 +128,11 @@ python-versions = "*" [[package]] name = "identify" -version = "1.5.13" +version = "2.1.1" description = "File identification library for Python" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = ">=3.6.1" [package.extras] license = ["editdistance"] @@ -162,7 +162,7 @@ testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] [[package]] name = "importlib-resources" -version = "5.1.0" +version = "5.1.2" description = "Read resources from Python packages" category = "dev" optional = false @@ -173,7 +173,7 @@ zipp = {version = ">=0.4", markers = "python_version < \"3.8\""} [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "pytest-black (>=0.3.7)", "pytest-mypy"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "pytest-black (>=0.3.7)", "pytest-mypy"] [[package]] name = "isort" @@ -198,7 +198,6 @@ python-versions = "*" [package.dependencies] attrs = ">=17.4.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} pyrsistent = ">=0.14.0" six = ">=1.11.0" @@ -208,7 +207,7 @@ format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator [[package]] name = "more-itertools" -version = "8.6.0" +version = "8.7.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false @@ -278,7 +277,7 @@ dev = ["pre-commit", "tox"] [[package]] name = "pre-commit" -version = "2.10.0" +version = "2.11.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false @@ -425,7 +424,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tox" -version = "3.21.4" +version = "3.23.0" description = "tox is a generic virtualenv management and test command line tool" category = "dev" optional = false @@ -524,20 +523,20 @@ python-versions = "*" [[package]] name = "zipp" -version = "3.4.0" +version = "3.4.1" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.6" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "7d0b49fb8df307d8f6299be0bc1d2017281c338c0a9d9eb8e648cc1de45e4c4b" +content-hash = "317b9ea5e19fcb7b559aaa52f325fe5dddb30ec5f938446b79608c6b8b10fcba" [metadata.files] appdirs = [ @@ -553,6 +552,7 @@ attrs = [ {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] black = [ + {file = "black-20.8b1-py3-none-any.whl", hash = "sha256:70b62ef1527c950db59062cda342ea224d772abdf6adc58b86a45421bab20a6b"}, {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] certifi = [ @@ -560,10 +560,13 @@ certifi = [ {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, ] cfgv = [ - {file = "cfgv-3.0.0-py2.py3-none-any.whl", hash = "sha256:f22b426ed59cd2ab2b54ff96608d846c33dfb8766a67f0b4a6ce130ce244414f"}, - {file = "cfgv-3.0.0.tar.gz", hash = "sha256:04b093b14ddf9fd4d17c53ebfd55582d27b76ed30050193c14e560770c5360eb"}, + {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"}, + {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, +] +chardet = [ + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] -chardet = [] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, @@ -573,55 +576,58 @@ colorama = [ {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] coverage = [ - {file = "coverage-5.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135"}, - {file = "coverage-5.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c"}, - {file = "coverage-5.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9754a5c265f991317de2bac0c70a746efc2b695cf4d49f5d2cddeac36544fb44"}, - {file = "coverage-5.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3"}, - {file = "coverage-5.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:b7f7421841f8db443855d2854e25914a79a1ff48ae92f70d0a5c2f8907ab98c9"}, - {file = "coverage-5.4-cp27-cp27m-win32.whl", hash = "sha256:4a780807e80479f281d47ee4af2eb2df3e4ccf4723484f77da0bb49d027e40a1"}, - {file = "coverage-5.4-cp27-cp27m-win_amd64.whl", hash = "sha256:87c4b38288f71acd2106f5d94f575bc2136ea2887fdb5dfe18003c881fa6b370"}, - {file = "coverage-5.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c6809ebcbf6c1049002b9ac09c127ae43929042ec1f1dbd8bb1615f7cd9f70a0"}, - {file = "coverage-5.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ba7ca81b6d60a9f7a0b4b4e175dcc38e8fef4992673d9d6e6879fd6de00dd9b8"}, - {file = "coverage-5.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:89fc12c6371bf963809abc46cced4a01ca4f99cba17be5e7d416ed7ef1245d19"}, - {file = "coverage-5.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a8eb7785bd23565b542b01fb39115a975fefb4a82f23d407503eee2c0106247"}, - {file = "coverage-5.4-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:7e40d3f8eb472c1509b12ac2a7e24158ec352fc8567b77ab02c0db053927e339"}, - {file = "coverage-5.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1ccae21a076d3d5f471700f6d30eb486da1626c380b23c70ae32ab823e453337"}, - {file = "coverage-5.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:755c56beeacac6a24c8e1074f89f34f4373abce8b662470d3aa719ae304931f3"}, - {file = "coverage-5.4-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:322549b880b2d746a7672bf6ff9ed3f895e9c9f108b714e7360292aa5c5d7cf4"}, - {file = "coverage-5.4-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:60a3307a84ec60578accd35d7f0c71a3a971430ed7eca6567399d2b50ef37b8c"}, - {file = "coverage-5.4-cp35-cp35m-win32.whl", hash = "sha256:1375bb8b88cb050a2d4e0da901001347a44302aeadb8ceb4b6e5aa373b8ea68f"}, - {file = "coverage-5.4-cp35-cp35m-win_amd64.whl", hash = "sha256:16baa799ec09cc0dcb43a10680573269d407c159325972dd7114ee7649e56c66"}, - {file = "coverage-5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2f2cf7a42d4b7654c9a67b9d091ec24374f7c58794858bff632a2039cb15984d"}, - {file = "coverage-5.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b62046592b44263fa7570f1117d372ae3f310222af1fc1407416f037fb3af21b"}, - {file = "coverage-5.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:812eaf4939ef2284d29653bcfee9665f11f013724f07258928f849a2306ea9f9"}, - {file = "coverage-5.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:859f0add98707b182b4867359e12bde806b82483fb12a9ae868a77880fc3b7af"}, - {file = "coverage-5.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:04b14e45d6a8e159c9767ae57ecb34563ad93440fc1b26516a89ceb5b33c1ad5"}, - {file = "coverage-5.4-cp36-cp36m-win32.whl", hash = "sha256:ebfa374067af240d079ef97b8064478f3bf71038b78b017eb6ec93ede1b6bcec"}, - {file = "coverage-5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:84df004223fd0550d0ea7a37882e5c889f3c6d45535c639ce9802293b39cd5c9"}, - {file = "coverage-5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1b811662ecf72eb2d08872731636aee6559cae21862c36f74703be727b45df90"}, - {file = "coverage-5.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6b588b5cf51dc0fd1c9e19f622457cc74b7d26fe295432e434525f1c0fae02bc"}, - {file = "coverage-5.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3fe50f1cac369b02d34ad904dfe0771acc483f82a1b54c5e93632916ba847b37"}, - {file = "coverage-5.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:32ab83016c24c5cf3db2943286b85b0a172dae08c58d0f53875235219b676409"}, - {file = "coverage-5.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:68fb816a5dd901c6aff352ce49e2a0ffadacdf9b6fae282a69e7a16a02dad5fb"}, - {file = "coverage-5.4-cp37-cp37m-win32.whl", hash = "sha256:a636160680c6e526b84f85d304e2f0bb4e94f8284dd765a1911de9a40450b10a"}, - {file = "coverage-5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:bb32ca14b4d04e172c541c69eec5f385f9a075b38fb22d765d8b0ce3af3a0c22"}, - {file = "coverage-5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4d7165a4e8f41eca6b990c12ee7f44fef3932fac48ca32cecb3a1b2223c21f"}, - {file = "coverage-5.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a565f48c4aae72d1d3d3f8e8fb7218f5609c964e9c6f68604608e5958b9c60c3"}, - {file = "coverage-5.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786"}, - {file = "coverage-5.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a839e25f07e428a87d17d857d9935dd743130e77ff46524abb992b962eb2076c"}, - {file = "coverage-5.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:6625e52b6f346a283c3d563d1fd8bae8956daafc64bb5bbd2b8f8a07608e3994"}, - {file = "coverage-5.4-cp38-cp38-win32.whl", hash = "sha256:5bee3970617b3d74759b2d2df2f6a327d372f9732f9ccbf03fa591b5f7581e39"}, - {file = "coverage-5.4-cp38-cp38-win_amd64.whl", hash = "sha256:03ed2a641e412e42cc35c244508cf186015c217f0e4d496bf6d7078ebe837ae7"}, - {file = "coverage-5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14a9f1887591684fb59fdba8feef7123a0da2424b0652e1b58dd5b9a7bb1188c"}, - {file = "coverage-5.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9564ac7eb1652c3701ac691ca72934dd3009997c81266807aef924012df2f4b3"}, - {file = "coverage-5.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:0f48fc7dc82ee14aeaedb986e175a429d24129b7eada1b7e94a864e4f0644dde"}, - {file = "coverage-5.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:107d327071061fd4f4a2587d14c389a27e4e5c93c7cba5f1f59987181903902f"}, - {file = "coverage-5.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:0cdde51bfcf6b6bd862ee9be324521ec619b20590787d1655d005c3fb175005f"}, - {file = "coverage-5.4-cp39-cp39-win32.whl", hash = "sha256:c67734cff78383a1f23ceba3b3239c7deefc62ac2b05fa6a47bcd565771e5880"}, - {file = "coverage-5.4-cp39-cp39-win_amd64.whl", hash = "sha256:c669b440ce46ae3abe9b2d44a913b5fd86bb19eb14a8701e88e3918902ecd345"}, - {file = "coverage-5.4-pp36-none-any.whl", hash = "sha256:c0ff1c1b4d13e2240821ef23c1efb1f009207cb3f56e16986f713c2b0e7cd37f"}, - {file = "coverage-5.4-pp37-none-any.whl", hash = "sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b"}, - {file = "coverage-5.4.tar.gz", hash = "sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca"}, + {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, + {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, + {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, + {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, + {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, + {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, + {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, + {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, + {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, + {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, + {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, + {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, + {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, + {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, + {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, + {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, + {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, + {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, + {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, + {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, + {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, + {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, + {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, + {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, ] dataclasses = [ {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, @@ -636,8 +642,8 @@ filelock = [ {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, ] identify = [ - {file = "identify-1.5.13-py2.py3-none-any.whl", hash = "sha256:9dfb63a2e871b807e3ba62f029813552a24b5289504f5b071dea9b041aee9fe4"}, - {file = "identify-1.5.13.tar.gz", hash = "sha256:70b638cf4743f33042bebb3b51e25261a0a10e80f978739f17e7fd4837664a66"}, + {file = "identify-2.1.1-py2.py3-none-any.whl", hash = "sha256:220169a38a0c977c8fef377dc808d6a3330641b5211ec7356c7bbe73cda487c7"}, + {file = "identify-2.1.1.tar.gz", hash = "sha256:da3d757c94596c50865aae63db6ba4e2e5e3f666c3ea6a6da0cd09a8b2d34abc"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, @@ -648,8 +654,8 @@ importlib-metadata = [ {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, ] importlib-resources = [ - {file = "importlib_resources-5.1.0-py3-none-any.whl", hash = "sha256:885b8eae589179f661c909d699a546cf10d83692553e34dca1bf5eb06f7f6217"}, - {file = "importlib_resources-5.1.0.tar.gz", hash = "sha256:bfdad047bce441405a49cf8eb48ddce5e56c696e185f59147a8b79e75e9e6380"}, + {file = "importlib_resources-5.1.2-py3-none-any.whl", hash = "sha256:ebab3efe74d83b04d6bf5cd9a17f0c5c93e60fb60f30c90f56265fce4682a469"}, + {file = "importlib_resources-5.1.2.tar.gz", hash = "sha256:642586fc4740bd1cad7690f836b3321309402b20b332529f25617ff18e8e1370"}, ] isort = [ {file = "isort-5.7.0-py3-none-any.whl", hash = "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"}, @@ -660,8 +666,8 @@ jsonschema = [ {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, ] more-itertools = [ - {file = "more-itertools-8.6.0.tar.gz", hash = "sha256:b3a9005928e5bed54076e6e549c792b306fddfe72b2d1d22dd63d42d5d3899cf"}, - {file = "more_itertools-8.6.0-py3-none-any.whl", hash = "sha256:8e1a2a43b2f2727425f2b5839587ae37093f19153dc26c0927d1048ff6557330"}, + {file = "more-itertools-8.7.0.tar.gz", hash = "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713"}, + {file = "more_itertools-8.7.0-py3-none-any.whl", hash = "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, @@ -688,10 +694,13 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] pre-commit = [ - {file = "pre_commit-2.10.0-py2.py3-none-any.whl", hash = "sha256:391ed331fdd0a21d0be48c1b9919921e9d372dfd60f6dc77b8f01dd6b13161c1"}, - {file = "pre_commit-2.10.0.tar.gz", hash = "sha256:f413348d3a8464b77987e36ef6e02c3372dadb823edf0dfe6fb0c3dc2f378ef9"}, + {file = "pre_commit-2.11.1-py2.py3-none-any.whl", hash = "sha256:94c82f1bf5899d56edb1d926732f4e75a7df29a0c8c092559c77420c9d62428b"}, + {file = "pre_commit-2.11.1.tar.gz", hash = "sha256:de55c5c72ce80d79106e48beb1b54104d16495ce7f95b0c7b13d4784193a00af"}, +] +py = [ + {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] -py = [] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, @@ -790,8 +799,8 @@ toml = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tox = [ - {file = "tox-3.21.4-py2.py3-none-any.whl", hash = "sha256:65d0e90ceb816638a50d64f4b47b11da767b284c0addda2294cb3cd69bd72425"}, - {file = "tox-3.21.4.tar.gz", hash = "sha256:cf7fef81a3a2434df4d7af2a6d1bf606d2970220addfbe7dea2615bd4bb2c252"}, + {file = "tox-3.23.0-py2.py3-none-any.whl", hash = "sha256:e007673f3595cede9b17a7c4962389e4305d4a3682a6c5a4159a1453b4f326aa"}, + {file = "tox-3.23.0.tar.gz", hash = "sha256:05a4dbd5e4d3d8269b72b55600f0b0303e2eb47ad5c6fe76d3576f4c58d93661"}, ] typed-ast = [ {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"}, @@ -847,6 +856,6 @@ wcwidth = [ {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] zipp = [ - {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, - {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, + {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, + {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"}, ] diff --git a/pyproject.toml b/pyproject.toml index 43cdf7e68..020ad8bed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ pytest = "^4.6" pytest-cov = "^2.8" pytest-mock = "^2.0" tox = "^3.0" -vendoring = {version = "^0.3", python = "~3.8"} +vendoring = {version = "^0.3", python = "^3.8"} pep517 = "^0.8.2" black = {version = "^20.8b1", markers = "python_version >= '3.6' and python_version < '4.0' and implementation_name != 'pypy'"} isort = "^5.7.0" From 5d5251c427aacedcf54f9743635a8124e5a26151 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 6 Oct 2020 12:18:34 +0200 Subject: [PATCH 31/77] package: do not default to develop Setting develop to `True` by default causes un-expected side-effects and requires downstream consumers to rely on checks like `package.develop and package.source_type == "directory""` to determine if develop mode. This also corresponds to current high-level behaviour of packages being non-develop by default. --- poetry/core/packages/package.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/poetry/core/packages/package.py b/poetry/core/packages/package.py index cf972dae5..4a8152e60 100644 --- a/poetry/core/packages/package.py +++ b/poetry/core/packages/package.py @@ -49,7 +49,8 @@ def __init__( source_url: Optional[str] = None, source_reference: Optional[str] = None, source_resolved_reference: Optional[str] = None, - features: Optional[List[str]] = None, # type + features: Optional[List[str]] = None, + develop: bool = False, ) -> None: """ Creates a new in memory package. @@ -105,7 +106,7 @@ def __init__( self.root_dir = None - self.develop = True + self.develop = develop @property def name(self) -> str: From 97102e02dc13fd48f624f5444ee2e7d31a1d78e1 Mon Sep 17 00:00:00 2001 From: stephsamson Date: Fri, 27 Nov 2020 13:08:40 +0100 Subject: [PATCH 32/77] Default include formats to both sdist and wheel. --- poetry/core/masonry/builders/builder.py | 8 +++++++- .../builders/fixtures/with_include_inline_table/both.txt | 0 .../fixtures/with_include_inline_table/pyproject.toml | 1 + tests/masonry/builders/test_sdist.py | 1 + tests/masonry/builders/test_wheel.py | 1 + 5 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/masonry/builders/fixtures/with_include_inline_table/both.txt diff --git a/poetry/core/masonry/builders/builder.py b/poetry/core/masonry/builders/builder.py index 04f12252c..513c26960 100644 --- a/poetry/core/masonry/builders/builder.py +++ b/poetry/core/masonry/builders/builder.py @@ -53,7 +53,13 @@ def __init__( packages = [] for p in self._package.packages: - formats = p.get("format", []) + formats = p.get("format") or None + + # Default to including the package in both sdist & wheel + # if the `format` key is not provided in the inline include table. + if formats is None: + formats = ["sdist", "wheel"] + if not isinstance(formats, list): formats = [formats] diff --git a/tests/masonry/builders/fixtures/with_include_inline_table/both.txt b/tests/masonry/builders/fixtures/with_include_inline_table/both.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/masonry/builders/fixtures/with_include_inline_table/pyproject.toml b/tests/masonry/builders/fixtures/with_include_inline_table/pyproject.toml index 95b0949c6..5309992f1 100644 --- a/tests/masonry/builders/fixtures/with_include_inline_table/pyproject.toml +++ b/tests/masonry/builders/fixtures/with_include_inline_table/pyproject.toml @@ -24,6 +24,7 @@ packages = [ include = [ { path = "tests", format = "sdist" }, + { path = "both.txt" }, { path = "wheel_only.txt", format = "wheel" } ] diff --git a/tests/masonry/builders/test_sdist.py b/tests/masonry/builders/test_sdist.py index 3cb2a3b12..b67be206c 100644 --- a/tests/masonry/builders/test_sdist.py +++ b/tests/masonry/builders/test_sdist.py @@ -529,6 +529,7 @@ def test_includes_with_inline_table(): assert sdist.exists() with tarfile.open(str(sdist), "r") as tar: + assert "with-include-1.2.3/both.txt" in tar.getnames() assert "with-include-1.2.3/wheel_only.txt" not in tar.getnames() assert "with-include-1.2.3/tests/__init__.py" in tar.getnames() assert "with-include-1.2.3/tests/test_foo/test.py" in tar.getnames() diff --git a/tests/masonry/builders/test_wheel.py b/tests/masonry/builders/test_wheel.py index 3db7c04d4..9a54ea9f2 100644 --- a/tests/masonry/builders/test_wheel.py +++ b/tests/masonry/builders/test_wheel.py @@ -182,6 +182,7 @@ def test_wheel_includes_inline_table(): assert whl.exists() with zipfile.ZipFile(str(whl)) as z: + assert "both.txt" in z.namelist() assert "wheel_only.txt" in z.namelist() assert "notes.txt" not in z.namelist() From b4ec173fb935ffd72f32c536a2a44b1e9048c6b7 Mon Sep 17 00:00:00 2001 From: Andrew Guenther Date: Mon, 22 Mar 2021 14:06:20 -0700 Subject: [PATCH 33/77] use uri for direct reference directory dependencies --- poetry/core/packages/directory_dependency.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/poetry/core/packages/directory_dependency.py b/poetry/core/packages/directory_dependency.py index d0cbf2523..184f534a2 100644 --- a/poetry/core/packages/directory_dependency.py +++ b/poetry/core/packages/directory_dependency.py @@ -10,6 +10,7 @@ from .constraints import BaseConstraint # noqa from .dependency import Dependency +from .utils.utils import path_to_url class DirectoryDependency(Dependency): @@ -121,7 +122,8 @@ def base_pep_508_name(self) -> str: if self.extras: requirement += "[{}]".format(",".join(self.extras)) - requirement += " @ {}".format(self._path.as_posix()) + path = path_to_url(self.path) if self.path.is_absolute() else self.path + requirement += " @ {}".format(path) return requirement From 748157e58b4676f9d2706e525c2a3f13cbda8ca4 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Fri, 4 Sep 2020 14:41:41 +0200 Subject: [PATCH 34/77] Improve PEP-440 support for versions This is a broad change aligning `poetry.core.semver.version.Version` implementation to be closer to PEP-440 that to semver. This adds the foundation for improved dev and local tag support in poetry managed version as well as fix ordering bugs that existed due to the differences between semantic versioning specification and PEP-440. Further, previously used inline copy of `packaging.version.Version` implementation has now been removed in favour of `poetry.core.version.pep440.PEP440Version` implementation. --- poetry.lock | 5 +- poetry/core/packages/dependency.py | 2 +- poetry/core/packages/package.py | 2 +- poetry/core/semver/helpers.py | 22 +- poetry/core/semver/version.py | 421 +++--------------- poetry/core/semver/version_constraint.py | 11 +- poetry/core/semver/version_range.py | 93 +--- .../core/semver/version_range_constraint.py | 91 ++++ poetry/core/semver/version_union.py | 15 +- poetry/core/utils/helpers.py | 4 +- poetry/core/version/__init__.py | 45 -- poetry/core/version/base.py | 34 -- poetry/core/version/grammars/parser.py | 34 +- poetry/core/version/grammars/pep440.lark | 32 ++ poetry/core/version/legacy_version.py | 92 ---- poetry/core/version/markers.py | 4 +- poetry/core/version/pep440/__init__.py | 4 + poetry/core/version/pep440/parser.py | 90 ++++ poetry/core/version/pep440/segments.py | 151 +++++++ poetry/core/version/pep440/version.py | 146 ++++++ poetry/core/version/requirements.py | 4 +- poetry/core/version/utils.py | 65 --- poetry/core/version/version.py | 258 ----------- pyproject.toml | 1 + tests/semver/test_helpers.py | 245 ++++++++-- tests/semver/test_parse_constraint.py | 133 +++++- tests/semver/test_version.py | 43 +- tests/version/test_requirements.py | 6 +- 28 files changed, 976 insertions(+), 1077 deletions(-) create mode 100644 poetry/core/semver/version_range_constraint.py delete mode 100644 poetry/core/version/base.py create mode 100644 poetry/core/version/grammars/pep440.lark delete mode 100644 poetry/core/version/legacy_version.py create mode 100644 poetry/core/version/pep440/__init__.py create mode 100644 poetry/core/version/pep440/parser.py create mode 100644 poetry/core/version/pep440/segments.py create mode 100644 poetry/core/version/pep440/version.py delete mode 100644 poetry/core/version/utils.py delete mode 100644 poetry/core/version/version.py diff --git a/poetry.lock b/poetry.lock index 0b585234d..e4fdb348c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -106,7 +106,7 @@ toml = ["toml"] name = "dataclasses" version = "0.8" description = "A backport of the dataclasses module for Python 3.6" -category = "dev" +category = "main" optional = false python-versions = ">=3.6, <3.7" @@ -198,6 +198,7 @@ python-versions = "*" [package.dependencies] attrs = ">=17.4.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} pyrsistent = ">=0.14.0" six = ">=1.11.0" @@ -536,7 +537,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "317b9ea5e19fcb7b559aaa52f325fe5dddb30ec5f938446b79608c6b8b10fcba" +content-hash = "9a3dbd14f45aa7c96293a0b4286c71f0b6501bc5fda78d834a9212c0c55cf3cd" [metadata.files] appdirs = [ diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py index 75a89da16..a0ebea0eb 100644 --- a/poetry/core/packages/dependency.py +++ b/poetry/core/packages/dependency.py @@ -62,7 +62,7 @@ def __init__( if isinstance(self._constraint, VersionRange) and self._constraint.min: allows_prereleases = ( - allows_prereleases or self._constraint.min.is_prerelease() + allows_prereleases or self._constraint.min.is_pre_release() ) self._allows_prereleases = allows_prereleases diff --git a/poetry/core/packages/package.py b/poetry/core/packages/package.py index 4a8152e60..69d1b9473 100644 --- a/poetry/core/packages/package.py +++ b/poetry/core/packages/package.py @@ -305,7 +305,7 @@ def urls(self) -> Dict[str, str]: return urls def is_prerelease(self) -> bool: - return self._version.is_prerelease() + return self._version.is_pre_release() def is_root(self) -> bool: return False diff --git a/poetry/core/semver/helpers.py b/poetry/core/semver/helpers.py index e20008f2e..7d9ca49c4 100644 --- a/poetry/core/semver/helpers.py +++ b/poetry/core/semver/helpers.py @@ -69,10 +69,9 @@ def parse_single_constraint(constraint: str) -> VersionTypes: m = TILDE_CONSTRAINT.match(constraint) if m: version = Version.parse(m.group(1)) - - high = version.stable.next_minor + high = version.stable.next_minor() if len(m.group(1).split(".")) == 1: - high = version.stable.next_major + high = version.stable.next_major() return VersionRange(version, high, include_min=True) @@ -89,9 +88,9 @@ def parse_single_constraint(constraint: str) -> VersionTypes: version = Version.parse(m.group(1)) if precision == 2: - high = version.stable.next_major + high = version.stable.next_major() else: - high = version.stable.next_minor + high = version.stable.next_minor() return VersionRange(version, high, include_min=True) @@ -100,7 +99,7 @@ def parse_single_constraint(constraint: str) -> VersionTypes: if m: version = Version.parse(m.group(1)) - return VersionRange(version, version.next_breaking, include_min=True) + return VersionRange(version, version.next_breaking(), include_min=True) # X Range m = X_CONSTRAINT.match(constraint) @@ -110,16 +109,15 @@ def parse_single_constraint(constraint: str) -> VersionTypes: minor = m.group(3) if minor is not None: - version = Version(major, int(minor), 0) - - result = VersionRange(version, version.next_minor, include_min=True) + version = Version.from_parts(major, int(minor), 0) + result = VersionRange(version, version.next_minor(), include_min=True) else: if major == 0: - result = VersionRange(max=Version(1, 0, 0)) + result = VersionRange(max=Version.from_parts(1, 0, 0)) else: - version = Version(major, 0, 0) + version = Version.from_parts(major, 0, 0) - result = VersionRange(version, version.next_major, include_min=True) + result = VersionRange(version, version.next_major(), include_min=True) if op == "!=": result = VersionRange().difference(result) diff --git a/poetry/core/semver/version.py b/poetry/core/semver/version.py index e20764d8c..5bdc2042c 100644 --- a/poetry/core/semver/version.py +++ b/poetry/core/semver/version.py @@ -1,184 +1,57 @@ -import re +import dataclasses from typing import TYPE_CHECKING -from typing import List from typing import Optional +from typing import Tuple from typing import Union -from .empty_constraint import EmptyConstraint -from .exceptions import ParseVersionError -from .patterns import COMPLETE_VERSION -from .version_constraint import VersionConstraint -from .version_range import VersionRange -from .version_union import VersionUnion +from poetry.core.semver.empty_constraint import EmptyConstraint +from poetry.core.semver.version_range_constraint import VersionRangeConstraint +from poetry.core.semver.version_union import VersionUnion +from poetry.core.version.pep440 import Release +from poetry.core.version.pep440 import ReleaseTag +from poetry.core.version.pep440.version import PEP440Version if TYPE_CHECKING: - from . import VersionTypes # noqa + from poetry.core.semver.helpers import VersionTypes + from poetry.core.semver.version_range import VersionRange + from poetry.core.version.pep440 import LocalSegmentType -class Version(VersionRange): +@dataclasses.dataclass(frozen=True) +class Version(PEP440Version, VersionRangeConstraint): """ A parsed semantic version number. """ - def __init__( - self, - major: int, - minor: Optional[int] = None, - patch: Optional[int] = None, - rest: Optional[int] = None, - pre: Optional[str] = None, - build: Optional[str] = None, - text: Optional[str] = None, - precision: Optional[int] = None, - ) -> None: - self._major = int(major) - self._precision = None - if precision is None: - self._precision = 1 - - if minor is None: - minor = 0 - else: - if self._precision is not None: - self._precision += 1 - - self._minor = int(minor) - - if patch is None: - patch = 0 - else: - if self._precision is not None: - self._precision += 1 - - if rest is None: - rest = 0 - else: - if self._precision is not None: - self._precision += 1 - - if precision is not None: - self._precision = precision - - self._patch = int(patch) - self._rest = int(rest) - - if text is None: - parts = [str(major)] - if self._precision >= 2 or minor != 0: - parts.append(str(minor)) - - if self._precision >= 3 or patch != 0: - parts.append(str(patch)) - - if self._precision >= 4 or rest != 0: - parts.append(str(rest)) - - text = ".".join(parts) - if pre: - text += "-{}".format(pre) - - if build: - text += "+{}".format(build) - - self._text = text - - pre = self._normalize_prerelease(pre) - - self._prerelease = [] - if pre is not None: - self._prerelease = self._split_parts(pre) - - build = self._normalize_build(build) - - self._build = [] - if build is not None: - if build.startswith(("-", "+")): - build = build[1:] - - self._build = self._split_parts(build) - - @property - def major(self) -> int: - return self._major - - @property - def minor(self) -> int: - return self._minor - - @property - def patch(self) -> int: - return self._patch - - @property - def rest(self) -> int: - return self._rest - - @property - def prerelease(self) -> List[str]: - return self._prerelease - - @property - def build(self) -> List[str]: - return self._build - - @property - def text(self) -> str: - return self._text - @property def precision(self) -> int: - return self._precision + return self.release.precision @property def stable(self) -> "Version": - if not self.is_prerelease(): + if self.is_stable_release(): return self - return self.next_patch - - @property - def next_major(self) -> "Version": - if self.is_prerelease() and self.minor == 0 and self.patch == 0: - return Version(self.major, self.minor, self.patch) - - return self._increment_major() - - @property - def next_minor(self) -> "Version": - if self.is_prerelease() and self.patch == 0: - return Version(self.major, self.minor, self.patch) - - return self._increment_minor() - - @property - def next_patch(self) -> "Version": - if self.is_prerelease(): - return Version(self.major, self.minor, self.patch) - - return self._increment_patch() + return self.next_patch() - @property def next_breaking(self) -> "Version": if self.major == 0: if self.minor != 0: - return self._increment_minor() + return self.next_minor() - if self._precision == 1: - return self._increment_major() - elif self._precision == 2: - return self._increment_minor() + if self.precision == 1: + return self.next_major() + elif self.precision == 2: + return self.next_minor() - return self._increment_patch() + return self.next_patch() - return self._increment_major() + return self.next_major() - @property - def first_prerelease(self) -> "Version": - return Version.parse( - "{}.{}.{}-alpha.0".format(self.major, self.minor, self.patch) - ) + def first_pre_release(self) -> "Version": + return self.__class__(release=self.release, pre=ReleaseTag("alpha")) @property def min(self) -> "Version": @@ -200,40 +73,12 @@ def include_min(self) -> bool: def include_max(self) -> bool: return True - @classmethod - def parse(cls, text: str) -> "Version": - try: - match = COMPLETE_VERSION.match(text) - except TypeError: - match = None - - if match is None: - raise ParseVersionError('Unable to parse "{}".'.format(text)) - - text = text.rstrip(".") - - major = int(match.group(1)) - minor = int(match.group(2)) if match.group(2) else None - patch = int(match.group(3)) if match.group(3) else None - rest = int(match.group(4)) if match.group(4) else None - - pre = match.group(5) - build = match.group(6) - - if build: - build = build.lstrip("+") - - return Version(major, minor, patch, rest, pre, build, text) - def is_any(self) -> bool: return False def is_empty(self) -> bool: return False - def is_prerelease(self) -> bool: - return len(self._prerelease) > 0 - def allows(self, version: "Version") -> bool: return self == version @@ -250,12 +95,12 @@ def intersect(self, other: "VersionTypes") -> Union["Version", EmptyConstraint]: return EmptyConstraint() def union(self, other: "VersionTypes") -> "VersionTypes": - from .version_range import VersionRange + from poetry.core.semver.version_range import VersionRange if other.allows(self): return other - if isinstance(other, VersionRange): + if isinstance(other, VersionRangeConstraint): if other.min == self: return VersionRange( other.min, @@ -280,193 +125,39 @@ def difference(self, other: "VersionTypes") -> Union["Version", EmptyConstraint] return self - def equals_without_prerelease(self, other: "Version") -> bool: - return ( - self.major == other.major - and self.minor == other.minor - and self.patch == other.patch - ) - - def _increment_major(self) -> "Version": - return Version(self.major + 1, 0, 0, precision=self._precision) - - def _increment_minor(self) -> "Version": - return Version(self.major, self.minor + 1, 0, precision=self._precision) - - def _increment_patch(self) -> "Version": - return Version( - self.major, self.minor, self.patch + 1, precision=self._precision - ) - - def _normalize_prerelease(self, pre: str) -> Optional[str]: - if not pre: - return - - m = re.match(r"(?i)^(a|alpha|b|beta|c|pre|rc|dev)[-.]?(\d+)?$", pre) - if not m: - return - - modifier = m.group(1) - number = m.group(2) - - if number is None: - number = 0 - - if modifier == "a": - modifier = "alpha" - elif modifier == "b": - modifier = "beta" - elif modifier in {"c", "pre"}: - modifier = "rc" - elif modifier == "dev": - modifier = "alpha" - - return "{}.{}".format(modifier, number) - - def _normalize_build(self, build: str) -> Optional[str]: - if not build: - return - - if build.startswith("post"): - build = build.lstrip("post") - - if not build: - return - - return build - - def _split_parts(self, text: str) -> List[Union[str, int]]: - parts = text.split(".") - - for i, part in enumerate(parts): - try: - parts[i] = int(part) - except (TypeError, ValueError): - continue - - return parts - - def __lt__(self, other: "Version") -> int: - return self._cmp(other) < 0 - - def __le__(self, other: "Version") -> int: - return self._cmp(other) <= 0 - - def __gt__(self, other: "Version") -> int: - return self._cmp(other) > 0 - - def __ge__(self, other: "Version") -> int: - return self._cmp(other) >= 0 - - def _cmp(self, other: "Version") -> int: - if not isinstance(other, VersionConstraint): - return NotImplemented - - if not isinstance(other, Version): - return -other._cmp(self) - - if self.major != other.major: - return self._cmp_parts(self.major, other.major) - - if self.minor != other.minor: - return self._cmp_parts(self.minor, other.minor) - - if self.patch != other.patch: - return self._cmp_parts(self.patch, other.patch) - - if self.rest != other.rest: - return self._cmp_parts(self.rest, other.rest) - - # Pre-releases always come before no pre-release string. - if not self.is_prerelease() and other.is_prerelease(): - return 1 - - if not other.is_prerelease() and self.is_prerelease(): - return -1 - - comparison = self._cmp_lists(self.prerelease, other.prerelease) - if comparison != 0: - return comparison - - # Builds always come after no build string. - if not self.build and other.build: - return -1 - - if not other.build and self.build: - return 1 - - return self._cmp_lists(self.build, other.build) - - def _cmp_parts(self, a: Optional[int], b: Optional[int]) -> int: - if a < b: - return -1 - elif a > b: - return 1 - - return 0 - - def _cmp_lists(self, a: List, b: List) -> int: - for i in range(max(len(a), len(b))): - a_part = None - if i < len(a): - a_part = a[i] - - b_part = None - if i < len(b): - b_part = b[i] - - if a_part == b_part: - continue - - # Missing parts come after present ones. - if a_part is None: - return -1 - - if b_part is None: - return 1 - - if isinstance(a_part, int): - if isinstance(b_part, int): - return self._cmp_parts(a_part, b_part) - - return -1 - else: - if isinstance(b_part, int): - return 1 - - return self._cmp_parts(a_part, b_part) - - return 0 - - def __eq__(self, other: "Version") -> bool: - if not isinstance(other, Version): - return NotImplemented - - return ( - self._major == other.major - and self._minor == other.minor - and self._patch == other.patch - and self._rest == other.rest - and self._prerelease == other.prerelease - and self._build == other.build - ) - - def __ne__(self, other: "VersionTypes") -> bool: - return not self == other - def __str__(self) -> str: - return self._text + return self.text def __repr__(self) -> str: return "".format(str(self)) - def __hash__(self) -> int: - return hash( - ( - self.major, - self.minor, - self.patch, - ".".join(str(p) for p in self.prerelease), - ".".join(str(p) for p in self.build), + def __eq__(self, other: Union["Version", "VersionRange"]) -> bool: + from poetry.core.semver.version_range import VersionRange + + if isinstance(other, VersionRange): + return ( + self == other.min + and self == other.max + and (other.include_min or other.include_max) ) + return super().__eq__(other) + + @classmethod + def from_parts( + cls, + major: int, + minor: Optional[int] = None, + patch: Optional[int] = None, + extra: Optional[Union[int, Tuple[int, ...]]] = None, + pre: Optional[ReleaseTag] = None, + post: Optional[ReleaseTag] = None, + dev: Optional[ReleaseTag] = None, + local: "LocalSegmentType" = None, + ): + return cls( + release=Release(major=major, minor=minor, patch=patch, extra=extra), + pre=pre, + post=post, + dev=dev, + local=local, ) diff --git a/poetry/core/semver/version_constraint.py b/poetry/core/semver/version_constraint.py index 127878779..4dcf7f1dd 100644 --- a/poetry/core/semver/version_constraint.py +++ b/poetry/core/semver/version_constraint.py @@ -1,31 +1,40 @@ +from abc import abstractmethod from typing import TYPE_CHECKING if TYPE_CHECKING: - from poetry.core.semver.version import Version # noqa + from poetry.core.semver.version import Version class VersionConstraint: + @abstractmethod def is_empty(self) -> bool: raise NotImplementedError() + @abstractmethod def is_any(self) -> bool: raise NotImplementedError() + @abstractmethod def allows(self, version: "Version") -> bool: raise NotImplementedError() + @abstractmethod def allows_all(self, other: "VersionConstraint") -> bool: raise NotImplementedError() + @abstractmethod def allows_any(self, other: "VersionConstraint") -> bool: raise NotImplementedError() + @abstractmethod def intersect(self, other: "VersionConstraint") -> "VersionConstraint": raise NotImplementedError() + @abstractmethod def union(self, other: "VersionConstraint") -> "VersionConstraint": raise NotImplementedError() + @abstractmethod def difference(self, other: "VersionConstraint") -> "VersionConstraint": raise NotImplementedError() diff --git a/poetry/core/semver/version_range.py b/poetry/core/semver/version_range.py index e07904f04..00909e28a 100644 --- a/poetry/core/semver/version_range.py +++ b/poetry/core/semver/version_range.py @@ -3,18 +3,17 @@ from typing import List from typing import Optional -from .empty_constraint import EmptyConstraint -from .version_constraint import VersionConstraint -from .version_union import VersionUnion +from poetry.core.semver.empty_constraint import EmptyConstraint +from poetry.core.semver.version_range_constraint import VersionRangeConstraint +from poetry.core.semver.version_union import VersionUnion if TYPE_CHECKING: + from poetry.core.semver.helpers import VersionTypes from poetry.core.semver.version import Version - from . import VersionTypes # noqa - -class VersionRange(VersionConstraint): +class VersionRange(VersionRangeConstraint): def __init__( self, min: Optional["Version"] = None, @@ -28,15 +27,15 @@ def __init__( not always_include_max_prerelease and not include_max and full_max is not None - and not full_max.is_prerelease() - and not full_max.build + and not full_max.is_pre_release() + and not full_max.is_post_release() and ( min is None - or not min.is_prerelease() - or not min.equals_without_prerelease(full_max) + or not min.is_pre_release() + or min.release != full_max.release ) ): - full_max = full_max.first_prerelease + full_max = full_max.first_pre_release() self._min = min self._max = max @@ -320,62 +319,6 @@ def difference(self, other: "VersionTypes") -> "VersionTypes": raise ValueError("Unknown VersionConstraint type {}.".format(other)) - def allows_lower(self, other: "VersionRange") -> bool: - if self.min is None: - return other.min is not None - - if other.min is None: - return False - - if self.min < other.min: - return True - - if self.min > other.min: - return False - - return self.include_min and not other.include_min - - def allows_higher(self, other: "VersionRange") -> bool: - if self.full_max is None: - return other.max is not None - - if other.full_max is None: - return False - - if self.full_max < other.full_max: - return False - - if self.full_max > other.full_max: - return True - - return self.include_max and not other.include_max - - def is_strictly_lower(self, other: "VersionRange") -> bool: - if self.full_max is None or other.min is None: - return False - - if self.full_max < other.min: - return True - - if self.full_max > other.min: - return False - - return not self.include_max or not other.include_min - - def is_strictly_higher(self, other: "VersionRange") -> bool: - return other.is_strictly_lower(self) - - def is_adjacent_to(self, other: "VersionRange") -> bool: - if self.max != other.min: - return False - - return ( - self.include_max - and not other.include_min - or not self.include_max - and other.include_min - ) - def __eq__(self, other: Any) -> int: if not isinstance(other, VersionRange): return False @@ -408,16 +351,17 @@ def _cmp(self, other: "VersionRange") -> int: elif other.min is None: return 1 - result = self.min._cmp(other.min) - if result != 0: - return result + if self.min > other.min: + return 1 + elif self.min < other.min: + return -1 if self.include_min != other.include_min: return -1 if self.include_min else 1 return self._compare_max(other) - def _compare_max(self, other: "VersionRange") -> int: + def _compare_max(self, other: "VersionRangeConstraint") -> int: if self.max is None: if other.max is None: return 0 @@ -426,9 +370,10 @@ def _compare_max(self, other: "VersionRange") -> int: elif other.max is None: return -1 - result = self.max._cmp(other.max) - if result != 0: - return result + if self.max > other.max: + return 1 + elif self.max < other.max: + return -1 if self.include_max != other.include_max: return 1 if self.include_max else -1 diff --git a/poetry/core/semver/version_range_constraint.py b/poetry/core/semver/version_range_constraint.py new file mode 100644 index 000000000..30c8c2a09 --- /dev/null +++ b/poetry/core/semver/version_range_constraint.py @@ -0,0 +1,91 @@ +from abc import abstractmethod +from typing import TYPE_CHECKING + +from poetry.core.semver.version_constraint import VersionConstraint + + +if TYPE_CHECKING: + from poetry.core.semver.version import Version + + +class VersionRangeConstraint(VersionConstraint): + @property + @abstractmethod + def min(self) -> "Version": + raise NotImplementedError() + + @property + @abstractmethod + def max(self) -> "Version": + raise NotImplementedError() + + @property + @abstractmethod + def full_max(self) -> "Version": + raise NotImplementedError() + + @property + @abstractmethod + def include_min(self) -> bool: + raise NotImplementedError() + + @property + @abstractmethod + def include_max(self) -> bool: + raise NotImplementedError() + + def allows_lower(self, other: "VersionRangeConstraint") -> bool: + if self.min is None: + return other.min is not None + + if other.min is None: + return False + + if self.min < other.min: + return True + + if self.min > other.min: + return False + + return self.include_min and not other.include_min + + def allows_higher(self, other: "VersionRangeConstraint") -> bool: + if self.full_max is None: + return other.max is not None + + if other.full_max is None: + return False + + if self.full_max < other.full_max: + return False + + if self.full_max > other.full_max: + return True + + return self.include_max and not other.include_max + + def is_strictly_lower(self, other: "VersionRangeConstraint") -> bool: + if self.full_max is None or other.min is None: + return False + + if self.full_max < other.min: + return True + + if self.full_max > other.min: + return False + + return not self.include_max or not other.include_min + + def is_strictly_higher(self, other: "VersionRangeConstraint") -> bool: + return other.is_strictly_lower(self) + + def is_adjacent_to(self, other: "VersionRangeConstraint") -> bool: + if self.max != other.min: + return False + + return ( + self.include_max + and not other.include_min + or not self.include_max + and other.include_min + ) diff --git a/poetry/core/semver/version_union.py b/poetry/core/semver/version_union.py index b37e99e8f..42bae9981 100644 --- a/poetry/core/semver/version_union.py +++ b/poetry/core/semver/version_union.py @@ -4,12 +4,13 @@ from .empty_constraint import EmptyConstraint from .version_constraint import VersionConstraint +from .version_range_constraint import VersionRangeConstraint if TYPE_CHECKING: - from . import VersionTypes # noqa - from .version import Version - from .version_range import VersionRange + from poetry.core.semver.helpers import VersionTypes + from poetry.core.semver.version import Version + from poetry.core.semver.version_range import VersionRange class VersionUnion(VersionConstraint): @@ -53,7 +54,7 @@ def of(cls, *ranges: "VersionTypes") -> "VersionTypes": # about everything in flattened. _EmptyVersions and VersionUnions are # filtered out above. for constraint in flattened: - if isinstance(constraint, VersionRange): + if isinstance(constraint, VersionRangeConstraint): continue raise ValueError("Unknown VersionConstraint type {}.".format(constraint)) @@ -222,16 +223,14 @@ def our_next_range(include_current: bool = True) -> bool: return VersionUnion.of(*new_ranges) - def _ranges_for(self, constraint: "VersionTypes") -> List["VersionRange"]: - from .version_range import VersionRange - + def _ranges_for(self, constraint: "VersionTypes") -> List["VersionRangeConstraint"]: if constraint.is_empty(): return [] if isinstance(constraint, VersionUnion): return constraint.ranges - if isinstance(constraint, VersionRange): + if isinstance(constraint, VersionRangeConstraint): return [constraint] raise ValueError("Unknown VersionConstraint type {}".format(constraint)) diff --git a/poetry/core/utils/helpers.py b/poetry/core/utils/helpers.py index 22ccff21f..9eb47e6fd 100644 --- a/poetry/core/utils/helpers.py +++ b/poetry/core/utils/helpers.py @@ -11,7 +11,7 @@ from typing import List from typing import Union -from poetry.core.version import Version +from poetry.core.version.pep440 import PEP440Version try: @@ -32,7 +32,7 @@ def module_name(name: str) -> str: def normalize_version(version: str) -> str: - return str(Version(version)) + return PEP440Version.parse(version).to_string(short=True) @contextmanager diff --git a/poetry/core/version/__init__.py b/poetry/core/version/__init__.py index 3b106577e..e69de29bb 100644 --- a/poetry/core/version/__init__.py +++ b/poetry/core/version/__init__.py @@ -1,45 +0,0 @@ -import operator - -from typing import Union - -from .exceptions import InvalidVersion -from .legacy_version import LegacyVersion -from .version import Version - - -OP_EQ = operator.eq -OP_LT = operator.lt -OP_LE = operator.le -OP_GT = operator.gt -OP_GE = operator.ge -OP_NE = operator.ne - -_trans_op = { - "=": OP_EQ, - "==": OP_EQ, - "<": OP_LT, - "<=": OP_LE, - ">": OP_GT, - ">=": OP_GE, - "!=": OP_NE, -} - - -def parse( - version: str, - strict: bool = False, -) -> Union[Version, LegacyVersion]: - """ - Parse the given version string and return either a :class:`Version` object - or a LegacyVersion object depending on if the given version is - a valid PEP 440 version or a legacy version. - - If strict=True only PEP 440 versions will be accepted. - """ - try: - return Version(version) - except InvalidVersion: - if strict: - raise - - return LegacyVersion(version) diff --git a/poetry/core/version/base.py b/poetry/core/version/base.py deleted file mode 100644 index 78197da90..000000000 --- a/poetry/core/version/base.py +++ /dev/null @@ -1,34 +0,0 @@ -from typing import Callable - - -class BaseVersion: - def __init__(self, version: str) -> None: - self._version = str(version) - self._key = None - - def __hash__(self) -> int: - return hash(self._key) - - def __lt__(self, other: "BaseVersion") -> bool: - return self._compare(other, lambda s, o: s < o) - - def __le__(self, other: "BaseVersion") -> bool: - return self._compare(other, lambda s, o: s <= o) - - def __eq__(self, other: "BaseVersion") -> bool: - return self._compare(other, lambda s, o: s == o) - - def __ge__(self, other: "BaseVersion") -> bool: - return self._compare(other, lambda s, o: s >= o) - - def __gt__(self, other: "BaseVersion") -> bool: - return self._compare(other, lambda s, o: s > o) - - def __ne__(self, other: "BaseVersion") -> bool: - return self._compare(other, lambda s, o: s != o) - - def _compare(self, other: "BaseVersion", method: Callable) -> bool: - if not isinstance(other, BaseVersion): - return NotImplemented - - return method(self._key, other._key) diff --git a/poetry/core/version/grammars/parser.py b/poetry/core/version/grammars/parser.py index 13b8d76e3..2ccf244d0 100644 --- a/poetry/core/version/grammars/parser.py +++ b/poetry/core/version/grammars/parser.py @@ -1,26 +1,20 @@ -import os +from pathlib import Path -from typing import TYPE_CHECKING -from typing import Optional +from lark import Lark -if TYPE_CHECKING: - from lark import Lark # noqa - from lark import Tree # noqa +GRAMMAR_DIR = Path(__file__).parent +# Parser: PEP 440 +# we use earley because the grammar is ambiguous +PARSER_PEP_440 = Lark.open(GRAMMAR_DIR / "pep440.lark", parser="earley", debug=False) -class Parser: - def __init__(self, grammar: str) -> None: - self._grammar = grammar - self._parser: Optional["Lark"] = None +# Parser: PEP 508 Constraints +PARSER_PEP_508_CONSTRAINTS = Lark.open( + GRAMMAR_DIR / "pep508.lark", parser="lalr", debug=False +) - def parse(self, string: str) -> "Tree": - from lark import Lark - - if self._parser is None: - self._parser = Lark.open( - os.path.join(os.path.dirname(__file__), f"{self._grammar}.lark"), - parser="lalr", - ) - - return self._parser.parse(string) +# Parser: PEP 508 Environment Markers +PARSER_PEP_508_MARKERS = Lark.open( + GRAMMAR_DIR / "markers.lark", parser="lalr", debug=False +) diff --git a/poetry/core/version/grammars/pep440.lark b/poetry/core/version/grammars/pep440.lark new file mode 100644 index 000000000..62749f3b7 --- /dev/null +++ b/poetry/core/version/grammars/pep440.lark @@ -0,0 +1,32 @@ +// this is a modified version of the semver 2.0 specification grammar, specificially +// crafted for use with Python PEP 440 version specifiers. +start: version + +version: "v"? epoch? release pre_release? post_release? dev_release? ("+" local)? +release: epoch? NUMERIC_IDENTIFIER (("." NUMERIC_IDENTIFIER)+)? + +major: NUMERIC_IDENTIFIER +minor: NUMERIC_IDENTIFIER +patch: NUMERIC_IDENTIFIER + +epoch: INT "!" + +pre_release: _SEPERATOR? PRE_RELEASE_TAG _SEPERATOR? NUMERIC_IDENTIFIER? +PRE_RELEASE_TAG: "a" "lpha"? | "b" "eta"? | "c" | "rc" | "pre" "view"? + +post_release: "-" NUMERIC_IDENTIFIER | _SEPERATOR? POST_RELEASE_TAG _SEPERATOR? NUMERIC_IDENTIFIER? +POST_RELEASE_TAG: "post" | "r" "ev"? + +dev_release: _SEPERATOR? DEV_RELEASE_TAG _SEPERATOR? NUMERIC_IDENTIFIER? +DEV_RELEASE_TAG: "dev" + +local: LOCAL_IDENTIFIER ((_SEPERATOR LOCAL_IDENTIFIER)+)? +LOCAL_IDENTIFIER: (LETTER | INT)+ + +NUMERIC_IDENTIFIER: INT + +_SEPERATOR: "-" | "." | "_" + +%import common.LETTER +%import common.DIGIT +%import common.INT diff --git a/poetry/core/version/legacy_version.py b/poetry/core/version/legacy_version.py deleted file mode 100644 index 49b62aeb3..000000000 --- a/poetry/core/version/legacy_version.py +++ /dev/null @@ -1,92 +0,0 @@ -import re - -from typing import Tuple - -from .base import BaseVersion - - -class LegacyVersion(BaseVersion): - def __init__(self, version: str) -> None: - self._version = str(version) - self._key = _legacy_cmpkey(self._version) - - def __str__(self) -> str: - return self._version - - def __repr__(self) -> str: - return "".format(repr(str(self))) - - @property - def public(self) -> str: - return self._version - - @property - def base_version(self) -> str: - return self._version - - @property - def local(self) -> None: - return None - - @property - def is_prerelease(self) -> bool: - return False - - @property - def is_postrelease(self) -> bool: - return False - - -_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) - -_legacy_version_replacement_map = { - "pre": "c", - "preview": "c", - "-": "final-", - "rc": "c", - "dev": "@", -} - - -def _parse_version_parts(s: str) -> str: - for part in _legacy_version_component_re.split(s): - part = _legacy_version_replacement_map.get(part, part) - - if not part or part == ".": - continue - - if part[:1] in "0123456789": - # pad for numeric comparison - yield part.zfill(8) - else: - yield "*" + part - - # ensure that alpha/beta/candidate are before final - yield "*final" - - -def _legacy_cmpkey(version: str) -> Tuple[int, Tuple[str]]: - # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch - # greater than or equal to 0. This will effectively put the LegacyVersion, - # which uses the defacto standard originally implemented by setuptools, - # as before all PEP 440 versions. - epoch = -1 - - # This scheme is taken from pkg_resources.parse_version setuptools prior to - # it's adoption of the packaging library. - parts = [] - for part in _parse_version_parts(version.lower()): - if part.startswith("*"): - # remove "-" before a prerelease tag - if part < "*final": - while parts and parts[-1] == "*final-": - parts.pop() - - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == "00000000": - parts.pop() - - parts.append(part) - parts = tuple(parts) - - return epoch, parts diff --git a/poetry/core/version/markers.py b/poetry/core/version/markers.py index af7d86e46..7a05e42e5 100644 --- a/poetry/core/version/markers.py +++ b/poetry/core/version/markers.py @@ -7,7 +7,7 @@ from typing import List from typing import Union -from .grammars.parser import Parser +from .grammars.parser import PARSER_PEP_508_MARKERS if TYPE_CHECKING: @@ -49,7 +49,7 @@ class UndefinedEnvironmentName(ValueError): } -_parser = Parser("markers") +_parser = PARSER_PEP_508_MARKERS class BaseMarker(object): diff --git a/poetry/core/version/pep440/__init__.py b/poetry/core/version/pep440/__init__.py new file mode 100644 index 000000000..dff35f255 --- /dev/null +++ b/poetry/core/version/pep440/__init__.py @@ -0,0 +1,4 @@ +from poetry.core.version.pep440.segments import LocalSegmentType +from poetry.core.version.pep440.segments import Release +from poetry.core.version.pep440.segments import ReleaseTag +from poetry.core.version.pep440.version import PEP440Version diff --git a/poetry/core/version/pep440/parser.py b/poetry/core/version/pep440/parser.py new file mode 100644 index 000000000..ea9eee308 --- /dev/null +++ b/poetry/core/version/pep440/parser.py @@ -0,0 +1,90 @@ +from typing import TYPE_CHECKING +from typing import List +from typing import Optional +from typing import Type + +from lark import LarkError +from lark import Transformer + +from poetry.core.version.exceptions import InvalidVersion +from poetry.core.version.grammars.parser import PARSER_PEP_440 +from poetry.core.version.pep440 import Release +from poetry.core.version.pep440 import ReleaseTag + + +if TYPE_CHECKING: + from poetry.core.version.pep440.version import PEP440Version + + +class _Transformer(Transformer): + def NUMERIC_IDENTIFIER(self, data: "Token"): # noqa + return int(data.value) + + def LOCAL_IDENTIFIER(self, data: "Token"): # noqa + try: + return int(data.value) + except ValueError: + return data.value + + def POST_RELEASE_TAG(self, data: "Token"): # noqa + return data.value + + def PRE_RELEASE_TAG(self, data: "Token"): # noqa + return data.value + + def DEV_RELEASE_TAG(self, data: "Token"): # noqa + return data.value + + def LOCAL(self, data: "Token"): # noqa + return data.value + + def INT(self, data: "Token"): # noqa + return int(data.value) + + def version(self, children: List["Tree"]): # noqa + epoch, release, dev, pre, post, local = 0, None, None, None, None, None + + for child in children: + if child.data == "epoch": + # epoch is always a single numeric value + epoch = child.children[0] + elif child.data == "release": + # release segment is of the form N(.N)* + release = Release.from_parts(*child.children) + elif child.data == "pre_release": + # pre-release tag is of the form (a|b|rc)N + pre = ReleaseTag(*child.children) + elif child.data == "post_release": + # post-release tags are of the form N (shortened) or post(N)* + if len(child.children) == 1 and isinstance(child.children[0], int): + post = ReleaseTag("post", child.children[0]) + else: + post = ReleaseTag(*child.children) + elif child.data == "dev_release": + # dev-release tag is of the form dev(N)* + dev = ReleaseTag(*child.children) + elif child.data == "local": + local = tuple(child.children) + + return epoch, release, pre, post, dev, local + + def start(self, children: List["Tree"]): # noqa + return children[0] + + +_TRANSFORMER = _Transformer() + + +def parse_pep440( + value: str, version_class: Optional[Type["PEP440Version"]] = None +) -> "PEP440Version": + if version_class is None: + from poetry.core.version.pep440.version import PEP440Version + + version_class = PEP440Version + + try: + tree = PARSER_PEP_440.parse(text=value) + return version_class(*_TRANSFORMER.transform(tree), text=value) + except (TypeError, LarkError): + raise InvalidVersion(f"Invalid PEP 440 version: '{value}'") diff --git a/poetry/core/version/pep440/segments.py b/poetry/core/version/pep440/segments.py new file mode 100644 index 000000000..cbc7ca094 --- /dev/null +++ b/poetry/core/version/pep440/segments.py @@ -0,0 +1,151 @@ +import dataclasses + +from typing import Optional +from typing import Tuple +from typing import Union + + +RELEASE_PHASE_ALPHA = "alpha" +RELEASE_PHASE_BETA = "beta" +RELEASE_PHASE_RC = "rc" +RELEASE_PHASE_PREVIEW = "preview" +RELEASE_PHASE_POST = "post" +RELEASE_PHASE_REV = "rev" +RELEASE_PHASE_DEV = "dev" +RELEASE_PHASES = { + RELEASE_PHASE_ALPHA: "a", + RELEASE_PHASE_BETA: "b", + RELEASE_PHASE_RC: "c", + RELEASE_PHASE_PREVIEW: "pre", + RELEASE_PHASE_POST: "-", # shorthand of 1.2.3-post1 is 1.2.3-1 + RELEASE_PHASE_REV: "r", + RELEASE_PHASE_DEV: "dev", +} +RELEASE_PHASES_SHORT = {v: k for k, v in RELEASE_PHASES.items() if k != "post"} + + +@dataclasses.dataclass(frozen=True, eq=True, order=True) +class Release: + major: int = dataclasses.field(default=0, compare=False) + minor: Optional[int] = dataclasses.field(default=None, compare=False) + patch: Optional[int] = dataclasses.field(default=None, compare=False) + # some projects use non-semver versioning schemes, eg: 1.2.3.4 + extra: Optional[Union[int, Tuple[int, ...]]] = dataclasses.field( + default=None, compare=False + ) + precision: int = dataclasses.field(default=None, init=False, compare=False) + text: str = dataclasses.field(default=None, init=False, compare=False) + _compare_key: Tuple[int, ...] = dataclasses.field( + default=None, init=False, compare=True + ) + + def __post_init__(self): + if self.extra is None: + object.__setattr__(self, "extra", tuple()) + elif not isinstance(self.extra, tuple): + object.__setattr__(self, "extra", (self.extra,)) + + parts = list( + map( + str, + filter( + lambda x: x is not None, + [self.major, self.minor, self.patch, *self.extra], + ), + ) + ) + object.__setattr__(self, "text", ".".join(parts)) + object.__setattr__(self, "precision", len(parts)) + object.__setattr__( + self, + "_compare_key", + (self.major, self.minor or 0, self.patch or 0, *self.extra), + ) + + @classmethod + def from_parts(cls, *parts: int) -> "Release": + if not parts: + return cls() + + return cls( + major=parts[0], + minor=parts[1] if len(parts) > 1 else None, + patch=parts[2] if len(parts) > 2 else None, + extra=parts[3:] if len(parts) > 3 else tuple(), + ) + + def to_string(self) -> str: + return self.text + + def next_major(self) -> "Release": + return dataclasses.replace( + self, + major=self.major + 1, + minor=0 if self.minor is not None else None, + patch=0 if self.patch is not None else None, + extra=tuple(0 for _ in self.extra), + ) + + def next_minor(self) -> "Release": + return dataclasses.replace( + self, + major=self.major, + minor=self.minor + 1 if self.minor is not None else 1, + patch=0 if self.patch is not None else None, + extra=tuple(0 for _ in self.extra), + ) + + def next_patch(self) -> "Release": + return dataclasses.replace( + self, + major=self.major, + minor=self.minor if self.minor is not None else 0, + patch=self.patch + 1 if self.patch is not None else 1, + extra=tuple(0 for _ in self.extra), + ) + + +@dataclasses.dataclass(frozen=True, eq=True, order=True) +class ReleaseTag: + phase: str + number: int = dataclasses.field(default=0) + + def __post_init__(self): + object.__setattr__(self, "phase", self.expand(self.phase)) + + @classmethod + def shorten(cls, phase: str) -> str: + return RELEASE_PHASES.get(phase, phase) + + @classmethod + def expand(cls, phase: str) -> str: + return RELEASE_PHASES_SHORT.get(phase, phase) + + def to_string(self, short: bool = False) -> str: + if short: + return f"{self.shorten(self.phase)}{self.number}" + return f"{self.phase}.{self.number}" + + def next(self) -> "ReleaseTag": + return dataclasses.replace(self, phase=self.phase, number=self.number + 1) + + def next_phase(self) -> Optional["ReleaseTag"]: + if self.phase in [ + RELEASE_PHASE_POST, + RELEASE_PHASE_RC, + RELEASE_PHASE_REV, + RELEASE_PHASE_DEV, + ]: + return None + + if self.phase == RELEASE_PHASE_ALPHA: + _phase = RELEASE_PHASE_BETA + elif self.phase == RELEASE_PHASE_BETA: + _phase = RELEASE_PHASE_RC + else: + return None + + return self.__class__(phase=_phase, number=0) + + +LocalSegmentType = Optional[Union[str, int, Tuple[Union[str, int], ...]]] diff --git a/poetry/core/version/pep440/version.py b/poetry/core/version/pep440/version.py new file mode 100644 index 000000000..58c5fcf2f --- /dev/null +++ b/poetry/core/version/pep440/version.py @@ -0,0 +1,146 @@ +import dataclasses +import math + +from typing import Optional +from typing import Tuple +from typing import Union + +from poetry.core.version.pep440.segments import LocalSegmentType +from poetry.core.version.pep440.segments import Release +from poetry.core.version.pep440.segments import ReleaseTag + + +# we use the phase "z" to ensure we always sort this after other phases +_INF_TAG = ReleaseTag("z", math.inf) # noqa +# we use the phase "" to ensure we always sort this before other phases +_NEG_INF_TAG = ReleaseTag("", -math.inf) # noqa + + +@dataclasses.dataclass(frozen=True, eq=True, order=True) +class PEP440Version: + epoch: int = dataclasses.field(default=0, compare=False) + release: Release = dataclasses.field(default_factory=Release, compare=False) + pre: Optional[ReleaseTag] = dataclasses.field(default=None, compare=False) + post: Optional[ReleaseTag] = dataclasses.field(default=None, compare=False) + dev: Optional[ReleaseTag] = dataclasses.field(default=None, compare=False) + local: LocalSegmentType = dataclasses.field(default=None, compare=False) + text: str = dataclasses.field(default=None, compare=False) + _compare_key: Tuple[ + int, Release, ReleaseTag, ReleaseTag, ReleaseTag, Tuple[Union[int, str], ...] + ] = dataclasses.field(default=None, init=False, compare=True) + + def __post_init__(self): + if self.local is not None and not isinstance(self.local, tuple): + object.__setattr__(self, "local", (self.local,)) + if not self.text: + object.__setattr__(self, "text", self.to_string()) + object.__setattr__(self, "_compare_key", self._make_compare_key()) + + def _make_compare_key(self): + """ + This code is based on the implementation of packaging.version._cmpkey(..) + """ + # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0. + # We'll do this by abusing the pre segment, but we _only_ want to do this + # if there is not a pre or a post segment. If we have one of those then + # the normal sorting rules will handle this case correctly. + if self.pre is None and self.post is None and self.dev is not None: + _pre = _NEG_INF_TAG + # Versions without a pre-release (except as noted above) should sort after + # those with one. + elif self.pre is None: + _pre = _INF_TAG + else: + _pre = self.pre + + # Versions without a post segment should sort before those with one. + _post = _NEG_INF_TAG if self.post is None else self.post + + # Versions without a development segment should sort after those with one. + _dev = _INF_TAG if self.dev is None else self.dev + + if self.local is None: + # Versions without a local segment should sort before those with one. + _local = ((-math.inf, ""),) + else: + # Versions with a local segment need that segment parsed to implement + # the sorting rules in PEP440. + # - Alpha numeric segments sort before numeric segments + # - Alpha numeric segments sort lexicographically + # - Numeric segments sort numerically + # - Shorter versions sort before longer versions when the prefixes + # match exactly + _local = tuple( + (i, "") if isinstance(i, int) else (-math.inf, i) for i in self.local + ) + return self.epoch, self.release, _pre, _post, _dev, _local + + @property + def major(self) -> int: + return self.release.major + + @property + def minor(self) -> Optional[int]: + return self.release.minor + + @property + def patch(self) -> Optional[int]: + return self.release.patch + + @property + def non_semver_parts(self) -> Optional[Tuple[int]]: + return self.release.extra + + def to_string(self, short=False): + dash = "-" if not short else "" + + version_string = dash.join( + filter( + bool, + [ + self.release.to_string(), + self.pre.to_string(short) if self.pre else self.pre, + self.post.to_string(short) if self.post else self.post, + self.dev.to_string(short) if self.dev else self.dev, + ], + ) + ) + + if self.epoch: + # if epoch is non-zero we should include it + version_string = "{}!{}".format(self.epoch, version_string) + + if self.local: + version_string += "+{}".format(".".join(map(str, self.local))) + + return version_string + + @classmethod + def parse(cls, value: str) -> "PEP440Version": + from poetry.core.version.pep440.parser import parse_pep440 + + return parse_pep440(value, cls) + + def is_pre_release(self) -> bool: + return self.pre is not None + + def is_post_release(self) -> bool: + return self.post is not None + + def is_developmental_release(self) -> bool: + return self.dev is not None + + def is_no_suffix_release(self) -> bool: + return not (self.pre or self.post or self.dev) + + def is_stable_release(self) -> bool: + return not (self.is_pre_release() or self.is_developmental_release()) + + def next_major(self) -> "PEP440Version": + return self.__class__(release=self.release.next_major()) + + def next_minor(self) -> "PEP440Version": + return self.__class__(release=self.release.next_minor()) + + def next_patch(self) -> "PEP440Version": + return self.__class__(release=self.release.next_patch()) diff --git a/poetry/core/version/requirements.py b/poetry/core/version/requirements.py index 1016bf612..2d6d1ce96 100644 --- a/poetry/core/version/requirements.py +++ b/poetry/core/version/requirements.py @@ -3,7 +3,7 @@ from poetry.core.semver.exceptions import ParseConstraintError from poetry.core.semver.helpers import parse_constraint -from .grammars.parser import Parser +from .grammars.parser import PARSER_PEP_508_CONSTRAINTS from .markers import _compact_markers @@ -13,7 +13,7 @@ class InvalidRequirement(ValueError): """ -_parser = Parser("pep508") +_parser = PARSER_PEP_508_CONSTRAINTS class Requirement(object): diff --git a/poetry/core/version/utils.py b/poetry/core/version/utils.py deleted file mode 100644 index 761fe3d36..000000000 --- a/poetry/core/version/utils.py +++ /dev/null @@ -1,65 +0,0 @@ -from typing import Any - - -class Infinity(object): - def __repr__(self) -> str: - return "Infinity" - - def __hash__(self) -> int: - return hash(repr(self)) - - def __lt__(self, other: Any) -> bool: - return False - - def __le__(self, other: Any) -> bool: - return False - - def __eq__(self, other: Any) -> bool: - return isinstance(other, self.__class__) - - def __ne__(self, other: Any) -> bool: - return not isinstance(other, self.__class__) - - def __gt__(self, other: Any) -> bool: - return True - - def __ge__(self, other: Any) -> bool: - return True - - def __neg__(self) -> "NegativeInfinity": - return NegativeInfinity - - -Infinity = Infinity() # type: ignore - - -class NegativeInfinity(object): - def __repr__(self) -> str: - return "-Infinity" - - def __hash__(self) -> int: - return hash(repr(self)) - - def __lt__(self, other: Any) -> bool: - return True - - def __le__(self, other: Any) -> bool: - return True - - def __eq__(self, other: Any) -> bool: - return isinstance(other, self.__class__) - - def __ne__(self, other: Any) -> bool: - return not isinstance(other, self.__class__) - - def __gt__(self, other: Any) -> bool: - return False - - def __ge__(self, other: Any) -> bool: - return False - - def __neg__(self) -> Infinity: - return Infinity - - -NegativeInfinity = NegativeInfinity() # type: ignore diff --git a/poetry/core/version/version.py b/poetry/core/version/version.py deleted file mode 100644 index bbe38a406..000000000 --- a/poetry/core/version/version.py +++ /dev/null @@ -1,258 +0,0 @@ -import re - -from collections import namedtuple -from itertools import dropwhile -from typing import Any -from typing import Optional -from typing import Tuple -from typing import Type -from typing import Union - -from .base import BaseVersion -from .exceptions import InvalidVersion -from .utils import Infinity -from .utils import NegativeInfinity - - -_Version = namedtuple("_Version", ["epoch", "release", "dev", "pre", "post", "local"]) - - -VERSION_PATTERN = re.compile( - r""" - ^ - v? - (?: - (?:(?P[0-9]+)!)? # epoch - (?P[0-9]+(?:\.[0-9]+)*) # release segment - (?P
                                          # pre-release
-            [-_.]?
-            (?P(a|b|c|rc|alpha|beta|pre|preview))
-            [-_.]?
-            (?P[0-9]+)?
-        )?
-        (?P                                         # post release
-            (?:-(?P[0-9]+))
-            |
-            (?:
-                [-_.]?
-                (?Ppost|rev|r)
-                [-_.]?
-                (?P[0-9]+)?
-            )
-        )?
-        (?P                                          # dev release
-            [-_.]?
-            (?Pdev)
-            [-_.]?
-            (?P[0-9]+)?
-        )?
-    )
-    (?:\+(?P[a-z0-9]+(?:[-_.][a-z0-9]+)*))?       # local version
-    $
-""",
-    re.IGNORECASE | re.VERBOSE,
-)
-
-
-class Version(BaseVersion):
-    def __init__(self, version: str) -> None:
-        # Validate the version and parse it into pieces
-        match = VERSION_PATTERN.match(version)
-        if not match:
-            raise InvalidVersion("Invalid version: '{0}'".format(version))
-
-        # Store the parsed out pieces of the version
-        self._version = _Version(
-            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
-            release=tuple(int(i) for i in match.group("release").split(".")),
-            pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
-            post=_parse_letter_version(
-                match.group("post_l"), match.group("post_n1") or match.group("post_n2")
-            ),
-            dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
-            local=_parse_local_version(match.group("local")),
-        )
-
-        # Generate a key which will be used for sorting
-        self._key = _cmpkey(
-            self._version.epoch,
-            self._version.release,
-            self._version.pre,
-            self._version.post,
-            self._version.dev,
-            self._version.local,
-        )
-
-    def __repr__(self) -> str:
-        return "".format(repr(str(self)))
-
-    def __str__(self) -> str:
-        parts = []
-
-        # Epoch
-        if self._version.epoch != 0:
-            parts.append("{0}!".format(self._version.epoch))
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self._version.release))
-
-        # Pre-release
-        if self._version.pre is not None:
-            parts.append("".join(str(x) for x in self._version.pre))
-
-        # Post-release
-        if self._version.post is not None:
-            parts.append(".post{0}".format(self._version.post[1]))
-
-        # Development release
-        if self._version.dev is not None:
-            parts.append(".dev{0}".format(self._version.dev[1]))
-
-        # Local version segment
-        if self._version.local is not None:
-            parts.append("+{0}".format(".".join(str(x) for x in self._version.local)))
-
-        return "".join(parts)
-
-    @property
-    def public(self) -> str:
-        return str(self).split("+", 1)[0]
-
-    @property
-    def base_version(self) -> str:
-        parts = []
-
-        # Epoch
-        if self._version.epoch != 0:
-            parts.append("{0}!".format(self._version.epoch))
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self._version.release))
-
-        return "".join(parts)
-
-    @property
-    def local(self) -> str:
-        version_string = str(self)
-        if "+" in version_string:
-            return version_string.split("+", 1)[1]
-
-    @property
-    def is_prerelease(self) -> bool:
-        return bool(self._version.dev or self._version.pre)
-
-    @property
-    def is_postrelease(self) -> bool:
-        return bool(self._version.post)
-
-
-def _parse_letter_version(letter: str, number: Optional[str]) -> Tuple[str, int]:
-    if letter:
-        # We consider there to be an implicit 0 in a pre-release if there is
-        # not a numeral associated with it.
-        if number is None:
-            number = 0
-
-        # We normalize any letters to their lower case form
-        letter = letter.lower()
-
-        # We consider some words to be alternate spellings of other words and
-        # in those cases we want to normalize the spellings to our preferred
-        # spelling.
-        if letter == "alpha":
-            letter = "a"
-        elif letter == "beta":
-            letter = "b"
-        elif letter in ["c", "pre", "preview"]:
-            letter = "rc"
-        elif letter in ["rev", "r"]:
-            letter = "post"
-
-        return letter, int(number)
-    if not letter and number:
-        # We assume if we are given a number, but we are not given a letter
-        # then this is using the implicit post release syntax (e.g. 1.0-1)
-        letter = "post"
-
-        return letter, int(number)
-
-
-_local_version_seperators = re.compile(r"[._-]")
-
-
-def _parse_local_version(local: Optional[str]) -> Tuple[Union[str, int], ...]:
-    """
-    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
-    """
-    if local is not None:
-        return tuple(
-            part.lower() if not part.isdigit() else int(part)
-            for part in _local_version_seperators.split(local)
-        )
-
-
-def _cmpkey(
-    epoch: int,
-    release: Optional[Tuple[int, ...]],
-    pre: Optional[Tuple[str, int]],
-    post: Optional[Tuple[str, int]],
-    dev: Optional[Tuple[str, int]],
-    local: Optional[Tuple[Union[str, int], ...]],
-) -> Tuple[
-    int,
-    Tuple[int, ...],
-    Union[Infinity.__class__, NegativeInfinity.__class__, Tuple[str, int], Any],
-    Union[NegativeInfinity.__class__, Tuple[str, int]],
-    Union[Infinity.__class__, Tuple[str, int], Any],
-    Union[
-        NegativeInfinity.__class__,
-        Tuple[
-            Union[
-                Tuple[int, str],
-                Tuple[Type[NegativeInfinity.__class__], Union[str, int]],
-            ],
-            ...,
-        ],
-    ],
-]:
-    # When we compare a release version, we want to compare it with all of the
-    # trailing zeros removed. So we'll use a reverse the list, drop all the now
-    # leading zeros until we come to something non zero, then take the rest
-    # re-reverse it back into the correct order and make it a tuple and use
-    # that for our sorting key.
-    release = tuple(reversed(list(dropwhile(lambda x: x == 0, reversed(release)))))
-
-    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
-    # We'll do this by abusing the pre segment, but we _only_ want to do this
-    # if there is not a pre or a post segment. If we have one of those then
-    # the normal sorting rules will handle this case correctly.
-    if pre is None and post is None and dev is not None:
-        pre = -Infinity
-
-    # Versions without a pre-release (except as noted above) should sort after
-    # those with one.
-    elif pre is None:
-        pre = Infinity
-
-    # Versions without a post segment should sort before those with one.
-    if post is None:
-        post = -Infinity
-
-    # Versions without a development segment should sort after those with one.
-    if dev is None:
-        dev = Infinity
-
-    if local is None:
-        # Versions without a local segment should sort before those with one.
-        local = -Infinity
-    else:
-        # Versions with a local segment need that segment parsed to implement
-        # the sorting rules in PEP440.
-        # - Alpha numeric segments sort before numeric segments
-        # - Alpha numeric segments sort lexicographically
-        # - Numeric segments sort numerically
-        # - Shorter versions sort before longer versions when the prefixes
-        #   match exactly
-        local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local)
-
-    return epoch, release, pre, post, dev, local
diff --git a/pyproject.toml b/pyproject.toml
index 020ad8bed..1e076896e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -34,6 +34,7 @@ python = "^3.6"
 
 # required for compatibility
 importlib-metadata = {version = "^1.7.0", python = ">=3.5, <3.8"}
+dataclasses = {version = "^0.8", python = "~3.6"}
 
 [tool.poetry.dev-dependencies]
 pre-commit = {version = "^2.10.0", python = "^3.6.1"}
diff --git a/tests/semver/test_helpers.py b/tests/semver/test_helpers.py
index a9f193b1a..03a285735 100644
--- a/tests/semver/test_helpers.py
+++ b/tests/semver/test_helpers.py
@@ -4,6 +4,7 @@
 from poetry.core.semver.version import Version
 from poetry.core.semver.version_range import VersionRange
 from poetry.core.semver.version_union import VersionUnion
+from poetry.core.version.pep440 import ReleaseTag
 
 
 @pytest.mark.parametrize(
@@ -15,16 +16,19 @@
         ("*.x.*", VersionRange()),
         ("x.X.x.*", VersionRange()),
         # ('!=1.0.0', Constraint('!=', '1.0.0.0')),
-        (">1.0.0", VersionRange(min=Version(1, 0, 0))),
-        ("<1.2.3", VersionRange(max=Version(1, 2, 3))),
-        ("<=1.2.3", VersionRange(max=Version(1, 2, 3), include_max=True)),
-        (">=1.2.3", VersionRange(min=Version(1, 2, 3), include_min=True)),
-        ("=1.2.3", Version(1, 2, 3)),
-        ("1.2.3", Version(1, 2, 3)),
-        ("=1.0", Version(1, 0, 0)),
-        ("1.2.3b5", Version(1, 2, 3, pre="b5")),
-        (">= 1.2.3", VersionRange(min=Version(1, 2, 3), include_min=True)),
-        (">dev", VersionRange(min=Version(0, 0, pre="dev"))),  # Issue 206
+        (">1.0.0", VersionRange(min=Version.from_parts(1, 0, 0))),
+        ("<1.2.3", VersionRange(max=Version.from_parts(1, 2, 3))),
+        ("<=1.2.3", VersionRange(max=Version.from_parts(1, 2, 3), include_max=True)),
+        (">=1.2.3", VersionRange(min=Version.from_parts(1, 2, 3), include_min=True)),
+        ("=1.2.3", Version.from_parts(1, 2, 3)),
+        ("1.2.3", Version.from_parts(1, 2, 3)),
+        ("=1.0", Version.from_parts(1, 0, 0)),
+        ("1.2.3b5", Version.from_parts(1, 2, 3, pre=ReleaseTag("beta", 5))),
+        (">= 1.2.3", VersionRange(min=Version.from_parts(1, 2, 3), include_min=True)),
+        (
+            ">dev",
+            VersionRange(min=Version.from_parts(0, 0, dev=ReleaseTag("dev"))),
+        ),  # Issue 206
     ],
 )
 def test_parse_constraint(input, constraint):
@@ -34,17 +38,57 @@ def test_parse_constraint(input, constraint):
 @pytest.mark.parametrize(
     "input,constraint",
     [
-        ("v2.*", VersionRange(Version(2, 0, 0), Version(3, 0, 0), True)),
-        ("2.*.*", VersionRange(Version(2, 0, 0), Version(3, 0, 0), True)),
-        ("20.*", VersionRange(Version(20, 0, 0), Version(21, 0, 0), True)),
-        ("20.*.*", VersionRange(Version(20, 0, 0), Version(21, 0, 0), True)),
-        ("2.0.*", VersionRange(Version(2, 0, 0), Version(2, 1, 0), True)),
-        ("2.x", VersionRange(Version(2, 0, 0), Version(3, 0, 0), True)),
-        ("2.x.x", VersionRange(Version(2, 0, 0), Version(3, 0, 0), True)),
-        ("2.2.X", VersionRange(Version(2, 2, 0), Version(2, 3, 0), True)),
-        ("0.*", VersionRange(max=Version(1, 0, 0))),
-        ("0.*.*", VersionRange(max=Version(1, 0, 0))),
-        ("0.x", VersionRange(max=Version(1, 0, 0))),
+        (
+            "v2.*",
+            VersionRange(
+                Version.from_parts(2, 0, 0), Version.from_parts(3, 0, 0), True
+            ),
+        ),
+        (
+            "2.*.*",
+            VersionRange(
+                Version.from_parts(2, 0, 0), Version.from_parts(3, 0, 0), True
+            ),
+        ),
+        (
+            "20.*",
+            VersionRange(
+                Version.from_parts(20, 0, 0), Version.from_parts(21, 0, 0), True
+            ),
+        ),
+        (
+            "20.*.*",
+            VersionRange(
+                Version.from_parts(20, 0, 0), Version.from_parts(21, 0, 0), True
+            ),
+        ),
+        (
+            "2.0.*",
+            VersionRange(
+                Version.from_parts(2, 0, 0), Version.from_parts(2, 1, 0), True
+            ),
+        ),
+        (
+            "2.x",
+            VersionRange(
+                Version.from_parts(2, 0, 0), Version.from_parts(3, 0, 0), True
+            ),
+        ),
+        (
+            "2.x.x",
+            VersionRange(
+                Version.from_parts(2, 0, 0), Version.from_parts(3, 0, 0), True
+            ),
+        ),
+        (
+            "2.2.X",
+            VersionRange(
+                Version.from_parts(2, 2, 0), Version.from_parts(2, 3, 0), True
+            ),
+        ),
+        ("0.*", VersionRange(max=Version.from_parts(1, 0, 0))),
+        ("0.*.*", VersionRange(max=Version.from_parts(1, 0, 0))),
+        ("0.x", VersionRange(max=Version.from_parts(1, 0, 0))),
     ],
 )
 def test_parse_constraint_wildcard(input, constraint):
@@ -54,23 +98,83 @@ def test_parse_constraint_wildcard(input, constraint):
 @pytest.mark.parametrize(
     "input,constraint",
     [
-        ("~v1", VersionRange(Version(1, 0, 0), Version(2, 0, 0), True)),
-        ("~1.0", VersionRange(Version(1, 0, 0), Version(1, 1, 0), True)),
-        ("~1.0.0", VersionRange(Version(1, 0, 0), Version(1, 1, 0), True)),
-        ("~1.2", VersionRange(Version(1, 2, 0), Version(1, 3, 0), True)),
-        ("~1.2.3", VersionRange(Version(1, 2, 3), Version(1, 3, 0), True)),
+        (
+            "~v1",
+            VersionRange(
+                Version.from_parts(1, 0, 0), Version.from_parts(2, 0, 0), True
+            ),
+        ),
+        (
+            "~1.0",
+            VersionRange(
+                Version.from_parts(1, 0, 0), Version.from_parts(1, 1, 0), True
+            ),
+        ),
+        (
+            "~1.0.0",
+            VersionRange(
+                Version.from_parts(1, 0, 0), Version.from_parts(1, 1, 0), True
+            ),
+        ),
+        (
+            "~1.2",
+            VersionRange(
+                Version.from_parts(1, 2, 0), Version.from_parts(1, 3, 0), True
+            ),
+        ),
+        (
+            "~1.2.3",
+            VersionRange(
+                Version.from_parts(1, 2, 3), Version.from_parts(1, 3, 0), True
+            ),
+        ),
         (
             "~1.2-beta",
-            VersionRange(Version(1, 2, 0, pre="beta"), Version(1, 3, 0), True),
+            VersionRange(
+                Version.from_parts(1, 2, 0, pre=ReleaseTag("beta")),
+                Version.from_parts(1, 3, 0),
+                True,
+            ),
         ),
-        ("~1.2-b2", VersionRange(Version(1, 2, 0, pre="b2"), Version(1, 3, 0), True)),
-        ("~0.3", VersionRange(Version(0, 3, 0), Version(0, 4, 0), True)),
-        ("~3.5", VersionRange(Version(3, 5, 0), Version(3, 6, 0), True)),
-        ("~=3.5", VersionRange(Version(3, 5, 0), Version(4, 0, 0), True)),  # PEP 440
-        ("~=3.5.3", VersionRange(Version(3, 5, 3), Version(3, 6, 0), True)),  # PEP 440
+        (
+            "~1.2-b2",
+            VersionRange(
+                Version.from_parts(1, 2, 0, pre=ReleaseTag("beta", 2)),
+                Version.from_parts(1, 3, 0),
+                True,
+            ),
+        ),
+        (
+            "~0.3",
+            VersionRange(
+                Version.from_parts(0, 3, 0), Version.from_parts(0, 4, 0), True
+            ),
+        ),
+        (
+            "~3.5",
+            VersionRange(
+                Version.from_parts(3, 5, 0), Version.from_parts(3, 6, 0), True
+            ),
+        ),
+        (
+            "~=3.5",
+            VersionRange(
+                Version.from_parts(3, 5, 0), Version.from_parts(4, 0, 0), True
+            ),
+        ),  # PEP 440
+        (
+            "~=3.5.3",
+            VersionRange(
+                Version.from_parts(3, 5, 3), Version.from_parts(3, 6, 0), True
+            ),
+        ),  # PEP 440
         (
             "~=3.5.3rc1",
-            VersionRange(Version(3, 5, 3, pre="rc1"), Version(3, 6, 0), True),
+            VersionRange(
+                Version.from_parts(3, 5, 3, pre=ReleaseTag("rc", 1)),
+                Version.from_parts(3, 6, 0),
+                True,
+            ),
         ),  # PEP 440
     ],
 )
@@ -81,19 +185,63 @@ def test_parse_constraint_tilde(input, constraint):
 @pytest.mark.parametrize(
     "input,constraint",
     [
-        ("^v1", VersionRange(Version(1, 0, 0), Version(2, 0, 0), True)),
-        ("^0", VersionRange(Version(0, 0, 0), Version(1, 0, 0), True)),
-        ("^0.0", VersionRange(Version(0, 0, 0), Version(0, 1, 0), True)),
-        ("^1.2", VersionRange(Version(1, 2, 0), Version(2, 0, 0), True)),
+        (
+            "^v1",
+            VersionRange(
+                Version.from_parts(1, 0, 0), Version.from_parts(2, 0, 0), True
+            ),
+        ),
+        ("^0", VersionRange(Version.from_parts(0), Version.from_parts(0, 1), True)),
+        (
+            "^0.0",
+            VersionRange(
+                Version.from_parts(0, 0, 0), Version.from_parts(0, 1, 0), True
+            ),
+        ),
+        (
+            "^1.2",
+            VersionRange(
+                Version.from_parts(1, 2, 0), Version.from_parts(2, 0, 0), True
+            ),
+        ),
         (
             "^1.2.3-beta.2",
-            VersionRange(Version(1, 2, 3, pre="beta.2"), Version(2, 0, 0), True),
+            VersionRange(
+                Version.from_parts(1, 2, 3, pre=ReleaseTag("beta", 2)),
+                Version.from_parts(2, 0, 0),
+                True,
+            ),
+        ),
+        (
+            "^1.2.3",
+            VersionRange(
+                Version.from_parts(1, 2, 3), Version.from_parts(2, 0, 0), True
+            ),
+        ),
+        (
+            "^0.2.3",
+            VersionRange(
+                Version.from_parts(0, 2, 3), Version.from_parts(0, 3, 0), True
+            ),
+        ),
+        (
+            "^0.2",
+            VersionRange(
+                Version.from_parts(0, 2, 0), Version.from_parts(0, 3, 0), True
+            ),
+        ),
+        (
+            "^0.2.0",
+            VersionRange(
+                Version.from_parts(0, 2, 0), Version.from_parts(0, 3, 0), True
+            ),
+        ),
+        (
+            "^0.0.3",
+            VersionRange(
+                Version.from_parts(0, 0, 3), Version.from_parts(0, 0, 4), True
+            ),
         ),
-        ("^1.2.3", VersionRange(Version(1, 2, 3), Version(2, 0, 0), True)),
-        ("^0.2.3", VersionRange(Version(0, 2, 3), Version(0, 3, 0), True)),
-        ("^0.2", VersionRange(Version(0, 2, 0), Version(0, 3, 0), True)),
-        ("^0.2.0", VersionRange(Version(0, 2, 0), Version(0, 3, 0), True)),
-        ("^0.0.3", VersionRange(Version(0, 0, 3), Version(0, 0, 4), True)),
     ],
 )
 def test_parse_constraint_caret(input, constraint):
@@ -117,7 +265,10 @@ def test_parse_constraint_caret(input, constraint):
 )
 def test_parse_constraint_multi(input):
     assert parse_constraint(input) == VersionRange(
-        Version(2, 0, 0), Version(3, 0, 0), include_min=False, include_max=True
+        Version.from_parts(2, 0, 0),
+        Version.from_parts(3, 0, 0),
+        include_min=False,
+        include_max=True,
     )
 
 
@@ -127,8 +278,10 @@ def test_parse_constraint_multi(input):
 )
 def test_parse_constraint_multi_wilcard(input):
     assert parse_constraint(input) == VersionUnion(
-        VersionRange(Version(2, 7, 0), Version(3, 0, 0), True, False),
-        VersionRange(Version(3, 2, 0), None, True, False),
+        VersionRange(
+            Version.from_parts(2, 7, 0), Version.from_parts(3, 0, 0), True, False
+        ),
+        VersionRange(Version.from_parts(3, 2, 0), None, True, False),
     )
 
 
diff --git a/tests/semver/test_parse_constraint.py b/tests/semver/test_parse_constraint.py
index 97e80dd1d..a5db226af 100644
--- a/tests/semver/test_parse_constraint.py
+++ b/tests/semver/test_parse_constraint.py
@@ -9,79 +9,166 @@
 @pytest.mark.parametrize(
     "constraint,version",
     [
-        ("~=3.8", VersionRange(min=Version(3, 8), max=Version(4, 0), include_min=True)),
+        (
+            "~=3.8",
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(4, 0),
+                include_min=True,
+            ),
+        ),
         (
             "== 3.8.*",
-            VersionRange(min=Version(3, 8), max=Version(3, 9, 0), include_min=True),
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(3, 9, 0),
+                include_min=True,
+            ),
         ),
         (
             "~= 3.8",
-            VersionRange(min=Version(3, 8), max=Version(4, 0), include_min=True),
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(4, 0),
+                include_min=True,
+            ),
+        ),
+        (
+            "~3.8",
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(3, 9),
+                include_min=True,
+            ),
+        ),
+        (
+            "~ 3.8",
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(3, 9),
+                include_min=True,
+            ),
         ),
-        ("~3.8", VersionRange(min=Version(3, 8), max=Version(3, 9), include_min=True)),
-        ("~ 3.8", VersionRange(min=Version(3, 8), max=Version(3, 9), include_min=True)),
-        (">3.8", VersionRange(min=Version(3, 8))),
-        (">=3.8", VersionRange(min=Version(3, 8), include_min=True)),
-        (">= 3.8", VersionRange(min=Version(3, 8), include_min=True)),
+        (">3.8", VersionRange(min=Version.from_parts(3, 8))),
+        (">=3.8", VersionRange(min=Version.from_parts(3, 8), include_min=True)),
+        (">= 3.8", VersionRange(min=Version.from_parts(3, 8), include_min=True)),
         (
             ">3.8,<=6.5",
-            VersionRange(min=Version(3, 8), max=Version(6, 5), include_max=True),
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(6, 5),
+                include_max=True,
+            ),
         ),
         (
             ">3.8,<= 6.5",
-            VersionRange(min=Version(3, 8), max=Version(6, 5), include_max=True),
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(6, 5),
+                include_max=True,
+            ),
         ),
         (
             "> 3.8,<= 6.5",
-            VersionRange(min=Version(3, 8), max=Version(6, 5), include_max=True),
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(6, 5),
+                include_max=True,
+            ),
         ),
         (
             "> 3.8,<=6.5",
-            VersionRange(min=Version(3, 8), max=Version(6, 5), include_max=True),
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(6, 5),
+                include_max=True,
+            ),
         ),
         (
             ">3.8 ,<=6.5",
-            VersionRange(min=Version(3, 8), max=Version(6, 5), include_max=True),
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(6, 5),
+                include_max=True,
+            ),
         ),
         (
             ">3.8, <=6.5",
-            VersionRange(min=Version(3, 8), max=Version(6, 5), include_max=True),
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(6, 5),
+                include_max=True,
+            ),
         ),
         (
             ">3.8 , <=6.5",
-            VersionRange(min=Version(3, 8), max=Version(6, 5), include_max=True),
+            VersionRange(
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(6, 5),
+                include_max=True,
+            ),
         ),
         (
             "==3.8",
             VersionRange(
-                min=Version(3, 8), max=Version(3, 8), include_min=True, include_max=True
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(3, 8),
+                include_min=True,
+                include_max=True,
             ),
         ),
         (
             "== 3.8",
             VersionRange(
-                min=Version(3, 8), max=Version(3, 8), include_min=True, include_max=True
+                min=Version.from_parts(3, 8),
+                max=Version.from_parts(3, 8),
+                include_min=True,
+                include_max=True,
             ),
         ),
         (
             "~2.7 || ~3.8",
             VersionUnion(
-                VersionRange(min=Version(2, 7), max=Version(2, 8), include_min=True),
-                VersionRange(min=Version(3, 8), max=Version(3, 9), include_min=True),
+                VersionRange(
+                    min=Version.from_parts(2, 7),
+                    max=Version.from_parts(2, 8),
+                    include_min=True,
+                ),
+                VersionRange(
+                    min=Version.from_parts(3, 8),
+                    max=Version.from_parts(3, 9),
+                    include_min=True,
+                ),
             ),
         ),
         (
             "~2.7||~3.8",
             VersionUnion(
-                VersionRange(min=Version(2, 7), max=Version(2, 8), include_min=True),
-                VersionRange(min=Version(3, 8), max=Version(3, 9), include_min=True),
+                VersionRange(
+                    min=Version.from_parts(2, 7),
+                    max=Version.from_parts(2, 8),
+                    include_min=True,
+                ),
+                VersionRange(
+                    min=Version.from_parts(3, 8),
+                    max=Version.from_parts(3, 9),
+                    include_min=True,
+                ),
             ),
         ),
         (
             "~ 2.7||~ 3.8",
             VersionUnion(
-                VersionRange(min=Version(2, 7), max=Version(2, 8), include_min=True),
-                VersionRange(min=Version(3, 8), max=Version(3, 9), include_min=True),
+                VersionRange(
+                    min=Version.from_parts(2, 7),
+                    max=Version.from_parts(2, 8),
+                    include_min=True,
+                ),
+                VersionRange(
+                    min=Version.from_parts(3, 8),
+                    max=Version.from_parts(3, 9),
+                    include_min=True,
+                ),
             ),
         ),
     ],
diff --git a/tests/semver/test_version.py b/tests/semver/test_version.py
index 69bb6a6c1..3e357fc78 100644
--- a/tests/semver/test_version.py
+++ b/tests/semver/test_version.py
@@ -1,41 +1,42 @@
 import pytest
 
 from poetry.core.semver.empty_constraint import EmptyConstraint
-from poetry.core.semver.exceptions import ParseVersionError
 from poetry.core.semver.version import Version
 from poetry.core.semver.version_range import VersionRange
+from poetry.core.version.exceptions import InvalidVersion
+from poetry.core.version.pep440 import ReleaseTag
 
 
 @pytest.mark.parametrize(
-    "input,version",
+    "text,version",
     [
-        ("1.0.0", Version(1, 0, 0)),
-        ("1", Version(1, 0, 0)),
-        ("1.0", Version(1, 0, 0)),
-        ("1b1", Version(1, 0, 0, pre="beta1")),
-        ("1.0b1", Version(1, 0, 0, pre="beta1")),
-        ("1.0.0b1", Version(1, 0, 0, pre="beta1")),
-        ("1.0.0-b1", Version(1, 0, 0, pre="beta1")),
-        ("1.0.0-beta.1", Version(1, 0, 0, pre="beta1")),
-        ("1.0.0+1", Version(1, 0, 0, build="1")),
-        ("1.0.0-1", Version(1, 0, 0, build="1")),
-        ("1.0.0.0", Version(1, 0, 0)),
-        ("1.0.0-post", Version(1, 0, 0)),
-        ("1.0.0-post1", Version(1, 0, 0, build="1")),
-        ("0.6c", Version(0, 6, 0, pre="rc0")),
-        ("0.6pre", Version(0, 6, 0, pre="rc0")),
+        ("1.0.0", Version.from_parts(1, 0, 0)),
+        ("1", Version.from_parts(1, 0, 0)),
+        ("1.0", Version.from_parts(1, 0, 0)),
+        ("1b1", Version.from_parts(1, 0, 0, pre=ReleaseTag("beta", 1))),
+        ("1.0b1", Version.from_parts(1, 0, 0, pre=ReleaseTag("beta", 1))),
+        ("1.0.0b1", Version.from_parts(1, 0, 0, pre=ReleaseTag("beta", 1))),
+        ("1.0.0-b1", Version.from_parts(1, 0, 0, pre=ReleaseTag("beta", 1))),
+        ("1.0.0-beta.1", Version.from_parts(1, 0, 0, pre=ReleaseTag("beta", 1))),
+        ("1.0.0+1", Version.from_parts(1, 0, 0, local=1)),
+        ("1.0.0-1", Version.from_parts(1, 0, 0, post=ReleaseTag("post", 1))),
+        ("1.0.0.0", Version.from_parts(1, 0, 0, extra=0)),
+        ("1.0.0-post", Version.from_parts(1, 0, 0, post=ReleaseTag("post"))),
+        ("1.0.0-post1", Version.from_parts(1, 0, 0, post=ReleaseTag("post", 1))),
+        ("0.6c", Version.from_parts(0, 6, 0, pre=ReleaseTag("rc", 0))),
+        ("0.6pre", Version.from_parts(0, 6, 0, pre=ReleaseTag("preview", 0))),
     ],
 )
-def test_parse_valid(input, version):
-    parsed = Version.parse(input)
+def test_parse_valid(text, version):
+    parsed = Version.parse(text)
 
     assert parsed == version
-    assert parsed.text == input
+    assert parsed.text == text
 
 
 @pytest.mark.parametrize("input", [(None, "example")])
 def test_parse_invalid(input):
-    with pytest.raises(ParseVersionError):
+    with pytest.raises(InvalidVersion):
         Version.parse(input)
 
 
diff --git a/tests/version/test_requirements.py b/tests/version/test_requirements.py
index 375294641..0a8a9bd87 100644
--- a/tests/version/test_requirements.py
+++ b/tests/version/test_requirements.py
@@ -28,12 +28,12 @@ def assert_requirement(req, name, url=None, extras=None, constraint="*", marker=
         ("name", {"name": "name"}),
         ("foo-bar.quux_baz", {"name": "foo-bar.quux_baz"}),
         ("name>=3", {"name": "name", "constraint": ">=3"}),
-        ("name==1.0.org1", {"name": "name", "constraint": "==1.0.org1"}),
+        ("name==1.0.post1", {"name": "name", "constraint": "==1.0.post1"}),
         (
-            "name>=1.x.y;python_version=='2.6'",
+            "name>=1.2.3;python_version=='2.6'",
             {
                 "name": "name",
-                "constraint": ">=1.x.y",
+                "constraint": ">=1.2.3",
                 "marker": 'python_version == "2.6"',
             },
         ),

From dd1d7adcf940e979ec7c32e3ed6327ca911c39e6 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Tue, 23 Mar 2021 00:12:27 +0100
Subject: [PATCH 35/77] tests: add PEP-440 comparison examples

---
 tests/semver/test_version.py | 65 +++++++++++++++++++++++++-----------
 1 file changed, 45 insertions(+), 20 deletions(-)

diff --git a/tests/semver/test_version.py b/tests/semver/test_version.py
index 3e357fc78..ad909f33d 100644
--- a/tests/semver/test_version.py
+++ b/tests/semver/test_version.py
@@ -40,26 +40,51 @@ def test_parse_invalid(input):
         Version.parse(input)
 
 
-def test_comparison():
-    versions = [
-        "1.0.0-alpha",
-        "1.0.0-alpha.1",
-        "1.0.0-beta.2",
-        "1.0.0-beta.11",
-        "1.0.0-rc.1",
-        "1.0.0-rc.1+build.1",
-        "1.0.0",
-        "1.0.0+0.3.7",
-        "1.3.7+build",
-        "1.3.7+build.2.b8f12d7",
-        "1.3.7+build.11.e0f985a",
-        "2.0.0",
-        "2.1.0",
-        "2.2.0",
-        "2.11.0",
-        "2.11.1",
-    ]
-
+@pytest.mark.parametrize(
+    "versions",
+    [
+        [
+            "1.0.0-alpha",
+            "1.0.0-alpha.1",
+            "1.0.0-beta.2",
+            "1.0.0-beta.11",
+            "1.0.0-rc.1",
+            "1.0.0-rc.1+build.1",
+            "1.0.0",
+            "1.0.0+0.3.7",
+            "1.3.7+build",
+            "1.3.7+build.2.b8f12d7",
+            "1.3.7+build.11.e0f985a",
+            "2.0.0",
+            "2.1.0",
+            "2.2.0",
+            "2.11.0",
+            "2.11.1",
+        ],
+        # PEP 440 example comparisons
+        [
+            "1.0.dev456",
+            "1.0a1",
+            "1.0a2.dev456",
+            "1.0a12.dev456",
+            "1.0a12",
+            "1.0b1.dev456",
+            "1.0b2",
+            "1.0b2.post345.dev456",
+            "1.0b2.post345",
+            "1.0rc1.dev456",
+            "1.0rc1",
+            "1.0",
+            "1.0+abc.5",
+            "1.0+abc.7",
+            "1.0+5",
+            "1.0.post456.dev34",
+            "1.0.post456",
+            "1.1.dev1",
+        ],
+    ],
+)
+def test_comparison(versions):
     for i in range(len(versions)):
         for j in range(len(versions)):
             a = Version.parse(versions[i])

From 0cded291d2e3a4e5222f1dd35e84649bb23a3346 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Thu, 25 Mar 2021 02:07:39 +0100
Subject: [PATCH 36/77] pep440: add helper methods and fixes

---
 poetry/core/packages/dependency.py    |  2 +-
 poetry/core/packages/package.py       |  2 +-
 poetry/core/semver/exceptions.py      |  4 --
 poetry/core/semver/version.py         |  2 +-
 poetry/core/semver/version_range.py   | 10 ++--
 poetry/core/version/pep440/version.py | 67 +++++++++++++++++++++++----
 6 files changed, 63 insertions(+), 24 deletions(-)

diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py
index a0ebea0eb..bd95f08ba 100644
--- a/poetry/core/packages/dependency.py
+++ b/poetry/core/packages/dependency.py
@@ -62,7 +62,7 @@ def __init__(
 
         if isinstance(self._constraint, VersionRange) and self._constraint.min:
             allows_prereleases = (
-                allows_prereleases or self._constraint.min.is_pre_release()
+                allows_prereleases or self._constraint.min.is_unstable()
             )
 
         self._allows_prereleases = allows_prereleases
diff --git a/poetry/core/packages/package.py b/poetry/core/packages/package.py
index 69d1b9473..01aa8fbbd 100644
--- a/poetry/core/packages/package.py
+++ b/poetry/core/packages/package.py
@@ -305,7 +305,7 @@ def urls(self) -> Dict[str, str]:
         return urls
 
     def is_prerelease(self) -> bool:
-        return self._version.is_pre_release()
+        return self._version.is_unstable()
 
     def is_root(self) -> bool:
         return False
diff --git a/poetry/core/semver/exceptions.py b/poetry/core/semver/exceptions.py
index b24323997..cfdbe5b56 100644
--- a/poetry/core/semver/exceptions.py
+++ b/poetry/core/semver/exceptions.py
@@ -1,6 +1,2 @@
-class ParseVersionError(ValueError):
-    pass
-
-
 class ParseConstraintError(ValueError):
     pass
diff --git a/poetry/core/semver/version.py b/poetry/core/semver/version.py
index 5bdc2042c..6ed622fc9 100644
--- a/poetry/core/semver/version.py
+++ b/poetry/core/semver/version.py
@@ -31,7 +31,7 @@ def precision(self) -> int:
 
     @property
     def stable(self) -> "Version":
-        if self.is_stable_release():
+        if self.is_stable():
             return self
 
         return self.next_patch()
diff --git a/poetry/core/semver/version_range.py b/poetry/core/semver/version_range.py
index 00909e28a..6b77e58d1 100644
--- a/poetry/core/semver/version_range.py
+++ b/poetry/core/semver/version_range.py
@@ -27,13 +27,9 @@ def __init__(
             not always_include_max_prerelease
             and not include_max
             and full_max is not None
-            and not full_max.is_pre_release()
-            and not full_max.is_post_release()
-            and (
-                min is None
-                or not min.is_pre_release()
-                or min.release != full_max.release
-            )
+            and full_max.is_stable()
+            and not full_max.is_postrelease()
+            and (min is None or min.is_stable() or min.release != full_max.release)
         ):
             full_max = full_max.first_pre_release()
 
diff --git a/poetry/core/version/pep440/version.py b/poetry/core/version/pep440/version.py
index 58c5fcf2f..2ea495778 100644
--- a/poetry/core/version/pep440/version.py
+++ b/poetry/core/version/pep440/version.py
@@ -5,6 +5,9 @@
 from typing import Tuple
 from typing import Union
 
+from poetry.core.version.pep440.segments import RELEASE_PHASE_ALPHA
+from poetry.core.version.pep440.segments import RELEASE_PHASE_DEV
+from poetry.core.version.pep440.segments import RELEASE_PHASE_POST
 from poetry.core.version.pep440.segments import LocalSegmentType
 from poetry.core.version.pep440.segments import Release
 from poetry.core.version.pep440.segments import ReleaseTag
@@ -32,8 +35,12 @@ class PEP440Version:
     def __post_init__(self):
         if self.local is not None and not isinstance(self.local, tuple):
             object.__setattr__(self, "local", (self.local,))
-        if not self.text:
-            object.__setattr__(self, "text", self.to_string())
+
+        # we do this here to handle both None and tomlkit string values
+        object.__setattr__(
+            self, "text", self.to_string() if not self.text else str(self.text)
+        )
+
         object.__setattr__(self, "_compare_key", self._make_compare_key())
 
     def _make_compare_key(self):
@@ -121,26 +128,66 @@ def parse(cls, value: str) -> "PEP440Version":
 
         return parse_pep440(value, cls)
 
-    def is_pre_release(self) -> bool:
+    def is_prerelease(self) -> bool:
         return self.pre is not None
 
-    def is_post_release(self) -> bool:
+    def is_postrelease(self) -> bool:
         return self.post is not None
 
-    def is_developmental_release(self) -> bool:
+    def is_devrelease(self) -> bool:
         return self.dev is not None
 
     def is_no_suffix_release(self) -> bool:
         return not (self.pre or self.post or self.dev)
 
-    def is_stable_release(self) -> bool:
-        return not (self.is_pre_release() or self.is_developmental_release())
+    def is_unstable(self) -> bool:
+        return self.is_prerelease() or self.is_devrelease()
+
+    def is_stable(self) -> bool:
+        return not self.is_unstable()
 
     def next_major(self) -> "PEP440Version":
-        return self.__class__(release=self.release.next_major())
+        release = self.release
+        if self.is_stable() or Release(self.release.major, 0, 0) < self.release:
+            release = self.release.next_major()
+        return self.__class__(release=release)
 
     def next_minor(self) -> "PEP440Version":
-        return self.__class__(release=self.release.next_minor())
+        release = self.release
+        if (
+            self.is_stable()
+            or Release(self.release.major, self.release.minor, 0) < self.release
+        ):
+            release = self.release.next_minor()
+        return self.__class__(release=release)
 
     def next_patch(self) -> "PEP440Version":
-        return self.__class__(release=self.release.next_patch())
+        return self.__class__(
+            release=self.release.next_patch() if self.is_stable() else self.release
+        )
+
+    def next_prerelease(self, next_phase: bool = False) -> "PEP440Version":
+        if self.is_prerelease():
+            pre = self.pre.next_phase() if next_phase else self.pre.next()
+        else:
+            pre = ReleaseTag(RELEASE_PHASE_ALPHA)
+        return self.__class__(release=self.release, pre=pre)
+
+    def next_postrelease(self) -> "PEP440Version":
+        if self.is_prerelease():
+            post = self.post.next()
+        else:
+            post = ReleaseTag(RELEASE_PHASE_POST)
+        return self.__class__(
+            release=self.release, pre=self.pre, dev=self.dev, post=post
+        )
+
+    def next_devrelease(self) -> "PEP440Version":
+        if self.is_prerelease():
+            dev = self.dev.next()
+        else:
+            dev = ReleaseTag(RELEASE_PHASE_DEV)
+        return self.__class__(release=self.release, pre=self.pre, dev=dev)
+
+    def first_prerelease(self) -> "PEP440Version":
+        return self.__class__(release=self.release, pre=ReleaseTag(RELEASE_PHASE_ALPHA))

From 345d6a4ca653e697480a5f5de46bd4f604fb813b Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Thu, 25 Mar 2021 02:13:22 +0100
Subject: [PATCH 37/77] pep440: fix bugs

---
 poetry/core/packages/dependency.py  | 8 ++++----
 poetry/core/packages/utils/utils.py | 8 ++++----
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py
index bd95f08ba..b090aed77 100644
--- a/poetry/core/packages/dependency.py
+++ b/poetry/core/packages/dependency.py
@@ -551,17 +551,17 @@ def create_from_pep_508(
                         if parsed_version.precision == 1:
                             if op == "<=":
                                 op = "<"
-                                version = parsed_version.next_major.text
+                                version = parsed_version.next_major().text
                             elif op == ">":
                                 op = ">="
-                                version = parsed_version.next_major.text
+                                version = parsed_version.next_major().text
                         elif parsed_version.precision == 2:
                             if op == "<=":
                                 op = "<"
-                                version = parsed_version.next_minor.text
+                                version = parsed_version.next_minor().text
                             elif op == ">":
                                 op = ">="
-                                version = parsed_version.next_minor.text
+                                version = parsed_version.next_minor().text
                     elif op in ("in", "not in"):
                         versions = []
                         for v in re.split("[ ,]+", version):
diff --git a/poetry/core/packages/utils/utils.py b/poetry/core/packages/utils/utils.py
index f2514b1bf..c97604c06 100644
--- a/poetry/core/packages/utils/utils.py
+++ b/poetry/core/packages/utils/utils.py
@@ -313,17 +313,17 @@ def get_python_constraint_from_marker(
                 if parsed_version.precision == 1:
                     if op == "<=":
                         op = "<"
-                        version = parsed_version.next_major.text
+                        version = parsed_version.next_major().text
                     elif op == ">":
                         op = ">="
-                        version = parsed_version.next_major.text
+                        version = parsed_version.next_major().text
                 elif parsed_version.precision == 2:
                     if op == "<=":
                         op = "<"
-                        version = parsed_version.next_minor.text
+                        version = parsed_version.next_minor().text
                     elif op == ">":
                         op = ">="
-                        version = parsed_version.next_minor.text
+                        version = parsed_version.next_minor().text
             elif op in ("in", "not in"):
                 versions = []
                 for v in re.split("[ ,]+", version):

From ea8d99f3105e4103d791ea4bad20af9c8d599337 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Thu, 25 Mar 2021 02:28:12 +0100
Subject: [PATCH 38/77] Update pyproject to include compat

---
 pyproject.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pyproject.toml b/pyproject.toml
index 1e076896e..45256ad1c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -109,6 +109,6 @@ appdirs = []
 pyrsistent = "https://raw.githubusercontent.com/tobgu/pyrsistent/master/LICENSE.mit"
 
 [build-system]
-requires = []
+requires = ["dataclasses>=0.6;python_version < '3.7'"]
 build-backend = "poetry.core.masonry.api"
 backend-path = ["."]

From c11cb9a6ebdda53d45dae78b45f6f73f5368e793 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Sat, 27 Mar 2021 20:05:32 +0100
Subject: [PATCH 39/77] pep440: ensure epoch is retained on version bumps

---
 poetry/core/version/pep440/version.py | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/poetry/core/version/pep440/version.py b/poetry/core/version/pep440/version.py
index 2ea495778..e84a87a72 100644
--- a/poetry/core/version/pep440/version.py
+++ b/poetry/core/version/pep440/version.py
@@ -150,7 +150,7 @@ def next_major(self) -> "PEP440Version":
         release = self.release
         if self.is_stable() or Release(self.release.major, 0, 0) < self.release:
             release = self.release.next_major()
-        return self.__class__(release=release)
+        return self.__class__(epoch=self.epoch, release=release)
 
     def next_minor(self) -> "PEP440Version":
         release = self.release
@@ -159,11 +159,12 @@ def next_minor(self) -> "PEP440Version":
             or Release(self.release.major, self.release.minor, 0) < self.release
         ):
             release = self.release.next_minor()
-        return self.__class__(release=release)
+        return self.__class__(epoch=self.epoch, release=release)
 
     def next_patch(self) -> "PEP440Version":
         return self.__class__(
-            release=self.release.next_patch() if self.is_stable() else self.release
+            epoch=self.epoch,
+            release=self.release.next_patch() if self.is_stable() else self.release,
         )
 
     def next_prerelease(self, next_phase: bool = False) -> "PEP440Version":
@@ -171,7 +172,7 @@ def next_prerelease(self, next_phase: bool = False) -> "PEP440Version":
             pre = self.pre.next_phase() if next_phase else self.pre.next()
         else:
             pre = ReleaseTag(RELEASE_PHASE_ALPHA)
-        return self.__class__(release=self.release, pre=pre)
+        return self.__class__(epoch=self.epoch, release=self.release, pre=pre)
 
     def next_postrelease(self) -> "PEP440Version":
         if self.is_prerelease():
@@ -179,7 +180,11 @@ def next_postrelease(self) -> "PEP440Version":
         else:
             post = ReleaseTag(RELEASE_PHASE_POST)
         return self.__class__(
-            release=self.release, pre=self.pre, dev=self.dev, post=post
+            epoch=self.epoch,
+            release=self.release,
+            pre=self.pre,
+            dev=self.dev,
+            post=post,
         )
 
     def next_devrelease(self) -> "PEP440Version":
@@ -187,7 +192,11 @@ def next_devrelease(self) -> "PEP440Version":
             dev = self.dev.next()
         else:
             dev = ReleaseTag(RELEASE_PHASE_DEV)
-        return self.__class__(release=self.release, pre=self.pre, dev=dev)
+        return self.__class__(
+            epoch=self.epoch, release=self.release, pre=self.pre, dev=dev
+        )
 
     def first_prerelease(self) -> "PEP440Version":
-        return self.__class__(release=self.release, pre=ReleaseTag(RELEASE_PHASE_ALPHA))
+        return self.__class__(
+            epoch=self.epoch, release=self.release, pre=ReleaseTag(RELEASE_PHASE_ALPHA)
+        )

From 2af8f991ca6fe5127b284f43f8d9bba4f23f1e46 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Sat, 27 Mar 2021 16:51:08 +0100
Subject: [PATCH 40/77] pyproject: minor improvement to handling of data

---
 poetry/core/pyproject/toml.py | 30 +++++++++++++-----------------
 1 file changed, 13 insertions(+), 17 deletions(-)

diff --git a/poetry/core/pyproject/toml.py b/poetry/core/pyproject/toml.py
index cb6dddcf0..52b668f68 100644
--- a/poetry/core/pyproject/toml.py
+++ b/poetry/core/pyproject/toml.py
@@ -6,12 +6,13 @@
 
 
 if TYPE_CHECKING:
+    from tomlkit.container import Container
+    from tomlkit.items import Item
     from tomlkit.toml_document import TOMLDocument
 
+    from poetry.core.pyproject.tables import BuildSystem
     from poetry.core.toml import TOMLFile
 
-    from .tables import BuildSystem
-
 
 class PyProjectTOML:
     def __init__(self, path: Union[str, Path]) -> None:
@@ -20,7 +21,6 @@ def __init__(self, path: Union[str, Path]) -> None:
         self._file = TOMLFile(path=path)
         self._data: Optional["TOMLDocument"] = None
         self._build_system: Optional["BuildSystem"] = None
-        self._poetry_config: Optional["TOMLDocument"] = None
 
     @property
     def file(self) -> "TOMLFile":
@@ -40,7 +40,7 @@ def data(self) -> "TOMLDocument":
 
     @property
     def build_system(self) -> "BuildSystem":
-        from .tables import BuildSystem
+        from poetry.core.pyproject.tables import BuildSystem
 
         if self._build_system is None:
             build_backend = None
@@ -59,17 +59,17 @@ def build_system(self) -> "BuildSystem":
         return self._build_system
 
     @property
-    def poetry_config(self) -> Optional["TOMLDocument"]:
-        from .exceptions import PyProjectException
+    def poetry_config(self) -> Optional[Union["Item", "Container"]]:
+        from tomlkit.exceptions import NonExistentKey
 
-        if self._poetry_config is None:
-            self._poetry_config = self.data.get("tool", {}).get("poetry")
-            if self._poetry_config is None:
-                raise PyProjectException(
-                    "[tool.poetry] section not found in {}".format(self._file)
-                )
+        try:
+            return self.data["tool"]["poetry"]
+        except NonExistentKey as e:
+            from poetry.core.pyproject.exceptions import PyProjectException
 
-        return self._poetry_config
+            raise PyProjectException(
+                "[tool.poetry] section not found in {}".format(self._file)
+            ) from e
 
     def is_poetry_project(self) -> bool:
         from .exceptions import PyProjectException
@@ -91,9 +91,6 @@ def save(self) -> None:
 
         data = self.data
 
-        if self._poetry_config is not None:
-            data["tool"]["poetry"] = self._poetry_config
-
         if self._build_system is not None:
             if "build-system" not in data:
                 data["build-system"] = Container()
@@ -106,4 +103,3 @@ def save(self) -> None:
     def reload(self) -> None:
         self._data = None
         self._build_system = None
-        self._poetry_config = None

From f3e3792786aa11f13e8c5626b26c3d52afe8e6a1 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Sat, 27 Mar 2021 18:18:50 +0100
Subject: [PATCH 41/77] schema: add support for source.links boolean

---
 poetry/core/json/schemas/poetry-schema.json | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/poetry/core/json/schemas/poetry-schema.json b/poetry/core/json/schemas/poetry-schema.json
index 81664910f..1f7243eac 100644
--- a/poetry/core/json/schemas/poetry-schema.json
+++ b/poetry/core/json/schemas/poetry-schema.json
@@ -559,6 +559,10 @@
                 "secondary": {
                     "type": "boolean",
                     "description": "Declare this repository as secondary, i.e. it will only be looked up last for packages."
+                },
+                "links": {
+                    "type": "boolean",
+                    "description": "Declare this as a link source. Links at uri/path can point to sdist or bdist archives."
                 }
             }
         },

From bfc6008a916f43ab065d2c73f44049e75c101ec7 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Thu, 25 Mar 2021 02:33:30 +0100
Subject: [PATCH 42/77] ci: expand python coverage for integration tests

---
 .github/workflows/integration.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
index 6a1c942df..856db99d5 100644
--- a/.github/workflows/integration.yml
+++ b/.github/workflows/integration.yml
@@ -7,7 +7,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.8]
+        python-version: [3.6, 3.7, 3.8, 3.9]
     steps:
       - uses: actions/checkout@v2
 

From a7c6e2c51e626c445720f9f7eaa1a94ce1a10eff Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Thu, 25 Mar 2021 02:39:54 +0100
Subject: [PATCH 43/77] ci: expand test matrix for integration tests

---
 .github/workflows/integration.yml | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
index 856db99d5..bc8e37f69 100644
--- a/.github/workflows/integration.yml
+++ b/.github/workflows/integration.yml
@@ -7,12 +7,16 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
+        os: [Ubuntu, MacOS, Windows]
         python-version: [3.6, 3.7, 3.8, 3.9]
+        include:
+          - os: Ubuntu
+            python-version: pypy3
     steps:
       - uses: actions/checkout@v2
 
       - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python@v1
+        uses: actions/setup-python@v2
         with:
           python-version: ${{ matrix.python-version }}
 

From e6c54435b58e8041c34202acec1d4eb8335764f9 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Thu, 25 Mar 2021 02:40:36 +0100
Subject: [PATCH 44/77] ci: add experimental support for python 3.10

---
 .github/workflows/integration.yml |  7 +++++++
 .github/workflows/tests.yml       | 14 +++++++++-----
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
index bc8e37f69..d44d5e77c 100644
--- a/.github/workflows/integration.yml
+++ b/.github/workflows/integration.yml
@@ -4,14 +4,21 @@ on: [push, pull_request]
 
 jobs:
   Tests:
+    name: ${{ matrix.os }} / ${{ matrix.python-version }}
     runs-on: ubuntu-latest
+    continue-on-error: ${{ matrix.experimental }}
     strategy:
       matrix:
         os: [Ubuntu, MacOS, Windows]
         python-version: [3.6, 3.7, 3.8, 3.9]
+        experimental: [false]
         include:
           - os: Ubuntu
             python-version: pypy3
+            experimental: false
+          - os: Ubuntu
+            python-version: "3.10.0-alpha - 3.10.0"
+            experimental: true
     steps:
       - uses: actions/checkout@v2
 
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 2add7486d..7e1a62c81 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -9,15 +9,19 @@ jobs:
   Tests:
     name: ${{ matrix.os }} / ${{ matrix.python-version }}
     runs-on: ${{ matrix.os }}-latest
+    continue-on-error: ${{ matrix.experimental }}
     strategy:
       matrix:
         os: [Ubuntu, MacOS, Windows]
-        python-version: [3.6, 3.7, 3.8, 3.9, pypy3]
-        exclude:
-          - os: MacOS
-            python-version: pypy3
-          - os: Windows
+        python-version: [3.6, 3.7, 3.8, 3.9]
+        experimental: [false]
+        include:
+          - os: Ubuntu
             python-version: pypy3
+            experimental: false
+          - os: Ubuntu
+            python-version: "3.10.0-alpha - 3.10.0"
+            experimental: true
     steps:
       - uses: actions/checkout@v2
 

From 25f6dedcd142148daf3e2f4f495d30055715ddd7 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Thu, 25 Mar 2021 03:05:45 +0100
Subject: [PATCH 45/77] ci: skip pypy for integration tests

---
 .github/workflows/integration.yml | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
index d44d5e77c..3eb106895 100644
--- a/.github/workflows/integration.yml
+++ b/.github/workflows/integration.yml
@@ -13,9 +13,6 @@ jobs:
         python-version: [3.6, 3.7, 3.8, 3.9]
         experimental: [false]
         include:
-          - os: Ubuntu
-            python-version: pypy3
-            experimental: false
           - os: Ubuntu
             python-version: "3.10.0-alpha - 3.10.0"
             experimental: true

From 9e51c35d2afd82f8be6e48be8998e242a108e22c Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Sat, 27 Mar 2021 13:43:52 +0100
Subject: [PATCH 46/77] tests: fix py3.6 compat issues in integration suite

---
 tests/testutils.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tests/testutils.py b/tests/testutils.py
index 193b95cb6..4b335e790 100644
--- a/tests/testutils.py
+++ b/tests/testutils.py
@@ -12,6 +12,7 @@
 from typing import Optional
 
 from poetry.core.toml import TOMLFile
+from poetry.core.utils._compat import PY37
 
 
 try:
@@ -60,8 +61,9 @@ def subprocess_run(*args, **kwargs):  # type: (str, Any) -> subprocess.Completed
     """
     Helper method to run a subprocess. Asserts for success.
     """
+    compat_kwargs = {"text" if PY37 else "universal_newlines": True}
     result = subprocess.run(
-        args, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs
+        args, **compat_kwargs, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs
     )
     assert result.returncode == 0
     return result

From 43047f11e9d292a257781fbea27222350dab4ed0 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Sat, 27 Mar 2021 14:03:40 +0100
Subject: [PATCH 47/77] package: add python 3.10 to available python list

---
 poetry/core/packages/package.py         | 36 ++++++++++++++++++++-----
 poetry/core/version/helpers.py          |  1 +
 tests/masonry/builders/test_builder.py  |  1 +
 tests/masonry/builders/test_complete.py |  2 ++
 tests/masonry/test_api.py               |  1 +
 tests/test_factory.py                   |  1 +
 6 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/poetry/core/packages/package.py b/poetry/core/packages/package.py
index 01aa8fbbd..1662071f1 100644
--- a/poetry/core/packages/package.py
+++ b/poetry/core/packages/package.py
@@ -38,6 +38,7 @@ class Package(PackageSpecification):
         "3.7",
         "3.8",
         "3.9",
+        "3.10",
     }
 
     def __init__(
@@ -270,24 +271,45 @@ def all_classifiers(self) -> List[str]:
         else:
             python_constraint = self.python_constraint
 
-        for version in sorted(self.AVAILABLE_PYTHONS):
+        python_classifier_prefix = "Programming Language :: Python"
+        python_classifiers = []
+
+        # we sort python versions by sorting an int tuple of (major, minor) version
+        # to ensure we sort 3.10 after 3.9
+        for version in sorted(
+            self.AVAILABLE_PYTHONS, key=lambda x: tuple(map(int, x.split(".")))
+        ):
             if len(version) == 1:
                 constraint = parse_constraint(version + ".*")
             else:
                 constraint = Version.parse(version)
 
             if python_constraint.allows_any(constraint):
-                classifiers.append(
-                    "Programming Language :: Python :: {}".format(version)
-                )
+                classifier = "{} :: {}".format(python_classifier_prefix, version)
+                if classifier not in python_classifiers:
+                    python_classifiers.append(classifier)
 
         # Automatically set license classifiers
         if self.license:
             classifiers.append(self.license.classifier)
 
-        classifiers = set(classifiers)
-
-        return sorted(classifiers)
+        # Sort classifiers and insert python classifiers at the right location. We do
+        # it like this so that 3.10 is sorted after 3.9.
+        sorted_classifiers = []
+        python_classifiers_inserted = False
+        for classifier in sorted(set(classifiers)):
+            if (
+                not python_classifiers_inserted
+                and classifier > python_classifier_prefix
+            ):
+                sorted_classifiers.extend(python_classifiers)
+                python_classifiers_inserted = True
+            sorted_classifiers.append(classifier)
+
+        if not python_classifiers_inserted:
+            sorted_classifiers.extend(python_classifiers)
+
+        return sorted_classifiers
 
     @property
     def urls(self) -> Dict[str, str]:
diff --git a/poetry/core/version/helpers.py b/poetry/core/version/helpers.py
index df64b8e0f..b3f801e0a 100644
--- a/poetry/core/version/helpers.py
+++ b/poetry/core/version/helpers.py
@@ -21,6 +21,7 @@
     "3.7.*",
     "3.8.*",
     "3.9.*",
+    "3.10.*",
 ]
 
 
diff --git a/tests/masonry/builders/test_builder.py b/tests/masonry/builders/test_builder.py
index 86cf4bad5..c178ea6b8 100644
--- a/tests/masonry/builders/test_builder.py
+++ b/tests/masonry/builders/test_builder.py
@@ -92,6 +92,7 @@ def test_get_metadata_content():
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3.10",
         "Topic :: Software Development :: Build Tools",
         "Topic :: Software Development :: Libraries :: Python Modules",
     ]
diff --git a/tests/masonry/builders/test_complete.py b/tests/masonry/builders/test_complete.py
index 01a8c114a..8d7e647ee 100644
--- a/tests/masonry/builders/test_complete.py
+++ b/tests/masonry/builders/test_complete.py
@@ -272,6 +272,7 @@ def test_complete():
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
 Classifier: Topic :: Software Development :: Build Tools
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Provides-Extra: time
@@ -375,6 +376,7 @@ def test_complete_no_vcs():
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
 Classifier: Topic :: Software Development :: Build Tools
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Provides-Extra: time
diff --git a/tests/masonry/test_api.py b/tests/masonry/test_api.py
index a649a197c..e0c73ab4c 100644
--- a/tests/masonry/test_api.py
+++ b/tests/masonry/test_api.py
@@ -164,6 +164,7 @@ def test_prepare_metadata_for_build_wheel():
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
 Classifier: Topic :: Software Development :: Build Tools
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Provides-Extra: time
diff --git a/tests/test_factory.py b/tests/test_factory.py
index da0329429..20ac95e39 100644
--- a/tests/test_factory.py
+++ b/tests/test_factory.py
@@ -117,6 +117,7 @@ def test_create_poetry():
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3.10",
         "Topic :: Software Development :: Build Tools",
         "Topic :: Software Development :: Libraries :: Python Modules",
     ]

From c22421a2019f2b1489eb0e8455a50d86ae8a58f9 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Sat, 27 Mar 2021 14:07:49 +0100
Subject: [PATCH 48/77] update development dependencies

---
 .pre-commit-config.yaml |   2 +-
 poetry.lock             | 190 +++++++++++++++++++---------------------
 pyproject.toml          |   6 +-
 3 files changed, 92 insertions(+), 106 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f7be580c1..a5cfd98a6 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -17,7 +17,7 @@ repos:
           )
 
   - repo: https://github.com/timothycrosley/isort
-    rev: 5.7.0
+    rev: 5.8.0
     hooks:
       - id: isort
         additional_dependencies: [toml]
diff --git a/poetry.lock b/poetry.lock
index e4fdb348c..f5970c175 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -128,14 +128,14 @@ python-versions = "*"
 
 [[package]]
 name = "identify"
-version = "2.1.1"
+version = "2.2.1"
 description = "File identification library for Python"
 category = "dev"
 optional = false
 python-versions = ">=3.6.1"
 
 [package.extras]
-license = ["editdistance"]
+license = ["editdistance-s"]
 
 [[package]]
 name = "idna"
@@ -175,9 +175,17 @@ zipp = {version = ">=0.4", markers = "python_version < \"3.8\""}
 docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
 testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "pytest-black (>=0.3.7)", "pytest-mypy"]
 
+[[package]]
+name = "iniconfig"
+version = "1.1.1"
+description = "iniconfig: brain-dead simple config-ini parsing"
+category = "dev"
+optional = false
+python-versions = "*"
+
 [[package]]
 name = "isort"
-version = "5.7.0"
+version = "5.8.0"
 description = "A Python utility / library to sort Python imports."
 category = "dev"
 optional = false
@@ -198,7 +206,6 @@ python-versions = "*"
 
 [package.dependencies]
 attrs = ">=17.4.0"
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
 pyrsistent = ">=0.14.0"
 six = ">=1.11.0"
 
@@ -206,14 +213,6 @@ six = ">=1.11.0"
 format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"]
 format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"]
 
-[[package]]
-name = "more-itertools"
-version = "8.7.0"
-description = "More routines for operating on iterables, beyond itertools"
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
 [[package]]
 name = "mypy-extensions"
 version = "0.4.3"
@@ -323,26 +322,25 @@ six = "*"
 
 [[package]]
 name = "pytest"
-version = "4.6.11"
+version = "6.2.2"
 description = "pytest: simple powerful testing with Python"
 category = "dev"
 optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+python-versions = ">=3.6"
 
 [package.dependencies]
-atomicwrites = ">=1.0"
-attrs = ">=17.4.0"
-colorama = {version = "*", markers = "sys_platform == \"win32\" and python_version != \"3.4\""}
+atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
+attrs = ">=19.2.0"
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
 importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
-more-itertools = {version = ">=4.0.0", markers = "python_version > \"2.7\""}
+iniconfig = "*"
 packaging = "*"
-pluggy = ">=0.12,<1.0"
-py = ">=1.5.0"
-six = ">=1.10.0"
-wcwidth = "*"
+pluggy = ">=0.12,<1.0.0a1"
+py = ">=1.8.2"
+toml = "*"
 
 [package.extras]
-testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"]
+testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
 
 [[package]]
 name = "pytest-cov"
@@ -361,17 +359,17 @@ testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist",
 
 [[package]]
 name = "pytest-mock"
-version = "2.0.0"
-description = "Thin-wrapper around the mock package for easier use with py.test"
+version = "3.5.1"
+description = "Thin-wrapper around the mock package for easier use with pytest"
 category = "dev"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = ">=3.5"
 
 [package.dependencies]
-pytest = ">=2.7"
+pytest = ">=5.0"
 
 [package.extras]
-dev = ["pre-commit", "tox"]
+dev = ["pre-commit", "tox", "pytest-asyncio"]
 
 [[package]]
 name = "pyyaml"
@@ -383,7 +381,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
 
 [[package]]
 name = "regex"
-version = "2020.11.13"
+version = "2021.3.17"
 description = "Alternative regular expression module, to replace re."
 category = "dev"
 optional = false
@@ -464,16 +462,16 @@ python-versions = "*"
 
 [[package]]
 name = "urllib3"
-version = "1.26.3"
+version = "1.26.4"
 description = "HTTP library with thread-safe connection pooling, file post, and more."
 category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
 
 [package.extras]
-brotli = ["brotlipy (>=0.6.0)"]
 secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
 socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+brotli = ["brotlipy (>=0.6.0)"]
 
 [[package]]
 name = "vendoring"
@@ -496,7 +494,7 @@ test = ["pytest", "pytest-xdist", "pytest-cov", "pytest-mock"]
 
 [[package]]
 name = "virtualenv"
-version = "20.4.2"
+version = "20.4.3"
 description = "Virtual Python Environment builder"
 category = "dev"
 optional = false
@@ -514,14 +512,6 @@ six = ">=1.9.0,<2"
 docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"]
 testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"]
 
-[[package]]
-name = "wcwidth"
-version = "0.2.5"
-description = "Measures the displayed width of unicode strings in a terminal"
-category = "dev"
-optional = false
-python-versions = "*"
-
 [[package]]
 name = "zipp"
 version = "3.4.1"
@@ -537,7 +527,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.6"
-content-hash = "9a3dbd14f45aa7c96293a0b4286c71f0b6501bc5fda78d834a9212c0c55cf3cd"
+content-hash = "f454025163c37b630e04ee93ceea5ad23471dcfc38127e3d7259deac52bee0f6"
 
 [metadata.files]
 appdirs = [
@@ -643,8 +633,8 @@ filelock = [
     {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
 ]
 identify = [
-    {file = "identify-2.1.1-py2.py3-none-any.whl", hash = "sha256:220169a38a0c977c8fef377dc808d6a3330641b5211ec7356c7bbe73cda487c7"},
-    {file = "identify-2.1.1.tar.gz", hash = "sha256:da3d757c94596c50865aae63db6ba4e2e5e3f666c3ea6a6da0cd09a8b2d34abc"},
+    {file = "identify-2.2.1-py2.py3-none-any.whl", hash = "sha256:9cc5f58996cd359b7b72f0a5917d8639de5323917e6952a3bfbf36301b576f40"},
+    {file = "identify-2.2.1.tar.gz", hash = "sha256:1cfb05b578de996677836d5a2dde14b3dffde313cf7d2b3e793a0787a36e26dd"},
 ]
 idna = [
     {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
@@ -658,18 +648,18 @@ importlib-resources = [
     {file = "importlib_resources-5.1.2-py3-none-any.whl", hash = "sha256:ebab3efe74d83b04d6bf5cd9a17f0c5c93e60fb60f30c90f56265fce4682a469"},
     {file = "importlib_resources-5.1.2.tar.gz", hash = "sha256:642586fc4740bd1cad7690f836b3321309402b20b332529f25617ff18e8e1370"},
 ]
+iniconfig = [
+    {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
+    {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
+]
 isort = [
-    {file = "isort-5.7.0-py3-none-any.whl", hash = "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"},
-    {file = "isort-5.7.0.tar.gz", hash = "sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e"},
+    {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"},
+    {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"},
 ]
 jsonschema = [
     {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"},
     {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"},
 ]
-more-itertools = [
-    {file = "more-itertools-8.7.0.tar.gz", hash = "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713"},
-    {file = "more_itertools-8.7.0-py3-none-any.whl", hash = "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced"},
-]
 mypy-extensions = [
     {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
     {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
@@ -710,16 +700,16 @@ pyrsistent = [
     {file = "pyrsistent-0.16.1.tar.gz", hash = "sha256:aa2ae1c2e496f4d6777f869ea5de7166a8ccb9c2e06ebcf6c7ff1b670c98c5ef"},
 ]
 pytest = [
-    {file = "pytest-4.6.11-py2.py3-none-any.whl", hash = "sha256:a00a7d79cbbdfa9d21e7d0298392a8dd4123316bfac545075e6f8f24c94d8c97"},
-    {file = "pytest-4.6.11.tar.gz", hash = "sha256:50fa82392f2120cc3ec2ca0a75ee615be4c479e66669789771f1758332be4353"},
+    {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"},
+    {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"},
 ]
 pytest-cov = [
     {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"},
     {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"},
 ]
 pytest-mock = [
-    {file = "pytest-mock-2.0.0.tar.gz", hash = "sha256:b35eb281e93aafed138db25c8772b95d3756108b601947f89af503f8c629413f"},
-    {file = "pytest_mock-2.0.0-py2.py3-none-any.whl", hash = "sha256:cb67402d87d5f53c579263d37971a164743dc33c159dfb4fb4a86f37c5552307"},
+    {file = "pytest-mock-3.5.1.tar.gz", hash = "sha256:a1e2aba6af9560d313c642dae7e00a2a12b022b80301d9d7fc8ec6858e1dd9fc"},
+    {file = "pytest_mock-3.5.1-py3-none-any.whl", hash = "sha256:379b391cfad22422ea2e252bdfc008edd08509029bcde3c25b2c0bd741e0424e"},
 ]
 pyyaml = [
     {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
@@ -745,47 +735,47 @@ pyyaml = [
     {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
 ]
 regex = [
-    {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"},
-    {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"},
-    {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"},
-    {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"},
-    {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"},
-    {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"},
-    {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"},
-    {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"},
-    {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"},
-    {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"},
-    {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"},
-    {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"},
-    {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"},
-    {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"},
-    {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"},
-    {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"},
-    {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"},
-    {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"},
-    {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"},
-    {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"},
-    {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"},
-    {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"},
-    {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"},
-    {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"},
-    {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"},
-    {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"},
-    {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"},
-    {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"},
-    {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"},
-    {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"},
-    {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"},
-    {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"},
-    {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"},
-    {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"},
-    {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"},
-    {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"},
-    {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"},
-    {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"},
-    {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"},
-    {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"},
-    {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"},
+    {file = "regex-2021.3.17-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b97ec5d299c10d96617cc851b2e0f81ba5d9d6248413cd374ef7f3a8871ee4a6"},
+    {file = "regex-2021.3.17-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:cb4ee827857a5ad9b8ae34d3c8cc51151cb4a3fe082c12ec20ec73e63cc7c6f0"},
+    {file = "regex-2021.3.17-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:633497504e2a485a70a3268d4fc403fe3063a50a50eed1039083e9471ad0101c"},
+    {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:a59a2ee329b3de764b21495d78c92ab00b4ea79acef0f7ae8c1067f773570afa"},
+    {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f85d6f41e34f6a2d1607e312820971872944f1661a73d33e1e82d35ea3305e14"},
+    {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:4651f839dbde0816798e698626af6a2469eee6d9964824bb5386091255a1694f"},
+    {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:39c44532d0e4f1639a89e52355b949573e1e2c5116106a395642cbbae0ff9bcd"},
+    {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3d9a7e215e02bd7646a91fb8bcba30bc55fd42a719d6b35cf80e5bae31d9134e"},
+    {file = "regex-2021.3.17-cp36-cp36m-win32.whl", hash = "sha256:159fac1a4731409c830d32913f13f68346d6b8e39650ed5d704a9ce2f9ef9cb3"},
+    {file = "regex-2021.3.17-cp36-cp36m-win_amd64.whl", hash = "sha256:13f50969028e81765ed2a1c5fcfdc246c245cf8d47986d5172e82ab1a0c42ee5"},
+    {file = "regex-2021.3.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9d8d286c53fe0cbc6d20bf3d583cabcd1499d89034524e3b94c93a5ab85ca90"},
+    {file = "regex-2021.3.17-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:201e2619a77b21a7780580ab7b5ce43835e242d3e20fef50f66a8df0542e437f"},
+    {file = "regex-2021.3.17-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d47d359545b0ccad29d572ecd52c9da945de7cd6cf9c0cfcb0269f76d3555689"},
+    {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ea2f41445852c660ba7c3ebf7d70b3779b20d9ca8ba54485a17740db49f46932"},
+    {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:486a5f8e11e1f5bbfcad87f7c7745eb14796642323e7e1829a331f87a713daaa"},
+    {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:18e25e0afe1cf0f62781a150c1454b2113785401ba285c745acf10c8ca8917df"},
+    {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:a2ee026f4156789df8644d23ef423e6194fad0bc53575534101bb1de5d67e8ce"},
+    {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:4c0788010a93ace8a174d73e7c6c9d3e6e3b7ad99a453c8ee8c975ddd9965643"},
+    {file = "regex-2021.3.17-cp37-cp37m-win32.whl", hash = "sha256:575a832e09d237ae5fedb825a7a5bc6a116090dd57d6417d4f3b75121c73e3be"},
+    {file = "regex-2021.3.17-cp37-cp37m-win_amd64.whl", hash = "sha256:8e65e3e4c6feadf6770e2ad89ad3deb524bcb03d8dc679f381d0568c024e0deb"},
+    {file = "regex-2021.3.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a0df9a0ad2aad49ea3c7f65edd2ffb3d5c59589b85992a6006354f6fb109bb18"},
+    {file = "regex-2021.3.17-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b98bc9db003f1079caf07b610377ed1ac2e2c11acc2bea4892e28cc5b509d8d5"},
+    {file = "regex-2021.3.17-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:808404898e9a765e4058bf3d7607d0629000e0a14a6782ccbb089296b76fa8fe"},
+    {file = "regex-2021.3.17-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:5770a51180d85ea468234bc7987f5597803a4c3d7463e7323322fe4a1b181578"},
+    {file = "regex-2021.3.17-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:976a54d44fd043d958a69b18705a910a8376196c6b6ee5f2596ffc11bff4420d"},
+    {file = "regex-2021.3.17-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:63f3ca8451e5ff7133ffbec9eda641aeab2001be1a01878990f6c87e3c44b9d5"},
+    {file = "regex-2021.3.17-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bcd945175c29a672f13fce13a11893556cd440e37c1b643d6eeab1988c8b209c"},
+    {file = "regex-2021.3.17-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:3d9356add82cff75413bec360c1eca3e58db4a9f5dafa1f19650958a81e3249d"},
+    {file = "regex-2021.3.17-cp38-cp38-win32.whl", hash = "sha256:f5d0c921c99297354cecc5a416ee4280bd3f20fd81b9fb671ca6be71499c3fdf"},
+    {file = "regex-2021.3.17-cp38-cp38-win_amd64.whl", hash = "sha256:14de88eda0976020528efc92d0a1f8830e2fb0de2ae6005a6fc4e062553031fa"},
+    {file = "regex-2021.3.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4c2e364491406b7888c2ad4428245fc56c327e34a5dfe58fd40df272b3c3dab3"},
+    {file = "regex-2021.3.17-cp39-cp39-manylinux1_i686.whl", hash = "sha256:8bd4f91f3fb1c9b1380d6894bd5b4a519409135bec14c0c80151e58394a4e88a"},
+    {file = "regex-2021.3.17-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:882f53afe31ef0425b405a3f601c0009b44206ea7f55ee1c606aad3cc213a52c"},
+    {file = "regex-2021.3.17-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:07ef35301b4484bce843831e7039a84e19d8d33b3f8b2f9aab86c376813d0139"},
+    {file = "regex-2021.3.17-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:360a01b5fa2ad35b3113ae0c07fb544ad180603fa3b1f074f52d98c1096fa15e"},
+    {file = "regex-2021.3.17-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:709f65bb2fa9825f09892617d01246002097f8f9b6dde8d1bb4083cf554701ba"},
+    {file = "regex-2021.3.17-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:c66221e947d7207457f8b6f42b12f613b09efa9669f65a587a2a71f6a0e4d106"},
+    {file = "regex-2021.3.17-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:c782da0e45aff131f0bed6e66fbcfa589ff2862fc719b83a88640daa01a5aff7"},
+    {file = "regex-2021.3.17-cp39-cp39-win32.whl", hash = "sha256:dc9963aacb7da5177e40874585d7407c0f93fb9d7518ec58b86e562f633f36cd"},
+    {file = "regex-2021.3.17-cp39-cp39-win_amd64.whl", hash = "sha256:a0d04128e005142260de3733591ddf476e4902c0c23c1af237d9acf3c96e1b38"},
+    {file = "regex-2021.3.17.tar.gz", hash = "sha256:4b8a1fb724904139149a43e172850f35aa6ea97fb0545244dc0b805e0154ed68"},
 ]
 requests = [
     {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
@@ -841,20 +831,16 @@ typing-extensions = [
     {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
 ]
 urllib3 = [
-    {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"},
-    {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"},
+    {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"},
+    {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"},
 ]
 vendoring = [
     {file = "vendoring-0.3.3-py2.py3-none-any.whl", hash = "sha256:2b91c302116320f903fdb5e60c2b0805d807e2b87425ab0c86624028aa5ffa57"},
     {file = "vendoring-0.3.3.tar.gz", hash = "sha256:2bbfc0c8da2863f4638c854b91e80c5d0ca57db62fb979d4b0f52088eeab1162"},
 ]
 virtualenv = [
-    {file = "virtualenv-20.4.2-py2.py3-none-any.whl", hash = "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3"},
-    {file = "virtualenv-20.4.2.tar.gz", hash = "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d"},
-]
-wcwidth = [
-    {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
-    {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
+    {file = "virtualenv-20.4.3-py2.py3-none-any.whl", hash = "sha256:83f95875d382c7abafe06bd2a4cdd1b363e1bb77e02f155ebe8ac082a916b37c"},
+    {file = "virtualenv-20.4.3.tar.gz", hash = "sha256:49ec4eb4c224c6f7dd81bb6d0a28a09ecae5894f4e593c89b0db0885f565a107"},
 ]
 zipp = [
     {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"},
diff --git a/pyproject.toml b/pyproject.toml
index 45256ad1c..8095d9d97 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -39,14 +39,14 @@ dataclasses = {version = "^0.8", python = "~3.6"}
 [tool.poetry.dev-dependencies]
 pre-commit = {version = "^2.10.0", python = "^3.6.1"}
 pyrsistent = "^0.16.0"
-pytest = "^4.6"
+pytest = "^6.2"
 pytest-cov = "^2.8"
-pytest-mock = "^2.0"
+pytest-mock = "^3.5"
 tox = "^3.0"
 vendoring = {version = "^0.3", python = "^3.8"}
 pep517 = "^0.8.2"
 black = {version = "^20.8b1", markers = "python_version >= '3.6' and python_version < '4.0' and implementation_name != 'pypy'"}
-isort = "^5.7.0"
+isort = "^5.8.0"
 
 [tool.black]
 line-length = 88

From d677150394f398048d0b4ccc75d8fe209089e806 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Sat, 27 Mar 2021 14:21:17 +0100
Subject: [PATCH 49/77] ci: switch to pip install when installing poetry

We do this since poetry releases currently do not work with python
3.10.

This can be replaced once new bootstrap is available.
---
 .github/workflows/tests.yml | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 7e1a62c81..20c68cafb 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -37,10 +37,7 @@ jobs:
 
       - name: Install poetry
         shell: bash
-        run: |
-          curl -fsS -o get-poetry.py https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py
-          python get-poetry.py --preview -y
-          echo "$HOME/.poetry/bin" >> $GITHUB_PATH
+        run: pip install poetry
 
       - name: Configure poetry
         shell: bash

From 28b7a07a782327bc2bf09067ab2518ff0e5ea732 Mon Sep 17 00:00:00 2001
From: david-fortini 
Date: Mon, 22 Mar 2021 10:44:06 +0100
Subject: [PATCH 50/77] Let poetry use default branch if none is specified.
 Resolves: python-poetry/poetry#3366

---
 poetry/core/packages/vcs_dependency.py | 10 ++++++++--
 tests/masonry/builders/test_sdist.py   |  2 +-
 tests/packages/test_vcs_dependency.py  | 14 +++++++++-----
 3 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/poetry/core/packages/vcs_dependency.py b/poetry/core/packages/vcs_dependency.py
index 932338c16..acb357008 100644
--- a/poetry/core/packages/vcs_dependency.py
+++ b/poetry/core/packages/vcs_dependency.py
@@ -34,8 +34,8 @@ def __init__(
         self._source = source
 
         if not any([branch, tag, rev]):
-            # If nothing has been specified, we assume master
-            branch = "master"
+            # If nothing has been specified, we assume nothing and use default branch
+            branch = ""
 
         self._branch = branch
         self._tag = tag
@@ -107,6 +107,12 @@ def base_pep_508_name(self) -> str:
         if self.extras:
             requirement += "[{}]".format(",".join(self.extras))
 
+        if self.branch == "":
+            if parsed_url.protocol is not None:
+                requirement += " @ {}+{}".format(self._vcs, self._source)
+            else:
+                requirement += " @ {}+ssh://{}".format(self._vcs, parsed_url.format())
+            return requirement
         if parsed_url.protocol is not None:
             requirement += " @ {}+{}@{}".format(self._vcs, self._source, self.reference)
         else:
diff --git a/tests/masonry/builders/test_sdist.py b/tests/masonry/builders/test_sdist.py
index b67be206c..df74c24f9 100644
--- a/tests/masonry/builders/test_sdist.py
+++ b/tests/masonry/builders/test_sdist.py
@@ -55,7 +55,7 @@ def test_convert_dependencies():
         "A>=1.0,<2.0",
         "B>=1.0,<1.1",
         "C==1.2.3",
-        "D @ git+https://github.com/sdispater/d.git@master",
+        "D @ git+https://github.com/sdispater/d.git",
         "E>=1.0,<2.0",
         "F>=1.0,<2.0,!=1.3",
     ]
diff --git a/tests/packages/test_vcs_dependency.py b/tests/packages/test_vcs_dependency.py
index 188350c6a..a56df84e4 100644
--- a/tests/packages/test_vcs_dependency.py
+++ b/tests/packages/test_vcs_dependency.py
@@ -8,7 +8,7 @@ def test_to_pep_508():
         "poetry", "git", "https://github.com/python-poetry/poetry.git"
     )
 
-    expected = "poetry @ git+https://github.com/python-poetry/poetry.git@master"
+    expected = "poetry @ git+https://github.com/python-poetry/poetry.git"
 
     assert expected == dependency.to_pep_508()
 
@@ -16,7 +16,7 @@ def test_to_pep_508():
 def test_to_pep_508_ssh():
     dependency = VCSDependency("poetry", "git", "git@github.com:sdispater/poetry.git")
 
-    expected = "poetry @ git+ssh://git@github.com/sdispater/poetry.git@master"
+    expected = "poetry @ git+ssh://git@github.com/sdispater/poetry.git"
 
     assert expected == dependency.to_pep_508()
 
@@ -26,7 +26,7 @@ def test_to_pep_508_with_extras():
         "poetry", "git", "https://github.com/python-poetry/poetry.git", extras=["foo"]
     )
 
-    expected = "poetry[foo] @ git+https://github.com/python-poetry/poetry.git@master"
+    expected = "poetry[foo] @ git+https://github.com/python-poetry/poetry.git"
 
     assert expected == dependency.to_pep_508()
 
@@ -37,7 +37,9 @@ def test_to_pep_508_in_extras():
     )
     dependency.in_extras.append("foo")
 
-    expected = 'poetry @ git+https://github.com/python-poetry/poetry.git@master ; extra == "foo"'
+    expected = (
+        'poetry @ git+https://github.com/python-poetry/poetry.git ; extra == "foo"'
+    )
     assert expected == dependency.to_pep_508()
 
     dependency = VCSDependency(
@@ -45,7 +47,9 @@ def test_to_pep_508_in_extras():
     )
     dependency.in_extras.append("foo")
 
-    expected = 'poetry[bar] @ git+https://github.com/python-poetry/poetry.git@master ; extra == "foo"'
+    expected = (
+        'poetry[bar] @ git+https://github.com/python-poetry/poetry.git ; extra == "foo"'
+    )
 
     assert expected == dependency.to_pep_508()
 

From bd772c6db151ee9abe7f9d8780f7092c85fa1044 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Sun, 28 Mar 2021 21:01:37 +0200
Subject: [PATCH 51/77] Revert "Let poetry use default branch if none is
 specified. Resolves: python-poetry/poetry#3366"

This reverts commit 28b7a07a782327bc2bf09067ab2518ff0e5ea732.
---
 poetry/core/packages/vcs_dependency.py | 10 ++--------
 tests/masonry/builders/test_sdist.py   |  2 +-
 tests/packages/test_vcs_dependency.py  | 14 +++++---------
 3 files changed, 8 insertions(+), 18 deletions(-)

diff --git a/poetry/core/packages/vcs_dependency.py b/poetry/core/packages/vcs_dependency.py
index acb357008..932338c16 100644
--- a/poetry/core/packages/vcs_dependency.py
+++ b/poetry/core/packages/vcs_dependency.py
@@ -34,8 +34,8 @@ def __init__(
         self._source = source
 
         if not any([branch, tag, rev]):
-            # If nothing has been specified, we assume nothing and use default branch
-            branch = ""
+            # If nothing has been specified, we assume master
+            branch = "master"
 
         self._branch = branch
         self._tag = tag
@@ -107,12 +107,6 @@ def base_pep_508_name(self) -> str:
         if self.extras:
             requirement += "[{}]".format(",".join(self.extras))
 
-        if self.branch == "":
-            if parsed_url.protocol is not None:
-                requirement += " @ {}+{}".format(self._vcs, self._source)
-            else:
-                requirement += " @ {}+ssh://{}".format(self._vcs, parsed_url.format())
-            return requirement
         if parsed_url.protocol is not None:
             requirement += " @ {}+{}@{}".format(self._vcs, self._source, self.reference)
         else:
diff --git a/tests/masonry/builders/test_sdist.py b/tests/masonry/builders/test_sdist.py
index df74c24f9..b67be206c 100644
--- a/tests/masonry/builders/test_sdist.py
+++ b/tests/masonry/builders/test_sdist.py
@@ -55,7 +55,7 @@ def test_convert_dependencies():
         "A>=1.0,<2.0",
         "B>=1.0,<1.1",
         "C==1.2.3",
-        "D @ git+https://github.com/sdispater/d.git",
+        "D @ git+https://github.com/sdispater/d.git@master",
         "E>=1.0,<2.0",
         "F>=1.0,<2.0,!=1.3",
     ]
diff --git a/tests/packages/test_vcs_dependency.py b/tests/packages/test_vcs_dependency.py
index a56df84e4..188350c6a 100644
--- a/tests/packages/test_vcs_dependency.py
+++ b/tests/packages/test_vcs_dependency.py
@@ -8,7 +8,7 @@ def test_to_pep_508():
         "poetry", "git", "https://github.com/python-poetry/poetry.git"
     )
 
-    expected = "poetry @ git+https://github.com/python-poetry/poetry.git"
+    expected = "poetry @ git+https://github.com/python-poetry/poetry.git@master"
 
     assert expected == dependency.to_pep_508()
 
@@ -16,7 +16,7 @@ def test_to_pep_508():
 def test_to_pep_508_ssh():
     dependency = VCSDependency("poetry", "git", "git@github.com:sdispater/poetry.git")
 
-    expected = "poetry @ git+ssh://git@github.com/sdispater/poetry.git"
+    expected = "poetry @ git+ssh://git@github.com/sdispater/poetry.git@master"
 
     assert expected == dependency.to_pep_508()
 
@@ -26,7 +26,7 @@ def test_to_pep_508_with_extras():
         "poetry", "git", "https://github.com/python-poetry/poetry.git", extras=["foo"]
     )
 
-    expected = "poetry[foo] @ git+https://github.com/python-poetry/poetry.git"
+    expected = "poetry[foo] @ git+https://github.com/python-poetry/poetry.git@master"
 
     assert expected == dependency.to_pep_508()
 
@@ -37,9 +37,7 @@ def test_to_pep_508_in_extras():
     )
     dependency.in_extras.append("foo")
 
-    expected = (
-        'poetry @ git+https://github.com/python-poetry/poetry.git ; extra == "foo"'
-    )
+    expected = 'poetry @ git+https://github.com/python-poetry/poetry.git@master ; extra == "foo"'
     assert expected == dependency.to_pep_508()
 
     dependency = VCSDependency(
@@ -47,9 +45,7 @@ def test_to_pep_508_in_extras():
     )
     dependency.in_extras.append("foo")
 
-    expected = (
-        'poetry[bar] @ git+https://github.com/python-poetry/poetry.git ; extra == "foo"'
-    )
+    expected = 'poetry[bar] @ git+https://github.com/python-poetry/poetry.git@master ; extra == "foo"'
 
     assert expected == dependency.to_pep_508()
 

From 8855eebfd3a16b8bbc0c493f83b31ccb7f5d4d63 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Sun, 28 Mar 2021 21:21:54 +0200
Subject: [PATCH 52/77] ci: add downstreap pipeline

---
 .github/workflows/downstream.yml | 70 ++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100644 .github/workflows/downstream.yml

diff --git a/.github/workflows/downstream.yml b/.github/workflows/downstream.yml
new file mode 100644
index 000000000..a6d854ad6
--- /dev/null
+++ b/.github/workflows/downstream.yml
@@ -0,0 +1,70 @@
+name: Poetry Downstream Tests
+
+on:
+  pull_request: {}
+  push:
+    branches: [master]
+
+jobs:
+  Tests:
+    name: ${{ matrix.os }} / ${{ matrix.python-version }}
+    runs-on: ubuntu-latest
+    continue-on-error: ${{ matrix.experimental }}
+    strategy:
+      matrix:
+        os: [Ubuntu]
+        python-version: [3.9]
+        experimental: [false]
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          path: poetry-core
+
+      - uses: actions/checkout@v2
+        with:
+          repository: python-poetry/poetry
+          path: poetry
+
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v2
+        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))")
+
+      - name: Install poetry
+        shell: bash
+        run: pip install poetry
+
+      - name: Configure poetry
+        shell: bash
+        run: poetry config virtualenvs.in-project true
+
+      - name: Set up cache
+        uses: actions/cache@v2
+        id: cache
+        with:
+          path: ./poetry/.venv
+          key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }}
+
+      - name: Ensure cache is healthy
+        if: steps.cache.outputs.cache-hit == 'true'
+        shell: bash
+        working-directory: ./poetry
+        run: timeout 10s poetry run pip --version >/dev/null 2>&1 || rm -rf .venv
+
+      - name: Update poetry-core version
+        shell: bash
+        working-directory: ./poetry
+        run: |
+          poetry add ../poetry-core
+          git diff
+
+      # TODO: mark run as success even when this fails and add comment to PR instead
+      - name: Run poetry test suite
+        shell: bash
+        working-directory: ./poetry
+        run: poetry run pytest

From 395f8324b8fcd055c516f9613532f4c9c7193670 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Tue, 30 Mar 2021 10:44:43 +0200
Subject: [PATCH 53/77] lark: lazy load parsers (#150)

---
 poetry/core/version/grammars/__init__.py | 10 +++++++++
 poetry/core/version/grammars/parser.py   | 20 -----------------
 poetry/core/version/markers.py           |  6 +++--
 poetry/core/version/parser.py            | 28 ++++++++++++++++++++++++
 poetry/core/version/pep440/parser.py     |  7 +++++-
 poetry/core/version/requirements.py      |  6 +++--
 6 files changed, 52 insertions(+), 25 deletions(-)
 delete mode 100644 poetry/core/version/grammars/parser.py
 create mode 100644 poetry/core/version/parser.py

diff --git a/poetry/core/version/grammars/__init__.py b/poetry/core/version/grammars/__init__.py
index e69de29bb..f94bf1e3b 100644
--- a/poetry/core/version/grammars/__init__.py
+++ b/poetry/core/version/grammars/__init__.py
@@ -0,0 +1,10 @@
+from pathlib import Path
+
+
+GRAMMAR_DIR = Path(__file__).parent
+
+GRAMMAR_PEP_440 = GRAMMAR_DIR / "pep440.lark"
+
+GRAMMAR_PEP_508_CONSTRAINTS = GRAMMAR_DIR / "pep508.lark"
+
+GRAMMAR_PEP_508_MARKERS = GRAMMAR_DIR / "markers.lark"
diff --git a/poetry/core/version/grammars/parser.py b/poetry/core/version/grammars/parser.py
deleted file mode 100644
index 2ccf244d0..000000000
--- a/poetry/core/version/grammars/parser.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from pathlib import Path
-
-from lark import Lark
-
-
-GRAMMAR_DIR = Path(__file__).parent
-
-# Parser: PEP 440
-# we use earley because the grammar is ambiguous
-PARSER_PEP_440 = Lark.open(GRAMMAR_DIR / "pep440.lark", parser="earley", debug=False)
-
-# Parser: PEP 508 Constraints
-PARSER_PEP_508_CONSTRAINTS = Lark.open(
-    GRAMMAR_DIR / "pep508.lark", parser="lalr", debug=False
-)
-
-# Parser: PEP 508 Environment Markers
-PARSER_PEP_508_MARKERS = Lark.open(
-    GRAMMAR_DIR / "markers.lark", parser="lalr", debug=False
-)
diff --git a/poetry/core/version/markers.py b/poetry/core/version/markers.py
index 7a05e42e5..93f5f1e34 100644
--- a/poetry/core/version/markers.py
+++ b/poetry/core/version/markers.py
@@ -7,7 +7,8 @@
 from typing import List
 from typing import Union
 
-from .grammars.parser import PARSER_PEP_508_MARKERS
+from .grammars import GRAMMAR_PEP_508_MARKERS
+from .parser import Parser
 
 
 if TYPE_CHECKING:
@@ -49,7 +50,8 @@ class UndefinedEnvironmentName(ValueError):
 }
 
 
-_parser = PARSER_PEP_508_MARKERS
+# Parser: PEP 508 Environment Markers
+_parser = Parser(GRAMMAR_PEP_508_MARKERS, "lalr")
 
 
 class BaseMarker(object):
diff --git a/poetry/core/version/parser.py b/poetry/core/version/parser.py
new file mode 100644
index 000000000..252736d56
--- /dev/null
+++ b/poetry/core/version/parser.py
@@ -0,0 +1,28 @@
+from pathlib import Path
+from typing import TYPE_CHECKING
+from typing import Optional
+
+
+if TYPE_CHECKING:
+    from lark import Lark
+    from lark import Tree
+
+
+class Parser:
+    def __init__(
+        self, grammar: Path, parser: str = "lalr", debug: bool = False
+    ) -> None:
+        self._grammar = grammar
+        self._parser = parser
+        self._debug = debug
+        self._lark: Optional["Lark"] = None
+
+    def parse(self, text: str, **kwargs) -> "Tree":
+        from lark import Lark
+
+        if self._lark is None:
+            self._lark = Lark.open(
+                grammar_filename=self._grammar, parser=self._parser, debug=self._debug
+            )
+
+        return self._lark.parse(text=text, **kwargs)
diff --git a/poetry/core/version/pep440/parser.py b/poetry/core/version/pep440/parser.py
index ea9eee308..ad963399f 100644
--- a/poetry/core/version/pep440/parser.py
+++ b/poetry/core/version/pep440/parser.py
@@ -7,7 +7,8 @@
 from lark import Transformer
 
 from poetry.core.version.exceptions import InvalidVersion
-from poetry.core.version.grammars.parser import PARSER_PEP_440
+from poetry.core.version.grammars import GRAMMAR_PEP_440
+from poetry.core.version.parser import Parser
 from poetry.core.version.pep440 import Release
 from poetry.core.version.pep440 import ReleaseTag
 
@@ -15,6 +16,10 @@
 if TYPE_CHECKING:
     from poetry.core.version.pep440.version import PEP440Version
 
+# Parser: PEP 440
+# we use earley because the grammar is ambiguous
+PARSER_PEP_440 = Parser(GRAMMAR_PEP_440, "earley", False)
+
 
 class _Transformer(Transformer):
     def NUMERIC_IDENTIFIER(self, data: "Token"):  # noqa
diff --git a/poetry/core/version/requirements.py b/poetry/core/version/requirements.py
index 2d6d1ce96..c1ed0fc2f 100644
--- a/poetry/core/version/requirements.py
+++ b/poetry/core/version/requirements.py
@@ -3,8 +3,9 @@
 from poetry.core.semver.exceptions import ParseConstraintError
 from poetry.core.semver.helpers import parse_constraint
 
-from .grammars.parser import PARSER_PEP_508_CONSTRAINTS
+from .grammars import GRAMMAR_PEP_508_CONSTRAINTS
 from .markers import _compact_markers
+from .parser import Parser
 
 
 class InvalidRequirement(ValueError):
@@ -13,7 +14,8 @@ class InvalidRequirement(ValueError):
     """
 
 
-_parser = PARSER_PEP_508_CONSTRAINTS
+# Parser: PEP 508 Constraints
+_parser = Parser(GRAMMAR_PEP_508_CONSTRAINTS, "lalr")
 
 
 class Requirement(object):

From b7ecfd1b68ebb2cd0a707d754d1fbe5a0ab377a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= 
Date: Mon, 29 Mar 2021 16:07:11 +0200
Subject: [PATCH 54/77] Update dependencies

---
 poetry.lock | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/poetry.lock b/poetry.lock
index f5970c175..e262d6acb 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -128,7 +128,7 @@ python-versions = "*"
 
 [[package]]
 name = "identify"
-version = "2.2.1"
+version = "2.2.2"
 description = "File identification library for Python"
 category = "dev"
 optional = false
@@ -633,8 +633,8 @@ filelock = [
     {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
 ]
 identify = [
-    {file = "identify-2.2.1-py2.py3-none-any.whl", hash = "sha256:9cc5f58996cd359b7b72f0a5917d8639de5323917e6952a3bfbf36301b576f40"},
-    {file = "identify-2.2.1.tar.gz", hash = "sha256:1cfb05b578de996677836d5a2dde14b3dffde313cf7d2b3e793a0787a36e26dd"},
+    {file = "identify-2.2.2-py2.py3-none-any.whl", hash = "sha256:c7c0f590526008911ccc5ceee6ed7b085cbc92f7b6591d0ee5913a130ad64034"},
+    {file = "identify-2.2.2.tar.gz", hash = "sha256:43cb1965e84cdd247e875dec6d13332ef5be355ddc16776396d98089b9053d87"},
 ]
 idna = [
     {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},

From 4d666b218ed014f74729d909350c754ad12d929b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= 
Date: Mon, 29 Mar 2021 18:10:50 +0200
Subject: [PATCH 55/77] Bump version to 1.1.0a1

---
 CHANGELOG.md            | 28 +++++++++++++++++++++++++++-
 poetry/core/__init__.py |  2 +-
 pyproject.toml          |  2 +-
 3 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0056c56d9..9214d9d73 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,30 @@
 # Change Log
 
+## [1.1.0a1] - 2021-03-30
+
+This version is the first to drop support for Python 2.7 and 3.5.
+
+If you are still using these versions you should update the `requires` property of the `build-system` section
+to restrict the version of `poetry-core`:
+
+```toml
+[build-system]
+requires = ["poetry-core<1.1.0"]
+build-backend = "poetry.core.masonry.api"
+```
+
+### Changed
+
+- Dropped support for Python 2.7 and 3.5 ([#131](https://github.com/python-poetry/poetry-core/pull/131)).
+- Reorganized imports internally to improve performances ([#131](https://github.com/python-poetry/poetry-core/pull/131)).
+- Directory dependencies are now in non-develop mode by default ([#98](https://github.com/python-poetry/poetry-core/pull/98)).
+- Improved support for PEP 440 specific versions that do not abide by semantic versioning ([#140](https://github.com/python-poetry/poetry-core/pull/140)).
+
+### Fixed
+
+- Fixed path dependencies PEP 508 representation ([#141](https://github.com/python-poetry/poetry-core/pull/141)).
+
+
 ## [1.0.2] - 2021-02-05
 
 ### Fixed
@@ -139,7 +164,8 @@ No changes.
 - Fixed support for stub-only packages ([#28](https://github.com/python-poetry/core/pull/28)).
 
 
-[Unreleased]: https://github.com/python-poetry/poetry-core/compare/1.0.2...master
+[Unreleased]: https://github.com/python-poetry/poetry-core/compare/1.1.0a1...master
+[1.1.0a1]: https://github.com/python-poetry/poetry-core/releases/tag/1.1.0a1
 [1.0.2]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.2
 [1.0.1]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.1
 [1.0.0]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.0
diff --git a/poetry/core/__init__.py b/poetry/core/__init__.py
index faa2c8436..8b1e52693 100644
--- a/poetry/core/__init__.py
+++ b/poetry/core/__init__.py
@@ -3,7 +3,7 @@
 from pathlib import Path
 
 
-__version__ = "1.1.0a0"
+__version__ = "1.1.0a1"
 
 __vendor_site__ = (Path(__file__).parent / "_vendor").as_posix()
 
diff --git a/pyproject.toml b/pyproject.toml
index 8095d9d97..d93419a65 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "poetry-core"
-version = "1.1.0a0"
+version = "1.1.0a1"
 description = "Poetry PEP 517 Build Backend"
 authors = ["Sébastien Eustace "]
 

From 8996496241ace59160411454f4a641ac391471f4 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Wed, 31 Mar 2021 10:33:25 +0200
Subject: [PATCH 56/77]  pep440: replace lark in favour of regex  (#152)

* tests: fix incorrect parameters

* pep440: replace lark in favour of regex

The lark early implementation of PEP440 that was introduced was not
performant. The implementation added on average around 7 seconds to
the test suite execution time.

This change drops the lark parser, and uses `packaging.version`
provided regex for version string parsing. New dataclass interface for
version instances remain unaffected.
---
 poetry/core/version/grammars/__init__.py |   2 -
 poetry/core/version/grammars/pep440.lark |  32 -----
 poetry/core/version/pep440/parser.py     | 145 +++++++++++------------
 tests/semver/test_version.py             |   6 +-
 4 files changed, 72 insertions(+), 113 deletions(-)
 delete mode 100644 poetry/core/version/grammars/pep440.lark

diff --git a/poetry/core/version/grammars/__init__.py b/poetry/core/version/grammars/__init__.py
index f94bf1e3b..1fdf5738c 100644
--- a/poetry/core/version/grammars/__init__.py
+++ b/poetry/core/version/grammars/__init__.py
@@ -3,8 +3,6 @@
 
 GRAMMAR_DIR = Path(__file__).parent
 
-GRAMMAR_PEP_440 = GRAMMAR_DIR / "pep440.lark"
-
 GRAMMAR_PEP_508_CONSTRAINTS = GRAMMAR_DIR / "pep508.lark"
 
 GRAMMAR_PEP_508_MARKERS = GRAMMAR_DIR / "markers.lark"
diff --git a/poetry/core/version/grammars/pep440.lark b/poetry/core/version/grammars/pep440.lark
deleted file mode 100644
index 62749f3b7..000000000
--- a/poetry/core/version/grammars/pep440.lark
+++ /dev/null
@@ -1,32 +0,0 @@
-// this is a modified version of the semver 2.0 specification grammar, specificially
-// crafted for use with Python PEP 440 version specifiers.
-start: version
-
-version: "v"? epoch? release pre_release? post_release? dev_release? ("+" local)?
-release: epoch? NUMERIC_IDENTIFIER (("." NUMERIC_IDENTIFIER)+)?
-
-major: NUMERIC_IDENTIFIER
-minor: NUMERIC_IDENTIFIER
-patch: NUMERIC_IDENTIFIER
-
-epoch: INT "!"
-
-pre_release: _SEPERATOR? PRE_RELEASE_TAG _SEPERATOR? NUMERIC_IDENTIFIER?
-PRE_RELEASE_TAG: "a" "lpha"? | "b" "eta"? | "c" | "rc" | "pre" "view"?
-
-post_release: "-" NUMERIC_IDENTIFIER | _SEPERATOR? POST_RELEASE_TAG _SEPERATOR? NUMERIC_IDENTIFIER?
-POST_RELEASE_TAG: "post" | "r" "ev"?
-
-dev_release: _SEPERATOR? DEV_RELEASE_TAG _SEPERATOR? NUMERIC_IDENTIFIER?
-DEV_RELEASE_TAG: "dev"
-
-local: LOCAL_IDENTIFIER ((_SEPERATOR LOCAL_IDENTIFIER)+)?
-LOCAL_IDENTIFIER: (LETTER | INT)+
-
-NUMERIC_IDENTIFIER: INT
-
-_SEPERATOR: "-" | "." | "_"
-
-%import common.LETTER
-%import common.DIGIT
-%import common.INT
diff --git a/poetry/core/version/pep440/parser.py b/poetry/core/version/pep440/parser.py
index ad963399f..5fb357094 100644
--- a/poetry/core/version/pep440/parser.py
+++ b/poetry/core/version/pep440/parser.py
@@ -1,14 +1,15 @@
+import re
+
 from typing import TYPE_CHECKING
-from typing import List
+from typing import AnyStr
+from typing import Match
 from typing import Optional
 from typing import Type
 
-from lark import LarkError
-from lark import Transformer
+from packaging.version import VERSION_PATTERN
 
 from poetry.core.version.exceptions import InvalidVersion
-from poetry.core.version.grammars import GRAMMAR_PEP_440
-from poetry.core.version.parser import Parser
+from poetry.core.version.pep440 import LocalSegmentType
 from poetry.core.version.pep440 import Release
 from poetry.core.version.pep440 import ReleaseTag
 
@@ -16,80 +17,72 @@
 if TYPE_CHECKING:
     from poetry.core.version.pep440.version import PEP440Version
 
-# Parser: PEP 440
-# we use earley because the grammar is ambiguous
-PARSER_PEP_440 = Parser(GRAMMAR_PEP_440, "earley", False)
-
-
-class _Transformer(Transformer):
-    def NUMERIC_IDENTIFIER(self, data: "Token"):  # noqa
-        return int(data.value)
-
-    def LOCAL_IDENTIFIER(self, data: "Token"):  # noqa
-        try:
-            return int(data.value)
-        except ValueError:
-            return data.value
-
-    def POST_RELEASE_TAG(self, data: "Token"):  # noqa
-        return data.value
-
-    def PRE_RELEASE_TAG(self, data: "Token"):  # noqa
-        return data.value
-
-    def DEV_RELEASE_TAG(self, data: "Token"):  # noqa
-        return data.value
-
-    def LOCAL(self, data: "Token"):  # noqa
-        return data.value
 
-    def INT(self, data: "Token"):  # noqa
-        return int(data.value)
-
-    def version(self, children: List["Tree"]):  # noqa
-        epoch, release, dev, pre, post, local = 0, None, None, None, None, None
-
-        for child in children:
-            if child.data == "epoch":
-                # epoch is always a single numeric value
-                epoch = child.children[0]
-            elif child.data == "release":
-                # release segment is of the form N(.N)*
-                release = Release.from_parts(*child.children)
-            elif child.data == "pre_release":
-                # pre-release tag is of the form (a|b|rc)N
-                pre = ReleaseTag(*child.children)
-            elif child.data == "post_release":
-                # post-release tags are of the form N (shortened) or post(N)*
-                if len(child.children) == 1 and isinstance(child.children[0], int):
-                    post = ReleaseTag("post", child.children[0])
-                else:
-                    post = ReleaseTag(*child.children)
-            elif child.data == "dev_release":
-                # dev-release tag is of the form dev(N)*
-                dev = ReleaseTag(*child.children)
-            elif child.data == "local":
-                local = tuple(child.children)
-
-        return epoch, release, pre, post, dev, local
-
-    def start(self, children: List["Tree"]):  # noqa
-        return children[0]
-
-
-_TRANSFORMER = _Transformer()
+class PEP440Parser:
+    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
+    _local_version_separators = re.compile(r"[._-]")
+
+    @classmethod
+    def _get_release(cls, match: Optional[Match[AnyStr]]) -> Release:
+        if not match or match.group("release") is None:
+            return Release(0)
+        return Release.from_parts(*(int(i) for i in match.group("release").split(".")))
+
+    @classmethod
+    def _get_prerelease(cls, match: Optional[Match[AnyStr]]) -> Optional[ReleaseTag]:
+        if not match or match.group("pre") is None:
+            return None
+        return ReleaseTag(match.group("pre_l"), int(match.group("pre_n") or 0))
+
+    @classmethod
+    def _get_postrelease(cls, match: Optional[Match[AnyStr]]) -> Optional[ReleaseTag]:
+        if not match or match.group("post") is None:
+            return None
+
+        return ReleaseTag(
+            match.group("post_l") or "post",
+            int(match.group("post_n1") or match.group("post_n2") or 0),
+        )
+
+    @classmethod
+    def _get_devrelease(cls, match: Optional[Match[AnyStr]]) -> Optional[ReleaseTag]:
+        if not match or match.group("dev") is None:
+            return None
+        return ReleaseTag(match.group("dev_l"), int(match.group("dev_n") or 0))
+
+    @classmethod
+    def _get_local(cls, match: Optional[Match[AnyStr]]) -> Optional[LocalSegmentType]:
+        if not match or match.group("local") is None:
+            return None
+
+        return tuple(
+            part.lower() if not part.isdigit() else int(part)
+            for part in cls._local_version_separators.split(match.group("local"))
+        )
+
+    @classmethod
+    def parse(cls, value: str, version_class: Optional[Type["PEP440Version"]] = None):
+        match = cls._regex.search(value) if value else None
+        if not match:
+            raise InvalidVersion(f"Invalid PEP 440 version: '{value}'")
+
+        if version_class is None:
+            from poetry.core.version.pep440.version import PEP440Version
+
+            version_class = PEP440Version
+
+        return version_class(
+            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
+            release=cls._get_release(match),
+            pre=cls._get_prerelease(match),
+            post=cls._get_postrelease(match),
+            dev=cls._get_devrelease(match),
+            local=cls._get_local(match),
+            text=value,
+        )
 
 
 def parse_pep440(
     value: str, version_class: Optional[Type["PEP440Version"]] = None
 ) -> "PEP440Version":
-    if version_class is None:
-        from poetry.core.version.pep440.version import PEP440Version
-
-        version_class = PEP440Version
-
-    try:
-        tree = PARSER_PEP_440.parse(text=value)
-        return version_class(*_TRANSFORMER.transform(tree), text=value)
-    except (TypeError, LarkError):
-        raise InvalidVersion(f"Invalid PEP 440 version: '{value}'")
+    return PEP440Parser.parse(value, version_class)
diff --git a/tests/semver/test_version.py b/tests/semver/test_version.py
index ad909f33d..499fafe4a 100644
--- a/tests/semver/test_version.py
+++ b/tests/semver/test_version.py
@@ -34,10 +34,10 @@ def test_parse_valid(text, version):
     assert parsed.text == text
 
 
-@pytest.mark.parametrize("input", [(None, "example")])
-def test_parse_invalid(input):
+@pytest.mark.parametrize("value", [None, "example"])
+def test_parse_invalid(value):
     with pytest.raises(InvalidVersion):
-        Version.parse(input)
+        Version.parse(value)
 
 
 @pytest.mark.parametrize(

From f5c6b648381c842223a3ea4ae28c4ea35458c7d3 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Fri, 2 Apr 2021 17:36:19 +0200
Subject: [PATCH 57/77] pep440: support upper bound post/local release
 comparisons (#157)

* tests: add coverage for poetry.core.version.pep440

* pep440: allow release tuples

* pep440: support post/local release comparisons

This change ensures that post and local releases are taken into
consideration when checking if version range allows a post release
local build release at upper and lower bounds.

The following conditions now hold for upper bound checks.

- `<=3.0.0` allows `3.0.0+local.1`, `3.0.0-1`
- `<=3.0.0+local.1` disallows `3.0.0+local.2`, allows `3.0.0-1`
- `<=3.0.0-1` allows `3.0.0+local.1`, `3.0.0`

Lower bound checks require no modification and works due to the
implicit version comparison of `poetry.core.pep440.PEP440Version`.
---
 poetry/core/semver/version_range.py   |  14 ++-
 poetry/core/version/pep440/version.py |  24 ++++
 tests/semver/test_version_range.py    | 140 +++++++++++++++++++++-
 tests/version/test_version_pep440.py  | 164 ++++++++++++++++++++++++++
 4 files changed, 337 insertions(+), 5 deletions(-)
 create mode 100644 tests/version/test_version_pep440.py

diff --git a/poetry/core/semver/version_range.py b/poetry/core/semver/version_range.py
index 6b77e58d1..e7860d95f 100644
--- a/poetry/core/semver/version_range.py
+++ b/poetry/core/semver/version_range.py
@@ -74,10 +74,20 @@ def allows(self, other: "Version") -> bool:
                 return False
 
         if self.full_max is not None:
-            if other > self.full_max:
+            _this, _other = self.full_max, other
+
+            if not _this.is_local() and _other.is_local():
+                # allow weak equality to allow `3.0.0+local.1` for `<=3.0.0`
+                _other = _other.without_local()
+
+            if not _this.is_postrelease() and _other.is_postrelease():
+                # allow weak equality to allow `3.0.0-1` for `<=3.0.0`
+                _other = _other.without_postrelease()
+
+            if _other > _this:
                 return False
 
-            if not self._include_max and other == self.full_max:
+            if not self._include_max and _other == _this:
                 return False
 
         return True
diff --git a/poetry/core/version/pep440/version.py b/poetry/core/version/pep440/version.py
index e84a87a72..d46a93822 100644
--- a/poetry/core/version/pep440/version.py
+++ b/poetry/core/version/pep440/version.py
@@ -36,6 +36,9 @@ def __post_init__(self):
         if self.local is not None and not isinstance(self.local, tuple):
             object.__setattr__(self, "local", (self.local,))
 
+        if isinstance(self.release, tuple):
+            object.__setattr__(self, "release", Release(*self.release))
+
         # we do this here to handle both None and tomlkit string values
         object.__setattr__(
             self, "text", self.to_string() if not self.text else str(self.text)
@@ -137,6 +140,9 @@ def is_postrelease(self) -> bool:
     def is_devrelease(self) -> bool:
         return self.dev is not None
 
+    def is_local(self) -> bool:
+        return self.local is not None
+
     def is_no_suffix_release(self) -> bool:
         return not (self.pre or self.post or self.dev)
 
@@ -200,3 +206,21 @@ def first_prerelease(self) -> "PEP440Version":
         return self.__class__(
             epoch=self.epoch, release=self.release, pre=ReleaseTag(RELEASE_PHASE_ALPHA)
         )
+
+    def replace(self, **kwargs):
+        return self.__class__(
+            **{
+                **{
+                    k: getattr(self, k)
+                    for k in self.__dataclass_fields__.keys()
+                    if k not in ("_compare_key", "text")
+                },  # setup defaults with current values, excluding compare keys and text
+                **kwargs,  # keys to replace
+            }
+        )
+
+    def without_local(self) -> "PEP440Version":
+        return self.replace(local=None)
+
+    def without_postrelease(self) -> "PEP440Version":
+        return self.replace(post=None)
diff --git a/tests/semver/test_version_range.py b/tests/semver/test_version_range.py
index e6e21e34c..d2363d5b4 100644
--- a/tests/semver/test_version_range.py
+++ b/tests/semver/test_version_range.py
@@ -75,7 +75,135 @@ def v300b1():
     return Version.parse("3.0.0b1")
 
 
-def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250, v300):
+@pytest.mark.parametrize(
+    "base,other",
+    [
+        pytest.param(Version.parse("3.0.0"), Version.parse("3.0.0-1"), id="post"),
+        pytest.param(
+            Version.parse("3.0.0"), Version.parse("3.0.0+local.1"), id="local"
+        ),
+    ],
+)
+def test_allows_post_releases_with_max(base, other):
+    range = VersionRange(max=base, include_max=True)
+    assert range.allows(other)
+
+
+@pytest.mark.parametrize(
+    "base,other",
+    [
+        pytest.param(Version.parse("3.0.0"), Version.parse("3.0.0-1"), id="post"),
+        pytest.param(
+            Version.parse("3.0.0"), Version.parse("3.0.0+local.1"), id="local"
+        ),
+    ],
+)
+def test_allows_post_releases_with_min(base, other):
+    range = VersionRange(min=base, include_min=True)
+    assert range.allows(other)
+
+
+def test_allows_post_releases_with_post_and_local_min():
+    one = Version.parse("3.0.0+local.1")
+    two = Version.parse("3.0.0-1")
+    three = Version.parse("3.0.0-1+local.1")
+    four = Version.parse("3.0.0+local.2")
+
+    assert VersionRange(min=one, include_min=True).allows(two)
+    assert VersionRange(min=one, include_min=True).allows(three)
+    assert VersionRange(min=one, include_min=True).allows(four)
+
+    assert not VersionRange(min=two, include_min=True).allows(one)
+    assert VersionRange(min=two, include_min=True).allows(three)
+    assert not VersionRange(min=two, include_min=True).allows(four)
+
+    assert not VersionRange(min=three, include_min=True).allows(one)
+    assert not VersionRange(min=three, include_min=True).allows(two)
+    assert not VersionRange(min=three, include_min=True).allows(four)
+
+    assert not VersionRange(min=four, include_min=True).allows(one)
+    assert VersionRange(min=four, include_min=True).allows(two)
+    assert VersionRange(min=four, include_min=True).allows(three)
+
+
+def test_allows_post_releases_with_post_and_local_max():
+    one = Version.parse("3.0.0+local.1")
+    two = Version.parse("3.0.0-1")
+    three = Version.parse("3.0.0-1+local.1")
+    four = Version.parse("3.0.0+local.2")
+
+    assert VersionRange(max=one, include_max=True).allows(two)
+    assert VersionRange(max=one, include_max=True).allows(three)
+    assert not VersionRange(max=one, include_max=True).allows(four)
+
+    assert VersionRange(max=two, include_max=True).allows(one)
+    assert VersionRange(max=two, include_max=True).allows(three)
+    assert VersionRange(max=two, include_max=True).allows(four)
+
+    assert VersionRange(max=three, include_max=True).allows(one)
+    assert VersionRange(max=three, include_max=True).allows(two)
+    assert VersionRange(max=three, include_max=True).allows(four)
+
+    assert VersionRange(max=four, include_max=True).allows(one)
+    assert VersionRange(max=four, include_max=True).allows(two)
+    assert VersionRange(max=four, include_max=True).allows(three)
+
+
+@pytest.mark.parametrize(
+    "base,one,two",
+    [
+        pytest.param(
+            Version.parse("3.0.0"),
+            Version.parse("3.0.0-1"),
+            Version.parse("3.0.0-2"),
+            id="post",
+        ),
+        pytest.param(
+            Version.parse("3.0.0"),
+            Version.parse("3.0.0+local.1"),
+            Version.parse("3.0.0+local.2"),
+            id="local",
+        ),
+    ],
+)
+def test_allows_post_releases_explicit_with_max(base, one, two):
+    range = VersionRange(max=one, include_max=True)
+    assert range.allows(base)
+    assert not range.allows(two)
+
+    range = VersionRange(max=two, include_max=True)
+    assert range.allows(base)
+    assert range.allows(one)
+
+
+@pytest.mark.parametrize(
+    "base,one,two",
+    [
+        pytest.param(
+            Version.parse("3.0.0"),
+            Version.parse("3.0.0-1"),
+            Version.parse("3.0.0-2"),
+            id="post",
+        ),
+        pytest.param(
+            Version.parse("3.0.0"),
+            Version.parse("3.0.0+local.1"),
+            Version.parse("3.0.0+local.2"),
+            id="local",
+        ),
+    ],
+)
+def test_allows_post_releases_explicit_with_min(base, one, two):
+    range = VersionRange(min=one, include_min=True)
+    assert not range.allows(base)
+    assert range.allows(two)
+
+    range = VersionRange(min=two, include_min=True)
+    assert not range.allows(base)
+    assert not range.allows(one)
+
+
+def test_allows_all(v123, v124, v140, v250, v300):
     assert VersionRange(v123, v250).allows_all(EmptyConstraint())
 
     range = VersionRange(v123, v250, include_max=True)
@@ -84,7 +212,8 @@ def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250,
     assert range.allows_all(v250)
     assert not range.allows_all(v300)
 
-    # with no min
+
+def test_allows_all_with_no_min(v080, v140, v250, v300):
     range = VersionRange(max=v250)
     assert range.allows_all(VersionRange(v080, v140))
     assert not range.allows_all(VersionRange(v080, v300))
@@ -93,7 +222,8 @@ def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250,
     assert range.allows_all(range)
     assert not range.allows_all(VersionRange())
 
-    # with no max
+
+def test_allows_all_with_no_max(v003, v010, v080, v140):
     range = VersionRange(min=v010)
     assert range.allows_all(VersionRange(v080, v140))
     assert not range.allows_all(VersionRange(v003, v140))
@@ -102,6 +232,8 @@ def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250,
     assert range.allows_all(range)
     assert not range.allows_all(VersionRange())
 
+
+def test_allows_all_bordering_range_not_more_inclusive(v010, v250):
     # Allows bordering range that is not more inclusive
     exclusive = VersionRange(v010, v250)
     inclusive = VersionRange(v010, v250, True, True)
@@ -110,6 +242,8 @@ def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250,
     assert not exclusive.allows_all(inclusive)
     assert exclusive.allows_all(exclusive)
 
+
+def test_allows_all_contained_unions(v010, v114, v123, v124, v140, v200, v234):
     # Allows unions that are completely contained
     range = VersionRange(v114, v200)
     assert range.allows_all(VersionRange(v123, v124).union(v140))
diff --git a/tests/version/test_version_pep440.py b/tests/version/test_version_pep440.py
new file mode 100644
index 000000000..11b873dac
--- /dev/null
+++ b/tests/version/test_version_pep440.py
@@ -0,0 +1,164 @@
+import pytest
+
+from poetry.core.version.exceptions import InvalidVersion
+from poetry.core.version.pep440 import PEP440Version
+from poetry.core.version.pep440 import Release
+from poetry.core.version.pep440 import ReleaseTag
+from poetry.core.version.pep440.segments import RELEASE_PHASES
+from poetry.core.version.pep440.segments import RELEASE_PHASES_SHORT
+
+
+@pytest.mark.parametrize(
+    "parts,result",
+    [
+        ((1,), Release(1)),
+        ((1, 2), Release(1, 2)),
+        ((1, 2, 3), Release(1, 2, 3)),
+        ((1, 2, 3, 4), Release(1, 2, 3, 4)),
+        ((1, 2, 3, 4, 5, 6), Release(1, 2, 3, (4, 5, 6))),
+    ],
+)
+def test_pep440_release_segment_from_parts(parts, result):
+    assert Release.from_parts(*parts) == result
+
+
+@pytest.mark.parametrize(
+    "parts,result",
+    [
+        (("a",), ReleaseTag("alpha", 0)),
+        (("a", 1), ReleaseTag("alpha", 1)),
+        (("b",), ReleaseTag("beta", 0)),
+        (("b", 1), ReleaseTag("beta", 1)),
+        (("pre",), ReleaseTag("preview", 0)),
+        (("pre", 1), ReleaseTag("preview", 1)),
+        (("c",), ReleaseTag("rc", 0)),
+        (("c", 1), ReleaseTag("rc", 1)),
+        (("r",), ReleaseTag("rev", 0)),
+        (("r", 1), ReleaseTag("rev", 1)),
+    ],
+)
+def test_pep440_release_tag_normalisation(parts, result):
+    tag = ReleaseTag(*parts)
+    assert tag == result
+    assert tag.to_string() == result.to_string()
+    assert tag.to_string(short=True) == result.to_string(short=True)
+
+
+@pytest.mark.parametrize(
+    "parts,result",
+    [
+        (("a",), ReleaseTag("beta")),
+        (("b",), ReleaseTag("rc")),
+        (("post",), None),
+        (("rc",), None),
+        (("rev",), None),
+        (("dev",), None),
+    ],
+)
+def test_pep440_release_tag_next_phase(parts, result):
+    assert ReleaseTag(*parts).next_phase() == result
+
+
+@pytest.mark.parametrize(
+    "phase", list({*RELEASE_PHASES.keys(), *RELEASE_PHASES_SHORT.keys()})
+)
+def test_pep440_release_tag_next(phase):
+    tag = ReleaseTag(phase=phase).next()
+    assert tag.phase == ReleaseTag.expand(phase)
+    assert tag.number == 1
+
+
+@pytest.mark.parametrize(
+    "text,result",
+    [
+        ("1", PEP440Version(release=Release.from_parts(1))),
+        ("1.2.3", PEP440Version(release=Release.from_parts(1, 2, 3))),
+        (
+            "1.2.3-1",
+            PEP440Version(
+                release=Release.from_parts(1, 2, 3), post=ReleaseTag("post", 1)
+            ),
+        ),
+        (
+            "1.2.3.dev1",
+            PEP440Version(
+                release=Release.from_parts(1, 2, 3), dev=ReleaseTag("dev", 1)
+            ),
+        ),
+        (
+            "1.2.3-1.dev1",
+            PEP440Version(
+                release=Release.from_parts(1, 2, 3),
+                post=ReleaseTag("post", 1),
+                dev=ReleaseTag("dev", 1),
+            ),
+        ),
+        (
+            "1.2.3+local",
+            PEP440Version(release=Release.from_parts(1, 2, 3), local="local"),
+        ),
+        (
+            "1.2.3+local.1",
+            PEP440Version(release=Release.from_parts(1, 2, 3), local=("local", 1)),
+        ),
+        (
+            "1.2.3+local1",
+            PEP440Version(release=Release.from_parts(1, 2, 3), local="local1"),
+        ),
+        ("1.2.3+1", PEP440Version(release=Release.from_parts(1, 2, 3), local=1)),
+        (
+            "1.2.3a1",
+            PEP440Version(
+                release=Release.from_parts(1, 2, 3), pre=ReleaseTag("alpha", 1)
+            ),
+        ),
+        (
+            "1.2.3.a1",
+            PEP440Version(
+                release=Release.from_parts(1, 2, 3), pre=ReleaseTag("alpha", 1)
+            ),
+        ),
+        (
+            "1.2.3alpha1",
+            PEP440Version(
+                release=Release.from_parts(1, 2, 3), pre=ReleaseTag("alpha", 1)
+            ),
+        ),
+        (
+            "1.2.3b1",
+            PEP440Version(
+                release=Release.from_parts(1, 2, 3), pre=ReleaseTag("beta", 1)
+            ),
+        ),
+        (
+            "1.2.3.b1",
+            PEP440Version(
+                release=Release.from_parts(1, 2, 3), pre=ReleaseTag("beta", 1)
+            ),
+        ),
+        (
+            "1.2.3beta1",
+            PEP440Version(
+                release=Release.from_parts(1, 2, 3), pre=ReleaseTag("beta", 1)
+            ),
+        ),
+        (
+            "1.2.3rc1",
+            PEP440Version(release=Release.from_parts(1, 2, 3), pre=ReleaseTag("rc", 1)),
+        ),
+        (
+            "1.2.3.rc1",
+            PEP440Version(release=Release.from_parts(1, 2, 3), pre=ReleaseTag("rc", 1)),
+        ),
+    ],
+)
+def test_pep440_parse_text(text, result):
+    assert PEP440Version.parse(text) == result
+
+
+@pytest.mark.parametrize(
+    "text", ["1.2.3.dev1-1" "example-1" "1.2.3-random1" "1.2.3-1-1"]
+)
+def test_pep440_parse_text_invalid_versions(text):
+    with pytest.raises(InvalidVersion):
+        assert PEP440Version.parse(text)

From 8d5e5c5d070940736ab72c5dec09cd7deab07cf3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= 
Date: Fri, 2 Apr 2021 14:58:52 +0200
Subject: [PATCH 58/77] Fix an error when handling single digit Python markers

---
 poetry/core/packages/dependency.py | 17 -----------------
 tests/packages/test_main.py        | 18 ++++++++++++++++++
 2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py
index b090aed77..20f9897fc 100644
--- a/poetry/core/packages/dependency.py
+++ b/poetry/core/packages/dependency.py
@@ -409,7 +409,6 @@ def create_from_pep_508(
         path is specified, this is used as the base directory if the identified dependency is
         of file or directory type.
         """
-        from poetry.core.semver.version import Version
         from poetry.core.utils.patterns import wheel_file_re
         from poetry.core.vcs.git import ParsedUrl
         from poetry.core.version.requirements import Requirement
@@ -546,22 +545,6 @@ def create_from_pep_508(
                         op = ""
                     elif op == "!=":
                         version += ".*"
-                    elif op in ("<=", ">"):
-                        parsed_version = Version.parse(version)
-                        if parsed_version.precision == 1:
-                            if op == "<=":
-                                op = "<"
-                                version = parsed_version.next_major().text
-                            elif op == ">":
-                                op = ">="
-                                version = parsed_version.next_major().text
-                        elif parsed_version.precision == 2:
-                            if op == "<=":
-                                op = "<"
-                                version = parsed_version.next_minor().text
-                            elif op == ">":
-                                op = ">="
-                                version = parsed_version.next_minor().text
                     elif op in ("in", "not in"):
                         versions = []
                         for v in re.split("[ ,]+", version):
diff --git a/tests/packages/test_main.py b/tests/packages/test_main.py
index d9cba1ac6..b8c1eed9c 100644
--- a/tests/packages/test_main.py
+++ b/tests/packages/test_main.py
@@ -1,4 +1,5 @@
 from poetry.core.packages.dependency import Dependency
+from poetry.core.semver.version import Version
 
 
 def test_dependency_from_pep_508():
@@ -254,3 +255,20 @@ def test_dependency_from_pep_508_with_python_full_version_pep440_compatible_rele
     assert dep.name == "pathlib2"
     assert str(dep.constraint) == "*"
     assert dep.python_versions == "~=3.4 || <3"
+
+
+def test_dependency_from_pep_508_should_not_produce_empty_constraints_for_correct_markers():
+    name = 'pytest-mypy; python_implementation != "PyPy" and python_version <= "3.10" and python_version > "3"'
+    dep = Dependency.create_from_pep_508(name)
+
+    assert dep.name == "pytest-mypy"
+    assert str(dep.constraint) == "*"
+    assert dep.python_versions == "<=3.10 >3"
+    assert dep.python_constraint.allows(Version.parse("3.6"))
+    assert dep.python_constraint.allows(Version.parse("3.10"))
+    assert not dep.python_constraint.allows(Version.parse("3"))
+    assert dep.python_constraint.allows(Version.parse("3.0.1"))
+    assert (
+        str(dep.marker)
+        == 'platform_python_implementation != "PyPy" and python_version <= "3.10" and python_version > "3"'
+    )

From 716375e87a60e2a0079fb1ba8e61c355e1cac793 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Thu, 8 Apr 2021 11:45:22 +0200
Subject: [PATCH 59/77] pep440: support post/local for semver allows (#158)

This change ensures that post and local releases are taken into
consideration when checking if semver version instance allows
post and local build releases.

The following conditions now hold `poetry.core.semver.Version.allows`.

- `3.0.0` allows `3.0.0+local.1`, `3.0.0-1`
- `3.0.0+local.1` disallows `3.0.0+local.2`, allows `3.0.0-1`
- `3.0.0-1` disallows ``3.0.0`, `3.0.0+local.1`, allows `3.0.0-1+local.1`
---
 poetry/core/semver/version.py | 27 +++++++++++++++++++++++----
 tests/semver/test_version.py  | 24 +++++++++++++++++++++++-
 2 files changed, 46 insertions(+), 5 deletions(-)

diff --git a/poetry/core/semver/version.py b/poetry/core/semver/version.py
index 6ed622fc9..3246d0f34 100644
--- a/poetry/core/semver/version.py
+++ b/poetry/core/semver/version.py
@@ -80,10 +80,29 @@ def is_empty(self) -> bool:
         return False
 
     def allows(self, version: "Version") -> bool:
-        return self == version
+        if version is None:
+            return False
+
+        _this, _other = self, version
+
+        # allow weak equality to allow `3.0.0+local.1` for `3.0.0`
+        if not _this.is_local() and _other.is_local():
+            _other = _other.without_local()
+        elif _this.is_local() and not _other.is_local():
+            _this = _this.without_local()
+
+        # allow weak equality to allow `3.0.0-1` for `3.0.0`
+        if not _this.is_postrelease() and _other.is_postrelease():
+            _other = _other.without_postrelease()
+        elif _this.without_postrelease() and not _other.without_postrelease():
+            _this = _this.without_postrelease()
+
+        return _this == _other
 
     def allows_all(self, other: "VersionTypes") -> bool:
-        return other.is_empty() or other == self
+        return other.is_empty() or (
+            self.allows(other) if isinstance(other, self.__class__) else other == self
+        )
 
     def allows_any(self, other: "VersionTypes") -> bool:
         return other.allows(self)
@@ -101,7 +120,7 @@ def union(self, other: "VersionTypes") -> "VersionTypes":
             return other
 
         if isinstance(other, VersionRangeConstraint):
-            if other.min == self:
+            if self.allows(other.min):
                 return VersionRange(
                     other.min,
                     other.max,
@@ -109,7 +128,7 @@ def union(self, other: "VersionTypes") -> "VersionTypes":
                     include_max=other.include_max,
                 )
 
-            if other.max == self:
+            if self.allows(other.max):
                 return VersionRange(
                     other.min,
                     other.max,
diff --git a/tests/semver/test_version.py b/tests/semver/test_version.py
index 499fafe4a..eb7734a69 100644
--- a/tests/semver/test_version.py
+++ b/tests/semver/test_version.py
@@ -113,7 +113,29 @@ def test_allows():
     assert not v.allows(Version.parse("1.3.3"))
     assert not v.allows(Version.parse("1.2.4"))
     assert not v.allows(Version.parse("1.2.3-dev"))
-    assert not v.allows(Version.parse("1.2.3+build"))
+    assert v.allows(Version.parse("1.2.3+build"))
+    assert v.allows(Version.parse("1.2.3-1"))
+    assert v.allows(Version.parse("1.2.3-1+build"))
+
+
+def test_allows_with_local():
+    v = Version.parse("1.2.3+build.1")
+    assert v.allows(v)
+    assert not v.allows(Version.parse("1.3.3"))
+    assert not v.allows(Version.parse("1.2.3-dev"))
+    assert not v.allows(Version.parse("1.2.3+build.2"))
+    assert v.allows(Version.parse("1.2.3-1"))
+    assert v.allows(Version.parse("1.2.3-1+build.1"))
+
+
+def test_allows_with_post():
+    v = Version.parse("1.2.3-1")
+    assert v.allows(v)
+    assert not v.allows(Version.parse("1.2.3"))
+    assert not v.allows(Version.parse("2.2.3"))
+    assert not v.allows(Version.parse("1.2.3-dev"))
+    assert not v.allows(Version.parse("1.2.3+build.2"))
+    assert v.allows(Version.parse("1.2.3-1+build.1"))
 
 
 def test_allows_all():

From 8290b1f905b31143b9ae3839437e95dfae042e5c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= 
Date: Fri, 2 Apr 2021 14:57:06 +0200
Subject: [PATCH 60/77] Bump version to 1.1.0a2

---
 CHANGELOG.md            | 12 +++++++++++-
 poetry/core/__init__.py |  2 +-
 pyproject.toml          |  2 +-
 3 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9214d9d73..b43a8584d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
 # Change Log
 
+## [1.1.0a2] - 2021-04-08
+
+### Fixed
+
+- Fixed performance regressions when parsing version constraints ([#152](https://github.com/python-poetry/poetry-core/pull/152)).
+- Fixed how local build versions are handled and compared ([#157](https://github.com/python-poetry/poetry-core/pull/157), [#158](https://github.com/python-poetry/poetry-core/pull/158)).
+- Fixed errors when parsing some environment markers ([#155](https://github.com/python-poetry/poetry-core/pull/155)).
+
+
 ## [1.1.0a1] - 2021-03-30
 
 This version is the first to drop support for Python 2.7 and 3.5.
@@ -164,7 +173,8 @@ No changes.
 - Fixed support for stub-only packages ([#28](https://github.com/python-poetry/core/pull/28)).
 
 
-[Unreleased]: https://github.com/python-poetry/poetry-core/compare/1.1.0a1...master
+[Unreleased]: https://github.com/python-poetry/poetry-core/compare/1.1.0a2...master
+[1.1.0a2]: https://github.com/python-poetry/poetry-core/releases/tag/1.1.0a2
 [1.1.0a1]: https://github.com/python-poetry/poetry-core/releases/tag/1.1.0a1
 [1.0.2]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.2
 [1.0.1]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.1
diff --git a/poetry/core/__init__.py b/poetry/core/__init__.py
index 8b1e52693..9446810b2 100644
--- a/poetry/core/__init__.py
+++ b/poetry/core/__init__.py
@@ -3,7 +3,7 @@
 from pathlib import Path
 
 
-__version__ = "1.1.0a1"
+__version__ = "1.1.0a2"
 
 __vendor_site__ = (Path(__file__).parent / "_vendor").as_posix()
 
diff --git a/pyproject.toml b/pyproject.toml
index d93419a65..28307d7b7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "poetry-core"
-version = "1.1.0a1"
+version = "1.1.0a2"
 description = "Poetry PEP 517 Build Backend"
 authors = ["Sébastien Eustace "]
 

From d3e60732ce9bd4f30dee3e594405fe6a80163b7e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= 
Date: Fri, 9 Apr 2021 11:51:47 +0200
Subject: [PATCH 61/77] Fix dependency markers not being copied in
 with_constraint()

---
 poetry/core/packages/dependency.py |  3 +++
 tests/packages/test_dependency.py  | 33 ++++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py
index 20f9897fc..6e06bc6fa 100644
--- a/poetry/core/packages/dependency.py
+++ b/poetry/core/packages/dependency.py
@@ -394,6 +394,9 @@ def with_constraint(self, constraint: Union[str, "VersionTypes"]) -> "Dependency
 
         new.is_root = self.is_root
         new.python_versions = self.python_versions
+        new.transitive_python_versions = self.transitive_python_versions
+        new.marker = self.marker
+        new.transitive_marker = self.transitive_marker
 
         for in_extra in self.in_extras:
             new.in_extras.append(in_extra)
diff --git a/tests/packages/test_dependency.py b/tests/packages/test_dependency.py
index 94ac58775..e849b1ebb 100644
--- a/tests/packages/test_dependency.py
+++ b/tests/packages/test_dependency.py
@@ -2,6 +2,7 @@
 
 from poetry.core.packages.dependency import Dependency
 from poetry.core.packages.package import Package
+from poetry.core.version.markers import parse_marker
 
 
 def test_accepts():
@@ -225,3 +226,35 @@ def test_complete_name():
 def test_dependency_string_representation(name, constraint, extras, expected):
     dependency = Dependency(name=name, constraint=constraint, extras=extras)
     assert str(dependency) == expected
+
+
+def test_with_constraint():
+    dependency = Dependency(
+        "foo",
+        "^1.2.3",
+        optional=True,
+        category="dev",
+        allows_prereleases=True,
+        extras=["bar", "baz"],
+    )
+    dependency.marker = parse_marker(
+        'python_version >= "3.6" and python_version < "4.0"'
+    )
+    dependency.transitive_marker = parse_marker(
+        'python_version >= "3.7" and python_version < "4.0"'
+    )
+    dependency.python_versions = "^3.6"
+    dependency.transitive_python_versions = "^3.7"
+
+    new = dependency.with_constraint("^1.2.6")
+
+    assert new.name == dependency.name
+    assert str(new.constraint) == ">=1.2.6,<2.0.0"
+    assert new.is_optional()
+    assert new.category == "dev"
+    assert new.allows_prereleases()
+    assert set(new.extras) == {"bar", "baz"}
+    assert new.marker == dependency.marker
+    assert new.transitive_marker == dependency.transitive_marker
+    assert new.python_constraint == dependency.python_constraint
+    assert new.transitive_python_constraint == dependency.transitive_python_constraint

From a01c556fb6591b4f5056ef526315f7cd8d95b9c9 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Fri, 9 Apr 2021 17:27:01 +0200
Subject: [PATCH 62/77] deps: relax compatibility requirement constraints
 (#164)

---
 poetry.lock    | 119 +++++++++++++++++++++++++------------------------
 pyproject.toml |   4 +-
 2 files changed, 62 insertions(+), 61 deletions(-)

diff --git a/poetry.lock b/poetry.lock
index e262d6acb..16d80cc39 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -147,18 +147,19 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
 [[package]]
 name = "importlib-metadata"
-version = "1.7.0"
+version = "3.10.0"
 description = "Read metadata from Python packages"
 category = "main"
 optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+python-versions = ">=3.6"
 
 [package.dependencies]
+typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
 zipp = ">=0.5"
 
 [package.extras]
-docs = ["sphinx", "rst.linker"]
-testing = ["packaging", "pep517", "importlib-resources (>=1.3)"]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
 
 [[package]]
 name = "importlib-resources"
@@ -223,7 +224,7 @@ python-versions = "*"
 
 [[package]]
 name = "nodeenv"
-version = "1.5.0"
+version = "1.6.0"
 description = "Node.js virtual environment builder"
 category = "dev"
 optional = false
@@ -277,7 +278,7 @@ dev = ["pre-commit", "tox"]
 
 [[package]]
 name = "pre-commit"
-version = "2.11.1"
+version = "2.12.0"
 description = "A framework for managing and maintaining multi-language pre-commit hooks."
 category = "dev"
 optional = false
@@ -322,7 +323,7 @@ six = "*"
 
 [[package]]
 name = "pytest"
-version = "6.2.2"
+version = "6.2.3"
 description = "pytest: simple powerful testing with Python"
 category = "dev"
 optional = false
@@ -381,7 +382,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
 
 [[package]]
 name = "regex"
-version = "2021.3.17"
+version = "2021.4.4"
 description = "Alternative regular expression module, to replace re."
 category = "dev"
 optional = false
@@ -456,7 +457,7 @@ python-versions = "*"
 name = "typing-extensions"
 version = "3.7.4.3"
 description = "Backported and Experimental Type Hints for Python 3.5+"
-category = "dev"
+category = "main"
 optional = false
 python-versions = "*"
 
@@ -527,7 +528,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.6"
-content-hash = "f454025163c37b630e04ee93ceea5ad23471dcfc38127e3d7259deac52bee0f6"
+content-hash = "b32f0571590c57077d864e4bca6adef82d6af64e7654215319d43a04a4d30128"
 
 [metadata.files]
 appdirs = [
@@ -641,8 +642,8 @@ idna = [
     {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
 ]
 importlib-metadata = [
-    {file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"},
-    {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"},
+    {file = "importlib_metadata-3.10.0-py3-none-any.whl", hash = "sha256:d2d46ef77ffc85cbf7dac7e81dd663fde71c45326131bea8033b9bad42268ebe"},
+    {file = "importlib_metadata-3.10.0.tar.gz", hash = "sha256:c9db46394197244adf2f0b08ec5bc3cf16757e9590b02af1fca085c16c0d600a"},
 ]
 importlib-resources = [
     {file = "importlib_resources-5.1.2-py3-none-any.whl", hash = "sha256:ebab3efe74d83b04d6bf5cd9a17f0c5c93e60fb60f30c90f56265fce4682a469"},
@@ -665,8 +666,8 @@ mypy-extensions = [
     {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
 ]
 nodeenv = [
-    {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"},
-    {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"},
+    {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"},
+    {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"},
 ]
 packaging = [
     {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
@@ -685,8 +686,8 @@ pluggy = [
     {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
 ]
 pre-commit = [
-    {file = "pre_commit-2.11.1-py2.py3-none-any.whl", hash = "sha256:94c82f1bf5899d56edb1d926732f4e75a7df29a0c8c092559c77420c9d62428b"},
-    {file = "pre_commit-2.11.1.tar.gz", hash = "sha256:de55c5c72ce80d79106e48beb1b54104d16495ce7f95b0c7b13d4784193a00af"},
+    {file = "pre_commit-2.12.0-py2.py3-none-any.whl", hash = "sha256:029d53cb83c241fe7d66eeee1e24db426f42c858f15a38d20bcefd8d8e05c9da"},
+    {file = "pre_commit-2.12.0.tar.gz", hash = "sha256:46b6ffbab37986c47d0a35e40906ae029376deed89a0eb2e446fb6e67b220427"},
 ]
 py = [
     {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
@@ -700,8 +701,8 @@ pyrsistent = [
     {file = "pyrsistent-0.16.1.tar.gz", hash = "sha256:aa2ae1c2e496f4d6777f869ea5de7166a8ccb9c2e06ebcf6c7ff1b670c98c5ef"},
 ]
 pytest = [
-    {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"},
-    {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"},
+    {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"},
+    {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"},
 ]
 pytest-cov = [
     {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"},
@@ -735,47 +736,47 @@ pyyaml = [
     {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
 ]
 regex = [
-    {file = "regex-2021.3.17-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b97ec5d299c10d96617cc851b2e0f81ba5d9d6248413cd374ef7f3a8871ee4a6"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:cb4ee827857a5ad9b8ae34d3c8cc51151cb4a3fe082c12ec20ec73e63cc7c6f0"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:633497504e2a485a70a3268d4fc403fe3063a50a50eed1039083e9471ad0101c"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:a59a2ee329b3de764b21495d78c92ab00b4ea79acef0f7ae8c1067f773570afa"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f85d6f41e34f6a2d1607e312820971872944f1661a73d33e1e82d35ea3305e14"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:4651f839dbde0816798e698626af6a2469eee6d9964824bb5386091255a1694f"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:39c44532d0e4f1639a89e52355b949573e1e2c5116106a395642cbbae0ff9bcd"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3d9a7e215e02bd7646a91fb8bcba30bc55fd42a719d6b35cf80e5bae31d9134e"},
-    {file = "regex-2021.3.17-cp36-cp36m-win32.whl", hash = "sha256:159fac1a4731409c830d32913f13f68346d6b8e39650ed5d704a9ce2f9ef9cb3"},
-    {file = "regex-2021.3.17-cp36-cp36m-win_amd64.whl", hash = "sha256:13f50969028e81765ed2a1c5fcfdc246c245cf8d47986d5172e82ab1a0c42ee5"},
-    {file = "regex-2021.3.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9d8d286c53fe0cbc6d20bf3d583cabcd1499d89034524e3b94c93a5ab85ca90"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:201e2619a77b21a7780580ab7b5ce43835e242d3e20fef50f66a8df0542e437f"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d47d359545b0ccad29d572ecd52c9da945de7cd6cf9c0cfcb0269f76d3555689"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ea2f41445852c660ba7c3ebf7d70b3779b20d9ca8ba54485a17740db49f46932"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:486a5f8e11e1f5bbfcad87f7c7745eb14796642323e7e1829a331f87a713daaa"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:18e25e0afe1cf0f62781a150c1454b2113785401ba285c745acf10c8ca8917df"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:a2ee026f4156789df8644d23ef423e6194fad0bc53575534101bb1de5d67e8ce"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:4c0788010a93ace8a174d73e7c6c9d3e6e3b7ad99a453c8ee8c975ddd9965643"},
-    {file = "regex-2021.3.17-cp37-cp37m-win32.whl", hash = "sha256:575a832e09d237ae5fedb825a7a5bc6a116090dd57d6417d4f3b75121c73e3be"},
-    {file = "regex-2021.3.17-cp37-cp37m-win_amd64.whl", hash = "sha256:8e65e3e4c6feadf6770e2ad89ad3deb524bcb03d8dc679f381d0568c024e0deb"},
-    {file = "regex-2021.3.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a0df9a0ad2aad49ea3c7f65edd2ffb3d5c59589b85992a6006354f6fb109bb18"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b98bc9db003f1079caf07b610377ed1ac2e2c11acc2bea4892e28cc5b509d8d5"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:808404898e9a765e4058bf3d7607d0629000e0a14a6782ccbb089296b76fa8fe"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:5770a51180d85ea468234bc7987f5597803a4c3d7463e7323322fe4a1b181578"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:976a54d44fd043d958a69b18705a910a8376196c6b6ee5f2596ffc11bff4420d"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:63f3ca8451e5ff7133ffbec9eda641aeab2001be1a01878990f6c87e3c44b9d5"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bcd945175c29a672f13fce13a11893556cd440e37c1b643d6eeab1988c8b209c"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:3d9356add82cff75413bec360c1eca3e58db4a9f5dafa1f19650958a81e3249d"},
-    {file = "regex-2021.3.17-cp38-cp38-win32.whl", hash = "sha256:f5d0c921c99297354cecc5a416ee4280bd3f20fd81b9fb671ca6be71499c3fdf"},
-    {file = "regex-2021.3.17-cp38-cp38-win_amd64.whl", hash = "sha256:14de88eda0976020528efc92d0a1f8830e2fb0de2ae6005a6fc4e062553031fa"},
-    {file = "regex-2021.3.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4c2e364491406b7888c2ad4428245fc56c327e34a5dfe58fd40df272b3c3dab3"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux1_i686.whl", hash = "sha256:8bd4f91f3fb1c9b1380d6894bd5b4a519409135bec14c0c80151e58394a4e88a"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:882f53afe31ef0425b405a3f601c0009b44206ea7f55ee1c606aad3cc213a52c"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:07ef35301b4484bce843831e7039a84e19d8d33b3f8b2f9aab86c376813d0139"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:360a01b5fa2ad35b3113ae0c07fb544ad180603fa3b1f074f52d98c1096fa15e"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:709f65bb2fa9825f09892617d01246002097f8f9b6dde8d1bb4083cf554701ba"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:c66221e947d7207457f8b6f42b12f613b09efa9669f65a587a2a71f6a0e4d106"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:c782da0e45aff131f0bed6e66fbcfa589ff2862fc719b83a88640daa01a5aff7"},
-    {file = "regex-2021.3.17-cp39-cp39-win32.whl", hash = "sha256:dc9963aacb7da5177e40874585d7407c0f93fb9d7518ec58b86e562f633f36cd"},
-    {file = "regex-2021.3.17-cp39-cp39-win_amd64.whl", hash = "sha256:a0d04128e005142260de3733591ddf476e4902c0c23c1af237d9acf3c96e1b38"},
-    {file = "regex-2021.3.17.tar.gz", hash = "sha256:4b8a1fb724904139149a43e172850f35aa6ea97fb0545244dc0b805e0154ed68"},
+    {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"},
+    {file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"},
+    {file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"},
+    {file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"},
+    {file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"},
+    {file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"},
+    {file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"},
+    {file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"},
+    {file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"},
+    {file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"},
+    {file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"},
+    {file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"},
+    {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"},
 ]
 requests = [
     {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
diff --git a/pyproject.toml b/pyproject.toml
index 28307d7b7..e81749ff9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -33,8 +33,8 @@ exclude = [
 python = "^3.6"
 
 # required for compatibility
-importlib-metadata = {version = "^1.7.0", python = ">=3.5, <3.8"}
-dataclasses = {version = "^0.8", python = "~3.6"}
+importlib-metadata = {version = ">=1.7.0", python = "<3.8"}
+dataclasses = {version = ">=0.8", python = "~3.6"}
 
 [tool.poetry.dev-dependencies]
 pre-commit = {version = "^2.10.0", python = "^3.6.1"}

From 871e10e81b858c3758434465c0886f9eac805606 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= 
Date: Fri, 9 Apr 2021 17:37:15 +0200
Subject: [PATCH 63/77] Bump version to 1.1.0a3

---
 CHANGELOG.md            | 10 +++++++++-
 poetry/core/__init__.py |  2 +-
 pyproject.toml          |  2 +-
 3 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b43a8584d..846a8d51c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Change Log
 
+## [1.1.0a3] - 2021-04-09
+
+### Fixed
+
+- Fixed dependency markers not being properly copied when changing the constraint ([#162](https://github.com/python-poetry/poetry-core/pull/162)).
+
+
 ## [1.1.0a2] - 2021-04-08
 
 ### Fixed
@@ -173,7 +180,8 @@ No changes.
 - Fixed support for stub-only packages ([#28](https://github.com/python-poetry/core/pull/28)).
 
 
-[Unreleased]: https://github.com/python-poetry/poetry-core/compare/1.1.0a2...master
+[Unreleased]: https://github.com/python-poetry/poetry-core/compare/1.1.0a3...master
+[1.1.0a3]: https://github.com/python-poetry/poetry-core/releases/tag/1.1.0a3
 [1.1.0a2]: https://github.com/python-poetry/poetry-core/releases/tag/1.1.0a2
 [1.1.0a1]: https://github.com/python-poetry/poetry-core/releases/tag/1.1.0a1
 [1.0.2]: https://github.com/python-poetry/poetry-core/releases/tag/1.0.2
diff --git a/poetry/core/__init__.py b/poetry/core/__init__.py
index 9446810b2..7bbf31b47 100644
--- a/poetry/core/__init__.py
+++ b/poetry/core/__init__.py
@@ -3,7 +3,7 @@
 from pathlib import Path
 
 
-__version__ = "1.1.0a2"
+__version__ = "1.1.0a3"
 
 __vendor_site__ = (Path(__file__).parent / "_vendor").as_posix()
 
diff --git a/pyproject.toml b/pyproject.toml
index e81749ff9..8077f32ec 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "poetry-core"
-version = "1.1.0a2"
+version = "1.1.0a3"
 description = "Poetry PEP 517 Build Backend"
 authors = ["Sébastien Eustace "]
 

From d3e51399d1f637ad3e52ff731625d466dedeb64b Mon Sep 17 00:00:00 2001
From: Steph Samson 
Date: Sun, 11 Apr 2021 00:41:38 +0200
Subject: [PATCH 64/77] pep440: preserve leading zeros in local segment

Resolves: python-poetry/poetry#3705
---
 poetry/core/version/pep440/parser.py  | 2 +-
 poetry/core/version/pep440/version.py | 4 +++-
 tests/version/test_version_pep440.py  | 8 ++++++++
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/poetry/core/version/pep440/parser.py b/poetry/core/version/pep440/parser.py
index 5fb357094..08962c53a 100644
--- a/poetry/core/version/pep440/parser.py
+++ b/poetry/core/version/pep440/parser.py
@@ -56,7 +56,7 @@ def _get_local(cls, match: Optional[Match[AnyStr]]) -> Optional[LocalSegmentType
             return None
 
         return tuple(
-            part.lower() if not part.isdigit() else int(part)
+            part.lower()
             for part in cls._local_version_separators.split(match.group("local"))
         )
 
diff --git a/poetry/core/version/pep440/version.py b/poetry/core/version/pep440/version.py
index d46a93822..2837bd195 100644
--- a/poetry/core/version/pep440/version.py
+++ b/poetry/core/version/pep440/version.py
@@ -81,7 +81,9 @@ def _make_compare_key(self):
             # - Shorter versions sort before longer versions when the prefixes
             #   match exactly
             _local = tuple(
-                (i, "") if isinstance(i, int) else (-math.inf, i) for i in self.local
+                # We typecast strings that are integers so that they can be compared
+                (int(i), "") if str(i).isnumeric() else (-math.inf, i)
+                for i in self.local
             )
         return self.epoch, self.release, _pre, _post, _dev, _local
 
diff --git a/tests/version/test_version_pep440.py b/tests/version/test_version_pep440.py
index 11b873dac..029b5f5dc 100644
--- a/tests/version/test_version_pep440.py
+++ b/tests/version/test_version_pep440.py
@@ -150,6 +150,14 @@ def test_pep440_release_tag_next(phase):
             "1.2.3.rc1",
             PEP440Version(release=Release.from_parts(1, 2, 3), pre=ReleaseTag("rc", 1)),
         ),
+        (
+            "2.2.0dev0+build.05669607",
+            PEP440Version(
+                release=Release.from_parts(2, 2, 0),
+                dev=ReleaseTag("dev", 0),
+                local=("build", "05669607"),
+            ),
+        ),
     ],
 )
 def test_pep440_parse_text(text, result):

From bddf441f8418f69b88afbc4b6abec4ad4fed9420 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Sun, 11 Apr 2021 01:09:03 +0200
Subject: [PATCH 65/77] ci: update test scripts to use install-poetry.py

This change also fixes python 3.10 failures.
---
 .github/workflows/tests.yml | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 20c68cafb..e91a4a846 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -6,15 +6,16 @@ on:
     branches: [master]
 
 jobs:
-  Tests:
+  tests:
     name: ${{ matrix.os }} / ${{ matrix.python-version }}
-    runs-on: ${{ matrix.os }}-latest
+    runs-on: "${{ matrix.os }}-latest"
     continue-on-error: ${{ matrix.experimental }}
     strategy:
       matrix:
         os: [Ubuntu, MacOS, Windows]
         python-version: [3.6, 3.7, 3.8, 3.9]
         experimental: [false]
+        bootstrap-args: [""]
         include:
           - os: Ubuntu
             python-version: pypy3
@@ -22,6 +23,8 @@ jobs:
           - os: Ubuntu
             python-version: "3.10.0-alpha - 3.10.0"
             experimental: true
+            bootstrap-args: "--git https://github.com/python-poetry/poetry.git"
+      fail-fast: false
     steps:
       - uses: actions/checkout@v2
 
@@ -30,21 +33,33 @@ jobs:
         with:
           python-version: ${{ matrix.python-version }}
 
-      - name: Get full 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))")
 
-      - name: Install poetry
+      - name: Bootstrap poetry
         shell: bash
-        run: pip install poetry
+        run: |
+          curl -sL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py \
+            | python - -y ${{ matrix.bootstrap-args }}
+
+      - name: Update PATH
+        if: ${{ matrix.os != 'Windows' }}
+        shell: bash
+        run: echo "$HOME/.local/bin" >> $GITHUB_PATH
+
+      - name: Update Path for Windows
+        if: ${{ matrix.os == 'Windows' }}
+        shell: bash
+        run: echo "$APPDATA\Python\Scripts" >> $GITHUB_PATH
 
       - name: Configure poetry
         shell: bash
         run: poetry config virtualenvs.in-project true
 
       - name: Set up cache
-        uses: actions/cache@v1
+        uses: actions/cache@v2
         id: cache
         with:
           path: .venv
@@ -53,7 +68,7 @@ jobs:
       - name: Ensure cache is healthy
         if: steps.cache.outputs.cache-hit == 'true'
         shell: bash
-        run: timeout 10s poetry run pip --version >/dev/null 2>&1 || rm -rf .venv
+        run: timeout 10s poetry run pip --version || rm -rf .venv
 
       - name: Install dependencies
         shell: bash
@@ -61,4 +76,4 @@ jobs:
 
       - name: Run pytest
         shell: bash
-        run: poetry run pytest -q tests
+        run: poetry run python -m pytest -p no:sugar -q tests/

From 015e05df84cec2a42dfe0701ad83c036a433a474 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Sun, 11 Apr 2021 01:09:27 +0200
Subject: [PATCH 66/77] ci: cleanup release workflow

---
 .github/workflows/release.yml | 139 ++++++++--------------------------
 1 file changed, 31 insertions(+), 108 deletions(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 27ca81553..d612e39d6 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -6,125 +6,48 @@ on:
       - '*.*.*'
 
 jobs:
-
-  Linux:
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: actions/checkout@v2
-    - name: Get tag
-      id: tag
-      run: |
-        echo ::set-output name=tag::${GITHUB_REF#refs/tags/}
-    - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v1
-      with:
-        python-version: 3.8
-    - name: Install and set up Poetry
-      run: |
-        curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py
-        python get-poetry.py --preview -y
-    - name: Build distributions
-      run: |
-        source $HOME/.poetry/env
-        poetry build -vvv
-    - name: Upload distribution artifacts
-      uses: actions/upload-artifact@v1
-      with:
-        name: pendulum-dist
-        path: dist
-
-  MacOS:
-    runs-on: macos-latest
-
-    steps:
-    - uses: actions/checkout@v2
-    - name: Get tag
-      id: tag
-      run: |
-        echo ::set-output name=tag::${GITHUB_REF#refs/tags/}
-    - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v1
-      with:
-        python-version: 3.8
-    - name: Install and set up Poetry
-      run: |
-        curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py
-        python get-poetry.py --preview -y
-    - name: Build distributions
-      run: |
-        source $HOME/.poetry/env
-        poetry build -vvv
-    - name: Upload distribution artifacts
-      uses: actions/upload-artifact@v1
-      with:
-        name: pendulum-dist
-        path: dist
-
-  Windows:
-    runs-on: windows-latest
-
-    steps:
-    - uses: actions/checkout@v2
-    - name: Get tag
-      id: tag
-      shell: bash
-      run: |
-        echo ::set-output name=tag::${GITHUB_REF#refs/tags/}
-    - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v1
-      with:
-        python-version: 3.8
-    - name: Install and setup Poetry
-      run: |
-        Invoke-WebRequest https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py -O get-poetry.py
-        python get-poetry.py --preview -y
-    - name: Build distributions
-      run: |
-        $env:Path += ";$env:Userprofile\.poetry\bin"
-        poetry build -vvv
-    - name: Upload distribution artifact
-      uses: actions/upload-artifact@v1
-      with:
-        name: pendulum-dist
-        path: dist
-
   Release:
-    needs: [Linux, MacOS, Windows]
     runs-on: ubuntu-latest
 
     steps:
       - name: Checkout code
         uses: actions/checkout@v2
+
       - name: Get tag
         id: tag
-        run: |
-          echo ::set-output name=tag::${GITHUB_REF#refs/tags/}
-      - name: Download distribution artifact
-        uses: actions/download-artifact@master
+        run: echo ::set-output name=tag::${GITHUB_REF#refs/tags/}
+
+      - name: Set up Python 3.9
+        uses: actions/setup-python@v2
         with:
-          name: pendulum-dist
-          path: dist
-      - name: Install and set up Poetry
-        run: |
-          curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py
-          python get-poetry.py --preview -y
-      - name: Check distributions
+          python-version: "3.9"
+
+      - name: Install Poetry
         run: |
-          ls -la dist
-      - name: Publish to PyPI
-        env:
-          POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
+          curl -sL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py \
+            | python - -y
+
+      - name: Update PATH
+        run: echo "$HOME/.local/bin" >> $GITHUB_PATH
+
+      - name: Build project for distribution
+        run: poetry build
+
+      - name: Check Version
+        id: check-version
         run: |
-          source $HOME/.poetry/env
-          poetry publish
+          [[ "$(poetry version --short)" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] \
+            || echo ::set-output name=prerelease::true
+
       - name: Create Release
-        id: create_release
-        uses: actions/create-release@v1
-        env:
-          GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
+        uses: ncipollo/release-action@v1
         with:
-          tag_name: ${{ steps.tag.outputs.tag }}
-          release_name: ${{ steps.tag.outputs.tag }}
+          artifacts: "dist/*"
+          token: ${{ secrets.GITHUB_TOKEN }}
           draft: false
-          prerelease: false
+          prerelease: steps.check-version.outputs.prerelease == 'true'
+
+      - name: Publish to PyPI
+        env:
+          POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
+        run: poetry publish

From f478ec31412a7f9e22df99e17e337e24a61cc989 Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Mon, 22 Mar 2021 01:55:07 +0100
Subject: [PATCH 67/77] Use deterministic time for generated sdist files

When generating setup.py and PKG-INFO files, ensure that generated
files use a deterministic timestamp to enhance reproducibility of
source distributions.
---
 poetry/core/masonry/builders/sdist.py |  7 ++++---
 tests/masonry/builders/test_sdist.py  | 26 ++++++++++++++++++++++++++
 2 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/poetry/core/masonry/builders/sdist.py b/poetry/core/masonry/builders/sdist.py
index add8dcd27..e0438cd10 100644
--- a/poetry/core/masonry/builders/sdist.py
+++ b/poetry/core/masonry/builders/sdist.py
@@ -2,7 +2,6 @@
 import os
 import re
 import tarfile
-import time
 
 from collections import defaultdict
 from contextlib import contextmanager
@@ -97,14 +96,16 @@ def build(self, target_dir: Optional[Path] = None) -> Path:
                 setup = self.build_setup()
                 tar_info = tarfile.TarInfo(pjoin(tar_dir, "setup.py"))
                 tar_info.size = len(setup)
-                tar_info.mtime = time.time()
+                tar_info.mtime = 0
+                tar_info = self.clean_tarinfo(tar_info)
                 tar.addfile(tar_info, BytesIO(setup))
 
             pkg_info = self.build_pkg_info()
 
             tar_info = tarfile.TarInfo(pjoin(tar_dir, "PKG-INFO"))
             tar_info.size = len(pkg_info)
-            tar_info.mtime = time.time()
+            tar_info.mtime = 0
+            tar_info = self.clean_tarinfo(tar_info)
             tar.addfile(tar_info, BytesIO(pkg_info))
         finally:
             tar.close()
diff --git a/tests/masonry/builders/test_sdist.py b/tests/masonry/builders/test_sdist.py
index b67be206c..effebd763 100644
--- a/tests/masonry/builders/test_sdist.py
+++ b/tests/masonry/builders/test_sdist.py
@@ -1,5 +1,6 @@
 import ast
 import gzip
+import hashlib
 import shutil
 import tarfile
 
@@ -250,6 +251,24 @@ def test_package():
         assert "my-package-1.2.3/LICENSE" in tar.getnames()
 
 
+def test_sdist_reproducibility():
+    poetry = Factory().create_poetry(project("complete"))
+
+    hashes = set()
+
+    for _ in range(2):
+        builder = SdistBuilder(poetry)
+        builder.build()
+
+        sdist = fixtures_dir / "complete" / "dist" / "my-package-1.2.3.tar.gz"
+
+        assert sdist.exists()
+
+        hashes.add(hashlib.sha256(sdist.read_bytes()).hexdigest())
+
+    assert len(hashes) == 1
+
+
 def test_setup_py_context():
     poetry = Factory().create_poetry(project("complete"))
 
@@ -438,6 +457,13 @@ def test_default_with_excluded_data(mocker):
         assert "my-package-1.2.3/PKG-INFO" in names
         # all last modified times should be set to a valid timestamp
         for tarinfo in tar.getmembers():
+            if tarinfo.name in [
+                "my-package-1.2.3/setup.py",
+                "my-package-1.2.3/PKG-INFO",
+            ]:
+                # generated files have timestamp set to 0
+                assert 0 == tarinfo.mtime
+                continue
             assert 0 < tarinfo.mtime
 
 

From 44167a577b6cccafb1baef2e42a2991e11d8ea1e Mon Sep 17 00:00:00 2001
From: Arun Babu Neelicattu 
Date: Tue, 20 Apr 2021 21:15:12 +0200
Subject: [PATCH 68/77] pre-commit: add json checks

---
 .pre-commit-config.yaml                       |   11 +-
 poetry/core/json/schemas/poetry-schema.json   | 1136 +++++++++--------
 .../complete/my_package/data1/test.json       |    1 +
 .../my_package/sub_pkg2/data2/data.json       |    1 +
 4 files changed, 587 insertions(+), 562 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a5cfd98a6..a2fffbc2d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -6,7 +6,7 @@ repos:
         exclude: ^poetry/core/_vendor
 
   - repo: https://gitlab.com/pycqa/flake8
-    rev: 3.8.4
+    rev: 3.9.1
     hooks:
       - id: flake8
         exclude: |
@@ -44,3 +44,12 @@ repos:
           )
       - id: debug-statements
         exclude: ^poetry/core/_vendor
+
+      - id: check-json
+        exclude: ^poetry/core/_vendor
+
+      - id: pretty-format-json
+        exclude: ^poetry/core/_vendor
+        args:
+          - --no-sort-keys
+          - --autofix
diff --git a/poetry/core/json/schemas/poetry-schema.json b/poetry/core/json/schemas/poetry-schema.json
index 1f7243eac..3773e49b3 100644
--- a/poetry/core/json/schemas/poetry-schema.json
+++ b/poetry/core/json/schemas/poetry-schema.json
@@ -1,595 +1,609 @@
 {
-    "$schema": "http://json-schema.org/draft-04/schema#",
-    "name": "Package",
-    "type": "object",
-    "additionalProperties": false,
-    "required": [
-        "name",
-        "version",
-        "description"
-    ],
-    "properties": {
-        "name": {
-            "type": "string",
-            "description": "Package name."
-        },
-        "version": {
-            "type": "string",
-            "description": "Package version."
-        },
-        "description": {
-            "type": "string",
-            "description": "Short package description."
-        },
-        "keywords": {
-            "type": "array",
-            "items": {
-                "type": "string",
-                "description": "A tag/keyword that this package relates to."
-            }
-        },
-        "homepage": {
-            "type": "string",
-            "description": "Homepage URL for the project.",
-            "format": "uri"
-        },
-        "repository": {
-            "type": "string",
-            "description": "Repository URL for the project.",
-            "format": "uri"
-        },
-        "documentation": {
-            "type": "string",
-            "description": "Documentation URL for the project.",
-            "format": "uri"
-        },
-        "license": {
-            "type": "string",
-            "description": "License name."
-        },
-        "authors": {
-            "$ref": "#/definitions/authors"
-        },
-        "maintainers": {
-            "$ref": "#/definitions/maintainers"
-        },
-        "readme": {
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "name": "Package",
+  "type": "object",
+  "additionalProperties": false,
+  "required": [
+    "name",
+    "version",
+    "description"
+  ],
+  "properties": {
+    "name": {
+      "type": "string",
+      "description": "Package name."
+    },
+    "version": {
+      "type": "string",
+      "description": "Package version."
+    },
+    "description": {
+      "type": "string",
+      "description": "Short package description."
+    },
+    "keywords": {
+      "type": "array",
+      "items": {
+        "type": "string",
+        "description": "A tag/keyword that this package relates to."
+      }
+    },
+    "homepage": {
+      "type": "string",
+      "description": "Homepage URL for the project.",
+      "format": "uri"
+    },
+    "repository": {
+      "type": "string",
+      "description": "Repository URL for the project.",
+      "format": "uri"
+    },
+    "documentation": {
+      "type": "string",
+      "description": "Documentation URL for the project.",
+      "format": "uri"
+    },
+    "license": {
+      "type": "string",
+      "description": "License name."
+    },
+    "authors": {
+      "$ref": "#/definitions/authors"
+    },
+    "maintainers": {
+      "$ref": "#/definitions/maintainers"
+    },
+    "readme": {
+      "type": "string",
+      "description": "The path to the README file"
+    },
+    "classifiers": {
+      "type": "array",
+      "description": "A list of trove classifers."
+    },
+    "packages": {
+      "type": "array",
+      "description": "A list of packages to include in the final distribution.",
+      "items": {
+        "type": "object",
+        "description": "Information about where the package resides.",
+        "additionalProperties": false,
+        "required": [
+          "include"
+        ],
+        "properties": {
+          "include": {
+            "$ref": "#/definitions/include-path"
+          },
+          "from": {
             "type": "string",
-            "description": "The path to the README file"
-        },
-        "classifiers": {
-            "type": "array",
-            "description": "A list of trove classifers."
-        },
-        "packages": {
-            "type": "array",
-            "description": "A list of packages to include in the final distribution.",
-            "items": {
-                "type": "object",
-                "description": "Information about where the package resides.",
-                "additionalProperties": false,
-                "required": [
-                    "include"
-                ],
-                "properties": {
-                    "include": {
-                        "$ref": "#/definitions/include-path"
-                    },
-                    "from": {
-                        "type": "string",
-                        "description": "Where the source directory of the package resides."
-                    },
-                    "format": {
-                        "$ref": "#/definitions/package-formats"
-                    }
-                }
-            }
-        },
-        "include": {
-            "type": "array",
-            "description": "A list of files and folders to include.",
-            "items": {
-                "anyOf": [
-                    {
-                        "$ref": "#/definitions/include-path"
-                    },
-                    {
-                        "type": "object",
-                        "additionalProperties": false,
-                        "required": [
-                            "path"
-                        ],
-                        "properties": {
-                            "path": {
-                                "$ref": "#/definitions/include-path"
-                            },
-                            "format": {
-                                "$ref": "#/definitions/package-formats"
-                            }
-                        }
-                    }
-                ]
-            }
-        },
-        "exclude": {
-            "type": "array",
-            "description": "A list of files and folders to exclude."
-        },
-        "dependencies": {
+            "description": "Where the source directory of the package resides."
+          },
+          "format": {
+            "$ref": "#/definitions/package-formats"
+          }
+        }
+      }
+    },
+    "include": {
+      "type": "array",
+      "description": "A list of files and folders to include.",
+      "items": {
+        "anyOf": [
+          {
+            "$ref": "#/definitions/include-path"
+          },
+          {
             "type": "object",
-            "description": "This is a hash of package name (keys) and version constraints (values) that are required to run this package.",
+            "additionalProperties": false,
             "required": [
-                "python"
+              "path"
             ],
             "properties": {
-                "python": {
-                    "type": "string",
-                    "description": "The Python versions the package is compatible with."
-                }
-            },
-            "$ref": "#/definitions/dependencies",
-            "additionalProperties": false
-        },
-        "dev-dependencies": {
-            "type": "object",
-            "description": "This is a hash of package name (keys) and version constraints (values) that this package requires for developing it (testing tools and such).",
-            "$ref": "#/definitions/dependencies",
-            "additionalProperties": false
-        },
-        "extras": {
-            "type": "object",
-            "patternProperties": {
-                "^[a-zA-Z-_.0-9]+$": {
-                    "type": "array",
-                    "items": {
-                        "type": "string"
-                    }
-                }
-            }
-        },
-        "build": {
-            "$ref": "#/definitions/build-section"
-        },
-        "source": {
-            "type": "array",
-            "description": "A set of additional repositories where packages can be found.",
-            "additionalProperties": {
-                "$ref": "#/definitions/repository"
-            },
-            "items": {
-                "$ref": "#/definitions/repository"
-            }
-        },
-        "scripts": {
-            "type": "object",
-            "description": "A hash of scripts to be installed.",
-            "items": {
-                "type": "string"
+              "path": {
+                "$ref": "#/definitions/include-path"
+              },
+              "format": {
+                "$ref": "#/definitions/package-formats"
+              }
             }
-        },
-        "plugins": {
-            "type": "object",
-            "description": "A hash of hashes representing plugins",
-            "patternProperties": {
-                "^[a-zA-Z-_.0-9]+$": {
-                    "type": "object",
-                    "patternProperties": {
-                        "^[a-zA-Z-_.0-9]+$": {
-                            "type": "string"
-                        }
-                    }
-                }
-            }
-        },
-        "urls": {
-            "type": "object",
-            "patternProperties": {
-                "^.+$": {
-                    "type": "string",
-                    "description": "The full url of the custom url."
-                }
+          }
+        ]
+      }
+    },
+    "exclude": {
+      "type": "array",
+      "description": "A list of files and folders to exclude."
+    },
+    "dependencies": {
+      "type": "object",
+      "description": "This is a hash of package name (keys) and version constraints (values) that are required to run this package.",
+      "required": [
+        "python"
+      ],
+      "properties": {
+        "python": {
+          "type": "string",
+          "description": "The Python versions the package is compatible with."
+        }
+      },
+      "$ref": "#/definitions/dependencies",
+      "additionalProperties": false
+    },
+    "dev-dependencies": {
+      "type": "object",
+      "description": "This is a hash of package name (keys) and version constraints (values) that this package requires for developing it (testing tools and such).",
+      "$ref": "#/definitions/dependencies",
+      "additionalProperties": false
+    },
+    "extras": {
+      "type": "object",
+      "patternProperties": {
+        "^[a-zA-Z-_.0-9]+$": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        }
+      }
+    },
+    "build": {
+      "$ref": "#/definitions/build-section"
+    },
+    "source": {
+      "type": "array",
+      "description": "A set of additional repositories where packages can be found.",
+      "additionalProperties": {
+        "$ref": "#/definitions/repository"
+      },
+      "items": {
+        "$ref": "#/definitions/repository"
+      }
+    },
+    "scripts": {
+      "type": "object",
+      "description": "A hash of scripts to be installed.",
+      "items": {
+        "type": "string"
+      }
+    },
+    "plugins": {
+      "type": "object",
+      "description": "A hash of hashes representing plugins",
+      "patternProperties": {
+        "^[a-zA-Z-_.0-9]+$": {
+          "type": "object",
+          "patternProperties": {
+            "^[a-zA-Z-_.0-9]+$": {
+              "type": "string"
             }
+          }
         }
+      }
     },
-    "definitions": {
-        "authors": {
-            "type": "array",
-            "description": "List of authors that contributed to the package. This is typically the main maintainers, not the full list.",
-            "items": {
-                "type": "string"
+    "urls": {
+      "type": "object",
+      "patternProperties": {
+        "^.+$": {
+          "type": "string",
+          "description": "The full url of the custom url."
+        }
+      }
+    }
+  },
+  "definitions": {
+    "authors": {
+      "type": "array",
+      "description": "List of authors that contributed to the package. This is typically the main maintainers, not the full list.",
+      "items": {
+        "type": "string"
+      }
+    },
+    "maintainers": {
+      "type": "array",
+      "description": "List of maintainers, other than the original author(s), that upkeep the package.",
+      "items": {
+        "type": "string"
+      }
+    },
+    "include-path": {
+      "type": "string",
+      "description": "Path to file or directory to include."
+    },
+    "package-format": {
+      "type": "string",
+      "enum": [
+        "sdist",
+        "wheel"
+      ],
+      "description": "A Python packaging format."
+    },
+    "package-formats": {
+      "oneOf": [
+        {
+          "$ref": "#/definitions/package-format"
+        },
+        {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/package-format"
+          }
+        }
+      ],
+      "description": "The format(s) for which the package must be included."
+    },
+    "dependencies": {
+      "type": "object",
+      "patternProperties": {
+        "^[a-zA-Z-_.0-9]+$": {
+          "oneOf": [
+            {
+              "$ref": "#/definitions/dependency"
+            },
+            {
+              "$ref": "#/definitions/long-dependency"
+            },
+            {
+              "$ref": "#/definitions/git-dependency"
+            },
+            {
+              "$ref": "#/definitions/file-dependency"
+            },
+            {
+              "$ref": "#/definitions/path-dependency"
+            },
+            {
+              "$ref": "#/definitions/url-dependency"
+            },
+            {
+              "$ref": "#/definitions/multiple-constraints-dependency"
             }
+          ]
+        }
+      }
+    },
+    "dependency": {
+      "type": "string",
+      "description": "The constraint of the dependency."
+    },
+    "long-dependency": {
+      "type": "object",
+      "required": [
+        "version"
+      ],
+      "additionalProperties": false,
+      "properties": {
+        "version": {
+          "type": "string",
+          "description": "The constraint of the dependency."
         },
-        "maintainers": {
-            "type": "array",
-            "description": "List of maintainers, other than the original author(s), that upkeep the package.",
-            "items": {
-                "type": "string"
-            }
+        "python": {
+          "type": "string",
+          "description": "The python versions for which the dependency should be installed."
         },
-        "include-path": {
-            "type": "string",
-            "description": "Path to file or directory to include."
+        "platform": {
+          "type": "string",
+          "description": "The platform(s) for which the dependency should be installed."
         },
-        "package-format": {
-            "type": "string",
-            "enum": ["sdist", "wheel"],
-            "description": "A Python packaging format."
+        "markers": {
+          "type": "string",
+          "description": "The PEP 508 compliant environment markers for which the dependency should be installed."
         },
-        "package-formats": {
-            "oneOf": [
-                {"$ref": "#/definitions/package-format"},
-                {"type": "array", "items": {"$ref": "#/definitions/package-format"}}
-            ],
-            "description": "The format(s) for which the package must be included."
+        "allow-prereleases": {
+          "type": "boolean",
+          "description": "Whether the dependency allows prereleases or not."
         },
-        "dependencies": {
-            "type": "object",
-            "patternProperties": {
-                "^[a-zA-Z-_.0-9]+$": {
-                    "oneOf": [
-                        {
-                            "$ref": "#/definitions/dependency"
-                        },
-                        {
-                            "$ref": "#/definitions/long-dependency"
-                        },
-                        {
-                            "$ref": "#/definitions/git-dependency"
-                        },
-                        {
-                            "$ref": "#/definitions/file-dependency"
-                        },
-                        {
-                            "$ref": "#/definitions/path-dependency"
-                        },
-                        {
-                            "$ref": "#/definitions/url-dependency"
-                        },
-                        {
-                            "$ref": "#/definitions/multiple-constraints-dependency"
-                        }
-                    ]
-                }
-            }
+        "allows-prereleases": {
+          "type": "boolean",
+          "description": "Whether the dependency allows prereleases or not."
         },
-        "dependency": {
-            "type": "string",
-            "description": "The constraint of the dependency."
+        "optional": {
+          "type": "boolean",
+          "description": "Whether the dependency is optional or not."
         },
-        "long-dependency": {
-            "type": "object",
-            "required": [
-                "version"
-            ],
-            "additionalProperties": false,
-            "properties": {
-                "version": {
-                    "type": "string",
-                    "description": "The constraint of the dependency."
-                },
-                "python": {
-                    "type": "string",
-                    "description": "The python versions for which the dependency should be installed."
-                },
-                "platform": {
-                    "type": "string",
-                    "description": "The platform(s) for which the dependency should be installed."
-                },
-                "markers": {
-                    "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."
-                },
-                "optional": {
-                    "type": "boolean",
-                    "description": "Whether the dependency is optional or not."
-                },
-                "extras": {
-                    "type": "array",
-                    "description": "The required extras for this dependency.",
-                    "items": {
-                        "type": "string"
-                    }
-                },
-                "source": {
-                    "type": "string",
-                    "description": "The exclusive source used to search for this dependency."
-                }
-            }
+        "extras": {
+          "type": "array",
+          "description": "The required extras for this dependency.",
+          "items": {
+            "type": "string"
+          }
         },
-        "git-dependency": {
-            "type": "object",
-            "required": [
-                "git"
-            ],
-            "additionalProperties": false,
-            "properties": {
-                "git": {
-                    "type": "string",
-                    "description": "The url of the git repository.",
-                    "format": "uri"
-                },
-                "branch": {
-                    "type": "string",
-                    "description": "The branch to checkout."
-                },
-                "tag": {
-                    "type": "string",
-                    "description": "The tag to checkout."
-                },
-                "rev": {
-                    "type": "string",
-                    "description": "The revision to checkout."
-                },
-                "python": {
-                    "type": "string",
-                    "description": "The python versions for which the dependency should be installed."
-                },
-                "platform": {
-                    "type": "string",
-                    "description": "The platform(s) for which the dependency should be installed."
-                },
-                "markers": {
-                    "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."
-                },
-                "optional": {
-                    "type": "boolean",
-                    "description": "Whether the dependency is optional or not."
-                },
-                "extras": {
-                    "type": "array",
-                    "description": "The required extras for this dependency.",
-                    "items": {
-                        "type": "string"
-                    }
-                },
-                "develop": {
-                    "type": "boolean",
-                    "description": "Whether to install the dependency in development mode."
-                }
-            }
+        "source": {
+          "type": "string",
+          "description": "The exclusive source used to search for this dependency."
+        }
+      }
+    },
+    "git-dependency": {
+      "type": "object",
+      "required": [
+        "git"
+      ],
+      "additionalProperties": false,
+      "properties": {
+        "git": {
+          "type": "string",
+          "description": "The url of the git repository.",
+          "format": "uri"
+        },
+        "branch": {
+          "type": "string",
+          "description": "The branch to checkout."
+        },
+        "tag": {
+          "type": "string",
+          "description": "The tag to checkout."
+        },
+        "rev": {
+          "type": "string",
+          "description": "The revision to checkout."
+        },
+        "python": {
+          "type": "string",
+          "description": "The python versions for which the dependency should be installed."
+        },
+        "platform": {
+          "type": "string",
+          "description": "The platform(s) for which the dependency should be installed."
+        },
+        "markers": {
+          "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."
+        },
+        "optional": {
+          "type": "boolean",
+          "description": "Whether the dependency is optional or not."
         },
-        "file-dependency": {
-            "type": "object",
-            "required": [
-                "file"
-            ],
-            "additionalProperties": false,
-            "properties": {
-                "file": {
-                    "type": "string",
-                    "description": "The path to the file."
-                },
-                "python": {
-                    "type": "string",
-                    "description": "The python versions for which the dependency should be installed."
-                },
-                "platform": {
-                    "type": "string",
-                    "description": "The platform(s) for which the dependency should be installed."
-                },
-                "markers": {
-                    "type": "string",
-                    "description": "The PEP 508 compliant environment markers for which the dependency should be installed."
-                },
-                "optional": {
-                    "type": "boolean",
-                    "description": "Whether the dependency is optional or not."
-                },
-                "extras": {
-                    "type": "array",
-                    "description": "The required extras for this dependency.",
-                    "items": {
-                        "type": "string"
-                    }
-                }
-            }
+        "extras": {
+          "type": "array",
+          "description": "The required extras for this dependency.",
+          "items": {
+            "type": "string"
+          }
+        },
+        "develop": {
+          "type": "boolean",
+          "description": "Whether to install the dependency in development mode."
+        }
+      }
+    },
+    "file-dependency": {
+      "type": "object",
+      "required": [
+        "file"
+      ],
+      "additionalProperties": false,
+      "properties": {
+        "file": {
+          "type": "string",
+          "description": "The path to the file."
+        },
+        "python": {
+          "type": "string",
+          "description": "The python versions for which the dependency should be installed."
+        },
+        "platform": {
+          "type": "string",
+          "description": "The platform(s) for which the dependency should be installed."
+        },
+        "markers": {
+          "type": "string",
+          "description": "The PEP 508 compliant environment markers for which the dependency should be installed."
+        },
+        "optional": {
+          "type": "boolean",
+          "description": "Whether the dependency is optional or not."
         },
-        "path-dependency": {
-            "type": "object",
-            "required": [
-                "path"
-            ],
-            "additionalProperties": false,
-            "properties": {
-                "path": {
-                    "type": "string",
-                    "description": "The path to the dependency."
-                },
-                "python": {
-                    "type": "string",
-                    "description": "The python versions for which the dependency should be installed."
-                },
-                "platform": {
-                    "type": "string",
-                    "description": "The platform(s) for which the dependency should be installed."
-                },
-                "markers": {
-                    "type": "string",
-                    "description": "The PEP 508 compliant environment markers for which the dependency should be installed."
-                },
-                "optional": {
-                    "type": "boolean",
-                    "description": "Whether the dependency is optional or not."
-                },
-                "extras": {
-                    "type": "array",
-                    "description": "The required extras for this dependency.",
-                    "items": {
-                        "type": "string"
-                    }
-                },
-                "develop": {
-                    "type": "boolean",
-                    "description": "Whether to install the dependency in development mode."
-                }
-            }
+        "extras": {
+          "type": "array",
+          "description": "The required extras for this dependency.",
+          "items": {
+            "type": "string"
+          }
+        }
+      }
+    },
+    "path-dependency": {
+      "type": "object",
+      "required": [
+        "path"
+      ],
+      "additionalProperties": false,
+      "properties": {
+        "path": {
+          "type": "string",
+          "description": "The path to the dependency."
+        },
+        "python": {
+          "type": "string",
+          "description": "The python versions for which the dependency should be installed."
+        },
+        "platform": {
+          "type": "string",
+          "description": "The platform(s) for which the dependency should be installed."
+        },
+        "markers": {
+          "type": "string",
+          "description": "The PEP 508 compliant environment markers for which the dependency should be installed."
+        },
+        "optional": {
+          "type": "boolean",
+          "description": "Whether the dependency is optional or not."
         },
-        "url-dependency": {
-            "type": "object",
-            "required": [
-                "url"
-            ],
-            "additionalProperties": false,
-            "properties": {
-                "url": {
-                    "type": "string",
-                    "description": "The url to the file."
-                },
-                "python": {
-                    "type": "string",
-                    "description": "The python versions for which the dependency should be installed."
-                },
-                "platform": {
-                    "type": "string",
-                    "description": "The platform(s) for which the dependency should be installed."
-                },
-                "markers": {
-                    "type": "string",
-                    "description": "The PEP 508 compliant environment markers for which the dependency should be installed."
-                },
-                "optional": {
-                    "type": "boolean",
-                    "description": "Whether the dependency is optional or not."
-                },
-                "extras": {
-                    "type": "array",
-                    "description": "The required extras for this dependency.",
-                    "items": {
-                        "type": "string"
-                    }
-                }
-            }
+        "extras": {
+          "type": "array",
+          "description": "The required extras for this dependency.",
+          "items": {
+            "type": "string"
+          }
+        },
+        "develop": {
+          "type": "boolean",
+          "description": "Whether to install the dependency in development mode."
+        }
+      }
+    },
+    "url-dependency": {
+      "type": "object",
+      "required": [
+        "url"
+      ],
+      "additionalProperties": false,
+      "properties": {
+        "url": {
+          "type": "string",
+          "description": "The url to the file."
+        },
+        "python": {
+          "type": "string",
+          "description": "The python versions for which the dependency should be installed."
+        },
+        "platform": {
+          "type": "string",
+          "description": "The platform(s) for which the dependency should be installed."
+        },
+        "markers": {
+          "type": "string",
+          "description": "The PEP 508 compliant environment markers for which the dependency should be installed."
+        },
+        "optional": {
+          "type": "boolean",
+          "description": "Whether the dependency is optional or not."
         },
-        "multiple-constraints-dependency": {
-            "type": "array",
-            "minItems": 1,
-            "items": {
-                "oneOf": [
-                    {
-                        "$ref": "#/definitions/dependency"
-                    },
-                    {
-                        "$ref": "#/definitions/long-dependency"
-                    },
-                    {
-                        "$ref": "#/definitions/git-dependency"
-                    },
-                    {
-                        "$ref": "#/definitions/file-dependency"
-                    },
-                    {
-                        "$ref": "#/definitions/path-dependency"
-                    },
-                    {
-                        "$ref": "#/definitions/url-dependency"
-                    }
-                ]
+        "extras": {
+          "type": "array",
+          "description": "The required extras for this dependency.",
+          "items": {
+            "type": "string"
+          }
+        }
+      }
+    },
+    "multiple-constraints-dependency": {
+      "type": "array",
+      "minItems": 1,
+      "items": {
+        "oneOf": [
+          {
+            "$ref": "#/definitions/dependency"
+          },
+          {
+            "$ref": "#/definitions/long-dependency"
+          },
+          {
+            "$ref": "#/definitions/git-dependency"
+          },
+          {
+            "$ref": "#/definitions/file-dependency"
+          },
+          {
+            "$ref": "#/definitions/path-dependency"
+          },
+          {
+            "$ref": "#/definitions/url-dependency"
+          }
+        ]
+      }
+    },
+    "scripts": {
+      "type": "object",
+      "patternProperties": {
+        "^[a-zA-Z-_.0-9]+$": {
+          "oneOf": [
+            {
+              "$ref": "#/definitions/script"
+            },
+            {
+              "$ref": "#/definitions/extra-script"
             }
+          ]
+        }
+      }
+    },
+    "script": {
+      "type": "string",
+      "description": "A simple script pointing to a callable object."
+    },
+    "extra-script": {
+      "type": "object",
+      "description": "A script that should be installed only if extras are activated.",
+      "additionalProperties": false,
+      "properties": {
+        "callable": {
+          "$ref": "#/definitions/script"
         },
-        "scripts": {
-            "type": "object",
-            "patternProperties": {
-                "^[a-zA-Z-_.0-9]+$": {
-                    "oneOf": [
-                        {
-                            "$ref": "#/definitions/script"
-                        },
-                        {
-                            "$ref": "#/definitions/extra-script"
-                        }
-                    ]
-                }
-            }
+        "extras": {
+          "type": "array",
+          "description": "The required extras for this script.",
+          "items": {
+            "type": "string"
+          }
+        }
+      }
+    },
+    "repository": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "name": {
+          "type": "string",
+          "description": "The name of the repository"
         },
-        "script": {
-            "type": "string",
-            "description": "A simple script pointing to a callable object."
+        "url": {
+          "type": "string",
+          "description": "The url of the repository",
+          "format": "uri"
         },
-        "extra-script": {
-            "type": "object",
-            "description": "A script that should be installed only if extras are activated.",
-            "additionalProperties": false,
-            "properties": {
-                "callable": {
-                    "$ref": "#/definitions/script"
-                },
-                "extras": {
-                    "type": "array",
-                    "description": "The required extras for this script.",
-                    "items": {
-                        "type": "string"
-                    }
-                }
-            }
+        "default": {
+          "type": "boolean",
+          "description": "Make this repository the default (disable PyPI)"
         },
-        "repository": {
-            "type": "object",
-            "additionalProperties": false,
-            "properties": {
-                "name": {
-                    "type": "string",
-                    "description": "The name of the repository"
-                },
-                "url": {
-                    "type": "string",
-                    "description": "The url of the repository",
-                    "format": "uri"
-                },
-                "default": {
-                    "type": "boolean",
-                    "description": "Make this repository the default (disable PyPI)"
-                },
-                "secondary": {
-                    "type": "boolean",
-                    "description": "Declare this repository as secondary, i.e. it will only be looked up last for packages."
-                },
-                "links": {
-                    "type": "boolean",
-                    "description": "Declare this as a link source. Links at uri/path can point to sdist or bdist archives."
-                }
-            }
+        "secondary": {
+          "type": "boolean",
+          "description": "Declare this repository as secondary, i.e. it will only be looked up last for packages."
         },
-        "build-script": {
-            "type": "string",
-            "description": "The python script file used to build extensions."
+        "links": {
+          "type": "boolean",
+          "description": "Declare this as a link source. Links at uri/path can point to sdist or bdist archives."
+        }
+      }
+    },
+    "build-script": {
+      "type": "string",
+      "description": "The python script file used to build extensions."
+    },
+    "build-config": {
+      "type": "object",
+      "description": "Build specific configurations.",
+      "additionalProperties": false,
+      "properties": {
+        "generate-setup-file": {
+          "type": "boolean",
+          "description": "Generate and include a setup.py file in sdist.",
+          "default": true
         },
-        "build-config": {
-            "type": "object",
-            "description": "Build specific configurations.",
-            "additionalProperties": false,
-            "properties": {
-                "generate-setup-file": {
-                    "type": "boolean",
-                    "description": "Generate and include a setup.py file in sdist.",
-                    "default": true
-                },
-                "script": {
-                    "$ref": "#/definitions/build-script"
-                }
-            }
+        "script": {
+          "$ref": "#/definitions/build-script"
+        }
+      }
+    },
+    "build-section": {
+      "oneOf": [
+        {
+          "$ref": "#/definitions/build-script"
         },
-        "build-section": {
-            "oneOf": [
-                {"$ref": "#/definitions/build-script"},
-                {"$ref": "#/definitions/build-config"}
-            ]
+        {
+          "$ref": "#/definitions/build-config"
         }
+      ]
     }
+  }
 }
diff --git a/tests/masonry/builders/fixtures/complete/my_package/data1/test.json b/tests/masonry/builders/fixtures/complete/my_package/data1/test.json
index e69de29bb..0967ef424 100644
--- a/tests/masonry/builders/fixtures/complete/my_package/data1/test.json
+++ b/tests/masonry/builders/fixtures/complete/my_package/data1/test.json
@@ -0,0 +1 @@
+{}
diff --git a/tests/masonry/builders/fixtures/complete/my_package/sub_pkg2/data2/data.json b/tests/masonry/builders/fixtures/complete/my_package/sub_pkg2/data2/data.json
index e69de29bb..0967ef424 100644
--- a/tests/masonry/builders/fixtures/complete/my_package/sub_pkg2/data2/data.json
+++ b/tests/masonry/builders/fixtures/complete/my_package/sub_pkg2/data2/data.json
@@ -0,0 +1 @@
+{}

From fe476e05a2f97076b0560573086d01019b5df994 Mon Sep 17 00:00:00 2001
From: Michael Ossareh 
Date: Mon, 26 Apr 2021 14:01:04 -0500
Subject: [PATCH 69/77] fix(packages/dependency): add space after filename for
 file dependencies with markers (#153)

* fix(packages/dependency): add space after filename for file dependencies with markers

local vendored files need a space after the file name and before the ";" which demarks the start of
markers

fix #3872
---
 poetry/core/packages/dependency.py     |  2 +-
 tests/packages/test_file_dependency.py | 37 +++++++++++++++++++++++++-
 2 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py
index 6e06bc6fa..aa75e3e2f 100644
--- a/poetry/core/packages/dependency.py
+++ b/poetry/core/packages/dependency.py
@@ -262,7 +262,7 @@ def to_pep_508(self, with_extras: bool = True) -> str:
             )
 
         if markers:
-            if self.is_vcs() or self.is_url():
+            if self.is_vcs() or self.is_url() or self.is_file():
                 requirement += " "
 
             if len(markers) > 1:
diff --git a/tests/packages/test_file_dependency.py b/tests/packages/test_file_dependency.py
index 3abe20f88..8315594ca 100644
--- a/tests/packages/test_file_dependency.py
+++ b/tests/packages/test_file_dependency.py
@@ -4,6 +4,7 @@
 
 from poetry.core.packages.dependency import Dependency
 from poetry.core.packages.file_dependency import FileDependency
+from poetry.core.version.markers import SingleMarker
 
 
 DIST_PATH = Path(__file__).parent.parent / "fixtures" / "distributions"
@@ -20,7 +21,7 @@ def test_file_dependency_dir():
 
 
 def _test_file_dependency_pep_508(
-    mocker, name, path, pep_508_input, pep_508_output=None
+    mocker, name, path, pep_508_input, pep_508_output=None, marker=None
 ):
     mocker.patch.object(Path, "exists").return_value = True
     mocker.patch.object(Path, "is_file").return_value = True
@@ -28,6 +29,8 @@ def _test_file_dependency_pep_508(
     dep = Dependency.create_from_pep_508(
         pep_508_input, relative_to=Path(__file__).parent
     )
+    if marker:
+        dep.marker = marker
 
     assert dep.is_file()
     assert dep.name == name
@@ -62,3 +65,35 @@ def test_file_dependency_pep_508_local_file_relative_path(mocker):
 
     requirement = "{} @ {}".format("demo", path)
     _test_file_dependency_pep_508(mocker, "demo", path, requirement)
+
+
+def test_absolute_file_dependency_to_pep_508_with_marker(mocker):
+    wheel = "demo-0.1.0-py2.py3-none-any.whl"
+
+    abs_path = DIST_PATH / wheel
+    requirement = '{} @ file://{} ; sys_platform == "linux"'.format(
+        "demo", abs_path.as_posix()
+    )
+    _test_file_dependency_pep_508(
+        mocker,
+        "demo",
+        abs_path,
+        requirement,
+        marker=SingleMarker("sys.platform", "linux"),
+    )
+
+
+def test_relative_file_dependency_to_pep_508_with_marker(mocker):
+    wheel = "demo-0.1.0-py2.py3-none-any.whl"
+
+    rel_path = Path("..") / "fixtures" / "distributions" / wheel
+    requirement = '{} @ {} ; sys_platform == "linux"'.format(
+        "demo", rel_path.as_posix()
+    )
+    _test_file_dependency_pep_508(
+        mocker,
+        "demo",
+        rel_path,
+        requirement,
+        marker=SingleMarker("sys.platform", "linux"),
+    )

From 915479fd204510afa2fa67a0066e1e8cd1481b1b Mon Sep 17 00:00:00 2001
From: finswimmer 
Date: Mon, 5 Oct 2020 19:03:04 +0200
Subject: [PATCH 70/77] fix (vcs.git): allow `+` in user name new (vcs.git):
 extract user credential (passord, deployment key, ...) from git url change
 (vcs.git): change order of init arguments for `ParsedUrl` change (vcs.git):
 make user, password, port, name and rev optional for `ParsedUrl`

---
 poetry/core/vcs/git.py |  21 ++++--
 tests/vcs/test_vcs.py  | 145 +++++++++++++++++++++++++++++++----------
 2 files changed, 125 insertions(+), 41 deletions(-)

diff --git a/poetry/core/vcs/git.py b/poetry/core/vcs/git.py
index 1633ad090..b17c9ade1 100644
--- a/poetry/core/vcs/git.py
+++ b/poetry/core/vcs/git.py
@@ -9,7 +9,8 @@
 
 pattern_formats = {
     "protocol": r"\w+",
-    "user": r"[a-zA-Z0-9_.-]+",
+    "user": r"[a-zA-Z0-9_.\+-]+",
+    "password": r"[\w\d-]+",
     "resource": r"[a-zA-Z0-9_.-]+",
     "port": r"\d+",
     "path": r"[\w~.\-/\\]+",
@@ -21,7 +22,7 @@
     re.compile(
         r"^(git\+)?"
         r"(?Phttps?|git|ssh|rsync|file)://"
-        r"(?:(?P{user})@)?"
+        r"(?:(?P{user})(:(?P{password}))?@)?"
         r"(?P{resource})?"
         r"(:(?P{port}))?"
         r"(?P[:/\\]({path}[/\\])?"
@@ -29,6 +30,7 @@
         r"([@#](?P{rev}))?"
         r"$".format(
             user=pattern_formats["user"],
+            password=pattern_formats["password"],
             resource=pattern_formats["resource"],
             port=pattern_formats["port"],
             path=pattern_formats["path"],
@@ -39,7 +41,7 @@
     re.compile(
         r"(git\+)?"
         r"((?P{protocol})://)"
-        r"(?:(?P{user})@)?"
+        r"(?:(?P{user})(:(?P{password}))?@)?"
         r"(?P{resource}:?)"
         r"(:(?P{port}))?"
         r"(?P({path})"
@@ -48,6 +50,7 @@
         r"$".format(
             protocol=pattern_formats["protocol"],
             user=pattern_formats["user"],
+            password=pattern_formats["password"],
             resource=pattern_formats["resource"],
             port=pattern_formats["port"],
             path=pattern_formats["path"],
@@ -56,7 +59,7 @@
         )
     ),
     re.compile(
-        r"^(?:(?P{user})@)?"
+        r"^(?:(?P{user})(:(?P{password}))?@)?"
         r"(?P{resource})"
         r"(:(?P{port}))?"
         r"(?P([:/]{path}/)"
@@ -64,6 +67,7 @@
         r"([@#](?P{rev}))?"
         r"$".format(
             user=pattern_formats["user"],
+            password=pattern_formats["password"],
             resource=pattern_formats["resource"],
             port=pattern_formats["port"],
             path=pattern_formats["path"],
@@ -72,7 +76,7 @@
         )
     ),
     re.compile(
-        r"((?P{user})@)?"
+        r"((?P{user})(:(?P{password}))?@)?"
         r"(?P{resource})"
         r"[:/]{{1,2}}"
         r"(?P({path})"
@@ -80,6 +84,7 @@
         r"([@#](?P{rev}))?"
         r"$".format(
             user=pattern_formats["user"],
+            password=pattern_formats["password"],
             resource=pattern_formats["resource"],
             path=pattern_formats["path"],
             name=pattern_formats["name"],
@@ -104,6 +109,7 @@ def __init__(
         self.resource = resource
         self.pathname = pathname
         self.user = user
+        self.password = password
         self.port = port
         self.name = name
         self.rev = rev
@@ -119,6 +125,7 @@ def parse(cls, url: str) -> "ParsedUrl":
                     groups.get("resource"),
                     groups.get("pathname"),
                     groups.get("user"),
+                    groups.get("password"),
                     groups.get("port"),
                     groups.get("name"),
                     groups.get("rev"),
@@ -130,7 +137,9 @@ def parse(cls, url: str) -> "ParsedUrl":
     def url(self) -> str:
         return "{}{}{}{}{}".format(
             "{}://".format(self.protocol) if self.protocol else "",
-            "{}@".format(self.user) if self.user else "",
+            "{}".format(self.user) if self.user else "",
+            ":{}".format(self.password) if self.password else "",
+            "@" if self.user else "",
             self.resource,
             ":{}".format(self.port) if self.port else "",
             "/" + self.pathname.lstrip(":/"),
diff --git a/tests/vcs/test_vcs.py b/tests/vcs/test_vcs.py
index ebd518cc7..bd454d897 100644
--- a/tests/vcs/test_vcs.py
+++ b/tests/vcs/test_vcs.py
@@ -78,6 +78,13 @@
             "git+ssh://git@git.example.com:sdispater/project/my_repo.git",
             GitUrl("git@git.example.com:sdispater/project/my_repo.git", None),
         ),
+        (
+            "git+https://user:fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a@hostname/project/blah.git",
+            GitUrl(
+                "https://user:fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a@hostname/project/blah.git",
+                None,
+            ),
+        ),
     ],
 )
 def test_normalize_url(url, normalized):
@@ -90,19 +97,37 @@ def test_normalize_url(url, normalized):
         (
             "git+ssh://user@hostname:project.git#commit",
             ParsedUrl(
-                "ssh", "hostname", ":project.git", "user", None, "project", "commit"
+                "ssh",
+                "hostname",
+                ":project.git",
+                "user",
+                port=None,
+                name="project",
+                rev="commit",
             ),
         ),
         (
             "git+http://user@hostname/project/blah.git@commit",
             ParsedUrl(
-                "http", "hostname", "/project/blah.git", "user", None, "blah", "commit"
+                "http",
+                "hostname",
+                "/project/blah.git",
+                "user",
+                port=None,
+                name="blah",
+                rev="commit",
             ),
         ),
         (
             "git+https://user@hostname/project/blah.git",
             ParsedUrl(
-                "https", "hostname", "/project/blah.git", "user", None, "blah", None
+                "https",
+                "hostname",
+                "/project/blah.git",
+                "user",
+                port=None,
+                name="blah",
+                rev=None,
             ),
         ),
         (
@@ -112,15 +137,21 @@ def test_normalize_url(url, normalized):
                 "hostname",
                 "/project~_-.foo/blah~_-.bar.git",
                 "user",
-                None,
-                "blah~_-.bar",
-                None,
+                port=None,
+                name="blah~_-.bar",
+                rev=None,
             ),
         ),
         (
             "git+https://user@hostname:project/blah.git",
             ParsedUrl(
-                "https", "hostname", ":project/blah.git", "user", None, "blah", None
+                "https",
+                "hostname",
+                ":project/blah.git",
+                "user",
+                port=None,
+                name="blah",
+                rev=None,
             ),
         ),
         (
@@ -130,9 +161,9 @@ def test_normalize_url(url, normalized):
                 "github.com",
                 ":sdispater/poetry.git",
                 "git",
-                None,
-                "poetry",
-                "v1.0.27",
+                port=None,
+                name="poetry",
+                rev="v1.0.27",
             ),
         ),
         (
@@ -142,26 +173,46 @@ def test_normalize_url(url, normalized):
                 "github.com",
                 ":/sdispater/poetry.git",
                 "git",
-                None,
-                "poetry",
-                None,
+                port=None,
+                name="poetry",
+                rev=None,
             ),
         ),
         (
             "git+ssh://git@github.com:org/repo",
-            ParsedUrl("ssh", "github.com", ":org/repo", "git", None, "repo", None),
+            ParsedUrl(
+                "ssh",
+                "github.com",
+                ":org/repo",
+                "git",
+                port=None,
+                name="repo",
+                rev=None,
+            ),
         ),
         (
             "git+ssh://git@github.com/org/repo",
-            ParsedUrl("ssh", "github.com", "/org/repo", "git", None, "repo", None),
+            ParsedUrl(
+                "ssh",
+                "github.com",
+                "/org/repo",
+                "git",
+                port=None,
+                name="repo",
+                rev=None,
+            ),
         ),
         (
             "git+ssh://foo:22/some/path",
-            ParsedUrl("ssh", "foo", "/some/path", None, "22", "path", None),
+            ParsedUrl(
+                "ssh", "foo", "/some/path", None, port="22", name="path", rev=None
+            ),
         ),
         (
             "git@github.com:org/repo",
-            ParsedUrl(None, "github.com", ":org/repo", "git", None, "repo", None),
+            ParsedUrl(
+                None, "github.com", ":org/repo", "git", port=None, name="repo", rev=None
+            ),
         ),
         (
             "git+https://github.com/sdispater/pendulum",
@@ -170,9 +221,9 @@ def test_normalize_url(url, normalized):
                 "github.com",
                 "/sdispater/pendulum",
                 None,
-                None,
-                "pendulum",
-                None,
+                port=None,
+                name="pendulum",
+                rev=None,
             ),
         ),
         (
@@ -182,14 +233,22 @@ def test_normalize_url(url, normalized):
                 "github.com",
                 "/sdispater/pendulum",
                 None,
-                None,
-                "pendulum",
-                "7a018f2d075b03a73409e8356f9b29c9ad4ea2c5",
+                port=None,
+                name="pendulum",
+                rev="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"),
+            ParsedUrl(
+                "ssh",
+                "git.example.com",
+                ":b/b.git",
+                "git",
+                port=None,
+                name="b",
+                rev="v1.0.0",
+            ),
         ),
         (
             "git+ssh://git@github.com:sdispater/pendulum.git#foo/bar",
@@ -198,14 +257,16 @@ def test_normalize_url(url, normalized):
                 "github.com",
                 ":sdispater/pendulum.git",
                 "git",
-                None,
-                "pendulum",
-                "foo/bar",
+                port=None,
+                name="pendulum",
+                rev="foo/bar",
             ),
         ),
         (
             "git+file:///foo/bar.git",
-            ParsedUrl("file", None, "/foo/bar.git", None, None, "bar", None),
+            ParsedUrl(
+                "file", None, "/foo/bar.git", None, port=None, name="bar", rev=None
+            ),
         ),
         (
             "git+file://C:\\Users\\hello\\testing.git#zkat/windows-files",
@@ -214,9 +275,9 @@ def test_normalize_url(url, normalized):
                 "C",
                 ":\\Users\\hello\\testing.git",
                 None,
-                None,
-                "testing",
-                "zkat/windows-files",
+                port=None,
+                name="testing",
+                rev="zkat/windows-files",
             ),
         ),
         (
@@ -226,9 +287,9 @@ def test_normalize_url(url, normalized):
                 "git.example.com",
                 "/sdispater/project/my_repo.git",
                 None,
-                None,
-                "my_repo",
-                None,
+                port=None,
+                name="my_repo",
+                rev=None,
             ),
         ),
         (
@@ -238,8 +299,21 @@ def test_normalize_url(url, normalized):
                 "git.example.com",
                 ":sdispater/project/my_repo.git",
                 "git",
+                port=None,
+                name="my_repo",
+                rev=None,
+            ),
+        ),
+        (
+            "git+https://user:fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a@hostname/project/blah.git",
+            ParsedUrl(
+                "https",
+                "hostname",
+                "/project/blah.git",
+                "user",
+                "fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a",
                 None,
-                "my_repo",
+                "blah",
                 None,
             ),
         ),
@@ -255,6 +329,7 @@ def test_parse_url(url, parsed):
     assert parsed.rev == result.rev
     assert parsed.url == result.url
     assert parsed.user == result.user
+    assert parsed.password == result.password
 
 
 def test_parse_url_should_fail():

From 9ff7a7e178c18609eda7a1d101ca218150f8ae0e Mon Sep 17 00:00:00 2001
From: finswimmer 
Date: Mon, 5 Oct 2020 20:01:59 +0200
Subject: [PATCH 71/77] change (vcs.git): make all arguments optional for
 `ParsedUrl`

---
 tests/vcs/test_vcs.py | 175 ++++++++++++++++++------------------------
 1 file changed, 73 insertions(+), 102 deletions(-)

diff --git a/tests/vcs/test_vcs.py b/tests/vcs/test_vcs.py
index bd454d897..dc1abba24 100644
--- a/tests/vcs/test_vcs.py
+++ b/tests/vcs/test_vcs.py
@@ -97,11 +97,10 @@ def test_normalize_url(url, normalized):
         (
             "git+ssh://user@hostname:project.git#commit",
             ParsedUrl(
-                "ssh",
-                "hostname",
-                ":project.git",
-                "user",
-                port=None,
+                protocol="ssh",
+                resource="hostname",
+                pathname=":project.git",
+                user="user",
                 name="project",
                 rev="commit",
             ),
@@ -109,11 +108,10 @@ def test_normalize_url(url, normalized):
         (
             "git+http://user@hostname/project/blah.git@commit",
             ParsedUrl(
-                "http",
-                "hostname",
-                "/project/blah.git",
-                "user",
-                port=None,
+                protocol="http",
+                resource="hostname",
+                pathname="/project/blah.git",
+                user="user",
                 name="blah",
                 rev="commit",
             ),
@@ -121,47 +119,40 @@ def test_normalize_url(url, normalized):
         (
             "git+https://user@hostname/project/blah.git",
             ParsedUrl(
-                "https",
-                "hostname",
-                "/project/blah.git",
-                "user",
-                port=None,
+                protocol="https",
+                resource="hostname",
+                pathname="/project/blah.git",
+                user="user",
                 name="blah",
-                rev=None,
             ),
         ),
         (
             "git+https://user@hostname/project~_-.foo/blah~_-.bar.git",
             ParsedUrl(
-                "https",
-                "hostname",
-                "/project~_-.foo/blah~_-.bar.git",
-                "user",
-                port=None,
+                protocol="https",
+                resource="hostname",
+                pathname="/project~_-.foo/blah~_-.bar.git",
+                user="user",
                 name="blah~_-.bar",
-                rev=None,
             ),
         ),
         (
             "git+https://user@hostname:project/blah.git",
             ParsedUrl(
-                "https",
-                "hostname",
-                ":project/blah.git",
-                "user",
-                port=None,
+                protocol="https",
+                resource="hostname",
+                pathname=":project/blah.git",
+                user="user",
                 name="blah",
-                rev=None,
             ),
         ),
         (
             "git+ssh://git@github.com:sdispater/poetry.git#v1.0.27",
             ParsedUrl(
-                "ssh",
-                "github.com",
-                ":sdispater/poetry.git",
-                "git",
-                port=None,
+                protocol="ssh",
+                resource="github.com",
+                pathname=":sdispater/poetry.git",
+                user="git",
                 name="poetry",
                 rev="v1.0.27",
             ),
@@ -169,71 +160,64 @@ def test_normalize_url(url, normalized):
         (
             "git+ssh://git@github.com:/sdispater/poetry.git",
             ParsedUrl(
-                "ssh",
-                "github.com",
-                ":/sdispater/poetry.git",
-                "git",
-                port=None,
+                protocol="ssh",
+                resource="github.com",
+                pathname=":/sdispater/poetry.git",
+                user="git",
                 name="poetry",
-                rev=None,
             ),
         ),
         (
             "git+ssh://git@github.com:org/repo",
             ParsedUrl(
-                "ssh",
-                "github.com",
-                ":org/repo",
-                "git",
-                port=None,
+                protocol="ssh",
+                resource="github.com",
+                pathname=":org/repo",
+                user="git",
                 name="repo",
-                rev=None,
             ),
         ),
         (
             "git+ssh://git@github.com/org/repo",
             ParsedUrl(
-                "ssh",
-                "github.com",
-                "/org/repo",
-                "git",
-                port=None,
+                protocol="ssh",
+                resource="github.com",
+                pathname="/org/repo",
+                user="git",
                 name="repo",
-                rev=None,
             ),
         ),
         (
             "git+ssh://foo:22/some/path",
             ParsedUrl(
-                "ssh", "foo", "/some/path", None, port="22", name="path", rev=None
+                protocol="ssh",
+                resource="foo",
+                pathname="/some/path",
+                port="22",
+                name="path",
             ),
         ),
         (
             "git@github.com:org/repo",
             ParsedUrl(
-                None, "github.com", ":org/repo", "git", port=None, name="repo", rev=None
+                resource="github.com", pathname=":org/repo", user="git", name="repo",
             ),
         ),
         (
             "git+https://github.com/sdispater/pendulum",
             ParsedUrl(
-                "https",
-                "github.com",
-                "/sdispater/pendulum",
-                None,
-                port=None,
+                protocol="https",
+                resource="github.com",
+                pathname="/sdispater/pendulum",
                 name="pendulum",
-                rev=None,
             ),
         ),
         (
             "git+https://github.com/sdispater/pendulum#7a018f2d075b03a73409e8356f9b29c9ad4ea2c5",
             ParsedUrl(
-                "https",
-                "github.com",
-                "/sdispater/pendulum",
-                None,
-                port=None,
+                protocol="https",
+                resource="github.com",
+                pathname="/sdispater/pendulum",
                 name="pendulum",
                 rev="7a018f2d075b03a73409e8356f9b29c9ad4ea2c5",
             ),
@@ -241,11 +225,10 @@ def test_normalize_url(url, normalized):
         (
             "git+ssh://git@git.example.com:b/b.git#v1.0.0",
             ParsedUrl(
-                "ssh",
-                "git.example.com",
-                ":b/b.git",
-                "git",
-                port=None,
+                protocol="ssh",
+                resource="git.example.com",
+                pathname=":b/b.git",
+                user="git",
                 name="b",
                 rev="v1.0.0",
             ),
@@ -253,29 +236,24 @@ def test_normalize_url(url, normalized):
         (
             "git+ssh://git@github.com:sdispater/pendulum.git#foo/bar",
             ParsedUrl(
-                "ssh",
-                "github.com",
-                ":sdispater/pendulum.git",
-                "git",
-                port=None,
+                protocol="ssh",
+                resource="github.com",
+                pathname=":sdispater/pendulum.git",
+                user="git",
                 name="pendulum",
                 rev="foo/bar",
             ),
         ),
         (
             "git+file:///foo/bar.git",
-            ParsedUrl(
-                "file", None, "/foo/bar.git", None, port=None, name="bar", rev=None
-            ),
+            ParsedUrl(protocol="file", pathname="/foo/bar.git", name="bar",),
         ),
         (
             "git+file://C:\\Users\\hello\\testing.git#zkat/windows-files",
             ParsedUrl(
-                "file",
-                "C",
-                ":\\Users\\hello\\testing.git",
-                None,
-                port=None,
+                protocol="file",
+                resource="C",
+                pathname=":\\Users\\hello\\testing.git",
                 name="testing",
                 rev="zkat/windows-files",
             ),
@@ -283,38 +261,31 @@ def test_normalize_url(url, normalized):
         (
             "git+https://git.example.com/sdispater/project/my_repo.git",
             ParsedUrl(
-                "https",
-                "git.example.com",
-                "/sdispater/project/my_repo.git",
-                None,
-                port=None,
+                protocol="https",
+                resource="git.example.com",
+                pathname="/sdispater/project/my_repo.git",
                 name="my_repo",
-                rev=None,
             ),
         ),
         (
             "git+ssh://git@git.example.com:sdispater/project/my_repo.git",
             ParsedUrl(
-                "ssh",
-                "git.example.com",
-                ":sdispater/project/my_repo.git",
-                "git",
-                port=None,
+                protocol="ssh",
+                resource="git.example.com",
+                pathname=":sdispater/project/my_repo.git",
+                user="git",
                 name="my_repo",
-                rev=None,
             ),
         ),
         (
             "git+https://user:fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a@hostname/project/blah.git",
             ParsedUrl(
-                "https",
-                "hostname",
-                "/project/blah.git",
-                "user",
-                "fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a",
-                None,
-                "blah",
-                None,
+                protocol="https",
+                resource="hostname",
+                pathname="/project/blah.git",
+                user="user",
+                password="fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a",
+                name="blah",
             ),
         ),
     ],

From 6bbc2567d19fa1d1773261e586d6dbc374f8c5ff Mon Sep 17 00:00:00 2001
From: finswimmer 
Date: Mon, 5 Oct 2020 20:13:33 +0200
Subject: [PATCH 72/77] new (vcs.git): add property `is_unsafe` to `ParsedUrl`
 to return a boolean whether the ParsedUrl contains a secret like a password

---
 poetry/core/vcs/git.py |  7 +++++++
 tests/vcs/test_vcs.py  | 30 ++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)

diff --git a/poetry/core/vcs/git.py b/poetry/core/vcs/git.py
index b17c9ade1..603f16a07 100644
--- a/poetry/core/vcs/git.py
+++ b/poetry/core/vcs/git.py
@@ -145,6 +145,13 @@ def url(self) -> str:
             "/" + self.pathname.lstrip(":/"),
         )
 
+    @property
+    def is_unsafe(self) -> bool:
+        if self.password is not None:
+            return True
+        else:
+            return False
+
     def format(self) -> str:
         return self.url
 
diff --git a/tests/vcs/test_vcs.py b/tests/vcs/test_vcs.py
index dc1abba24..1d7654dba 100644
--- a/tests/vcs/test_vcs.py
+++ b/tests/vcs/test_vcs.py
@@ -308,3 +308,33 @@ def test_parse_url_should_fail():
 
     with pytest.raises(ValueError):
         ParsedUrl.parse(url)
+
+
+@pytest.mark.parametrize(
+    ["url", "is_unsafe"],
+    [
+        (
+            ParsedUrl(
+                protocol="ssh",
+                resource="git.example.com",
+                pathname=":sdispater/project/my_repo.git",
+                user="git",
+                name="my_repo",
+            ),
+            False,
+        ),
+        (
+            ParsedUrl(
+                protocol="https",
+                resource="hostname",
+                pathname="/project/blah.git",
+                user="user",
+                password="fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a",
+                name="blah",
+            ),
+            True,
+        ),
+    ],
+)
+def test_is_unsafe(url, is_unsafe):
+    assert url.is_unsafe == is_unsafe

From e54923e15b441fd2e8b848361f38fa011b916f64 Mon Sep 17 00:00:00 2001
From: Setu Shah 
Date: Sun, 22 Nov 2020 00:56:01 -0800
Subject: [PATCH 73/77] Add test for x-oauth github url

---
 tests/vcs/test_vcs.py | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/tests/vcs/test_vcs.py b/tests/vcs/test_vcs.py
index 1d7654dba..34c498e26 100644
--- a/tests/vcs/test_vcs.py
+++ b/tests/vcs/test_vcs.py
@@ -85,6 +85,13 @@
                 None,
             ),
         ),
+        (
+            "git+https://fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a:x-oauth-basic@hostname/project/blah.git",
+            GitUrl(
+                "https://fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a:x-oauth-basic@hostname/project/blah.git",
+                None,
+            ),
+        ),
     ],
 )
 def test_normalize_url(url, normalized):
@@ -288,6 +295,17 @@ def test_normalize_url(url, normalized):
                 name="blah",
             ),
         ),
+        (
+            "git+https://fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a:x-oauth-basic@hostname/project/blah.git",
+            ParsedUrl(
+                protocol="https",
+                resource="hostname",
+                pathname="/project/blah.git",
+                user="fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a",
+                password="x-oauth-basic",
+                name="blah",
+            ),
+        ),
     ],
 )
 def test_parse_url(url, parsed):

From e2fd7f4cd3846618154aa941b9e073d012590ce0 Mon Sep 17 00:00:00 2001
From: Setu Shah 
Date: Sun, 22 Nov 2020 00:56:37 -0800
Subject: [PATCH 74/77] Add warning, tests for when password is stored

---
 poetry/core/vcs/git.py | 15 ++++++++++++++-
 tests/vcs/test_vcs.py  | 15 +++++++++++++++
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/poetry/core/vcs/git.py b/poetry/core/vcs/git.py
index 603f16a07..bd985145d 100644
--- a/poetry/core/vcs/git.py
+++ b/poetry/core/vcs/git.py
@@ -1,5 +1,7 @@
+import logging
 import re
 import subprocess
+import warnings
 
 from collections import namedtuple
 from pathlib import Path
@@ -94,6 +96,9 @@
 ]
 
 
+logger = logging.getLogger(__name__)
+
+
 class ParsedUrl:
     def __init__(
         self,
@@ -101,6 +106,7 @@ def __init__(
         resource: Optional[str],
         pathname: Optional[str],
         user: Optional[str],
+        password: Optional[str],
         port: Optional[str],
         name: Optional[str],
         rev: Optional[str],
@@ -114,6 +120,14 @@ def __init__(
         self.name = name
         self.rev = rev
 
+        # Warn if password is stored when adding the dependency.
+        if self.is_unsafe:
+            message = "Password being stored in plain text for dependency '{name}' to pyproject.toml and poetry.lock.".format(
+                name=self.name
+            )
+            warnings.warn(message, Warning)
+            logger.warning(message)
+
     @classmethod
     def parse(cls, url: str) -> "ParsedUrl":
         for pattern in PATTERNS:
@@ -130,7 +144,6 @@ def parse(cls, url: str) -> "ParsedUrl":
                     groups.get("name"),
                     groups.get("rev"),
                 )
-
         raise ValueError('Invalid git url "{}"'.format(url))
 
     @property
diff --git a/tests/vcs/test_vcs.py b/tests/vcs/test_vcs.py
index 34c498e26..625113f8c 100644
--- a/tests/vcs/test_vcs.py
+++ b/tests/vcs/test_vcs.py
@@ -356,3 +356,18 @@ def test_parse_url_should_fail():
 )
 def test_is_unsafe(url, is_unsafe):
     assert url.is_unsafe == is_unsafe
+
+
+@pytest.mark.parametrize(
+    "url",
+    [
+        "git+https://user:fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a@hostname/project/blah.git",
+        "git+https://fafb334-cb038533f851c23d0b63254223Abf72ce4f02987e7064b0c95566699a:x-oauth-basic@hostname/project/blah.git",
+    ],
+)
+def test_is_unsafe_warning(url):
+    with pytest.warns(Warning) as records:
+        parsed_url = ParsedUrl.parse(url)
+        assert parsed_url.password is not None
+        assert parsed_url.is_unsafe
+        assert len(records) == 1

From 710c664419b9f4050ca6df5272b3c3591acef8c1 Mon Sep 17 00:00:00 2001
From: Setu Shah 
Date: Tue, 27 Apr 2021 23:27:44 -0700
Subject: [PATCH 75/77] Set optional args to ParsedUrl to None as default

---
 poetry/core/vcs/git.py | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/poetry/core/vcs/git.py b/poetry/core/vcs/git.py
index bd985145d..799f26192 100644
--- a/poetry/core/vcs/git.py
+++ b/poetry/core/vcs/git.py
@@ -102,14 +102,14 @@
 class ParsedUrl:
     def __init__(
         self,
-        protocol: Optional[str],
-        resource: Optional[str],
-        pathname: Optional[str],
-        user: Optional[str],
-        password: Optional[str],
-        port: Optional[str],
-        name: Optional[str],
-        rev: Optional[str],
+        protocol: Optional[str] = None,
+        resource: Optional[str] = None,
+        pathname: Optional[str] = None,
+        user: Optional[str] = None,
+        password: Optional[str] = None,
+        port: Optional[str] = None,
+        name: Optional[str] = None,
+        rev: Optional[str] = None,
     ):
         self.protocol = protocol
         self.resource = resource

From 75f18a0716b5f77cc30dc16f5992fca356e38925 Mon Sep 17 00:00:00 2001
From: Setu Shah 
Date: Tue, 27 Apr 2021 23:41:09 -0700
Subject: [PATCH 76/77] Add missing format vars for ParsedUrl

---
 poetry/core/vcs/git.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/poetry/core/vcs/git.py b/poetry/core/vcs/git.py
index 799f26192..68f5300c8 100644
--- a/poetry/core/vcs/git.py
+++ b/poetry/core/vcs/git.py
@@ -148,7 +148,7 @@ def parse(cls, url: str) -> "ParsedUrl":
 
     @property
     def url(self) -> str:
-        return "{}{}{}{}{}".format(
+        return "{}{}{}{}{}{}{}".format(
             "{}://".format(self.protocol) if self.protocol else "",
             "{}".format(self.user) if self.user else "",
             ":{}".format(self.password) if self.password else "",

From 15233b31261a6517e7226b193035121f5c5eafb1 Mon Sep 17 00:00:00 2001
From: Setu Shah 
Date: Tue, 27 Apr 2021 23:42:09 -0700
Subject: [PATCH 77/77] Apply black

---
 tests/vcs/test_vcs.py | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/tests/vcs/test_vcs.py b/tests/vcs/test_vcs.py
index 625113f8c..7704c825e 100644
--- a/tests/vcs/test_vcs.py
+++ b/tests/vcs/test_vcs.py
@@ -207,7 +207,10 @@ def test_normalize_url(url, normalized):
         (
             "git@github.com:org/repo",
             ParsedUrl(
-                resource="github.com", pathname=":org/repo", user="git", name="repo",
+                resource="github.com",
+                pathname=":org/repo",
+                user="git",
+                name="repo",
             ),
         ),
         (
@@ -253,7 +256,11 @@ def test_normalize_url(url, normalized):
         ),
         (
             "git+file:///foo/bar.git",
-            ParsedUrl(protocol="file", pathname="/foo/bar.git", name="bar",),
+            ParsedUrl(
+                protocol="file",
+                pathname="/foo/bar.git",
+                name="bar",
+            ),
         ),
         (
             "git+file://C:\\Users\\hello\\testing.git#zkat/windows-files",