"""Tests for build/wheel/package.py metadata plumbing. The Nuitka wheel is built from a generated ``setup.py``, which is exactly where the early 0.5.x releases silently lost ``Requires-Python``, classifiers, project URLs and the license. These tests run setuptools for real (in a temp dir, on a stub package tree) and assert on the METADATA it produces. """ from __future__ import annotations import subprocess import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] BUILD_ROOT = REPO_ROOT / "build" if str(BUILD_ROOT) not in sys.path: sys.path.insert(0, str(BUILD_ROOT)) from wheel.package import ( # noqa: E402 metadata_kwargs, read_long_description, write_wheel_setup, ) PROJECT = { "name": "orisys", "description": "SDK for Orisys Technology Visuotactile Sensors", "requires-python": ">=3.10", "license": {"text": "Proprietary"}, "authors": [{"name": "Orisys-Tech"}], "maintainers": [{"name": "Yijian Zhang", "email": "zhangyijian@orisys-tech.cn"}], "dependencies": ["numpy>=1.24.4"], "optional-dependencies": {"gui": ["open3d>=0.17.0"]}, "classifiers": [ "Development Status :: 4 - Beta", "License :: Other/Proprietary License", ], "urls": { "Homepage": "https://www.orisys-tech.cn", "Documentation": "https://github.com/Orisys-Tech/orisys-sdk", }, } LONG_DESCRIPTION = "# Orisys SDK\n\nlong description body.\n" def _stub_wheel_root(wheel_dir: Path) -> None: """Minimal package tree so the generated setup.py can build metadata.""" (wheel_dir / "wheel_root" / "orisys").mkdir(parents=True) (wheel_dir / "wheel_root" / "orisys" / "__init__.cp312-win_amd64.pyd").write_bytes(b"x") (wheel_dir / "wheel_root" / "orisys" / "examples").mkdir() (wheel_dir / "wheel_root" / "orisys" / "examples" / "__init__.py").write_text("", encoding="utf-8") (wheel_dir / "wheel_root" / "orisys_blend").mkdir() (wheel_dir / "wheel_root" / "orisys_blend" / "__init__.py").write_text("", encoding="utf-8") def _generate(wheel_dir: Path, tmp_path: Path) -> Path: _stub_wheel_root(wheel_dir) write_wheel_setup( wheel_dir, version="1.2.3", package_data={"orisys": ["*.pyd"]}, install_requires=["numpy>=1.24.4"], extras_require={"gui": ["open3d>=0.17.0"]}, project=PROJECT, long_description=LONG_DESCRIPTION, ) completed = subprocess.run( [sys.executable, "setup.py", "-q", "dist_info"], cwd=wheel_dir, capture_output=True, text=True, check=False, ) assert completed.returncode == 0, ( f"generated setup.py failed:\nstdout:\n{completed.stdout}\nstderr:\n{completed.stderr}" ) candidates = sorted(wheel_dir.glob("**/*.dist-info/METADATA")) assert candidates, f"no METADATA produced under {wheel_dir}" return candidates[0] def test_metadata_kwargs_translates_pyproject(): kwargs = metadata_kwargs(PROJECT) assert kwargs["description"] == PROJECT["description"] assert kwargs["python_requires"] == ">=3.10" assert kwargs["license"] == "Proprietary" assert kwargs["classifiers"] == PROJECT["classifiers"] assert kwargs["project_urls"] == PROJECT["urls"] assert kwargs["author"] == "Orisys-Tech" assert kwargs["maintainer"] == "Yijian Zhang " def test_metadata_kwargs_accepts_string_license_and_drops_empties(): kwargs = metadata_kwargs({"name": "orisys", "license": "Proprietary"}) assert kwargs["license"] == "Proprietary" assert "python_requires" not in kwargs assert "classifiers" not in kwargs def test_read_long_description_uses_pypi_readme(tmp_path): (tmp_path / "docs").mkdir() (tmp_path / "docs" / "pypi_readme.md").write_text(LONG_DESCRIPTION, encoding="utf-8") assert read_long_description(tmp_path) == LONG_DESCRIPTION def test_read_long_description_missing_returns_none(tmp_path, capsys): assert read_long_description(tmp_path) is None assert "not found" in capsys.readouterr().out def test_generated_wheel_metadata_is_complete(tmp_path): wheel_dir = tmp_path / "wheel" wheel_dir.mkdir() metadata_path = _generate(wheel_dir, tmp_path) text = metadata_path.read_text(encoding="utf-8") assert "Name: orisys" in text assert "Version: 1.2.3" in text assert f"Summary: {PROJECT['description']}" in text assert "Requires-Python: >=3.10" in text assert "License: Proprietary" in text assert "Classifier: Development Status :: 4 - Beta" in text assert "Classifier: License :: Other/Proprietary License" in text assert "Project-URL: Homepage, https://www.orisys-tech.cn" in text assert "Project-URL: Documentation, https://github.com/Orisys-Tech/orisys-sdk" in text assert "Requires-Dist: numpy>=1.24.4" in text assert 'Provides-Extra: gui' in text assert "Description-Content-Type: text/markdown" in text assert "long description body." in text def test_generated_wheel_metadata_keeps_version_static(tmp_path): """``Version`` (and ``Name``) must never be annotated as dynamic. setuptools marks every field supplied through the legacy ``setup()`` path as ``Dynamic:`` even when the value is present and correct (verified by ``test_generated_wheel_metadata_is_complete``). PEP 643 ``Dynamic`` is sdist-only and pip/PyPI ignore it in wheels, so only the identity fields are asserted here. """ wheel_dir = tmp_path / "wheel" wheel_dir.mkdir() text = _generate(wheel_dir, tmp_path).read_text(encoding="utf-8") dynamic = {line[len("Dynamic: ") :].strip() for line in text.splitlines() if line.startswith("Dynamic:")} assert "version" not in dynamic, f"wheel METADATA marks Version as dynamic: {sorted(dynamic)}" assert "name" not in dynamic, f"wheel METADATA marks Name as dynamic: {sorted(dynamic)}" def test_write_wheel_setup_without_project_keeps_legacy_shape(tmp_path): """Calling without metadata must not crash (backwards compatible).""" wheel_dir = tmp_path / "wheel" wheel_dir.mkdir() setup_py = write_wheel_setup( wheel_dir, version="0.0.1", package_data={"orisys": ["*.pyd"]}, install_requires=["numpy"], extras_require={}, ) text = setup_py.read_text(encoding="utf-8") assert 'name="orisys"' in text assert 'version="0.0.1"' in text assert "long_description" not in text def test_pypi_readme_exists_in_repo(): assert (REPO_ROOT / "docs" / "pypi_readme.md").is_file(), ( "docs/pypi_readme.md is the PyPI long description source; without it " "wheels are published with no project description." )