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