1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
# Copyright (C) 2003 by Intevation GmbH
# Authors:
# Bernhard Herzog <bh@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with the software for details.
"""XML support code for the test cases"""
__version__ = "$Revision: 1683 $"
# $Source$
# $Id: xmlsupport.py 1683 2003-08-28 15:20:57Z bh $
import sys
import os
from StringIO import StringIO
import xml.sax
import xml.sax.handler
from xml.sax import make_parser, ErrorHandler, SAXNotRecognizedException
import support
try:
import pyRXP
except ImportError:
pyRXP = None
class ValidationTest:
"""Mix-in class for tests that want to check for validity of XML data"""
# If true, at least one test case tried to validate XML data but
# couldn't because PyRXP is not available
validation_attempt_ignored = False
def validate_data(self, data):
"""Validate the XML data"""
if pyRXP is not None:
parser = pyRXP.Parser()
try:
parser.parse(data, eoCB = self.rxp_eo_cb)
except pyRXP.error, val:
raise AssertionError, str(val), sys.exc_info()[2]
else:
ValidationTest.validation_attempt_ignored = True
def rxp_eo_cb(self, filename):
"""Resolve an eternal entity
In the Thuban test cases the only external entities that have to
be resolved are the DTDs which now live in the resource
directory. So, interpret any filename relative to that
directory.
"""
return os.path.join(support.resource_dir(), "XML", filename)
#
# Classes and functions to convert XML to a normalized list
# representation for comparisons
#
class SaxEventLister(xml.sax.handler.ContentHandler):
"""Create a normalized list representation containing the SAX events
The normalization includes the following
- The attribute dictionary of a staertElement event is converted to
a sorted list of (key, value) pairs
- ID and IDREF attribute values are normalized in such a way that
two documents that only use different values for IDs can still
lead to the same normalized representation.
The implementation of this feature assumes that all IDs are
defined before they are used. The normalized ID values are of the
form 'D<NUM>' where <NUM> is a counter starting with 0, so the
first ID value will become 'D0', the second 'D1', etc.
Which attributes are IDs or IDREFS is defined with the
correspoding constructor arguments.
- Filenames are normalized with os.path.normpath. Which attributes
are filenames is defiend with the corresponding constructor
argument.
"""
def __init__(self, ids = (), idrefs = (), filenames = ()):
"""Initialize the SaxEventLister
The ids and idrefs parameters should be lists of (element, attr)
pairs where element is the name of an attribute as passed to the
startElementNS method and attr is the name of an attribute as
used in the mapping passed to startElementNS, so both name and
attr usually must include the namespace.
The filenames parameter should be a sequence of the same form as
ids and idrefs identifying the attributes which are filenames
that should be normalized.
"""
self.eventlist = []
self.ids = ids
self.idrefs = idrefs
self.idremap = {}
self.filenames = filenames
def startElementNS(self, name, qname, attrs):
items = attrs.items()
items.sort()
for i, (attr, value) in zip(range(len(items)), items):
#print '++++'
#print self.idremap
#print name, attr, value
if (name, attr) in self.ids:
newid = len(self.idremap)
self.idremap[value] = "D" + str(newid)
value = self.idremap[value]
elif (name, attr) in self.idrefs:
value = self.idremap[value]
elif (name, attr) in self.filenames:
value = os.path.normpath(value)
items[i] = (attr, value)
#print name, attr, value
self.eventlist.append(("start", name, items))
def endElementNS(self, name, qname):
self.eventlist.append(("end", name))
def sax_eventlist(data = None, filename = None,
ids = (), idrefs = (), filenames = ()):
"""Return a list of SAX event generated for a given XML source
The xml source may either be a string with the actual XML, in which
case it should be given as the keyword argument data or the name of
an xml file given as the keyword argument filename
"""
if filename is not None:
data = open(filename).read()
handler = SaxEventLister(ids = ids, idrefs = idrefs, filenames = filenames)
parser = make_parser()
parser.setContentHandler(handler)
parser.setErrorHandler(ErrorHandler())
parser.setFeature(xml.sax.handler.feature_namespaces, 1)
#
# see comment at the end of Thuban/Model/load.py
#
try:
parser.setFeature(xml.sax.handler.feature_validation, 0)
parser.setFeature(xml.sax.handler.feature_external_ges, 0)
parser.setFeature(xml.sax.handler.feature_external_pes, 0)
except SAXNotRecognizedException:
pass
inpsrc = xml.sax.InputSource()
inpsrc.setByteStream(StringIO(data))
parser.parse(inpsrc)
return handler.eventlist
def print_summary_message():
"""Print a summary message about validation tests
Currently simply print a message about pyRXP not being available if
a test case's attempt to validate XML was ignored because of that.
"""
if ValidationTest.validation_attempt_ignored:
print "XML validation attempts ignored because pyRXP is not available"
|