root/pkgcore-checks/pkgcore_checks/repo_metadata.py @ ferringb%2540gmail.com-20080624174036-q45fk4fxguj9kjlv

Revision ferringb%2540gmail.com-20080624174036-q45fk4fxguj9kjlv, 8.2 kB (checked in by Brian Harring <ferringb@…>, 7 months ago)

punt trailing whitespace

Line 
1# Copyright: 2006 Brian Harring <ferringb@gmail.com>
2# License: GPL2
3
4import os.path
5import operator, itertools
6from pkgcore_checks import base, util, addons
7from pkgcore.ebuild.repository import SlavedTree
8from snakeoil.osutils import listdir_dirs
9from pkgcore.chksum.errors import MissingChksum
10
11from snakeoil.demandload import demandload
12demandload(globals(),
13    'snakeoil.xml:escape',
14    'snakeoil.osutils:listdir_files,pjoin',
15    'snakeoil.lists:iflatten_instance',
16    'pkgcore:fetch',
17    'pkgcore.ebuild:misc',
18)
19
20
21class UnusedLocalFlagsResult(base.Result):
22
23    """
24    unused use.local.desc flag(s)
25    """
26
27    __slots__ = ("category", "package", "flags")
28
29    threshold = base.package_feed
30
31    def __init__(self, pkg, flags):
32        base.Result.__init__(self)
33        # tricky, but it works; atoms have the same attrs
34        self._store_cp(pkg)
35        self.flags = tuple(sorted(flags))
36
37    @property
38    def short_desc(self):
39        return "use.local.desc unused flag(s) %s" % ', '.join(self.flags)
40
41
42class UnusedLocalFlags(base.Template):
43
44    """
45    check for unused use.local.desc entries
46    """
47
48    feed_type = base.package_feed
49    required_addons = (addons.UseAddon,)
50    known_results = (UnusedLocalFlagsResult,) + addons.UseAddon.known_results
51
52    def __init__(self, options, use_handler):
53        base.Template.__init__(self, options)
54        self.iuse_handler = use_handler
55
56    def start(self):
57        self.collapsed = misc.non_incremental_collapsed_restrict_to_data(
58            self.iuse_handler.specific_iuse)
59
60    def feed(self, pkgs, reporter):
61        unused = set()
62        for pkg in pkgs:
63            unused.update(self.collapsed.iter_pull_data(pkg))
64        for pkg in pkgs:
65            unused.difference_update(self.iuse_handler.iuse_strip(pkg.iuse))
66        if unused:
67            reporter.add_report(UnusedLocalFlagsResult(pkg, unused))
68
69
70class UnusedGlobalFlagsResult(base.Result):
71
72    """
73    unused use.desc flag(s)
74    """
75
76    __slots__ = ("flags",)
77
78    threshold = base.repository_feed
79
80    def __init__(self, flags):
81        base.Result.__init__(self)
82        # tricky, but it works; atoms have the same attrs
83        self.flags = tuple(sorted(flags))
84
85    @property
86    def short_desc(self):
87        return "use.desc unused flag(s): %s" % ', '.join(self.flags)
88
89
90class UnusedGlobalFlags(base.Template):
91    """
92    check for unused use.desc entries
93    """
94
95    feed_type = base.versioned_feed
96    scope = base.repository_scope
97    required_addons = (addons.UseAddon,)
98    known_results = (UnusedGlobalFlagsResult,) + addons.UseAddon.known_results
99
100    def __init__(self, options, iuse_handler):
101        base.Template.__init__(self, options)
102        self.flags = None
103        self.iuse_handler = iuse_handler
104
105    def start(self):
106        if not isinstance(self.options.target_repo,SlavedTree):
107            self.flags = set(self.iuse_handler.global_iuse)
108
109    def feed(self, pkg, reporter):
110        if self.flags:
111            self.flags.difference_update(self.iuse_handler.iuse_strip(pkg.iuse))
112
113    def finish(self, reporter):
114        if self.flags:
115            reporter.add_report(UnusedGlobalFlagsResult(self.flags))
116            self.flags.clear()
117
118
119class UnusedLicenseReport(base.Result):
120    """
121    unused license(s) detected
122    """
123
124    __slots__ = ("licenses",)
125
126    threshold = base.repository_feed
127
128    def __init__(self, licenses):
129        base.Result.__init__(self)
130        self.licenses = tuple(sorted(licenses))
131
132    @property
133    def short_desc(self):
134        return "unused license(s): %s" % ', '.join(self.licenses)
135
136
137class UnusedLicense(base.Template):
138    """
139    unused license file(s) check
140    """
141
142    feed_type = base.versioned_feed
143    scope = base.repository_scope
144    required_addons = (addons.LicenseAddon,)
145    known_results = (UnusedLicenseReport,)
146
147    def __init__(self, options, licenses):
148        base.Template.__init__(self, options)
149        self.licenses = None
150
151    def start(self):
152        self.licenses = set()
153        if isinstance(self.options.target_repo,SlavedTree):
154            if 'licenses' in listdir_dirs(self.options.target_repo.location):
155                self.licenses.update(listdir_files(pjoin(self.options.target_repo.location,"licenses")))
156        else:
157            for license_dir in self.options.license_dirs:
158                self.licenses.update(listdir_files(license_dir))
159
160    def feed(self, pkg, reporter):
161        self.licenses.difference_update(iflatten_instance(pkg.license))
162
163    def finish(self, reporter):
164        if self.licenses:
165            reporter.add_report(UnusedLicenseReport(self.licenses))
166        self.licenses = None
167
168
169def reformat_chksums(iterable):
170    for chf, val1, val2 in iterable:
171        if chf == "size":
172            yield chf, val1, val2
173        else:
174            yield chf, "%x" % val1, "%x" % val2
175
176
177class ConflictingChksums(base.Result):
178
179    """
180    checksum conflict detected between two files
181    """
182
183    __slots__ = ("category", "package", "version",
184        "filename", "chksums", "others")
185
186    threshold = base.versioned_feed
187
188    _sorter = staticmethod(operator.itemgetter(0))
189
190    def __init__(self, pkg, filename, chksums, others):
191        base.Result.__init__(self)
192        self._store_cpv(pkg)
193        self.filename = filename
194        self.chksums = tuple(sorted(reformat_chksums(chksums),
195            key=self._sorter))
196        self.others = tuple(sorted(others))
197
198    @property
199    def short_desc(self):
200        return "conflicts with (%s) for file %s chksums %s" % (
201            ', '.join(self.others), self.filename, self.chksums)
202
203
204class OrphanedManifestDist(base.Result):
205    """
206    manifest2 has a checksum entry digest lacks
207    """
208
209    __slots__ = ("category", "package", "files")
210
211    threshold = base.package_feed
212
213    def __init__(self, pkg, files):
214        base.Result.__init__(self)
215        self._store_cp(pkg)
216        self.files = tuple(sorted(files))
217
218    @property
219    def short_desc(self):
220        return "manifest2 knows of files %r, but digest1 doesn't" % (self.files,)
221
222
223class MissingChksum(base.Result):
224    """
225    a file in the chksum data lacks required checksums
226    """
227    threshold = base.versioned_feed
228    __slots__ = ('category', 'package', 'version', 'filename', 'missing',
229        'existing')
230
231    def __init__(self, pkg, filename, missing, existing):
232        self._store_cpv(pkg)
233        self.filename, self.missing = filename, tuple(sorted(missing))
234        self.existing = tuple(sorted(existing))
235
236    @property
237    def short_desc(self):
238        return "file %s is missing required chksums: %s; has chksums: %s" % \
239            (self.filename, ', '.join(self.missing), ', '.join(self.existing))
240
241
242class DeprecatedManifest1(base.Result):
243    """
244    a package's checksum data still is manifest1, instead of manifest2
245    """
246
247    threshold = base.package_feed
248    __slots__ = ("category", "package")
249
250    def __init__(self, pkg):
251        self._store_cp(pkg)
252
253    short_desc = "still is using manifest1 format, should be using manifest2"
254
255
256class Manifest2Transition(base.Template):
257
258    """
259    various checks for Manifest1/digest transition to Manifest2;
260    check for packages not converted, check for manifest2 packages lacking
261    required checksums
262    """
263
264    feed_type = base.package_feed
265    known_results = (MissingChksum, DeprecatedManifest1)
266    required_checksums = frozenset(("sha1", "sha256", "rmd160", "size"))
267
268    repo_grabber = operator.attrgetter("repo")
269
270    def feed(self, full_pkgset, reporter):
271        # sort it by repo.
272        for repo, pkgset in itertools.groupby(full_pkgset, self.repo_grabber):
273            pkgset = list(pkgset)
274            manifest = pkgset[0].manifest
275            if manifest.version == 1:
276                reporter.add_report(DeprecatedManifest1(pkgset[0]))
277                continue
278
279            seen = set()
280            for pkg in pkgset:
281                for f_inst in (iflatten_instance(pkg.fetchables,
282                    fetch.fetchable)):
283                    if f_inst.filename in seen:
284                        continue
285                    missing = self.required_checksums.difference(f_inst.chksums)
286                    if missing:
287                        reporter.add_report(
288                            MissingChksum(pkg, f_inst.filename, missing,
289                                f_inst.chksums))
290                    seen.add(f_inst.filename)
Note: See TracBrowser for help on using the browser.