1#!/usr/bin/env python 2# coding=utf-8 3# 4# spyne - Copyright (C) Spyne contributors. 5# 6# This library is free software; you can redistribute it and/or 7# modify it under the terms of the GNU Lesser General Public 8# License as published by the Free Software Foundation; either 9# version 2.1 of the License, or (at your option) any later version. 10# 11# This library is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14# Lesser General Public License for more details. 15# 16# You should have received a copy of the GNU Lesser General Public 17# License along with this library; if not, write to the Free Software 18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19# 20 21from __future__ import print_function 22 23import re 24import uuid 25import datetime 26import unittest 27import warnings 28 29import pytz 30import spyne 31 32from datetime import timedelta 33 34from lxml import etree 35from spyne.model.primitive._base import re_match_with_span as rmws 36 37from spyne.util import six 38from spyne.const import xml as ns 39 40from spyne import Null, AnyDict, Uuid, Array, ComplexModel, Date, Time, \ 41 Boolean, DateTime, Duration, Float, Integer, NumberLimitsWarning, Unicode, \ 42 String, Decimal, Integer16, ModelBase, MimeType, MimeTypeStrict, MediaType 43 44from spyne.protocol import ProtocolBase 45from spyne.protocol.xml import XmlDocument 46 47ns_test = 'test_namespace' 48 49 50class TestCast(unittest.TestCase): 51 pass # TODO: test Unicode(cast=str) 52 53 54class TestPrimitive(unittest.TestCase): 55 def test_mime_type_family(self): 56 mime_attr = MimeType.Attributes 57 mime_strict_attr = MimeTypeStrict.Attributes 58 assert rmws(mime_attr, u'application/foo') 59 assert not rmws(mime_attr, u'application/ foo') 60 assert not rmws(mime_attr, u'application/') 61 assert rmws(mime_attr, u'foo/bar') 62 assert not rmws(mime_attr, u'foo/bar ') 63 assert not rmws(mime_strict_attr, u'foo/bar') 64 65 media_attr = MediaType.Attributes 66 media_strict_attr = MediaType.Attributes 67 68 print(media_attr.pattern) 69 70 assert rmws(media_attr, u'text/plain; charset="utf-8"') 71 assert rmws(media_attr, u'text/plain; charset=utf-8') 72 assert rmws(media_attr, u'text/plain; charset=utf-8 ') 73 assert rmws(media_attr, u'text/plain; charset=utf-8') 74 assert rmws(media_attr, u'text/plain; charset=utf-8;') 75 assert rmws(media_attr, u'text/plain; charset=utf-8; ') 76 assert not rmws(media_attr, u'text/plain; charset=utf-8; foo') 77 assert not rmws(media_attr, u'text/plain; charset=utf-8; foo=') 78 assert rmws(media_attr, u'text/plain; charset=utf-8; foo=""') 79 assert rmws(media_attr, u'text/plain; charset=utf-8; foo="";') 80 assert rmws(media_attr, u'text/plain; charset=utf-8; foo=""; ') 81 assert rmws(media_attr, u'text/plain; charset=utf-8; foo=""; ') 82 assert not rmws(media_attr, u'text/plain;; charset=utf-8; foo=""') 83 assert not rmws(media_attr, u'text/plain;;; charset=utf-8; foo=""') 84 assert not rmws(media_attr, u'text/plain; charset=utf-8;; foo=""') 85 assert not rmws(media_attr, u'text/plain; charset=utf-8;;; foo=""') 86 assert not rmws(media_attr, u'text/plain; charset=utf-8;;; foo="";') 87 assert not rmws(media_attr, u'text/plain; charset=utf-8;;; foo=""; ; ') 88 assert not rmws(media_strict_attr, u'foo/bar;') 89 assert not rmws(media_strict_attr, u' applicaton/json;') 90 assert not rmws(media_strict_attr, u'applicaton/json;') 91 92 assert MediaType 93 94 95 def test_getitem_cust(self): 96 assert Unicode[dict(max_len=2)].Attributes.max_len 97 98 def test_ancestors(self): 99 class A(ComplexModel): i = Integer 100 class B(A): i2 = Integer 101 class C(B): i3 = Integer 102 103 assert C.ancestors() == [B, A] 104 assert B.ancestors() == [A] 105 assert A.ancestors() == [] 106 107 def test_nillable_quirks(self): 108 assert ModelBase.Attributes.nillable == True 109 110 class Attributes(ModelBase.Attributes): 111 nillable = False 112 nullable = False 113 114 assert Attributes.nillable == False 115 assert Attributes.nullable == False 116 117 class Attributes(ModelBase.Attributes): 118 nillable = True 119 120 assert Attributes.nillable == True 121 assert Attributes.nullable == True 122 123 class Attributes(ModelBase.Attributes): 124 nillable = False 125 126 assert Attributes.nillable == False 127 assert Attributes.nullable == False 128 129 class Attributes(ModelBase.Attributes): 130 nullable = True 131 132 assert Attributes.nillable == True 133 assert Attributes.nullable == True 134 135 class Attributes(ModelBase.Attributes): 136 nullable = False 137 138 assert Attributes.nillable == False 139 assert Attributes.nullable == False 140 141 class Attributes(ModelBase.Attributes): 142 nullable = False 143 144 class Attributes(Attributes): 145 pass 146 147 assert Attributes.nullable == False 148 149 def test_nillable_inheritance_quirks(self): 150 class Attributes(ModelBase.Attributes): 151 nullable = False 152 153 class AttrMixin: 154 pass 155 156 class NewAttributes(Attributes, AttrMixin): 157 pass 158 159 assert NewAttributes.nullable is False 160 161 class AttrMixin: 162 pass 163 164 class NewAttributes(AttrMixin, Attributes): 165 pass 166 167 assert NewAttributes.nullable is False 168 169 def test_decimal(self): 170 assert Decimal(10, 4).Attributes.total_digits == 10 171 assert Decimal(10, 4).Attributes.fraction_digits == 4 172 173 def test_decimal_format(self): 174 f = 123456 175 str_format = '${0}' 176 element = etree.Element('test') 177 XmlDocument().to_parent(None, Decimal(str_format=str_format), f, 178 element, ns_test) 179 element = element[0] 180 181 self.assertEqual(element.text, '$123456') 182 183 def test_string(self): 184 s = String() 185 element = etree.Element('test') 186 XmlDocument().to_parent(None, String, 'value', element, ns_test) 187 element = element[0] 188 189 self.assertEqual(element.text, 'value') 190 value = XmlDocument().from_element(None, String, element) 191 self.assertEqual(value, 'value') 192 193 def test_datetime(self): 194 n = datetime.datetime.now(pytz.utc) 195 196 element = etree.Element('test') 197 XmlDocument().to_parent(None, DateTime, n, element, ns_test) 198 element = element[0] 199 200 self.assertEqual(element.text, n.isoformat()) 201 dt = XmlDocument().from_element(None, DateTime, element) 202 self.assertEqual(n, dt) 203 204 def test_datetime_format(self): 205 n = datetime.datetime.now().replace(microsecond=0) 206 format = "%Y %m %d %H %M %S" 207 208 element = etree.Element('test') 209 XmlDocument().to_parent(None, DateTime(dt_format=format), n, element, 210 ns_test) 211 element = element[0] 212 213 assert element.text == datetime.datetime.strftime(n, format) 214 dt = XmlDocument().from_element(None, DateTime(dt_format=format), 215 element) 216 assert n == dt 217 218 def test_datetime_unicode_format(self): 219 n = datetime.datetime.now().replace(microsecond=0) 220 format = u"%Y %m %d\u00a0%H %M %S" 221 222 element = etree.Element('test') 223 XmlDocument().to_parent(None, DateTime(dt_format=format), n, 224 element, ns_test) 225 element = element[0] 226 227 if six.PY2: 228 assert element.text == n.strftime(format.encode('utf8')) \ 229 .decode('utf8') 230 else: 231 assert element.text == n.strftime(format) 232 233 dt = XmlDocument().from_element(None, DateTime(dt_format=format), 234 element) 235 assert n == dt 236 237 def test_date_format(self): 238 t = datetime.date.today() 239 format = "%Y-%m-%d" 240 241 element = etree.Element('test') 242 XmlDocument().to_parent(None, 243 Date(date_format=format), t, element, ns_test) 244 assert element[0].text == datetime.date.strftime(t, format) 245 246 dt = XmlDocument().from_element(None, 247 Date(date_format=format), element[0]) 248 assert t == dt 249 250 def test_datetime_timezone(self): 251 import pytz 252 253 n = datetime.datetime.now(pytz.timezone('EST')) 254 element = etree.Element('test') 255 cls = DateTime(as_timezone=pytz.utc, timezone=False) 256 XmlDocument().to_parent(None, cls, n, element, ns_test) 257 element = element[0] 258 259 c = n.astimezone(pytz.utc).replace(tzinfo=None) 260 self.assertEqual(element.text, c.isoformat()) 261 dt = XmlDocument().from_element(None, cls, element) 262 assert dt.tzinfo is not None 263 dt = dt.replace(tzinfo=None) 264 self.assertEqual(c, dt) 265 266 def test_date_timezone(self): 267 elt = etree.Element('wot') 268 elt.text = '2013-08-09+02:00' 269 dt = XmlDocument().from_element(None, Date, elt) 270 print("ok without validation.") 271 dt = XmlDocument(validator='soft').from_element(None, Date, elt) 272 print(dt) 273 274 def test_time(self): 275 n = datetime.time(1, 2, 3, 4) 276 277 ret = ProtocolBase().to_bytes(Time, n) 278 self.assertEqual(ret, n.isoformat()) 279 280 dt = ProtocolBase().from_unicode(Time, ret) 281 self.assertEqual(n, dt) 282 283 def test_time_usec(self): 284 # python's datetime and time only accept ints between [0, 1e6[ 285 # if the incoming data is 999999.8 microseconds, rounding it up means 286 # adding 1 second to time. For many reasons, we want to avoid that. (see 287 # http://bugs.python.org/issue1487389) That's why 999999.8 usec is 288 # rounded to 999999. 289 290 # rounding 0.1 µsec down 291 t = ProtocolBase().from_unicode(Time, "12:12:12.0000001") 292 self.assertEqual(datetime.time(12, 12, 12), t) 293 294 # rounding 1.5 µsec up. 0.5 is rounded down by python 3 and up by 295 # python 2 so we test with 1.5 µsec instead. frikkin' nonsense. 296 t = ProtocolBase().from_unicode(Time, "12:12:12.0000015") 297 self.assertEqual(datetime.time(12, 12, 12, 2), t) 298 299 # rounding 999998.8 µsec up 300 t = ProtocolBase().from_unicode(Time, "12:12:12.9999988") 301 self.assertEqual(datetime.time(12, 12, 12, 999999), t) 302 303 # rounding 999999.1 µsec down 304 t = ProtocolBase().from_unicode(Time, "12:12:12.9999991") 305 self.assertEqual(datetime.time(12, 12, 12, 999999), t) 306 307 # rounding 999999.8 µsec down, not up. 308 t = ProtocolBase().from_unicode(Time, "12:12:12.9999998") 309 self.assertEqual(datetime.time(12, 12, 12, 999999), t) 310 311 def test_date(self): 312 n = datetime.date(2011, 12, 13) 313 314 ret = ProtocolBase().to_unicode(Date, n) 315 self.assertEqual(ret, n.isoformat()) 316 317 dt = ProtocolBase().from_unicode(Date, ret) 318 self.assertEqual(n, dt) 319 320 def test_utcdatetime(self): 321 datestring = '2007-05-15T13:40:44Z' 322 e = etree.Element('test') 323 e.text = datestring 324 325 dt = XmlDocument().from_element(None, DateTime, e) 326 327 self.assertEqual(dt.year, 2007) 328 self.assertEqual(dt.month, 5) 329 self.assertEqual(dt.day, 15) 330 331 datestring = '2007-05-15T13:40:44.003Z' 332 e = etree.Element('test') 333 e.text = datestring 334 335 dt = XmlDocument().from_element(None, DateTime, e) 336 337 self.assertEqual(dt.year, 2007) 338 self.assertEqual(dt.month, 5) 339 self.assertEqual(dt.day, 15) 340 341 def test_date_exclusive_boundaries(self): 342 test_model = Date.customize(gt=datetime.date(2016, 1, 1), 343 lt=datetime.date(2016, 2, 1)) 344 self.assertFalse( 345 test_model.validate_native(test_model, datetime.date(2016, 1, 1))) 346 self.assertFalse( 347 test_model.validate_native(test_model, datetime.date(2016, 2, 1))) 348 349 def test_date_inclusive_boundaries(self): 350 test_model = Date.customize(ge=datetime.date(2016, 1, 1), 351 le=datetime.date(2016, 2, 1)) 352 self.assertTrue( 353 test_model.validate_native(test_model, datetime.date(2016, 1, 1))) 354 self.assertTrue( 355 test_model.validate_native(test_model, datetime.date(2016, 2, 1))) 356 357 def test_datetime_exclusive_boundaries(self): 358 test_model = DateTime.customize( 359 gt=datetime.datetime(2016, 1, 1, 12, 00) 360 .replace(tzinfo=spyne.LOCAL_TZ), 361 lt=datetime.datetime(2016, 2, 1, 12, 00) 362 .replace(tzinfo=spyne.LOCAL_TZ), 363 ) 364 self.assertFalse(test_model.validate_native(test_model, 365 datetime.datetime(2016, 1, 1, 12, 00))) 366 self.assertFalse(test_model.validate_native(test_model, 367 datetime.datetime(2016, 2, 1, 12, 00))) 368 369 def test_datetime_inclusive_boundaries(self): 370 test_model = DateTime.customize( 371 ge=datetime.datetime(2016, 1, 1, 12, 00) 372 .replace(tzinfo=spyne.LOCAL_TZ), 373 le=datetime.datetime(2016, 2, 1, 12, 00) 374 .replace(tzinfo=spyne.LOCAL_TZ) 375 ) 376 377 self.assertTrue(test_model.validate_native(test_model, 378 datetime.datetime(2016, 1, 1, 12, 00))) 379 self.assertTrue(test_model.validate_native(test_model, 380 datetime.datetime(2016, 2, 1, 12, 00))) 381 382 def test_time_exclusive_boundaries(self): 383 test_model = Time.customize(gt=datetime.time(12, 00), 384 lt=datetime.time(13, 00)) 385 386 self.assertFalse( 387 test_model.validate_native(test_model, datetime.time(12, 00))) 388 self.assertFalse( 389 test_model.validate_native(test_model, datetime.time(13, 00))) 390 391 def test_time_inclusive_boundaries(self): 392 test_model = Time.customize(ge=datetime.time(12, 00), 393 le=datetime.time(13, 00)) 394 395 self.assertTrue( 396 test_model.validate_native(test_model, datetime.time(12, 00))) 397 self.assertTrue( 398 test_model.validate_native(test_model, datetime.time(13, 00))) 399 400 def test_datetime_extreme_boundary(self): 401 self.assertTrue( 402 DateTime.validate_native(DateTime, datetime.datetime.min)) 403 self.assertTrue( 404 DateTime.validate_native(DateTime, datetime.datetime.max)) 405 406 def test_time_extreme_boundary(self): 407 self.assertTrue(Time.validate_native(Time, datetime.time(0, 0, 0, 0))) 408 self.assertTrue( 409 Time.validate_native(Time, datetime.time(23, 59, 59, 999999))) 410 411 def test_date_extreme_boundary(self): 412 self.assertTrue(Date.validate_native(Date, datetime.date.min)) 413 self.assertTrue(Date.validate_native(Date, datetime.date.max)) 414 415 def test_integer(self): 416 i = 12 417 integer = Integer() 418 419 element = etree.Element('test') 420 XmlDocument().to_parent(None, Integer, i, element, ns_test) 421 element = element[0] 422 423 self.assertEqual(element.text, '12') 424 value = XmlDocument().from_element(None, integer, element) 425 self.assertEqual(value, i) 426 427 def test_integer_limits(self): 428 with warnings.catch_warnings(record=True) as w: 429 warnings.simplefilter("always") 430 431 integer = Integer16(ge=-32768) 432 433 assert len(w) == 0 434 435 integer = Integer16(ge=-32769) 436 assert len(w) == 1 437 assert issubclass(w[-1].category, NumberLimitsWarning) 438 assert "smaller than min_bound" in str(w[-1].message) 439 440 with warnings.catch_warnings(record=True) as w: 441 warnings.simplefilter("always") 442 443 integer = Integer16(le=32767) 444 445 assert len(w) == 0 446 447 integer = Integer16(le=32768) 448 assert len(w) == 1 449 assert issubclass(w[-1].category, NumberLimitsWarning) 450 assert "greater than max_bound" in str(w[-1].message) 451 452 try: 453 Integer16(ge=32768) 454 except ValueError: 455 pass 456 else: 457 raise Exception("must fail") 458 459 try: 460 Integer16(lt=-32768) 461 except ValueError: 462 pass 463 else: 464 raise Exception("must fail") 465 466 def test_large_integer(self): 467 i = 128375873458473 468 integer = Integer() 469 470 element = etree.Element('test') 471 XmlDocument().to_parent(None, Integer, i, element, ns_test) 472 element = element[0] 473 474 self.assertEqual(element.text, '128375873458473') 475 value = XmlDocument().from_element(None, integer, element) 476 self.assertEqual(value, i) 477 478 def test_float(self): 479 f = 1.22255645 480 481 element = etree.Element('test') 482 XmlDocument().to_parent(None, Float, f, element, ns_test) 483 element = element[0] 484 485 self.assertEqual(element.text, repr(f)) 486 487 f2 = XmlDocument().from_element(None, Float, element) 488 self.assertEqual(f2, f) 489 490 def test_array(self): 491 type = Array(String) 492 type.resolve_namespace(type, "zbank") 493 494 values = ['a', 'b', 'c', 'd', 'e', 'f'] 495 496 element = etree.Element('test') 497 XmlDocument().to_parent(None, type, values, element, ns_test) 498 element = element[0] 499 500 self.assertEqual(len(values), len(element.getchildren())) 501 502 values2 = XmlDocument().from_element(None, type, element) 503 self.assertEqual(values[3], values2[3]) 504 505 def test_array_empty(self): 506 type = Array(String) 507 type.resolve_namespace(type, "zbank") 508 509 values = [] 510 511 element = etree.Element('test') 512 XmlDocument().to_parent(None, type, values, element, ns_test) 513 element = element[0] 514 515 self.assertEqual(len(values), len(element.getchildren())) 516 517 values2 = XmlDocument().from_element(None, type, element) 518 self.assertEqual(len(values2), 0) 519 520 def test_unicode(self): 521 s = u'\x34\x55\x65\x34' 522 self.assertEqual(4, len(s)) 523 element = etree.Element('test') 524 XmlDocument().to_parent(None, String, s, element, 'test_ns') 525 element = element[0] 526 value = XmlDocument().from_element(None, String, element) 527 self.assertEqual(value, s) 528 529 def test_unicode_pattern_mult_cust(self): 530 assert Unicode(pattern='a').Attributes.pattern == 'a' 531 assert Unicode(pattern='a')(5).Attributes.pattern == 'a' 532 533 def test_unicode_upattern(self): 534 patt = r'[\w .-]+' 535 attr = Unicode(unicode_pattern=patt).Attributes 536 assert attr.pattern == patt 537 assert attr._pattern_re.flags & re.UNICODE 538 assert attr._pattern_re.match(u"Ğ Ğ ç .-") 539 assert attr._pattern_re.match(u"\t") is None 540 541 def test_unicode_nullable_mult_cust_false(self): 542 assert Unicode(nullable=False).Attributes.nullable == False 543 assert Unicode(nullable=False)(5).Attributes.nullable == False 544 545 def test_unicode_nullable_mult_cust_true(self): 546 assert Unicode(nullable=True).Attributes.nullable == True 547 assert Unicode(nullable=True)(5).Attributes.nullable == True 548 549 def test_null(self): 550 element = etree.Element('test') 551 XmlDocument().to_parent(None, Null, None, element, ns_test) 552 print(etree.tostring(element)) 553 554 element = element[0] 555 self.assertTrue(bool(element.attrib.get(ns.XSI('nil')))) 556 value = XmlDocument().from_element(None, Null, element) 557 self.assertEqual(None, value) 558 559 def test_point(self): 560 from spyne.model.primitive.spatial import _get_point_pattern 561 562 a = re.compile(_get_point_pattern(2)) 563 assert a.match('POINT (10 40)') is not None 564 assert a.match('POINT(10 40)') is not None 565 566 assert a.match('POINT(10.0 40)') is not None 567 assert a.match('POINT(1.310e4 40)') is not None 568 569 def test_multipoint(self): 570 from spyne.model.primitive.spatial import _get_multipoint_pattern 571 572 a = re.compile(_get_multipoint_pattern(2)) 573 assert a.match('MULTIPOINT (10 40, 40 30, 20 20, 30 10)') is not None 574 # FIXME: 575 # assert a.match('MULTIPOINT ((10 40), (40 30), (20 20), (30 10))') is not None 576 577 def test_linestring(self): 578 from spyne.model.primitive.spatial import _get_linestring_pattern 579 580 a = re.compile(_get_linestring_pattern(2)) 581 assert a.match('LINESTRING (30 10, 10 30, 40 40)') is not None 582 583 def test_multilinestring(self): 584 from spyne.model.primitive.spatial import _get_multilinestring_pattern 585 586 a = re.compile(_get_multilinestring_pattern(2)) 587 assert a.match('''MULTILINESTRING ((10 10, 20 20, 10 40), 588 (40 40, 30 30, 40 20, 30 10))''') is not None 589 590 def test_polygon(self): 591 from spyne.model.primitive.spatial import _get_polygon_pattern 592 593 a = re.compile(_get_polygon_pattern(2)) 594 assert a.match( 595 'POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))') is not None 596 597 def test_multipolygon(self): 598 from spyne.model.primitive.spatial import _get_multipolygon_pattern 599 600 a = re.compile(_get_multipolygon_pattern(2)) 601 assert a.match('''MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), 602 ((15 5, 40 10, 10 20, 5 10, 15 5)))''') is not None 603 assert a.match('''MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), 604 ((20 35, 45 20, 30 5, 10 10, 10 30, 20 35), 605 (30 20, 20 25, 20 15, 30 20)))''') is not None 606 607 def test_boolean(self): 608 b = etree.Element('test') 609 XmlDocument().to_parent(None, Boolean, True, b, ns_test) 610 b = b[0] 611 self.assertEqual('true', b.text) 612 613 b = etree.Element('test') 614 XmlDocument().to_parent(None, Boolean, 0, b, ns_test) 615 b = b[0] 616 self.assertEqual('false', b.text) 617 618 b = etree.Element('test') 619 XmlDocument().to_parent(None, Boolean, 1, b, ns_test) 620 b = b[0] 621 self.assertEqual('true', b.text) 622 623 b = XmlDocument().from_element(None, Boolean, b) 624 self.assertEqual(b, True) 625 626 b = etree.Element('test') 627 XmlDocument().to_parent(None, Boolean, False, b, ns_test) 628 b = b[0] 629 self.assertEqual('false', b.text) 630 631 b = XmlDocument().from_element(None, Boolean, b) 632 self.assertEqual(b, False) 633 634 b = etree.Element('test') 635 XmlDocument().to_parent(None, Boolean, None, b, ns_test) 636 b = b[0] 637 self.assertEqual('true', b.get(ns.XSI('nil'))) 638 639 b = XmlDocument().from_element(None, Boolean, b) 640 self.assertEqual(b, None) 641 642 def test_new_type(self): 643 """Customized primitives go into namespace based on module name.""" 644 custom_type = Unicode(pattern='123') 645 self.assertEqual(custom_type.get_namespace(), custom_type.__module__) 646 647 def test_default_nullable(self): 648 """Test if default nullable changes nullable attribute.""" 649 try: 650 self.assertTrue(Unicode.Attributes.nullable) 651 orig_default = Unicode.Attributes.NULLABLE_DEFAULT 652 Unicode.Attributes.NULLABLE_DEFAULT = False 653 self.assertFalse(Unicode.Attributes.nullable) 654 self.assertFalse(Unicode.Attributes.nillable) 655 finally: 656 Unicode.Attributes.NULLABLE_DEFAULT = orig_default 657 self.assertEqual(Unicode.Attributes.nullable, orig_default) 658 659 def test_simple_type_explicit_customization(self): 660 assert Unicode(max_len=5).__extends__ is not None 661 assert Unicode.customize(max_len=5).__extends__ is not None 662 663 def test_anydict_customization(self): 664 from spyne.model import json 665 assert isinstance( 666 AnyDict.customize(store_as='json').Attributes.store_as, json) 667 668 def test_uuid_serialize(self): 669 value = uuid.UUID('12345678123456781234567812345678') 670 671 assert ProtocolBase().to_unicode(Uuid, value) \ 672 == '12345678-1234-5678-1234-567812345678' 673 assert ProtocolBase().to_unicode(Uuid(serialize_as='hex'), value) \ 674 == '12345678123456781234567812345678' 675 assert ProtocolBase().to_unicode(Uuid(serialize_as='urn'), value) \ 676 == 'urn:uuid:12345678-1234-5678-1234-567812345678' 677 assert ProtocolBase().to_unicode(Uuid(serialize_as='bytes'), value) \ 678 == b'\x124Vx\x124Vx\x124Vx\x124Vx' 679 assert ProtocolBase().to_unicode(Uuid(serialize_as='bytes_le'), value) \ 680 == b'xV4\x124\x12xV\x124Vx\x124Vx' 681 assert ProtocolBase().to_unicode(Uuid(serialize_as='fields'), value) \ 682 == (305419896, 4660, 22136, 18, 52, 95073701484152) 683 assert ProtocolBase().to_unicode(Uuid(serialize_as='int'), value) \ 684 == 24197857161011715162171839636988778104 685 686 def test_uuid_deserialize(self): 687 value = uuid.UUID('12345678123456781234567812345678') 688 689 assert ProtocolBase().from_unicode(Uuid, 690 '12345678-1234-5678-1234-567812345678') == value 691 assert ProtocolBase().from_unicode(Uuid(serialize_as='hex'), 692 '12345678123456781234567812345678') == value 693 assert ProtocolBase().from_unicode(Uuid(serialize_as='urn'), 694 'urn:uuid:12345678-1234-5678-1234-567812345678') == value 695 assert ProtocolBase().from_bytes(Uuid(serialize_as='bytes'), 696 b'\x124Vx\x124Vx\x124Vx\x124Vx') == value 697 assert ProtocolBase().from_bytes(Uuid(serialize_as='bytes_le'), 698 b'xV4\x124\x12xV\x124Vx\x124Vx') == value 699 assert ProtocolBase().from_unicode(Uuid(serialize_as='fields'), 700 (305419896, 4660, 22136, 18, 52, 95073701484152)) == value 701 assert ProtocolBase().from_unicode(Uuid(serialize_as='int'), 702 24197857161011715162171839636988778104) == value 703 704 def test_uuid_validate(self): 705 assert Uuid.validate_string(Uuid, 706 '12345678-1234-5678-1234-567812345678') 707 assert Uuid.validate_native(Uuid, 708 uuid.UUID('12345678-1234-5678-1234-567812345678')) 709 710 def test_datetime_serialize_as(self): 711 i = 1234567890123456 712 v = datetime.datetime.fromtimestamp(i / 1e6) 713 714 assert ProtocolBase().to_unicode( 715 DateTime(serialize_as='sec'), v) == i//1e6 716 assert ProtocolBase().to_unicode( 717 DateTime(serialize_as='sec_float'), v) == i/1e6 718 assert ProtocolBase().to_unicode( 719 DateTime(serialize_as='msec'), v) == i//1e3 720 assert ProtocolBase().to_unicode( 721 DateTime(serialize_as='msec_float'), v) == i/1e3 722 assert ProtocolBase().to_unicode( 723 DateTime(serialize_as='usec'), v) == i 724 725 def test_datetime_deserialize(self): 726 i = 1234567890123456 727 v = datetime.datetime.fromtimestamp(i / 1e6) 728 729 assert ProtocolBase().from_unicode( 730 DateTime(serialize_as='sec'), i//1e6) == \ 731 datetime.datetime.fromtimestamp(i//1e6) 732 assert ProtocolBase().from_unicode( 733 DateTime(serialize_as='sec_float'), i/1e6) == v 734 735 assert ProtocolBase().from_unicode( 736 DateTime(serialize_as='msec'), i//1e3) == \ 737 datetime.datetime.fromtimestamp(i/1e3//1000) 738 assert ProtocolBase().from_unicode( 739 DateTime(serialize_as='msec_float'), i/1e3) == v 740 741 assert ProtocolBase().from_unicode( 742 DateTime(serialize_as='usec'), i) == v 743 744 def test_datetime_ancient(self): 745 t = DateTime(dt_format="%Y-%m-%d %H:%M:%S") # to trigger strftime 746 v = datetime.datetime(1881, 1, 1) 747 vs = '1881-01-01 00:00:00' 748 749 dt = ProtocolBase().from_unicode(t, vs) 750 self.assertEqual(v, dt) 751 752 dt = ProtocolBase().to_unicode(t, v) 753 self.assertEqual(vs, dt) 754 755 def test_custom_strftime(self): 756 s = ProtocolBase.strftime(datetime.date(1800, 9, 23), 757 "%Y has the same days as 1980 and 2008") 758 if s != "1800 has the same days as 1980 and 2008": 759 raise AssertionError(s) 760 761 print("Testing all day names from 0001/01/01 until 2000/08/01") 762 # Get the weekdays. Can't hard code them; they could be 763 # localized. 764 days = [] 765 for i in range(1, 10): 766 days.append(datetime.date(2000, 1, i).strftime("%A")) 767 nextday = {} 768 for i in range(8): 769 nextday[days[i]] = days[i + 1] 770 771 startdate = datetime.date(1, 1, 1) 772 enddate = datetime.date(2000, 8, 1) 773 prevday = ProtocolBase.strftime(startdate, "%A") 774 one_day = datetime.timedelta(1) 775 776 testdate = startdate + one_day 777 while testdate < enddate: 778 if (testdate.day == 1 and testdate.month == 1 and 779 (testdate.year % 100 == 0)): 780 print("Testing century", testdate.year) 781 day = ProtocolBase.strftime(testdate, "%A") 782 if nextday[prevday] != day: 783 raise AssertionError(str(testdate)) 784 prevday = day 785 testdate = testdate + one_day 786 787 def test_datetime_usec(self): 788 # see the comments on time test for why the rounding here is weird 789 790 # rounding 0.1 µsec down 791 dt = ProtocolBase().from_unicode(DateTime, 792 "2015-01-01 12:12:12.0000001") 793 self.assertEqual(datetime.datetime(2015, 1, 1, 12, 12, 12), dt) 794 795 # rounding 1.5 µsec up. 0.5 is rounded down by python 3 and up by 796 # python 2 so we test with 1.5 µsec instead. frikkin' nonsense. 797 dt = ProtocolBase().from_unicode(DateTime, 798 "2015-01-01 12:12:12.0000015") 799 self.assertEqual(datetime.datetime(2015, 1, 1, 12, 12, 12, 2), dt) 800 801 # rounding 999998.8 µsec up 802 dt = ProtocolBase().from_unicode(DateTime, 803 "2015-01-01 12:12:12.9999988") 804 self.assertEqual(datetime.datetime(2015, 1, 1, 12, 12, 12, 999999), dt) 805 806 # rounding 999999.1 µsec down 807 dt = ProtocolBase().from_unicode(DateTime, 808 "2015-01-01 12:12:12.9999991") 809 self.assertEqual(datetime.datetime(2015, 1, 1, 12, 12, 12, 999999), dt) 810 811 # rounding 999999.8 µsec down, not up. 812 dt = ProtocolBase().from_unicode(DateTime, 813 "2015-01-01 12:12:12.9999998") 814 self.assertEqual(datetime.datetime(2015, 1, 1, 12, 12, 12, 999999), dt) 815 816 817### Duration Data Type 818## http://www.w3schools.com/schema/schema_dtypes_date.asp 819# Duration Data type 820# The time interval is specified in the following form "PnYnMnDTnHnMnS" where: 821# P indicates the period (required) 822# nY indicates the number of years 823# nM indicates the number of months 824# nD indicates the number of days 825# T indicates the start of a time section (*required* if you are going to 826# specify hours, minutes, seconds or microseconds) 827# nH indicates the number of hours 828# nM indicates the number of minutes 829# nS indicates the number of seconds 830 831class SomeBlob(ComplexModel): 832 __namespace__ = 'myns' 833 howlong = Duration() 834 835 836class TestDurationPrimitive(unittest.TestCase): 837 def test_onehour_oneminute_onesecond(self): 838 answer = 'PT1H1M1S' 839 gg = SomeBlob() 840 gg.howlong = timedelta(hours=1, minutes=1, seconds=1) 841 842 element = etree.Element('test') 843 XmlDocument().to_parent(None, SomeBlob, gg, element, gg.get_namespace()) 844 element = element[0] 845 846 print(gg.howlong) 847 print(etree.tostring(element, pretty_print=True)) 848 assert element[0].text == answer 849 850 data = element.find('{%s}howlong' % gg.get_namespace()).text 851 self.assertEqual(data, answer) 852 s1 = XmlDocument().from_element(None, SomeBlob, element) 853 assert s1.howlong.total_seconds() == gg.howlong.total_seconds() 854 855 def test_4suite(self): 856 # borrowed from 4Suite 857 tests_seconds = [ 858 (0, u'PT0S'), 859 (1, u'PT1S'), 860 (59, u'PT59S'), 861 (60, u'PT1M'), 862 (3599, u'PT59M59S'), 863 (3600, u'PT1H'), 864 (86399, u'PT23H59M59S'), 865 (86400, u'P1D'), 866 (86400 * 60, u'P60D'), 867 (86400 * 400, u'P400D') 868 ] 869 870 for secs, answer in tests_seconds: 871 gg = SomeBlob() 872 gg.howlong = timedelta(seconds=secs) 873 874 element = etree.Element('test') 875 XmlDocument()\ 876 .to_parent(None, SomeBlob, gg, element, gg.get_namespace()) 877 element = element[0] 878 879 print(gg.howlong) 880 print(etree.tostring(element, pretty_print=True)) 881 assert element[0].text == answer 882 883 data = element.find('{%s}howlong' % gg.get_namespace()).text 884 self.assertEqual(data, answer) 885 s1 = XmlDocument().from_element(None, SomeBlob, element) 886 assert s1.howlong.total_seconds() == secs 887 888 for secs, answer in tests_seconds: 889 if secs > 0: 890 secs *= -1 891 answer = '-' + answer 892 gg = SomeBlob() 893 gg.howlong = timedelta(seconds=secs) 894 895 element = etree.Element('test') 896 XmlDocument()\ 897 .to_parent(None, SomeBlob, gg, element, gg.get_namespace()) 898 element = element[0] 899 900 print(gg.howlong) 901 print(etree.tostring(element, pretty_print=True)) 902 assert element[0].text == answer 903 904 data = element.find('{%s}howlong' % gg.get_namespace()).text 905 self.assertEqual(data, answer) 906 s1 = XmlDocument().from_element(None, SomeBlob, element) 907 assert s1.howlong.total_seconds() == secs 908 909 def test_duration_positive_seconds_only(self): 910 answer = 'PT35S' 911 gg = SomeBlob() 912 gg.howlong = timedelta(seconds=35) 913 914 element = etree.Element('test') 915 XmlDocument().to_parent(None, SomeBlob, gg, element, gg.get_namespace()) 916 element = element[0] 917 918 print(gg.howlong) 919 print(etree.tostring(element, pretty_print=True)) 920 assert element[0].text == answer 921 922 data = element.find('{%s}howlong' % gg.get_namespace()).text 923 self.assertEqual(data, answer) 924 s1 = XmlDocument().from_element(None, SomeBlob, element) 925 assert s1.howlong.total_seconds() == gg.howlong.total_seconds() 926 927 def test_duration_positive_minutes_and_seconds_only(self): 928 answer = 'PT5M35S' 929 gg = SomeBlob() 930 gg.howlong = timedelta(minutes=5, seconds=35) 931 932 element = etree.Element('test') 933 XmlDocument().to_parent(None, SomeBlob, gg, element, gg.get_namespace()) 934 element = element[0] 935 936 print(gg.howlong) 937 print(etree.tostring(element, pretty_print=True)) 938 assert element[0].text == answer 939 940 data = element.find('{%s}howlong' % gg.get_namespace()).text 941 self.assertEqual(data, answer) 942 s1 = XmlDocument().from_element(None, SomeBlob, element) 943 assert s1.howlong.total_seconds() == gg.howlong.total_seconds() 944 945 def test_duration_positive_milliseconds_only(self): 946 answer = 'PT0.666000S' 947 gg = SomeBlob() 948 gg.howlong = timedelta(milliseconds=666) 949 950 element = etree.Element('test') 951 XmlDocument().to_parent(None, SomeBlob, gg, element, gg.get_namespace()) 952 element = element[0] 953 954 print(gg.howlong) 955 print(etree.tostring(element, pretty_print=True)) 956 assert element[0].text == answer 957 958 data = element.find('{%s}howlong' % gg.get_namespace()).text 959 self.assertEqual(data, answer) 960 s1 = XmlDocument().from_element(None, SomeBlob, element) 961 assert s1.howlong.total_seconds() == gg.howlong.total_seconds() 962 963 def test_duration_xml_duration(self): 964 dur = datetime.timedelta(days=5 + 30 + 365, hours=1, minutes=1, 965 seconds=12, microseconds=8e5) 966 967 str1 = 'P400DT3672.8S' 968 str2 = 'P1Y1M5DT1H1M12.8S' 969 970 self.assertEqual(dur, ProtocolBase().from_unicode(Duration, str1)) 971 self.assertEqual(dur, ProtocolBase().from_unicode(Duration, str2)) 972 973 self.assertEqual(dur, ProtocolBase().from_unicode(Duration, 974 ProtocolBase().to_unicode(Duration, dur))) 975 976 977if __name__ == '__main__': 978 unittest.main() 979