DatatypeHandling.py

  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)