1
2 """This module contains a system for creating rdf query classes in a mainly
3 declarative manner. These are built by subclassing ``RdfQuery`` and creating
4 class attributes by using ``Selector``:s. There are several selectors provided
5 in this module to cover all regular cases of data acquisition.
6 """
7
8 from itertools import chain
9 from types import ModuleType
10 import warnings
11 from rdflib import RDF, RDFS, Namespace, URIRef, BNode, Literal
12 from rdflib import ConjunctiveGraph
13 try: import simplejson
14 except ImportError: simplejson = None
15 from oort.util.code import contract
16
17
18
19
20 THIS_QUERY = object()
21
22 MODULE_SEP = ':'
23
24
25
26
27
29 __slots__ = ('predicate', '_namespace', 'filters',
30 '_subQueryMarker', '_finalSubQuery',
31 '_name', '_queryClass')
32
33 - def __init__(self, predBase=None, subQuery=None):
43
44 @contract.state_change
55
56
57
58
59
75
76 - def __get__(self, rdfQueryInstance, rdfQueryOwnerClass=None):
87
88 @contract.default_method
96
99 returnList = isinstance(rawresults, list)
100 if not rawresults:
101 if returnList: return []
102 else: return None
103
104 subQuery = self.get_sub_query()
105 if not subQuery:
106 return rawresults
107 else:
108
109
110 run_query = query_or_cached(subQuery, rdfQueryInstance._execCache)
111 if returnList:
112 return [run_query(graph, lang, uri) for uri in rawresults]
113 else:
114 return run_query(graph, lang, rawresults)
115
116 @contract.template_method
117 - def select(self, graph, lang, subject):
118 raise NotImplementedError
119 return None or []
120
121 @contract.default_method
124
128
131
134
138
139 - def __set__(self, rdfQueryInstance, value):
156
157 @contract.default_method
163
164
166 __slots__ = ('selectArgs', 'result', 'hasRun')
167 - def __init__(self, graph, lang, subject):
171
172
199
200
202 __metaclass__ = _rdf_query_meta
203
204 RDF_TYPE = RDFS.Resource
205
206
207
208 - def __init__(self, graph, lang, subject, execCache=None):
209 self._graph = graph
210 self._subject = subject
211 self._lang = lang
212 self._preparedSelects = self._make_prepare_selects()
213 self._execCache = execCache
214
216 prepareds = {}
217 graph, lang, subject = self._graph, self._lang, self._subject
218 for name, selector in self._selectors.items():
219 if not subject:
220
221
222
223
224
225 continue
226 prep = PreparedSelect(graph, lang, subject)
227 prepareds[name] = prep
228 return prepareds
229
231 return str(self._subject)
232
234 if isinstance(other, RdfQuery):
235 return self._subject == other._subject
236 else:
237 return self._subject == other
238
239 @classmethod
241 def bound_query(graph, _lang, _subject):
242 return cls(graph, lang or _lang, subject)
243 bound_query.query = cls
244 bound_query.__name__ = cls.__name__
245 return bound_query
246
247 @classmethod
249 graph = ConjunctiveGraph()
250 query = cls(graph, lang, subject)
251 for k, v in data.items():
252 setattr(query, k, v)
253 return query
254
255 @classmethod
256 - def find_by(cls, graph, lang, execCache=None, **kwargs):
262
263 @property
266
269
271 subject = self._subject or BNode()
272 if not subject: return
273
274 lgraph = newgraph or ConjunctiveGraph()
275 if not newgraph:
276 for key, ns in self._graph.namespaces():
277 lgraph.bind(key, ns)
278
279 for t in self._graph.objects(subject, RDF.type):
280 lgraph.add((subject, RDF.type, t))
281
282 for selector in self._selectors.values():
283 value = selector.__get__(self)
284 if not value:
285 continue
286 selector.back_to_graph(lgraph, subject, value)
287
288
289 for t in lgraph:
290 if None in t: lgraph.remove(t)
291 return lgraph
292
294 return self.to_graph().serialize(format='pretty-xml')
295
296 - def to_dict(self, keepSubject=False):
320
326
327 - def to_json(self, keepSubject=False):
328 if simplejson:
329 return simplejson.dumps(self.to_dict(keepSubject))
330 else:
331 raise NotImplementedError
332
333
334
335
336
337
338
339
340 from weakref import WeakValueDictionary
341
343 """
344 This is a query execution cache which reuses results for the same query,
345 subject and language, avoiding multiple instances of the same query when
346 given the same subject and lang.
347 """
349 self.cache = WeakValueDictionary()
350 - def __call__(self, query, graph, lang, subject):
359
360
362 if execCache:
363 def run_query(graph, lang, uri):
364 return execCache(rdfQuery, graph, lang, uri)
365 return run_query
366 else:
367 return rdfQuery
368
369
374
375
376
377
378
383 return filter(self.func, items)
384
385
387 - def __init__(self, obj=None, reverse=False, ignoreCase=False):
388 if callable(obj):
389 self.attr = None
390 self.func = obj
391 else:
392 self.attr = obj
393 self.func = None
394 self.reverse = reverse
395 self.ignoreCase = ignoreCase
397 copy = items[:]
398 copy.sort(self.sort)
399 if self.reverse:
400 copy.reverse()
401 return copy
402 - def sort(self, r1, r2):
403 attr = self.attr
404 func = self.func
405 if attr:
406 v1, v2 = getattr(r1, attr, r1), getattr(r2, attr, r2)
407 elif func:
408 v1, v2 = func(r1), func(r2)
409 else:
410 v1, v2 = r1, r2
411 if self.ignoreCase:
412 v1, v2 = v1.lower(), v2.lower()
413 return cmp(v1, v2)
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
442
443
447
452
453
454 -class one(UnarySelector):
455 - def select(self, graph, lang, subject):
457
458
459 -class each(EachSelector):
460 - def select(self, graph, lang, subject):
461 return list(graph.objects(subject, self.predicate))
462
463
465 - def select(self, graph, lang, subject):
467
470
471
473 - def select(self, graph, lang, subject):
474 return list(graph.subjects(self.predicate, subject))
475
479
480
482 - def __init__(self, predBase=None, subQuery=None, multiple=False):
485
486 - def select(self, graph, lang, subject):
487 if self.multiple:
488 allItems = [graph.items(res)
489 for res in graph.objects(subject, self.predicate)]
490 return list(chain(*allItems))
491 else:
492 return list(graph.items(
493 graph.value(subject, self.predicate, None, any=True)
494 ))
495
497 if not values:
498 graph.add((subject, self.predicate, RDF.nil))
499 return
500 bnode = BNode()
501 graph.add((subject, self.predicate, bnode))
502 for value in values:
503 back_from_value(graph, bnode, RDF.first, value)
504 newBnode = BNode()
505 graph.add((bnode, RDF.rest, newBnode))
506 bnode = newBnode
507 graph.add((bnode, RDF.rest, RDF.nil))
508
509
515
516 -class localized(TypeLocalized, UnarySelector):
517 - def select(self, graph, lang, subject):
518 first = None
519 for value in graph.objects(subject, self.predicate):
520 if not first: first = value
521 if getattr(value, 'language', None) == lang:
522 return value
523 return first
524
525
526
527
528 try:
529 from oort.util._genshifilters import language_filtered_xml
530 except ImportError, e:
531 warnings.warn("Could not import _genshifilters. Error was: %r. The selector 'localized_xml' will not be available." % e)
532 else:
534 """This selector removes any elements with an xml:lang other than the
535 current language. It also supports the never standardized 'rdf-wrapper' in
536 XML Literals, who are removed from the output.
537
538 Important! This is currently tied to the Genshi Templating System, and may
539 not work as expected in all cases."""
540
541 - def select(self, graph, lang, subject):
544
546 if isinstance(value, basestring):
547 value = Literal(value, datatype=RDF.XMLLiteral)
548 return value
549
551 - def select(self, graph, lang, subject):
552 valueDict = {}
553 for value in graph.objects(subject, self.predicate):
554 valueDict[value.language] = value
555 return valueDict
556
560
561
563 - def select(self, graph, lang, subject):
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
582 "Use as decorator for methods of an RdfQuery subclass to convert them to selectors."
587 return self.func(rdfQueryInstance, *selectArgs)
588 @classmethod
590 def decorator(func):
591 sel = cls(func)
592 for fltr in filters:
593 sel.add_filter(fltr)
594 return sel
595 return decorator
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612 -class QueryContext(object):
613 """
614 A query context, used to provide a managed context for query execution.
615
616 Initalized with:
617
618 - graph
619 - language or getter for language
620 - a set of queries or a modules containing queries
621 Accessible as attributes on the context or via view_for using RDF_TYPE
622
623 """
624
625 - def __init__(self, graph, langobj, queries=None, query_modules=None):
626 self._graph = graph
627 self._execCache = ExecCache()
628
629 if callable(langobj):
630 get_lang = langobj
631 else:
632 def get_lang(): return langobj
633 self._get_lang = get_lang
634
635 self._querydict = querydict = {}
636 if queries:
637 for query in queries:
638 querydict[query.__name__] = query
639 if query_modules:
640 for module in query_modules:
641 for name, obj in module.__dict__.items():
642 if isinstance(obj, type) and issubclass(obj, RdfQuery):
643 querydict[name] = obj
644
645 self._queryTypeMap = {}
646 for query in querydict.values():
647 self._queryTypeMap[query.RDF_TYPE] = query
648
649 - def __getattr__(self, name):
650 try:
651 query = self._querydict[name]
652 return self._prepared_query(query)
653 except KeyError:
654 raise AttributeError("%s has no attribute '%s'" % (self, name))
655
656 - def view_for(self, uriref):
657 for typeref in self._graph.objects(uriref, RDF.type):
658 query = self._queryTypeMap.get(typeref)
659 if query:
660 return self._prepared_query(query)(uriref)
661 raise KeyError("%s has no query for type '%s'" % (self, uriref))
662
663 - def _prepared_query(self, query):
664 return self.PreparedQuery(self, query)
665
666 - class PreparedQuery(object):
667 __slots__ = ('query', 'context')
668
669 - def __init__(self, context, query):
670 self.context = context
671 self.query = query
672
673 - def __call__(self, subject):
674 cx = self.context
675 return cx._execCache(self.query, cx._graph, cx._get_lang(), subject)
676
677 - def find_all(self):
678 cx = self.context
679 for subject in cx._graph.subjects(RDF.type, self.query.RDF_TYPE):
680 yield cx.view_for(subject)
681
682 - def find_by(self, **kwargs):
683 cx = self.context
684 return self.query.find_by(cx._graph, cx._get_lang(),
685 execCache=cx._execCache, **kwargs)
686