| 1 | # Copyright: 2006 Brian Harring <ferringb@gmail.com> |
|---|
| 2 | # Copyright: 2006 Marien Zwart <marienz@gentoo.org> |
|---|
| 3 | # License: GPL2 |
|---|
| 4 | |
|---|
| 5 | |
|---|
| 6 | """Basic reporters and reporter factories.""" |
|---|
| 7 | |
|---|
| 8 | from pkgcore_checks import base |
|---|
| 9 | from pkgcore.config import configurable |
|---|
| 10 | from snakeoil import formatters |
|---|
| 11 | from snakeoil.demandload import demandload |
|---|
| 12 | |
|---|
| 13 | demandload(globals(), |
|---|
| 14 | 'pkgcore_checks:errors', |
|---|
| 15 | 'snakeoil:currying', |
|---|
| 16 | 'snakeoil:pickling', |
|---|
| 17 | 'snakeoil:xml', |
|---|
| 18 | ) |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | class StrReporter(base.Reporter): |
|---|
| 22 | |
|---|
| 23 | """ |
|---|
| 24 | Simple string reporter, pcheck-0.1 behaviour. example: |
|---|
| 25 | sys-apps/portage-2.1-r2: sys-apps/portage-2.1-r2.ebuild has whitespace in indentation on line 169 |
|---|
| 26 | sys-apps/portage-2.1-r2: rdepends ppc-macos: unsolvable default-darwin/macos/10.4, solutions: [ >=app-misc/pax-utils-0.1.13 ] |
|---|
| 27 | sys-apps/portage-2.1-r2: no change in 75 days, keywords [ ~x86-fbsd ] |
|---|
| 28 | """ |
|---|
| 29 | |
|---|
| 30 | # simple reporter; fallback default |
|---|
| 31 | priority = 0 |
|---|
| 32 | |
|---|
| 33 | def __init__(self, out): |
|---|
| 34 | """Initialize. |
|---|
| 35 | @type out: L{snakeoil.formatters.Formatter}. |
|---|
| 36 | """ |
|---|
| 37 | base.Reporter.__init__(self) |
|---|
| 38 | self.out = out |
|---|
| 39 | self.first_report = True |
|---|
| 40 | |
|---|
| 41 | def add_report(self, result): |
|---|
| 42 | if self.first_report: |
|---|
| 43 | self.out.write() |
|---|
| 44 | self.first_report = False |
|---|
| 45 | if result.threshold == base.versioned_feed: |
|---|
| 46 | self.out.write("%s/%s-%s: %s" % (result.category, result.package, |
|---|
| 47 | result.version, result.short_desc)) |
|---|
| 48 | elif result.threshold == base.package_feed: |
|---|
| 49 | self.out.write("%s/%s: %s" % (result.category, result.package, |
|---|
| 50 | result.short_desc)) |
|---|
| 51 | elif result.threshold == base.category_feed: |
|---|
| 52 | self.out.write("%s: %s" % (result.category, result.short_desc)) |
|---|
| 53 | else: |
|---|
| 54 | self.out.write(result.short_desc) |
|---|
| 55 | |
|---|
| 56 | def finish(self): |
|---|
| 57 | if not self.first_report: |
|---|
| 58 | self.out.write() |
|---|
| 59 | |
|---|
| 60 | |
|---|
| 61 | class FancyReporter(base.Reporter): |
|---|
| 62 | |
|---|
| 63 | """ |
|---|
| 64 | groupped colored output, example: |
|---|
| 65 | |
|---|
| 66 | sys-apps/portage |
|---|
| 67 | WrongIndentFound: sys-apps/portage-2.1-r2.ebuild has whitespace in indentation on line 169 |
|---|
| 68 | NonsolvableDeps: sys-apps/portage-2.1-r2: rdepends ppc-macos: unsolvable default-darwin/macos/10.4, solutions: [ >=app-misc/pax-utils-0.1.13 ] |
|---|
| 69 | StaleUnstableKeyword: sys-apps/portage-2.1-r2: no change in 75 days, keywords [ ~x86-fbsd ] |
|---|
| 70 | """ |
|---|
| 71 | |
|---|
| 72 | # default report, akin to repoman |
|---|
| 73 | priority = 1 |
|---|
| 74 | |
|---|
| 75 | def __init__(self, out): |
|---|
| 76 | """Initialize. |
|---|
| 77 | |
|---|
| 78 | @type out: L{snakeoil.formatters.Formatter}. |
|---|
| 79 | """ |
|---|
| 80 | base.Reporter.__init__(self) |
|---|
| 81 | self.out = out |
|---|
| 82 | self.key = None |
|---|
| 83 | |
|---|
| 84 | def add_report(self, result): |
|---|
| 85 | cat = getattr(result, 'category', None) |
|---|
| 86 | pkg = getattr(result, 'package', None) |
|---|
| 87 | if cat is None or pkg is None: |
|---|
| 88 | key = 'unknown' |
|---|
| 89 | else: |
|---|
| 90 | key = '%s/%s' % (cat, pkg) |
|---|
| 91 | if key != self.key: |
|---|
| 92 | self.out.write() |
|---|
| 93 | self.out.write(self.out.bold, key) |
|---|
| 94 | self.key = key |
|---|
| 95 | self.out.first_prefix.append(' ') |
|---|
| 96 | self.out.later_prefix.append(' ') |
|---|
| 97 | s = '' |
|---|
| 98 | if result.threshold == base.versioned_feed: |
|---|
| 99 | s = "version %s: " % result.version |
|---|
| 100 | self.out.write( |
|---|
| 101 | self.out.fg('yellow'), result.__class__.__name__, self.out.reset, |
|---|
| 102 | ': ', s, result.short_desc) |
|---|
| 103 | self.out.first_prefix.pop() |
|---|
| 104 | self.out.later_prefix.pop() |
|---|
| 105 | |
|---|
| 106 | |
|---|
| 107 | class XmlReporter(base.Reporter): |
|---|
| 108 | |
|---|
| 109 | """ |
|---|
| 110 | dump an xml feed of reports |
|---|
| 111 | """ |
|---|
| 112 | |
|---|
| 113 | # xml report, shouldn't be used but in worst case. |
|---|
| 114 | priority = -1000 |
|---|
| 115 | |
|---|
| 116 | repo_template = "<result><msg>%s</msg></result>" |
|---|
| 117 | cat_template = "<result><category>%(category)s</category><msg>%(msg)s</msg></result>" |
|---|
| 118 | pkg_template = ("<result><category>%(category)s</category>" |
|---|
| 119 | "<package>%(package)s</package><msg>%(msg)s</msg></result>") |
|---|
| 120 | ver_template = ("<result><category>%(category)s</category>" |
|---|
| 121 | "<package>%(package)s</package><version>%(version)s</version>" |
|---|
| 122 | "<msg>%(msg)s</msg></result>") |
|---|
| 123 | |
|---|
| 124 | threshold_map = {base.repository_feed:repo_template, |
|---|
| 125 | base.category_feed:cat_template, |
|---|
| 126 | base.package_feed:pkg_template, |
|---|
| 127 | base.versioned_feed:ver_template, |
|---|
| 128 | base.ebuild_feed:ver_template |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | def __init__(self, out): |
|---|
| 132 | """Initialize. |
|---|
| 133 | |
|---|
| 134 | @type out: L{snakeoil.formatters.Formatter}. |
|---|
| 135 | """ |
|---|
| 136 | base.Reporter.__init__(self) |
|---|
| 137 | self.out = out |
|---|
| 138 | |
|---|
| 139 | def start(self): |
|---|
| 140 | self.out.write('<checks>') |
|---|
| 141 | |
|---|
| 142 | def add_report(self, result): |
|---|
| 143 | d = dict((k, getattr(result, k, '')) for k in |
|---|
| 144 | ("category", "package", "version")) |
|---|
| 145 | d["msg"] = xml.escape(result.short_desc) |
|---|
| 146 | self.out.write(self.threshold_map[result.threshold] % d) |
|---|
| 147 | |
|---|
| 148 | def finish(self): |
|---|
| 149 | self.out.write('</checks>') |
|---|
| 150 | |
|---|
| 151 | |
|---|
| 152 | class MultiplexReporter(base.Reporter): |
|---|
| 153 | |
|---|
| 154 | def __init__(self, *reporters): |
|---|
| 155 | if len(reporters) < 2: |
|---|
| 156 | raise ValueError("need at least two reporters") |
|---|
| 157 | base.Reporter.__init__(self) |
|---|
| 158 | self.reporters = tuple(reporters) |
|---|
| 159 | |
|---|
| 160 | def start(self): |
|---|
| 161 | for x in self.reporters: |
|---|
| 162 | x.start() |
|---|
| 163 | |
|---|
| 164 | def add_report(self, result): |
|---|
| 165 | for x in self.reporters: |
|---|
| 166 | x.add_report(result) |
|---|
| 167 | |
|---|
| 168 | def finish(self): |
|---|
| 169 | for x in self.reporters: |
|---|
| 170 | x.finish() |
|---|
| 171 | |
|---|
| 172 | |
|---|
| 173 | def make_configurable_reporter_factory(klass): |
|---|
| 174 | @configurable({'dest': 'str'}, typename='pcheck_reporter_factory') |
|---|
| 175 | def configurable_reporter_factory(dest=None): |
|---|
| 176 | if dest is None: |
|---|
| 177 | return klass |
|---|
| 178 | def reporter_factory(out): |
|---|
| 179 | try: |
|---|
| 180 | f = open(dest, 'w') |
|---|
| 181 | except (IOError, OSError), e: |
|---|
| 182 | raise errors.ReporterInitError( |
|---|
| 183 | 'Cannot write to %r (%s)' % (dest, e)) |
|---|
| 184 | return klass(formatters.PlainTextFormatter(f)) |
|---|
| 185 | return reporter_factory |
|---|
| 186 | return configurable_reporter_factory |
|---|
| 187 | |
|---|
| 188 | xml_reporter = make_configurable_reporter_factory(XmlReporter) |
|---|
| 189 | xml_reporter.__name__ = 'xml_reporter' |
|---|
| 190 | plain_reporter = make_configurable_reporter_factory(StrReporter) |
|---|
| 191 | plain_reporter.__name__ = 'plain_reporter' |
|---|
| 192 | fancy_reporter = make_configurable_reporter_factory(FancyReporter) |
|---|
| 193 | fancy_reporter.__name__ = 'fancy_reporter' |
|---|
| 194 | |
|---|
| 195 | @configurable({'reporters': 'refs:pcheck_reporter_factory'}, |
|---|
| 196 | typename='pcheck_reporter_factory') |
|---|
| 197 | def multiplex_reporter(reporters): |
|---|
| 198 | def make_multiplex_reporter(out): |
|---|
| 199 | return MultiplexReporter(*list(factory(out) for factory in reporters)) |
|---|
| 200 | return make_multiplex_reporter |
|---|