1# -*- coding: utf-8 -*-
2#
3"""
4Most of the XSD datatypes are handled directly by RDFLib. However, in some cases, that is not good enough. There are two
5major reasons for this:
6
7#. Some datatypes are missing from RDFLib and required by OWL 2 RL and/or RDFS.
8#. In other cases, though the datatype is present, RDFLib is fairly lax in checking the lexical value of those datatypes. Typical case is boolean.
9
10Some of these deficiencies are handled by this module. All the functions convert the lexical value into a
11python datatype (or return the original string if this is not possible) which will be used, e.g.,
12for comparisons (equalities). If the lexical value constraints are not met, exceptions are raised.
13
14**Requires**: `RDFLib`_, 4.0.0 and higher.
15
16.. _RDFLib: https://github.com/RDFLib/rdflib
17
18**License**: This software is available for use under the `W3C Software License`_.
19
20.. _W3C Software License: http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
21
22**Organization**: `World Wide Web Consortium`_
23
24.. _World Wide Web Consortium: http://www.w3.org
25
26**Author**: `Ivan Herman`_
27
28.. _Ivan Herman: http://www.w3.org/People/Ivan/
29"""
30
31__author__ = "Ivan Herman"
32__contact__ = "Ivan Herman, ivan@w3.org"
33__license__ = "W3C® SOFTWARE NOTICE AND LICENSE, http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231"
34
35# noinspection PyPep8Naming
36from owlrl.RDFS import RDFNS as ns_rdf
37
38from rdflib.term import XSDToPython, Literal, _toPythonMapping
39
40# noinspection PyPep8Naming
41from rdflib.namespace import XSD as ns_xsd
42
43import datetime, time, re
44from decimal import Decimal
45
46
47# noinspection PyMissingConstructor,PyPep8Naming
48class _namelessTZ(datetime.tzinfo):
49 """
50 (Nameless) timezone object. The python datetime object requires timezones as
51 a specific object added to the conversion, rather than the explicit hour and minute
52 difference used by XSD. This class is used to wrap around the hour/minute values.
53
54 :param hours: Hour offset.
55 :param minutes: Minute offset
56 """
57
58 def __init__(self, hours, minutes):
59 """
60 @param hours: hour offset
61 @param minutes: minute offset
62 """
63 self.__offset = datetime.timedelta(hours=hours, minutes=minutes)
64 self.__name = "nameless"
65
66 def utcoffset(self, dt):
67 return self.__offset
68
69 def tzname(self, dt):
70 return self.__name
71
72 def dst(self, dt):
73 return datetime.timedelta(0)
74
75
76# noinspection PyPep8Naming
77def _returnTimeZone(incoming_v):
78 """Almost all time/date related methods require the extraction of an optional time zone information.
79 @param incoming_v: the time/date string
80 @return (v,timezone) tuple; 'v' is the input string with the timezone info cut off, 'timezone' is a L{_namelessTZ}
81 instance or None
82 """
83 if incoming_v[-1] == "Z":
84 v = incoming_v[:-1]
85 tzone = _namelessTZ(0, 0)
86 else:
87 pattern = ".*(\+|-)([0-9][0-9]):([0-9][0-9])"
88 match = re.match(pattern, incoming_v)
89 if match is None:
90 v = incoming_v
91 tzone = None
92 else:
93 hours = int(match.groups()[1])
94 if match.groups()[0] == "-":
95 hours = -hours - 1
96 minutes = int(match.groups()[2])
97 v = incoming_v[:-6]
98 tzone = _namelessTZ(hours, minutes)
99 return v, tzone
100
101
102# Booleans ##################################################
103# noinspection PyPep8Naming
104def _strToBool(v):
105 """The built-in conversion to boolean is way too lax. The xsd specification requires that only true, false, 1 or 0 should be used...
106 @param v: the literal string defined as boolean
107 @return corresponding boolean value
108 @raise ValueError: invalid boolean values
109 """
110 if v.lower() == "true" or v.lower() == "1":
111 return True
112 elif v.lower() == "false" or v.lower() == "0":
113 return False
114 else:
115 raise ValueError("Invalid boolean literal value %s" % v)
116
117
118# Decimals ##################################################
119# noinspection PyPep8Naming
120def _strToDecimal(v):
121 """The built in datatype handling for RDFLib maps a decimal number to float, but the python version 2.4 and upwards
122 also has a Decimal number. Better make use of that to use very high numbers.
123 However, there is also a big difference between Python's decimal and XSD's decimal, because the latter does not
124 allow for an exponential normal form (why???). This must be filtered out.
125 @param v: the literal string defined as decimal
126 @return Decimal
127 @raise ValueError: invalid decimal value
128 """
129 # check whether the lexical form of 'v' is o.k.
130 if v.find("E") != -1 or v.find("e") != -1:
131 # this is an invalid lexical form, though would be accepted by Python
132 raise ValueError("Invalid decimal literal value %s" % v)
133 else:
134 return Decimal(v)
135
136
137# ANY URIS ##################################################
138# set of characters allowed in a hexadecimal number
139_hexc = ["A", "B", "C", "D", "E", "F", "a", "b", "c", "d", "e", "f"]
140# set of numerals
141_numb = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
142# noinspection PyPep8Naming
143def _strToAnyURI(v):
144 """Rudimentary test for the AnyURI value. If it is a relative URI, then some tests are done to filter out
145 mistakes. I am not sure this is the full implementation of the RFC, though, may have to be checked at some point
146 later.
147 @param v: the literal string defined as a URI
148 @return the incoming value
149 @raise ValueError: invalid URI value
150 """
151 import urllib.parse
152
153 if len(v) == 0:
154 return v
155 if urllib.parse.urlsplit(v)[0] != "":
156 # this means that there is a proper scheme, the URI should be kosher
157 return v
158 else:
159 # this is meant to be a relative URI.
160 # If I am correct, that cannot begin with one or more "?" or ":" characters
161 # all others are o.k.
162 # if it begins with a % then it should be followed by two hexa characters,
163 # otherwise it is also a bug
164 if v[0] == "%":
165 if (
166 len(v) >= 3
167 and (v[1] in _hexc or v[1] in _numb)
168 and (v[2] in _hexc or v[2] in _numb)
169 ):
170 return v
171 else:
172 raise ValueError("Invalid IRI %s" % v)
173 elif v[0] == "?" or v[0] == ":":
174 raise ValueError("Invalid IRI %s" % v)
175 else:
176 return v
177
178
179# Base64Binary ##################################################
180# noinspection PyPep8Naming
181def _strToBase64Binary(v):
182 """Rudimentary test for the base64Binary value. The problem is that the built-in b64 module functions ignore the
183 fact that only a certain family of characters are allowed to appear in the lexical value, so this is checked first.
184 @param v: the literal string defined as a base64encoded string
185 @return the decoded (binary) content
186 @raise ValueError: invalid base 64 binary value
187 """
188 import base64
189
190 if v.replace("=", "x").replace("+", "y").replace("/", "z").isalnum():
191 try:
192 return base64.standard_b64decode(v)
193 except:
194 raise ValueError("Invalid Base64Binary %s" % v)
195 else:
196 raise ValueError("Invalid Base64Binary %s" % v)
197
198
199# Numerical types ##################################################
200# limits for unsigned bytes
201_limits_unsignedByte = [-1, 256]
202
203
204# limits for bytes
205_limits_byte = [-129, 128]
206
207
208# limits for unsigned int
209_limits_unsignedInt = [-1, 4294967296]
210
211
212# limits for int
213_limits_int = [-2147483649, 2147483648]
214
215
216# limits for unsigned short
217_limits_unsignedShort = [-1, 65536]
218
219
220# limits for short
221_limits_short = [-32769, 32768]
222
223
224# limits for unsigned long
225_limits_unsignedLong = [-1, 18446744073709551616]
226
227
228# limits for long
229_limits_long = [-9223372036854775809, 9223372036854775808]
230
231
232# limits for positive integer
233_limits_positiveInteger = [0, None]
234
235
236# limits for non positive integer
237_limits_nonPositiveInteger = [None, 1]
238
239
240# limits for non negative integer
241_limits_nonNegativeInteger = [-1, None]
242
243
244# limits for negative integer
245_limits_negativeInteger = [None, 0]
246
247
248# noinspection PyPep8Naming,PyBroadException
249def _strToBoundNumeral(v, interval, conversion):
250 """Test (and convert) a generic numerical type, with a check against a lower and upper limit.
251 @param v: the literal string to be converted
252 @param interval: lower and upper bounds (non inclusive). If the value is None, no comparison should be done
253 @param conversion: conversion function, ie, int, long, etc
254 @raise ValueError: invalid value
255 """
256 try:
257 i = conversion(v)
258 if (interval[0] is None or interval[0] < i) and (
259 interval[1] is None or i < interval[1]
260 ):
261 return i
262 except:
263 pass
264 raise ValueError("Invalid numerical value %s" % v)
265
266
267# Double and float ##################################################
268# noinspection PyPep8Naming
269def _strToDouble(v):
270 """Test and convert a double value into a Decimal or float. Raises an exception if the number is outside the
271 permitted range, ie, 1.0E+310 and 1.0E-330. To be on the safe side (python does not have double!) Decimals are used
272 if possible. Upper and lower values, as required by xsd, are checked (and these fixed values are the reasons
273 why Decimal is used!)
274
275 @param v: the literal string defined as a double
276 @return Decimal
277 @raise ValueError: invalid value
278 """
279 try:
280 value = Decimal(v)
281 upper = Decimal("1.0E+310")
282 lower = Decimal("1.0E-330")
283 if lower < abs(value) < upper:
284 # bingo
285 return value
286 else:
287 raise ValueError("Invalid double %s" % v)
288 except:
289 # there was a problem in creating a decimal...
290 raise ValueError("Invalid double %s" % v)
291
292
293# noinspection PyPep8Naming
294def _strToFloat(v):
295 """Test and convert a float value into Decimal or (python) float. Raises an exception if the number is outside the
296 permitted range, ie, 1.0E+40 and 1.0E-50. (And these fixed values are the reasons why Decimal is used!)
297
298 @param v: the literal string defined as a float
299 @return Decimal if the local python version is >= 2.4, float otherwise
300 @raise ValueError: invalid value
301 """
302 try:
303 value = Decimal(v)
304 upper = Decimal("1.0E+40")
305 lower = Decimal("1.0E-50")
306 if lower < abs(value) < upper:
307 # bingo
308 return value
309 else:
310 raise ValueError("Invalid float %s" % v)
311 except:
312 # there was a problem in creating a decimal...
313 raise ValueError("Invalid float %s" % v)
314
315
316# hexa ##################################################
317# noinspection PyPep8Naming
318def _strToHexBinary(v):
319 """Test (and convert) hexa integer values. The number of characters should be even.
320 @param v: the literal string defined as a hexa number
321 @return long value
322 @raise ValueError: invalid value
323 """
324 # first of all, the number of characters must be even according to the xsd spec:
325 length = len(v)
326 if (length / 2) * 2 != length:
327 raise ValueError("Invalid hex binary number %s" % v)
328 return int(v, 16)
329
330
331# Datetime, date timestamp, etc ################################
332# noinspection PyPep8Naming
333def _strToDateTimeAndStamp(incoming_v, timezone_required=False):
334 """Test (and convert) datetime and date timestamp values.
335 @param incoming_v: the literal string defined as the date and time
336 @param timezone_required: whether the timezone is required (ie, for date timestamp) or not
337 @return datetime
338 @rtype: datetime.datetime
339 @raise ValueError: invalid datetime or date timestamp
340 """
341
342 # First, handle the timezone portion, if there is any
343 (v, tzone) = _returnTimeZone(incoming_v)
344
345 # Check on the timezone. For time date stamp object it is required
346 if timezone_required and tzone is None:
347 raise ValueError("Invalid datetime %s" % incoming_v)
348
349 # The microseconds should be handled here...
350 final_v = v
351 milliseconds = 0
352 milpattern = "(.*)(\.)([0-9]*)"
353 match = re.match(milpattern, v)
354 if match is not None:
355 # we have a millisecond portion...
356 try:
357 final_v = match.groups()[0]
358 milliseconds = int(match.groups()[2])
359 except:
360 raise ValueError("Invalid datetime %s" % incoming_v)
361 #
362 # By now, the pattern should be clear
363 # This may raise an exception...
364 try:
365 tstr = time.strptime(final_v, "%Y-%m-%dT%H:%M:%S")
366 if tzone is not None:
367 return datetime.datetime(
368 tstr.tm_year,
369 tstr.tm_mon,
370 tstr.tm_mday,
371 tstr.tm_hour,
372 tstr.tm_min,
373 tstr.tm_sec,
374 milliseconds,
375 tzone,
376 )
377 else:
378 return datetime.datetime(
379 tstr.tm_year,
380 tstr.tm_mon,
381 tstr.tm_mday,
382 tstr.tm_hour,
383 tstr.tm_min,
384 tstr.tm_sec,
385 milliseconds,
386 )
387 except:
388 raise ValueError("Invalid datetime %s" % incoming_v)
389
390
391# noinspection PyPep8Naming
392def _strToTime(incoming_v):
393 """Test (and convert) time values.
394 @param incoming_v: the literal string defined as time value
395 @return time
396 @rtype datetime.time
397 @raise ValueError: invalid datetime or date timestamp
398 """
399
400 # First, handle the timezone portion, if there is any
401 (v, tzone) = _returnTimeZone(incoming_v)
402
403 # The microseconds should be handled here...
404 final_v = v
405 milliseconds = 0
406 milpattern = "(.*)(\.)([0-9]*)"
407 match = re.match(milpattern, v)
408 if match is not None:
409 # we have a millisecond portion...
410 try:
411 final_v = match.groups()[0]
412 milliseconds = int(match.groups()[2])
413 except:
414 raise ValueError("Invalid datetime %s" % incoming_v)
415 #
416 # By now, the pattern should be clear
417 # This may raise an exception...
418 try:
419 tstr = time.strptime(final_v, "%H:%M:%S")
420 if tzone is not None:
421 return datetime.time(
422 tstr.tm_hour, tstr.tm_min, tstr.tm_sec, milliseconds, tzone
423 )
424 else:
425 return datetime.time(tstr.tm_hour, tstr.tm_min, tstr.tm_sec, milliseconds)
426 except:
427 raise ValueError("Invalid time %s" % incoming_v)
428
429
430# noinspection PyPep8Naming
431def _strToDate(incoming_v):
432 """Test (and convert) date values.
433 @param incoming_v: the literal string defined as date (in iso format)
434 @return date
435 @return datetime.date
436 @raise ValueError: invalid datetime or date timestamp
437 """
438
439 # First, handle the timezone portion, if there is any
440 (final_v, tzone) = _returnTimeZone(incoming_v)
441
442 # This may raise an exception...
443 try:
444 tstr = time.strptime(final_v, "%Y-%m-%d")
445 return datetime.date(tstr.tm_year, tstr.tm_mon, tstr.tm_mday)
446 except:
447 raise ValueError("Invalid date %s" % incoming_v)
448
449
450# The 'g' series for dates ############################
451# The 'g' datatypes (eg, gYear) cannot be directly represented as a python datatype
452# the series of methods below simply check whether the incoming string is o.k., but the
453# returned value is the same as the original
454# noinspection PyPep8Naming
455def _strTogYearMonth(v):
456 """Test gYearMonth value
457 @param v: the literal string
458 @return v
459 @raise ValueError: invalid value
460 """
461 try:
462 time.strptime(v + "-01", "%Y-%m-%d")
463 return v
464 except:
465 raise ValueError("Invalid gYearMonth %s" % v)
466
467
468# noinspection PyPep8Naming
469def _strTogYear(v):
470 """Test gYear value
471 @param v: the literal string
472 @return v
473 @raise ValueError: invalid value
474 """
475 try:
476 time.strptime(v + "-01-01", "%Y-%m-%d")
477 return v
478 except:
479 raise ValueError("Invalid gYear %s" % v)
480
481
482# noinspection PyPep8Naming
483def _strTogMonthDay(v):
484 """Test gYearMonth value
485 @param v: the literal string
486 @return v
487 @raise ValueError: invalid value
488 """
489 try:
490 time.strptime("2008-" + v, "%Y-%m-%d")
491 return v
492 except:
493 raise ValueError("Invalid gMonthDay %s" % v)
494
495
496# noinspection PyPep8Naming
497def _strTogDay(v):
498 """Test gYearMonth value
499 @param v: the literal string
500 @return v
501 @raise ValueError: invalid value
502 """
503 try:
504 time.strptime("2001-01-" + v, "%Y-%m-%d")
505 return v
506 except:
507 raise ValueError("Invalid gDay %s" % v)
508
509
510# noinspection PyPep8Naming
511def _strTogMonth(v):
512 """Test gYearMonth value
513 @param v: the literal string
514 @return v
515 @raise ValueError: invalid value
516 """
517 try:
518 time.strptime("2001-" + v + "-01", "%Y-%m-%d")
519 return v
520 except:
521 raise ValueError("Invalid gMonth %s" % v)
522
523
524# XML Literal #########################################
525# noinspection PyPep8Naming
526def _strToXMLLiteral(v):
527 """Test (and convert) XML Literal values.
528 @param v: the literal string defined as an xml literal
529 @return the canonical version of the same xml text
530 @raise ValueError: incorrect xml string
531 """
532 import xml.dom.minidom
533
534 try:
535 dom = xml.dom.minidom.parseString(v)
536 return dom.toxml()
537 except:
538 raise ValueError("Invalid XML Literal %s" % v)
539
540
541# language, NMTOKEN, NAME, etc #########################
542# regular expression for a 'language' datatype
543_re_language = "[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*"
544
545
546# regexp for NMTOKEN. It must be used with a re.U flag (the '(?U' regexp form did not work. It may depend on the
547# locale...)
548_re_NMTOKEN = "[\w:_.\-]+"
549
550
551# characters not permitted at a starting position for Name (otherwise Name is like NMTOKEN
552_re_Name_ex = [".", "-"] + _numb
553
554
555# regexp for NCName. It must be used with a re.U flag (the '(?U' regexp form did not work. It may depend on the
556# locale...)
557_re_NCName = "[\w_.\-]+"
558
559
560# characters not permitted at a starting position for NCName
561_re_NCName_ex = [".", "-"] + _numb
562
563
564# noinspection PyDefaultArgument,PyPep8Naming,PyPep8Naming
565def _strToVal_Regexp(v, regexp, flag=0, excludeStart=[]):
566 """Test (and convert) a generic string type, with a check against a regular expression.
567 @param v: the literal string to be converted
568 @param regexp: the regular expression to check against
569 @param flag: flags to be used in the regular expression
570 @param excludeStart: array of characters disallowed in the first position
571 @return original string
572 @raise ValueError: invalid value
573 """
574 match = re.match(regexp, v, flag)
575 if match is None or match.end() != len(v):
576 raise ValueError("Invalid literal %s" % v)
577 else:
578 if len(excludeStart) > 0 and v[0] in excludeStart:
579 raise ValueError("Invalid literal %s" % v)
580 return v
581
582
583# Disallowed characters in a token or a normalized string, as a regexp
584_re_token = "[^\n\t\r]+"
585
586
587# noinspection PyPep8Naming
588def _strToToken(v):
589 """Test (and convert) a string to a token.
590 @param v: the literal string to be converted
591 @return original string
592 @raise ValueError: invalid value
593 """
594 if len(v) == 0:
595 return v
596 # filter out the case when there are new lines and similar (if there is a problem, an exception is raised)
597 _strToVal_Regexp(v, _re_token)
598 v1 = " ".join(v.strip().split())
599 # normalize the string, and see if the result is the same:
600 if len(v1) == len(v):
601 # no characters lost, ie, no unnecessary spaces
602 return v
603 else:
604 raise ValueError("Invalid literal %s" % v)
605
606
607# plain literal ########################################
608# noinspection PyPep8Naming
609def _strToPlainLiteral(v):
610 """Test (and convert) a plain literal
611 @param v: the literal to be converted
612 @return a new RDFLib Literal with language tag
613 @raise ValueError: invalid value
614 """
615 reg = "(.*)@([^@]*)"
616 # a plain literal must match this regexp!
617 match = re.match(reg, v)
618 if match is None:
619 raise ValueError("Invalid plain literal %s" % v)
620 else:
621 lit = match.groups()[0]
622 if len(match.groups()) == 1 or match.groups()[1] == "":
623 # no language tag
624 return Literal(lit)
625 else:
626 lang = match.groups()[1]
627 # check if this is a correct language tag. Note that can raise an exception!
628 try:
629 lang = _strToVal_Regexp(lang, _re_language)
630 return Literal(lit, lang=lang.lower())
631 except:
632 raise ValueError("Invalid plain literal %s" % v)
633
634
635#####################################################################################
636# Replacement of RDFLib's conversion function. Each entry assigns a function to an XSD datatype, attempting to convert
637# a string to a Python datatype (or raise an exception if some problem is found)
638AltXSDToPYTHON = {
639 ns_xsd["language"]: lambda v: _strToVal_Regexp(v, _re_language),
640 ns_xsd["NMTOKEN"]: lambda v: _strToVal_Regexp(v, _re_NMTOKEN, re.U),
641 ns_xsd["Name"]: lambda v: _strToVal_Regexp(v, _re_NMTOKEN, re.U, _re_Name_ex),
642 ns_xsd["NCName"]: lambda v: _strToVal_Regexp(v, _re_NCName, re.U, _re_NCName_ex),
643 ns_xsd["token"]: _strToToken,
644 ns_rdf["PlainLiteral"]: _strToPlainLiteral,
645 ns_xsd["boolean"]: _strToBool,
646 ns_xsd["decimal"]: _strToDecimal,
647 ns_xsd["anyURI"]: _strToAnyURI,
648 ns_xsd["base64Binary"]: _strToBase64Binary,
649 ns_xsd["double"]: _strToDouble,
650 ns_xsd["float"]: _strToFloat,
651 ns_xsd["byte"]: lambda v: _strToBoundNumeral(v, _limits_byte, int),
652 ns_xsd["int"]: lambda v: _strToBoundNumeral(v, _limits_int, int),
653 ns_xsd["long"]: lambda v: _strToBoundNumeral(v, _limits_long, int),
654 ns_xsd["positiveInteger"]: lambda v: _strToBoundNumeral(
655 v, _limits_positiveInteger, int
656 ),
657 ns_xsd["nonPositiveInteger"]: lambda v: _strToBoundNumeral(
658 v, _limits_nonPositiveInteger, int
659 ),
660 ns_xsd["negativeInteger"]: lambda v: _strToBoundNumeral(
661 v, _limits_negativeInteger, int
662 ),
663 ns_xsd["nonNegativeInteger"]: lambda v: _strToBoundNumeral(
664 v, _limits_nonNegativeInteger, int
665 ),
666 ns_xsd["short"]: lambda v: _strToBoundNumeral(v, _limits_short, int),
667 ns_xsd["unsignedByte"]: lambda v: _strToBoundNumeral(v, _limits_unsignedByte, int),
668 ns_xsd["unsignedShort"]: lambda v: _strToBoundNumeral(
669 v, _limits_unsignedShort, int
670 ),
671 ns_xsd["unsignedInt"]: lambda v: _strToBoundNumeral(v, _limits_unsignedInt, int),
672 ns_xsd["unsignedLong"]: lambda v: _strToBoundNumeral(v, _limits_unsignedLong, int),
673 ns_xsd["hexBinary"]: _strToHexBinary,
674 ns_xsd["dateTime"]: lambda v: _strToDateTimeAndStamp(v, False),
675 ns_xsd["dateTimeStamp"]: lambda v: _strToDateTimeAndStamp(v, True),
676 ns_rdf["XMLLiteral"]: _strToXMLLiteral,
677 ns_xsd["integer"]: int,
678 ns_xsd["string"]: lambda v: v,
679 ns_rdf["HTML"]: lambda v: v,
680 ns_xsd["normalizedString"]: lambda v: _strToVal_Regexp(v, _re_token),
681 # These are RDFS specific...
682 ns_xsd["time"]: _strToTime,
683 ns_xsd["date"]: _strToDate,
684 ns_xsd["gYearMonth"]: _strTogYearMonth,
685 ns_xsd["gYear"]: _strTogYear,
686 ns_xsd["gMonthDay"]: _strTogMonthDay,
687 ns_xsd["gDay"]: _strTogDay,
688 ns_xsd["gMonth"]: _strTogMonth,
689}
690
691
692def use_Alt_lexical_conversions():
693 """
694 Registering the datatypes item for RDFLib, ie, bind the dictionary values. The 'bind' method of RDFLib adds
695 extra datatypes to the registered ones in RDFLib, though the table used here (I.e., :py:data:`.AltXSDToPYTHON`) actually
696 overrides all of the default conversion routines. The method also add a Decimal entry to the :code:`PythonToXSD` list of
697 RDFLib.
698 """
699 _toPythonMapping.update(AltXSDToPYTHON)
700
701
702def use_RDFLib_lexical_conversions():
703 """
704 Restore the original (ie, RDFLib) set of lexical conversion routines.
705 """
706 _toPythonMapping.update(XSDToPython)
707
708
709#######################################################################################
710# This module can pretty much tested individually...
711
712
713if __name__ == "__main__":
714 import sys
715
716 dtype = sys.argv[1]
717 string = sys.argv[2]
718 datatype = ns_xsd[dtype]
719 result = AltXSDToPYTHON[datatype](string)
720 print(type(result))
721 print(result)