From ee6db683ae8690bd4fcd69e660147478eb47527b Mon Sep 17 00:00:00 2001 From: Graham Higgins Date: Sun, 11 Jun 2023 15:57:29 +0100 Subject: [PATCH] id-as-context copied over from PR 409 --- rdflib/graph.py | 90 ++++++++++++++++------------ rdflib/plugins/parsers/notation3.py | 2 +- rdflib/plugins/sparql/update.py | 4 +- rdflib/plugins/stores/auditable.py | 36 +++-------- rdflib/plugins/stores/sparqlstore.py | 4 +- rdflib/store.py | 14 ++++- 6 files changed, 76 insertions(+), 74 deletions(-) diff --git a/rdflib/graph.py b/rdflib/graph.py index 4d8645b2f..c272c3a5b 100644 --- a/rdflib/graph.py +++ b/rdflib/graph.py @@ -530,14 +530,14 @@ def add(self: _GraphT, triple: "_TripleType") -> _GraphT: assert isinstance(s, Node), "Subject %s must be an rdflib term" % (s,) assert isinstance(p, Node), "Predicate %s must be an rdflib term" % (p,) assert isinstance(o, Node), "Object %s must be an rdflib term" % (o,) - self.__store.add((s, p, o), self, quoted=False) + self.__store.add((s, p, o), self.identifier, quoted=False) return self def addN(self: _GraphT, quads: Iterable["_QuadType"]) -> _GraphT: # noqa: N802 """Add a sequence of triple with context""" self.__store.addN( - (s, p, o, c) + (s, p, o, c.identifier) for s, p, o, c in quads if isinstance(c, Graph) and c.identifier is self.identifier @@ -551,7 +551,7 @@ def remove(self: _GraphT, triple: "_TriplePatternType") -> _GraphT: If the triple does not provide a context attribute, removes the triple from all contexts. """ - self.__store.remove(triple, context=self) + self.__store.remove(triple, context=self.identifier) return self @overload @@ -589,7 +589,9 @@ def triples( for _s, _o in p.eval(self, s, o): yield _s, p, _o else: - for (_s, _p, _o), cg in self.__store.triples((s, p, o), context=self): + for (_s, _p, _o), cg in self.__store.triples( + (s, p, o), context=self.identifier + ): yield _s, _p, _o def __getitem__(self, item): @@ -669,7 +671,7 @@ def __len__(self) -> int: returned instead. """ # type error: Unexpected keyword argument "context" for "__len__" of "Store" - return self.__store.__len__(context=self) # type: ignore[call-arg] + return self.__store.__len__(context=self.identifier) # type: ignore[call-arg] def __iter__(self) -> Generator["_TripleType", None, None]: """Iterates over all triples in the store""" @@ -947,7 +949,7 @@ def triples_choices( # type error: Argument 1 to "triples_choices" of "Store" has incompatible type "Tuple[Union[List[Node], Node], Union[Node, List[Node]], Union[Node, List[Node]]]"; expected "Union[Tuple[List[Node], Node, Node], Tuple[Node, List[Node], Node], Tuple[Node, Node, List[Node]]]" # type error note: unpacking discards type info for (s, p, o), cg in self.store.triples_choices( - (subject, predicate, object_), context=self # type: ignore[arg-type] + (subject, predicate, object_), context=self.identifier # type: ignore[arg-type] ): yield s, p, o @@ -1904,7 +1906,7 @@ def __init__( self.context_aware = True self.default_union = True # Conjunctive! self.default_context: _ContextType = Graph( - store=self.store, identifier=identifier or BNode(), base=default_graph_base + store=self.store, identifier=self.identifier, base=default_graph_base ) def __str__(self) -> str: @@ -1972,15 +1974,14 @@ def _spoc( either triples or quads """ if triple_or_quad is None: - return (None, None, None, self.default_context if default else None) + return (None, None, None, self.identifier if default else None) if len(triple_or_quad) == 3: - c = self.default_context if default else None + c = self.identifier if default else None # type error: Too many values to unpack (3 expected, 4 provided) (s, p, o) = triple_or_quad # type: ignore[misc] elif len(triple_or_quad) == 4: # type error: Need more than 3 values to unpack (4 expected) (s, p, o, c) = triple_or_quad # type: ignore[misc] - c = self._graph(c) return s, p, o, c def __contains__(self, triple_or_quad: _TripleOrQuadSelectorType) -> bool: @@ -2005,7 +2006,9 @@ def add( _assertnode(s, p, o) # type error: Argument "context" to "add" of "Store" has incompatible type "Optional[Graph]"; expected "Graph" - self.store.add((s, p, o), context=c, quoted=False) # type: ignore[arg-type] + self.store.add( + (s, p, o), context=c.identifier if isinstance(c, Graph) else c, quoted=False + ) # type: ignore[arg-type] return self @overload @@ -2032,7 +2035,9 @@ def addN( # noqa: N802 """Add a sequence of triples with context""" self.store.addN( - (s, p, o, self._graph(c)) for s, p, o, c in quads if _assertnode(s, p, o) + (s, p, o, c.identifier if isinstance(c, Graph) else c) + for s, p, o, c in quads + if _assertnode(s, p, o) ) return self @@ -2048,7 +2053,9 @@ def remove(self: _ConjunctiveGraphT, triple_or_quad: _TripleOrOptionalQuadType) """ s, p, o, c = self._spoc(triple_or_quad) - self.store.remove((s, p, o), context=c) + self.store.remove( + (s, p, o), context=c.identifier if isinstance(c, Graph) else c + ) return self @overload @@ -2089,14 +2096,14 @@ def triples( """ s, p, o, c = self._spoc(triple_or_quad) - context = self._graph(context or c) + context = context or c if self.default_union: - if context == self.default_context: + if context == self.identifier: context = None else: if context is None: - context = self.default_context + context = self.identifier if isinstance(p, Path): if context is None: @@ -2115,9 +2122,11 @@ def quads( s, p, o, c = self._spoc(triple_or_quad) - for (s, p, o), cg in self.store.triples((s, p, o), context=c): + for (s, p, o), cg in self.store.triples( + (s, p, o), context=c if isinstance(c, Graph) else c + ): for ctx in cg: - yield s, p, o, ctx + yield s, p, o, self._graph(ctx) def triples_choices( self, @@ -2132,9 +2141,10 @@ def triples_choices( s, p, o = triple if context is None: if not self.default_union: - context = self.default_context + context = self.identifier else: - context = self._graph(context) + if isinstance(context, Graph): + context = context.identifier # type error: Argument 1 to "triples_choices" of "Store" has incompatible type "Tuple[Union[List[Node], Node], Union[Node, List[Node]], Union[Node, List[Node]]]"; expected "Union[Tuple[List[Node], Node, Node], Tuple[Node, List[Node], Node], Tuple[Node, Node, List[Node]]]" # type error note: unpacking discards type info for (s1, p1, o1), cg in self.store.triples_choices((s, p, o), context=context): # type: ignore[arg-type] @@ -2153,10 +2163,7 @@ def contexts( """ for context in self.store.contexts(triple): if isinstance(context, Graph): - # TODO: One of these should never happen and probably - # should raise an exception rather than smoothing over - # the weirdness - see #225 - yield context + raise Exception("Got graph object as context, not Identifier!") else: # type error: Statement is unreachable yield self.get_context(context) # type: ignore[unreachable] @@ -2184,7 +2191,10 @@ def get_context( def remove_context(self, context: "_ContextType") -> None: """Removes the given context from the graph""" - self.store.remove((None, None, None), context) + self.store.remove( + (None, None, None), + context.identifier if isinstance(context, Graph) else context, + ) def context_id(self, uri: str, context_id: Optional[str] = None) -> URIRef: """URI#context""" @@ -2403,14 +2413,16 @@ def __init__( if not self.store.graph_aware: raise Exception("DataSet must be backed by a graph-aware store!") - self.default_context = Graph( - store=self.store, - identifier=DATASET_DEFAULT_GRAPH_ID, - base=default_graph_base, - ) self.default_union = default_union + def add(self, triple_or_quad): + if len(triple_or_quad) == 4: + res = super(Dataset, self).add(triple_or_quad) + else: + res = super(Dataset, self).add(triple_or_quad + (DATASET_DEFAULT_GRAPH_ID,)) + return res + def __str__(self) -> str: pattern = ( "[a rdflib:Dataset;rdflib:storage " "[a rdflib:Store;rdfs:label '%s']]" @@ -2437,6 +2449,8 @@ def graph( identifier: Optional[Union[_ContextIdentifierType, _ContextType, str]] = None, base: Optional[str] = None, ) -> Graph: + if isinstance(identifier, Graph): + identifier = identifier.identifier if identifier is None: from rdflib.term import _SKOLEM_DEFAULT_AUTHORITY, rdflib_skolem_genid @@ -2450,7 +2464,7 @@ def graph( g = self._graph(identifier) g.base = base - self.store.add_graph(g) + self.store.add_graph(identifier) return g def parse( @@ -2512,14 +2526,14 @@ def add_graph( def remove_graph( self: _DatasetT, g: Optional[Union[_ContextIdentifierType, _ContextType, str]] ) -> _DatasetT: - if not isinstance(g, Graph): - g = self.get_context(g) + if isinstance(g, Graph): + g = g.identifier self.store.remove_graph(g) - if g is None or g == self.default_context: + if g is None or g == DATASET_DEFAULT_GRAPH_ID: # default graph cannot be removed # only triples deleted, so add it back in - self.store.add_graph(self.default_context) + self.store.add_graph(DATASET_DEFAULT_GRAPH_ID) return self def contexts( @@ -2540,7 +2554,7 @@ def quads( # type: ignore[override] ) -> Generator[_OptionalIdentifiedQuadType, None, None]: for s, p, o, c in super(Dataset, self).quads(quad): # type error: Item "None" of "Optional[Graph]" has no attribute "identifier" - if c.identifier == self.default_context: # type: ignore[union-attr] + if c.identifier == DATASET_DEFAULT_GRAPH_ID: # type: ignore[union-attr] yield s, p, o, None else: # type error: Item "None" of "Optional[Graph]" has no attribute "identifier" [union-attr] @@ -2576,14 +2590,14 @@ def add(self: _GraphT, triple: "_TripleType") -> _GraphT: assert isinstance(p, Node), "Predicate %s must be an rdflib term" % (p,) assert isinstance(o, Node), "Object %s must be an rdflib term" % (o,) - self.store.add((s, p, o), self, quoted=True) + self.store.add((s, p, o), self.identifier, quoted=True) return self def addN(self: _GraphT, quads: Iterable["_QuadType"]) -> _GraphT: # noqa: N802 """Add a sequence of triple with context""" self.store.addN( - (s, p, o, c) + (s, p, o, c.identifier) for s, p, o, c in quads if isinstance(c, QuotedGraph) and c.identifier is self.identifier diff --git a/rdflib/plugins/parsers/notation3.py b/rdflib/plugins/parsers/notation3.py index 2a64be24f..049cb2ef9 100755 --- a/rdflib/plugins/parsers/notation3.py +++ b/rdflib/plugins/parsers/notation3.py @@ -2049,7 +2049,7 @@ def parse( # type: ignore[override] elif not fa: raise ParserError("Cannot parse N3 into non-formula-aware store.") - conj_graph = ConjunctiveGraph(store=graph.store) + conj_graph = ConjunctiveGraph(identifier=graph.identifier, store=graph.store) conj_graph.default_context = graph # TODO: CG __init__ should have a # default_context arg # TODO: update N3Processor so that it can use conj_graph as the sink diff --git a/rdflib/plugins/sparql/update.py b/rdflib/plugins/sparql/update.py index 5ce86f393..a5b64027d 100644 --- a/rdflib/plugins/sparql/update.py +++ b/rdflib/plugins/sparql/update.py @@ -80,7 +80,7 @@ def evalDrop(ctx: QueryContext, u: CompValue) -> None: """ if ctx.dataset.store.graph_aware: for g in _graphAll(ctx, u.graphiri): - ctx.dataset.store.remove_graph(g) + ctx.dataset.remove_graph(g) else: evalClear(ctx, u) @@ -248,7 +248,7 @@ def evalMove(ctx: QueryContext, u: CompValue) -> None: if ctx.dataset.store.graph_aware: # type error: Argument 1 to "remove_graph" of "Store" has incompatible type "Optional[Graph]"; expected "Graph" - ctx.dataset.store.remove_graph(srcg) # type: ignore[arg-type] + ctx.dataset.remove_graph(srcg) # type: ignore[arg-type] else: # type error: Item "None" of "Optional[Graph]" has no attribute "remove" srcg.remove((None, None, None)) # type: ignore[union-attr] diff --git a/rdflib/plugins/stores/auditable.py b/rdflib/plugins/stores/auditable.py index 17fa0e548..c4abb9a0c 100644 --- a/rdflib/plugins/stores/auditable.py +++ b/rdflib/plugins/stores/auditable.py @@ -79,12 +79,7 @@ def add( lock = destructiveOpLocks["add"] lock = lock if lock else threading.RLock() with lock: - context = ( - context.__class__(self.store, context.identifier) - if context is not None - else None - ) - ctxId = context.identifier if context is not None else None # noqa: N806 + ctxId = context if context is not None else None # noqa: N806 if list(self.store.triples(triple, context)): return # triple already in store, do nothing self.reverseOps.append((s, p, o, ctxId, "remove")) @@ -103,16 +98,11 @@ def remove( with lock: # Need to determine which quads will be removed if any term is a # wildcard - context = ( - context.__class__(self.store, context.identifier) - if context is not None - else None - ) - ctxId = context.identifier if context is not None else None # noqa: N806 + ctxId = context if context is not None else None # noqa: N806 if None in [subject, predicate, object_, context]: if ctxId: # type error: Item "None" of "Optional[Graph]" has no attribute "triples" - for s, p, o in context.triples((subject, predicate, object_)): # type: ignore[union-attr] + for (s, p, o), cg in self.store.triples((subject, predicate, object_), context): try: self.reverseOps.remove((s, p, o, ctxId, "remove")) except ValueError: @@ -123,10 +113,10 @@ def remove( ): try: # type error: Item "None" of "Optional[Graph]" has no attribute "identifier" - self.reverseOps.remove((s, p, o, ctx.identifier, "remove")) # type: ignore[union-attr] + self.reverseOps.remove((s, p, o, ctx, "remove")) # type: ignore[union-attr] except ValueError: # type error: Item "None" of "Optional[Graph]" has no attribute "identifier" - self.reverseOps.append((s, p, o, ctx.identifier, "add")) # type: ignore[union-attr] + self.reverseOps.append((s, p, o, ctx, "add")) # type: ignore[union-attr] else: if not list(self.triples((subject, predicate, object_), context)): return # triple not present in store, do nothing @@ -142,20 +132,10 @@ def triples( self, triple: "_TriplePatternType", context: Optional["_ContextType"] = None ) -> Iterator[Tuple["_TripleType", Iterator[Optional["_ContextType"]]]]: (su, pr, ob) = triple - context = ( - context.__class__(self.store, context.identifier) - if context is not None - else None - ) for (s, p, o), cg in self.store.triples((su, pr, ob), context): yield (s, p, o), cg def __len__(self, context: Optional["_ContextType"] = None): - context = ( - context.__class__(self.store, context.identifier) - if context is not None - else None - ) return self.store.__len__(context) def contexts( @@ -187,11 +167,9 @@ def rollback(self) -> None: if op == "add": # type error: Argument 2 to "Graph" has incompatible type "Optional[Node]"; expected "Union[IdentifiedNode, str, None]" self.store.add( - (subject, predicate, obj), Graph(self.store, context) # type: ignore[arg-type] - ) + (subject, predicate, obj), context) # type: ignore[arg-type] else: self.store.remove( - (subject, predicate, obj), Graph(self.store, context) - ) + (subject, predicate, obj), context) self.reverseOps = [] diff --git a/rdflib/plugins/stores/sparqlstore.py b/rdflib/plugins/stores/sparqlstore.py index cfffbd768..786d3d157 100644 --- a/rdflib/plugins/stores/sparqlstore.py +++ b/rdflib/plugins/stores/sparqlstore.py @@ -341,7 +341,7 @@ def triples( # type: ignore[override] result = self._query( query, # type error: Item "None" of "Optional[Graph]" has no attribute "identifier" - default_graph=context.identifier if self._is_contextual(context) else None, # type: ignore[union-attr] + default_graph=context if self._is_contextual(context) else None, # type: ignore[union-attr] ) if vars: @@ -485,7 +485,7 @@ def _is_contextual(self, graph: Optional[Union["Graph", "str"]]) -> bool: if isinstance(graph, str): return graph != "__UNION__" else: - return graph.identifier != DATASET_DEFAULT_GRAPH_ID + return graph != DATASET_DEFAULT_GRAPH_ID def subjects( self, diff --git a/rdflib/store.py b/rdflib/store.py index e3c9f7ab2..9e7c86b1c 100644 --- a/rdflib/store.py +++ b/rdflib/store.py @@ -17,6 +17,7 @@ ) from rdflib.events import Dispatcher, Event +from rdflib.term import Identifier if TYPE_CHECKING: from rdflib.graph import ( @@ -31,7 +32,7 @@ ) from rdflib.plugins.sparql.sparql import Query, Update from rdflib.query import Result - from rdflib.term import Identifier, Node, URIRef + from rdflib.term import Node, URIRef """ ============ @@ -198,7 +199,6 @@ def node_pickler(self) -> NodePickler: np.register(URIRef, "U") np.register(BNode, "B") np.register(Literal, "L") - np.register(Graph, "G") np.register(QuotedGraph, "Q") np.register(Variable, "V") return self.__node_pickler @@ -254,6 +254,11 @@ def add( be an error for the quoted argument to be True when the store is not formula-aware. """ + if not isinstance(context, Identifier): + raise Exception( + f"Trying to add to a context that isn't an identifier: {context}" + ) + self.dispatcher.dispatch(TripleAddedEvent(triple=triple, context=context)) def addN(self, quads: Iterable["_QuadType"]) -> None: # noqa: N802 @@ -277,6 +282,11 @@ def remove( context: Optional["_ContextType"] = None, ) -> None: """Remove the set of triples matching the pattern from the store""" + if context is not None and not isinstance(context, Identifier): + raise Exception( + f"Trying to remove from a context that isn't an identifier: {context}" + ) + self.dispatcher.dispatch(TripleRemovedEvent(triple=triple, context=context)) def triples_choices(