From 654fb0aabf6f641c69113ac06f63c30c4b878c10 Mon Sep 17 00:00:00 2001 From: Christopher Barber Date: Mon, 4 Sep 2023 13:09:25 -0400 Subject: [PATCH 1/2] Added unit tests cases for metadata (#5,#32) --- src/whl2conda/api/converter.py | 15 +++++++------ test/api/validator.py | 40 ++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/whl2conda/api/converter.py b/src/whl2conda/api/converter.py index b3e6969..fb07a52 100644 --- a/src/whl2conda/api/converter.py +++ b/src/whl2conda/api/converter.py @@ -468,9 +468,6 @@ def _write_about(self, conda_info_dir: Path, md: Dict[str, Any]) -> None: ) ) - def _write_license_files(self): - pass - # pylint: disable=too-many-locals def _compute_conda_dependencies(self, dependencies: Sequence[str]) -> List[str]: conda_dependencies: List[str] = [] @@ -534,10 +531,14 @@ def _copy_licenses(self, conda_info_dir: Path, wheel_md: MetadataFromWheel) -> N from_license_dir = wheel_md.wheel_info_dir.joinpath("licenses") to_license_dir = conda_info_dir.joinpath("licenses") for license_file in license_files: - from_file = from_license_dir.joinpath(license_file) - to_file = to_license_dir.joinpath(license_file) - to_license_dir.mkdir(parents=True, exist_ok=True) - shutil.copyfile(from_file, to_file) + # copy license file if it exists + for from_dir in [from_license_dir, wheel_md.wheel_info_dir]: + from_file = from_dir.joinpath(license_file) + if from_file.is_file(): + to_file = to_license_dir.joinpath(license_file) + to_license_dir.mkdir(parents=True, exist_ok=True) + shutil.copyfile(from_file, to_file) + break # pylint: disable=too-many-locals def _parse_wheel_metadata(self, wheel_dir: Path) -> MetadataFromWheel: diff --git a/test/api/validator.py b/test/api/validator.py index bb8666e..45b2aec 100644 --- a/test/api/validator.py +++ b/test/api/validator.py @@ -29,7 +29,8 @@ import pytest from wheel.wheelfile import WheelFile -from whl2conda.api.converter import RequiresDistEntry +from whl2conda.__about__ import __version__ +from whl2conda.api.converter import RequiresDistEntry, Wheel2CondaConverter class PackageValidator: @@ -85,7 +86,7 @@ def _parse_wheel_metadata(self, wheel_dir: Path) -> Dict[str, Any]: md_file = metdata_files[0] md_msg = email.message_from_string(md_file.read_text()) - list_keys = {"classifier", "license-file", "requires-dist"} + list_keys = set(s.lower() for s in Wheel2CondaConverter.MULTI_USE_METADATA_KEYS) md: Dict[str, Any] = {} for key, value in md_msg.items(): key = key.lower() @@ -132,8 +133,39 @@ def _validate_unpacked(self) -> None: def _validate_about(self, info_dir: Path) -> None: about_file = info_dir.joinpath("about.json") assert about_file.is_file() - _about = json.loads(about_file.read_text()) - # TODO - check about contents + md = self._wheel_md + _about: Dict[str, Any] = json.loads(about_file.read_text()) + + assert _about.get("home") == md.get("home-page") + assert _about.get("keywords") == md.get("keywords") + assert _about.get("summary") == md.get("summary") + assert _about.get("description") == md.get("description") + assert _about.get("classifiers") == md.get("classifier") + assert _about.get("whl2conda_version") == __version__ + + license = _about.get("license") + if license_expr := md.get("license-expression"): + assert license == license_expr + else: + assert license == md.get("license") + + extra = _about.get("extra", {}) + dev_url = "" + doc_url = "" + for urlpair in md.get("project-url", ()): + key, url = re.split(r"\s*,\s*", urlpair) + assert extra.get(key) == url + first_word = re.split(r"\W+", key)[0].lower() + if first_word in {"doc", "documentation"}: + doc_url = url + if first_word in {"dev", "development", "repo", "repository"}: + dev_url = url + assert _about.get("doc_url", "") == doc_url + assert _about.get("dev_url", "") == dev_url + + assert extra.get("license_files", ()) == md.get("license-file", ()) + for key in ["author", "maintainer", "author-email", "maintainer-email"]: + assert extra.get(key) == md.get(key) def _validate_hash_input(self, info_dir: Path) -> None: hash_input_file = info_dir.joinpath("hash_input.json") From 65d3f70540587c204c49fc222ca89cacac3e8384 Mon Sep 17 00:00:00 2001 From: Christopher Barber Date: Mon, 4 Sep 2023 13:57:39 -0400 Subject: [PATCH 2/2] Converter test using poetry project --- src/whl2conda/__about__.py | 2 +- test-projects/{poetry/simple => }/__init__.py | 0 test-projects/poetry/__init__.py | 14 ++++++++++++++ test-projects/poetry/pyproject.toml | 13 +++++++++++-- .../poetry/src/poetry_example/__init__.py | 14 ++++++++++++++ test/api/test_converter.py | 16 +++++++++++++++- 6 files changed, 55 insertions(+), 4 deletions(-) rename test-projects/{poetry/simple => }/__init__.py (100%) create mode 100644 test-projects/poetry/__init__.py create mode 100644 test-projects/poetry/src/poetry_example/__init__.py diff --git a/src/whl2conda/__about__.py b/src/whl2conda/__about__.py index 900081a..b1c2613 100644 --- a/src/whl2conda/__about__.py +++ b/src/whl2conda/__about__.py @@ -21,5 +21,5 @@ # pylint: disable=no-member if sys.version_info >= (3, 9): # pragma: no cover __version__ = res.files('whl2conda').joinpath("VERSION").read_text().strip() -else: +else: # pragma: no cover __version__ = res.read_text('whl2conda', "VERSION").strip() diff --git a/test-projects/poetry/simple/__init__.py b/test-projects/__init__.py similarity index 100% rename from test-projects/poetry/simple/__init__.py rename to test-projects/__init__.py diff --git a/test-projects/poetry/__init__.py b/test-projects/poetry/__init__.py new file mode 100644 index 0000000..a64a240 --- /dev/null +++ b/test-projects/poetry/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2023 Christopher Barber +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/test-projects/poetry/pyproject.toml b/test-projects/poetry/pyproject.toml index d8bd37a..cfb42cf 100644 --- a/test-projects/poetry/pyproject.toml +++ b/test-projects/poetry/pyproject.toml @@ -3,9 +3,18 @@ requires = ["poetry-core","setuptools"] build-backend = "poetry.core.masonry.api" [tool.poetry] -name = "simple" +name = "poetry.example" version = "1.0.2" description = "simple test project" -authors = ["bob"] +authors = ["bob "] license = "MIT" +homepage = "https://nowhere.com/poetry.example" +documentation = "https://nowhere.com/poetry.example/docs" +packages = [ + { include = "poetry_example", from ="src"} +] + +[tool.poetry.dependencies] +python = ">= 3.8" +requests = "^2.13" diff --git a/test-projects/poetry/src/poetry_example/__init__.py b/test-projects/poetry/src/poetry_example/__init__.py new file mode 100644 index 0000000..a64a240 --- /dev/null +++ b/test-projects/poetry/src/poetry_example/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2023 Christopher Barber +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/test/api/test_converter.py b/test/api/test_converter.py index debdcda..ec1351f 100644 --- a/test/api/test_converter.py +++ b/test/api/test_converter.py @@ -34,6 +34,7 @@ CondaPackageFormat, DependencyRename, ) +from whl2conda.cli.build import do_build_wheel from whl2conda.cli.install import install_main from .validator import PackageValidator @@ -41,6 +42,7 @@ this_dir = Path(__file__).parent.absolute() root_dir = this_dir.parent.parent +test_projects = root_dir / "test-projects" # # Converter test fixture @@ -264,7 +266,7 @@ def test_dependency_rename() -> None: def test_this(test_case: ConverterTestCaseFactory) -> None: """Test using this own project's wheel""" - wheel_dir = test_case.tmp_path_factory.mktemp("test_this_wheel_dir") + wheel_dir = test_case.tmp_path_factory.mktemp("test_this_wjheel_dir") subprocess.check_call( [ "pip", @@ -304,6 +306,18 @@ def test_simple_wheel( assert v1pkg.name.endswith(".tar.bz2") +def test_poetry( + test_case: ConverterTestCaseFactory, + tmp_path: Path, +) -> None: + """Unit test on simple poetry package""" + poetry_dir = test_projects / "poetry" + wheel = do_build_wheel(poetry_dir, tmp_path) + pkg = test_case(wheel).build() + # conda package name taken from project name + assert pkg.name.startswith("poetry.example") + + # # External pypi tests #