1#!/usr/bin/env python 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 20from __future__ import print_function 21 22import logging 23logger = logging.getLogger(__name__) 24 25import unittest 26 27from lxml import etree, html 28from lxml.builder import E 29 30from spyne import ComplexModel, XmlAttribute, Unicode, Array, Integer, \ 31 SelfReference, XmlData 32from spyne.protocol.cloth import XmlCloth 33from spyne.test import FakeContext 34from spyne.util.six import BytesIO 35 36 37class TestModelCloth(unittest.TestCase): 38 def test_root_html(self): 39 class SomeObject(ComplexModel): 40 class Attributes(ComplexModel.Attributes): 41 html_cloth = html.fromstring("<html><body spyne-root></body></html>") 42 43 assert SomeObject.Attributes._html_cloth is None 44 assert SomeObject.Attributes._html_root_cloth is not None 45 46 def test_html(self): 47 class SomeObject(ComplexModel): 48 class Attributes(ComplexModel.Attributes): 49 html_cloth = html.fromstring('<html><body spyne-id="za"></body></html>') 50 51 assert SomeObject.Attributes._html_cloth is not None 52 assert SomeObject.Attributes._html_root_cloth is None 53 54 def test_root_xml(self): 55 class SomeObject(ComplexModel): 56 class Attributes(ComplexModel.Attributes): 57 xml_cloth = etree.fromstring('<html><body spyne-root=""></body></html>') 58 59 assert SomeObject.Attributes._xml_cloth is None 60 assert SomeObject.Attributes._xml_root_cloth is not None 61 62 def test_xml(self): 63 class SomeObject(ComplexModel): 64 class Attributes(ComplexModel.Attributes): 65 xml_cloth = html.fromstring('<html><body foo="za"></body></html>') 66 67 assert SomeObject.Attributes._xml_cloth is not None 68 assert SomeObject.Attributes._xml_root_cloth is None 69 70 71class TestXmlClothToParent(unittest.TestCase): 72 def setUp(self): 73 self.ctx = FakeContext() 74 self.stream = BytesIO() 75 logging.basicConfig(level=logging.DEBUG) 76 77 def _run(self, inst, cls=None): 78 if cls is None: 79 cls = inst.__class__ 80 81 with etree.xmlfile(self.stream) as parent: 82 XmlCloth().subserialize(self.ctx, cls, inst, parent, 83 name=cls.__name__) 84 85 elt = etree.fromstring(self.stream.getvalue()) 86 87 print(etree.tostring(elt, pretty_print=True)) 88 return elt 89 90 def test_simple(self): 91 v = 'punk.' 92 elt = self._run(v, Unicode) 93 94 assert elt.text == v 95 96 def test_complex_primitive(self): 97 class SomeObject(ComplexModel): 98 s = Unicode 99 100 v = 'punk.' 101 elt = self._run(SomeObject(s=v)) 102 103 assert elt[0].text == v 104 105 def test_complex_inheritance(self): 106 class A(ComplexModel): 107 i = Integer 108 109 class B(A): 110 s = Unicode 111 112 i = 42 113 s = 'punk.' 114 elt = self._run(B(i=i, s=s)) 115 116 # order is important 117 assert len(elt) == 2 118 assert elt[0].text == str(i) 119 assert elt[1].text == s 120 121 122### !!! WARNING !!! ### !!! WARNING !!! ### !!! WARNING !!! ### !!! WARNING !!! 123# 124# This test uses spyne_id and spyne_tagbag instead of spyne-id and spyne-tagbag 125# for ease of testing. The attributes used here are different from what you are 126# going to see in the real-world uses of this functionality. 127# You have been warned !!! 128# 129### !!! WARNING !!! ### !!! WARNING !!! ### !!! WARNING !!! ### !!! WARNING !!! 130class TestXmlCloth(unittest.TestCase): 131 def setUp(self): 132 self.ctx = FakeContext() 133 self.stream = BytesIO() 134 logging.basicConfig(level=logging.DEBUG) 135 136 def _run(self, inst, spid=None, cloth=None): 137 cls = inst.__class__ 138 if cloth is None: 139 assert spid is not None 140 cloth = etree.fromstring("""<a><b spyne_id="%s"></b></a>""" % spid) 141 else: 142 assert spid is None 143 144 with etree.xmlfile(self.stream) as parent: 145 XmlCloth(cloth=cloth).set_identifier_prefix('spyne_') \ 146 .subserialize(self.ctx, cls, inst, parent) 147 148 elt = etree.fromstring(self.stream.getvalue()) 149 150 print(etree.tostring(elt, pretty_print=True)) 151 return elt 152 153 def test_simple_value(self): 154 class SomeObject(ComplexModel): 155 s = Unicode 156 157 v = 'punk.' 158 elt = self._run(SomeObject(s=v), spid='s') 159 160 assert elt[0].text == v 161 162 def test_simple_empty(self): 163 class SomeObject(ComplexModel): 164 s = Unicode 165 166 elt = self._run(SomeObject(), spid='s') 167 168 assert len(elt) == 0 169 170 # FIXME: just fix it 171 def _test_simple_empty_nonoptional(self): 172 class SomeObject(ComplexModel): 173 s = Unicode(min_occurs=1) 174 175 elt = self._run(SomeObject(), spid='s') 176 177 assert elt[0].text is None 178 179 # FIXME: just fix it 180 def _test_simple_empty_nonoptional_clear(self): 181 class SomeObject(ComplexModel): 182 s = Unicode(min_occurs=1) 183 184 cloth = etree.fromstring("""<a><b spyne_id="s">oi punk!</b></a>""") 185 186 elt = self._run(SomeObject(), cloth=cloth) 187 188 assert elt[0].text is None 189 190 def test_xml_data_tag(self): 191 class SomeObject(ComplexModel): 192 d = XmlData(Unicode) 193 194 cloth = etree.fromstring('<a><spyne_data spyne_id="d"/></a>') 195 196 elt = self._run(SomeObject(d='data'), cloth=cloth) 197 198 assert elt.text == 'data' 199 200 def test_xml_data_attr(self): 201 class SomeObject(ComplexModel): 202 d = XmlData(Unicode) 203 204 cloth = etree.fromstring('<a spyne_data="d"></a>') 205 206 elt = self._run(SomeObject(d='data'), cloth=cloth) 207 208 assert elt.text == 'data' 209 210 def test_xml_data_attr_undesignated(self): 211 class SomeObject(ComplexModel): 212 d = Unicode 213 214 cloth = etree.fromstring('<a spyne_data="d"></a>') 215 216 elt = self._run(SomeObject(d='data'), cloth=cloth) 217 218 assert elt.text == 'data' 219 220 def test_simple_value_xmlattribute(self): 221 v = 'punk.' 222 223 class SomeObject(ComplexModel): 224 s = XmlAttribute(Unicode(min_occurs=1)) 225 226 cloth = etree.fromstring("""<a></a>""") 227 elt = self._run(SomeObject(s=v), cloth=cloth) 228 229 assert elt.attrib['s'] == v 230 231 def test_simple_value_xmlattribute_subname(self): 232 v = 'punk.' 233 234 class SomeObject(ComplexModel): 235 s = XmlAttribute(Unicode(min_occurs=1, sub_name='foo')) 236 237 cloth = etree.fromstring("""<a></a>""") 238 elt = self._run(SomeObject(s=v), cloth=cloth) 239 240 assert elt.attrib['foo'] == v 241 242 def test_simple_value_xmlattribute_non_immediate(self): 243 v = 'punk.' 244 245 class SomeObject(ComplexModel): 246 s = XmlAttribute(Unicode(min_occurs=1, sub_name='foo')) 247 248 cloth = etree.fromstring("""<a><b spyne_attr="s"/></a>""") 249 elt = self._run(SomeObject(s=v), cloth=cloth) 250 251 assert elt.attrib['foo'] == v 252 assert elt[0].attrib['foo'] == v 253 254 def test_simple_value_xmlattribute_non_immediate_non_designated(self): 255 v = 'punk.' 256 257 class SomeObject(ComplexModel): 258 s = Unicode(min_occurs=1, sub_name='foo') 259 260 cloth = etree.fromstring("""<a><b spyne_attr="s"/></a>""") 261 elt = self._run(SomeObject(s=v), cloth=cloth) 262 263 assert not 'foo' in elt.attrib 264 assert elt[0].attrib['foo'] == v 265 266 def test_non_tagbag(self): 267 cloth = E.a( 268 E.b( 269 E.c( 270 E.d( 271 spyne_id="i", 272 ), 273 spyne_id="c", 274 ), 275 spyne_id="i", 276 ), 277 spyne_tagbag='', 278 ) 279 280 class C2(ComplexModel): 281 i = Integer 282 283 class C1(ComplexModel): 284 i = Integer 285 c = C2 286 287 elt = self._run(C1(i=1, c=C2(i=2)), cloth=cloth) 288 assert elt.xpath('//b/text()') == ['1'] 289 # no order guarantee is given 290 assert set(elt.xpath('//d/text()')) == set(['1', '2']) 291 292 def test_array(self): 293 v = range(3) 294 295 class SomeObject(ComplexModel): 296 s = Array(Integer) 297 298 cloth = E.a( 299 E.b( 300 E.c(spyne_id="integer"), 301 spyne_id="s", 302 ) 303 ) 304 305 elt = self._run(SomeObject(s=v), cloth=cloth) 306 307 assert elt.xpath('//c/text()') == [str(i) for i in v] 308 309 def test_array_empty(self): 310 class SomeObject(ComplexModel): 311 s = Array(Integer) 312 313 elt_str = '<a><b spyne_id="s"><c spyne_id="integer"></c></b></a>' 314 cloth = etree.fromstring(elt_str) 315 316 elt = self._run(SomeObject(), cloth=cloth) 317 318 assert elt.xpath('//c') == [] 319 320 # FIXME: just fix it 321 def _test_array_empty_nonoptional(self): 322 class SomeObject(ComplexModel): 323 s = Array(Integer(min_occurs=1)) 324 325 elt_str = '<a><b spyne_id="s"><c spyne_id="integer"></c></b></a>' 326 cloth = etree.fromstring(elt_str) 327 328 elt = self._run(SomeObject(), cloth=cloth) 329 330 assert elt.xpath('//c') == [cloth[0][0]] 331 332 def test_simple_two_tags(self): 333 class SomeObject(ComplexModel): 334 s = Unicode 335 i = Integer 336 337 v = SomeObject(s='s', i=5) 338 339 cloth = E.a( 340 E.b1(), 341 E.b2( 342 E.c1(spyne_id="s"), 343 E.c2(), 344 ), 345 E.e( 346 E.g1(), 347 E.g2(spyne_id="i"), 348 E.g3(), 349 ), 350 ) 351 352 elt = self._run(v, cloth=cloth) 353 354 print(etree.tostring(elt, pretty_print=True)) 355 assert elt[0].tag == 'b1' 356 assert elt[1].tag == 'b2' 357 assert elt[1][0].tag == 'c1' 358 assert elt[1][0].text == 's' 359 assert elt[1][1].tag == 'c2' 360 assert elt[2].tag == 'e' 361 assert elt[2][0].tag == 'g1' 362 assert elt[2][1].tag == 'g2' 363 assert elt[2][1].text == '5' 364 assert elt[2][2].tag == 'g3' 365 366 def test_sibling_order(self): 367 class SomeObject(ComplexModel): 368 s = Unicode 369 370 v = SomeObject(s='s') 371 372 cloth = E.a( 373 E.b1(), 374 E.b2( 375 E.c0(), 376 E.c1(), 377 E.c2(spyne_id="s"), 378 E.c3(), 379 E.c4(), 380 ), 381 ) 382 383 elt = self._run(v, cloth=cloth) 384 print(etree.tostring(elt, pretty_print=True)) 385 assert elt[0].tag == 'b1' 386 assert elt[1].tag == 'b2' 387 assert elt[1][0].tag == 'c0' 388 assert elt[1][1].tag == 'c1' 389 assert elt[1][2].tag == 'c2' 390 assert elt[1][2].text == 's' 391 assert elt[1][3].tag == 'c3' 392 assert elt[1][4].tag == 'c4' 393 394 def test_parent_text(self): 395 class SomeObject(ComplexModel): 396 s = Unicode 397 398 v = SomeObject(s='s') 399 400 cloth = E.a( 401 "text 0", 402 E.b1(spyne_id="s"), 403 ) 404 405 print(etree.tostring(cloth, pretty_print=True)) 406 elt = self._run(v, cloth=cloth) 407 print(etree.tostring(elt, pretty_print=True)) 408 409 assert elt.tag == 'a' 410 assert elt.text == 'text 0' 411 412 assert elt[0].tag == 'b1' 413 assert elt[0].text == 's' 414 415 def test_anc_text(self): 416 class SomeObject(ComplexModel): 417 s = Unicode 418 419 v = SomeObject(s='s') 420 421 cloth = E.a( 422 E.b1( 423 "text 1", 424 E.c1(spyne_id="s"), 425 ) 426 ) 427 428 print(etree.tostring(cloth, pretty_print=True)) 429 elt = self._run(v, cloth=cloth) 430 print(etree.tostring(elt, pretty_print=True)) 431 432 assert elt[0].tag == 'b1' 433 assert elt[0].text == 'text 1' 434 assert elt[0][0].tag == 'c1' 435 assert elt[0][0].text == 's' 436 437 def test_prevsibl_tail(self): 438 class SomeObject(ComplexModel): 439 s = Unicode 440 441 v = SomeObject(s='s') 442 443 cloth = E.a( 444 E.b1( 445 E.c1(), 446 "text 2", 447 E.c2(spyne_id="s"), 448 ) 449 ) 450 451 print(etree.tostring(cloth, pretty_print=True)) 452 elt = self._run(v, cloth=cloth) 453 print(etree.tostring(elt, pretty_print=True)) 454 455 assert elt[0].tag == 'b1' 456 assert elt[0][0].tag == 'c1' 457 assert elt[0][0].tail == 'text 2' 458 assert elt[0][1].text == 's' 459 460 def test_sibling_tail_close(self): 461 class SomeObject(ComplexModel): 462 s = Unicode 463 464 v = SomeObject(s='s') 465 466 cloth = E.a( 467 E.b0(spyne_id="s"), 468 "text 3", 469 ) 470 471 print(etree.tostring(cloth, pretty_print=True)) 472 elt = self._run(v, cloth=cloth) 473 print(etree.tostring(elt, pretty_print=True)) 474 475 assert elt[0].tag == 'b0' 476 assert elt[0].text == 's' 477 assert elt[0].tail == 'text 3' 478 479 def test_sibling_tail_close_sibling(self): 480 class SomeObject(ComplexModel): 481 s = Unicode 482 i = Integer 483 484 v = SomeObject(s='s', i=5) 485 486 cloth = E.a( 487 E.b0(spyne_id="s"), 488 "text 3", 489 E.b1(spyne_id="i"), 490 ) 491 492 print(etree.tostring(cloth, pretty_print=True)) 493 elt = self._run(v, cloth=cloth) 494 print(etree.tostring(elt, pretty_print=True)) 495 496 assert elt[0].tag == 'b0' 497 assert elt[0].text == 's' 498 assert elt[0].tail == 'text 3' 499 500 def test_sibling_tail_close_anc(self): 501 class SomeObject(ComplexModel): 502 s = Unicode 503 i = Integer 504 505 v = SomeObject(s='s', i=5) 506 507 cloth = E.a( 508 E.b0(), 509 "text 0", 510 E.b1( 511 E.c0(spyne_id="s"), 512 "text 1", 513 E.c1(), 514 "text 2", 515 ), 516 "text 3", 517 E.b2( 518 E.c1(spyne_id="i"), 519 "text 4", 520 ) 521 ) 522 523 print(etree.tostring(cloth, pretty_print=True)) 524 elt = self._run(v, cloth=cloth) 525 print(etree.tostring(elt, pretty_print=True)) 526 527 assert elt.xpath('/a/b1/c0')[0].tail == 'text 1' 528 assert elt.xpath('/a/b1/c1')[0].tail == 'text 2' 529 assert elt.xpath('/a/b2/c1')[0].tail == 'text 4' 530 531 def test_nested_conflicts(self): 532 class SomeObject(ComplexModel): 533 s = Unicode 534 i = Integer 535 c = SelfReference 536 537 v = SomeObject(s='x', i=1, c=SomeObject(s='y', i=2)) 538 539 cloth = E.a( 540 E.b0(), 541 "text 0", 542 E.b1( 543 E.c0(spyne_id="s"), 544 "text 1", 545 E.c1( 546 E.d0(spyne_id="s"), 547 E.d1(spyne_id="i"), 548 spyne_id="c", 549 ), 550 "text 2", 551 ), 552 "text 3", 553 E.b2( 554 E.c2(spyne_id="i"), 555 "text 4", 556 ) 557 ) 558 559 print(etree.tostring(cloth, pretty_print=True)) 560 elt = self._run(v, cloth=cloth) 561 print(etree.tostring(elt, pretty_print=True)) 562 563 assert elt.xpath('/a/b1/c0')[0].text == str(v.s) 564 assert elt.xpath('/a/b1/c1/d0')[0].text == str(v.c.s) 565 assert elt.xpath('/a/b1/c1/d1')[0].text == str(v.c.i) 566 assert elt.xpath('/a/b2/c2')[0].text == str(v.i) 567 568 569if __name__ == '__main__': 570 unittest.main() 571