From d14401cc46e73384f0217951bfdbe23ad7dc6e5d Mon Sep 17 00:00:00 2001 From: Joonalai Date: Wed, 27 Sep 2023 13:30:57 +0300 Subject: [PATCH] fix: fix fetching recursive runtime deps --- src/qgis_plugin_dev_tools/config/__init__.py | 11 ++- .../utils/distributions.py | 29 ++++++-- test/test_build.py | 69 +++++++++++++++++++ 3 files changed, 101 insertions(+), 8 deletions(-) diff --git a/src/qgis_plugin_dev_tools/config/__init__.py b/src/qgis_plugin_dev_tools/config/__init__.py index b187908..4134781 100644 --- a/src/qgis_plugin_dev_tools/config/__init__.py +++ b/src/qgis_plugin_dev_tools/config/__init__.py @@ -78,14 +78,19 @@ def __init__( if auto_add_recursive_runtime_dependencies: # Add the requirements of the distributions as well - self.extra_runtime_distributions = list( - ChainMap( + distributions_versions = { + dist.name: dist.version for dist in self.runtime_distributions + } + self.extra_runtime_distributions = [ + dist + for dist in ChainMap( *( get_distribution_requirements(dist) for dist in self.runtime_distributions ) ).values() - ) + if distributions_versions.get(dist.name) != dist.version + ] @staticmethod def from_pyproject_config(pyproject_file_path: Path) -> "DevToolsConfig": diff --git a/src/qgis_plugin_dev_tools/utils/distributions.py b/src/qgis_plugin_dev_tools/utils/distributions.py index f89aa20..11bf68f 100644 --- a/src/qgis_plugin_dev_tools/utils/distributions.py +++ b/src/qgis_plugin_dev_tools/utils/distributions.py @@ -16,12 +16,17 @@ # # You should have received a copy of the GNU General Public License # along with qgis-plugin-dev-tools. If not, see . +import importlib.util +import logging +from importlib.machinery import SourceFileLoader +from typing import Dict, List, Optional, cast -from typing import Dict, List - +import importlib_metadata from importlib_metadata import Distribution, distribution from packaging.requirements import Requirement +LOGGER = logging.getLogger(__name__) + def get_distribution_top_level_package_names(dist: Distribution) -> List[str]: return (dist.read_text("top_level.txt") or "").split() @@ -33,9 +38,23 @@ def get_distribution_requirements(dist: Distribution) -> Dict[str, Distribution] for requirement in dist.requires or [] if "extra ==" not in requirement ] - distributions = { - requirement.name: distribution(requirement.name) for requirement in requirements - } + distributions = {} + for requirement in requirements: + try: + distributions[requirement.name] = distribution(requirement.name) + except importlib_metadata.PackageNotFoundError: + LOGGER.warning( + "Getting distribution for %s failed. " + "This may be caused by including builtin " + "packages as requirements.", + requirement.name, + ) + spec = importlib.util.find_spec(requirement.name) + loader = cast(Optional[SourceFileLoader], spec.loader) if spec else None + if spec and loader and loader.is_package(requirement.name): + LOGGER.error("Could not find package %s", requirement.name) + continue + sub_requirements = {} for requirement in distributions.values(): sub_requirements.update(get_distribution_requirements(requirement)) diff --git a/test/test_build.py b/test/test_build.py index f3d8aff..fd8fd46 100644 --- a/test/test_build.py +++ b/test/test_build.py @@ -72,6 +72,19 @@ def dev_tools_config(plugin_dir: Path): ) +@pytest.fixture() +def dev_tools_config_with_duplicate_dependencies(plugin_dir: Path): + from qgis_plugin_dev_tools.config import DevToolsConfig + + return DevToolsConfig( + plugin_package_name="Plugin", + runtime_requires=["pytest-cov"], + changelog_file_path=plugin_dir / "CHANGELOG.md", + append_distributions_to_path=True, + auto_add_recursive_runtime_dependencies=True, + ) + + @pytest.fixture() def dev_tools_config_minimal(plugin_dir: Path): # No python path append and not recursive deps @@ -134,6 +147,62 @@ def test_make_zip(dev_tools_config: "DevToolsConfig", plugin_dir: Path, tmp_path } +def test_make_zip_with_duplicate_dependencies( + dev_tools_config_with_duplicate_dependencies: "DevToolsConfig", + plugin_dir: Path, + tmp_path: Path, +): + target_path = tmp_path / "dist" + expected_zip = target_path / "Plugin-0.1.0.zip" + + make_plugin_zip(dev_tools_config_with_duplicate_dependencies, target_path) + + assert target_path.exists() + assert expected_zip.exists() + + plugin_init_file_contents = _get_file_from_zip(expected_zip, "Plugin/__init__.py") + vendor_init_file_contents = _get_file_from_zip( + expected_zip, "Plugin/_vendor/__init__.py" + ) + vendor_files = _get_file_names(expected_zip, "Plugin/_vendor/") + + assert "import Plugin._vendor" in plugin_init_file_contents + assert "sys.path.append" in vendor_init_file_contents + assert vendor_files == { + "__init__.py", + "_pytest", + "atomicwrites", + "atomicwrites-1.4.0.dist-info", + "attr", + "attrs", + "attrs-21.4.0.dist-info", + "colorama", + "colorama-0.4.4.dist-info", + "coverage", + "coverage-6.3.2.dist-info", + "importlib_metadata", + "importlib_metadata-4.11.3.dist-info", + "iniconfig", + "iniconfig-1.1.1.dist-info", + "packaging", + "packaging-21.3.dist-info", + "pluggy", + "pluggy-1.0.0.dist-info", + "py", + "py-1.11.0.dist-info", + "pyparsing-3.0.8.dist-info", + "pytest", + "pytest-6.2.5.dist-info", + "pytest_cov", + "pytest_cov-2.12.0.dist-info", + "toml", + "toml-0.10.2.dist-info", + "typing_extensions-4.2.0.dist-info", + "zipp-3.8.0.dist-info", + "zipp.py", + } + + def test_make_zip_with_minimal_config( dev_tools_config_minimal: "DevToolsConfig", plugin_dir: Path, tmp_path: Path ):