commit:     59cfd9b5e53577b59fd513b1cdd9e0cda184ed13
Author:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
AuthorDate: Sun Jul  2 19:24:58 2023 +0000
Commit:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Sat Jul 15 08:36:00 2023 +0000
URL:        
https://gitweb.gentoo.org/proj/pkgcore/pkgcheck.git/commit/?id=59cfd9b5

PerlCheck: check versioned virtual perl dependencies

Resolves: https://github.com/pkgcore/pkgcheck/issues/593
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>

 src/pkgcheck/checks/perl.py | 66 ++++++++++++++++++++++++++++++++++++---------
 tests/checks/test_perl.py   | 36 +++++++++++++++++++++++++
 2 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/src/pkgcheck/checks/perl.py b/src/pkgcheck/checks/perl.py
index f2c3bd25..2774f990 100644
--- a/src/pkgcheck/checks/perl.py
+++ b/src/pkgcheck/checks/perl.py
@@ -2,8 +2,11 @@ import multiprocessing
 import re
 import subprocess
 
+from pkgcore.ebuild.atom import atom as atom_cls
 from pkgcore.restrictions import packages, values
+from pkgcore.package.errors import MetadataException
 from snakeoil.osutils import pjoin
+from snakeoil.sequences import iflatten_instance
 
 from .. import const, results, sources
 from . import OptionalCheck, SkipCheck
@@ -22,6 +25,24 @@ class MismatchedPerlVersion(results.VersionResult, 
results.Warning):
         return f"DIST_VERSION={self.dist_version} normalizes to 
{self.normalized}"
 
 
+class MissingVersionedVirtualPerlDependency(results.VersionResult, 
results.Warning):
+    """Missing version restriction for virtual perl dependency.
+
+    The virtuals ``virtual/perl-*`` stand for packages that have releases both
+    as part of ``dev-lang/perl`` and standalone in ``perl-core/*``. Apart from
+    rare special cases, if you require "any" version of such a virtual, this
+    will always be fulfilled by ``dev-lang/perl``.
+    """
+
+    def __init__(self, atom, **kwargs):
+        super().__init__(**kwargs)
+        self.atom = atom
+
+    @property
+    def desc(self):
+        return f"missing version restriction for virtual perl: {self.atom!r}"
+
+
 class _PerlException(Exception):
     """Generic error during perl script initialization."""
 
@@ -70,12 +91,13 @@ class _PerlConnection:
 class PerlCheck(OptionalCheck):
     """Perl ebuild related checks."""
 
-    _restricted_source = (
-        sources.RestrictionRepoSource,
-        (packages.PackageRestriction("inherited", 
values.ContainmentMatch2("perl-module")),),
+    _source = sources.EbuildFileRepoSource
+    known_results = frozenset(
+        {
+            MismatchedPerlVersion,
+            MissingVersionedVirtualPerlDependency,
+        }
     )
-    _source = (sources.EbuildFileRepoSource, (), (("source", 
_restricted_source),))
-    known_results = frozenset([MismatchedPerlVersion])
 
     def __init__(self, *args):
         super().__init__(*args)
@@ -86,12 +108,32 @@ class PerlCheck(OptionalCheck):
         # it easier to disable this check if required perl deps are missing.
         try:
             self.perl = _PerlConnection(self.options)
-        except _PerlException as e:
-            raise SkipCheck(self, str(e))
+        except _PerlException as exc:
+            raise SkipCheck(self, str(exc))
 
     def feed(self, pkg):
-        if mo := self.dist_version_re.search("".join(pkg.lines)):
-            dist_version = mo.group("dist_version")
-            normalized = self.perl.normalize(dist_version)
-            if normalized != pkg.version:
-                yield MismatchedPerlVersion(dist_version, normalized, pkg=pkg)
+        if "perl-module" in pkg.inherited:
+            if mo := self.dist_version_re.search("".join(pkg.lines)):
+                dist_version = mo.group("dist_version")
+                normalized = self.perl.normalize(dist_version)
+                if normalized != pkg.version:
+                    yield MismatchedPerlVersion(dist_version, normalized, 
pkg=pkg)
+
+        missing_virtual_perl = set()
+        for attr in (x.lower() for x in pkg.eapi.dep_keys):
+            try:
+                deps = getattr(pkg, attr)
+            except MetadataException:
+                continue
+            for atom in iflatten_instance(deps, (atom_cls,)):
+                if (
+                    not atom.op
+                    and atom.key.startswith("virtual/perl-")
+                    and pkg.key != "dev-lang/perl"
+                    and pkg.category != "perl-core"
+                    and not pkg.key.startswith("virtual/perl-")
+                ):
+                    missing_virtual_perl.add(str(atom))
+
+        for atom in sorted(missing_virtual_perl):
+            yield MissingVersionedVirtualPerlDependency(str(atom), pkg=pkg)

diff --git a/tests/checks/test_perl.py b/tests/checks/test_perl.py
index 8de1a795..b2f76eab 100644
--- a/tests/checks/test_perl.py
+++ b/tests/checks/test_perl.py
@@ -73,3 +73,39 @@ class TestPerlCheck(misc.ReportTestCase):
             for verbosity in (0, 1):
                 with pytest.raises(SkipCheck, match="failed to run perl 
script"):
                     self.mk_check(verbosity=verbosity)
+
+    @pytest.mark.parametrize(
+        "cpv", ("dev-lang/perl-0", "perl-core/pkgcheck-0", 
"virtual/perl-pkgcheck-0")
+    )
+    def test_missing_op_virtual_good_packages(self, cpv):
+        pkg = misc.FakePkg(cpv, data={"RDEPEND": "virtual/perl-pkgcheck"})
+        self.assertNoReport(self.mk_check(), pkg)
+
+    @pytest.mark.parametrize(
+        "rdepend",
+        (
+            ">=virtual/perl-pkgcheck-0",
+            "<virtual/perl-pkgcheck-0",
+            "virtual/pkgcheck",
+            "dev-cpp/perl-pkgcheck-0",
+        ),
+    )
+    def test_missing_op_virtual_good_deps(self, rdepend):
+        pkg = misc.FakePkg("dev-cpp/perl-pkgcheck-0", data={"RDEPEND": 
rdepend})
+        self.assertNoReport(self.mk_check(), pkg)
+
+    def test_missing_op_virtual_bad(self):
+        pkg = misc.FakePkg(
+            "dev-cpp/perl-pkgcheck-0",
+            data={
+                "RDEPEND": "virtual/perl-pkgcore virtual/perl-pkgcheck",
+                "DEPEND": "virtual/perl-pkgcheck",
+                "BDEPEND": ">=virtual/perl-pkgcore-0",
+            },
+        )
+        reports = self.assertReports(self.mk_check(), pkg)
+        assert len(reports) == 2
+        for r in reports:
+            assert isinstance(r, perl.MissingVersionedVirtualPerlDependency)
+        assert reports[0].atom == "virtual/perl-pkgcheck"
+        assert reports[1].atom == "virtual/perl-pkgcore"

Reply via email to