home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / pyshared / rdflib / Literal.py < prev    next >
Encoding:
Python Source  |  2009-03-04  |  12.9 KB  |  412 lines

  1. from rdflib.Identifier import Identifier
  2. from rdflib.URIRef import URIRef
  3. from rdflib.Namespace import Namespace
  4. from rdflib.exceptions import Error
  5. from datetime import date,time,datetime
  6. from time import strptime
  7. import base64
  8.  
  9. try:
  10.     from hashlib import md5
  11. except ImportError:
  12.     from md5 import md5    
  13.  
  14. import logging
  15.  
  16. _logger = logging.getLogger(__name__)
  17.  
  18. class Literal(Identifier):
  19.     """
  20.     RDF Literal: http://www.w3.org/TR/rdf-concepts/#section-Graph-Literal
  21.  
  22.     >>> Literal(1).toPython()
  23.     1L
  24.     >>> cmp(Literal("adsf"), 1)
  25.     1
  26.     >>> lit2006 = Literal('2006-01-01',datatype=_XSD_NS.date)
  27.     >>> lit2006.toPython()
  28.     datetime.date(2006, 1, 1)
  29.     >>> lit2006 < Literal('2007-01-01',datatype=_XSD_NS.date)
  30.     True
  31.     >>> Literal(datetime.utcnow()).datatype
  32.     rdflib.URIRef('http://www.w3.org/2001/XMLSchema#dateTime')
  33.     >>> oneInt     = Literal(1)
  34.     >>> twoInt     = Literal(2)
  35.     >>> twoInt < oneInt
  36.     False
  37.     >>> Literal('1') < Literal(1)
  38.     False
  39.     >>> Literal('1') < Literal('1')
  40.     False
  41.     >>> Literal(1) < Literal('1')
  42.     True
  43.     >>> Literal(1) < Literal(2.0)
  44.     True
  45.     >>> Literal(1) < URIRef('foo')
  46.     True
  47.     >>> Literal(1) < 2.0
  48.     True
  49.     >>> Literal(1) < object  
  50.     True
  51.     >>> lit2006 < "2007"
  52.     True
  53.     >>> "2005" < lit2006
  54.     True
  55.     """
  56.  
  57.     __slots__ = ("language", "datatype", "_cmp_value")
  58.  
  59.     def __new__(cls, value, lang=None, datatype=None):
  60.         if datatype:
  61.             lang = None
  62.         else:
  63.             value,datatype = _castPythonToLiteral(value)
  64.             if datatype:
  65.                 lang = None
  66.         if datatype:
  67.             datatype = URIRef(datatype)
  68.         try:
  69.             inst = unicode.__new__(cls,value)
  70.         except UnicodeDecodeError:
  71.             inst = unicode.__new__(cls,value,'utf-8')
  72.         inst.language = lang
  73.         inst.datatype = datatype
  74.         inst._cmp_value = inst._toCompareValue()
  75.         return inst
  76.  
  77.     def __reduce__(self):
  78.         return (Literal, (unicode(self), self.language, self.datatype),)
  79.  
  80.     def __getstate__(self):
  81.         return (None, dict(language=self.language, datatype=self.datatype))
  82.  
  83.     def __setstate__(self, arg):
  84.         _, d = arg
  85.         self.language = d["language"]
  86.         self.datatype = d["datatype"]
  87.  
  88.     def __add__(self, val):
  89.         """
  90.         >>> Literal(1) + 1
  91.         2L
  92.         >>> Literal("1") + "1"
  93.         rdflib.Literal(u'11')
  94.         """
  95.  
  96.         py = self.toPython()
  97.         if isinstance(py, Literal):
  98.             s = super(Literal, self).__add__(val)            
  99.             return Literal(s, self.language, self.datatype)
  100.         else:
  101.             return py + val 
  102.  
  103.  
  104.     
  105.     def __lt__(self, other):
  106.         """
  107.         >>> Literal("YXNkZg==", datatype=_XSD_NS[u'base64Binary']) < "foo"
  108.         True
  109.         >>> u"\xfe" < Literal(u"foo")
  110.         False
  111.         >>> Literal(base64.encodestring(u"\xfe".encode("utf-8")), datatype=URIRef("http://www.w3.org/2001/XMLSchema#base64Binary")) < u"foo"
  112.         False
  113.         """
  114.  
  115.         if other is None:
  116.             return False # Nothing is less than None
  117.         try:
  118.             return self._cmp_value < other
  119.         except TypeError, te:
  120.             return unicode(self._cmp_value) < other
  121.         except UnicodeDecodeError, ue:
  122.             if isinstance(self._cmp_value, str):
  123.                 return self._cmp_value < other.encode("utf-8")
  124.             else:
  125.                 raise ue
  126.  
  127.     def __le__(self, other):
  128.         if other is None:
  129.             return False
  130.         if self==other:
  131.             return True
  132.         else:
  133.             return self < other
  134.  
  135.     def __gt__(self, other):
  136.         if other is None:
  137.             return True # Everything is greater than None
  138.         try:
  139.             return self._cmp_value > other
  140.         except TypeError, te:
  141.             return unicode(self._cmp_value) > other
  142.         except UnicodeDecodeError, ue:
  143.             if isinstance(self._cmp_value, str):
  144.                 return self._cmp_value > other.encode("utf-8")
  145.             else:
  146.                 raise ue
  147.  
  148.     def __ge__(self, other):
  149.         if other is None:
  150.             return False
  151.         if self==other:
  152.             return True
  153.         else:
  154.             return self > other
  155.  
  156.     def __ne__(self, other):
  157.         """
  158.         Overriden to ensure property result for comparisons with None via !=.
  159.         Routes all other such != and <> comparisons to __eq__
  160.         
  161.         >>> Literal('') != None
  162.         True
  163.         >>> Literal('2') <> Literal('2')
  164.         False
  165.          
  166.         """
  167.         return not self.__eq__(other)
  168.  
  169.     def __hash__(self):
  170.         """
  171.         >>> a = {Literal('1',datatype=_XSD_NS.integer):'one'}
  172.         >>> Literal('1',datatype=_XSD_NS.double) in a
  173.         False
  174.         
  175.         [[
  176.         Called for the key object for dictionary operations, 
  177.         and by the built-in function hash(). Should return 
  178.         a 32-bit integer usable as a hash value for 
  179.         dictionary operations. The only required property 
  180.         is that objects which compare equal have the same 
  181.         hash value; it is advised to somehow mix together 
  182.         (e.g., using exclusive or) the hash values for the 
  183.         components of the object that also play a part in 
  184.         comparison of objects. 
  185.         ]] -- 3.4.1 Basic customization (Python)
  186.  
  187.     
  188.         [[
  189.         Two literals are equal if and only if all of the following hold:
  190.         * The strings of the two lexical forms compare equal, character by character.
  191.         * Either both or neither have language tags.
  192.         * The language tags, if any, compare equal.
  193.         * Either both or neither have datatype URIs.
  194.         * The two datatype URIs, if any, compare equal, character by character.
  195.         ]] -- 6.5.1 Literal Equality (RDF: Concepts and Abstract Syntax)
  196.         
  197.         """
  198.         return hash(str(self)) ^ hash(self.language) ^ hash(self.datatype) 
  199.  
  200.     def __eq__(self, other):
  201.         """        
  202.         >>> f = URIRef("foo")
  203.         >>> f is None or f == ''
  204.         False
  205.         >>> Literal("1", datatype=URIRef("foo")) == Literal("1", datatype=URIRef("foo"))
  206.         True
  207.         >>> Literal("1", datatype=URIRef("foo")) == Literal("2", datatype=URIRef("foo"))
  208.         False
  209.         >>> Literal("1", datatype=URIRef("foo")) == "asdf"
  210.         False
  211.         >>> Literal('2007-01-01', datatype=_XSD_NS.date) == Literal('2007-01-01', datatype=_XSD_NS.date)
  212.         True
  213.         >>> Literal('2007-01-01', datatype=_XSD_NS.date) == date(2007, 1, 1)
  214.         True
  215.         >>> oneInt     = Literal(1)
  216.         >>> oneNoDtype = Literal('1')
  217.         >>> oneInt == oneNoDtype
  218.         False
  219.         >>> Literal("1",_XSD_NS[u'string']) == Literal("1",_XSD_NS[u'string'])
  220.         True
  221.         >>> Literal("one",lang="en") == Literal("one",lang="en")
  222.         True
  223.         >>> Literal("hast",lang='en') == Literal("hast",lang='de')
  224.         False
  225.         >>> oneInt == Literal(1)
  226.         True
  227.         >>> oneFloat   = Literal(1.0)
  228.         >>> oneInt == oneFloat
  229.         True
  230.         >>> oneInt == 1
  231.         True
  232.         """
  233.         if other is None:
  234.             return False
  235.         if isinstance(other, Literal):
  236.             return self._cmp_value == other._cmp_value
  237.         else:
  238.             return self._cmp_value == other
  239.  
  240.     def n3(self):
  241.         language = self.language
  242.         datatype = self.datatype
  243.         # unfortunately this doesn't work: a newline gets encoded as \\n, which is ok in sourcecode, but we want \n
  244.         #encoded = self.encode('unicode-escape').replace('\\', '\\\\').replace('"','\\"')
  245.         #encoded = self.replace.replace('\\', '\\\\').replace('"','\\"')
  246.  
  247.         # TODO: We could also chose quotes based on the quotes appearing in the string, i.e. '"' and "'" ...
  248.  
  249.         # which is nicer?
  250.         #if self.find("\"")!=-1 or self.find("'")!=-1 or self.find("\n")!=-1:
  251.         if self.find("\n")!=-1:
  252.             # Triple quote this string.
  253.             encoded=self.replace('\\', '\\\\')
  254.             if self.find('"""')!=-1: 
  255.                 # is this ok?
  256.                 encoded=encoded.replace('"""','\\"""')
  257.             if encoded.endswith('"'): encoded=encoded[:-1]+"\\\""
  258.             encoded='"""%s"""'%encoded
  259.         else: 
  260.             encoded='"%s"'%self.replace('\n','\\n').replace('\\', '\\\\').replace('"','\\"')
  261.         if language:
  262.             if datatype:    
  263.                 return '%s@%s^^<%s>' % (encoded, language, datatype)
  264.             else:
  265.                 return '%s@%s' % (encoded, language)
  266.         else:
  267.             if datatype:
  268.                 return '%s^^<%s>' % (encoded, datatype)
  269.             else:
  270.                 return '%s' % encoded
  271.  
  272.     def __str__(self):
  273.         return self.encode("unicode-escape")
  274.  
  275.     def __repr__(self):
  276.         args = [super(Literal, self).__repr__()]
  277.         if self.language is not None:
  278.             args.append("lang=%s" % repr(self.language))
  279.         if self.datatype is not None:
  280.             args.append("datatype=%s" % repr(self.datatype))
  281.         return """rdflib.Literal(%s)""" % ", ".join(args)
  282.  
  283.     def toPython(self):
  284.         """
  285.         Returns an appropriate python datatype derived from this RDF Literal
  286.         """
  287.         convFunc = _toPythonMapping.get(self.datatype, None)
  288.  
  289.         if convFunc:
  290.             rt = convFunc(self)
  291.         else:
  292.             rt = self
  293.         return rt
  294.  
  295.     def _toCompareValue(self):
  296.         try:
  297.             rt = self.toPython()
  298.         except Exception, e:
  299.             _logger.warning("could not convert %s to a Python datatype" % repr(self))
  300.             rt = self
  301.                 
  302.         if rt is self:
  303.             if self.language is None and self.datatype is None:
  304.                 return unicode(rt)
  305.             else:
  306.                 return (unicode(rt), rt.datatype, rt.language)
  307.         return rt
  308.  
  309.     def md5_term_hash(self):
  310.         d = md5(str(self))
  311.         d.update("L")
  312.         return d.hexdigest()
  313.  
  314.  
  315. _XSD_NS = Namespace(u'http://www.w3.org/2001/XMLSchema#')
  316.  
  317. #Casts a python datatype to a tuple of the lexical value and a datatype URI (or None)
  318. def _castPythonToLiteral(obj):
  319.     for pType,(castFunc,dType) in _PythonToXSD:
  320.         if isinstance(obj,pType):
  321.             if castFunc:
  322.                 return castFunc(obj),dType
  323.             elif dType:
  324.                 return obj,dType
  325.             else:
  326.                 return obj,None
  327.     return obj, None # TODO: is this right for the fall through case?
  328.  
  329. # Mappings from Python types to XSD datatypes and back (burrowed from sparta)
  330. # datetime instances are also instances of date... so we need to order these.
  331. _PythonToXSD = [
  332.     (basestring, (None,None)),
  333.     (float     , (None,_XSD_NS[u'float'])),
  334.     (int       , (None,_XSD_NS[u'integer'])),
  335.     (long      , (None,_XSD_NS[u'long'])),
  336.     (bool      , (None,_XSD_NS[u'boolean'])),
  337.     (datetime  , (lambda i:i.isoformat(),_XSD_NS[u'dateTime'])),
  338.     (date      , (lambda i:i.isoformat(),_XSD_NS[u'date'])),
  339.     (time      , (lambda i:i.isoformat(),_XSD_NS[u'time'])),
  340. ]
  341.  
  342. def _strToTime(v) :
  343.     return strptime(v,"%H:%M:%S")
  344.  
  345. def _strToDate(v) :
  346.     tstr = strptime(v,"%Y-%m-%d")
  347.     return date(tstr.tm_year,tstr.tm_mon,tstr.tm_mday)
  348.  
  349. def _strToDateTime(v) :
  350.     """
  351.     Attempt to cast to datetime, or just return the string (otherwise)
  352.     """
  353.     try:
  354.         tstr = strptime(v,"%Y-%m-%dT%H:%M:%S")
  355.     except:
  356.         try:
  357.             tstr = strptime(v,"%Y-%m-%dT%H:%M:%SZ")
  358.         except:
  359.             try:
  360.                 tstr = strptime(v,"%Y-%m-%dT%H:%M:%S%Z")
  361.             except:
  362.                 return v
  363.  
  364.     return datetime(tstr.tm_year,tstr.tm_mon,tstr.tm_mday,tstr.tm_hour,tstr.tm_min,tstr.tm_sec)
  365.  
  366. XSDToPython = {
  367.     _XSD_NS[u'time']               : _strToTime,
  368.     _XSD_NS[u'date']               : _strToDate,
  369.     _XSD_NS[u'dateTime']           : _strToDateTime,
  370.     _XSD_NS[u'string']             : None,
  371.     _XSD_NS[u'normalizedString']   : None,
  372.     _XSD_NS[u'token']              : None,
  373.     _XSD_NS[u'language']           : None,
  374.     _XSD_NS[u'boolean']            : lambda i:i.lower() in ['1','true'],
  375.     _XSD_NS[u'decimal']            : float,
  376.     _XSD_NS[u'integer']            : long,
  377.     _XSD_NS[u'nonPositiveInteger'] : int,
  378.     _XSD_NS[u'long']               : long,
  379.     _XSD_NS[u'nonNegativeInteger'] : int,
  380.     _XSD_NS[u'negativeInteger']    : int,
  381.     _XSD_NS[u'int']                : long,
  382.     _XSD_NS[u'unsignedLong']       : long,
  383.     _XSD_NS[u'positiveInteger']    : int,
  384.     _XSD_NS[u'short']              : int,
  385.     _XSD_NS[u'unsignedInt']        : long,
  386.     _XSD_NS[u'byte']               : int,
  387.     _XSD_NS[u'unsignedShort']      : int,
  388.     _XSD_NS[u'unsignedByte']       : int,
  389.     _XSD_NS[u'float']              : float,
  390.     _XSD_NS[u'double']             : float,
  391.     _XSD_NS[u'base64Binary']       : base64.decodestring,
  392.     _XSD_NS[u'anyURI']             : None,
  393. }
  394.  
  395. _toPythonMapping = {}
  396. _toPythonMapping.update(XSDToPython)
  397.  
  398. def bind(datatype, conversion_function):
  399.     """bind a datatype to a function for converting it into a Python instance."""
  400.     if datatype in _toPythonMapping:
  401.         _logger.warning("datatype '%s' was already bound. Rebinding." % datatype)
  402.     _toPythonMapping[datatype] = conversion_function
  403.  
  404.  
  405.  
  406. def test():
  407.     import doctest
  408.     doctest.testmod()
  409.  
  410. if __name__ == '__main__':
  411.     test()
  412.