1 | #!/usr/bin/python -u
|
---|
2 | #
|
---|
3 | # This tests custom input callbacks
|
---|
4 | #
|
---|
5 | import sys
|
---|
6 | import libxml2
|
---|
7 | try:
|
---|
8 | import StringIO
|
---|
9 | str_io = StringIO.StringIO
|
---|
10 | except:
|
---|
11 | import io
|
---|
12 | str_io = io.StringIO
|
---|
13 |
|
---|
14 | # We implement a new scheme, py://strings/ that will reference this dictionary
|
---|
15 | pystrings = {
|
---|
16 | 'catalogs/catalog.xml' :
|
---|
17 | '''<?xml version="1.0" encoding="utf-8"?>
|
---|
18 | <!DOCTYPE catalog PUBLIC "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN" "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd">
|
---|
19 | <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
|
---|
20 | <rewriteSystem systemIdStartString="http://example.com/dtds/" rewritePrefix="../dtds/"/>
|
---|
21 | </catalog>''',
|
---|
22 |
|
---|
23 | 'xml/sample.xml' :
|
---|
24 | '''<?xml version="1.0" encoding="utf-8"?>
|
---|
25 | <!DOCTYPE root SYSTEM "http://example.com/dtds/sample.dtd">
|
---|
26 | <root>&sample.entity;</root>''',
|
---|
27 |
|
---|
28 | 'dtds/sample.dtd' :
|
---|
29 | '''
|
---|
30 | <!ELEMENT root (#PCDATA)>
|
---|
31 | <!ENTITY sample.entity "replacement text">'''
|
---|
32 | }
|
---|
33 |
|
---|
34 | prefix = "py://strings/"
|
---|
35 | startURL = prefix + "xml/sample.xml"
|
---|
36 | catURL = prefix + "catalogs/catalog.xml"
|
---|
37 |
|
---|
38 | def my_input_cb(URI):
|
---|
39 | if not(URI.startswith(prefix)):
|
---|
40 | return None
|
---|
41 | path = URI[len(prefix):]
|
---|
42 | if path not in pystrings:
|
---|
43 | return None
|
---|
44 | return str_io(pystrings[path])
|
---|
45 |
|
---|
46 |
|
---|
47 | def run_test(desc, docpath, catalog, exp_status="verified", exp_err=[], test_callback=None,
|
---|
48 | root_name="root", root_content="replacement text"):
|
---|
49 | opts = libxml2.XML_PARSE_DTDLOAD | libxml2.XML_PARSE_NONET | libxml2.XML_PARSE_COMPACT
|
---|
50 | actual_err = []
|
---|
51 |
|
---|
52 | def my_global_error_cb(ctx, msg):
|
---|
53 | actual_err.append((-1, msg))
|
---|
54 | def my_ctx_error_cb(arg, msg, severity, reserved):
|
---|
55 | actual_err.append((severity, msg))
|
---|
56 |
|
---|
57 | libxml2.registerErrorHandler(my_global_error_cb, None)
|
---|
58 | try:
|
---|
59 | parser = libxml2.createURLParserCtxt(docpath, opts)
|
---|
60 | parser.setErrorHandler(my_ctx_error_cb, None)
|
---|
61 | if catalog is not None:
|
---|
62 | parser.addLocalCatalog(catalog)
|
---|
63 | if test_callback is not None:
|
---|
64 | test_callback()
|
---|
65 | parser.parseDocument()
|
---|
66 | doc = parser.doc()
|
---|
67 | actual_status = "loaded"
|
---|
68 | e = doc.getRootElement()
|
---|
69 | if e.name == root_name and e.content == root_content:
|
---|
70 | actual_status = "verified"
|
---|
71 | doc.freeDoc()
|
---|
72 | except libxml2.parserError:
|
---|
73 | actual_status = "not loaded"
|
---|
74 |
|
---|
75 | if actual_status != exp_status:
|
---|
76 | print("Test '%s' failed: expect status '%s', actual '%s'" % (desc, exp_status, actual_status))
|
---|
77 | sys.exit(1)
|
---|
78 | elif actual_err != exp_err:
|
---|
79 | print("Test '%s' failed" % desc)
|
---|
80 | print("Expect errors:")
|
---|
81 | for s,m in exp_err: print(" [%2d] '%s'" % (s,m))
|
---|
82 | print("Actual errors:")
|
---|
83 | for s,m in actual_err: print(" [%2d] '%s'" % (s,m))
|
---|
84 | sys.exit(1)
|
---|
85 |
|
---|
86 |
|
---|
87 | # Check that we cannot read custom schema without custom callback
|
---|
88 | run_test(desc="Loading entity without custom callback",
|
---|
89 | docpath=startURL, catalog=None,
|
---|
90 | exp_status="not loaded", exp_err=[
|
---|
91 | (-1, "I/O "),
|
---|
92 | (-1, "warning : "),
|
---|
93 | (-1, "failed to load external entity \"py://strings/xml/sample.xml\"\n")
|
---|
94 | ])
|
---|
95 |
|
---|
96 | # Register handler and try to load the same entity
|
---|
97 | libxml2.registerInputCallback(my_input_cb)
|
---|
98 | run_test(desc="Loading entity with custom callback",
|
---|
99 | docpath=startURL, catalog=None,
|
---|
100 | exp_status="loaded", exp_err=[
|
---|
101 | (-1, "Attempt to load network entity http://example.com/dtds/sample.dtd"),
|
---|
102 | ( 4, "Entity 'sample.entity' not defined\n")
|
---|
103 | ])
|
---|
104 |
|
---|
105 | # Register a catalog (also accessible via pystr://) and retry
|
---|
106 | run_test(desc="Loading entity with custom callback and catalog",
|
---|
107 | docpath=startURL, catalog=catURL)
|
---|
108 |
|
---|
109 | # Unregister custom callback when parser is already created
|
---|
110 | run_test(desc="Loading entity and unregistering callback",
|
---|
111 | docpath=startURL, catalog=catURL,
|
---|
112 | test_callback=lambda: libxml2.popInputCallbacks(),
|
---|
113 | exp_status="loaded", exp_err=[
|
---|
114 | ( 3, "failed to load external entity \"py://strings/dtds/sample.dtd\"\n"),
|
---|
115 | ( 4, "Entity 'sample.entity' not defined\n")
|
---|
116 | ])
|
---|
117 |
|
---|
118 | # Try to load the document again
|
---|
119 | run_test(desc="Retry loading document after unregistering callback",
|
---|
120 | docpath=startURL, catalog=catURL,
|
---|
121 | exp_status="not loaded", exp_err=[
|
---|
122 | (-1, "I/O "),
|
---|
123 | (-1, "warning : "),
|
---|
124 | (-1, "failed to load external entity \"py://strings/xml/sample.xml\"\n")
|
---|
125 | ])
|
---|
126 |
|
---|
127 | # But should be able to read standard I/O yet...
|
---|
128 | run_test(desc="Loading using standard i/o after unregistering callback",
|
---|
129 | docpath="tst.xml", catalog=None,
|
---|
130 | root_name='doc', root_content='bar')
|
---|
131 |
|
---|
132 | # Now pop ALL input callbacks, should fail to load even standard I/O
|
---|
133 | try:
|
---|
134 | while True:
|
---|
135 | libxml2.popInputCallbacks()
|
---|
136 | except IndexError:
|
---|
137 | pass
|
---|
138 |
|
---|
139 | run_test(desc="Loading using standard i/o after unregistering all callbacks",
|
---|
140 | docpath="tst.xml", catalog=None,
|
---|
141 | exp_status="not loaded", exp_err=[
|
---|
142 | (-1, "I/O "),
|
---|
143 | (-1, "warning : "),
|
---|
144 | (-1, "failed to load external entity \"tst.xml\"\n")
|
---|
145 | ])
|
---|
146 |
|
---|
147 | print("OK")
|
---|
148 | sys.exit(0);
|
---|