forked from openkylin/poetry-core
412 lines
14 KiB
Python
412 lines
14 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING
|
|
from typing import Any
|
|
from typing import cast
|
|
|
|
import pytest
|
|
|
|
from packaging.utils import canonicalize_name
|
|
|
|
from poetry.core.constraints.version import parse_constraint
|
|
from poetry.core.factory import Factory
|
|
from poetry.core.packages.url_dependency import URLDependency
|
|
from poetry.core.toml import TOMLFile
|
|
from poetry.core.version.markers import SingleMarker
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
from poetry.core.packages.dependency import Dependency
|
|
from poetry.core.packages.vcs_dependency import VCSDependency
|
|
|
|
|
|
fixtures_dir = Path(__file__).parent / "fixtures"
|
|
|
|
|
|
def test_create_poetry() -> None:
|
|
poetry = Factory().create_poetry(fixtures_dir / "sample_project")
|
|
|
|
package = poetry.package
|
|
|
|
assert package.name == "my-package"
|
|
assert package.version.text == "1.2.3"
|
|
assert package.description == "Some description."
|
|
assert package.authors == ["Sébastien Eustace <sebastien@eustace.io>"]
|
|
assert package.license
|
|
assert package.license.id == "MIT"
|
|
assert (
|
|
package.readmes[0].relative_to(fixtures_dir).as_posix()
|
|
== "sample_project/README.rst"
|
|
)
|
|
assert package.homepage == "https://python-poetry.org"
|
|
assert package.repository_url == "https://github.com/python-poetry/poetry"
|
|
assert package.keywords == ["packaging", "dependency", "poetry"]
|
|
|
|
assert package.python_versions == "~2.7 || ^3.6"
|
|
assert str(package.python_constraint) == ">=2.7,<2.8 || >=3.6,<4.0"
|
|
|
|
dependencies: dict[str, Dependency] = {}
|
|
for dep in package.requires:
|
|
dependencies[dep.name] = dep
|
|
|
|
cleo = dependencies["cleo"]
|
|
assert cleo.pretty_constraint == "^0.6"
|
|
assert not cleo.is_optional()
|
|
|
|
pendulum = dependencies["pendulum"]
|
|
assert pendulum.pretty_constraint == "branch 2.0"
|
|
assert pendulum.is_vcs()
|
|
pendulum = cast("VCSDependency", pendulum)
|
|
assert pendulum.vcs == "git"
|
|
assert pendulum.branch == "2.0"
|
|
assert pendulum.source == "https://github.com/sdispater/pendulum.git"
|
|
assert pendulum.allows_prereleases()
|
|
assert not pendulum.develop
|
|
|
|
tomlkit = dependencies["tomlkit"]
|
|
assert tomlkit.pretty_constraint == "rev 3bff550"
|
|
assert tomlkit.is_vcs()
|
|
tomlkit = cast("VCSDependency", tomlkit)
|
|
assert tomlkit.vcs == "git"
|
|
assert tomlkit.rev == "3bff550"
|
|
assert tomlkit.source == "https://github.com/sdispater/tomlkit.git"
|
|
assert tomlkit.allows_prereleases()
|
|
assert not tomlkit.develop
|
|
|
|
requests = dependencies["requests"]
|
|
assert requests.pretty_constraint == "^2.18"
|
|
assert not requests.is_vcs()
|
|
assert not requests.allows_prereleases()
|
|
assert requests.is_optional()
|
|
assert requests.extras == frozenset({"security"})
|
|
|
|
pathlib2 = dependencies["pathlib2"]
|
|
assert pathlib2.pretty_constraint == "^2.2"
|
|
assert pathlib2.python_versions == ">=2.7 <2.8"
|
|
assert not pathlib2.is_optional()
|
|
|
|
demo = dependencies["demo"]
|
|
assert demo.is_file()
|
|
assert not demo.is_vcs()
|
|
assert demo.name == "demo"
|
|
assert demo.pretty_constraint == "*"
|
|
|
|
demo = dependencies["my-package"]
|
|
assert not demo.is_file()
|
|
assert demo.is_directory()
|
|
assert not demo.is_vcs()
|
|
assert demo.name == "my-package"
|
|
assert demo.pretty_constraint == "*"
|
|
|
|
simple_project = dependencies["simple-project"]
|
|
assert not simple_project.is_file()
|
|
assert simple_project.is_directory()
|
|
assert not simple_project.is_vcs()
|
|
assert simple_project.name == "simple-project"
|
|
assert simple_project.pretty_constraint == "*"
|
|
|
|
functools32 = dependencies["functools32"]
|
|
assert functools32.name == "functools32"
|
|
assert functools32.pretty_constraint == "^3.2.3"
|
|
assert (
|
|
str(functools32.marker)
|
|
== 'python_version ~= "2.7" and sys_platform == "win32" or python_version in'
|
|
' "3.4 3.5"'
|
|
)
|
|
|
|
dataclasses = dependencies["dataclasses"]
|
|
assert dataclasses.name == "dataclasses"
|
|
assert dataclasses.pretty_constraint == "^0.7"
|
|
assert dataclasses.python_versions == ">=3.6.1 <3.7"
|
|
assert (
|
|
str(dataclasses.marker)
|
|
== 'python_full_version >= "3.6.1" and python_version < "3.7"'
|
|
)
|
|
|
|
assert "db" in package.extras
|
|
|
|
classifiers = package.classifiers
|
|
|
|
assert classifiers == [
|
|
"Topic :: Software Development :: Build Tools",
|
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
]
|
|
|
|
assert package.all_classifiers == [
|
|
"License :: OSI Approved :: MIT License",
|
|
"Programming Language :: Python :: 2",
|
|
"Programming Language :: Python :: 2.7",
|
|
"Programming Language :: Python :: 3",
|
|
"Programming Language :: Python :: 3.6",
|
|
"Programming Language :: Python :: 3.7",
|
|
"Programming Language :: Python :: 3.8",
|
|
"Programming Language :: Python :: 3.9",
|
|
"Programming Language :: Python :: 3.10",
|
|
"Programming Language :: Python :: 3.11",
|
|
"Topic :: Software Development :: Build Tools",
|
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
]
|
|
|
|
|
|
def test_create_poetry_with_packages_and_includes() -> None:
|
|
poetry = Factory().create_poetry(
|
|
fixtures_dir.parent / "masonry" / "builders" / "fixtures" / "with-include"
|
|
)
|
|
|
|
package = poetry.package
|
|
|
|
assert package.packages == [
|
|
{"include": "extra_dir/**/*.py"},
|
|
{"include": "extra_dir/**/*.py"},
|
|
{"include": "my_module.py"},
|
|
{"include": "package_with_include"},
|
|
{"include": "tests", "format": "sdist"},
|
|
{"include": "for_wheel_only", "format": ["wheel"]},
|
|
{"include": "src_package", "from": "src"},
|
|
]
|
|
|
|
assert package.include == [
|
|
{"path": "extra_dir/vcs_excluded.txt", "format": []},
|
|
{"path": "notes.txt", "format": []},
|
|
]
|
|
|
|
|
|
def test_create_poetry_with_multi_constraints_dependency() -> None:
|
|
poetry = Factory().create_poetry(
|
|
fixtures_dir / "project_with_multi_constraints_dependency"
|
|
)
|
|
|
|
package = poetry.package
|
|
|
|
assert len(package.requires) == 2
|
|
|
|
|
|
def test_validate() -> None:
|
|
complete = TOMLFile(fixtures_dir / "complete.toml")
|
|
doc: dict[str, Any] = complete.read()
|
|
content = doc["tool"]["poetry"]
|
|
|
|
assert Factory.validate(content) == {"errors": [], "warnings": []}
|
|
|
|
|
|
def test_validate_fails() -> None:
|
|
complete = TOMLFile(fixtures_dir / "complete.toml")
|
|
doc: dict[str, Any] = complete.read()
|
|
content = doc["tool"]["poetry"]
|
|
content["authors"] = "this is not a valid array"
|
|
|
|
expected = "[authors] 'this is not a valid array' is not of type 'array'"
|
|
|
|
assert Factory.validate(content) == {"errors": [expected], "warnings": []}
|
|
|
|
|
|
def test_validate_without_strict_fails_only_non_strict() -> None:
|
|
project_failing_strict_validation = TOMLFile(
|
|
fixtures_dir / "project_failing_strict_validation" / "pyproject.toml"
|
|
)
|
|
doc: dict[str, Any] = project_failing_strict_validation.read()
|
|
content = doc["tool"]["poetry"]
|
|
|
|
assert Factory.validate(content) == {
|
|
"errors": [
|
|
"'name' is a required property",
|
|
"'version' is a required property",
|
|
"'description' is a required property",
|
|
"'authors' is a required property",
|
|
],
|
|
"warnings": [],
|
|
}
|
|
|
|
|
|
def test_validate_strict_fails_strict_and_non_strict() -> None:
|
|
project_failing_strict_validation = TOMLFile(
|
|
fixtures_dir / "project_failing_strict_validation" / "pyproject.toml"
|
|
)
|
|
doc: dict[str, Any] = project_failing_strict_validation.read()
|
|
content = doc["tool"]["poetry"]
|
|
|
|
assert Factory.validate(content, strict=True) == {
|
|
"errors": [
|
|
"'name' is a required property",
|
|
"'version' is a required property",
|
|
"'description' is a required property",
|
|
"'authors' is a required property",
|
|
(
|
|
'Script "a_script_with_unknown_extra" requires extra "foo" which is not'
|
|
" defined."
|
|
),
|
|
(
|
|
"Declared README files must be of same type: found text/markdown,"
|
|
" text/x-rst"
|
|
),
|
|
],
|
|
"warnings": [
|
|
(
|
|
"A wildcard Python dependency is ambiguous. Consider specifying a more"
|
|
" explicit one."
|
|
),
|
|
(
|
|
'The "pathlib2" dependency specifies the "allows-prereleases" property,'
|
|
' which is deprecated. Use "allow-prereleases" instead.'
|
|
),
|
|
],
|
|
}
|
|
|
|
|
|
def test_strict_validation_success_on_multiple_readme_files() -> None:
|
|
with_readme_files = TOMLFile(fixtures_dir / "with_readme_files" / "pyproject.toml")
|
|
doc: dict[str, Any] = with_readme_files.read()
|
|
content = doc["tool"]["poetry"]
|
|
|
|
assert Factory.validate(content, strict=True) == {"errors": [], "warnings": []}
|
|
|
|
|
|
def test_strict_validation_fails_on_readme_files_with_unmatching_types() -> None:
|
|
with_readme_files = TOMLFile(fixtures_dir / "with_readme_files" / "pyproject.toml")
|
|
doc: dict[str, Any] = with_readme_files.read()
|
|
content = doc["tool"]["poetry"]
|
|
content["readme"][0] = "README.md"
|
|
|
|
assert Factory.validate(content, strict=True) == {
|
|
"errors": [
|
|
"Declared README files must be of same type: found text/markdown,"
|
|
" text/x-rst"
|
|
],
|
|
"warnings": [],
|
|
}
|
|
|
|
|
|
def test_create_poetry_fails_on_invalid_configuration() -> None:
|
|
with pytest.raises(RuntimeError) as e:
|
|
Factory().create_poetry(
|
|
Path(__file__).parent / "fixtures" / "invalid_pyproject" / "pyproject.toml"
|
|
)
|
|
|
|
expected = """\
|
|
The Poetry configuration is invalid:
|
|
- 'description' is a required property
|
|
"""
|
|
assert str(e.value) == expected
|
|
|
|
|
|
def test_create_poetry_omits_dev_dependencies_iff_with_dev_is_false() -> None:
|
|
poetry = Factory().create_poetry(fixtures_dir / "sample_project", with_groups=False)
|
|
assert not any("dev" in r.groups for r in poetry.package.all_requires)
|
|
|
|
poetry = Factory().create_poetry(fixtures_dir / "sample_project")
|
|
assert any("dev" in r.groups for r in poetry.package.all_requires)
|
|
|
|
|
|
def test_create_poetry_fails_with_invalid_dev_dependencies_iff_with_dev_is_true() -> (
|
|
None
|
|
):
|
|
with pytest.raises(ValueError) as err:
|
|
Factory().create_poetry(fixtures_dir / "project_with_invalid_dev_deps")
|
|
assert "does not exist" in str(err.value)
|
|
|
|
Factory().create_poetry(
|
|
fixtures_dir / "project_with_invalid_dev_deps", with_groups=False
|
|
)
|
|
|
|
|
|
def test_create_poetry_with_groups_and_legacy_dev() -> None:
|
|
poetry = Factory().create_poetry(
|
|
fixtures_dir / "project_with_groups_and_legacy_dev"
|
|
)
|
|
|
|
package = poetry.package
|
|
dependencies = package.all_requires
|
|
|
|
assert len(dependencies) == 2
|
|
assert {dependency.name for dependency in dependencies} == {"pytest", "pre-commit"}
|
|
|
|
|
|
def test_create_poetry_with_groups_and_explicit_main() -> None:
|
|
poetry = Factory().create_poetry(
|
|
fixtures_dir / "project_with_groups_and_explicit_main"
|
|
)
|
|
|
|
package = poetry.package
|
|
dependencies = package.requires
|
|
|
|
assert len(dependencies) == 1
|
|
assert {dependency.name for dependency in dependencies} == {
|
|
"aiohttp",
|
|
}
|
|
|
|
|
|
def test_create_poetry_with_markers_and_extras() -> None:
|
|
poetry = Factory().create_poetry(fixtures_dir / "project_with_markers_and_extras")
|
|
|
|
package = poetry.package
|
|
dependencies = package.requires
|
|
extras = package.extras
|
|
|
|
assert len(dependencies) == 2
|
|
assert {dependency.name for dependency in dependencies} == {"orjson"}
|
|
assert set(extras[canonicalize_name("all")]) == set(dependencies)
|
|
for dependency in dependencies:
|
|
assert dependency.in_extras == ["all"]
|
|
assert isinstance(dependency, URLDependency)
|
|
assert isinstance(dependency.marker, SingleMarker)
|
|
assert dependency.marker.name == "sys_platform"
|
|
assert dependency.marker.value == (
|
|
"darwin" if "macosx" in dependency.url else "linux"
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"constraint, exp_python, exp_marker",
|
|
[
|
|
({"python": "3.7"}, "~3.7", 'python_version == "3.7"'),
|
|
({"platform": "linux"}, "*", 'sys_platform == "linux"'),
|
|
({"markers": 'python_version == "3.7"'}, "~3.7", 'python_version == "3.7"'),
|
|
(
|
|
{"markers": 'platform_machine == "x86_64"'},
|
|
"*",
|
|
'platform_machine == "x86_64"',
|
|
),
|
|
(
|
|
{"python": "3.7", "markers": 'platform_machine == "x86_64"'},
|
|
"~3.7",
|
|
'platform_machine == "x86_64" and python_version == "3.7"',
|
|
),
|
|
(
|
|
{"platform": "linux", "markers": 'platform_machine == "x86_64"'},
|
|
"*",
|
|
'platform_machine == "x86_64" and sys_platform == "linux"',
|
|
),
|
|
(
|
|
{
|
|
"python": "3.7",
|
|
"platform": "linux",
|
|
"markers": 'platform_machine == "x86_64"',
|
|
},
|
|
"~3.7",
|
|
(
|
|
'platform_machine == "x86_64" and python_version == "3.7" and'
|
|
' sys_platform == "linux"'
|
|
),
|
|
),
|
|
(
|
|
{"python": ">=3.7", "markers": 'python_version < "4.0"'},
|
|
"<4.0 >=3.7",
|
|
'python_version < "4.0" and python_version >= "3.7"',
|
|
),
|
|
(
|
|
{"platform": "linux", "markers": 'sys_platform == "win32"'},
|
|
"*",
|
|
"<empty>",
|
|
),
|
|
],
|
|
)
|
|
def test_create_dependency_marker_variants(
|
|
constraint: dict[str, Any], exp_python: str, exp_marker: str
|
|
) -> None:
|
|
constraint["version"] = "1.0.0"
|
|
dep = Factory.create_dependency("foo", constraint)
|
|
assert dep.python_versions == exp_python
|
|
assert dep.python_constraint == parse_constraint(exp_python)
|
|
assert str(dep.marker) == exp_marker
|