test_locker.py (poetry-1.1.15) | : | test_locker.py (poetry-1.2.0) | ||
---|---|---|---|---|
from __future__ import annotations | ||||
import json | ||||
import logging | import logging | |||
import os | ||||
import sys | import sys | |||
import tempfile | import tempfile | |||
import uuid | ||||
from hashlib import sha256 | ||||
from pathlib import Path | ||||
from typing import TYPE_CHECKING | ||||
import pytest | import pytest | |||
import tomlkit | import tomlkit | |||
from poetry.core.packages.package import Package | from poetry.core.packages.package import Package | |||
from poetry.core.packages.project_package import ProjectPackage | from poetry.core.packages.project_package import ProjectPackage | |||
from poetry.core.semver.version import Version | from poetry.core.semver.version import Version | |||
from poetry.factory import Factory | from poetry.factory import Factory | |||
from poetry.packages.locker import Locker | from poetry.packages.locker import Locker | |||
from poetry.utils._compat import Path | from tests.helpers import get_dependency | |||
from tests.helpers import get_package | ||||
from ..helpers import get_dependency | if TYPE_CHECKING: | |||
from ..helpers import get_package | from _pytest.logging import LogCaptureFixture | |||
from pytest_mock import MockerFixture | ||||
@pytest.fixture | @pytest.fixture | |||
def locker(): | def locker() -> Locker: | |||
with tempfile.NamedTemporaryFile() as f: | with tempfile.NamedTemporaryFile() as f: | |||
f.close() | f.close() | |||
locker = Locker(f.name, {}) | locker = Locker(f.name, {}) | |||
return locker | return locker | |||
@pytest.fixture | @pytest.fixture | |||
def root(): | def root() -> ProjectPackage: | |||
return ProjectPackage("root", "1.2.3") | return ProjectPackage("root", "1.2.3") | |||
def test_lock_file_data_is_ordered(locker, root): | def test_lock_file_data_is_ordered(locker: Locker, root: ProjectPackage): | |||
package_a = get_package("A", "1.0.0") | package_a = get_package("A", "1.0.0") | |||
package_a.add_dependency(Factory.create_dependency("B", "^1.0")) | package_a.add_dependency(Factory.create_dependency("B", "^1.0")) | |||
package_a.files = [{"file": "foo", "hash": "456"}, {"file": "bar", "hash": " 123"}] | package_a.files = [{"file": "foo", "hash": "456"}, {"file": "bar", "hash": " 123"}] | |||
package_a2 = get_package("A", "2.0.0") | ||||
package_a2.files = [{"file": "baz", "hash": "345"}] | ||||
package_git = Package( | package_git = Package( | |||
"git-package", | "git-package", | |||
"1.2.3", | "1.2.3", | |||
source_type="git", | source_type="git", | |||
source_url="https://github.com/python-poetry/poetry.git", | source_url="https://github.com/python-poetry/poetry.git", | |||
source_reference="develop", | source_reference="develop", | |||
source_resolved_reference="123456", | source_resolved_reference="123456", | |||
) | ) | |||
packages = [package_a, get_package("B", "1.2"), package_git] | package_git_with_subdirectory = Package( | |||
"git-package-subdir", | ||||
"1.2.3", | ||||
source_type="git", | ||||
source_url="https://github.com/python-poetry/poetry.git", | ||||
source_reference="develop", | ||||
source_resolved_reference="123456", | ||||
source_subdirectory="subdir", | ||||
) | ||||
package_url_linux = Package( | ||||
"url-package", | ||||
"1.0", | ||||
source_type="url", | ||||
source_url="https://example.org/url-package-1.0-cp39-manylinux_2_17_x86_ | ||||
64.whl", | ||||
) | ||||
package_url_win32 = Package( | ||||
"url-package", | ||||
"1.0", | ||||
source_type="url", | ||||
source_url="https://example.org/url-package-1.0-cp39-win_amd64.whl", | ||||
) | ||||
packages = [ | ||||
package_a2, | ||||
package_a, | ||||
get_package("B", "1.2"), | ||||
package_git, | ||||
package_git_with_subdirectory, | ||||
package_url_win32, | ||||
package_url_linux, | ||||
] | ||||
locker.set_lock_data(root, packages) | locker.set_lock_data(root, packages) | |||
with locker.lock.open(encoding="utf-8") as f: | with locker.lock.open(encoding="utf-8") as f: | |||
content = f.read() | content = f.read() | |||
expected = """[[package]] | expected = """\ | |||
[[package]] | ||||
name = "A" | name = "A" | |||
version = "1.0.0" | version = "1.0.0" | |||
description = "" | description = "" | |||
category = "main" | category = "main" | |||
optional = false | optional = false | |||
python-versions = "*" | python-versions = "*" | |||
[package.dependencies] | [package.dependencies] | |||
B = "^1.0" | B = "^1.0" | |||
[[package]] | [[package]] | |||
name = "A" | ||||
version = "2.0.0" | ||||
description = "" | ||||
category = "main" | ||||
optional = false | ||||
python-versions = "*" | ||||
[[package]] | ||||
name = "B" | name = "B" | |||
version = "1.2" | version = "1.2" | |||
description = "" | description = "" | |||
category = "main" | category = "main" | |||
optional = false | optional = false | |||
python-versions = "*" | python-versions = "*" | |||
[[package]] | [[package]] | |||
name = "git-package" | name = "git-package" | |||
version = "1.2.3" | version = "1.2.3" | |||
description = "" | description = "" | |||
category = "main" | category = "main" | |||
optional = false | optional = false | |||
python-versions = "*" | python-versions = "*" | |||
develop = true | develop = false | |||
[package.source] | [package.source] | |||
type = "git" | type = "git" | |||
url = "https://github.com/python-poetry/poetry.git" | url = "https://github.com/python-poetry/poetry.git" | |||
reference = "develop" | reference = "develop" | |||
resolved_reference = "123456" | resolved_reference = "123456" | |||
[[package]] | ||||
name = "git-package-subdir" | ||||
version = "1.2.3" | ||||
description = "" | ||||
category = "main" | ||||
optional = false | ||||
python-versions = "*" | ||||
develop = false | ||||
[package.source] | ||||
type = "git" | ||||
url = "https://github.com/python-poetry/poetry.git" | ||||
reference = "develop" | ||||
resolved_reference = "123456" | ||||
subdirectory = "subdir" | ||||
[[package]] | ||||
name = "url-package" | ||||
version = "1.0" | ||||
description = "" | ||||
category = "main" | ||||
optional = false | ||||
python-versions = "*" | ||||
[package.source] | ||||
type = "url" | ||||
url = "https://example.org/url-package-1.0-cp39-manylinux_2_17_x86_64.whl" | ||||
[[package]] | ||||
name = "url-package" | ||||
version = "1.0" | ||||
description = "" | ||||
category = "main" | ||||
optional = false | ||||
python-versions = "*" | ||||
[package.source] | ||||
type = "url" | ||||
url = "https://example.org/url-package-1.0-cp39-win_amd64.whl" | ||||
[metadata] | [metadata] | |||
lock-version = "1.1" | lock-version = "1.1" | |||
python-versions = "*" | python-versions = "*" | |||
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | |||
[metadata.files] | [metadata.files] | |||
A = [ | A = [ | |||
{file = "bar", hash = "123"}, | {file = "bar", hash = "123"}, | |||
{file = "foo", hash = "456"}, | {file = "foo", hash = "456"}, | |||
{file = "baz", hash = "345"}, | ||||
] | ] | |||
B = [] | B = [] | |||
git-package = [] | git-package = [] | |||
git-package-subdir = [] | ||||
url-package = [] | ||||
""" | """ | |||
assert expected == content | assert content == expected | |||
def test_locker_properly_loads_extras(locker): | def test_locker_properly_loads_extras(locker: Locker): | |||
content = """\ | content = """\ | |||
[[package]] | [[package]] | |||
name = "cachecontrol" | name = "cachecontrol" | |||
version = "0.12.5" | version = "0.12.5" | |||
description = "httplib2 caching for requests" | description = "httplib2 caching for requests" | |||
category = "main" | category = "main" | |||
optional = false | optional = false | |||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | |||
[package.dependencies] | [package.dependencies] | |||
skipping to change at line 134 | skipping to change at line 229 | |||
content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77 " | content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77 " | |||
[metadata.files] | [metadata.files] | |||
cachecontrol = [] | cachecontrol = [] | |||
""" | """ | |||
locker.lock.write(tomlkit.parse(content)) | locker.lock.write(tomlkit.parse(content)) | |||
packages = locker.locked_repository().packages | packages = locker.locked_repository().packages | |||
assert 1 == len(packages) | assert len(packages) == 1 | |||
package = packages[0] | package = packages[0] | |||
assert 3 == len(package.requires) | assert len(package.requires) == 3 | |||
assert 2 == len(package.extras) | assert len(package.extras) == 2 | |||
lockfile_dep = package.extras["filecache"][0] | lockfile_dep = package.extras["filecache"][0] | |||
assert lockfile_dep.name == "lockfile" | assert lockfile_dep.name == "lockfile" | |||
def test_locker_properly_loads_nested_extras(locker): | def test_locker_properly_loads_nested_extras(locker: Locker): | |||
content = """\ | content = """\ | |||
[[package]] | [[package]] | |||
name = "a" | name = "a" | |||
version = "1.0" | version = "1.0" | |||
description = "" | description = "" | |||
category = "main" | category = "main" | |||
optional = false | optional = false | |||
python-versions = "*" | python-versions = "*" | |||
[package.dependencies] | [package.dependencies] | |||
skipping to change at line 195 | skipping to change at line 290 | |||
[metadata.files] | [metadata.files] | |||
"a" = [] | "a" = [] | |||
"b" = [] | "b" = [] | |||
"c" = [] | "c" = [] | |||
""" | """ | |||
locker.lock.write(tomlkit.parse(content)) | locker.lock.write(tomlkit.parse(content)) | |||
repository = locker.locked_repository() | repository = locker.locked_repository() | |||
assert 3 == len(repository.packages) | assert len(repository.packages) == 3 | |||
packages = repository.find_packages(get_dependency("a", "1.0")) | packages = repository.find_packages(get_dependency("a", "1.0")) | |||
assert len(packages) == 1 | assert len(packages) == 1 | |||
package = packages[0] | package = packages[0] | |||
assert len(package.requires) == 1 | assert len(package.requires) == 1 | |||
assert len(package.extras) == 1 | assert len(package.extras) == 1 | |||
dependency_b = package.extras["b"][0] | dependency_b = package.extras["b"][0] | |||
assert dependency_b.name == "b" | assert dependency_b.name == "b" | |||
skipping to change at line 222 | skipping to change at line 317 | |||
assert len(package.requires) == 1 | assert len(package.requires) == 1 | |||
assert len(package.extras) == 1 | assert len(package.extras) == 1 | |||
dependency_c = package.extras["c"][0] | dependency_c = package.extras["c"][0] | |||
assert dependency_c.name == "c" | assert dependency_c.name == "c" | |||
assert dependency_c.extras == frozenset() | assert dependency_c.extras == frozenset() | |||
packages = repository.find_packages(dependency_c) | packages = repository.find_packages(dependency_c) | |||
assert len(packages) == 1 | assert len(packages) == 1 | |||
def test_locker_properly_loads_extras_legacy(locker): | def test_locker_properly_loads_extras_legacy(locker: Locker): | |||
content = """\ | content = """\ | |||
[[package]] | [[package]] | |||
name = "a" | name = "a" | |||
version = "1.0" | version = "1.0" | |||
description = "" | description = "" | |||
category = "main" | category = "main" | |||
optional = false | optional = false | |||
python-versions = "*" | python-versions = "*" | |||
[package.dependencies] | [package.dependencies] | |||
skipping to change at line 259 | skipping to change at line 354 | |||
content-hash = "123456789" | content-hash = "123456789" | |||
[metadata.files] | [metadata.files] | |||
"a" = [] | "a" = [] | |||
"b" = [] | "b" = [] | |||
""" | """ | |||
locker.lock.write(tomlkit.parse(content)) | locker.lock.write(tomlkit.parse(content)) | |||
repository = locker.locked_repository() | repository = locker.locked_repository() | |||
assert 2 == len(repository.packages) | assert len(repository.packages) == 2 | |||
packages = repository.find_packages(get_dependency("a", "1.0")) | packages = repository.find_packages(get_dependency("a", "1.0")) | |||
assert len(packages) == 1 | assert len(packages) == 1 | |||
package = packages[0] | package = packages[0] | |||
assert len(package.requires) == 1 | assert len(package.requires) == 1 | |||
assert len(package.extras) == 1 | assert len(package.extras) == 1 | |||
dependency_b = package.extras["b"][0] | dependency_b = package.extras["b"][0] | |||
assert dependency_b.name == "b" | assert dependency_b.name == "b" | |||
def test_lock_packages_with_null_description(locker, root): | def test_locker_properly_loads_subdir(locker: Locker) -> None: | |||
content = """\ | ||||
[[package]] | ||||
name = "git-package-subdir" | ||||
version = "1.2.3" | ||||
description = "" | ||||
category = "main" | ||||
optional = false | ||||
python-versions = "*" | ||||
develop = false | ||||
[package.source] | ||||
type = "git" | ||||
url = "https://github.com/python-poetry/poetry.git" | ||||
reference = "develop" | ||||
resolved_reference = "123456" | ||||
subdirectory = "subdir" | ||||
[metadata] | ||||
lock-version = "1.1" | ||||
python-versions = "*" | ||||
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 | ||||
" | ||||
[metadata.files] | ||||
git-package-subdir = [] | ||||
""" | ||||
locker.lock.write(tomlkit.parse(content)) | ||||
repository = locker.locked_repository() | ||||
assert len(repository.packages) == 1 | ||||
packages = repository.find_packages(get_dependency("git-package-subdir", "1. | ||||
2.3")) | ||||
assert len(packages) == 1 | ||||
package = packages[0] | ||||
assert package.source_subdirectory == "subdir" | ||||
def test_lock_packages_with_null_description(locker: Locker, root: ProjectPackag | ||||
e): | ||||
package_a = get_package("A", "1.0.0") | package_a = get_package("A", "1.0.0") | |||
package_a.description = None | package_a.description = None | |||
locker.set_lock_data(root, [package_a]) | locker.set_lock_data(root, [package_a]) | |||
with locker.lock.open(encoding="utf-8") as f: | with locker.lock.open(encoding="utf-8") as f: | |||
content = f.read() | content = f.read() | |||
expected = """[[package]] | expected = """[[package]] | |||
name = "A" | name = "A" | |||
skipping to change at line 297 | skipping to change at line 429 | |||
[metadata] | [metadata] | |||
lock-version = "1.1" | lock-version = "1.1" | |||
python-versions = "*" | python-versions = "*" | |||
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | |||
[metadata.files] | [metadata.files] | |||
A = [] | A = [] | |||
""" | """ | |||
assert expected == content | assert content == expected | |||
def test_lock_file_should_not_have_mixed_types(locker, root): | def test_lock_file_should_not_have_mixed_types(locker: Locker, root: ProjectPack age): | |||
package_a = get_package("A", "1.0.0") | package_a = get_package("A", "1.0.0") | |||
package_a.add_dependency(Factory.create_dependency("B", "^1.0.0")) | package_a.add_dependency(Factory.create_dependency("B", "^1.0.0")) | |||
package_a.add_dependency( | package_a.add_dependency( | |||
Factory.create_dependency("B", {"version": ">=1.0.0", "optional": True}) | Factory.create_dependency("B", {"version": ">=1.0.0", "optional": True}) | |||
) | ) | |||
package_a.requires[-1].activate() | package_a.requires[-1].activate() | |||
package_a.extras["foo"] = [get_dependency("B", ">=1.0.0")] | package_a.extras["foo"] = [get_dependency("B", ">=1.0.0")] | |||
locker.set_lock_data(root, [package_a]) | locker.set_lock_data(root, [package_a]) | |||
skipping to change at line 339 | skipping to change at line 471 | |||
python-versions = "*" | python-versions = "*" | |||
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | |||
[metadata.files] | [metadata.files] | |||
A = [] | A = [] | |||
""" | """ | |||
with locker.lock.open(encoding="utf-8") as f: | with locker.lock.open(encoding="utf-8") as f: | |||
content = f.read() | content = f.read() | |||
assert expected == content | assert content == expected | |||
def test_reading_lock_file_should_raise_an_error_on_invalid_data(locker): | def test_reading_lock_file_should_raise_an_error_on_invalid_data(locker: Locker) | |||
content = u"""[[package]] | : | |||
content = """[[package]] | ||||
name = "A" | name = "A" | |||
version = "1.0.0" | version = "1.0.0" | |||
description = "" | description = "" | |||
category = "main" | category = "main" | |||
optional = false | optional = false | |||
python-versions = "*" | python-versions = "*" | |||
[package.extras] | [package.extras] | |||
foo = ["bar"] | foo = ["bar"] | |||
skipping to change at line 372 | skipping to change at line 504 | |||
A = [] | A = [] | |||
""" | """ | |||
with locker.lock.open("w", encoding="utf-8") as f: | with locker.lock.open("w", encoding="utf-8") as f: | |||
f.write(content) | f.write(content) | |||
with pytest.raises(RuntimeError) as e: | with pytest.raises(RuntimeError) as e: | |||
_ = locker.lock_data | _ = locker.lock_data | |||
assert "Unable to read the lock file" in str(e.value) | assert "Unable to read the lock file" in str(e.value) | |||
def test_locking_legacy_repository_package_should_include_source_section(root, l | def test_locking_legacy_repository_package_should_include_source_section( | |||
ocker): | root: ProjectPackage, locker: Locker | |||
): | ||||
package_a = Package( | package_a = Package( | |||
"A", | "A", | |||
"1.0.0", | "1.0.0", | |||
source_type="legacy", | source_type="legacy", | |||
source_url="https://foo.bar", | source_url="https://foo.bar", | |||
source_reference="legacy", | source_reference="legacy", | |||
) | ) | |||
packages = [package_a] | packages = [package_a] | |||
locker.set_lock_data(root, packages) | locker.set_lock_data(root, packages) | |||
skipping to change at line 409 | skipping to change at line 543 | |||
[metadata] | [metadata] | |||
lock-version = "1.1" | lock-version = "1.1" | |||
python-versions = "*" | python-versions = "*" | |||
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | |||
[metadata.files] | [metadata.files] | |||
A = [] | A = [] | |||
""" | """ | |||
assert expected == content | assert content == expected | |||
def test_locker_should_emit_warnings_if_lock_version_is_newer_but_allowed( | def test_locker_should_emit_warnings_if_lock_version_is_newer_but_allowed( | |||
locker, caplog | locker: Locker, caplog: LogCaptureFixture | |||
): | ): | |||
content = """\ | version = ".".join(Version.parse(Locker._VERSION).next_minor().text.split(". | |||
")[:2]) | ||||
content = f"""\ | ||||
[metadata] | [metadata] | |||
lock-version = "{version}" | lock-version = "{version}" | |||
python-versions = "~2.7 || ^3.4" | python-versions = "~2.7 || ^3.4" | |||
content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77 " | content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77 " | |||
[metadata.files] | [metadata.files] | |||
""".format( | """ | |||
version=".".join(Version.parse(Locker._VERSION).next_minor.text.split(". | ||||
")[:2]) | ||||
) | ||||
caplog.set_level(logging.WARNING, logger="poetry.packages.locker") | caplog.set_level(logging.WARNING, logger="poetry.packages.locker") | |||
locker.lock.write(tomlkit.parse(content)) | locker.lock.write(tomlkit.parse(content)) | |||
_ = locker.lock_data | _ = locker.lock_data | |||
assert 1 == len(caplog.records) | assert len(caplog.records) == 1 | |||
record = caplog.records[0] | record = caplog.records[0] | |||
assert "WARNING" == record.levelname | assert record.levelname == "WARNING" | |||
expected = """\ | expected = """\ | |||
The lock file might not be compatible with the current version of Poetry. | The lock file might not be compatible with the current version of Poetry. | |||
Upgrade Poetry to ensure the lock file is read properly or, alternatively, \ | Upgrade Poetry to ensure the lock file is read properly or, alternatively, \ | |||
regenerate the lock file with the `poetry lock` command.\ | regenerate the lock file with the `poetry lock` command.\ | |||
""" | """ | |||
assert expected == record.message | assert record.message == expected | |||
def test_locker_should_raise_an_error_if_lock_version_is_newer_and_not_allowed( | def test_locker_should_raise_an_error_if_lock_version_is_newer_and_not_allowed( | |||
locker, caplog | locker: Locker, caplog: LogCaptureFixture | |||
): | ): | |||
content = """\ | content = """\ | |||
[metadata] | [metadata] | |||
lock-version = "2.0" | lock-version = "2.0" | |||
python-versions = "~2.7 || ^3.4" | python-versions = "~2.7 || ^3.4" | |||
content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77 " | content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77 " | |||
[metadata.files] | [metadata.files] | |||
""" | """ | |||
caplog.set_level(logging.WARNING, logger="poetry.packages.locker") | caplog.set_level(logging.WARNING, logger="poetry.packages.locker") | |||
locker.lock.write(tomlkit.parse(content)) | locker.lock.write(tomlkit.parse(content)) | |||
with pytest.raises(RuntimeError, match="^The lock file is not compatible"): | with pytest.raises(RuntimeError, match="^The lock file is not compatible"): | |||
_ = locker.lock_data | _ = locker.lock_data | |||
def test_extras_dependencies_are_ordered(locker, root): | def test_extras_dependencies_are_ordered(locker: Locker, root: ProjectPackage): | |||
package_a = get_package("A", "1.0.0") | package_a = get_package("A", "1.0.0") | |||
package_a.add_dependency( | package_a.add_dependency( | |||
Factory.create_dependency( | Factory.create_dependency( | |||
"B", {"version": "^1.0.0", "optional": True, "extras": ["c", "a", "b "]} | "B", {"version": "^1.0.0", "optional": True, "extras": ["c", "a", "b "]} | |||
) | ) | |||
) | ) | |||
package_a.requires[-1].activate() | package_a.requires[-1].activate() | |||
locker.set_lock_data(root, [package_a]) | locker.set_lock_data(root, [package_a]) | |||
skipping to change at line 494 | skipping to change at line 627 | |||
python-versions = "*" | python-versions = "*" | |||
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | |||
[metadata.files] | [metadata.files] | |||
A = [] | A = [] | |||
""" | """ | |||
with locker.lock.open(encoding="utf-8") as f: | with locker.lock.open(encoding="utf-8") as f: | |||
content = f.read() | content = f.read() | |||
assert expected == content | assert content == expected | |||
def test_locker_should_neither_emit_warnings_nor_raise_error_for_lower_compatibl | def test_locker_should_neither_emit_warnings_nor_raise_error_for_lower_compatibl | |||
e_versions( | e_versions( # noqa: E501 | |||
locker, caplog | locker: Locker, caplog: LogCaptureFixture | |||
): | ): | |||
current_version = Version.parse(Locker._VERSION) | current_version = Version.parse(Locker._VERSION) | |||
older_version = ".".join( | older_version = ".".join( | |||
[str(current_version.major), str(current_version.minor - 1)] | [str(current_version.major), str(current_version.minor - 1)] | |||
) | ) | |||
content = """\ | content = f"""\ | |||
[metadata] | [metadata] | |||
lock-version = "{version}" | lock-version = "{older_version}" | |||
python-versions = "~2.7 || ^3.4" | python-versions = "~2.7 || ^3.4" | |||
content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77 " | content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77 " | |||
[metadata.files] | [metadata.files] | |||
""".format( | """ | |||
version=older_version | ||||
) | ||||
caplog.set_level(logging.WARNING, logger="poetry.packages.locker") | caplog.set_level(logging.WARNING, logger="poetry.packages.locker") | |||
locker.lock.write(tomlkit.parse(content)) | locker.lock.write(tomlkit.parse(content)) | |||
_ = locker.lock_data | _ = locker.lock_data | |||
assert 0 == len(caplog.records) | assert len(caplog.records) == 0 | |||
def test_locker_dumps_dependency_information_correctly(locker, root): | def test_locker_dumps_dependency_information_correctly( | |||
locker: Locker, root: ProjectPackage | ||||
): | ||||
root_dir = Path(__file__).parent.parent.joinpath("fixtures") | root_dir = Path(__file__).parent.parent.joinpath("fixtures") | |||
package_a = get_package("A", "1.0.0") | package_a = get_package("A", "1.0.0") | |||
package_a.add_dependency( | package_a.add_dependency( | |||
Factory.create_dependency( | Factory.create_dependency( | |||
"B", {"path": "project_with_extras", "develop": True}, root_dir=root _dir | "B", {"path": "project_with_extras", "develop": True}, root_dir=root _dir | |||
) | ) | |||
) | ) | |||
package_a.add_dependency( | package_a.add_dependency( | |||
Factory.create_dependency( | Factory.create_dependency( | |||
"C", | "C", | |||
skipping to change at line 583 | skipping to change at line 716 | |||
[metadata] | [metadata] | |||
lock-version = "1.1" | lock-version = "1.1" | |||
python-versions = "*" | python-versions = "*" | |||
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | |||
[metadata.files] | [metadata.files] | |||
A = [] | A = [] | |||
""" | """ | |||
assert expected == content | assert content == expected | |||
def test_locker_dumps_subdir(locker: Locker, root: ProjectPackage) -> None: | ||||
package_git_with_subdirectory = Package( | ||||
"git-package-subdir", | ||||
"1.2.3", | ||||
source_type="git", | ||||
source_url="https://github.com/python-poetry/poetry.git", | ||||
source_reference="develop", | ||||
source_resolved_reference="123456", | ||||
source_subdirectory="subdir", | ||||
) | ||||
locker.set_lock_data(root, [package_git_with_subdirectory]) | ||||
with locker.lock.open(encoding="utf-8") as f: | ||||
content = f.read() | ||||
expected = """\ | ||||
[[package]] | ||||
name = "git-package-subdir" | ||||
version = "1.2.3" | ||||
description = "" | ||||
category = "main" | ||||
optional = false | ||||
python-versions = "*" | ||||
develop = false | ||||
def test_locker_dumps_dependency_extras_in_correct_order(locker, root): | [package.source] | |||
type = "git" | ||||
url = "https://github.com/python-poetry/poetry.git" | ||||
reference = "develop" | ||||
resolved_reference = "123456" | ||||
subdirectory = "subdir" | ||||
[metadata] | ||||
lock-version = "1.1" | ||||
python-versions = "*" | ||||
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 | ||||
" | ||||
[metadata.files] | ||||
git-package-subdir = [] | ||||
""" | ||||
assert content == expected | ||||
def test_locker_dumps_dependency_extras_in_correct_order( | ||||
locker: Locker, root: ProjectPackage | ||||
): | ||||
root_dir = Path(__file__).parent.parent.joinpath("fixtures") | root_dir = Path(__file__).parent.parent.joinpath("fixtures") | |||
package_a = get_package("A", "1.0.0") | package_a = get_package("A", "1.0.0") | |||
Factory.create_dependency("B", "1.0.0", root_dir=root_dir) | Factory.create_dependency("B", "1.0.0", root_dir=root_dir) | |||
Factory.create_dependency("C", "1.0.0", root_dir=root_dir) | Factory.create_dependency("C", "1.0.0", root_dir=root_dir) | |||
package_first = Factory.create_dependency("first", "1.0.0", root_dir=root_di r) | package_first = Factory.create_dependency("first", "1.0.0", root_dir=root_di r) | |||
package_second = Factory.create_dependency("second", "1.0.0", root_dir=root_ dir) | package_second = Factory.create_dependency("second", "1.0.0", root_dir=root_ dir) | |||
package_third = Factory.create_dependency("third", "1.0.0", root_dir=root_di r) | package_third = Factory.create_dependency("third", "1.0.0", root_dir=root_di r) | |||
package_a.extras = { | package_a.extras = { | |||
"C": [package_third, package_second, package_first], | "C": [package_third, package_second, package_first], | |||
skipping to change at line 627 | skipping to change at line 806 | |||
lock-version = "1.1" | lock-version = "1.1" | |||
python-versions = "*" | python-versions = "*" | |||
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | |||
[metadata.files] | [metadata.files] | |||
A = [] | A = [] | |||
""" | """ | |||
assert content == expected | assert content == expected | |||
@pytest.mark.skipif(sys.version_info[:2] == (3, 5), reason="Skip for Python 3.5" | def test_locked_repository_uses_root_dir_of_package( | |||
) | locker: Locker, mocker: MockerFixture | |||
def test_locked_repository_uses_root_dir_of_package(locker, mocker): | ): | |||
content = """\ | content = """\ | |||
[[package]] | [[package]] | |||
name = "lib-a" | name = "lib-a" | |||
version = "0.1.0" | version = "0.1.0" | |||
description = "" | description = "" | |||
category = "main" | category = "main" | |||
optional = false | optional = false | |||
python-versions = "^2.7.9" | python-versions = "^2.7.9" | |||
develop = true | develop = true | |||
skipping to change at line 657 | skipping to change at line 837 | |||
lock-version = "1.1" | lock-version = "1.1" | |||
python-versions = "*" | python-versions = "*" | |||
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 " | |||
[metadata.files] | [metadata.files] | |||
lib-a = [] | lib-a = [] | |||
lib-b = [] | lib-b = [] | |||
""" | """ | |||
locker.lock.write(tomlkit.parse(content)) | locker.lock.write(tomlkit.parse(content)) | |||
create_dependency_patch = mocker.patch("poetry.factory.Factory.create_depend | create_dependency_patch = mocker.patch( | |||
ency") | "poetry.factory.Factory.create_dependency", autospec=True | |||
) | ||||
locker.locked_repository() | locker.locked_repository() | |||
create_dependency_patch.assert_called_once_with( | create_dependency_patch.assert_called_once_with( | |||
"lib-b", {"develop": True, "path": "../libB"}, root_dir=mocker.ANY | "lib-b", {"develop": True, "path": "../libB"}, root_dir=mocker.ANY | |||
) | ) | |||
call_kwargs = create_dependency_patch.call_args[1] | call_kwargs = create_dependency_patch.call_args[1] | |||
root_dir = call_kwargs["root_dir"] | root_dir = call_kwargs["root_dir"] | |||
assert root_dir.match("*/lib/libA") | assert root_dir.match("*/lib/libA") | |||
# relative_to raises an exception if not relative - is_relative_to comes in py3.9 | # relative_to raises an exception if not relative - is_relative_to comes in py3.9 | |||
assert root_dir.relative_to(locker.lock.path.parent.resolve()) is not None | assert root_dir.relative_to(locker.lock.path.parent.resolve()) is not None | |||
@pytest.mark.parametrize( | ||||
("local_config", "fresh"), | ||||
[ | ||||
({}, True), | ||||
({"dependencies": [uuid.uuid4().hex]}, True), | ||||
( | ||||
{ | ||||
"dependencies": [uuid.uuid4().hex], | ||||
"dev-dependencies": [uuid.uuid4().hex], | ||||
}, | ||||
True, | ||||
), | ||||
( | ||||
{ | ||||
"dependencies": [uuid.uuid4().hex], | ||||
"dev-dependencies": None, | ||||
}, | ||||
True, | ||||
), | ||||
({"dependencies": [uuid.uuid4().hex], "groups": [uuid.uuid4().hex]}, Fal | ||||
se), | ||||
], | ||||
) | ||||
def test_content_hash_with_legacy_is_compatible( | ||||
local_config: dict[str, list[str]], fresh: bool, locker: Locker | ||||
) -> None: | ||||
# old hash generation | ||||
relevant_content = {} | ||||
for key in locker._legacy_keys: | ||||
relevant_content[key] = local_config.get(key) | ||||
locker = locker.__class__( | ||||
lock=locker.lock.path, | ||||
local_config=local_config, | ||||
) | ||||
old_content_hash = sha256( | ||||
json.dumps(relevant_content, sort_keys=True).encode() | ||||
).hexdigest() | ||||
content_hash = locker._get_content_hash() | ||||
assert (content_hash == old_content_hash) or fresh | ||||
def test_lock_file_resolves_file_url_symlinks(root: ProjectPackage): | ||||
""" | ||||
Create directories and file structure as follows: | ||||
d1/ | ||||
d1/testsymlink -> d1/d2/d3 | ||||
d1/d2/d3/lock_file | ||||
d1/d4/source_file | ||||
Using the testsymlink as the Locker.lock file path should correctly resolve | ||||
to | ||||
the real physical path of the source_file when calculating the relative path | ||||
from the lock_file, i.e. "../../d4/source_file" instead of the unresolved pa | ||||
th | ||||
from the symlink itself which would have been "../d4/source_file" | ||||
See https://github.com/python-poetry/poetry/issues/5849 | ||||
""" | ||||
with tempfile.TemporaryDirectory() as d1: | ||||
symlink_path = Path(d1).joinpath("testsymlink") | ||||
with tempfile.TemporaryDirectory(dir=d1) as d2, tempfile.TemporaryDirect | ||||
ory( | ||||
dir=d1 | ||||
) as d4, tempfile.TemporaryDirectory(dir=d2) as d3, tempfile.NamedTempor | ||||
aryFile( | ||||
dir=d4 | ||||
) as source_file, tempfile.NamedTemporaryFile( | ||||
dir=d3 | ||||
) as lock_file: | ||||
lock_file.close() | ||||
try: | ||||
os.symlink(Path(d3), symlink_path) | ||||
except OSError: | ||||
if sys.platform == "win32": | ||||
# os.symlink requires either administrative privileges or de | ||||
veloper | ||||
# mode on Win10, throwing an OSError if neither is active. | ||||
# Test is not possible in that case. | ||||
return | ||||
raise | ||||
locker = Locker(str(symlink_path) + os.sep + Path(lock_file.name).na | ||||
me, {}) | ||||
package_local = Package( | ||||
"local-package", | ||||
"1.2.3", | ||||
source_type="file", | ||||
source_url=source_file.name, | ||||
source_reference="develop", | ||||
source_resolved_reference="123456", | ||||
) | ||||
packages = [ | ||||
package_local, | ||||
] | ||||
locker.set_lock_data(root, packages) | ||||
with locker.lock.open(encoding="utf-8") as f: | ||||
content = f.read() | ||||
expected = f"""\ | ||||
[[package]] | ||||
name = "local-package" | ||||
version = "1.2.3" | ||||
description = "" | ||||
category = "main" | ||||
optional = false | ||||
python-versions = "*" | ||||
[package.source] | ||||
type = "file" | ||||
url = "{ | ||||
Path( | ||||
os.path.relpath( | ||||
Path(source_file.name).resolve().as_posix(), | ||||
Path(Path(lock_file.name).parent).resolve().as_posix(), | ||||
) | ||||
).as_posix() | ||||
}" | ||||
reference = "develop" | ||||
resolved_reference = "123456" | ||||
[metadata] | ||||
lock-version = "1.1" | ||||
python-versions = "*" | ||||
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8 | ||||
" | ||||
[metadata.files] | ||||
local-package = [] | ||||
""" | ||||
assert content == expected | ||||
End of changes. 52 change blocks. | ||||
56 lines changed or deleted | 241 lines changed or added |