Source code for pkgcheck.checks.perl

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


[docs] class MismatchedPerlVersion(results.VersionResult, results.Warning): """A package's normalized perl module version doesn't match its $PV.""" def __init__(self, dist_version, normalized, **kwargs): super().__init__(**kwargs) self.dist_version = dist_version self.normalized = normalized @property def desc(self): return f"DIST_VERSION={self.dist_version} normalizes to {self.normalized}"
[docs] 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.""" class _PerlConnection: """Connection to perl script the check is going to communicate with.""" def __init__(self, options): self.perl_client = None self.process_lock = multiprocessing.Lock() # start perl client for normalizing perl module versions into package versions try: self.perl_client = subprocess.Popen( ["perl", pjoin(const.DATA_PATH, "perl-version.pl")], text=True, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) except FileNotFoundError: raise _PerlException("perl not installed on system") # check if the script is running ready = self.perl_client.stdout.readline().strip() if ready != "ready" or self.perl_client.poll(): err_msg = "failed to run perl script" if options.verbosity > 0: stderr = self.perl_client.stderr.read().strip() err_msg += f": {stderr}" raise _PerlException(err_msg) def normalize(self, version): """Normalize a given version number to its perl equivalent.""" with self.process_lock: self.perl_client.stdin.write(version + "\n") return self.perl_client.stdout.readline().strip() def __del__(self): # kill perl process if it still exists if self.perl_client is not None: self.perl_client.kill()
[docs] class PerlCheck(OptionalCheck): """Perl ebuild related checks.""" _source = sources.EbuildFileRepoSource known_results = frozenset( { MismatchedPerlVersion, MissingVersionedVirtualPerlDependency, } ) def __init__(self, *args): super().__init__(*args) self.dist_version_re = re.compile(r"DIST_VERSION=(?P<dist_version>\d+(\.\d+)*)\s*\n") # Initialize connection with perl script. This is done during # __init__() since only one running version of the script is shared # between however many scanning processes will be run. Also, it makes # it easier to disable this check if required perl deps are missing. try: self.perl = _PerlConnection(self.options) except _PerlException as exc: raise SkipCheck(self, str(exc))
[docs] def feed(self, 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)