root/releases/pkgcore-checks/0.3.5/pkgcore_checks/metadata_xml.py @ ferringb%2540gmail.com-20070408184900-k7li2y7xdgo2sbto

Revision ferringb%2540gmail.com-20070408184900-k7li2y7xdgo2sbto, 7.2 KB (checked in by Brian Harring <ferringb@…>, 21 months ago)

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