def is_ncname(value): __doc__ = format_doctest_out(""" BNode identifiers must be valid NCNames. From the `W3C RDF Syntax doc <http://www.w3.org/TR/REC-rdf-syntax/#section-blank-nodeid-event>`_ "The value is a function of the value of the ``identifier`` accessor. The string value begins with "_:" and the entire value MUST match the `N-Triples nodeID <http://www.w3.org/TR/2004/REC-rdf-testcases-20040210/#nodeID>`_ production". The nodeID production is specified to be a `name <http://www.w3.org/TR/2004/REC-rdf-testcases-20040210/#name>`_ name ::= [A-Za-z][A-Za-z0-9]* >>> assert is_ncname('') == False >>> assert is_ncname('999') == False >>> assert is_ncname('x') == True >>> assert is_ncname(%(u)s'x') == True >>> assert is_ncname(%(u)s'Michèle') == True However, vanilla uuid4s are not necessarily NCNames: >>> assert is_ncname('6fa459ea-ee8a-3ca4-894e-db77e160355e') == False So this has to be finessed with an appropriate prefix ... >>> assert is_ncname("urn:uuid:"+str(uuid4())) == True >>> from rdflib import BNode >>> assert is_ncname(BNode(_sn_gen=bnode_uuid(), _prefix="urn:uuid:")) == True """) ncnameexp = re.compile('[A-Za-z][A-Za-z0-9]*') if ncnameexp.match(value): return True else: return False
class URIPattern(unicode): __doc__ = format_doctest_out(""" Utility class for creating URIs according to some pattern This supports either new style formatting with .format or old-style with %% operator >>> u=URIPattern("http://example.org/%%s/%%d/resource") >>> u%%('books', 12345) rdflib.term.URIRef(%(u)s'http://example.org/books/12345/resource') """) def __new__(cls, value): try: rt = unicode.__new__(cls, value) except UnicodeDecodeError: rt = unicode.__new__(cls, value, 'utf-8') return rt def __mod__(self, *args, **kwargs): return URIRef(unicode(self).__mod__(*args, **kwargs)) def format(self, *args, **kwargs): return URIRef(unicode.format(self, *args, **kwargs)) def __repr__(self): return "URIPattern(%r)" % unicode.__repr__(self)
class Namespace(unicode): __doc__ = format_doctest_out(""" Utility class for quickly generating URIRefs with a common prefix >>> from rdflib import Namespace >>> n = Namespace("http://example.org/") >>> n.Person # as attribute rdflib.term.URIRef(%(u)s'http://example.org/Person') >>> n['first-name'] # as item - for things that are not valid python identifiers rdflib.term.URIRef(%(u)s'http://example.org/first-name') """) def __new__(cls, value): try: rt = unicode.__new__(cls, value) except UnicodeDecodeError: rt = unicode.__new__(cls, value, 'utf-8') return rt @property def title(self): # overrides unicode.title to allow DCTERMS.title for example return URIRef(self + 'title') def term(self, name): # need to handle slices explicitly because of __getitem__ override return URIRef(self + (name if isinstance(name, basestring) else '')) def __getitem__(self, key, default=None): return self.term(key) def __getattr__(self, name): if name.startswith("__"): # ignore any special Python names! raise AttributeError else: return self.term(name) def __repr__(self): return "Namespace(%s)" % unicode.__repr__(self)
__doc__ = py3compat.format_doctest_out(""" The :class:`~rdflib.resource.Resource` class wraps a :class:`~rdflib.graph.Graph` and a resource reference (i.e. a :class:`rdflib.term.URIRef` or :class:`rdflib.term.BNode`) to support a resource-oriented way of working with a graph. It contains methods directly corresponding to those methods of the Graph interface that relate to reading and writing data. The difference is that a Resource also binds a resource identifier, making it possible to work without tracking both the graph and a current subject. This makes for a "resource oriented" style, as compared to the triple orientation of the Graph API. Resulting generators are also wrapped so that any resource reference values (:class:`rdflib.term.URIRef`s and :class:`rdflib.term.BNode`s) are in turn wrapped as Resources. (Note that this behaviour differs from the corresponding methods in :class:`~rdflib.graph.Graph`, where no such conversion takes place.) Basic Usage Scenario -------------------- Start by importing things we need and define some namespaces:: >>> from rdflib import * >>> FOAF = Namespace("http://xmlns.com/foaf/0.1/") >>> CV = Namespace("http://purl.org/captsolo/resume-rdf/0.2/cv#") Load some RDF data:: >>> graph = Graph().parse(format='n3', data=''' ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . ... @prefix xsd: <http://www.w3.org/2001/XMLSchema#>. ... @prefix foaf: <http://xmlns.com/foaf/0.1/> . ... @prefix cv: <http://purl.org/captsolo/resume-rdf/0.2/cv#> . ... ... @base <http://example.org/> . ... ... </person/some1#self> a foaf:Person; ... rdfs:comment "Just a Python & RDF hacker."@en; ... foaf:depiction </images/person/some1.jpg>; ... foaf:homepage <http://example.net/>; ... foaf:name "Some Body" . ... ... </images/person/some1.jpg> a foaf:Image; ... rdfs:label "some 1"@en; ... rdfs:comment "Just an image"@en; ... foaf:thumbnail </images/person/some1-thumb.jpg> . ... ... </images/person/some1-thumb.jpg> a foaf:Image . ... ... [] a cv:CV; ... cv:aboutPerson </person/some1#self>; ... cv:hasWorkHistory [ cv:employedIn </#company>; ... cv:startDate "2009-09-04"^^xsd:date ] . ... ''') Create a Resource:: >>> person = Resource( ... graph, URIRef("http://example.org/person/some1#self")) Retrieve some basic facts:: >>> person.identifier rdflib.term.URIRef(%(u)s'http://example.org/person/some1#self') >>> person.value(FOAF.name) rdflib.term.Literal(%(u)s'Some Body') >>> person.value(RDFS.comment) rdflib.term.Literal(%(u)s'Just a Python & RDF hacker.', lang=%(u)s'en') Resources as unicode are represented by their identifiers as unicode:: >>> %(unicode)s(person) #doctest: +SKIP %(u)s'Resource(http://example.org/person/some1#self' Resource references are also Resources, so you can easily get e.g. a qname for the type of a resource, like:: >>> person.value(RDF.type).qname() %(u)s'foaf:Person' Or for the predicates of a resource:: >>> sorted( ... p.qname() for p in person.predicates() ... ) #doctest: +NORMALIZE_WHITESPACE +SKIP [%(u)s'foaf:depiction', %(u)s'foaf:homepage', %(u)s'foaf:name', %(u)s'rdf:type', %(u)s'rdfs:comment'] Follow relations and get more data from their Resources as well:: >>> for pic in person.objects(FOAF.depiction): ... print((pic.identifier)) ... print((pic.value(RDF.type).qname())) ... print((pic.label())) ... print((pic.comment())) ... print((pic.value(FOAF.thumbnail).identifier)) http://example.org/images/person/some1.jpg foaf:Image some 1 Just an image http://example.org/images/person/some1-thumb.jpg >>> for cv in person.subjects(CV.aboutPerson): ... work = list(cv.objects(CV.hasWorkHistory))[0] ... print((work.value(CV.employedIn).identifier)) ... print((work.value(CV.startDate))) http://example.org/#company 2009-09-04 It's just as easy to work with the predicates of a resource:: >>> for s, p in person.subject_predicates(): ... print((s.value(RDF.type).qname())) ... print((p.qname())) ... for s, o in p.subject_objects(): ... print((s.value(RDF.type).qname())) ... print((o.value(RDF.type).qname())) cv:CV cv:aboutPerson cv:CV foaf:Person This is useful for e.g. inspection:: >>> thumb_ref = URIRef("http://example.org/images/person/some1-thumb.jpg") >>> thumb = Resource(graph, thumb_ref) >>> for p, o in thumb.predicate_objects(): ... print((p.qname())) ... print((o.qname())) rdf:type foaf:Image Similarly, adding, setting and removing data is easy:: >>> thumb.add(RDFS.label, Literal("thumb")) >>> print((thumb.label())) thumb >>> thumb.set(RDFS.label, Literal("thumbnail")) >>> print((thumb.label())) thumbnail >>> thumb.remove(RDFS.label) >>> list(thumb.objects(RDFS.label)) [] Schema Example -------------- With this artificial schema data:: >>> graph = Graph().parse(format='n3', data=''' ... @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . ... @prefix owl: <http://www.w3.org/2002/07/owl#> . ... @prefix v: <http://example.org/def/v#> . ... ... v:Artifact a owl:Class . ... ... v:Document a owl:Class; ... rdfs:subClassOf v:Artifact . ... ... v:Paper a owl:Class; ... rdfs:subClassOf v:Document . ... ... v:Choice owl:oneOf (v:One v:Other) . ... ... v:Stuff a rdf:Seq; rdf:_1 v:One; rdf:_2 v:Other . ... ... ''') From this class:: >>> artifact = Resource(graph, URIRef("http://example.org/def/v#Artifact")) we can get at subclasses:: >>> subclasses = list(artifact.transitive_subjects(RDFS.subClassOf)) >>> [c.qname() for c in subclasses] [%(u)s'v:Artifact', %(u)s'v:Document', %(u)s'v:Paper'] and superclasses from the last subclass:: >>> [c.qname() for c in subclasses[-1].transitive_objects(RDFS.subClassOf)] [%(u)s'v:Paper', %(u)s'v:Document', %(u)s'v:Artifact'] Get items from the Choice:: >>> choice = Resource(graph, URIRef("http://example.org/def/v#Choice")) >>> [it.qname() for it in list(choice.value(OWL.oneOf).items())] [%(u)s'v:One', %(u)s'v:Other'] And the sequence of Stuff:: >>> stuff = Resource(graph, URIRef("http://example.org/def/v#Stuff")) >>> [it.qname() for it in stuff.seq()] [%(u)s'v:One', %(u)s'v:Other'] On add, other resources are auto-unboxed: >>> paper = Resource(graph, URIRef("http://example.org/def/v#Paper")) >>> paper.add(RDFS.subClassOf, artifact) >>> artifact in paper.objects(RDFS.subClassOf) # checks Resource instance True >>> (paper._identifier, RDFS.subClassOf, artifact._identifier) in graph True Technical Details ----------------- Comparison is based on graph and identifier:: >>> g1 = Graph() >>> t1 = Resource(g1, URIRef("http://example.org/thing")) >>> t2 = Resource(g1, URIRef("http://example.org/thing")) >>> t3 = Resource(g1, URIRef("http://example.org/other")) >>> t4 = Resource(Graph(), URIRef("http://example.org/other")) >>> t1 is t2 False >>> t1 == t2 True >>> t1 != t2 False >>> t1 == t3 False >>> t1 != t3 True >>> t3 != t4 True >>> t3 < t1 and t1 > t3 True >>> t1 >= t1 and t1 >= t3 True >>> t1 <= t1 and t3 <= t1 True >>> t1 < t1 or t1 < t3 or t3 > t1 or t3 > t3 False Hash is computed from graph and identifier:: >>> g1 = Graph() >>> t1 = Resource(g1, URIRef("http://example.org/thing")) >>> hash(t1) == hash(Resource(g1, URIRef("http://example.org/thing"))) True >>> hash(t1) == hash(Resource(Graph(), t1.identifier)) False >>> hash(t1) == hash(Resource(Graph(), URIRef("http://example.org/thing"))) False The Resource class is suitable as a base class for mapper toolkits. For example, consider this utility for accessing RDF properties via qname-like attributes:: >>> class Item(Resource): ... ... def __getattr__(self, p): ... return list(self.objects(self._to_ref(*p.split('_', 1)))) ... ... def _to_ref(self, pfx, name): ... return URIRef(self._graph.store.namespace(pfx) + name) It works as follows:: >>> graph = Graph().parse(format='n3', data=''' ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . ... @prefix foaf: <http://xmlns.com/foaf/0.1/> . ... ... @base <http://example.org/> . ... </person/some1#self> ... foaf:name "Some Body"; ... foaf:depiction </images/person/some1.jpg> . ... </images/person/some1.jpg> rdfs:comment "Just an image"@en . ... ''') >>> person = Item(graph, URIRef("http://example.org/person/some1#self")) >>> print((person.foaf_name[0])) Some Body The mechanism for wrapping references as resources cooperates with subclasses. Therefore, accessing referenced resources automatically creates new ``Item`` objects:: >>> isinstance(person.foaf_depiction[0], Item) True >>> print((person.foaf_depiction[0].rdfs_comment[0])) Just an image """)
__doc__ = py3compat.format_doctest_out(""" The ``Resource`` class wraps a ``Graph`` and a resource reference (i.e. an ``URIRef`` or ``BNode``), to support a resource oriented way of working with a graph. It contains methods directly corresponding to those methods of the Graph interface that relate to reading and writing data. The difference is that a Resource also binds a resource identifier, making it possible to work without tracking both the graph and a current subject. This makes for a "resource oriented" style, as compared to the triple orientation of the Graph API. Resulting generators are also wrapped so that any resource reference values (``URIRef``s and ``BNode``s) are in turn wrapped as Resources. (Note that this behaviour differs from the corresponding methods in Graph, were no such conversion takes place.) Basic Usage Scenario -------------------- Start by importing things we need and define some namespaces:: >>> from rdflib import * >>> FOAF = Namespace("http://xmlns.com/foaf/0.1/") >>> CV = Namespace("http://purl.org/captsolo/resume-rdf/0.2/cv#") Load some RDF data:: >>> graph = Graph().parse(format='n3', data=''' ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . ... @prefix xsd: <http://www.w3.org/2001/XMLSchema#>. ... @prefix foaf: <http://xmlns.com/foaf/0.1/> . ... @prefix cv: <http://purl.org/captsolo/resume-rdf/0.2/cv#> . ... ... @base <http://example.org/> . ... ... </person/some1#self> a foaf:Person; ... rdfs:comment "Just a Python & RDF hacker."@en; ... foaf:depiction </images/person/some1.jpg>; ... foaf:homepage <http://example.net/>; ... foaf:name "Some Body" . ... ... </images/person/some1.jpg> a foaf:Image; ... rdfs:label "some 1"@en; ... rdfs:comment "Just an image"@en; ... foaf:thumbnail </images/person/some1-thumb.jpg> . ... ... </images/person/some1-thumb.jpg> a foaf:Image . ... ... [] a cv:CV; ... cv:aboutPerson </person/some1#self>; ... cv:hasWorkHistory [ cv:employedIn </#company>; ... cv:startDate "2009-09-04"^^xsd:date ] . ... ''') Create a Resource:: >>> person = Resource(graph, URIRef("http://example.org/person/some1#self")) Retrieve some basic facts:: >>> person.identifier rdflib.term.URIRef('http://example.org/person/some1#self') >>> person.value(FOAF.name) rdflib.term.Literal(%(u)s'Some Body') >>> person.value(RDFS.comment) rdflib.term.Literal(%(u)s'Just a Python & RDF hacker.', lang=%(u)s'en') Resources as unicode are represented by their identifiers as unicode:: >>> unicode(person) %(u)s'http://example.org/person/some1#self' Resource references are also Resources, so you can easily get e.g. a qname for the type of a resource, like:: >>> person.value(RDF.type).qname() %(u)s'foaf:Person' Or for the predicates of a resource:: >>> sorted(p.qname() for p in person.predicates()) [%(u)s'foaf:depiction', %(u)s'foaf:homepage', %(u)s'foaf:name', %(u)s'rdf:type', %(u)s'rdfs:comment'] Follow relations and get more data from their Resources as well:: >>> for pic in person.objects(FOAF.depiction): ... print(pic.identifier) ... print(pic.value(RDF.type).qname()) ... print(pic.label()) ... print(pic.comment()) ... print(pic.value(FOAF.thumbnail).identifier) http://example.org/images/person/some1.jpg foaf:Image some 1 Just an image http://example.org/images/person/some1-thumb.jpg >>> for cv in person.subjects(CV.aboutPerson): ... work = list(cv.objects(CV.hasWorkHistory))[0] ... print(work.value(CV.employedIn).identifier) ... print(work.value(CV.startDate)) http://example.org/#company 2009-09-04 It's just as easy to work with the predicates of a resource:: >>> for s, p in person.subject_predicates(): ... print(s.value(RDF.type).qname()) ... print(p.qname()) ... for s, o in p.subject_objects(): ... print(s.value(RDF.type).qname()) ... print(o.value(RDF.type).qname()) cv:CV cv:aboutPerson cv:CV foaf:Person This is useful for e.g. inspection:: >>> thumb_ref = URIRef("http://example.org/images/person/some1-thumb.jpg") >>> thumb = Resource(graph, thumb_ref) >>> for p, o in thumb.predicate_objects(): ... print(p.qname()) ... print(o.qname()) rdf:type foaf:Image Similarly, adding, setting and removing data is easy:: >>> thumb.add(RDFS.label, Literal("thumb")) >>> print(thumb.label()) thumb >>> thumb.set(RDFS.label, Literal("thumbnail")) >>> print(thumb.label()) thumbnail >>> thumb.remove(RDFS.label) >>> list(thumb.objects(RDFS.label)) [] Schema Example -------------- With this artificial schema data:: >>> graph = Graph().parse(format='n3', data=''' ... @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . ... @prefix owl: <http://www.w3.org/2002/07/owl#> . ... @prefix v: <http://example.org/def/v#> . ... ... v:Artifact a owl:Class . ... ... v:Document a owl:Class; ... rdfs:subClassOf v:Artifact . ... ... v:Paper a owl:Class; ... rdfs:subClassOf v:Document . ... ... v:Choice owl:oneOf (v:One v:Other) . ... ... v:Stuff a rdf:Seq; rdf:_1 v:One; rdf:_2 v:Other . ... ... ''') From this class:: >>> artifact = Resource(graph, URIRef("http://example.org/def/v#Artifact")) we can get at subclasses:: >>> subclasses = list(artifact.transitive_subjects(RDFS.subClassOf)) >>> [c.qname() for c in subclasses] [%(u)s'v:Artifact', %(u)s'v:Document', %(u)s'v:Paper'] and superclasses from the last subclass:: >>> [c.qname() for c in subclasses[-1].transitive_objects(RDFS.subClassOf)] [%(u)s'v:Paper', %(u)s'v:Document', %(u)s'v:Artifact'] Get items from the Choice:: >>> choice = Resource(graph, URIRef("http://example.org/def/v#Choice")) >>> [it.qname() for it in choice.value(OWL.oneOf).items()] [%(u)s'v:One', %(u)s'v:Other'] And the sequence of Stuff:: >>> stuff = Resource(graph, URIRef("http://example.org/def/v#Stuff")) >>> [it.qname() for it in stuff.seq()] [%(u)s'v:One', %(u)s'v:Other'] Technical Details ----------------- Comparison is based on graph and identifier:: >>> g1 = Graph() >>> t1 = Resource(g1, URIRef("http://example.org/thing")) >>> t2 = Resource(g1, URIRef("http://example.org/thing")) >>> t3 = Resource(g1, URIRef("http://example.org/other")) >>> t4 = Resource(Graph(), URIRef("http://example.org/other")) >>> t1 is t2 False >>> t1 == t2 True >>> t1 != t2 False >>> t1 == t3 False >>> t1 != t3 True >>> t3 != t4 True >>> t3 < t1 and t1 > t3 True >>> t1 >= t1 and t1 >= t3 True >>> t1 <= t1 and t3 <= t1 True >>> t1 < t1 or t1 < t3 or t3 > t1 or t3 > t3 False Hash is computed from graph and identifier:: >>> g1 = Graph() >>> t1 = Resource(g1, URIRef("http://example.org/thing")) >>> hash(t1) == hash(Resource(g1, URIRef("http://example.org/thing"))) True >>> hash(t1) == hash(Resource(Graph(), t1.identifier)) False >>> hash(t1) == hash(Resource(Graph(), URIRef("http://example.org/thing"))) False The Resource class is suitable as a base class for mapper toolkits. For example, consider this utility for accessing RDF properties via qname-like attributes:: >>> class Item(Resource): ... ... def __getattr__(self, p): ... return list(self.objects(self._to_ref(*p.split('_', 1)))) ... ... def _to_ref(self, pfx, name): ... return URIRef(self._graph.store.namespace(pfx) + name) It works as follows:: >>> graph = Graph().parse(format='n3', data=''' ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . ... @prefix foaf: <http://xmlns.com/foaf/0.1/> . ... ... @base <http://example.org/> . ... </person/some1#self> ... foaf:name "Some Body"; ... foaf:depiction </images/person/some1.jpg> . ... </images/person/some1.jpg> rdfs:comment "Just an image"@en . ... ''') >>> person = Item(graph, URIRef("http://example.org/person/some1#self")) >>> print(person.foaf_name[0]) Some Body The mechanism for wrapping references as resources cooperates with subclasses. Therefore, accessing referenced resources automatically creates new ``Item`` objects:: >>> isinstance(person.foaf_depiction[0], Item) True >>> print(person.foaf_depiction[0].rdfs_comment[0]) Just an image """)
__doc__ = format_doctest_out(""" This module implements the SPARQL 1.1 Property path operators, as defined in: http://www.w3.org/TR/sparql11-query/#propertypaths In SPARQL the syntax is as follows: +--------------------+-------------------------------------------------+ |Syntax | Matches | +====================+=================================================+ |iri | An IRI. A path of length one. | +--------------------+-------------------------------------------------+ |^elt | Inverse path (object to subject). | +--------------------+-------------------------------------------------+ |elt1 / elt2 | A sequence path of elt1 followed by elt2. | +--------------------+-------------------------------------------------+ |elt1 | elt2 | A alternative path of elt1 or elt2 | | | (all possibilities are tried). | +--------------------+-------------------------------------------------+ |elt* | A path that connects the subject and object | | | of the path by zero or more matches of elt. | +--------------------+-------------------------------------------------+ |elt+ | A path that connects the subject and object | | | of the path by one or more matches of elt. | +--------------------+-------------------------------------------------+ |elt? | A path that connects the subject and object | | | of the path by zero or one matches of elt. | +--------------------+-------------------------------------------------+ |!iri or | Negated property set. An IRI which is not one of| |!(iri\ :sub:`1`\ | | iri\ :sub:`1`...iri\ :sub:`n`. | |... |iri\ :sub:`n`) | !iri is short for !(iri). | +--------------------+-------------------------------------------------+ |!^iri or | Negated property set where the excluded matches | |!(^iri\ :sub:`1`\ | | are based on reversed path. That is, not one of | |... |^iri\ :sub:`n`)| iri\ :sub:`1`...iri\ :sub:`n` as reverse paths. | | | !^iri is short for !(^iri). | +--------------------+-------------------------------------------------+ |!(iri\ :sub:`1`\ | | A combination of forward and reverse | |...|iri\ :sub:`j`\ || properties in a negated property set. | |^iri\ :sub:`j+1`\ | | | |... |^iri\ :sub:`n`)| | +--------------------+-------------------------------------------------+ |(elt) | A group path elt, brackets control precedence. | +--------------------+-------------------------------------------------+ This module is used internally be the SPARQL engine, but they property paths can also be used to query RDFLib Graphs directly. Where possible the SPARQL syntax is mapped to python operators, and property path objects can be constructed from existing URIRefs. >>> from rdflib import Graph, Namespace >>> foaf=Namespace('http://xmlns.com/foaf/0.1/') >>> ~foaf.knows Path(~http://xmlns.com/foaf/0.1/knows) >>> foaf.knows/foaf.name Path(http://xmlns.com/foaf/0.1/knows / http://xmlns.com/foaf/0.1/name) >>> foaf.name|foaf.firstName Path(http://xmlns.com/foaf/0.1/name | http://xmlns.com/foaf/0.1/firstName) Modifiers (?, *, +) are done using * (the multiplication operator) and the strings '*', '?', '+', also defined as constants in this file. >>> foaf.knows*OneOrMore Path(http://xmlns.com/foaf/0.1/knows+) The path objects can also be used with the normal graph methods. First some example data: >>> g=Graph() >>> g=g.parse(data=''' ... @prefix : <ex:> . ... ... :a :p1 :c ; :p2 :f . ... :c :p2 :e ; :p3 :g . ... :g :p3 :h ; :p2 :j . ... :h :p3 :a ; :p2 :g . ... ... :q :px :q . ... ... ''', format='n3') # doctest: +ELLIPSIS >>> e=Namespace('ex:') Graph contains: >>> (e.a, e.p1/e.p2, e.e) in g True Graph generator functions, triples, subjects, objects, etc. : >>> list(g.objects(e.c, (e.p3*OneOrMore)/e.p2)) # doctest: +NORMALIZE_WHITESPACE [rdflib.term.URIRef(%(u)s'ex:j'), rdflib.term.URIRef(%(u)s'ex:g'), rdflib.term.URIRef(%(u)s'ex:f')] A more complete set of tests: >>> list(evalPath(g, (None, e.p1/e.p2, None)))==[(e.a, e.e)] True >>> list(evalPath(g, (e.a, e.p1|e.p2, None)))==[(e.a,e.c), (e.a,e.f)] True >>> list(evalPath(g, (e.c, ~e.p1, None))) == [ (e.c, e.a) ] True >>> list(evalPath(g, (e.a, e.p1*ZeroOrOne, None))) == [(e.a, e.a), (e.a, e.c)] True >>> list(evalPath(g, (e.c, e.p3*OneOrMore, None))) == [ ... (e.c, e.g), (e.c, e.h), (e.c, e.a)] True >>> list(evalPath(g, (e.c, e.p3*ZeroOrMore, None))) == [(e.c, e.c), ... (e.c, e.g), (e.c, e.h), (e.c, e.a)] True >>> list(evalPath(g, (e.a, -e.p1, None))) == [(e.a, e.f)] True >>> list(evalPath(g, (e.a, -(e.p1|e.p2), None))) == [] True >>> list(evalPath(g, (e.g, -~e.p2, None))) == [(e.g, e.j)] True >>> list(evalPath(g, (e.e, ~(e.p1/e.p2), None))) == [(e.e, e.a)] True >>> list(evalPath(g, (e.a, e.p1/e.p3/e.p3, None))) == [(e.a, e.h)] True >>> list(evalPath(g, (e.q, e.px*OneOrMore, None))) [(rdflib.term.URIRef(%(u)s'ex:q'), rdflib.term.URIRef(%(u)s'ex:q'))] >>> list(evalPath(g, (None, e.p1|e.p2, e.c))) [(rdflib.term.URIRef(%(u)s'ex:a'), rdflib.term.URIRef(%(u)s'ex:c'))] >>> list(evalPath(g, (None, ~e.p1, e.a))) == [ (e.c, e.a) ] True >>> list(evalPath(g, (None, e.p1*ZeroOrOne, e.c))) # doctest: +NORMALIZE_WHITESPACE [(rdflib.term.URIRef(%(u)s'ex:c'), rdflib.term.URIRef(%(u)s'ex:c')), (rdflib.term.URIRef(%(u)s'ex:a'), rdflib.term.URIRef(%(u)s'ex:c'))] >>> list(evalPath(g, (None, e.p3*OneOrMore, e.a))) # doctest: +NORMALIZE_WHITESPACE [(rdflib.term.URIRef(%(u)s'ex:h'), rdflib.term.URIRef(%(u)s'ex:a')), (rdflib.term.URIRef(%(u)s'ex:g'), rdflib.term.URIRef(%(u)s'ex:a')), (rdflib.term.URIRef(%(u)s'ex:c'), rdflib.term.URIRef(%(u)s'ex:a'))] >>> list(evalPath(g, (None, e.p3*ZeroOrMore, e.a))) # doctest: +NORMALIZE_WHITESPACE [(rdflib.term.URIRef(%(u)s'ex:a'), rdflib.term.URIRef(%(u)s'ex:a')), (rdflib.term.URIRef(%(u)s'ex:h'), rdflib.term.URIRef(%(u)s'ex:a')), (rdflib.term.URIRef(%(u)s'ex:g'), rdflib.term.URIRef(%(u)s'ex:a')), (rdflib.term.URIRef(%(u)s'ex:c'), rdflib.term.URIRef(%(u)s'ex:a'))] >>> list(evalPath(g, (None, -e.p1, e.f))) == [(e.a, e.f)] True >>> list(evalPath(g, (None, -(e.p1|e.p2), e.c))) == [] True >>> list(evalPath(g, (None, -~e.p2, e.j))) == [(e.g, e.j)] True >>> list(evalPath(g, (None, ~(e.p1/e.p2), e.a))) == [(e.e, e.a)] True >>> list(evalPath(g, (None, e.p1/e.p3/e.p3, e.h))) == [(e.a, e.h)] True >>> list(evalPath(g, (e.q, e.px*OneOrMore, None))) [(rdflib.term.URIRef(%(u)s'ex:q'), rdflib.term.URIRef(%(u)s'ex:q'))] >>> list(evalPath(g, (e.c, (e.p2|e.p3)*ZeroOrMore, e.j))) [(rdflib.term.URIRef(%(u)s'ex:c'), rdflib.term.URIRef(%(u)s'ex:j'))] No vars specified: >>> sorted(list(evalPath(g, (None, e.p3*OneOrMore, None)))) #doctest: +NORMALIZE_WHITESPACE [(rdflib.term.URIRef(%(u)s'ex:c'), rdflib.term.URIRef(%(u)s'ex:a')), (rdflib.term.URIRef(%(u)s'ex:c'), rdflib.term.URIRef(%(u)s'ex:g')), (rdflib.term.URIRef(%(u)s'ex:c'), rdflib.term.URIRef(%(u)s'ex:h')), (rdflib.term.URIRef(%(u)s'ex:g'), rdflib.term.URIRef(%(u)s'ex:a')), (rdflib.term.URIRef(%(u)s'ex:g'), rdflib.term.URIRef(%(u)s'ex:h')), (rdflib.term.URIRef(%(u)s'ex:h'), rdflib.term.URIRef(%(u)s'ex:a'))] .. versionadded:: 4.0 """)
__doc__ = format_doctest_out(""" =================== Namespace Utilities =================== RDFLib provides mechanisms for managing Namespaces. In particular, there is a :class:`~rdflib.namespace.Namespace` class that takes as its argument the base URI of the namespace. .. code-block:: pycon >>> from rdflib.namespace import Namespace >>> fuxi = Namespace('http://metacognition.info/ontologies/FuXi.n3#') Fully qualified URIs in the namespace can be constructed either by attribute or by dictionary access on Namespace instances: .. code-block:: pycon >>> fuxi.ruleBase rdflib.term.URIRef(u'http://metacognition.info/ontologies/FuXi.n3#ruleBase') >>> fuxi['ruleBase'] rdflib.term.URIRef(u'http://metacognition.info/ontologies/FuXi.n3#ruleBase') Automatic handling of unknown predicates ----------------------------------------- As a programming convenience, a namespace binding is automatically created when :class:`rdflib.term.URIRef` predicates are added to the graph: .. code-block:: pycon >>> from rdflib import Graph, URIRef >>> g = Graph() >>> g.add((URIRef("http://example0.com/foo"), ... URIRef("http://example1.com/bar"), ... URIRef("http://example2.com/baz"))) >>> print(g.serialize(format="n3")) @prefix ns1: <http://example1.com/> . <BLANKLINE> <http://example0.com/foo> ns1:bar <http://example2.com/baz> . <BLANKLINE> <BLANKLINE> >>> Importable namespaces ----------------------- The following namespaces are available by directly importing from rdflib: * RDF * RDFS * OWL * XSD .. code-block:: pycon >>> from rdflib import OWL >>> OWL.seeAlso rdflib.term.URIRef(u'http://www.w3.org/2002/07/owl#seeAlso') """)
__doc__ = format_doctest_out(""" =================== Namespace Utilities =================== RDFLib provides mechanisms for managing Namespaces. In particular, there is a :class:`~rdflib.namespace.Namespace` class that takes as its argument the base URI of the namespace. .. code-block:: pycon >>> from rdflib.namespace import Namespace >>> owl = Namespace('http://www.w3.org/2002/07/owl#') Fully qualified URIs in the namespace can be constructed either by attribute or by dictionary access on Namespace instances: .. code-block:: pycon >>> owl.seeAlso rdflib.term.URIRef(%(u)s'http://www.w3.org/2002/07/owl#seeAlso') >>> owl['seeAlso'] rdflib.term.URIRef(%(u)s'http://www.w3.org/2002/07/owl#seeAlso') Automatic handling of unknown predicates ----------------------------------------- As a programming convenience, a namespace binding is automatically created when :class:`rdflib.term.URIRef` predicates are added to the graph. Importable namespaces ----------------------- The following namespaces are available by directly importing from rdflib: * RDF * RDFS * OWL * XSD * FOAF * SKOS * DOAP * DC * DCTERMS * VOID .. code-block:: pycon >>> from rdflib import OWL >>> OWL.seeAlso rdflib.term.URIRef(%(u)s'http://www.w3.org/2002/07/owl#seeAlso') """)
__doc__ = py3compat.format_doctest_out(""" A Describer is a stateful utility for creating RDF statements in a semi-declarative manner. It has methods for creating literal values, rel and rev resource relations (somewhat resembling RDFa). The `rel` and ``rev`` methods return a context manager which sets the current about to the referenced resource for the context scope (for use with the ``with`` statement). Full example in the ``to_rdf`` method below:: >>> import datetime >>> from rdflib.graph import Graph >>> from rdflib.namespace import Namespace, RDFS >>> >>> ORG_URI = "http://example.org/" >>> >>> FOAF = Namespace("http://xmlns.com/foaf/0.1/") >>> CV = Namespace("http://purl.org/captsolo/resume-rdf/0.2/cv#") >>> >>> class Person(object): ... def __init__(self): ... self.first_name = %(u)s"Some" ... self.last_name = %(u)s"Body" ... self.username = "******" ... self.presentation = %(u)s"Just a Python & RDF hacker." ... self.image = "/images/persons/" + self.username + ".jpg" ... self.site = "http://example.net/" ... self.start_date = datetime.date(2009, 9, 4) ... def get_full_name(self): ... return %(u)s" ".join([self.first_name, self.last_name]) ... def get_absolute_url(self): ... return "/persons/" + self.username ... def get_thumbnail_url(self): ... return self.image.replace('.jpg', '-thumb.jpg') ... ... def to_rdf(self): ... graph = Graph() ... graph.bind('foaf', FOAF) ... graph.bind('cv', CV) ... lang = 'en' ... d = Describer(graph, base=ORG_URI) ... d.about(self.get_absolute_url()+'#person') ... d.rdftype(FOAF.Person) ... d.value(FOAF.name, self.get_full_name()) ... d.value(FOAF.firstName, self.first_name) ... d.value(FOAF.surname, self.last_name) ... d.rel(FOAF.homepage, self.site) ... d.value(RDFS.comment, self.presentation, lang=lang) ... with d.rel(FOAF.depiction, self.image): ... d.rdftype(FOAF.Image) ... d.rel(FOAF.thumbnail, self.get_thumbnail_url()) ... with d.rev(CV.aboutPerson): ... d.rdftype(CV.CV) ... with d.rel(CV.hasWorkHistory): ... d.value(CV.startDate, self.start_date) ... d.rel(CV.employedIn, ORG_URI+"#company") ... return graph ... >>> person_graph = Person().to_rdf() >>> expected = Graph().parse(data='''<?xml version="1.0" encoding="utf-8"?> ... <rdf:RDF ... xmlns:foaf="http://xmlns.com/foaf/0.1/" ... xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ... xmlns:cv="http://purl.org/captsolo/resume-rdf/0.2/cv#" ... xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"> ... <foaf:Person rdf:about="http://example.org/persons/some1#person"> ... <foaf:name>Some Body</foaf:name> ... <foaf:firstName>Some</foaf:firstName> ... <foaf:surname>Body</foaf:surname> ... <foaf:depiction> ... <foaf:Image ... rdf:about= ... "http://example.org/images/persons/some1.jpg"> ... <foaf:thumbnail ... rdf:resource= ... "http://example.org/images/persons/some1-thumb.jpg"/> ... </foaf:Image> ... </foaf:depiction> ... <rdfs:comment xml:lang="en"> ... Just a Python & RDF hacker. ... </rdfs:comment> ... <foaf:homepage rdf:resource="http://example.net/"/> ... </foaf:Person> ... <cv:CV> ... <cv:aboutPerson ... rdf:resource="http://example.org/persons/some1#person"> ... </cv:aboutPerson> ... <cv:hasWorkHistory> ... <rdf:Description> ... <cv:startDate ... rdf:datatype="http://www.w3.org/2001/XMLSchema#date" ... >2009-09-04</cv:startDate> ... <cv:employedIn rdf:resource="http://example.org/#company"/> ... </rdf:Description> ... </cv:hasWorkHistory> ... </cv:CV> ... </rdf:RDF> ... ''') >>> >>> from rdflib.compare import isomorphic >>> isomorphic(person_graph, expected) #doctest: +SKIP True """)
class Collection(object): __doc__ = format_doctest_out(""" See 3.3.5 Emulating container types: http://docs.python.org/ref/sequence-types.html#l2h-232 >>> from rdflib.graph import Graph >>> listName = BNode() >>> g = Graph('IOMemory') >>> listItem1 = BNode() >>> listItem2 = BNode() >>> g.add((listName,RDF.first,Literal(1))) >>> g.add((listName,RDF.rest,listItem1)) >>> g.add((listItem1,RDF.first,Literal(2))) >>> g.add((listItem1,RDF.rest,listItem2)) >>> g.add((listItem2,RDF.rest,RDF.nil)) >>> g.add((listItem2,RDF.first,Literal(3))) >>> c=Collection(g,listName) >>> print(list(c)) [rdflib.term.Literal(%(u)s'1', datatype=rdflib.term.URIRef(%(u)s'http://www.w3.org/2001/XMLSchema#integer')), rdflib.term.Literal(%(u)s'2', datatype=rdflib.term.URIRef(%(u)s'http://www.w3.org/2001/XMLSchema#integer')), rdflib.term.Literal(%(u)s'3', datatype=rdflib.term.URIRef(%(u)s'http://www.w3.org/2001/XMLSchema#integer'))] >>> 1 in c True >>> len(c) 3 >>> c._get_container(1) == listItem1 True >>> c.index(Literal(2)) == 1 True """) def __init__(self, graph, uri, seq=[]): self.graph = graph self.uri = uri or BNode() for item in seq: self.append(item) def n3(self): """ >>> from rdflib.graph import Graph >>> listName = BNode() >>> g = Graph('IOMemory') >>> listItem1 = BNode() >>> listItem2 = BNode() >>> g.add((listName,RDF.first,Literal(1))) >>> g.add((listName,RDF.rest,listItem1)) >>> g.add((listItem1,RDF.first,Literal(2))) >>> g.add((listItem1,RDF.rest,listItem2)) >>> g.add((listItem2,RDF.rest,RDF.nil)) >>> g.add((listItem2,RDF.first,Literal(3))) >>> c=Collection(g,listName) >>> print(c.n3()) ( "1"^^<http://www.w3.org/2001/XMLSchema#integer> "2"^^<http://www.w3.org/2001/XMLSchema#integer> "3"^^<http://www.w3.org/2001/XMLSchema#integer> ) """ return "( %s )"%(' '.join([i.n3() for i in self])) def _get_container(self, index): """Gets the first, rest holding node at index.""" assert isinstance(index, int) graph = self.graph container = self.uri i = 0 while i<index: i += 1 container = graph.value(container, RDF.rest) if container is None: break return container def __len__(self): """length of items in collection.""" count = 0 links=set() for item in self.graph.items(self.uri): assert item not in links,"There is a loop in the RDF list! (%s has been processed before)"%item links.add(item) count += 1 return count def index(self, item): """ Returns the 0-based numerical index of the item in the list """ listName = self.uri index = 0 while True: if (listName,RDF.first,item) in self.graph: return index else: newLink = list(self.graph.objects(listName,RDF.rest)) index += 1 if newLink == [RDF.nil]: raise ValueError("%s is not in %s"%(item,self.uri)) elif not newLink: raise Exception("Malformed RDF Collection: %s"%self.uri) else: assert len(newLink)==1, "Malformed RDF Collection: %s"%self.uri listName = newLink[0] def __getitem__(self, key): """TODO""" c = self._get_container(key) if c: v = self.graph.value(c, RDF.first) if v: return v else: raise KeyError, key else: raise IndexError, key def __setitem__(self, key, value): """TODO""" c = self._get_container(key) if c: self.graph.add((c, RDF.first, value)) else: raise IndexError, key def __delitem__(self, key): """ >>> from rdflib.namespace import RDF, RDFS >>> from rdflib import Graph >>> from pprint import pformat >>> g=Graph() >>> a=BNode('foo') >>> b=BNode('bar') >>> c=BNode('baz') >>> g.add((a,RDF.first,RDF.type)) >>> g.add((a,RDF.rest,b)) >>> g.add((b,RDF.first,RDFS.label)) >>> g.add((b,RDF.rest,c)) >>> g.add((c,RDF.first,RDFS.comment)) >>> g.add((c,RDF.rest,RDF.nil)) >>> len(g) 6 >>> def listAncestry(node,graph): ... for i in graph.subjects(RDF.rest,node): ... yield i >>> [str(node.n3()) for node in g.transitiveClosure(listAncestry,RDF.nil)] ['_:baz', '_:bar', '_:foo'] >>> lst=Collection(g,a) >>> len(lst) 3 >>> b==lst._get_container(1) True >>> c==lst._get_container(2) True >>> del lst[1] >>> len(lst) 2 >>> len(g) 4 """ self[key] # to raise any potential key exceptions graph = self.graph current = self._get_container(key) assert current if len(self)==1 and key>0: pass elif key==len(self)-1: #the tail priorLink = self._get_container(key-1) self.graph.set((priorLink,RDF.rest,RDF.nil)) graph.remove((current, None, None)) else: next = self._get_container(key+1) prior = self._get_container(key-1) assert next and prior graph.remove((current, None, None)) graph.set((prior, RDF.rest, next)) def __iter__(self): """Iterator over items in Collections""" return self.graph.items(self.uri) def append(self, item): """ >>> from rdflib.graph import Graph >>> listName = BNode() >>> g = Graph() >>> c=Collection(g,listName,[Literal(1),Literal(2)]) >>> links = [list(g.subjects(object=i,predicate=RDF.first))[0] for i in c] >>> len([i for i in links if (i,RDF.rest,RDF.nil) in g]) 1 """ container = self.uri graph = self.graph #iterate to the end of the linked list rest = graph.value(container, RDF.rest) while rest: if rest == RDF.nil: #the end, append to the end of the linked list node = BNode() graph.set((container, RDF.rest, node)) container=node break else: #move down one link if container != self.uri: rest = graph.value(rest, RDF.rest) if not rest == RDF.nil: container=rest graph.add((container, RDF.first, item)) graph.add((container, RDF.rest, RDF.nil)) def clear(self): container = self.uri graph = self.graph while container: rest = graph.value(container, RDF.rest) graph.remove((container, RDF.first, None)) graph.remove((container, RDF.rest, None)) container = rest
class Collection(object): __doc__ = format_doctest_out(""" A tweak of rdflib Connection. This one doesnt update the store when its created. """) def __init__(self, graph, uri): self.graph = graph self.uri = uri or BNode() def n3(self): """ >>> from rdflib.graph import Graph >>> listName = BNode() >>> g = Graph('IOMemory') >>> listItem1 = BNode() >>> listItem2 = BNode() >>> g.add((listName, RDF.first, Literal(1))) >>> g.add((listName, RDF.rest, listItem1)) >>> g.add((listItem1, RDF.first, Literal(2))) >>> g.add((listItem1, RDF.rest, listItem2)) >>> g.add((listItem2, RDF.rest, RDF.nil)) >>> g.add((listItem2, RDF.first, Literal(3))) >>> c = Collection(g, listName) >>> print(c.n3()) #doctest: +NORMALIZE_WHITESPACE ( "1"^^<http://www.w3.org/2001/XMLSchema#integer> "2"^^<http://www.w3.org/2001/XMLSchema#integer> "3"^^<http://www.w3.org/2001/XMLSchema#integer> ) """ return "( %s )" % (' '.join([i.n3() for i in self])) def _get_container(self, index): """Gets the first, rest holding node at index.""" assert isinstance(index, int) graph = self.graph container = self.uri i = 0 while i < index: i += 1 container = graph.value(container, RDF.rest) if container is None: break return container def __len__(self): """length of items in collection.""" return len(list(self.graph.items(self.uri))) def index(self, item): """ Returns the 0-based numerical index of the item in the list """ listName = self.uri index = 0 while True: if (listName, RDF.first, item) in self.graph: return index else: newLink = list(self.graph.objects(listName, RDF.rest)) index += 1 if newLink == [RDF.nil]: raise ValueError("%s is not in %s" % (item, self.uri)) elif not newLink: raise Exception("Malformed RDF Collection: %s" % self.uri) else: assert len(newLink) == 1, \ "Malformed RDF Collection: %s" % self.uri listName = newLink[0] def __getitem__(self, key): """TODO""" c = self._get_container(key) if c: v = self.graph.value(c, RDF.first) if v: return v else: raise KeyError(key) else: raise IndexError(key) def __setitem__(self, key, value): """TODO""" c = self._get_container(key) if c: self.graph.set((c, RDF.first, value)) else: raise IndexError(key) def __delitem__(self, key): """ >>> from rdflib.namespace import RDF, RDFS >>> from rdflib import Graph >>> from pprint import pformat >>> g = Graph() >>> a = BNode('foo') >>> b = BNode('bar') >>> c = BNode('baz') >>> g.add((a, RDF.first, RDF.type)) >>> g.add((a, RDF.rest, b)) >>> g.add((b, RDF.first, RDFS.label)) >>> g.add((b, RDF.rest, c)) >>> g.add((c, RDF.first, RDFS.comment)) >>> g.add((c, RDF.rest, RDF.nil)) >>> len(g) 6 >>> def listAncestry(node, graph): ... for i in graph.subjects(RDF.rest, node): ... yield i >>> [str(node.n3()) ... for node in g.transitiveClosure(listAncestry, RDF.nil)] ['_:baz', '_:bar', '_:foo'] >>> lst = Collection(g, a) >>> len(lst) 3 >>> b == lst._get_container(1) True >>> c == lst._get_container(2) True >>> del lst[1] >>> len(lst) 2 >>> len(g) 4 """ self[key] # to raise any potential key exceptions graph = self.graph current = self._get_container(key) assert current if len(self) == 1 and key > 0: pass elif key == len(self) - 1: # the tail priorLink = self._get_container(key - 1) self.graph.set((priorLink, RDF.rest, RDF.nil)) graph.remove((current, None, None)) else: next = self._get_container(key + 1) prior = self._get_container(key - 1) assert next and prior graph.remove((current, None, None)) graph.set((prior, RDF.rest, next)) def __iter__(self): """Iterator over items in Collections""" return self.graph.items(self.uri) def _end(self): # find end of list container = self.uri while True: rest = self.graph.value(container, RDF.rest) if rest == None or rest == RDF.nil: return container else: container = rest def append(self, item): """ >>> from rdflib.graph import Graph >>> listName = BNode() >>> g = Graph() >>> c = Collection(g,listName,[Literal(1),Literal(2)]) >>> links = [ ... list(g.subjects(object=i, predicate=RDF.first))[0] for i in c] >>> len([i for i in links if (i, RDF.rest, RDF.nil) in g]) 1 """ end = self._end() if (end, RDF.first, None) in self.graph: # append new node to the end of the linked list node = BNode() self.graph.set((end, RDF.rest, node)) end = node self.graph.add((end, RDF.first, item)) self.graph.add((end, RDF.rest, RDF.nil)) def __iadd__(self, other): end = self._end() self.graph.remove((end, RDF.rest, None)) for item in other: if (end, RDF.first, None) in self.graph: nxt = BNode() self.graph.add((end, RDF.rest, nxt)) end = nxt self.graph.add((end, RDF.first, item)) self.graph.add((end, RDF.rest, RDF.nil)) def clear(self): container = self.uri graph = self.graph while container: rest = graph.value(container, RDF.rest) graph.remove((container, RDF.first, None)) graph.remove((container, RDF.rest, None)) container = rest
__doc__ = format_doctest_out( """ =================== Namespace Utilities =================== RDFLib provides mechanisms for managing Namespaces. In particular, there is a :class:`~rdflib.namespace.Namespace` class that takes as its argument the base URI of the namespace. .. code-block:: pycon >>> from rdflib.namespace import Namespace >>> fuxi = Namespace('http://metacognition.info/ontologies/FuXi.n3#') Fully qualified URIs in the namespace can be constructed either by attribute or by dictionary access on Namespace instances: .. code-block:: pycon >>> fuxi.ruleBase rdflib.term.URIRef(u'http://metacognition.info/ontologies/FuXi.n3#ruleBase') >>> fuxi['ruleBase'] rdflib.term.URIRef(u'http://metacognition.info/ontologies/FuXi.n3#ruleBase') Automatic handling of unknown predicates ----------------------------------------- As a programming convenience, a namespace binding is automatically created when :class:`rdflib.term.URIRef` predicates are added to the graph: .. code-block:: pycon >>> from rdflib import Graph, URIRef >>> g = Graph() >>> g.add((URIRef("http://example0.com/foo"), ... URIRef("http://example1.com/bar"), ... URIRef("http://example2.com/baz"))) >>> print(g.serialize(format="n3")) @prefix ns1: <http://example1.com/> . <BLANKLINE> <http://example0.com/foo> ns1:bar <http://example2.com/baz> . <BLANKLINE> <BLANKLINE> >>> Importable namespaces ----------------------- The following namespaces are available by directly importing from rdflib: * RDF * RDFS * OWL * XSD .. code-block:: pycon >>> from rdflib import OWL >>> OWL.seeAlso rdflib.term.URIRef(u'http://www.w3.org/2002/07/owl#seeAlso') """ )
class N3RuleStore(Store): doc = """ A specialized Store which maintains order of statements and creates N3Filters, Rules, Formula objects, and other facts Ensures builtin filters refer to variables that have preceded >>> s = N3RuleStore() >>> g = Graph(s) >>> src = \"\"\" ... @prefix : <http://metacognition.info/FuXi/test#>. ... @prefix str: <http://www.w3.org/2000/10/swap/string#>. ... @prefix math: <http://www.w3.org/2000/10/swap/math#>. ... @prefix log: <http://www.w3.org/2000/10/swap/log#>. ... @prefix m: <http://metacognition.info/FuXi/test#>. ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. ... @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>. ... @prefix owl: <http://www.w3.org/2002/07/owl#>. ... m:a a rdfs:Class; ... m:prop1 1; ... m:prop2 4. ... m:b a owl:Class; ... m:prop1 2; ... m:prop2 4, 1, 5. ... (1 2) :relatedTo (3 4). ... { ?X a owl:Class. ?X :prop1 ?M. ?X :prop2 ?N. ?N math:equalTo 3 } => { [] :selected (?M ?N) }.\"\"\" >>> g = g.parse(data=src, format='n3') >>> s._finalize() >>> len([pred for subj, pred, obj in s.facts if pred == %(u)s'http://metacognition.info/FuXi/test#relatedTo']) #doctest: +SKIP 1 >>> len(s.rules) 1 >>> print(len(s.rules[0][RULE_LHS])) 4 >>> print(len(s.rules[0][RULE_RHS])) 5 >>> print(s.rules[0][RULE_LHS][1]) (rdflib.term.Variable(%(u)s'X'), rdflib.term.URIRef(%(u)s'http://metacognition.info/FuXi/test#prop1'), rdflib.term.Variable(%(u)s'M')) >>> print(s.rules[0][RULE_LHS][-1]) <http://www.w3.org/2000/10/swap/math#equalTo>(?N, 3) Description Rule Patterns Compilation >>> s = N3RuleStore() >>> g = Graph(s) >>> src = \"\"\" ... @prefix math: <http://www.w3.org/2000/10/swap/math#>. ... @prefix : <http://metacognition.info/FuXi/test#>. ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. ... @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>. ... @prefix owl: <http://www.w3.org/2002/07/owl#>. ... { ?S a [ rdfs:subClassOf ?C ] } => { ?S a ?C }.\"\"\" >>> g = g.parse(data=src, format='n3') >>> s._finalize() >>> assert s.rules >>> assert [pattern for pattern in s.rules[0][RULE_LHS] if isinstance(pattern, tuple) and [term for term in pattern if isinstance(term, BNode) ]], repr(s.rules[0][RULE_LHS]) Test single fact with collection >>> s = N3RuleStore() >>> g = Graph(s) >>> src = \"\"\" ... @prefix math: <http://www.w3.org/2000/10/swap/math#>. ... @prefix : <http://metacognition.info/FuXi/test#>. ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. ... @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>. ... @prefix owl: <http://www.w3.org/2002/07/owl#>. ... (1 2) :relatedTo owl:Class.\"\"\" >>> g = g.parse(data=src, format='n3') >>> s._finalize() >>> print(len(s.facts)) 5 RHS can only include RDF triples >>> s = N3RuleStore() >>> g = Graph(s) >>> src = \"\"\" ... @prefix math: <http://www.w3.org/2000/10/swap/math#>. ... @prefix : <http://metacognition.info/FuXi/test#>. ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. ... @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>. ... @prefix owl: <http://www.w3.org/2002/07/owl#>. ... {} => { 3 math:lessThan 2}.\"\"\" >>> g = g.parse(data=src, format='n3') >>> try: ... s._finalize() ... except Exception as e: ... print(e) Rule RHS must only include RDF triples (<http://www.w3.org/2000/10/swap/math#lessThan>(3, 2)) BuiltIn used out of order >>> s = N3RuleStore() >>> g = Graph(s) >>> src = \"\"\" ... @prefix math: <http://www.w3.org/2000/10/swap/math#>. ... @prefix : <http://metacognition.info/FuXi/test#>. ... { ?M math:lessThan ?Z. ?R :value ?M; :value2 ?Z} => { ?R a :Selected. }.\"\"\" >>> try: ... g = g.parse(data=src, format='n3') ... except Exception as e: ... print(e) #doctest: +SKIP Builtin refers to variables without previous reference (<http://www.w3.org/2000/10/swap/math#lessThan>(?M, ?Z)) Empty LHS & RHS >>> s = N3RuleStore() >>> g = Graph(s) >>> src = \"\"\" ... @prefix math: <http://www.w3.org/2000/10/swap/math#>. ... @prefix : <http://metacognition.info/FuXi/test#>. ... @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. ... @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>. ... @prefix owl: <http://www.w3.org/2002/07/owl#>. ... {} => {rdf:nil :allClasses ?C}. ... {?C owl:oneOf ?L. ?X a ?C. ?L :notItem ?X} => {}.\"\"\" >>> g = g.parse(data=src, format='n3') >>> len(s.formulae) 2 >>> s._finalize() >>> len(s.rules[0][0]) 0 >>> len(s.rules[1][-1]) 0 """ __doc__ = py3compat.format_doctest_out(doc) context_aware = True formula_aware = True def __init__(self, identifier=None, additionalBuiltins=None): self.formulae = {} self.facts = [] self.rootFormula = None self._lists = {} self.currentList = None self._listBuffer = [] self.rules = [] self.referencedVariables = set() self.nsMgr = { 'skolem': URIRef('http://code.google.com/p/python-dlp/wiki/SkolemTerm#') } # or https://code.google.com/archive/p/python-dlp/wikis/SkolemTerm.wiki self.filters = {} self.filters.update(FILTERS) if additionalBuiltins: self.filters.update(additionalBuiltins) def namespace(self, prefix): return self.nsMgr.get(prefix) def bind(self, prefix, namespace, override=True): if override or prefix not in self.nsMgr: self.nsMgr[prefix] = namespace def prefix(self, namespace): return dict([(v, k) for k, v in list(self.nsMgr.items())]).get(namespace) def _unrollList(self, l, listName): listTriples = [] lastItemName = None for linkItem in l: linkName = l.index(linkItem) == 0 and listName or BNode() if lastItemName: listTriples.append((lastItemName, RDF.rest, linkName)) listTriples.append((linkName, RDF.first, linkItem)) lastItemName = linkName listTriples.append((lastItemName, RDF.rest, RDF.nil)) return listTriples def _finalize(self): def unrollFunc(left, right): leftListsToUnroll = [] rightListsToUnroll = [] if isinstance(left, tuple): s, p, o = left leftListsToUnroll = [ term for term in [s, o] if term in self._lists ] if leftListsToUnroll: leftListsToUnroll = reduce(lambda x, y: x + y, [ self._unrollList(self._lists[l], l) for l in leftListsToUnroll ]) left = [left] elif isinstance(left, N3Builtin): left = [left] if isinstance(right, tuple): s, p, o = right rightListsToUnroll = [ term for term in [s, o] if term in self._lists ] if rightListsToUnroll: rightListsToUnroll = reduce(lambda x, y: x + y, [ self._unrollList(self._lists[l], l) for l in rightListsToUnroll ]) right = [right] elif isinstance(right, N3Builtin): right = [right] return left + leftListsToUnroll + right + rightListsToUnroll if len(self.facts) == 1: s, p, o = self.facts[0] listsToUnroll = [term for term in [s, o] if term in self._lists] if listsToUnroll: self.facts.extend( reduce(lambda x, y: x + y, [ self._unrollList(self._lists[l], l) for l in listsToUnroll ])) elif self.facts: self.facts = reduce(unrollFunc, self.facts) for formula in list(self.formulae.values()): if len(formula) == 1: if isinstance(formula[0], tuple): s, p, o = formula[0] listsToUnroll = [ term for term in [s, o] if term in self._lists ] if listsToUnroll: listTriples = reduce(lambda x, y: x + y, [ self._unrollList(self._lists[l], l) for l in listsToUnroll ]) formula.extend(listTriples) elif len(formula): formula.triples = reduce(unrollFunc, [i for i in formula]) for lhs, rhs in self.rules: for item in self.formulae.get(rhs, []): assert isinstance(item, tuple), \ "Rule RHS must only include RDF triples (%s)" % item self.rules = [(self.formulae.get(lhs, Formula(lhs)), self.formulae.get(rhs, Formula(rhs))) for lhs, rhs in self.rules] def _checkVariableReferences(self, referencedVariables, terms, funcObj): for term in [i for i in terms if isinstance(i, Variable)]: if term not in referencedVariables: raise Exception( "Builtin refers to variables without previous reference (%s)" % funcObj) def add(self, triple, context=None, quoted=False): (subject, predicate, obj) = triple if predicate == RDF.first and not isinstance( subject, Variable) and not isinstance(object, Variable): if not self.currentList: self._listBuffer.append(obj) self.currentList = subject else: self._listBuffer.append(obj) elif predicate == RDF.rest and not isinstance( subject, Variable) and not isinstance(object, Variable): if obj == RDF.nil: self._lists[self.currentList] = [ item for item in self._listBuffer ] self._listBuffer = [] self.currentList = None elif not isinstance(context, QuotedGraph): if not self.rootFormula: self.rootFormula = context.identifier if predicate == LOG.implies: self.rules.append( (isinstance(subject, URIRef) and subject or subject.identifier, isinstance(obj, (URIRef, Literal)) and obj or obj.identifier if hasattr(obj, 'identifier') else str(obj))) else: self.facts.append((subject, predicate, obj)) else: formula = self.formulae.get(context.identifier, Formula(context.identifier)) if predicate in self.filters: newFilter = N3Builtin(predicate, self.filters[predicate](subject, obj), subject, obj) # @attention: The non-deterministic parse order of an RDF graph makes this # check hard to enforce # self._checkVariableReferences(self.referencedVariables, [subject, obj], newFilter) formula.append(newFilter) else: # print("(%s, %s, %s) pattern in %s"%(subject, predicate, obj, context.identifier)) variables = [ arg for arg in [subject, predicate, obj] if isinstance(arg, Variable) ] self.referencedVariables.update(variables) formula.append((subject, predicate, obj)) self.formulae[context.identifier] = formula def __repr__(self): return "" def __len__(self, context=None): return 0 def optimizeRules(self): patternDict = {} for lhs, rhs in self.rules: for pattern in lhs: if not isinstance(pattern, N3Builtin): _hashList = [ isinstance(term, (Variable, BNode)) and '\t' or term for term in pattern ] patternDict.setdefault( reduce(lambda x, y: x + y, _hashList), set()).add(pattern) for key, vals in list(patternDict.items()): if len(vals) > 1: print("###### Similar Patterns ######") for val in vals: print(val) print("##############################")
__doc__ = py3compat.format_doctest_out(""" A Describer is a stateful utility for creating RDF statements in a semi-declarative manner. It has methods for creating literal values, rel and rev resource relations (somewhat resembling RDFa). The `rel` and ``rev`` methods return a context manager which sets the current about to the referenced resource for the context scope (for use with the ``with`` statement). Full example in the ``to_rdf`` method below:: >>> import datetime >>> from rdflib.graph import Graph >>> from rdflib.namespace import Namespace, RDFS, FOAF >>> >>> ORG_URI = "http://example.org/" >>> >>> CV = Namespace("http://purl.org/captsolo/resume-rdf/0.2/cv#") >>> >>> class Person(object): ... def __init__(self): ... self.first_name = %(u)s"Some" ... self.last_name = %(u)s"Body" ... self.username = "******" ... self.presentation = %(u)s"Just a Python & RDF hacker." ... self.image = "/images/persons/" + self.username + ".jpg" ... self.site = "http://example.net/" ... self.start_date = datetime.date(2009, 9, 4) ... def get_full_name(self): ... return %(u)s" ".join([self.first_name, self.last_name]) ... def get_absolute_url(self): ... return "/persons/" + self.username ... def get_thumbnail_url(self): ... return self.image.replace('.jpg', '-thumb.jpg') ... ... def to_rdf(self): ... graph = Graph() ... graph.bind('foaf', FOAF) ... graph.bind('cv', CV) ... lang = 'en' ... d = Describer(graph, base=ORG_URI) ... d.about(self.get_absolute_url()+'#person') ... d.rdftype(FOAF.Person) ... d.value(FOAF.name, self.get_full_name()) ... d.value(FOAF.firstName, self.first_name) ... d.value(FOAF.surname, self.last_name) ... d.rel(FOAF.homepage, self.site) ... d.value(RDFS.comment, self.presentation, lang=lang) ... with d.rel(FOAF.depiction, self.image): ... d.rdftype(FOAF.Image) ... d.rel(FOAF.thumbnail, self.get_thumbnail_url()) ... with d.rev(CV.aboutPerson): ... d.rdftype(CV.CV) ... with d.rel(CV.hasWorkHistory): ... d.value(CV.startDate, self.start_date) ... d.rel(CV.employedIn, ORG_URI+"#company") ... return graph ... >>> person_graph = Person().to_rdf() >>> expected = Graph().parse(data='''<?xml version="1.0" encoding="utf-8"?> ... <rdf:RDF ... xmlns:foaf="http://xmlns.com/foaf/0.1/" ... xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ... xmlns:cv="http://purl.org/captsolo/resume-rdf/0.2/cv#" ... xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"> ... <foaf:Person rdf:about="http://example.org/persons/some1#person"> ... <foaf:name>Some Body</foaf:name> ... <foaf:firstName>Some</foaf:firstName> ... <foaf:surname>Body</foaf:surname> ... <foaf:depiction> ... <foaf:Image ... rdf:about= ... "http://example.org/images/persons/some1.jpg"> ... <foaf:thumbnail ... rdf:resource= ... "http://example.org/images/persons/some1-thumb.jpg"/> ... </foaf:Image> ... </foaf:depiction> ... <rdfs:comment xml:lang="en"> ... Just a Python & RDF hacker. ... </rdfs:comment> ... <foaf:homepage rdf:resource="http://example.net/"/> ... </foaf:Person> ... <cv:CV> ... <cv:aboutPerson ... rdf:resource="http://example.org/persons/some1#person"> ... </cv:aboutPerson> ... <cv:hasWorkHistory> ... <rdf:Description> ... <cv:startDate ... rdf:datatype="http://www.w3.org/2001/XMLSchema#date" ... >2009-09-04</cv:startDate> ... <cv:employedIn rdf:resource="http://example.org/#company"/> ... </rdf:Description> ... </cv:hasWorkHistory> ... </cv:CV> ... </rdf:RDF> ... ''') >>> >>> from rdflib.compare import isomorphic >>> isomorphic(person_graph, expected) #doctest: +SKIP True """)