root/releases/pkgcore-checks/0.3/pkgcore_checks/metadata_xml.py @ ferringb%2540gmail.com-20070207174621-yb3eqrqc6jtmdgse

Revision ferringb%2540gmail.com-20070207174621-yb3eqrqc6jtmdgse, 6.4 KB (checked in by Brian Harring <ferringb@…>, 2 years ago)

kill off to_str, no longer used; use short_desc and long_desc properties instead

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