1 2# 3# spyne - Copyright (C) Spyne contributors. 4# 5# This library is free software; you can redistribute it and/or 6# modify it under the terms of the GNU Lesser General Public 7# License as published by the Free Software Foundation; either 8# version 2.1 of the License, or (at your option) any later version. 9# 10# This library is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13# Lesser General Public License for more details. 14# 15# You should have received a copy of the GNU Lesser General Public 16# License along with this library; if not, write to the Free Software 17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 18# 19 20 21"""The `spyne.util.xml` module contains various Xml and Xml Schema related 22utility functions. 23""" 24from inspect import isgenerator 25 26from lxml import etree 27 28from os.path import dirname 29from os.path import abspath 30 31from spyne import ServiceBase, Application, srpc 32from spyne.context import FakeContext 33from spyne.interface import Interface 34from spyne.interface.xml_schema import XmlSchema 35from spyne.interface.xml_schema.parser import XmlSchemaParser, Thier_repr, PARSER 36from spyne.protocol import ProtocolMixin 37from spyne.protocol.cloth import XmlCloth 38 39from spyne.protocol.xml import XmlDocument 40from spyne.util.appreg import unregister_application 41from spyne.util.six import BytesIO 42from spyne.util.tlist import tlist 43 44 45class FakeApplication(object): 46 def __init__(self, default_namespace): 47 self.tns = default_namespace 48 self.services = () 49 self.classes = () 50 51 52def get_schema_documents(models, default_namespace=None): 53 """Returns the schema documents in a dict whose keys are namespace prefixes 54 and values are Element objects. 55 56 :param models: A list of spyne.model classes that will be represented in 57 the schema. 58 """ 59 60 if default_namespace is None: 61 default_namespace = models[0].get_namespace() 62 63 fake_app = FakeApplication(default_namespace) 64 65 interface = Interface(fake_app) 66 for m in models: 67 m.resolve_namespace(m, default_namespace) 68 interface.add_class(m) 69 interface.populate_interface(fake_app) 70 71 document = XmlSchema(interface) 72 document.build_interface_document() 73 74 return document.get_interface_document() 75 76 77def get_validation_schema(models, default_namespace=None): 78 """Returns the validation schema object for the given models. 79 80 :param models: A list of spyne.model classes that will be represented in 81 the schema. 82 """ 83 84 if default_namespace is None: 85 default_namespace = models[0].get_namespace() 86 87 fake_app = FakeApplication(default_namespace) 88 89 interface = Interface(fake_app) 90 for m in models: 91 m.resolve_namespace(m, default_namespace) 92 interface.add_class(m) 93 94 schema = XmlSchema(interface) 95 schema.build_validation_schema() 96 97 return schema.validation_schema 98 99 100def _dig(par): 101 for elt in par: 102 elt.tag = elt.tag.split('}')[-1] 103 _dig(elt) 104 105 106_xml_object = XmlDocument() 107 108 109def get_object_as_xml(inst, cls=None, root_tag_name=None, no_namespace=False): 110 """Returns an ElementTree representation of a 111 :class:`spyne.model.complex.ComplexModel` subclass. 112 113 :param inst: The instance of the class to be serialized. 114 :param cls: The class to be serialized. Optional. 115 :param root_tag_name: The root tag string to use. Defaults to the output of 116 ``value.__class__.get_type_name_ns()``. 117 :param no_namespace: When true, namespace information is discarded. 118 """ 119 120 if cls is None: 121 cls = inst.__class__ 122 123 parent = etree.Element("parent") 124 _xml_object.to_parent(None, cls, inst, parent, cls.get_namespace(), 125 root_tag_name) 126 if no_namespace: 127 _dig(parent) 128 etree.cleanup_namespaces(parent) 129 130 return parent[0] 131 132 133def get_object_as_xml_polymorphic(inst, cls=None, root_tag_name=None, 134 no_namespace=False): 135 """Returns an ElementTree representation of a 136 :class:`spyne.model.complex.ComplexModel` subclass. 137 138 :param inst: The instance of the class to be serialized. 139 :param cls: The class to be serialized. Optional. 140 :param root_tag_name: The root tag string to use. Defaults to the output of 141 ``value.__class__.get_type_name_ns()``. 142 :param no_namespace: When true, namespace information is discarded. 143 """ 144 145 if cls is None: 146 cls = inst.__class__ 147 148 if no_namespace: 149 app = Application([ServiceBase], tns="", 150 out_protocol=XmlDocument(polymorphic=True)) 151 else: 152 tns = cls.get_namespace() 153 if tns is None: 154 raise ValueError( 155 "Either set a namespace for %r or pass no_namespace=True" 156 % (cls, )) 157 158 class _DummyService(ServiceBase): 159 @srpc(cls) 160 def f(_): 161 pass 162 163 app = Application([_DummyService], tns=tns, 164 out_protocol=XmlDocument(polymorphic=True)) 165 166 unregister_application(app) 167 168 parent = etree.Element("parent", nsmap=app.interface.nsmap) 169 170 app.out_protocol.to_parent(None, cls, inst, parent, cls.get_namespace(), 171 root_tag_name) 172 173 if no_namespace: 174 _dig(parent) 175 176 etree.cleanup_namespaces(parent) 177 178 return parent[0] 179 180 181def get_xml_as_object_polymorphic(elt, cls): 182 """Returns a native :class:`spyne.model.complex.ComplexModel` child from an 183 ElementTree representation of the same class. 184 185 :param elt: The xml document to be deserialized. 186 :param cls: The class the xml document represents. 187 """ 188 189 tns = cls.get_namespace() 190 if tns is None: 191 raise ValueError("Please set a namespace for %r" % (cls, )) 192 193 class _DummyService(ServiceBase): 194 @srpc(cls) 195 def f(_): 196 pass 197 198 app = Application([_DummyService], tns=tns, 199 in_protocol=XmlDocument(polymorphic=True)) 200 201 unregister_application(app) 202 203 return app.in_protocol.from_element(FakeContext(app=app), cls, elt) 204 205 206def get_object_as_xml_cloth(inst, cls=None, no_namespace=False, encoding='utf8'): 207 """Returns an ElementTree representation of a 208 :class:`spyne.model.complex.ComplexModel` subclass. 209 210 :param inst: The instance of the class to be serialized. 211 :param cls: The class to be serialized. Optional. 212 :param root_tag_name: The root tag string to use. Defaults to the output of 213 ``value.__class__.get_type_name_ns()``. 214 :param no_namespace: When true, namespace information is discarded. 215 """ 216 217 if cls is None: 218 cls = inst.__class__ 219 220 if cls.get_namespace() is None and no_namespace is None: 221 no_namespace = True 222 223 if no_namespace is None: 224 no_namespace = False 225 226 ostr = BytesIO() 227 xml_cloth = XmlCloth(use_ns=(not no_namespace)) 228 ctx = FakeContext() 229 with etree.xmlfile(ostr, encoding=encoding) as xf: 230 ctx.outprot_ctx.doctype_written = False 231 ctx.protocol.prot_stack = tlist([], ProtocolMixin) 232 tn = cls.get_type_name() 233 ret = xml_cloth.subserialize(ctx, cls, inst, xf, tn) 234 235 assert not isgenerator(ret) 236 237 return ostr.getvalue() 238 239 240def get_xml_as_object(elt, cls): 241 """Returns a native :class:`spyne.model.complex.ComplexModel` child from an 242 ElementTree representation of the same class. 243 244 :param elt: The xml document to be deserialized. 245 :param cls: The class the xml document represents. 246 """ 247 return _xml_object.from_element(None, cls, elt) 248 249 250def parse_schema_string(s, files={}, repr_=Thier_repr(with_ns=False), 251 skip_errors=False): 252 """Parses a schema string and returns a _Schema object. 253 254 :param s: The string or bytes object that contains the schema document. 255 :param files: A dict that maps namespaces to path to schema files that 256 contain the schema document for those namespaces. 257 :param repr_: A callable that functions as `repr`. 258 :param skip_errors: Skip parsing errors and return a partial schema. 259 See debug log for details. 260 261 :return: :class:`spyne.interface.xml_schema.parser._Schema` instance. 262 """ 263 264 elt = etree.fromstring(s, parser=PARSER) 265 return XmlSchemaParser(files, repr_=repr_, 266 skip_errors=skip_errors).parse_schema(elt) 267 268 269def parse_schema_element(elt, files={}, repr_=Thier_repr(with_ns=False), 270 skip_errors=False): 271 """Parses a `<xs:schema>` element and returns a _Schema object. 272 273 :param elt: The `<xs:schema>` element, an lxml.etree._Element instance. 274 :param files: A dict that maps namespaces to path to schema files that 275 contain the schema document for those namespaces. 276 :param repr_: A callable that functions as `repr`. 277 :param skip_errors: Skip parsing errors and return a partial schema. 278 See debug log for details. 279 280 :return: :class:`spyne.interface.xml_schema.parser._Schema` instance. 281 """ 282 283 return XmlSchemaParser(files, repr_=repr_, 284 skip_errors=skip_errors).parse_schema(elt) 285 286 287def parse_schema_file(file_name, files=None, repr_=Thier_repr(with_ns=False), 288 skip_errors=False): 289 """Parses a schema file and returns a _Schema object. Schema files typically 290 have the `*.xsd` extension. 291 292 :param file_name: The path to the file that contains the schema document 293 to be parsed. 294 :param files: A dict that maps namespaces to path to schema files that 295 contain the schema document for those namespaces. 296 :param repr_: A callable that functions as `repr`. 297 :param skip_errors: Skip parsing errors and return a partial schema. 298 See debug log for details. 299 300 :return: :class:`spyne.interface.xml_schema.parser._Schema` instance. 301 """ 302 303 if files is None: 304 files = dict() 305 306 elt = etree.fromstring(open(file_name, 'rb').read(), parser=PARSER) 307 wd = abspath(dirname(file_name)) 308 return XmlSchemaParser(files, wd, repr_=repr_, 309 skip_errors=skip_errors).parse_schema(elt) 310