root/releases/pkgcore-checks/0.3.4/pkgcore_checks/metadata_xml.py @ ferringb%2540gmail.com-20070303132903-k4w87hgppwrgrw01

Revision ferringb%2540gmail.com-20070303132903-k4w87hgppwrgrw01, 7.1 KB (checked in by Brian Harring <ferringb@…>, 23 months ago)

drop digest specific awareness due to portage filtering digest-* regardless of if the repo is pure m2; back down the existance check for files directory if the package is m2, add check for missing metadata_xml

Line 
1# Copyright: 2006 Brian Harring <ferringb@gmail.com>
2# License: GPL2
3
4import os
5from pkgcore_checks import base
6from pkgcore.util.demandload import demandload
7demandload(
8    globals(),
9    "urllib:urlopen "
10    "tempfile:NamedTemporaryFile "
11    "libxml2 "
12    "pkgcore.log:logger "
13    "pkgcore.spawn:spawn,find_binary ")
14
15
16class base_MissingXml(base.Result):
17    """required xml file is missing"""
18    __slots__ = ('category', 'package', 'filename')
19   
20    def __init__(self, filename, category, package=None):
21        base.Result.__init__(self)
22        self.category = category
23        self.package = package
24        self.filename = filename
25
26    @property
27    def short_desc(self):
28        return "%s is missing" % self.filename
29
30
31class base_BadlyFormedXml(base.Result):
32    """xml isn't well formed"""
33    __slots__ = ("category", "package", "filename")
34   
35    def __init__(self, filename, category, package=None):
36        base.Result.__init__(self)
37        self.category = category
38        self.package = package
39        self.filename = filename
40   
41    @property
42    def short_desc(self):
43        return "%s is not well formed xml" % self.filename
44   
45
46class base_InvalidXml(base.Result):
47    """xml fails dtd validation"""
48    __slots__ = ("category", "package", "filename")
49   
50    def __init__(self, filename, category, package=None):
51        base.Result.__init__(self, filename, category, package=None)
52        self.category = category
53        self.package = package
54        self.filename = filename
55
56    @property
57    def short_desc(self):
58        return "%s violates metadata.dtd" % self.filename
59
60
61class PkgMissingMetadataXml(base_MissingXml):
62    __slots__ = ()
63    threshold = base.package_feed
64
65class CatMissingMetadataXml(base_MissingXml):
66    __slots__ = ()
67    threshold = base.category_feed
68
69class PkgInvalidXml(base_InvalidXml):
70    __slots__ = ()
71    threshold = base.package_feed
72
73class CatInvalidXml(base_InvalidXml):
74    __slots__ = ()
75    threshold = base.category_feed
76
77class PkgBadlyFormedXml(base_BadlyFormedXml):
78    __slots__ = ()
79    threshold = base.package_feed
80
81class CatBadlyFormedXml(base_BadlyFormedXml):
82    __slots__ = ()
83    threshold = base.category_feed
84
85
86class base_check(base.Template):
87    """base class for metadata.xml scans"""
88
89    dtd_url = "http://www.gentoo.org/dtd/metadata.dtd"
90    misformed_error = None
91    invalid_error = None
92    missing_error = None
93
94    @classmethod
95    def mangle_option_parser(cls, parser):
96        if not parser.has_option('--metadata-dtd'):
97            parser.add_option(
98                '--metadata-dtd', help='location to cache %s' % (cls.dtd_url,))
99
100    def __init__(self, options):
101        base.Template.__init__(self, options)
102        self.base = getattr(options.src_repo, "base", None)
103        self.dtd_file = None
104
105    def start(self):
106        loc = self.base
107        if self.base is not None:
108            loc = os.path.join(self.base, "metadata", "dtd", "metadata.dtd")
109            if not os.path.exists(loc):
110                loc = None
111
112        if loc is not None:
113            self.dtd_loc = loc
114        else:
115            self.dtd_loc = self.options.metadata_dtd
116            if self.dtd_loc is not None:
117                if not os.path.exists(self.dtd_loc):
118                    logger.warn('metadata.dtd cannot be opened, refetching')
119                    dtd = urlopen(self.dtd_url).read()
120                    try:
121                        open(self.dtd_loc, 'w').write(dtd)
122                    except (IOError, OSError), e:
123                        logger.warn(
124                            'metadata.dtd could not be written (%s)', e)
125                        self.dtd_loc = None
126            if self.dtd_loc is None:
127                dtd = urlopen(self.dtd_url).read()
128                self.dtd_file = NamedTemporaryFile()
129                self.dtd_loc = self.dtd_file.name
130                os.chmod(self.dtd_loc, 0644)
131                self.dtd_file.write(dtd)
132                self.dtd_file.flush()
133        try:
134            self.validator = libxml_parser(self.dtd_loc).validate
135        except ImportError:
136            self.validator = xmllint_parser(self.dtd_loc).validate
137        self.last_seen = None
138
139    def feed(self, thing, reporter):
140        raise NotImplementedError(self.feed)
141
142    def finish(self, reporter):
143        self.last_seen = None
144
145    def check_file(self, loc):
146        if not os.path.exists(loc):
147            return self.missing_error
148        ret = self.validator(loc)
149        if ret == 0:
150            return None
151        elif ret == 1:
152            return self.misformed_error
153        elif ret == 2:
154            return self.invalid_error
155        raise AssertionError("got %r from validator, which isn't "
156            "valid" % ret)
157
158
159class PackageMetadataXmlCheck(base_check):
160    """package level metadata.xml scans"""
161
162    feed_type = base.versioned_feed
163    scope = base.package_scope
164    misformed_error = PkgBadlyFormedXml
165    invalid_error = PkgInvalidXml
166    missing_error = PkgMissingMetadataXml
167
168    known_results = (PkgBadlyFormedXml, PkgInvalidXml)
169
170    def feed(self, pkg, reporter):
171        if self.last_seen == pkg.key:
172            return
173        self.last_seen = pkg.key
174        loc = os.path.join(os.path.dirname(pkg.ebuild.get_path()),
175                           "metadata.xml")
176        ret = self.check_file(loc)
177        if ret is not None:
178            reporter.add_report(ret(loc, pkg.category, pkg.package))
179
180
181class CategoryMetadataXmlCheck(base_check):
182    """metadata.xml scans"""
183    feed_type = base.versioned_feed
184    scope = base.category_scope
185    misformed_error = CatBadlyFormedXml
186    invalid_error = CatInvalidXml
187    missing_error = CatMissingMetadataXml
188
189    known_results = (CatBadlyFormedXml, CatInvalidXml)
190
191    dtd_url = "http://www.gentoo.org/dtd/metadata.dtd"
192
193    def feed(self, pkg, reporter):
194        if self.last_seen == pkg.category:
195            return
196        self.last_seen = pkg.category
197        loc = os.path.join(self.base, pkg.category, "metadata.xml")
198        ret = self.check_file(loc)
199        if ret is not None:
200            reporter.add_report(ret(loc, pkg.category))
201
202
203class libxml_parser(object):
204
205    def __init__(self, loc):
206        self.parsed_dtd = libxml2.parseDTD(None, loc)
207        self.validator = libxml2.newValidCtxt()
208   
209    def validate(self, loc):
210        """
211        @param loc: location to verify
212        @return: 0 no issue
213                 1 badly formed
214                 2 invalid xml
215        """
216        xml = libxml2.createFileParserCtxt(loc)
217        xml.parseDocument()
218        if not xml.isValid():
219            return 2
220        elif not xml.doc().validateDtd(self.validator, self.parsed_dtd):
221            return 1
222        return 0
223
224
225class xmllint_parser(object):
226
227    def __init__(self, loc):
228        self.dtd_loc = loc
229        self.bin_loc = find_binary("xmllint")
230   
231    def validate(self, loc):
232        """
233        @param loc: location to verify
234        @return: 0 no issue
235                 1 badly formed
236                 2 invalid xml
237        """
238        ret = spawn([self.bin_loc, "--nonet", "--noout", "--dtdvalid",
239            self.dtd_loc, loc], fd_pipes={})
240
241        if ret == 1:
242            return 1
243
244        elif ret == 3:
245            return 2
246
247        return 0
Note: See TracBrowser for help on using the browser.