1
2
3 """
4 descriptors.py
5
6 Created by Philip Cooper on 2008-02-03.
7 Copyright (c) 2008 Openvest. All rights reserved.
8 """
9
10 from rdflib import URIRef, BNode, Namespace
11 from rdflib.Identifier import Identifier
12 from rdfalchemy import rdfSubject, Literal
13 from copy import copy
14
15 import logging
16
17 __all__=["rdfSingle","rdfMultiple","rdfList","rdfContainer","owlTransitive"]
18
19
20
21
22 log=logging.getLogger(__name__)
23
24
25
26 RDF = Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#")
27
28
29 -def getList(sub, pred=None, db=None):
30 """Attempts to return a list from sub (subject that is)
31 passed in if it is a Collection or a Container (Bag,Seq or Alt)"""
32 if not db:
33 if isinstance(sub,rdfSubject):
34 db=sub.db
35 else:
36 db=rdfSubject.db
37 if isinstance(sub,rdfSubject):
38 sub = sub.resUri
39 if pred:
40 base = db.value(sub, pred, any=True)
41 else:
42
43 base=sub
44 if type(base) != BNode:
45
46 val=[o for o in db.objects(sub, pred)]
47 return val
48 members=[]
49 first = db.value(base, RDF.first)
50
51 if first:
52 while first:
53 members.append(first)
54 base = db.value(base, RDF.rest)
55 first = db.value(base, RDF.first)
56 return members
57
58 else:
59 i=1
60 first=db.value(base, RDF._1)
61 if not first:
62 raise AttributeError, "Not a list, or collection but another type of BNode"
63 while first:
64 members.append(first)
65 i += 1
66 first=db.value(base, RDF['_%d'%i])
67 return members
68
70 """suitable for a triple takes a value and returns a Literal, URIRef or BNode
71 suitable for a triple"""
72 if isinstance(value, rdfSubject):
73 return value.resUri
74 elif isinstance(value, Identifier):
75 return value
76 else:
77 return Literal(value)
78
87 """Abstract base class for descriptors
88 Descriptors are to map class instance variables to predicates
89 optional cacheName is where to store items
90 range_type is the rdf:type of the range of this predicate"""
91 - def __init__(self, pred, cacheName=None, range_type=None):
92 self.pred = pred
93 self.name = cacheName or pred
94 self.range_type = range_type
95
96 @property
98 """return the class that this descriptor is mapped to through the range_type"""
99 if self.range_type:
100 try:
101 return self._mappedClass
102 except AttributeError:
103 log.warn("Descriptor %s has range of: %s but not yet mapped"%(self, self.range_type))
104 return rdfSubject
105 else:
106 return rdfSubject
107
109 """deletes or removes from the database triples with:
110 obj.resUri as subject and self.pred as predicate
111 if the object of that triple is a Literal that stop
112 if the object of that triple is a BNode
113 then cascade the delete if that BNode has no further references to it
114 i.e. it is not the object in any other triples.
115 """
116
117 log.debug("DELETE with descriptor for %s on %s"%(self.pred, obj.n3()))
118
119 if obj.__dict__.has_key(self.name):
120 del obj.__dict__[self.name]
121
122 obj.__delitem__(self.pred)
123
128 '''This is a Discriptor
129 Takes a the URI of the predicate at initialization
130 Expects to return a single item
131 on Assignment will set that value to the
132 ONLY triple with that subject,predicate pair'''
133 - def __init__(self, pred, cacheName=None, range_type=None):
135
137 if obj is None:
138 return self
139 if self.name in obj.__dict__:
140 return obj.__dict__[self.name]
141 log.debug("Geting with descriptor %s for %s"%(self.pred,obj.n3()))
142 val=obj.__getitem__(self.pred)
143 if isinstance(val, (rdfSubject, BNode, URIRef)):
144 val = self.range_class(val)
145 obj.__dict__[self.name]= val
146 return val
147
149 log.debug("SET with descriptor value %s of type %s"%(value,type(value)))
150
151 if isinstance(value,(list,tuple,set)):
152 raise AttributeError("to set an rdfSingle you must pass in a single value")
153 obj.__dict__[self.name]= value
154 o =value2object(value)
155 obj.db.set((obj.resUri, self.pred, o))
156
159 '''This is a Discriptor
160 Expects to return a list of values (could be a list of one)'''
161 - def __init__(self, pred, cacheName=None, range_type=None):
163
165 if obj is None:
166 return self
167 if self.name in obj.__dict__:
168 return obj.__dict__[self.name]
169 val=[o for o in obj.db.objects(obj.resUri, self.pred)]
170 log.debug("Geting with descriptor %s for %s"%(self.pred,obj.n3()))
171
172
173 if len(val) == 1 \
174 and (obj.db.value(o,RDF.first) or obj.db.value(o,RDF._1)):
175 val=getList(obj, self.pred)
176 val=[(isinstance(v, (BNode,URIRef)) and self.range_class(v) or v.toPython()) for v in val]
177 obj.__dict__[self.name]= val
178 return val
179
181 log.debug("SET with descriptor value %s of type %s"%(newvals,type(newvals)))
182 if not isinstance(newvals, (list,tuple)):
183 raise AttributeError("to set a rdfMultiple you must pass in a list (it can be a list of one)")
184 try:
185 oldvals = obj.__dict__[self.name]
186 except KeyError:
187 oldvals = []
188 obj.__dict__[self.name] = oldvals
189 for value in oldvals:
190 if value not in newvals:
191 obj.db.remove((obj.resUri,self.pred, value2object(value)))
192 log.debug("removing: %s, %s, %s"%(obj.n3(),self.pred,value))
193 for value in newvals:
194 if value not in oldvals:
195 obj.db.add((obj.resUri, self.pred, value2object(value)))
196 log.debug("adding: %s, %s, %s"%(obj.n3(),self.pred,value))
197 obj.__dict__[self.name] = copy(newvals)
198
200 '''This is a Discriptor that returns one value that is the
201 "best" result out of possible multiple matches
202
203 returns a single value or None
204
205 It is the responsibility of the select_fun to return a default
206 like choices[0] if no "Best" is found'''
207
208 - def __init__(self, pred, select_fun = None, cacheName=None, range_type=None ):
212
214 if obj is None:
215 return self
216 if self.name in obj.__dict__:
217 return obj.__dict__[self.name]
218 log.debug("Geting with descriptor %s for %s"%(self.pred,obj.n3()))
219 vals=[o for o in obj.db.objects(obj.resUri, self.pred)]
220 if vals:
221 val = self.select_fun(vals)
222 val = isinstance(val, (BNode,URIRef)) and self.range_class(val) or val.toPython()
223 else:
224 val = None
225 obj.__dict__[self.name]= val
226 return val
227
229 '''This is like rdfBest with a predefined select_fun to select
230 from multiple choices like labels or comments and select the one
231 with the correct locale'''
232 - def __init__(self, pred, lang, cacheName=None):
233 self.lang = lang
234 cacheNameLang = cacheName or ("%s@%s" % (pred, lang))
235 super(rdfBest, self).__init__(pred,cacheName = cacheNameLang)
236
238 for x in choices:
239 if isinstance(x,Literal) and x.language==self.lang:
240 return x
241 return choices[0]
242
244 '''This is a Discriptor
245 Expects to return a list of values (could be a list of one)
246 `__set__` will set the predicate as a RDF List'''
247
248 - def __init__(self, pred, range_type=None):
250
252 if obj is None:
253 return self
254 if self.name in obj.__dict__:
255 return obj.__dict__[self.name]
256
257 log.debug("Geting %s for %s"%(self.pred,obj.n3()))
258 base = obj.db.value(obj.resUri,self.pred)
259 if not base or base==RDF.nil:
260 return []
261 members=[]
262 first = obj.db.value(base, RDF.first)
263
264 if not first:
265 raise AttributeError, ("expected node [%s] to be a list but it's not" %base.n3())
266 while first:
267 members.append(first)
268 base = obj.db.value(base, RDF.rest)
269 first = obj.db.value(base, RDF.first)
270
271 val=[((isinstance(v,BNode) or isinstance(v,URIRef)) and self.range_class(v) or v.toPython()) for v in members]
272 obj.__dict__[self.name] = val
273 return val
274
276 log.debug("SET with descriptor value %s of type %s"%(newvals,type(newvals)))
277 if not isinstance(newvals, (list,tuple)):
278 raise AttributeError("to set a rdfList you must pass in a list (it can be a list of one)")
279 try:
280 oldvals = obj.__dict__[self.name]
281 except KeyError:
282 oldvals = []
283 obj.__dict__[self.name] = oldvals
284 oldhead = obj.db.value(obj.resUri,self.pred)
285
286
287
288
289
290
291
292 if not newvals:
293 newhead = RDF.nil
294 else:
295 newhead = BNode()
296 newtail = newhead
297 oldtail = None
298 for value in newvals:
299 if oldtail:
300 obj.db.add((oldtail, RDF.rest, newtail))
301 obj.db.add((newtail, RDF.first, value2object(value)))
302 oldtail = newtail
303 newtail = BNode()
304 obj.db.add((oldtail, RDF.rest, RDF.nil))
305 obj.db.set((obj.resUri, self.pred, newhead))
306 if oldhead:
307 rdfSubject(oldhead)._remove(db=obj.db)
308 obj.__dict__[self.name] = copy(newvals)
309
313 '''This is a Discriptor
314 Expects to return a list of values (could be a list of one)
315
316 container_type in `__init__` should be one of
317
318 * rdf:Seq
319 * rdf:Bag
320 * rdf:Alt
321
322 `__set__` will set the predicate as a RDF Container type (defaults to rdf:Seq)'''
323
324 - def __init__(self, pred, range_type=None, container_type="http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq"):
325 super(rdfMultiple, self).__init__(pred, range_type)
326 self.container_type = container_type
327
328
330 if obj is None:
331 return self
332 if self.name in obj.__dict__:
333 return obj.__dict__[self.name]
334
335 log.debug("Geting %s for %s"%(self.pred,obj.n3()))
336 base = obj.db.