From 9c8155c6a15376e41e01f4867f239b0299a8da35 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 13 Oct 2020 14:24:57 +0200 Subject: [PATCH 01/54] Drop Python 2.7/3.5 support validation This change drops support for Python 2.7 and 3.5 in tox, ci and release workflows in preparation for dropping support for these versions in Poetry 1.2.0. --- .cirrus.yml | 8 +++----- .github/workflows/main.yml | 2 +- .github/workflows/release.yml | 15 ++------------- .github/workflows/skip.yml | 2 +- sonnet | 2 -- tox.ini | 2 +- 6 files changed, 8 insertions(+), 23 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index e2341cdf4b7..daf4efc9203 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -7,8 +7,8 @@ test_task: skip: "!changesInclude('.cirrus.yml', 'poetry.lock', 'pyproject.toml', '**.json','**.py')" env: matrix: - - PYTHON: python2.7 - - PYTHON: python3.7 + - PYTHON: python3.6 + - PYTHON: python3.8 python_script: - PYPACKAGE=$(printf '%s' $PYTHON | tr -d '.') - SQLPACKAGE=$(printf '%s-sqlite3' $PYPACKAGE | sed 's/thon//') @@ -31,7 +31,6 @@ release_task: env: GITHUB_TOKEN: ENCRYPTED[2b573a2d28a03523ac6fb5b3c2f513a41c0a98db81e40e50e1d103b171f85c57e58ae38d957499dbf7fd7635cfcfd7be] PYTHON: python3.8 - PYTHON27: python2.7 PYTHON36: python3.6 PYTHON37: python3.7 PYTHON38: python3.8 @@ -40,9 +39,8 @@ release_task: - image_family: freebsd-12-1-snap - image_family: freebsd-13-0-snap - image_family: freebsd-11-4-snap - python_script: pkg install -y curl bash jq python3 python27 python36 python37 python38 + python_script: pkg install -y curl bash jq python3 python36 python37 python38 pip_script: - - python2.7 -m ensurepip - python3.6 -m ensurepip - python3.7 -m ensurepip - python3.8 -m ensurepip diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 32379cdf8b2..4c9bdd876c7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: os: [Ubuntu, MacOS, Windows] - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] + python-version: [3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 43e7d357cd1..f6a15041ae0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,14 +53,10 @@ jobs: poetry install --no-dev - name: Preparing Python executables run: | - curl -L https://github.com/sdispater/python-binaries/releases/download/2.7.17/python-2.7.17.macos.tar.xz -o python-2.7.17.tar.xz - curl -L https://github.com/sdispater/python-binaries/releases/download/3.5.9/python-3.5.9.macos.tar.xz -o python-3.5.9.tar.xz curl -L https://github.com/sdispater/python-binaries/releases/download/3.6.8/python-3.6.8.macos.tar.xz -o python-3.6.8.tar.xz curl -L https://github.com/sdispater/python-binaries/releases/download/3.7.6/python-3.7.6.macos.tar.xz -o python-3.7.6.tar.xz curl -L https://github.com/sdispater/python-binaries/releases/download/3.8.3/python-3.8.3.macos.tar.xz -o python-3.8.3.tar.xz curl -L https://github.com/sdispater/python-binaries/releases/download/3.9.0b4/python-3.9.0b4.macos.tar.xz -o python-3.9.0b4.tar.xz - tar -zxf python-2.7.17.tar.xz - tar -zxf python-3.5.9.tar.xz tar -zxf python-3.6.8.tar.xz tar -zxf python-3.7.6.tar.xz tar -zxf python-3.8.3.tar.xz @@ -68,7 +64,7 @@ jobs: - name: Build specific release run: | source $HOME/.poetry/env - poetry run python sonnet make release --ansi -P "2.7:python-2.7.17/bin/python" -P "3.5:python-3.5.9/bin/python" -P "3.6:python-3.6.8/bin/python" -P "3.7:python-3.7.6/bin/python" -P "3.8:python-3.8.3/bin/python" -P "3.9:python-3.9.0b4/bin/python" + poetry run python sonnet make release --ansi -P "3.6:python-3.6.8/bin/python" -P "3.7:python-3.7.6/bin/python" -P "3.8:python-3.8.3/bin/python" -P "3.9:python-3.9.0b4/bin/python" - name: Upload release file uses: actions/upload-artifact@v1 with: @@ -104,21 +100,14 @@ jobs: poetry install --no-dev - name: Preparing Python executables run: | - Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/2.7.17/python-2.7.17.windows.tar.xz -O python-2.7.17.tar.xz - Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/3.5.4/python-3.5.4.windows.tar.xz -O python-3.5.4.tar.xz Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/3.6.8/python-3.6.8.windows.tar.xz -O python-3.6.8.tar.xz Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/3.7.6/python-3.7.6.windows.tar.xz -O python-3.7.6.tar.xz Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/3.8.3/python-3.8.3.windows.tar.xz -O python-3.8.3.tar.xz Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/3.9.0b4/python-3.9.0b4.windows.tar.xz -O python-3.9.0b4.tar.xz - 7z x python-2.7.17.tar.xz - 7z x python-3.5.4.tar.xz 7z x python-3.6.8.tar.xz 7z x python-3.7.6.tar.xz 7z x python-3.8.3.tar.xz 7z x python-3.9.0b4.tar.xz - 7z x python-2.7.17.tar - 7z x python-3.4.4.tar - 7z x python-3.5.4.tar 7z x python-3.6.8.tar 7z x python-3.7.6.tar 7z x python-3.8.3.tar @@ -126,7 +115,7 @@ jobs: - name: Build specific release run: | $env:Path += ";$env:Userprofile\.poetry\bin" - poetry run python sonnet make release --ansi -P "2.7:python-2.7.17\python.exe" -P "3.5:python-3.5.4\python.exe" -P "3.6:python-3.6.8\python.exe" -P "3.7:python-3.7.6\python.exe" -P "3.8:python-3.8.3\python.exe" -P "3.9:python-3.9.0b4\python.exe" + poetry run python sonnet make release --ansi -P "3.6:python-3.6.8\python.exe" -P "3.7:python-3.7.6\python.exe" -P "3.8:python-3.8.3\python.exe" -P "3.9:python-3.9.0b4\python.exe" - name: Upload release file uses: actions/upload-artifact@v1 with: diff --git a/.github/workflows/skip.yml b/.github/workflows/skip.yml index aea8d88274c..752ed7fa911 100644 --- a/.github/workflows/skip.yml +++ b/.github/workflows/skip.yml @@ -32,6 +32,6 @@ jobs: strategy: matrix: os: [Ubuntu, MacOS, Windows] - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] + python-version: [3.6, 3.7, 3.8, 3.9] steps: - run: exit 0 diff --git a/sonnet b/sonnet index ecfda99037b..facbe34e280 100755 --- a/sonnet +++ b/sonnet @@ -25,8 +25,6 @@ class MakeReleaseCommand(Command): """ PYTHON = { - "2.7": "python2.7", - "3.5": "python3.5", "3.6": "python3.6", "3.7": "python3.7", "3.8": "python3.8", diff --git a/tox.ini b/tox.ini index 17834f2e709..722c0e1adf1 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, py39, doc +envlist = py36, py37, py38, py39, doc [testenv] whitelist_externals = poetry From cb34c3d097ad21aee5d1731a739b0c70f5cd351d Mon Sep 17 00:00:00 2001 From: finswimmer Date: Fri, 16 Oct 2020 14:51:25 +0200 Subject: [PATCH 02/54] config: support virtualenv `--always-copy` option Mitigates: #3134 --- docs/docs/configuration.md | 7 +++++ poetry/config/config.py | 19 ++++++++++-- poetry/console/commands/config.py | 5 ++++ poetry/utils/env.py | 41 ++++++++++++++++++-------- tests/console/commands/env/helpers.py | 4 ++- tests/console/commands/env/test_use.py | 4 ++- tests/console/commands/test_config.py | 3 ++ tests/utils/test_env.py | 32 +++++++++++++++----- 8 files changed, 90 insertions(+), 25 deletions(-) diff --git a/docs/docs/configuration.md b/docs/docs/configuration.md index 0378f2fca9e..782c2f9ac87 100644 --- a/docs/docs/configuration.md +++ b/docs/docs/configuration.md @@ -34,6 +34,7 @@ which will give you something similar to this: cache-dir = "/path/to/cache/directory" virtualenvs.create = true virtualenvs.in-project = null +virtualenvs.options.always-copy = true virtualenvs.path = "{cache-dir}/virtualenvs" # /path/to/cache/directory/virtualenvs ``` @@ -128,6 +129,12 @@ If not set explicitly (default), `poetry` will use the virtualenv from the `.ven Directory where virtual environments will be created. Defaults to `{cache-dir}/virtualenvs` (`{cache-dir}\virtualenvs` on Windows). +### `virtualenvs.options.always-copy`: boolean + +If set to `true` the `--always-copy` parameter is passed to `virtualenv` on creation of the venv. Thus all needed files are copied into the venv instead of symlinked. +Defaults to `false`. + + ### `repositories.`: string Set a new alternative repository. See [Repositories](/docs/repositories/) for more information. diff --git a/poetry/config/config.py b/poetry/config/config.py index be585575c05..d1fb421e53c 100644 --- a/poetry/config/config.py +++ b/poetry/config/config.py @@ -36,6 +36,7 @@ class Config(object): "create": True, "in-project": None, "path": os.path.join("{cache-dir}", "virtualenvs"), + "options": {"always-copy": False}, }, "experimental": {"new-installer": True}, } @@ -87,7 +88,11 @@ def _all(config, parent_key=""): for key in config: value = self.get(parent_key + key) if isinstance(value, dict): - all_[key] = _all(config[key], parent_key=key + ".") + if parent_key != "": + current_parent = parent_key + key + "." + else: + current_parent = key + "." + all_[key] = _all(config[key], parent_key=current_parent) continue all_[key] = value @@ -131,14 +136,22 @@ def process(self, value): # type: (Any) -> Any return re.sub(r"{(.+?)}", lambda m: self.get(m.group(1)), value) def _get_validator(self, name): # type: (str) -> Callable - if name in {"virtualenvs.create", "virtualenvs.in-project"}: + if name in { + "virtualenvs.create", + "virtualenvs.in-project", + "virtualenvs.options.always-copy", + }: return boolean_validator if name == "virtualenvs.path": return str def _get_normalizer(self, name): # type: (str) -> Callable - if name in {"virtualenvs.create", "virtualenvs.in-project"}: + if name in { + "virtualenvs.create", + "virtualenvs.in-project", + "virtualenvs.options.always-copy", + }: return boolean_normalizer if name == "virtualenvs.path": diff --git a/poetry/console/commands/config.py b/poetry/console/commands/config.py index 70b6c7673f1..52e1cf125ab 100644 --- a/poetry/console/commands/config.py +++ b/poetry/console/commands/config.py @@ -64,6 +64,11 @@ def unique_config_values(self): boolean_normalizer, True, ), + "virtualenvs.options.always-copy": ( + boolean_validator, + boolean_normalizer, + False, + ), } return unique_config_values diff --git a/poetry/utils/env.py b/poetry/utils/env.py index ccd855b5c60..0e3df320d6a 100644 --- a/poetry/utils/env.py +++ b/poetry/utils/env.py @@ -642,7 +642,11 @@ def create_venv( "Creating virtualenv {} in {}".format(name, str(venv_path)) ) - self.build_venv(venv, executable=executable) + self.build_venv( + venv, + executable=executable, + flags=self._poetry.config.get("virtualenvs.options"), + ) else: if force: if not env.is_sane(): @@ -655,7 +659,11 @@ def create_venv( "Recreating virtualenv {} in {}".format(name, str(venv)) ) self.remove_venv(venv) - self.build_venv(venv, executable=executable) + self.build_venv( + venv, + executable=executable, + flags=self._poetry.config.get("virtualenvs.options"), + ) elif io.is_very_verbose(): io.write_line("Virtualenv {} already exists.".format(name)) @@ -679,19 +687,26 @@ def create_venv( @classmethod def build_venv( - cls, path, executable=None - ): # type: (Union[Path,str], Optional[Union[str, Path]]) -> virtualenv.run.session.Session + cls, path, executable=None, flags=None + ): # type: (Union[Path,str], Optional[Union[str, Path]], Dict[str, bool]) -> virtualenv.run.session.Session + flags = flags or {} + if isinstance(executable, Path): executable = executable.resolve().as_posix() - return virtualenv.cli_run( - [ - "--no-download", - "--no-periodic-update", - "--python", - executable or sys.executable, - str(path), - ] - ) + + args = [ + "--no-download", + "--no-periodic-update", + "--python", + executable or sys.executable, + str(path), + ] + + for flag, value in flags.items(): + if value is True: + args.insert(0, "--{}".format(flag)) + + return virtualenv.cli_run(args) @classmethod def remove_venv(cls, path): # type: (Union[Path,str]) -> None diff --git a/tests/console/commands/env/helpers.py b/tests/console/commands/env/helpers.py index 17d4c2ac274..1c7e64dc17e 100644 --- a/tests/console/commands/env/helpers.py +++ b/tests/console/commands/env/helpers.py @@ -5,7 +5,9 @@ from poetry.utils._compat import Path -def build_venv(path, executable=None): # type: (Union[Path,str], Optional[str]) -> () +def build_venv( + path, executable=None, flags=None +): # type: (Union[Path,str], Optional[str], bool) -> () Path(path).mkdir(parents=True, exist_ok=True) diff --git a/tests/console/commands/env/test_use.py b/tests/console/commands/env/test_use.py index 563e0ac7ffe..b3ea3458d06 100644 --- a/tests/console/commands/env/test_use.py +++ b/tests/console/commands/env/test_use.py @@ -50,7 +50,9 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( tester.execute("3.7") venv_py37 = venv_cache / "{}-py3.7".format(venv_name) - mock_build_env.assert_called_with(venv_py37, executable="python3.7") + mock_build_env.assert_called_with( + venv_py37, executable="python3.7", flags={"always-copy": False} + ) envs_file = TOMLFile(venv_cache / "envs.toml") assert envs_file.exists() diff --git a/tests/console/commands/test_config.py b/tests/console/commands/test_config.py index bb13a268e2f..9923f1b77af 100644 --- a/tests/console/commands/test_config.py +++ b/tests/console/commands/test_config.py @@ -30,6 +30,7 @@ def test_list_displays_default_value_if_not_set(tester, config): experimental.new-installer = true virtualenvs.create = true virtualenvs.in-project = null +virtualenvs.options.always-copy = false virtualenvs.path = {path} # /foo{sep}virtualenvs """.format( path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep @@ -47,6 +48,7 @@ def test_list_displays_set_get_setting(tester, config): experimental.new-installer = true virtualenvs.create = false virtualenvs.in-project = null +virtualenvs.options.always-copy = false virtualenvs.path = {path} # /foo{sep}virtualenvs """.format( path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep @@ -86,6 +88,7 @@ def test_list_displays_set_get_local_setting(tester, config): experimental.new-installer = true virtualenvs.create = false virtualenvs.in-project = null +virtualenvs.options.always-copy = false virtualenvs.path = {path} # /foo{sep}virtualenvs """.format( path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py index 3779623839f..8c816a2d32a 100644 --- a/tests/utils/test_env.py +++ b/tests/utils/test_env.py @@ -118,7 +118,9 @@ def test_env_get_venv_with_venv_folder_present( assert venv.path == in_project_venv_dir -def build_venv(path, executable=None): # type: (Union[Path,str], Optional[str]) -> () +def build_venv( + path, executable=None, flags=None +): # type: (Union[Path,str], Optional[str], bool) -> () os.mkdir(str(path)) @@ -156,7 +158,9 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( venv_name = EnvManager.generate_env_name("simple-project", str(poetry.file.parent)) m.assert_called_with( - Path(tmp_dir) / "{}-py3.7".format(venv_name), executable="python3.7" + Path(tmp_dir) / "{}-py3.7".format(venv_name), + executable="python3.7", + flags={"always-copy": False}, ) envs_file = TOMLFile(Path(tmp_dir) / "envs.toml") @@ -274,7 +278,9 @@ def test_activate_activates_different_virtualenv_with_envs_file( env = manager.activate("python3.6", NullIO()) m.assert_called_with( - Path(tmp_dir) / "{}-py3.6".format(venv_name), executable="python3.6" + Path(tmp_dir) / "{}-py3.6".format(venv_name), + executable="python3.6", + flags={"always-copy": False}, ) assert envs_file.exists() @@ -326,7 +332,9 @@ def test_activate_activates_recreates_for_different_patch( env = manager.activate("python3.7", NullIO()) build_venv_m.assert_called_with( - Path(tmp_dir) / "{}-py3.7".format(venv_name), executable="python3.7" + Path(tmp_dir) / "{}-py3.7".format(venv_name), + executable="python3.7", + flags={"always-copy": False}, ) remove_venv_m.assert_called_with(Path(tmp_dir) / "{}-py3.7".format(venv_name)) @@ -654,7 +662,9 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_ manager.create_venv(NullIO()) m.assert_called_with( - Path("/foo/virtualenvs/{}-py3.7".format(venv_name)), executable="python3" + Path("/foo/virtualenvs/{}-py3.7".format(venv_name)), + executable="python3", + flags={"always-copy": False}, ) @@ -678,7 +688,9 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific manager.create_venv(NullIO()) m.assert_called_with( - Path("/foo/virtualenvs/{}-py3.9".format(venv_name)), executable="python3.9" + Path("/foo/virtualenvs/{}-py3.9".format(venv_name)), + executable="python3.9", + flags={"always-copy": False}, ) @@ -767,6 +779,7 @@ def test_create_venv_uses_patch_version_to_detect_compatibility( ) ), executable=None, + flags={"always-copy": False}, ) @@ -804,6 +817,7 @@ def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable( ) ), executable="python{}.{}".format(version.major, version.minor - 1), + flags={"always-copy": False}, ) @@ -834,7 +848,11 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir( manager.activate("python3.7", NullIO()) - m.assert_called_with(poetry.file.parent / ".venv", executable="python3.7") + m.assert_called_with( + poetry.file.parent / ".venv", + executable="python3.7", + flags={"always-copy": False}, + ) envs_file = TOMLFile(Path(tmp_dir) / "virtualenvs" / "envs.toml") assert not envs_file.exists() From 9c085a585c093a722717fec318bfc6610e93a9f1 Mon Sep 17 00:00:00 2001 From: Arjan Keeman Date: Fri, 16 Oct 2020 14:03:57 +0200 Subject: [PATCH 03/54] Ensure editable builder generate valid scripts --- poetry/masonry/builders/editable.py | 2 +- tests/fixtures/simple_project/pyproject.toml | 1 + tests/masonry/builders/test_editable_builder.py | 16 ++++++++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/poetry/masonry/builders/editable.py b/poetry/masonry/builders/editable.py index 11bda4efcbd..99136ecbd3e 100644 --- a/poetry/masonry/builders/editable.py +++ b/poetry/masonry/builders/editable.py @@ -139,7 +139,7 @@ def _add_scripts(self): for script in scripts: name, script = script.split(" = ") module, callable_ = script.split(":") - callable_holder = callable_.rsplit(".", 1)[0] + callable_holder = callable_.split(".", 1)[0] script_file = scripts_path.joinpath(name) self._debug( diff --git a/tests/fixtures/simple_project/pyproject.toml b/tests/fixtures/simple_project/pyproject.toml index 0b41162c6f4..0fd938e41a0 100644 --- a/tests/fixtures/simple_project/pyproject.toml +++ b/tests/fixtures/simple_project/pyproject.toml @@ -27,3 +27,4 @@ python = "~2.7 || ^3.4" [tool.poetry.scripts] foo = "foo:bar" baz = "bar:baz.boom.bim" +fox = "fuz.foo:bar.baz" diff --git a/tests/masonry/builders/test_editable_builder.py b/tests/masonry/builders/test_editable_builder.py index e2aac5beb41..3aee74e7c77 100644 --- a/tests/masonry/builders/test_editable_builder.py +++ b/tests/masonry/builders/test_editable_builder.py @@ -92,7 +92,7 @@ def test_builder_installs_proper_files_for_standard_packages(simple_poetry, tmp_ assert "poetry" == dist_info.joinpath("INSTALLER").read_text() assert ( - "[console_scripts]\nbaz=bar:baz.boom.bim\nfoo=foo:bar\n\n" + "[console_scripts]\nbaz=bar:baz.boom.bim\nfoo=foo:bar\nfox=fuz.foo:bar.baz\n\n" == dist_info.joinpath("entry_points.txt").read_text() ) @@ -140,7 +140,7 @@ def test_builder_installs_proper_files_for_standard_packages(simple_poetry, tmp_ baz_script = """\ #!{python} -from bar import baz.boom +from bar import baz if __name__ == '__main__': baz.boom.bim() @@ -162,6 +162,18 @@ def test_builder_installs_proper_files_for_standard_packages(simple_poetry, tmp_ assert foo_script == tmp_venv._bin_dir.joinpath("foo").read_text() + fox_script = """\ +#!{python} +from fuz.foo import bar + +if __name__ == '__main__': + bar.baz() +""".format( + python=tmp_venv._bin("python") + ) + + assert fox_script == tmp_venv._bin_dir.joinpath("fox").read_text() + def test_builder_falls_back_on_setup_and_pip_for_packages_with_build_scripts( extended_poetry, From 559687837cca46fba773c2d45be12387a1490e8f Mon Sep 17 00:00:00 2001 From: Cere Blanco <743526+cereblanco@users.noreply.github.com> Date: Mon, 19 Oct 2020 20:16:31 +0800 Subject: [PATCH 04/54] tests: re-enable lock --no-update cases on windows --- tests/console/commands/test_lock.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index 823a8ba4beb..44c72967b32 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -1,5 +1,4 @@ import shutil -import sys import pytest @@ -26,9 +25,6 @@ def poetry_with_old_lockfile(fixture_dir, source_dir): return poetry -@pytest.mark.skipif( - sys.platform == "win32", reason="does not work on windows under ci environments" -) def test_lock_no_update(command_tester_factory, poetry_with_old_lockfile, http): http.disable() From a66033bd8fa99488fccd21b90afb006ebe430706 Mon Sep 17 00:00:00 2001 From: Etty Date: Mon, 19 Oct 2020 22:49:59 +0100 Subject: [PATCH 05/54] add: fix revert for dry-run and interrupts This change correctly reverts pyproject.toml changes for dry runs as well as keyboard interrupts. Resolves: #2933 #318 --- poetry/console/commands/add.py | 33 +++++++++++++++--------------- tests/console/commands/test_add.py | 23 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/poetry/console/commands/add.py b/poetry/console/commands/add.py index 3ad77ad686b..29cf07d2a26 100644 --- a/poetry/console/commands/add.py +++ b/poetry/console/commands/add.py @@ -100,7 +100,6 @@ def handle(self): packages = [name for name in packages if name not in existing_packages] if not packages: - self.poetry.file.write(content) self.line("Nothing to add.") return 0 @@ -152,29 +151,29 @@ def handle(self): poetry_content[section][_constraint["name"]] = constraint - # Write new content - self.poetry.file.write(content) + try: + # Write new content + self.poetry.file.write(content) - # Cosmetic new line - self.line("") + # Cosmetic new line + self.line("") - # Update packages - self.reset_poetry() + # Update packages + self.reset_poetry() - self._installer.set_package(self.poetry.package) - self._installer.dry_run(self.option("dry-run")) - self._installer.verbose(self._io.is_verbose()) - self._installer.update(True) - if self.option("lock"): - self._installer.lock() + self._installer.set_package(self.poetry.package) + self._installer.dry_run(self.option("dry-run")) + self._installer.verbose(self._io.is_verbose()) + self._installer.update(True) + if self.option("lock"): + self._installer.lock() - self._installer.whitelist([r["name"] for r in requirements]) + self._installer.whitelist([r["name"] for r in requirements]) - try: status = self._installer.run() - except Exception: + except BaseException: + # Using BaseException here as some exceptions, eg: KeyboardInterrupt, do not inherit from Exception self.poetry.file.write(original_content) - raise if status != 0 or self.option("dry-run"): diff --git a/tests/console/commands/test_add.py b/tests/console/commands/test_add.py index 09fae525aec..19998592ac3 100644 --- a/tests/console/commands/test_add.py +++ b/tests/console/commands/test_add.py @@ -1630,3 +1630,26 @@ def test_add_with_lock_old_installer(app, repo, installer, old_tester): """ assert expected == old_tester.io.fetch_output() + + +def test_add_keyboard_interrupt_restore_content(app, repo, installer, tester, mocker): + mocker.patch( + "poetry.installation.installer.Installer.run", side_effect=KeyboardInterrupt() + ) + original_content = app.poetry.file.read() + + repo.add_package(get_package("cachy", "0.2.0")) + + tester.execute("cachy --dry-run") + + assert original_content == app.poetry.file.read() + + +def test_dry_run_restore_original_content(app, repo, installer, tester): + original_content = app.poetry.file.read() + + repo.add_package(get_package("cachy", "0.2.0")) + + tester.execute("cachy --dry-run") + + assert original_content == app.poetry.file.read() From 68d6939f2e9cac1178fc5ffbcc3bac7a9db43fce Mon Sep 17 00:00:00 2001 From: Etty Date: Mon, 19 Oct 2020 22:57:47 +0100 Subject: [PATCH 06/54] export: use --trusted-host for non HTTPS index Resolves: #1894 --- poetry/utils/exporter.py | 4 ++++ tests/utils/test_exporter.py | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/poetry/utils/exporter.py b/poetry/utils/exporter.py index 5f2d6303817..f7c40f6975f 100644 --- a/poetry/utils/exporter.py +++ b/poetry/utils/exporter.py @@ -7,6 +7,7 @@ from poetry.poetry import Poetry from poetry.utils._compat import Path from poetry.utils._compat import decode +from poetry.utils._compat import urlparse class Exporter(object): @@ -139,6 +140,9 @@ def _export_requirements_txt( url = ( repository.authenticated_url if with_credentials else repository.url ) + parsed_url = urlparse.urlsplit(url) + if parsed_url.scheme == "http": + indexes_header += "--trusted-host {}\n".format(parsed_url.netloc) indexes_header += "--extra-index-url {}\n".format(url) content = indexes_header + "\n" + content diff --git a/tests/utils/test_exporter.py b/tests/utils/test_exporter.py index d810bb8b08b..c5ce0214ff7 100644 --- a/tests/utils/test_exporter.py +++ b/tests/utils/test_exporter.py @@ -972,6 +972,52 @@ def test_exporter_exports_requirements_txt_with_legacy_packages(tmp_dir, poetry) assert expected == content +def test_exporter_exports_requirements_txt_with_legacy_packages_trusted_host( + tmp_dir, poetry +): + poetry.pool.add_repository(LegacyRepository("custom", "http://example.com/simple",)) + poetry.locker.mock_lock_data( + { + "package": [ + { + "name": "bar", + "version": "4.5.6", + "category": "dev", + "optional": False, + "python-versions": "*", + "source": { + "type": "legacy", + "url": "http://example.com/simple", + "reference": "", + }, + }, + ], + "metadata": { + "python-versions": "*", + "content-hash": "123456789", + "hashes": {"bar": ["67890"]}, + }, + } + ) + set_package_requires(poetry) + exporter = Exporter(poetry) + + exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt", dev=True) + + with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: + content = f.read() + + expected = """\ +--trusted-host example.com +--extra-index-url http://example.com/simple + +bar==4.5.6 \\ + --hash=sha256:67890 +""" + + assert expected == content + + @pytest.mark.parametrize( ("dev", "expected"), [ From a9387815dbb6297c8caf51a37b4a466269029e49 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Mon, 5 Oct 2020 19:26:59 +0200 Subject: [PATCH 07/54] config: add support for installer.parallel This change allows users to disable parallel execution while using the new installer. Resolves: #3087 --- docs/docs/configuration.md | 9 +++++++++ poetry/config/config.py | 3 +++ poetry/console/commands/config.py | 1 + poetry/installation/executor.py | 5 ++++- tests/config/test_config.py | 27 ++++++++++++++++++++------- tests/console/commands/test_config.py | 27 +++++++++++++++++++++++++++ 6 files changed, 64 insertions(+), 8 deletions(-) diff --git a/docs/docs/configuration.md b/docs/docs/configuration.md index 782c2f9ac87..72310886ca7 100644 --- a/docs/docs/configuration.md +++ b/docs/docs/configuration.md @@ -103,6 +103,15 @@ Defaults to one of the following directories: - Windows: `C:\Users\\AppData\Local\pypoetry\Cache` - Unix: `~/.cache/pypoetry` +### `installer.parallel`: boolean + +Use parallel execution when using the new (`>=1.1.0`) installer. +Defaults to `true`. + +!!!note: + This configuration will be ignored, and parallel execution disabled when running + Python 2.7 under Windows. + ### `virtualenvs.create`: boolean Create a new virtual environment if one doesn't already exist. diff --git a/poetry/config/config.py b/poetry/config/config.py index d1fb421e53c..6f18127e8ff 100644 --- a/poetry/config/config.py +++ b/poetry/config/config.py @@ -39,6 +39,7 @@ class Config(object): "options": {"always-copy": False}, }, "experimental": {"new-installer": True}, + "installer": {"parallel": True}, } def __init__( @@ -140,6 +141,7 @@ def _get_validator(self, name): # type: (str) -> Callable "virtualenvs.create", "virtualenvs.in-project", "virtualenvs.options.always-copy", + "installer.parallel", }: return boolean_validator @@ -151,6 +153,7 @@ def _get_normalizer(self, name): # type: (str) -> Callable "virtualenvs.create", "virtualenvs.in-project", "virtualenvs.options.always-copy", + "installer.parallel", }: return boolean_normalizer diff --git a/poetry/console/commands/config.py b/poetry/console/commands/config.py index 52e1cf125ab..934b2c818eb 100644 --- a/poetry/console/commands/config.py +++ b/poetry/console/commands/config.py @@ -69,6 +69,7 @@ def unique_config_values(self): boolean_normalizer, False, ), + "installer.parallel": (boolean_validator, boolean_normalizer, True,), } return unique_config_values diff --git a/poetry/installation/executor.py b/poetry/installation/executor.py index a3184b718d5..a65dbb4a0e1 100644 --- a/poetry/installation/executor.py +++ b/poetry/installation/executor.py @@ -32,7 +32,7 @@ class Executor(object): - def __init__(self, env, pool, config, io, parallel=True): + def __init__(self, env, pool, config, io, parallel=None): self._env = env self._io = io self._dry_run = False @@ -42,6 +42,9 @@ def __init__(self, env, pool, config, io, parallel=True): self._chef = Chef(config, self._env) self._chooser = Chooser(pool, self._env) + if parallel is None: + parallel = config.get("installer.parallel", True) + if parallel and not (PY2 and WINDOWS): # This should be directly handled by ThreadPoolExecutor # however, on some systems the number of CPUs cannot be determined diff --git a/tests/config/test_config.py b/tests/config/test_config.py index 07373ade0b4..4bd0cd048da 100644 --- a/tests/config/test_config.py +++ b/tests/config/test_config.py @@ -1,16 +1,29 @@ import os +import pytest -def test_config_get_default_value(config): - assert config.get("virtualenvs.create") is True + +@pytest.mark.parametrize( + ("name", "value"), [("installer.parallel", True), ("virtualenvs.create", True)] +) +def test_config_get_default_value(config, name, value): + assert config.get(name) is value def test_config_get_processes_depended_on_values(config): assert os.path.join("/foo", "virtualenvs") == config.get("virtualenvs.path") -def test_config_get_from_environment_variable(config, environ): - assert config.get("virtualenvs.create") - - os.environ["POETRY_VIRTUALENVS_CREATE"] = "false" - assert not config.get("virtualenvs.create") +@pytest.mark.parametrize( + ("name", "env_value", "value"), + [ + ("installer.parallel", "true", True), + ("installer.parallel", "false", False), + ("virtualenvs.create", "true", True), + ("virtualenvs.create", "false", False), + ], +) +def test_config_get_from_environment_variable(config, environ, name, env_value, value): + env_var = "POETRY_{}".format("_".join(k.upper() for k in name.split("."))) + os.environ[env_var] = env_value + assert config.get(name) is value diff --git a/tests/console/commands/test_config.py b/tests/console/commands/test_config.py index 9923f1b77af..4d0b9ada0c8 100644 --- a/tests/console/commands/test_config.py +++ b/tests/console/commands/test_config.py @@ -6,6 +6,8 @@ from poetry.config.config_source import ConfigSource from poetry.core.pyproject import PyProjectException from poetry.factory import Factory +from poetry.utils._compat import PY2 +from poetry.utils._compat import WINDOWS @pytest.fixture() @@ -28,6 +30,7 @@ def test_list_displays_default_value_if_not_set(tester, config): expected = """cache-dir = "/foo" experimental.new-installer = true +installer.parallel = true virtualenvs.create = true virtualenvs.in-project = null virtualenvs.options.always-copy = false @@ -46,6 +49,7 @@ def test_list_displays_set_get_setting(tester, config): expected = """cache-dir = "/foo" experimental.new-installer = true +installer.parallel = true virtualenvs.create = false virtualenvs.in-project = null virtualenvs.options.always-copy = false @@ -86,6 +90,7 @@ def test_list_displays_set_get_local_setting(tester, config): expected = """cache-dir = "/foo" experimental.new-installer = true +installer.parallel = true virtualenvs.create = false virtualenvs.in-project = null virtualenvs.options.always-copy = false @@ -122,3 +127,25 @@ def test_set_cert(tester, auth_config_source, mocker): tester.execute("certificates.foo.cert path/to/ca.pem") assert "path/to/ca.pem" == auth_config_source.config["certificates"]["foo"]["cert"] + + +def test_config_installer_parallel(tester, command_tester_factory): + serial_enforced = PY2 and WINDOWS + + tester.execute("--local installer.parallel") + assert tester.io.fetch_output().strip() == "true" + + workers = command_tester_factory( + "install" + )._command._installer._executor._max_workers + assert workers > 1 or (serial_enforced and workers == 1) + + tester.io.clear_output() + tester.execute("--local installer.parallel false") + tester.execute("--local installer.parallel") + assert tester.io.fetch_output().strip() == "false" + + workers = command_tester_factory( + "install" + )._command._installer._executor._max_workers + assert workers == 1 From 4c81bcc60c99963fc67cea41883913a3d3a4cc65 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 6 Oct 2020 21:07:04 +0200 Subject: [PATCH 08/54] utils/env: better support system site packages dir This change improves handling of site-packages under system env, by gracefully handling fallbacks to user site when required and possible. Resolves: #3079 --- poetry/installation/pip_installer.py | 4 +- poetry/masonry/builders/editable.py | 61 +++++---- poetry/utils/env.py | 129 +++++++++++++++++- poetry/utils/helpers.py | 5 + tests/installation/test_pip_installer.py | 4 +- .../masonry/builders/test_editable_builder.py | 10 +- tests/utils/test_env.py | 4 +- tests/utils/test_env_site.py | 41 ++++++ 8 files changed, 218 insertions(+), 40 deletions(-) create mode 100644 tests/utils/test_env_site.py diff --git a/poetry/installation/pip_installer.py b/poetry/installation/pip_installer.py index f5de6642f2d..e44d7a61297 100644 --- a/poetry/installation/pip_installer.py +++ b/poetry/installation/pip_installer.py @@ -113,7 +113,9 @@ def remove(self, package): raise # This is a workaround for https://github.com/pypa/pip/issues/4176 - nspkg_pth_file = self._env.site_packages / "{}-nspkg.pth".format(package.name) + nspkg_pth_file = self._env.site_packages.path / "{}-nspkg.pth".format( + package.name + ) if nspkg_pth_file.exists(): nspkg_pth_file.unlink() diff --git a/poetry/masonry/builders/editable.py b/poetry/masonry/builders/editable.py index 99136ecbd3e..c31f657b656 100644 --- a/poetry/masonry/builders/editable.py +++ b/poetry/masonry/builders/editable.py @@ -94,7 +94,6 @@ def _setup_build(self): os.remove(str(setup)) def _add_pth(self): - pth_file = Path(self._module.name).with_suffix(".pth") paths = set() for include in self._module.includes: if isinstance(include, PackageInclude) and ( @@ -106,29 +105,25 @@ def _add_pth(self): for path in paths: content += decode(path + os.linesep) - for site_package in [self._env.site_packages, self._env.usersite]: - if not site_package: - continue - - try: - site_package.mkdir(parents=True, exist_ok=True) - path = site_package.joinpath(pth_file) - self._debug( - " - Adding {} to {} for {}".format( - path.name, site_package, self._poetry.file.parent - ) + pth_file = Path(self._module.name).with_suffix(".pth") + try: + pth_file = self._env.site_packages.write_text( + pth_file, content, encoding="utf-8" + ) + self._debug( + " - Adding {} to {} for {}".format( + pth_file.name, pth_file.parent, self._poetry.file.parent ) - path.write_text(content, encoding="utf-8") - return [path] - except PermissionError: - self._debug("- {} is not writable trying next available site") - - self._io.error_line( - " - Failed to create {} for {}".format( - pth_file.name, self._poetry.file.parent ) - ) - return [] + return [pth_file] + except OSError: + # TODO: Replace with PermissionError + self._io.error_line( + " - Failed to create {} for {}".format( + pth_file.name, self._poetry.file.parent + ) + ) + return [] def _add_scripts(self): added = [] @@ -187,19 +182,27 @@ def _add_dist_info(self, added_files): added_files = added_files[:] builder = WheelBuilder(self._poetry) - dist_info = self._env.site_packages.joinpath(builder.dist_info) + + dist_info_path = Path(builder.dist_info) + for dist_info in self._env.site_packages.find( + dist_info_path, writable_only=True + ): + if dist_info.exists(): + self._debug( + " - Removing existing {} directory from {}".format( + dist_info.name, dist_info.parent + ) + ) + shutil.rmtree(str(dist_info)) + + dist_info = self._env.site_packages.mkdir(dist_info_path) self._debug( " - Adding the {} directory to {}".format( - dist_info.name, self._env.site_packages + dist_info.name, dist_info.parent ) ) - if dist_info.exists(): - shutil.rmtree(str(dist_info)) - - dist_info.mkdir() - with dist_info.joinpath("METADATA").open("w", encoding="utf-8") as f: builder._write_metadata_file(f) diff --git a/poetry/utils/env.py b/poetry/utils/env.py index 0e3df320d6a..a81de75458d 100644 --- a/poetry/utils/env.py +++ b/poetry/utils/env.py @@ -7,6 +7,7 @@ import shutil import sys import sysconfig +import tempfile import textwrap from contextlib import contextmanager @@ -39,6 +40,7 @@ from poetry.utils._compat import encode from poetry.utils._compat import list_to_shell_command from poetry.utils._compat import subprocess +from poetry.utils.helpers import paths_csv GET_ENVIRONMENT_INFO = """\ @@ -143,6 +145,125 @@ def _version_nodot(version): """ +class SitePackages: + def __init__( + self, path, fallbacks=None, skip_write_checks=False + ): # type: (Path, List[Path], bool) -> None + self._path = path + self._fallbacks = fallbacks or [] + self._skip_write_checks = skip_write_checks + self._candidates = [self._path] + self._fallbacks + self._writable_candidates = None if not skip_write_checks else self._candidates + + @property + def path(self): # type: () -> Path + return self._path + + @property + def candidates(self): # type: () -> List[Path] + return self._candidates + + @property + def writable_candidates(self): # type: () -> List[Path] + if self._writable_candidates is not None: + return self._writable_candidates + + self._writable_candidates = [] + for candidate in self._candidates: + try: + if not candidate.exists(): + continue + + with tempfile.TemporaryFile(dir=str(candidate)): + self._writable_candidates.append(candidate) + except (IOError, OSError): + pass + + return self._writable_candidates + + def make_candidates( + self, path, writable_only=False + ): # type: (Path, bool) -> List[Path] + candidates = self._candidates if not writable_only else self.writable_candidates + if path.is_absolute(): + for candidate in candidates: + try: + path.relative_to(candidate) + return [path] + except ValueError: + pass + else: + raise ValueError( + "{} is not relative to any discovered {}sites".format( + path, "writable " if writable_only else "" + ) + ) + + return [candidate / path for candidate in candidates if candidate] + + def _path_method_wrapper( + self, path, method, *args, **kwargs + ): # type: (Path, str, *Any, **Any) -> Union[Tuple[Path, Any], List[Tuple[Path, Any]]] + + # TODO: Move to parameters after dropping Python 2.7 + return_first = kwargs.pop("return_first", True) + writable_only = kwargs.pop("writable_only", False) + + candidates = self.make_candidates(path, writable_only=writable_only) + + if not candidates: + raise RuntimeError( + 'Unable to find a suitable destination for "{}" in {}'.format( + str(path), paths_csv(self._candidates) + ) + ) + + results = [] + + for candidate in candidates: + try: + result = candidate, getattr(candidate, method)(*args, **kwargs) + if return_first: + return result + else: + results.append(result) + except (IOError, OSError): + # TODO: Replace with PermissionError + pass + + if results: + return results + + raise OSError("Unable to access any of {}".format(paths_csv(candidates))) + + def write_text(self, path, *args, **kwargs): # type: (Path, *Any, **Any) -> Path + return self._path_method_wrapper(path, "write_text", *args, **kwargs)[0] + + def mkdir(self, path, *args, **kwargs): # type: (Path, *Any, **Any) -> Path + return self._path_method_wrapper(path, "mkdir", *args, **kwargs)[0] + + def exists(self, path): # type: (Path) -> bool + return any( + value[-1] + for value in self._path_method_wrapper(path, "exists", return_first=False) + ) + + def find(self, path, writable_only=False): # type: (Path, bool) -> List[Path] + return [ + value[0] + for value in self._path_method_wrapper( + path, "exists", return_first=False, writable_only=writable_only + ) + if value[-1] is True + ] + + def __getattr__(self, item): + try: + return super(SitePackages, self).__getattribute__(item) + except AttributeError: + return getattr(self.path, item) + + class EnvError(Exception): pass @@ -825,9 +946,13 @@ def pip_version(self): return self._pip_version @property - def site_packages(self): # type: () -> Path + def site_packages(self): # type: () -> SitePackages if self._site_packages is None: - self._site_packages = self.purelib + # we disable write checks if no user site exist + fallbacks = [self.usersite] if self.usersite else [] + self._site_packages = SitePackages( + self.purelib, fallbacks, skip_write_checks=False if fallbacks else True + ) return self._site_packages @property diff --git a/poetry/utils/helpers.py b/poetry/utils/helpers.py index 180a90d50f3..e6162d508db 100644 --- a/poetry/utils/helpers.py +++ b/poetry/utils/helpers.py @@ -5,6 +5,7 @@ import tempfile from contextlib import contextmanager +from typing import List from typing import Optional import requests @@ -113,3 +114,7 @@ def get_package_version_display_string( ) return package.full_pretty_version + + +def paths_csv(paths): # type: (List[Path]) -> str + return ", ".join('"{}"'.format(str(c)) for c in paths) diff --git a/tests/installation/test_pip_installer.py b/tests/installation/test_pip_installer.py index 206bcce59cc..d0e2e5a4dcd 100644 --- a/tests/installation/test_pip_installer.py +++ b/tests/installation/test_pip_installer.py @@ -189,7 +189,9 @@ def test_uninstall_git_package_nspkg_pth_cleanup(mocker, tmp_venv, pool): ) # we do this here because the virtual env might not be usable if failure case is triggered - pth_file_candidate = tmp_venv.site_packages / "{}-nspkg.pth".format(package.name) + pth_file_candidate = tmp_venv.site_packages.path / "{}-nspkg.pth".format( + package.name + ) # in order to reproduce the scenario where the git source is removed prior to proper # clean up of nspkg.pth file, we need to make sure the fixture is copied and not diff --git a/tests/masonry/builders/test_editable_builder.py b/tests/masonry/builders/test_editable_builder.py index 3aee74e7c77..daeff0e7777 100644 --- a/tests/masonry/builders/test_editable_builder.py +++ b/tests/masonry/builders/test_editable_builder.py @@ -76,14 +76,14 @@ def test_builder_installs_proper_files_for_standard_packages(simple_poetry, tmp_ builder.build() assert tmp_venv._bin_dir.joinpath("foo").exists() - assert tmp_venv.site_packages.joinpath("simple_project.pth").exists() - assert simple_poetry.file.parent.resolve().as_posix() == tmp_venv.site_packages.joinpath( + assert tmp_venv.site_packages.path.joinpath("simple_project.pth").exists() + assert simple_poetry.file.parent.resolve().as_posix() == tmp_venv.site_packages.path.joinpath( "simple_project.pth" ).read_text().strip( os.linesep ) - dist_info = tmp_venv.site_packages.joinpath("simple_project-1.2.3.dist-info") + dist_info = tmp_venv.site_packages.path.joinpath("simple_project-1.2.3.dist-info") assert dist_info.exists() assert dist_info.joinpath("INSTALLER").exists() assert dist_info.joinpath("METADATA").exists() @@ -130,7 +130,7 @@ def test_builder_installs_proper_files_for_standard_packages(simple_poetry, tmp_ assert metadata == dist_info.joinpath("METADATA").read_text(encoding="utf-8") records = dist_info.joinpath("RECORD").read_text() - assert str(tmp_venv.site_packages.joinpath("simple_project.pth")) in records + assert str(tmp_venv.site_packages.path.joinpath("simple_project.pth")) in records assert str(tmp_venv._bin_dir.joinpath("foo")) in records assert str(tmp_venv._bin_dir.joinpath("baz")) in records assert str(dist_info.joinpath("METADATA")) in records @@ -202,7 +202,7 @@ def test_builder_installs_proper_files_when_packages_configured( builder = EditableBuilder(project_with_include, tmp_venv, NullIO()) builder.build() - pth_file = tmp_venv.site_packages.joinpath("with_include.pth") + pth_file = tmp_venv.site_packages.path.joinpath("with_include.pth") assert pth_file.is_file() paths = set() diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py index 8c816a2d32a..8934ae43427 100644 --- a/tests/utils/test_env.py +++ b/tests/utils/test_env.py @@ -866,7 +866,7 @@ def test_system_env_has_correct_paths(): assert paths.get("purelib") is not None assert paths.get("platlib") is not None assert paths.get("scripts") is not None - assert env.site_packages == Path(paths["purelib"]) + assert env.site_packages.path == Path(paths["purelib"]) @pytest.mark.parametrize( @@ -886,4 +886,4 @@ def test_venv_has_correct_paths(tmp_venv): assert paths.get("purelib") is not None assert paths.get("platlib") is not None assert paths.get("scripts") is not None - assert tmp_venv.site_packages == Path(paths["purelib"]) + assert tmp_venv.site_packages.path == Path(paths["purelib"]) diff --git a/tests/utils/test_env_site.py b/tests/utils/test_env_site.py new file mode 100644 index 00000000000..a13089160e6 --- /dev/null +++ b/tests/utils/test_env_site.py @@ -0,0 +1,41 @@ +import uuid + +from poetry.utils._compat import Path +from poetry.utils._compat import decode +from poetry.utils.env import SitePackages + + +def test_env_site_simple(tmp_dir): + site_packages = SitePackages(Path("/non-existent"), fallbacks=[Path(tmp_dir)]) + candidates = site_packages.make_candidates(Path("hello.txt"), writable_only=True) + hello = Path(tmp_dir) / "hello.txt" + + assert len(candidates) == 1 + assert candidates[0].as_posix() == hello.as_posix() + + content = decode(str(uuid.uuid4())) + site_packages.write_text(Path("hello.txt"), content, encoding="utf-8") + + assert hello.read_text(encoding="utf-8") == content + + assert not (site_packages.path / "hello.txt").exists() + + +def test_env_site_select_first(tmp_dir): + path = Path(tmp_dir) + fallback = path / "fallback" + fallback.mkdir(parents=True) + + site_packages = SitePackages(path, fallbacks=[fallback]) + candidates = site_packages.make_candidates(Path("hello.txt"), writable_only=True) + + assert len(candidates) == 2 + assert len(site_packages.find(Path("hello.txt"))) == 0 + + content = decode(str(uuid.uuid4())) + site_packages.write_text(Path("hello.txt"), content, encoding="utf-8") + + assert (site_packages.path / "hello.txt").exists() + assert not (fallback / "hello.txt").exists() + + assert len(site_packages.find(Path("hello.txt"))) == 1 From e9e6b324ef2ea1d8745a805acbba3493bce02e2b Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Fri, 23 Oct 2020 03:23:06 +0200 Subject: [PATCH 09/54] editable: use writable script dir for system env This change ensures that, When using system environment, poetry falls back to `userbase` if default location is not writable. --- poetry/masonry/builders/editable.py | 19 +++++++++++---- poetry/utils/env.py | 38 +++++++++++++++++++++-------- poetry/utils/helpers.py | 15 ++++++++++++ 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/poetry/masonry/builders/editable.py b/poetry/masonry/builders/editable.py index c31f657b656..0493423cdd2 100644 --- a/poetry/masonry/builders/editable.py +++ b/poetry/masonry/builders/editable.py @@ -13,6 +13,7 @@ from poetry.utils._compat import WINDOWS from poetry.utils._compat import Path from poetry.utils._compat import decode +from poetry.utils.helpers import is_dir_writable SCRIPT_TEMPLATE = """\ @@ -128,7 +129,17 @@ def _add_pth(self): def _add_scripts(self): added = [] entry_points = self.convert_entry_points() - scripts_path = Path(self._env.paths["scripts"]) + + for scripts_path in self._env.script_dirs: + if is_dir_writable(scripts_path): + break + else: + self._io.error_line( + " - Failed to find a suitable script installation directory for {}".format( + self._poetry.file.parent + ) + ) + return [] scripts = entry_points.get("console_scripts", []) for script in scripts: @@ -146,7 +157,7 @@ def _add_scripts(self): f.write( decode( SCRIPT_TEMPLATE.format( - python=self._env._bin("python"), + python=self._env.python, module=module, callable_holder=callable_holder, callable_=callable_, @@ -160,9 +171,7 @@ def _add_scripts(self): if WINDOWS: cmd_script = script_file.with_suffix(".cmd") - cmd = WINDOWS_CMD_TEMPLATE.format( - python=self._env._bin("python"), script=name - ) + cmd = WINDOWS_CMD_TEMPLATE.format(python=self._env.python, script=name) self._debug( " - Adding the {} script wrapper to {}".format( cmd_script.name, scripts_path diff --git a/poetry/utils/env.py b/poetry/utils/env.py index a81de75458d..33886926d4b 100644 --- a/poetry/utils/env.py +++ b/poetry/utils/env.py @@ -7,7 +7,6 @@ import shutil import sys import sysconfig -import tempfile import textwrap from contextlib import contextmanager @@ -40,6 +39,7 @@ from poetry.utils._compat import encode from poetry.utils._compat import list_to_shell_command from poetry.utils._compat import subprocess +from poetry.utils.helpers import is_dir_writable from poetry.utils.helpers import paths_csv @@ -170,14 +170,9 @@ def writable_candidates(self): # type: () -> List[Path] self._writable_candidates = [] for candidate in self._candidates: - try: - if not candidate.exists(): - continue - - with tempfile.TemporaryFile(dir=str(candidate)): - self._writable_candidates.append(candidate) - except (IOError, OSError): - pass + if not is_dir_writable(candidate): + continue + self._writable_candidates.append(candidate) return self._writable_candidates @@ -892,6 +887,7 @@ def __init__(self, path, base=None): # type: (Path, Optional[Path]) -> None self._supported_tags = None self._purelib = None self._platlib = None + self._script_dirs = None @property def path(self): # type: () -> Path @@ -960,6 +956,11 @@ def usersite(self): # type: () -> Optional[Path] if "usersite" in self.paths: return Path(self.paths["usersite"]) + @property + def userbase(self): # type: () -> Optional[Path] + if "userbase" in self.paths: + return Path(self.paths["userbase"]) + @property def purelib(self): # type: () -> Path if self._purelib is None: @@ -1106,6 +1107,18 @@ def execute(self, bin, *args, **kwargs): def is_venv(self): # type: () -> bool raise NotImplementedError() + @property + def script_dirs(self): # type: () -> List[Path] + if self._script_dirs is None: + self._script_dirs = ( + [Path(self.paths["scripts"])] + if "scripts" in self.paths + else self._bin_dir + ) + if self.userbase: + self._script_dirs.append(self.userbase / self._script_dirs[0].name) + return self._script_dirs + def _bin(self, bin): # type: (str) -> str """ Return path to the given executable. @@ -1141,6 +1154,10 @@ class SystemEnv(Env): A system (i.e. not a virtualenv) Python environment. """ + @property + def python(self): # type: () -> str + return sys.executable + @property def sys_path(self): # type: () -> List[str] return sys.path @@ -1181,6 +1198,7 @@ def get_paths(self): # type: () -> Dict[str, str] if site.check_enableusersite() and hasattr(obj, "install_usersite"): paths["usersite"] = getattr(obj, "install_usersite") + paths["userbase"] = getattr(obj, "install_userbase") return paths @@ -1316,7 +1334,7 @@ def is_venv(self): # type: () -> bool def is_sane(self): # A virtualenv is considered sane if both "python" and "pip" exist. - return os.path.exists(self._bin("python")) and os.path.exists(self._bin("pip")) + return os.path.exists(self.python) and os.path.exists(self._bin("pip")) def _run(self, cmd, **kwargs): with self.temp_environ(): diff --git a/poetry/utils/helpers.py b/poetry/utils/helpers.py index e6162d508db..232e65b7d44 100644 --- a/poetry/utils/helpers.py +++ b/poetry/utils/helpers.py @@ -118,3 +118,18 @@ def get_package_version_display_string( def paths_csv(paths): # type: (List[Path]) -> str return ", ".join('"{}"'.format(str(c)) for c in paths) + + +def is_dir_writable(path, create=False): # type: (Path, bool) -> bool + try: + if not path.exists(): + if not create: + return False + path.mkdir(parents=True, exist_ok=True) + + with tempfile.TemporaryFile(dir=str(path)): + pass + except (IOError, OSError): + return False + else: + return True From 5d0c7965a9c1afacd96faeacf722ae361983af36 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Fri, 23 Oct 2020 03:34:53 +0200 Subject: [PATCH 10/54] utils/env: ensure user directories are created --- poetry/masonry/builders/editable.py | 2 +- poetry/utils/env.py | 2 +- tests/utils/test_env_site.py | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/poetry/masonry/builders/editable.py b/poetry/masonry/builders/editable.py index 0493423cdd2..74d1f69c886 100644 --- a/poetry/masonry/builders/editable.py +++ b/poetry/masonry/builders/editable.py @@ -131,7 +131,7 @@ def _add_scripts(self): entry_points = self.convert_entry_points() for scripts_path in self._env.script_dirs: - if is_dir_writable(scripts_path): + if is_dir_writable(path=scripts_path, create=True): break else: self._io.error_line( diff --git a/poetry/utils/env.py b/poetry/utils/env.py index 33886926d4b..c247bf014f7 100644 --- a/poetry/utils/env.py +++ b/poetry/utils/env.py @@ -170,7 +170,7 @@ def writable_candidates(self): # type: () -> List[Path] self._writable_candidates = [] for candidate in self._candidates: - if not is_dir_writable(candidate): + if not is_dir_writable(path=candidate, create=True): continue self._writable_candidates.append(candidate) diff --git a/tests/utils/test_env_site.py b/tests/utils/test_env_site.py index a13089160e6..f25e2142193 100644 --- a/tests/utils/test_env_site.py +++ b/tests/utils/test_env_site.py @@ -5,7 +5,9 @@ from poetry.utils.env import SitePackages -def test_env_site_simple(tmp_dir): +def test_env_site_simple(tmp_dir, mocker): + # emulate permission error when creating directory + mocker.patch("poetry.utils._compat.Path.mkdir", side_effect=OSError()) site_packages = SitePackages(Path("/non-existent"), fallbacks=[Path(tmp_dir)]) candidates = site_packages.make_candidates(Path("hello.txt"), writable_only=True) hello = Path(tmp_dir) / "hello.txt" From 68f2cc7032d738981a4af52fdff159df95e55a12 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Thu, 15 Oct 2020 01:52:30 +0200 Subject: [PATCH 11/54] Handle non-editable packages with pth files When detecting installed packages, this change ensures that packages with .pth files are not incorrectly marked as editable. A package is considered editable only if at least one of the paths detected is not in the environment site. Resolves: #3077 --- poetry/repositories/installed_repository.py | 12 ++++++++-- .../standard-1.2.3.dist-info/METADATA | 22 +++++++++++++++++++ .../lib/python3.7/site-packages/standard.pth | 1 + .../repositories/test_installed_repository.py | 11 ++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 tests/repositories/fixtures/installed/lib/python3.7/site-packages/standard-1.2.3.dist-info/METADATA create mode 100644 tests/repositories/fixtures/installed/lib/python3.7/site-packages/standard.pth diff --git a/poetry/repositories/installed_repository.py b/poetry/repositories/installed_repository.py index a0630116f53..1320fdd6698 100644 --- a/poetry/repositories/installed_repository.py +++ b/poetry/repositories/installed_repository.py @@ -138,14 +138,22 @@ def load(cls, env): # type: (Env) -> InstalledRepository if path.name.endswith(".dist-info"): paths = cls.get_package_paths(env=env, name=package.pretty_name) if paths: + is_editable_package = False for src in paths: if cls.is_vcs_package(src, env): cls.set_package_vcs_properties(package, env) break + + if not ( + is_editable_package + or env.is_path_relative_to_lib(src) + ): + is_editable_package = True else: # TODO: handle multiple source directories? - package._source_type = "directory" - package._source_url = paths.pop().as_posix() + if is_editable_package: + package._source_type = "directory" + package._source_url = paths.pop().as_posix() continue if cls.is_vcs_package(path, env): diff --git a/tests/repositories/fixtures/installed/lib/python3.7/site-packages/standard-1.2.3.dist-info/METADATA b/tests/repositories/fixtures/installed/lib/python3.7/site-packages/standard-1.2.3.dist-info/METADATA new file mode 100644 index 00000000000..245121d496d --- /dev/null +++ b/tests/repositories/fixtures/installed/lib/python3.7/site-packages/standard-1.2.3.dist-info/METADATA @@ -0,0 +1,22 @@ +Metadata-Version: 2.1 +Name: standard +Version: 1.2.3 +Summary: Standard description. +License: MIT +Keywords: cli,commands +Author: Foo Bar +Author-email: foo@bar.com +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Description-Content-Type: text/x-rst + +Editable +#### diff --git a/tests/repositories/fixtures/installed/lib/python3.7/site-packages/standard.pth b/tests/repositories/fixtures/installed/lib/python3.7/site-packages/standard.pth new file mode 100644 index 00000000000..aa0bc074b62 --- /dev/null +++ b/tests/repositories/fixtures/installed/lib/python3.7/site-packages/standard.pth @@ -0,0 +1 @@ +standard \ No newline at end of file diff --git a/tests/repositories/test_installed_repository.py b/tests/repositories/test_installed_repository.py index 79ac262671c..3caa702a09c 100644 --- a/tests/repositories/test_installed_repository.py +++ b/tests/repositories/test_installed_repository.py @@ -26,6 +26,7 @@ zipp.Path(str(SITE_PURELIB / "foo-0.1.0-py3.8.egg"), "EGG-INFO") ), metadata.PathDistribution(VENDOR_DIR / "attrs-19.3.0.dist-info"), + metadata.PathDistribution(SITE_PURELIB / "standard-1.2.3.dist-info"), metadata.PathDistribution(SITE_PURELIB / "editable-2.3.4.dist-info"), metadata.PathDistribution(SITE_PURELIB / "editable-with-import-2.3.4.dist-info"), metadata.PathDistribution(SITE_PLATLIB / "lib64-2.3.4.dist-info"), @@ -158,3 +159,13 @@ def test_load_editable_with_import_package(repository): assert editable.version.text == "2.3.4" assert editable.source_type is None assert editable.source_url is None + + +def test_load_standard_package_with_pth_file(repository): + # test standard packages with .pth file is not treated as editable + standard = get_package_from_repository("standard", repository) + assert standard is not None + assert standard.name == "standard" + assert standard.version.text == "1.2.3" + assert standard.source_type is None + assert standard.source_url is None From 89e1d7c11c7566e5735173e17a041a4894f870a4 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Fri, 16 Oct 2020 23:42:10 +0200 Subject: [PATCH 12/54] locker: handle nested extras requirement Previously, when using locked repository, incorrect dependency instance was created when a dependency's extra requirement activated a nested extra. This change ensures that these are correctly loaded. As part of this change new lock files write PEP 508 serialised form of extra dependencies in order to reuse core logic to parse specification of extra requirement. Resolves: #3224 --- poetry/packages/locker.py | 23 +++- .../fixtures/with-dependencies-extras.test | 2 +- .../with-dependencies-nested-extras.test | 45 ++++++ tests/installation/test_installer.py | 29 ++++ tests/packages/test_locker.py | 130 ++++++++++++++++++ 5 files changed, 222 insertions(+), 7 deletions(-) create mode 100644 tests/installation/fixtures/with-dependencies-nested-extras.test diff --git a/poetry/packages/locker.py b/poetry/packages/locker.py index 9dd75e66519..ac791537450 100644 --- a/poetry/packages/locker.py +++ b/poetry/packages/locker.py @@ -24,12 +24,14 @@ import poetry.repositories +from poetry.core.packages import dependency_from_pep_508 from poetry.core.packages.package import Dependency from poetry.core.packages.package import Package from poetry.core.semver import parse_constraint from poetry.core.semver.version import Version from poetry.core.toml.file import TOMLFile from poetry.core.version.markers import parse_marker +from poetry.core.version.requirements import InvalidRequirement from poetry.packages import DependencyPackage from poetry.utils._compat import OrderedDict from poetry.utils._compat import Path @@ -142,11 +144,18 @@ def locked_repository( package.extras[name] = [] for dep in deps: - m = re.match(r"^(.+?)(?:\s+\((.+)\))?$", dep) - dep_name = m.group(1) - constraint = m.group(2) or "*" - - package.extras[name].append(Dependency(dep_name, constraint)) + try: + dependency = dependency_from_pep_508(dep) + except InvalidRequirement: + # handle lock files with invalid PEP 508 + m = re.match(r"^(.+?)(?:\[(.+?)])?(?:\s+\((.+)\))?$", dep) + dep_name = m.group(1) + extras = m.group(2) or "" + constraint = m.group(3) or "*" + dependency = Dependency( + dep_name, constraint, extras=extras.split(",") + ) + package.extras[name].append(dependency) if "marker" in info: package.marker = parse_marker(info["marker"]) @@ -543,8 +552,10 @@ def _dump_package(self, package): # type: (Package) -> dict if package.extras: extras = {} for name, deps in package.extras.items(): + # TODO: This should use dep.to_pep_508() once this is fixed + # https://github.com/python-poetry/poetry-core/pull/102 extras[name] = [ - str(dep) if not dep.constraint.is_any() else dep.name + dep.base_pep_508_name if not dep.constraint.is_any() else dep.name for dep in deps ] diff --git a/tests/installation/fixtures/with-dependencies-extras.test b/tests/installation/fixtures/with-dependencies-extras.test index 63560bb4793..042e29670e1 100644 --- a/tests/installation/fixtures/with-dependencies-extras.test +++ b/tests/installation/fixtures/with-dependencies-extras.test @@ -18,7 +18,7 @@ python-versions = "*" C = {version = "^1.0", optional = true} [package.extras] -foo = ["C (^1.0)"] +foo = ["C (>=1.0,<2.0)"] [[package]] name = "C" diff --git a/tests/installation/fixtures/with-dependencies-nested-extras.test b/tests/installation/fixtures/with-dependencies-nested-extras.test new file mode 100644 index 00000000000..48a22a7c7f3 --- /dev/null +++ b/tests/installation/fixtures/with-dependencies-nested-extras.test @@ -0,0 +1,45 @@ +[[package]] +name = "A" +version = "1.0" +description = "" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +B = {version = "^1.0", optional = true, extras = ["C"]} + +[package.extras] +B = ["B[C] (>=1.0,<2.0)"] + +[[package]] +name = "B" +version = "1.0" +description = "" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +C = {version = "^1.0", optional = true} + +[package.extras] +C = ["C (>=1.0,<2.0)"] + +[[package]] +name = "C" +version = "1.0" +description = "" +category = "main" +optional = false +python-versions = "*" + +[metadata] +python-versions = "*" +lock-version = "1.1" +content-hash = "123456789" + +[metadata.files] +"A" = [] +"B" = [] +"C" = [] diff --git a/tests/installation/test_installer.py b/tests/installation/test_installer.py index 7a1660670ee..106efde6e9c 100644 --- a/tests/installation/test_installer.py +++ b/tests/installation/test_installer.py @@ -639,6 +639,35 @@ def test_run_with_dependencies_extras(installer, locker, repo, package): assert locker.written_data == expected +def test_run_with_dependencies_nested_extras(installer, locker, repo, package): + package_a = get_package("A", "1.0") + package_b = get_package("B", "1.0") + package_c = get_package("C", "1.0") + + dependency_c = Factory.create_dependency("C", {"version": "^1.0", "optional": True}) + dependency_b = Factory.create_dependency( + "B", {"version": "^1.0", "optional": True, "extras": ["C"]} + ) + dependency_a = Factory.create_dependency("A", {"version": "^1.0", "extras": ["B"]}) + + package_b.extras = {"C": [dependency_c]} + package_b.add_dependency(dependency_c) + + package_a.add_dependency(dependency_b) + package_a.extras = {"B": [dependency_b]} + + repo.add_package(package_a) + repo.add_package(package_b) + repo.add_package(package_c) + + package.add_dependency(dependency_a) + + installer.run() + expected = fixture("with-dependencies-nested-extras") + + assert locker.written_data == expected + + def test_run_does_not_install_extras_if_not_requested(installer, locker, repo, package): package.extras["foo"] = [get_dependency("D")] package_a = get_package("A", "1.0") diff --git a/tests/packages/test_locker.py b/tests/packages/test_locker.py index 35c584f2767..a4aa17971f8 100644 --- a/tests/packages/test_locker.py +++ b/tests/packages/test_locker.py @@ -142,6 +142,136 @@ def test_locker_properly_loads_extras(locker): assert lockfile_dep.name == "lockfile" +def test_locker_properly_loads_nested_extras(locker): + content = """\ +[[package]] +name = "a" +version = "1.0" +description = "" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +b = {version = "^1.0", optional = true, extras = "c"} + +[package.extras] +b = ["b[c] (>=1.0,<2.0)"] + +[[package]] +name = "b" +version = "1.0" +description = "" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +c = {version = "^1.0", optional = true} + +[package.extras] +c = ["c (>=1.0,<2.0)"] + +[[package]] +name = "c" +version = "1.0" +description = "" +category = "main" +optional = false +python-versions = "*" + +[metadata] +python-versions = "*" +lock-version = "1.1" +content-hash = "123456789" + +[metadata.files] +"a" = [] +"b" = [] +"c" = [] +""" + + locker.lock.write(tomlkit.parse(content)) + + repository = locker.locked_repository() + assert 3 == len(repository.packages) + + packages = repository.find_packages(get_dependency("a", "1.0")) + assert len(packages) == 1 + + package = packages[0] + assert len(package.requires) == 1 + assert len(package.extras) == 1 + + dependency_b = package.extras["b"][0] + assert dependency_b.name == "b" + assert dependency_b.extras == frozenset({"c"}) + + packages = repository.find_packages(dependency_b) + assert len(packages) == 1 + + package = packages[0] + assert len(package.requires) == 1 + assert len(package.extras) == 1 + + dependency_c = package.extras["c"][0] + assert dependency_c.name == "c" + assert dependency_c.extras == frozenset() + + packages = repository.find_packages(dependency_c) + assert len(packages) == 1 + + +def test_locker_properly_loads_extras_legacy(locker): + content = """\ +[[package]] +name = "a" +version = "1.0" +description = "" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +b = {version = "^1.0", optional = true} + +[package.extras] +b = ["b (^1.0)"] + +[[package]] +name = "b" +version = "1.0" +description = "" +category = "main" +optional = false +python-versions = "*" + +[metadata] +python-versions = "*" +lock-version = "1.1" +content-hash = "123456789" + +[metadata.files] +"a" = [] +"b" = [] +""" + + locker.lock.write(tomlkit.parse(content)) + + repository = locker.locked_repository() + assert 2 == len(repository.packages) + + packages = repository.find_packages(get_dependency("a", "1.0")) + assert len(packages) == 1 + + package = packages[0] + assert len(package.requires) == 1 + assert len(package.extras) == 1 + + dependency_b = package.extras["b"][0] + assert dependency_b.name == "b" + + def test_lock_packages_with_null_description(locker, root): package_a = get_package("A", "1.0.0") package_a.description = None From e878a5d4e7aeaf022201a55254d5878d1972b63c Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Sun, 18 Oct 2020 00:14:48 +0200 Subject: [PATCH 13/54] locker: handle cyclic dependencies during walk Resolves: #3213 --- poetry/packages/locker.py | 52 ++++++++++++++++----------------- tests/utils/test_exporter.py | 56 ++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 27 deletions(-) diff --git a/poetry/packages/locker.py b/poetry/packages/locker.py index ac791537450..d0d03a56779 100644 --- a/poetry/packages/locker.py +++ b/poetry/packages/locker.py @@ -226,45 +226,43 @@ def __walk_dependency_level( next_level_dependencies = [] for requirement in dependencies: + key = (requirement.name, requirement.pretty_constraint) locked_package = cls.__get_locked_package(requirement, packages_by_name) if locked_package: - for require in locked_package.requires: - if require.marker.is_empty(): - require.marker = requirement.marker - else: - require.marker = require.marker.intersect(requirement.marker) + # create dependency from locked package to retain dependency metadata + # if this is not done, we can end-up with incorrect nested dependencies + marker = requirement.marker + requirement = locked_package.to_dependency() + requirement.marker = requirement.marker.intersect(marker) + + key = (requirement.name, requirement.pretty_constraint) + + if pinned_versions: + requirement.set_constraint( + locked_package.to_dependency().constraint + ) + + if key not in nested_dependencies: + for require in locked_package.requires: + if require.marker.is_empty(): + require.marker = requirement.marker + else: + require.marker = require.marker.intersect( + requirement.marker + ) - require.marker = require.marker.intersect(locked_package.marker) - next_level_dependencies.append(require) + require.marker = require.marker.intersect(locked_package.marker) + next_level_dependencies.append(require) if requirement.name in project_level_dependencies and level == 0: # project level dependencies take precedence continue - if locked_package: - # create dependency from locked package to retain dependency metadata - # if this is not done, we can end-up with incorrect nested dependencies - marker = requirement.marker - requirement = locked_package.to_dependency() - requirement.marker = requirement.marker.intersect(marker) - else: + if not locked_package: # we make a copy to avoid any side-effects requirement = deepcopy(requirement) - if pinned_versions: - requirement.set_constraint( - cls.__get_locked_package(requirement, packages_by_name) - .to_dependency() - .constraint - ) - - # dependencies use extra to indicate that it was activated via parent - # package's extras, this is not required for nested exports as we assume - # the resolver already selected this dependency - requirement.marker = requirement.marker.without_extras() - - key = (requirement.name, requirement.pretty_constraint) if key not in nested_dependencies: nested_dependencies[key] = requirement else: diff --git a/tests/utils/test_exporter.py b/tests/utils/test_exporter.py index c5ce0214ff7..1e660ce69cd 100644 --- a/tests/utils/test_exporter.py +++ b/tests/utils/test_exporter.py @@ -696,6 +696,62 @@ def test_exporter_can_export_requirements_txt_with_nested_packages(tmp_dir, poet assert expected == content +def test_exporter_can_export_requirements_txt_with_nested_packages_cyclic( + tmp_dir, poetry +): + poetry.locker.mock_lock_data( + { + "package": [ + { + "name": "foo", + "version": "1.2.3", + "category": "main", + "optional": False, + "python-versions": "*", + "dependencies": {"bar": {"version": "4.5.6"}}, + }, + { + "name": "bar", + "version": "4.5.6", + "category": "main", + "optional": False, + "python-versions": "*", + "dependencies": {"baz": {"version": "7.8.9"}}, + }, + { + "name": "baz", + "version": "7.8.9", + "category": "main", + "optional": False, + "python-versions": "*", + "dependencies": {"foo": {"version": "1.2.3"}}, + }, + ], + "metadata": { + "python-versions": "*", + "content-hash": "123456789", + "hashes": {"foo": [], "bar": [], "baz": []}, + }, + } + ) + set_package_requires(poetry, skip={"bar", "baz"}) + + exporter = Exporter(poetry) + + exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") + + with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: + content = f.read() + + expected = """\ +bar==4.5.6 +baz==7.8.9 +foo==1.2.3 +""" + + assert expected == content + + def test_exporter_can_export_requirements_txt_with_git_packages_and_markers( tmp_dir, poetry ): From 3067fd4af68addc13aad37048637617a0c68e322 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 20 Oct 2020 01:42:39 +0200 Subject: [PATCH 14/54] pool: ensure sources are prioritised over PyPI When a project specifies non default sources, PyPI gets added as the default source. This will prioritise packages available in PyPI when the package exists in both index. This change ensures that PyPI is only used as a default when no other sources are provided. Resolves: #1677 #2564 #3238 --- poetry/factory.py | 6 +++-- .../with_non_default_source/pyproject.toml | 18 +++++++++++++ tests/test_factory.py | 27 +++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/with_non_default_source/pyproject.toml diff --git a/poetry/factory.py b/poetry/factory.py index fd863ba5991..08a6faca170 100644 --- a/poetry/factory.py +++ b/poetry/factory.py @@ -70,7 +70,8 @@ def create_poetry( ) # Configuring sources - for source in poetry.local_config.get("source", []): + sources = poetry.local_config.get("source", []) + for source in sources: repository = self.create_legacy_repository(source, config) is_default = source.get("default", False) is_secondary = source.get("secondary", False) @@ -90,7 +91,8 @@ def create_poetry( # Always put PyPI last to prefer private repositories # but only if we have no other default source if not poetry.pool.has_default(): - poetry.pool.add_repository(PyPiRepository(), True) + has_sources = bool(sources) + poetry.pool.add_repository(PyPiRepository(), not has_sources, has_sources) else: if io.is_debug(): io.write_line("Deactivating the PyPI repository") diff --git a/tests/fixtures/with_non_default_source/pyproject.toml b/tests/fixtures/with_non_default_source/pyproject.toml new file mode 100644 index 00000000000..d36abb55a25 --- /dev/null +++ b/tests/fixtures/with_non_default_source/pyproject.toml @@ -0,0 +1,18 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Your Name " +] +license = "MIT" + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.6" + +[tool.poetry.dev-dependencies] + +[[tool.poetry.source]] +name = "foo" +url = "https://foo.bar/simple/" diff --git a/tests/test_factory.py b/tests/test_factory.py index d7758e8deaa..b2c232b20e7 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -6,6 +6,8 @@ from poetry.core.toml.file import TOMLFile from poetry.factory import Factory +from poetry.repositories.legacy_repository import LegacyRepository +from poetry.repositories.pypi_repository import PyPiRepository from poetry.utils._compat import PY2 from poetry.utils._compat import Path @@ -150,6 +152,31 @@ def test_poetry_with_default_source(): assert 1 == len(poetry.pool.repositories) +def test_poetry_with_non_default_source(): + poetry = Factory().create_poetry(fixtures_dir / "with_non_default_source") + + assert len(poetry.pool.repositories) == 2 + + assert not poetry.pool.has_default() + + assert poetry.pool.repositories[0].name == "foo" + assert isinstance(poetry.pool.repositories[0], LegacyRepository) + + assert poetry.pool.repositories[1].name == "PyPI" + assert isinstance(poetry.pool.repositories[1], PyPiRepository) + + +def test_poetry_with_no_default_source(): + poetry = Factory().create_poetry(fixtures_dir / "sample_project") + + assert len(poetry.pool.repositories) == 1 + + assert poetry.pool.has_default() + + assert poetry.pool.repositories[0].name == "PyPI" + assert isinstance(poetry.pool.repositories[0], PyPiRepository) + + def test_poetry_with_two_default_sources(): with pytest.raises(ValueError) as e: Factory().create_poetry(fixtures_dir / "with_two_default_sources") From 7c728f0aacb0abf1084c7cc189f2ed24e43386b3 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Wed, 21 Oct 2020 21:59:42 +0200 Subject: [PATCH 15/54] Ensure vcs dependencies preserve editable flag Resolves: #3263 --- poetry/packages/locker.py | 2 +- poetry/puzzle/provider.py | 1 + tests/packages/test_locker.py | 1 + tests/puzzle/test_provider.py | 9 +++++++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/poetry/packages/locker.py b/poetry/packages/locker.py index d0d03a56779..f1637407068 100644 --- a/poetry/packages/locker.py +++ b/poetry/packages/locker.py @@ -582,7 +582,7 @@ def _dump_package(self, package): # type: (Package) -> dict if package.source_resolved_reference: data["source"]["resolved_reference"] = package.source_resolved_reference - if package.source_type == "directory": + if package.source_type in ["directory", "git"]: data["develop"] = package.develop return data diff --git a/poetry/puzzle/provider.py b/poetry/puzzle/provider.py index e2838370a79..c05efbd6844 100644 --- a/poetry/puzzle/provider.py +++ b/poetry/puzzle/provider.py @@ -168,6 +168,7 @@ def search_for_vcs(self, dependency): # type: (VCSDependency) -> List[Package] rev=dependency.rev, name=dependency.name, ) + package.develop = dependency.develop dependency._constraint = package.version dependency._pretty_constraint = package.version.text diff --git a/tests/packages/test_locker.py b/tests/packages/test_locker.py index a4aa17971f8..7fa89a44962 100644 --- a/tests/packages/test_locker.py +++ b/tests/packages/test_locker.py @@ -73,6 +73,7 @@ def test_lock_file_data_is_ordered(locker, root): category = "main" optional = false python-versions = "*" +develop = true [package.source] type = "git" diff --git a/tests/puzzle/test_provider.py b/tests/puzzle/test_provider.py index 693470156f4..ecab7f3ab2d 100644 --- a/tests/puzzle/test_provider.py +++ b/tests/puzzle/test_provider.py @@ -47,6 +47,15 @@ def provider(root, pool): return Provider(root, pool, NullIO()) +@pytest.mark.parametrize("value", [True, False]) +def test_search_for_vcs_retains_develop_flag(provider, value): + dependency = VCSDependency( + "demo", "git", "https://github.com/demo/demo.git", develop=value + ) + package = provider.search_for_vcs(dependency)[0] + assert package.develop == value + + def test_search_for_vcs_setup_egg_info(provider): dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git") From 5daeb89b0800dbc282d2b9ef1105fb20e7e712c2 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Fri, 23 Oct 2020 23:08:33 +0200 Subject: [PATCH 16/54] Add 1.1.4 changelog --- CHANGELOG.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec01b75abde..168944d8f4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Change Log +## [1.1.4] - 2020-10-23 + +### Added + +- Added `installer.parallel` boolean flag (defaults to `true`) configuration to enable/disable parallel execution of operations when using the new installer. ([#3088](https://github.com/python-poetry/poetry/pull/3088)) + +### Changed + +- When using system environments as an unprivileged user, user site and bin directories are created if they do not already exist. ([#3107](https://github.com/python-poetry/poetry/pull/3107)) + +### Fixed + +- Fixed editable installation of poetry projects when using system environments. ([#3107](https://github.com/python-poetry/poetry/pull/3107)) +- Fixed locking of nested extra activations. If you were affected by this issue, you will need to regenerate the lock file using `poetry lock --no-update`. ([#3229](https://github.com/python-poetry/poetry/pull/3229)) +- Fixed prioritisation of non-default custom package sources. ([#3251](https://github.com/python-poetry/poetry/pull/3251)) +- Fixed detection of installed editable packages when non-poetry managed `.pth` file exists. ([#3210](https://github.com/python-poetry/poetry/pull/3210)) +- Fixed scripts generated by editable builder to use valid import statements. ([#3214](https://github.com/python-poetry/poetry/pull/3214)) +- Fixed recursion error when locked dependencies contain cyclic dependencies. ([#3237](https://github.com/python-poetry/poetry/pull/3237)) +- Fixed propagation of editable flag for VCS dependencies. ([#3264](https://github.com/python-poetry/poetry/pull/3264)) + ## [1.1.3] - 2020-10-14 ### Changed @@ -1063,7 +1083,8 @@ Initial release -[Unreleased]: https://github.com/python-poetry/poetry/compare/1.1.3...master +[Unreleased]: https://github.com/python-poetry/poetry/compare/1.1.4...master +[1.1.3]: https://github.com/python-poetry/poetry/compare/1.1.4 [1.1.3]: https://github.com/python-poetry/poetry/compare/1.1.3 [1.1.2]: https://github.com/python-poetry/poetry/releases/tag/1.1.2 [1.1.1]: https://github.com/python-poetry/poetry/releases/tag/1.1.1 From d1529a19118c0aead054ef9d4f1dbf1210bad153 Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Wed, 28 Oct 2020 12:30:43 +0100 Subject: [PATCH 17/54] show --tree: stop ignoring --no-dev (#3296) --- poetry/console/commands/show.py | 4 +- tests/console/commands/test_show.py | 67 +++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/poetry/console/commands/show.py b/poetry/console/commands/show.py index f6f42f42654..86be1ae7a36 100644 --- a/poetry/console/commands/show.py +++ b/poetry/console/commands/show.py @@ -55,7 +55,9 @@ def handle(self): # Show tree view if requested if self.option("tree") and not package: - requires = self.poetry.package.requires + self.poetry.package.dev_requires + requires = self.poetry.package.requires + if include_dev: + requires += self.poetry.package.dev_requires packages = locked_repo.packages for package in packages: for require in requires: diff --git a/tests/console/commands/test_show.py b/tests/console/commands/test_show.py index cf4dffcacc4..56e964abfe4 100644 --- a/tests/console/commands/test_show.py +++ b/tests/console/commands/test_show.py @@ -1152,3 +1152,70 @@ def test_show_tree(tester, poetry, installed): """ assert expected == tester.io.fetch_output() + + +def test_show_tree_no_dev(tester, poetry, installed): + poetry.package.add_dependency(Factory.create_dependency("cachy", "^0.2.0")) + poetry.package.add_dependency( + Factory.create_dependency("pytest", "^6.1.0", category="dev") + ) + + cachy2 = get_package("cachy", "0.2.0") + cachy2.add_dependency(Factory.create_dependency("msgpack-python", ">=0.5 <0.6")) + installed.add_package(cachy2) + + pytest = get_package("pytest", "6.1.1") + installed.add_package(pytest) + + poetry.locker.mock_lock_data( + { + "package": [ + { + "name": "cachy", + "version": "0.2.0", + "description": "", + "category": "main", + "optional": False, + "platform": "*", + "python-versions": "*", + "checksum": [], + "dependencies": {"msgpack-python": ">=0.5 <0.6"}, + }, + { + "name": "msgpack-python", + "version": "0.5.1", + "description": "", + "category": "main", + "optional": False, + "platform": "*", + "python-versions": "*", + "checksum": [], + }, + { + "name": "pytest", + "version": "6.1.1", + "description": "", + "category": "dev", + "optional": False, + "platform": "*", + "python-versions": "*", + "checksum": [], + }, + ], + "metadata": { + "python-versions": "*", + "platform": "*", + "content-hash": "123456789", + "hashes": {"cachy": [], "msgpack-python": [], "pytest": []}, + }, + } + ) + + tester.execute("--tree --no-dev") + + expected = """\ +cachy 0.2.0 +`-- msgpack-python >=0.5 <0.6 +""" + + assert expected == tester.io.fetch_output() From 3ef60e71ce5ca2c17d0169c5b2c98be28264096e Mon Sep 17 00:00:00 2001 From: Gabriel Simonetto Date: Sat, 17 Oct 2020 14:35:59 -0300 Subject: [PATCH 18/54] Add no interaction flag to website --- docs/docs/cli.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/cli.md b/docs/docs/cli.md index 1a2615a9d9d..bf207e8dc42 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -14,6 +14,7 @@ then `--help` combined with any of those can give you more information. * `--ansi`: Force ANSI output. * `--no-ansi`: Disable ANSI output. * `--version (-V)`: Display this application version. +* `--no-interaction (-n)`: Do not ask any interactive question. ## new From 19cafe30893a71117b086ae3769ddd41462dd3de Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Fri, 23 Oct 2020 23:50:00 +0200 Subject: [PATCH 19/54] pip installer: fix incorrect method call Resolves: #3272 --- poetry/installation/pip_installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry/installation/pip_installer.py b/poetry/installation/pip_installer.py index e44d7a61297..b8fb97314be 100644 --- a/poetry/installation/pip_installer.py +++ b/poetry/installation/pip_installer.py @@ -229,7 +229,7 @@ def install_directory(self, package): args.append(req) - return self.run_pip(*args) + return self.run(*args) if package.develop: args.append("-e") From 6ddd58f81287c1e63d155c120325066c71692b41 Mon Sep 17 00:00:00 2001 From: Cere Blanco <743526+cereblanco@users.noreply.github.com> Date: Fri, 30 Oct 2020 12:57:14 +0800 Subject: [PATCH 20/54] Improve caret version constraint documentation (#3280) * Add context to caret version constraint documentation --- docs/docs/dependency-specification.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/docs/dependency-specification.md b/docs/docs/dependency-specification.md index 2a1b63afdae..f71480dfa41 100644 --- a/docs/docs/dependency-specification.md +++ b/docs/docs/dependency-specification.md @@ -7,12 +7,7 @@ of the dependency and on the optional constraints that might be needed for it to ### Caret requirements -**Caret requirements** allow SemVer compatible updates to a specified version. -An update is allowed if the new version number does not modify the left-most non-zero digit in the major, minor, patch grouping. -In this case, if we ran `poetry update requests`, poetry would update us to version `2.14.0` if it was available, -but would not update us to `3.0.0`. -If instead we had specified the version string as `^0.1.13`, poetry would update to `0.1.14` but not `0.2.0`. -`0.0.x` is not considered compatible with any other version. +**Caret requirements** allow [SemVer](https://semver.org/) compatible updates to a specified version. An update is allowed if the new version number does not modify the left-most non-zero digit in the major, minor, patch grouping. For instance, if we previously ran `poetry add requests@^2.13.0` and wanted to update the library and ran `poetry update requests`, poetry would update us to version `2.14.0` if it was available, but would not update us to `3.0.0`. If instead we had specified the version string as `^0.1.13`, poetry would update to `0.1.14` but not `0.2.0`. `0.0.x` is not considered compatible with any other version. Here are some more examples of caret requirements and the versions that would be allowed with them: From bf61dd56399b5d0cfadf66fed72b4d63062a827f Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 20 Oct 2020 18:32:36 +0200 Subject: [PATCH 21/54] tests: cleanup cache and http usage - ensure tests rely on temporary cache directory - remove external http call requirement for lock --no-update Relates-to: #1645 --- tests/config/test_config.py | 4 +- tests/conftest.py | 21 ++- tests/console/commands/test_config.py | 30 ++-- tests/console/commands/test_lock.py | 29 ++-- tests/fixtures/old_lock/poetry.lock | 150 +----------------- tests/fixtures/old_lock/pyproject.toml | 2 +- tests/installation/test_chef.py | 7 +- tests/installation/test_executor.py | 6 +- .../masonry/builders/test_editable_builder.py | 8 +- tests/utils/test_env.py | 26 ++- 10 files changed, 90 insertions(+), 193 deletions(-) diff --git a/tests/config/test_config.py b/tests/config/test_config.py index 4bd0cd048da..f3b13f23003 100644 --- a/tests/config/test_config.py +++ b/tests/config/test_config.py @@ -10,8 +10,8 @@ def test_config_get_default_value(config, name, value): assert config.get(name) is value -def test_config_get_processes_depended_on_values(config): - assert os.path.join("/foo", "virtualenvs") == config.get("virtualenvs.path") +def test_config_get_processes_depended_on_values(config, config_cache_dir): + assert str(config_cache_dir / "virtualenvs") == config.get("virtualenvs.path") @pytest.mark.parametrize( diff --git a/tests/conftest.py b/tests/conftest.py index e2b73936924..51128f764bb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -54,9 +54,21 @@ def all(self): # type: () -> Dict[str, Any] @pytest.fixture -def config_source(): +def config_cache_dir(tmp_dir): + path = Path(tmp_dir) / ".cache" / "pypoetry" + path.mkdir(parents=True) + return path + + +@pytest.fixture +def config_virtualenvs_path(config_cache_dir): + return config_cache_dir / "virtualenvs" + + +@pytest.fixture +def config_source(config_cache_dir): source = DictConfigSource() - source.add_property("cache-dir", "/foo") + source.add_property("cache-dir", str(config_cache_dir)) return source @@ -226,6 +238,7 @@ def _factory( dependencies=None, dev_dependencies=None, pyproject_content=None, + poetry_lock_content=None, install_deps=True, ): project_dir = workspace / "poetry-fixture-{}".format(name) @@ -249,6 +262,10 @@ def _factory( dev_dependencies=dev_dependencies, ).create(project_dir, with_tests=False) + if poetry_lock_content: + lock_file = project_dir / "poetry.lock" + lock_file.write_text(data=poetry_lock_content, encoding="utf-8") + poetry = Factory().create_poetry(project_dir) locker = TestLocker( diff --git a/tests/console/commands/test_config.py b/tests/console/commands/test_config.py index 4d0b9ada0c8..58bc8662bf5 100644 --- a/tests/console/commands/test_config.py +++ b/tests/console/commands/test_config.py @@ -25,37 +25,41 @@ def test_show_config_with_local_config_file_empty(tester, mocker): assert "" == tester.io.fetch_output() -def test_list_displays_default_value_if_not_set(tester, config): +def test_list_displays_default_value_if_not_set(tester, config, config_cache_dir): tester.execute("--list") - expected = """cache-dir = "/foo" + expected = """cache-dir = {cache} experimental.new-installer = true installer.parallel = true virtualenvs.create = true virtualenvs.in-project = null virtualenvs.options.always-copy = false -virtualenvs.path = {path} # /foo{sep}virtualenvs +virtualenvs.path = {path} # {virtualenvs} """.format( - path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep + cache=json.dumps(str(config_cache_dir)), + path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), + virtualenvs=str(config_cache_dir / "virtualenvs"), ) assert expected == tester.io.fetch_output() -def test_list_displays_set_get_setting(tester, config): +def test_list_displays_set_get_setting(tester, config, config_cache_dir): tester.execute("virtualenvs.create false") tester.execute("--list") - expected = """cache-dir = "/foo" + expected = """cache-dir = {cache} experimental.new-installer = true installer.parallel = true virtualenvs.create = false virtualenvs.in-project = null virtualenvs.options.always-copy = false -virtualenvs.path = {path} # /foo{sep}virtualenvs +virtualenvs.path = {path} # {virtualenvs} """.format( - path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep + cache=json.dumps(str(config_cache_dir)), + path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), + virtualenvs=str(config_cache_dir / "virtualenvs"), ) assert 0 == config.set_config_source.call_count @@ -83,20 +87,22 @@ def test_display_single_local_setting(command_tester_factory, fixture_dir): assert expected == tester.io.fetch_output() -def test_list_displays_set_get_local_setting(tester, config): +def test_list_displays_set_get_local_setting(tester, config, config_cache_dir): tester.execute("virtualenvs.create false --local") tester.execute("--list") - expected = """cache-dir = "/foo" + expected = """cache-dir = {cache} experimental.new-installer = true installer.parallel = true virtualenvs.create = false virtualenvs.in-project = null virtualenvs.options.always-copy = false -virtualenvs.path = {path} # /foo{sep}virtualenvs +virtualenvs.path = {path} # {virtualenvs} """.format( - path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep + cache=json.dumps(str(config_cache_dir)), + path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), + virtualenvs=str(config_cache_dir / "virtualenvs"), ) assert 1 == config.set_config_source.call_count diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index 44c72967b32..c05ba257882 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -1,10 +1,8 @@ -import shutil - import pytest -from poetry.factory import Factory from poetry.packages import Locker from poetry.utils._compat import Path +from tests.helpers import get_package @pytest.fixture @@ -18,15 +16,26 @@ def tester(command_tester_factory): @pytest.fixture -def poetry_with_old_lockfile(fixture_dir, source_dir): - project_dir = source_dir / "project" - shutil.copytree(str(fixture_dir("old_lock")), str(project_dir)) - poetry = Factory().create_poetry(cwd=project_dir) - return poetry +def poetry_with_old_lockfile(project_factory, fixture_dir, source_dir): + source = fixture_dir("old_lock") + pyproject_content = (source / "pyproject.toml").read_text(encoding="utf-8") + poetry_lock_content = (source / "poetry.lock").read_text(encoding="utf-8") + return project_factory( + name="foobar", + pyproject_content=pyproject_content, + poetry_lock_content=poetry_lock_content, + ) + +def test_lock_no_update(command_tester_factory, poetry_with_old_lockfile, repo): + repo.add_package(get_package("sampleproject", "1.3.1")) + repo.add_package(get_package("sampleproject", "2.0.0")) -def test_lock_no_update(command_tester_factory, poetry_with_old_lockfile, http): - http.disable() + locker = Locker( + lock=poetry_with_old_lockfile.pyproject.file.path.parent / "poetry.lock", + local_config=poetry_with_old_lockfile.locker._local_config, + ) + poetry_with_old_lockfile.set_locker(locker) locked_repository = poetry_with_old_lockfile.locker.locked_repository( with_dev_reqs=True diff --git a/tests/fixtures/old_lock/poetry.lock b/tests/fixtures/old_lock/poetry.lock index 57d585709e8..498df2edc9f 100644 --- a/tests/fixtures/old_lock/poetry.lock +++ b/tests/fixtures/old_lock/poetry.lock @@ -1,153 +1,19 @@ [[package]] category = "main" -description = "Python package for providing Mozilla's CA Bundle." -name = "certifi" -optional = false -python-versions = "*" -version = "2020.6.20" - -[[package]] -category = "main" -description = "Universal encoding detector for Python 2 and 3" -name = "chardet" -optional = false -python-versions = "*" -version = "3.0.4" - -[[package]] -category = "main" -description = "A Python library for the Docker Engine API." -name = "docker" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "4.3.1" - -[package.dependencies] -pywin32 = "227" -requests = ">=2.14.2,<2.18.0 || >2.18.0" -six = ">=1.4.0" -websocket-client = ">=0.32.0" - -[package.extras] -ssh = ["paramiko (>=2.4.2)"] -tls = ["pyOpenSSL (>=17.5.0)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"] - -[[package]] -category = "main" -description = "Internationalized Domain Names in Applications (IDNA)" -name = "idna" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.10" - -[[package]] -category = "main" -description = "Python for Window Extensions" -marker = "sys_platform == \"win32\"" -name = "pywin32" -optional = false -python-versions = "*" -version = "227" - -[[package]] -category = "main" -description = "Python HTTP for Humans." -name = "requests" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.24.0" - -[package.dependencies] -certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" -idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" - -[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]] -category = "main" -description = "Python 2 and 3 compatibility utilities" -name = "six" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "1.15.0" - -[[package]] -category = "main" -description = "HTTP library with thread-safe connection pooling, file post, and more." -name = "urllib3" +description = "A sample Python project" +name = "sampleproject" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "1.25.10" - -[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)"] - -[[package]] -category = "main" -description = "WebSocket client for Python. hybi13 is supported." -name = "websocket-client" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.57.0" - -[package.dependencies] -six = "*" +version = "1.3.1" [metadata] -content-hash = "bb4c2f3c089b802c1930b6acbeed04711d93e9cdfd9a003eb17518a6d9f350c6" +content-hash = "c8c2c9d899e47bac3972e029ef0e71b75d5df98a28eebef25a75640a19aac177" lock-version = "1.0" python-versions = "^3.8" [metadata.files] -certifi = [ - {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, - {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, -] -chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, -] -docker = [ - {file = "docker-4.3.1-py2.py3-none-any.whl", hash = "sha256:13966471e8bc23b36bfb3a6fb4ab75043a5ef1dac86516274777576bed3b9828"}, - {file = "docker-4.3.1.tar.gz", hash = "sha256:bad94b8dd001a8a4af19ce4becc17f41b09f228173ffe6a4e0355389eef142f2"}, -] -idna = [ - {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, - {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, -] -pywin32 = [ - {file = "pywin32-227-cp27-cp27m-win32.whl", hash = "sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0"}, - {file = "pywin32-227-cp27-cp27m-win_amd64.whl", hash = "sha256:4cdad3e84191194ea6d0dd1b1b9bdda574ff563177d2adf2b4efec2a244fa116"}, - {file = "pywin32-227-cp35-cp35m-win32.whl", hash = "sha256:f4c5be1a293bae0076d93c88f37ee8da68136744588bc5e2be2f299a34ceb7aa"}, - {file = "pywin32-227-cp35-cp35m-win_amd64.whl", hash = "sha256:a929a4af626e530383a579431b70e512e736e9588106715215bf685a3ea508d4"}, - {file = "pywin32-227-cp36-cp36m-win32.whl", hash = "sha256:300a2db938e98c3e7e2093e4491439e62287d0d493fe07cce110db070b54c0be"}, - {file = "pywin32-227-cp36-cp36m-win_amd64.whl", hash = "sha256:9b31e009564fb95db160f154e2aa195ed66bcc4c058ed72850d047141b36f3a2"}, - {file = "pywin32-227-cp37-cp37m-win32.whl", hash = "sha256:47a3c7551376a865dd8d095a98deba954a98f326c6fe3c72d8726ca6e6b15507"}, - {file = "pywin32-227-cp37-cp37m-win_amd64.whl", hash = "sha256:31f88a89139cb2adc40f8f0e65ee56a8c585f629974f9e07622ba80199057511"}, - {file = "pywin32-227-cp38-cp38-win32.whl", hash = "sha256:7f18199fbf29ca99dff10e1f09451582ae9e372a892ff03a28528a24d55875bc"}, - {file = "pywin32-227-cp38-cp38-win_amd64.whl", hash = "sha256:7c1ae32c489dc012930787f06244426f8356e129184a02c25aef163917ce158e"}, - {file = "pywin32-227-cp39-cp39-win32.whl", hash = "sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295"}, - {file = "pywin32-227-cp39-cp39-win_amd64.whl", hash = "sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c"}, -] -requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, -] -six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, -] -urllib3 = [ - {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, - {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, -] -websocket-client = [ - {file = "websocket_client-0.57.0-py2.py3-none-any.whl", hash = "sha256:0fc45c961324d79c781bab301359d5a1b00b13ad1b10415a4780229ef71a5549"}, - {file = "websocket_client-0.57.0.tar.gz", hash = "sha256:d735b91d6d1692a6a181f2a8c9e0238e5f6373356f561bb9dc4c7af36f452010"}, +sampleproject = [ + {file = "sampleproject-1.3.1-py2.py3-none-any.whl", hash = "sha256:26c9172e08244873b0e09c574a229bf2c251c67723a05e08fd3ec0c5ee423796"}, + {file = "sampleproject-1.3.1-py3-none-any.whl", hash = "sha256:75bb5bb4e74a1b77dc0cff25ebbacb54fe1318aaf99a86a036cefc86ed885ced"}, + {file = "sampleproject-1.3.1.tar.gz", hash = "sha256:3593ca2f1e057279d70d6144b14472fb28035b1da213dde60906b703d6f82c55"}, ] diff --git a/tests/fixtures/old_lock/pyproject.toml b/tests/fixtures/old_lock/pyproject.toml index 56ea6350953..377aa676be9 100644 --- a/tests/fixtures/old_lock/pyproject.toml +++ b/tests/fixtures/old_lock/pyproject.toml @@ -6,7 +6,7 @@ authors = ["Poetry Developer "] [tool.poetry.dependencies] python = "^3.8" -docker = "^4.3.1" +sampleproject = ">=1.3.1" [tool.poetry.dev-dependencies] diff --git a/tests/installation/test_chef.py b/tests/installation/test_chef.py index 9fcbbea184c..4e59b608a24 100644 --- a/tests/installation/test_chef.py +++ b/tests/installation/test_chef.py @@ -60,7 +60,7 @@ def test_get_cached_archives_for_link(config, mocker): } -def test_get_cache_directory_for_link(config): +def test_get_cache_directory_for_link(config, config_cache_dir): chef = Chef( config, MockEnv( @@ -71,8 +71,11 @@ def test_get_cache_directory_for_link(config): directory = chef.get_cache_directory_for_link( Link("https://files.python-poetry.org/poetry-1.1.0.tar.gz") ) + expected = Path( - "/foo/artifacts/ba/63/13/283a3b3b7f95f05e9e6f84182d276f7bb0951d5b0cc24422b33f7a4648" + "{}/artifacts/ba/63/13/283a3b3b7f95f05e9e6f84182d276f7bb0951d5b0cc24422b33f7a4648".format( + config_cache_dir.as_posix() + ) ) assert expected == directory diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index bb659321d0f..3dfd818be21 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -126,9 +126,11 @@ def test_execute_executes_a_batch_of_operations( assert 5 == len(env.executed) -def test_execute_shows_skipped_operations_if_verbose(config, pool, io): +def test_execute_shows_skipped_operations_if_verbose( + config, pool, io, config_cache_dir +): config = Config() - config.merge({"cache-dir": "/foo"}) + config.merge({"cache-dir": config_cache_dir.as_posix()}) env = MockEnv() executor = Executor(env, pool, config, io) diff --git a/tests/masonry/builders/test_editable_builder.py b/tests/masonry/builders/test_editable_builder.py index daeff0e7777..3bf1e59c98e 100644 --- a/tests/masonry/builders/test_editable_builder.py +++ b/tests/masonry/builders/test_editable_builder.py @@ -176,9 +176,9 @@ def test_builder_installs_proper_files_for_standard_packages(simple_poetry, tmp_ def test_builder_falls_back_on_setup_and_pip_for_packages_with_build_scripts( - extended_poetry, + extended_poetry, tmp_dir ): - env = MockEnv(path=Path("/foo")) + env = MockEnv(path=Path(tmp_dir) / "foo") builder = EditableBuilder(extended_poetry, env, NullIO()) builder.build() @@ -219,8 +219,8 @@ def test_builder_installs_proper_files_when_packages_configured( assert len(paths) == len(expected) -def test_builder_should_execute_build_scripts(extended_without_setup_poetry): - env = MockEnv(path=Path("/foo")) +def test_builder_should_execute_build_scripts(extended_without_setup_poetry, tmp_dir): + env = MockEnv(path=Path(tmp_dir) / "foo") builder = EditableBuilder(extended_without_setup_poetry, env, NullIO()) builder.build() diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py index 8934ae43427..9c1e2c623d9 100644 --- a/tests/utils/test_env.py +++ b/tests/utils/test_env.py @@ -642,7 +642,7 @@ def test_run_with_input_non_zero_return(tmp_dir, tmp_venv): def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_ones_first( - manager, poetry, config, mocker + manager, poetry, config, mocker, config_virtualenvs_path ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -662,14 +662,14 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_ manager.create_venv(NullIO()) m.assert_called_with( - Path("/foo/virtualenvs/{}-py3.7".format(venv_name)), + config_virtualenvs_path / "{}-py3.7".format(venv_name), executable="python3", flags={"always-copy": False}, ) def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific_ones( - manager, poetry, config, mocker + manager, poetry, config, mocker, config_virtualenvs_path ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -688,7 +688,7 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific manager.create_venv(NullIO()) m.assert_called_with( - Path("/foo/virtualenvs/{}-py3.9".format(venv_name)), + config_virtualenvs_path / "{}-py3.9".format(venv_name), executable="python3.9", flags={"always-copy": False}, ) @@ -749,7 +749,7 @@ def test_create_venv_does_not_try_to_find_compatible_versions_with_executable( def test_create_venv_uses_patch_version_to_detect_compatibility( - manager, poetry, config, mocker + manager, poetry, config, mocker, config_virtualenvs_path ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -773,18 +773,15 @@ def test_create_venv_uses_patch_version_to_detect_compatibility( assert not check_output.called m.assert_called_with( - Path( - "/foo/virtualenvs/{}-py{}.{}".format( - venv_name, version.major, version.minor - ) - ), + config_virtualenvs_path + / "{}-py{}.{}".format(venv_name, version.major, version.minor), executable=None, flags={"always-copy": False}, ) def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable( - manager, poetry, config, mocker + manager, poetry, config, mocker, config_virtualenvs_path ): if "VIRTUAL_ENV" in os.environ: del os.environ["VIRTUAL_ENV"] @@ -811,11 +808,8 @@ def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable( assert check_output.called m.assert_called_with( - Path( - "/foo/virtualenvs/{}-py{}.{}".format( - venv_name, version.major, version.minor - 1 - ) - ), + config_virtualenvs_path + / "{}-py{}.{}".format(venv_name, version.major, version.minor - 1), executable="python{}.{}".format(version.major, version.minor - 1), flags={"always-copy": False}, ) From 4a14ebae1803c4ed46592e70236a298d5cc287c8 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 20 Oct 2020 22:32:48 +0200 Subject: [PATCH 22/54] tests: remove scripts for extended fixtures --- tests/fixtures/extended_project/pyproject.toml | 3 --- tests/fixtures/extended_project_without_setup/pyproject.toml | 3 --- 2 files changed, 6 deletions(-) diff --git a/tests/fixtures/extended_project/pyproject.toml b/tests/fixtures/extended_project/pyproject.toml index 954b12b3497..ecb7deb9107 100644 --- a/tests/fixtures/extended_project/pyproject.toml +++ b/tests/fixtures/extended_project/pyproject.toml @@ -25,6 +25,3 @@ build = "build.py" # Requirements [tool.poetry.dependencies] python = "~2.7 || ^3.4" - -[tool.poetry.scripts] -foo = "foo:bar" diff --git a/tests/fixtures/extended_project_without_setup/pyproject.toml b/tests/fixtures/extended_project_without_setup/pyproject.toml index 42375c03c25..5c9dc2774c7 100644 --- a/tests/fixtures/extended_project_without_setup/pyproject.toml +++ b/tests/fixtures/extended_project_without_setup/pyproject.toml @@ -27,6 +27,3 @@ generate-setup-file = false # Requirements [tool.poetry.dependencies] python = "~2.7 || ^3.4" - -[tool.poetry.scripts] -foo = "foo:bar" From 21abbfc43141e551507acee2051d4a11997fc2ba Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 20 Oct 2020 23:18:37 +0200 Subject: [PATCH 23/54] tests: ensure mock env uses accessible paths --- tests/console/conftest.py | 6 ++++-- tests/installation/test_executor.py | 25 +++++++++++++------------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/console/conftest.py b/tests/console/conftest.py index 8a3a5cba8ae..6d7ef0fa675 100644 --- a/tests/console/conftest.py +++ b/tests/console/conftest.py @@ -22,8 +22,10 @@ def installer(): @pytest.fixture -def env(): - return MockEnv(path=Path("/prefix"), base=Path("/base/prefix"), is_venv=True) +def env(tmp_dir): + path = Path(tmp_dir) / ".venv" + path.mkdir(parents=True) + return MockEnv(path=path, is_venv=True) @pytest.fixture(autouse=True) diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index 3dfd818be21..e192f92df40 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -22,6 +22,13 @@ from tests.repositories.test_pypi_repository import MockRepository +@pytest.fixture +def env(tmp_dir): + path = Path(tmp_dir) / ".venv" + path.mkdir(parents=True) + return MockEnv(path=path, is_venv=True) + + @pytest.fixture() def io(): io = BufferedIO() @@ -57,12 +64,11 @@ def callback(request, uri, headers): def test_execute_executes_a_batch_of_operations( - config, pool, io, tmp_dir, mock_file_downloads + config, pool, io, tmp_dir, mock_file_downloads, env ): config = Config() config.merge({"cache-dir": tmp_dir}) - env = MockEnv(path=Path(tmp_dir)) executor = Executor(env, pool, config, io) file_package = Package( @@ -127,12 +133,11 @@ def test_execute_executes_a_batch_of_operations( def test_execute_shows_skipped_operations_if_verbose( - config, pool, io, config_cache_dir + config, pool, io, config_cache_dir, env ): config = Config() config.merge({"cache-dir": config_cache_dir.as_posix()}) - env = MockEnv() executor = Executor(env, pool, config, io) executor.verbose() @@ -152,8 +157,7 @@ def test_execute_shows_skipped_operations_if_verbose( @pytest.mark.skipif( not PY36, reason="Improved error rendering is only available on Python >=3.6" ) -def test_execute_should_show_errors(config, mocker, io): - env = MockEnv() +def test_execute_should_show_errors(config, mocker, io, env): executor = Executor(env, pool, config, io) executor.verbose() @@ -175,9 +179,8 @@ def test_execute_should_show_errors(config, mocker, io): def test_execute_should_show_operation_as_cancelled_on_subprocess_keyboard_interrupt( - config, mocker, io + config, mocker, io, env ): - env = MockEnv() executor = Executor(env, pool, config, io) executor.verbose() @@ -196,8 +199,7 @@ def test_execute_should_show_operation_as_cancelled_on_subprocess_keyboard_inter assert expected == io.fetch_output() -def test_execute_should_gracefully_handle_io_error(config, mocker, io): - env = MockEnv() +def test_execute_should_gracefully_handle_io_error(config, mocker, io, env): executor = Executor(env, pool, config, io) executor.verbose() @@ -223,7 +225,7 @@ def write_line(string, flags=None): def test_executor_should_delete_incomplete_downloads( - config, io, tmp_dir, mocker, pool, mock_file_downloads + config, io, tmp_dir, mocker, pool, mock_file_downloads, env ): fixture = Path(__file__).parent.parent.joinpath( "fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl" @@ -246,7 +248,6 @@ def test_executor_should_delete_incomplete_downloads( config = Config() config.merge({"cache-dir": tmp_dir}) - env = MockEnv(path=Path(tmp_dir)) executor = Executor(env, pool, config, io) with pytest.raises(Exception, match="Download error"): From e43bc9ad1dccdcb1dcc25a3e4b670500ef289c20 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 20 Oct 2020 23:19:48 +0200 Subject: [PATCH 24/54] tests/executor: verify return code after output --- tests/installation/test_executor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index e192f92df40..53ad3677350 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -101,7 +101,7 @@ def test_execute_executes_a_batch_of_operations( source_url="https://github.com/demo/demo.git", ) - assert 0 == executor.execute( + return_code = executor.execute( [ Install(Package("pytest", "3.5.2")), Uninstall(Package("attrs", "17.4.0")), @@ -130,6 +130,7 @@ def test_execute_executes_a_batch_of_operations( output = set(io.fetch_output().splitlines()) assert expected == output assert 5 == len(env.executed) + assert 0 == return_code def test_execute_shows_skipped_operations_if_verbose( From 5b4c6a5eb47ac1eb1ea34e7a5d87e13e577024bb Mon Sep 17 00:00:00 2001 From: Lucas Cavalcante de Sousa Date: Sat, 31 Oct 2020 16:56:56 -0300 Subject: [PATCH 25/54] Remove unused functions Resolves: #3236 \#3236 states that some functions are defined but never used, thus this PR remove then. While removing them, this also makes sure that there is no trouble with: ```bash $ poetry run pytest tests/ $ poetry run pre-commit run --all-files ``` as stated at `CONTRIBUTING.md`. The functions removed were: - `poetry/config/config.py`: + `_get_validator` - `poetry/console/commands/config.py`: + `_list_setting` + `_get_formatted_value` - `poetry/console/commands/run.py` : + `_module` - `poetry/console/commands/self/update.py : + `_bin_path` - `poetry/mixology/version_solver.py`: + `_excludes_single_version` - `poetry/puzzle/provider.py`: + `_formatter_elapsed` - `poetry/utils/setup_reader.py`: + `_is_empty_result` By removing some of those functions Flack announced that there is no more need to do some includes, so they were removed too: - `poetry/mixology/version_solver.py`: + `from typing import Any` + `from poetry.core.semver import Version` + `from poetry.core.semver import VersionRange` - `poetry/puzzle/provider.py`: + `import time` --- poetry/config/config.py | 12 ------------ poetry/console/commands/config.py | 16 ---------------- poetry/console/commands/run.py | 11 ----------- poetry/console/commands/self/update.py | 8 -------- poetry/mixology/version_solver.py | 6 ------ poetry/puzzle/provider.py | 6 +----- poetry/utils/setup_reader.py | 8 -------- 7 files changed, 1 insertion(+), 66 deletions(-) diff --git a/poetry/config/config.py b/poetry/config/config.py index 6f18127e8ff..666f2e74ce2 100644 --- a/poetry/config/config.py +++ b/poetry/config/config.py @@ -136,18 +136,6 @@ def process(self, value): # type: (Any) -> Any return re.sub(r"{(.+?)}", lambda m: self.get(m.group(1)), value) - def _get_validator(self, name): # type: (str) -> Callable - if name in { - "virtualenvs.create", - "virtualenvs.in-project", - "virtualenvs.options.always-copy", - "installer.parallel", - }: - return boolean_validator - - if name == "virtualenvs.path": - return str - def _get_normalizer(self, name): # type: (str) -> Callable if name in { "virtualenvs.create", diff --git a/poetry/console/commands/config.py b/poetry/console/commands/config.py index 934b2c818eb..2e84429ec36 100644 --- a/poetry/console/commands/config.py +++ b/poetry/console/commands/config.py @@ -302,14 +302,6 @@ def _list_configuration(self, config, raw, k=""): self.line(message) - def _list_setting(self, contents, setting=None, k=None, default=None): - values = self._get_setting(contents, setting, k, default) - - for value in values: - self.line( - "{} = {}".format(value[0], value[1]) - ) - def _get_setting(self, contents, setting=None, k=None, default=None): orig_k = k @@ -351,11 +343,3 @@ def _get_setting(self, contents, setting=None, k=None, default=None): values.append(((k or "") + key, value)) return values - - def _get_formatted_value(self, value): - if isinstance(value, list): - value = [json.dumps(val) if isinstance(val, list) else val for val in value] - - value = "[{}]".format(", ".join(value)) - - return json.dumps(value) diff --git a/poetry/console/commands/run.py b/poetry/console/commands/run.py index fda01114426..46202c52c37 100644 --- a/poetry/console/commands/run.py +++ b/poetry/console/commands/run.py @@ -47,14 +47,3 @@ def run_script(self, script, args): ] return self.env.execute(*cmd) - - @property - def _module(self): - from poetry.core.masonry.utils.module import Module - - poetry = self.poetry - package = poetry.package - path = poetry.file.parent - module = Module(package.name, path.as_posix(), package.packages) - - return module diff --git a/poetry/console/commands/self/update.py b/poetry/console/commands/self/update.py index 8660bea2f09..f98da89632f 100644 --- a/poetry/console/commands/self/update.py +++ b/poetry/console/commands/self/update.py @@ -257,14 +257,6 @@ def _get_release_name(self, version): return "poetry-{}-{}".format(version, platform) - def _bin_path(self, base_path, bin): - from poetry.utils._compat import WINDOWS - - if WINDOWS: - return (base_path / "Scripts" / bin).with_suffix(".exe") - - return base_path / "bin" / bin - def make_bin(self): from poetry.utils._compat import WINDOWS diff --git a/poetry/mixology/version_solver.py b/poetry/mixology/version_solver.py index 679ecf07c2d..cee323ea367 100644 --- a/poetry/mixology/version_solver.py +++ b/poetry/mixology/version_solver.py @@ -2,7 +2,6 @@ import time from typing import TYPE_CHECKING -from typing import Any from typing import Dict from typing import List from typing import Union @@ -10,8 +9,6 @@ from poetry.core.packages import Dependency from poetry.core.packages import Package from poetry.core.packages import ProjectPackage -from poetry.core.semver import Version -from poetry.core.semver import VersionRange from .failure import SolveFailure from .incompatibility import Incompatibility @@ -423,9 +420,6 @@ def _get_min(dependency): return dependency.complete_name - def _excludes_single_version(self, constraint): # type: (Any) -> bool - return isinstance(VersionRange().difference(constraint), Version) - def _result(self): # type: () -> SolverResult """ Creates a #SolverResult from the decisions in _solution diff --git a/poetry/puzzle/provider.py b/poetry/puzzle/provider.py index c05efbd6844..d158a31dc2d 100644 --- a/poetry/puzzle/provider.py +++ b/poetry/puzzle/provider.py @@ -1,7 +1,6 @@ import logging import os import re -import time from contextlib import contextmanager from tempfile import mkdtemp @@ -44,10 +43,7 @@ class Indicator(ProgressIndicator): - def _formatter_elapsed(self): - elapsed = time.time() - self._start_time - - return "{:.1f}s".format(elapsed) + pass class Provider: diff --git a/poetry/utils/setup_reader.py b/poetry/utils/setup_reader.py index 6a45103c1f4..3a1ce2f170d 100644 --- a/poetry/utils/setup_reader.py +++ b/poetry/utils/setup_reader.py @@ -59,14 +59,6 @@ def read_from_directory( return result - @classmethod - def _is_empty_result(cls, result): # type: (Dict[str, Any]) -> bool - return ( - not result["install_requires"] - and not result["extras_require"] - and not result["python_requires"] - ) - def read_setup_py( self, filepath ): # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]] From cc195f1dd086d1c4d12a3acc8d6766981ba431ac Mon Sep 17 00:00:00 2001 From: Anatoli Babenia Date: Sat, 10 Oct 2020 13:01:30 +0300 Subject: [PATCH 26/54] Use `.zshrc` instead of `.zprofile` Because `.zprofile` is only run for login shells, it is not used by graphical terminals. `.zprofile` is also not created by default. Fixes #2620 --- get-poetry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/get-poetry.py b/get-poetry.py index c14c9d4d6ec..73b818c7a5c 100644 --- a/get-poetry.py +++ b/get-poetry.py @@ -903,7 +903,7 @@ def get_unix_profiles(self): if "zsh" in SHELL: zdotdir = os.getenv("ZDOTDIR", HOME) - profiles.append(os.path.join(zdotdir, ".zprofile")) + profiles.append(os.path.join(zdotdir, ".zshrc")) bash_profile = os.path.join(HOME, ".bash_profile") if os.path.exists(bash_profile): From 73585b600dc528ffb0904872befd94f9ea0b9d02 Mon Sep 17 00:00:00 2001 From: Rayan Das Date: Sat, 31 Oct 2020 13:32:29 +0530 Subject: [PATCH 27/54] consider using if expression Signed-off-by: Rayan Das --- poetry/utils/appdirs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry/utils/appdirs.py b/poetry/utils/appdirs.py index 5b9da0cdde3..ab99506a028 100644 --- a/poetry/utils/appdirs.py +++ b/poetry/utils/appdirs.py @@ -92,7 +92,7 @@ def user_data_dir(appname, roaming=False): That means, by default "~/.local/share/". """ if WINDOWS: - const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + const = "CSIDL_APPDATA" if roaming else "CSIDL_LOCAL_APPDATA" path = os.path.join(os.path.normpath(_get_win_folder(const)), appname) elif sys.platform == "darwin": path = os.path.join(expanduser("~/Library/Application Support/"), appname) From f5f6efca250bdc3df530d1764943976ccbd58db0 Mon Sep 17 00:00:00 2001 From: Rayan Das Date: Sat, 31 Oct 2020 13:35:20 +0530 Subject: [PATCH 28/54] iterate the dictionary directly instead of calling .keys() Signed-off-by: Rayan Das --- poetry/puzzle/provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry/puzzle/provider.py b/poetry/puzzle/provider.py index d158a31dc2d..333dfc60cf3 100644 --- a/poetry/puzzle/provider.py +++ b/poetry/puzzle/provider.py @@ -101,7 +101,7 @@ def search_for(self, dependency): # type: (Dependency) -> List[Package] if dependency.is_root: return PackageCollection(dependency, [self._package]) - for constraint in self._search_for.keys(): + for constraint in self._search_for: if ( constraint.is_same_package_as(dependency) and constraint.constraint.intersect(dependency.constraint) From e35f0819e3a9d6ea8c87be935d39db948d91f105 Mon Sep 17 00:00:00 2001 From: Rayan Das Date: Sat, 31 Oct 2020 13:44:14 +0530 Subject: [PATCH 29/54] the built-in function being used does not require comprehension and can work directly with a generator expression Signed-off-by: Rayan Das --- poetry/mixology/incompatibility.py | 2 +- poetry/mixology/version_solver.py | 8 +++----- poetry/utils/password_manager.py | 8 +++----- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/poetry/mixology/incompatibility.py b/poetry/mixology/incompatibility.py index 3cbc867874a..a5be2dadbdf 100644 --- a/poetry/mixology/incompatibility.py +++ b/poetry/mixology/incompatibility.py @@ -23,7 +23,7 @@ def __init__( if ( len(terms) != 1 and isinstance(cause, ConflictCause) - and any([term.is_positive() and term.dependency.is_root for term in terms]) + and any(term.is_positive() and term.dependency.is_root for term in terms) ): terms = [ term diff --git a/poetry/mixology/version_solver.py b/poetry/mixology/version_solver.py index cee323ea367..8d70a0df288 100644 --- a/poetry/mixology/version_solver.py +++ b/poetry/mixology/version_solver.py @@ -403,11 +403,9 @@ def _get_min(dependency): # We'll continue adding its dependencies, then go back to # unit propagation which will guide us to choose a better version. conflict = conflict or all( - [ - term.dependency.complete_name == dependency.complete_name - or self._solution.satisfies(term) - for term in incompatibility.terms - ] + term.dependency.complete_name == dependency.complete_name + or self._solution.satisfies(term) + for term in incompatibility.terms ) if not conflict: diff --git a/poetry/utils/password_manager.py b/poetry/utils/password_manager.py index 24a615a46bf..6c993840282 100644 --- a/poetry/utils/password_manager.py +++ b/poetry/utils/password_manager.py @@ -101,11 +101,9 @@ def _check(self): backends = keyring.backend.get_all_keyring() self._is_available = any( - [ - b.name.split(" ")[0] not in ["chainer", "fail"] - and "plaintext" not in b.name.lower() - for b in backends - ] + b.name.split(" ")[0] not in ["chainer", "fail"] + and "plaintext" not in b.name.lower() + for b in backends ) except Exception: self._is_available = False From d646d45d8776a88bff6ad8126f52b92cd944f8b9 Mon Sep 17 00:00:00 2001 From: Steph Samson Date: Mon, 2 Nov 2020 23:29:48 +0100 Subject: [PATCH 30/54] doc: mention Conda's dependency resolver Resolves: #477 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3ad7b97d5f1..bc392a15eaa 100644 --- a/README.md +++ b/README.md @@ -195,8 +195,8 @@ dependency management, packaging and publishing. It takes inspiration in tools that exist in other languages, like `composer` (PHP) or `cargo` (Rust). -And, finally, there is no reliable tool to properly resolve dependencies in Python, so I started `poetry` -to bring an exhaustive dependency resolver to the Python community. +And, finally, I started `poetry` to bring another exhaustive dependency resolver to the Python community apart from +[Conda's](https://conda.io). ### What about Pipenv? From 369ced14be5218a6237d8eb0a579f6592fd922ee Mon Sep 17 00:00:00 2001 From: adnan-st <72813655+adnan-st@users.noreply.github.com> Date: Fri, 13 Nov 2020 00:43:44 -0500 Subject: [PATCH 31/54] Fixed missing space in env activation 'one-liner' (#3350) `source`poetry env info --path`/bin/activate` replaced with: `source `poetry env info --path`/bin/activate` --- docs/docs/basic-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/basic-usage.md b/docs/docs/basic-usage.md index 178c75954d6..c8a4e0a7e3a 100644 --- a/docs/docs/basic-usage.md +++ b/docs/docs/basic-usage.md @@ -120,7 +120,7 @@ To deactivate this virtual environment simply use `deactivate`. |-------------------|------------------------------------------------|---------------------------------------------|-----------------| | New Shell | `poetry shell` | `poetry shell` | `exit` | | Manual Activation | `source {path_to_venv}/bin/activate` | `source {path_to_venv}\Scripts\activate.bat`| `deactivate` | -| One-liner | ```source`poetry env info --path`/bin/activate```| | `deactivate` | +| One-liner | ```source `poetry env info --path`/bin/activate```| | `deactivate` | ### Version constraints From 639d5e05773ed8b91b80ec03c2ee64f500534c10 Mon Sep 17 00:00:00 2001 From: Murad Bashirov <66242799+spitfire-hash@users.noreply.github.com> Date: Thu, 19 Nov 2020 00:07:41 +0400 Subject: [PATCH 32/54] Fixed the command for activating venv in Windows (#3373) * Fixed the command(deleted the `source` because you don't need `source` in order to run batch files * Made code of the table look better, nothing changed in client. --- docs/docs/basic-usage.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/docs/basic-usage.md b/docs/docs/basic-usage.md index c8a4e0a7e3a..eed4817379f 100644 --- a/docs/docs/basic-usage.md +++ b/docs/docs/basic-usage.md @@ -111,15 +111,15 @@ To deactivate the virtual environment without leaving the shell use `deactivate` Alternatively, to avoid creating a new shell, you can manually activate the -virtual environment by running `source {path_to_venv}/bin/activate` (`source {path_to_venv}\Scripts\activate.bat` on Windows). +virtual environment by running `source {path_to_venv}/bin/activate` (`{path_to_venv}\Scripts\activate.bat` on Windows). To get the path to your virtual environment run `poetry env info --path`. You can also combine these into a nice one-liner, `source $(poetry env info --path)/bin/activate` To deactivate this virtual environment simply use `deactivate`. -| | POSIX Shell | Windows | Exit/Deactivate | -|-------------------|------------------------------------------------|---------------------------------------------|-----------------| -| New Shell | `poetry shell` | `poetry shell` | `exit` | -| Manual Activation | `source {path_to_venv}/bin/activate` | `source {path_to_venv}\Scripts\activate.bat`| `deactivate` | +| | POSIX Shell | Windows | Exit/Deactivate | +|-------------------|---------------------------------------------------|---------------------------------------------|-----------------| +| New Shell | `poetry shell` | `poetry shell` | `exit` | +| Manual Activation | `source {path_to_venv}/bin/activate` | `{path_to_venv}\Scripts\activate.bat` | `deactivate` | | One-liner | ```source `poetry env info --path`/bin/activate```| | `deactivate` | From bf30ca696aa47bcc42937910d620c95c95c0741e Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 24 Nov 2020 13:55:55 +0100 Subject: [PATCH 33/54] Remove python 2.7/3.5 compatibility code (#3405) * Remove python 2.7/3.5 compatibility code This change removes code, dependencies and tests related to python 2.7 and 3.5 compatibility. The minimum supported runtime python version for poetry is now 3.6. * tests: mitigate httpretty urllib incompatibility Relates-to: #3404 --- poetry.lock | 570 +++--------------- poetry/config/config.py | 5 +- poetry/console/application.py | 3 +- poetry/console/commands/check.py | 3 +- poetry/console/commands/config.py | 13 +- poetry/console/commands/init.py | 20 +- poetry/console/commands/new.py | 3 +- poetry/console/commands/publish.py | 4 +- poetry/console/commands/self/update.py | 4 +- poetry/console/config/application_config.py | 13 +- poetry/factory.py | 2 +- poetry/inspection/info.py | 14 +- poetry/installation/authenticator.py | 8 +- poetry/installation/chef.py | 2 +- poetry/installation/executor.py | 14 +- poetry/installation/pip_installer.py | 9 +- poetry/locations.py | 3 +- poetry/masonry/builders/editable.py | 2 +- poetry/mixology/partial_solution.py | 7 +- poetry/packages/locker.py | 9 +- poetry/poetry.py | 3 +- poetry/publishing/publisher.py | 2 +- poetry/publishing/uploader.py | 2 +- poetry/puzzle/provider.py | 11 +- poetry/repositories/installed_repository.py | 2 +- poetry/repositories/legacy_repository.py | 12 +- poetry/repositories/pypi_repository.py | 21 +- poetry/utils/_compat.py | 249 +------- poetry/utils/env.py | 6 +- poetry/utils/exporter.py | 7 +- poetry/utils/helpers.py | 2 +- poetry/utils/setup_reader.py | 25 +- poetry/utils/shell.py | 3 +- pyproject.toml | 34 +- tests/compat.py | 4 + tests/conftest.py | 4 +- tests/console/commands/env/conftest.py | 3 +- tests/console/commands/env/helpers.py | 2 +- tests/console/commands/env/test_info.py | 3 +- tests/console/commands/env/test_remove.py | 2 +- tests/console/commands/env/test_use.py | 10 +- tests/console/commands/self/test_update.py | 3 +- tests/console/commands/test_add.py | 23 +- tests/console/commands/test_cache.py | 4 +- tests/console/commands/test_check.py | 14 +- tests/console/commands/test_config.py | 6 +- tests/console/commands/test_init.py | 5 +- tests/console/commands/test_lock.py | 3 +- tests/console/commands/test_publish.py | 34 +- tests/console/commands/test_search.py | 4 +- tests/console/conftest.py | 3 +- tests/helpers.py | 20 +- tests/inspection/test_info.py | 22 +- tests/installation/test_chef.py | 3 +- tests/installation/test_chooser.py | 3 +- tests/installation/test_executor.py | 7 +- tests/installation/test_installer.py | 15 +- tests/installation/test_installer_old.py | 15 +- tests/installation/test_pip_installer.py | 3 +- .../masonry/builders/test_editable_builder.py | 3 +- ...st_python_requirement_solution_provider.py | 9 - .../test_python_requirement_solution.py | 6 - tests/publishing/test_publisher.py | 3 +- tests/publishing/test_uploader.py | 3 +- tests/puzzle/conftest.py | 4 +- tests/puzzle/test_provider.py | 8 +- tests/puzzle/test_solver.py | 3 +- .../repositories/test_installed_repository.py | 8 +- tests/repositories/test_legacy_repository.py | 14 +- tests/repositories/test_pypi_repository.py | 4 +- tests/test_factory.py | 26 +- tests/utils/test_env.py | 78 +-- tests/utils/test_env_site.py | 5 +- tests/utils/test_exporter.py | 7 +- tests/utils/test_helpers.py | 3 +- tests/utils/test_setup_reader.py | 8 - 76 files changed, 343 insertions(+), 1151 deletions(-) create mode 100644 tests/compat.py diff --git a/poetry.lock b/poetry.lock index 973733a6d61..b066ade9a7c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -16,30 +16,18 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "20.2.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] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] -docs = ["sphinx", "sphinx-rtd-theme", "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" -version = "1.6.1" -description = "Backport of functools.lru_cache" -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 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-black-multipy", "pytest-cov"] - [[package]] name = "cachecontrol" version = "0.12.6" @@ -72,7 +60,7 @@ msgpack = ["msgpack-python (>=0.5,<0.6)"] [[package]] name = "certifi" -version = "2020.6.20" +version = "2020.11.8" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -126,40 +114,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] crashtest = {version = ">=0.3.0,<0.4.0", markers = "python_version >= \"3.6\" and python_version < \"4.0\""} -enum34 = {version = ">=1.1,<2.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""} pastel = ">=0.2.0,<0.3.0" pylev = ">=1.3,<2.0" -typing = {version = ">=3.6,<4.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\""} -typing-extensions = {version = ">=3.6,<4.0", markers = "python_version >= \"3.5\" and python_full_version < \"3.5.4\""} [[package]] name = "colorama" -version = "0.4.3" +version = "0.4.4" description = "Cross-platform colored terminal text." 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 || >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.3" @@ -181,7 +146,7 @@ python-versions = ">=3.6,<4.0" [[package]] name = "cryptography" -version = "3.1.1" +version = "3.2.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = false @@ -189,16 +154,14 @@ python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" [package.dependencies] cffi = ">=1.8,<1.11.3 || >1.11.3" -enum34 = {version = "*", markers = "python_version < \"3\""} -ipaddress = {version = "*", markers = "python_version < \"3\""} six = ">=1.4.1" [package.extras] -docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0,<3.1.0 || >3.1.0,<3.1.1 || >3.1.1)", "sphinx-rtd-theme"] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"] +test = ["pytest (>=3.6.0,!=3.9.0,!=3.9.1,!=3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] [[package]] name = "distlib" @@ -208,25 +171,6 @@ category = "main" optional = false python-versions = "*" -[[package]] -name = "entrypoints" -version = "0.3" -description = "Discover and load entry points from installed packages." -category = "main" -optional = false -python-versions = ">=2.7" - -[package.dependencies] -configparser = {version = ">=3.5", markers = "python_version == \"2.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" -optional = false -python-versions = "*" - [[package]] name = "filelock" version = "3.0.12" @@ -235,38 +179,6 @@ category = "main" 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 = "main" -optional = false -python-versions = ">=2.6, <3" - -[[package]] -name = "glob2" -version = "0.6" -description = "Version of the glob module that can capture patterns and supports recursive wildcards" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "html5lib" version = "1.1" @@ -287,18 +199,15 @@ lxml = ["lxml"] [[package]] name = "httpretty" -version = "0.9.7" +version = "1.0.2" description = "HTTP client mock for Python" category = "dev" optional = false -python-versions = "*" - -[package.dependencies] -six = "*" +python-versions = ">=3" [[package]] name = "identify" -version = "1.5.5" +version = "1.5.10" description = "File identification library for Python" category = "dev" optional = false @@ -324,9 +233,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] @@ -335,92 +241,46 @@ testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] [[package]] name = "importlib-resources" -version = "3.0.0" +version = "3.3.0" description = "Read resources from Python packages" category = "main" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" [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"] -[[package]] -name = "ipaddress" -version = "1.0.23" -description = "IPv4/IPv6 manipulation library" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "jeepney" -version = "0.4.3" +version = "0.6.0" description = "Low-level, pure Python DBus protocol wrapper." category = "main" optional = false -python-versions = ">=3.5" - -[package.extras] -dev = ["testpath"] - -[[package]] -name = "keyring" -version = "18.0.1" -description = "Store and access your passwords safely." -category = "main" -optional = false -python-versions = ">=2.7" - -[package.dependencies] -entrypoints = "*" -pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""} -secretstorage = {version = "<3", markers = "(sys_platform == \"linux2\" or sys_platform == \"linux\") and python_version < \"3.5\""} - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs", "pytest-flake8"] - -[[package]] -name = "keyring" -version = "20.0.1" -description = "Store and access your passwords safely." -category = "main" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""} -secretstorage = {version = "*", markers = "sys_platform == \"linux\""} +python-versions = ">=3.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"] +test = ["pytest", "pytest-trio", "pytest-asyncio", "testpath", "trio"] [[package]] name = "keyring" -version = "21.4.0" +version = "21.5.0" description = "Store and access your passwords safely." category = "main" optional = false python-versions = ">=3.6" [package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-metadata = {version = ">=1", markers = "python_version < \"3.8\""} jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""} -SecretStorage = {version = ">=3", markers = "sys_platform == \"linux\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [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 (>=0.3.7)", "pytest-cov", "pytest-mypy"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "pytest-black (>=0.3.7)", "pytest-mypy"] [[package]] name = "lockfile" @@ -430,37 +290,9 @@ category = "main" optional = false python-versions = "*" -[[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" -description = "More routines for operating on iterables, beyond itertools" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = ">=1.0.0,<2.0.0" - -[[package]] -name = "more-itertools" -version = "8.5.0" +version = "8.6.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false @@ -502,18 +334,6 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[[package]] -name = "pathlib2" -version = "2.3.5" -description = "Object-oriented filesystem paths" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -scandir = {version = "*", markers = "python_version < \"3.5\""} -six = "*" - [[package]] name = "pexpect" version = "4.8.0" @@ -527,7 +347,7 @@ ptyprocess = ">=0.5" [[package]] name = "pkginfo" -version = "1.5.0.1" +version = "1.6.1" description = "Query metadatdata from sdists / bdists / installed packages." category = "main" optional = false @@ -559,15 +379,11 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -enum34 = {version = ">=1.1.10,<2.0.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""} -functools32 = {version = ">=3.2.3-2,<4.0.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""} importlib-metadata = {version = ">=1.7.0,<2.0.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.5\" and python_version < \"3.8\""} -pathlib2 = {version = ">=2.3.5,<3.0.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""} -typing = {version = ">=3.7.4.1,<4.0.0.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""} [[package]] name = "pre-commit" -version = "2.7.1" +version = "2.9.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false @@ -623,34 +439,6 @@ category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -[[package]] -name = "pytest" -version = "4.6.11" -description = "pytest: simple powerful testing with Python" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" - -[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 = "*" - -[package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"] - [[package]] name = "pytest" version = "5.4.3" @@ -666,13 +454,12 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} more-itertools = ">=4.0.0" packaging = "*" -pathlib2 = {version = ">=2.2.0", markers = "python_version < \"3.6\""} pluggy = ">=0.12,<1.0" py = ">=1.5.0" wcwidth = "*" [package.extras] -checkqa-mypy = ["mypy (v0.761)"] +checkqa-mypy = ["mypy (==v0.761)"] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] @@ -688,7 +475,7 @@ coverage = ">=4.4" pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "pytest-xdist", "virtualenv"] +testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-mock" @@ -699,7 +486,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] @@ -736,7 +522,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "requests" -version = "2.24.0" +version = "2.25.0" description = "Python HTTP for Humans." category = "main" optional = false @@ -746,11 +532,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" certifi = ">=2017.4.17" chardet = ">=3.0.2,<4" 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 || >1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] [[package]] name = "requests-toolbelt" @@ -763,38 +549,16 @@ python-versions = "*" [package.dependencies] requests = ">=2.0.1,<3.0.0" -[[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 = "secretstorage" -version = "2.3.1" -description = "Python bindings to FreeDesktop.org Secret Service API" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -cryptography = "*" - -[package.extras] -dbus-python = ["dbus-python"] - [[package]] name = "secretstorage" -version = "3.1.2" +version = "3.2.0" description = "Python bindings to FreeDesktop.org Secret Service API" category = "main" optional = false python-versions = ">=3.5" [package.dependencies] -cryptography = "*" +cryptography = ">=2.0" jeepney = ">=0.4.2" [[package]] @@ -805,17 +569,6 @@ category = "main" optional = false python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,>=2.6" -[[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 = "main" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" - [[package]] name = "six" version = "1.15.0" @@ -824,14 +577,6 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -[[package]] -name = "subprocess32" -version = "3.5.4" -description = "A backport of the subprocess module from Python 3 for use on 2.x." -category = "main" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4" - [[package]] name = "termcolor" version = "1.1.0" @@ -842,11 +587,11 @@ python-versions = "*" [[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 = "tomlkit" @@ -856,14 +601,9 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -[package.dependencies] -enum34 = {version = ">=1.1,<2.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""} -functools32 = {version = ">=3.2.3,<4.0.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""} -typing = {version = ">=3.6,<4.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\""} - [[package]] name = "tox" -version = "3.20.0" +version = "3.20.1" description = "tox is a generic virtualenv management and test command line tool" category = "dev" optional = false @@ -872,7 +612,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} filelock = ">=3.0.0" -importlib-metadata = {version = ">=0.12,<2", markers = "python_version < \"3.8\""} +importlib-metadata = {version = ">=0.12,<3", markers = "python_version < \"3.8\""} packaging = ">=14" pluggy = ">=0.12.0" py = ">=1.4.17" @@ -884,25 +624,6 @@ virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2, 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]] -name = "typing" -version = "3.7.4.3" -description = "Type Hints for Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[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.dependencies] -typing = {version = ">=3.7.4", markers = "python_version < \"3.5\""} - [[package]] name = "urllib3" version = "1.25.10" @@ -914,11 +635,11 @@ 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)"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.0.31" +version = "20.2.1" description = "Virtual Python Environment builder" category = "main" optional = false @@ -928,14 +649,13 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" appdirs = ">=1.4.3,<2" distlib = ">=0.3.1,<1" filelock = ">=3.0.0,<4" -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 (>=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)"] +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)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] [[package]] name = "wcwidth" @@ -945,9 +665,6 @@ category = "dev" optional = false python-versions = "*" -[package.dependencies] -"backports.functools-lru-cache" = {version = ">=1.2.1", markers = "python_version < \"3.2\""} - [[package]] name = "webencodings" version = "0.5.1" @@ -958,23 +675,20 @@ python-versions = "*" [[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 = "1e774c9d8b7f6812d721cff08b51554f9a0cd051e2ae0e884421bcb56718d131" +python-versions = "^3.6" +content-hash = "077bd512e57f2e31d9f8b72b9a8b3a918f6844afdd3c7e25c2babc7f95fdfc4e" [metadata.files] appdirs = [ @@ -986,12 +700,8 @@ atomicwrites = [ {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-20.2.0-py2.py3-none-any.whl", hash = "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"}, - {file = "attrs-20.2.0.tar.gz", hash = "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594"}, -] -"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"}, + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] cachecontrol = [ {file = "CacheControl-0.12.6-py2.py3-none-any.whl", hash = "sha256:10d056fa27f8563a271b345207402a6dcce8efab7e5b377e270329c62471b10d"}, @@ -1002,8 +712,8 @@ cachy = [ {file = "cachy-0.3.0.tar.gz", hash = "sha256:186581f4ceb42a0bbe040c407da73c14092379b1e4c0e327fdb72ae4a9b269b1"}, ] 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.11.8-py2.py3-none-any.whl", hash = "sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd"}, + {file = "certifi-2020.11.8.tar.gz", hash = "sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"}, ] cffi = [ {file = "cffi-1.14.3-2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc"}, @@ -1060,16 +770,8 @@ clikit = [ {file = "clikit-0.6.2.tar.gz", hash = "sha256:442ee5db9a14120635c5990bcdbfe7c03ada5898291f0c802f77be71569ded59"}, ] colorama = [ - {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, - {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, -] -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"}, + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] coverage = [ {file = "coverage-5.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270"}, @@ -1112,71 +814,47 @@ crashtest = [ {file = "crashtest-0.3.1.tar.gz", hash = "sha256:42ca7b6ce88b6c7433e2ce47ea884e91ec93104a4b754998be498a8e6c3d37dd"}, ] cryptography = [ - {file = "cryptography-3.1.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:65beb15e7f9c16e15934569d29fb4def74ea1469d8781f6b3507ab896d6d8719"}, - {file = "cryptography-3.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:983c0c3de4cb9fcba68fd3f45ed846eb86a2a8b8d8bc5bb18364c4d00b3c61fe"}, - {file = "cryptography-3.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e97a3b627e3cb63c415a16245d6cef2139cca18bb1183d1b9375a1c14e83f3b3"}, - {file = "cryptography-3.1.1-cp27-cp27m-win32.whl", hash = "sha256:cb179acdd4ae1e4a5a160d80b87841b3d0e0be84af46c7bb2cd7ece57a39c4ba"}, - {file = "cryptography-3.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:b372026ebf32fe2523159f27d9f0e9f485092e43b00a5adacf732192a70ba118"}, - {file = "cryptography-3.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:680da076cad81cdf5ffcac50c477b6790be81768d30f9da9e01960c4b18a66db"}, - {file = "cryptography-3.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5d52c72449bb02dd45a773a203196e6d4fae34e158769c896012401f33064396"}, - {file = "cryptography-3.1.1-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:f0e099fc4cc697450c3dd4031791559692dd941a95254cb9aeded66a7aa8b9bc"}, - {file = "cryptography-3.1.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:a7597ffc67987b37b12e09c029bd1dc43965f75d328076ae85721b84046e9ca7"}, - {file = "cryptography-3.1.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4549b137d8cbe3c2eadfa56c0c858b78acbeff956bd461e40000b2164d9167c6"}, - {file = "cryptography-3.1.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:89aceb31cd5f9fc2449fe8cf3810797ca52b65f1489002d58fe190bfb265c536"}, - {file = "cryptography-3.1.1-cp35-cp35m-win32.whl", hash = "sha256:559d622aef2a2dff98a892eef321433ba5bc55b2485220a8ca289c1ecc2bd54f"}, - {file = "cryptography-3.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:451cdf60be4dafb6a3b78802006a020e6cd709c22d240f94f7a0696240a17154"}, - {file = "cryptography-3.1.1-cp36-abi3-win32.whl", hash = "sha256:762bc5a0df03c51ee3f09c621e1cee64e3a079a2b5020de82f1613873d79ee70"}, - {file = "cryptography-3.1.1-cp36-abi3-win_amd64.whl", hash = "sha256:b12e715c10a13ca1bd27fbceed9adc8c5ff640f8e1f7ea76416352de703523c8"}, - {file = "cryptography-3.1.1-cp36-cp36m-win32.whl", hash = "sha256:21b47c59fcb1c36f1113f3709d37935368e34815ea1d7073862e92f810dc7499"}, - {file = "cryptography-3.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:48ee615a779ffa749d7d50c291761dc921d93d7cf203dca2db663b4f193f0e49"}, - {file = "cryptography-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:b2bded09c578d19e08bd2c5bb8fed7f103e089752c9cf7ca7ca7de522326e921"}, - {file = "cryptography-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f99317a0fa2e49917689b8cf977510addcfaaab769b3f899b9c481bbd76730c2"}, - {file = "cryptography-3.1.1-cp38-cp38-win32.whl", hash = "sha256:ab010e461bb6b444eaf7f8c813bb716be2d78ab786103f9608ffd37a4bd7d490"}, - {file = "cryptography-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:99d4984aabd4c7182050bca76176ce2dbc9fa9748afe583a7865c12954d714ba"}, - {file = "cryptography-3.1.1.tar.gz", hash = "sha256:9d9fc6a16357965d282dd4ab6531013935425d0dc4950df2e0cf2a1b1ac1017d"}, + {file = "cryptography-3.2.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:6dc59630ecce8c1f558277ceb212c751d6730bd12c80ea96b4ac65637c4f55e7"}, + {file = "cryptography-3.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:75e8e6684cf0034f6bf2a97095cb95f81537b12b36a8fedf06e73050bb171c2d"}, + {file = "cryptography-3.2.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4e7268a0ca14536fecfdf2b00297d4e407da904718658c1ff1961c713f90fd33"}, + {file = "cryptography-3.2.1-cp27-cp27m-win32.whl", hash = "sha256:7117319b44ed1842c617d0a452383a5a052ec6aa726dfbaffa8b94c910444297"}, + {file = "cryptography-3.2.1-cp27-cp27m-win_amd64.whl", hash = "sha256:a733671100cd26d816eed39507e585c156e4498293a907029969234e5e634bc4"}, + {file = "cryptography-3.2.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:a75f306a16d9f9afebfbedc41c8c2351d8e61e818ba6b4c40815e2b5740bb6b8"}, + {file = "cryptography-3.2.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5849d59358547bf789ee7e0d7a9036b2d29e9a4ddf1ce5e06bb45634f995c53e"}, + {file = "cryptography-3.2.1-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:bd717aa029217b8ef94a7d21632a3bb5a4e7218a4513d2521c2a2fd63011e98b"}, + {file = "cryptography-3.2.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:efe15aca4f64f3a7ea0c09c87826490e50ed166ce67368a68f315ea0807a20df"}, + {file = "cryptography-3.2.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:32434673d8505b42c0de4de86da8c1620651abd24afe91ae0335597683ed1b77"}, + {file = "cryptography-3.2.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:7b8d9d8d3a9bd240f453342981f765346c87ade811519f98664519696f8e6ab7"}, + {file = "cryptography-3.2.1-cp35-cp35m-win32.whl", hash = "sha256:d3545829ab42a66b84a9aaabf216a4dce7f16dbc76eb69be5c302ed6b8f4a29b"}, + {file = "cryptography-3.2.1-cp35-cp35m-win_amd64.whl", hash = "sha256:a4e27ed0b2504195f855b52052eadcc9795c59909c9d84314c5408687f933fc7"}, + {file = "cryptography-3.2.1-cp36-abi3-win32.whl", hash = "sha256:13b88a0bd044b4eae1ef40e265d006e34dbcde0c2f1e15eb9896501b2d8f6c6f"}, + {file = "cryptography-3.2.1-cp36-abi3-win_amd64.whl", hash = "sha256:07ca431b788249af92764e3be9a488aa1d39a0bc3be313d826bbec690417e538"}, + {file = "cryptography-3.2.1-cp36-cp36m-win32.whl", hash = "sha256:a035a10686532b0587d58a606004aa20ad895c60c4d029afa245802347fab57b"}, + {file = "cryptography-3.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:d26a2557d8f9122f9bf445fc7034242f4375bd4e95ecda007667540270965b13"}, + {file = "cryptography-3.2.1-cp37-cp37m-win32.whl", hash = "sha256:545a8550782dda68f8cdc75a6e3bf252017aa8f75f19f5a9ca940772fc0cb56e"}, + {file = "cryptography-3.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:55d0b896631412b6f0c7de56e12eb3e261ac347fbaa5d5e705291a9016e5f8cb"}, + {file = "cryptography-3.2.1-cp38-cp38-win32.whl", hash = "sha256:3cd75a683b15576cfc822c7c5742b3276e50b21a06672dc3a800a2d5da4ecd1b"}, + {file = "cryptography-3.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:d25cecbac20713a7c3bc544372d42d8eafa89799f492a43b79e1dfd650484851"}, + {file = "cryptography-3.2.1.tar.gz", hash = "sha256:d3d5e10be0cf2a12214ddee45c6bd203dab435e3d83b4560c03066eda600bfe3"}, ] distlib = [ {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, ] -entrypoints = [ - {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, - {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, -] -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"}, -] -glob2 = [ - {file = "glob2-0.6.tar.gz", hash = "sha256:f5b0a686ff21f820c4d3f0c4edd216704cea59d79d00fa337e244a2f2ff83ed6"}, -] html5lib = [ {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, ] httpretty = [ - {file = "httpretty-0.9.7.tar.gz", hash = "sha256:66216f26b9d2c52e81808f3e674a6fb65d4bf719721394a1a9be926177e55fbe"}, + {file = "httpretty-1.0.2.tar.gz", hash = "sha256:24a6fd2fe1c76e94801b74db8f52c0fb42718dc4a199a861b305b1a492b9d868"}, ] identify = [ - {file = "identify-1.5.5-py2.py3-none-any.whl", hash = "sha256:da683bfb7669fa749fc7731f378229e2dbf29a1d1337cbde04106f02236eb29d"}, - {file = "identify-1.5.5.tar.gz", hash = "sha256:7c22c384a2c9b32c5cc891d13f923f6b2653aa83e2d75d8f79be240d6c86c4f4"}, + {file = "identify-1.5.10-py2.py3-none-any.whl", hash = "sha256:cc86e6a9a390879dcc2976cef169dd9cc48843ed70b7380f321d1b118163c60e"}, + {file = "identify-1.5.10.tar.gz", hash = "sha256:943cd299ac7f5715fcb3f684e2fc1594c1e0f22a90d15398e5888143bd4144b5"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, @@ -1187,39 +865,24 @@ 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"}, -] -ipaddress = [ - {file = "ipaddress-1.0.23-py2.py3-none-any.whl", hash = "sha256:6e0f4a39e66cb5bb9a137b00276a2eff74f93b71dcbdad6f10ff7df9d3557fcc"}, - {file = "ipaddress-1.0.23.tar.gz", hash = "sha256:b7f8e0369580bb4a24d5ba1d7cc29660a4a6987763faf1d8a8046830e020e7e2"}, + {file = "importlib_resources-3.3.0-py2.py3-none-any.whl", hash = "sha256:a3d34a8464ce1d5d7c92b0ea4e921e696d86f2aa212e684451cb1482c8d84ed5"}, + {file = "importlib_resources-3.3.0.tar.gz", hash = "sha256:7b51f0106c8ec564b1bef3d9c588bc694ce2b92125bbb6278f4f2f5b54ec3592"}, ] jeepney = [ - {file = "jeepney-0.4.3-py3-none-any.whl", hash = "sha256:d6c6b49683446d2407d2fe3acb7a368a77ff063f9182fe427da15d622adc24cf"}, - {file = "jeepney-0.4.3.tar.gz", hash = "sha256:3479b861cc2b6407de5188695fa1a8d57e5072d7059322469b62628869b8e36e"}, + {file = "jeepney-0.6.0-py3-none-any.whl", hash = "sha256:aec56c0eb1691a841795111e184e13cad504f7703b9a64f63020816afa79a8ae"}, + {file = "jeepney-0.6.0.tar.gz", hash = "sha256:7d59b6622675ca9e993a6bd38de845051d315f8b0c72cca3aef733a20b648657"}, ] keyring = [ - {file = "keyring-18.0.1-py2.py3-none-any.whl", hash = "sha256:7b29ebfcf8678c4da531b2478a912eea01e80007e5ddca9ee0c7038cb3489ec6"}, - {file = "keyring-18.0.1.tar.gz", hash = "sha256:67d6cc0132bd77922725fae9f18366bb314fd8f95ff4d323a4df41890a96a838"}, - {file = "keyring-20.0.1-py2.py3-none-any.whl", hash = "sha256:c674f032424b4bffc62abeac5523ec49cc84aed07a480c3233e0baf618efc15c"}, - {file = "keyring-20.0.1.tar.gz", hash = "sha256:963bfa7f090269d30bdc5e25589e5fd9dad2cf2a7c6f176a7f2386910e5d0d8d"}, - {file = "keyring-21.4.0-py3-none-any.whl", hash = "sha256:4e34ea2fdec90c1c43d6610b5a5fafa1b9097db1802948e90caf5763974b8f8d"}, - {file = "keyring-21.4.0.tar.gz", hash = "sha256:9aeadd006a852b78f4b4ef7c7556c2774d2432bbef8ee538a3e9089ac8b11466"}, + {file = "keyring-21.5.0-py3-none-any.whl", hash = "sha256:12de23258a95f3b13e5b167f7a641a878e91eab8ef16fafc077720a95e6115bb"}, + {file = "keyring-21.5.0.tar.gz", hash = "sha256:207bd66f2a9881c835dad653da04e196c678bf104f8252141d2d3c4f31051579"}, ] lockfile = [ {file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"}, {file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"}, ] -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.5.0.tar.gz", hash = "sha256:6f83822ae94818eae2612063a5101a7311e68ae8002005b5e05f03fd74a86a20"}, - {file = "more_itertools-8.5.0-py3-none-any.whl", hash = "sha256:9b30f12df9393f0d28af9210ff8efe48d10c94f73e5daf886f10c4b0b0b4f03c"}, + {file = "more-itertools-8.6.0.tar.gz", hash = "sha256:b3a9005928e5bed54076e6e549c792b306fddfe72b2d1d22dd63d42d5d3899cf"}, + {file = "more_itertools-8.6.0-py3-none-any.whl", hash = "sha256:8e1a2a43b2f2727425f2b5839587ae37093f19153dc26c0927d1048ff6557330"}, ] msgpack = [ {file = "msgpack-1.0.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:cec8bf10981ed70998d98431cd814db0ecf3384e6b113366e7f36af71a0fca08"}, @@ -1253,17 +916,13 @@ pastel = [ {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, ] -pathlib2 = [ - {file = "pathlib2-2.3.5-py2.py3-none-any.whl", hash = "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db"}, - {file = "pathlib2-2.3.5.tar.gz", hash = "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868"}, -] pexpect = [ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, ] pkginfo = [ - {file = "pkginfo-1.5.0.1-py2.py3-none-any.whl", hash = "sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32"}, - {file = "pkginfo-1.5.0.1.tar.gz", hash = "sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb"}, + {file = "pkginfo-1.6.1-py2.py3-none-any.whl", hash = "sha256:ce14d7296c673dc4c61c759a0b6c14bae34e34eb819c0017bb6ca5b7292c56e9"}, + {file = "pkginfo-1.6.1.tar.gz", hash = "sha256:a6a4ac943b496745cec21f14f021bbd869d5e9b4f6ec06918cffea5a2f4b9193"}, ] pluggy = [ {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, @@ -1274,8 +933,8 @@ poetry-core = [ {file = "poetry_core-1.0.0-py2.py3-none-any.whl", hash = "sha256:769288e0e1b88dfcceb3185728f0b7388b26d5f93d6c22d2dcae372da51d200d"}, ] pre-commit = [ - {file = "pre_commit-2.7.1-py2.py3-none-any.whl", hash = "sha256:810aef2a2ba4f31eed1941fc270e72696a1ad5590b9751839c90807d0fff6b9a"}, - {file = "pre_commit-2.7.1.tar.gz", hash = "sha256:c54fd3e574565fe128ecc5e7d2f91279772ddb03f8729645fa812fe809084a70"}, + {file = "pre_commit-2.9.0-py2.py3-none-any.whl", hash = "sha256:4aee0db4808fa48d2458cedd5b9a084ef24dda1a0fa504432a11977a4d1cfd0a"}, + {file = "pre_commit-2.9.0.tar.gz", hash = "sha256:b2d106d51c6ba6217e859d81774aae33fd825fe7de0dcf0c46e2586333d7a92e"}, ] ptyprocess = [ {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, @@ -1298,8 +957,6 @@ pyparsing = [ {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] 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-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, ] @@ -1332,78 +989,47 @@ 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.0-py2.py3-none-any.whl", hash = "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"}, + {file = "requests-2.25.0.tar.gz", hash = "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8"}, ] requests-toolbelt = [ {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, ] -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"}, -] secretstorage = [ - {file = "SecretStorage-2.3.1.tar.gz", hash = "sha256:3af65c87765323e6f64c83575b05393f9e003431959c9395d1791d51497f29b6"}, - {file = "SecretStorage-3.1.2-py3-none-any.whl", hash = "sha256:b5ec909dde94d4ae2fa26af7c089036997030f0cf0a5cb372b4cccabd81c143b"}, - {file = "SecretStorage-3.1.2.tar.gz", hash = "sha256:15da8a989b65498e29be338b3b279965f1b8f09b9668bd8010da183024c8bff6"}, + {file = "SecretStorage-3.2.0-py3-none-any.whl", hash = "sha256:ed5279d788af258e4676fa26b6efb6d335a31f1f9f529b6f1e200f388fac33e1"}, + {file = "SecretStorage-3.2.0.tar.gz", hash = "sha256:46305c3847ee3f7252b284e0eee5590fa6341c891104a2fd2313f8798c615a82"}, ] shellingham = [ {file = "shellingham-1.3.2-py2.py3-none-any.whl", hash = "sha256:7f6206ae169dc1a03af8a138681b3f962ae61cc93ade84d0585cca3aaf770044"}, {file = "shellingham-1.3.2.tar.gz", hash = "sha256:576c1982bea0ba82fb46c36feb951319d7f42214a82634233f58b40d858a751e"}, ] -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"}, ] -subprocess32 = [ - {file = "subprocess32-3.5.4-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:88e37c1aac5388df41cc8a8456bb49ebffd321a3ad4d70358e3518176de3a56b"}, - {file = "subprocess32-3.5.4.tar.gz", hash = "sha256:eb2937c80497978d181efa1b839ec2d9622cf9600a039a79d0e108d1f9aec79d"}, -] termcolor = [ {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, ] 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"}, ] tomlkit = [ {file = "tomlkit-0.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"}, {file = "tomlkit-0.7.0.tar.gz", hash = "sha256:ac57f29693fab3e309ea789252fcce3061e19110085aa31af5446ca749325618"}, ] tox = [ - {file = "tox-3.20.0-py2.py3-none-any.whl", hash = "sha256:e6318f404aff16522ff5211c88cab82b39af121735a443674e4e2e65f4e4637b"}, - {file = "tox-3.20.0.tar.gz", hash = "sha256:eb629ddc60e8542fd4a1956b2462e3b8771d49f1ff630cecceacaa0fbfb7605a"}, -] -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"}, -] -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"}, + {file = "tox-3.20.1-py2.py3-none-any.whl", hash = "sha256:42ce19ce5dc2f6d6b1fdc5666c476e1f1e2897359b47e0aa3a5b774f335d57c2"}, + {file = "tox-3.20.1.tar.gz", hash = "sha256:4321052bfe28f9d85082341ca8e233e3ea901fdd14dab8a5d3fbd810269fbaf6"}, ] urllib3 = [ {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, ] virtualenv = [ - {file = "virtualenv-20.0.31-py2.py3-none-any.whl", hash = "sha256:e0305af10299a7fb0d69393d8f04cb2965dda9351140d11ac8db4e5e3970451b"}, - {file = "virtualenv-20.0.31.tar.gz", hash = "sha256:43add625c53c596d38f971a465553f6318decc39d98512bc100fa1b1e839c8dc"}, + {file = "virtualenv-20.2.1-py2.py3-none-any.whl", hash = "sha256:07cff122e9d343140366055f31be4dcd61fd598c69d11cd33a9d9c8df4546dd7"}, + {file = "virtualenv-20.2.1.tar.gz", hash = "sha256:e0aac7525e880a429764cefd3aaaff54afb5d9f25c82627563603f5d7de5a6e5"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, @@ -1414,6 +1040,6 @@ webencodings = [ {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] 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/config/config.py b/poetry/config/config.py index 666f2e74ce2..6be9457cffe 100644 --- a/poetry/config/config.py +++ b/poetry/config/config.py @@ -4,14 +4,13 @@ import re from copy import deepcopy +from pathlib import Path from typing import Any from typing import Callable from typing import Dict from typing import Optional from poetry.locations import CACHE_DIR -from poetry.utils._compat import Path -from poetry.utils._compat import basestring from .config_source import ConfigSource from .dict_config_source import DictConfigSource @@ -131,7 +130,7 @@ def get(self, setting_name, default=None): # type: (str, Any) -> Any return self.process(value) def process(self, value): # type: (Any) -> Any - if not isinstance(value, basestring): + if not isinstance(value, str): return value return re.sub(r"{(.+?)}", lambda m: self.get(m.group(1)), value) diff --git a/poetry/console/application.py b/poetry/console/application.py index 8fb32480f49..027fec85776 100644 --- a/poetry/console/application.py +++ b/poetry/console/application.py @@ -60,8 +60,9 @@ def __init__(self): @property def poetry(self): + from pathlib import Path + from poetry.factory import Factory - from poetry.utils._compat import Path if self._poetry is not None: return self._poetry diff --git a/poetry/console/commands/check.py b/poetry/console/commands/check.py index bb97da14640..72c7ca4d947 100644 --- a/poetry/console/commands/check.py +++ b/poetry/console/commands/check.py @@ -1,6 +1,7 @@ +from pathlib import Path + from poetry.core.pyproject.toml import PyProjectTOML from poetry.factory import Factory -from poetry.utils._compat import Path from .command import Command diff --git a/poetry/console/commands/config.py b/poetry/console/commands/config.py index 2e84429ec36..310524d4ee2 100644 --- a/poetry/console/commands/config.py +++ b/poetry/console/commands/config.py @@ -41,10 +41,11 @@ class ConfigCommand(Command): @property def unique_config_values(self): + from pathlib import Path + from poetry.config.config import boolean_normalizer from poetry.config.config import boolean_validator from poetry.locations import CACHE_DIR - from poetry.utils._compat import Path unique_config_values = { "cache-dir": ( @@ -75,10 +76,10 @@ def unique_config_values(self): return unique_config_values def handle(self): + from pathlib import Path + from poetry.config.file_config_source import FileConfigSource from poetry.locations import CONFIG_DIR - from poetry.utils._compat import Path - from poetry.utils._compat import basestring config = Factory.create_config(self.io) config_file = TOMLFile(Path(CONFIG_DIR) / "config.toml") @@ -134,7 +135,7 @@ def handle(self): value = config.get(setting_key) - if not isinstance(value, basestring): + if not isinstance(value, str): value = json.dumps(value) self.line(value) @@ -267,8 +268,6 @@ def _handle_single_value(self, source, key, callbacks, values): return 0 def _list_configuration(self, config, raw, k=""): - from poetry.utils._compat import basestring - orig_k = k for key, value in sorted(config.items()): if k + key in self.LIST_PROHIBITED_SETTINGS: @@ -293,7 +292,7 @@ def _list_configuration(self, config, raw, k=""): message = "{} = {}".format( k + key, json.dumps(raw_val) ) - elif isinstance(raw_val, basestring) and raw_val != value: + elif isinstance(raw_val, str) and raw_val != value: message = "{} = {} # {}".format( k + key, json.dumps(raw_val), value ) diff --git a/poetry/console/commands/init.py b/poetry/console/commands/init.py index af72318c31a..ef56484abd1 100644 --- a/poetry/console/commands/init.py +++ b/poetry/console/commands/init.py @@ -4,7 +4,9 @@ import os import re import sys +import urllib.parse +from pathlib import Path from typing import Dict from typing import List from typing import Tuple @@ -15,9 +17,6 @@ from poetry.core.pyproject import PyProjectException from poetry.core.pyproject.toml import PyProjectTOML -from poetry.utils._compat import OrderedDict -from poetry.utils._compat import Path -from poetry.utils._compat import urlparse from .command import Command from .env_command import EnvCommand @@ -63,9 +62,10 @@ def __init__(self): self._pool = None def handle(self): + from pathlib import Path + from poetry.core.vcs.git import GitConfig from poetry.layouts import layout - from poetry.utils._compat import Path from poetry.utils.env import SystemEnv pyproject = PyProjectTOML(Path.cwd() / "pyproject.toml") @@ -390,7 +390,7 @@ def _parse_requirements( extras = [e.strip() for e in extras_m.group(1).split(",")] requirement, _ = requirement.split("[") - url_parsed = urlparse.urlparse(requirement) + url_parsed = urllib.parse.urlparse(requirement) if url_parsed.scheme and url_parsed.netloc: # Url if url_parsed.scheme in ["git+https", "git+ssh"]: @@ -400,7 +400,7 @@ def _parse_requirements( parsed = ParsedUrl.parse(requirement) url = Git.normalize_url(requirement) - pair = OrderedDict([("name", parsed.name), ("git", url.url)]) + pair = dict([("name", parsed.name), ("git", url.url)]) if parsed.rev: pair["rev"] = url.revision @@ -417,9 +417,7 @@ def _parse_requirements( elif url_parsed.scheme in ["http", "https"]: package = Provider.get_package_from_url(requirement) - pair = OrderedDict( - [("name", package.name), ("url", package.source_url)] - ) + pair = dict([("name", package.name), ("url", package.source_url)]) if extras: pair["extras"] = extras @@ -435,7 +433,7 @@ def _parse_requirements( package = Provider.get_package_from_directory(path) result.append( - OrderedDict( + dict( [ ("name", package.name), ("path", path.relative_to(cwd).as_posix()), @@ -451,7 +449,7 @@ def _parse_requirements( ) pair = pair.strip() - require = OrderedDict() + require = dict() if " " in pair: name, version = pair.split(" ", 2) extras_m = re.search(r"\[([\w\d,-_]+)\]$", name) diff --git a/poetry/console/commands/new.py b/poetry/console/commands/new.py index 481b0577bcc..4856ff69c96 100644 --- a/poetry/console/commands/new.py +++ b/poetry/console/commands/new.py @@ -20,10 +20,11 @@ class NewCommand(Command): ] def handle(self): + from pathlib import Path + from poetry.core.semver import parse_constraint from poetry.core.vcs.git import GitConfig from poetry.layouts import layout - from poetry.utils._compat import Path from poetry.utils.env import SystemEnv if self.option("src"): diff --git a/poetry/console/commands/publish.py b/poetry/console/commands/publish.py index 557cd1d7ab7..98d4165fd8d 100644 --- a/poetry/console/commands/publish.py +++ b/poetry/console/commands/publish.py @@ -1,6 +1,6 @@ -from cleo import option +from pathlib import Path -from poetry.utils._compat import Path +from cleo import option from .command import Command diff --git a/poetry/console/commands/self/update.py b/poetry/console/commands/self/update.py index f98da89632f..7d477ddce16 100644 --- a/poetry/console/commands/self/update.py +++ b/poetry/console/commands/self/update.py @@ -62,7 +62,7 @@ class SelfUpdateCommand(Command): @property def home(self): - from poetry.utils._compat import Path + from pathlib import Path return Path(os.environ.get("POETRY_HOME", "~/.poetry")).expanduser() @@ -239,7 +239,7 @@ def process(self, *args): return subprocess.check_output(list(args), stderr=subprocess.STDOUT) def _check_recommended_installation(self): - from poetry.utils._compat import Path + from pathlib import Path current = Path(__file__) try: diff --git a/poetry/console/config/application_config.py b/poetry/console/config/application_config.py index 09cc2cb1b44..492a2137259 100644 --- a/poetry/console/config/application_config.py +++ b/poetry/console/config/application_config.py @@ -30,7 +30,7 @@ from poetry.console.commands.installer_command import InstallerCommand from poetry.console.logging.io_formatter import IOFormatter from poetry.console.logging.io_handler import IOHandler -from poetry.utils._compat import PY36 +from poetry.mixology.solutions.providers import PythonRequirementSolutionProvider class ApplicationConfig(BaseApplicationConfig): @@ -55,14 +55,9 @@ def configure(self): self.add_event_listener(PRE_HANDLE, self.set_env) self.add_event_listener(PRE_HANDLE, self.set_installer) - if PY36: - from poetry.mixology.solutions.providers import ( - PythonRequirementSolutionProvider, - ) - - self._solution_provider_repository.register_solution_providers( - [PythonRequirementSolutionProvider] - ) + self._solution_provider_repository.register_solution_providers( + [PythonRequirementSolutionProvider] + ) def register_command_loggers( self, event, event_name, _ diff --git a/poetry/factory.py b/poetry/factory.py index 08a6faca170..b38f6da25ae 100644 --- a/poetry/factory.py +++ b/poetry/factory.py @@ -1,6 +1,7 @@ from __future__ import absolute_import from __future__ import unicode_literals +from pathlib import Path from typing import Dict from typing import Optional @@ -16,7 +17,6 @@ from .packages.locker import Locker from .poetry import Poetry from .repositories.pypi_repository import PyPiRepository -from .utils._compat import Path class Factory(BaseFactory): diff --git a/poetry/inspection/info.py b/poetry/inspection/info.py index 251dac95f91..94ac097aeac 100644 --- a/poetry/inspection/info.py +++ b/poetry/inspection/info.py @@ -4,6 +4,7 @@ import tarfile import zipfile +from pathlib import Path from typing import Dict from typing import Iterator from typing import List @@ -17,8 +18,6 @@ from poetry.core.packages import ProjectPackage from poetry.core.packages import dependency_from_pep_508 from poetry.core.pyproject.toml import PyProjectTOML -from poetry.core.utils._compat import PY35 -from poetry.core.utils._compat import Path from poetry.core.utils.helpers import parse_requires from poetry.core.utils.helpers import temporary_directory from poetry.core.version.markers import InvalidMarker @@ -364,13 +363,10 @@ def _find_dist_info(path): # type: (Path) -> Iterator[Path] :param path: Path to search. """ pattern = "**/*.*-info" - if PY35: - # Sometimes pathlib will fail on recursive symbolic links, so we need to workaround it - # and use the glob module instead. Note that this does not happen with pathlib2 - # so it's safe to use it for Python < 3.4. - directories = glob.iglob(path.joinpath(pattern).as_posix(), recursive=True) - else: - directories = path.glob(pattern) + # Sometimes pathlib will fail on recursive symbolic links, so we need to workaround it + # and use the glob module instead. Note that this does not happen with pathlib2 + # so it's safe to use it for Python < 3.4. + directories = glob.iglob(path.joinpath(pattern).as_posix(), recursive=True) for d in directories: yield Path(d) diff --git a/poetry/installation/authenticator.py b/poetry/installation/authenticator.py index 69adb844809..1e7a72e751a 100644 --- a/poetry/installation/authenticator.py +++ b/poetry/installation/authenticator.py @@ -1,5 +1,6 @@ import logging import time +import urllib.parse from typing import TYPE_CHECKING @@ -8,7 +9,6 @@ import requests.exceptions from poetry.exceptions import PoetryException -from poetry.utils._compat import urlparse from poetry.utils.password_manager import PasswordManager @@ -107,7 +107,7 @@ def request( def get_credentials_for_url( self, url ): # type: (str) -> Tuple[Optional[str], Optional[str]] - parsed_url = urlparse.urlsplit(url) + parsed_url = urllib.parse.urlsplit(url) netloc = parsed_url.netloc @@ -130,7 +130,7 @@ def get_credentials_for_url( credentials = auth, None credentials = tuple( - None if x is None else urlparse.unquote(x) for x in credentials + None if x is None else urllib.parse.unquote(x) for x in credentials ) if credentials[0] is not None or credentials[1] is not None: @@ -156,7 +156,7 @@ def _get_credentials_for_netloc_from_config( if not url: continue - parsed_url = urlparse.urlsplit(url) + parsed_url = urllib.parse.urlsplit(url) if netloc == parsed_url.netloc: auth = self._password_manager.get_http_auth(repository_name) diff --git a/poetry/installation/chef.py b/poetry/installation/chef.py index 669ce17768e..6009ac0a98d 100644 --- a/poetry/installation/chef.py +++ b/poetry/installation/chef.py @@ -1,10 +1,10 @@ import hashlib import json +from pathlib import Path from typing import TYPE_CHECKING from poetry.core.packages.utils.link import Link -from poetry.utils._compat import Path from .chooser import InvalidWheelName from .chooser import Wheel diff --git a/poetry/installation/executor.py b/poetry/installation/executor.py index a65dbb4a0e1..5523ed2813f 100644 --- a/poetry/installation/executor.py +++ b/poetry/installation/executor.py @@ -7,17 +7,13 @@ from concurrent.futures import ThreadPoolExecutor from concurrent.futures import wait +from pathlib import Path from subprocess import CalledProcessError from poetry.core.packages.file_dependency import FileDependency from poetry.core.packages.utils.link import Link from poetry.core.pyproject.toml import PyProjectTOML from poetry.io.null_io import NullIO -from poetry.utils._compat import PY2 -from poetry.utils._compat import WINDOWS -from poetry.utils._compat import OrderedDict -from poetry.utils._compat import Path -from poetry.utils._compat import cpu_count from poetry.utils._compat import decode from poetry.utils.env import EnvCommandError from poetry.utils.helpers import safe_rmtree @@ -45,13 +41,13 @@ def __init__(self, env, pool, config, io, parallel=None): if parallel is None: parallel = config.get("installer.parallel", True) - if parallel and not (PY2 and WINDOWS): + if parallel: # This should be directly handled by ThreadPoolExecutor # however, on some systems the number of CPUs cannot be determined # (it raises a NotImplementedError), so, in this case, we assume # that the system only has one CPU. try: - self._max_workers = cpu_count() + 4 + self._max_workers = os.cpu_count() + 4 except NotImplementedError: self._max_workers = 5 else: @@ -62,7 +58,7 @@ def __init__(self, env, pool, config, io, parallel=None): self._executed_operations = 0 self._executed = {"install": 0, "update": 0, "uninstall": 0} self._skipped = {"install": 0, "update": 0, "uninstall": 0} - self._sections = OrderedDict() + self._sections = dict() self._lock = threading.Lock() self._shutdown = False @@ -107,7 +103,7 @@ def execute(self, operations): # type: (Operation) -> int # We group operations by priority groups = itertools.groupby(operations, key=lambda o: -o.priority) - self._sections = OrderedDict() + self._sections = dict() for _, group in groups: tasks = [] serial_operations = [] diff --git a/poetry/installation/pip_installer.py b/poetry/installation/pip_installer.py index b8fb97314be..df1249737a1 100644 --- a/poetry/installation/pip_installer.py +++ b/poetry/installation/pip_installer.py @@ -1,5 +1,6 @@ import os import tempfile +import urllib.parse from subprocess import CalledProcessError @@ -14,12 +15,6 @@ from .base_installer import BaseInstaller -try: - import urllib.parse as urlparse -except ImportError: - import urlparse - - class PipInstaller(BaseInstaller): def __init__(self, env, io, pool): # type: (Env, IO, Pool) -> None self._env = env @@ -44,7 +39,7 @@ def install(self, package, update=False): and package.source_url ): repository = self._pool.repository(package.source_reference) - parsed = urlparse.urlparse(package.source_url) + parsed = urllib.parse.urlparse(package.source_url) if parsed.scheme == "http": self._io.error( " Installing from unsecure host: {}".format( diff --git a/poetry/locations.py b/poetry/locations.py index 003950d500d..5bd4b7feb17 100644 --- a/poetry/locations.py +++ b/poetry/locations.py @@ -1,4 +1,5 @@ -from .utils._compat import Path +from pathlib import Path + from .utils.appdirs import user_cache_dir from .utils.appdirs import user_config_dir diff --git a/poetry/masonry/builders/editable.py b/poetry/masonry/builders/editable.py index 74d1f69c886..dc4be9af4cc 100644 --- a/poetry/masonry/builders/editable.py +++ b/poetry/masonry/builders/editable.py @@ -5,13 +5,13 @@ import shutil from base64 import urlsafe_b64encode +from pathlib import Path from poetry.core.masonry.builders.builder import Builder from poetry.core.masonry.builders.sdist import SdistBuilder from poetry.core.masonry.utils.package_include import PackageInclude from poetry.core.semver.version import Version from poetry.utils._compat import WINDOWS -from poetry.utils._compat import Path from poetry.utils._compat import decode from poetry.utils.helpers import is_dir_writable diff --git a/poetry/mixology/partial_solution.py b/poetry/mixology/partial_solution.py index df17f7184b2..55230425ce8 100644 --- a/poetry/mixology/partial_solution.py +++ b/poetry/mixology/partial_solution.py @@ -1,4 +1,3 @@ -from collections import OrderedDict from typing import Dict from typing import List @@ -26,13 +25,13 @@ def __init__(self): self._assignments = [] # type: List[Assignment] # The decisions made for each package. - self._decisions = OrderedDict() # type: Dict[str, Package] + self._decisions = dict() # type: Dict[str, Package] # The intersection of all positive Assignments for each package, minus any # negative Assignments that refer to that package. # # This is derived from self._assignments. - self._positive = OrderedDict() # type: Dict[str, Term] + self._positive = dict() # type: Dict[str, Term] # The union of all negative Assignments for each package. # @@ -40,7 +39,7 @@ def __init__(self): # map. # # This is derived from self._assignments. - self._negative = OrderedDict() # type: Dict[str, Dict[str, Term]] + self._negative = dict() # type: Dict[str, Dict[str, Term]] # The number of distinct solutions that have been attempted so far. self._attempted_solutions = 1 diff --git a/poetry/packages/locker.py b/poetry/packages/locker.py index f1637407068..8cb6d91f916 100644 --- a/poetry/packages/locker.py +++ b/poetry/packages/locker.py @@ -5,6 +5,7 @@ from copy import deepcopy from hashlib import sha256 +from pathlib import Path from typing import Dict from typing import Iterable from typing import Iterator @@ -33,8 +34,6 @@ from poetry.core.version.markers import parse_marker from poetry.core.version.requirements import InvalidRequirement from poetry.packages import DependencyPackage -from poetry.utils._compat import OrderedDict -from poetry.utils._compat import Path from poetry.utils.extras import get_extra_package_names @@ -412,7 +411,7 @@ def set_lock_data(self, root, packages): # type: (...) -> bool for extra, deps in sorted(root.extras.items()) } - lock["metadata"] = OrderedDict( + lock["metadata"] = dict( [ ("lock-version", self._VERSION), ("python-versions", root.python_versions), @@ -525,7 +524,7 @@ def _dump_package(self, package): # type: (Package) -> dict constraint["version"] for constraint in constraints ] - data = OrderedDict( + data = dict( [ ("name", package.pretty_name), ("version", package.pretty_version), @@ -569,7 +568,7 @@ def _dump_package(self, package): # type: (Package) -> dict ) ).as_posix() - data["source"] = OrderedDict() + data["source"] = dict() if package.source_type: data["source"]["type"] = package.source_type diff --git a/poetry/poetry.py b/poetry/poetry.py index 4878f0a22aa..325c6074df3 100644 --- a/poetry/poetry.py +++ b/poetry/poetry.py @@ -1,6 +1,8 @@ from __future__ import absolute_import from __future__ import unicode_literals +from pathlib import Path + from poetry.core.packages import ProjectPackage from poetry.core.poetry import Poetry as BasePoetry @@ -8,7 +10,6 @@ from .config.config import Config from .packages import Locker from .repositories.pool import Pool -from .utils._compat import Path class Poetry(BasePoetry): diff --git a/poetry/publishing/publisher.py b/poetry/publishing/publisher.py index 67515f77f64..5cec7eca2fe 100644 --- a/poetry/publishing/publisher.py +++ b/poetry/publishing/publisher.py @@ -1,8 +1,8 @@ import logging +from pathlib import Path from typing import Optional -from poetry.utils._compat import Path from poetry.utils.helpers import get_cert from poetry.utils.helpers import get_client_cert from poetry.utils.password_manager import PasswordManager diff --git a/poetry/publishing/uploader.py b/poetry/publishing/uploader.py index 7c0783d1cac..bb1673e3bff 100644 --- a/poetry/publishing/uploader.py +++ b/poetry/publishing/uploader.py @@ -1,6 +1,7 @@ import hashlib import io +from pathlib import Path from typing import Any from typing import Dict from typing import List @@ -21,7 +22,6 @@ from poetry.core.masonry.metadata import Metadata from poetry.core.masonry.utils.helpers import escape_name from poetry.core.masonry.utils.helpers import escape_version -from poetry.utils._compat import Path from poetry.utils.helpers import normalize_version from poetry.utils.patterns import wheel_file_re diff --git a/poetry/puzzle/provider.py b/poetry/puzzle/provider.py index 333dfc60cf3..e9719a924d9 100644 --- a/poetry/puzzle/provider.py +++ b/poetry/puzzle/provider.py @@ -1,8 +1,10 @@ import logging import os import re +import urllib.parse from contextlib import contextmanager +from pathlib import Path from tempfile import mkdtemp from typing import Any from typing import List @@ -30,9 +32,6 @@ from poetry.packages.package_collection import PackageCollection from poetry.puzzle.exceptions import OverrideNeeded from poetry.repositories import Pool -from poetry.utils._compat import OrderedDict -from poetry.utils._compat import Path -from poetry.utils._compat import urlparse from poetry.utils.env import Env from poetry.utils.helpers import download_file from poetry.utils.helpers import safe_rmtree @@ -324,7 +323,7 @@ def search_for_url(self, dependency): # type: (URLDependency) -> List[Package] def get_package_from_url(cls, url): # type: (str) -> Package with temporary_directory() as temp_dir: temp_dir = Path(temp_dir) - file_name = os.path.basename(urlparse.urlparse(url).path) + file_name = os.path.basename(urllib.parse.urlparse(url).path) download_file(url, str(temp_dir / file_name)) package = cls.get_package_from_file(temp_dir / file_name) @@ -517,7 +516,7 @@ def complete_package( # An example of this is: # - pypiwin32 (220); sys_platform == "win32" and python_version >= "3.6" # - pypiwin32 (219); sys_platform == "win32" and python_version < "3.6" - duplicates = OrderedDict() + duplicates = dict() for dep in dependencies: if dep.name not in duplicates: duplicates[dep.name] = [] @@ -533,7 +532,7 @@ def complete_package( self.debug("Duplicate dependencies for {}".format(dep_name)) # Regrouping by constraint - by_constraint = OrderedDict() + by_constraint = dict() for dep in deps: if dep.constraint not in by_constraint: by_constraint[dep.constraint] = [] diff --git a/poetry/repositories/installed_repository.py b/poetry/repositories/installed_repository.py index 1320fdd6698..29424dbaeb5 100644 --- a/poetry/repositories/installed_repository.py +++ b/poetry/repositories/installed_repository.py @@ -1,11 +1,11 @@ import itertools +from pathlib import Path from typing import Set from typing import Union from poetry.core.packages import Package from poetry.core.utils.helpers import module_name -from poetry.utils._compat import Path from poetry.utils._compat import metadata from poetry.utils.env import Env diff --git a/poetry/repositories/legacy_repository.py b/poetry/repositories/legacy_repository.py index f9963ddc4d4..e059c3bb8d4 100644 --- a/poetry/repositories/legacy_repository.py +++ b/poetry/repositories/legacy_repository.py @@ -1,8 +1,10 @@ import cgi import re +import urllib.parse import warnings from collections import defaultdict +from pathlib import Path from typing import Generator from typing import Optional from typing import Union @@ -21,7 +23,6 @@ from poetry.core.semver import VersionRange from poetry.core.semver import parse_constraint from poetry.locations import REPOSITORY_CACHE_DIR -from poetry.utils._compat import Path from poetry.utils.helpers import canonicalize_name from poetry.utils.patterns import wheel_file_re @@ -33,11 +34,6 @@ from .pypi_repository import PyPiRepository -try: - import urllib.parse as urlparse -except ImportError: - import urlparse - try: from html import unescape except ImportError: @@ -115,7 +111,7 @@ def links(self): # type: () -> Generator[Link] for anchor in self._parsed.findall(".//a"): if anchor.get("href"): href = anchor.get("href") - url = self.clean_link(urlparse.urljoin(self._url, href)) + url = self.clean_link(urllib.parse.urljoin(self._url, href)) pyrequire = anchor.get("data-requires-python") pyrequire = unescape(pyrequire) if pyrequire else None @@ -219,7 +215,7 @@ def authenticated_url(self): # type: () -> str if not self._session.auth: return self.url - parsed = urlparse.urlparse(self.url) + parsed = urllib.parse.urlparse(self.url) return "{scheme}://{username}:{password}@{netloc}{path}".format( scheme=parsed.scheme, diff --git a/poetry/repositories/pypi_repository.py b/poetry/repositories/pypi_repository.py index 16105992a1c..715ba557379 100644 --- a/poetry/repositories/pypi_repository.py +++ b/poetry/repositories/pypi_repository.py @@ -1,7 +1,9 @@ import logging import os +import urllib.parse from collections import defaultdict +from pathlib import Path from typing import Dict from typing import List from typing import Union @@ -24,7 +26,6 @@ from poetry.core.semver.exceptions import ParseVersionError from poetry.core.version.markers import parse_marker from poetry.locations import REPOSITORY_CACHE_DIR -from poetry.utils._compat import Path from poetry.utils._compat import to_str from poetry.utils.helpers import download_file from poetry.utils.helpers import temporary_directory @@ -35,12 +36,6 @@ from .remote_repository import RemoteRepository -try: - import urllib.parse as urlparse -except ImportError: - import urlparse - - cache_control_logger.setLevel(logging.ERROR) logger = logging.getLogger(__name__) @@ -424,11 +419,13 @@ def _get_info_from_urls(self, urls): # type: (Dict[str, List[str]]) -> PackageI def _get_info_from_wheel(self, url): # type: (str) -> PackageInfo self._log( - "Downloading wheel: {}".format(urlparse.urlparse(url).path.rsplit("/")[-1]), + "Downloading wheel: {}".format( + urllib.parse.urlparse(url).path.rsplit("/")[-1] + ), level="debug", ) - filename = os.path.basename(urlparse.urlparse(url).path.rsplit("/")[-1]) + filename = os.path.basename(urllib.parse.urlparse(url).path.rsplit("/")[-1]) with temporary_directory() as temp_dir: filepath = Path(temp_dir) / filename @@ -438,11 +435,13 @@ def _get_info_from_wheel(self, url): # type: (str) -> PackageInfo def _get_info_from_sdist(self, url): # type: (str) -> PackageInfo self._log( - "Downloading sdist: {}".format(urlparse.urlparse(url).path.rsplit("/")[-1]), + "Downloading sdist: {}".format( + urllib.parse.urlparse(url).path.rsplit("/")[-1] + ), level="debug", ) - filename = os.path.basename(urlparse.urlparse(url).path) + filename = os.path.basename(urllib.parse.urlparse(url).path) with temporary_directory() as temp_dir: filepath = Path(temp_dir) / filename diff --git a/poetry/utils/_compat.py b/poetry/utils/_compat.py index 937f9b300e6..1ede84dcd3f 100644 --- a/poetry/utils/_compat.py +++ b/poetry/utils/_compat.py @@ -2,236 +2,16 @@ try: - from functools32 import lru_cache -except ImportError: - from functools import lru_cache - -try: - from glob2 import glob -except ImportError: - from glob import glob - -try: - import zipfile as zipp - from importlib import metadata except ImportError: - import importlib_metadata as metadata - import zipp - -try: - import urllib.parse as urlparse -except ImportError: - import urlparse - -try: - from os import cpu_count -except ImportError: # Python 2 - from multiprocessing import cpu_count - -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) + # compatibility for python <3.8 + import importlib_metadata as metadata # noqa WINDOWS = sys.platform == "win32" -try: - from shlex import quote -except ImportError: - # PY2 - from pipes import quote # noqa - -if PY34: - from importlib.machinery import EXTENSION_SUFFIXES -else: - from imp import get_suffixes - - EXTENSION_SUFFIXES = [suffix[0] for suffix in get_suffixes()] - - -if PY35: - from pathlib import Path -else: - from pathlib2 import Path - -if not PY36: - from collections import OrderedDict -else: - OrderedDict = dict - - -if PY35: - import subprocess as subprocess - - from subprocess import CalledProcessError -else: - import subprocess32 as subprocess - - from subprocess32 import CalledProcessError - - -if PY34: - # subprocess32 pass the calls directly to subprocess - # on Python 3.3+ but Python 3.4 does not provide run() - # so we backport it - import signal - - from subprocess import PIPE - from subprocess import Popen - from subprocess import SubprocessError - from subprocess import TimeoutExpired - - class CalledProcessError(SubprocessError): - """Raised when run() is called with check=True and the process - returns a non-zero exit status. - - Attributes: - cmd, returncode, stdout, stderr, output - """ - - def __init__(self, returncode, cmd, output=None, stderr=None): - self.returncode = returncode - self.cmd = cmd - self.output = output - self.stderr = stderr - - def __str__(self): - if self.returncode and self.returncode < 0: - try: - return "Command '%s' died with %r." % ( - self.cmd, - signal.Signals(-self.returncode), - ) - except ValueError: - return "Command '%s' died with unknown signal %d." % ( - self.cmd, - -self.returncode, - ) - else: - return "Command '%s' returned non-zero exit status %d." % ( - self.cmd, - self.returncode, - ) - - @property - def stdout(self): - """Alias for output attribute, to match stderr""" - return self.output - - @stdout.setter - def stdout(self, value): - # There's no obvious reason to set this, but allow it anyway so - # .stdout is a transparent alias for .output - self.output = value - - class CompletedProcess(object): - """A process that has finished running. - This is returned by run(). - Attributes: - args: The list or str args passed to run(). - returncode: The exit code of the process, negative for signals. - stdout: The standard output (None if not captured). - stderr: The standard error (None if not captured). - """ - - def __init__(self, args, returncode, stdout=None, stderr=None): - self.args = args - self.returncode = returncode - self.stdout = stdout - self.stderr = stderr - - def __repr__(self): - args = [ - "args={!r}".format(self.args), - "returncode={!r}".format(self.returncode), - ] - if self.stdout is not None: - args.append("stdout={!r}".format(self.stdout)) - if self.stderr is not None: - args.append("stderr={!r}".format(self.stderr)) - return "{}({})".format(type(self).__name__, ", ".join(args)) - - def check_returncode(self): - """Raise CalledProcessError if the exit code is non-zero.""" - if self.returncode: - raise CalledProcessError( - self.returncode, self.args, self.stdout, self.stderr - ) - - def run(*popenargs, **kwargs): - """Run command with arguments and return a CompletedProcess instance. - The returned instance will have attributes args, returncode, stdout and - stderr. By default, stdout and stderr are not captured, and those attributes - will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them. - If check is True and the exit code was non-zero, it raises a - CalledProcessError. The CalledProcessError object will have the return code - in the returncode attribute, and output & stderr attributes if those streams - were captured. - If timeout is given, and the process takes too long, a TimeoutExpired - exception will be raised. - There is an optional argument "input", allowing you to - pass a string to the subprocess's stdin. If you use this argument - you may not also use the Popen constructor's "stdin" argument, as - it will be used internally. - The other arguments are the same as for the Popen constructor. - If universal_newlines=True is passed, the "input" argument must be a - string and stdout/stderr in the returned object will be strings rather than - bytes. - """ - input = kwargs.pop("input", None) - timeout = kwargs.pop("timeout", None) - check = kwargs.pop("check", False) - if input is not None: - if "stdin" in kwargs: - raise ValueError("stdin and input arguments may not both be used.") - kwargs["stdin"] = PIPE - - process = Popen(*popenargs, **kwargs) - try: - process.__enter__() # No-Op really... illustrate "with in 2.4" - try: - stdout, stderr = process.communicate(input, timeout=timeout) - except TimeoutExpired: - process.kill() - stdout, stderr = process.communicate() - raise TimeoutExpired( - process.args, timeout, output=stdout, stderr=stderr - ) - except: - process.kill() - process.wait() - raise - retcode = process.poll() - if check and retcode: - raise CalledProcessError( - retcode, process.args, output=stdout, stderr=stderr - ) - finally: - # None because our context manager __exit__ does not use them. - process.__exit__(None, None, None) - - return CompletedProcess(process.args, retcode, stdout, stderr) - - subprocess.run = run - subprocess.CalledProcessError = CalledProcessError - def decode(string, encodings=None): - if not PY2 and not isinstance(string, bytes): - return string - - if PY2 and isinstance(string, unicode): + if not isinstance(string, bytes): return string encodings = encodings or ["utf-8", "latin1", "ascii"] @@ -246,10 +26,7 @@ def decode(string, encodings=None): def encode(string, encodings=None): - if not PY2 and isinstance(string, bytes): - return string - - if PY2 and isinstance(string, str): + if isinstance(string, bytes): return string encodings = encodings or ["utf-8", "latin1", "ascii"] @@ -264,23 +41,7 @@ def encode(string, encodings=None): def to_str(string): - 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") + return decode(string) def list_to_shell_command(cmd): diff --git a/poetry/utils/env.py b/poetry/utils/env.py index c247bf014f7..22db246911e 100644 --- a/poetry/utils/env.py +++ b/poetry/utils/env.py @@ -5,11 +5,14 @@ import platform import re import shutil +import subprocess import sys import sysconfig import textwrap from contextlib import contextmanager +from pathlib import Path +from subprocess import CalledProcessError from typing import Any from typing import Dict from typing import List @@ -33,12 +36,9 @@ from poetry.core.version.markers import BaseMarker from poetry.locations import CACHE_DIR from poetry.poetry import Poetry -from poetry.utils._compat import CalledProcessError -from poetry.utils._compat import Path from poetry.utils._compat import decode from poetry.utils._compat import encode from poetry.utils._compat import list_to_shell_command -from poetry.utils._compat import subprocess from poetry.utils.helpers import is_dir_writable from poetry.utils.helpers import paths_csv diff --git a/poetry/utils/exporter.py b/poetry/utils/exporter.py index f7c40f6975f..f86fdcf19ee 100644 --- a/poetry/utils/exporter.py +++ b/poetry/utils/exporter.py @@ -1,3 +1,6 @@ +import urllib.parse + +from pathlib import Path from typing import Optional from typing import Sequence from typing import Union @@ -5,9 +8,7 @@ from clikit.api.io import IO from poetry.poetry import Poetry -from poetry.utils._compat import Path from poetry.utils._compat import decode -from poetry.utils._compat import urlparse class Exporter(object): @@ -140,7 +141,7 @@ def _export_requirements_txt( url = ( repository.authenticated_url if with_credentials else repository.url ) - parsed_url = urlparse.urlsplit(url) + parsed_url = urllib.parse.urlsplit(url) if parsed_url.scheme == "http": indexes_header += "--trusted-host {}\n".format(parsed_url.netloc) indexes_header += "--extra-index-url {}\n".format(url) diff --git a/poetry/utils/helpers.py b/poetry/utils/helpers.py index 232e65b7d44..909ec3c1438 100644 --- a/poetry/utils/helpers.py +++ b/poetry/utils/helpers.py @@ -5,6 +5,7 @@ import tempfile from contextlib import contextmanager +from pathlib import Path from typing import List from typing import Optional @@ -13,7 +14,6 @@ from poetry.config.config import Config from poetry.core.packages.package import Package from poetry.core.version import Version -from poetry.utils._compat import Path try: diff --git a/poetry/utils/setup_reader.py b/poetry/utils/setup_reader.py index 3a1ce2f170d..498210f45f5 100644 --- a/poetry/utils/setup_reader.py +++ b/poetry/utils/setup_reader.py @@ -1,5 +1,7 @@ import ast +from configparser import ConfigParser +from pathlib import Path from typing import Any from typing import Dict from typing import Iterable @@ -10,16 +12,6 @@ from poetry.core.semver import Version -from ._compat import PY35 -from ._compat import Path -from ._compat import basestring - - -try: - from configparser import ConfigParser -except ImportError: - from ConfigParser import ConfigParser - class SetupReader(object): """ @@ -39,8 +31,8 @@ class SetupReader(object): @classmethod def read_from_directory( cls, directory - ): # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]] - if isinstance(directory, basestring): + ): # type: (Union[str, Path]) -> Dict[str, Union[List, Dict]] + if isinstance(directory, str): directory = Path(directory) result = cls.DEFAULT.copy() @@ -61,11 +53,8 @@ def read_from_directory( def read_setup_py( self, filepath - ): # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]] - if not PY35: - return self.DEFAULT - - if isinstance(filepath, basestring): + ): # type: (Union[str, Path]) -> Dict[str, Union[List, Dict]] + if isinstance(filepath, str): filepath = Path(filepath) with filepath.open(encoding="utf-8") as f: @@ -92,7 +81,7 @@ def read_setup_py( def read_setup_cfg( self, filepath - ): # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]] + ): # type: (Union[str, Path]) -> Dict[str, Union[List, Dict]] parser = ConfigParser() parser.read(str(filepath)) diff --git a/poetry/utils/shell.py b/poetry/utils/shell.py index 2b2fe91f946..3c0e5fbe92c 100644 --- a/poetry/utils/shell.py +++ b/poetry/utils/shell.py @@ -2,6 +2,8 @@ import signal import sys +from pathlib import Path + import pexpect from clikit.utils.terminal import Terminal @@ -9,7 +11,6 @@ from shellingham import detect_shell from ._compat import WINDOWS -from ._compat import Path from .env import VirtualEnv diff --git a/pyproject.toml b/pyproject.toml index 59c49f74cf7..412ce84c639 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,12 +22,12 @@ classifiers = [ # Requirements [tool.poetry.dependencies] -python = "~2.7 || ^3.5" +python = "^3.6" poetry-core = "^1.0.0" cleo = "^0.8.1" clikit = "^0.6.2" -crashtest = { version = "^0.3.0", python = "^3.6" } +crashtest = "^0.3.0" requests = "^2.18" cachy = "^0.3.0" requests-toolbelt = "^0.9.1" @@ -39,38 +39,20 @@ tomlkit = ">=0.7.0,<1.0.0" pexpect = "^4.7.0" packaging = "^20.4" virtualenv = { version = "^20.0.26" } - -# The typing module is not in the stdlib in Python 2.7 -typing = { version = "^3.6", python = "~2.7" } - -# Use pathlib2 for Python 2.7 -pathlib2 = { version = "^2.3", python = "~2.7" } -# Use futures on Python 2.7 -futures = { version = "^3.3.0", python = "~2.7" } -# Use glob2 for Python 2.7 and 3.4 -glob2 = { version = "^0.6", python = "~2.7" } -# functools32 is needed for Python 2.7 -functools32 = { version = "^3.2.3", python = "~2.7" } -keyring = [ - { version = "^18.0.1", python = "~2.7" }, - { version = "^20.0.1", python = "~3.5" }, - { version = "^21.2.0", python = "^3.6" } -] -# Use subprocess32 for Python 2.7 -subprocess32 = { version = "^3.5", python = "~2.7" } +keyring = "^21.2.0" importlib-metadata = {version = "^1.6.0", python = "<3.8"} [tool.poetry.dev-dependencies] -pytest = [ - {version = "^4.1", python = "<3.5"}, - {version = "^5.4.3", python = ">=3.5"} -] +pytest = "^5.4.3" pytest-cov = "^2.5" pytest-mock = "^1.9" pre-commit = { version = "^2.6", python = "^3.6.1" } tox = "^3.0" pytest-sugar = "^0.9.2" -httpretty = "^0.9.6" +httpretty = "^1.0" +zipp = { version = "^3.4", python = "<3.8"} +# temporary workaround for https://github.com/python-poetry/poetry/issues/3404 +urllib3 = "1.25.10" [tool.poetry.scripts] poetry = "poetry.console:main" diff --git a/tests/compat.py b/tests/compat.py new file mode 100644 index 00000000000..13c5bb7da98 --- /dev/null +++ b/tests/compat.py @@ -0,0 +1,4 @@ +try: + import zipp +except ImportError: + import zipfile as zipp # noqa diff --git a/tests/conftest.py b/tests/conftest.py index 51128f764bb..178a4632b90 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,6 +4,7 @@ import sys import tempfile +from pathlib import Path from typing import Any from typing import Dict @@ -21,7 +22,6 @@ from poetry.layouts import layout from poetry.repositories import Pool from poetry.repositories import Repository -from poetry.utils._compat import Path from poetry.utils.env import EnvManager from poetry.utils.env import SystemEnv from poetry.utils.env import VirtualEnv @@ -184,7 +184,7 @@ def mocked_open(self, *args, **kwargs): return mocker.MagicMock() return original(self, *args, **kwargs) - mocker.patch("poetry.utils._compat.Path.open", mocked_open) + mocker.patch("pathlib.Path.open", mocked_open) yield files diff --git a/tests/console/commands/env/conftest.py b/tests/console/commands/env/conftest.py index 5fbddf1aa6f..22b12f0d44b 100644 --- a/tests/console/commands/env/conftest.py +++ b/tests/console/commands/env/conftest.py @@ -1,8 +1,9 @@ import os +from pathlib import Path + import pytest -from poetry.utils._compat import Path from poetry.utils.env import EnvManager diff --git a/tests/console/commands/env/helpers.py b/tests/console/commands/env/helpers.py index 1c7e64dc17e..0c65e4f14fb 100644 --- a/tests/console/commands/env/helpers.py +++ b/tests/console/commands/env/helpers.py @@ -1,8 +1,8 @@ +from pathlib import Path from typing import Optional from typing import Union from poetry.core.semver import Version -from poetry.utils._compat import Path def build_venv( diff --git a/tests/console/commands/env/test_info.py b/tests/console/commands/env/test_info.py index 9d1a0c8426a..123835ba7b9 100644 --- a/tests/console/commands/env/test_info.py +++ b/tests/console/commands/env/test_info.py @@ -1,6 +1,7 @@ +from pathlib import Path + import pytest -from poetry.utils._compat import Path from poetry.utils.env import MockEnv diff --git a/tests/console/commands/env/test_remove.py b/tests/console/commands/env/test_remove.py index 2b4f3ae775c..b95419bb6fb 100644 --- a/tests/console/commands/env/test_remove.py +++ b/tests/console/commands/env/test_remove.py @@ -13,7 +13,7 @@ def test_remove_by_python_version( mocker, tester, venvs_in_cache_dirs, venv_name, venv_cache ): check_output = mocker.patch( - "poetry.utils._compat.subprocess.check_output", + "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.6")), ) diff --git a/tests/console/commands/env/test_use.py b/tests/console/commands/env/test_use.py index b3ea3458d06..4ec106b434e 100644 --- a/tests/console/commands/env/test_use.py +++ b/tests/console/commands/env/test_use.py @@ -1,11 +1,12 @@ import os +from pathlib import Path + import pytest import tomlkit from poetry.core.semver import Version from poetry.core.toml.file import TOMLFile -from poetry.utils._compat import Path from poetry.utils.env import MockEnv from tests.console.commands.env.helpers import build_venv from tests.console.commands.env.helpers import check_output_wrapper @@ -21,11 +22,11 @@ def setup(mocker): @pytest.fixture(autouse=True) def mock_subprocess_calls(setup, current_python, mocker): mocker.patch( - "poetry.utils._compat.subprocess.check_output", + "subprocess.check_output", side_effect=check_output_wrapper(Version(*current_python)), ) mocker.patch( - "poetry.utils._compat.subprocess.Popen.communicate", + "subprocess.Popen.communicate", side_effect=[("/prefix", None), ("/prefix", None), ("/prefix", None)], ) @@ -39,8 +40,7 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( mocker, tester, venv_cache, venv_name, venvs_in_cache_config ): mocker.patch( - "poetry.utils._compat.subprocess.check_output", - side_effect=check_output_wrapper(), + "subprocess.check_output", side_effect=check_output_wrapper(), ) mock_build_env = mocker.patch( diff --git a/tests/console/commands/self/test_update.py b/tests/console/commands/self/test_update.py index 6e094111ad0..1598f47865a 100644 --- a/tests/console/commands/self/test_update.py +++ b/tests/console/commands/self/test_update.py @@ -1,12 +1,13 @@ import os +from pathlib import Path + import pytest from poetry.__version__ import __version__ from poetry.core.packages.package import Package from poetry.core.semver.version import Version from poetry.utils._compat import WINDOWS -from poetry.utils._compat import Path FIXTURES = Path(__file__).parent.joinpath("fixtures") diff --git a/tests/console/commands/test_add.py b/tests/console/commands/test_add.py index 19998592ac3..a0811c1c965 100644 --- a/tests/console/commands/test_add.py +++ b/tests/console/commands/test_add.py @@ -3,11 +3,12 @@ import sys +from pathlib import Path + import pytest from poetry.core.semver import Version from poetry.repositories.legacy_repository import LegacyRepository -from poetry.utils._compat import Path from tests.helpers import get_dependency from tests.helpers import get_package @@ -279,7 +280,7 @@ def test_add_git_ssh_constraint(app, repo, tester, tmp_venv): def test_add_directory_constraint(app, repo, tester, mocker): - p = mocker.patch("poetry.utils._compat.Path.cwd") + p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__).parent repo.add_package(get_package("pendulum", "1.4.4")) @@ -313,7 +314,7 @@ def test_add_directory_constraint(app, repo, tester, mocker): def test_add_directory_with_poetry(app, repo, tester, mocker): - p = mocker.patch("poetry.utils._compat.Path.cwd") + p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__) / ".." repo.add_package(get_package("pendulum", "1.4.4")) @@ -341,7 +342,7 @@ def test_add_directory_with_poetry(app, repo, tester, mocker): def test_add_file_constraint_wheel(app, repo, tester, mocker, poetry): - p = mocker.patch("poetry.utils._compat.Path.cwd") + p = mocker.patch("pathlib.Path.cwd") p.return_value = poetry.file.parent repo.add_package(get_package("pendulum", "1.4.4")) @@ -376,7 +377,7 @@ def test_add_file_constraint_wheel(app, repo, tester, mocker, poetry): def test_add_file_constraint_sdist(app, repo, tester, mocker): - p = mocker.patch("poetry.utils._compat.Path.cwd") + p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__) / ".." repo.add_package(get_package("pendulum", "1.4.4")) @@ -448,7 +449,7 @@ def test_add_constraint_with_extras_option(app, repo, tester): def test_add_url_constraint_wheel(app, repo, tester, mocker): - p = mocker.patch("poetry.utils._compat.Path.cwd") + p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__) / ".." repo.add_package(get_package("pendulum", "1.4.4")) @@ -1069,7 +1070,7 @@ def test_add_git_ssh_constraint_old_installer(app, repo, installer, old_tester): def test_add_directory_constraint_old_installer( app, repo, installer, mocker, old_tester ): - p = mocker.patch("poetry.utils._compat.Path.cwd") + p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__) / ".." repo.add_package(get_package("pendulum", "1.4.4")) @@ -1106,7 +1107,7 @@ def test_add_directory_constraint_old_installer( def test_add_directory_with_poetry_old_installer( app, repo, installer, mocker, old_tester ): - p = mocker.patch("poetry.utils._compat.Path.cwd") + p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__) / ".." repo.add_package(get_package("pendulum", "1.4.4")) @@ -1137,7 +1138,7 @@ def test_add_directory_with_poetry_old_installer( def test_add_file_constraint_wheel_old_installer( app, repo, installer, mocker, old_tester ): - p = mocker.patch("poetry.utils._compat.Path.cwd") + p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__) / ".." repo.add_package(get_package("pendulum", "1.4.4")) @@ -1175,7 +1176,7 @@ def test_add_file_constraint_wheel_old_installer( def test_add_file_constraint_sdist_old_installer( app, repo, installer, mocker, old_tester ): - p = mocker.patch("poetry.utils._compat.Path.cwd") + p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__) / ".." repo.add_package(get_package("pendulum", "1.4.4")) @@ -1253,7 +1254,7 @@ def test_add_constraint_with_extras_option_old_installer( def test_add_url_constraint_wheel_old_installer( app, repo, installer, mocker, old_tester ): - p = mocker.patch("poetry.utils._compat.Path.cwd") + p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__) / ".." repo.add_package(get_package("pendulum", "1.4.4")) diff --git a/tests/console/commands/test_cache.py b/tests/console/commands/test_cache.py index a8d47842d1e..f33fa9ad31a 100644 --- a/tests/console/commands/test_cache.py +++ b/tests/console/commands/test_cache.py @@ -5,9 +5,9 @@ @pytest.fixture def repository_cache_dir(monkeypatch, tmpdir): - import poetry.locations + from pathlib import Path - from poetry.utils._compat import Path + import poetry.locations path = Path(str(tmpdir)) monkeypatch.setattr(poetry.locations, "REPOSITORY_CACHE_DIR", path) diff --git a/tests/console/commands/test_check.py b/tests/console/commands/test_check.py index caa5485154f..90225bd6857 100644 --- a/tests/console/commands/test_check.py +++ b/tests/console/commands/test_check.py @@ -1,7 +1,6 @@ -import pytest +from pathlib import Path -from poetry.utils._compat import PY2 -from poetry.utils._compat import Path +import pytest @pytest.fixture() @@ -30,14 +29,7 @@ def test_check_invalid(mocker, tester): tester.execute() - if PY2: - expected = """\ -Error: u'description' is a required property -Warning: A wildcard Python dependency is ambiguous. Consider specifying a more explicit one. -Warning: The "pendulum" dependency specifies the "allows-prereleases" property, which is deprecated. Use "allow-prereleases" instead. -""" - else: - expected = """\ + expected = """\ Error: 'description' is a required property Warning: A wildcard Python dependency is ambiguous. Consider specifying a more explicit one. Warning: The "pendulum" dependency specifies the "allows-prereleases" property, which is deprecated. Use "allow-prereleases" instead. diff --git a/tests/console/commands/test_config.py b/tests/console/commands/test_config.py index 58bc8662bf5..ad20da54d39 100644 --- a/tests/console/commands/test_config.py +++ b/tests/console/commands/test_config.py @@ -6,8 +6,6 @@ from poetry.config.config_source import ConfigSource from poetry.core.pyproject import PyProjectException from poetry.factory import Factory -from poetry.utils._compat import PY2 -from poetry.utils._compat import WINDOWS @pytest.fixture() @@ -136,15 +134,13 @@ def test_set_cert(tester, auth_config_source, mocker): def test_config_installer_parallel(tester, command_tester_factory): - serial_enforced = PY2 and WINDOWS - tester.execute("--local installer.parallel") assert tester.io.fetch_output().strip() == "true" workers = command_tester_factory( "install" )._command._installer._executor._max_workers - assert workers > 1 or (serial_enforced and workers == 1) + assert workers > 1 tester.io.clear_output() tester.execute("--local installer.parallel false") diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index 7a9212ab262..812cc23ab96 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -2,12 +2,13 @@ import shutil import sys +from pathlib import Path + import pytest from cleo import CommandTester from poetry.repositories import Pool -from poetry.utils._compat import Path from poetry.utils._compat import decode from tests.helpers import TestApplication from tests.helpers import get_package @@ -26,7 +27,7 @@ def source_dir(tmp_path): # type: (...) -> Path @pytest.fixture def patches(mocker, source_dir, repo): - mocker.patch("poetry.utils._compat.Path.cwd", return_value=source_dir) + mocker.patch("pathlib.Path.cwd", return_value=source_dir) mocker.patch( "poetry.console.commands.init.InitCommand._get_pool", return_value=Pool([repo]) ) diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index c05ba257882..51e56d155ca 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -1,7 +1,8 @@ +from pathlib import Path + import pytest from poetry.packages import Locker -from poetry.utils._compat import Path from tests.helpers import get_package diff --git a/tests/console/commands/test_publish.py b/tests/console/commands/test_publish.py index dccc43ee87d..bcbf4c50006 100644 --- a/tests/console/commands/test_publish.py +++ b/tests/console/commands/test_publish.py @@ -1,14 +1,10 @@ -import pytest +from pathlib import Path + import requests from poetry.publishing.uploader import UploadError -from poetry.utils._compat import PY36 -from poetry.utils._compat import Path -@pytest.mark.skipif( - not PY36, reason="Improved error rendering is only available on Python >=3.6" -) def test_publish_returns_non_zero_code_for_upload_errors(app, app_tester, http): http.register_uri( http.POST, "https://upload.pypi.org/legacy/", status=400, body="Bad Request" @@ -47,32 +43,6 @@ def request_callback(*_, **__): assert expected in app_tester.io.fetch_output() -@pytest.mark.skipif( - PY36, reason="Improved error rendering is not available on Python <3.6" -) -def test_publish_returns_non_zero_code_for_upload_errors_older_python( - app, app_tester, http -): - http.register_uri( - http.POST, "https://upload.pypi.org/legacy/", status=400, body="Bad Request" - ) - - exit_code = app_tester.execute("publish --username foo --password bar") - - assert 1 == exit_code - - expected = """ -Publishing simple-project (1.2.3) to PyPI - - -UploadError - -HTTP Error 400: Bad Request -""" - - assert app_tester.io.fetch_output() == expected - - def test_publish_with_cert(app_tester, mocker): publisher_publish = mocker.patch("poetry.publishing.Publisher.publish") diff --git a/tests/console/commands/test_search.py b/tests/console/commands/test_search.py index 9b61476c0b5..18e094ff1ae 100644 --- a/tests/console/commands/test_search.py +++ b/tests/console/commands/test_search.py @@ -1,6 +1,6 @@ -import pytest +from pathlib import Path -from poetry.utils._compat import Path +import pytest TESTS_DIRECTORY = Path(__file__).parent.parent.parent diff --git a/tests/console/conftest.py b/tests/console/conftest.py index 6d7ef0fa675..a9b06f6c3b2 100644 --- a/tests/console/conftest.py +++ b/tests/console/conftest.py @@ -1,5 +1,7 @@ import os +from pathlib import Path + import pytest from cleo import ApplicationTester @@ -8,7 +10,6 @@ from poetry.installation.noop_installer import NoopInstaller from poetry.io.null_io import NullIO from poetry.repositories import Pool -from poetry.utils._compat import Path from poetry.utils.env import MockEnv from tests.helpers import TestApplication from tests.helpers import TestExecutor diff --git a/tests/helpers.py b/tests/helpers.py index f380bb8de16..8fc2a381c0f 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,5 +1,8 @@ import os import shutil +import urllib.parse + +from pathlib import Path from poetry.console import Application from poetry.core.masonry.utils.helpers import escape_name @@ -14,10 +17,7 @@ from poetry.packages import Locker from poetry.repositories import Repository from poetry.repositories.exceptions import PackageNotFound -from poetry.utils._compat import PY2 from poetry.utils._compat import WINDOWS -from poetry.utils._compat import Path -from poetry.utils._compat import urlparse FIXTURE_PATH = Path(__file__).parent / "fixtures" @@ -59,19 +59,13 @@ def copy_or_symlink(source, dest): # os.symlink requires either administrative privileges or developer mode on Win10, # throwing an OSError if neither is active. if WINDOWS: - if PY2: + try: + os.symlink(str(source), str(dest), target_is_directory=source.is_dir()) + except OSError: if source.is_dir(): shutil.copytree(str(source), str(dest)) else: shutil.copyfile(str(source), str(dest)) - else: - try: - os.symlink(str(source), str(dest), target_is_directory=source.is_dir()) - except OSError: - if source.is_dir(): - shutil.copytree(str(source), str(dest)) - else: - shutil.copyfile(str(source), str(dest)) else: os.symlink(str(source), str(dest)) @@ -92,7 +86,7 @@ def mock_clone(_, source, dest): def mock_download(url, dest, **__): - parts = urlparse.urlparse(url) + parts = urllib.parse.urlparse(url) fixtures = Path(__file__).parent / "fixtures" fixture = fixtures / parts.path.lstrip("/") diff --git a/tests/inspection/test_info.py b/tests/inspection/test_info.py index a128469a23f..e04bd52a5c3 100644 --- a/tests/inspection/test_info.py +++ b/tests/inspection/test_info.py @@ -1,12 +1,11 @@ +from pathlib import Path +from subprocess import CalledProcessError from typing import Set import pytest from poetry.inspection.info import PackageInfo from poetry.inspection.info import PackageInfoError -from poetry.utils._compat import PY35 -from poetry.utils._compat import CalledProcessError -from poetry.utils._compat import Path from poetry.utils._compat import decode from poetry.utils.env import EnvCommandError from poetry.utils.env import VirtualEnv @@ -134,13 +133,11 @@ def test_info_from_requires_txt(): demo_check_info(info) -@pytest.mark.skipif(not PY35, reason="Parsing of setup.py is skipped for Python < 3.5") def test_info_from_setup_py(demo_setup): info = PackageInfo.from_setup_files(demo_setup) demo_check_info(info, requires_dist={"package"}) -@pytest.mark.skipif(not PY35, reason="Parsing of setup.cfg is skipped for Python < 3.5") def test_info_from_setup_cfg(demo_setup_cfg): info = PackageInfo.from_setup_files(demo_setup_cfg) demo_check_info(info, requires_dist={"package"}) @@ -155,7 +152,6 @@ def test_info_no_setup_pkg_info_no_deps(): assert info.requires_dist is None -@pytest.mark.skipif(not PY35, reason="Parsing of setup.py is skipped for Python < 3.5") def test_info_setup_simple(mocker, demo_setup): spy = mocker.spy(VirtualEnv, "run") info = PackageInfo.from_directory(demo_setup) @@ -163,18 +159,6 @@ def test_info_setup_simple(mocker, demo_setup): demo_check_info(info, requires_dist={"package"}) -@pytest.mark.skipif( - PY35, - reason="For projects with setup.py using Python < 3.5 fallback to pep517 build", -) -def test_info_setup_simple_py2(mocker, demo_setup): - spy = mocker.spy(VirtualEnv, "run") - info = PackageInfo.from_directory(demo_setup) - assert spy.call_count == 2 - demo_check_info(info, requires_dist={"package"}) - - -@pytest.mark.skipif(not PY35, reason="Parsing of setup.cfg is skipped for Python < 3.5") def test_info_setup_cfg(mocker, demo_setup_cfg): spy = mocker.spy(VirtualEnv, "run") info = PackageInfo.from_directory(demo_setup_cfg) @@ -203,7 +187,6 @@ def test_info_setup_complex_pep517_legacy(demo_setup_complex_pep517_legacy): demo_check_info(info, requires_dist={"package"}) -@pytest.mark.skipif(not PY35, reason="Parsing of setup.py is skipped for Python < 3.5") def test_info_setup_complex_disable_build(mocker, demo_setup_complex): spy = mocker.spy(VirtualEnv, "run") info = PackageInfo.from_directory(demo_setup_complex, disable_build=True) @@ -213,7 +196,6 @@ def test_info_setup_complex_disable_build(mocker, demo_setup_complex): assert info.requires_dist is None -@pytest.mark.skipif(not PY35, reason="Parsing of setup.py is skipped for Python < 3.5") @pytest.mark.parametrize("missing", ["version", "name", "install_requires"]) def test_info_setup_missing_mandatory_should_trigger_pep517( mocker, source_dir, missing diff --git a/tests/installation/test_chef.py b/tests/installation/test_chef.py index 4e59b608a24..d25f276748d 100644 --- a/tests/installation/test_chef.py +++ b/tests/installation/test_chef.py @@ -1,8 +1,9 @@ +from pathlib import Path + from packaging.tags import Tag from poetry.core.packages.utils.link import Link from poetry.installation.chef import Chef -from poetry.utils._compat import Path from poetry.utils.env import MockEnv diff --git a/tests/installation/test_chooser.py b/tests/installation/test_chooser.py index cf3f931b942..75fd52c7926 100644 --- a/tests/installation/test_chooser.py +++ b/tests/installation/test_chooser.py @@ -1,5 +1,7 @@ import re +from pathlib import Path + import pytest from packaging.tags import Tag @@ -9,7 +11,6 @@ from poetry.repositories.legacy_repository import LegacyRepository from poetry.repositories.pool import Pool from poetry.repositories.pypi_repository import PyPiRepository -from poetry.utils._compat import Path from poetry.utils.env import MockEnv diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index 53ad3677350..ddf6faa2d66 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -4,6 +4,8 @@ import re import shutil +from pathlib import Path + import pytest from clikit.api.formatter.style import Style @@ -16,8 +18,6 @@ from poetry.installation.operations import Uninstall from poetry.installation.operations import Update from poetry.repositories.pool import Pool -from poetry.utils._compat import PY36 -from poetry.utils._compat import Path from poetry.utils.env import MockEnv from tests.repositories.test_pypi_repository import MockRepository @@ -155,9 +155,6 @@ def test_execute_shows_skipped_operations_if_verbose( assert 0 == len(env.executed) -@pytest.mark.skipif( - not PY36, reason="Improved error rendering is only available on Python >=3.6" -) def test_execute_should_show_errors(config, mocker, io, env): executor = Executor(env, pool, config, io) executor.verbose() diff --git a/tests/installation/test_installer.py b/tests/installation/test_installer.py index 106efde6e9c..2adcb13cd25 100644 --- a/tests/installation/test_installer.py +++ b/tests/installation/test_installer.py @@ -3,6 +3,8 @@ import json import sys +from pathlib import Path + import pytest from clikit.io import NullIO @@ -17,8 +19,6 @@ from poetry.repositories import Pool from poetry.repositories import Repository from poetry.repositories.installed_repository import InstalledRepository -from poetry.utils._compat import PY2 -from poetry.utils._compat import Path from poetry.utils.env import MockEnv from poetry.utils.env import NullEnv from tests.helpers import get_dependency @@ -112,15 +112,6 @@ def _get_content_hash(self): def _write_lock_data(self, data): for package in data["package"]: python_versions = str(package["python-versions"]) - if PY2: - python_versions = python_versions.decode() - if "requirements" in package: - requirements = {} - for key, value in package["requirements"].items(): - requirements[key.decode()] = value.decode() - - package["requirements"] = requirements - package["python-versions"] = python_versions self._written_data = json.loads(json.dumps(data)) @@ -1583,7 +1574,7 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de installer.whitelist(["pytest"]) installer.run() - assert (6 if not PY2 else 7) == installer.executor.installations_count + assert 6 == installer.executor.installations_count assert 0 == installer.executor.updates_count assert 0 == installer.executor.removals_count diff --git a/tests/installation/test_installer_old.py b/tests/installation/test_installer_old.py index b92bdce460e..97c1b163636 100644 --- a/tests/installation/test_installer_old.py +++ b/tests/installation/test_installer_old.py @@ -2,6 +2,8 @@ import sys +from pathlib import Path + import pytest from clikit.io import NullIO @@ -15,8 +17,6 @@ from poetry.repositories import Pool from poetry.repositories import Repository from poetry.repositories.installed_repository import InstalledRepository -from poetry.utils._compat import PY2 -from poetry.utils._compat import Path from poetry.utils.env import MockEnv from poetry.utils.env import NullEnv from tests.helpers import get_dependency @@ -74,15 +74,6 @@ def _get_content_hash(self): def _write_lock_data(self, data): for package in data["package"]: python_versions = str(package["python-versions"]) - if PY2: - python_versions = python_versions.decode() - if "requirements" in package: - requirements = {} - for key, value in package["requirements"].items(): - requirements[key.decode()] = value.decode() - - package["requirements"] = requirements - package["python-versions"] = python_versions self._written_data = data @@ -1514,7 +1505,7 @@ def test_installer_required_extras_should_not_be_removed_when_updating_single_de installer.whitelist(["pytest"]) installer.run() - assert len(installer.installer.installs) == 6 if not PY2 else 7 + assert len(installer.installer.installs) == 6 assert len(installer.installer.updates) == 0 assert len(installer.installer.removals) == 0 diff --git a/tests/installation/test_pip_installer.py b/tests/installation/test_pip_installer.py index d0e2e5a4dcd..64c64409051 100644 --- a/tests/installation/test_pip_installer.py +++ b/tests/installation/test_pip_installer.py @@ -1,5 +1,7 @@ import shutil +from pathlib import Path + import pytest from poetry.core.packages.package import Package @@ -7,7 +9,6 @@ from poetry.io.null_io import NullIO from poetry.repositories.legacy_repository import LegacyRepository from poetry.repositories.pool import Pool -from poetry.utils._compat import Path from poetry.utils.env import NullEnv diff --git a/tests/masonry/builders/test_editable_builder.py b/tests/masonry/builders/test_editable_builder.py index 3bf1e59c98e..5500bc49a4d 100644 --- a/tests/masonry/builders/test_editable_builder.py +++ b/tests/masonry/builders/test_editable_builder.py @@ -4,12 +4,13 @@ import os import shutil +from pathlib import Path + import pytest from poetry.factory import Factory from poetry.io.null_io import NullIO from poetry.masonry.builders.editable import EditableBuilder -from poetry.utils._compat import Path from poetry.utils.env import EnvManager from poetry.utils.env import MockEnv from poetry.utils.env import VirtualEnv diff --git a/tests/mixology/solutions/providers/test_python_requirement_solution_provider.py b/tests/mixology/solutions/providers/test_python_requirement_solution_provider.py index 81c11d215d3..14f1b3cb6e1 100644 --- a/tests/mixology/solutions/providers/test_python_requirement_solution_provider.py +++ b/tests/mixology/solutions/providers/test_python_requirement_solution_provider.py @@ -1,5 +1,3 @@ -import pytest - from poetry.core.packages.dependency import Dependency from poetry.mixology.failure import SolveFailure from poetry.mixology.incompatibility import Incompatibility @@ -7,12 +5,8 @@ from poetry.mixology.incompatibility_cause import PythonCause from poetry.mixology.term import Term from poetry.puzzle.exceptions import SolverProblemError -from poetry.utils._compat import PY36 -@pytest.mark.skipif( - not PY36, reason="Error solutions are only available for Python ^3.6" -) def test_it_can_solve_python_incompatibility_solver_errors(): from poetry.mixology.solutions.providers import PythonRequirementSolutionProvider from poetry.mixology.solutions.solutions import PythonRequirementSolution @@ -27,9 +21,6 @@ def test_it_can_solve_python_incompatibility_solver_errors(): assert isinstance(provider.get_solutions(exception)[0], PythonRequirementSolution) -@pytest.mark.skipif( - not PY36, reason="Error solutions are only available for Python ^3.6" -) def test_it_cannot_solve_other_solver_errors(): from poetry.mixology.solutions.providers import PythonRequirementSolutionProvider diff --git a/tests/mixology/solutions/solutions/test_python_requirement_solution.py b/tests/mixology/solutions/solutions/test_python_requirement_solution.py index e264ad8d04b..8ea58a4078d 100644 --- a/tests/mixology/solutions/solutions/test_python_requirement_solution.py +++ b/tests/mixology/solutions/solutions/test_python_requirement_solution.py @@ -1,5 +1,3 @@ -import pytest - from clikit.io.buffered_io import BufferedIO from poetry.core.packages.dependency import Dependency @@ -8,12 +6,8 @@ from poetry.mixology.incompatibility_cause import PythonCause from poetry.mixology.term import Term from poetry.puzzle.exceptions import SolverProblemError -from poetry.utils._compat import PY36 -@pytest.mark.skipif( - not PY36, reason="Error solutions are only available for Python ^3.6" -) def test_it_provides_the_correct_solution(): from poetry.mixology.solutions.solutions import PythonRequirementSolution diff --git a/tests/publishing/test_publisher.py b/tests/publishing/test_publisher.py index ceb9e7d4da0..786d659b0db 100644 --- a/tests/publishing/test_publisher.py +++ b/tests/publishing/test_publisher.py @@ -1,5 +1,7 @@ import os +from pathlib import Path + import pytest from cleo.io import BufferedIO @@ -7,7 +9,6 @@ from poetry.factory import Factory from poetry.io.null_io import NullIO from poetry.publishing.publisher import Publisher -from poetry.utils._compat import Path def test_publish_publishes_to_pypi_by_default(fixture_dir, mocker, config): diff --git a/tests/publishing/test_uploader.py b/tests/publishing/test_uploader.py index 0b32e77d864..ae0eb041b7b 100644 --- a/tests/publishing/test_uploader.py +++ b/tests/publishing/test_uploader.py @@ -1,10 +1,11 @@ +from pathlib import Path + import pytest from poetry.factory import Factory from poetry.io.null_io import NullIO from poetry.publishing.uploader import Uploader from poetry.publishing.uploader import UploadError -from poetry.utils._compat import Path fixtures_dir = Path(__file__).parent.parent / "fixtures" diff --git a/tests/puzzle/conftest.py b/tests/puzzle/conftest.py index e3812530bf5..5848294b765 100644 --- a/tests/puzzle/conftest.py +++ b/tests/puzzle/conftest.py @@ -1,8 +1,8 @@ import shutil -import pytest +from pathlib import Path -from poetry.utils._compat import Path +import pytest try: diff --git a/tests/puzzle/test_provider.py b/tests/puzzle/test_provider.py index ecab7f3ab2d..2d0c4557bdb 100644 --- a/tests/puzzle/test_provider.py +++ b/tests/puzzle/test_provider.py @@ -1,3 +1,4 @@ +from pathlib import Path from subprocess import CalledProcessError import pytest @@ -12,8 +13,6 @@ from poetry.puzzle.provider import Provider from poetry.repositories.pool import Pool from poetry.repositories.repository import Repository -from poetry.utils._compat import PY35 -from poetry.utils._compat import Path from poetry.utils.env import EnvCommandError from poetry.utils.env import MockEnv as BaseMockEnv from tests.helpers import get_dependency @@ -94,7 +93,6 @@ def test_search_for_vcs_setup_egg_info_with_extras(provider): } -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_search_for_vcs_read_setup(provider, mocker): mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) @@ -115,7 +113,6 @@ def test_search_for_vcs_read_setup(provider, mocker): } -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_search_for_vcs_read_setup_with_extras(provider, mocker): mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) @@ -241,7 +238,6 @@ def test_search_for_directory_setup_with_base(provider, directory): ) -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_search_for_directory_setup_read_setup(provider, mocker): mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) @@ -270,7 +266,6 @@ def test_search_for_directory_setup_read_setup(provider, mocker): } -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_search_for_directory_setup_read_setup_with_extras(provider, mocker): mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv()) @@ -300,7 +295,6 @@ def test_search_for_directory_setup_read_setup_with_extras(provider, mocker): } -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_search_for_directory_setup_read_setup_with_no_dependencies(provider): dependency = DirectoryDependency( "demo", diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 1393b13f1ef..6e2a53a5c22 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -1,3 +1,5 @@ +from pathlib import Path + import pytest from clikit.io import NullIO @@ -13,7 +15,6 @@ from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.pool import Pool from poetry.repositories.repository import Repository -from poetry.utils._compat import Path from poetry.utils.env import MockEnv from tests.helpers import get_dependency from tests.helpers import get_package diff --git a/tests/repositories/test_installed_repository.py b/tests/repositories/test_installed_repository.py index 3caa702a09c..5097d3bc4a0 100644 --- a/tests/repositories/test_installed_repository.py +++ b/tests/repositories/test_installed_repository.py @@ -1,3 +1,4 @@ +from pathlib import Path from typing import Optional import pytest @@ -6,11 +7,9 @@ from poetry.core.packages import Package from poetry.repositories.installed_repository import InstalledRepository -from poetry.utils._compat import PY36 -from poetry.utils._compat import Path from poetry.utils._compat import metadata -from poetry.utils._compat import zipp from poetry.utils.env import MockEnv as BaseMockEnv +from tests.compat import zipp FIXTURES_DIR = Path(__file__).parent / "fixtures" @@ -135,9 +134,6 @@ def test_load_platlib_package(repository): assert lib64.version.text == "2.3.4" -@pytest.mark.skipif( - not PY36, reason="pathlib.resolve() does not support strict argument" -) def test_load_editable_package(repository): # test editable package with text .pth file editable = get_package_from_repository("editable", repository) diff --git a/tests/repositories/test_legacy_repository.py b/tests/repositories/test_legacy_repository.py index 47ccc1052f9..1402f4799af 100644 --- a/tests/repositories/test_legacy_repository.py +++ b/tests/repositories/test_legacy_repository.py @@ -1,5 +1,7 @@ import shutil +from pathlib import Path + import pytest from poetry.core.packages import Dependency @@ -8,8 +10,6 @@ from poetry.repositories.exceptions import RepositoryError from poetry.repositories.legacy_repository import LegacyRepository from poetry.repositories.legacy_repository import Page -from poetry.utils._compat import PY35 -from poetry.utils._compat import Path try: @@ -95,16 +95,6 @@ def test_get_package_information_fallback_read_setup(): == "Jupyter metapackage. Install all the Jupyter components in one go." ) - if PY35: - assert package.requires == [ - Dependency("notebook", "*"), - Dependency("qtconsole", "*"), - Dependency("jupyter-console", "*"), - Dependency("nbconvert", "*"), - Dependency("ipykernel", "*"), - Dependency("ipywidgets", "*"), - ] - def test_get_package_information_skips_dependencies_with_invalid_constraints(): repo = MockRepository() diff --git a/tests/repositories/test_pypi_repository.py b/tests/repositories/test_pypi_repository.py index 55afdd39485..cdd8484cc34 100644 --- a/tests/repositories/test_pypi_repository.py +++ b/tests/repositories/test_pypi_repository.py @@ -2,6 +2,7 @@ import shutil from io import BytesIO +from pathlib import Path import pytest @@ -11,8 +12,6 @@ from poetry.core.packages import Dependency from poetry.factory import Factory from poetry.repositories.pypi_repository import PyPiRepository -from poetry.utils._compat import PY35 -from poetry.utils._compat import Path from poetry.utils._compat import encode @@ -136,7 +135,6 @@ def test_fallback_inspects_sdist_first_if_no_matching_wheels_can_be_found(): assert dep.python_versions == "~2.7" -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_fallback_can_read_setup_to_get_dependencies(): repo = MockRepository(fallback=True) diff --git a/tests/test_factory.py b/tests/test_factory.py index b2c232b20e7..d213d808405 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -2,14 +2,14 @@ from __future__ import absolute_import from __future__ import unicode_literals +from pathlib import Path + import pytest from poetry.core.toml.file import TOMLFile from poetry.factory import Factory from poetry.repositories.legacy_repository import LegacyRepository from poetry.repositories.pypi_repository import PyPiRepository -from poetry.utils._compat import PY2 -from poetry.utils._compat import Path fixtures_dir = Path(__file__).parent / "fixtures" @@ -196,16 +196,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": []} @@ -216,13 +210,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/utils/test_env.py b/tests/utils/test_env.py index 9c1e2c623d9..f0e2bd21a45 100644 --- a/tests/utils/test_env.py +++ b/tests/utils/test_env.py @@ -2,6 +2,7 @@ import shutil import sys +from pathlib import Path from typing import Optional from typing import Union @@ -13,8 +14,6 @@ from poetry.core.semver import Version from poetry.core.toml.file import TOMLFile from poetry.factory import Factory -from poetry.utils._compat import PY2 -from poetry.utils._compat import Path from poetry.utils.env import GET_BASE_PREFIX from poetry.utils.env import EnvCommandError from poetry.utils.env import EnvManager @@ -145,11 +144,10 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "poetry.utils._compat.subprocess.check_output", - side_effect=check_output_wrapper(), + "subprocess.check_output", side_effect=check_output_wrapper(), ) mocker.patch( - "poetry.utils._compat.subprocess.Popen.communicate", + "subprocess.Popen.communicate", side_effect=[("/prefix", None), ("/prefix", None)], ) m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build_venv) @@ -186,12 +184,10 @@ def test_activate_activates_existing_virtualenv_no_envs_file( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "poetry.utils._compat.subprocess.check_output", - side_effect=check_output_wrapper(), + "subprocess.check_output", side_effect=check_output_wrapper(), ) mocker.patch( - "poetry.utils._compat.subprocess.Popen.communicate", - side_effect=[("/prefix", None)], + "subprocess.Popen.communicate", side_effect=[("/prefix", None)], ) m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build_venv) @@ -227,12 +223,10 @@ def test_activate_activates_same_virtualenv_with_envs_file( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "poetry.utils._compat.subprocess.check_output", - side_effect=check_output_wrapper(), + "subprocess.check_output", side_effect=check_output_wrapper(), ) mocker.patch( - "poetry.utils._compat.subprocess.Popen.communicate", - side_effect=[("/prefix", None)], + "subprocess.Popen.communicate", side_effect=[("/prefix", None)], ) m = mocker.patch("poetry.utils.env.EnvManager.create_venv") @@ -266,11 +260,11 @@ def test_activate_activates_different_virtualenv_with_envs_file( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "poetry.utils._compat.subprocess.check_output", + "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.6")), ) mocker.patch( - "poetry.utils._compat.subprocess.Popen.communicate", + "subprocess.Popen.communicate", side_effect=[("/prefix", None), ("/prefix", None), ("/prefix", None)], ) m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build_venv) @@ -309,11 +303,10 @@ def test_activate_activates_recreates_for_different_patch( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "poetry.utils._compat.subprocess.check_output", - side_effect=check_output_wrapper(), + "subprocess.check_output", side_effect=check_output_wrapper(), ) mocker.patch( - "poetry.utils._compat.subprocess.Popen.communicate", + "subprocess.Popen.communicate", side_effect=[ ("/prefix", None), ('{"version_info": [3, 7, 0]}', None), @@ -366,11 +359,11 @@ def test_activate_does_not_recreate_when_switching_minor( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "poetry.utils._compat.subprocess.check_output", + "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.6")), ) mocker.patch( - "poetry.utils._compat.subprocess.Popen.communicate", + "subprocess.Popen.communicate", side_effect=[("/prefix", None), ("/prefix", None), ("/prefix", None)], ) build_venv_m = mocker.patch( @@ -411,8 +404,7 @@ def test_deactivate_non_activated_but_existing( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "poetry.utils._compat.subprocess.check_output", - side_effect=check_output_wrapper(), + "subprocess.check_output", side_effect=check_output_wrapper(), ) manager.deactivate(NullIO()) @@ -450,8 +442,7 @@ def test_deactivate_activated(tmp_dir, manager, poetry, config, mocker): config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "poetry.utils._compat.subprocess.check_output", - side_effect=check_output_wrapper(), + "subprocess.check_output", side_effect=check_output_wrapper(), ) manager.deactivate(NullIO()) @@ -482,12 +473,10 @@ def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( envs_file.write(doc) mocker.patch( - "poetry.utils._compat.subprocess.check_output", - side_effect=check_output_wrapper(), + "subprocess.check_output", side_effect=check_output_wrapper(), ) mocker.patch( - "poetry.utils._compat.subprocess.Popen.communicate", - side_effect=[("/prefix", None)], + "subprocess.Popen.communicate", side_effect=[("/prefix", None)], ) env = manager.get() @@ -518,7 +507,7 @@ def test_remove_by_python_version(tmp_dir, manager, poetry, config, mocker): (Path(tmp_dir) / "{}-py3.6".format(venv_name)).mkdir() mocker.patch( - "poetry.utils._compat.subprocess.check_output", + "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.6")), ) @@ -536,7 +525,7 @@ def test_remove_by_name(tmp_dir, manager, poetry, config, mocker): (Path(tmp_dir) / "{}-py3.6".format(venv_name)).mkdir() mocker.patch( - "poetry.utils._compat.subprocess.check_output", + "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.6")), ) @@ -554,7 +543,7 @@ def test_remove_also_deactivates(tmp_dir, manager, poetry, config, mocker): (Path(tmp_dir) / "{}-py3.6".format(venv_name)).mkdir() mocker.patch( - "poetry.utils._compat.subprocess.check_output", + "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.6")), ) @@ -591,7 +580,7 @@ def test_remove_keeps_dir_if_not_deleteable(tmp_dir, manager, poetry, config, mo file2_path.touch(exist_ok=False) mocker.patch( - "poetry.utils._compat.subprocess.check_output", + "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.6")), ) @@ -619,9 +608,7 @@ def err_on_rm_venv_only(path, *args, **kwargs): m.side_effect = original_rmtree # Avoid teardown using `err_on_rm_venv_only` -@pytest.mark.skipif( - os.name == "nt" or PY2, reason="Symlinks are not support for Windows" -) +@pytest.mark.skipif(os.name == "nt", reason="Symlinks are not support for Windows") def test_env_has_symlinks_on_nix(tmp_dir, tmp_venv): assert os.path.islink(tmp_venv.python) @@ -652,7 +639,7 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_ mocker.patch("sys.version_info", (2, 7, 16)) mocker.patch( - "poetry.utils._compat.subprocess.check_output", + "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.7.5")), ) m = mocker.patch( @@ -678,9 +665,7 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific venv_name = manager.generate_env_name("simple-project", str(poetry.file.parent)) mocker.patch("sys.version_info", (2, 7, 16)) - mocker.patch( - "poetry.utils._compat.subprocess.check_output", side_effect=["3.5.3", "3.9.0"] - ) + mocker.patch("subprocess.check_output", side_effect=["3.5.3", "3.9.0"]) m = mocker.patch( "poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kwargs: "" ) @@ -702,9 +687,7 @@ def test_create_venv_fails_if_no_compatible_python_version_could_be_found( poetry.package.python_versions = "^4.8" - mocker.patch( - "poetry.utils._compat.subprocess.check_output", side_effect=["", "", "", ""] - ) + mocker.patch("subprocess.check_output", side_effect=["", "", "", ""]) m = mocker.patch( "poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kwargs: "" ) @@ -730,7 +713,7 @@ def test_create_venv_does_not_try_to_find_compatible_versions_with_executable( poetry.package.python_versions = "^4.8" - mocker.patch("poetry.utils._compat.subprocess.check_output", side_effect=["3.8.0"]) + mocker.patch("subprocess.check_output", side_effect=["3.8.0"]) m = mocker.patch( "poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kwargs: "" ) @@ -762,7 +745,7 @@ def test_create_venv_uses_patch_version_to_detect_compatibility( mocker.patch("sys.version_info", (version.major, version.minor, version.patch + 1)) check_output = mocker.patch( - "poetry.utils._compat.subprocess.check_output", + "subprocess.check_output", side_effect=check_output_wrapper(Version.parse("3.6.9")), ) m = mocker.patch( @@ -793,7 +776,7 @@ def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable( venv_name = manager.generate_env_name("simple-project", str(poetry.file.parent)) check_output = mocker.patch( - "poetry.utils._compat.subprocess.check_output", + "subprocess.check_output", side_effect=check_output_wrapper( Version.parse("{}.{}.0".format(version.major, version.minor - 1)) ), @@ -831,11 +814,10 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir( ) mocker.patch( - "poetry.utils._compat.subprocess.check_output", - side_effect=check_output_wrapper(), + "subprocess.check_output", side_effect=check_output_wrapper(), ) mocker.patch( - "poetry.utils._compat.subprocess.Popen.communicate", + "subprocess.Popen.communicate", side_effect=[("/prefix", None), ("/prefix", None)], ) m = mocker.patch("poetry.utils.env.EnvManager.build_venv") diff --git a/tests/utils/test_env_site.py b/tests/utils/test_env_site.py index f25e2142193..01abb113771 100644 --- a/tests/utils/test_env_site.py +++ b/tests/utils/test_env_site.py @@ -1,13 +1,14 @@ import uuid -from poetry.utils._compat import Path +from pathlib import Path + from poetry.utils._compat import decode from poetry.utils.env import SitePackages def test_env_site_simple(tmp_dir, mocker): # emulate permission error when creating directory - mocker.patch("poetry.utils._compat.Path.mkdir", side_effect=OSError()) + mocker.patch("pathlib.Path.mkdir", side_effect=OSError()) site_packages = SitePackages(Path("/non-existent"), fallbacks=[Path(tmp_dir)]) candidates = site_packages.make_candidates(Path("hello.txt"), writable_only=True) hello = Path(tmp_dir) / "hello.txt" diff --git a/tests/utils/test_exporter.py b/tests/utils/test_exporter.py index 1e660ce69cd..f15e2dc53a3 100644 --- a/tests/utils/test_exporter.py +++ b/tests/utils/test_exporter.py @@ -1,5 +1,7 @@ import sys +from pathlib import Path + import pytest from poetry.core.packages import dependency_from_pep_508 @@ -7,7 +9,6 @@ from poetry.factory import Factory from poetry.packages import Locker as BaseLocker from poetry.repositories.legacy_repository import LegacyRepository -from poetry.utils._compat import Path from poetry.utils.exporter import Exporter @@ -42,9 +43,7 @@ def working_directory(): @pytest.fixture(autouse=True) def mock_path_cwd(mocker, working_directory): - yield mocker.patch( - "poetry.core.utils._compat.Path.cwd", return_value=working_directory - ) + yield mocker.patch("pathlib.Path.cwd", return_value=working_directory) @pytest.fixture() diff --git a/tests/utils/test_helpers.py b/tests/utils/test_helpers.py index 83bf2030780..fe105df6b74 100644 --- a/tests/utils/test_helpers.py +++ b/tests/utils/test_helpers.py @@ -1,5 +1,6 @@ +from pathlib import Path + from poetry.core.utils.helpers import parse_requires -from poetry.utils._compat import Path from poetry.utils.helpers import get_cert from poetry.utils.helpers import get_client_cert diff --git a/tests/utils/test_setup_reader.py b/tests/utils/test_setup_reader.py index 68fc005a496..ccde90ac253 100644 --- a/tests/utils/test_setup_reader.py +++ b/tests/utils/test_setup_reader.py @@ -3,7 +3,6 @@ import pytest from poetry.core.semver.exceptions import ParseVersionError -from poetry.utils._compat import PY35 from poetry.utils.setup_reader import SetupReader @@ -15,7 +14,6 @@ def _setup(name): return _setup -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_setup_reader_read_first_level_setup_call_with_direct_types(setup): result = SetupReader.read_from_directory(setup("flask")) @@ -48,7 +46,6 @@ def test_setup_reader_read_first_level_setup_call_with_direct_types(setup): assert expected_python_requires == result["python_requires"] -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_setup_reader_read_first_level_setup_call_with_variables(setup): result = SetupReader.read_from_directory(setup("requests")) @@ -74,7 +71,6 @@ def test_setup_reader_read_first_level_setup_call_with_variables(setup): assert expected_python_requires == result["python_requires"] -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_setup_reader_read_sub_level_setup_call_with_direct_types(setup): result = SetupReader.read_from_directory(setup("sqlalchemy")) @@ -123,7 +119,6 @@ def test_setup_reader_read_setup_cfg_with_attr(setup): SetupReader.read_from_directory(setup("with-setup-cfg-attr")) -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_setup_reader_read_setup_kwargs(setup): result = SetupReader.read_from_directory(setup("pendulum")) @@ -140,7 +135,6 @@ def test_setup_reader_read_setup_kwargs(setup): assert expected_python_requires == result["python_requires"] -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_setup_reader_read_setup_call_in_main(setup): result = SetupReader.read_from_directory(setup("pyyaml")) @@ -157,7 +151,6 @@ def test_setup_reader_read_setup_call_in_main(setup): assert expected_python_requires == result["python_requires"] -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_setup_reader_read_extras_require_with_variables(setup): result = SetupReader.read_from_directory(setup("extras_require_with_vars")) @@ -174,7 +167,6 @@ def test_setup_reader_read_extras_require_with_variables(setup): assert expected_python_requires == result["python_requires"] -@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4") def test_setup_reader_setuptools(setup): result = SetupReader.read_from_directory(setup("setuptools_setup")) From 2aab9bcd495e11e5f4491aa72a9510773cc4a90e Mon Sep 17 00:00:00 2001 From: Vladislav Goncharenko Date: Sat, 26 Dec 2020 17:39:51 +0300 Subject: [PATCH 34/54] Added missing Option in install command description (#3500) * Added missing Option in install command description --- docs/docs/cli.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/cli.md b/docs/docs/cli.md index bf207e8dc42..d2a56788de0 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -146,6 +146,8 @@ poetry install --no-root * `--no-dev`: Do not install dev dependencies. * `--no-root`: Do not install the root package (your project). +* `--dry-run`: Output the operations but do not execute anything (implicitly enables --verbose). +* `--remove-untracked`: Remove dependencies not presented in the lock file * `--extras (-E)`: Features to install (multiple values allowed). ## update From 6cd4e05eaacba4756ec3b5b4e44f3471cf6506dc Mon Sep 17 00:00:00 2001 From: Torsten Rudolf Date: Tue, 5 Jan 2021 00:49:24 +1100 Subject: [PATCH 35/54] document ssh connection for git dependencies (#3389) Co-authored-by: Steph Samson --- docs/docs/dependency-specification.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/docs/dependency-specification.md b/docs/docs/dependency-specification.md index f71480dfa41..d90067d31e3 100644 --- a/docs/docs/dependency-specification.md +++ b/docs/docs/dependency-specification.md @@ -98,6 +98,13 @@ flask = { git = "https://github.com/pallets/flask.git", rev = "38eb5d3b" } numpy = { git = "https://github.com/numpy/numpy.git", tag = "v0.13.2" } ``` +To use an SSH connection, for example in the case of private repositories, use the following example syntax: + +```toml +[tool.poetry.dependencies] +requests = { git = "git@github.com:requests/requests.git" } +``` + ## `path` dependencies To depend on a library located in a local directory or file, From 529c5985c1e12be700d20927289d126955ff0b80 Mon Sep 17 00:00:00 2001 From: atanas argirov Date: Tue, 5 Jan 2021 18:07:58 +0000 Subject: [PATCH 36/54] * added additional description for poetry add covering git+ssh URL use cases (#3521) --- docs/docs/cli.md | 9 +++++++++ poetry/console/commands/add.py | 2 ++ 2 files changed, 11 insertions(+) diff --git a/docs/docs/cli.md b/docs/docs/cli.md index d2a56788de0..018c1d9c3cd 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -215,6 +215,10 @@ or use ssh instead of https: ```bash poetry add git+ssh://git@github.com/sdispater/pendulum.git + +or alternatively: + +poetry add git+ssh://git@github.com:sdispater/pendulum.git ``` If you need to checkout a specific branch, tag or revision, @@ -223,6 +227,11 @@ you can specify it when using `add`: ```bash poetry add git+https://github.com/sdispater/pendulum.git#develop poetry add git+https://github.com/sdispater/pendulum.git#2.0.5 + +or using SSH instead: + +poetry add git+ssh://github.com/sdispater/pendulum.git#develop +poetry add git+ssh://github.com/sdispater/pendulum.git#2.0.5 ``` or make them point to a local directory or file: diff --git a/poetry/console/commands/add.py b/poetry/console/commands/add.py index 29cf07d2a26..72ddeb015c1 100644 --- a/poetry/console/commands/add.py +++ b/poetry/console/commands/add.py @@ -59,6 +59,8 @@ class AddCommand(InstallerCommand, InitCommand): " - A name and a constraint (requests@^2.23.0)\n" " - A git url (git+https://github.com/python-poetry/poetry.git)\n" " - A git url with a revision (git+https://github.com/python-poetry/poetry.git#develop)\n" + " - A git SSH url (git+ssh://github.com/python-poetry/poetry.git)\n" + " - A git SSH url with a revision (git+ssh://github.com/python-poetry/poetry.git#develop)\n" " - A file path (../my-package/my-package.whl)\n" " - A directory (../my-package/)\n" " - A url (https://example.com/packages/my-package-0.1.0.tar.gz)\n" From e36fb739ee7be9748e1176bd10030760473e880c Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Tue, 5 Jan 2021 19:22:07 +0100 Subject: [PATCH 37/54] Remove non-existing --path option, add missing options (#3522) * Remove non-existing --path option * Add missing options --- docs/docs/cli.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/docs/cli.md b/docs/docs/cli.md index 018c1d9c3cd..9ceb059bfe7 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -266,10 +266,14 @@ poetry add "git+https://github.com/pallets/flask.git@1.1.1[dotenv,dev]" ### Options * `--dev (-D)`: Add package as development dependency. -* `--path`: The path to a dependency. -* `--optional` : Add as an optional dependency. -* `--dry-run` : Outputs the operations but will not execute anything (implicitly enables --verbose). -* `--lock` : Do not perform install (only update the lockfile). +* `--extras (-E)`: Extras to activate for the dependency. (multiple values allowed) +* `--optional`: Add as an optional dependency. +* `--python`: Python version for which the dependency must be installed. +* `--platform`: Platforms for which the dependency must be installed. +* `--source`: Name of the source to use to install the package. +* `---allow-prereleases`: Accept prereleases. +* `--dry-run`: Outputs the operations but will not execute anything (implicitly enables --verbose). +* `--lock`: Do not perform install (only update the lockfile). ## remove From b8f9547aa19efbd9cb81dfb6fffde1bd392d332d Mon Sep 17 00:00:00 2001 From: John Peter Yamauchi Date: Tue, 5 Jan 2021 17:55:26 -0600 Subject: [PATCH 38/54] legacy repository: support redirected url --- poetry/repositories/legacy_repository.py | 13 +++++++++++-- tests/repositories/test_legacy_repository.py | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/poetry/repositories/legacy_repository.py b/poetry/repositories/legacy_repository.py index e059c3bb8d4..94e1dbf4d7b 100644 --- a/poetry/repositories/legacy_repository.py +++ b/poetry/repositories/legacy_repository.py @@ -387,8 +387,17 @@ def _get(self, endpoint): # type: (str) -> Union[Page, None] if response.status_code in (401, 403): self._log( - "Authorization error accessing {url}".format(url=url), level="warn" + "Authorization error accessing {url}".format(url=response.url), + level="warn", ) return - return Page(url, response.content, response.headers) + if response.url != url: + self._log( + "Response URL {response_url} differs from request URL {url}".format( + response_url=response.url, url=url + ), + level="debug", + ) + + return Page(response.url, response.content, response.headers) diff --git a/tests/repositories/test_legacy_repository.py b/tests/repositories/test_legacy_repository.py index 1402f4799af..9de70520728 100644 --- a/tests/repositories/test_legacy_repository.py +++ b/tests/repositories/test_legacy_repository.py @@ -3,6 +3,7 @@ from pathlib import Path import pytest +import requests from poetry.core.packages import Dependency from poetry.factory import Factory @@ -322,3 +323,17 @@ def test_get_4xx_and_5xx_raises(http): for endpoint in endpoints: with pytest.raises(RepositoryError): repo._get(endpoint) + + +def test_get_redirected_response_url(http, monkeypatch): + repo = MockHttpRepository({"/foo": 200}, http) + redirect_url = "http://legacy.redirect.bar" + + def get_mock(url): + response = requests.Response() + response.status_code = 200 + response.url = redirect_url + "/foo" + return response + + monkeypatch.setattr(repo.session, "get", get_mock) + assert repo._get("/foo")._url == "http://legacy.redirect.bar/foo/" From de0b32c245c72568cf546090600d4626917cd3a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 6 Jan 2021 14:10:09 +0100 Subject: [PATCH 39/54] tests: fix a typo in mocker autospec --- tests/inspection/test_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/inspection/test_info.py b/tests/inspection/test_info.py index e04bd52a5c3..ac0c4504e2c 100644 --- a/tests/inspection/test_info.py +++ b/tests/inspection/test_info.py @@ -174,7 +174,7 @@ def test_info_setup_complex(demo_setup_complex): def test_info_setup_complex_pep517_error(mocker, demo_setup_complex): mocker.patch( "poetry.utils.env.VirtualEnv.run", - auto_spec=True, + autospec=True, side_effect=EnvCommandError(CalledProcessError(1, "mock", output="mock")), ) From a106cb21205568126432266160a9743885e13443 Mon Sep 17 00:00:00 2001 From: sprt Date: Sat, 16 Jan 2021 23:50:12 +0100 Subject: [PATCH 40/54] version: take --short into account when updating Fixes #3577. This makes `poetry version --short ` output only the updated version number, similarly to `poetry version --short`. --- poetry/console/commands/version.py | 11 +++++++---- tests/console/commands/test_version.py | 10 ++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/poetry/console/commands/version.py b/poetry/console/commands/version.py index 21ed676f54f..5ac5c666afb 100644 --- a/poetry/console/commands/version.py +++ b/poetry/console/commands/version.py @@ -48,11 +48,14 @@ def handle(self): self.poetry.package.pretty_version, version ) - self.line( - "Bumping version from {} to {}".format( - self.poetry.package.pretty_version, version + if self.option("short"): + self.line("{}".format(version)) + else: + self.line( + "Bumping version from {} to {}".format( + self.poetry.package.pretty_version, version + ) ) - ) content = self.poetry.file.read() poetry_content = content["tool"]["poetry"] diff --git a/tests/console/commands/test_version.py b/tests/console/commands/test_version.py index 77f6d8aa41e..ac91df1690c 100644 --- a/tests/console/commands/test_version.py +++ b/tests/console/commands/test_version.py @@ -51,3 +51,13 @@ def test_version_show(tester): def test_short_version_show(tester): tester.execute("--short") assert "1.2.3\n" == tester.io.fetch_output() + + +def test_version_update(tester): + tester.execute("2.0.0") + assert "Bumping version from 1.2.3 to 2.0.0\n" == tester.io.fetch_output() + + +def test_short_version_update(tester): + tester.execute("--short 2.0.0") + assert "2.0.0\n" == tester.io.fetch_output() From 1346497b2bb8c1d1353243a153f6a85b41a57728 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Fri, 30 Oct 2020 23:03:19 +0100 Subject: [PATCH 41/54] fix and add several type hints --- poetry/config/config.py | 10 +- poetry/config/file_config_source.py | 5 +- poetry/console/__init__.py | 2 +- poetry/console/application.py | 10 +- poetry/console/commands/about.py | 2 +- poetry/console/commands/add.py | 2 +- poetry/console/commands/build.py | 2 +- poetry/console/commands/cache/cache.py | 2 +- poetry/console/commands/cache/clear.py | 2 +- poetry/console/commands/cache/list.py | 4 +- poetry/console/commands/check.py | 2 +- poetry/console/commands/command.py | 8 +- poetry/console/commands/config.py | 25 ++++- poetry/console/commands/debug/info.py | 2 +- poetry/console/commands/debug/resolve.py | 4 +- poetry/console/commands/env/info.py | 11 ++- poetry/console/commands/env/list.py | 2 +- poetry/console/commands/env/remove.py | 2 +- poetry/console/commands/env/use.py | 2 +- poetry/console/commands/env_command.py | 12 ++- poetry/console/commands/export.py | 2 +- poetry/console/commands/init.py | 15 +-- poetry/console/commands/install.py | 2 +- poetry/console/commands/installer_command.py | 4 +- poetry/console/commands/lock.py | 2 +- poetry/console/commands/new.py | 2 +- poetry/console/commands/publish.py | 3 +- poetry/console/commands/remove.py | 2 +- poetry/console/commands/run.py | 7 +- poetry/console/commands/search.py | 2 +- poetry/console/commands/self/self.py | 2 +- poetry/console/commands/self/update.py | 32 ++++--- poetry/console/commands/shell.py | 2 +- poetry/console/commands/show.py | 48 +++++++--- poetry/console/commands/update.py | 2 +- poetry/console/commands/version.py | 4 +- poetry/console/config/application_config.py | 2 +- poetry/console/logging/io_formatter.py | 8 +- poetry/console/logging/io_handler.py | 12 ++- poetry/factory.py | 2 +- poetry/inspection/info.py | 4 +- poetry/installation/base_installer.py | 13 ++- poetry/installation/chooser.py | 5 +- poetry/installation/executor.py | 72 ++++++++++----- poetry/installation/installer.py | 32 ++++--- poetry/installation/noop_installer.py | 21 +++-- poetry/installation/operations/__init__.py | 5 + poetry/installation/operations/install.py | 19 +++- poetry/installation/operations/operation.py | 17 ++-- poetry/installation/operations/uninstall.py | 19 +++- poetry/installation/operations/update.py | 23 +++-- poetry/installation/pip_installer.py | 23 +++-- poetry/io/null_io.py | 4 +- poetry/layouts/layout.py | 34 +++---- poetry/layouts/src.py | 7 +- poetry/layouts/standard.py | 6 +- poetry/masonry/builders/editable.py | 26 ++++-- poetry/mixology/__init__.py | 16 +++- poetry/mixology/assignment.py | 20 +++- poetry/mixology/failure.py | 11 ++- poetry/mixology/incompatibility.py | 30 +++--- poetry/mixology/incompatibility_cause.py | 31 ++++--- poetry/mixology/partial_solution.py | 24 +++-- poetry/mixology/result.py | 17 +++- .../solutions/python_requirement_solution.py | 15 ++- poetry/mixology/term.py | 23 +++-- poetry/mixology/version_solver.py | 12 +-- poetry/packages/dependency_package.py | 16 ++-- poetry/packages/locker.py | 20 ++-- poetry/packages/package_collection.py | 15 ++- poetry/publishing/publisher.py | 16 +++- poetry/publishing/uploader.py | 26 ++++-- poetry/puzzle/exceptions.py | 12 ++- poetry/puzzle/provider.py | 17 ++-- poetry/puzzle/solver.py | 64 +++++++++---- poetry/repositories/base_repository.py | 24 +++-- poetry/repositories/legacy_repository.py | 26 ++++-- poetry/repositories/pool.py | 13 ++- poetry/repositories/pypi_repository.py | 12 ++- poetry/repositories/repository.py | 34 +++++-- poetry/utils/appdirs.py | 24 +++-- poetry/utils/env.py | 91 +++++++++++-------- poetry/utils/extras.py | 3 +- poetry/utils/helpers.py | 14 ++- poetry/utils/password_manager.py | 39 +++++--- poetry/utils/setup_reader.py | 4 +- poetry/utils/shell.py | 7 +- poetry/version/version_selector.py | 16 +++- 88 files changed, 859 insertions(+), 429 deletions(-) diff --git a/poetry/config/config.py b/poetry/config/config.py index 6be9457cffe..79c7a75e95a 100644 --- a/poetry/config/config.py +++ b/poetry/config/config.py @@ -19,11 +19,11 @@ _NOT_SET = object() -def boolean_validator(val): +def boolean_validator(val): # type: (str) -> bool return val in {"true", "false", "1", "0"} -def boolean_normalizer(val): +def boolean_normalizer(val): # type: (str) -> bool return val in ["true", "1"] @@ -51,11 +51,11 @@ def __init__( self._auth_config_source = DictConfigSource() @property - def name(self): + def name(self): # type: () -> str return str(self._file.path) @property - def config(self): + def config(self): # type: () -> Dict return self._config @property @@ -82,7 +82,7 @@ def merge(self, config): # type: (Dict[str, Any]) -> None merge_dicts(self._config, config) def all(self): # type: () -> Dict[str, Any] - def _all(config, parent_key=""): + def _all(config, parent_key=""): # type: (Dict, str) -> Dict all_ = {} for key in config: diff --git a/poetry/config/file_config_source.py b/poetry/config/file_config_source.py index ed4e3a8522e..3e7cf71a5d9 100644 --- a/poetry/config/file_config_source.py +++ b/poetry/config/file_config_source.py @@ -1,6 +1,7 @@ from contextlib import contextmanager from typing import TYPE_CHECKING from typing import Any +from typing import Generator from tomlkit import document from tomlkit import table @@ -9,6 +10,8 @@ if TYPE_CHECKING: + from tomlkit.toml_document import TOMLDocument # noqa + from poetry.core.toml.file import TOMLFile # noqa @@ -56,7 +59,7 @@ def remove_property(self, key): # type: (str) -> None current_config = current_config[key] @contextmanager - def secure(self): + def secure(self): # type: () -> Generator["TOMLDocument"] if self.file.exists(): initial_config = self.file.read() config = self.file.read() diff --git a/poetry/console/__init__.py b/poetry/console/__init__.py index c0c25738482..56adf27e2ee 100644 --- a/poetry/console/__init__.py +++ b/poetry/console/__init__.py @@ -1,5 +1,5 @@ from .application import Application -def main(): +def main(): # type: () -> int return Application().run() diff --git a/poetry/console/application.py b/poetry/console/application.py index 027fec85776..1fd9dbc0bc3 100644 --- a/poetry/console/application.py +++ b/poetry/console/application.py @@ -1,5 +1,7 @@ import sys +from typing import TYPE_CHECKING + from cleo import Application as BaseApplication from poetry.__version__ import __version__ @@ -29,8 +31,12 @@ from .config import ApplicationConfig +if TYPE_CHECKING: + from poetry.poetry import Poetry # noqa + + class Application(BaseApplication): - def __init__(self): + def __init__(self): # type: () -> None super(Application, self).__init__( "poetry", __version__, config=ApplicationConfig("poetry", __version__) ) @@ -59,7 +65,7 @@ def __init__(self): self._preliminary_io.error_line("{}\n".format(message)) @property - def poetry(self): + def poetry(self): # type: () -> "Poetry" from pathlib import Path from poetry.factory import Factory diff --git a/poetry/console/commands/about.py b/poetry/console/commands/about.py index a84a2b6fec6..c4415591814 100644 --- a/poetry/console/commands/about.py +++ b/poetry/console/commands/about.py @@ -7,7 +7,7 @@ class AboutCommand(Command): description = "Shows information about Poetry." - def handle(self): + def handle(self): # type: () -> None self.line( """Poetry - Package Management for Python diff --git a/poetry/console/commands/add.py b/poetry/console/commands/add.py index 72ddeb015c1..a3868b4bff8 100644 --- a/poetry/console/commands/add.py +++ b/poetry/console/commands/add.py @@ -68,7 +68,7 @@ class AddCommand(InstallerCommand, InitCommand): loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] - def handle(self): + def handle(self): # type: () -> int from tomlkit import inline_table from poetry.core.semver import parse_constraint diff --git a/poetry/console/commands/build.py b/poetry/console/commands/build.py index 72b7319fcd5..bd938764726 100644 --- a/poetry/console/commands/build.py +++ b/poetry/console/commands/build.py @@ -18,7 +18,7 @@ class BuildCommand(EnvCommand): "poetry.core.masonry.builders.wheel", ] - def handle(self): + def handle(self): # type: () -> None from poetry.core.masonry import Builder fmt = "all" diff --git a/poetry/console/commands/cache/cache.py b/poetry/console/commands/cache/cache.py index 695e27e0af7..ff04fecb041 100644 --- a/poetry/console/commands/cache/cache.py +++ b/poetry/console/commands/cache/cache.py @@ -11,5 +11,5 @@ class CacheCommand(Command): commands = [CacheClearCommand(), CacheListCommand()] - def handle(self): + def handle(self): # type: () -> int return self.call("help", self._config.name) diff --git a/poetry/console/commands/cache/clear.py b/poetry/console/commands/cache/clear.py index 42e71091526..9969ebbaa1c 100644 --- a/poetry/console/commands/cache/clear.py +++ b/poetry/console/commands/cache/clear.py @@ -14,7 +14,7 @@ class CacheClearCommand(Command): arguments = [argument("cache", description="The name of the cache to clear.")] options = [option("all", description="Clear all entries in the cache.")] - def handle(self): + def handle(self): # type: () -> int from cachy import CacheManager from poetry.locations import REPOSITORY_CACHE_DIR diff --git a/poetry/console/commands/cache/list.py b/poetry/console/commands/cache/list.py index 6a030fa2eba..090cba601df 100644 --- a/poetry/console/commands/cache/list.py +++ b/poetry/console/commands/cache/list.py @@ -1,5 +1,7 @@ import os +from typing import Optional + from ..command import Command @@ -8,7 +10,7 @@ class CacheListCommand(Command): name = "list" description = "List Poetry's caches." - def handle(self): + def handle(self): # type: () -> Optional[int] from poetry.locations import REPOSITORY_CACHE_DIR if os.path.exists(str(REPOSITORY_CACHE_DIR)): diff --git a/poetry/console/commands/check.py b/poetry/console/commands/check.py index 72c7ca4d947..62af8379163 100644 --- a/poetry/console/commands/check.py +++ b/poetry/console/commands/check.py @@ -11,7 +11,7 @@ class CheckCommand(Command): name = "check" description = "Checks the validity of the pyproject.toml file." - def handle(self): + def handle(self): # type: () -> int # Load poetry config and display errors, if any poetry_file = Factory.locate(Path.cwd()) config = PyProjectTOML(poetry_file).poetry_config diff --git a/poetry/console/commands/command.py b/poetry/console/commands/command.py index 1e22142341a..a575f46044a 100644 --- a/poetry/console/commands/command.py +++ b/poetry/console/commands/command.py @@ -1,12 +1,18 @@ +from typing import TYPE_CHECKING + from cleo import Command as BaseCommand +if TYPE_CHECKING: + from poetry.poetry import Poetry # noqa + + class Command(BaseCommand): loggers = [] @property - def poetry(self): + def poetry(self): # type: () -> "Poetry" return self.application.poetry def reset_poetry(self): # type: () -> None diff --git a/poetry/console/commands/config.py b/poetry/console/commands/config.py index 310524d4ee2..5b35eb5a26d 100644 --- a/poetry/console/commands/config.py +++ b/poetry/console/commands/config.py @@ -1,6 +1,13 @@ import json import re +from typing import TYPE_CHECKING +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple + from cleo import argument from cleo import option @@ -11,6 +18,10 @@ from .command import Command +if TYPE_CHECKING: + from poetry.config.config_source import ConfigSource # noqa + + class ConfigCommand(Command): name = "config" @@ -40,7 +51,7 @@ class ConfigCommand(Command): LIST_PROHIBITED_SETTINGS = {"http-basic", "pypi-token"} @property - def unique_config_values(self): + def unique_config_values(self): # type: () -> Dict[str, Tuple[Any, Any, Any]] from pathlib import Path from poetry.config.config import boolean_normalizer @@ -75,7 +86,7 @@ def unique_config_values(self): return unique_config_values - def handle(self): + def handle(self): # type: () -> Optional[int] from pathlib import Path from poetry.config.file_config_source import FileConfigSource @@ -253,7 +264,9 @@ def handle(self): raise ValueError("Setting {} does not exist".format(self.argument("key"))) - def _handle_single_value(self, source, key, callbacks, values): + def _handle_single_value( + self, source, key, callbacks, values + ): # type: ("ConfigSource", str, Tuple[Any, Any, Any], List[Any]) -> int validator, normalizer, _ = callbacks if len(values) > 1: @@ -267,7 +280,7 @@ def _handle_single_value(self, source, key, callbacks, values): return 0 - def _list_configuration(self, config, raw, k=""): + def _list_configuration(self, config, raw, k=""): # type: (Dict, Dict, str) -> None orig_k = k for key, value in sorted(config.items()): if k + key in self.LIST_PROHIBITED_SETTINGS: @@ -301,7 +314,9 @@ def _list_configuration(self, config, raw, k=""): self.line(message) - def _get_setting(self, contents, setting=None, k=None, default=None): + def _get_setting( + self, contents, setting=None, k=None, default=None + ): # type: (Dict, Optional[str], Optional[str], Optional[Any]) -> List[Tuple[str, str]] orig_k = k if setting and setting.split(".")[0] not in contents: diff --git a/poetry/console/commands/debug/info.py b/poetry/console/commands/debug/info.py index 81096a6ffc8..8950a3e5710 100644 --- a/poetry/console/commands/debug/info.py +++ b/poetry/console/commands/debug/info.py @@ -10,7 +10,7 @@ class DebugInfoCommand(Command): name = "info" description = "Shows debug information." - def handle(self): + def handle(self): # type: () -> int poetry_python_version = ".".join(str(s) for s in sys.version_info[:3]) self.line("") diff --git a/poetry/console/commands/debug/resolve.py b/poetry/console/commands/debug/resolve.py index 52ae1951b21..743ef1d7443 100644 --- a/poetry/console/commands/debug/resolve.py +++ b/poetry/console/commands/debug/resolve.py @@ -1,3 +1,5 @@ +from typing import Optional + from cleo import argument from cleo import option @@ -27,7 +29,7 @@ class DebugResolveCommand(InitCommand): loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] - def handle(self): + def handle(self): # type: () -> Optional[int] from poetry.core.packages.project_package import ProjectPackage from poetry.factory import Factory from poetry.io.null_io import NullIO diff --git a/poetry/console/commands/env/info.py b/poetry/console/commands/env/info.py index 301d88f9520..fc0e4a75936 100644 --- a/poetry/console/commands/env/info.py +++ b/poetry/console/commands/env/info.py @@ -1,8 +1,15 @@ +from typing import TYPE_CHECKING +from typing import Optional + from cleo import option from ..command import Command +if TYPE_CHECKING: + from poetry.utils.env import Env # noqa + + class EnvInfoCommand(Command): name = "info" @@ -10,7 +17,7 @@ class EnvInfoCommand(Command): options = [option("path", "p", "Only display the environment's path.")] - def handle(self): + def handle(self): # type: () -> Optional[int] from poetry.utils.env import EnvManager env = EnvManager(self.poetry).get() @@ -25,7 +32,7 @@ def handle(self): self._display_complete_info(env) - def _display_complete_info(self, env): + def _display_complete_info(self, env): # type: ("Env") -> None env_python_version = ".".join(str(s) for s in env.version_info[:3]) self.line("") self.line("Virtualenv") diff --git a/poetry/console/commands/env/list.py b/poetry/console/commands/env/list.py index 272a853b976..46423d142c7 100644 --- a/poetry/console/commands/env/list.py +++ b/poetry/console/commands/env/list.py @@ -10,7 +10,7 @@ class EnvListCommand(Command): options = [option("full-path", None, "Output the full paths of the virtualenvs.")] - def handle(self): + def handle(self): # type: () -> None from poetry.utils.env import EnvManager manager = EnvManager(self.poetry) diff --git a/poetry/console/commands/env/remove.py b/poetry/console/commands/env/remove.py index 5f208851deb..4b4bca29c9d 100644 --- a/poetry/console/commands/env/remove.py +++ b/poetry/console/commands/env/remove.py @@ -12,7 +12,7 @@ class EnvRemoveCommand(Command): argument("python", "The python executable to remove the virtualenv for.") ] - def handle(self): + def handle(self): # type: () -> None from poetry.utils.env import EnvManager manager = EnvManager(self.poetry) diff --git a/poetry/console/commands/env/use.py b/poetry/console/commands/env/use.py index ef9cf3def6b..57db24c97bd 100644 --- a/poetry/console/commands/env/use.py +++ b/poetry/console/commands/env/use.py @@ -10,7 +10,7 @@ class EnvUseCommand(Command): arguments = [argument("python", "The python executable to use.")] - def handle(self): + def handle(self): # type: () -> None from poetry.utils.env import EnvManager manager = EnvManager(self.poetry) diff --git a/poetry/console/commands/env_command.py b/poetry/console/commands/env_command.py index 2fb298d7bb2..a7ae5e7bdda 100644 --- a/poetry/console/commands/env_command.py +++ b/poetry/console/commands/env_command.py @@ -1,15 +1,21 @@ +from typing import TYPE_CHECKING + from .command import Command +if TYPE_CHECKING: + from poetry.utils.env import VirtualEnv # noqa + + class EnvCommand(Command): - def __init__(self): + def __init__(self): # type: () -> None self._env = None super(EnvCommand, self).__init__() @property - def env(self): + def env(self): # type: () -> "VirtualEnv" return self._env - def set_env(self, env): + def set_env(self, env): # type: ("VirtualEnv") -> None self._env = env diff --git a/poetry/console/commands/export.py b/poetry/console/commands/export.py index 126b657b937..11ef2a37377 100644 --- a/poetry/console/commands/export.py +++ b/poetry/console/commands/export.py @@ -31,7 +31,7 @@ class ExportCommand(Command): option("with-credentials", None, "Include credentials for extra indices."), ] - def handle(self): + def handle(self): # type: () -> None fmt = self.option("format") if fmt not in Exporter.ACCEPTED_FORMATS: diff --git a/poetry/console/commands/init.py b/poetry/console/commands/init.py index ef56484abd1..92870cb614a 100644 --- a/poetry/console/commands/init.py +++ b/poetry/console/commands/init.py @@ -9,6 +9,7 @@ from pathlib import Path from typing import Dict from typing import List +from typing import Optional from typing import Tuple from typing import Union @@ -56,12 +57,12 @@ class InitCommand(Command): The init command creates a basic pyproject.toml file in the current directory. """ - def __init__(self): + def __init__(self): # type: () -> None super(InitCommand, self).__init__() self._pool = None - def handle(self): + def handle(self): # type: () -> int from pathlib import Path from poetry.core.vcs.git import GitConfig @@ -227,7 +228,7 @@ def handle(self): def _determine_requirements( self, requires, allow_prereleases=False, source=None - ): # type: (List[str], bool) -> List[Dict[str, str]] + ): # type: (List[str], bool, Optional[str]) -> List[Dict[str, Union[str, List[str]]]] if not requires: requires = [] @@ -354,7 +355,7 @@ def _determine_requirements( def _find_best_version_for_package( self, name, required_version=None, allow_prereleases=False, source=None - ): # type: (...) -> Tuple[str, str] + ): # type: (str, Optional[str], bool, Optional[str]) -> Tuple[str, str] from poetry.version.version_selector import VersionSelector selector = VersionSelector(self._get_pool()) @@ -505,7 +506,7 @@ def _format_requirements( return requires - def _validate_author(self, author, default): + def _validate_author(self, author, default): # type: (str, str) -> Optional[str] from poetry.core.packages.package import AUTHOR_REGEX author = author or default @@ -522,7 +523,7 @@ def _validate_author(self, author, default): return author - def _validate_license(self, license): + def _validate_license(self, license): # type: (str) -> str from poetry.core.spdx import license_by_id if license: @@ -530,7 +531,7 @@ def _validate_license(self, license): return license - def _get_pool(self): + def _get_pool(self): # type: () -> "Pool" from poetry.repositories import Pool from poetry.repositories.pypi_repository import PyPiRepository diff --git a/poetry/console/commands/install.py b/poetry/console/commands/install.py index 6a9ef2cb41d..96e2dca0ad6 100644 --- a/poetry/console/commands/install.py +++ b/poetry/console/commands/install.py @@ -47,7 +47,7 @@ class InstallCommand(InstallerCommand): _loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] - def handle(self): + def handle(self): # type: () -> int from poetry.core.masonry.utils.module import ModuleOrPackageNotFound from poetry.masonry.builders import EditableBuilder diff --git a/poetry/console/commands/installer_command.py b/poetry/console/commands/installer_command.py index 51647eff471..dd8ca1a8842 100644 --- a/poetry/console/commands/installer_command.py +++ b/poetry/console/commands/installer_command.py @@ -9,12 +9,12 @@ class InstallerCommand(EnvCommand): - def __init__(self): + def __init__(self): # type: () -> None self._installer = None # type: Optional[Installer] super(InstallerCommand, self).__init__() - def reset_poetry(self): + def reset_poetry(self): # type: () -> None super(InstallerCommand, self).reset_poetry() self._installer.set_package(self.poetry.package) diff --git a/poetry/console/commands/lock.py b/poetry/console/commands/lock.py index 4157c02c5cc..59beb1386e2 100644 --- a/poetry/console/commands/lock.py +++ b/poetry/console/commands/lock.py @@ -24,7 +24,7 @@ class LockCommand(InstallerCommand): loggers = ["poetry.repositories.pypi_repository"] - def handle(self): + def handle(self): # type: () -> int self._installer.use_executor( self.poetry.config.get("experimental.new-installer", False) ) diff --git a/poetry/console/commands/new.py b/poetry/console/commands/new.py index 4856ff69c96..8d709fb6c4a 100644 --- a/poetry/console/commands/new.py +++ b/poetry/console/commands/new.py @@ -19,7 +19,7 @@ class NewCommand(Command): option("src", None, "Use the src layout for the project."), ] - def handle(self): + def handle(self): # type: () -> None from pathlib import Path from poetry.core.semver import parse_constraint diff --git a/poetry/console/commands/publish.py b/poetry/console/commands/publish.py index 98d4165fd8d..cedbca85468 100644 --- a/poetry/console/commands/publish.py +++ b/poetry/console/commands/publish.py @@ -1,4 +1,5 @@ from pathlib import Path +from typing import Optional from cleo import option @@ -40,7 +41,7 @@ class PublishCommand(Command): loggers = ["poetry.masonry.publishing.publisher"] - def handle(self): + def handle(self): # type: () -> Optional[int] from poetry.publishing.publisher import Publisher publisher = Publisher(self.poetry, self.io) diff --git a/poetry/console/commands/remove.py b/poetry/console/commands/remove.py index d9a289cba7f..3fb42713ced 100644 --- a/poetry/console/commands/remove.py +++ b/poetry/console/commands/remove.py @@ -27,7 +27,7 @@ class RemoveCommand(InstallerCommand): loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] - def handle(self): + def handle(self): # type: () -> int packages = self.argument("packages") is_dev = self.option("dev") diff --git a/poetry/console/commands/run.py b/poetry/console/commands/run.py index 46202c52c37..bafa5ce2cea 100644 --- a/poetry/console/commands/run.py +++ b/poetry/console/commands/run.py @@ -1,3 +1,6 @@ +from typing import Any +from typing import Union + from cleo import argument from .env_command import EnvCommand @@ -19,7 +22,7 @@ def __init__(self): # type: () -> None self.config.set_args_parser(RunArgsParser()) - def handle(self): + def handle(self): # type: () -> Any args = self.argument("args") script = args[0] scripts = self.poetry.local_config.get("scripts") @@ -29,7 +32,7 @@ def handle(self): return self.env.execute(*args) - def run_script(self, script, args): + def run_script(self, script, args): # type: (Union[str, dict], str) -> Any if isinstance(script, dict): script = script["callable"] diff --git a/poetry/console/commands/search.py b/poetry/console/commands/search.py index 299dee6a96a..3ca8ac8e94d 100644 --- a/poetry/console/commands/search.py +++ b/poetry/console/commands/search.py @@ -10,7 +10,7 @@ class SearchCommand(Command): arguments = [argument("tokens", "The tokens to search for.", multiple=True)] - def handle(self): + def handle(self): # type: () -> None from poetry.repositories.pypi_repository import PyPiRepository results = PyPiRepository().search(self.argument("tokens")) diff --git a/poetry/console/commands/self/self.py b/poetry/console/commands/self/self.py index 3e5cafa9180..29a0214c230 100644 --- a/poetry/console/commands/self/self.py +++ b/poetry/console/commands/self/self.py @@ -9,5 +9,5 @@ class SelfCommand(Command): commands = [SelfUpdateCommand()] - def handle(self): + def handle(self): # type: () -> int return self.call("help", self._config.name) diff --git a/poetry/console/commands/self/update.py b/poetry/console/commands/self/update.py index 7d477ddce16..279de274188 100644 --- a/poetry/console/commands/self/update.py +++ b/poetry/console/commands/self/update.py @@ -11,6 +11,8 @@ from functools import cmp_to_key from gzip import GzipFile +from typing import TYPE_CHECKING +from typing import Any from cleo import argument from cleo import option @@ -20,6 +22,12 @@ from ..command import Command +if TYPE_CHECKING: + from poetry.core.packages import Package # noqa + from poetry.core.semver import Version + from poetry.utils._compat import Path + + try: from urllib.error import HTTPError from urllib.request import urlopen @@ -61,24 +69,24 @@ class SelfUpdateCommand(Command): BASE_URL = REPOSITORY_URL + "/releases/download" @property - def home(self): + def home(self): # type: () -> Path from pathlib import Path return Path(os.environ.get("POETRY_HOME", "~/.poetry")).expanduser() @property - def bin(self): + def bin(self): # type: () -> Path return self.home / "bin" @property - def lib(self): + def lib(self): # type: () -> Path return self.home / "lib" @property - def lib_backup(self): + def lib_backup(self): # type: () -> Path return self.home / "lib-backup" - def handle(self): + def handle(self): # type: () -> None from poetry.__version__ import __version__ from poetry.core.semver import Version from poetry.repositories.pypi_repository import PyPiRepository @@ -129,7 +137,7 @@ def handle(self): self.update(release) - def update(self, release): + def update(self, release): # type: ("Package") -> None version = release.version self.line("Updating to {}".format(version)) @@ -165,7 +173,7 @@ def update(self, release): ) ) - def _update(self, version): + def _update(self, version): # type: ("Version") -> None from poetry.utils.helpers import temporary_directory release_name = self._get_release_name(version) @@ -235,10 +243,10 @@ def _update(self, version): finally: gz.close() - def process(self, *args): + def process(self, *args): # type: (*Any) -> str return subprocess.check_output(list(args), stderr=subprocess.STDOUT) - def _check_recommended_installation(self): + def _check_recommended_installation(self): # type: () -> None from pathlib import Path current = Path(__file__) @@ -250,14 +258,14 @@ def _check_recommended_installation(self): "Cannot update automatically." ) - def _get_release_name(self, version): + def _get_release_name(self, version): # type: ("Version") -> str platform = sys.platform if platform == "linux2": platform = "linux" return "poetry-{}-{}".format(version, platform) - def make_bin(self): + def make_bin(self): # type: () -> None from poetry.utils._compat import WINDOWS self.bin.mkdir(0o755, parents=True, exist_ok=True) @@ -286,7 +294,7 @@ def make_bin(self): st = os.stat(str(self.bin.joinpath("poetry"))) os.chmod(str(self.bin.joinpath("poetry")), st.st_mode | stat.S_IEXEC) - def _which_python(self): + def _which_python(self): # type: () -> str """ Decides which python executable we'll embed in the launcher script. """ diff --git a/poetry/console/commands/shell.py b/poetry/console/commands/shell.py index 033ab207e31..35269d38d3d 100644 --- a/poetry/console/commands/shell.py +++ b/poetry/console/commands/shell.py @@ -16,7 +16,7 @@ class ShellCommand(EnvCommand): If one doesn't exist yet, it will be created. """ - def handle(self): + def handle(self): # type: () -> None from poetry.utils.shell import Shell # Check if it's already activated or doesn't exist and won't be created diff --git a/poetry/console/commands/show.py b/poetry/console/commands/show.py index 86be1ae7a36..545717be8ee 100644 --- a/poetry/console/commands/show.py +++ b/poetry/console/commands/show.py @@ -1,10 +1,24 @@ # -*- coding: utf-8 -*- +from typing import TYPE_CHECKING +from typing import List +from typing import Optional +from typing import Union + from cleo import argument from cleo import option from .env_command import EnvCommand +if TYPE_CHECKING: + from clikit.api.io import IO # noqa + + from poetry.core.packages import Dependency # noqa + from poetry.core.packages import Package # noqa + from poetry.repositories import Repository + from poetry.repositories.installed_repository import InstalledRepository + + class ShowCommand(EnvCommand): name = "show" @@ -32,7 +46,7 @@ class ShowCommand(EnvCommand): colors = ["cyan", "yellow", "green", "magenta", "blue"] - def handle(self): + def handle(self): # type: () -> Optional[int] from clikit.utils.terminal import Terminal from poetry.io.null_io import NullIO @@ -257,7 +271,9 @@ def handle(self): self.line(line) - def display_package_tree(self, io, package, installed_repo): + def display_package_tree( + self, io, package, installed_repo + ): # type: ("IO", "Package", "Repository") -> None io.write("{}".format(package.pretty_name)) description = "" if package.description: @@ -294,13 +310,13 @@ def display_package_tree(self, io, package, installed_repo): def _display_tree( self, - io, - dependency, - installed_repo, - packages_in_tree, - previous_tree_bar="├", - level=1, - ): + io, # type: "IO" + dependency, # type: "Dependency" + installed_repo, # type: "Repository" + packages_in_tree, # type: List[str] + previous_tree_bar="├", # type: str + level=1, # type: int + ): # type: (...) -> None previous_tree_bar = previous_tree_bar.replace("├", "│") dependencies = [] @@ -345,7 +361,7 @@ def _display_tree( io, dependency, installed_repo, current_tree, tree_bar, level + 1 ) - def _write_tree_line(self, io, line): + def _write_tree_line(self, io, line): # type: ("IO", str) -> None if not io.output.supports_ansi(): line = line.replace("└", "`-") line = line.replace("├", "|-") @@ -354,7 +370,7 @@ def _write_tree_line(self, io, line): io.write_line(line) - def init_styles(self, io): + def init_styles(self, io): # type: ("IO") -> None from clikit.api.formatter import Style for color in self.colors: @@ -362,7 +378,9 @@ def init_styles(self, io): io.output.formatter.add_style(style) io.error_output.formatter.add_style(style) - def find_latest_package(self, package, include_dev): + def find_latest_package( + self, package, include_dev + ): # type: ("Package", bool) -> Union["Package", bool] from clikit.io import NullIO from poetry.puzzle.provider import Provider @@ -390,7 +408,7 @@ def find_latest_package(self, package, include_dev): return selector.find_best_candidate(name, ">={}".format(package.pretty_version)) - def get_update_status(self, latest, package): + def get_update_status(self, latest, package): # type: ("Package", "Package") -> str from poetry.core.semver import parse_constraint if latest.full_pretty_version == package.full_pretty_version: @@ -405,7 +423,9 @@ def get_update_status(self, latest, package): # it needs an upgrade but has potential BC breaks so is not urgent return "update-possible" - def get_installed_status(self, locked, installed_repo): + def get_installed_status( + self, locked, installed_repo + ): # type: ("Package", "InstalledRepository") -> str for package in installed_repo.packages: if locked.name == package.name: return "installed" diff --git a/poetry/console/commands/update.py b/poetry/console/commands/update.py index 9e18feb78b6..77c0adf52cc 100644 --- a/poetry/console/commands/update.py +++ b/poetry/console/commands/update.py @@ -27,7 +27,7 @@ class UpdateCommand(InstallerCommand): loggers = ["poetry.repositories.pypi_repository"] - def handle(self): + def handle(self): # type: () -> int packages = self.argument("packages") self._installer.use_executor( diff --git a/poetry/console/commands/version.py b/poetry/console/commands/version.py index 5ac5c666afb..403c357390a 100644 --- a/poetry/console/commands/version.py +++ b/poetry/console/commands/version.py @@ -40,7 +40,7 @@ class VersionCommand(Command): "prerelease", } - def handle(self): + def handle(self): # type: () -> None version = self.argument("version") if version: @@ -72,7 +72,7 @@ def handle(self): ) ) - def increment_version(self, version, rule): + def increment_version(self, version, rule): # type: (str, str) -> "Version" from poetry.core.semver import Version try: diff --git a/poetry/console/config/application_config.py b/poetry/console/config/application_config.py index 492a2137259..563e6b55db0 100644 --- a/poetry/console/config/application_config.py +++ b/poetry/console/config/application_config.py @@ -34,7 +34,7 @@ class ApplicationConfig(BaseApplicationConfig): - def configure(self): + def configure(self): # type: () -> None super(ApplicationConfig, self).configure() self.add_style(Style("c1").fg("cyan")) diff --git a/poetry/console/logging/io_formatter.py b/poetry/console/logging/io_formatter.py index 9ff57fec761..68d15691edd 100644 --- a/poetry/console/logging/io_formatter.py +++ b/poetry/console/logging/io_formatter.py @@ -1,8 +1,14 @@ import logging +from typing import TYPE_CHECKING + from .formatters import FORMATTERS +if TYPE_CHECKING: + from logging import LogRecord # noqa + + class IOFormatter(logging.Formatter): _colors = { @@ -12,7 +18,7 @@ class IOFormatter(logging.Formatter): "info": "fg=blue", } - def format(self, record): + def format(self, record): # type: ("LogRecord") -> str if not record.exc_info: level = record.levelname.lower() msg = record.msg diff --git a/poetry/console/logging/io_handler.py b/poetry/console/logging/io_handler.py index 14fd176986c..03d9607c8e5 100644 --- a/poetry/console/logging/io_handler.py +++ b/poetry/console/logging/io_handler.py @@ -1,13 +1,21 @@ import logging +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from logging import LogRecord # noqa + + from clikit.api.io import IO # noqa + class IOHandler(logging.Handler): - def __init__(self, io): + def __init__(self, io): # type: ("IO") -> None self._io = io super(IOHandler, self).__init__() - def emit(self, record): + def emit(self, record): # type: ("LogRecord") -> None try: msg = self.format(record) level = record.levelname.lower() diff --git a/poetry/factory.py b/poetry/factory.py index b38f6da25ae..0e3ef0f6046 100644 --- a/poetry/factory.py +++ b/poetry/factory.py @@ -137,7 +137,7 @@ def create_config(cls, io=None): # type: (Optional[IO]) -> Config def create_legacy_repository( self, source, auth_config - ): # type: (Dict[str, str], Config) -> LegacyRepository + ): # type: (Dict[str, str], Config) -> "LegacyRepository" from .repositories.legacy_repository import LegacyRepository from .utils.helpers import get_cert from .utils.helpers import get_client_cert diff --git a/poetry/inspection/info.py b/poetry/inspection/info.py index 94ac097aeac..495a62ca3c0 100644 --- a/poetry/inspection/info.py +++ b/poetry/inspection/info.py @@ -120,7 +120,7 @@ def load( return cls(cache_version=cache_version, **data) @classmethod - def _log(cls, msg, level="info"): + def _log(cls, msg, level="info"): # type: (str, str) -> None """Internal helper method to log information.""" getattr(logger, level)("{}: {}".format(cls.__name__, msg)) @@ -436,7 +436,7 @@ def _get_poetry_package(path): # type: (Path) -> Optional[ProjectPackage] return Factory().create_poetry(path).package @classmethod - def _pep517_metadata(cls, path): # type (Path) -> PackageInfo + def _pep517_metadata(cls, path): # type: (Path) -> PackageInfo """ Helper method to use PEP-517 library to build and read package metadata. diff --git a/poetry/installation/base_installer.py b/poetry/installation/base_installer.py index 1e068d076cd..4f600e880d4 100644 --- a/poetry/installation/base_installer.py +++ b/poetry/installation/base_installer.py @@ -1,9 +1,16 @@ +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from poetry.core.packages import Package # noqa + + class BaseInstaller: - def install(self, package): + def install(self, package): # type: ("Package") -> None raise NotImplementedError - def update(self, source, target): + def update(self, source, target): # type: ("Package", "Package") -> None raise NotImplementedError - def remove(self, package): + def remove(self, package): # type: ("Package") -> None raise NotImplementedError diff --git a/poetry/installation/chooser.py b/poetry/installation/chooser.py index 6d9e92e0b1f..1762f2ce546 100644 --- a/poetry/installation/chooser.py +++ b/poetry/installation/chooser.py @@ -1,6 +1,7 @@ import re from typing import List +from typing import Optional from typing import Tuple from packaging.tags import Tag @@ -34,12 +35,12 @@ def __init__(self, filename): # type: (str) -> None Tag(x, y, z) for x in self.pyversions for y in self.abis for z in self.plats } - def get_minimum_supported_index(self, tags): + def get_minimum_supported_index(self, tags): # type: (List[Tag]) -> Optional[int] indexes = [tags.index(t) for t in self.tags if t in tags] return min(indexes) if indexes else None - def is_supported_by_environment(self, env): + def is_supported_by_environment(self, env): # type: (Env) -> bool return bool(set(env.supported_tags).intersection(self.tags)) diff --git a/poetry/installation/executor.py b/poetry/installation/executor.py index 5523ed2813f..8123c2d6956 100644 --- a/poetry/installation/executor.py +++ b/poetry/installation/executor.py @@ -9,6 +9,10 @@ from concurrent.futures import wait from pathlib import Path from subprocess import CalledProcessError +from typing import TYPE_CHECKING +from typing import Any +from typing import List +from typing import Union from poetry.core.packages.file_dependency import FileDependency from poetry.core.packages.utils.link import Link @@ -27,8 +31,20 @@ from .operations.update import Update +if TYPE_CHECKING: + from clikit.api.io import IO # noqa + + from poetry.config.config import Config # noqa + from poetry.repositories import Pool # noqa + from poetry.utils.env import Env # noqa + + from .operations import OperationTypes # noqa + + class Executor(object): - def __init__(self, env, pool, config, io, parallel=None): + def __init__( + self, env, pool, config, io, parallel=None + ): # type: ("Env", "Pool", "Config", "IO", bool) -> None self._env = env self._io = io self._dry_run = False @@ -77,22 +93,22 @@ def removals_count(self): # type: () -> int def supports_fancy_output(self): # type: () -> bool return self._io.supports_ansi() and not self._dry_run - def disable(self): + def disable(self): # type: () -> "Executor" self._enabled = False return self - def dry_run(self, dry_run=True): + def dry_run(self, dry_run=True): # type: (bool) -> Executor self._dry_run = dry_run return self - def verbose(self, verbose=True): + def verbose(self, verbose=True): # type: (bool) -> Executor self._verbose = verbose return self - def execute(self, operations): # type: (Operation) -> int + def execute(self, operations): # type: (List["OperationTypes"]) -> int self._total_operations = len(operations) for job_type in self._executed: self._executed[job_type] = 0 @@ -145,7 +161,7 @@ def execute(self, operations): # type: (Operation) -> int return 1 if self._shutdown else 0 - def _write(self, operation, line): + def _write(self, operation, line): # type: ("OperationTypes", str) -> None if not self.supports_fancy_output() or not self._should_write_operation( operation ): @@ -163,7 +179,7 @@ def _write(self, operation, line): section.output.clear() section.write(line) - def _execute_operation(self, operation): + def _execute_operation(self, operation): # type: ("OperationTypes") -> None try: if self.supports_fancy_output(): if id(operation) not in self._sections: @@ -240,7 +256,7 @@ def _execute_operation(self, operation): with self._lock: self._shutdown = True - def _do_execute_operation(self, operation): + def _do_execute_operation(self, operation): # type: ("OperationTypes") -> int method = operation.job_type operation_message = self.get_operation_message(operation) @@ -283,7 +299,9 @@ def _do_execute_operation(self, operation): return result - def _increment_operations_count(self, operation, executed): + def _increment_operations_count( + self, operation, executed + ): # type: ("OperationTypes", bool) -> None with self._lock: if executed: self._executed_operations += 1 @@ -291,7 +309,7 @@ def _increment_operations_count(self, operation, executed): else: self._skipped[operation.job_type] += 1 - def run_pip(self, *args, **kwargs): # type: (...) -> int + def run_pip(self, *args, **kwargs): # type: (*Any, **Any) -> int try: self._env.run_pip(*args, **kwargs) except EnvCommandError as e: @@ -306,7 +324,9 @@ def run_pip(self, *args, **kwargs): # type: (...) -> int return 0 - def get_operation_message(self, operation, done=False, error=False, warning=False): + def get_operation_message( + self, operation, done=False, error=False, warning=False + ): # type: ("OperationTypes", bool, bool, bool) -> str base_tag = "fg=default" operation_color = "c2" source_operation_color = "c2" @@ -360,7 +380,7 @@ def get_operation_message(self, operation, done=False, error=False, warning=Fals return "" - def _display_summary(self, operations): + def _display_summary(self, operations): # type: (List["OperationTypes"]) -> None installs = 0 updates = 0 uninstalls = 0 @@ -403,13 +423,13 @@ def _display_summary(self, operations): ) self._io.write_line("") - def _execute_install(self, operation): # type: (Install) -> None + def _execute_install(self, operation): # type: (Union[Install, Update]) -> int return self._install(operation) - def _execute_update(self, operation): # type: (Update) -> None + def _execute_update(self, operation): # type: (Union[Install, Update]) -> int return self._update(operation) - def _execute_uninstall(self, operation): # type: (Uninstall) -> None + def _execute_uninstall(self, operation): # type: (Uninstall) -> int message = " • {message}: Removing...".format( message=self.get_operation_message(operation), ) @@ -417,7 +437,7 @@ def _execute_uninstall(self, operation): # type: (Uninstall) -> None return self._remove(operation) - def _install(self, operation): + def _install(self, operation): # type: (Union[Install, Update]) -> int package = operation.package if package.source_type == "directory": return self._install_directory(operation) @@ -444,10 +464,10 @@ def _install(self, operation): return self.run_pip(*args) - def _update(self, operation): + def _update(self, operation): # type: (Union[Install, Update]) -> int return self._install(operation) - def _remove(self, operation): + def _remove(self, operation): # type: (Uninstall) -> int package = operation.package # If we have a VCS package, remove its source directory @@ -464,7 +484,7 @@ def _remove(self, operation): raise - def _prepare_file(self, operation): + def _prepare_file(self, operation): # type: (Union[Install, Update]) -> Path package = operation.package message = " • {message}: Preparing...".format( @@ -480,7 +500,7 @@ def _prepare_file(self, operation): return archive - def _install_directory(self, operation): + def _install_directory(self, operation): # type: (Union[Install, Update]) -> int from poetry.factory import Factory package = operation.package @@ -544,7 +564,7 @@ def _install_directory(self, operation): return self.run_pip(*args) - def _install_git(self, operation): + def _install_git(self, operation): # type: (Union[Install, Update]) -> int from poetry.core.vcs import Git package = operation.package @@ -570,12 +590,14 @@ def _install_git(self, operation): return self._install_directory(operation) - def _download(self, operation): # type: (Operation) -> Path + def _download(self, operation): # type: (Union[Install, Update]) -> Link link = self._chooser.choose_for(operation.package) return self._download_link(operation, link) - def _download_link(self, operation, link): + def _download_link( + self, operation, link + ): # type: (Union[Install, Update], Link) -> Link package = operation.package archive = self._chef.get_cached_archive_for_link(link) @@ -607,7 +629,9 @@ def _download_link(self, operation, link): return archive - def _download_archive(self, operation, link): # type: (Operation, Link) -> Path + def _download_archive( + self, operation, link + ): # type: (Union[Install, Update], Link) -> Path response = self._authenticator.request( "get", link.url, stream=True, io=self._sections.get(id(operation), self._io) ) diff --git a/poetry/installation/installer.py b/poetry/installation/installer.py index 2164d39f4b1..f7eea2cdcd0 100644 --- a/poetry/installation/installer.py +++ b/poetry/installation/installer.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING +from typing import Iterable from typing import List from typing import Optional from typing import Union @@ -23,11 +25,17 @@ from .pip_installer import PipInstaller +if TYPE_CHECKING: + from poetry.utils.env import Env # noqa + + from .operations import OperationTypes # noqa + + class Installer: def __init__( self, io, # type: IO - env, + env, # type: "Env" package, # type: ProjectPackage locker, # type: Locker pool, # type: Pool @@ -67,11 +75,11 @@ def __init__( self._installed_repository = installed @property - def executor(self): + def executor(self): # type: () -> Executor return self._executor @property - def installer(self): + def installer(self): # type: () -> BaseInstaller return self._installer def set_package(self, package): # type: (ProjectPackage) -> Installer @@ -84,7 +92,7 @@ def set_locker(self, locker): # type: (Locker) -> Installer return self - def run(self): + def run(self): # type: () -> int # Check if refresh if not self._update and self._lock and self._locker.is_locked(): return self._do_refresh() @@ -162,7 +170,7 @@ def execute_operations(self, execute=True): # type: (bool) -> Installer return self - def whitelist(self, packages): # type: (dict) -> Installer + def whitelist(self, packages): # type: (Iterable[str]) -> Installer self._whitelist = [canonicalize_name(p) for p in packages] return self @@ -177,7 +185,7 @@ def use_executor(self, use_executor=True): # type: (bool) -> Installer return self - def _do_refresh(self): + def _do_refresh(self): # type: () -> int from poetry.puzzle import Solver # Checking extras @@ -203,7 +211,7 @@ def _do_refresh(self): return 0 - def _do_install(self, local_repo): + def _do_install(self, local_repo): # type: (Repository) -> int from poetry.puzzle import Solver locked_repository = Repository() @@ -323,7 +331,7 @@ def _write_lock_file(self, repo, force=True): # type: (Repository, bool) -> Non self._io.write_line("") self._io.write_line("Writing lock file") - def _execute(self, operations): + def _execute(self, operations): # type: (List["OperationTypes"]) -> int if self._use_executor: return self._executor.execute(operations) @@ -459,7 +467,9 @@ def _execute_uninstall(self, operation): # type: (Uninstall) -> None self._installer.remove(operation.package) - def _populate_local_repo(self, local_repo, ops): + def _populate_local_repo( + self, local_repo, ops + ): # type: (Repository, List[Operation]) -> None for op in ops: if isinstance(op, Uninstall): continue @@ -472,8 +482,8 @@ def _populate_local_repo(self, local_repo, ops): local_repo.add_package(package) def _get_operations_from_lock( - self, locked_repository # type: Repository - ): # type: (...) -> List[Operation] + self, locked_repository + ): # type: (Repository) -> List[Operation] installed_repo = self._installed_repository ops = [] diff --git a/poetry/installation/noop_installer.py b/poetry/installation/noop_installer.py index 0f0c6cda007..8b39543671f 100644 --- a/poetry/installation/noop_installer.py +++ b/poetry/installation/noop_installer.py @@ -1,29 +1,36 @@ +from typing import TYPE_CHECKING +from typing import List + from .base_installer import BaseInstaller +if TYPE_CHECKING: + from poetry.core.packages import Package # noqa + + class NoopInstaller(BaseInstaller): - def __init__(self): + def __init__(self): # type: () -> None self._installs = [] self._updates = [] self._removals = [] @property - def installs(self): + def installs(self): # type: () -> List["Package"] return self._installs @property - def updates(self): + def updates(self): # type: () -> List["Package"] return self._updates @property - def removals(self): + def removals(self): # type: () -> List["Package"] return self._removals - def install(self, package): + def install(self, package): # type: ("Package") -> None self._installs.append(package) - def update(self, source, target): + def update(self, source, target): # type: ("Package", "Package") -> None self._updates.append((source, target)) - def remove(self, package): + def remove(self, package): # type: ("Package") -> None self._removals.append(package) diff --git a/poetry/installation/operations/__init__.py b/poetry/installation/operations/__init__.py index 42573c10e8e..d7b27fe2a20 100644 --- a/poetry/installation/operations/__init__.py +++ b/poetry/installation/operations/__init__.py @@ -1,3 +1,8 @@ +from typing import Union + from .install import Install from .uninstall import Uninstall from .update import Update + + +OperationTypes = Union[Install, Uninstall, Update] diff --git a/poetry/installation/operations/install.py b/poetry/installation/operations/install.py index 48097c7c6ce..381f8f48f30 100644 --- a/poetry/installation/operations/install.py +++ b/poetry/installation/operations/install.py @@ -1,26 +1,35 @@ +from typing import TYPE_CHECKING +from typing import Optional + from .operation import Operation +if TYPE_CHECKING: + from poetry.core.packages import Package # noqa + + class Install(Operation): - def __init__(self, package, reason=None, priority=0): + def __init__( + self, package, reason=None, priority=0 + ): # type: ("Package", Optional[str], int) -> None super(Install, self).__init__(reason, priority=priority) self._package = package @property - def package(self): + def package(self): # type: () -> "Package" return self._package @property - def job_type(self): + def job_type(self): # type: () -> str return "install" - def __str__(self): + def __str__(self): # type: () -> str return "Installing {} ({})".format( self.package.pretty_name, self.format_version(self.package) ) - def __repr__(self): + def __repr__(self): # type: () -> str return "".format( self.package.pretty_name, self.format_version(self.package) ) diff --git a/poetry/installation/operations/operation.py b/poetry/installation/operations/operation.py index 0c72cc8c044..847510c80f8 100644 --- a/poetry/installation/operations/operation.py +++ b/poetry/installation/operations/operation.py @@ -1,12 +1,15 @@ # -*- coding: utf-8 -*- -from typing import Union +from typing import TYPE_CHECKING +from typing import Optional + + +if TYPE_CHECKING: + from poetry.core.packages import Package # noqa class Operation(object): - def __init__( - self, reason=None, priority=0 - ): # type: (Union[str, None], int) -> None + def __init__(self, reason=None, priority=0): # type: (Optional[str], int) -> None self._reason = reason self._skipped = False @@ -26,7 +29,7 @@ def skipped(self): # type: () -> bool return self._skipped @property - def skip_reason(self): # type: () -> Union[str, None] + def skip_reason(self): # type: () -> Optional[str] return self._skip_reason @property @@ -34,10 +37,10 @@ def priority(self): # type: () -> int return self._priority @property - def package(self): + def package(self): # type: () -> "Package" raise NotImplementedError() - def format_version(self, package): # type: (...) -> str + def format_version(self, package): # type: ("Package") -> str return package.full_pretty_version def skip(self, reason): # type: (str) -> Operation diff --git a/poetry/installation/operations/uninstall.py b/poetry/installation/operations/uninstall.py index b7e40bc606e..32c6e4e3583 100644 --- a/poetry/installation/operations/uninstall.py +++ b/poetry/installation/operations/uninstall.py @@ -1,26 +1,35 @@ +from typing import TYPE_CHECKING +from typing import Optional + from .operation import Operation +if TYPE_CHECKING: + from poetry.core.packages import Package # noqa + + class Uninstall(Operation): - def __init__(self, package, reason=None, priority=float("inf")): + def __init__( + self, package, reason=None, priority=float("inf") + ): # type: ("Package", Optional[str], int) -> None super(Uninstall, self).__init__(reason, priority=priority) self._package = package @property - def package(self): + def package(self): # type: () -> "Package" return self._package @property - def job_type(self): + def job_type(self): # type: () -> str return "uninstall" - def __str__(self): + def __str__(self): # type: () -> str return "Uninstalling {} ({})".format( self.package.pretty_name, self.format_version(self._package) ) - def __repr__(self): + def __repr__(self): # type: () -> str return "".format( self.package.pretty_name, self.format_version(self.package) ) diff --git a/poetry/installation/operations/update.py b/poetry/installation/operations/update.py index 87803fd7a23..02cd86ccfb8 100644 --- a/poetry/installation/operations/update.py +++ b/poetry/installation/operations/update.py @@ -1,30 +1,39 @@ +from typing import TYPE_CHECKING +from typing import Optional + from .operation import Operation +if TYPE_CHECKING: + from poetry.core.packages import Package # noqa + + class Update(Operation): - def __init__(self, initial, target, reason=None, priority=0): + def __init__( + self, initial, target, reason=None, priority=0 + ): # type: ("Package", "Package", Optional[str], int) -> None self._initial_package = initial self._target_package = target super(Update, self).__init__(reason, priority=priority) @property - def initial_package(self): + def initial_package(self): # type: () -> "Package" return self._initial_package @property - def target_package(self): + def target_package(self): # type: () -> "Package" return self._target_package @property - def package(self): + def package(self): # type: () -> "Package" return self._target_package @property - def job_type(self): + def job_type(self): # type: () -> str return "update" - def __str__(self): + def __str__(self): # type: () -> str return "Updating {} ({}) to {} ({})".format( self.initial_package.pretty_name, self.format_version(self.initial_package), @@ -32,7 +41,7 @@ def __str__(self): self.format_version(self.target_package), ) - def __repr__(self): + def __repr__(self): # type: () -> str return "".format( self.initial_package.pretty_name, self.format_version(self.initial_package), diff --git a/poetry/installation/pip_installer.py b/poetry/installation/pip_installer.py index df1249737a1..e4a89349d0a 100644 --- a/poetry/installation/pip_installer.py +++ b/poetry/installation/pip_installer.py @@ -3,6 +3,9 @@ import urllib.parse from subprocess import CalledProcessError +from typing import TYPE_CHECKING +from typing import Any +from typing import Union from clikit.api.io import IO @@ -15,13 +18,17 @@ from .base_installer import BaseInstaller +if TYPE_CHECKING: + from poetry.core.packages import Package # noqa + + class PipInstaller(BaseInstaller): def __init__(self, env, io, pool): # type: (Env, IO, Pool) -> None self._env = env self._io = io self._pool = pool - def install(self, package, update=False): + def install(self, package, update=False): # type: ("Package", bool) -> None if package.source_type == "directory": self.install_directory(package) @@ -90,7 +97,7 @@ def install(self, package, update=False): self.run(*args) - def update(self, package, target): + def update(self, package, target): # type: ("Package", "Package") -> None if package.source_type != target.source_type: # If the source type has changed, we remove the current # package to avoid perpetual updates in some cases @@ -98,7 +105,7 @@ def update(self, package, target): self.install(target, update=True) - def remove(self, package): + def remove(self, package): # type: ("Package") -> None try: self.run("uninstall", package.name, "-y") except CalledProcessError as e: @@ -120,10 +127,10 @@ def remove(self, package): if src_dir.exists(): safe_rmtree(str(src_dir)) - def run(self, *args, **kwargs): # type: (...) -> str + def run(self, *args, **kwargs): # type: (*Any,**Any) -> str return self._env.run_pip(*args, **kwargs) - def requirement(self, package, formatted=False): + def requirement(self, package, formatted=False): # type: ("Package", bool) -> str if formatted and not package.source_type: req = "{}=={}".format(package.name, package.version) for f in package.files: @@ -164,7 +171,7 @@ def requirement(self, package, formatted=False): return "{}=={}".format(package.name, package.version) - def create_temporary_requirement(self, package): + def create_temporary_requirement(self, package): # type: ("Package") -> str fd, name = tempfile.mkstemp( "reqs.txt", "{}-{}".format(package.name, package.version) ) @@ -176,7 +183,7 @@ def create_temporary_requirement(self, package): return name - def install_directory(self, package): + def install_directory(self, package): # type: ("Package") -> Union[str, int] from poetry.factory import Factory from poetry.io.null_io import NullIO @@ -233,7 +240,7 @@ def install_directory(self, package): return self.run(*args) - def install_git(self, package): + def install_git(self, package): # type: ("Package") -> None from poetry.core.packages import Package from poetry.core.vcs import Git diff --git a/poetry/io/null_io.py b/poetry/io/null_io.py index d81cd595527..786b188a4e2 100644 --- a/poetry/io/null_io.py +++ b/poetry/io/null_io.py @@ -1,3 +1,5 @@ +from typing import Any + from cleo.io.io_mixin import IOMixin from clikit.io import NullIO as BaseNullIO @@ -7,5 +9,5 @@ class NullIO(IOMixin, BaseNullIO): A wrapper around CliKit's NullIO. """ - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs): # type: (*Any, **Any) -> None super(NullIO, self).__init__(*args, **kwargs) diff --git a/poetry/layouts/layout.py b/poetry/layouts/layout.py index 8a74060f06e..af5b48ad746 100644 --- a/poetry/layouts/layout.py +++ b/poetry/layouts/layout.py @@ -1,4 +1,5 @@ from typing import TYPE_CHECKING +from typing import Dict from typing import Optional from tomlkit import dumps @@ -9,7 +10,8 @@ if TYPE_CHECKING: - from poetry.core.pyproject.toml import PyProjectTOML + from poetry.core.pyproject.toml import PyProjectTOML # noqa + from poetry.utils._compat import Path # noqa TESTS_DEFAULT = u"""from {package_name} import __version__ @@ -45,21 +47,21 @@ def test_version(): """ BUILD_SYSTEM_MIN_VERSION = "1.0.0" -BUILD_SYSTEM_MAX_VERSION = None +BUILD_SYSTEM_MAX_VERSION = None # type: Optional[str] class Layout(object): def __init__( self, - project, - version="0.1.0", - description="", - readme_format="md", - author=None, - license=None, - python="*", - dependencies=None, - dev_dependencies=None, + project, # type: str + version="0.1.0", # type: str + description="", # type: str + readme_format="md", # type: str + author=None, # type: Optional[str] + license=None, # type: Optional[str] + python="*", # type: str + dependencies=None, # type: Optional[Dict[str, str]] + dev_dependencies=None, # type: Optional[Dict[str, str]] ): self._project = project self._package_name = module_name(project) @@ -76,7 +78,7 @@ def __init__( self._author = author - def create(self, path, with_tests=True): + def create(self, path, with_tests=True): # type: (Path, bool) -> None path.mkdir(parents=True, exist_ok=True) self._create_default(path) @@ -129,10 +131,10 @@ def generate_poetry_content( return content - def _create_default(self, path, src=True): + def _create_default(self, path, src=True): # type: (Path, bool) -> None raise NotImplementedError() - def _create_readme(self, path): + def _create_readme(self, path): # type: (Path) -> None if self._readme_format == "rst": readme_file = path / "README.rst" else: @@ -140,7 +142,7 @@ def _create_readme(self, path): readme_file.touch() - def _create_tests(self, path): + def _create_tests(self, path): # type: (Path) -> None tests = path / "tests" tests_init = tests / "__init__.py" tests_default = tests / "test_{}.py".format(self._package_name) @@ -155,7 +157,7 @@ def _create_tests(self, path): ) ) - def _write_poetry(self, path): + def _write_poetry(self, path): # type: ("Path") -> None content = self.generate_poetry_content() poetry = path / "pyproject.toml" diff --git a/poetry/layouts/src.py b/poetry/layouts/src.py index 06db7a71f92..a703ed2f2c2 100644 --- a/poetry/layouts/src.py +++ b/poetry/layouts/src.py @@ -1,14 +1,19 @@ # -*- coding: utf-8 -*- +from typing import TYPE_CHECKING + from .layout import Layout +if TYPE_CHECKING: + from poetry.utils._compat import Path # noqa + DEFAULT = u"""__version__ = '{version}' """ class SrcLayout(Layout): - def _create_default(self, path): + def _create_default(self, path): # type: ("Path") -> None package_path = path / "src" / self._package_name package_init = package_path / "__init__.py" diff --git a/poetry/layouts/standard.py b/poetry/layouts/standard.py index eca4c435c40..4372d71d6b1 100644 --- a/poetry/layouts/standard.py +++ b/poetry/layouts/standard.py @@ -1,14 +1,18 @@ # -*- coding: utf-8 -*- +from typing import TYPE_CHECKING + from .layout import Layout +if TYPE_CHECKING: + from poetry.utils._compat import Path # noqa DEFAULT = u"""__version__ = '{version}' """ class StandardLayout(Layout): - def _create_default(self, path): + def _create_default(self, path): # type: ("Path") -> None package_path = path / self._package_name package_init = package_path / "__init__.py" diff --git a/poetry/masonry/builders/editable.py b/poetry/masonry/builders/editable.py index dc4be9af4cc..4f4c059ce10 100644 --- a/poetry/masonry/builders/editable.py +++ b/poetry/masonry/builders/editable.py @@ -6,6 +6,8 @@ from base64 import urlsafe_b64encode from pathlib import Path +from typing import TYPE_CHECKING +from typing import List from poetry.core.masonry.builders.builder import Builder from poetry.core.masonry.builders.sdist import SdistBuilder @@ -16,6 +18,12 @@ from poetry.utils.helpers import is_dir_writable +if TYPE_CHECKING: + from clikit.api.io import IO # noqa + + from poetry.core.poetry import Poetry # noqa + from poetry.utils.env import Env # noqa + SCRIPT_TEMPLATE = """\ #!{python} from {module} import {callable_holder} @@ -30,13 +38,13 @@ class EditableBuilder(Builder): - def __init__(self, poetry, env, io): + def __init__(self, poetry, env, io): # type: ("Poetry", "Env", "IO") -> None super(EditableBuilder, self).__init__(poetry) self._env = env self._io = io - def build(self): + def build(self): # type: () -> None self._debug( " - Building package {} in editable mode".format( self._package.name @@ -58,11 +66,11 @@ def build(self): added_files += self._add_scripts() self._add_dist_info(added_files) - def _run_build_script(self, build_script): + def _run_build_script(self, build_script): # type: (Path) -> None self._debug(" - Executing build script: {}".format(build_script)) self._env.run("python", str(self._path.joinpath(build_script)), call=True) - def _setup_build(self): + def _setup_build(self): # type: () -> None builder = SdistBuilder(self._poetry) setup = self._path / "setup.py" has_setup = setup.exists() @@ -94,7 +102,7 @@ def _setup_build(self): if not has_setup: os.remove(str(setup)) - def _add_pth(self): + def _add_pth(self): # type: () -> List[Path] paths = set() for include in self._module.includes: if isinstance(include, PackageInclude) and ( @@ -126,7 +134,7 @@ def _add_pth(self): ) return [] - def _add_scripts(self): + def _add_scripts(self): # type: () -> List[Path] added = [] entry_points = self.convert_entry_points() @@ -185,7 +193,7 @@ def _add_scripts(self): return added - def _add_dist_info(self, added_files): + def _add_dist_info(self, added_files): # type: (List[Path]) -> None from poetry.core.masonry.builders.wheel import WheelBuilder added_files = added_files[:] @@ -239,7 +247,7 @@ def _add_dist_info(self, added_files): # RECORD itself is recorded with no hash or size f.write("{},,\n".format(dist_info.joinpath("RECORD"))) - def _get_file_hash(self, filepath): + def _get_file_hash(self, filepath): # type: (Path) -> str hashsum = hashlib.sha256() with filepath.open("rb") as src: while True: @@ -252,6 +260,6 @@ def _get_file_hash(self, filepath): return urlsafe_b64encode(hashsum.digest()).decode("ascii").rstrip("=") - def _debug(self, msg): + def _debug(self, msg): # type: (str) -> None if self._io.is_debug(): self._io.write_line(msg) diff --git a/poetry/mixology/__init__.py b/poetry/mixology/__init__.py index 50fbffb27cb..8dd76488438 100644 --- a/poetry/mixology/__init__.py +++ b/poetry/mixology/__init__.py @@ -1,7 +1,21 @@ +from typing import TYPE_CHECKING +from typing import Dict +from typing import List + from .version_solver import VersionSolver -def resolve_version(root, provider, locked=None, use_latest=None): +if TYPE_CHECKING: + from poetry.core.packages import DependencyPackage # noqa + from poetry.core.packages import ProjectPackage # noqa + from poetry.puzzle.provider import Provider # noqa + + from .result import SolverResult # noqa + + +def resolve_version( + root, provider, locked=None, use_latest=None +): # type: ("ProjectPackage", "Provider", Dict[str, "DependencyPackage"],List[str]) -> "SolverResult" solver = VersionSolver(root, provider, locked=locked, use_latest=use_latest) return solver.solve() diff --git a/poetry/mixology/assignment.py b/poetry/mixology/assignment.py index e288c5da520..21765a22fe5 100644 --- a/poetry/mixology/assignment.py +++ b/poetry/mixology/assignment.py @@ -1,15 +1,25 @@ +from typing import TYPE_CHECKING from typing import Any +from typing import Optional -from .incompatibility import Incompatibility from .term import Term +if TYPE_CHECKING: + from poetry.core.packages import Dependency # noqa + from poetry.core.packages import Package # noqa + + from .incompatibility import Incompatibility # noqa + + class Assignment(Term): """ A term in a PartialSolution that tracks some additional metadata. """ - def __init__(self, dependency, is_positive, decision_level, index, cause=None): + def __init__( + self, dependency, is_positive, decision_level, index, cause=None + ): # type: ("Dependency", bool, int, int, Optional["Incompatibility"]) -> None super(Assignment, self).__init__(dependency, is_positive) self._decision_level = decision_level @@ -25,19 +35,19 @@ def index(self): # type: () -> int return self._index @property - def cause(self): # type: () -> Incompatibility + def cause(self): # type: () -> "Incompatibility" return self._cause @classmethod def decision( cls, package, decision_level, index - ): # type: (Any, int, int) -> Assignment + ): # type: ("Package", int, int) -> Assignment return cls(package.to_dependency(), True, decision_level, index) @classmethod def derivation( cls, dependency, is_positive, cause, decision_level, index - ): # type: (Any, bool, Incompatibility, int, int) -> Assignment + ): # type: (Any, bool, "Incompatibility", int, int) -> "Assignment" return cls(dependency, is_positive, decision_level, index, cause) def is_decision(self): # type: () -> bool diff --git a/poetry/mixology/failure.py b/poetry/mixology/failure.py index afa332085b3..daffd12b94b 100644 --- a/poetry/mixology/failure.py +++ b/poetry/mixology/failure.py @@ -1,5 +1,6 @@ from typing import Dict from typing import List +from typing import Optional from typing import Tuple from poetry.core.semver import parse_constraint @@ -14,10 +15,10 @@ def __init__(self, incompatibility): # type: (Incompatibility) -> None self._incompatibility = incompatibility @property - def message(self): + def message(self): # type: () -> str return str(self) - def __str__(self): + def __str__(self): # type: () -> str return _Writer(self._incompatibility).write() @@ -25,12 +26,12 @@ class _Writer: def __init__(self, root): # type: (Incompatibility) -> None self._root = root self._derivations = {} # type: Dict[Incompatibility, int] - self._lines = [] # type: List[Tuple[str, int]] + self._lines = [] # type: List[Tuple[str, Optional[int]]] self._line_numbers = {} # type: Dict[Incompatibility, int] self._count_derivations(self._root) - def write(self): + def write(self): # type: () -> str buffer = [] required_python_version_notification = False @@ -113,7 +114,7 @@ def _visit( conjunction = "So," if conclusion or incompatibility == self._root else "And" incompatibility_string = str(incompatibility) - cause = incompatibility.cause # type: ConflictCause + cause = incompatibility.cause details_for_cause = {} if isinstance(cause.conflict.cause, ConflictCause) and isinstance( cause.other.cause, ConflictCause diff --git a/poetry/mixology/incompatibility.py b/poetry/mixology/incompatibility.py index a5be2dadbdf..1e9a20440f3 100644 --- a/poetry/mixology/incompatibility.py +++ b/poetry/mixology/incompatibility.py @@ -1,6 +1,8 @@ from typing import Dict from typing import Generator from typing import List +from typing import Optional +from typing import Union from .incompatibility_cause import ConflictCause from .incompatibility_cause import DependencyCause @@ -82,11 +84,15 @@ def terms(self): # type: () -> List[Term] return self._terms @property - def cause(self): # type: () -> IncompatibilityCause + def cause( + self, + ): # type: () -> Union[RootCause, NoVersionsCause, DependencyCause, ConflictCause, PythonCause, PlatformCause, PackageNotFoundCause] return self._cause @property - def external_incompatibilities(self): # type: () -> Generator[Incompatibility] + def external_incompatibilities( + self, + ): # type: () -> Generator[Union[ConflictCause, Incompatibility]] """ Returns all external incompatibilities in this incompatibility's derivation graph. @@ -106,7 +112,7 @@ def is_failure(self): # type: () -> bool len(self._terms) == 1 and self._terms[0].dependency.is_root ) - def __str__(self): + def __str__(self): # type: () -> str if isinstance(self._cause, DependencyCause): assert len(self._terms) == 2 @@ -222,7 +228,7 @@ def __str__(self): def and_to_string( self, other, details, this_line, other_line - ): # type: (Incompatibility, dict, int, int) -> str + ): # type: (Incompatibility, dict, Optional[int], Optional[int]) -> str requires_both = self._try_requires_both(other, details, this_line, other_line) if requires_both is not None: return requires_both @@ -241,18 +247,18 @@ def and_to_string( buffer = [str(self)] if this_line is not None: - buffer.append(" " + this_line) + buffer.append(" " + str(this_line)) buffer.append(" and {}".format(str(other))) if other_line is not None: - buffer.append(" " + other_line) + buffer.append(" " + str(other_line)) return "\n".join(buffer) def _try_requires_both( self, other, details, this_line, other_line - ): # type: (Incompatibility, dict, int, int) -> str + ): # type: (Incompatibility, dict, Optional[int], Optional[int]) -> Optional[str] if len(self._terms) == 1 or len(other.terms) == 1: return @@ -298,7 +304,7 @@ def _try_requires_both( def _try_requires_through( self, other, details, this_line, other_line - ): # type: (Incompatibility, dict, int, int) -> str + ): # type: (Incompatibility, dict, int, int) -> Optional[str] if len(self._terms) == 1 or len(other.terms) == 1: return @@ -376,7 +382,7 @@ def _try_requires_through( def _try_requires_forbidden( self, other, details, this_line, other_line - ): # type: (Incompatibility, dict, int, int) -> str + ): # type: (Incompatibility, dict, int, int) -> Optional[str] if len(self._terms) != 1 and len(other.terms) != 1: return None @@ -430,13 +436,13 @@ def _try_requires_forbidden( return "".join(buffer) - def _terse(self, term, allow_every=False): + def _terse(self, term, allow_every=False): # type: (Term, bool) -> str if allow_every and term.constraint.is_any(): return "every version of {}".format(term.dependency.complete_name) return str(term.dependency) - def _single_term_where(self, callable): # type: (callable) -> Term + def _single_term_where(self, callable): # type: (callable) -> Optional[Term] found = None for term in self._terms: if not callable(term): @@ -449,5 +455,5 @@ def _single_term_where(self, callable): # type: (callable) -> Term return found - def __repr__(self): + def __repr__(self): # type: () -> str return "".format(str(self)) diff --git a/poetry/mixology/incompatibility_cause.py b/poetry/mixology/incompatibility_cause.py index 8156b4fa42b..227b9dd0e81 100644 --- a/poetry/mixology/incompatibility_cause.py +++ b/poetry/mixology/incompatibility_cause.py @@ -1,3 +1,10 @@ +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from poetry.mixology.incompatibility import Incompatibility # noqa + + class IncompatibilityCause(Exception): """ The reason and Incompatibility's terms are incompatible. @@ -25,19 +32,21 @@ class ConflictCause(IncompatibilityCause): during conflict resolution. """ - def __init__(self, conflict, other): + def __init__( + self, conflict, other + ): # type: ("Incompatibility", "Incompatibility") -> None self._conflict = conflict self._other = other @property - def conflict(self): + def conflict(self): # type: () -> "Incompatibility" return self._conflict @property - def other(self): + def other(self): # type: () -> "Incompatibility" return self._other - def __str__(self): + def __str__(self): # type: () -> str return str(self._conflict) @@ -48,16 +57,16 @@ class PythonCause(IncompatibilityCause): with the current python version. """ - def __init__(self, python_version, root_python_version): + def __init__(self, python_version, root_python_version): # type: (str, str) -> None self._python_version = python_version self._root_python_version = root_python_version @property - def python_version(self): + def python_version(self): # type: () -> str return self._python_version @property - def root_python_version(self): + def root_python_version(self): # type: () -> str return self._root_python_version @@ -67,11 +76,11 @@ class PlatformCause(IncompatibilityCause): (OS most likely) being incompatible with the current platform. """ - def __init__(self, platform): + def __init__(self, platform): # type: (str) -> None self._platform = platform @property - def platform(self): + def platform(self): # type: () -> str return self._platform @@ -81,9 +90,9 @@ class PackageNotFoundCause(IncompatibilityCause): source. """ - def __init__(self, error): + def __init__(self, error): # type: (Exception) -> None self._error = error @property - def error(self): + def error(self): # type: () -> Exception return self._error diff --git a/poetry/mixology/partial_solution.py b/poetry/mixology/partial_solution.py index 55230425ce8..93a6f73a2fb 100644 --- a/poetry/mixology/partial_solution.py +++ b/poetry/mixology/partial_solution.py @@ -1,15 +1,19 @@ +from typing import TYPE_CHECKING from typing import Dict from typing import List -from poetry.core.packages import Dependency -from poetry.core.packages import Package - from .assignment import Assignment from .incompatibility import Incompatibility from .set_relation import SetRelation from .term import Term +if TYPE_CHECKING: + from poetry.core.packages import Dependency # noqa + from poetry.core.packages import Package # noqa + from poetry.packages import DependencyPackage # noqa + + class PartialSolution: """ # A list of Assignments that represent the solver's current best guess about @@ -19,13 +23,13 @@ class PartialSolution: # See https://github.com/dart-lang/mixology/tree/master/doc/solver.md#partial-solution. """ - def __init__(self): + def __init__(self): # type: () -> None # The assignments that have been made so far, in the order they were # assigned. self._assignments = [] # type: List[Assignment] # The decisions made for each package. - self._decisions = dict() # type: Dict[str, Package] + self._decisions = dict() # type: Dict[str, "Package"] # The intersection of all positive Assignments for each package, minus any # negative Assignments that refer to that package. @@ -48,7 +52,7 @@ def __init__(self): self._backtracking = False @property - def decisions(self): # type: () -> List[Package] + def decisions(self): # type: () -> List["Package"] return list(self._decisions.values()) @property @@ -60,14 +64,14 @@ def attempted_solutions(self): # type: () -> int return self._attempted_solutions @property - def unsatisfied(self): # type: () -> List[Dependency] + def unsatisfied(self): # type: () -> List["Dependency"] return [ term.dependency for term in self._positive.values() if term.dependency.complete_name not in self._decisions ] - def decide(self, package): # type: (Package) -> None + def decide(self, package): # type: ("Package") -> None """ Adds an assignment of package as a decision and increments the decision level. @@ -88,7 +92,7 @@ def decide(self, package): # type: (Package) -> None def derive( self, dependency, is_positive, cause - ): # type: (Dependency, bool, Incompatibility) -> None + ): # type: ("Dependency", bool, Incompatibility) -> None """ Adds an assignment of package as a derivation. """ @@ -170,7 +174,7 @@ def satisfier(self, term): # type: (Term) -> Assignment Returns the first Assignment in this solution such that the sublist of assignments up to and including that entry collectively satisfies term. """ - assigned_term = None # type: Term + assigned_term = None for assignment in self._assignments: if assignment.dependency.complete_name != term.dependency.complete_name: diff --git a/poetry/mixology/result.py b/poetry/mixology/result.py index 5eadeb75ddc..62a5029e5e3 100644 --- a/poetry/mixology/result.py +++ b/poetry/mixology/result.py @@ -1,13 +1,24 @@ +from typing import TYPE_CHECKING +from typing import List + + +if TYPE_CHECKING: + from poetry.core.packages import Package # noqa + from poetry.core.packages import ProjectPackage # noqa + + class SolverResult: - def __init__(self, root, packages, attempted_solutions): + def __init__( + self, root, packages, attempted_solutions + ): # type: ("ProjectPackage", List["Package"], int) -> None self._root = root self._packages = packages self._attempted_solutions = attempted_solutions @property - def packages(self): + def packages(self): # type: () -> List["Package"] return self._packages @property - def attempted_solutions(self): + def attempted_solutions(self): # type: () -> int return self._attempted_solutions diff --git a/poetry/mixology/solutions/solutions/python_requirement_solution.py b/poetry/mixology/solutions/solutions/python_requirement_solution.py index 9ec7cf22301..b24f7f09915 100644 --- a/poetry/mixology/solutions/solutions/python_requirement_solution.py +++ b/poetry/mixology/solutions/solutions/python_requirement_solution.py @@ -1,8 +1,15 @@ +from typing import TYPE_CHECKING +from typing import List + from crashtest.contracts.solution import Solution +if TYPE_CHECKING: + from poetry.mixology.incompatibility_cause import PackageNotFoundCause # noqa + + class PythonRequirementSolution(Solution): - def __init__(self, exception): + def __init__(self, exception): # type: ("PackageNotFoundCause") -> None from poetry.core.semver import parse_constraint from poetry.mixology.incompatibility_cause import PythonCause @@ -37,15 +44,15 @@ def __init__(self, exception): self._description = description @property - def solution_title(self) -> str: + def solution_title(self): # type: () -> str return self._title @property - def solution_description(self): + def solution_description(self): # type: () -> str return self._description @property - def documentation_links(self): + def documentation_links(self): # type: () -> List[str] return [ "https://python-poetry.org/docs/dependency-specification/#python-restricted-dependencies", "https://python-poetry.org/docs/dependency-specification/#using-environment-markers", diff --git a/poetry/mixology/term.py b/poetry/mixology/term.py index 0889e984a3b..b232e268af0 100644 --- a/poetry/mixology/term.py +++ b/poetry/mixology/term.py @@ -1,11 +1,16 @@ # -*- coding: utf-8 -*- -from typing import Union +from typing import TYPE_CHECKING +from typing import Optional from poetry.core.packages import Dependency from .set_relation import SetRelation +if TYPE_CHECKING: + from poetry.core.semver import VersionTypes # noqa + + class Term(object): """ A statement about a package which is true or false for a given selection of @@ -23,11 +28,11 @@ def inverse(self): # type: () -> Term return Term(self._dependency, not self.is_positive()) @property - def dependency(self): + def dependency(self): # type: () -> Dependency return self._dependency @property - def constraint(self): + def constraint(self): # type: () -> "VersionTypes" return self._dependency.constraint def is_positive(self): # type: () -> bool @@ -106,7 +111,7 @@ def relation(self, other): # type: (Term) -> int # not foo ^1.5.0 is a superset of not foo ^1.0.0 return SetRelation.OVERLAPPING - def intersect(self, other): # type: (Term) -> Union[Term, None] + def intersect(self, other): # type: (Term) -> Optional[Term] """ Returns a Term that represents the packages allowed by both this term and another @@ -147,21 +152,23 @@ def difference(self, other): # type: (Term) -> Term """ return self.intersect(other.inverse) - def _compatible_dependency(self, other): + def _compatible_dependency(self, other): # type: (Term) -> bool return ( self.dependency.is_root or other.is_root or other.is_same_package_as(self.dependency) ) - def _non_empty_term(self, constraint, is_positive): + def _non_empty_term( + self, constraint, is_positive + ): # type: ("VersionTypes", bool) -> Optional[Term] if constraint.is_empty(): return return Term(self.dependency.with_constraint(constraint), is_positive) - def __str__(self): + def __str__(self): # type: () -> str return "{}{}".format("not " if not self.is_positive() else "", self._dependency) - def __repr__(self): + def __repr__(self): # type: () -> str return "".format(str(self)) diff --git a/poetry/mixology/version_solver.py b/poetry/mixology/version_solver.py index 8d70a0df288..1d1d19d2d55 100644 --- a/poetry/mixology/version_solver.py +++ b/poetry/mixology/version_solver.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING from typing import Dict from typing import List -from typing import Union +from typing import Optional from poetry.core.packages import Dependency from poetry.core.packages import Package @@ -130,7 +130,7 @@ def _propagate(self, package): # type: (str) -> None def _propagate_incompatibility( self, incompatibility - ): # type: (Incompatibility) -> Union[str, _conflict, None] + ): # type: (Incompatibility) -> Optional[str, _conflict] """ If incompatibility is almost satisfied by _solution, adds the negation of the unsatisfied term to _solution. @@ -317,7 +317,7 @@ def _resolve_conflict( raise SolveFailure(incompatibility) - def _choose_package_version(self): # type: () -> Union[str, None] + def _choose_package_version(self): # type: () -> Optional[str] """ Tries to select a version of a required package. @@ -331,7 +331,7 @@ def _choose_package_version(self): # type: () -> Union[str, None] # Prefer packages with as few remaining versions as possible, # so that if a conflict is necessary it's forced quickly. - def _get_min(dependency): + def _get_min(dependency): # type: (Dependency) -> int if dependency.name in self._use_latest: # If we're forced to use the latest version of a package, it effectively # only has one version to choose from. @@ -447,7 +447,7 @@ def _add_incompatibility(self, incompatibility): # type: (Incompatibility) -> N incompatibility ) - def _get_locked(self, dependency): # type: (Dependency) -> Union[Package, None] + def _get_locked(self, dependency): # type: (Dependency) -> Optional[Package] if dependency.name in self._use_latest: return @@ -460,5 +460,5 @@ def _get_locked(self, dependency): # type: (Dependency) -> Union[Package, None] return locked - def _log(self, text): + def _log(self, text): # type: (str) -> None self._provider.debug(text, self._solution.attempted_solutions) diff --git a/poetry/packages/dependency_package.py b/poetry/packages/dependency_package.py index 60c51007ed8..e375118a5b8 100644 --- a/poetry/packages/dependency_package.py +++ b/poetry/packages/dependency_package.py @@ -1,4 +1,6 @@ +from typing import Any from typing import List +from typing import Union from poetry.core.packages.dependency import Dependency from poetry.core.packages.package import Package @@ -17,7 +19,7 @@ def dependency(self): # type: () -> Dependency def package(self): # type: () -> Package return self._package - def clone(self): # type: () -> DependencyPackage + def clone(self): # type: () -> "DependencyPackage" return self.__class__(self._dependency, self._package.clone()) def with_features(self, features): # type: (List[str]) -> "DependencyPackage" @@ -26,25 +28,25 @@ def with_features(self, features): # type: (List[str]) -> "DependencyPackage" def without_features(self): # type: () -> "DependencyPackage" return self.with_features([]) - def __getattr__(self, name): + def __getattr__(self, name): # type: (str) -> Any return getattr(self._package, name) - def __setattr__(self, key, value): + def __setattr__(self, key, value): # type: (str, Any) -> None if key in {"_dependency", "_package"}: return super(DependencyPackage, self).__setattr__(key, value) setattr(self._package, key, value) - def __str__(self): + def __str__(self): # type: () -> str return str(self._package) - def __repr__(self): + def __repr__(self): # type: () -> str return repr(self._package) - def __hash__(self): + def __hash__(self): # type: () -> int return hash(self._package) - def __eq__(self, other): + def __eq__(self, other): # type: (Union[Package, "DependencyPackage"]) -> bool if isinstance(other, DependencyPackage): other = other.package diff --git a/poetry/packages/locker.py b/poetry/packages/locker.py index 8cb6d91f916..80350f66fe4 100644 --- a/poetry/packages/locker.py +++ b/poetry/packages/locker.py @@ -6,6 +6,7 @@ from copy import deepcopy from hashlib import sha256 from pathlib import Path +from typing import TYPE_CHECKING from typing import Dict from typing import Iterable from typing import Iterator @@ -26,7 +27,7 @@ import poetry.repositories from poetry.core.packages import dependency_from_pep_508 -from poetry.core.packages.package import Dependency +from poetry.core.packages.dependency import Dependency from poetry.core.packages.package import Package from poetry.core.semver import parse_constraint from poetry.core.semver.version import Version @@ -37,6 +38,9 @@ from poetry.utils.extras import get_extra_package_names +if TYPE_CHECKING: + from ptomlkit.toml_document import TOMLDocument # noqa + logger = logging.getLogger(__name__) @@ -46,7 +50,7 @@ class Locker(object): _relevant_keys = ["dependencies", "dev-dependencies", "source", "extras"] - def __init__(self, lock, local_config): # type: (Path, dict) -> None + def __init__(self, lock, local_config): # type: (Union[str, Path], dict) -> None self._lock = TOMLFile(lock) self._local_config = local_config self._lock_data = None @@ -57,7 +61,7 @@ def lock(self): # type: () -> TOMLFile return self._lock @property - def lock_data(self): + def lock_data(self): # type: () -> TOMLDocument if self._lock_data is None: self._lock_data = self._get_lock_data() @@ -382,7 +386,7 @@ def get_project_dependency_packages( yield DependencyPackage(dependency=dependency, package=package) - def set_lock_data(self, root, packages): # type: (...) -> bool + def set_lock_data(self, root, packages): # type: (Package, List[Package]) -> bool files = table() packages = self._lock_packages(packages) # Retrieving hashes @@ -427,7 +431,7 @@ def set_lock_data(self, root, packages): # type: (...) -> bool return False - def _write_lock_data(self, data): + def _write_lock_data(self, data): # type: ("TOMLDocument") -> None self.lock.write(data) # Checking lock file data consistency @@ -452,7 +456,7 @@ def _get_content_hash(self): # type: () -> str return content_hash - def _get_lock_data(self): # type: () -> dict + def _get_lock_data(self): # type: () -> "TOMLDocument" if not self._lock.exists(): raise RuntimeError("No lockfile found. Unable to read locked packages") @@ -484,9 +488,7 @@ def _get_lock_data(self): # type: () -> dict return lock_data - def _lock_packages( - self, packages - ): # type: (List['poetry.packages.Package']) -> list + def _lock_packages(self, packages): # type: (List[Package]) -> list locked = [] for package in sorted(packages, key=lambda x: x.name): diff --git a/poetry/packages/package_collection.py b/poetry/packages/package_collection.py index e10ea635bca..e572dcbfdeb 100644 --- a/poetry/packages/package_collection.py +++ b/poetry/packages/package_collection.py @@ -1,8 +1,19 @@ +from typing import TYPE_CHECKING +from typing import List +from typing import Union + from .dependency_package import DependencyPackage +if TYPE_CHECKING: + from poetry.core.packages import Dependency # noqa + from poetry.core.packages import Package # noqa + + class PackageCollection(list): - def __init__(self, dependency, packages=None): + def __init__( + self, dependency, packages=None + ): # type: (Dependency, List[Union["Package", DependencyPackage]]) -> None self._dependency = dependency if packages is None: @@ -13,7 +24,7 @@ def __init__(self, dependency, packages=None): for package in packages: self.append(package) - def append(self, package): + def append(self, package): # type: (Union["Package", DependencyPackage]) -> None if isinstance(package, DependencyPackage): package = package.package diff --git a/poetry/publishing/publisher.py b/poetry/publishing/publisher.py index 5cec7eca2fe..c6855deee2d 100644 --- a/poetry/publishing/publisher.py +++ b/poetry/publishing/publisher.py @@ -1,7 +1,10 @@ import logging from pathlib import Path +from typing import TYPE_CHECKING +from typing import List from typing import Optional +from typing import Union from poetry.utils.helpers import get_cert from poetry.utils.helpers import get_client_cert @@ -10,6 +13,13 @@ from .uploader import Uploader +if TYPE_CHECKING: + from cleo.io import BufferedIO # noqa + from cleo.io import ConsoleIO # noqa + from clikit.io import NullIO # noqa + + from ..poetry import Poetry # noqa + logger = logging.getLogger(__name__) @@ -18,7 +28,9 @@ class Publisher: Registers and publishes packages to remote repositories. """ - def __init__(self, poetry, io): + def __init__( + self, poetry, io + ): # type: ("Poetry", Union["ConsoleIO", "BufferedIO", "NullIO"]) -> None self._poetry = poetry self._package = poetry.package self._io = io @@ -26,7 +38,7 @@ def __init__(self, poetry, io): self._password_manager = PasswordManager(poetry.config) @property - def files(self): + def files(self): # type: () -> List[Path] return self._uploader.files def publish( diff --git a/poetry/publishing/uploader.py b/poetry/publishing/uploader.py index bb1673e3bff..43f4589b383 100644 --- a/poetry/publishing/uploader.py +++ b/poetry/publishing/uploader.py @@ -2,10 +2,12 @@ import io from pathlib import Path +from typing import TYPE_CHECKING from typing import Any from typing import Dict from typing import List from typing import Optional +from typing import Tuple from typing import Union import requests @@ -26,11 +28,17 @@ from poetry.utils.patterns import wheel_file_re +if TYPE_CHECKING: + from cleo.io import ConsoleIO # noqa + from clikit.io import NullIO # noqa + + from poetry.poetry import Poetry # noqa + _has_blake2 = hasattr(hashlib, "blake2b") class UploadError(Exception): - def __init__(self, error): # type: (Union[ConnectionError, HTTPError]) -> None + def __init__(self, error): # type: (Union[ConnectionError, HTTPError, str]) -> None if isinstance(error, HTTPError): message = "HTTP Error {}: {}".format( error.response.status_code, error.response.reason @@ -46,7 +54,9 @@ def __init__(self, error): # type: (Union[ConnectionError, HTTPError]) -> None class Uploader: - def __init__(self, poetry, io): + def __init__( + self, poetry, io + ): # type: ("Poetry", Union["ConsoleIO", "NullIO"]) -> None self._poetry = poetry self._package = poetry.package self._io = io @@ -54,11 +64,11 @@ def __init__(self, poetry, io): self._password = None @property - def user_agent(self): + def user_agent(self): # type: () -> str return user_agent("poetry", __version__) @property - def adapter(self): + def adapter(self): # type: () -> adapters.HTTPAdapter retry = util.Retry( connect=5, total=10, @@ -86,7 +96,7 @@ def files(self): # type: () -> List[Path] return sorted(wheels + tars) - def auth(self, username, password): + def auth(self, username, password): # type: (str, str) -> None self._username = username self._password = password @@ -101,7 +111,7 @@ def make_session(self): # type: () -> requests.Session return session - def is_authenticated(self): + def is_authenticated(self): # type: () -> bool return self._username is not None and self._password is not None def upload( @@ -326,7 +336,7 @@ def _register( return resp - def _prepare_data(self, data): + def _prepare_data(self, data): # type: (Dict) -> List[Tuple[str, str]] data_to_send = [] for key, value in data.items(): if not isinstance(value, (list, tuple)): @@ -337,7 +347,7 @@ def _prepare_data(self, data): return data_to_send - def _get_type(self, file): + def _get_type(self, file): # type: (Path) -> str exts = file.suffixes if exts[-1] == ".whl": return "bdist_wheel" diff --git a/poetry/puzzle/exceptions.py b/poetry/puzzle/exceptions.py index e2e0b0dcce7..8ae381a88ba 100644 --- a/poetry/puzzle/exceptions.py +++ b/poetry/puzzle/exceptions.py @@ -1,18 +1,22 @@ +from typing import Dict +from typing import Tuple + + class SolverProblemError(Exception): - def __init__(self, error): + def __init__(self, error): # type: (Exception) -> None self._error = error super(SolverProblemError, self).__init__(str(error)) @property - def error(self): + def error(self): # type: () -> Exception return self._error class OverrideNeeded(Exception): - def __init__(self, *overrides): + def __init__(self, *overrides): # type: (*Dict) -> None self._overrides = overrides @property - def overrides(self): + def overrides(self): # type: () -> Tuple[Dict] return self._overrides diff --git a/poetry/puzzle/provider.py b/poetry/puzzle/provider.py index e9719a924d9..43d6ed63df3 100644 --- a/poetry/puzzle/provider.py +++ b/poetry/puzzle/provider.py @@ -7,8 +7,11 @@ from pathlib import Path from tempfile import mkdtemp from typing import Any +from typing import Dict +from typing import Iterator from typing import List from typing import Optional +from typing import Union from clikit.ui.components import ProgressIndicator @@ -68,10 +71,10 @@ def __init__( def pool(self): # type: () -> Pool return self._pool - def is_debugging(self): + def is_debugging(self): # type: () -> bool return self._is_debugging - def set_overrides(self, overrides): + def set_overrides(self, overrides): # type: (Dict) -> None self._overrides = overrides def load_deferred(self, load_deferred): # type: (bool) -> None @@ -90,7 +93,9 @@ def use_environment(self, env): # type: (Env) -> Provider self._env = original_env self._python_constraint = original_python_constraint - def search_for(self, dependency): # type: (Dependency) -> List[Package] + def search_for( + self, dependency + ): # type: (Union[Dependency, VCSDependency, FileDependency, DirectoryDependency, URLDependency]) -> List[DependencyPackage] """ Search for the specifications that match the given dependency. @@ -175,7 +180,7 @@ def search_for_vcs(self, dependency): # type: (VCSDependency) -> List[Package] @classmethod def get_package_from_vcs( cls, vcs, url, branch=None, tag=None, rev=None, name=None - ): # type: (str, str, Optional[str], Optional[str]) -> Package + ): # type: (str, str, Optional[str], Optional[str], Optional[str], Optional[str]) -> Package if vcs != "git": raise ValueError("Unsupported VCS dependency {}".format(vcs)) @@ -684,7 +689,7 @@ def complete_package( return package - def debug(self, message, depth=0): + def debug(self, message, depth=0): # type: (str, int) -> None if not (self._io.is_very_verbose() or self._io.is_debug()): return @@ -771,7 +776,7 @@ def debug(self, message, depth=0): self._io.write(debug_info) @contextmanager - def progress(self): + def progress(self): # type: () -> Iterator[None] if not self._io.output.supports_ansi() or self.is_debugging(): self._io.write_line("Resolving dependencies...") yield diff --git a/poetry/puzzle/solver.py b/poetry/puzzle/solver.py index 31858bb3a1d..47e3e8153d6 100644 --- a/poetry/puzzle/solver.py +++ b/poetry/puzzle/solver.py @@ -3,17 +3,21 @@ from collections import defaultdict from contextlib import contextmanager +from typing import TYPE_CHECKING +from typing import Callable +from typing import Dict from typing import List from typing import Optional +from typing import Tuple +from typing import Union -from clikit.io import ConsoleIO +from clikit.api.io import IO from poetry.core.packages import Package from poetry.core.packages.project_package import ProjectPackage from poetry.installation.operations import Install from poetry.installation.operations import Uninstall from poetry.installation.operations import Update -from poetry.installation.operations.operation import Operation from poetry.mixology import resolve_version from poetry.mixology.failure import SolveFailure from poetry.packages import DependencyPackage @@ -26,6 +30,15 @@ from .provider import Provider +if TYPE_CHECKING: + from poetry.core.packages import Dependency # noqa + from poetry.core.packages import DirectoryDependency # noqa + from poetry.core.packages import FileDependency # noqa + from poetry.core.packages import URLDependency # noqa + from poetry.core.packages import VCSDependency # noqa + from poetry.installation.operations import OperationTypes # noqa + + class Solver: def __init__( self, @@ -33,7 +46,7 @@ def __init__( pool, # type: Pool installed, # type: Repository locked, # type: Repository - io, # type: ConsoleIO + io, # type: IO remove_untracked=False, # type: bool provider=None, # type: Optional[Provider] ): @@ -59,7 +72,7 @@ def use_environment(self, env): # type: (Env) -> None with self.provider.use_environment(env): yield - def solve(self, use_latest=None): # type: (...) -> List[Operation] + def solve(self, use_latest=None): # type: (List[str]) -> List["OperationTypes"] with self._provider.progress(): start = time.time() packages, depths = self._solve(use_latest=use_latest) @@ -191,7 +204,9 @@ def solve(self, use_latest=None): # type: (...) -> List[Operation] operations, key=lambda o: (-o.priority, o.package.name, o.package.version,), ) - def solve_in_compatibility_mode(self, overrides, use_latest=None): + def solve_in_compatibility_mode( + self, overrides, use_latest=None + ): # type: (Tuple[Dict], List[str]) -> Tuple[List["Package"], List[int]] locked = {} for package in self._locked.packages: locked[package.name] = DependencyPackage(package.to_dependency(), package) @@ -221,7 +236,9 @@ def solve_in_compatibility_mode(self, overrides, use_latest=None): return packages, depths - def _solve(self, use_latest=None): + def _solve( + self, use_latest=None + ): # type: (List[str]) -> Tuple[List[Package], List[int]] if self._provider._overrides: self._overrides.append(self._provider._overrides) @@ -274,18 +291,20 @@ def _solve(self, use_latest=None): class DFSNode(object): - def __init__(self, id, name, base_name): + def __init__( + self, id, name, base_name + ): # type: (Tuple[str, str, bool], str, str) -> None self.id = id self.name = name self.base_name = base_name - def reachable(self): + def reachable(self): # type: () -> List return [] - def visit(self, parents): + def visit(self, parents): # type: (List[PackageNode]) -> None pass - def __str__(self): + def __str__(self): # type: () -> str return str(self.id) @@ -295,7 +314,9 @@ class VisitedState(enum.Enum): Visited = 2 -def depth_first_search(source, aggregator): +def depth_first_search( + source, aggregator +): # type: (PackageNode, Callable) -> List[Tuple[Package, int]] back_edges = defaultdict(list) visited = {} topo_sorted_nodes = [] @@ -322,7 +343,9 @@ def depth_first_search(source, aggregator): return results -def dfs_visit(node, back_edges, visited, sorted_nodes): +def dfs_visit( + node, back_edges, visited, sorted_nodes +): # type: (PackageNode, Dict[str, List[PackageNode]], Dict[str, VisitedState], List[PackageNode]) -> bool if visited.get(node.id, VisitedState.Unvisited) == VisitedState.Visited: return True if visited.get(node.id, VisitedState.Unvisited) == VisitedState.PartiallyVisited: @@ -343,8 +366,13 @@ def dfs_visit(node, back_edges, visited, sorted_nodes): class PackageNode(DFSNode): def __init__( - self, package, packages, previous=None, previous_dep=None, dep=None, - ): + self, + package, # type: Package + packages, # type: List[Package] + previous=None, # type: Optional[PackageNode] + previous_dep=None, # type: Optional[Union["DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency", "Dependency"]] + dep=None, # type: Optional[Union["DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency", "Dependency"]] + ): # type: (...) -> None self.package = package self.packages = packages @@ -366,7 +394,7 @@ def __init__( package.name, ) - def reachable(self): + def reachable(self): # type: () -> List[PackageNode] children = [] # type: List[PackageNode] if ( @@ -413,7 +441,7 @@ def reachable(self): return children - def visit(self, parents): + def visit(self, parents): # type: (PackageNode) -> None # The root package, which has no parents, is defined as having depth -1 # So that the root package's top-level dependencies have depth 0. self.depth = 1 + max( @@ -425,7 +453,9 @@ def visit(self, parents): ) -def aggregate_package_nodes(nodes, children): +def aggregate_package_nodes( + nodes, children +): # type: (List[PackageNode], List[PackageNode]) -> Tuple[Package, int] package = nodes[0].package depth = max(node.depth for node in nodes) category = ( diff --git a/poetry/repositories/base_repository.py b/poetry/repositories/base_repository.py index 46422ca0edc..801056661a1 100644 --- a/poetry/repositories/base_repository.py +++ b/poetry/repositories/base_repository.py @@ -1,19 +1,31 @@ +from typing import TYPE_CHECKING +from typing import List +from typing import Optional + + +if TYPE_CHECKING: + from poetry.core.packages import Dependency # noqa + from poetry.core.packages import Package # noqa + + class BaseRepository(object): - def __init__(self): + def __init__(self): # type: () -> None self._packages = [] @property - def packages(self): + def packages(self): # type: () -> List["Package"] return self._packages - def has_package(self, package): + def has_package(self, package): # type: ("Package") -> None raise NotImplementedError() - def package(self, name, version, extras=None): + def package( + self, name, version, extras=None + ): # type: (str, str, Optional[List[str]]) -> None raise NotImplementedError() - def find_packages(self, dependency): + def find_packages(self, dependency): # type: ("Dependency") -> None raise NotImplementedError() - def search(self, query): + def search(self, query): # type: (str) -> None raise NotImplementedError() diff --git a/poetry/repositories/legacy_repository.py b/poetry/repositories/legacy_repository.py index 94e1dbf4d7b..aa870562e8b 100644 --- a/poetry/repositories/legacy_repository.py +++ b/poetry/repositories/legacy_repository.py @@ -5,9 +5,12 @@ from collections import defaultdict from pathlib import Path +from typing import TYPE_CHECKING +from typing import Any +from typing import Dict from typing import Generator +from typing import List from typing import Optional -from typing import Union import requests import requests.auth @@ -34,6 +37,9 @@ from .pypi_repository import PyPiRepository +if TYPE_CHECKING: + from poetry.core.packages import Dependency # noqa + try: from html import unescape except ImportError: @@ -69,7 +75,9 @@ class Page: ".tar", ] - def __init__(self, url, content, headers): + def __init__( + self, url, content, headers + ): # type: (str, str, Dict[str, Any]) -> None if not url.endswith("/"): url += "/" @@ -127,7 +135,7 @@ def links_for_version(self, version): # type: (Version) -> Generator[Link] if self.link_version(link) == version: yield link - def link_version(self, link): # type: (Link) -> Union[Version, None] + def link_version(self, link): # type: (Link) -> Optional[Version] m = wheel_file_re.match(link.filename) if m: version = m.group("ver") @@ -148,7 +156,7 @@ def link_version(self, link): # type: (Link) -> Union[Version, None] _clean_re = re.compile(r"[^a-z0-9$&+,/:;=?@.#%_\\|-]", re.I) - def clean_link(self, url): + def clean_link(self, url): # type: (str) -> str """Makes sure a link is fully encoded. That is, if a ' ' shows up in the link, it will be rewritten to %20 (while not over-quoting % or other characters).""" @@ -225,7 +233,7 @@ def authenticated_url(self): # type: () -> str path=parsed.path, ) - def find_packages(self, dependency): + def find_packages(self, dependency): # type: ("Dependency") -> List[Package] packages = [] constraint = dependency.constraint @@ -296,7 +304,9 @@ def find_packages(self, dependency): return packages - def package(self, name, version, extras=None): # type: (...) -> Package + def package( + self, name, version, extras=None + ): # type: (str, str, Optional[List[str]]) -> Package """ Retrieve the release information. @@ -320,7 +330,7 @@ def package(self, name, version, extras=None): # type: (...) -> Package return package - def find_links_for_package(self, package): + def find_links_for_package(self, package): # type: (Package) -> List[Link] page = self._get("/{}/".format(package.name.replace(".", "-"))) if page is None: return [] @@ -375,7 +385,7 @@ def _get_release_info(self, name, version): # type: (str, str) -> dict return data.asdict() - def _get(self, endpoint): # type: (str) -> Union[Page, None] + def _get(self, endpoint): # type: (str) -> Optional[Page] url = self._url + endpoint try: response = self.session.get(url) diff --git a/poetry/repositories/pool.py b/poetry/repositories/pool.py index ac712831d3a..d8181ba06c0 100644 --- a/poetry/repositories/pool.py +++ b/poetry/repositories/pool.py @@ -9,7 +9,8 @@ if TYPE_CHECKING: - from poetry.core.packages import Package + from poetry.core.packages import Dependency # noqa + from poetry.core.packages import Package # noqa class Pool(BaseRepository): @@ -108,12 +109,12 @@ def remove_repository(self, repository_name): # type: (str) -> Pool return self - def has_package(self, package): + def has_package(self, package): # type: ("Package") -> bool raise NotImplementedError() def package( self, name, version, extras=None, repository=None - ): # type: (str, str, List[str], str) -> Package + ): # type: (str, str, List[str], str) -> "Package" if repository is not None: repository = repository.lower() @@ -143,9 +144,7 @@ def package( raise PackageNotFound("Package {} ({}) not found.".format(name, version)) - def find_packages( - self, dependency, - ): + def find_packages(self, dependency): # type: ("Dependency") -> List["Package"] repository = dependency.source_name if repository is not None: repository = repository.lower() @@ -166,7 +165,7 @@ def find_packages( return packages - def search(self, query): + def search(self, query): # type: (str) -> List["Package"] from .legacy_repository import LegacyRepository results = [] diff --git a/poetry/repositories/pypi_repository.py b/poetry/repositories/pypi_repository.py index 715ba557379..951f305dbfc 100644 --- a/poetry/repositories/pypi_repository.py +++ b/poetry/repositories/pypi_repository.py @@ -45,7 +45,9 @@ class PyPiRepository(RemoteRepository): CACHE_VERSION = parse_constraint("1.0.0") - def __init__(self, url="https://pypi.org/", disable_cache=False, fallback=True): + def __init__( + self, url="https://pypi.org/", disable_cache=False, fallback=True + ): # type: (str, bool, bool) -> None super(PyPiRepository, self).__init__(url.rstrip("/") + "/simple/") self._base_url = url @@ -72,7 +74,7 @@ def __init__(self, url="https://pypi.org/", disable_cache=False, fallback=True): self._name = "PyPI" @property - def session(self): + def session(self): # type: () -> CacheControl return self._session def find_packages(self, dependency): # type: (Dependency) -> List[Package] @@ -156,7 +158,7 @@ def package( ): # type: (...) -> Package return self.get_release_info(name, version).to_package(name=name, extras=extras) - def search(self, query): + def search(self, query): # type: (str) -> List[Package] results = [] search = {"q": query} @@ -236,7 +238,7 @@ def get_release_info(self, name, version): # type: (str, str) -> PackageInfo return PackageInfo.load(cached) - def find_links_for_package(self, package): + def find_links_for_package(self, package): # type: (Package) -> List[Link] json_data = self._get("pypi/{}/{}/json".format(package.name, package.version)) if json_data is None: return [] @@ -452,5 +454,5 @@ def _get_info_from_sdist(self, url): # type: (str) -> PackageInfo def _download(self, url, dest): # type: (str, str) -> None return download_file(url, dest, session=self.session) - def _log(self, msg, level="info"): + def _log(self, msg, level="info"): # type: (str, str) -> None getattr(logger, level)("{}: {}".format(self._name, msg)) diff --git a/poetry/repositories/repository.py b/poetry/repositories/repository.py index 1ebe702bb9c..f65e44bafc8 100644 --- a/poetry/repositories/repository.py +++ b/poetry/repositories/repository.py @@ -1,3 +1,7 @@ +from typing import TYPE_CHECKING +from typing import List +from typing import Optional + from poetry.core.semver import VersionConstraint from poetry.core.semver import VersionRange from poetry.core.semver import parse_constraint @@ -5,8 +9,16 @@ from .base_repository import BaseRepository +if TYPE_CHECKING: + from poetry.core.packages import Dependency # noqa + from poetry.core.packages import Link # noqa + from poetry.core.packages import Package # noqa + + class Repository(BaseRepository): - def __init__(self, packages=None, name=None): + def __init__( + self, packages=None, name=None + ): # type: (List["Package"], str) -> None super(Repository, self).__init__() self._name = name @@ -18,17 +30,19 @@ def __init__(self, packages=None, name=None): self.add_package(package) @property - def name(self): + def name(self): # type: () -> str return self._name - def package(self, name, version, extras=None): + def package( + self, name, version, extras=None + ): # type: (str, str, Optional[List[str]]) -> "Package" name = name.lower() for package in self.packages: if name == package.name and package.version.text == version: return package.clone() - def find_packages(self, dependency): + def find_packages(self, dependency): # type: ("Dependency") -> List["Package"] constraint = dependency.constraint packages = [] ignored_pre_release_packages = [] @@ -71,7 +85,7 @@ def find_packages(self, dependency): return packages or ignored_pre_release_packages - def has_package(self, package): + def has_package(self, package): # type: ("Package") -> bool package_id = package.unique_name for repo_package in self.packages: @@ -80,10 +94,10 @@ def has_package(self, package): return False - def add_package(self, package): + def add_package(self, package): # type: ("Package") -> None self._packages.append(package) - def remove_package(self, package): + def remove_package(self, package): # type: ("Package") -> None package_id = package.unique_name index = None @@ -95,10 +109,10 @@ def remove_package(self, package): if index is not None: del self._packages[index] - def find_links_for_package(self, package): + def find_links_for_package(self, package): # type: ("Package") -> List["Link"] return [] - def search(self, query): + def search(self, query): # type: (str) -> List["Package"] results = [] for package in self.packages: @@ -107,5 +121,5 @@ def search(self, query): return results - def __len__(self): + def __len__(self): # type: () -> int return len(self._packages) diff --git a/poetry/utils/appdirs.py b/poetry/utils/appdirs.py index ab99506a028..aac3ad36076 100644 --- a/poetry/utils/appdirs.py +++ b/poetry/utils/appdirs.py @@ -5,11 +5,19 @@ import os import sys +from typing import TYPE_CHECKING +from typing import List +from typing import Union + + +if TYPE_CHECKING: + from poetry.utils._compat import Path # noqa + WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt") -def expanduser(path): +def expanduser(path): # type: (Union[str, "Path"]) -> str """ Expand ~ and ~user constructions. @@ -21,7 +29,7 @@ def expanduser(path): return expanded -def user_cache_dir(appname): +def user_cache_dir(appname): # type: (str) -> str r""" Return full path to the user-specific cache dir for this application. @@ -64,7 +72,7 @@ def user_cache_dir(appname): return path -def user_data_dir(appname, roaming=False): +def user_data_dir(appname, roaming=False): # type: (str, bool) -> str r""" Return full path to the user-specific data dir for this application. @@ -104,7 +112,7 @@ def user_data_dir(appname, roaming=False): return path -def user_config_dir(appname, roaming=True): +def user_config_dir(appname, roaming=True): # type: (str, bool) -> str """Return full path to the user-specific config dir for this application. "appname" is the name of application. @@ -137,7 +145,7 @@ def user_config_dir(appname, roaming=True): # for the discussion regarding site_config_dirs locations # see -def site_config_dirs(appname): +def site_config_dirs(appname): # type: (str) -> List[str] r"""Return a list of potential user-shared config dirs for this application. "appname" is the name of application. @@ -178,7 +186,7 @@ def site_config_dirs(appname): # -- Windows support functions -- -def _get_win_folder_from_registry(csidl_name): +def _get_win_folder_from_registry(csidl_name): # type: (str) -> str """ This is a fallback technique at best. I'm not sure if using the registry for this guarantees us the correct answer for all CSIDL_* @@ -200,7 +208,7 @@ def _get_win_folder_from_registry(csidl_name): return directory -def _get_win_folder_with_ctypes(csidl_name): +def _get_win_folder_with_ctypes(csidl_name): # type: (str) -> str csidl_const = { "CSIDL_APPDATA": 26, "CSIDL_COMMON_APPDATA": 35, @@ -234,7 +242,7 @@ def _get_win_folder_with_ctypes(csidl_name): _get_win_folder = _get_win_folder_from_registry -def _win_path_to_bytes(path): +def _win_path_to_bytes(path): # type: (str) -> Union[str, bytes] """Encode Windows paths to bytes. Only used on Python 2. Motivation is to be consistent with other operating systems where paths diff --git a/poetry/utils/env.py b/poetry/utils/env.py index 22db246911e..77a185130d5 100644 --- a/poetry/utils/env.py +++ b/poetry/utils/env.py @@ -15,6 +15,7 @@ from subprocess import CalledProcessError from typing import Any from typing import Dict +from typing import Iterator from typing import List from typing import Optional from typing import Tuple @@ -252,7 +253,7 @@ def find(self, path, writable_only=False): # type: (Path, bool) -> List[Path] if value[-1] is True ] - def __getattr__(self, item): + def __getattr__(self, item): # type: (str) -> Any try: return super(SitePackages, self).__getattribute__(item) except AttributeError: @@ -265,7 +266,9 @@ class EnvError(Exception): class EnvCommandError(EnvError): - def __init__(self, e, input=None): # type: (CalledProcessError) -> None + def __init__( + self, e, input=None + ): # type: (CalledProcessError, Optional[str]) -> None self.e = e message = "Command {} errored with the following return code {}, and output: \n{}".format( @@ -277,7 +280,7 @@ def __init__(self, e, input=None): # type: (CalledProcessError) -> None class NoCompatiblePythonVersionFound(EnvError): - def __init__(self, expected, given=None): + def __init__(self, expected, given=None): # type: (str, Optional[str]) -> None if given: message = ( "The specified Python version ({}) " @@ -618,7 +621,7 @@ def remove(self, python): # type: (str) -> Env def create_venv( self, io, name=None, executable=None, force=False - ): # type: (IO, Optional[str], Optional[str], bool) -> Env + ): # type: (IO, Optional[str], Optional[str], bool) -> VirtualEnv if self._env is not None and not force: return self._env @@ -849,12 +852,12 @@ def remove_venv(cls, path): # type: (Union[Path,str]) -> None def get_base_prefix(self): # type: () -> Path if hasattr(sys, "real_prefix"): - return sys.real_prefix + return Path(sys.real_prefix) if hasattr(sys, "base_prefix"): - return sys.base_prefix + return Path(sys.base_prefix) - return sys.prefix + return Path(sys.prefix) @classmethod def generate_env_name(cls, name, cwd): # type: (str, str) -> str @@ -913,7 +916,7 @@ def python(self): # type: () -> str return self._bin("python") @property - def marker_env(self): + def marker_env(self): # type: () -> Dict[str, Any] if self._marker_env is None: self._marker_env = self.get_marker_env() @@ -935,7 +938,7 @@ def os(self): # type: () -> str return os.name @property - def pip_version(self): + def pip_version(self): # type: () -> Version if self._pip_version is None: self._pip_version = self.get_pip_version() @@ -1009,12 +1012,12 @@ def supported_tags(self): # type: () -> List[Tag] @classmethod def get_base_prefix(cls): # type: () -> Path if hasattr(sys, "real_prefix"): - return sys.real_prefix + return Path(sys.real_prefix) if hasattr(sys, "base_prefix"): - return sys.base_prefix + return Path(sys.base_prefix) - return sys.prefix + return Path(sys.prefix) def get_version_info(self): # type: () -> Tuple[int] raise NotImplementedError() @@ -1046,17 +1049,17 @@ def is_sane(self): # type: () -> bool """ return True - def run(self, bin, *args, **kwargs): + def run(self, bin, *args, **kwargs): # type: (str, *str, **Any) -> Union[str, int] bin = self._bin(bin) cmd = [bin] + list(args) return self._run(cmd, **kwargs) - def run_pip(self, *args, **kwargs): + def run_pip(self, *args, **kwargs): # type: (*str, **Any) -> Union[int, str] pip = self.get_pip_command() cmd = pip + list(args) return self._run(cmd, **kwargs) - def _run(self, cmd, **kwargs): + def _run(self, cmd, **kwargs): # type: (List[str], **Any) -> Union[int, str] """ Run a command inside the Python environment. """ @@ -1077,7 +1080,7 @@ def _run(self, cmd, **kwargs): stderr=subprocess.STDOUT, input=encode(input_), check=True, - **kwargs + **kwargs, ).stdout elif call: return subprocess.call(cmd, stderr=subprocess.STDOUT, **kwargs) @@ -1090,7 +1093,9 @@ def _run(self, cmd, **kwargs): return decode(output) - def execute(self, bin, *args, **kwargs): + def execute( + self, bin, *args, **kwargs + ): # type: (str, *str, **Any) -> Optional[int] bin = self._bin(bin) if not self._is_windows: @@ -1145,7 +1150,7 @@ def _bin(self, bin): # type: (str) -> str def __eq__(self, other): # type: (Env) -> bool return other.__class__ == self.__class__ and other.path == self.path - def __repr__(self): + def __repr__(self): # type: () -> str return '{}("{}")'.format(self.__class__.__name__, self._path) @@ -1332,11 +1337,11 @@ def get_paths(self): # type: () -> Dict[str, str] def is_venv(self): # type: () -> bool return True - def is_sane(self): + def is_sane(self): # type: () -> bool # A virtualenv is considered sane if both "python" and "pip" exist. return os.path.exists(self.python) and os.path.exists(self._bin("pip")) - def _run(self, cmd, **kwargs): + def _run(self, cmd, **kwargs): # type: (List[str], **Any) -> Optional[int] with self.temp_environ(): os.environ["PATH"] = self._updated_path() os.environ["VIRTUAL_ENV"] = str(self._path) @@ -1346,7 +1351,9 @@ def _run(self, cmd, **kwargs): return super(VirtualEnv, self)._run(cmd, **kwargs) - def execute(self, bin, *args, **kwargs): + def execute( + self, bin, *args, **kwargs + ): # type: (str, *str, **Any) -> Optional[int] with self.temp_environ(): os.environ["PATH"] = self._updated_path() os.environ["VIRTUAL_ENV"] = str(self._path) @@ -1357,7 +1364,7 @@ def execute(self, bin, *args, **kwargs): return super(VirtualEnv, self).execute(bin, *args, **kwargs) @contextmanager - def temp_environ(self): + def temp_environ(self): # type: () -> Iterator[None] environ = dict(os.environ) try: yield @@ -1365,16 +1372,18 @@ def temp_environ(self): os.environ.clear() os.environ.update(environ) - def unset_env(self, key): + def unset_env(self, key): # type: (str) -> None if key in os.environ: del os.environ[key] - def _updated_path(self): + def _updated_path(self): # type: () -> str return os.pathsep.join([str(self._bin_dir), os.environ.get("PATH", "")]) class NullEnv(SystemEnv): - def __init__(self, path=None, base=None, execute=False): + def __init__( + self, path=None, base=None, execute=False + ): # type: (Path, Optional[Path], bool) -> None if path is None: path = Path(sys.prefix) @@ -1386,35 +1395,37 @@ def __init__(self, path=None, base=None, execute=False): def get_pip_command(self): # type: () -> List[str] return [self._bin("python"), "-m", "pip"] - def _run(self, cmd, **kwargs): + def _run(self, cmd, **kwargs): # type: (List[str], **Any) -> int self.executed.append(cmd) if self._execute: return super(NullEnv, self)._run(cmd, **kwargs) - def execute(self, bin, *args, **kwargs): + def execute( + self, bin, *args, **kwargs + ): # type: (str, *str, **Any) -> Optional[int] self.executed.append([bin] + list(args)) if self._execute: return super(NullEnv, self).execute(bin, *args, **kwargs) - def _bin(self, bin): + def _bin(self, bin): # type: (str) -> str return bin class MockEnv(NullEnv): def __init__( self, - version_info=(3, 7, 0), - python_implementation="CPython", - platform="darwin", - os_name="posix", - is_venv=False, - pip_version="19.1", - sys_path=None, - marker_env=None, - supported_tags=None, - **kwargs + version_info=(3, 7, 0), # type: Tuple[int, int, int] + python_implementation="CPython", # type: str + platform="darwin", # type: str + os_name="posix", # type: str + is_venv=False, # type: bool + pip_version="19.1", # type: str + sys_path=None, # type: Optional[List[str]] + marker_env=None, # type: Dict[str, Any] + supported_tags=None, # type: List[Tag] + **kwargs, # type: Any ): super(MockEnv, self).__init__(**kwargs) @@ -1437,11 +1448,11 @@ def os(self): # type: () -> str return self._os_name @property - def pip_version(self): + def pip_version(self): # type: () -> Version return self._pip_version @property - def sys_path(self): + def sys_path(self): # type: () -> List[str] if self._sys_path is None: return super(MockEnv, self).sys_path diff --git a/poetry/utils/extras.py b/poetry/utils/extras.py index c97d6ee79e5..cff3f5a1ff6 100644 --- a/poetry/utils/extras.py +++ b/poetry/utils/extras.py @@ -1,3 +1,4 @@ +from typing import Iterable from typing import Iterator from typing import List from typing import Mapping @@ -36,7 +37,7 @@ def get_extra_package_names( # keep record of packages seen during recursion in order to avoid recursion error seen_package_names = set() - def _extra_packages(package_names): + def _extra_packages(package_names): # type: (Iterable[str]) -> Iterator[str] """Recursively find dependencies for packages names""" # for each extra pacakge name for package_name in package_names: diff --git a/poetry/utils/helpers.py b/poetry/utils/helpers.py index 909ec3c1438..b7bc0429045 100644 --- a/poetry/utils/helpers.py +++ b/poetry/utils/helpers.py @@ -6,6 +6,10 @@ from contextlib import contextmanager from pathlib import Path +from typing import Any +from typing import Callable +from typing import Dict +from typing import Iterator from typing import List from typing import Optional @@ -37,13 +41,13 @@ def normalize_version(version): # type: (str) -> str return str(Version(version)) -def _del_ro(action, name, exc): +def _del_ro(action, name, exc): # type: (Callable, str, Exception) -> None os.chmod(name, stat.S_IWRITE) os.remove(name) @contextmanager -def temporary_directory(*args, **kwargs): +def temporary_directory(*args, **kwargs): # type: (*Any, **Any) -> Iterator[str] name = tempfile.mkdtemp(*args, **kwargs) yield name @@ -67,7 +71,7 @@ def get_client_cert(config, repository_name): # type: (Config, str) -> Optional return None -def _on_rm_error(func, path, exc_info): +def _on_rm_error(func, path, exc_info): # type: (Callable, str, Exception) -> None if not os.path.exists(path): return @@ -75,14 +79,14 @@ def _on_rm_error(func, path, exc_info): func(path) -def safe_rmtree(path): +def safe_rmtree(path): # type: (str) -> 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/utils/password_manager.py b/poetry/utils/password_manager.py index 6c993840282..e6cc11d65e2 100644 --- a/poetry/utils/password_manager.py +++ b/poetry/utils/password_manager.py @@ -1,5 +1,12 @@ import logging +from typing import TYPE_CHECKING +from typing import Dict +from typing import Optional + + +if TYPE_CHECKING: + from poetry.config.config import Config # noqa logger = logging.getLogger(__name__) @@ -15,16 +22,16 @@ class KeyRingError(Exception): class KeyRing: - def __init__(self, namespace): + def __init__(self, namespace): # type: (str) -> None self._namespace = namespace self._is_available = True self._check() - def is_available(self): + def is_available(self): # type: () -> bool return self._is_available - def get_password(self, name, username): + def get_password(self, name, username): # type: (str, str) -> Optional[str] if not self.is_available(): return @@ -40,7 +47,7 @@ def get_password(self, name, username): "Unable to retrieve the password for {} from the key ring".format(name) ) - def set_password(self, name, username, password): + def set_password(self, name, username, password): # type: (str, str, str) -> None if not self.is_available(): return @@ -58,7 +65,7 @@ def set_password(self, name, username, password): ) ) - def delete_password(self, name, username): + def delete_password(self, name, username): # type: (str, str) -> None if not self.is_available(): return @@ -74,10 +81,10 @@ def delete_password(self, name, username): "Unable to delete the password for {} from the key ring".format(name) ) - def get_entry_name(self, name): + def get_entry_name(self, name): # type: (str) -> str return "{}-{}".format(self._namespace, name) - def _check(self): + def _check(self): # type: () -> None try: import keyring except Exception as e: @@ -113,12 +120,12 @@ def _check(self): class PasswordManager: - def __init__(self, config): + def __init__(self, config): # type: ("Config") -> None self._config = config self._keyring = None @property - def keyring(self): + def keyring(self): # type: () -> KeyRing if self._keyring is None: self._keyring = KeyRing("poetry-repository") if not self._keyring.is_available(): @@ -128,7 +135,7 @@ def keyring(self): return self._keyring - def set_pypi_token(self, name, token): + def set_pypi_token(self, name, token): # type: (str, str) -> None if not self.keyring.is_available(): self._config.auth_config_source.add_property( "pypi-token.{}".format(name), token @@ -136,13 +143,13 @@ def set_pypi_token(self, name, token): else: self.keyring.set_password(name, "__token__", token) - def get_pypi_token(self, name): + def get_pypi_token(self, name): # type: (str) -> str if not self.keyring.is_available(): return self._config.get("pypi-token.{}".format(name)) return self.keyring.get_password(name, "__token__") - def delete_pypi_token(self, name): + def delete_pypi_token(self, name): # type: (str) -> None if not self.keyring.is_available(): return self._config.auth_config_source.remove_property( "pypi-token.{}".format(name) @@ -150,7 +157,7 @@ def delete_pypi_token(self, name): self.keyring.delete_password(name, "__token__") - def get_http_auth(self, name): + def get_http_auth(self, name): # type: (str) -> Optional[Dict[str, str]] auth = self._config.get("http-basic.{}".format(name)) if not auth: username = self._config.get("http-basic.{}.username".format(name)) @@ -167,7 +174,9 @@ def get_http_auth(self, name): "password": password, } - def set_http_password(self, name, username, password): + def set_http_password( + self, name, username, password + ): # type: (str, str, str) -> None auth = {"username": username} if not self.keyring.is_available(): @@ -177,7 +186,7 @@ def set_http_password(self, name, username, password): self._config.auth_config_source.add_property("http-basic.{}".format(name), auth) - def delete_http_password(self, name): + def delete_http_password(self, name): # type: (str) -> None auth = self.get_http_auth(name) if not auth or "username" not in auth: return diff --git a/poetry/utils/setup_reader.py b/poetry/utils/setup_reader.py index 498210f45f5..48d511f9252 100644 --- a/poetry/utils/setup_reader.py +++ b/poetry/utils/setup_reader.py @@ -356,7 +356,9 @@ def _find_variable_in_body( if target.id == name: return elem.value - def _find_in_dict(self, dict_, name): # type: (ast.Call, str) -> Optional[Any] + def _find_in_dict( + self, dict_, name + ): # type: (Union[ast.Dict, ast.Call], str) -> Optional[Any] for key, val in zip(dict_.keys, dict_.values): if isinstance(key, ast.Str) and key.s == name: return val diff --git a/poetry/utils/shell.py b/poetry/utils/shell.py index 3c0e5fbe92c..e009903a6de 100644 --- a/poetry/utils/shell.py +++ b/poetry/utils/shell.py @@ -3,6 +3,7 @@ import sys from pathlib import Path +from typing import Any import pexpect @@ -78,7 +79,7 @@ def activate(self, env): # type: (VirtualEnv) -> None activate_path = env.path / bin_dir / activate_script c.sendline("{} {}".format(self._get_source_command(), activate_path)) - def resize(sig, data): + def resize(sig, data): # type: (Any, Any) -> None terminal = Terminal() c.setwinsize(terminal.height, terminal.width) @@ -90,7 +91,7 @@ def resize(sig, data): sys.exit(c.exitstatus) - def _get_activate_script(self): + def _get_activate_script(self): # type: () -> str if "fish" == self._name: suffix = ".fish" elif "csh" == self._name: @@ -102,7 +103,7 @@ def _get_activate_script(self): return "activate" + suffix - def _get_source_command(self): + def _get_source_command(self): # type: () -> str if "fish" == self._name: return "source" elif "csh" == self._name: diff --git a/poetry/version/version_selector.py b/poetry/version/version_selector.py index ea002860938..350e3ec7ca5 100644 --- a/poetry/version/version_selector.py +++ b/poetry/version/version_selector.py @@ -1,19 +1,25 @@ +from typing import TYPE_CHECKING +from typing import Optional from typing import Union from poetry.core.packages import Package from poetry.core.semver import Version +if TYPE_CHECKING: + from poetry.repositories import Pool # noqa + + class VersionSelector(object): - def __init__(self, pool): + def __init__(self, pool): # type: ("Pool") -> None self._pool = pool def find_best_candidate( self, package_name, # type: str - target_package_version=None, # type: Union[str, None] + target_package_version=None, # type: Optional[str] allow_prereleases=False, # type: bool - source=None, # type: str + source=None, # type: Optional[str] ): # type: (...) -> Union[Package, bool] """ Given a package name and optional version, @@ -52,12 +58,12 @@ def find_best_candidate( return False return package - def find_recommended_require_version(self, package): + def find_recommended_require_version(self, package): # type: (Package) -> str version = package.version return self._transform_version(version.text, package.pretty_version) - def _transform_version(self, version, pretty_version): + def _transform_version(self, version, pretty_version): # type: (str, str) -> str try: parsed = Version.parse(version) parts = [parsed.major, parsed.minor, parsed.patch] From d2485b8f849f0bbfd8f6933b625ea07f11f80615 Mon Sep 17 00:00:00 2001 From: Ryan Opel Date: Mon, 14 Sep 2020 10:36:57 -0700 Subject: [PATCH 42/54] Add option to install only dev dependencies Add an option to poetry install to install only dev dependencies, e.g. poetry install --dev-only. Fixes https://github.com/python-poetry/poetry/issues/2572 --- docs/docs/cli.md | 11 ++++++++++ poetry/console/commands/install.py | 4 +++- poetry/installation/installer.py | 12 +++++++++++ tests/installation/test_installer.py | 32 +++++++++++++++++++++++++++- 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/docs/docs/cli.md b/docs/docs/cli.md index 9ceb059bfe7..4675ce1023b 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -109,6 +109,13 @@ the `--no-dev` option. poetry install --no-dev ``` +Conversely, you can specify to the command that you only want to install the development dependencies +by passing the `--dev-only` option. Note that `--no-dev` takes priority if both options are passed. + +```bash +poetry install --dev-only +``` + If you want to remove old dependencies no longer present in the lock file, use the `--remove-untracked` option. @@ -142,9 +149,13 @@ If you want to skip this installation, use the `--no-root` option. poetry install --no-root ``` +Installation of your project's package is also skipped when the `--dev-only` +option is passed. + ### Options * `--no-dev`: Do not install dev dependencies. +* `--dev-only`: Only install dev dependencies. * `--no-root`: Do not install the root package (your project). * `--dry-run`: Output the operations but do not execute anything (implicitly enables --verbose). * `--remove-untracked`: Remove dependencies not presented in the lock file diff --git a/poetry/console/commands/install.py b/poetry/console/commands/install.py index 6a9ef2cb41d..af05e6a14f7 100644 --- a/poetry/console/commands/install.py +++ b/poetry/console/commands/install.py @@ -10,6 +10,7 @@ class InstallCommand(InstallerCommand): options = [ option("no-dev", None, "Do not install the development dependencies."), + option("dev-only", None, "Only install the development dependencies."), option( "no-root", None, "Do not install the root package (the current project)." ), @@ -64,6 +65,7 @@ def handle(self): self._installer.extras(extras) self._installer.dev_mode(not self.option("no-dev")) + self._installer.dev_only(self.option("dev-only")) self._installer.dry_run(self.option("dry-run")) self._installer.remove_untracked(self.option("remove-untracked")) self._installer.verbose(self._io.is_verbose()) @@ -73,7 +75,7 @@ def handle(self): if return_code != 0: return return_code - if self.option("no-root"): + if self.option("no-root") or self.option("dev-only"): return 0 try: diff --git a/poetry/installation/installer.py b/poetry/installation/installer.py index 2164d39f4b1..ae566ef776e 100644 --- a/poetry/installation/installer.py +++ b/poetry/installation/installer.py @@ -47,6 +47,7 @@ def __init__( self._verbose = False self._write_lock = True self._dev_mode = True + self._dev_only = False self._execute_operations = True self._lock = False @@ -136,6 +137,14 @@ def dev_mode(self, dev_mode=True): # type: (bool) -> Installer def is_dev_mode(self): # type: () -> bool return self._dev_mode + def dev_only(self, dev_only=False): # type: (bool) -> Installer + self._dev_only = dev_only + + return self + + def is_dev_only(self): # type: () -> bool + return self._dev_only + def update(self, update=True): # type: (bool) -> Installer self._update = update @@ -270,6 +279,9 @@ def _do_install(self, local_repo): if not self.is_dev_mode(): root = root.clone() del root.dev_requires[:] + elif self.is_dev_only(): + root = root.clone() + del root.requires[:] if self._io.is_verbose(): self._io.write_line("") diff --git a/tests/installation/test_installer.py b/tests/installation/test_installer.py index 2adcb13cd25..67ee09b0c2c 100644 --- a/tests/installation/test_installer.py +++ b/tests/installation/test_installer.py @@ -267,7 +267,10 @@ def test_run_update_after_removing_dependencies( assert 1 == installer.executor.removals_count -def test_run_install_no_dev(installer, locker, repo, package, installed): +def _configure_run_install_dev(locker, repo, package, installed): + """ + Perform common test setup for `test_run_install_*dev*()` methods. + """ locker.locked(True) locker.mock_lock_data( { @@ -323,7 +326,34 @@ def test_run_install_no_dev(installer, locker, repo, package, installed): package.add_dependency(Factory.create_dependency("B", "~1.1")) package.add_dependency(Factory.create_dependency("C", "~1.2", category="dev")) + +def test_run_install_no_dev(installer, locker, repo, package, installed): + _configure_run_install_dev(locker, repo, package, installed) + + installer.dev_mode(False) + installer.run() + + assert 0 == installer.executor.installations_count + assert 0 == installer.executor.updates_count + assert 1 == installer.executor.removals_count + + +def test_run_install_dev_only(installer, locker, repo, package, installed): + _configure_run_install_dev(locker, repo, package, installed) + + installer.dev_only(True) + installer.run() + + assert 0 == installer.executor.installations_count + assert 0 == installer.executor.updates_count + assert 2 == installer.executor.removals_count + + +def test_run_install_no_dev_and_dev_only(installer, locker, repo, package, installed): + _configure_run_install_dev(locker, repo, package, installed) + installer.dev_mode(False) + installer.dev_only(True) installer.run() assert 0 == installer.executor.installations_count From a8d02ef59f60c001458f27b7cb2557a346cfb6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 22 Jan 2021 18:21:28 +0100 Subject: [PATCH 43/54] Migrate to the new version of Cleo --- .flake8 | 4 +- .pre-commit-config.yaml | 14 +- poetry.lock | 42 +-- poetry/__main__.py | 2 +- poetry/console/__init__.py | 5 - poetry/console/application.py | 321 +++++++++++++----- poetry/console/commands/__init__.py | 18 - poetry/console/commands/add.py | 4 +- poetry/console/commands/build.py | 2 +- poetry/console/commands/cache/cache.py | 15 - poetry/console/commands/cache/clear.py | 6 +- poetry/console/commands/cache/list.py | 2 +- poetry/console/commands/command.py | 2 +- poetry/console/commands/config.py | 10 +- poetry/console/commands/debug/debug.py | 11 - poetry/console/commands/debug/info.py | 9 +- poetry/console/commands/debug/resolve.py | 23 +- poetry/console/commands/env/env.py | 16 - poetry/console/commands/env/info.py | 4 +- poetry/console/commands/env/list.py | 4 +- poetry/console/commands/env/remove.py | 4 +- poetry/console/commands/env/use.py | 4 +- poetry/console/commands/export.py | 4 +- poetry/console/commands/init.py | 2 +- poetry/console/commands/install.py | 10 +- poetry/console/commands/lock.py | 2 +- poetry/console/commands/new.py | 4 +- poetry/console/commands/publish.py | 2 +- poetry/console/commands/remove.py | 4 +- poetry/console/commands/run.py | 20 +- poetry/console/commands/search.py | 2 +- poetry/console/commands/self/self.py | 13 - poetry/console/commands/self/update.py | 13 +- poetry/console/commands/show.py | 27 +- poetry/console/commands/update.py | 4 +- poetry/console/commands/version.py | 4 +- poetry/console/config/__init__.py | 1 - poetry/console/config/application_config.py | 244 ------------- poetry/console/exceptions.py | 6 + poetry/{ => console}/io/__init__.py | 0 poetry/console/io/inputs/__init__.py | 0 poetry/console/io/inputs/run_argv_input.py | 81 +++++ poetry/factory.py | 4 +- poetry/installation/authenticator.py | 2 +- poetry/installation/executor.py | 58 ++-- poetry/installation/installer.py | 4 +- poetry/installation/pip_installer.py | 5 +- poetry/io/null_io.py | 13 - poetry/packages/locker.py | 4 +- poetry/publishing/uploader.py | 8 +- poetry/puzzle/provider.py | 16 +- poetry/puzzle/solver.py | 9 +- poetry/repositories/installed_repository.py | 6 +- poetry/utils/env.py | 2 +- poetry/utils/exporter.py | 2 +- pyproject.toml | 5 +- sonnet | 52 +-- tests/conftest.py | 15 +- tests/console/commands/debug/test_resolve.py | 12 +- tests/console/commands/env/test_use.py | 11 +- tests/console/commands/self/test_update.py | 2 +- tests/console/commands/test_add.py | 52 +-- tests/console/commands/test_init.py | 4 +- tests/console/commands/test_publish.py | 11 +- tests/console/commands/test_run.py | 14 +- tests/console/commands/test_search.py | 3 +- tests/console/commands/test_show.py | 20 +- tests/console/commands/test_version.py | 2 +- tests/console/conftest.py | 5 +- tests/helpers.py | 2 +- tests/inspection/test_info.py | 3 +- tests/installation/test_authenticator.py | 6 +- tests/installation/test_chef.py | 4 +- tests/installation/test_chooser.py | 20 +- tests/installation/test_executor.py | 20 +- tests/installation/test_installer.py | 14 +- tests/installation/test_installer_old.py | 2 +- tests/installation/test_pip_installer.py | 3 +- .../pep_561_stub_only/pkg-stubs/module.pyi | 1 + .../pkg-stubs/module.pyi | 1 + .../src/pkg-stubs/module.pyi | 1 + .../masonry/builders/test_editable_builder.py | 12 +- .../test_python_requirement_solution.py | 2 +- tests/mixology/version_solver/conftest.py | 2 +- tests/publishing/test_publisher.py | 4 +- tests/publishing/test_uploader.py | 3 +- tests/puzzle/test_provider.py | 2 +- tests/puzzle/test_solver.py | 14 +- tests/utils/test_env.py | 38 ++- tests/utils/test_exporter.py | 24 +- 90 files changed, 753 insertions(+), 731 deletions(-) delete mode 100644 poetry/console/commands/cache/cache.py delete mode 100644 poetry/console/commands/debug/debug.py delete mode 100644 poetry/console/commands/env/env.py delete mode 100644 poetry/console/commands/self/self.py delete mode 100644 poetry/console/config/__init__.py delete mode 100644 poetry/console/config/application_config.py create mode 100644 poetry/console/exceptions.py rename poetry/{ => console}/io/__init__.py (100%) create mode 100644 poetry/console/io/inputs/__init__.py create mode 100644 poetry/console/io/inputs/run_argv_input.py delete mode 100644 poetry/io/null_io.py diff --git a/.flake8 b/.flake8 index 130c44c6752..6005a30d32d 100644 --- a/.flake8 +++ b/.flake8 @@ -1,7 +1,9 @@ [flake8] max-line-length = 88 ignore = E501, E203, W503 -per-file-ignores = __init__.py:F401 +per-file-ignores = + __init__.py:F401 + tests/console/commands/debug/test_resolve.py:W291 exclude = .git __pycache__ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7766f56e108..9377e9a6992 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,26 +1,30 @@ repos: - repo: https://github.com/psf/black - rev: 19.10b0 + rev: 20.8b1 hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.3 + rev: 3.8.4 hooks: - id: flake8 - repo: https://github.com/timothycrosley/isort - rev: 5.4.2 + rev: 5.7.0 hooks: - id: isort additional_dependencies: [toml] exclude: ^.*/?setup\.py$ - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v3.4.0 hooks: - id: trailing-whitespace - exclude: ^tests/.*/fixtures/.* + exclude: | + (?x)( + ^tests/.*/fixtures/.* + | ^tests/console/commands/debug/test_resolve.py + ) - id: end-of-file-fixer exclude: ^tests/.*/fixtures/.* - id: debug-statements diff --git a/poetry.lock b/poetry.lock index b066ade9a7c..6a04096460f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -95,27 +95,15 @@ python-versions = "*" [[package]] name = "cleo" -version = "0.8.1" +version = "1.0.0a1" description = "Cleo allows you to create beautiful and testable command-line interfaces." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -clikit = ">=0.6.0,<0.7.0" - -[[package]] -name = "clikit" -version = "0.6.2" -description = "CliKit is a group of utilities to build beautiful and testable command line interfaces." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6,<4.0" [package.dependencies] -crashtest = {version = ">=0.3.0,<0.4.0", markers = "python_version >= \"3.6\" and python_version < \"4.0\""} -pastel = ">=0.2.0,<0.3.0" -pylev = ">=1.3,<2.0" +crashtest = ">=0.3.1,<0.4.0" +pylev = ">=1.3.0,<2.0.0" [[package]] name = "colorama" @@ -326,14 +314,6 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" pyparsing = ">=2.0.2" six = "*" -[[package]] -name = "pastel" -version = "0.2.1" -description = "Bring colors to your terminal." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - [[package]] name = "pexpect" version = "4.8.0" @@ -688,7 +668,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 = "077bd512e57f2e31d9f8b72b9a8b3a918f6844afdd3c7e25c2babc7f95fdfc4e" +content-hash = "c495920c853f794d4046d2d4cc47410ea076d4647e54fd3364e46051d5f18da3" [metadata.files] appdirs = [ @@ -762,12 +742,8 @@ chardet = [ {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, ] cleo = [ - {file = "cleo-0.8.1-py2.py3-none-any.whl", hash = "sha256:141cda6dc94a92343be626bb87a0b6c86ae291dfc732a57bf04310d4b4201753"}, - {file = "cleo-0.8.1.tar.gz", hash = "sha256:3d0e22d30117851b45970b6c14aca4ab0b18b1b53c8af57bed13208147e4069f"}, -] -clikit = [ - {file = "clikit-0.6.2-py2.py3-none-any.whl", hash = "sha256:71268e074e68082306e23d7369a7b99f824a0ef926e55ba2665e911f7208489e"}, - {file = "clikit-0.6.2.tar.gz", hash = "sha256:442ee5db9a14120635c5990bcdbfe7c03ada5898291f0c802f77be71569ded59"}, + {file = "cleo-1.0.0a1-py3-none-any.whl", hash = "sha256:e4a45adc6b56a04d350e7b4893352fdcc07d89d35991e5df16753e05a7c78c2b"}, + {file = "cleo-1.0.0a1.tar.gz", hash = "sha256:45bc5f04278c2f183c7ab77b3ec20f5204711fecb37ae688424c39ea8badf3fe"}, ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, @@ -912,10 +888,6 @@ packaging = [ {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, ] -pastel = [ - {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, - {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, -] pexpect = [ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, diff --git a/poetry/__main__.py b/poetry/__main__.py index b280ed84e4a..1c442536107 100644 --- a/poetry/__main__.py +++ b/poetry/__main__.py @@ -2,6 +2,6 @@ if __name__ == "__main__": - from .console import main + from .console.application import main sys.exit(main()) diff --git a/poetry/console/__init__.py b/poetry/console/__init__.py index 56adf27e2ee..e69de29bb2d 100644 --- a/poetry/console/__init__.py +++ b/poetry/console/__init__.py @@ -1,5 +0,0 @@ -from .application import Application - - -def main(): # type: () -> int - return Application().run() diff --git a/poetry/console/application.py b/poetry/console/application.py index 1fd9dbc0bc3..75d759efdc9 100644 --- a/poetry/console/application.py +++ b/poetry/console/application.py @@ -1,34 +1,77 @@ -import sys +import logging +import re +from importlib import import_module from typing import TYPE_CHECKING +from typing import Any +from typing import Callable +from typing import Optional +from typing import cast -from cleo import Application as BaseApplication +from cleo.application import Application as BaseApplication +from cleo.events.console_command_event import ConsoleCommandEvent +from cleo.events.console_events import COMMAND +from cleo.events.event_dispatcher import EventDispatcher +from cleo.exceptions import CleoException +from cleo.formatters.style import Style +from cleo.io.inputs.argv_input import ArgvInput +from cleo.io.inputs.input import Input +from cleo.io.io import IO +from cleo.io.outputs.output import Output +from cleo.loaders.factory_command_loader import FactoryCommandLoader from poetry.__version__ import __version__ -from .commands.about import AboutCommand -from .commands.add import AddCommand -from .commands.build import BuildCommand -from .commands.cache.cache import CacheCommand -from .commands.check import CheckCommand -from .commands.config import ConfigCommand -from .commands.debug.debug import DebugCommand -from .commands.env.env import EnvCommand -from .commands.export import ExportCommand -from .commands.init import InitCommand -from .commands.install import InstallCommand -from .commands.lock import LockCommand -from .commands.new import NewCommand -from .commands.publish import PublishCommand -from .commands.remove import RemoveCommand -from .commands.run import RunCommand -from .commands.search import SearchCommand -from .commands.self.self import SelfCommand -from .commands.shell import ShellCommand -from .commands.show import ShowCommand -from .commands.update import UpdateCommand -from .commands.version import VersionCommand -from .config import ApplicationConfig +from .commands.command import Command + + +def load_command(name: str) -> Callable: + def _load(): + module = import_module( + "poetry.console.commands.{}".format(".".join(name.split(" "))) + ) + command_class = getattr( + module, "{}Command".format("".join(c.title() for c in name.split(" "))) + ) + + return command_class() + + return _load + + +COMMANDS = [ + "about", + "add", + "build", + "check", + "config", + "export", + "init", + "install", + "lock", + "new", + "publish", + "remove", + "run", + "search", + "shell", + "show", + "update", + "version", + # Cache commands + "cache clear", + "cache list", + # Debug commands + "debug info", + "debug resolve", + # Env commands + "env info", + "env list", + "env remove", + "env use", + # Self commands + "self update", +] if TYPE_CHECKING: @@ -36,36 +79,24 @@ class Application(BaseApplication): - def __init__(self): # type: () -> None - super(Application, self).__init__( - "poetry", __version__, config=ApplicationConfig("poetry", __version__) - ) + def __init__(self) -> None: + super(Application, self).__init__("poetry", __version__) self._poetry = None - for command in self.get_default_commands(): - self.add(command) - - if sys.version_info[:2] < (3, 6): - python_version = "{}".format( - ".".join(str(v) for v in sys.version_info[:2]) - ) - poetry_feature_release = "1.2" - message = ( - "\n" - "Python {} will no longer be supported " - "in the next feature release of Poetry ({}).\n" - "You should consider updating your Python version to a supported one.\n\n" - "" - "Note that you will still be able to manage Python {} projects " - "by using the env command.\n" - "See https://python-poetry.org/docs/managing-environments/ " - "for more information." - ).format(python_version, poetry_feature_release, python_version) - self._preliminary_io.error_line("{}\n".format(message)) + dispatcher = EventDispatcher() + dispatcher.add_listener(COMMAND, self.register_command_loggers) + dispatcher.add_listener(COMMAND, self.set_env) + dispatcher.add_listener(COMMAND, self.set_installer) + self.set_event_dispatcher(dispatcher) + + command_loader = FactoryCommandLoader( + {name: load_command(name) for name in COMMANDS} + ) + self.set_command_loader(command_loader) @property - def poetry(self): # type: () -> "Poetry" + def poetry(self) -> "Poetry": from pathlib import Path from poetry.factory import Factory @@ -77,45 +108,173 @@ def poetry(self): # type: () -> "Poetry" return self._poetry - def reset_poetry(self): # type: () -> None + def reset_poetry(self) -> None: self._poetry = None - def get_default_commands(self): # type: () -> list - commands = [ - AboutCommand(), - AddCommand(), - BuildCommand(), - CheckCommand(), - ConfigCommand(), - ExportCommand(), - InitCommand(), - InstallCommand(), - LockCommand(), - NewCommand(), - PublishCommand(), - RemoveCommand(), - RunCommand(), - SearchCommand(), - ShellCommand(), - ShowCommand(), - UpdateCommand(), - VersionCommand(), + def create_io( + self, + input: Optional[Input] = None, + output: Optional[Output] = None, + error_output: Optional[Output] = None, + ) -> IO: + io = super(Application, self).create_io(input, output, error_output) + + # Set our own CLI styles + formatter = io.output.formatter + formatter.set_style("c1", Style("cyan")) + formatter.set_style("c2", Style("default", options=["bold"])) + formatter.set_style("info", Style("blue")) + formatter.set_style("comment", Style("green")) + formatter.set_style("warning", Style("yellow")) + formatter.set_style("debug", Style("default", options=["dark"])) + formatter.set_style("success", Style("green")) + + # Dark variants + formatter.set_style("c1_dark", Style("cyan", options=["dark"])) + formatter.set_style("c2_dark", Style("default", options=["bold", "dark"])) + formatter.set_style("success_dark", Style("green", options=["dark"])) + + io.output.set_formatter(formatter) + io.error_output.set_formatter(formatter) + + return io + + def _configure_io(self, io: IO) -> None: + # We need to check if the command being run + # is the "run" command. + definition = self.definition + try: + io.input.bind(definition) + except CleoException: + pass + + name = io.input.first_argument + if name == "run": + from .io.inputs.run_argv_input import RunArgvInput + + input = cast(ArgvInput, io.input) + run_input = RunArgvInput([self._name or ""] + input._tokens) + # For the run command reset the definition + # with only the set options (i.e. the options given before the command) + for option_name, value in input.options.items(): + if value: + option = definition.option(option_name) + run_input.add_parameter_option("--" + option.name) + if option.shortcut: + shortcuts = re.split(r"\|-?", option.shortcut.lstrip("-")) + shortcuts = [s for s in shortcuts if s] + for shortcut in shortcuts: + run_input.add_parameter_option("-" + shortcut.lstrip("-")) + + try: + run_input.bind(definition) + except CleoException: + pass + + for option_name, value in input.options.items(): + if value: + run_input.set_option(option_name, value) + + io.set_input(run_input) + + return super()._configure_io(io) + + def register_command_loggers( + self, event: ConsoleCommandEvent, event_name: str, _: Any + ) -> None: + from .logging.io_formatter import IOFormatter + from .logging.io_handler import IOHandler + + command = event.command + if not isinstance(command, Command): + return + + io = event.io + + loggers = [ + "poetry.packages.locker", + "poetry.packages.package", + "poetry.utils.password_manager", ] - # Cache commands - commands += [CacheCommand()] + loggers += command.loggers + + handler = IOHandler(io) + handler.setFormatter(IOFormatter()) + + for logger in loggers: + logger = logging.getLogger(logger) + + logger.handlers = [handler] + + level = logging.WARNING + # The builders loggers are special and we can actually + # start at the INFO level. + if logger.name.startswith("poetry.core.masonry.builders"): + level = logging.INFO + + if io.is_debug(): + level = logging.DEBUG + elif io.is_very_verbose() or io.is_verbose(): + level = logging.INFO + + logger.setLevel(level) + + def set_env(self, event: ConsoleCommandEvent, event_name: str, _: Any): + from .commands.env_command import EnvCommand - # Debug command - commands += [DebugCommand()] + command: EnvCommand = cast(EnvCommand, event.command) + if not isinstance(command, EnvCommand): + return - # Env command - commands += [EnvCommand()] + if command.env is not None: + return + + from poetry.utils.env import EnvManager + + io = event.io + poetry = command.poetry + + env_manager = EnvManager(poetry) + env = env_manager.create_venv(io) + + if env.is_venv() and io.is_verbose(): + io.write_line("Using virtualenv: {}".format(env.path)) + + command.set_env(env) + + def set_installer( + self, event: ConsoleCommandEvent, event_name: str, _: Any + ) -> None: + from .commands.installer_command import InstallerCommand + + command: InstallerCommand = cast(InstallerCommand, event.command) + if not isinstance(command, InstallerCommand): + return + + # If the command already has an installer + # we skip this step + if command.installer is not None: + return + + from poetry.installation.installer import Installer + + poetry = command.poetry + installer = Installer( + event.io, + command.env, + poetry.package, + poetry.locker, + poetry.pool, + poetry.config, + ) + installer.use_executor(poetry.config.get("experimental.new-installer", False)) + command.set_installer(installer) - # Self commands - commands += [SelfCommand()] - return commands +def main(): + return Application().run() if __name__ == "__main__": - Application().run() + main() diff --git a/poetry/console/commands/__init__.py b/poetry/console/commands/__init__.py index b8cb3f4e1f4..e69de29bb2d 100644 --- a/poetry/console/commands/__init__.py +++ b/poetry/console/commands/__init__.py @@ -1,18 +0,0 @@ -from .about import AboutCommand -from .add import AddCommand -from .build import BuildCommand -from .check import CheckCommand -from .config import ConfigCommand -from .export import ExportCommand -from .init import InitCommand -from .install import InstallCommand -from .lock import LockCommand -from .new import NewCommand -from .publish import PublishCommand -from .remove import RemoveCommand -from .run import RunCommand -from .search import SearchCommand -from .shell import ShellCommand -from .show import ShowCommand -from .update import UpdateCommand -from .version import VersionCommand diff --git a/poetry/console/commands/add.py b/poetry/console/commands/add.py index a3868b4bff8..abfcc5b15f6 100644 --- a/poetry/console/commands/add.py +++ b/poetry/console/commands/add.py @@ -2,8 +2,8 @@ from typing import Dict from typing import List -from cleo import argument -from cleo import option +from cleo.helpers import argument +from cleo.helpers import option from .init import InitCommand from .installer_command import InstallerCommand diff --git a/poetry/console/commands/build.py b/poetry/console/commands/build.py index bd938764726..82b5b0f86c7 100644 --- a/poetry/console/commands/build.py +++ b/poetry/console/commands/build.py @@ -1,4 +1,4 @@ -from cleo import option +from cleo.helpers import option from .env_command import EnvCommand diff --git a/poetry/console/commands/cache/cache.py b/poetry/console/commands/cache/cache.py deleted file mode 100644 index ff04fecb041..00000000000 --- a/poetry/console/commands/cache/cache.py +++ /dev/null @@ -1,15 +0,0 @@ -from poetry.console.commands.cache.list import CacheListCommand - -from ..command import Command -from .clear import CacheClearCommand - - -class CacheCommand(Command): - - name = "cache" - description = "Interact with Poetry's cache" - - commands = [CacheClearCommand(), CacheListCommand()] - - def handle(self): # type: () -> int - return self.call("help", self._config.name) diff --git a/poetry/console/commands/cache/clear.py b/poetry/console/commands/cache/clear.py index 9969ebbaa1c..581f83c041d 100644 --- a/poetry/console/commands/cache/clear.py +++ b/poetry/console/commands/cache/clear.py @@ -1,14 +1,14 @@ import os -from cleo import argument -from cleo import option +from cleo.helpers import argument +from cleo.helpers import option from ..command import Command class CacheClearCommand(Command): - name = "clear" + name = "cache clear" description = "Clears Poetry's cache." arguments = [argument("cache", description="The name of the cache to clear.")] diff --git a/poetry/console/commands/cache/list.py b/poetry/console/commands/cache/list.py index 090cba601df..e6346f98ebb 100644 --- a/poetry/console/commands/cache/list.py +++ b/poetry/console/commands/cache/list.py @@ -7,7 +7,7 @@ class CacheListCommand(Command): - name = "list" + name = "cache list" description = "List Poetry's caches." def handle(self): # type: () -> Optional[int] diff --git a/poetry/console/commands/command.py b/poetry/console/commands/command.py index a575f46044a..cb13c867909 100644 --- a/poetry/console/commands/command.py +++ b/poetry/console/commands/command.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING -from cleo import Command as BaseCommand +from cleo.commands.command import Command as BaseCommand if TYPE_CHECKING: diff --git a/poetry/console/commands/config.py b/poetry/console/commands/config.py index 5b35eb5a26d..30dc11ecf50 100644 --- a/poetry/console/commands/config.py +++ b/poetry/console/commands/config.py @@ -8,8 +8,8 @@ from typing import Optional from typing import Tuple -from cleo import argument -from cleo import option +from cleo.helpers import argument +from cleo.helpers import option from poetry.core.pyproject import PyProjectException from poetry.core.toml.file import TOMLFile @@ -81,7 +81,11 @@ def unique_config_values(self): # type: () -> Dict[str, Tuple[Any, Any, Any]] boolean_normalizer, False, ), - "installer.parallel": (boolean_validator, boolean_normalizer, True,), + "installer.parallel": ( + boolean_validator, + boolean_normalizer, + True, + ), } return unique_config_values diff --git a/poetry/console/commands/debug/debug.py b/poetry/console/commands/debug/debug.py deleted file mode 100644 index 468e2faad1f..00000000000 --- a/poetry/console/commands/debug/debug.py +++ /dev/null @@ -1,11 +0,0 @@ -from ..command import Command -from .info import DebugInfoCommand -from .resolve import DebugResolveCommand - - -class DebugCommand(Command): - - name = "debug" - description = "Debug various elements of Poetry." - - commands = [DebugInfoCommand().default(), DebugResolveCommand()] diff --git a/poetry/console/commands/debug/info.py b/poetry/console/commands/debug/info.py index 8950a3e5710..140b26725f6 100644 --- a/poetry/console/commands/debug/info.py +++ b/poetry/console/commands/debug/info.py @@ -1,13 +1,11 @@ import sys -from clikit.args import StringArgs - from ..command import Command class DebugInfoCommand(Command): - name = "info" + name = "debug info" description = "Shows debug information." def handle(self): # type: () -> int @@ -25,7 +23,6 @@ def handle(self): # type: () -> int ] ) ) - args = StringArgs("") - command = self.application.get_command("env").get_sub_command("info") + command = self.application.get("env info") - return command.run(args, self._io) + return command.run(self._io) diff --git a/poetry/console/commands/debug/resolve.py b/poetry/console/commands/debug/resolve.py index 743ef1d7443..ef60cbf134a 100644 --- a/poetry/console/commands/debug/resolve.py +++ b/poetry/console/commands/debug/resolve.py @@ -1,14 +1,15 @@ from typing import Optional -from cleo import argument -from cleo import option +from cleo.helpers import argument +from cleo.helpers import option +from cleo.io.outputs.output import Verbosity from ..init import InitCommand class DebugResolveCommand(InitCommand): - name = "resolve" + name = "debug resolve" description = "Debugs dependency resolution." arguments = [ @@ -30,9 +31,10 @@ class DebugResolveCommand(InitCommand): loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] def handle(self): # type: () -> Optional[int] + from cleo.io.null_io import NullIO + from poetry.core.packages.project_package import ProjectPackage from poetry.factory import Factory - from poetry.io.null_io import NullIO from poetry.puzzle import Solver from poetry.repositories.pool import Pool from poetry.repositories.repository import Repository @@ -51,14 +53,12 @@ def handle(self): # type: () -> Optional[int] ) # Silencing output - is_quiet = self.io.output.is_quiet() - if not is_quiet: - self.io.output.set_quiet(True) + verbosity = self.io.output.verbosity + self.io.output.set_verbosity(Verbosity.QUIET) requirements = self._determine_requirements(packages) - if not is_quiet: - self.io.output.set_quiet(False) + self.io.output.set_verbosity(verbosity) for constraint in requirements: name = constraint.pop("name") @@ -103,7 +103,8 @@ def handle(self): # type: () -> Optional[int] return 0 - table = self.table([], style="borderless") + table = self.table([], style="compact") + table.style.set_vertical_border_chars("", " ") rows = [] if self.option("install"): @@ -136,4 +137,4 @@ def handle(self): # type: () -> Optional[int] rows.append(row) table.set_rows(rows) - table.render(self.io) + table.render() diff --git a/poetry/console/commands/env/env.py b/poetry/console/commands/env/env.py deleted file mode 100644 index f979b66e436..00000000000 --- a/poetry/console/commands/env/env.py +++ /dev/null @@ -1,16 +0,0 @@ -from ..command import Command -from .info import EnvInfoCommand -from .list import EnvListCommand -from .remove import EnvRemoveCommand -from .use import EnvUseCommand - - -class EnvCommand(Command): - - name = "env" - description = "Interact with Poetry's project environments." - - commands = [EnvInfoCommand(), EnvListCommand(), EnvRemoveCommand(), EnvUseCommand()] - - def handle(self): # type: () -> int - return self.call("help", self._config.name) diff --git a/poetry/console/commands/env/info.py b/poetry/console/commands/env/info.py index fc0e4a75936..dea2ade994a 100644 --- a/poetry/console/commands/env/info.py +++ b/poetry/console/commands/env/info.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING from typing import Optional -from cleo import option +from cleo.helpers import option from ..command import Command @@ -12,7 +12,7 @@ class EnvInfoCommand(Command): - name = "info" + name = "env info" description = "Displays information about the current environment." options = [option("path", "p", "Only display the environment's path.")] diff --git a/poetry/console/commands/env/list.py b/poetry/console/commands/env/list.py index 46423d142c7..f422cc8ef2a 100644 --- a/poetry/console/commands/env/list.py +++ b/poetry/console/commands/env/list.py @@ -1,11 +1,11 @@ -from cleo import option +from cleo.helpers import option from ..command import Command class EnvListCommand(Command): - name = "list" + name = "env list" description = "Lists all virtualenvs associated with the current project." options = [option("full-path", None, "Output the full paths of the virtualenvs.")] diff --git a/poetry/console/commands/env/remove.py b/poetry/console/commands/env/remove.py index 4b4bca29c9d..c8e5f1360e1 100644 --- a/poetry/console/commands/env/remove.py +++ b/poetry/console/commands/env/remove.py @@ -1,11 +1,11 @@ -from cleo import argument +from cleo.helpers import argument from ..command import Command class EnvRemoveCommand(Command): - name = "remove" + name = "env remove" description = "Removes a specific virtualenv associated with the project." arguments = [ diff --git a/poetry/console/commands/env/use.py b/poetry/console/commands/env/use.py index 57db24c97bd..13728c1d9ee 100644 --- a/poetry/console/commands/env/use.py +++ b/poetry/console/commands/env/use.py @@ -1,11 +1,11 @@ -from cleo import argument +from cleo.helpers import argument from ..command import Command class EnvUseCommand(Command): - name = "use" + name = "env use" description = "Activates or creates a new virtualenv for the current project." arguments = [argument("python", "The python executable to use.")] diff --git a/poetry/console/commands/export.py b/poetry/console/commands/export.py index 11ef2a37377..6d4f2a830fc 100644 --- a/poetry/console/commands/export.py +++ b/poetry/console/commands/export.py @@ -1,4 +1,4 @@ -from cleo import option +from cleo.helpers import option from poetry.utils.exporter import Exporter @@ -50,7 +50,7 @@ def handle(self): # type: () -> None elif self.io.is_verbose(): options.append(("-v", None)) - self.call("lock", options) + self.call("lock", " ".join(options)) if not locker.is_fresh(): self.line( diff --git a/poetry/console/commands/init.py b/poetry/console/commands/init.py index 92870cb614a..110f97a9a9c 100644 --- a/poetry/console/commands/init.py +++ b/poetry/console/commands/init.py @@ -13,7 +13,7 @@ from typing import Tuple from typing import Union -from cleo import option +from cleo.helpers import option from tomlkit import inline_table from poetry.core.pyproject import PyProjectException diff --git a/poetry/console/commands/install.py b/poetry/console/commands/install.py index f8d1ab0c560..d07c5cef93f 100644 --- a/poetry/console/commands/install.py +++ b/poetry/console/commands/install.py @@ -1,4 +1,4 @@ -from cleo import option +from cleo.helpers import option from .installer_command import InstallerCommand @@ -21,7 +21,9 @@ class InstallCommand(InstallerCommand): "(implicitly enables --verbose).", ), option( - "remove-untracked", None, "Removes packages not present in the lock file.", + "remove-untracked", + None, + "Removes packages not present in the lock file.", ), option( "extras", @@ -87,7 +89,7 @@ def handle(self): # type: () -> int return 0 self.line("") - if not self._io.supports_ansi() or self.io.is_debug(): + if not self._io.output.is_decorated() or self.io.is_debug(): self.line( "Installing the current project: {} ({})".format( self.poetry.package.pretty_name, self.poetry.package.pretty_version @@ -106,7 +108,7 @@ def handle(self): # type: () -> int builder.build() - if self._io.supports_ansi() and not self.io.is_debug(): + if self._io.output.is_decorated() and not self.io.is_debug(): self.overwrite( "Installing the current project: {} ({})".format( self.poetry.package.pretty_name, self.poetry.package.pretty_version diff --git a/poetry/console/commands/lock.py b/poetry/console/commands/lock.py index 59beb1386e2..d2895e8bee6 100644 --- a/poetry/console/commands/lock.py +++ b/poetry/console/commands/lock.py @@ -1,4 +1,4 @@ -from cleo import option +from cleo.helpers import option from .installer_command import InstallerCommand diff --git a/poetry/console/commands/new.py b/poetry/console/commands/new.py index 8d709fb6c4a..3276ee7d9fd 100644 --- a/poetry/console/commands/new.py +++ b/poetry/console/commands/new.py @@ -1,7 +1,7 @@ import sys -from cleo import argument -from cleo import option +from cleo.helpers import argument +from cleo.helpers import option from poetry.utils.helpers import module_name diff --git a/poetry/console/commands/publish.py b/poetry/console/commands/publish.py index cedbca85468..724e868a58a 100644 --- a/poetry/console/commands/publish.py +++ b/poetry/console/commands/publish.py @@ -1,7 +1,7 @@ from pathlib import Path from typing import Optional -from cleo import option +from cleo.helpers import option from .command import Command diff --git a/poetry/console/commands/remove.py b/poetry/console/commands/remove.py index 3fb42713ced..15e05de2860 100644 --- a/poetry/console/commands/remove.py +++ b/poetry/console/commands/remove.py @@ -1,5 +1,5 @@ -from cleo import argument -from cleo import option +from cleo.helpers import argument +from cleo.helpers import option from .installer_command import InstallerCommand diff --git a/poetry/console/commands/run.py b/poetry/console/commands/run.py index bafa5ce2cea..9879b3ed09c 100644 --- a/poetry/console/commands/run.py +++ b/poetry/console/commands/run.py @@ -1,7 +1,7 @@ from typing import Any from typing import Union -from cleo import argument +from cleo.helpers import argument from .env_command import EnvCommand @@ -15,13 +15,6 @@ class RunCommand(EnvCommand): argument("args", "The command and arguments/options to run.", multiple=True) ] - def __init__(self): # type: () -> None - from poetry.console.args.run_args_parser import RunArgsParser - - super(RunCommand, self).__init__() - - self.config.set_args_parser(RunArgsParser()) - def handle(self): # type: () -> Any args = self.argument("args") script = args[0] @@ -32,6 +25,17 @@ def handle(self): # type: () -> Any return self.env.execute(*args) + @property + def _module(self): + from poetry.core.masonry.utils.module import Module + + poetry = self.poetry + package = poetry.package + path = poetry.file.parent + module = Module(package.name, path.as_posix(), package.packages) + + return module + def run_script(self, script, args): # type: (Union[str, dict], str) -> Any if isinstance(script, dict): script = script["callable"] diff --git a/poetry/console/commands/search.py b/poetry/console/commands/search.py index 3ca8ac8e94d..27c4671a45b 100644 --- a/poetry/console/commands/search.py +++ b/poetry/console/commands/search.py @@ -1,4 +1,4 @@ -from cleo import argument +from cleo.helpers import argument from .command import Command diff --git a/poetry/console/commands/self/self.py b/poetry/console/commands/self/self.py deleted file mode 100644 index 29a0214c230..00000000000 --- a/poetry/console/commands/self/self.py +++ /dev/null @@ -1,13 +0,0 @@ -from ..command import Command -from .update import SelfUpdateCommand - - -class SelfCommand(Command): - - name = "self" - description = "Interact with Poetry directly." - - commands = [SelfUpdateCommand()] - - def handle(self): # type: () -> int - return self.call("help", self._config.name) diff --git a/poetry/console/commands/self/update.py b/poetry/console/commands/self/update.py index 279de274188..d0d00ddbefd 100644 --- a/poetry/console/commands/self/update.py +++ b/poetry/console/commands/self/update.py @@ -14,9 +14,10 @@ from typing import TYPE_CHECKING from typing import Any -from cleo import argument -from cleo import option +from cleo.helpers import argument +from cleo.helpers import option +from poetry.console.exceptions import PoetrySimpleConsoleException from poetry.core.packages import Dependency from ..command import Command @@ -59,7 +60,7 @@ class SelfUpdateCommand(Command): - name = "update" + name = "self update" description = "Updates Poetry to the latest version." arguments = [argument("version", "The version to update to.", optional=True)] @@ -253,9 +254,9 @@ def _check_recommended_installation(self): # type: () -> None try: current.relative_to(self.home) except ValueError: - raise RuntimeError( - "Poetry was not installed with the recommended installer. " - "Cannot update automatically." + raise PoetrySimpleConsoleException( + "Poetry was not installed with the recommended installer, " + "so it cannot be updated automatically." ) def _get_release_name(self, version): # type: ("Version") -> str diff --git a/poetry/console/commands/show.py b/poetry/console/commands/show.py index 545717be8ee..66c505f5545 100644 --- a/poetry/console/commands/show.py +++ b/poetry/console/commands/show.py @@ -4,8 +4,8 @@ from typing import Optional from typing import Union -from cleo import argument -from cleo import option +from cleo.helpers import argument +from cleo.helpers import option from .env_command import EnvCommand @@ -47,9 +47,9 @@ class ShowCommand(EnvCommand): colors = ["cyan", "yellow", "green", "magenta", "blue"] def handle(self): # type: () -> Optional[int] - from clikit.utils.terminal import Terminal + from cleo.io.null_io import NullIO + from cleo.terminal import Terminal - from poetry.io.null_io import NullIO from poetry.puzzle.solver import Solver from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.pool import Pool @@ -62,7 +62,7 @@ def handle(self): # type: () -> Optional[int] self.init_styles(self.io) if self.option("outdated"): - self._args.set_option("latest", True) + self._io.input.set_option("latest", True) include_dev = not self.option("no-dev") locked_repo = self.poetry.locker.locked_repository(True) @@ -82,7 +82,6 @@ def handle(self): # type: () -> Optional[int] return 0 table = self.table(style="compact") - # table.style.line_vc_char = "" locked_packages = locked_repo.packages pool = Pool(ignore_repository_names=True) pool.add_repository(locked_repo) @@ -155,7 +154,7 @@ def handle(self): # type: () -> Optional[int] continue current_length = len(locked.pretty_name) - if not self._io.output.supports_ansi(): + if not self._io.output.is_decorated(): installed_status = self.get_installed_status(locked, installed_repo) if installed_status == "not-installed": @@ -218,7 +217,7 @@ def handle(self): # type: () -> Optional[int] if installed_status == "not-installed": color = "red" - if not self._io.output.supports_ansi(): + if not self._io.output.is_decorated(): # Non installed in non decorated mode install_marker = " (!)" @@ -362,7 +361,7 @@ def _display_tree( ) def _write_tree_line(self, io, line): # type: ("IO", str) -> None - if not io.output.supports_ansi(): + if not io.output.supports_utf8(): line = line.replace("└", "`-") line = line.replace("├", "|-") line = line.replace("──", "-") @@ -371,17 +370,17 @@ def _write_tree_line(self, io, line): # type: ("IO", str) -> None io.write_line(line) def init_styles(self, io): # type: ("IO") -> None - from clikit.api.formatter import Style + from cleo.formatters.style import Style for color in self.colors: - style = Style(color).fg(color) - io.output.formatter.add_style(style) - io.error_output.formatter.add_style(style) + style = Style(color) + io.output.formatter.set_style(color, style) + io.error_output.formatter.set_style(color, style) def find_latest_package( self, package, include_dev ): # type: ("Package", bool) -> Union["Package", bool] - from clikit.io import NullIO + from cleo.io.null_io import NullIO from poetry.puzzle.provider import Provider from poetry.version.version_selector import VersionSelector diff --git a/poetry/console/commands/update.py b/poetry/console/commands/update.py index 77c0adf52cc..42bb64aba37 100644 --- a/poetry/console/commands/update.py +++ b/poetry/console/commands/update.py @@ -1,5 +1,5 @@ -from cleo import argument -from cleo import option +from cleo.helpers import argument +from cleo.helpers import option from .installer_command import InstallerCommand diff --git a/poetry/console/commands/version.py b/poetry/console/commands/version.py index 403c357390a..2fadaac4b39 100644 --- a/poetry/console/commands/version.py +++ b/poetry/console/commands/version.py @@ -1,5 +1,5 @@ -from cleo import argument -from cleo import option +from cleo.helpers import argument +from cleo.helpers import option from .command import Command diff --git a/poetry/console/config/__init__.py b/poetry/console/config/__init__.py deleted file mode 100644 index 14e86b4365b..00000000000 --- a/poetry/console/config/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .application_config import ApplicationConfig diff --git a/poetry/console/config/application_config.py b/poetry/console/config/application_config.py deleted file mode 100644 index 563e6b55db0..00000000000 --- a/poetry/console/config/application_config.py +++ /dev/null @@ -1,244 +0,0 @@ -import logging - -from typing import Any - -from cleo.config import ApplicationConfig as BaseApplicationConfig -from clikit.api.application.application import Application -from clikit.api.args.raw_args import RawArgs -from clikit.api.event import PRE_HANDLE -from clikit.api.event import PreHandleEvent -from clikit.api.event import PreResolveEvent -from clikit.api.event.event_dispatcher import EventDispatcher -from clikit.api.exceptions import CliKitException -from clikit.api.formatter import Style -from clikit.api.io import Input -from clikit.api.io import InputStream -from clikit.api.io import Output -from clikit.api.io import OutputStream -from clikit.api.io.flags import DEBUG -from clikit.api.io.flags import VERBOSE -from clikit.api.io.flags import VERY_VERBOSE -from clikit.api.io.io import IO -from clikit.formatter import AnsiFormatter -from clikit.formatter import PlainFormatter -from clikit.io.input_stream import StandardInputStream -from clikit.io.output_stream import ErrorOutputStream -from clikit.io.output_stream import StandardOutputStream - -from poetry.console.commands.command import Command -from poetry.console.commands.env_command import EnvCommand -from poetry.console.commands.installer_command import InstallerCommand -from poetry.console.logging.io_formatter import IOFormatter -from poetry.console.logging.io_handler import IOHandler -from poetry.mixology.solutions.providers import PythonRequirementSolutionProvider - - -class ApplicationConfig(BaseApplicationConfig): - def configure(self): # type: () -> None - super(ApplicationConfig, self).configure() - - self.add_style(Style("c1").fg("cyan")) - self.add_style(Style("c2").fg("default").bold()) - self.add_style(Style("info").fg("blue")) - self.add_style(Style("comment").fg("green")) - self.add_style(Style("error").fg("red").bold()) - self.add_style(Style("warning").fg("yellow").bold()) - self.add_style(Style("debug").fg("default").dark()) - self.add_style(Style("success").fg("green")) - - # Dark variants - self.add_style(Style("c1_dark").fg("cyan").dark()) - self.add_style(Style("c2_dark").fg("default").bold().dark()) - self.add_style(Style("success_dark").fg("green").dark()) - - self.add_event_listener(PRE_HANDLE, self.register_command_loggers) - self.add_event_listener(PRE_HANDLE, self.set_env) - self.add_event_listener(PRE_HANDLE, self.set_installer) - - self._solution_provider_repository.register_solution_providers( - [PythonRequirementSolutionProvider] - ) - - def register_command_loggers( - self, event, event_name, _ - ): # type: (PreHandleEvent, str, Any) -> None - command = event.command.config.handler - if not isinstance(command, Command): - return - - io = event.io - - loggers = [ - "poetry.packages.locker", - "poetry.packages.package", - "poetry.utils.password_manager", - ] - - loggers += command.loggers - - handler = IOHandler(io) - handler.setFormatter(IOFormatter()) - - for logger in loggers: - logger = logging.getLogger(logger) - - logger.handlers = [handler] - - level = logging.WARNING - # The builders loggers are special and we can actually - # start at the INFO level. - if logger.name.startswith("poetry.core.masonry.builders"): - level = logging.INFO - - if io.is_debug(): - level = logging.DEBUG - elif io.is_very_verbose() or io.is_verbose(): - level = logging.INFO - - logger.setLevel(level) - - def set_env(self, event, event_name, _): # type: (PreHandleEvent, str, Any) -> None - from poetry.utils.env import EnvManager - - command = event.command.config.handler # type: EnvCommand - if not isinstance(command, EnvCommand): - return - - if command.env is not None: - return - - io = event.io - poetry = command.poetry - - env_manager = EnvManager(poetry) - env = env_manager.create_venv(io) - - if env.is_venv() and io.is_verbose(): - io.write_line("Using virtualenv: {}".format(env.path)) - - command.set_env(env) - - def set_installer( - self, event, event_name, _ - ): # type: (PreHandleEvent, str, Any) -> None - command = event.command.config.handler # type: InstallerCommand - if not isinstance(command, InstallerCommand): - return - - # If the command already has an installer - # we skip this step - if command.installer is not None: - return - - from poetry.installation.installer import Installer - - poetry = command.poetry - installer = Installer( - event.io, - command.env, - poetry.package, - poetry.locker, - poetry.pool, - poetry.config, - ) - installer.use_executor(poetry.config.get("experimental.new-installer", False)) - command.set_installer(installer) - - def resolve_help_command( - self, event, event_name, dispatcher - ): # type: (PreResolveEvent, str, EventDispatcher) -> None - args = event.raw_args - application = event.application - - if args.has_option_token("-h") or args.has_option_token("--help"): - from clikit.api.resolver import ResolvedCommand - - try: - resolved_command = self.command_resolver.resolve(args, application) - except CliKitException: - # We weren't able to resolve the command, - # due to a parse error most likely, - # so we fall back on the default behavior - return super(ApplicationConfig, self).resolve_help_command( - event, event_name, dispatcher - ) - - # If the current command is the run one, skip option - # check and interpret them as part of the executed command - if resolved_command.command.name == "run": - event.set_resolved_command(resolved_command) - - return event.stop_propagation() - - command = application.get_command("help") - - # Enable lenient parsing - parsed_args = command.parse(args, True) - - event.set_resolved_command(ResolvedCommand(command, parsed_args)) - event.stop_propagation() - - def create_io( - self, - application, - args, - input_stream=None, - output_stream=None, - error_stream=None, - ): # type: (Application, RawArgs, InputStream, OutputStream, OutputStream) -> IO - if input_stream is None: - input_stream = StandardInputStream() - - if output_stream is None: - output_stream = StandardOutputStream() - - if error_stream is None: - error_stream = ErrorOutputStream() - - style_set = application.config.style_set - - if output_stream.supports_ansi(): - output_formatter = AnsiFormatter(style_set) - else: - output_formatter = PlainFormatter(style_set) - - if error_stream.supports_ansi(): - error_formatter = AnsiFormatter(style_set) - else: - error_formatter = PlainFormatter(style_set) - - io = self.io_class( - Input(input_stream), - Output(output_stream, output_formatter), - Output(error_stream, error_formatter), - ) - - resolved_command = application.resolve_command(args) - # If the current command is the run one, skip option - # check and interpret them as part of the executed command - if resolved_command.command.name == "run": - return io - - if args.has_option_token("--no-ansi"): - formatter = PlainFormatter(style_set) - io.output.set_formatter(formatter) - io.error_output.set_formatter(formatter) - elif args.has_option_token("--ansi"): - formatter = AnsiFormatter(style_set, True) - io.output.set_formatter(formatter) - io.error_output.set_formatter(formatter) - - if args.has_option_token("-vvv") or self.is_debug(): - io.set_verbosity(DEBUG) - elif args.has_option_token("-vv"): - io.set_verbosity(VERY_VERBOSE) - elif args.has_option_token("-v"): - io.set_verbosity(VERBOSE) - - if args.has_option_token("--quiet") or args.has_option_token("-q"): - io.set_quiet(True) - - if args.has_option_token("--no-interaction") or args.has_option_token("-n"): - io.set_interactive(False) - - return io diff --git a/poetry/console/exceptions.py b/poetry/console/exceptions.py new file mode 100644 index 00000000000..04e2d84ffa7 --- /dev/null +++ b/poetry/console/exceptions.py @@ -0,0 +1,6 @@ +from cleo.exceptions import CleoSimpleException + + +class PoetrySimpleConsoleException(CleoSimpleException): + + pass diff --git a/poetry/io/__init__.py b/poetry/console/io/__init__.py similarity index 100% rename from poetry/io/__init__.py rename to poetry/console/io/__init__.py diff --git a/poetry/console/io/inputs/__init__.py b/poetry/console/io/inputs/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/poetry/console/io/inputs/run_argv_input.py b/poetry/console/io/inputs/run_argv_input.py new file mode 100644 index 00000000000..e052f3e9c9a --- /dev/null +++ b/poetry/console/io/inputs/run_argv_input.py @@ -0,0 +1,81 @@ +from typing import List +from typing import Optional +from typing import Union + +from cleo.io.inputs.argv_input import ArgvInput +from cleo.io.inputs.definition import Definition + + +class RunArgvInput(ArgvInput): + def __init__( + self, argv: Optional[List[str]] = None, definition: Optional[Definition] = None + ) -> None: + super().__init__(argv, definition=definition) + + self._parameter_options = [] + + @property + def first_argument(self) -> Optional[str]: + return "run" + + def add_parameter_option(self, name: str) -> None: + self._parameter_options.append(name) + + def has_parameter_option( + self, values: Union[str, List[str]], only_params: bool = False + ) -> bool: + if not isinstance(values, list): + values = [values] + + for token in self._tokens: + if only_params and token == "--": + return False + + for value in values: + if value not in self._parameter_options: + continue + + # Options with values: + # For long options, test for '--option=' at beginning + # For short options, test for '-o' at beginning + if value.find("--") == 0: + leading = value + "=" + else: + leading = value + + if token == value or leading != "" and token.find(leading) == 0: + return True + + return False + + def _parse(self) -> None: + parse_options = True + self._parsed = self._tokens[:] + + try: + token = self._parsed.pop(0) + except IndexError: + token = None + + while token is not None: + if parse_options and token == "": + self._parse_argument(token) + elif parse_options and token == "--": + parse_options = False + elif parse_options and token.find("--") == 0: + if token in self._parameter_options: + self._parse_long_option(token) + else: + self._parse_argument(token) + elif parse_options and token[0] == "-" and token != "-": + if token in self._parameter_options: + self._parse_short_option(token) + else: + self._parse_argument(token) + else: + self._parse_argument(token) + + try: + token = self._parsed.pop(0) + except IndexError: + token = None diff --git a/poetry/factory.py b/poetry/factory.py index 0e3ef0f6046..d8a9da7ed77 100644 --- a/poetry/factory.py +++ b/poetry/factory.py @@ -5,14 +5,14 @@ from typing import Dict from typing import Optional -from clikit.api.io.io import IO +from cleo.io.io import IO +from cleo.io.null_io import NullIO from poetry.core.factory import Factory as BaseFactory from poetry.core.toml.file import TOMLFile from .config.config import Config from .config.file_config_source import FileConfigSource -from .io.null_io import NullIO from .locations import CONFIG_DIR from .packages.locker import Locker from .poetry import Poetry diff --git a/poetry/installation/authenticator.py b/poetry/installation/authenticator.py index 1e7a72e751a..f40c03d2b7e 100644 --- a/poetry/installation/authenticator.py +++ b/poetry/installation/authenticator.py @@ -17,7 +17,7 @@ from typing import Optional from typing import Tuple - from clikit.api.io import IO + from cleo.io.io import IO from poetry.config.config import Config diff --git a/poetry/installation/executor.py b/poetry/installation/executor.py index 8123c2d6956..ecfd2f7ce12 100644 --- a/poetry/installation/executor.py +++ b/poetry/installation/executor.py @@ -14,10 +14,11 @@ from typing import List from typing import Union +from cleo.io.null_io import NullIO + from poetry.core.packages.file_dependency import FileDependency from poetry.core.packages.utils.link import Link from poetry.core.pyproject.toml import PyProjectTOML -from poetry.io.null_io import NullIO from poetry.utils._compat import decode from poetry.utils.env import EnvCommandError from poetry.utils.helpers import safe_rmtree @@ -91,7 +92,7 @@ def removals_count(self): # type: () -> int return self._executed["uninstall"] def supports_fancy_output(self): # type: () -> bool - return self._io.supports_ansi() and not self._dry_run + return self._io.output.is_decorated() and not self._dry_run def disable(self): # type: () -> "Executor" self._enabled = False @@ -176,7 +177,7 @@ def _write(self, operation, line): # type: ("OperationTypes", str) -> None with self._lock: section = self._sections[id(operation)] - section.output.clear() + section.clear() section.write(line) def _execute_operation(self, operation): # type: ("OperationTypes") -> None @@ -225,13 +226,15 @@ def _execute_operation(self, operation): # type: ("OperationTypes") -> None raise KeyboardInterrupt except Exception as e: try: - from clikit.ui.components.exception_trace import ExceptionTrace + from cleo.ui.exception_trace import ExceptionTrace if not self.supports_fancy_output(): io = self._io else: - message = " {message}: Failed".format( - message=self.get_operation_message(operation, error=True), + message = ( + " {message}: Failed".format( + message=self.get_operation_message(operation, error=True), + ) ) self._write(operation, message) io = self._sections.get(id(operation), self._io) @@ -268,7 +271,8 @@ def _do_execute_operation(self, operation): # type: ("OperationTypes") -> int "Skipped " "for the following reason: " "{reason}".format( - message=operation_message, reason=operation.skip_reason, + message=operation_message, + reason=operation.skip_reason, ), ) @@ -430,8 +434,10 @@ def _execute_update(self, operation): # type: (Union[Install, Update]) -> int return self._update(operation) def _execute_uninstall(self, operation): # type: (Uninstall) -> int - message = " • {message}: Removing...".format( - message=self.get_operation_message(operation), + message = ( + " • {message}: Removing...".format( + message=self.get_operation_message(operation), + ) ) self._write(operation, message) @@ -453,8 +459,10 @@ def _install(self, operation): # type: (Union[Install, Update]) -> int archive = self._download(operation) operation_message = self.get_operation_message(operation) - message = " • {message}: Installing...".format( - message=operation_message, + message = ( + " • {message}: Installing...".format( + message=operation_message, + ) ) self._write(operation, message) @@ -487,8 +495,10 @@ def _remove(self, operation): # type: (Uninstall) -> int def _prepare_file(self, operation): # type: (Union[Install, Update]) -> Path package = operation.package - message = " • {message}: Preparing...".format( - message=self.get_operation_message(operation), + message = ( + " • {message}: Preparing...".format( + message=self.get_operation_message(operation), + ) ) self._write(operation, message) @@ -506,8 +516,10 @@ def _install_directory(self, operation): # type: (Union[Install, Update]) -> in package = operation.package operation_message = self.get_operation_message(operation) - message = " • {message}: Building...".format( - message=operation_message, + message = ( + " • {message}: Building...".format( + message=operation_message, + ) ) self._write(operation, message) @@ -570,8 +582,10 @@ def _install_git(self, operation): # type: (Union[Install, Update]) -> int package = operation.package operation_message = self.get_operation_message(operation) - message = " • {message}: Cloning...".format( - message=operation_message, + message = ( + " • {message}: Cloning...".format( + message=operation_message, + ) ) self._write(operation, message) @@ -637,18 +651,20 @@ def _download_archive( ) wheel_size = response.headers.get("content-length") operation_message = self.get_operation_message(operation) - message = " • {message}: Downloading...".format( - message=operation_message, + message = ( + " • {message}: Downloading...".format( + message=operation_message, + ) ) progress = None if self.supports_fancy_output(): if wheel_size is None: self._write(operation, message) else: - from clikit.ui.components.progress_bar import ProgressBar + from cleo.ui.progress_bar import ProgressBar progress = ProgressBar( - self._sections[id(operation)].output, max=int(wheel_size) + self._sections[id(operation)], max=int(wheel_size) ) progress.set_format(message + " %percent%%") diff --git a/poetry/installation/installer.py b/poetry/installation/installer.py index ace3ee5bcf0..ecd0ba1ad78 100644 --- a/poetry/installation/installer.py +++ b/poetry/installation/installer.py @@ -4,11 +4,11 @@ from typing import Optional from typing import Union -from clikit.api.io import IO +from cleo.io.io import IO +from cleo.io.null_io import NullIO from poetry.config.config import Config from poetry.core.packages.project_package import ProjectPackage -from poetry.io.null_io import NullIO from poetry.packages import Locker from poetry.repositories import Pool from poetry.repositories import Repository diff --git a/poetry/installation/pip_installer.py b/poetry/installation/pip_installer.py index e4a89349d0a..6e5f7bdcc48 100644 --- a/poetry/installation/pip_installer.py +++ b/poetry/installation/pip_installer.py @@ -7,7 +7,7 @@ from typing import Any from typing import Union -from clikit.api.io import IO +from cleo.io.io import IO from poetry.core.pyproject.toml import PyProjectTOML from poetry.repositories.pool import Pool @@ -184,8 +184,9 @@ def create_temporary_requirement(self, package): # type: ("Package") -> str return name def install_directory(self, package): # type: ("Package") -> Union[str, int] + from cleo.io.null_io import NullIO + from poetry.factory import Factory - from poetry.io.null_io import NullIO if package.root_dir: req = (package.root_dir / package.source_url).as_posix() diff --git a/poetry/io/null_io.py b/poetry/io/null_io.py deleted file mode 100644 index 786b188a4e2..00000000000 --- a/poetry/io/null_io.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Any - -from cleo.io.io_mixin import IOMixin -from clikit.io import NullIO as BaseNullIO - - -class NullIO(IOMixin, BaseNullIO): - """ - A wrapper around CliKit's NullIO. - """ - - def __init__(self, *args, **kwargs): # type: (*Any, **Any) -> None - super(NullIO, self).__init__(*args, **kwargs) diff --git a/poetry/packages/locker.py b/poetry/packages/locker.py index 80350f66fe4..4b3adb19542 100644 --- a/poetry/packages/locker.py +++ b/poetry/packages/locker.py @@ -351,7 +351,9 @@ def get_project_dependency_packages( if extra_package_names is not None: extra_package_names = set( get_extra_package_names( - repository.packages, self.lock_data.get("extras", {}), extras or (), + repository.packages, + self.lock_data.get("extras", {}), + extras or (), ) ) diff --git a/poetry/publishing/uploader.py b/poetry/publishing/uploader.py index 43f4589b383..9c712213158 100644 --- a/poetry/publishing/uploader.py +++ b/poetry/publishing/uploader.py @@ -239,6 +239,8 @@ def _do_upload( def _upload_file( self, session, url, file, dry_run=False ): # type: (requests.Session, str, Path, Optional[bool]) -> requests.Response + from cleo.ui.progress_bar import ProgressBar + data = self.post_data(file) data.update( { @@ -255,7 +257,7 @@ def _upload_file( ("content", (file.name, fp, "application/octet-stream")) ) encoder = MultipartEncoder(data_to_send) - bar = self._io.progress_bar(encoder.len) + bar = ProgressBar(self._io, max=encoder.len) bar.set_format( " - Uploading {0} %percent%%".format(file.name) ) @@ -283,7 +285,7 @@ def _upload_file( ) bar.finish() elif resp.status_code == 301: - if self._io.output.supports_ansi(): + if self._io.output.is_decorated(): self._io.overwrite( " - Uploading {0} {1}".format( file.name, "FAILED" @@ -294,7 +296,7 @@ def _upload_file( "Is the URL missing a trailing slash?" ) except (requests.ConnectionError, requests.HTTPError) as e: - if self._io.output.supports_ansi(): + if self._io.output.is_decorated(): self._io.overwrite( " - Uploading {0} {1}".format( file.name, "FAILED" diff --git a/poetry/puzzle/provider.py b/poetry/puzzle/provider.py index 43d6ed63df3..63ce89a4c8d 100644 --- a/poetry/puzzle/provider.py +++ b/poetry/puzzle/provider.py @@ -1,6 +1,7 @@ import logging import os import re +import time import urllib.parse from contextlib import contextmanager @@ -13,7 +14,7 @@ from typing import Optional from typing import Union -from clikit.ui.components import ProgressIndicator +from cleo.ui.progress_indicator import ProgressIndicator from poetry.core.packages import Dependency from poetry.core.packages import DirectoryDependency @@ -45,7 +46,10 @@ class Indicator(ProgressIndicator): - pass + def _formatter_elapsed(self): + elapsed = time.time() - self._start_time + + return "{:.1f}s".format(elapsed) class Provider: @@ -663,8 +667,10 @@ def complete_package( clean_dependencies = [] for dep in dependencies: if not package.dependency.transitive_marker.without_extras().is_any(): - marker_intersection = package.dependency.transitive_marker.without_extras().intersect( - dep.marker.without_extras() + marker_intersection = ( + package.dependency.transitive_marker.without_extras().intersect( + dep.marker.without_extras() + ) ) if marker_intersection.is_empty(): # The dependency is not needed, since the markers specified @@ -777,7 +783,7 @@ def debug(self, message, depth=0): # type: (str, int) -> None @contextmanager def progress(self): # type: () -> Iterator[None] - if not self._io.output.supports_ansi() or self.is_debugging(): + if not self._io.output.is_decorated() or self.is_debugging(): self._io.write_line("Resolving dependencies...") yield else: diff --git a/poetry/puzzle/solver.py b/poetry/puzzle/solver.py index 47e3e8153d6..f78c07d71d5 100644 --- a/poetry/puzzle/solver.py +++ b/poetry/puzzle/solver.py @@ -11,7 +11,7 @@ from typing import Tuple from typing import Union -from clikit.api.io import IO +from cleo.io.io import IO from poetry.core.packages import Package from poetry.core.packages.project_package import ProjectPackage @@ -201,7 +201,12 @@ def solve(self, use_latest=None): # type: (List[str]) -> List["OperationTypes"] operations.append(Uninstall(installed)) return sorted( - operations, key=lambda o: (-o.priority, o.package.name, o.package.version,), + operations, + key=lambda o: ( + -o.priority, + o.package.name, + o.package.version, + ), ) def solve_in_compatibility_mode( diff --git a/poetry/repositories/installed_repository.py b/poetry/repositories/installed_repository.py index 29424dbaeb5..1202d7f6612 100644 --- a/poetry/repositories/installed_repository.py +++ b/poetry/repositories/installed_repository.py @@ -44,7 +44,8 @@ def get_package_paths(cls, env, name): # type: (Env, str) -> Set[Path] # where the pth file for foo-bar might have been installed as either foo-bar.pth or # foo_bar.pth (expected) in either pure or platform lib directories. candidates = itertools.product( - {env.purelib, env.platlib}, {name, module_name(name)}, + {env.purelib, env.platlib}, + {name, module_name(name)}, ) for lib, module in candidates: @@ -110,7 +111,8 @@ def load(cls, env): # type: (Env) -> InstalledRepository for entry in reversed(env.sys_path): for distribution in sorted( - metadata.distributions(path=[entry]), key=lambda d: str(d._path), + metadata.distributions(path=[entry]), + key=lambda d: str(d._path), ): name = distribution.metadata["name"] path = Path(str(distribution._path)) diff --git a/poetry/utils/env.py b/poetry/utils/env.py index 77a185130d5..36851104831 100644 --- a/poetry/utils/env.py +++ b/poetry/utils/env.py @@ -25,7 +25,7 @@ import tomlkit import virtualenv -from clikit.api.io import IO +from cleo.io.io import IO from packaging.tags import Tag from packaging.tags import interpreter_name from packaging.tags import interpreter_version diff --git a/poetry/utils/exporter.py b/poetry/utils/exporter.py index f86fdcf19ee..23f7f0f1503 100644 --- a/poetry/utils/exporter.py +++ b/poetry/utils/exporter.py @@ -5,7 +5,7 @@ from typing import Sequence from typing import Union -from clikit.api.io import IO +from cleo.io.io import IO from poetry.poetry import Poetry from poetry.utils._compat import decode diff --git a/pyproject.toml b/pyproject.toml index 412ce84c639..562bfe68901 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,8 +25,7 @@ classifiers = [ python = "^3.6" poetry-core = "^1.0.0" -cleo = "^0.8.1" -clikit = "^0.6.2" +cleo = "^1.0.0a1" crashtest = "^0.3.0" requests = "^2.18" cachy = "^0.3.0" @@ -55,7 +54,7 @@ zipp = { version = "^3.4", python = "<3.8"} urllib3 = "1.25.10" [tool.poetry.scripts] -poetry = "poetry.console:main" +poetry = "poetry.console.application:main" [build-system] diff --git a/sonnet b/sonnet index facbe34e280..090e31ecda2 100755 --- a/sonnet +++ b/sonnet @@ -7,10 +7,31 @@ import sys import tarfile from gzip import GzipFile +from pathlib import Path +from typing import Optional -from cleo import Application -from cleo import Command -from clikit.api.formatter import Style +from cleo.application import Application as BaseApplication +from cleo.commands.command import Command +from cleo.formatters.style import Style +from cleo.helpers import option +from cleo.io.inputs.input import Input +from cleo.io.io import IO +from cleo.io.outputs.output import Output + + +class Application(BaseApplication): + def create_io( + self, + input: Optional[Input] = None, + output: Optional[Output] = None, + error_output: Optional[Output] = None, + ) -> IO: + io = super(Application, self).create_io(input, output, error_output) + + io.output.formatter.set_style("debug", Style("default", options=["dark"])) + io.error_output.formatter.set_style("debug", Style("default", options=["dark"])) + + return io WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt") @@ -19,11 +40,12 @@ WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name = class MakeReleaseCommand(Command): """ Makes a self-contained package of Poetry. - - release - {--P|python=?* : Python version to use} """ + name = "make release" + + options = [option("--python", "-P", flag=False, multiple=True)] + PYTHON = { "3.6": "python3.6", "3.7": "python3.7", @@ -52,7 +74,6 @@ class MakeReleaseCommand(Command): from poetry.puzzle import Solver from poetry.repositories.pool import Pool from poetry.repositories.repository import Repository - from poetry.utils._compat import Path from poetry.utils.env import EnvManager from poetry.utils.env import VirtualEnv from poetry.utils.helpers import temporary_directory @@ -244,23 +265,8 @@ class MakeReleaseCommand(Command): return vendor_dir -class MakeCommand(Command): - """ - Build poetry releases. - - make - """ - - commands = [MakeReleaseCommand()] - - def handle(self): - return self.call("help", self.config.name) - - app = Application("sonnet") -app.config.add_style(Style("debug").fg("default").dark()) - -app.add(MakeCommand()) +app.add(MakeReleaseCommand()) if __name__ == "__main__": app.run() diff --git a/tests/conftest.py b/tests/conftest.py index 178a4632b90..d2773c95d56 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,7 +11,7 @@ import httpretty import pytest -from cleo import CommandTester +from cleo.testers.command_tester import CommandTester from poetry.config.config import Config as BaseConfig from poetry.config.dict_config_source import DictConfigSource @@ -118,7 +118,8 @@ def _pep517_metadata(cls, path): return PackageInfo(name="demo", version="0.1.2") mocker.patch( - "poetry.inspection.info.PackageInfo._pep517_metadata", _pep517_metadata, + "poetry.inspection.info.PackageInfo._pep517_metadata", + _pep517_metadata, ) @@ -224,7 +225,8 @@ def default_python(current_python): @pytest.fixture def repo(http): http.register_uri( - http.GET, re.compile("^https?://foo.bar/(.+?)$"), + http.GET, + re.compile("^https?://foo.bar/(.+?)$"), ) return TestRepository(name="foo") @@ -299,6 +301,13 @@ def _tester(command, poetry=None, installer=None, executor=None, environment=Non command = app.find(command) tester = CommandTester(command) + # Setting the formatter from the application + # TODO: Find a better way to do this in Cleo + app_io = app.create_io() + formatter = app_io.output.formatter + tester.io.output.set_formatter(formatter) + tester.io.error_output.set_formatter(formatter) + if poetry: app._poetry = poetry diff --git a/tests/console/commands/debug/test_resolve.py b/tests/console/commands/debug/test_resolve.py index ffc1e509aef..f3f0db24c6e 100644 --- a/tests/console/commands/debug/test_resolve.py +++ b/tests/console/commands/debug/test_resolve.py @@ -10,7 +10,7 @@ def tester(command_tester_factory): @pytest.fixture(autouse=True) -def __add_packages(repo): +def _add_packages(repo): cachy020 = get_package("cachy", "0.2.0") cachy020.add_dependency(Factory.create_dependency("msgpack-python", ">=0.5 <0.6")) @@ -30,8 +30,8 @@ def test_debug_resolve_gives_resolution_results(tester): Resolution results: -msgpack-python 0.5.3 -cachy 0.2.0 +msgpack-python 0.5.3 +cachy 0.2.0 """ assert expected == tester.io.fetch_output() @@ -46,7 +46,7 @@ def test_debug_resolve_tree_option_gives_the_dependency_tree(tester): Resolution results: cachy 0.2.0 -`-- msgpack-python >=0.5 <0.6 +└── msgpack-python >=0.5 <0.6 """ assert expected == tester.io.fetch_output() @@ -60,8 +60,8 @@ def test_debug_resolve_git_dependency(tester): Resolution results: -pendulum 2.0.3 -demo 0.1.2 +pendulum 2.0.3 +demo 0.1.2 """ assert expected == tester.io.fetch_output() diff --git a/tests/console/commands/env/test_use.py b/tests/console/commands/env/test_use.py index 4ec106b434e..e288bd66003 100644 --- a/tests/console/commands/env/test_use.py +++ b/tests/console/commands/env/test_use.py @@ -40,7 +40,8 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( mocker, tester, venv_cache, venv_name, venvs_in_cache_config ): mocker.patch( - "subprocess.check_output", side_effect=check_output_wrapper(), + "subprocess.check_output", + side_effect=check_output_wrapper(), ) mock_build_env = mocker.patch( @@ -64,7 +65,9 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( Creating virtualenv {} in {} Using virtualenv: {} """.format( - venv_py37.name, venv_py37.parent, venv_py37, + venv_py37.name, + venv_py37.parent, + venv_py37, ) assert expected == tester.io.fetch_output() @@ -122,7 +125,9 @@ def test_get_prefers_explicitly_activated_non_existing_virtualenvs_over_env_var( Creating virtualenv {} in {} Using virtualenv: {} """.format( - venv_dir.name, venv_dir.parent, venv_dir, + venv_dir.name, + venv_dir.parent, + venv_dir, ) assert expected == tester.io.fetch_output() diff --git a/tests/console/commands/self/test_update.py b/tests/console/commands/self/test_update.py index 1598f47865a..2512654dea9 100644 --- a/tests/console/commands/self/test_update.py +++ b/tests/console/commands/self/test_update.py @@ -23,7 +23,7 @@ def test_self_update_should_install_all_necessary_elements( ): os.environ["POETRY_HOME"] = tmp_dir - command = tester._command + command = tester.command version = Version.parse(__version__).next_minor.text mocker.patch( diff --git a/tests/console/commands/test_add.py b/tests/console/commands/test_add.py index a0811c1c965..6405bc7431c 100644 --- a/tests/console/commands/test_add.py +++ b/tests/console/commands/test_add.py @@ -20,7 +20,7 @@ def tester(command_tester_factory): @pytest.fixture() def old_tester(tester): - tester._command.installer.use_executor(False) + tester.command.installer.use_executor(False) return tester @@ -45,7 +45,7 @@ def test_add_no_constraint(app, repo, tester): """ assert expected == tester.io.fetch_output() - assert 1 == tester._command.installer.executor.installations_count + assert 1 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -72,7 +72,7 @@ def test_add_equal_constraint(app, repo, tester): """ assert expected == tester.io.fetch_output() - assert 1 == tester._command.installer.executor.installations_count + assert 1 == tester.command.installer.executor.installations_count def test_add_greater_constraint(app, repo, tester): @@ -94,7 +94,7 @@ def test_add_greater_constraint(app, repo, tester): """ assert expected == tester.io.fetch_output() - assert 1 == tester._command.installer.executor.installations_count + assert 1 == tester.command.installer.executor.installations_count def test_add_constraint_with_extras(app, repo, tester): @@ -123,7 +123,7 @@ def test_add_constraint_with_extras(app, repo, tester): """ assert expected == tester.io.fetch_output() - assert 2 == tester._command.installer.executor.installations_count + assert 2 == tester.command.installer.executor.installations_count def test_add_constraint_dependencies(app, repo, tester): @@ -151,11 +151,11 @@ def test_add_constraint_dependencies(app, repo, tester): """ assert expected == tester.io.fetch_output() - assert 2 == tester._command.installer.executor.installations_count + assert 2 == tester.command.installer.executor.installations_count def test_add_git_constraint(app, repo, tester, tmp_venv): - tester._command.set_env(tmp_venv) + tester.command.set_env(tmp_venv) repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) @@ -176,7 +176,7 @@ def test_add_git_constraint(app, repo, tester, tmp_venv): """ assert expected == tester.io.fetch_output() - assert 2 == tester._command.installer.executor.installations_count + assert 2 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -187,7 +187,7 @@ def test_add_git_constraint(app, repo, tester, tmp_venv): def test_add_git_constraint_with_poetry(app, repo, tester, tmp_venv): - tester._command.set_env(tmp_venv) + tester.command.set_env(tmp_venv) repo.add_package(get_package("pendulum", "1.4.4")) @@ -207,11 +207,11 @@ def test_add_git_constraint_with_poetry(app, repo, tester, tmp_venv): """ assert expected == tester.io.fetch_output() - assert 2 == tester._command.installer.executor.installations_count + assert 2 == tester.command.installer.executor.installations_count def test_add_git_constraint_with_extras(app, repo, tester, tmp_venv): - tester._command.set_env(tmp_venv) + tester.command.set_env(tmp_venv) repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) @@ -235,7 +235,7 @@ def test_add_git_constraint_with_extras(app, repo, tester, tmp_venv): """ assert expected.strip() == tester.io.fetch_output().strip() - assert 4 == tester._command.installer.executor.installations_count + assert 4 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -247,7 +247,7 @@ def test_add_git_constraint_with_extras(app, repo, tester, tmp_venv): def test_add_git_ssh_constraint(app, repo, tester, tmp_venv): - tester._command.set_env(tmp_venv) + tester.command.set_env(tmp_venv) repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) @@ -268,7 +268,7 @@ def test_add_git_ssh_constraint(app, repo, tester, tmp_venv): """ assert expected == tester.io.fetch_output() - assert 2 == tester._command.installer.executor.installations_count + assert 2 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -305,7 +305,7 @@ def test_add_directory_constraint(app, repo, tester, mocker): ) assert expected == tester.io.fetch_output() - assert 2 == tester._command.installer.executor.installations_count + assert 2 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -338,7 +338,7 @@ def test_add_directory_with_poetry(app, repo, tester, mocker): ) assert expected == tester.io.fetch_output() - assert 2 == tester._command.installer.executor.installations_count + assert 2 == tester.command.installer.executor.installations_count def test_add_file_constraint_wheel(app, repo, tester, mocker, poetry): @@ -366,7 +366,7 @@ def test_add_file_constraint_wheel(app, repo, tester, mocker, poetry): ) assert expected == tester.io.fetch_output() - assert 2 == tester._command.installer.executor.installations_count + assert 2 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -401,7 +401,7 @@ def test_add_file_constraint_sdist(app, repo, tester, mocker): ) assert expected == tester.io.fetch_output() - assert 2 == tester._command.installer.executor.installations_count + assert 2 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -437,7 +437,7 @@ def test_add_constraint_with_extras_option(app, repo, tester): """ assert expected == tester.io.fetch_output() - assert 2 == tester._command.installer.executor.installations_count + assert 2 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -472,7 +472,7 @@ def test_add_url_constraint_wheel(app, repo, tester, mocker): """ assert expected == tester.io.fetch_output() - assert 2 == tester._command.installer.executor.installations_count + assert 2 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -509,7 +509,7 @@ def test_add_url_constraint_wheel_with_extras(app, repo, tester, mocker): expected = set(expected.splitlines()) output = set(tester.io.fetch_output().splitlines()) assert expected == output - assert 4 == tester._command.installer.executor.installations_count + assert 4 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -541,7 +541,7 @@ def test_add_constraint_with_python(app, repo, tester): """ assert expected == tester.io.fetch_output() - assert 1 == tester._command.installer.executor.installations_count + assert 1 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -573,7 +573,7 @@ def test_add_constraint_with_platform(app, repo, tester, env): """ assert expected == tester.io.fetch_output() - assert 1 == tester._command.installer.executor.installations_count + assert 1 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -606,7 +606,7 @@ def test_add_constraint_with_source(app, poetry, tester): """ assert expected == tester.io.fetch_output() - assert 1 == tester._command.installer.executor.installations_count + assert 1 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -659,7 +659,7 @@ def test_add_to_section_that_does_no_exist_yet(app, repo, tester): """ assert expected == tester.io.fetch_output() - assert 1 == tester._command.installer.executor.installations_count + assert 1 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] @@ -687,7 +687,7 @@ def test_add_should_not_select_prereleases(app, repo, tester): """ assert expected == tester.io.fetch_output() - assert 1 == tester._command.installer.executor.installations_count + assert 1 == tester.command.installer.executor.installations_count content = app.poetry.file.read()["tool"]["poetry"] diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index 812cc23ab96..442a0123284 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -6,7 +6,7 @@ import pytest -from cleo import CommandTester +from cleo.testers.command_tester import CommandTester from poetry.repositories import Pool from poetry.utils._compat import decode @@ -599,7 +599,7 @@ def test_predefined_and_interactive_dev_dependencies(tester, repo): def test_add_package_with_extras_and_whitespace(tester): - result = tester._command._parse_requirements(["databases[postgresql, sqlite]"]) + result = tester.command._parse_requirements(["databases[postgresql, sqlite]"]) assert result[0]["name"] == "databases" assert len(result[0]["extras"]) == 2 diff --git a/tests/console/commands/test_publish.py b/tests/console/commands/test_publish.py index bcbf4c50006..62671eda624 100644 --- a/tests/console/commands/test_publish.py +++ b/tests/console/commands/test_publish.py @@ -14,16 +14,17 @@ def test_publish_returns_non_zero_code_for_upload_errors(app, app_tester, http): assert 1 == exit_code - expected = """ + expected_output = """ Publishing simple-project (1.2.3) to PyPI - - +""" + expected_error_output = """\ UploadError HTTP Error 400: Bad Request """ - assert expected in app_tester.io.fetch_output() + assert expected_output in app_tester.io.fetch_output() + assert expected_error_output in app_tester.io.fetch_error() def test_publish_returns_non_zero_code_for_connection_errors(app, app_tester, http): @@ -40,7 +41,7 @@ def request_callback(*_, **__): expected = str(UploadError(error=requests.ConnectionError())) - assert expected in app_tester.io.fetch_output() + assert expected in app_tester.io.fetch_error() def test_publish_with_cert(app_tester, mocker): diff --git a/tests/console/commands/test_run.py b/tests/console/commands/test_run.py index 351d869d1a9..33314fa53c0 100644 --- a/tests/console/commands/test_run.py +++ b/tests/console/commands/test_run.py @@ -11,6 +11,16 @@ def patches(mocker, env): mocker.patch("poetry.utils.env.EnvManager.get", return_value=env) -def test_run_passes_all_args(tester, env): - tester.execute("python -V") +def test_run_passes_all_args(app_tester, env): + app_tester.execute("run python -V") assert [["python", "-V"]] == env.executed + + +def test_run_keeps_options_passed_before_command(app_tester, env): + app_tester.execute("-V --no-ansi run python", decorated=True) + + assert not app_tester.io.is_decorated() + assert app_tester.io.fetch_output() == app_tester.io.remove_format( + app_tester.application.long_version + "\n" + ) + assert [] == env.executed diff --git a/tests/console/commands/test_search.py b/tests/console/commands/test_search.py index 18e094ff1ae..ef668454084 100644 --- a/tests/console/commands/test_search.py +++ b/tests/console/commands/test_search.py @@ -21,7 +21,8 @@ def tester(command_tester_factory): def test_search( - tester, http, + tester, + http, ): tester.execute("sqlalchemy") diff --git a/tests/console/commands/test_show.py b/tests/console/commands/test_show.py index 56e964abfe4..f31c82882ea 100644 --- a/tests/console/commands/test_show.py +++ b/tests/console/commands/test_show.py @@ -1,7 +1,5 @@ import pytest -from clikit.formatter.ansi_formatter import AnsiFormatter - from poetry.factory import Factory from tests.helpers import get_package @@ -186,12 +184,11 @@ def test_show_basic_with_not_installed_packages_decorated(tester, poetry, instal } ) - tester.io.set_formatter(AnsiFormatter(forced=True)) - tester.execute() + tester.execute(decorated=True) expected = """\ -\033[36mcachy \033[0m \033[1m0.1.0\033[0m Cachy package -\033[31mpendulum\033[0m \033[1m2.0.0\033[0m Pendulum package +\033[36mcachy \033[39m \033[39;1m0.1.0\033[39;22m Cachy package +\033[31mpendulum\033[39m \033[39;1m2.0.0\033[39;22m Pendulum package """ assert expected == tester.io.fetch_output() @@ -317,12 +314,11 @@ def test_show_latest_decorated(tester, poetry, installed, repo): } ) - tester.io.set_formatter(AnsiFormatter(forced=True)) - tester.execute("--latest") + tester.execute("--latest", decorated=True) expected = """\ -\033[36mcachy \033[0m \033[1m0.1.0\033[0m \033[33m0.2.0\033[0m Cachy package -\033[36mpendulum\033[0m \033[1m2.0.0\033[0m \033[31m2.0.1\033[0m Pendulum package +\033[36mcachy \033[39m \033[39;1m0.1.0\033[39;22m \033[33m0.2.0\033[39m Cachy package +\033[36mpendulum\033[39m \033[39;1m2.0.0\033[39;22m \033[31m2.0.1\033[39m Pendulum package """ assert expected == tester.io.fetch_output() @@ -1144,7 +1140,7 @@ def test_show_tree(tester, poetry, installed): } ) - tester.execute("--tree") + tester.execute("--tree", supports_utf8=False) expected = """\ cachy 0.2.0 @@ -1215,7 +1211,7 @@ def test_show_tree_no_dev(tester, poetry, installed): expected = """\ cachy 0.2.0 -`-- msgpack-python >=0.5 <0.6 +└── msgpack-python >=0.5 <0.6 """ assert expected == tester.io.fetch_output() diff --git a/tests/console/commands/test_version.py b/tests/console/commands/test_version.py index ac91df1690c..f868d9f1060 100644 --- a/tests/console/commands/test_version.py +++ b/tests/console/commands/test_version.py @@ -1,6 +1,6 @@ import pytest -from poetry.console.commands import VersionCommand +from poetry.console.commands.version import VersionCommand @pytest.fixture() diff --git a/tests/console/conftest.py b/tests/console/conftest.py index a9b06f6c3b2..82143358d4d 100644 --- a/tests/console/conftest.py +++ b/tests/console/conftest.py @@ -4,11 +4,11 @@ import pytest -from cleo import ApplicationTester +from cleo.io.null_io import NullIO +from cleo.testers.application_tester import ApplicationTester from poetry.factory import Factory from poetry.installation.noop_installer import NoopInstaller -from poetry.io.null_io import NullIO from poetry.repositories import Pool from poetry.utils.env import MockEnv from tests.helpers import TestApplication @@ -98,7 +98,6 @@ def poetry(repo, project_directory, config): @pytest.fixture def app(poetry): app_ = TestApplication(poetry) - app_.config.set_terminate_after_run(False) return app_ diff --git a/tests/helpers.py b/tests/helpers.py index 8fc2a381c0f..078e15b6143 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -4,7 +4,7 @@ from pathlib import Path -from poetry.console import Application +from poetry.console.application import Application from poetry.core.masonry.utils.helpers import escape_name from poetry.core.masonry.utils.helpers import escape_version from poetry.core.packages import Dependency diff --git a/tests/inspection/test_info.py b/tests/inspection/test_info.py index ac0c4504e2c..a1edea4a39b 100644 --- a/tests/inspection/test_info.py +++ b/tests/inspection/test_info.py @@ -145,7 +145,8 @@ def test_info_from_setup_cfg(demo_setup_cfg): def test_info_no_setup_pkg_info_no_deps(): info = PackageInfo.from_directory( - FIXTURE_DIR_INSPECTIONS / "demo_no_setup_pkg_info_no_deps", disable_build=True, + FIXTURE_DIR_INSPECTIONS / "demo_no_setup_pkg_info_no_deps", + disable_build=True, ) assert info.name == "demo" assert info.version == "0.1.0" diff --git a/tests/installation/test_authenticator.py b/tests/installation/test_authenticator.py index d19364741d1..5f756c34f91 100644 --- a/tests/installation/test_authenticator.py +++ b/tests/installation/test_authenticator.py @@ -5,14 +5,16 @@ import pytest import requests +from cleo.io.null_io import NullIO + from poetry.installation.authenticator import Authenticator -from poetry.io.null_io import NullIO @pytest.fixture() def mock_remote(http): http.register_uri( - http.GET, re.compile("^https?://foo.bar/(.+?)$"), + http.GET, + re.compile("^https?://foo.bar/(.+?)$"), ) diff --git a/tests/installation/test_chef.py b/tests/installation/test_chef.py index d25f276748d..332d9c4794f 100644 --- a/tests/installation/test_chef.py +++ b/tests/installation/test_chef.py @@ -48,7 +48,9 @@ def test_get_cached_archives_for_link(config, mocker): distributions = Path(__file__).parent.parent.joinpath("fixtures/distributions") mocker.patch.object( - chef, "get_cache_directory_for_link", return_value=distributions, + chef, + "get_cache_directory_for_link", + return_value=distributions, ) archives = chef.get_cached_archives_for_link( diff --git a/tests/installation/test_chooser.py b/tests/installation/test_chooser.py index 75fd52c7926..64d58eab95d 100644 --- a/tests/installation/test_chooser.py +++ b/tests/installation/test_chooser.py @@ -50,7 +50,9 @@ def callback(request, uri, headers): return [200, headers, f.read()] http.register_uri( - http.GET, re.compile("^https://pypi.org/(.+?)/(.+?)/json$"), body=callback, + http.GET, + re.compile("^https://pypi.org/(.+?)/(.+?)/json$"), + body=callback, ) @@ -66,7 +68,9 @@ def callback(request, uri, headers): return [200, headers, f.read()] http.register_uri( - http.GET, re.compile("^https://foo.bar/simple/(.+?)$"), body=callback, + http.GET, + re.compile("^https://foo.bar/simple/(.+?)$"), + body=callback, ) @@ -150,7 +154,11 @@ def test_chooser_chooses_system_specific_wheel_link_if_available( @pytest.mark.parametrize("source_type", ["", "legacy"]) def test_chooser_chooses_sdist_if_no_compatible_wheel_link_is_available( - env, mock_pypi, mock_legacy, source_type, pool, + env, + mock_pypi, + mock_legacy, + source_type, + pool, ): chooser = Chooser(pool, env) @@ -171,7 +179,11 @@ def test_chooser_chooses_sdist_if_no_compatible_wheel_link_is_available( @pytest.mark.parametrize("source_type", ["", "legacy"]) def test_chooser_chooses_distributions_that_match_the_package_hashes( - env, mock_pypi, mock_legacy, source_type, pool, + env, + mock_pypi, + mock_legacy, + source_type, + pool, ): chooser = Chooser(pool, env) diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index ddf6faa2d66..4b1596aeb5e 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -8,8 +8,8 @@ import pytest -from clikit.api.formatter.style import Style -from clikit.io.buffered_io import BufferedIO +from cleo.formatters.style import Style +from cleo.io.buffered_io import BufferedIO from poetry.config.config import Config from poetry.core.packages.package import Package @@ -32,10 +32,10 @@ def env(tmp_dir): @pytest.fixture() def io(): io = BufferedIO() - io.formatter.add_style(Style("c1_dark").fg("cyan").dark()) - io.formatter.add_style(Style("c2_dark").fg("default").bold().dark()) - io.formatter.add_style(Style("success_dark").fg("green").dark()) - io.formatter.add_style(Style("warning").fg("yellow")) + io.output.formatter.set_style("c1_dark", Style("cyan", options=["dark"])) + io.output.formatter.set_style("c2_dark", Style("default", options=["bold", "dark"])) + io.output.formatter.set_style("success_dark", Style("green", options=["dark"])) + io.output.formatter.set_style("warning", Style("yellow")) return io @@ -59,7 +59,9 @@ def callback(request, uri, headers): return [200, headers, f.read()] http.register_uri( - http.GET, re.compile("^https://files.pythonhosted.org/.*$"), body=callback, + http.GET, + re.compile("^https://files.pythonhosted.org/.*$"), + body=callback, ) @@ -203,10 +205,10 @@ def test_execute_should_gracefully_handle_io_error(config, mocker, io, env): original_write_line = executor._io.write_line - def write_line(string, flags=None): + def write_line(string, **kwargs): # Simulate UnicodeEncodeError string.encode("ascii") - original_write_line(string, flags) + original_write_line(string, **kwargs) mocker.patch.object(io, "write_line", side_effect=write_line) diff --git a/tests/installation/test_installer.py b/tests/installation/test_installer.py index 67ee09b0c2c..32eb63149ea 100644 --- a/tests/installation/test_installer.py +++ b/tests/installation/test_installer.py @@ -7,7 +7,7 @@ import pytest -from clikit.io import NullIO +from cleo.io.null_io import NullIO from poetry.core.packages import ProjectPackage from poetry.core.toml.file import TOMLFile @@ -1854,7 +1854,12 @@ def test_installer_can_handle_old_lock_files( pool, config, installed=installed, - executor=Executor(MockEnv(version_info=(2, 7, 18)), pool, config, NullIO(),), + executor=Executor( + MockEnv(version_info=(2, 7, 18)), + pool, + config, + NullIO(), + ), ) installer.use_executor() @@ -1872,7 +1877,10 @@ def test_installer_can_handle_old_lock_files( config, installed=installed, executor=Executor( - MockEnv(version_info=(2, 7, 18), platform="win32"), pool, config, NullIO(), + MockEnv(version_info=(2, 7, 18), platform="win32"), + pool, + config, + NullIO(), ), ) installer.use_executor() diff --git a/tests/installation/test_installer_old.py b/tests/installation/test_installer_old.py index 97c1b163636..21c66e2a176 100644 --- a/tests/installation/test_installer_old.py +++ b/tests/installation/test_installer_old.py @@ -6,7 +6,7 @@ import pytest -from clikit.io import NullIO +from cleo.io.null_io import NullIO from poetry.core.packages import ProjectPackage from poetry.core.toml.file import TOMLFile diff --git a/tests/installation/test_pip_installer.py b/tests/installation/test_pip_installer.py index 64c64409051..34c9cc6b4bd 100644 --- a/tests/installation/test_pip_installer.py +++ b/tests/installation/test_pip_installer.py @@ -4,9 +4,10 @@ import pytest +from cleo.io.null_io import NullIO + from poetry.core.packages.package import Package from poetry.installation.pip_installer import PipInstaller -from poetry.io.null_io import NullIO from poetry.repositories.legacy_repository import LegacyRepository from poetry.repositories.pool import Pool from poetry.utils.env import NullEnv 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 d79e6e39ee0..f85a07d465a 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 d79e6e39ee0..f85a07d465a 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 d79e6e39ee0..f85a07d465a 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/test_editable_builder.py b/tests/masonry/builders/test_editable_builder.py index 5500bc49a4d..8e236b9b188 100644 --- a/tests/masonry/builders/test_editable_builder.py +++ b/tests/masonry/builders/test_editable_builder.py @@ -8,8 +8,9 @@ import pytest +from cleo.io.null_io import NullIO + from poetry.factory import Factory -from poetry.io.null_io import NullIO from poetry.masonry.builders.editable import EditableBuilder from poetry.utils.env import EnvManager from poetry.utils.env import MockEnv @@ -78,10 +79,11 @@ def test_builder_installs_proper_files_for_standard_packages(simple_poetry, tmp_ assert tmp_venv._bin_dir.joinpath("foo").exists() assert tmp_venv.site_packages.path.joinpath("simple_project.pth").exists() - assert simple_poetry.file.parent.resolve().as_posix() == tmp_venv.site_packages.path.joinpath( - "simple_project.pth" - ).read_text().strip( - os.linesep + assert ( + simple_poetry.file.parent.resolve().as_posix() + == tmp_venv.site_packages.path.joinpath("simple_project.pth") + .read_text() + .strip(os.linesep) ) dist_info = tmp_venv.site_packages.path.joinpath("simple_project-1.2.3.dist-info") diff --git a/tests/mixology/solutions/solutions/test_python_requirement_solution.py b/tests/mixology/solutions/solutions/test_python_requirement_solution.py index 8ea58a4078d..46646b5ec81 100644 --- a/tests/mixology/solutions/solutions/test_python_requirement_solution.py +++ b/tests/mixology/solutions/solutions/test_python_requirement_solution.py @@ -1,4 +1,4 @@ -from clikit.io.buffered_io import BufferedIO +from cleo.io.buffered_io import BufferedIO from poetry.core.packages.dependency import Dependency from poetry.mixology.failure import SolveFailure diff --git a/tests/mixology/version_solver/conftest.py b/tests/mixology/version_solver/conftest.py index b31634b85db..ad0dacfbe75 100644 --- a/tests/mixology/version_solver/conftest.py +++ b/tests/mixology/version_solver/conftest.py @@ -1,6 +1,6 @@ import pytest -from clikit.io import NullIO +from cleo.io.null_io import NullIO from poetry.core.packages.project_package import ProjectPackage from poetry.puzzle.provider import Provider as BaseProvider diff --git a/tests/publishing/test_publisher.py b/tests/publishing/test_publisher.py index 786d659b0db..d35dcf8f2ca 100644 --- a/tests/publishing/test_publisher.py +++ b/tests/publishing/test_publisher.py @@ -4,10 +4,10 @@ import pytest -from cleo.io import BufferedIO +from cleo.io.buffered_io import BufferedIO +from cleo.io.null_io import NullIO from poetry.factory import Factory -from poetry.io.null_io import NullIO from poetry.publishing.publisher import Publisher diff --git a/tests/publishing/test_uploader.py b/tests/publishing/test_uploader.py index ae0eb041b7b..8f5ec1b9e7a 100644 --- a/tests/publishing/test_uploader.py +++ b/tests/publishing/test_uploader.py @@ -2,8 +2,9 @@ import pytest +from cleo.io.null_io import NullIO + from poetry.factory import Factory -from poetry.io.null_io import NullIO from poetry.publishing.uploader import Uploader from poetry.publishing.uploader import UploadError diff --git a/tests/puzzle/test_provider.py b/tests/puzzle/test_provider.py index 2d0c4557bdb..d7ab8f8788a 100644 --- a/tests/puzzle/test_provider.py +++ b/tests/puzzle/test_provider.py @@ -3,7 +3,7 @@ import pytest -from clikit.io import NullIO +from cleo.io.null_io import NullIO from poetry.core.packages import ProjectPackage from poetry.core.packages.directory_dependency import DirectoryDependency diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 6e2a53a5c22..75b5c565ed5 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -2,7 +2,7 @@ import pytest -from clikit.io import NullIO +from cleo.io.null_io import NullIO from poetry.core.packages import Package from poetry.core.packages import ProjectPackage @@ -499,7 +499,8 @@ def test_solver_returns_extras_only_requested(solver, repo, package, enabled_ext ) check_solver_result( - ops, expected, + ops, + expected, ) assert ops[-1].package.marker.is_any() @@ -544,7 +545,8 @@ def test_solver_returns_extras_when_multiple_extras_use_same_dependency( expected.insert(0, {"job": "install", "package": package_c}) check_solver_result( - ops, expected, + ops, + expected, ) assert ops[-1].package.marker.is_any() @@ -2353,7 +2355,8 @@ def test_ignore_python_constraint_no_overlap_dependencies(solver, repo, package) ops = solver.solve() check_solver_result( - ops, [{"job": "install", "package": pytest}], + ops, + [{"job": "install", "package": pytest}], ) @@ -2570,7 +2573,8 @@ def test_solver_should_use_the_python_constraint_from_the_environment_if_availab ops = solver.solve() check_solver_result( - ops, [{"job": "install", "package": b}, {"job": "install", "package": a}], + ops, + [{"job": "install", "package": b}, {"job": "install", "package": a}], ) diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py index f0e2bd21a45..da810307a9f 100644 --- a/tests/utils/test_env.py +++ b/tests/utils/test_env.py @@ -9,7 +9,7 @@ import pytest import tomlkit -from clikit.io import NullIO +from cleo.io.null_io import NullIO from poetry.core.semver import Version from poetry.core.toml.file import TOMLFile @@ -144,7 +144,8 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "subprocess.check_output", side_effect=check_output_wrapper(), + "subprocess.check_output", + side_effect=check_output_wrapper(), ) mocker.patch( "subprocess.Popen.communicate", @@ -184,10 +185,12 @@ def test_activate_activates_existing_virtualenv_no_envs_file( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "subprocess.check_output", side_effect=check_output_wrapper(), + "subprocess.check_output", + side_effect=check_output_wrapper(), ) mocker.patch( - "subprocess.Popen.communicate", side_effect=[("/prefix", None)], + "subprocess.Popen.communicate", + side_effect=[("/prefix", None)], ) m = mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build_venv) @@ -223,10 +226,12 @@ def test_activate_activates_same_virtualenv_with_envs_file( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "subprocess.check_output", side_effect=check_output_wrapper(), + "subprocess.check_output", + side_effect=check_output_wrapper(), ) mocker.patch( - "subprocess.Popen.communicate", side_effect=[("/prefix", None)], + "subprocess.Popen.communicate", + side_effect=[("/prefix", None)], ) m = mocker.patch("poetry.utils.env.EnvManager.create_venv") @@ -303,7 +308,8 @@ def test_activate_activates_recreates_for_different_patch( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "subprocess.check_output", side_effect=check_output_wrapper(), + "subprocess.check_output", + side_effect=check_output_wrapper(), ) mocker.patch( "subprocess.Popen.communicate", @@ -404,7 +410,8 @@ def test_deactivate_non_activated_but_existing( config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "subprocess.check_output", side_effect=check_output_wrapper(), + "subprocess.check_output", + side_effect=check_output_wrapper(), ) manager.deactivate(NullIO()) @@ -442,7 +449,8 @@ def test_deactivate_activated(tmp_dir, manager, poetry, config, mocker): config.merge({"virtualenvs": {"path": str(tmp_dir)}}) mocker.patch( - "subprocess.check_output", side_effect=check_output_wrapper(), + "subprocess.check_output", + side_effect=check_output_wrapper(), ) manager.deactivate(NullIO()) @@ -473,10 +481,12 @@ def test_get_prefers_explicitly_activated_virtualenvs_over_env_var( envs_file.write(doc) mocker.patch( - "subprocess.check_output", side_effect=check_output_wrapper(), + "subprocess.check_output", + side_effect=check_output_wrapper(), ) mocker.patch( - "subprocess.Popen.communicate", side_effect=[("/prefix", None)], + "subprocess.Popen.communicate", + side_effect=[("/prefix", None)], ) env = manager.get() @@ -814,7 +824,8 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir( ) mocker.patch( - "subprocess.check_output", side_effect=check_output_wrapper(), + "subprocess.check_output", + side_effect=check_output_wrapper(), ) mocker.patch( "subprocess.Popen.communicate", @@ -846,7 +857,8 @@ def test_system_env_has_correct_paths(): @pytest.mark.parametrize( - ("enabled",), [(True,), (False,)], + ("enabled",), + [(True,), (False,)], ) def test_system_env_usersite(mocker, enabled): mocker.patch("site.check_enableusersite", return_value=enabled) diff --git a/tests/utils/test_exporter.py b/tests/utils/test_exporter.py index f15e2dc53a3..5fed10d1c99 100644 --- a/tests/utils/test_exporter.py +++ b/tests/utils/test_exporter.py @@ -974,7 +974,10 @@ def test_exporter_can_export_requirements_txt_with_file_packages_and_markers( def test_exporter_exports_requirements_txt_with_legacy_packages(tmp_dir, poetry): poetry.pool.add_repository( - LegacyRepository("custom", "https://example.com/simple",) + LegacyRepository( + "custom", + "https://example.com/simple", + ) ) poetry.locker.mock_lock_data( { @@ -1030,7 +1033,12 @@ def test_exporter_exports_requirements_txt_with_legacy_packages(tmp_dir, poetry) def test_exporter_exports_requirements_txt_with_legacy_packages_trusted_host( tmp_dir, poetry ): - poetry.pool.add_repository(LegacyRepository("custom", "http://example.com/simple",)) + poetry.pool.add_repository( + LegacyRepository( + "custom", + "http://example.com/simple", + ) + ) poetry.locker.mock_lock_data( { "package": [ @@ -1139,9 +1147,17 @@ def test_exporter_exports_requirements_txt_with_legacy_packages_and_duplicate_so tmp_dir, poetry ): poetry.pool.add_repository( - LegacyRepository("custom", "https://example.com/simple",) + LegacyRepository( + "custom", + "https://example.com/simple", + ) + ) + poetry.pool.add_repository( + LegacyRepository( + "custom", + "https://foobaz.com/simple", + ) ) - poetry.pool.add_repository(LegacyRepository("custom", "https://foobaz.com/simple",)) poetry.locker.mock_lock_data( { "package": [ From 18a9e2dfad36332c9b6c53f7a1cf980b66325fe4 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Wed, 3 Feb 2021 20:06:09 +0100 Subject: [PATCH 44/54] fix: replace clikit by cleo in several places (#3634) --- poetry/console/args/__init__.py | 0 poetry/console/args/run_args_parser.py | 41 -------------------------- poetry/console/commands/show.py | 2 +- poetry/console/logging/io_handler.py | 4 +-- poetry/installation/executor.py | 2 +- poetry/masonry/builders/editable.py | 2 +- poetry/publishing/publisher.py | 7 ++--- poetry/publishing/uploader.py | 7 ++--- poetry/utils/shell.py | 2 +- 9 files changed, 11 insertions(+), 56 deletions(-) delete mode 100644 poetry/console/args/__init__.py delete mode 100644 poetry/console/args/run_args_parser.py diff --git a/poetry/console/args/__init__.py b/poetry/console/args/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/poetry/console/args/run_args_parser.py b/poetry/console/args/run_args_parser.py deleted file mode 100644 index 9f8cff8b209..00000000000 --- a/poetry/console/args/run_args_parser.py +++ /dev/null @@ -1,41 +0,0 @@ -from clikit.api.args import Args -from clikit.api.args import RawArgs -from clikit.api.args.format import ArgsFormat -from clikit.api.args.format import ArgsFormatBuilder -from clikit.args import DefaultArgsParser - - -class RunArgsParser(DefaultArgsParser): - """ - Parser that just parses command names and leave the rest - alone to be passed to the command. - """ - - def parse( - self, args, fmt, lenient=False - ): # type: (RawArgs, ArgsFormat, bool) -> Args - builder = ArgsFormatBuilder() - builder.set_command_names(*fmt.get_command_names()) - builder.set_arguments(*fmt.get_arguments().values()) - fmt = builder.format - - return super(RunArgsParser, self).parse(args, fmt, True) - - def _parse( - self, raw_args, fmt, lenient - ): # type: (RawArgs, ArgsFormat, bool) -> None - """ - Parse everything as a single, multi-valued argument. - """ - tokens = raw_args.tokens[:] - - last_arg = list(fmt.get_arguments().values())[-1] - self._arguments[last_arg.name] = [] - - while True: - try: - token = tokens.pop(0) - except IndexError: - break - - self._arguments[last_arg.name].append(token) diff --git a/poetry/console/commands/show.py b/poetry/console/commands/show.py index 66c505f5545..07a58cd0754 100644 --- a/poetry/console/commands/show.py +++ b/poetry/console/commands/show.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: - from clikit.api.io import IO # noqa + from cleo.io.io import IO # noqa from poetry.core.packages import Dependency # noqa from poetry.core.packages import Package # noqa diff --git a/poetry/console/logging/io_handler.py b/poetry/console/logging/io_handler.py index 03d9607c8e5..03804c3a669 100644 --- a/poetry/console/logging/io_handler.py +++ b/poetry/console/logging/io_handler.py @@ -6,7 +6,7 @@ if TYPE_CHECKING: from logging import LogRecord # noqa - from clikit.api.io import IO # noqa + from cleo.io.io import IO # noqa class IOHandler(logging.Handler): @@ -21,7 +21,7 @@ def emit(self, record): # type: ("LogRecord") -> None level = record.levelname.lower() err = level in ("warning", "error", "exception", "critical") if err: - self._io.error_line(msg) + self._io.write_error_line(msg) else: self._io.write_line(msg) except Exception: diff --git a/poetry/installation/executor.py b/poetry/installation/executor.py index ecfd2f7ce12..2ffca66a1e7 100644 --- a/poetry/installation/executor.py +++ b/poetry/installation/executor.py @@ -33,7 +33,7 @@ if TYPE_CHECKING: - from clikit.api.io import IO # noqa + from cleo.io.io import IO # noqa from poetry.config.config import Config # noqa from poetry.repositories import Pool # noqa diff --git a/poetry/masonry/builders/editable.py b/poetry/masonry/builders/editable.py index 4f4c059ce10..1756a0f0f15 100644 --- a/poetry/masonry/builders/editable.py +++ b/poetry/masonry/builders/editable.py @@ -19,7 +19,7 @@ if TYPE_CHECKING: - from clikit.api.io import IO # noqa + from cleo.io.io import IO # noqa from poetry.core.poetry import Poetry # noqa from poetry.utils.env import Env # noqa diff --git a/poetry/publishing/publisher.py b/poetry/publishing/publisher.py index c6855deee2d..f8a87a20388 100644 --- a/poetry/publishing/publisher.py +++ b/poetry/publishing/publisher.py @@ -14,9 +14,8 @@ if TYPE_CHECKING: - from cleo.io import BufferedIO # noqa - from cleo.io import ConsoleIO # noqa - from clikit.io import NullIO # noqa + from cleo.io.buffered_io import BufferedIO # noqa + from cleo.io.null_io import NullIO # noqa from ..poetry import Poetry # noqa @@ -30,7 +29,7 @@ class Publisher: def __init__( self, poetry, io - ): # type: ("Poetry", Union["ConsoleIO", "BufferedIO", "NullIO"]) -> None + ): # type: ("Poetry", Union["BufferedIO", "NullIO"]) -> None self._poetry = poetry self._package = poetry.package self._io = io diff --git a/poetry/publishing/uploader.py b/poetry/publishing/uploader.py index 9c712213158..8dccb4ef390 100644 --- a/poetry/publishing/uploader.py +++ b/poetry/publishing/uploader.py @@ -29,8 +29,7 @@ if TYPE_CHECKING: - from cleo.io import ConsoleIO # noqa - from clikit.io import NullIO # noqa + from cleo.io.null_io import NullIO # noqa from poetry.poetry import Poetry # noqa @@ -54,9 +53,7 @@ def __init__(self, error): # type: (Union[ConnectionError, HTTPError, str]) -> class Uploader: - def __init__( - self, poetry, io - ): # type: ("Poetry", Union["ConsoleIO", "NullIO"]) -> None + def __init__(self, poetry, io): # type: ("Poetry", "NullIO") -> None self._poetry = poetry self._package = poetry.package self._io = io diff --git a/poetry/utils/shell.py b/poetry/utils/shell.py index e009903a6de..a0f539fdece 100644 --- a/poetry/utils/shell.py +++ b/poetry/utils/shell.py @@ -7,7 +7,7 @@ import pexpect -from clikit.utils.terminal import Terminal +from cleo.terminal import Terminal from shellingham import ShellDetectionFailure from shellingham import detect_shell From cddd6759f707d5a9d4865d07fddb0ff8107670e3 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Fri, 5 Feb 2021 11:38:35 +0100 Subject: [PATCH 45/54] convert type comments to type hints (#3638) --- poetry/config/config.py | 34 +-- poetry/config/config_source.py | 4 +- poetry/config/dict_config_source.py | 8 +- poetry/config/file_config_source.py | 18 +- poetry/console/application.py | 9 +- poetry/console/commands/about.py | 2 +- poetry/console/commands/add.py | 10 +- poetry/console/commands/build.py | 2 +- poetry/console/commands/cache/clear.py | 2 +- poetry/console/commands/cache/list.py | 2 +- poetry/console/commands/check.py | 2 +- poetry/console/commands/command.py | 15 +- poetry/console/commands/config.py | 24 +- poetry/console/commands/debug/info.py | 2 +- poetry/console/commands/debug/resolve.py | 9 +- poetry/console/commands/env/info.py | 6 +- poetry/console/commands/env/list.py | 2 +- poetry/console/commands/env/remove.py | 2 +- poetry/console/commands/env/use.py | 2 +- poetry/console/commands/env_command.py | 8 +- poetry/console/commands/export.py | 2 +- poetry/console/commands/init.py | 38 ++- poetry/console/commands/install.py | 2 +- poetry/console/commands/installer_command.py | 10 +- poetry/console/commands/lock.py | 2 +- poetry/console/commands/new.py | 2 +- poetry/console/commands/publish.py | 2 +- poetry/console/commands/remove.py | 2 +- poetry/console/commands/run.py | 11 +- poetry/console/commands/search.py | 2 +- poetry/console/commands/self/update.py | 28 +- poetry/console/commands/shell.py | 2 +- poetry/console/commands/show.py | 38 +-- poetry/console/commands/update.py | 2 +- poetry/console/commands/version.py | 10 +- .../logging/formatters/builder_formatter.py | 2 +- .../console/logging/formatters/formatter.py | 2 +- poetry/console/logging/io_formatter.py | 4 +- poetry/console/logging/io_handler.py | 6 +- poetry/factory.py | 15 +- poetry/inspection/info.py | 71 +++-- poetry/installation/authenticator.py | 25 +- poetry/installation/base_installer.py | 8 +- poetry/installation/chef.py | 22 +- poetry/installation/chooser.py | 18 +- poetry/installation/executor.py | 85 +++--- poetry/installation/installer.py | 100 ++++--- poetry/installation/noop_installer.py | 16 +- poetry/installation/operations/install.py | 14 +- poetry/installation/operations/operation.py | 22 +- poetry/installation/operations/uninstall.py | 17 +- poetry/installation/operations/update.py | 22 +- poetry/installation/pip_installer.py | 22 +- poetry/json/__init__.py | 2 +- poetry/layouts/__init__.py | 2 +- poetry/layouts/layout.py | 39 +-- poetry/layouts/src.py | 4 +- poetry/layouts/standard.py | 4 +- poetry/masonry/builders/editable.py | 26 +- poetry/mixology/__init__.py | 15 +- poetry/mixology/assignment.py | 36 ++- poetry/mixology/failure.py | 37 +-- poetry/mixology/incompatibility.py | 64 +++-- poetry/mixology/incompatibility_cause.py | 26 +- poetry/mixology/partial_solution.py | 41 ++- poetry/mixology/result.py | 15 +- .../python_requirement_solution_provider.py | 4 +- .../solutions/python_requirement_solution.py | 10 +- poetry/mixology/term.py | 30 +-- poetry/mixology/version_solver.py | 37 ++- poetry/packages/dependency_package.py | 24 +- poetry/packages/locker.py | 61 +++-- poetry/packages/package_collection.py | 12 +- poetry/poetry.py | 22 +- poetry/publishing/publisher.py | 26 +- poetry/publishing/uploader.py | 54 ++-- poetry/puzzle/exceptions.py | 8 +- poetry/puzzle/provider.py | 67 +++-- poetry/puzzle/solver.py | 99 ++++--- poetry/repositories/base_repository.py | 18 +- poetry/repositories/installed_repository.py | 12 +- poetry/repositories/legacy_repository.py | 46 ++-- poetry/repositories/pool.py | 38 +-- poetry/repositories/pypi_repository.py | 43 +-- poetry/repositories/remote_repository.py | 6 +- poetry/repositories/repository.py | 30 +-- poetry/utils/appdirs.py | 18 +- poetry/utils/env.py | 247 +++++++++--------- poetry/utils/exporter.py | 36 ++- poetry/utils/extras.py | 10 +- poetry/utils/helpers.py | 35 +-- poetry/utils/password_manager.py | 34 ++- poetry/utils/setup_reader.py | 44 ++-- poetry/utils/shell.py | 18 +- poetry/version/version_selector.py | 18 +- tests/conftest.py | 6 +- tests/console/commands/env/helpers.py | 4 +- tests/console/commands/test_init.py | 2 +- tests/console/commands/test_lock.py | 2 +- tests/inspection/test_info.py | 16 +- .../repositories/test_installed_repository.py | 8 +- tests/utils/test_env.py | 4 +- 102 files changed, 1176 insertions(+), 1071 deletions(-) diff --git a/poetry/config/config.py b/poetry/config/config.py index 79c7a75e95a..37844aa1fc9 100644 --- a/poetry/config/config.py +++ b/poetry/config/config.py @@ -19,11 +19,11 @@ _NOT_SET = object() -def boolean_validator(val): # type: (str) -> bool +def boolean_validator(val: str) -> bool: return val in {"true", "false", "1", "0"} -def boolean_normalizer(val): # type: (str) -> bool +def boolean_normalizer(val: str) -> bool: return val in ["true", "1"] @@ -42,8 +42,8 @@ class Config(object): } def __init__( - self, use_environment=True, base_dir=None - ): # type: (bool, Optional[Path]) -> None + self, use_environment: bool = True, base_dir: Optional[Path] = None + ) -> None: self._config = deepcopy(self.default_config) self._use_environment = use_environment self._base_dir = base_dir @@ -51,38 +51,38 @@ def __init__( self._auth_config_source = DictConfigSource() @property - def name(self): # type: () -> str + def name(self) -> str: return str(self._file.path) @property - def config(self): # type: () -> Dict + def config(self) -> Dict: return self._config @property - def config_source(self): # type: () -> ConfigSource + def config_source(self) -> ConfigSource: return self._config_source @property - def auth_config_source(self): # type: () -> ConfigSource + def auth_config_source(self) -> ConfigSource: return self._auth_config_source - def set_config_source(self, config_source): # type: (ConfigSource) -> Config + def set_config_source(self, config_source: ConfigSource) -> "Config": self._config_source = config_source return self - def set_auth_config_source(self, config_source): # type: (ConfigSource) -> Config + def set_auth_config_source(self, config_source: ConfigSource) -> "Config": self._auth_config_source = config_source return self - def merge(self, config): # type: (Dict[str, Any]) -> None + def merge(self, config: Dict[str, Any]) -> None: from poetry.utils.helpers import merge_dicts merge_dicts(self._config, config) - def all(self): # type: () -> Dict[str, Any] - def _all(config, parent_key=""): # type: (Dict, str) -> Dict + def all(self) -> Dict[str, Any]: + def _all(config: Dict, parent_key: str = "") -> Dict: all_ = {} for key in config: @@ -101,10 +101,10 @@ def _all(config, parent_key=""): # type: (Dict, str) -> Dict return _all(self.config) - def raw(self): # type: () -> Dict[str, Any] + def raw(self) -> Dict[str, Any]: return self._config - def get(self, setting_name, default=None): # type: (str, Any) -> Any + def get(self, setting_name: str, default: Any = None) -> Any: """ Retrieve a setting value. """ @@ -129,13 +129,13 @@ def get(self, setting_name, default=None): # type: (str, Any) -> Any return self.process(value) - def process(self, value): # type: (Any) -> Any + def process(self, value: Any) -> Any: if not isinstance(value, str): return value return re.sub(r"{(.+?)}", lambda m: self.get(m.group(1)), value) - def _get_normalizer(self, name): # type: (str) -> Callable + def _get_normalizer(self, name: str) -> Callable: if name in { "virtualenvs.create", "virtualenvs.in-project", diff --git a/poetry/config/config_source.py b/poetry/config/config_source.py index 63a4ad6b628..0a6707f28c9 100644 --- a/poetry/config/config_source.py +++ b/poetry/config/config_source.py @@ -2,8 +2,8 @@ class ConfigSource(object): - def add_property(self, key, value): # type: (str, Any) -> None + def add_property(self, key: str, value: Any) -> None: raise NotImplementedError() - def remove_property(self, key): # type: (str) -> None + def remove_property(self, key: str) -> None: raise NotImplementedError() diff --git a/poetry/config/dict_config_source.py b/poetry/config/dict_config_source.py index aaa6ee3b9d1..941e39e6be6 100644 --- a/poetry/config/dict_config_source.py +++ b/poetry/config/dict_config_source.py @@ -5,14 +5,14 @@ class DictConfigSource(ConfigSource): - def __init__(self): # type: () -> None + def __init__(self) -> None: self._config = {} @property - def config(self): # type: () -> Dict[str, Any] + def config(self) -> Dict[str, Any]: return self._config - def add_property(self, key, value): # type: (str, Any) -> None + def add_property(self, key: str, value: Any) -> None: keys = key.split(".") config = self._config @@ -26,7 +26,7 @@ def add_property(self, key, value): # type: (str, Any) -> None config = config[key] - def remove_property(self, key): # type: (str) -> None + def remove_property(self, key: str) -> None: keys = key.split(".") config = self._config diff --git a/poetry/config/file_config_source.py b/poetry/config/file_config_source.py index 3e7cf71a5d9..cfcdfced45b 100644 --- a/poetry/config/file_config_source.py +++ b/poetry/config/file_config_source.py @@ -1,7 +1,7 @@ from contextlib import contextmanager from typing import TYPE_CHECKING from typing import Any -from typing import Generator +from typing import Iterator from tomlkit import document from tomlkit import table @@ -10,25 +10,25 @@ if TYPE_CHECKING: - from tomlkit.toml_document import TOMLDocument # noqa + from tomlkit.toml_document import TOMLDocument - from poetry.core.toml.file import TOMLFile # noqa + from poetry.core.toml.file import TOMLFile class FileConfigSource(ConfigSource): - def __init__(self, file, auth_config=False): # type: ("TOMLFile", bool) -> None + def __init__(self, file: "TOMLFile", auth_config: bool = False) -> None: self._file = file self._auth_config = auth_config @property - def name(self): # type: () -> str + def name(self) -> str: return str(self._file.path) @property - def file(self): # type: () -> "TOMLFile" + def file(self) -> "TOMLFile": return self._file - def add_property(self, key, value): # type: (str, Any) -> None + def add_property(self, key: str, value: Any) -> None: with self.secure() as config: keys = key.split(".") @@ -42,7 +42,7 @@ def add_property(self, key, value): # type: (str, Any) -> None config = config[key] - def remove_property(self, key): # type: (str) -> None + def remove_property(self, key: str) -> None: with self.secure() as config: keys = key.split(".") @@ -59,7 +59,7 @@ def remove_property(self, key): # type: (str) -> None current_config = current_config[key] @contextmanager - def secure(self): # type: () -> Generator["TOMLDocument"] + def secure(self) -> Iterator["TOMLDocument"]: if self.file.exists(): initial_config = self.file.read() config = self.file.read() diff --git a/poetry/console/application.py b/poetry/console/application.py index 75d759efdc9..e54cbb5d846 100644 --- a/poetry/console/application.py +++ b/poetry/console/application.py @@ -6,6 +6,7 @@ from typing import Any from typing import Callable from typing import Optional +from typing import Type from typing import cast from cleo.application import Application as BaseApplication @@ -26,7 +27,7 @@ def load_command(name: str) -> Callable: - def _load(): + def _load() -> Type[Command]: module = import_module( "poetry.console.commands.{}".format(".".join(name.split(" "))) ) @@ -75,7 +76,7 @@ def _load(): if TYPE_CHECKING: - from poetry.poetry import Poetry # noqa + from poetry.poetry import Poetry class Application(BaseApplication): @@ -220,7 +221,7 @@ def register_command_loggers( logger.setLevel(level) - def set_env(self, event: ConsoleCommandEvent, event_name: str, _: Any): + def set_env(self, event: ConsoleCommandEvent, event_name: str, _: Any) -> None: from .commands.env_command import EnvCommand command: EnvCommand = cast(EnvCommand, event.command) @@ -272,7 +273,7 @@ def set_installer( command.set_installer(installer) -def main(): +def main() -> int: return Application().run() diff --git a/poetry/console/commands/about.py b/poetry/console/commands/about.py index c4415591814..76461852fd7 100644 --- a/poetry/console/commands/about.py +++ b/poetry/console/commands/about.py @@ -7,7 +7,7 @@ class AboutCommand(Command): description = "Shows information about Poetry." - def handle(self): # type: () -> None + def handle(self) -> None: self.line( """Poetry - Package Management for Python diff --git a/poetry/console/commands/add.py b/poetry/console/commands/add.py index abfcc5b15f6..af5e0837827 100644 --- a/poetry/console/commands/add.py +++ b/poetry/console/commands/add.py @@ -68,7 +68,7 @@ class AddCommand(InstallerCommand, InitCommand): loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] - def handle(self): # type: () -> int + def handle(self) -> int: from tomlkit import inline_table from poetry.core.semver import parse_constraint @@ -192,8 +192,8 @@ def handle(self): # type: () -> int return status def get_existing_packages_from_input( - self, packages, poetry_content, target_section - ): # type: (List[str], Dict, str) -> List[str] + self, packages: List[str], poetry_content: Dict, target_section: str + ) -> List[str]: existing_packages = [] for name in packages: @@ -203,9 +203,7 @@ def get_existing_packages_from_input( return existing_packages - def notify_about_existing_packages( - self, existing_packages - ): # type: (List[str]) -> None + def notify_about_existing_packages(self, existing_packages: List[str]) -> None: self.line( "The following packages are already present in the pyproject.toml and will be skipped:\n" ) diff --git a/poetry/console/commands/build.py b/poetry/console/commands/build.py index 82b5b0f86c7..7d0eda85e9b 100644 --- a/poetry/console/commands/build.py +++ b/poetry/console/commands/build.py @@ -18,7 +18,7 @@ class BuildCommand(EnvCommand): "poetry.core.masonry.builders.wheel", ] - def handle(self): # type: () -> None + def handle(self) -> None: from poetry.core.masonry import Builder fmt = "all" diff --git a/poetry/console/commands/cache/clear.py b/poetry/console/commands/cache/clear.py index 581f83c041d..7bacdb891cb 100644 --- a/poetry/console/commands/cache/clear.py +++ b/poetry/console/commands/cache/clear.py @@ -14,7 +14,7 @@ class CacheClearCommand(Command): arguments = [argument("cache", description="The name of the cache to clear.")] options = [option("all", description="Clear all entries in the cache.")] - def handle(self): # type: () -> int + def handle(self) -> int: from cachy import CacheManager from poetry.locations import REPOSITORY_CACHE_DIR diff --git a/poetry/console/commands/cache/list.py b/poetry/console/commands/cache/list.py index e6346f98ebb..b22dc9c312c 100644 --- a/poetry/console/commands/cache/list.py +++ b/poetry/console/commands/cache/list.py @@ -10,7 +10,7 @@ class CacheListCommand(Command): name = "cache list" description = "List Poetry's caches." - def handle(self): # type: () -> Optional[int] + def handle(self) -> Optional[int]: from poetry.locations import REPOSITORY_CACHE_DIR if os.path.exists(str(REPOSITORY_CACHE_DIR)): diff --git a/poetry/console/commands/check.py b/poetry/console/commands/check.py index 62af8379163..f6531dab992 100644 --- a/poetry/console/commands/check.py +++ b/poetry/console/commands/check.py @@ -11,7 +11,7 @@ class CheckCommand(Command): name = "check" description = "Checks the validity of the pyproject.toml file." - def handle(self): # type: () -> int + def handle(self) -> int: # Load poetry config and display errors, if any poetry_file = Factory.locate(Path.cwd()) config = PyProjectTOML(poetry_file).poetry_config diff --git a/poetry/console/commands/command.py b/poetry/console/commands/command.py index cb13c867909..be87fe99b7a 100644 --- a/poetry/console/commands/command.py +++ b/poetry/console/commands/command.py @@ -4,16 +4,19 @@ if TYPE_CHECKING: - from poetry.poetry import Poetry # noqa + from poetry.console.application import Application + from poetry.poetry import Poetry class Command(BaseCommand): - loggers = [] @property - def poetry(self): # type: () -> "Poetry" - return self.application.poetry + def poetry(self) -> "Poetry": + return self.get_application().poetry + + def get_application(self) -> "Application": + return self.application - def reset_poetry(self): # type: () -> None - self.application.reset_poetry() + def reset_poetry(self) -> None: + self.get_application().reset_poetry() diff --git a/poetry/console/commands/config.py b/poetry/console/commands/config.py index 30dc11ecf50..798b700fd45 100644 --- a/poetry/console/commands/config.py +++ b/poetry/console/commands/config.py @@ -19,7 +19,7 @@ if TYPE_CHECKING: - from poetry.config.config_source import ConfigSource # noqa + from poetry.config.config_source import ConfigSource class ConfigCommand(Command): @@ -51,7 +51,7 @@ class ConfigCommand(Command): LIST_PROHIBITED_SETTINGS = {"http-basic", "pypi-token"} @property - def unique_config_values(self): # type: () -> Dict[str, Tuple[Any, Any, Any]] + def unique_config_values(self) -> Dict[str, Tuple[Any, Any, Any]]: from pathlib import Path from poetry.config.config import boolean_normalizer @@ -90,7 +90,7 @@ def unique_config_values(self): # type: () -> Dict[str, Tuple[Any, Any, Any]] return unique_config_values - def handle(self): # type: () -> Optional[int] + def handle(self) -> Optional[int]: from pathlib import Path from poetry.config.file_config_source import FileConfigSource @@ -269,8 +269,12 @@ def handle(self): # type: () -> Optional[int] raise ValueError("Setting {} does not exist".format(self.argument("key"))) def _handle_single_value( - self, source, key, callbacks, values - ): # type: ("ConfigSource", str, Tuple[Any, Any, Any], List[Any]) -> int + self, + source: "ConfigSource", + key: str, + callbacks: Tuple[Any, Any, Any], + values: List[Any], + ) -> int: validator, normalizer, _ = callbacks if len(values) > 1: @@ -284,7 +288,7 @@ def _handle_single_value( return 0 - def _list_configuration(self, config, raw, k=""): # type: (Dict, Dict, str) -> None + def _list_configuration(self, config: Dict, raw: Dict, k: str = "") -> None: orig_k = k for key, value in sorted(config.items()): if k + key in self.LIST_PROHIBITED_SETTINGS: @@ -319,8 +323,12 @@ def _list_configuration(self, config, raw, k=""): # type: (Dict, Dict, str) -> self.line(message) def _get_setting( - self, contents, setting=None, k=None, default=None - ): # type: (Dict, Optional[str], Optional[str], Optional[Any]) -> List[Tuple[str, str]] + self, + contents: Dict, + setting: Optional[str] = None, + k: Optional[str] = None, + default: Optional[Any] = None, + ) -> List[Tuple[str, str]]: orig_k = k if setting and setting.split(".")[0] not in contents: diff --git a/poetry/console/commands/debug/info.py b/poetry/console/commands/debug/info.py index 140b26725f6..b007d93d4c8 100644 --- a/poetry/console/commands/debug/info.py +++ b/poetry/console/commands/debug/info.py @@ -8,7 +8,7 @@ class DebugInfoCommand(Command): name = "debug info" description = "Shows debug information." - def handle(self): # type: () -> int + def handle(self) -> int: poetry_python_version = ".".join(str(s) for s in sys.version_info[:3]) self.line("") diff --git a/poetry/console/commands/debug/resolve.py b/poetry/console/commands/debug/resolve.py index ef60cbf134a..06b0fa9d21f 100644 --- a/poetry/console/commands/debug/resolve.py +++ b/poetry/console/commands/debug/resolve.py @@ -1,3 +1,4 @@ +from typing import TYPE_CHECKING from typing import Optional from cleo.helpers import argument @@ -7,6 +8,10 @@ from ..init import InitCommand +if TYPE_CHECKING: + from poetry.console.commands.show import ShowCommand + + class DebugResolveCommand(InitCommand): name = "debug resolve" @@ -30,7 +35,7 @@ class DebugResolveCommand(InitCommand): loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] - def handle(self): # type: () -> Optional[int] + def handle(self) -> Optional[int]: from cleo.io.null_io import NullIO from poetry.core.packages.project_package import ProjectPackage @@ -88,7 +93,7 @@ def handle(self): # type: () -> Optional[int] self.line("") if self.option("tree"): - show_command = self.application.find("show") + show_command: ShowCommand = self.application.find("show") show_command.init_styles(self.io) packages = [op.package for op in ops] diff --git a/poetry/console/commands/env/info.py b/poetry/console/commands/env/info.py index dea2ade994a..aecce3628ac 100644 --- a/poetry/console/commands/env/info.py +++ b/poetry/console/commands/env/info.py @@ -7,7 +7,7 @@ if TYPE_CHECKING: - from poetry.utils.env import Env # noqa + from poetry.utils.env import Env class EnvInfoCommand(Command): @@ -17,7 +17,7 @@ class EnvInfoCommand(Command): options = [option("path", "p", "Only display the environment's path.")] - def handle(self): # type: () -> Optional[int] + def handle(self) -> Optional[int]: from poetry.utils.env import EnvManager env = EnvManager(self.poetry).get() @@ -32,7 +32,7 @@ def handle(self): # type: () -> Optional[int] self._display_complete_info(env) - def _display_complete_info(self, env): # type: ("Env") -> None + def _display_complete_info(self, env: "Env") -> None: env_python_version = ".".join(str(s) for s in env.version_info[:3]) self.line("") self.line("Virtualenv") diff --git a/poetry/console/commands/env/list.py b/poetry/console/commands/env/list.py index f422cc8ef2a..e7b1eac6cd5 100644 --- a/poetry/console/commands/env/list.py +++ b/poetry/console/commands/env/list.py @@ -10,7 +10,7 @@ class EnvListCommand(Command): options = [option("full-path", None, "Output the full paths of the virtualenvs.")] - def handle(self): # type: () -> None + def handle(self) -> None: from poetry.utils.env import EnvManager manager = EnvManager(self.poetry) diff --git a/poetry/console/commands/env/remove.py b/poetry/console/commands/env/remove.py index c8e5f1360e1..9d5153c420b 100644 --- a/poetry/console/commands/env/remove.py +++ b/poetry/console/commands/env/remove.py @@ -12,7 +12,7 @@ class EnvRemoveCommand(Command): argument("python", "The python executable to remove the virtualenv for.") ] - def handle(self): # type: () -> None + def handle(self) -> None: from poetry.utils.env import EnvManager manager = EnvManager(self.poetry) diff --git a/poetry/console/commands/env/use.py b/poetry/console/commands/env/use.py index 13728c1d9ee..fa8a455bcf7 100644 --- a/poetry/console/commands/env/use.py +++ b/poetry/console/commands/env/use.py @@ -10,7 +10,7 @@ class EnvUseCommand(Command): arguments = [argument("python", "The python executable to use.")] - def handle(self): # type: () -> None + def handle(self) -> None: from poetry.utils.env import EnvManager manager = EnvManager(self.poetry) diff --git a/poetry/console/commands/env_command.py b/poetry/console/commands/env_command.py index a7ae5e7bdda..beb40e1e88e 100644 --- a/poetry/console/commands/env_command.py +++ b/poetry/console/commands/env_command.py @@ -4,18 +4,18 @@ if TYPE_CHECKING: - from poetry.utils.env import VirtualEnv # noqa + from poetry.utils.env import VirtualEnv class EnvCommand(Command): - def __init__(self): # type: () -> None + def __init__(self) -> None: self._env = None super(EnvCommand, self).__init__() @property - def env(self): # type: () -> "VirtualEnv" + def env(self) -> "VirtualEnv": return self._env - def set_env(self, env): # type: ("VirtualEnv") -> None + def set_env(self, env: "VirtualEnv") -> None: self._env = env diff --git a/poetry/console/commands/export.py b/poetry/console/commands/export.py index 6d4f2a830fc..a58d96f50fe 100644 --- a/poetry/console/commands/export.py +++ b/poetry/console/commands/export.py @@ -31,7 +31,7 @@ class ExportCommand(Command): option("with-credentials", None, "Include credentials for extra indices."), ] - def handle(self): # type: () -> None + def handle(self) -> None: fmt = self.option("format") if fmt not in Exporter.ACCEPTED_FORMATS: diff --git a/poetry/console/commands/init.py b/poetry/console/commands/init.py index 110f97a9a9c..fbc94b9358c 100644 --- a/poetry/console/commands/init.py +++ b/poetry/console/commands/init.py @@ -7,6 +7,7 @@ import urllib.parse from pathlib import Path +from typing import TYPE_CHECKING from typing import Dict from typing import List from typing import Optional @@ -23,6 +24,10 @@ from .env_command import EnvCommand +if TYPE_CHECKING: + from poetry.repositories import Pool + + class InitCommand(Command): name = "init" description = ( @@ -57,12 +62,12 @@ class InitCommand(Command): The init command creates a basic pyproject.toml file in the current directory. """ - def __init__(self): # type: () -> None + def __init__(self) -> None: super(InitCommand, self).__init__() self._pool = None - def handle(self): # type: () -> int + def handle(self) -> int: from pathlib import Path from poetry.core.vcs.git import GitConfig @@ -227,8 +232,11 @@ def handle(self): # type: () -> int f.write(content) def _determine_requirements( - self, requires, allow_prereleases=False, source=None - ): # type: (List[str], bool, Optional[str]) -> List[Dict[str, Union[str, List[str]]]] + self, + requires: List[str], + allow_prereleases: bool = False, + source: Optional[str] = None, + ) -> List[Dict[str, Union[str, List[str]]]]: if not requires: requires = [] @@ -354,8 +362,12 @@ def _determine_requirements( return result def _find_best_version_for_package( - self, name, required_version=None, allow_prereleases=False, source=None - ): # type: (str, Optional[str], bool, Optional[str]) -> Tuple[str, str] + self, + name: str, + required_version: Optional[str] = None, + allow_prereleases: bool = False, + source: Optional[str] = None, + ) -> Tuple[str, str]: from poetry.version.version_selector import VersionSelector selector = VersionSelector(self._get_pool()) @@ -371,9 +383,7 @@ def _find_best_version_for_package( return package.pretty_name, selector.find_recommended_require_version(package) - def _parse_requirements( - self, requirements - ): # type: (List[str]) -> List[Dict[str, str]] + def _parse_requirements(self, requirements: List[str]) -> List[Dict[str, str]]: from poetry.puzzle.provider import Provider result = [] @@ -490,8 +500,8 @@ def _parse_requirements( return result def _format_requirements( - self, requirements - ): # type: (List[Dict[str, str]]) -> Dict[str, Union[str, Dict[str, str]]] + self, requirements: List[Dict[str, str]] + ) -> Dict[str, Union[str, Dict[str, str]]]: requires = {} for requirement in requirements: name = requirement.pop("name") @@ -506,7 +516,7 @@ def _format_requirements( return requires - def _validate_author(self, author, default): # type: (str, str) -> Optional[str] + def _validate_author(self, author: str, default: str) -> Optional[str]: from poetry.core.packages.package import AUTHOR_REGEX author = author or default @@ -523,7 +533,7 @@ def _validate_author(self, author, default): # type: (str, str) -> Optional[str return author - def _validate_license(self, license): # type: (str) -> str + def _validate_license(self, license: str) -> str: from poetry.core.spdx import license_by_id if license: @@ -531,7 +541,7 @@ def _validate_license(self, license): # type: (str) -> str return license - def _get_pool(self): # type: () -> "Pool" + def _get_pool(self) -> "Pool": from poetry.repositories import Pool from poetry.repositories.pypi_repository import PyPiRepository diff --git a/poetry/console/commands/install.py b/poetry/console/commands/install.py index d07c5cef93f..0ffb30a3756 100644 --- a/poetry/console/commands/install.py +++ b/poetry/console/commands/install.py @@ -50,7 +50,7 @@ class InstallCommand(InstallerCommand): _loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] - def handle(self): # type: () -> int + def handle(self) -> int: from poetry.core.masonry.utils.module import ModuleOrPackageNotFound from poetry.masonry.builders import EditableBuilder diff --git a/poetry/console/commands/installer_command.py b/poetry/console/commands/installer_command.py index dd8ca1a8842..409e2fc456c 100644 --- a/poetry/console/commands/installer_command.py +++ b/poetry/console/commands/installer_command.py @@ -9,20 +9,20 @@ class InstallerCommand(EnvCommand): - def __init__(self): # type: () -> None - self._installer = None # type: Optional[Installer] + def __init__(self) -> None: + self._installer: Optional["Installer"] = None super(InstallerCommand, self).__init__() - def reset_poetry(self): # type: () -> None + def reset_poetry(self) -> None: super(InstallerCommand, self).reset_poetry() self._installer.set_package(self.poetry.package) self._installer.set_locker(self.poetry.locker) @property - def installer(self): # type: () -> Installer + def installer(self) -> "Installer": return self._installer - def set_installer(self, installer): # type: (Installer) -> None + def set_installer(self, installer: "Installer") -> None: self._installer = installer diff --git a/poetry/console/commands/lock.py b/poetry/console/commands/lock.py index d2895e8bee6..c62515f8a67 100644 --- a/poetry/console/commands/lock.py +++ b/poetry/console/commands/lock.py @@ -24,7 +24,7 @@ class LockCommand(InstallerCommand): loggers = ["poetry.repositories.pypi_repository"] - def handle(self): # type: () -> int + def handle(self) -> int: self._installer.use_executor( self.poetry.config.get("experimental.new-installer", False) ) diff --git a/poetry/console/commands/new.py b/poetry/console/commands/new.py index 3276ee7d9fd..e80e589d39c 100644 --- a/poetry/console/commands/new.py +++ b/poetry/console/commands/new.py @@ -19,7 +19,7 @@ class NewCommand(Command): option("src", None, "Use the src layout for the project."), ] - def handle(self): # type: () -> None + def handle(self) -> None: from pathlib import Path from poetry.core.semver import parse_constraint diff --git a/poetry/console/commands/publish.py b/poetry/console/commands/publish.py index 724e868a58a..bd777bb65d2 100644 --- a/poetry/console/commands/publish.py +++ b/poetry/console/commands/publish.py @@ -41,7 +41,7 @@ class PublishCommand(Command): loggers = ["poetry.masonry.publishing.publisher"] - def handle(self): # type: () -> Optional[int] + def handle(self) -> Optional[int]: from poetry.publishing.publisher import Publisher publisher = Publisher(self.poetry, self.io) diff --git a/poetry/console/commands/remove.py b/poetry/console/commands/remove.py index 15e05de2860..a7b2e00a26f 100644 --- a/poetry/console/commands/remove.py +++ b/poetry/console/commands/remove.py @@ -27,7 +27,7 @@ class RemoveCommand(InstallerCommand): loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] - def handle(self): # type: () -> int + def handle(self) -> int: packages = self.argument("packages") is_dev = self.option("dev") diff --git a/poetry/console/commands/run.py b/poetry/console/commands/run.py index 9879b3ed09c..fd23f93ddd7 100644 --- a/poetry/console/commands/run.py +++ b/poetry/console/commands/run.py @@ -1,3 +1,4 @@ +from typing import TYPE_CHECKING from typing import Any from typing import Union @@ -6,6 +7,10 @@ from .env_command import EnvCommand +if TYPE_CHECKING: + from poetry.core.masonry.utils.module import Module + + class RunCommand(EnvCommand): name = "run" @@ -15,7 +20,7 @@ class RunCommand(EnvCommand): argument("args", "The command and arguments/options to run.", multiple=True) ] - def handle(self): # type: () -> Any + def handle(self) -> Any: args = self.argument("args") script = args[0] scripts = self.poetry.local_config.get("scripts") @@ -26,7 +31,7 @@ def handle(self): # type: () -> Any return self.env.execute(*args) @property - def _module(self): + def _module(self) -> "Module": from poetry.core.masonry.utils.module import Module poetry = self.poetry @@ -36,7 +41,7 @@ def _module(self): return module - def run_script(self, script, args): # type: (Union[str, dict], str) -> Any + def run_script(self, script: Union[str, dict], args: str) -> Any: if isinstance(script, dict): script = script["callable"] diff --git a/poetry/console/commands/search.py b/poetry/console/commands/search.py index 27c4671a45b..85384125bca 100644 --- a/poetry/console/commands/search.py +++ b/poetry/console/commands/search.py @@ -10,7 +10,7 @@ class SearchCommand(Command): arguments = [argument("tokens", "The tokens to search for.", multiple=True)] - def handle(self): # type: () -> None + def handle(self) -> None: from poetry.repositories.pypi_repository import PyPiRepository results = PyPiRepository().search(self.argument("tokens")) diff --git a/poetry/console/commands/self/update.py b/poetry/console/commands/self/update.py index d0d00ddbefd..06ef628b786 100644 --- a/poetry/console/commands/self/update.py +++ b/poetry/console/commands/self/update.py @@ -11,6 +11,7 @@ from functools import cmp_to_key from gzip import GzipFile +from pathlib import Path from typing import TYPE_CHECKING from typing import Any @@ -24,9 +25,8 @@ if TYPE_CHECKING: - from poetry.core.packages import Package # noqa + from poetry.core.packages import Package from poetry.core.semver import Version - from poetry.utils._compat import Path try: @@ -70,24 +70,24 @@ class SelfUpdateCommand(Command): BASE_URL = REPOSITORY_URL + "/releases/download" @property - def home(self): # type: () -> Path + def home(self) -> Path: from pathlib import Path return Path(os.environ.get("POETRY_HOME", "~/.poetry")).expanduser() @property - def bin(self): # type: () -> Path + def bin(self) -> Path: return self.home / "bin" @property - def lib(self): # type: () -> Path + def lib(self) -> Path: return self.home / "lib" @property - def lib_backup(self): # type: () -> Path + def lib_backup(self) -> Path: return self.home / "lib-backup" - def handle(self): # type: () -> None + def handle(self) -> None: from poetry.__version__ import __version__ from poetry.core.semver import Version from poetry.repositories.pypi_repository import PyPiRepository @@ -138,7 +138,7 @@ def handle(self): # type: () -> None self.update(release) - def update(self, release): # type: ("Package") -> None + def update(self, release: "Package") -> None: version = release.version self.line("Updating to {}".format(version)) @@ -174,7 +174,7 @@ def update(self, release): # type: ("Package") -> None ) ) - def _update(self, version): # type: ("Version") -> None + def _update(self, version: "Version") -> None: from poetry.utils.helpers import temporary_directory release_name = self._get_release_name(version) @@ -244,10 +244,10 @@ def _update(self, version): # type: ("Version") -> None finally: gz.close() - def process(self, *args): # type: (*Any) -> str + def process(self, *args: Any) -> str: return subprocess.check_output(list(args), stderr=subprocess.STDOUT) - def _check_recommended_installation(self): # type: () -> None + def _check_recommended_installation(self) -> None: from pathlib import Path current = Path(__file__) @@ -259,14 +259,14 @@ def _check_recommended_installation(self): # type: () -> None "so it cannot be updated automatically." ) - def _get_release_name(self, version): # type: ("Version") -> str + def _get_release_name(self, version: "Version") -> str: platform = sys.platform if platform == "linux2": platform = "linux" return "poetry-{}-{}".format(version, platform) - def make_bin(self): # type: () -> None + def make_bin(self) -> None: from poetry.utils._compat import WINDOWS self.bin.mkdir(0o755, parents=True, exist_ok=True) @@ -295,7 +295,7 @@ def make_bin(self): # type: () -> None st = os.stat(str(self.bin.joinpath("poetry"))) os.chmod(str(self.bin.joinpath("poetry")), st.st_mode | stat.S_IEXEC) - def _which_python(self): # type: () -> str + def _which_python(self) -> str: """ Decides which python executable we'll embed in the launcher script. """ diff --git a/poetry/console/commands/shell.py b/poetry/console/commands/shell.py index 35269d38d3d..f0f7f851fba 100644 --- a/poetry/console/commands/shell.py +++ b/poetry/console/commands/shell.py @@ -16,7 +16,7 @@ class ShellCommand(EnvCommand): If one doesn't exist yet, it will be created. """ - def handle(self): # type: () -> None + def handle(self) -> None: from poetry.utils.shell import Shell # Check if it's already activated or doesn't exist and won't be created diff --git a/poetry/console/commands/show.py b/poetry/console/commands/show.py index 07a58cd0754..e07b97e13a6 100644 --- a/poetry/console/commands/show.py +++ b/poetry/console/commands/show.py @@ -13,8 +13,8 @@ if TYPE_CHECKING: from cleo.io.io import IO # noqa - from poetry.core.packages import Dependency # noqa - from poetry.core.packages import Package # noqa + from poetry.core.packages import Dependency + from poetry.core.packages import Package from poetry.repositories import Repository from poetry.repositories.installed_repository import InstalledRepository @@ -46,7 +46,7 @@ class ShowCommand(EnvCommand): colors = ["cyan", "yellow", "green", "magenta", "blue"] - def handle(self): # type: () -> Optional[int] + def handle(self) -> Optional[int]: from cleo.io.null_io import NullIO from cleo.terminal import Terminal @@ -271,8 +271,8 @@ def handle(self): # type: () -> Optional[int] self.line(line) def display_package_tree( - self, io, package, installed_repo - ): # type: ("IO", "Package", "Repository") -> None + self, io: "IO", package: "Package", installed_repo: "Repository" + ) -> None: io.write("{}".format(package.pretty_name)) description = "" if package.description: @@ -309,13 +309,13 @@ def display_package_tree( def _display_tree( self, - io, # type: "IO" - dependency, # type: "Dependency" - installed_repo, # type: "Repository" - packages_in_tree, # type: List[str] - previous_tree_bar="├", # type: str - level=1, # type: int - ): # type: (...) -> None + io: "IO", + dependency: "Dependency", + installed_repo: "Repository", + packages_in_tree: List[str], + previous_tree_bar: str = "├", + level: int = 1, + ) -> None: previous_tree_bar = previous_tree_bar.replace("├", "│") dependencies = [] @@ -360,7 +360,7 @@ def _display_tree( io, dependency, installed_repo, current_tree, tree_bar, level + 1 ) - def _write_tree_line(self, io, line): # type: ("IO", str) -> None + def _write_tree_line(self, io: "IO", line: str) -> None: if not io.output.supports_utf8(): line = line.replace("└", "`-") line = line.replace("├", "|-") @@ -369,7 +369,7 @@ def _write_tree_line(self, io, line): # type: ("IO", str) -> None io.write_line(line) - def init_styles(self, io): # type: ("IO") -> None + def init_styles(self, io: "IO") -> None: from cleo.formatters.style import Style for color in self.colors: @@ -378,8 +378,8 @@ def init_styles(self, io): # type: ("IO") -> None io.error_output.formatter.set_style(color, style) def find_latest_package( - self, package, include_dev - ): # type: ("Package", bool) -> Union["Package", bool] + self, package: "Package", include_dev: bool + ) -> Union["Package", bool]: from cleo.io.null_io import NullIO from poetry.puzzle.provider import Provider @@ -407,7 +407,7 @@ def find_latest_package( return selector.find_best_candidate(name, ">={}".format(package.pretty_version)) - def get_update_status(self, latest, package): # type: ("Package", "Package") -> str + def get_update_status(self, latest: "Package", package: "Package") -> str: from poetry.core.semver import parse_constraint if latest.full_pretty_version == package.full_pretty_version: @@ -423,8 +423,8 @@ def get_update_status(self, latest, package): # type: ("Package", "Package") -> return "update-possible" def get_installed_status( - self, locked, installed_repo - ): # type: ("Package", "InstalledRepository") -> str + self, locked: "Package", installed_repo: "InstalledRepository" + ) -> str: for package in installed_repo.packages: if locked.name == package.name: return "installed" diff --git a/poetry/console/commands/update.py b/poetry/console/commands/update.py index 42bb64aba37..fd2b421bce2 100644 --- a/poetry/console/commands/update.py +++ b/poetry/console/commands/update.py @@ -27,7 +27,7 @@ class UpdateCommand(InstallerCommand): loggers = ["poetry.repositories.pypi_repository"] - def handle(self): # type: () -> int + def handle(self) -> int: packages = self.argument("packages") self._installer.use_executor( diff --git a/poetry/console/commands/version.py b/poetry/console/commands/version.py index 2fadaac4b39..cffe1b0f723 100644 --- a/poetry/console/commands/version.py +++ b/poetry/console/commands/version.py @@ -1,9 +1,15 @@ +from typing import TYPE_CHECKING + from cleo.helpers import argument from cleo.helpers import option from .command import Command +if TYPE_CHECKING: + from poetry.core.semver import Version + + class VersionCommand(Command): name = "version" @@ -40,7 +46,7 @@ class VersionCommand(Command): "prerelease", } - def handle(self): # type: () -> None + def handle(self) -> None: version = self.argument("version") if version: @@ -72,7 +78,7 @@ def handle(self): # type: () -> None ) ) - def increment_version(self, version, rule): # type: (str, str) -> "Version" + def increment_version(self, version: str, rule: str) -> "Version": from poetry.core.semver import Version try: diff --git a/poetry/console/logging/formatters/builder_formatter.py b/poetry/console/logging/formatters/builder_formatter.py index 56bed9b67a3..69641e7662a 100644 --- a/poetry/console/logging/formatters/builder_formatter.py +++ b/poetry/console/logging/formatters/builder_formatter.py @@ -4,7 +4,7 @@ class BuilderLogFormatter(Formatter): - def format(self, msg): # type: (str) -> str + def format(self, msg: str) -> str: if msg.startswith("Building "): msg = re.sub("Building (.+)", " - Building \\1", msg) elif msg.startswith("Built "): diff --git a/poetry/console/logging/formatters/formatter.py b/poetry/console/logging/formatters/formatter.py index 35b59374be4..f2ab7b18624 100644 --- a/poetry/console/logging/formatters/formatter.py +++ b/poetry/console/logging/formatters/formatter.py @@ -2,5 +2,5 @@ class Formatter(object): - def format(self, record): # type: (logging.LogRecord) -> str + def format(self, record: logging.LogRecord) -> str: raise NotImplementedError() diff --git a/poetry/console/logging/io_formatter.py b/poetry/console/logging/io_formatter.py index 68d15691edd..c7ca46fef97 100644 --- a/poetry/console/logging/io_formatter.py +++ b/poetry/console/logging/io_formatter.py @@ -6,7 +6,7 @@ if TYPE_CHECKING: - from logging import LogRecord # noqa + from logging import LogRecord class IOFormatter(logging.Formatter): @@ -18,7 +18,7 @@ class IOFormatter(logging.Formatter): "info": "fg=blue", } - def format(self, record): # type: ("LogRecord") -> str + def format(self, record: "LogRecord") -> str: if not record.exc_info: level = record.levelname.lower() msg = record.msg diff --git a/poetry/console/logging/io_handler.py b/poetry/console/logging/io_handler.py index 03804c3a669..9a13e68b0e9 100644 --- a/poetry/console/logging/io_handler.py +++ b/poetry/console/logging/io_handler.py @@ -4,18 +4,18 @@ if TYPE_CHECKING: - from logging import LogRecord # noqa + from logging import LogRecord from cleo.io.io import IO # noqa class IOHandler(logging.Handler): - def __init__(self, io): # type: ("IO") -> None + def __init__(self, io: "IO") -> None: self._io = io super(IOHandler, self).__init__() - def emit(self, record): # type: ("LogRecord") -> None + def emit(self, record: "LogRecord") -> None: try: msg = self.format(record) level = record.levelname.lower() diff --git a/poetry/factory.py b/poetry/factory.py index d8a9da7ed77..43555eef6c3 100644 --- a/poetry/factory.py +++ b/poetry/factory.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from pathlib import Path +from typing import TYPE_CHECKING from typing import Dict from typing import Optional @@ -19,14 +20,18 @@ from .repositories.pypi_repository import PyPiRepository +if TYPE_CHECKING: + from .repositories.legacy_repository import LegacyRepository + + class Factory(BaseFactory): """ Factory class to create various elements needed by Poetry. """ def create_poetry( - self, cwd=None, io=None - ): # type: (Optional[Path], Optional[IO]) -> Poetry + self, cwd: Optional[Path] = None, io: Optional[IO] = None + ) -> Poetry: if io is None: io = NullIO() @@ -100,7 +105,7 @@ def create_poetry( return poetry @classmethod - def create_config(cls, io=None): # type: (Optional[IO]) -> Config + def create_config(cls, io: Optional[IO] = None) -> Config: if io is None: io = NullIO() @@ -136,8 +141,8 @@ def create_config(cls, io=None): # type: (Optional[IO]) -> Config return config def create_legacy_repository( - self, source, auth_config - ): # type: (Dict[str, str], Config) -> "LegacyRepository" + self, source: Dict[str, str], auth_config: Config + ) -> "LegacyRepository": from .repositories.legacy_repository import LegacyRepository from .utils.helpers import get_cert from .utils.helpers import get_client_cert diff --git a/poetry/inspection/info.py b/poetry/inspection/info.py index 495a62ca3c0..1814519d240 100644 --- a/poetry/inspection/info.py +++ b/poetry/inspection/info.py @@ -43,8 +43,8 @@ class PackageInfoError(ValueError): def __init__( - self, path, *reasons - ): # type: (Union[Path, str], *Union[BaseException, str]) -> None + self, path: Union[Path, str], *reasons: Union[BaseException, str] + ) -> None: reasons = ( "Unable to determine package info for path: {}".format(str(path)), ) + reasons @@ -56,14 +56,14 @@ def __init__( class PackageInfo: def __init__( self, - name=None, # type: Optional[str] - version=None, # type: Optional[str] - summary=None, # type: Optional[str] - platform=None, # type: Optional[str] - requires_dist=None, # type: Optional[List[str]] - requires_python=None, # type: Optional[str] - files=None, # type: Optional[List[str]] - cache_version=None, # type: Optional[str] + name: Optional[str] = None, + version: Optional[str] = None, + summary: Optional[str] = None, + platform: Optional[str] = None, + requires_dist: Optional[List[str]] = None, + requires_python: Optional[str] = None, + files: Optional[List[str]] = None, + cache_version: Optional[str] = None, ): self.name = name self.version = version @@ -78,10 +78,10 @@ def __init__( self._source_reference = None @property - def cache_version(self): # type: () -> Optional[str] + def cache_version(self) -> Optional[str]: return self._cache_version - def update(self, other): # type: (PackageInfo) -> PackageInfo + def update(self, other: "PackageInfo") -> "PackageInfo": self.name = other.name or self.name self.version = other.version or self.version self.summary = other.summary or self.summary @@ -92,7 +92,7 @@ def update(self, other): # type: (PackageInfo) -> PackageInfo self._cache_version = other.cache_version or self._cache_version return self - def asdict(self): # type: () -> Dict[str, Optional[Union[str, List[str]]]] + def asdict(self) -> Dict[str, Optional[Union[str, List[str]]]]: """ Helper method to convert package info into a dictionary used for caching. """ @@ -108,9 +108,7 @@ def asdict(self): # type: () -> Dict[str, Optional[Union[str, List[str]]]] } @classmethod - def load( - cls, data - ): # type: (Dict[str, Optional[Union[str, List[str]]]]) -> PackageInfo + def load(cls, data: Dict[str, Optional[Union[str, List[str]]]]) -> "PackageInfo": """ Helper method to load data from a dictionary produced by `PackageInfo.asdict()`. @@ -120,13 +118,16 @@ def load( return cls(cache_version=cache_version, **data) @classmethod - def _log(cls, msg, level="info"): # type: (str, str) -> None + def _log(cls, msg: str, level: str = "info") -> None: """Internal helper method to log information.""" getattr(logger, level)("{}: {}".format(cls.__name__, msg)) def to_package( - self, name=None, extras=None, root_dir=None - ): # type: (Optional[str], Optional[List[str]], Optional[Path]) -> Package + self, + name: Optional[str] = None, + extras: Optional[List[str]] = None, + root_dir: Optional[Path] = None, + ) -> Package: """ Create a new `poetry.core.packages.package.Package` instance using metadata from this instance. @@ -202,8 +203,8 @@ def to_package( @classmethod def _from_distribution( - cls, dist - ): # type: (Union[pkginfo.BDist, pkginfo.SDist, pkginfo.Wheel]) -> PackageInfo + cls, dist: Union[pkginfo.BDist, pkginfo.SDist, pkginfo.Wheel] + ) -> "PackageInfo": """ Helper method to parse package information from a `pkginfo.Distribution` instance. @@ -234,7 +235,7 @@ def _from_distribution( return info @classmethod - def _from_sdist_file(cls, path): # type: (Path) -> PackageInfo + def _from_sdist_file(cls, path: Path) -> "PackageInfo": """ Helper method to parse package information from an sdist file. We attempt to first inspect the file using `pkginfo.SDist`. If this does not provide us with package requirements, we extract the @@ -295,11 +296,11 @@ def _from_sdist_file(cls, path): # type: (Path) -> PackageInfo return info.update(new_info) @staticmethod - def has_setup_files(path): # type: (Path) -> bool + def has_setup_files(path: Path) -> bool: return any((path / f).exists() for f in SetupReader.FILES) @classmethod - def from_setup_files(cls, path): # type: (Path) -> PackageInfo + def from_setup_files(cls, path: Path) -> "PackageInfo": """ Mechanism to parse package information from a `setup.[py|cfg]` file. This uses the implementation at `poetry.utils.setup_reader.SetupReader` in order to parse the file. This is not reliable for @@ -356,7 +357,7 @@ def from_setup_files(cls, path): # type: (Path) -> PackageInfo return info @staticmethod - def _find_dist_info(path): # type: (Path) -> Iterator[Path] + def _find_dist_info(path: Path) -> Iterator[Path]: """ Discover all `*.*-info` directories in a given path. @@ -372,7 +373,7 @@ def _find_dist_info(path): # type: (Path) -> Iterator[Path] yield Path(d) @classmethod - def from_metadata(cls, path): # type: (Path) -> Optional[PackageInfo] + def from_metadata(cls, path: Path) -> Optional["PackageInfo"]: """ Helper method to parse package information from an unpacked metadata directory. @@ -406,7 +407,7 @@ def from_metadata(cls, path): # type: (Path) -> Optional[PackageInfo] return info @classmethod - def from_package(cls, package): # type: (Package) -> PackageInfo + def from_package(cls, package: Package) -> "PackageInfo": """ Helper method to inspect a `Package` object, in order to generate package info. @@ -429,14 +430,14 @@ def from_package(cls, package): # type: (Package) -> PackageInfo ) @staticmethod - def _get_poetry_package(path): # type: (Path) -> Optional[ProjectPackage] + def _get_poetry_package(path: Path) -> Optional[ProjectPackage]: # Note: we ignore any setup.py file at this step # TODO: add support for handling non-poetry PEP-517 builds if PyProjectTOML(path.joinpath("pyproject.toml")).is_poetry_project(): return Factory().create_poetry(path).package @classmethod - def _pep517_metadata(cls, path): # type: (Path) -> PackageInfo + def _pep517_metadata(cls, path: Path) -> "PackageInfo": """ Helper method to use PEP-517 library to build and read package metadata. @@ -511,9 +512,7 @@ def _pep517_metadata(cls, path): # type: (Path) -> PackageInfo raise PackageInfoError(path, "Exhausted all core metadata sources.") @classmethod - def from_directory( - cls, path, disable_build=False - ): # type: (Path, bool) -> PackageInfo + def from_directory(cls, path: Path, disable_build: bool = False) -> "PackageInfo": """ Generate package information from a package source directory. If `disable_build` is not `True` and introspection of all available metadata fails, the package is attempted to be build in an isolated @@ -547,7 +546,7 @@ def from_directory( return info @classmethod - def from_sdist(cls, path): # type: (Path) -> PackageInfo + def from_sdist(cls, path: Path) -> "PackageInfo": """ Gather package information from an sdist file, packed or unpacked. @@ -561,7 +560,7 @@ def from_sdist(cls, path): # type: (Path) -> PackageInfo return cls.from_directory(path=path) @classmethod - def from_wheel(cls, path): # type: (Path) -> PackageInfo + def from_wheel(cls, path: Path) -> "PackageInfo": """ Gather package information from a wheel. @@ -573,7 +572,7 @@ def from_wheel(cls, path): # type: (Path) -> PackageInfo return PackageInfo() @classmethod - def from_bdist(cls, path): # type: (Path) -> PackageInfo + def from_bdist(cls, path: Path) -> "PackageInfo": """ Gather package information from a bdist (wheel etc.). @@ -591,7 +590,7 @@ def from_bdist(cls, path): # type: (Path) -> PackageInfo raise PackageInfoError(path, e) @classmethod - def from_path(cls, path): # type: (Path) -> PackageInfo + def from_path(cls, path: Path) -> "PackageInfo": """ Gather package information from a given path (bdist, sdist, directory). diff --git a/poetry/installation/authenticator.py b/poetry/installation/authenticator.py index f40c03d2b7e..84ba26c027e 100644 --- a/poetry/installation/authenticator.py +++ b/poetry/installation/authenticator.py @@ -3,6 +3,9 @@ import urllib.parse from typing import TYPE_CHECKING +from typing import Any +from typing import Optional +from typing import Tuple import requests import requests.auth @@ -13,10 +16,6 @@ if TYPE_CHECKING: - from typing import Any - from typing import Optional - from typing import Tuple - from cleo.io.io import IO from poetry.config.config import Config @@ -26,14 +25,14 @@ class Authenticator(object): - def __init__(self, config, io=None): # type: (Config, Optional[IO]) -> None + def __init__(self, config: "Config", io: Optional["IO"] = None) -> None: self._config = config self._io = io self._session = None self._credentials = {} self._password_manager = PasswordManager(self._config) - def _log(self, message, level="debug"): # type: (str, str) -> None + def _log(self, message: str, level: str = "debug") -> None: if self._io is not None: self._io.write_line( "<{level:s}>{message:s}".format( @@ -44,15 +43,13 @@ def _log(self, message, level="debug"): # type: (str, str) -> None getattr(logger, level, logger.debug)(message) @property - def session(self): # type: () -> requests.Session + def session(self) -> requests.Session: if self._session is None: self._session = requests.Session() return self._session - def request( - self, method, url, **kwargs - ): # type: (str, str, Any) -> requests.Response + def request(self, method: str, url: str, **kwargs: Any) -> requests.Response: request = requests.Request(method, url) username, password = self.get_credentials_for_url(url) @@ -104,9 +101,7 @@ def request( # this should never really be hit under any sane circumstance raise PoetryException("Failed HTTP {} request", method.upper()) - def get_credentials_for_url( - self, url - ): # type: (str) -> Tuple[Optional[str], Optional[str]] + def get_credentials_for_url(self, url: str) -> Tuple[Optional[str], Optional[str]]: parsed_url = urllib.parse.urlsplit(url) netloc = parsed_url.netloc @@ -141,8 +136,8 @@ def get_credentials_for_url( return credentials[0], credentials[1] def _get_credentials_for_netloc_from_config( - self, netloc - ): # type: (str) -> Tuple[Optional[str], Optional[str]] + self, netloc: str + ) -> Tuple[Optional[str], Optional[str]]: credentials = (None, None) for repository_name in self._config.get("repositories", []): diff --git a/poetry/installation/base_installer.py b/poetry/installation/base_installer.py index 4f600e880d4..c377dea7058 100644 --- a/poetry/installation/base_installer.py +++ b/poetry/installation/base_installer.py @@ -2,15 +2,15 @@ if TYPE_CHECKING: - from poetry.core.packages import Package # noqa + from poetry.core.packages import Package class BaseInstaller: - def install(self, package): # type: ("Package") -> None + def install(self, package: "Package") -> None: raise NotImplementedError - def update(self, source, target): # type: ("Package", "Package") -> None + def update(self, source: "Package", target: "Package") -> None: raise NotImplementedError - def remove(self, package): # type: ("Package") -> None + def remove(self, package: "Package") -> None: raise NotImplementedError diff --git a/poetry/installation/chef.py b/poetry/installation/chef.py index 6009ac0a98d..4f373b8752a 100644 --- a/poetry/installation/chef.py +++ b/poetry/installation/chef.py @@ -3,6 +3,8 @@ from pathlib import Path from typing import TYPE_CHECKING +from typing import List +from typing import Optional from poetry.core.packages.utils.link import Link @@ -11,37 +13,35 @@ if TYPE_CHECKING: - from typing import List - from typing import Optional from poetry.config.config import Config from poetry.utils.env import Env class Chef: - def __init__(self, config, env): # type: (Config, Env) -> None + def __init__(self, config: "Config", env: "Env") -> None: self._config = config self._env = env self._cache_dir = ( Path(config.get("cache-dir")).expanduser().joinpath("artifacts") ) - def prepare(self, archive): # type: (Path) -> Path + def prepare(self, archive: Path) -> Path: return archive - def prepare_sdist(self, archive): # type: (Path) -> Path + def prepare_sdist(self, archive: Path) -> Path: return archive - def prepare_wheel(self, archive): # type: (Path) -> Path + def prepare_wheel(self, archive: Path) -> Path: return archive - def should_prepare(self, archive): # type: (Path) -> bool + def should_prepare(self, archive: Path) -> bool: return not self.is_wheel(archive) - def is_wheel(self, archive): # type: (Path) -> bool + def is_wheel(self, archive: Path) -> bool: return archive.suffix == ".whl" - def get_cached_archive_for_link(self, link): # type: (Link) -> Optional[Link] + def get_cached_archive_for_link(self, link: Link) -> Optional[Link]: # If the archive is already a wheel, there is no need to cache it. if link.is_wheel: pass @@ -74,7 +74,7 @@ def get_cached_archive_for_link(self, link): # type: (Link) -> Optional[Link] return min(candidates)[1] - def get_cached_archives_for_link(self, link): # type: (Link) -> List[Link] + def get_cached_archives_for_link(self, link: Link) -> List[Link]: cache_dir = self.get_cache_directory_for_link(link) archive_types = ["whl", "tar.gz", "tar.bz2", "bz2", "zip"] @@ -85,7 +85,7 @@ def get_cached_archives_for_link(self, link): # type: (Link) -> List[Link] return links - def get_cache_directory_for_link(self, link): # type: (Link) -> Path + def get_cache_directory_for_link(self, link: Link) -> Path: key_parts = {"url": link.url_without_fragment} if link.hash_name is not None and link.hash is not None: diff --git a/poetry/installation/chooser.py b/poetry/installation/chooser.py index 1762f2ce546..10c89c1ce6d 100644 --- a/poetry/installation/chooser.py +++ b/poetry/installation/chooser.py @@ -18,7 +18,7 @@ class InvalidWheelName(Exception): class Wheel(object): - def __init__(self, filename): # type: (str) -> None + def __init__(self, filename: str) -> None: wheel_info = wheel_file_re.match(filename) if not wheel_info: raise InvalidWheelName("{} is not a valid wheel filename.".format(filename)) @@ -35,12 +35,12 @@ def __init__(self, filename): # type: (str) -> None Tag(x, y, z) for x in self.pyversions for y in self.abis for z in self.plats } - def get_minimum_supported_index(self, tags): # type: (List[Tag]) -> Optional[int] + def get_minimum_supported_index(self, tags: List[Tag]) -> Optional[int]: indexes = [tags.index(t) for t in self.tags if t in tags] return min(indexes) if indexes else None - def is_supported_by_environment(self, env): # type: (Env) -> bool + def is_supported_by_environment(self, env: Env) -> bool: return bool(set(env.supported_tags).intersection(self.tags)) @@ -49,11 +49,11 @@ class Chooser: A Chooser chooses an appropriate release archive for packages. """ - def __init__(self, pool, env): # type: (Pool, Env) -> None + def __init__(self, pool: Pool, env: Env) -> None: self._pool = pool self._env = env - def choose_for(self, package): # type: (Package) -> Link + def choose_for(self, package: Package) -> Link: """ Return the url of the selected archive for a given package. """ @@ -83,7 +83,7 @@ def choose_for(self, package): # type: (Package) -> Link return chosen - def _get_links(self, package): # type: (Package) -> List[Link] + def _get_links(self, package: Package) -> List[Link]: if not package.source_type: if not self._pool.has_repository("pypi"): repository = self._pool.repositories[0] @@ -112,7 +112,7 @@ def _get_links(self, package): # type: (Package) -> List[Link] return selected_links - def _sort_key(self, package, link): # type: (Package, Link) -> Tuple + def _sort_key(self, package: Package, link: Link) -> Tuple: """ Function to pass as the `key` argument to a call to sorted() to sort InstallationCandidates by preference. @@ -170,9 +170,7 @@ def _sort_key(self, package, link): # type: (Package, Link) -> Tuple pri, ) - def _is_link_hash_allowed_for_package( - self, link, package - ): # type: (Link, Package) -> bool + def _is_link_hash_allowed_for_package(self, link: Link, package: Package) -> bool: if not link.hash: return True diff --git a/poetry/installation/executor.py b/poetry/installation/executor.py index 2ffca66a1e7..520487d2536 100644 --- a/poetry/installation/executor.py +++ b/poetry/installation/executor.py @@ -35,17 +35,22 @@ if TYPE_CHECKING: from cleo.io.io import IO # noqa - from poetry.config.config import Config # noqa - from poetry.repositories import Pool # noqa - from poetry.utils.env import Env # noqa + from poetry.config.config import Config + from poetry.repositories import Pool + from poetry.utils.env import Env - from .operations import OperationTypes # noqa + from .operations import OperationTypes class Executor(object): def __init__( - self, env, pool, config, io, parallel=None - ): # type: ("Env", "Pool", "Config", "IO", bool) -> None + self, + env: "Env", + pool: "Pool", + config: "Config", + io: "IO", + parallel: bool = None, + ) -> None: self._env = env self._io = io self._dry_run = False @@ -80,36 +85,36 @@ def __init__( self._shutdown = False @property - def installations_count(self): # type: () -> int + def installations_count(self) -> int: return self._executed["install"] @property - def updates_count(self): # type: () -> int + def updates_count(self) -> int: return self._executed["update"] @property - def removals_count(self): # type: () -> int + def removals_count(self) -> int: return self._executed["uninstall"] - def supports_fancy_output(self): # type: () -> bool + def supports_fancy_output(self) -> bool: return self._io.output.is_decorated() and not self._dry_run - def disable(self): # type: () -> "Executor" + def disable(self) -> "Executor": self._enabled = False return self - def dry_run(self, dry_run=True): # type: (bool) -> Executor + def dry_run(self, dry_run: bool = True) -> "Executor": self._dry_run = dry_run return self - def verbose(self, verbose=True): # type: (bool) -> Executor + def verbose(self, verbose: bool = True) -> "Executor": self._verbose = verbose return self - def execute(self, operations): # type: (List["OperationTypes"]) -> int + def execute(self, operations: List["OperationTypes"]) -> int: self._total_operations = len(operations) for job_type in self._executed: self._executed[job_type] = 0 @@ -162,7 +167,7 @@ def execute(self, operations): # type: (List["OperationTypes"]) -> int return 1 if self._shutdown else 0 - def _write(self, operation, line): # type: ("OperationTypes", str) -> None + def _write(self, operation: "OperationTypes", line: str) -> None: if not self.supports_fancy_output() or not self._should_write_operation( operation ): @@ -180,7 +185,7 @@ def _write(self, operation, line): # type: ("OperationTypes", str) -> None section.clear() section.write(line) - def _execute_operation(self, operation): # type: ("OperationTypes") -> None + def _execute_operation(self, operation: "OperationTypes") -> None: try: if self.supports_fancy_output(): if id(operation) not in self._sections: @@ -259,7 +264,7 @@ def _execute_operation(self, operation): # type: ("OperationTypes") -> None with self._lock: self._shutdown = True - def _do_execute_operation(self, operation): # type: ("OperationTypes") -> int + def _do_execute_operation(self, operation: "OperationTypes") -> int: method = operation.job_type operation_message = self.get_operation_message(operation) @@ -304,8 +309,8 @@ def _do_execute_operation(self, operation): # type: ("OperationTypes") -> int return result def _increment_operations_count( - self, operation, executed - ): # type: ("OperationTypes", bool) -> None + self, operation: "OperationTypes", executed: bool + ) -> None: with self._lock: if executed: self._executed_operations += 1 @@ -313,7 +318,7 @@ def _increment_operations_count( else: self._skipped[operation.job_type] += 1 - def run_pip(self, *args, **kwargs): # type: (*Any, **Any) -> int + def run_pip(self, *args: Any, **kwargs: Any) -> int: try: self._env.run_pip(*args, **kwargs) except EnvCommandError as e: @@ -329,8 +334,12 @@ def run_pip(self, *args, **kwargs): # type: (*Any, **Any) -> int return 0 def get_operation_message( - self, operation, done=False, error=False, warning=False - ): # type: ("OperationTypes", bool, bool, bool) -> str + self, + operation: "OperationTypes", + done: bool = False, + error: bool = False, + warning: bool = False, + ) -> str: base_tag = "fg=default" operation_color = "c2" source_operation_color = "c2" @@ -384,7 +393,7 @@ def get_operation_message( return "" - def _display_summary(self, operations): # type: (List["OperationTypes"]) -> None + def _display_summary(self, operations: List["OperationTypes"]) -> None: installs = 0 updates = 0 uninstalls = 0 @@ -427,13 +436,13 @@ def _display_summary(self, operations): # type: (List["OperationTypes"]) -> Non ) self._io.write_line("") - def _execute_install(self, operation): # type: (Union[Install, Update]) -> int + def _execute_install(self, operation: Union[Install, Update]) -> int: return self._install(operation) - def _execute_update(self, operation): # type: (Union[Install, Update]) -> int + def _execute_update(self, operation: Union[Install, Update]) -> int: return self._update(operation) - def _execute_uninstall(self, operation): # type: (Uninstall) -> int + def _execute_uninstall(self, operation: Uninstall) -> int: message = ( " • {message}: Removing...".format( message=self.get_operation_message(operation), @@ -443,7 +452,7 @@ def _execute_uninstall(self, operation): # type: (Uninstall) -> int return self._remove(operation) - def _install(self, operation): # type: (Union[Install, Update]) -> int + def _install(self, operation: Union[Install, Update]) -> int: package = operation.package if package.source_type == "directory": return self._install_directory(operation) @@ -472,10 +481,10 @@ def _install(self, operation): # type: (Union[Install, Update]) -> int return self.run_pip(*args) - def _update(self, operation): # type: (Union[Install, Update]) -> int + def _update(self, operation: Union[Install, Update]) -> int: return self._install(operation) - def _remove(self, operation): # type: (Uninstall) -> int + def _remove(self, operation: Uninstall) -> int: package = operation.package # If we have a VCS package, remove its source directory @@ -492,7 +501,7 @@ def _remove(self, operation): # type: (Uninstall) -> int raise - def _prepare_file(self, operation): # type: (Union[Install, Update]) -> Path + def _prepare_file(self, operation: Union[Install, Update]) -> Path: package = operation.package message = ( @@ -510,7 +519,7 @@ def _prepare_file(self, operation): # type: (Union[Install, Update]) -> Path return archive - def _install_directory(self, operation): # type: (Union[Install, Update]) -> int + def _install_directory(self, operation: Union[Install, Update]) -> int: from poetry.factory import Factory package = operation.package @@ -576,7 +585,7 @@ def _install_directory(self, operation): # type: (Union[Install, Update]) -> in return self.run_pip(*args) - def _install_git(self, operation): # type: (Union[Install, Update]) -> int + def _install_git(self, operation: Union[Install, Update]) -> int: from poetry.core.vcs import Git package = operation.package @@ -604,14 +613,12 @@ def _install_git(self, operation): # type: (Union[Install, Update]) -> int return self._install_directory(operation) - def _download(self, operation): # type: (Union[Install, Update]) -> Link + def _download(self, operation: Union[Install, Update]) -> Link: link = self._chooser.choose_for(operation.package) return self._download_link(operation, link) - def _download_link( - self, operation, link - ): # type: (Union[Install, Update], Link) -> Link + def _download_link(self, operation: Union[Install, Update], link: Link) -> Link: package = operation.package archive = self._chef.get_cached_archive_for_link(link) @@ -643,9 +650,7 @@ def _download_link( return archive - def _download_archive( - self, operation, link - ): # type: (Union[Install, Update], Link) -> Path + def _download_archive(self, operation: Union[Install, Update], link: Link) -> Path: response = self._authenticator.request( "get", link.url, stream=True, io=self._sections.get(id(operation), self._io) ) @@ -694,7 +699,7 @@ def _download_archive( return archive - def _should_write_operation(self, operation): # type: (Operation) -> bool + def _should_write_operation(self, operation: Operation) -> bool: if not operation.skipped: return True diff --git a/poetry/installation/installer.py b/poetry/installation/installer.py index ecd0ba1ad78..b778711479b 100644 --- a/poetry/installation/installer.py +++ b/poetry/installation/installer.py @@ -26,22 +26,22 @@ if TYPE_CHECKING: - from poetry.utils.env import Env # noqa + from poetry.utils.env import Env - from .operations import OperationTypes # noqa + from .operations import OperationTypes class Installer: def __init__( self, - io, # type: IO - env, # type: "Env" - package, # type: ProjectPackage - locker, # type: Locker - pool, # type: Pool - config, # type: Config - installed=None, # type: Union[InstalledRepository, None] - executor=None, # type: Optional[Executor] + io: IO, + env: "Env", + package: ProjectPackage, + locker: Locker, + pool: Pool, + config: Config, + installed: Union[InstalledRepository, None] = None, + executor: Optional[Executor] = None, ): self._io = io self._env = env @@ -76,24 +76,24 @@ def __init__( self._installed_repository = installed @property - def executor(self): # type: () -> Executor + def executor(self) -> Executor: return self._executor @property - def installer(self): # type: () -> BaseInstaller + def installer(self) -> BaseInstaller: return self._installer - def set_package(self, package): # type: (ProjectPackage) -> Installer + def set_package(self, package: ProjectPackage) -> "Installer": self._package = package return self - def set_locker(self, locker): # type: (Locker) -> Installer + def set_locker(self, locker: Locker) -> "Installer": self._locker = locker return self - def run(self): # type: () -> int + def run(self) -> int: # Check if refresh if not self._update and self._lock and self._locker.is_locked(): return self._do_refresh() @@ -111,54 +111,54 @@ def run(self): # type: () -> int return self._do_install(local_repo) - def dry_run(self, dry_run=True): # type: (bool) -> Installer + def dry_run(self, dry_run: bool = True) -> "Installer": self._dry_run = dry_run self._executor.dry_run(dry_run) return self - def is_dry_run(self): # type: () -> bool + def is_dry_run(self) -> bool: return self._dry_run - def remove_untracked(self, remove_untracked=True): # type: (bool) -> Installer + def remove_untracked(self, remove_untracked: bool = True) -> "Installer": self._remove_untracked = remove_untracked return self - def is_remove_untracked(self): # type: () -> bool + def is_remove_untracked(self) -> bool: return self._remove_untracked - def verbose(self, verbose=True): # type: (bool) -> Installer + def verbose(self, verbose: bool = True) -> "Installer": self._verbose = verbose self._executor.verbose(verbose) return self - def is_verbose(self): # type: () -> bool + def is_verbose(self) -> bool: return self._verbose - def dev_mode(self, dev_mode=True): # type: (bool) -> Installer + def dev_mode(self, dev_mode: bool = True) -> "Installer": self._dev_mode = dev_mode return self - def is_dev_mode(self): # type: () -> bool + def is_dev_mode(self) -> bool: return self._dev_mode - def dev_only(self, dev_only=False): # type: (bool) -> Installer + def dev_only(self, dev_only: bool = False) -> "Installer": self._dev_only = dev_only return self - def is_dev_only(self): # type: () -> bool + def is_dev_only(self) -> bool: return self._dev_only - def update(self, update=True): # type: (bool) -> Installer + def update(self, update: bool = True) -> "Installer": self._update = update return self - def lock(self, update=True): # type: (bool) -> Installer + def lock(self, update: bool = True) -> "Installer": """ Prepare the installer for locking only. """ @@ -168,10 +168,10 @@ def lock(self, update=True): # type: (bool) -> Installer return self - def is_updating(self): # type: () -> bool + def is_updating(self) -> bool: return self._update - def execute_operations(self, execute=True): # type: (bool) -> Installer + def execute_operations(self, execute: bool = True) -> "Installer": self._execute_operations = execute if not execute: @@ -179,22 +179,22 @@ def execute_operations(self, execute=True): # type: (bool) -> Installer return self - def whitelist(self, packages): # type: (Iterable[str]) -> Installer + def whitelist(self, packages: Iterable[str]) -> "Installer": self._whitelist = [canonicalize_name(p) for p in packages] return self - def extras(self, extras): # type: (list) -> Installer + def extras(self, extras: list) -> "Installer": self._extras = extras return self - def use_executor(self, use_executor=True): # type: (bool) -> Installer + def use_executor(self, use_executor: bool = True) -> "Installer": self._use_executor = use_executor return self - def _do_refresh(self): # type: () -> int + def _do_refresh(self) -> int: from poetry.puzzle import Solver # Checking extras @@ -208,7 +208,7 @@ def _do_refresh(self): # type: () -> int self._pool, locked_repository, locked_repository, - self._io, # noqa + self._io, ) ops = solver.solve(use_latest=[]) @@ -220,7 +220,7 @@ def _do_refresh(self): # type: () -> int return 0 - def _do_install(self, local_repo): # type: (Repository) -> int + def _do_install(self, local_repo: Repository) -> int: from poetry.puzzle import Solver locked_repository = Repository() @@ -335,7 +335,7 @@ def _do_install(self, local_repo): # type: (Repository) -> int # Execute operations return self._execute(ops) - def _write_lock_file(self, repo, force=True): # type: (Repository, bool) -> None + def _write_lock_file(self, repo: Repository, force: bool = True) -> None: if force or (self._update and self._write_lock): updated_lock = self._locker.set_lock_data(self._package, repo.packages) @@ -343,7 +343,7 @@ def _write_lock_file(self, repo, force=True): # type: (Repository, bool) -> Non self._io.write_line("") self._io.write_line("Writing lock file") - def _execute(self, operations): # type: (List["OperationTypes"]) -> int + def _execute(self, operations: List["OperationTypes"]) -> int: if self._use_executor: return self._executor.execute(operations) @@ -391,7 +391,7 @@ def _execute(self, operations): # type: (List["OperationTypes"]) -> int return 0 - def _execute_operation(self, operation): # type: (Operation) -> None + def _execute_operation(self, operation: Operation) -> None: """ Execute a given operation. """ @@ -399,7 +399,7 @@ def _execute_operation(self, operation): # type: (Operation) -> None getattr(self, "_execute_{}".format(method))(operation) - def _execute_install(self, operation): # type: (Install) -> None + def _execute_install(self, operation: Install) -> None: if operation.skipped: if self.is_verbose() and (self._execute_operations or self.is_dry_run()): self._io.write_line( @@ -424,7 +424,7 @@ def _execute_install(self, operation): # type: (Install) -> None self._installer.install(operation.package) - def _execute_update(self, operation): # type: (Update) -> None + def _execute_update(self, operation: Update) -> None: source = operation.initial_package target = operation.target_package @@ -454,7 +454,7 @@ def _execute_update(self, operation): # type: (Update) -> None self._installer.update(source, target) - def _execute_uninstall(self, operation): # type: (Uninstall) -> None + def _execute_uninstall(self, operation: Uninstall) -> None: if operation.skipped: if self.is_verbose() and (self._execute_operations or self.is_dry_run()): self._io.write_line( @@ -480,8 +480,8 @@ def _execute_uninstall(self, operation): # type: (Uninstall) -> None self._installer.remove(operation.package) def _populate_local_repo( - self, local_repo, ops - ): # type: (Repository, List[Operation]) -> None + self, local_repo: Repository, ops: List[Operation] + ) -> None: for op in ops: if isinstance(op, Uninstall): continue @@ -494,8 +494,8 @@ def _populate_local_repo( local_repo.add_package(package) def _get_operations_from_lock( - self, locked_repository - ): # type: (Repository) -> List[Operation] + self, locked_repository: Repository + ) -> List[Operation]: installed_repo = self._installed_repository ops = [] @@ -526,9 +526,7 @@ def _get_operations_from_lock( return ops - def _filter_operations( - self, ops, repo - ): # type: (List[Operation], Repository) -> None + def _filter_operations(self, ops: List[Operation], repo: Repository) -> None: extra_packages = self._get_extra_packages(repo) for op in ops: if isinstance(op, Update): @@ -563,7 +561,7 @@ def _filter_operations( if package.category == "dev" and not self.is_dev_mode(): op.skip("Dev dependencies not requested") - def _get_extra_packages(self, repo): # type: (Repository) -> List[str] + def _get_extra_packages(self, repo: Repository) -> List[str]: """ Returns all package names required by extras. @@ -576,8 +574,8 @@ def _get_extra_packages(self, repo): # type: (Repository) -> List[str] return list(get_extra_package_names(repo.packages, extras, self._extras)) - def _get_installer(self): # type: () -> BaseInstaller + def _get_installer(self) -> BaseInstaller: return PipInstaller(self._env, self._io, self._pool) - def _get_installed(self): # type: () -> InstalledRepository + def _get_installed(self) -> InstalledRepository: return InstalledRepository.load(self._env) diff --git a/poetry/installation/noop_installer.py b/poetry/installation/noop_installer.py index 8b39543671f..3ef7dab543c 100644 --- a/poetry/installation/noop_installer.py +++ b/poetry/installation/noop_installer.py @@ -5,32 +5,32 @@ if TYPE_CHECKING: - from poetry.core.packages import Package # noqa + from poetry.core.packages import Package class NoopInstaller(BaseInstaller): - def __init__(self): # type: () -> None + def __init__(self) -> None: self._installs = [] self._updates = [] self._removals = [] @property - def installs(self): # type: () -> List["Package"] + def installs(self) -> List["Package"]: return self._installs @property - def updates(self): # type: () -> List["Package"] + def updates(self) -> List["Package"]: return self._updates @property - def removals(self): # type: () -> List["Package"] + def removals(self) -> List["Package"]: return self._removals - def install(self, package): # type: ("Package") -> None + def install(self, package: "Package") -> None: self._installs.append(package) - def update(self, source, target): # type: ("Package", "Package") -> None + def update(self, source: "Package", target: "Package") -> None: self._updates.append((source, target)) - def remove(self, package): # type: ("Package") -> None + def remove(self, package: "Package") -> None: self._removals.append(package) diff --git a/poetry/installation/operations/install.py b/poetry/installation/operations/install.py index 381f8f48f30..b83f449bd45 100644 --- a/poetry/installation/operations/install.py +++ b/poetry/installation/operations/install.py @@ -5,31 +5,31 @@ if TYPE_CHECKING: - from poetry.core.packages import Package # noqa + from poetry.core.packages import Package class Install(Operation): def __init__( - self, package, reason=None, priority=0 - ): # type: ("Package", Optional[str], int) -> None + self, package: "Package", reason: Optional[str] = None, priority: int = 0 + ) -> None: super(Install, self).__init__(reason, priority=priority) self._package = package @property - def package(self): # type: () -> "Package" + def package(self) -> "Package": return self._package @property - def job_type(self): # type: () -> str + def job_type(self) -> str: return "install" - def __str__(self): # type: () -> str + def __str__(self) -> str: return "Installing {} ({})".format( self.package.pretty_name, self.format_version(self.package) ) - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "".format( self.package.pretty_name, self.format_version(self.package) ) diff --git a/poetry/installation/operations/operation.py b/poetry/installation/operations/operation.py index 847510c80f8..e001cf97608 100644 --- a/poetry/installation/operations/operation.py +++ b/poetry/installation/operations/operation.py @@ -5,11 +5,11 @@ if TYPE_CHECKING: - from poetry.core.packages import Package # noqa + from poetry.core.packages import Package class Operation(object): - def __init__(self, reason=None, priority=0): # type: (Optional[str], int) -> None + def __init__(self, reason: Optional[str] = None, priority: int = 0) -> None: self._reason = reason self._skipped = False @@ -17,39 +17,39 @@ def __init__(self, reason=None, priority=0): # type: (Optional[str], int) -> No self._priority = priority @property - def job_type(self): # type: () -> str + def job_type(self) -> str: raise NotImplementedError @property - def reason(self): # type: () -> str + def reason(self) -> str: return self._reason @property - def skipped(self): # type: () -> bool + def skipped(self) -> bool: return self._skipped @property - def skip_reason(self): # type: () -> Optional[str] + def skip_reason(self) -> Optional[str]: return self._skip_reason @property - def priority(self): # type: () -> int + def priority(self) -> int: return self._priority @property - def package(self): # type: () -> "Package" + def package(self) -> "Package": raise NotImplementedError() - def format_version(self, package): # type: ("Package") -> str + def format_version(self, package: "Package") -> str: return package.full_pretty_version - def skip(self, reason): # type: (str) -> Operation + def skip(self, reason: str) -> "Operation": self._skipped = True self._skip_reason = reason return self - def unskip(self): # type: () -> Operation + def unskip(self) -> "Operation": self._skipped = False self._skip_reason = None diff --git a/poetry/installation/operations/uninstall.py b/poetry/installation/operations/uninstall.py index 32c6e4e3583..12a163a3367 100644 --- a/poetry/installation/operations/uninstall.py +++ b/poetry/installation/operations/uninstall.py @@ -5,31 +5,34 @@ if TYPE_CHECKING: - from poetry.core.packages import Package # noqa + from poetry.core.packages import Package class Uninstall(Operation): def __init__( - self, package, reason=None, priority=float("inf") - ): # type: ("Package", Optional[str], int) -> None + self, + package: "Package", + reason: Optional[str] = None, + priority: int = float("inf"), + ) -> None: super(Uninstall, self).__init__(reason, priority=priority) self._package = package @property - def package(self): # type: () -> "Package" + def package(self) -> "Package": return self._package @property - def job_type(self): # type: () -> str + def job_type(self) -> str: return "uninstall" - def __str__(self): # type: () -> str + def __str__(self) -> str: return "Uninstalling {} ({})".format( self.package.pretty_name, self.format_version(self._package) ) - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "".format( self.package.pretty_name, self.format_version(self.package) ) diff --git a/poetry/installation/operations/update.py b/poetry/installation/operations/update.py index 02cd86ccfb8..c1f33fad30f 100644 --- a/poetry/installation/operations/update.py +++ b/poetry/installation/operations/update.py @@ -5,35 +5,39 @@ if TYPE_CHECKING: - from poetry.core.packages import Package # noqa + from poetry.core.packages import Package class Update(Operation): def __init__( - self, initial, target, reason=None, priority=0 - ): # type: ("Package", "Package", Optional[str], int) -> None + self, + initial: "Package", + target: "Package", + reason: Optional[str] = None, + priority: int = 0, + ) -> None: self._initial_package = initial self._target_package = target super(Update, self).__init__(reason, priority=priority) @property - def initial_package(self): # type: () -> "Package" + def initial_package(self) -> "Package": return self._initial_package @property - def target_package(self): # type: () -> "Package" + def target_package(self) -> "Package": return self._target_package @property - def package(self): # type: () -> "Package" + def package(self) -> "Package": return self._target_package @property - def job_type(self): # type: () -> str + def job_type(self) -> str: return "update" - def __str__(self): # type: () -> str + def __str__(self) -> str: return "Updating {} ({}) to {} ({})".format( self.initial_package.pretty_name, self.format_version(self.initial_package), @@ -41,7 +45,7 @@ def __str__(self): # type: () -> str self.format_version(self.target_package), ) - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "".format( self.initial_package.pretty_name, self.format_version(self.initial_package), diff --git a/poetry/installation/pip_installer.py b/poetry/installation/pip_installer.py index 6e5f7bdcc48..e84774d39ca 100644 --- a/poetry/installation/pip_installer.py +++ b/poetry/installation/pip_installer.py @@ -19,16 +19,16 @@ if TYPE_CHECKING: - from poetry.core.packages import Package # noqa + from poetry.core.packages import Package class PipInstaller(BaseInstaller): - def __init__(self, env, io, pool): # type: (Env, IO, Pool) -> None + def __init__(self, env: Env, io: IO, pool: Pool) -> None: self._env = env self._io = io self._pool = pool - def install(self, package, update=False): # type: ("Package", bool) -> None + def install(self, package: "Package", update: bool = False) -> None: if package.source_type == "directory": self.install_directory(package) @@ -48,7 +48,7 @@ def install(self, package, update=False): # type: ("Package", bool) -> None repository = self._pool.repository(package.source_reference) parsed = urllib.parse.urlparse(package.source_url) if parsed.scheme == "http": - self._io.error( + self._io.write_error( " Installing from unsecure host: {}".format( parsed.hostname ) @@ -97,7 +97,7 @@ def install(self, package, update=False): # type: ("Package", bool) -> None self.run(*args) - def update(self, package, target): # type: ("Package", "Package") -> None + def update(self, package: "Package", target: "Package") -> None: if package.source_type != target.source_type: # If the source type has changed, we remove the current # package to avoid perpetual updates in some cases @@ -105,7 +105,7 @@ def update(self, package, target): # type: ("Package", "Package") -> None self.install(target, update=True) - def remove(self, package): # type: ("Package") -> None + def remove(self, package: "Package") -> None: try: self.run("uninstall", package.name, "-y") except CalledProcessError as e: @@ -127,10 +127,10 @@ def remove(self, package): # type: ("Package") -> None if src_dir.exists(): safe_rmtree(str(src_dir)) - def run(self, *args, **kwargs): # type: (*Any,**Any) -> str + def run(self, *args: Any, **kwargs: Any) -> str: return self._env.run_pip(*args, **kwargs) - def requirement(self, package, formatted=False): # type: ("Package", bool) -> str + def requirement(self, package: "Package", formatted: bool = False) -> str: if formatted and not package.source_type: req = "{}=={}".format(package.name, package.version) for f in package.files: @@ -171,7 +171,7 @@ def requirement(self, package, formatted=False): # type: ("Package", bool) -> s return "{}=={}".format(package.name, package.version) - def create_temporary_requirement(self, package): # type: ("Package") -> str + def create_temporary_requirement(self, package: "Package") -> str: fd, name = tempfile.mkstemp( "reqs.txt", "{}-{}".format(package.name, package.version) ) @@ -183,7 +183,7 @@ def create_temporary_requirement(self, package): # type: ("Package") -> str return name - def install_directory(self, package): # type: ("Package") -> Union[str, int] + def install_directory(self, package: "Package") -> Union[str, int]: from cleo.io.null_io import NullIO from poetry.factory import Factory @@ -241,7 +241,7 @@ def install_directory(self, package): # type: ("Package") -> Union[str, int] return self.run(*args) - def install_git(self, package): # type: ("Package") -> None + def install_git(self, package: "Package") -> None: from poetry.core.packages import Package from poetry.core.vcs import Git diff --git a/poetry/json/__init__.py b/poetry/json/__init__.py index d50eb7a7500..d65edd6106a 100644 --- a/poetry/json/__init__.py +++ b/poetry/json/__init__.py @@ -15,7 +15,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): diff --git a/poetry/layouts/__init__.py b/poetry/layouts/__init__.py index 9969ce5e3a7..291319e82c1 100644 --- a/poetry/layouts/__init__.py +++ b/poetry/layouts/__init__.py @@ -8,7 +8,7 @@ _LAYOUTS = {"src": SrcLayout, "standard": StandardLayout} -def layout(name): # type: (str) -> Type[Layout] +def layout(name: str) -> Type[Layout]: if name not in _LAYOUTS: raise ValueError("Invalid layout") diff --git a/poetry/layouts/layout.py b/poetry/layouts/layout.py index af5b48ad746..ed3b7b45777 100644 --- a/poetry/layouts/layout.py +++ b/poetry/layouts/layout.py @@ -10,8 +10,9 @@ if TYPE_CHECKING: - from poetry.core.pyproject.toml import PyProjectTOML # noqa - from poetry.utils._compat import Path # noqa + from pathlib import Path + + from poetry.core.pyproject.toml import PyProjectTOML TESTS_DEFAULT = u"""from {package_name} import __version__ @@ -47,21 +48,21 @@ def test_version(): """ BUILD_SYSTEM_MIN_VERSION = "1.0.0" -BUILD_SYSTEM_MAX_VERSION = None # type: Optional[str] +BUILD_SYSTEM_MAX_VERSION: Optional[str] = None class Layout(object): def __init__( self, - project, # type: str - version="0.1.0", # type: str - description="", # type: str - readme_format="md", # type: str - author=None, # type: Optional[str] - license=None, # type: Optional[str] - python="*", # type: str - dependencies=None, # type: Optional[Dict[str, str]] - dev_dependencies=None, # type: Optional[Dict[str, str]] + project: str, + version: str = "0.1.0", + description: str = "", + readme_format: str = "md", + author: Optional[str] = None, + license: Optional[str] = None, + python: str = "*", + dependencies: Optional[Dict[str, str]] = None, + dev_dependencies: Optional[Dict[str, str]] = None, ): self._project = project self._package_name = module_name(project) @@ -78,7 +79,7 @@ def __init__( self._author = author - def create(self, path, with_tests=True): # type: (Path, bool) -> None + def create(self, path: "Path", with_tests: bool = True) -> None: path.mkdir(parents=True, exist_ok=True) self._create_default(path) @@ -90,8 +91,8 @@ def create(self, path, with_tests=True): # type: (Path, bool) -> None self._write_poetry(path) def generate_poetry_content( - self, original=None - ): # type: (Optional["PyProjectTOML"]) -> str + self, original: Optional["PyProjectTOML"] = None + ) -> str: template = POETRY_DEFAULT if self._license: template = POETRY_WITH_LICENSE @@ -131,10 +132,10 @@ def generate_poetry_content( return content - def _create_default(self, path, src=True): # type: (Path, bool) -> None + def _create_default(self, path: "Path", src: bool = True) -> None: raise NotImplementedError() - def _create_readme(self, path): # type: (Path) -> None + def _create_readme(self, path: "Path") -> None: if self._readme_format == "rst": readme_file = path / "README.rst" else: @@ -142,7 +143,7 @@ def _create_readme(self, path): # type: (Path) -> None readme_file.touch() - def _create_tests(self, path): # type: (Path) -> None + def _create_tests(self, path: "Path") -> None: tests = path / "tests" tests_init = tests / "__init__.py" tests_default = tests / "test_{}.py".format(self._package_name) @@ -157,7 +158,7 @@ def _create_tests(self, path): # type: (Path) -> None ) ) - def _write_poetry(self, path): # type: ("Path") -> None + def _write_poetry(self, path: "Path") -> None: content = self.generate_poetry_content() poetry = path / "pyproject.toml" diff --git a/poetry/layouts/src.py b/poetry/layouts/src.py index a703ed2f2c2..3d844b4a4fc 100644 --- a/poetry/layouts/src.py +++ b/poetry/layouts/src.py @@ -6,14 +6,14 @@ if TYPE_CHECKING: - from poetry.utils._compat import Path # noqa + from pathlib import Path DEFAULT = u"""__version__ = '{version}' """ class SrcLayout(Layout): - def _create_default(self, path): # type: ("Path") -> None + def _create_default(self, path: "Path") -> None: package_path = path / "src" / self._package_name package_init = package_path / "__init__.py" diff --git a/poetry/layouts/standard.py b/poetry/layouts/standard.py index 4372d71d6b1..e0a1db99204 100644 --- a/poetry/layouts/standard.py +++ b/poetry/layouts/standard.py @@ -6,13 +6,13 @@ if TYPE_CHECKING: - from poetry.utils._compat import Path # noqa + from pathlib import Path DEFAULT = u"""__version__ = '{version}' """ class StandardLayout(Layout): - def _create_default(self, path): # type: ("Path") -> None + def _create_default(self, path: "Path") -> None: package_path = path / self._package_name package_init = package_path / "__init__.py" diff --git a/poetry/masonry/builders/editable.py b/poetry/masonry/builders/editable.py index 1756a0f0f15..07f59d0b311 100644 --- a/poetry/masonry/builders/editable.py +++ b/poetry/masonry/builders/editable.py @@ -21,8 +21,8 @@ if TYPE_CHECKING: from cleo.io.io import IO # noqa - from poetry.core.poetry import Poetry # noqa - from poetry.utils.env import Env # noqa + from poetry.core.poetry import Poetry + from poetry.utils.env import Env SCRIPT_TEMPLATE = """\ #!{python} @@ -38,13 +38,13 @@ class EditableBuilder(Builder): - def __init__(self, poetry, env, io): # type: ("Poetry", "Env", "IO") -> None + def __init__(self, poetry: "Poetry", env: "Env", io: "IO") -> None: super(EditableBuilder, self).__init__(poetry) self._env = env self._io = io - def build(self): # type: () -> None + def build(self) -> None: self._debug( " - Building package {} in editable mode".format( self._package.name @@ -66,11 +66,11 @@ def build(self): # type: () -> None added_files += self._add_scripts() self._add_dist_info(added_files) - def _run_build_script(self, build_script): # type: (Path) -> None + def _run_build_script(self, build_script: Path) -> None: self._debug(" - Executing build script: {}".format(build_script)) self._env.run("python", str(self._path.joinpath(build_script)), call=True) - def _setup_build(self): # type: () -> None + def _setup_build(self) -> None: builder = SdistBuilder(self._poetry) setup = self._path / "setup.py" has_setup = setup.exists() @@ -102,7 +102,7 @@ def _setup_build(self): # type: () -> None if not has_setup: os.remove(str(setup)) - def _add_pth(self): # type: () -> List[Path] + def _add_pth(self) -> List[Path]: paths = set() for include in self._module.includes: if isinstance(include, PackageInclude) and ( @@ -127,14 +127,14 @@ def _add_pth(self): # type: () -> List[Path] return [pth_file] except OSError: # TODO: Replace with PermissionError - self._io.error_line( + self._io.write_error_line( " - Failed to create {} for {}".format( pth_file.name, self._poetry.file.parent ) ) return [] - def _add_scripts(self): # type: () -> List[Path] + def _add_scripts(self) -> List[Path]: added = [] entry_points = self.convert_entry_points() @@ -142,7 +142,7 @@ def _add_scripts(self): # type: () -> List[Path] if is_dir_writable(path=scripts_path, create=True): break else: - self._io.error_line( + self._io.write_error_line( " - Failed to find a suitable script installation directory for {}".format( self._poetry.file.parent ) @@ -193,7 +193,7 @@ def _add_scripts(self): # type: () -> List[Path] return added - def _add_dist_info(self, added_files): # type: (List[Path]) -> None + def _add_dist_info(self, added_files: List[Path]) -> None: from poetry.core.masonry.builders.wheel import WheelBuilder added_files = added_files[:] @@ -247,7 +247,7 @@ def _add_dist_info(self, added_files): # type: (List[Path]) -> None # RECORD itself is recorded with no hash or size f.write("{},,\n".format(dist_info.joinpath("RECORD"))) - def _get_file_hash(self, filepath): # type: (Path) -> str + def _get_file_hash(self, filepath: Path) -> str: hashsum = hashlib.sha256() with filepath.open("rb") as src: while True: @@ -260,6 +260,6 @@ def _get_file_hash(self, filepath): # type: (Path) -> str return urlsafe_b64encode(hashsum.digest()).decode("ascii").rstrip("=") - def _debug(self, msg): # type: (str) -> None + def _debug(self, msg: str) -> None: if self._io.is_debug(): self._io.write_line(msg) diff --git a/poetry/mixology/__init__.py b/poetry/mixology/__init__.py index 8dd76488438..09b3708b6c5 100644 --- a/poetry/mixology/__init__.py +++ b/poetry/mixology/__init__.py @@ -6,16 +6,19 @@ if TYPE_CHECKING: - from poetry.core.packages import DependencyPackage # noqa - from poetry.core.packages import ProjectPackage # noqa - from poetry.puzzle.provider import Provider # noqa + from poetry.core.packages import ProjectPackage + from poetry.packages import DependencyPackage + from poetry.puzzle.provider import Provider - from .result import SolverResult # noqa + from .result import SolverResult def resolve_version( - root, provider, locked=None, use_latest=None -): # type: ("ProjectPackage", "Provider", Dict[str, "DependencyPackage"],List[str]) -> "SolverResult" + root: "ProjectPackage", + provider: "Provider", + locked: Dict[str, "DependencyPackage"] = None, + use_latest: List[str] = None, +) -> "SolverResult": solver = VersionSolver(root, provider, locked=locked, use_latest=use_latest) return solver.solve() diff --git a/poetry/mixology/assignment.py b/poetry/mixology/assignment.py index 21765a22fe5..c90cd837271 100644 --- a/poetry/mixology/assignment.py +++ b/poetry/mixology/assignment.py @@ -6,10 +6,10 @@ if TYPE_CHECKING: - from poetry.core.packages import Dependency # noqa - from poetry.core.packages import Package # noqa + from poetry.core.packages import Dependency + from poetry.core.packages import Package - from .incompatibility import Incompatibility # noqa + from .incompatibility import Incompatibility class Assignment(Term): @@ -18,8 +18,13 @@ class Assignment(Term): """ def __init__( - self, dependency, is_positive, decision_level, index, cause=None - ): # type: ("Dependency", bool, int, int, Optional["Incompatibility"]) -> None + self, + dependency: "Dependency", + is_positive: bool, + decision_level: int, + index: int, + cause: Optional["Incompatibility"] = None, + ) -> None: super(Assignment, self).__init__(dependency, is_positive) self._decision_level = decision_level @@ -27,28 +32,33 @@ def __init__( self._cause = cause @property - def decision_level(self): # type: () -> int + def decision_level(self) -> int: return self._decision_level @property - def index(self): # type: () -> int + def index(self) -> int: return self._index @property - def cause(self): # type: () -> "Incompatibility" + def cause(self) -> "Incompatibility": return self._cause @classmethod def decision( - cls, package, decision_level, index - ): # type: ("Package", int, int) -> Assignment + cls, package: "Package", decision_level: int, index: int + ) -> "Assignment": return cls(package.to_dependency(), True, decision_level, index) @classmethod def derivation( - cls, dependency, is_positive, cause, decision_level, index - ): # type: (Any, bool, "Incompatibility", int, int) -> "Assignment" + cls, + dependency: Any, + is_positive: bool, + cause: "Incompatibility", + decision_level: int, + index: int, + ) -> "Assignment": return cls(dependency, is_positive, decision_level, index, cause) - def is_decision(self): # type: () -> bool + def is_decision(self) -> bool: return self._cause is None diff --git a/poetry/mixology/failure.py b/poetry/mixology/failure.py index daffd12b94b..a4707c93342 100644 --- a/poetry/mixology/failure.py +++ b/poetry/mixology/failure.py @@ -11,27 +11,27 @@ class SolveFailure(Exception): - def __init__(self, incompatibility): # type: (Incompatibility) -> None + def __init__(self, incompatibility: Incompatibility) -> None: self._incompatibility = incompatibility @property - def message(self): # type: () -> str + def message(self) -> str: return str(self) - def __str__(self): # type: () -> str + def __str__(self) -> str: return _Writer(self._incompatibility).write() class _Writer: - def __init__(self, root): # type: (Incompatibility) -> None + def __init__(self, root: Incompatibility) -> None: self._root = root - self._derivations = {} # type: Dict[Incompatibility, int] - self._lines = [] # type: List[Tuple[str, Optional[int]]] - self._line_numbers = {} # type: Dict[Incompatibility, int] + self._derivations: Dict[Incompatibility, int] = {} + self._lines: List[Tuple[str, Optional[int]]] = [] + self._line_numbers: Dict[Incompatibility, int] = {} self._count_derivations(self._root) - def write(self): # type: () -> str + def write(self) -> str: buffer = [] required_python_version_notification = False @@ -98,8 +98,8 @@ def write(self): # type: () -> str return "\n".join(buffer) def _write( - self, incompatibility, message, numbered=False - ): # type: (Incompatibility, str, bool) -> None + self, incompatibility: Incompatibility, message: str, numbered: bool = False + ) -> None: if numbered: number = len(self._line_numbers) + 1 self._line_numbers[incompatibility] = number @@ -108,8 +108,11 @@ def _write( self._lines.append((message, None)) def _visit( - self, incompatibility, details_for_incompatibility, conclusion=False - ): # type: (Incompatibility, Dict, bool) -> None + self, + incompatibility: Incompatibility, + details_for_incompatibility: Dict, + conclusion: bool = False, + ) -> None: numbered = conclusion or self._derivations[incompatibility] > 1 conjunction = "So," if conclusion or incompatibility == self._root else "And" incompatibility_string = str(incompatibility) @@ -208,7 +211,7 @@ def _visit( numbered=numbered, ) elif self._is_collapsible(derived): - derived_cause = derived.cause # type: ConflictCause + derived_cause: ConflictCause = derived.cause if isinstance(derived_cause.conflict.cause, ConflictCause): collapsed_derived = derived_cause.conflict else: @@ -252,11 +255,11 @@ def _visit( numbered=numbered, ) - def _is_collapsible(self, incompatibility): # type: (Incompatibility) -> bool + def _is_collapsible(self, incompatibility: Incompatibility) -> bool: if self._derivations[incompatibility] > 1: return False - cause = incompatibility.cause # type: ConflictCause + cause: ConflictCause = incompatibility.cause if isinstance(cause.conflict.cause, ConflictCause) and isinstance( cause.other.cause, ConflictCause ): @@ -275,12 +278,12 @@ def _is_collapsible(self, incompatibility): # type: (Incompatibility) -> bool return complex not in self._line_numbers - def _is_single_line(self, cause): # type: (ConflictCause) -> bool + def _is_single_line(self, cause: ConflictCause) -> bool: return not isinstance(cause.conflict.cause, ConflictCause) and not isinstance( cause.other.cause, ConflictCause ) - def _count_derivations(self, incompatibility): # type: (Incompatibility) -> None + def _count_derivations(self, incompatibility: Incompatibility) -> None: if incompatibility in self._derivations: self._derivations[incompatibility] += 1 else: diff --git a/poetry/mixology/incompatibility.py b/poetry/mixology/incompatibility.py index 1e9a20440f3..615c0cec741 100644 --- a/poetry/mixology/incompatibility.py +++ b/poetry/mixology/incompatibility.py @@ -1,5 +1,5 @@ from typing import Dict -from typing import Generator +from typing import Iterator from typing import List from typing import Optional from typing import Union @@ -16,9 +16,7 @@ class Incompatibility: - def __init__( - self, terms, cause - ): # type: (List[Term], IncompatibilityCause) -> None + def __init__(self, terms: List[Term], cause: IncompatibilityCause) -> None: # Remove the root package from generated incompatibilities, since it will # always be satisfied. This makes error reporting clearer, and may also # make solving more efficient. @@ -43,7 +41,7 @@ def __init__( pass else: # Coalesce multiple terms about the same package if possible. - by_name = {} # type: Dict[str, Dict[str, Term]] + by_name: Dict[str, Dict[str, Term]] = {} for term in terms: if term.dependency.complete_name not in by_name: by_name[term.dependency.complete_name] = {} @@ -80,25 +78,33 @@ def __init__( self._cause = cause @property - def terms(self): # type: () -> List[Term] + def terms(self) -> List[Term]: return self._terms @property def cause( self, - ): # type: () -> Union[RootCause, NoVersionsCause, DependencyCause, ConflictCause, PythonCause, PlatformCause, PackageNotFoundCause] + ) -> Union[ + RootCause, + NoVersionsCause, + DependencyCause, + ConflictCause, + PythonCause, + PlatformCause, + PackageNotFoundCause, + ]: return self._cause @property def external_incompatibilities( self, - ): # type: () -> Generator[Union[ConflictCause, Incompatibility]] + ) -> Iterator[Union[ConflictCause, "Incompatibility"]]: """ Returns all external incompatibilities in this incompatibility's derivation graph. """ if isinstance(self._cause, ConflictCause): - cause = self._cause # type: ConflictCause + cause: ConflictCause = self._cause for incompatibility in cause.conflict.external_incompatibilities: yield incompatibility @@ -107,12 +113,12 @@ def external_incompatibilities( else: yield self - def is_failure(self): # type: () -> bool + def is_failure(self) -> bool: return len(self._terms) == 0 or ( len(self._terms) == 1 and self._terms[0].dependency.is_root ) - def __str__(self): # type: () -> str + def __str__(self) -> str: if isinstance(self._cause, DependencyCause): assert len(self._terms) == 2 @@ -128,7 +134,7 @@ def __str__(self): # type: () -> str assert len(self._terms) == 1 assert self._terms[0].is_positive() - cause = self._cause # type: PythonCause + cause: PythonCause = self._cause text = "{} requires ".format(self._terse(self._terms[0], allow_every=True)) text += "Python {}".format(cause.python_version) @@ -137,7 +143,7 @@ def __str__(self): # type: () -> str assert len(self._terms) == 1 assert self._terms[0].is_positive() - cause = self._cause # type: PlatformCause + cause: PlatformCause = self._cause text = "{} requires ".format(self._terse(self._terms[0], allow_every=True)) text += "platform {}".format(cause.platform) @@ -227,8 +233,12 @@ def __str__(self): # type: () -> str return "one of {} must be true".format(" or ".join(negative)) def and_to_string( - self, other, details, this_line, other_line - ): # type: (Incompatibility, dict, Optional[int], Optional[int]) -> str + self, + other: "Incompatibility", + details: dict, + this_line: Optional[int], + other_line: Optional[int], + ) -> str: requires_both = self._try_requires_both(other, details, this_line, other_line) if requires_both is not None: return requires_both @@ -257,8 +267,12 @@ def and_to_string( return "\n".join(buffer) def _try_requires_both( - self, other, details, this_line, other_line - ): # type: (Incompatibility, dict, Optional[int], Optional[int]) -> Optional[str] + self, + other: "Incompatibility", + details: dict, + this_line: Optional[int], + other_line: Optional[int], + ) -> Optional[str]: if len(self._terms) == 1 or len(other.terms) == 1: return @@ -303,8 +317,8 @@ def _try_requires_both( return "".join(buffer) def _try_requires_through( - self, other, details, this_line, other_line - ): # type: (Incompatibility, dict, int, int) -> Optional[str] + self, other: "Incompatibility", details: dict, this_line: int, other_line: int + ) -> Optional[str]: if len(self._terms) == 1 or len(other.terms) == 1: return @@ -381,8 +395,8 @@ def _try_requires_through( return "".join(buffer) def _try_requires_forbidden( - self, other, details, this_line, other_line - ): # type: (Incompatibility, dict, int, int) -> Optional[str] + self, other: "Incompatibility", details: dict, this_line: int, other_line: int + ) -> Optional[str]: if len(self._terms) != 1 and len(other.terms) != 1: return None @@ -422,7 +436,7 @@ def _try_requires_forbidden( buffer.append("({}) ".format(prior_line)) if isinstance(latter.cause, PythonCause): - cause = latter.cause # type: PythonCause + cause: PythonCause = latter.cause buffer.append("which requires Python {}".format(cause.python_version)) elif isinstance(latter.cause, NoVersionsCause): buffer.append("which doesn't match any versions") @@ -436,13 +450,13 @@ def _try_requires_forbidden( return "".join(buffer) - def _terse(self, term, allow_every=False): # type: (Term, bool) -> str + def _terse(self, term: Term, allow_every: bool = False) -> str: if allow_every and term.constraint.is_any(): return "every version of {}".format(term.dependency.complete_name) return str(term.dependency) - def _single_term_where(self, callable): # type: (callable) -> Optional[Term] + def _single_term_where(self, callable: callable) -> Optional[Term]: found = None for term in self._terms: if not callable(term): @@ -455,5 +469,5 @@ def _single_term_where(self, callable): # type: (callable) -> Optional[Term] return found - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "".format(str(self)) diff --git a/poetry/mixology/incompatibility_cause.py b/poetry/mixology/incompatibility_cause.py index 227b9dd0e81..3267be56d7a 100644 --- a/poetry/mixology/incompatibility_cause.py +++ b/poetry/mixology/incompatibility_cause.py @@ -2,7 +2,7 @@ if TYPE_CHECKING: - from poetry.mixology.incompatibility import Incompatibility # noqa + from poetry.mixology.incompatibility import Incompatibility class IncompatibilityCause(Exception): @@ -32,21 +32,19 @@ class ConflictCause(IncompatibilityCause): during conflict resolution. """ - def __init__( - self, conflict, other - ): # type: ("Incompatibility", "Incompatibility") -> None + def __init__(self, conflict: "Incompatibility", other: "Incompatibility") -> None: self._conflict = conflict self._other = other @property - def conflict(self): # type: () -> "Incompatibility" + def conflict(self) -> "Incompatibility": return self._conflict @property - def other(self): # type: () -> "Incompatibility" + def other(self) -> "Incompatibility": return self._other - def __str__(self): # type: () -> str + def __str__(self) -> str: return str(self._conflict) @@ -57,16 +55,16 @@ class PythonCause(IncompatibilityCause): with the current python version. """ - def __init__(self, python_version, root_python_version): # type: (str, str) -> None + def __init__(self, python_version: str, root_python_version: str) -> None: self._python_version = python_version self._root_python_version = root_python_version @property - def python_version(self): # type: () -> str + def python_version(self) -> str: return self._python_version @property - def root_python_version(self): # type: () -> str + def root_python_version(self) -> str: return self._root_python_version @@ -76,11 +74,11 @@ class PlatformCause(IncompatibilityCause): (OS most likely) being incompatible with the current platform. """ - def __init__(self, platform): # type: (str) -> None + def __init__(self, platform: str) -> None: self._platform = platform @property - def platform(self): # type: () -> str + def platform(self) -> str: return self._platform @@ -90,9 +88,9 @@ class PackageNotFoundCause(IncompatibilityCause): source. """ - def __init__(self, error): # type: (Exception) -> None + def __init__(self, error: Exception) -> None: self._error = error @property - def error(self): # type: () -> Exception + def error(self) -> Exception: return self._error diff --git a/poetry/mixology/partial_solution.py b/poetry/mixology/partial_solution.py index 93a6f73a2fb..b9f36ec13fd 100644 --- a/poetry/mixology/partial_solution.py +++ b/poetry/mixology/partial_solution.py @@ -9,9 +9,8 @@ if TYPE_CHECKING: - from poetry.core.packages import Dependency # noqa - from poetry.core.packages import Package # noqa - from poetry.packages import DependencyPackage # noqa + from poetry.core.packages import Dependency + from poetry.core.packages import Package class PartialSolution: @@ -23,19 +22,19 @@ class PartialSolution: # See https://github.com/dart-lang/mixology/tree/master/doc/solver.md#partial-solution. """ - def __init__(self): # type: () -> None + def __init__(self) -> None: # The assignments that have been made so far, in the order they were # assigned. - self._assignments = [] # type: List[Assignment] + self._assignments: List[Assignment] = [] # The decisions made for each package. - self._decisions = dict() # type: Dict[str, "Package"] + self._decisions: Dict[str, "Package"] = dict() # The intersection of all positive Assignments for each package, minus any # negative Assignments that refer to that package. # # This is derived from self._assignments. - self._positive = dict() # type: Dict[str, Term] + self._positive: Dict[str, Term] = dict() # The union of all negative Assignments for each package. # @@ -43,7 +42,7 @@ def __init__(self): # type: () -> None # map. # # This is derived from self._assignments. - self._negative = dict() # type: Dict[str, Dict[str, Term]] + self._negative: Dict[str, Dict[str, Term]] = dict() # The number of distinct solutions that have been attempted so far. self._attempted_solutions = 1 @@ -52,26 +51,26 @@ def __init__(self): # type: () -> None self._backtracking = False @property - def decisions(self): # type: () -> List["Package"] + def decisions(self) -> List["Package"]: return list(self._decisions.values()) @property - def decision_level(self): # type: () -> int + def decision_level(self) -> int: return len(self._decisions) @property - def attempted_solutions(self): # type: () -> int + def attempted_solutions(self) -> int: return self._attempted_solutions @property - def unsatisfied(self): # type: () -> List["Dependency"] + def unsatisfied(self) -> List["Dependency"]: return [ term.dependency for term in self._positive.values() if term.dependency.complete_name not in self._decisions ] - def decide(self, package): # type: ("Package") -> None + def decide(self, package: "Package") -> None: """ Adds an assignment of package as a decision and increments the decision level. @@ -91,8 +90,8 @@ def decide(self, package): # type: ("Package") -> None ) def derive( - self, dependency, is_positive, cause - ): # type: ("Dependency", bool, Incompatibility) -> None + self, dependency: "Dependency", is_positive: bool, cause: Incompatibility + ) -> None: """ Adds an assignment of package as a derivation. """ @@ -106,14 +105,14 @@ def derive( ) ) - def _assign(self, assignment): # type: (Assignment) -> None + def _assign(self, assignment: Assignment) -> None: """ Adds an Assignment to _assignments and _positive or _negative. """ self._assignments.append(assignment) self._register(assignment) - def backtrack(self, decision_level): # type: (int) -> None + def backtrack(self, decision_level: int) -> None: """ Resets the current decision level to decision_level, and removes all assignments made after that level. @@ -139,7 +138,7 @@ def backtrack(self, decision_level): # type: (int) -> None if assignment.dependency.complete_name in packages: self._register(assignment) - def _register(self, assignment): # type: (Assignment) -> None + def _register(self, assignment: Assignment) -> None: """ Registers an Assignment in _positive or _negative. """ @@ -169,7 +168,7 @@ def _register(self, assignment): # type: (Assignment) -> None self._negative[name][ref] = term - def satisfier(self, term): # type: (Term) -> Assignment + def satisfier(self, term: Term) -> Assignment: """ Returns the first Assignment in this solution such that the sublist of assignments up to and including that entry collectively satisfies term. @@ -202,10 +201,10 @@ def satisfier(self, term): # type: (Term) -> Assignment raise RuntimeError("[BUG] {} is not satisfied.".format(term)) - def satisfies(self, term): # type: (Term) -> bool + def satisfies(self, term: Term) -> bool: return self.relation(term) == SetRelation.SUBSET - def relation(self, term): # type: (Term) -> int + def relation(self, term: Term) -> int: positive = self._positive.get(term.dependency.complete_name) if positive is not None: return positive.relation(term) diff --git a/poetry/mixology/result.py b/poetry/mixology/result.py index 62a5029e5e3..6f83b01825f 100644 --- a/poetry/mixology/result.py +++ b/poetry/mixology/result.py @@ -3,22 +3,25 @@ if TYPE_CHECKING: - from poetry.core.packages import Package # noqa - from poetry.core.packages import ProjectPackage # noqa + from poetry.core.packages import Package + from poetry.core.packages import ProjectPackage class SolverResult: def __init__( - self, root, packages, attempted_solutions - ): # type: ("ProjectPackage", List["Package"], int) -> None + self, + root: "ProjectPackage", + packages: List["Package"], + attempted_solutions: int, + ) -> None: self._root = root self._packages = packages self._attempted_solutions = attempted_solutions @property - def packages(self): # type: () -> List["Package"] + def packages(self) -> List["Package"]: return self._packages @property - def attempted_solutions(self): # type: () -> int + def attempted_solutions(self) -> int: return self._attempted_solutions diff --git a/poetry/mixology/solutions/providers/python_requirement_solution_provider.py b/poetry/mixology/solutions/providers/python_requirement_solution_provider.py index 4c903677fd0..ac061e20f0f 100644 --- a/poetry/mixology/solutions/providers/python_requirement_solution_provider.py +++ b/poetry/mixology/solutions/providers/python_requirement_solution_provider.py @@ -7,7 +7,7 @@ class PythonRequirementSolutionProvider(HasSolutionsForException): - def can_solve(self, exception): # type: (Exception) -> bool + def can_solve(self, exception: Exception) -> bool: from poetry.puzzle.exceptions import SolverProblemError if not isinstance(exception, SolverProblemError): @@ -24,7 +24,7 @@ def can_solve(self, exception): # type: (Exception) -> bool return True - def get_solutions(self, exception): # type: (Exception) -> List[Solution] + def get_solutions(self, exception: Exception) -> List[Solution]: from ..solutions.python_requirement_solution import PythonRequirementSolution return [PythonRequirementSolution(exception)] diff --git a/poetry/mixology/solutions/solutions/python_requirement_solution.py b/poetry/mixology/solutions/solutions/python_requirement_solution.py index b24f7f09915..764c8f7fa68 100644 --- a/poetry/mixology/solutions/solutions/python_requirement_solution.py +++ b/poetry/mixology/solutions/solutions/python_requirement_solution.py @@ -5,11 +5,11 @@ if TYPE_CHECKING: - from poetry.mixology.incompatibility_cause import PackageNotFoundCause # noqa + from poetry.mixology.incompatibility_cause import PackageNotFoundCause class PythonRequirementSolution(Solution): - def __init__(self, exception): # type: ("PackageNotFoundCause") -> None + def __init__(self, exception: "PackageNotFoundCause") -> None: from poetry.core.semver import parse_constraint from poetry.mixology.incompatibility_cause import PythonCause @@ -44,15 +44,15 @@ def __init__(self, exception): # type: ("PackageNotFoundCause") -> None self._description = description @property - def solution_title(self): # type: () -> str + def solution_title(self) -> str: return self._title @property - def solution_description(self): # type: () -> str + def solution_description(self) -> str: return self._description @property - def documentation_links(self): # type: () -> List[str] + def documentation_links(self) -> List[str]: return [ "https://python-poetry.org/docs/dependency-specification/#python-restricted-dependencies", "https://python-poetry.org/docs/dependency-specification/#using-environment-markers", diff --git a/poetry/mixology/term.py b/poetry/mixology/term.py index b232e268af0..751895e37c8 100644 --- a/poetry/mixology/term.py +++ b/poetry/mixology/term.py @@ -8,7 +8,7 @@ if TYPE_CHECKING: - from poetry.core.semver import VersionTypes # noqa + from poetry.core.semver import VersionTypes class Term(object): @@ -19,26 +19,26 @@ class Term(object): See https://github.com/dart-lang/pub/tree/master/doc/solver.md#term. """ - def __init__(self, dependency, is_positive): # type: (Dependency, bool) -> None + def __init__(self, dependency: Dependency, is_positive: bool) -> None: self._dependency = dependency self._positive = is_positive @property - def inverse(self): # type: () -> Term + def inverse(self) -> "Term": return Term(self._dependency, not self.is_positive()) @property - def dependency(self): # type: () -> Dependency + def dependency(self) -> Dependency: return self._dependency @property - def constraint(self): # type: () -> "VersionTypes" + def constraint(self) -> "VersionTypes": return self._dependency.constraint - def is_positive(self): # type: () -> bool + def is_positive(self) -> bool: return self._positive - def satisfies(self, other): # type: (Term) -> bool + def satisfies(self, other: "Term") -> bool: """ Returns whether this term satisfies another. """ @@ -47,7 +47,7 @@ def satisfies(self, other): # type: (Term) -> bool and self.relation(other) == SetRelation.SUBSET ) - def relation(self, other): # type: (Term) -> int + def relation(self, other: "Term") -> int: """ Returns the relationship between the package versions allowed by this term and another. @@ -111,7 +111,7 @@ def relation(self, other): # type: (Term) -> int # not foo ^1.5.0 is a superset of not foo ^1.0.0 return SetRelation.OVERLAPPING - def intersect(self, other): # type: (Term) -> Optional[Term] + def intersect(self, other: "Term") -> Optional["Term"]: """ Returns a Term that represents the packages allowed by both this term and another @@ -145,14 +145,14 @@ def intersect(self, other): # type: (Term) -> Optional[Term] else: return - def difference(self, other): # type: (Term) -> Term + def difference(self, other: "Term") -> "Term": """ Returns a Term that represents packages allowed by this term and not by the other """ return self.intersect(other.inverse) - def _compatible_dependency(self, other): # type: (Term) -> bool + def _compatible_dependency(self, other: "Dependency") -> bool: return ( self.dependency.is_root or other.is_root @@ -160,15 +160,15 @@ def _compatible_dependency(self, other): # type: (Term) -> bool ) def _non_empty_term( - self, constraint, is_positive - ): # type: ("VersionTypes", bool) -> Optional[Term] + self, constraint: "VersionTypes", is_positive: bool + ) -> Optional["Term"]: if constraint.is_empty(): return return Term(self.dependency.with_constraint(constraint), is_positive) - def __str__(self): # type: () -> str + def __str__(self) -> str: return "{}{}".format("not " if not self.is_positive() else "", self._dependency) - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return "".format(str(self)) diff --git a/poetry/mixology/version_solver.py b/poetry/mixology/version_solver.py index 1d1d19d2d55..f2aab10f88a 100644 --- a/poetry/mixology/version_solver.py +++ b/poetry/mixology/version_solver.py @@ -5,6 +5,7 @@ from typing import Dict from typing import List from typing import Optional +from typing import Union from poetry.core.packages import Dependency from poetry.core.packages import Package @@ -40,10 +41,10 @@ class VersionSolver: def __init__( self, - root, # type: ProjectPackage - provider, # type: Provider - locked=None, # type: Dict[str, Package] - use_latest=None, # type: List[str] + root: ProjectPackage, + provider: "Provider", + locked: Dict[str, Package] = None, + use_latest: List[str] = None, ): self._root = root self._provider = provider @@ -54,14 +55,14 @@ def __init__( self._use_latest = use_latest - self._incompatibilities = {} # type: Dict[str, List[Incompatibility]] + self._incompatibilities: Dict[str, List[Incompatibility]] = {} self._solution = PartialSolution() @property - def solution(self): # type: () -> PartialSolution + def solution(self) -> PartialSolution: return self._solution - def solve(self): # type: () -> SolverResult + def solve(self) -> SolverResult: """ Finds a set of dependencies that match the root package's constraints, or raises an error if no such set is available. @@ -91,7 +92,7 @@ def solve(self): # type: () -> SolverResult ) ) - def _propagate(self, package): # type: (str) -> None + def _propagate(self, package: str) -> None: """ Performs unit propagation on incompatibilities transitively related to package to derive new assignments for _solution. @@ -129,8 +130,8 @@ def _propagate(self, package): # type: (str) -> None changed.add(result) def _propagate_incompatibility( - self, incompatibility - ): # type: (Incompatibility) -> Optional[str, _conflict] + self, incompatibility: Incompatibility + ) -> Optional[Union[str, object]]: """ If incompatibility is almost satisfied by _solution, adds the negation of the unsatisfied term to _solution. @@ -182,9 +183,7 @@ def _propagate_incompatibility( return unsatisfied.dependency.complete_name - def _resolve_conflict( - self, incompatibility - ): # type: (Incompatibility) -> Incompatibility + def _resolve_conflict(self, incompatibility: Incompatibility) -> Incompatibility: """ Given an incompatibility that's satisfied by _solution, The `conflict resolution`_ constructs a new incompatibility that encapsulates the root @@ -317,7 +316,7 @@ def _resolve_conflict( raise SolveFailure(incompatibility) - def _choose_package_version(self): # type: () -> Optional[str] + def _choose_package_version(self) -> Optional[str]: """ Tries to select a version of a required package. @@ -331,7 +330,7 @@ def _choose_package_version(self): # type: () -> Optional[str] # Prefer packages with as few remaining versions as possible, # so that if a conflict is necessary it's forced quickly. - def _get_min(dependency): # type: (Dependency) -> int + def _get_min(dependency: Dependency) -> int: if dependency.name in self._use_latest: # If we're forced to use the latest version of a package, it effectively # only has one version to choose from. @@ -418,7 +417,7 @@ def _get_min(dependency): # type: (Dependency) -> int return dependency.complete_name - def _result(self): # type: () -> SolverResult + def _result(self) -> SolverResult: """ Creates a #SolverResult from the decisions in _solution """ @@ -430,7 +429,7 @@ def _result(self): # type: () -> SolverResult self._solution.attempted_solutions, ) - def _add_incompatibility(self, incompatibility): # type: (Incompatibility) -> None + def _add_incompatibility(self, incompatibility: Incompatibility) -> None: self._log("fact: {}".format(incompatibility)) for term in incompatibility.terms: @@ -447,7 +446,7 @@ def _add_incompatibility(self, incompatibility): # type: (Incompatibility) -> N incompatibility ) - def _get_locked(self, dependency): # type: (Dependency) -> Optional[Package] + def _get_locked(self, dependency: Dependency) -> Optional[Package]: if dependency.name in self._use_latest: return @@ -460,5 +459,5 @@ def _get_locked(self, dependency): # type: (Dependency) -> Optional[Package] return locked - def _log(self, text): # type: (str) -> None + def _log(self, text: str) -> None: self._provider.debug(text, self._solution.attempted_solutions) diff --git a/poetry/packages/dependency_package.py b/poetry/packages/dependency_package.py index e375118a5b8..cc50e8d94dc 100644 --- a/poetry/packages/dependency_package.py +++ b/poetry/packages/dependency_package.py @@ -7,46 +7,46 @@ class DependencyPackage(object): - def __init__(self, dependency, package): # type: (Dependency, Package) -> None + def __init__(self, dependency: Dependency, package: Package) -> None: self._dependency = dependency self._package = package @property - def dependency(self): # type: () -> Dependency + def dependency(self) -> Dependency: return self._dependency @property - def package(self): # type: () -> Package + def package(self) -> Package: return self._package - def clone(self): # type: () -> "DependencyPackage" + def clone(self) -> "DependencyPackage": return self.__class__(self._dependency, self._package.clone()) - def with_features(self, features): # type: (List[str]) -> "DependencyPackage" + def with_features(self, features: List[str]) -> "DependencyPackage": return self.__class__(self._dependency, self._package.with_features(features)) - def without_features(self): # type: () -> "DependencyPackage" + def without_features(self) -> "DependencyPackage": return self.with_features([]) - def __getattr__(self, name): # type: (str) -> Any + def __getattr__(self, name: str) -> Any: return getattr(self._package, name) - def __setattr__(self, key, value): # type: (str, Any) -> None + def __setattr__(self, key: str, value: Any) -> None: if key in {"_dependency", "_package"}: return super(DependencyPackage, self).__setattr__(key, value) setattr(self._package, key, value) - def __str__(self): # type: () -> str + def __str__(self) -> str: return str(self._package) - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return repr(self._package) - def __hash__(self): # type: () -> int + def __hash__(self) -> int: return hash(self._package) - def __eq__(self, other): # type: (Union[Package, "DependencyPackage"]) -> bool + def __eq__(self, other: Union[Package, "DependencyPackage"]) -> bool: if isinstance(other, DependencyPackage): other = other.package diff --git a/poetry/packages/locker.py b/poetry/packages/locker.py index 4b3adb19542..48d68e950ac 100644 --- a/poetry/packages/locker.py +++ b/poetry/packages/locker.py @@ -39,7 +39,7 @@ if TYPE_CHECKING: - from ptomlkit.toml_document import TOMLDocument # noqa + from tomlkit.toml_document import TOMLDocument logger = logging.getLogger(__name__) @@ -50,24 +50,24 @@ class Locker(object): _relevant_keys = ["dependencies", "dev-dependencies", "source", "extras"] - def __init__(self, lock, local_config): # type: (Union[str, Path], dict) -> None + def __init__(self, lock: Union[str, Path], local_config: dict) -> None: self._lock = TOMLFile(lock) self._local_config = local_config self._lock_data = None self._content_hash = self._get_content_hash() @property - def lock(self): # type: () -> TOMLFile + def lock(self) -> TOMLFile: return self._lock @property - def lock_data(self): # type: () -> TOMLDocument + def lock_data(self) -> "TOMLDocument": if self._lock_data is None: self._lock_data = self._get_lock_data() return self._lock_data - def is_locked(self): # type: () -> bool + def is_locked(self) -> bool: """ Checks whether the locker has been locked (lockfile found). """ @@ -76,7 +76,7 @@ def is_locked(self): # type: () -> bool return "package" in self.lock_data - def is_fresh(self): # type: () -> bool + def is_fresh(self) -> bool: """ Checks whether the lock file is still up to date with the current hash. """ @@ -89,8 +89,8 @@ def is_fresh(self): # type: () -> bool return False def locked_repository( - self, with_dev_reqs=False - ): # type: (bool) -> poetry.repositories.Repository + self, with_dev_reqs: bool = False + ) -> poetry.repositories.Repository: """ Searches and returns a repository of locked packages. """ @@ -202,8 +202,8 @@ def locked_repository( @staticmethod def __get_locked_package( - _dependency, packages_by_name - ): # type: (Dependency, Dict[str, List[Package]]) -> Optional[Package] + _dependency: Dependency, packages_by_name: Dict[str, List[Package]] + ) -> Optional[Package]: """ Internal helper to identify corresponding locked package using dependency version constraints. @@ -216,13 +216,13 @@ def __get_locked_package( @classmethod def __walk_dependency_level( cls, - dependencies, - level, - pinned_versions, - packages_by_name, - project_level_dependencies, - nested_dependencies, - ): # type: (List[Dependency], int, bool, Dict[str, List[Package]], Set[str], Dict[Tuple[str, str], Dependency]) -> Dict[Tuple[str, str], Dependency] + dependencies: List[Dependency], + level: int, + pinned_versions: bool, + packages_by_name: Dict[str, List[Package]], + project_level_dependencies: Set[str], + nested_dependencies: Dict[Tuple[str, str], Dependency], + ) -> Dict[Tuple[str, str], Dependency]: if not dependencies: return nested_dependencies @@ -284,8 +284,12 @@ def __walk_dependency_level( @classmethod def get_project_dependencies( - cls, project_requires, locked_packages, pinned_versions=False, with_nested=False - ): # type: (List[Dependency], List[Package], bool, bool) -> Iterable[Dependency] + cls, + project_requires: List[Dependency], + locked_packages: List[Package], + pinned_versions: bool = False, + with_nested: bool = False, + ) -> Iterable[Dependency]: # group packages entries by name, this is required because requirement might use different constraints packages_by_name = {} for pkg in locked_packages: @@ -339,8 +343,11 @@ def get_project_dependencies( return sorted(nested_dependencies.values(), key=lambda x: x.name.lower()) def get_project_dependency_packages( - self, project_requires, dev=False, extras=None - ): # type: (List[Dependency], bool, Optional[Union[bool, Sequence[str]]]) -> Iterator[DependencyPackage] + self, + project_requires: List[Dependency], + dev: bool = False, + extras: Optional[Union[bool, Sequence[str]]] = None, + ) -> Iterator[DependencyPackage]: repository = self.locked_repository(with_dev_reqs=dev) # Build a set of all packages required by our selected extras @@ -388,7 +395,7 @@ def get_project_dependency_packages( yield DependencyPackage(dependency=dependency, package=package) - def set_lock_data(self, root, packages): # type: (Package, List[Package]) -> bool + def set_lock_data(self, root: Package, packages: List[Package]) -> bool: files = table() packages = self._lock_packages(packages) # Retrieving hashes @@ -433,7 +440,7 @@ def set_lock_data(self, root, packages): # type: (Package, List[Package]) -> bo return False - def _write_lock_data(self, data): # type: ("TOMLDocument") -> None + def _write_lock_data(self, data: "TOMLDocument") -> None: self.lock.write(data) # Checking lock file data consistency @@ -442,7 +449,7 @@ def _write_lock_data(self, data): # type: ("TOMLDocument") -> None self._lock_data = None - def _get_content_hash(self): # type: () -> str + def _get_content_hash(self) -> str: """ Returns the sha256 hash of the sorted content of the pyproject file. """ @@ -458,7 +465,7 @@ def _get_content_hash(self): # type: () -> str return content_hash - def _get_lock_data(self): # type: () -> "TOMLDocument" + def _get_lock_data(self) -> "TOMLDocument": if not self._lock.exists(): raise RuntimeError("No lockfile found. Unable to read locked packages") @@ -490,7 +497,7 @@ def _get_lock_data(self): # type: () -> "TOMLDocument" return lock_data - def _lock_packages(self, packages): # type: (List[Package]) -> list + def _lock_packages(self, packages: List[Package]) -> list: locked = [] for package in sorted(packages, key=lambda x: x.name): @@ -500,7 +507,7 @@ def _lock_packages(self, packages): # type: (List[Package]) -> list return locked - def _dump_package(self, package): # type: (Package) -> dict + def _dump_package(self, package: Package) -> dict: dependencies = {} for dependency in sorted(package.requires, key=lambda d: d.name): if dependency.pretty_name not in dependencies: diff --git a/poetry/packages/package_collection.py b/poetry/packages/package_collection.py index e572dcbfdeb..7aa7b29c233 100644 --- a/poetry/packages/package_collection.py +++ b/poetry/packages/package_collection.py @@ -6,14 +6,16 @@ if TYPE_CHECKING: - from poetry.core.packages import Dependency # noqa - from poetry.core.packages import Package # noqa + from poetry.core.packages import Dependency + from poetry.core.packages import Package class PackageCollection(list): def __init__( - self, dependency, packages=None - ): # type: (Dependency, List[Union["Package", DependencyPackage]]) -> None + self, + dependency: "Dependency", + packages: List[Union["Package", DependencyPackage]] = None, + ) -> None: self._dependency = dependency if packages is None: @@ -24,7 +26,7 @@ def __init__( for package in packages: self.append(package) - def append(self, package): # type: (Union["Package", DependencyPackage]) -> None + def append(self, package: Union["Package", DependencyPackage]) -> None: if isinstance(package, DependencyPackage): package = package.package diff --git a/poetry/poetry.py b/poetry/poetry.py index 325c6074df3..f5f46e1b968 100644 --- a/poetry/poetry.py +++ b/poetry/poetry.py @@ -18,11 +18,11 @@ class Poetry(BasePoetry): def __init__( self, - file, # type: Path - local_config, # type: dict - package, # type: ProjectPackage - locker, # type: Locker - config, # type: Config + file: Path, + local_config: dict, + package: ProjectPackage, + locker: Locker, + config: Config, ): super(Poetry, self).__init__(file, local_config, package) @@ -31,28 +31,28 @@ def __init__( self._pool = Pool() @property - def locker(self): # type: () -> Locker + def locker(self) -> Locker: return self._locker @property - def pool(self): # type: () -> Pool + def pool(self) -> Pool: return self._pool @property - def config(self): # type: () -> Config + def config(self) -> Config: return self._config - def set_locker(self, locker): # type: (Locker) -> Poetry + def set_locker(self, locker: Locker) -> "Poetry": self._locker = locker return self - def set_pool(self, pool): # type: (Pool) -> Poetry + def set_pool(self, pool: Pool) -> "Poetry": self._pool = pool return self - def set_config(self, config): # type: (Config) -> Poetry + def set_config(self, config: Config) -> "Poetry": self._config = config return self diff --git a/poetry/publishing/publisher.py b/poetry/publishing/publisher.py index f8a87a20388..2c9d1e14d8a 100644 --- a/poetry/publishing/publisher.py +++ b/poetry/publishing/publisher.py @@ -14,10 +14,10 @@ if TYPE_CHECKING: - from cleo.io.buffered_io import BufferedIO # noqa - from cleo.io.null_io import NullIO # noqa + from cleo.io import BufferedIO + from cleo.io import ConsoleIO - from ..poetry import Poetry # noqa + from ..poetry import Poetry logger = logging.getLogger(__name__) @@ -27,9 +27,7 @@ class Publisher: Registers and publishes packages to remote repositories. """ - def __init__( - self, poetry, io - ): # type: ("Poetry", Union["BufferedIO", "NullIO"]) -> None + def __init__(self, poetry: "Poetry", io: Union["BufferedIO", "ConsoleIO"]) -> None: self._poetry = poetry self._package = poetry.package self._io = io @@ -37,18 +35,18 @@ def __init__( self._password_manager = PasswordManager(poetry.config) @property - def files(self): # type: () -> List[Path] + def files(self) -> List[Path]: return self._uploader.files def publish( self, - repository_name, - username, - password, - cert=None, - client_cert=None, - dry_run=False, - ): # type: (Optional[str], Optional[str], Optional[str], Optional[Path], Optional[Path], Optional[bool]) -> None + repository_name: Optional[str], + username: Optional[str], + password: Optional[str], + cert: Optional[Path] = None, + client_cert: Optional[Path] = None, + dry_run: Optional[bool] = False, + ) -> None: if not repository_name: url = "https://upload.pypi.org/legacy/" repository_name = "pypi" diff --git a/poetry/publishing/uploader.py b/poetry/publishing/uploader.py index 8dccb4ef390..72889c023e2 100644 --- a/poetry/publishing/uploader.py +++ b/poetry/publishing/uploader.py @@ -29,15 +29,15 @@ if TYPE_CHECKING: - from cleo.io.null_io import NullIO # noqa + from cleo.io.null_io import NullIO - from poetry.poetry import Poetry # noqa + from poetry.poetry import Poetry _has_blake2 = hasattr(hashlib, "blake2b") class UploadError(Exception): - def __init__(self, error): # type: (Union[ConnectionError, HTTPError, str]) -> None + def __init__(self, error: Union[ConnectionError, HTTPError, str]) -> None: if isinstance(error, HTTPError): message = "HTTP Error {}: {}".format( error.response.status_code, error.response.reason @@ -53,7 +53,7 @@ def __init__(self, error): # type: (Union[ConnectionError, HTTPError, str]) -> class Uploader: - def __init__(self, poetry, io): # type: ("Poetry", "NullIO") -> None + def __init__(self, poetry: "Poetry", io: "NullIO") -> None: self._poetry = poetry self._package = poetry.package self._io = io @@ -61,11 +61,11 @@ def __init__(self, poetry, io): # type: ("Poetry", "NullIO") -> None self._password = None @property - def user_agent(self): # type: () -> str + def user_agent(self) -> str: return user_agent("poetry", __version__) @property - def adapter(self): # type: () -> adapters.HTTPAdapter + def adapter(self) -> adapters.HTTPAdapter: retry = util.Retry( connect=5, total=10, @@ -76,7 +76,7 @@ def adapter(self): # type: () -> adapters.HTTPAdapter return adapters.HTTPAdapter(max_retries=retry) @property - def files(self): # type: () -> List[Path] + def files(self) -> List[Path]: dist = self._poetry.file.parent / "dist" version = normalize_version(self._package.version.text) @@ -93,11 +93,11 @@ def files(self): # type: () -> List[Path] return sorted(wheels + tars) - def auth(self, username, password): # type: (str, str) -> None + def auth(self, username: str, password: str) -> None: self._username = username self._password = password - def make_session(self): # type: () -> requests.Session + def make_session(self) -> requests.Session: session = requests.session() if self.is_authenticated(): session.auth = (self._username, self._password) @@ -108,12 +108,16 @@ def make_session(self): # type: () -> requests.Session return session - def is_authenticated(self): # type: () -> bool + def is_authenticated(self) -> bool: return self._username is not None and self._password is not None def upload( - self, url, cert=None, client_cert=None, dry_run=False - ): # type: (str, Optional[Path], Optional[Path], bool) -> None + self, + url: str, + cert: Optional[Path] = None, + client_cert: Optional[Path] = None, + dry_run: bool = False, + ) -> None: session = self.make_session() if cert: @@ -127,7 +131,7 @@ def upload( finally: session.close() - def post_data(self, file): # type: (Path) -> Dict[str, Any] + def post_data(self, file: Path) -> Dict[str, Any]: meta = Metadata.from_package(self._package) file_type = self._get_type(file) @@ -206,8 +210,8 @@ def post_data(self, file): # type: (Path) -> Dict[str, Any] return data def _upload( - self, session, url, dry_run=False - ): # type: (requests.Session, str, Optional[bool]) -> None + self, session: requests.Session, url: str, dry_run: Optional[bool] = False + ) -> None: try: self._do_upload(session, url, dry_run) except HTTPError as e: @@ -223,8 +227,8 @@ def _upload( raise UploadError(e) def _do_upload( - self, session, url, dry_run=False - ): # type: (requests.Session, str, Optional[bool]) -> None + self, session: requests.Session, url: str, dry_run: Optional[bool] = False + ) -> None: for file in self.files: # TODO: Check existence @@ -234,8 +238,12 @@ def _do_upload( resp.raise_for_status() def _upload_file( - self, session, url, file, dry_run=False - ): # type: (requests.Session, str, Path, Optional[bool]) -> requests.Response + self, + session: requests.Session, + url: str, + file: Path, + dry_run: Optional[bool] = False, + ) -> requests.Response: from cleo.ui.progress_bar import ProgressBar data = self.post_data(file) @@ -305,9 +313,7 @@ def _upload_file( return resp - def _register( - self, session, url - ): # type: (requests.Session, str) -> requests.Response + def _register(self, session: requests.Session, url: str) -> requests.Response: """ Register a package to a repository. """ @@ -335,7 +341,7 @@ def _register( return resp - def _prepare_data(self, data): # type: (Dict) -> List[Tuple[str, str]] + def _prepare_data(self, data: Dict) -> List[Tuple[str, str]]: data_to_send = [] for key, value in data.items(): if not isinstance(value, (list, tuple)): @@ -346,7 +352,7 @@ def _prepare_data(self, data): # type: (Dict) -> List[Tuple[str, str]] return data_to_send - def _get_type(self, file): # type: (Path) -> str + def _get_type(self, file: Path) -> str: exts = file.suffixes if exts[-1] == ".whl": return "bdist_wheel" diff --git a/poetry/puzzle/exceptions.py b/poetry/puzzle/exceptions.py index 8ae381a88ba..5c032da5970 100644 --- a/poetry/puzzle/exceptions.py +++ b/poetry/puzzle/exceptions.py @@ -3,20 +3,20 @@ class SolverProblemError(Exception): - def __init__(self, error): # type: (Exception) -> None + def __init__(self, error: Exception) -> None: self._error = error super(SolverProblemError, self).__init__(str(error)) @property - def error(self): # type: () -> Exception + def error(self) -> Exception: return self._error class OverrideNeeded(Exception): - def __init__(self, *overrides): # type: (*Dict) -> None + def __init__(self, *overrides: Dict) -> None: self._overrides = overrides @property - def overrides(self): # type: () -> Tuple[Dict] + def overrides(self) -> Tuple[Dict]: return self._overrides diff --git a/poetry/puzzle/provider.py b/poetry/puzzle/provider.py index 63ce89a4c8d..6c4823a8d69 100644 --- a/poetry/puzzle/provider.py +++ b/poetry/puzzle/provider.py @@ -46,7 +46,7 @@ class Indicator(ProgressIndicator): - def _formatter_elapsed(self): + def _formatter_elapsed(self) -> str: elapsed = time.time() - self._start_time return "{:.1f}s".format(elapsed) @@ -57,8 +57,8 @@ class Provider: UNSAFE_PACKAGES = {"setuptools", "distribute", "pip", "wheel"} def __init__( - self, package, pool, io, env=None - ): # type: (Package, Pool, Any, Optional[Env]) -> None + self, package: Package, pool: Pool, io: Any, env: Optional[Env] = None + ) -> None: self._package = package self._pool = pool self._io = io @@ -72,20 +72,20 @@ def __init__( self._load_deferred = True @property - def pool(self): # type: () -> Pool + def pool(self) -> Pool: return self._pool - def is_debugging(self): # type: () -> bool + def is_debugging(self) -> bool: return self._is_debugging - def set_overrides(self, overrides): # type: (Dict) -> None + def set_overrides(self, overrides: Dict) -> None: self._overrides = overrides - def load_deferred(self, load_deferred): # type: (bool) -> None + def load_deferred(self, load_deferred: bool) -> None: self._load_deferred = load_deferred @contextmanager - def use_environment(self, env): # type: (Env) -> Provider + def use_environment(self, env: Env) -> "Provider": original_env = self._env original_python_constraint = self._python_constraint @@ -98,8 +98,15 @@ def use_environment(self, env): # type: (Env) -> Provider self._python_constraint = original_python_constraint def search_for( - self, dependency - ): # type: (Union[Dependency, VCSDependency, FileDependency, DirectoryDependency, URLDependency]) -> List[DependencyPackage] + self, + dependency: Union[ + Dependency, + VCSDependency, + FileDependency, + DirectoryDependency, + URLDependency, + ], + ) -> List[DependencyPackage]: """ Search for the specifications that match the given dependency. @@ -154,7 +161,7 @@ def search_for( return PackageCollection(dependency, packages) - def search_for_vcs(self, dependency): # type: (VCSDependency) -> List[Package] + def search_for_vcs(self, dependency: VCSDependency) -> List[Package]: """ Search for the specifications that match the given VCS dependency. @@ -183,8 +190,14 @@ def search_for_vcs(self, dependency): # type: (VCSDependency) -> List[Package] @classmethod def get_package_from_vcs( - cls, vcs, url, branch=None, tag=None, rev=None, name=None - ): # type: (str, str, Optional[str], Optional[str], Optional[str], Optional[str]) -> Package + cls, + vcs: str, + url: str, + branch: Optional[str] = None, + tag: Optional[str] = None, + rev: Optional[str] = None, + name: Optional[str] = None, + ) -> Package: if vcs != "git": raise ValueError("Unsupported VCS dependency {}".format(vcs)) @@ -215,7 +228,7 @@ def get_package_from_vcs( return package - def search_for_file(self, dependency): # type: (FileDependency) -> List[Package] + def search_for_file(self, dependency: FileDependency) -> List[Package]: if dependency in self._deferred_cache: dependency, _package = self._deferred_cache[dependency] @@ -246,7 +259,7 @@ def search_for_file(self, dependency): # type: (FileDependency) -> List[Package return [package] @classmethod - def get_package_from_file(cls, file_path): # type: (Path) -> Package + def get_package_from_file(cls, file_path: Path) -> Package: try: package = PackageInfo.from_path(path=file_path).to_package( root_dir=file_path @@ -258,9 +271,7 @@ def get_package_from_file(cls, file_path): # type: (Path) -> Package return package - def search_for_directory( - self, dependency - ): # type: (DirectoryDependency) -> List[Package] + def search_for_directory(self, dependency: DirectoryDependency) -> List[Package]: if dependency in self._deferred_cache: dependency, _package = self._deferred_cache[dependency] @@ -284,8 +295,8 @@ def search_for_directory( @classmethod def get_package_from_directory( - cls, directory, name=None - ): # type: (Path, Optional[str]) -> Package + cls, directory: Path, name: Optional[str] = None + ) -> Package: package = PackageInfo.from_directory(path=directory).to_package( root_dir=directory ) @@ -300,7 +311,7 @@ def get_package_from_directory( return package - def search_for_url(self, dependency): # type: (URLDependency) -> List[Package] + def search_for_url(self, dependency: URLDependency) -> List[Package]: if dependency in self._deferred_cache: return [self._deferred_cache[dependency]] @@ -329,7 +340,7 @@ def search_for_url(self, dependency): # type: (URLDependency) -> List[Package] return [package] @classmethod - def get_package_from_url(cls, url): # type: (str) -> Package + def get_package_from_url(cls, url: str) -> Package: with temporary_directory() as temp_dir: temp_dir = Path(temp_dir) file_name = os.path.basename(urllib.parse.urlparse(url).path) @@ -343,8 +354,8 @@ def get_package_from_url(cls, url): # type: (str) -> Package return package def incompatibilities_for( - self, package - ): # type: (DependencyPackage) -> List[Incompatibility] + self, package: DependencyPackage + ) -> List[Incompatibility]: """ Returns incompatibilities that encapsulate a given package's dependencies, or that it can't be safely selected. @@ -419,9 +430,7 @@ def incompatibilities_for( for dep in dependencies ] - def complete_package( - self, package - ): # type: (DependencyPackage) -> DependencyPackage + def complete_package(self, package: DependencyPackage) -> DependencyPackage: if package.is_root(): package = package.clone() @@ -695,7 +704,7 @@ def complete_package( return package - def debug(self, message, depth=0): # type: (str, int) -> None + def debug(self, message: str, depth: int = 0) -> None: if not (self._io.is_very_verbose() or self._io.is_debug()): return @@ -782,7 +791,7 @@ def debug(self, message, depth=0): # type: (str, int) -> None self._io.write(debug_info) @contextmanager - def progress(self): # type: () -> Iterator[None] + def progress(self) -> Iterator[None]: if not self._io.output.is_decorated() or self.is_debugging(): self._io.write_line("Resolving dependencies...") yield diff --git a/poetry/puzzle/solver.py b/poetry/puzzle/solver.py index f78c07d71d5..7465df1bfcf 100644 --- a/poetry/puzzle/solver.py +++ b/poetry/puzzle/solver.py @@ -31,24 +31,24 @@ if TYPE_CHECKING: - from poetry.core.packages import Dependency # noqa - from poetry.core.packages import DirectoryDependency # noqa - from poetry.core.packages import FileDependency # noqa - from poetry.core.packages import URLDependency # noqa - from poetry.core.packages import VCSDependency # noqa - from poetry.installation.operations import OperationTypes # noqa + from poetry.core.packages import Dependency + from poetry.core.packages import DirectoryDependency + from poetry.core.packages import FileDependency + from poetry.core.packages import URLDependency + from poetry.core.packages import VCSDependency + from poetry.installation.operations import OperationTypes class Solver: def __init__( self, - package, # type: ProjectPackage - pool, # type: Pool - installed, # type: Repository - locked, # type: Repository - io, # type: IO - remove_untracked=False, # type: bool - provider=None, # type: Optional[Provider] + package: ProjectPackage, + pool: Pool, + installed: Repository, + locked: Repository, + io: IO, + remove_untracked: bool = False, + provider: Optional[Provider] = None, ): self._package = package self._pool = pool @@ -64,15 +64,15 @@ def __init__( self._remove_untracked = remove_untracked @property - def provider(self): # type: () -> Provider + def provider(self) -> Provider: return self._provider @contextmanager - def use_environment(self, env): # type: (Env) -> None + def use_environment(self, env: Env) -> None: with self.provider.use_environment(env): yield - def solve(self, use_latest=None): # type: (List[str]) -> List["OperationTypes"] + def solve(self, use_latest: List[str] = None) -> List["OperationTypes"]: with self._provider.progress(): start = time.time() packages, depths = self._solve(use_latest=use_latest) @@ -210,8 +210,8 @@ def solve(self, use_latest=None): # type: (List[str]) -> List["OperationTypes"] ) def solve_in_compatibility_mode( - self, overrides, use_latest=None - ): # type: (Tuple[Dict], List[str]) -> Tuple[List["Package"], List[int]] + self, overrides: Tuple[Dict], use_latest: List[str] = None + ) -> Tuple[List["Package"], List[int]]: locked = {} for package in self._locked.packages: locked[package.name] = DependencyPackage(package.to_dependency(), package) @@ -241,9 +241,7 @@ def solve_in_compatibility_mode( return packages, depths - def _solve( - self, use_latest=None - ): # type: (List[str]) -> Tuple[List[Package], List[int]] + def _solve(self, use_latest: List[str] = None) -> Tuple[List[Package], List[int]]: if self._provider._overrides: self._overrides.append(self._provider._overrides) @@ -296,20 +294,18 @@ def _solve( class DFSNode(object): - def __init__( - self, id, name, base_name - ): # type: (Tuple[str, str, bool], str, str) -> None + def __init__(self, id: Tuple[str, str, bool], name: str, base_name: str) -> None: self.id = id self.name = name self.base_name = base_name - def reachable(self): # type: () -> List + def reachable(self) -> List: return [] - def visit(self, parents): # type: (List[PackageNode]) -> None + def visit(self, parents: List["PackageNode"]) -> None: pass - def __str__(self): # type: () -> str + def __str__(self) -> str: return str(self.id) @@ -320,8 +316,8 @@ class VisitedState(enum.Enum): def depth_first_search( - source, aggregator -): # type: (PackageNode, Callable) -> List[Tuple[Package, int]] + source: "PackageNode", aggregator: Callable +) -> List[Tuple[Package, int]]: back_edges = defaultdict(list) visited = {} topo_sorted_nodes = [] @@ -349,8 +345,11 @@ def depth_first_search( def dfs_visit( - node, back_edges, visited, sorted_nodes -): # type: (PackageNode, Dict[str, List[PackageNode]], Dict[str, VisitedState], List[PackageNode]) -> bool + node: "PackageNode", + back_edges: Dict[str, List["PackageNode"]], + visited: Dict[str, VisitedState], + sorted_nodes: List["PackageNode"], +) -> bool: if visited.get(node.id, VisitedState.Unvisited) == VisitedState.Visited: return True if visited.get(node.id, VisitedState.Unvisited) == VisitedState.PartiallyVisited: @@ -372,12 +371,28 @@ def dfs_visit( class PackageNode(DFSNode): def __init__( self, - package, # type: Package - packages, # type: List[Package] - previous=None, # type: Optional[PackageNode] - previous_dep=None, # type: Optional[Union["DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency", "Dependency"]] - dep=None, # type: Optional[Union["DirectoryDependency", "FileDependency", "URLDependency", "VCSDependency", "Dependency"]] - ): # type: (...) -> None + package: Package, + packages: List[Package], + previous: Optional["PackageNode"] = None, + previous_dep: Optional[ + Union[ + "DirectoryDependency", + "FileDependency", + "URLDependency", + "VCSDependency", + "Dependency", + ] + ] = None, + dep: Optional[ + Union[ + "DirectoryDependency", + "FileDependency", + "URLDependency", + "VCSDependency", + "Dependency", + ] + ] = None, + ) -> None: self.package = package self.packages = packages @@ -399,8 +414,8 @@ def __init__( package.name, ) - def reachable(self): # type: () -> List[PackageNode] - children = [] # type: List[PackageNode] + def reachable(self) -> List["PackageNode"]: + children: List[PackageNode] = [] if ( self.previous_dep @@ -446,7 +461,7 @@ def reachable(self): # type: () -> List[PackageNode] return children - def visit(self, parents): # type: (PackageNode) -> None + def visit(self, parents: "PackageNode") -> None: # The root package, which has no parents, is defined as having depth -1 # So that the root package's top-level dependencies have depth 0. self.depth = 1 + max( @@ -459,8 +474,8 @@ def visit(self, parents): # type: (PackageNode) -> None def aggregate_package_nodes( - nodes, children -): # type: (List[PackageNode], List[PackageNode]) -> Tuple[Package, int] + nodes: List[PackageNode], children: List[PackageNode] +) -> Tuple[Package, int]: package = nodes[0].package depth = max(node.depth for node in nodes) category = ( diff --git a/poetry/repositories/base_repository.py b/poetry/repositories/base_repository.py index 801056661a1..4954c569ca9 100644 --- a/poetry/repositories/base_repository.py +++ b/poetry/repositories/base_repository.py @@ -4,28 +4,28 @@ if TYPE_CHECKING: - from poetry.core.packages import Dependency # noqa - from poetry.core.packages import Package # noqa + from poetry.core.packages import Dependency + from poetry.core.packages import Package class BaseRepository(object): - def __init__(self): # type: () -> None + def __init__(self) -> None: self._packages = [] @property - def packages(self): # type: () -> List["Package"] + def packages(self) -> List["Package"]: return self._packages - def has_package(self, package): # type: ("Package") -> None + def has_package(self, package: "Package") -> None: raise NotImplementedError() def package( - self, name, version, extras=None - ): # type: (str, str, Optional[List[str]]) -> None + self, name: str, version: str, extras: Optional[List[str]] = None + ) -> None: raise NotImplementedError() - def find_packages(self, dependency): # type: ("Dependency") -> None + def find_packages(self, dependency: "Dependency") -> None: raise NotImplementedError() - def search(self, query): # type: (str) -> None + def search(self, query: str) -> None: raise NotImplementedError() diff --git a/poetry/repositories/installed_repository.py b/poetry/repositories/installed_repository.py index 1202d7f6612..6fba0dd372e 100644 --- a/poetry/repositories/installed_repository.py +++ b/poetry/repositories/installed_repository.py @@ -23,7 +23,7 @@ class InstalledRepository(Repository): @classmethod - def get_package_paths(cls, env, name): # type: (Env, str) -> Set[Path] + def get_package_paths(cls, env: Env, name: str) -> Set[Path]: """ Process a .pth file within the site-packages directories, and return any valid paths. We skip executable .pth files as there is no reliable means to do this @@ -68,9 +68,7 @@ def get_package_paths(cls, env, name): # type: (Env, str) -> Set[Path] return paths @classmethod - def set_package_vcs_properties_from_path( - cls, src, package - ): # type: (Path, Package) -> None + def set_package_vcs_properties_from_path(cls, src: Path, package: Package) -> None: from poetry.core.vcs.git import Git git = Git() @@ -82,12 +80,12 @@ def set_package_vcs_properties_from_path( package._source_reference = revision @classmethod - def set_package_vcs_properties(cls, package, env): # type: (Package, Env) -> None + def set_package_vcs_properties(cls, package: Package, env: Env) -> None: src = env.path / "src" / package.name cls.set_package_vcs_properties_from_path(src, package) @classmethod - def is_vcs_package(cls, package, env): # type: (Union[Path, Package], Env) -> bool + def is_vcs_package(cls, package: Union[Path, Package], env: Env) -> bool: # A VCS dependency should have been installed # in the src directory. src = env.path / "src" @@ -102,7 +100,7 @@ def is_vcs_package(cls, package, env): # type: (Union[Path, Package], Env) -> b return True @classmethod - def load(cls, env): # type: (Env) -> InstalledRepository + def load(cls, env: Env) -> "InstalledRepository": """ Load installed packages. """ diff --git a/poetry/repositories/legacy_repository.py b/poetry/repositories/legacy_repository.py index aa870562e8b..d14d48fe2fe 100644 --- a/poetry/repositories/legacy_repository.py +++ b/poetry/repositories/legacy_repository.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING from typing import Any from typing import Dict -from typing import Generator +from typing import Iterator from typing import List from typing import Optional @@ -38,7 +38,7 @@ if TYPE_CHECKING: - from poetry.core.packages import Dependency # noqa + from poetry.core.packages import Dependency try: from html import unescape @@ -75,9 +75,7 @@ class Page: ".tar", ] - def __init__( - self, url, content, headers - ): # type: (str, str, Dict[str, Any]) -> None + def __init__(self, url: str, content: str, headers: Dict[str, Any]) -> None: if not url.endswith("/"): url += "/" @@ -99,7 +97,7 @@ def __init__( ) @property - def versions(self): # type: () -> Generator[Version] + def versions(self) -> Iterator[Version]: seen = set() for link in self.links: version = self.link_version(link) @@ -115,7 +113,7 @@ def versions(self): # type: () -> Generator[Version] yield version @property - def links(self): # type: () -> Generator[Link] + def links(self) -> Iterator[Link]: for anchor in self._parsed.findall(".//a"): if anchor.get("href"): href = anchor.get("href") @@ -130,12 +128,12 @@ def links(self): # type: () -> Generator[Link] yield link - def links_for_version(self, version): # type: (Version) -> Generator[Link] + def links_for_version(self, version: Version) -> Iterator[Link]: for link in self.links: if self.link_version(link) == version: yield link - def link_version(self, link): # type: (Link) -> Optional[Version] + def link_version(self, link: Link) -> Optional[Version]: m = wheel_file_re.match(link.filename) if m: version = m.group("ver") @@ -156,7 +154,7 @@ def link_version(self, link): # type: (Link) -> Optional[Version] _clean_re = re.compile(r"[^a-z0-9$&+,/:;=?@.#%_\\|-]", re.I) - def clean_link(self, url): # type: (str) -> str + def clean_link(self, url: str) -> str: """Makes sure a link is fully encoded. That is, if a ' ' shows up in the link, it will be rewritten to %20 (while not over-quoting % or other characters).""" @@ -165,8 +163,14 @@ def clean_link(self, url): # type: (str) -> str class LegacyRepository(PyPiRepository): def __init__( - self, name, url, config=None, disable_cache=False, cert=None, client_cert=None - ): # type: (str, str, Optional[Config], bool, Optional[Path], Optional[Path]) -> None + self, + name: str, + url: str, + config: Optional[Config] = None, + disable_cache: bool = False, + cert: Optional[Path] = None, + client_cert: Optional[Path] = None, + ) -> None: if name == "pypi": raise ValueError("The name [pypi] is reserved for repositories") @@ -211,15 +215,15 @@ def __init__( self._disable_cache = disable_cache @property - def cert(self): # type: () -> Optional[Path] + def cert(self) -> Optional[Path]: return self._cert @property - def client_cert(self): # type: () -> Optional[Path] + def client_cert(self) -> Optional[Path]: return self._client_cert @property - def authenticated_url(self): # type: () -> str + def authenticated_url(self) -> str: if not self._session.auth: return self.url @@ -233,7 +237,7 @@ def authenticated_url(self): # type: () -> str path=parsed.path, ) - def find_packages(self, dependency): # type: ("Dependency") -> List[Package] + def find_packages(self, dependency: "Dependency") -> List[Package]: packages = [] constraint = dependency.constraint @@ -305,8 +309,8 @@ def find_packages(self, dependency): # type: ("Dependency") -> List[Package] return packages def package( - self, name, version, extras=None - ): # type: (str, str, Optional[List[str]]) -> Package + self, name: str, version: str, extras: Optional[List[str]] = None + ) -> Package: """ Retrieve the release information. @@ -330,14 +334,14 @@ def package( return package - def find_links_for_package(self, package): # type: (Package) -> List[Link] + def find_links_for_package(self, package: Package) -> List[Link]: page = self._get("/{}/".format(package.name.replace(".", "-"))) if page is None: return [] return list(page.links_for_version(package.version)) - def _get_release_info(self, name, version): # type: (str, str) -> dict + def _get_release_info(self, name: str, version: str) -> dict: page = self._get("/{}/".format(canonicalize_name(name).replace(".", "-"))) if page is None: raise PackageNotFound('No package named "{}"'.format(name)) @@ -385,7 +389,7 @@ def _get_release_info(self, name, version): # type: (str, str) -> dict return data.asdict() - def _get(self, endpoint): # type: (str) -> Optional[Page] + def _get(self, endpoint: str) -> Optional[Page]: url = self._url + endpoint try: response = self.session.get(url) diff --git a/poetry/repositories/pool.py b/poetry/repositories/pool.py index d8181ba06c0..20579b5fdab 100644 --- a/poetry/repositories/pool.py +++ b/poetry/repositories/pool.py @@ -9,19 +9,21 @@ if TYPE_CHECKING: - from poetry.core.packages import Dependency # noqa - from poetry.core.packages import Package # noqa + from poetry.core.packages import Dependency + from poetry.core.packages import Package class Pool(BaseRepository): def __init__( - self, repositories=None, ignore_repository_names=False - ): # type: (Optional[List[Repository]], bool) -> None + self, + repositories: Optional[List[Repository]] = None, + ignore_repository_names: bool = False, + ) -> None: if repositories is None: repositories = [] - self._lookup = {} # type: Dict[str, int] - self._repositories = [] # type: List[Repository] + self._lookup: Dict[str, int] = {} + self._repositories: List[Repository] = [] self._default = False self._secondary_start_idx = None @@ -33,18 +35,18 @@ def __init__( super(Pool, self).__init__() @property - def repositories(self): # type: () -> List[Repository] + def repositories(self) -> List[Repository]: return self._repositories - def has_default(self): # type: () -> bool + def has_default(self) -> bool: return self._default - def has_repository(self, name): # type: (str) -> bool + def has_repository(self, name: str) -> bool: name = name.lower() if name is not None else None return name in self._lookup - def repository(self, name): # type: (str) -> Repository + def repository(self, name: str) -> Repository: if name is not None: name = name.lower() @@ -54,8 +56,8 @@ def repository(self, name): # type: (str) -> Repository raise ValueError('Repository "{}" does not exist.'.format(name)) def add_repository( - self, repository, default=False, secondary=False - ): # type: (Repository, bool, bool) -> Pool + self, repository: Repository, default: bool = False, secondary: bool = False + ) -> "Pool": """ Adds a repository to the pool. """ @@ -99,7 +101,7 @@ def add_repository( return self - def remove_repository(self, repository_name): # type: (str) -> Pool + def remove_repository(self, repository_name: str) -> "Pool": if repository_name is not None: repository_name = repository_name.lower() @@ -109,12 +111,12 @@ def remove_repository(self, repository_name): # type: (str) -> Pool return self - def has_package(self, package): # type: ("Package") -> bool + def has_package(self, package: "Package") -> bool: raise NotImplementedError() def package( - self, name, version, extras=None, repository=None - ): # type: (str, str, List[str], str) -> "Package" + self, name: str, version: str, extras: List[str] = None, repository: str = None + ) -> "Package": if repository is not None: repository = repository.lower() @@ -144,7 +146,7 @@ def package( raise PackageNotFound("Package {} ({}) not found.".format(name, version)) - def find_packages(self, dependency): # type: ("Dependency") -> List["Package"] + def find_packages(self, dependency: "Dependency") -> List["Package"]: repository = dependency.source_name if repository is not None: repository = repository.lower() @@ -165,7 +167,7 @@ def find_packages(self, dependency): # type: ("Dependency") -> List["Package"] return packages - def search(self, query): # type: (str) -> List["Package"] + def search(self, query: str) -> List["Package"]: from .legacy_repository import LegacyRepository results = [] diff --git a/poetry/repositories/pypi_repository.py b/poetry/repositories/pypi_repository.py index 951f305dbfc..02d9543d92b 100644 --- a/poetry/repositories/pypi_repository.py +++ b/poetry/repositories/pypi_repository.py @@ -46,8 +46,11 @@ class PyPiRepository(RemoteRepository): CACHE_VERSION = parse_constraint("1.0.0") def __init__( - self, url="https://pypi.org/", disable_cache=False, fallback=True - ): # type: (str, bool, bool) -> None + self, + url: str = "https://pypi.org/", + disable_cache: bool = False, + fallback: bool = True, + ) -> None: super(PyPiRepository, self).__init__(url.rstrip("/") + "/simple/") self._base_url = url @@ -74,10 +77,10 @@ def __init__( self._name = "PyPI" @property - def session(self): # type: () -> CacheControl + def session(self) -> CacheControl: return self._session - def find_packages(self, dependency): # type: (Dependency) -> List[Package] + def find_packages(self, dependency: Dependency) -> List[Package]: """ Find packages on the remote server. """ @@ -152,13 +155,13 @@ def find_packages(self, dependency): # type: (Dependency) -> List[Package] def package( self, - name, # type: str - version, # type: str - extras=None, # type: (Union[list, None]) - ): # type: (...) -> Package + name: str, + version: str, + extras: (Union[list, None]) = None, + ) -> Package: return self.get_release_info(name, version).to_package(name=name, extras=extras) - def search(self, query): # type: (str) -> List[Package] + def search(self, query: str) -> List[Package]: results = [] search = {"q": query} @@ -190,7 +193,7 @@ def search(self, query): # type: (str) -> List[Package] return results - def get_package_info(self, name): # type: (str) -> dict + def get_package_info(self, name: str) -> dict: """ Return the package information given its name. @@ -204,14 +207,14 @@ def get_package_info(self, name): # type: (str) -> dict name, lambda: self._get_package_info(name) ) - def _get_package_info(self, name): # type: (str) -> dict + def _get_package_info(self, name: str) -> dict: data = self._get("pypi/{}/json".format(name)) if data is None: raise PackageNotFound("Package [{}] not found.".format(name)) return data - def get_release_info(self, name, version): # type: (str, str) -> PackageInfo + def get_release_info(self, name: str, version: str) -> PackageInfo: """ Return the release information given a package name and a version. @@ -238,7 +241,7 @@ def get_release_info(self, name, version): # type: (str, str) -> PackageInfo return PackageInfo.load(cached) - def find_links_for_package(self, package): # type: (Package) -> List[Link] + def find_links_for_package(self, package: Package) -> List[Link]: json_data = self._get("pypi/{}/{}/json".format(package.name, package.version)) if json_data is None: return [] @@ -250,7 +253,7 @@ def find_links_for_package(self, package): # type: (Package) -> List[Link] return links - def _get_release_info(self, name, version): # type: (str, str) -> dict + def _get_release_info(self, name: str, version: str) -> dict: self._log("Getting info for {} ({}) from PyPI".format(name, version), "debug") json_data = self._get("pypi/{}/{}/json".format(name, version)) @@ -311,7 +314,7 @@ def _get_release_info(self, name, version): # type: (str, str) -> dict return data.asdict() - def _get(self, endpoint): # type: (str) -> Union[dict, None] + def _get(self, endpoint: str) -> Union[dict, None]: try: json_response = self.session.get(self._base_url + endpoint) except requests.exceptions.TooManyRedirects: @@ -327,7 +330,7 @@ def _get(self, endpoint): # type: (str) -> Union[dict, None] return json_data - def _get_info_from_urls(self, urls): # type: (Dict[str, List[str]]) -> PackageInfo + def _get_info_from_urls(self, urls: Dict[str, List[str]]) -> PackageInfo: # Checking wheels first as they are more likely to hold # the necessary information if "bdist_wheel" in urls: @@ -419,7 +422,7 @@ def _get_info_from_urls(self, urls): # type: (Dict[str, List[str]]) -> PackageI return self._get_info_from_sdist(urls["sdist"][0]) - def _get_info_from_wheel(self, url): # type: (str) -> PackageInfo + def _get_info_from_wheel(self, url: str) -> PackageInfo: self._log( "Downloading wheel: {}".format( urllib.parse.urlparse(url).path.rsplit("/")[-1] @@ -435,7 +438,7 @@ def _get_info_from_wheel(self, url): # type: (str) -> PackageInfo return PackageInfo.from_wheel(filepath) - def _get_info_from_sdist(self, url): # type: (str) -> PackageInfo + def _get_info_from_sdist(self, url: str) -> PackageInfo: self._log( "Downloading sdist: {}".format( urllib.parse.urlparse(url).path.rsplit("/")[-1] @@ -451,8 +454,8 @@ def _get_info_from_sdist(self, url): # type: (str) -> PackageInfo return PackageInfo.from_sdist(filepath) - def _download(self, url, dest): # type: (str, str) -> None + def _download(self, url: str, dest: str) -> None: return download_file(url, dest, session=self.session) - def _log(self, msg, level="info"): # type: (str, str) -> None + def _log(self, msg: str, level: str = "info") -> None: getattr(logger, level)("{}: {}".format(self._name, msg)) diff --git a/poetry/repositories/remote_repository.py b/poetry/repositories/remote_repository.py index 7717740d87c..a893b31ec4f 100644 --- a/poetry/repositories/remote_repository.py +++ b/poetry/repositories/remote_repository.py @@ -2,15 +2,15 @@ class RemoteRepository(Repository): - def __init__(self, url): # type: (str) -> None + def __init__(self, url: str) -> None: self._url = url super(RemoteRepository, self).__init__() @property - def url(self): # type: () -> str + def url(self) -> str: return self._url @property - def authenticated_url(self): # type: () -> str + def authenticated_url(self) -> str: return self._url diff --git a/poetry/repositories/repository.py b/poetry/repositories/repository.py index f65e44bafc8..96932ec8edf 100644 --- a/poetry/repositories/repository.py +++ b/poetry/repositories/repository.py @@ -10,15 +10,13 @@ if TYPE_CHECKING: - from poetry.core.packages import Dependency # noqa - from poetry.core.packages import Link # noqa - from poetry.core.packages import Package # noqa + from poetry.core.packages import Dependency + from poetry.core.packages import Link + from poetry.core.packages import Package class Repository(BaseRepository): - def __init__( - self, packages=None, name=None - ): # type: (List["Package"], str) -> None + def __init__(self, packages: List["Package"] = None, name: str = None) -> None: super(Repository, self).__init__() self._name = name @@ -30,19 +28,19 @@ def __init__( self.add_package(package) @property - def name(self): # type: () -> str + def name(self) -> str: return self._name def package( - self, name, version, extras=None - ): # type: (str, str, Optional[List[str]]) -> "Package" + self, name: str, version: str, extras: Optional[List[str]] = None + ) -> "Package": name = name.lower() for package in self.packages: if name == package.name and package.version.text == version: return package.clone() - def find_packages(self, dependency): # type: ("Dependency") -> List["Package"] + def find_packages(self, dependency: "Dependency") -> List["Package"]: constraint = dependency.constraint packages = [] ignored_pre_release_packages = [] @@ -85,7 +83,7 @@ def find_packages(self, dependency): # type: ("Dependency") -> List["Package"] return packages or ignored_pre_release_packages - def has_package(self, package): # type: ("Package") -> bool + def has_package(self, package: "Package") -> bool: package_id = package.unique_name for repo_package in self.packages: @@ -94,10 +92,10 @@ def has_package(self, package): # type: ("Package") -> bool return False - def add_package(self, package): # type: ("Package") -> None + def add_package(self, package: "Package") -> None: self._packages.append(package) - def remove_package(self, package): # type: ("Package") -> None + def remove_package(self, package: "Package") -> None: package_id = package.unique_name index = None @@ -109,10 +107,10 @@ def remove_package(self, package): # type: ("Package") -> None if index is not None: del self._packages[index] - def find_links_for_package(self, package): # type: ("Package") -> List["Link"] + def find_links_for_package(self, package: "Package") -> List["Link"]: return [] - def search(self, query): # type: (str) -> List["Package"] + def search(self, query: str) -> List["Package"]: results = [] for package in self.packages: @@ -121,5 +119,5 @@ def search(self, query): # type: (str) -> List["Package"] return results - def __len__(self): # type: () -> int + def __len__(self) -> int: return len(self._packages) diff --git a/poetry/utils/appdirs.py b/poetry/utils/appdirs.py index aac3ad36076..f5592f6b0da 100644 --- a/poetry/utils/appdirs.py +++ b/poetry/utils/appdirs.py @@ -11,13 +11,13 @@ if TYPE_CHECKING: - from poetry.utils._compat import Path # noqa + from pathlib import Path WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt") -def expanduser(path): # type: (Union[str, "Path"]) -> str +def expanduser(path: Union[str, "Path"]) -> str: """ Expand ~ and ~user constructions. @@ -29,7 +29,7 @@ def expanduser(path): # type: (Union[str, "Path"]) -> str return expanded -def user_cache_dir(appname): # type: (str) -> str +def user_cache_dir(appname: str) -> str: r""" Return full path to the user-specific cache dir for this application. @@ -72,7 +72,7 @@ def user_cache_dir(appname): # type: (str) -> str return path -def user_data_dir(appname, roaming=False): # type: (str, bool) -> str +def user_data_dir(appname: str, roaming: bool = False) -> str: r""" Return full path to the user-specific data dir for this application. @@ -112,7 +112,7 @@ def user_data_dir(appname, roaming=False): # type: (str, bool) -> str return path -def user_config_dir(appname, roaming=True): # type: (str, bool) -> str +def user_config_dir(appname: str, roaming: bool = True) -> str: """Return full path to the user-specific config dir for this application. "appname" is the name of application. @@ -145,7 +145,7 @@ def user_config_dir(appname, roaming=True): # type: (str, bool) -> str # for the discussion regarding site_config_dirs locations # see -def site_config_dirs(appname): # type: (str) -> List[str] +def site_config_dirs(appname: str) -> List[str]: r"""Return a list of potential user-shared config dirs for this application. "appname" is the name of application. @@ -186,7 +186,7 @@ def site_config_dirs(appname): # type: (str) -> List[str] # -- Windows support functions -- -def _get_win_folder_from_registry(csidl_name): # type: (str) -> str +def _get_win_folder_from_registry(csidl_name: str) -> str: """ This is a fallback technique at best. I'm not sure if using the registry for this guarantees us the correct answer for all CSIDL_* @@ -208,7 +208,7 @@ def _get_win_folder_from_registry(csidl_name): # type: (str) -> str return directory -def _get_win_folder_with_ctypes(csidl_name): # type: (str) -> str +def _get_win_folder_with_ctypes(csidl_name: str) -> str: csidl_const = { "CSIDL_APPDATA": 26, "CSIDL_COMMON_APPDATA": 35, @@ -242,7 +242,7 @@ def _get_win_folder_with_ctypes(csidl_name): # type: (str) -> str _get_win_folder = _get_win_folder_from_registry -def _win_path_to_bytes(path): # type: (str) -> Union[str, bytes] +def _win_path_to_bytes(path: str) -> Union[str, bytes]: """Encode Windows paths to bytes. Only used on Python 2. Motivation is to be consistent with other operating systems where paths diff --git a/poetry/utils/env.py b/poetry/utils/env.py index 36851104831..ff78bf0161b 100644 --- a/poetry/utils/env.py +++ b/poetry/utils/env.py @@ -148,8 +148,8 @@ def _version_nodot(version): class SitePackages: def __init__( - self, path, fallbacks=None, skip_write_checks=False - ): # type: (Path, List[Path], bool) -> None + self, path: Path, fallbacks: List[Path] = None, skip_write_checks: bool = False + ) -> None: self._path = path self._fallbacks = fallbacks or [] self._skip_write_checks = skip_write_checks @@ -157,15 +157,15 @@ def __init__( self._writable_candidates = None if not skip_write_checks else self._candidates @property - def path(self): # type: () -> Path + def path(self) -> Path: return self._path @property - def candidates(self): # type: () -> List[Path] + def candidates(self) -> List[Path]: return self._candidates @property - def writable_candidates(self): # type: () -> List[Path] + def writable_candidates(self) -> List[Path]: if self._writable_candidates is not None: return self._writable_candidates @@ -177,9 +177,7 @@ def writable_candidates(self): # type: () -> List[Path] return self._writable_candidates - def make_candidates( - self, path, writable_only=False - ): # type: (Path, bool) -> List[Path] + def make_candidates(self, path: Path, writable_only: bool = False) -> List[Path]: candidates = self._candidates if not writable_only else self.writable_candidates if path.is_absolute(): for candidate in candidates: @@ -198,8 +196,8 @@ def make_candidates( return [candidate / path for candidate in candidates if candidate] def _path_method_wrapper( - self, path, method, *args, **kwargs - ): # type: (Path, str, *Any, **Any) -> Union[Tuple[Path, Any], List[Tuple[Path, Any]]] + self, path: Path, method: str, *args: Any, **kwargs: Any + ) -> Union[Tuple[Path, Any], List[Tuple[Path, Any]]]: # TODO: Move to parameters after dropping Python 2.7 return_first = kwargs.pop("return_first", True) @@ -232,19 +230,19 @@ def _path_method_wrapper( raise OSError("Unable to access any of {}".format(paths_csv(candidates))) - def write_text(self, path, *args, **kwargs): # type: (Path, *Any, **Any) -> Path + def write_text(self, path: Path, *args: Any, **kwargs: Any) -> Path: return self._path_method_wrapper(path, "write_text", *args, **kwargs)[0] - def mkdir(self, path, *args, **kwargs): # type: (Path, *Any, **Any) -> Path + def mkdir(self, path: Path, *args: Any, **kwargs: Any) -> Path: return self._path_method_wrapper(path, "mkdir", *args, **kwargs)[0] - def exists(self, path): # type: (Path) -> bool + def exists(self, path: Path) -> bool: return any( value[-1] for value in self._path_method_wrapper(path, "exists", return_first=False) ) - def find(self, path, writable_only=False): # type: (Path, bool) -> List[Path] + def find(self, path: Path, writable_only: bool = False) -> List[Path]: return [ value[0] for value in self._path_method_wrapper( @@ -253,7 +251,7 @@ def find(self, path, writable_only=False): # type: (Path, bool) -> List[Path] if value[-1] is True ] - def __getattr__(self, item): # type: (str) -> Any + def __getattr__(self, item: str) -> Any: try: return super(SitePackages, self).__getattribute__(item) except AttributeError: @@ -266,9 +264,7 @@ class EnvError(Exception): class EnvCommandError(EnvError): - def __init__( - self, e, input=None - ): # type: (CalledProcessError, Optional[str]) -> None + def __init__(self, e: CalledProcessError, input: Optional[str] = None) -> None: self.e = e message = "Command {} errored with the following return code {}, and output: \n{}".format( @@ -280,7 +276,7 @@ def __init__( class NoCompatiblePythonVersionFound(EnvError): - def __init__(self, expected, given=None): # type: (str, Optional[str]) -> None + def __init__(self, expected: str, given: Optional[str] = None) -> None: if given: message = ( "The specified Python version ({}) " @@ -308,10 +304,10 @@ class EnvManager(object): ENVS_FILE = "envs.toml" - def __init__(self, poetry): # type: (Poetry) -> None + def __init__(self, poetry: Poetry) -> None: self._poetry = poetry - def activate(self, python, io): # type: (str, IO) -> Env + def activate(self, python: str, io: IO) -> "Env": venv_path = self._poetry.config.get("virtualenvs.path") if venv_path is None: venv_path = Path(CACHE_DIR) / "virtualenvs" @@ -408,7 +404,7 @@ def activate(self, python, io): # type: (str, IO) -> Env return self.get(reload=True) - def deactivate(self, io): # type: (IO) -> None + def deactivate(self, io: IO) -> None: venv_path = self._poetry.config.get("virtualenvs.path") if venv_path is None: venv_path = Path(CACHE_DIR) / "virtualenvs" @@ -432,7 +428,7 @@ def deactivate(self, io): # type: (IO) -> None envs_file.write(envs) - def get(self, reload=False): # type: (bool) -> Env + def get(self, reload: bool = False) -> Union["VirtualEnv", "SystemEnv"]: if self._env is not None and not reload: return self._env @@ -500,7 +496,7 @@ def get(self, reload=False): # type: (bool) -> Env return VirtualEnv(prefix, base_prefix) - def list(self, name=None): # type: (Optional[str]) -> List[VirtualEnv] + def list(self, name: Optional[str] = None) -> List["VirtualEnv"]: if name is None: name = self._poetry.package.name @@ -526,7 +522,7 @@ def list(self, name=None): # type: (Optional[str]) -> List[VirtualEnv] env_list.insert(0, VirtualEnv(venv)) return env_list - def remove(self, python): # type: (str) -> Env + def remove(self, python: str) -> "Env": venv_path = self._poetry.config.get("virtualenvs.path") if venv_path is None: venv_path = Path(CACHE_DIR) / "virtualenvs" @@ -620,8 +616,12 @@ def remove(self, python): # type: (str) -> Env return VirtualEnv(venv) def create_venv( - self, io, name=None, executable=None, force=False - ): # type: (IO, Optional[str], Optional[str], bool) -> VirtualEnv + self, + io: IO, + name: Optional[str] = None, + executable: Optional[str] = None, + force: bool = False, + ) -> Union["SystemEnv", "VirtualEnv"]: if self._env is not None and not force: return self._env @@ -806,8 +806,11 @@ def create_venv( @classmethod def build_venv( - cls, path, executable=None, flags=None - ): # type: (Union[Path,str], Optional[Union[str, Path]], Dict[str, bool]) -> virtualenv.run.session.Session + cls, + path: Union[Path, str], + executable: Optional[Union[str, Path]] = None, + flags: Dict[str, bool] = None, + ) -> virtualenv.run.session.Session: flags = flags or {} if isinstance(executable, Path): @@ -828,7 +831,7 @@ def build_venv( return virtualenv.cli_run(args) @classmethod - def remove_venv(cls, path): # type: (Union[Path,str]) -> None + def remove_venv(cls, path: Union[Path, str]) -> None: if isinstance(path, str): path = Path(path) assert path.is_dir() @@ -850,7 +853,7 @@ def remove_venv(cls, path): # type: (Union[Path,str]) -> None elif file_path.is_dir(): shutil.rmtree(str(file_path)) - def get_base_prefix(self): # type: () -> Path + def get_base_prefix(self) -> Path: if hasattr(sys, "real_prefix"): return Path(sys.real_prefix) @@ -860,7 +863,7 @@ def get_base_prefix(self): # type: () -> Path return Path(sys.prefix) @classmethod - def generate_env_name(cls, name, cwd): # type: (str, str) -> str + def generate_env_name(cls, name: str, cwd: str) -> str: name = name.lower() sanitized_name = re.sub(r'[ $`!*@"\\\r\n\t]', "_", name)[:42] h = hashlib.sha256(encode(cwd)).digest() @@ -874,7 +877,7 @@ class Env(object): An abstract Python environment. """ - def __init__(self, path, base=None): # type: (Path, Optional[Path]) -> None + def __init__(self, path: Path, base: Optional[Path] = None) -> None: self._is_windows = sys.platform == "win32" self._path = path @@ -893,59 +896,59 @@ def __init__(self, path, base=None): # type: (Path, Optional[Path]) -> None self._script_dirs = None @property - def path(self): # type: () -> Path + def path(self) -> Path: return self._path @property - def base(self): # type: () -> Path + def base(self) -> Path: return self._base @property - def version_info(self): # type: () -> Tuple[int] + def version_info(self) -> Tuple[int]: return tuple(self.marker_env["version_info"]) @property - def python_implementation(self): # type: () -> str + def python_implementation(self) -> str: return self.marker_env["platform_python_implementation"] @property - def python(self): # type: () -> str + def python(self) -> str: """ Path to current python executable """ return self._bin("python") @property - def marker_env(self): # type: () -> Dict[str, Any] + def marker_env(self) -> Dict[str, Any]: if self._marker_env is None: self._marker_env = self.get_marker_env() return self._marker_env @property - def pip(self): # type: () -> str + def pip(self) -> str: """ Path to current pip executable """ return self._bin("pip") @property - def platform(self): # type: () -> str + def platform(self) -> str: return sys.platform @property - def os(self): # type: () -> str + def os(self) -> str: return os.name @property - def pip_version(self): # type: () -> Version + def pip_version(self) -> Version: if self._pip_version is None: self._pip_version = self.get_pip_version() return self._pip_version @property - def site_packages(self): # type: () -> SitePackages + def site_packages(self) -> SitePackages: if self._site_packages is None: # we disable write checks if no user site exist fallbacks = [self.usersite] if self.usersite else [] @@ -955,24 +958,24 @@ def site_packages(self): # type: () -> SitePackages return self._site_packages @property - def usersite(self): # type: () -> Optional[Path] + def usersite(self) -> Optional[Path]: if "usersite" in self.paths: return Path(self.paths["usersite"]) @property - def userbase(self): # type: () -> Optional[Path] + def userbase(self) -> Optional[Path]: if "userbase" in self.paths: return Path(self.paths["userbase"]) @property - def purelib(self): # type: () -> Path + def purelib(self) -> Path: if self._purelib is None: self._purelib = Path(self.paths["purelib"]) return self._purelib @property - def platlib(self): # type: () -> Path + def platlib(self) -> Path: if self._platlib is None: if "platlib" in self.paths: self._platlib = Path(self.paths["platlib"]) @@ -981,7 +984,7 @@ def platlib(self): # type: () -> Path return self._platlib - def is_path_relative_to_lib(self, path): # type: (Path) -> bool + def is_path_relative_to_lib(self, path: Path) -> bool: for lib_path in [self.purelib, self.platlib]: try: path.relative_to(lib_path) @@ -992,25 +995,25 @@ def is_path_relative_to_lib(self, path): # type: (Path) -> bool return False @property - def sys_path(self): # type: () -> List[str] + def sys_path(self) -> List[str]: raise NotImplementedError() @property - def paths(self): # type: () -> Dict[str, str] + def paths(self) -> Dict[str, str]: if self._paths is None: self._paths = self.get_paths() return self._paths @property - def supported_tags(self): # type: () -> List[Tag] + def supported_tags(self) -> List[Tag]: if self._supported_tags is None: self._supported_tags = self.get_supported_tags() return self._supported_tags @classmethod - def get_base_prefix(cls): # type: () -> Path + def get_base_prefix(cls) -> Path: if hasattr(sys, "real_prefix"): return Path(sys.real_prefix) @@ -1019,47 +1022,47 @@ def get_base_prefix(cls): # type: () -> Path return Path(sys.prefix) - def get_version_info(self): # type: () -> Tuple[int] + def get_version_info(self) -> Tuple[int]: raise NotImplementedError() - def get_python_implementation(self): # type: () -> str + def get_python_implementation(self) -> str: raise NotImplementedError() - def get_marker_env(self): # type: () -> Dict[str, Any] + def get_marker_env(self) -> Dict[str, Any]: raise NotImplementedError() - def get_pip_command(self): # type: () -> List[str] + def get_pip_command(self) -> List[str]: raise NotImplementedError() - def get_supported_tags(self): # type: () -> List[Tag] + def get_supported_tags(self) -> List[Tag]: raise NotImplementedError() - def get_pip_version(self): # type: () -> Version + def get_pip_version(self) -> Version: raise NotImplementedError() - def get_paths(self): # type: () -> Dict[str, str] + def get_paths(self) -> Dict[str, str]: raise NotImplementedError() - def is_valid_for_marker(self, marker): # type: (BaseMarker) -> bool + def is_valid_for_marker(self, marker: BaseMarker) -> bool: return marker.validate(self.marker_env) - def is_sane(self): # type: () -> bool + def is_sane(self) -> bool: """ Checks whether the current environment is sane or not. """ return True - def run(self, bin, *args, **kwargs): # type: (str, *str, **Any) -> Union[str, int] + def run(self, bin: str, *args: str, **kwargs: Any) -> Union[str, int]: bin = self._bin(bin) cmd = [bin] + list(args) return self._run(cmd, **kwargs) - def run_pip(self, *args, **kwargs): # type: (*str, **Any) -> Union[int, str] + def run_pip(self, *args: str, **kwargs: Any) -> Union[int, str]: pip = self.get_pip_command() cmd = pip + list(args) return self._run(cmd, **kwargs) - def _run(self, cmd, **kwargs): # type: (List[str], **Any) -> Union[int, str] + def _run(self, cmd: List[str], **kwargs: Any) -> Union[int, str]: """ Run a command inside the Python environment. """ @@ -1093,9 +1096,7 @@ def _run(self, cmd, **kwargs): # type: (List[str], **Any) -> Union[int, str] return decode(output) - def execute( - self, bin, *args, **kwargs - ): # type: (str, *str, **Any) -> Optional[int] + def execute(self, bin: str, *args: str, **kwargs: Any) -> Optional[int]: bin = self._bin(bin) if not self._is_windows: @@ -1109,11 +1110,11 @@ def execute( exe.communicate() return exe.returncode - def is_venv(self): # type: () -> bool + def is_venv(self) -> bool: raise NotImplementedError() @property - def script_dirs(self): # type: () -> List[Path] + def script_dirs(self) -> List[Path]: if self._script_dirs is None: self._script_dirs = ( [Path(self.paths["scripts"])] @@ -1124,7 +1125,7 @@ def script_dirs(self): # type: () -> List[Path] self._script_dirs.append(self.userbase / self._script_dirs[0].name) return self._script_dirs - def _bin(self, bin): # type: (str) -> str + def _bin(self, bin: str) -> str: """ Return path to the given executable. """ @@ -1147,10 +1148,10 @@ def _bin(self, bin): # type: (str) -> str return str(bin_path) - def __eq__(self, other): # type: (Env) -> bool + def __eq__(self, other: "Env") -> bool: return other.__class__ == self.__class__ and other.path == self.path - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return '{}("{}")'.format(self.__class__.__name__, self._path) @@ -1160,25 +1161,25 @@ class SystemEnv(Env): """ @property - def python(self): # type: () -> str + def python(self) -> str: return sys.executable @property - def sys_path(self): # type: () -> List[str] + def sys_path(self) -> List[str]: return sys.path - def get_version_info(self): # type: () -> Tuple[int] + def get_version_info(self) -> Tuple[int]: return sys.version_info - def get_python_implementation(self): # type: () -> str + def get_python_implementation(self) -> str: return platform.python_implementation() - def get_pip_command(self): # type: () -> List[str] + def get_pip_command(self) -> List[str]: # If we're not in a venv, assume the interpreter we're running on # has a pip and use that return [sys.executable, "-m", "pip"] - def get_paths(self): # type: () -> Dict[str, str] + def get_paths(self) -> Dict[str, str]: # We can't use sysconfig.get_paths() because # on some distributions it does not return the proper paths # (those used by pip for instance). We go through distutils @@ -1207,10 +1208,10 @@ def get_paths(self): # type: () -> Dict[str, str] return paths - def get_supported_tags(self): # type: () -> List[Tag] + def get_supported_tags(self) -> List[Tag]: return list(sys_tags()) - def get_marker_env(self): # type: () -> Dict[str, Any] + def get_marker_env(self) -> Dict[str, Any]: if hasattr(sys, "implementation"): info = sys.implementation.version iver = "{0.major}.{0.minor}.{0.micro}".format(info) @@ -1243,12 +1244,12 @@ def get_marker_env(self): # type: () -> Dict[str, Any] "interpreter_version": interpreter_version(), } - def get_pip_version(self): # type: () -> Version + def get_pip_version(self) -> Version: from pip import __version__ return Version.parse(__version__) - def is_venv(self): # type: () -> bool + def is_venv(self) -> bool: return self._path != self._base @@ -1257,7 +1258,7 @@ class VirtualEnv(Env): A virtual Python environment. """ - def __init__(self, path, base=None): # type: (Path, Optional[Path]) -> None + def __init__(self, path: Path, base: Optional[Path] = None) -> None: super(VirtualEnv, self).__init__(path, base) # If base is None, it probably means this is @@ -1268,25 +1269,25 @@ def __init__(self, path, base=None): # type: (Path, Optional[Path]) -> None self._base = Path(self.run("python", "-", input_=GET_BASE_PREFIX).strip()) @property - def sys_path(self): # type: () -> List[str] + def sys_path(self) -> List[str]: output = self.run("python", "-", input_=GET_SYS_PATH) return json.loads(output) - def get_version_info(self): # type: () -> Tuple[int] + def get_version_info(self) -> Tuple[int]: output = self.run("python", "-", input_=GET_PYTHON_VERSION) return tuple([int(s) for s in output.strip().split(".")]) - def get_python_implementation(self): # type: () -> str + def get_python_implementation(self) -> str: return self.marker_env["platform_python_implementation"] - def get_pip_command(self): # type: () -> List[str] + def get_pip_command(self) -> List[str]: # We're in a virtualenv that is known to be sane, # so assume that we have a functional pip return [self._bin("pip")] - def get_supported_tags(self): # type: () -> List[Tag] + def get_supported_tags(self) -> List[Tag]: file_path = Path(packaging.tags.__file__) if file_path.suffix == ".pyc": # Python 2 @@ -1316,12 +1317,12 @@ def get_supported_tags(self): # type: () -> List[Tag] return [Tag(*t) for t in json.loads(output)] - def get_marker_env(self): # type: () -> Dict[str, Any] + def get_marker_env(self) -> Dict[str, Any]: output = self.run("python", "-", input_=GET_ENVIRONMENT_INFO) return json.loads(output) - def get_pip_version(self): # type: () -> Version + def get_pip_version(self) -> Version: output = self.run_pip("--version").strip() m = re.match("pip (.+?)(?: from .+)?$", output) if not m: @@ -1329,19 +1330,19 @@ def get_pip_version(self): # type: () -> Version return Version.parse(m.group(1)) - def get_paths(self): # type: () -> Dict[str, str] + def get_paths(self) -> Dict[str, str]: output = self.run("python", "-", input_=GET_PATHS) return json.loads(output) - def is_venv(self): # type: () -> bool + def is_venv(self) -> bool: return True - def is_sane(self): # type: () -> bool + def is_sane(self) -> bool: # A virtualenv is considered sane if both "python" and "pip" exist. return os.path.exists(self.python) and os.path.exists(self._bin("pip")) - def _run(self, cmd, **kwargs): # type: (List[str], **Any) -> Optional[int] + def _run(self, cmd: List[str], **kwargs: Any) -> Optional[int]: with self.temp_environ(): os.environ["PATH"] = self._updated_path() os.environ["VIRTUAL_ENV"] = str(self._path) @@ -1351,9 +1352,7 @@ def _run(self, cmd, **kwargs): # type: (List[str], **Any) -> Optional[int] return super(VirtualEnv, self)._run(cmd, **kwargs) - def execute( - self, bin, *args, **kwargs - ): # type: (str, *str, **Any) -> Optional[int] + def execute(self, bin: str, *args: str, **kwargs: Any) -> Optional[int]: with self.temp_environ(): os.environ["PATH"] = self._updated_path() os.environ["VIRTUAL_ENV"] = str(self._path) @@ -1364,7 +1363,7 @@ def execute( return super(VirtualEnv, self).execute(bin, *args, **kwargs) @contextmanager - def temp_environ(self): # type: () -> Iterator[None] + def temp_environ(self) -> Iterator[None]: environ = dict(os.environ) try: yield @@ -1372,18 +1371,18 @@ def temp_environ(self): # type: () -> Iterator[None] os.environ.clear() os.environ.update(environ) - def unset_env(self, key): # type: (str) -> None + def unset_env(self, key: str) -> None: if key in os.environ: del os.environ[key] - def _updated_path(self): # type: () -> str + def _updated_path(self) -> str: return os.pathsep.join([str(self._bin_dir), os.environ.get("PATH", "")]) class NullEnv(SystemEnv): def __init__( - self, path=None, base=None, execute=False - ): # type: (Path, Optional[Path], bool) -> None + self, path: Path = None, base: Optional[Path] = None, execute: bool = False + ) -> None: if path is None: path = Path(sys.prefix) @@ -1392,40 +1391,38 @@ def __init__( self._execute = execute self.executed = [] - def get_pip_command(self): # type: () -> List[str] + def get_pip_command(self) -> List[str]: return [self._bin("python"), "-m", "pip"] - def _run(self, cmd, **kwargs): # type: (List[str], **Any) -> int + def _run(self, cmd: List[str], **kwargs: Any) -> int: self.executed.append(cmd) if self._execute: return super(NullEnv, self)._run(cmd, **kwargs) - def execute( - self, bin, *args, **kwargs - ): # type: (str, *str, **Any) -> Optional[int] + def execute(self, bin: str, *args: str, **kwargs: Any) -> Optional[int]: self.executed.append([bin] + list(args)) if self._execute: return super(NullEnv, self).execute(bin, *args, **kwargs) - def _bin(self, bin): # type: (str) -> str + def _bin(self, bin: str) -> str: return bin class MockEnv(NullEnv): def __init__( self, - version_info=(3, 7, 0), # type: Tuple[int, int, int] - python_implementation="CPython", # type: str - platform="darwin", # type: str - os_name="posix", # type: str - is_venv=False, # type: bool - pip_version="19.1", # type: str - sys_path=None, # type: Optional[List[str]] - marker_env=None, # type: Dict[str, Any] - supported_tags=None, # type: List[Tag] - **kwargs, # type: Any + version_info: Tuple[int, int, int] = (3, 7, 0), + python_implementation: str = "CPython", + platform: str = "darwin", + os_name: str = "posix", + is_venv: bool = False, + pip_version: str = "19.1", + sys_path: Optional[List[str]] = None, + marker_env: Dict[str, Any] = None, + supported_tags: List[Tag] = None, + **kwargs: Any, ): super(MockEnv, self).__init__(**kwargs) @@ -1440,25 +1437,25 @@ def __init__( self._supported_tags = supported_tags @property - def platform(self): # type: () -> str + def platform(self) -> str: return self._platform @property - def os(self): # type: () -> str + def os(self) -> str: return self._os_name @property - def pip_version(self): # type: () -> Version + def pip_version(self) -> Version: return self._pip_version @property - def sys_path(self): # type: () -> List[str] + def sys_path(self) -> List[str]: if self._sys_path is None: return super(MockEnv, self).sys_path return self._sys_path - def get_marker_env(self): # type: () -> Dict[str, Any] + def get_marker_env(self) -> Dict[str, Any]: if self._mock_marker_env is not None: return self._mock_marker_env @@ -1475,5 +1472,5 @@ def get_marker_env(self): # type: () -> Dict[str, Any] return marker_env - def is_venv(self): # type: () -> bool + def is_venv(self) -> bool: return self._is_venv diff --git a/poetry/utils/exporter.py b/poetry/utils/exporter.py index 23f7f0f1503..c15d4508150 100644 --- a/poetry/utils/exporter.py +++ b/poetry/utils/exporter.py @@ -21,19 +21,19 @@ class Exporter(object): ACCEPTED_FORMATS = (FORMAT_REQUIREMENTS_TXT,) ALLOWED_HASH_ALGORITHMS = ("sha256", "sha384", "sha512") - def __init__(self, poetry): # type: (Poetry) -> None + def __init__(self, poetry: Poetry) -> None: self._poetry = poetry def export( self, - fmt, - cwd, - output, - with_hashes=True, - dev=False, - extras=None, - with_credentials=False, - ): # type: (str, Path, Union[IO, str], bool, bool, Optional[Union[bool, Sequence[str]]], bool) -> None + fmt: str, + cwd: Path, + output: Union[IO, str], + with_hashes: bool = True, + dev: bool = False, + extras: Optional[Union[bool, Sequence[str]]] = None, + with_credentials: bool = False, + ) -> None: if fmt not in self.ACCEPTED_FORMATS: raise ValueError("Invalid export format: {}".format(fmt)) @@ -48,13 +48,13 @@ def export( def _export_requirements_txt( self, - cwd, - output, - with_hashes=True, - dev=False, - extras=None, - with_credentials=False, - ): # type: (Path, Union[IO, str], bool, bool, Optional[Union[bool, Sequence[str]]], bool) -> None + cwd: Path, + output: Union[IO, str], + with_hashes: bool = True, + dev: bool = False, + extras: Optional[Union[bool, Sequence[str]]] = None, + with_credentials: bool = False, + ) -> None: indexes = set() content = "" dependency_lines = set() @@ -150,9 +150,7 @@ def _export_requirements_txt( self._output(content, cwd, output) - def _output( - self, content, cwd, output - ): # type: (str, Path, Union[IO, str]) -> None + def _output(self, content: str, cwd: Path, output: Union[IO, str]) -> None: decoded = decode(content) try: output.write(decoded) diff --git a/poetry/utils/extras.py b/poetry/utils/extras.py index cff3f5a1ff6..7c4361092a4 100644 --- a/poetry/utils/extras.py +++ b/poetry/utils/extras.py @@ -9,10 +9,10 @@ def get_extra_package_names( - packages, # type: Sequence[Package] - extras, # type: Mapping[str, List[str]] - extra_names, # type: Sequence[str] -): # type: (...) -> Iterator[str] + packages: Sequence[Package], + extras: Mapping[str, List[str]], + extra_names: Sequence[str], +) -> Iterator[str]: """ Returns all package names required by the given extras. @@ -37,7 +37,7 @@ def get_extra_package_names( # keep record of packages seen during recursion in order to avoid recursion error seen_package_names = set() - def _extra_packages(package_names): # type: (Iterable[str]) -> Iterator[str] + def _extra_packages(package_names: Iterable[str]) -> Iterator[str]: """Recursively find dependencies for packages names""" # for each extra pacakge name for package_name in package_names: diff --git a/poetry/utils/helpers.py b/poetry/utils/helpers.py index b7bc0429045..925ff2d8543 100644 --- a/poetry/utils/helpers.py +++ b/poetry/utils/helpers.py @@ -29,25 +29,25 @@ _canonicalize_regex = re.compile("[-_]+") -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)) -def _del_ro(action, name, exc): # type: (Callable, str, Exception) -> None +def _del_ro(action: Callable, name: str, exc: Exception) -> None: os.chmod(name, stat.S_IWRITE) os.remove(name) @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 @@ -55,7 +55,7 @@ def temporary_directory(*args, **kwargs): # type: (*Any, **Any) -> Iterator[str shutil.rmtree(name, onerror=_del_ro) -def get_cert(config, repository_name): # type: (Config, str) -> Optional[Path] +def get_cert(config: Config, repository_name: str) -> Optional[Path]: cert = config.get("certificates.{}.cert".format(repository_name)) if cert: return Path(cert) @@ -63,7 +63,7 @@ def get_cert(config, repository_name): # type: (Config, str) -> Optional[Path] return None -def get_client_cert(config, repository_name): # type: (Config, str) -> Optional[Path] +def get_client_cert(config: Config, repository_name: str) -> Optional[Path]: client_cert = config.get("certificates.{}.client-cert".format(repository_name)) if client_cert: return Path(client_cert) @@ -71,7 +71,7 @@ def get_client_cert(config, repository_name): # type: (Config, str) -> Optional return None -def _on_rm_error(func, path, exc_info): # type: (Callable, str, Exception) -> None +def _on_rm_error(func: Callable, path: str, exc_info: Exception) -> None: if not os.path.exists(path): return @@ -79,14 +79,14 @@ def _on_rm_error(func, path, exc_info): # type: (Callable, str, Exception) -> N func(path) -def safe_rmtree(path): # type: (str) -> None +def safe_rmtree(path: str) -> 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]) @@ -95,8 +95,11 @@ def merge_dicts(d1, d2): # type: (Dict, Dict) -> None def download_file( - url, dest, session=None, chunk_size=1024 -): # type: (str, str, Optional[requests.Session], int) -> None + url: str, + dest: str, + session: Optional[requests.Session] = None, + chunk_size: int = 1024, +) -> None: get = requests.get if not session else session.get with get(url, stream=True) as response: @@ -109,8 +112,8 @@ def download_file( def get_package_version_display_string( - package, root=None -): # type: (Package, Optional[Path]) -> str + package: Package, root: Optional[Path] = None +) -> str: if package.source_type in ["file", "directory"] and root: return "{} {}".format( package.version, @@ -120,11 +123,11 @@ def get_package_version_display_string( return package.full_pretty_version -def paths_csv(paths): # type: (List[Path]) -> str +def paths_csv(paths: List[Path]) -> str: return ", ".join('"{}"'.format(str(c)) for c in paths) -def is_dir_writable(path, create=False): # type: (Path, bool) -> bool +def is_dir_writable(path: Path, create: bool = False) -> bool: try: if not path.exists(): if not create: diff --git a/poetry/utils/password_manager.py b/poetry/utils/password_manager.py index e6cc11d65e2..bbf2a1a210a 100644 --- a/poetry/utils/password_manager.py +++ b/poetry/utils/password_manager.py @@ -6,7 +6,7 @@ if TYPE_CHECKING: - from poetry.config.config import Config # noqa + from poetry.config.config import Config logger = logging.getLogger(__name__) @@ -22,16 +22,16 @@ class KeyRingError(Exception): class KeyRing: - def __init__(self, namespace): # type: (str) -> None + def __init__(self, namespace: str) -> None: self._namespace = namespace self._is_available = True self._check() - def is_available(self): # type: () -> bool + def is_available(self) -> bool: return self._is_available - def get_password(self, name, username): # type: (str, str) -> Optional[str] + def get_password(self, name: str, username: str) -> Optional[str]: if not self.is_available(): return @@ -47,7 +47,7 @@ def get_password(self, name, username): # type: (str, str) -> Optional[str] "Unable to retrieve the password for {} from the key ring".format(name) ) - def set_password(self, name, username, password): # type: (str, str, str) -> None + def set_password(self, name: str, username: str, password: str) -> None: if not self.is_available(): return @@ -65,7 +65,7 @@ def set_password(self, name, username, password): # type: (str, str, str) -> No ) ) - def delete_password(self, name, username): # type: (str, str) -> None + def delete_password(self, name: str, username: str) -> None: if not self.is_available(): return @@ -81,10 +81,10 @@ def delete_password(self, name, username): # type: (str, str) -> None "Unable to delete the password for {} from the key ring".format(name) ) - def get_entry_name(self, name): # type: (str) -> str + def get_entry_name(self, name: str) -> str: return "{}-{}".format(self._namespace, name) - def _check(self): # type: () -> None + def _check(self) -> None: try: import keyring except Exception as e: @@ -120,12 +120,12 @@ def _check(self): # type: () -> None class PasswordManager: - def __init__(self, config): # type: ("Config") -> None + def __init__(self, config: "Config") -> None: self._config = config self._keyring = None @property - def keyring(self): # type: () -> KeyRing + def keyring(self) -> KeyRing: if self._keyring is None: self._keyring = KeyRing("poetry-repository") if not self._keyring.is_available(): @@ -135,7 +135,7 @@ def keyring(self): # type: () -> KeyRing return self._keyring - def set_pypi_token(self, name, token): # type: (str, str) -> None + def set_pypi_token(self, name: str, token: str) -> None: if not self.keyring.is_available(): self._config.auth_config_source.add_property( "pypi-token.{}".format(name), token @@ -143,13 +143,13 @@ def set_pypi_token(self, name, token): # type: (str, str) -> None else: self.keyring.set_password(name, "__token__", token) - def get_pypi_token(self, name): # type: (str) -> str + def get_pypi_token(self, name: str) -> str: if not self.keyring.is_available(): return self._config.get("pypi-token.{}".format(name)) return self.keyring.get_password(name, "__token__") - def delete_pypi_token(self, name): # type: (str) -> None + def delete_pypi_token(self, name: str) -> None: if not self.keyring.is_available(): return self._config.auth_config_source.remove_property( "pypi-token.{}".format(name) @@ -157,7 +157,7 @@ def delete_pypi_token(self, name): # type: (str) -> None self.keyring.delete_password(name, "__token__") - def get_http_auth(self, name): # type: (str) -> Optional[Dict[str, str]] + def get_http_auth(self, name: str) -> Optional[Dict[str, str]]: auth = self._config.get("http-basic.{}".format(name)) if not auth: username = self._config.get("http-basic.{}.username".format(name)) @@ -174,9 +174,7 @@ def get_http_auth(self, name): # type: (str) -> Optional[Dict[str, str]] "password": password, } - def set_http_password( - self, name, username, password - ): # type: (str, str, str) -> None + def set_http_password(self, name: str, username: str, password: str) -> None: auth = {"username": username} if not self.keyring.is_available(): @@ -186,7 +184,7 @@ def set_http_password( self._config.auth_config_source.add_property("http-basic.{}".format(name), auth) - def delete_http_password(self, name): # type: (str) -> None + def delete_http_password(self, name: str) -> None: auth = self.get_http_auth(name) if not auth or "username" not in auth: return diff --git a/poetry/utils/setup_reader.py b/poetry/utils/setup_reader.py index 48d511f9252..78f7a96af46 100644 --- a/poetry/utils/setup_reader.py +++ b/poetry/utils/setup_reader.py @@ -30,8 +30,8 @@ class SetupReader(object): @classmethod def read_from_directory( - cls, directory - ): # type: (Union[str, Path]) -> Dict[str, Union[List, Dict]] + cls, directory: Union[str, Path] + ) -> Dict[str, Union[List, Dict]]: if isinstance(directory, str): directory = Path(directory) @@ -51,9 +51,7 @@ def read_from_directory( return result - def read_setup_py( - self, filepath - ): # type: (Union[str, Path]) -> Dict[str, Union[List, Dict]] + def read_setup_py(self, filepath: Union[str, Path]) -> Dict[str, Union[List, Dict]]: if isinstance(filepath, str): filepath = Path(filepath) @@ -80,8 +78,8 @@ def read_setup_py( return result def read_setup_cfg( - self, filepath - ): # type: (Union[str, Path]) -> Dict[str, Union[List, Dict]] + self, filepath: Union[str, Path] + ) -> Dict[str, Union[List, Dict]]: parser = ConfigParser() parser.read(str(filepath)) @@ -129,8 +127,8 @@ def read_setup_cfg( } def _find_setup_call( - self, elements - ): # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]] + self, elements: List[Any] + ) -> Tuple[Optional[ast.Call], Optional[List[Any]]]: funcdefs = [] for i, element in enumerate(elements): if isinstance(element, ast.If) and i == len(elements) - 1: @@ -178,8 +176,8 @@ def _find_setup_call( return self._find_sub_setup_call(funcdefs) def _find_sub_setup_call( - self, elements - ): # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]] + self, elements: List[Any] + ) -> Tuple[Optional[ast.Call], Optional[List[Any]]]: for element in elements: if not isinstance(element, (ast.FunctionDef, ast.If)): continue @@ -194,9 +192,7 @@ def _find_sub_setup_call( return None, None - def _find_install_requires( - self, call, body - ): # type: (ast.Call, Iterable[Any]) -> List[str] + def _find_install_requires(self, call: ast.Call, body: Iterable[Any]) -> List[str]: install_requires = [] value = self._find_in_call(call, "install_requires") if value is None: @@ -237,8 +233,8 @@ def _find_install_requires( return install_requires def _find_extras_require( - self, call, body - ): # type: (ast.Call, Iterable[Any]) -> Dict[str, List] + self, call: ast.Call, body: Iterable[Any] + ) -> Dict[str, List]: extras_require = {} value = self._find_in_call(call, "extras_require") if value is None: @@ -289,8 +285,8 @@ def _find_extras_require( return extras_require def _find_single_string( - self, call, body, name - ): # type: (ast.Call, List[Any], str) -> Optional[str] + self, call: ast.Call, body: List[Any], name: str + ) -> Optional[str]: value = self._find_in_call(call, name) if value is None: # Trying to find in kwargs @@ -325,12 +321,12 @@ def _find_single_string( if variable is not None and isinstance(variable, ast.Str): return variable.s - def _find_in_call(self, call, name): # type: (ast.Call, str) -> Optional[Any] + def _find_in_call(self, call: ast.Call, name: str) -> Optional[Any]: for keyword in call.keywords: if keyword.arg == name: return keyword.value - def _find_call_kwargs(self, call): # type: (ast.Call) -> Optional[Any] + def _find_call_kwargs(self, call: ast.Call) -> Optional[Any]: kwargs = None for keyword in call.keywords: if keyword.arg is None: @@ -338,9 +334,7 @@ def _find_call_kwargs(self, call): # type: (ast.Call) -> Optional[Any] return kwargs - def _find_variable_in_body( - self, body, name - ): # type: (Iterable[Any], str) -> Optional[Any] + def _find_variable_in_body(self, body: Iterable[Any], name: str) -> Optional[Any]: found = None for elem in body: if found: @@ -357,8 +351,8 @@ def _find_variable_in_body( return elem.value def _find_in_dict( - self, dict_, name - ): # type: (Union[ast.Dict, ast.Call], str) -> Optional[Any] + self, dict_: Union[ast.Dict, ast.Call], name: str + ) -> Optional[Any]: for key, val in zip(dict_.keys, dict_.values): if isinstance(key, ast.Str) and key.s == name: return val diff --git a/poetry/utils/shell.py b/poetry/utils/shell.py index a0f539fdece..dbf1db3fb8c 100644 --- a/poetry/utils/shell.py +++ b/poetry/utils/shell.py @@ -22,20 +22,20 @@ class Shell: _shell = None - def __init__(self, name, path): # type: (str, str) -> None + def __init__(self, name: str, path: str) -> None: self._name = name self._path = path @property - def name(self): # type: () -> str + def name(self) -> str: return self._name @property - def path(self): # type: () -> str + def path(self) -> str: return self._path @classmethod - def get(cls): # type: () -> Shell + def get(cls) -> "Shell": """ Retrieve the current shell. """ @@ -61,7 +61,7 @@ def get(cls): # type: () -> Shell return cls._shell - def activate(self, env): # type: (VirtualEnv) -> None + def activate(self, env: VirtualEnv) -> None: if WINDOWS: return env.execute(self.path) @@ -79,7 +79,7 @@ def activate(self, env): # type: (VirtualEnv) -> None activate_path = env.path / bin_dir / activate_script c.sendline("{} {}".format(self._get_source_command(), activate_path)) - def resize(sig, data): # type: (Any, Any) -> None + def resize(sig: Any, data: Any) -> None: terminal = Terminal() c.setwinsize(terminal.height, terminal.width) @@ -91,7 +91,7 @@ def resize(sig, data): # type: (Any, Any) -> None sys.exit(c.exitstatus) - def _get_activate_script(self): # type: () -> str + def _get_activate_script(self) -> str: if "fish" == self._name: suffix = ".fish" elif "csh" == self._name: @@ -103,7 +103,7 @@ def _get_activate_script(self): # type: () -> str return "activate" + suffix - def _get_source_command(self): # type: () -> str + def _get_source_command(self) -> str: if "fish" == self._name: return "source" elif "csh" == self._name: @@ -113,5 +113,5 @@ def _get_source_command(self): # type: () -> str return "." - def __repr__(self): # type: () -> str + def __repr__(self) -> str: return '{}("{}", "{}")'.format(self.__class__.__name__, self._name, self._path) diff --git a/poetry/version/version_selector.py b/poetry/version/version_selector.py index 350e3ec7ca5..1c7cc254e04 100644 --- a/poetry/version/version_selector.py +++ b/poetry/version/version_selector.py @@ -7,20 +7,20 @@ if TYPE_CHECKING: - from poetry.repositories import Pool # noqa + from poetry.repositories import Pool class VersionSelector(object): - def __init__(self, pool): # type: ("Pool") -> None + def __init__(self, pool: "Pool") -> None: self._pool = pool def find_best_candidate( self, - package_name, # type: str - target_package_version=None, # type: Optional[str] - allow_prereleases=False, # type: bool - source=None, # type: Optional[str] - ): # type: (...) -> Union[Package, bool] + package_name: str, + target_package_version: Optional[str] = None, + allow_prereleases: bool = False, + source: Optional[str] = None, + ) -> Union[Package, bool]: """ Given a package name and optional version, returns the latest Package that matches @@ -58,12 +58,12 @@ def find_best_candidate( return False return package - def find_recommended_require_version(self, package): # type: (Package) -> str + def find_recommended_require_version(self, package: Package) -> str: version = package.version return self._transform_version(version.text, package.pretty_version) - def _transform_version(self, version, pretty_version): # type: (str, str) -> str + def _transform_version(self, version: str, pretty_version: str) -> str: try: parsed = Version.parse(version) parts = [parsed.major, parsed.minor, parsed.patch] diff --git a/tests/conftest.py b/tests/conftest.py index d2773c95d56..9481c31457b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -34,19 +34,19 @@ class Config(BaseConfig): - def get(self, setting_name, default=None): # type: (str, Any) -> Any + def get(self, setting_name: str, default: Any = None) -> Any: self.merge(self._config_source.config) self.merge(self._auth_config_source.config) return super(Config, self).get(setting_name, default=default) - def raw(self): # type: () -> Dict[str, Any] + def raw(self) -> Dict[str, Any]: self.merge(self._config_source.config) self.merge(self._auth_config_source.config) return super(Config, self).raw() - def all(self): # type: () -> Dict[str, Any] + def all(self) -> Dict[str, Any]: self.merge(self._config_source.config) self.merge(self._auth_config_source.config) diff --git a/tests/console/commands/env/helpers.py b/tests/console/commands/env/helpers.py index 0c65e4f14fb..4f8b7a4d818 100644 --- a/tests/console/commands/env/helpers.py +++ b/tests/console/commands/env/helpers.py @@ -6,8 +6,8 @@ def build_venv( - path, executable=None, flags=None -): # type: (Union[Path,str], Optional[str], bool) -> () + path: Union[Path, str], executable: Optional[str] = None, flags: bool = None +) -> (): Path(path).mkdir(parents=True, exist_ok=True) diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index 442a0123284..087c3f2088b 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -15,7 +15,7 @@ @pytest.fixture -def source_dir(tmp_path): # type: (...) -> Path +def source_dir(tmp_path) -> Path: cwd = os.getcwd() try: diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index 51e56d155ca..f3ed49bb76f 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -7,7 +7,7 @@ @pytest.fixture -def source_dir(tmp_path): # type: (Path) -> Path +def source_dir(tmp_path: Path) -> Path: yield Path(tmp_path.as_posix()) diff --git a/tests/inspection/test_info.py b/tests/inspection/test_info.py index a1edea4a39b..3a15e605a6f 100644 --- a/tests/inspection/test_info.py +++ b/tests/inspection/test_info.py @@ -21,22 +21,22 @@ def pep517_metadata_mock(): @pytest.fixture -def demo_sdist(): # type: () -> Path +def demo_sdist() -> Path: return FIXTURE_DIR_BASE / "distributions" / "demo-0.1.0.tar.gz" @pytest.fixture -def demo_wheel(): # type: () -> Path +def demo_wheel() -> Path: return FIXTURE_DIR_BASE / "distributions" / "demo-0.1.0-py2.py3-none-any.whl" @pytest.fixture -def source_dir(tmp_path): # type: (Path) -> Path +def source_dir(tmp_path: Path) -> Path: yield Path(tmp_path.as_posix()) @pytest.fixture -def demo_setup(source_dir): # type: (Path) -> Path +def demo_setup(source_dir: Path) -> Path: setup_py = source_dir / "setup.py" setup_py.write_text( decode( @@ -50,7 +50,7 @@ def demo_setup(source_dir): # type: (Path) -> Path @pytest.fixture -def demo_setup_cfg(source_dir): # type: (Path) -> Path +def demo_setup_cfg(source_dir: Path) -> Path: setup_cfg = source_dir / "setup.cfg" setup_cfg.write_text( decode( @@ -69,7 +69,7 @@ def demo_setup_cfg(source_dir): # type: (Path) -> Path @pytest.fixture -def demo_setup_complex(source_dir): # type: (Path) -> Path +def demo_setup_complex(source_dir: Path) -> Path: setup_py = source_dir / "setup.py" setup_py.write_text( decode( @@ -83,7 +83,7 @@ def demo_setup_complex(source_dir): # type: (Path) -> Path @pytest.fixture -def demo_setup_complex_pep517_legacy(demo_setup_complex): # type: (Path) -> Path +def demo_setup_complex_pep517_legacy(demo_setup_complex: Path) -> Path: pyproject_toml = demo_setup_complex / "pyproject.toml" pyproject_toml.write_text( decode("[build-system]\n" 'requires = ["setuptools", "wheel"]') @@ -91,7 +91,7 @@ def demo_setup_complex_pep517_legacy(demo_setup_complex): # type: (Path) -> Pat yield demo_setup_complex -def demo_check_info(info, requires_dist=None): # type: (PackageInfo, Set[str]) -> None +def demo_check_info(info: PackageInfo, requires_dist: Set[str] = None) -> None: assert info.name == "demo" assert info.version == "0.1.0" assert info.requires_dist diff --git a/tests/repositories/test_installed_repository.py b/tests/repositories/test_installed_repository.py index 5097d3bc4a0..3fcb7449cf7 100644 --- a/tests/repositories/test_installed_repository.py +++ b/tests/repositories/test_installed_repository.py @@ -47,12 +47,12 @@ def sys_path(self): @pytest.fixture -def env(): # type: () -> MockEnv +def env() -> MockEnv: return MockEnv(path=ENV_DIR) @pytest.fixture -def repository(mocker, env): # type: (MockFixture, MockEnv) -> InstalledRepository +def repository(mocker: MockFixture, env: MockEnv) -> InstalledRepository: mocker.patch( "poetry.utils._compat.metadata.Distribution.discover", return_value=INSTALLED_RESULTS, @@ -73,8 +73,8 @@ def repository(mocker, env): # type: (MockFixture, MockEnv) -> InstalledReposit def get_package_from_repository( - name, repository -): # type: (str, InstalledRepository) -> Optional[Package] + name: str, repository: InstalledRepository +) -> Optional[Package]: for pkg in repository.packages: if pkg.name == name: return pkg diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py index da810307a9f..dc65d6dce23 100644 --- a/tests/utils/test_env.py +++ b/tests/utils/test_env.py @@ -118,8 +118,8 @@ def test_env_get_venv_with_venv_folder_present( def build_venv( - path, executable=None, flags=None -): # type: (Union[Path,str], Optional[str], bool) -> () + path: Union[Path, str], executable: Optional[str] = None, flags: bool = None +) -> (): os.mkdir(str(path)) From 73437a847e5dd7e23e1dff2774637c2437d40c7f Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Mon, 8 Feb 2021 15:24:10 +0100 Subject: [PATCH 46/54] Fixed propagation of markers to dependencies. Fixes #3254 --- poetry/packages/locker.py | 6 +- tests/utils/test_exporter.py | 196 ++++++++++++++++++++++++++++++++++- 2 files changed, 198 insertions(+), 4 deletions(-) diff --git a/poetry/packages/locker.py b/poetry/packages/locker.py index 48d68e950ac..f06d36d6caf 100644 --- a/poetry/packages/locker.py +++ b/poetry/packages/locker.py @@ -269,9 +269,9 @@ def __walk_dependency_level( if key not in nested_dependencies: nested_dependencies[key] = requirement else: - nested_dependencies[key].marker = nested_dependencies[ - key - ].marker.intersect(requirement.marker) + nested_dependencies[key].marker = nested_dependencies[key].marker.union( + requirement.marker + ) return cls.__walk_dependency_level( dependencies=next_level_dependencies, diff --git a/tests/utils/test_exporter.py b/tests/utils/test_exporter.py index 5fed10d1c99..5491ed7e3fa 100644 --- a/tests/utils/test_exporter.py +++ b/tests/utils/test_exporter.py @@ -175,6 +175,200 @@ def test_exporter_can_export_requirements_txt_with_standard_packages_and_markers assert expected == content +def test_exporter_can_export_requirements_txt_poetry(tmp_dir, poetry): + """Regression test for #3254""" + + poetry.locker.mock_lock_data( + { + "package": [ + { + "name": "poetry", + "version": "1.1.4", + "category": "main", + "optional": False, + "python-versions": "*", + "dependencies": {"keyring": "*"}, + }, + { + "name": "junit-xml", + "version": "1.9", + "category": "main", + "optional": False, + "python-versions": "*", + "dependencies": {"six": "*"}, + }, + { + "name": "keyring", + "version": "21.8.0", + "category": "main", + "optional": False, + "python-versions": "*", + "dependencies": { + "SecretStorage": { + "version": "*", + "markers": "sys_platform == 'linux'", + } + }, + }, + { + "name": "secretstorage", + "version": "3.3.0", + "category": "main", + "optional": False, + "python-versions": "*", + "dependencies": {"cryptography": "*"}, + }, + { + "name": "cryptography", + "version": "3.2", + "category": "main", + "optional": False, + "python-versions": "*", + "dependencies": {"six": "*"}, + }, + { + "name": "six", + "version": "1.15.0", + "category": "main", + "optional": False, + "python-versions": "*", + }, + ], + "metadata": { + "python-versions": "*", + "content-hash": "123456789", + "hashes": { + "poetry": [], + "keyring": [], + "secretstorage": [], + "cryptography": [], + "six": [], + "junit-xml": [], + }, + }, + } + ) + set_package_requires( + poetry, skip={"keyring", "secretstorage", "cryptography", "six"} + ) + + exporter = Exporter(poetry) + + exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") + + with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: + content = f.read() + + # The dependency graph: + # junit-xml 1.9 Creates JUnit XML test result documents that can be read by tools such as Jenkins + # └── six * + # poetry 1.1.4 Python dependency management and packaging made easy. + # ├── keyring >=21.2.0,<22.0.0 + # │ ├── importlib-metadata >=1 + # │ │ └── zipp >=0.5 + # │ ├── jeepney >=0.4.2 + # │ ├── pywin32-ctypes <0.1.0 || >0.1.0,<0.1.1 || >0.1.1 + # │ └── secretstorage >=3.2 -- On linux only + # │ ├── cryptography >=2.0 + # │ │ └── six >=1.4.1 + # │ └── jeepney >=0.6 (circular dependency aborted here) + expected = { + "poetry": dependency_from_pep_508("poetry==1.1.4"), + "junit-xml": dependency_from_pep_508("junit-xml==1.9"), + "keyring": dependency_from_pep_508("keyring==21.8.0"), + "secretstorage": dependency_from_pep_508( + "secretstorage==3.3.0; sys_platform=='linux'" + ), + "cryptography": dependency_from_pep_508( + "cryptography==3.2; sys_platform=='linux'" + ), + "six": dependency_from_pep_508("six==1.15.0"), + } + + for line in content.strip().split("\n"): + dependency = dependency_from_pep_508(line) + assert dependency.name in expected + expected_dependency = expected.pop(dependency.name) + assert dependency == expected_dependency + print(dependency.marker) + print(expected_dependency.marker) + assert dependency.marker == expected_dependency.marker + + +def test_exporter_can_export_requirements_txt_pyinstaller(tmp_dir, poetry): + """Regression test for #3254""" + + poetry.locker.mock_lock_data( + { + "package": [ + { + "name": "pyinstaller", + "version": "4.0", + "category": "main", + "optional": False, + "python-versions": "*", + "dependencies": { + "altgraph": "*", + "macholib": { + "version": "*", + "markers": "sys_platform == 'darwin'", + }, + }, + }, + { + "name": "altgraph", + "version": "0.17", + "category": "main", + "optional": False, + "python-versions": "*", + }, + { + "name": "macholib", + "version": "1.8", + "category": "main", + "optional": False, + "python-versions": "*", + "dependencies": {"altgraph": ">=0.15"}, + }, + ], + "metadata": { + "python-versions": "*", + "content-hash": "123456789", + "hashes": {"pyinstaller": [], "altgraph": [], "macholib": []}, + }, + } + ) + set_package_requires(poetry, skip={"altgraph", "macholib"}) + + exporter = Exporter(poetry) + + exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") + + with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: + content = f.read() + + # Rationale for the results: + # * PyInstaller has an explicit dependency on altgraph, so it must always be installed. + # * PyInstaller requires macholib on Darwin, which in turn requires altgraph. + # The dependency graph: + # pyinstaller 4.0 PyInstaller bundles a Python application and all its dependencies into a single package. + # ├── altgraph * + # ├── macholib >=1.8 -- only on Darwin + # │ └── altgraph >=0.15 + expected = { + "pyinstaller": dependency_from_pep_508("pyinstaller==4.0"), + "altgraph": dependency_from_pep_508("altgraph==0.17"), + "macholib": dependency_from_pep_508("macholib==1.8; sys_platform == 'darwin'"), + } + + for line in content.strip().split("\n"): + dependency = dependency_from_pep_508(line) + assert dependency.name in expected + expected_dependency = expected.pop(dependency.name) + assert dependency == expected_dependency + assert dependency.marker == expected_dependency.marker + + def test_exporter_can_export_requirements_txt_with_nested_packages_and_markers( tmp_dir, poetry ): @@ -241,7 +435,7 @@ def test_exporter_can_export_requirements_txt_with_nested_packages_and_markers( "c==7.8.9; sys_platform == 'win32' and python_version < '3.7'" ), "d": dependency_from_pep_508( - "d==0.0.1; python_version < '3.7' and platform_system == 'Windows' and sys_platform == 'win32'" + "d==0.0.1; platform_system == 'Windows' and python_version < '3.7' or sys_platform == 'win32' and python_version < '3.7'" ), } From ae60ac58d4b52a2ccf6595a0c9d8e6d8d6eee090 Mon Sep 17 00:00:00 2001 From: 0xflotus <0xflotus@gmail.com> Date: Wed, 10 Feb 2021 00:53:55 +0100 Subject: [PATCH 47/54] docs: fix typo in basic usage --- docs/docs/basic-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/basic-usage.md b/docs/docs/basic-usage.md index eed4817379f..c9805bb3996 100644 --- a/docs/docs/basic-usage.md +++ b/docs/docs/basic-usage.md @@ -106,7 +106,7 @@ To deactivate the virtual environment without leaving the shell use `deactivate` that an activated virtual environment remains active after the Poetry command has completed execution. - Therefore, Poetry has to create a sub-shell with the virtual envrionment activated + Therefore, Poetry has to create a sub-shell with the virtual environment activated in order for the subsequent commands to run from within the virtual environment. From bc3ad7db771bfeabf1fcea6c9b402c279ff54866 Mon Sep 17 00:00:00 2001 From: pwoolvett Date: Tue, 9 Feb 2021 20:58:10 -0300 Subject: [PATCH 48/54] fix(shell): quote path before activating env Resolves: #3630 --- poetry/utils/shell.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/poetry/utils/shell.py b/poetry/utils/shell.py index dbf1db3fb8c..544be053c4c 100644 --- a/poetry/utils/shell.py +++ b/poetry/utils/shell.py @@ -65,6 +65,8 @@ def activate(self, env: VirtualEnv) -> None: if WINDOWS: return env.execute(self.path) + import shlex + terminal = Terminal() with env.temp_environ(): c = pexpect.spawn( @@ -77,7 +79,9 @@ def activate(self, env: VirtualEnv) -> None: activate_script = self._get_activate_script() bin_dir = "Scripts" if WINDOWS else "bin" activate_path = env.path / bin_dir / activate_script - c.sendline("{} {}".format(self._get_source_command(), activate_path)) + c.sendline( + "{} {}".format(self._get_source_command(), shlex.quote(str(activate_path))) + ) def resize(sig: Any, data: Any) -> None: terminal = Terminal() From 25771b4d27e6ba4926f52aea876a7d8dd35f7574 Mon Sep 17 00:00:00 2001 From: Aykut Yilmaz Date: Wed, 10 Feb 2021 01:01:41 +0100 Subject: [PATCH 49/54] doc: fix changelog typo for release 1.1.4 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 168944d8f4b..e7fe3d6e588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1084,7 +1084,7 @@ Initial release [Unreleased]: https://github.com/python-poetry/poetry/compare/1.1.4...master -[1.1.3]: https://github.com/python-poetry/poetry/compare/1.1.4 +[1.1.4]: https://github.com/python-poetry/poetry/compare/1.1.4 [1.1.3]: https://github.com/python-poetry/poetry/compare/1.1.3 [1.1.2]: https://github.com/python-poetry/poetry/releases/tag/1.1.2 [1.1.1]: https://github.com/python-poetry/poetry/releases/tag/1.1.1 From 6c92d42af70e1ca60f8e55599866ca8e609659ad Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Wed, 10 Feb 2021 02:04:02 +0100 Subject: [PATCH 50/54] Bump pkginfo to ^1.5 Fixes #3362 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 562bfe68901..1f6ec062e7b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ requests = "^2.18" cachy = "^0.3.0" requests-toolbelt = "^0.9.1" cachecontrol = { version = "^0.12.4", extras = ["filecache"] } -pkginfo = "^1.4" +pkginfo = "^1.5" html5lib = "^1.0" shellingham = "^1.1" tomlkit = ">=0.7.0,<1.0.0" From 6f70c3e257cdd97d41b82ba26267ee753230a39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Wed, 10 Feb 2021 22:16:37 +0100 Subject: [PATCH 51/54] Upgrade poetry-core to 1.0.2 --- poetry.lock | 8 ++++---- poetry/mixology/incompatibility.py | 7 ++++++- pyproject.toml | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6a04096460f..c576b9e1eff 100644 --- a/poetry.lock +++ b/poetry.lock @@ -352,7 +352,7 @@ dev = ["pre-commit", "tox"] [[package]] name = "poetry-core" -version = "1.0.0" +version = "1.0.2" description = "Poetry PEP 517 Build Backend" category = "main" optional = false @@ -668,7 +668,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 = "c495920c853f794d4046d2d4cc47410ea076d4647e54fd3364e46051d5f18da3" +content-hash = "3061627bdc17958f2b0d1dfc86e448e167ff3e97639e6f3c8f67bf0b06d9995c" [metadata.files] appdirs = [ @@ -901,8 +901,8 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] poetry-core = [ - {file = "poetry-core-1.0.0.tar.gz", hash = "sha256:6a664ff389b9f45382536f8fa1611a0cb4d2de7c5a5c885db1f0c600cd11fbd5"}, - {file = "poetry_core-1.0.0-py2.py3-none-any.whl", hash = "sha256:769288e0e1b88dfcceb3185728f0b7388b26d5f93d6c22d2dcae372da51d200d"}, + {file = "poetry-core-1.0.2.tar.gz", hash = "sha256:ff505d656a6cf40ffbf84393d8b5bf37b78523a15def3ac473b6fad74261ee71"}, + {file = "poetry_core-1.0.2-py2.py3-none-any.whl", hash = "sha256:ee0ed4164440eeab27d1b01bc7b9b3afdc3124f68d4ea28d0821a402a9c7c044"}, ] pre-commit = [ {file = "pre_commit-2.9.0-py2.py3-none-any.whl", hash = "sha256:4aee0db4808fa48d2458cedd5b9a084ef24dda1a0fa504432a11977a4d1cfd0a"}, diff --git a/poetry/mixology/incompatibility.py b/poetry/mixology/incompatibility.py index 615c0cec741..0a848d1374c 100644 --- a/poetry/mixology/incompatibility.py +++ b/poetry/mixology/incompatibility.py @@ -454,7 +454,12 @@ def _terse(self, term: Term, allow_every: bool = False) -> str: if allow_every and term.constraint.is_any(): return "every version of {}".format(term.dependency.complete_name) - return str(term.dependency) + if term.dependency.is_root: + return term.dependency.pretty_name + + return "{} ({})".format( + term.dependency.pretty_name, term.dependency.pretty_constraint + ) def _single_term_where(self, callable: callable) -> Optional[Term]: found = None diff --git a/pyproject.toml b/pyproject.toml index 1f6ec062e7b..716fd2f27aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.6" -poetry-core = "^1.0.0" +poetry-core = "^1.0.2" cleo = "^1.0.0a1" crashtest = "^0.3.0" requests = "^2.18" From 74ec16905d74235f028a5e03686df5459a9bdcee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 12 Feb 2021 11:22:29 +0100 Subject: [PATCH 52/54] Update Cirrus config --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index daf4efc9203..e771b621abf 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,5 +1,5 @@ freebsd_instance: - image_family: freebsd-12-1-snap + image_family: freebsd-12-2 test_task: name: "Tests / FreeBSD / " From 6eaa2984d9fb51a837c4ab11643f4dd9d7828d4d Mon Sep 17 00:00:00 2001 From: Paul Sanders Date: Sat, 13 Feb 2021 14:58:00 -0500 Subject: [PATCH 53/54] Refactoring uploader into a fixture (#3679) * Relates-to: #3155 converting duplicted code to fixture --- tests/console/commands/debug/test_resolve.py | 2 +- tests/publishing/test_uploader.py | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/console/commands/debug/test_resolve.py b/tests/console/commands/debug/test_resolve.py index f3f0db24c6e..9af3e0010d8 100644 --- a/tests/console/commands/debug/test_resolve.py +++ b/tests/console/commands/debug/test_resolve.py @@ -10,7 +10,7 @@ def tester(command_tester_factory): @pytest.fixture(autouse=True) -def _add_packages(repo): +def __add_packages(repo): cachy020 = get_package("cachy", "0.2.0") cachy020.add_dependency(Factory.create_dependency("msgpack-python", ">=0.5 <0.6")) diff --git a/tests/publishing/test_uploader.py b/tests/publishing/test_uploader.py index 8f5ec1b9e7a..e4deb372884 100644 --- a/tests/publishing/test_uploader.py +++ b/tests/publishing/test_uploader.py @@ -16,9 +16,13 @@ def project(name): return fixtures_dir / name -def test_uploader_properly_handles_400_errors(http): +@pytest.fixture +def uploader(): + return Uploader(Factory().create_poetry(project("simple_project")), NullIO()) + + +def test_uploader_properly_handles_400_errors(http, uploader): http.register_uri(http.POST, "https://foo.com", status=400, body="Bad request") - uploader = Uploader(Factory().create_poetry(project("simple_project")), NullIO()) with pytest.raises(UploadError) as e: uploader.upload("https://foo.com") @@ -26,9 +30,8 @@ def test_uploader_properly_handles_400_errors(http): assert "HTTP Error 400: Bad Request" == str(e.value) -def test_uploader_properly_handles_403_errors(http): +def test_uploader_properly_handles_403_errors(http, uploader): http.register_uri(http.POST, "https://foo.com", status=403, body="Unauthorized") - uploader = Uploader(Factory().create_poetry(project("simple_project")), NullIO()) with pytest.raises(UploadError) as e: uploader.upload("https://foo.com") @@ -36,9 +39,8 @@ def test_uploader_properly_handles_403_errors(http): assert "HTTP Error 403: Forbidden" == str(e.value) -def test_uploader_properly_handles_301_redirects(http): +def test_uploader_properly_handles_301_redirects(http, uploader): http.register_uri(http.POST, "https://foo.com", status=301, body="Redirect") - uploader = Uploader(Factory().create_poetry(project("simple_project")), NullIO()) with pytest.raises(UploadError) as e: uploader.upload("https://foo.com") @@ -48,12 +50,11 @@ def test_uploader_properly_handles_301_redirects(http): ) -def test_uploader_registers_for_appropriate_400_errors(mocker, http): +def test_uploader_registers_for_appropriate_400_errors(mocker, http, uploader): register = mocker.patch("poetry.publishing.uploader.Uploader._register") http.register_uri( http.POST, "https://foo.com", status=400, body="No package was ever registered" ) - uploader = Uploader(Factory().create_poetry(project("simple_project")), NullIO()) with pytest.raises(UploadError): uploader.upload("https://foo.com") From adc3cde3473e85eb744f1f73d937439a681ae365 Mon Sep 17 00:00:00 2001 From: Akhilesh Raju Date: Sat, 6 Mar 2021 00:23:51 -0800 Subject: [PATCH 54/54] Doc: Update "extras" section in pyproject.toml file Resolves: #1076 --- docs/docs/pyproject.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/docs/pyproject.md b/docs/docs/pyproject.md index 14221b8b03a..bdcc6e0bc49 100644 --- a/docs/docs/pyproject.md +++ b/docs/docs/pyproject.md @@ -243,15 +243,32 @@ mysqlclient = { version = "^1.3", optional = true } [tool.poetry.extras] mysql = ["mysqlclient"] pgsql = ["psycopg2"] +databases = ["mysqlclient", "psycopg2"] ``` -When installing packages, you can specify extras by using the `-E|--extras` option: +When installing packages with Poetry, you can specify extras by using the `-E|--extras` option: ```bash poetry install --extras "mysql pgsql" poetry install -E mysql -E pgsql ``` +When installing or specifying Poetry-built packages, the extras defined in this section can be activated +as described in [PEP 508](https://www.python.org/dev/peps/pep-0508/#extras). + +For example, when installing the package using `pip`, the dependencies required by +the `databases` extra can be installed as shown below. + +```bash +pip install awesome[databases] +``` + +!!!note + + The dependencies specified for each `extra` must already be defined as project dependencies. + Dependencies listed in the `dev-dependencies` section cannot be specified as extras. + + ## `plugins` Poetry supports arbitrary plugins which work similarly to