From 7a7cc1f929ff6fbc8c3238368e518a00af1025b6 Mon Sep 17 00:00:00 2001 From: Iwan Aucamp Date: Mon, 13 Mar 2023 00:23:47 +0100 Subject: [PATCH] feat: more type hints for `rdflib.plugins.sparql` (#2268) A bit of a roundabout reason why this matters now, but basically: I want to add examples for securing RDFLib with `sys.addaudithook` and `urllib.request.install_opener`. I also want to be sure examples are actually valid, and runnable, so I was adding static analysis and simple execution of examples to our CI. During this, I noticed that examples use `initBindings` with `Dict[str,...]`, which was not valid according to mypy, but then after some investigation I realized the type hints in some places were too strict. So the main impetus for this is actually to relax the type hints in `rdflib.graph`, but to ensure this is valid I'm adding a bunch of type hints I had saved up to `rdflib.plugins.sparql`. Even though this PR looks big, it has no runtime changes. --- rdflib/graph.py | 5 +- rdflib/plugins/sparql/aggregates.py | 135 ++++++++++++++++++--------- rdflib/plugins/sparql/datatypes.py | 22 +++-- rdflib/plugins/sparql/evaluate.py | 64 ++++++++++--- rdflib/plugins/sparql/evalutils.py | 105 +++++++++++++++++---- rdflib/plugins/sparql/parser.py | 19 ++-- rdflib/plugins/sparql/parserutils.py | 70 ++++++++++---- rdflib/plugins/sparql/processor.py | 47 ++++++++-- rdflib/plugins/sparql/sparql.py | 40 ++++++-- rdflib/plugins/sparql/update.py | 102 +++++++++++++------- rdflib/plugins/stores/memory.py | 10 +- rdflib/plugins/stores/sparqlstore.py | 6 +- rdflib/query.py | 4 +- rdflib/store.py | 6 +- 14 files changed, 453 insertions(+), 182 deletions(-) diff --git a/rdflib/graph.py b/rdflib/graph.py index 56119e438..717788fda 100644 --- a/rdflib/graph.py +++ b/rdflib/graph.py @@ -51,7 +51,6 @@ Node, RDFLibGenid, URIRef, - Variable, ) if TYPE_CHECKING: @@ -1501,7 +1500,7 @@ def query( processor: Union[str, query.Processor] = "sparql", result: Union[str, Type[query.Result]] = "sparql", initNs: Optional[Mapping[str, Any]] = None, # noqa: N803 - initBindings: Optional[Mapping[Variable, Identifier]] = None, + initBindings: Optional[Mapping[str, Identifier]] = None, use_store_provided: bool = True, **kwargs: Any, ) -> query.Result: @@ -1547,7 +1546,7 @@ def update( update_object: Union[Update, str], processor: Union[str, rdflib.query.UpdateProcessor] = "sparql", initNs: Optional[Mapping[str, Any]] = None, # noqa: N803 - initBindings: Optional[Mapping[Variable, Identifier]] = None, + initBindings: Optional[Mapping[str, Identifier]] = None, use_store_provided: bool = True, **kwargs: Any, ) -> None: diff --git a/rdflib/plugins/sparql/aggregates.py b/rdflib/plugins/sparql/aggregates.py index 005a539d5..fd40ab055 100644 --- a/rdflib/plugins/sparql/aggregates.py +++ b/rdflib/plugins/sparql/aggregates.py @@ -1,10 +1,29 @@ -from decimal import Decimal +from __future__ import annotations -from rdflib import XSD, Literal +from decimal import Decimal +from typing import ( + Any, + Callable, + Dict, + Iterable, + List, + Mapping, + MutableMapping, + Optional, + Set, + Tuple, + TypeVar, + Union, + overload, +) + +from rdflib.namespace import XSD from rdflib.plugins.sparql.datatypes import type_promotion -from rdflib.plugins.sparql.evalutils import NotBoundError, _eval, _val +from rdflib.plugins.sparql.evalutils import _eval, _val from rdflib.plugins.sparql.operators import numeric -from rdflib.plugins.sparql.sparql import SPARQLTypeError +from rdflib.plugins.sparql.parserutils import CompValue +from rdflib.plugins.sparql.sparql import FrozenBindings, NotBoundError, SPARQLTypeError +from rdflib.term import BNode, Identifier, Literal, URIRef, Variable """ Aggregation functions @@ -14,38 +33,43 @@ class Accumulator(object): """abstract base class for different aggregation functions""" - def __init__(self, aggregation): + def __init__(self, aggregation: CompValue): + self.get_value: Callable[[], Optional[Literal]] + self.update: Callable[[FrozenBindings, "Aggregator"], None] self.var = aggregation.res self.expr = aggregation.vars if not aggregation.distinct: - self.use_row = self.dont_care + # type error: Cannot assign to a method + self.use_row = self.dont_care # type: ignore[assignment] self.distinct = False else: self.distinct = aggregation.distinct - self.seen = set() + self.seen: Set[Any] = set() - def dont_care(self, row): + def dont_care(self, row: FrozenBindings) -> bool: """skips distinct test""" return True - def use_row(self, row): + def use_row(self, row: FrozenBindings) -> bool: """tests distinct with set""" return _eval(self.expr, row) not in self.seen - def set_value(self, bindings): + def set_value(self, bindings: MutableMapping[Variable, Identifier]) -> None: """sets final value in bindings""" - bindings[self.var] = self.get_value() + # type error: Incompatible types in assignment (expression has type "Optional[Literal]", target has type "Identifier") + bindings[self.var] = self.get_value() # type: ignore[assignment] class Counter(Accumulator): - def __init__(self, aggregation): + def __init__(self, aggregation: CompValue): super(Counter, self).__init__(aggregation) self.value = 0 if self.expr == "*": # cannot eval "*" => always use the full row - self.eval_row = self.eval_full_row + # type error: Cannot assign to a method + self.eval_row = self.eval_full_row # type: ignore[assignment] - def update(self, row, aggregator): + def update(self, row: FrozenBindings, aggregator: "Aggregator") -> None: try: val = self.eval_row(row) except NotBoundError: @@ -55,41 +79,54 @@ def update(self, row, aggregator): if self.distinct: self.seen.add(val) - def get_value(self): + def get_value(self) -> Literal: return Literal(self.value) - def eval_row(self, row): + def eval_row(self, row: FrozenBindings) -> Identifier: return _eval(self.expr, row) - def eval_full_row(self, row): + def eval_full_row(self, row: FrozenBindings) -> FrozenBindings: return row - def use_row(self, row): + def use_row(self, row: FrozenBindings) -> bool: return self.eval_row(row) not in self.seen -def type_safe_numbers(*args): +@overload +def type_safe_numbers(*args: int) -> Tuple[int]: + ... + + +@overload +def type_safe_numbers(*args: Union[Decimal, float, int]) -> Tuple[Union[float, int]]: + ... + + +def type_safe_numbers(*args: Union[Decimal, float, int]) -> Iterable[Union[float, int]]: if any(isinstance(arg, float) for arg in args) and any( isinstance(arg, Decimal) for arg in args ): return map(float, args) - return args + # type error: Incompatible return value type (got "Tuple[Union[Decimal, float, int], ...]", expected "Iterable[Union[float, int]]") + # NOTE on type error: if args contains a Decimal it will nopt get here. + return args # type: ignore[return-value] class Sum(Accumulator): - def __init__(self, aggregation): + def __init__(self, aggregation: CompValue): super(Sum, self).__init__(aggregation) self.value = 0 - self.datatype = None + self.datatype: Optional[str] = None - def update(self, row, aggregator): + def update(self, row: FrozenBindings, aggregator: "Aggregator") -> None: try: value = _eval(self.expr, row) dt = self.datatype if dt is None: dt = value.datatype else: - dt = type_promotion(dt, value.datatype) + # type error: Argument 1 to "type_promotion" has incompatible type "str"; expected "URIRef" + dt = type_promotion(dt, value.datatype) # type: ignore[arg-type] self.datatype = dt self.value = sum(type_safe_numbers(self.value, numeric(value))) if self.distinct: @@ -98,18 +135,18 @@ def update(self, row, aggregator): # skip UNDEF pass - def get_value(self): + def get_value(self) -> Literal: return Literal(self.value, datatype=self.datatype) class Average(Accumulator): - def __init__(self, aggregation): + def __init__(self, aggregation: CompValue): super(Average, self).__init__(aggregation) self.counter = 0 self.sum = 0 - self.datatype = None + self.datatype: Optional[str] = None - def update(self, row, aggregator): + def update(self, row: FrozenBindings, aggregator: "Aggregator") -> None: try: value = _eval(self.expr, row) dt = self.datatype @@ -117,7 +154,8 @@ def update(self, row, aggregator): if dt is None: dt = value.datatype else: - dt = type_promotion(dt, value.datatype) + # type error: Argument 1 to "type_promotion" has incompatible type "str"; expected "URIRef" + dt = type_promotion(dt, value.datatype) # type: ignore[arg-type] self.datatype = dt if self.distinct: self.seen.add(value) @@ -128,7 +166,7 @@ def update(self, row, aggregator): except SPARQLTypeError: pass - def get_value(self): + def get_value(self) -> Literal: if self.counter == 0: return Literal(0) if self.datatype in (XSD.float, XSD.double): @@ -140,18 +178,20 @@ def get_value(self): class Extremum(Accumulator): """abstract base class for Minimum and Maximum""" - def __init__(self, aggregation): + def __init__(self, aggregation: CompValue): + self.compare: Callable[[Any, Any], Any] super(Extremum, self).__init__(aggregation) - self.value = None + self.value: Any = None # DISTINCT would not change the value for MIN or MAX - self.use_row = self.dont_care + # type error: Cannot assign to a method + self.use_row = self.dont_care # type: ignore[assignment] - def set_value(self, bindings): + def set_value(self, bindings: MutableMapping[Variable, Identifier]) -> None: if self.value is not None: # simply do not set if self.value is still None bindings[self.var] = Literal(self.value) - def update(self, row, aggregator): + def update(self, row: FrozenBindings, aggregator: "Aggregator") -> None: try: if self.value is None: self.value = _eval(self.expr, row) @@ -165,13 +205,16 @@ def update(self, row, aggregator): pass +_ValueT = TypeVar("_ValueT", Variable, BNode, URIRef, Literal) + + class Minimum(Extremum): - def compare(self, val1, val2): + def compare(self, val1: _ValueT, val2: _ValueT) -> _ValueT: return min(val1, val2, key=_val) class Maximum(Extremum): - def compare(self, val1, val2): + def compare(self, val1: _ValueT, val2: _ValueT) -> _ValueT: return max(val1, val2, key=_val) @@ -183,7 +226,7 @@ def __init__(self, aggregation): # DISTINCT would not change the value self.use_row = self.dont_care - def update(self, row, aggregator): + def update(self, row: FrozenBindings, aggregator: "Aggregator") -> None: try: # set the value now aggregator.bindings[self.var] = _eval(self.expr, row) @@ -192,7 +235,7 @@ def update(self, row, aggregator): except NotBoundError: pass - def get_value(self): + def get_value(self) -> None: # set None if no value was set return None @@ -204,7 +247,7 @@ def __init__(self, aggregation): self.value = [] self.separator = aggregation.separator or " " - def update(self, row, aggregator): + def update(self, row: FrozenBindings, aggregator: "Aggregator") -> None: try: value = _eval(self.expr, row) # skip UNDEF @@ -221,7 +264,7 @@ def update(self, row, aggregator): except NotBoundError: pass - def get_value(self): + def get_value(self) -> Literal: return Literal(self.separator.join(str(v) for v in self.value)) @@ -238,16 +281,16 @@ class Aggregator(object): "Aggregate_GroupConcat": GroupConcat, } - def __init__(self, aggregations): - self.bindings = {} - self.accumulators = {} + def __init__(self, aggregations: List[CompValue]): + self.bindings: Dict[Variable, Identifier] = {} + self.accumulators: Dict[str, Accumulator] = {} for a in aggregations: accumulator_class = self.accumulator_classes.get(a.name) if accumulator_class is None: raise Exception("Unknown aggregate function " + a.name) self.accumulators[a.res] = accumulator_class(a) - def update(self, row): + def update(self, row: FrozenBindings) -> None: """update all own accumulators""" # SAMPLE accumulators may delete themselves # => iterate over list not generator @@ -256,7 +299,7 @@ def update(self, row): if acc.use_row(row): acc.update(row, self) - def get_bindings(self): + def get_bindings(self) -> Mapping[Variable, Identifier]: """calculate and set last values""" for acc in self.accumulators.values(): acc.set_value(self.bindings) diff --git a/rdflib/plugins/sparql/datatypes.py b/rdflib/plugins/sparql/datatypes.py index 115a953b6..2f60fe428 100644 --- a/rdflib/plugins/sparql/datatypes.py +++ b/rdflib/plugins/sparql/datatypes.py @@ -1,10 +1,17 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Dict, List, Optional, Set + """ Utility functions for supporting the XML Schema Datatypes hierarchy """ -from rdflib import XSD +from rdflib.namespace import XSD + +if TYPE_CHECKING: + from rdflib.term import URIRef -XSD_DTs = set( +XSD_DTs: Set[URIRef] = set( ( XSD.integer, XSD.decimal, @@ -35,7 +42,7 @@ XSD_Duration_DTs = set((XSD.duration, XSD.dayTimeDuration, XSD.yearMonthDuration)) -_sub_types = { +_sub_types: Dict[URIRef, List[URIRef]] = { XSD.integer: [ XSD.nonPositiveInteger, XSD.negativeInteger, @@ -52,13 +59,13 @@ ], } -_super_types = {} +_super_types: Dict[URIRef, URIRef] = {} for superdt in XSD_DTs: for subdt in _sub_types.get(superdt, []): _super_types[subdt] = superdt # we only care about float, double, integer, decimal -_typePromotionMap = { +_typePromotionMap: Dict[URIRef, Dict[URIRef, URIRef]] = { XSD.float: {XSD.integer: XSD.float, XSD.decimal: XSD.float, XSD.double: XSD.double}, XSD.double: { XSD.integer: XSD.double, @@ -78,7 +85,7 @@ } -def type_promotion(t1, t2): +def type_promotion(t1: URIRef, t2: Optional[URIRef]) -> URIRef: if t2 is None: return t1 t1 = _super_types.get(t1, t1) @@ -86,6 +93,9 @@ def type_promotion(t1, t2): if t1 == t2: return t1 # matching super-types try: + if TYPE_CHECKING: + # type assert because mypy is confused and thinks t2 can be None + assert t2 is not None return _typePromotionMap[t1][t2] except KeyError: raise TypeError("Operators cannot combine datatypes %s and %s" % (t1, t2)) diff --git a/rdflib/plugins/sparql/evaluate.py b/rdflib/plugins/sparql/evaluate.py index 6b8284f96..252c73ba4 100644 --- a/rdflib/plugins/sparql/evaluate.py +++ b/rdflib/plugins/sparql/evaluate.py @@ -1,3 +1,5 @@ +from __future__ import annotations + """ These method recursively evaluate the SPARQL Algebra @@ -18,7 +20,19 @@ import itertools import json as j import re -from typing import Any, Deque, Dict, Generator, Iterable, List, Tuple, Union +from typing import ( + TYPE_CHECKING, + Any, + Deque, + Dict, + Generator, + Iterable, + List, + Mapping, + Optional, + Tuple, + Union, +) from urllib.parse import urlencode from urllib.request import Request, urlopen @@ -46,6 +60,9 @@ ) from rdflib.term import BNode, Identifier, Literal, URIRef, Variable +if TYPE_CHECKING: + from rdflib.paths import Path + _Triple = Tuple[Identifier, Identifier, Identifier] @@ -67,24 +84,28 @@ def evalBGP( _o = ctx[o] # type error: Item "None" of "Optional[Graph]" has no attribute "triples" - for ss, sp, so in ctx.graph.triples((_s, _p, _o)): # type: ignore[union-attr] + # type Argument 1 to "triples" of "Graph" has incompatible type "Tuple[Union[str, Path, None], Union[str, Path, None], Union[str, Path, None]]"; expected "Tuple[Optional[Node], Optional[Node], Optional[Node]]" + for ss, sp, so in ctx.graph.triples((_s, _p, _o)): # type: ignore[union-attr, arg-type] if None in (_s, _p, _o): c = ctx.push() else: c = ctx if _s is None: - c[s] = ss + # type error: Incompatible types in assignment (expression has type "Union[Node, Any]", target has type "Identifier") + c[s] = ss # type: ignore[assignment] try: if _p is None: - c[p] = sp + # type error: Incompatible types in assignment (expression has type "Union[Node, Any]", target has type "Identifier") + c[p] = sp # type: ignore[assignment] except AlreadyBound: continue try: if _o is None: - c[o] = so + # type error: Incompatible types in assignment (expression has type "Union[Node, Any]", target has type "Identifier") + c[o] = so # type: ignore[assignment] except AlreadyBound: continue @@ -198,7 +219,7 @@ def evalGraph( ) ctx = ctx.clone() - graph = ctx[part.term] + graph: Union[str, Path, None, Graph] = ctx[part.term] prev_graph = ctx.graph if graph is None: for graph in ctx.dataset.contexts(): @@ -214,7 +235,10 @@ def evalGraph( yield x else: - c = ctx.pushGraph(ctx.dataset.get_context(graph)) + if TYPE_CHECKING: + assert not isinstance(graph, Graph) + # type error: Argument 1 to "get_context" of "ConjunctiveGraph" has incompatible type "Union[str, Path]"; expected "Union[Node, str, None]" + c = ctx.pushGraph(ctx.dataset.get_context(graph)) # type: ignore[arg-type] for x in evalPart(c, part.p): x.ctx.graph = prev_graph yield x @@ -242,7 +266,7 @@ def evalMultiset(ctx: QueryContext, part: CompValue): return evalPart(ctx, part.p) -def evalPart(ctx: QueryContext, part: CompValue): +def evalPart(ctx: QueryContext, part: CompValue) -> Any: # try custom evaluation functions for name, c in CUSTOM_EVALS.items(): try: @@ -312,7 +336,8 @@ def evalServiceQuery(ctx: QueryContext, part: CompValue): res = {} match = re.match( "^service <(.*)>[ \n]*{(.*)}[ \n]*$", - part.get("service_string", ""), + # type error: Argument 2 to "get" of "CompValue" has incompatible type "str"; expected "bool" [arg-type] + part.get("service_string", ""), # type: ignore[arg-type] re.DOTALL | re.I, ) @@ -372,7 +397,7 @@ def _buildQueryStringForServiceCall(ctx: QueryContext, service_query: str) -> st for p in ctx.prologue.namespace_manager.store.namespaces(): # type: ignore[union-attr] service_query = "PREFIX " + p[0] + ":" + p[1].n3() + " " + service_query # re add the base if one was defined - # type error: Item "None" of "Optional[Prologue]" has no attribute "base" [union-attr] + # type error: Item "None" of "Optional[Prologue]" has no attribute "base" base = ctx.prologue.base # type: ignore[union-attr] if base is not None and len(base) > 0: service_query = "BASE <" + base + "> " + service_query @@ -537,15 +562,17 @@ def evalProject(ctx: QueryContext, project: CompValue): return (row.project(project.PV) for row in res) -def evalSelectQuery(ctx: QueryContext, query: CompValue): - res = {} +def evalSelectQuery( + ctx: QueryContext, query: CompValue +) -> Mapping[str, Union[str, List[Variable], Iterable[FrozenDict]]]: + res: Dict[str, Union[str, List[Variable], Iterable[FrozenDict]]] = {} res["type_"] = "SELECT" res["bindings"] = evalPart(ctx, query.p) res["vars_"] = query.PV return res -def evalAskQuery(ctx: QueryContext, query: CompValue): +def evalAskQuery(ctx: QueryContext, query: CompValue) -> Mapping[str, Union[str, bool]]: res: Dict[str, Union[bool, str]] = {} res["type_"] = "ASK" res["askAnswer"] = False @@ -556,7 +583,9 @@ def evalAskQuery(ctx: QueryContext, query: CompValue): return res -def evalConstructQuery(ctx: QueryContext, query) -> Dict[str, Union[str, Graph]]: +def evalConstructQuery( + ctx: QueryContext, query: CompValue +) -> Mapping[str, Union[str, Graph]]: template = query.template if not template: @@ -610,7 +639,12 @@ def evalDescribeQuery(ctx: QueryContext, query) -> Dict[str, Union[str, Graph]]: return res -def evalQuery(graph: Graph, query: Query, initBindings, base=None): +def evalQuery( + graph: Graph, + query: Query, + initBindings: Mapping[str, Identifier], + base: Optional[str] = None, +) -> Mapping[Any, Any]: initBindings = dict((Variable(k), v) for k, v in initBindings.items()) ctx = QueryContext(graph, initBindings=initBindings) diff --git a/rdflib/plugins/sparql/evalutils.py b/rdflib/plugins/sparql/evalutils.py index b5181dbf3..84c868c94 100644 --- a/rdflib/plugins/sparql/evalutils.py +++ b/rdflib/plugins/sparql/evalutils.py @@ -1,13 +1,37 @@ +from __future__ import annotations + import collections -from typing import Dict, Iterable +from typing import ( + Any, + DefaultDict, + Generator, + Iterable, + Mapping, + Set, + Tuple, + TypeVar, + Union, + overload, +) from rdflib.plugins.sparql.operators import EBV from rdflib.plugins.sparql.parserutils import CompValue, Expr -from rdflib.plugins.sparql.sparql import FrozenDict, NotBoundError, SPARQLError -from rdflib.term import BNode, Literal, URIRef, Variable - - -def _diff(a: Iterable[FrozenDict], b: Iterable[FrozenDict], expr): +from rdflib.plugins.sparql.sparql import ( + FrozenBindings, + FrozenDict, + NotBoundError, + QueryContext, + SPARQLError, +) +from rdflib.term import BNode, Identifier, Literal, URIRef, Variable + +_ContextType = Union[FrozenBindings, QueryContext] +_FrozenDictT = TypeVar("_FrozenDictT", bound=FrozenDict) + + +def _diff( + a: Iterable[_FrozenDictT], b: Iterable[_FrozenDictT], expr +) -> Set[_FrozenDictT]: res = set() for x in a: @@ -17,20 +41,38 @@ def _diff(a: Iterable[FrozenDict], b: Iterable[FrozenDict], expr): return res -def _minus(a: Iterable[FrozenDict], b: Iterable[FrozenDict]): +def _minus( + a: Iterable[_FrozenDictT], b: Iterable[_FrozenDictT] +) -> Generator[_FrozenDictT, None, None]: for x in a: if all((not x.compatible(y)) or x.disjointDomain(y) for y in b): yield x -def _join(a: Iterable[FrozenDict], b: Iterable[Dict]): +@overload +def _join( + a: Iterable[FrozenBindings], b: Iterable[Mapping[Identifier, Identifier]] +) -> Generator[FrozenBindings, None, None]: + ... + + +@overload +def _join( + a: Iterable[FrozenDict], b: Iterable[Mapping[Identifier, Identifier]] +) -> Generator[FrozenDict, None, None]: + ... + + +def _join( + a: Iterable[FrozenDict], b: Iterable[Mapping[Identifier, Identifier]] +) -> Generator[FrozenDict, None, None]: for x in a: for y in b: if x.compatible(y): yield x.merge(y) -def _ebv(expr, ctx): +def _ebv(expr: Union[Literal, Variable, Expr], ctx: FrozenDict) -> bool: """ Return true/false for the given expr Either the expr is itself true/false @@ -48,7 +90,8 @@ def _ebv(expr, ctx): return EBV(expr.eval(ctx)) except SPARQLError: return False # filter error == False - elif isinstance(expr, CompValue): + # type error: Subclass of "Literal" and "CompValue" cannot exist: would have incompatible method signatures + elif isinstance(expr, CompValue): # type: ignore[unreachable] raise Exception("Weird - filter got a CompValue without evalfn! %r" % expr) elif isinstance(expr, Variable): try: @@ -58,7 +101,29 @@ def _ebv(expr, ctx): return False -def _eval(expr, ctx, raise_not_bound_error=True): +@overload +def _eval( + expr: Union[Literal, URIRef], + ctx: FrozenBindings, + raise_not_bound_error: bool = ..., +) -> Union[Literal, URIRef]: + ... + + +@overload +def _eval( + expr: Union[Variable, Expr], + ctx: FrozenBindings, + raise_not_bound_error: bool = ..., +) -> Union[Any, SPARQLError]: + ... + + +def _eval( + expr: Union[Literal, URIRef, Variable, Expr], + ctx: FrozenBindings, + raise_not_bound_error: bool = True, +) -> Any: if isinstance(expr, (Literal, URIRef)): return expr if isinstance(expr, Expr): @@ -71,26 +136,31 @@ def _eval(expr, ctx, raise_not_bound_error=True): raise NotBoundError("Variable %s is not bound" % expr) else: return None - elif isinstance(expr, CompValue): + elif isinstance(expr, CompValue): # type: ignore[unreachable] raise Exception("Weird - _eval got a CompValue without evalfn! %r" % expr) else: raise Exception("Cannot eval thing: %s (%s)" % (expr, type(expr))) -def _filter(a, expr): +def _filter( + a: Iterable[FrozenDict], expr: Union[Literal, Variable, Expr] +) -> Generator[FrozenDict, None, None]: for c in a: if _ebv(expr, c): yield c -def _fillTemplate(template, solution): +def _fillTemplate( + template: Iterable[Tuple[Identifier, Identifier, Identifier]], + solution: _ContextType, +) -> Generator[Tuple[Identifier, Identifier, Identifier], None, None]: """ For construct/deleteWhere and friends Fill a triple template with instantiated variables """ - bnodeMap = collections.defaultdict(BNode) + bnodeMap: DefaultDict[BNode, BNode] = collections.defaultdict(BNode) for t in template: s, p, o = t @@ -107,7 +177,10 @@ def _fillTemplate(template, solution): yield (_s, _p, _o) -def _val(v): +_ValueT = TypeVar("_ValueT", Variable, BNode, URIRef, Literal) + + +def _val(v: _ValueT) -> Tuple[int, _ValueT]: """utilitity for ordering things""" if isinstance(v, Variable): return (0, v) diff --git a/rdflib/plugins/sparql/parser.py b/rdflib/plugins/sparql/parser.py index 302271f7b..455377ed1 100644 --- a/rdflib/plugins/sparql/parser.py +++ b/rdflib/plugins/sparql/parser.py @@ -3,10 +3,11 @@ based on pyparsing """ +from __future__ import annotations import re import sys -from typing import Any, BinaryIO +from typing import Any, BinaryIO, List from typing import Optional as OptionalType from typing import TextIO, Tuple, Union @@ -30,7 +31,7 @@ from rdflib.compat import decodeUnicodeEscape from . import operators as op -from .parserutils import Comp, Param, ParamList +from .parserutils import Comp, CompValue, Param, ParamList # from pyparsing import Keyword as CaseSensitiveKeyword @@ -40,7 +41,7 @@ # ---------------- ACTIONS -def neg(literal) -> rdflib.Literal: +def neg(literal: rdflib.Literal) -> rdflib.Literal: return rdflib.Literal(-literal, datatype=literal.datatype) @@ -52,13 +53,13 @@ def setDataType(terms: Tuple[Any, OptionalType[str]]) -> rdflib.Literal: return rdflib.Literal(terms[0], datatype=terms[1]) -def expandTriples(terms): +def expandTriples(terms: ParseResults) -> List[Any]: """ Expand ; and , syntax for repeat predicates, subjects """ # import pdb; pdb.set_trace() try: - res = [] + res: List[Any] = [] if DEBUG: print("Terms", terms) l_ = len(terms) @@ -105,7 +106,7 @@ def expandTriples(terms): raise -def expandBNodeTriples(terms): +def expandBNodeTriples(terms: ParseResults) -> List[Any]: """ expand [ ?p ?o ] syntax for implicit bnodes """ @@ -122,14 +123,14 @@ def expandBNodeTriples(terms): raise -def expandCollection(terms): +def expandCollection(terms: ParseResults) -> List[List[Any]]: """ expand ( 1 2 3 ) notation for collections """ if DEBUG: print("Collection: ", terms) - res = [] + res: List[Any] = [] other = [] for x in terms: if isinstance(x, list): # is this a [ .. ] ? @@ -1541,7 +1542,7 @@ def parseQuery(q: Union[str, bytes, TextIO, BinaryIO]) -> ParseResults: return Query.parseString(q, parseAll=True) -def parseUpdate(q: Union[str, bytes, TextIO, BinaryIO]): +def parseUpdate(q: Union[str, bytes, TextIO, BinaryIO]) -> CompValue: if hasattr(q, "read"): q = q.read() diff --git a/rdflib/plugins/sparql/parserutils.py b/rdflib/plugins/sparql/parserutils.py index 09f19ff8b..5b3df78be 100644 --- a/rdflib/plugins/sparql/parserutils.py +++ b/rdflib/plugins/sparql/parserutils.py @@ -1,6 +1,18 @@ +from __future__ import annotations + from collections import OrderedDict from types import MethodType -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import ( + TYPE_CHECKING, + Any, + Callable, + List, + Mapping, + Optional, + Tuple, + TypeVar, + Union, +) from pyparsing import ParseResults, TokenConverter, originalTextFor @@ -48,7 +60,7 @@ def value( val: Any, variables: bool = False, errors: bool = False, -): +) -> Any: """ utility function for evaluating something... @@ -95,7 +107,9 @@ class ParamValue(object): All cleverness is in the CompValue """ - def __init__(self, name, tokenList, isList): + def __init__( + self, name: str, tokenList: Union[List[Any], ParseResults], isList: bool + ): self.isList = isList self.name = name if isinstance(tokenList, (list, ParseResults)) and len(tokenList) == 1: @@ -103,7 +117,7 @@ def __init__(self, name, tokenList, isList): self.tokenList = tokenList - def __str__(self): + def __str__(self) -> str: return "Param(%s, %s)" % (self.name, self.tokenList) @@ -114,13 +128,13 @@ class Param(TokenConverter): their values merged in a list """ - def __init__(self, name, expr, isList=False): + def __init__(self, name: str, expr, isList: bool = False): self.isList = isList TokenConverter.__init__(self, expr) self.setName(name) self.addParseAction(self.postParse2) - def postParse2(self, tokenList): + def postParse2(self, tokenList: Union[List[Any], ParseResults]) -> ParamValue: return ParamValue(self.name, tokenList, self.isList) @@ -129,10 +143,13 @@ class ParamList(Param): A shortcut for a Param with isList=True """ - def __init__(self, name, expr): + def __init__(self, name: str, expr): Param.__init__(self, name, expr, True) +_ValT = TypeVar("_ValT") + + class CompValue(OrderedDict): """ @@ -147,16 +164,18 @@ def __init__(self, name: str, **values): self.name = name self.update(values) - def clone(self): + def clone(self) -> CompValue: return CompValue(self.name, **self) - def __str__(self): + def __str__(self) -> str: return self.name + "_" + OrderedDict.__str__(self) - def __repr__(self): + def __repr__(self) -> str: return self.name + "_" + dict.__repr__(self) - def _value(self, val, variables=False, errors=False): + def _value( + self, val: _ValT, variables: bool = False, errors: bool = False + ) -> Union[_ValT, Any]: if self.ctx is not None: return value(self.ctx, val, variables) else: @@ -165,7 +184,9 @@ def _value(self, val, variables=False, errors=False): def __getitem__(self, a): return self._value(OrderedDict.__getitem__(self, a)) - def get(self, a, variables=False, errors=False): + # type error: Signature of "get" incompatible with supertype "dict" + # type error: Signature of "get" incompatible with supertype "Mapping" [override] + def get(self, a, variables: bool = False, errors: bool = False): # type: ignore[override] return self._value(OrderedDict.get(self, a, a), variables, errors) def __getattr__(self, a: str) -> Any: @@ -189,17 +210,23 @@ class Expr(CompValue): A CompValue that is evaluatable """ - def __init__(self, name, evalfn=None, **values): + def __init__( + self, + name: str, + evalfn: Optional[Callable[[Any, Any], Any]] = None, + **values, + ): super(Expr, self).__init__(name, **values) self._evalfn = None if evalfn: self._evalfn = MethodType(evalfn, self) - def eval(self, ctx={}): + def eval(self, ctx: Any = {}) -> Union[SPARQLError, Any]: try: - self.ctx = ctx - return self._evalfn(ctx) + self.ctx: Optional[Union[Mapping, FrozenBindings]] = ctx + # type error: "None" not callable + return self._evalfn(ctx) # type: ignore[misc] except SPARQLError as e: return e finally: @@ -215,13 +242,16 @@ class Comp(TokenConverter): Returns CompValue / Expr objects - depending on whether evalFn is set. """ - def __init__(self, name, expr): + def __init__(self, name: str, expr): self.expr = expr TokenConverter.__init__(self, expr) self.setName(name) - self.evalfn = None + self.evalfn: Optional[Callable[[Any, Any], Any]] = None - def postParse(self, instring, loc, tokenList): + def postParse( + self, instring: str, loc: int, tokenList: ParseResults + ) -> Union[Expr, CompValue]: + res: Union[Expr, CompValue] if self.evalfn: res = Expr(self.name) res._evalfn = MethodType(self.evalfn, res) @@ -248,7 +278,7 @@ def postParse(self, instring, loc, tokenList): # res.update(t) return res - def setEvalFn(self, evalfn): + def setEvalFn(self, evalfn: Callable[[Any, Any], Any]) -> Comp: self.evalfn = evalfn return self diff --git a/rdflib/plugins/sparql/processor.py b/rdflib/plugins/sparql/processor.py index 26a72dd21..e4d83494e 100644 --- a/rdflib/plugins/sparql/processor.py +++ b/rdflib/plugins/sparql/processor.py @@ -4,17 +4,23 @@ These should be automatically registered with RDFLib """ +from __future__ import annotations +from typing import Any, Mapping, Optional, Union +from rdflib.graph import Graph from rdflib.plugins.sparql.algebra import translateQuery, translateUpdate from rdflib.plugins.sparql.evaluate import evalQuery from rdflib.plugins.sparql.parser import parseQuery, parseUpdate -from rdflib.plugins.sparql.sparql import Query +from rdflib.plugins.sparql.sparql import Query, Update from rdflib.plugins.sparql.update import evalUpdate from rdflib.query import Processor, Result, UpdateProcessor +from rdflib.term import Identifier -def prepareQuery(queryString, initNs={}, base=None) -> Query: +def prepareQuery( + queryString: str, initNs: Mapping[str, Any] = {}, base: Optional[str] = None +) -> Query: """ Parse and translate a SPARQL Query """ @@ -23,7 +29,9 @@ def prepareQuery(queryString, initNs={}, base=None) -> Query: return ret -def prepareUpdate(updateString, initNs={}, base=None): +def prepareUpdate( + updateString: str, initNs: Mapping[str, Any] = {}, base: Optional[str] = None +) -> Update: """ Parse and translate a SPARQL Update """ @@ -32,7 +40,13 @@ def prepareUpdate(updateString, initNs={}, base=None): return ret -def processUpdate(graph, updateString, initBindings={}, initNs={}, base=None): +def processUpdate( + graph: Graph, + updateString: str, + initBindings: Mapping[str, Identifier] = {}, + initNs: Mapping[str, Any] = {}, + base: Optional[str] = None, +) -> None: """ Process a SPARQL Update Request returns Nothing on success or raises Exceptions on error @@ -43,10 +57,11 @@ def processUpdate(graph, updateString, initBindings={}, initNs={}, base=None): class SPARQLResult(Result): - def __init__(self, res): + def __init__(self, res: Mapping[str, Any]): Result.__init__(self, res["type_"]) self.vars = res.get("vars_") - self.bindings = res.get("bindings") + # type error: Incompatible types in assignment (expression has type "Optional[Any]", variable has type "MutableSequence[Mapping[Variable, Identifier]]") + self.bindings = res.get("bindings") # type: ignore[assignment] self.askAnswer = res.get("askAnswer") self.graph = res.get("graph") @@ -55,7 +70,12 @@ class SPARQLUpdateProcessor(UpdateProcessor): def __init__(self, graph): self.graph = graph - def update(self, strOrQuery, initBindings={}, initNs={}): + def update( + self, + strOrQuery: Union[str, Update], + initBindings: Mapping[str, Identifier] = {}, + initNs: Mapping[str, Any] = {}, + ) -> None: if isinstance(strOrQuery, str): strOrQuery = translateUpdate(parseUpdate(strOrQuery), initNs=initNs) @@ -66,7 +86,18 @@ class SPARQLProcessor(Processor): def __init__(self, graph): self.graph = graph - def query(self, strOrQuery, initBindings={}, initNs={}, base=None, DEBUG=False): + # NOTE on type error: this is because the super type constructor does not + # accept base argument and thie position of the DEBUG argument is + # different. + # type error: Signature of "query" incompatible with supertype "Processor" + def query( # type: ignore[override] + self, + strOrQuery: Union[str, Query], + initBindings: Mapping[str, Identifier] = {}, + initNs: Mapping[str, Any] = {}, + base: Optional[str] = None, + DEBUG: bool = False, + ) -> Mapping[str, Any]: """ Evaluate a query with the given initial bindings, and initial namespaces. The given base is used to resolve relative URIs in diff --git a/rdflib/plugins/sparql/sparql.py b/rdflib/plugins/sparql/sparql.py index f1b514e57..8f6a002da 100644 --- a/rdflib/plugins/sparql/sparql.py +++ b/rdflib/plugins/sparql/sparql.py @@ -1,8 +1,22 @@ +from __future__ import annotations + import collections import datetime import itertools import typing as t -from typing import Any, Container, Dict, Iterable, List, Optional, Tuple, Union +from typing import ( + TYPE_CHECKING, + Any, + Container, + Dict, + Generator, + Iterable, + List, + Optional, + Tuple, + TypeVar, + Union, +) import isodate @@ -13,6 +27,12 @@ from rdflib.plugins.sparql.parserutils import CompValue from rdflib.term import BNode, Identifier, Literal, Node, URIRef, Variable +if TYPE_CHECKING: + from rdflib.paths import Path + + +_AnyT = TypeVar("_AnyT") + class SPARQLError(Exception): def __init__(self, msg: Optional[str] = None): @@ -80,8 +100,8 @@ def __len__(self) -> int: d = d.outer return i - def __iter__(self): - d = self + def __iter__(self) -> Generator[str, None, None]: + d: Optional[Bindings] = self while d is not None: yield from d._d d = d.outer @@ -196,7 +216,7 @@ def prologue(self) -> Optional["Prologue"]: def forget( self, before: "QueryContext", _except: Optional[Container[Variable]] = None - ): + ) -> FrozenBindings: """ return a frozen dict only of bindings made in self since before @@ -219,7 +239,7 @@ def forget( ), ) - def remember(self, these): + def remember(self, these) -> FrozenBindings: """ return a frozen dict only of bindings in these """ @@ -235,7 +255,7 @@ def __init__( self, graph: Optional[Graph] = None, bindings: Optional[Union[Bindings, FrozenBindings, List[Any]]] = None, - initBindings: Optional[Dict[Variable, Identifier]] = None, + initBindings: Optional[Mapping[str, Identifier]] = None, ): self.initBindings = initBindings self.bindings = Bindings(d=bindings or []) @@ -291,7 +311,7 @@ def dataset(self) -> ConjunctiveGraph: ) return self._dataset - def load(self, source: URIRef, default: bool = False, **kwargs): + def load(self, source: URIRef, default: bool = False, **kwargs: Any) -> None: def _load(graph, source): try: return graph.parse(source, format="turtle", **kwargs) @@ -324,7 +344,7 @@ def _load(graph, source): else: _load(self.dataset, source) - def __getitem__(self, key) -> Any: + def __getitem__(self, key: Union[str, Path]) -> Optional[Union[str, Path]]: # in SPARQL BNodes are just labels if not isinstance(key, (BNode, Variable)): return key @@ -333,7 +353,7 @@ def __getitem__(self, key) -> Any: except KeyError: return None - def get(self, key: Variable, default: Optional[Any] = None): + def get(self, key: str, default: Optional[Any] = None) -> Any: try: return self[key] except KeyError: @@ -350,7 +370,7 @@ def solution(self, vars: Optional[Iterable[Variable]] = None) -> FrozenBindings: else: return FrozenBindings(self, self.bindings.items()) - def __setitem__(self, key: Identifier, value: Identifier) -> None: + def __setitem__(self, key: str, value: str) -> None: if key in self.bindings and self.bindings[key] != value: raise AlreadyBound() diff --git a/rdflib/plugins/sparql/update.py b/rdflib/plugins/sparql/update.py index 887d1addc..9be375bd2 100644 --- a/rdflib/plugins/sparql/update.py +++ b/rdflib/plugins/sparql/update.py @@ -3,29 +3,38 @@ Code for carrying out Update Operations """ +from __future__ import annotations -from rdflib import Graph, Variable +from typing import TYPE_CHECKING, Iterator, Mapping, Optional, Sequence + +from rdflib.graph import Graph from rdflib.plugins.sparql.evaluate import evalBGP, evalPart from rdflib.plugins.sparql.evalutils import _fillTemplate, _join -from rdflib.plugins.sparql.sparql import QueryContext +from rdflib.plugins.sparql.parserutils import CompValue +from rdflib.plugins.sparql.sparql import FrozenDict, QueryContext, Update +from rdflib.term import Identifier, URIRef, Variable -def _graphOrDefault(ctx, g): +def _graphOrDefault(ctx: QueryContext, g: str) -> Optional[Graph]: if g == "DEFAULT": return ctx.graph else: return ctx.dataset.get_context(g) -def _graphAll(ctx, g): +def _graphAll(ctx: QueryContext, g: str) -> Sequence[Graph]: """ return a list of graphs """ if g == "DEFAULT": - return [ctx.graph] + # type error: List item 0 has incompatible type "Optional[Graph]"; expected "Graph" + return [ctx.graph] # type: ignore[list-item] elif g == "NAMED": return [ - c for c in ctx.dataset.contexts() if c.identifier != ctx.graph.identifier + # type error: Item "None" of "Optional[Graph]" has no attribute "identifier" + c + for c in ctx.dataset.contexts() + if c.identifier != ctx.graph.identifier # type: ignore[union-attr] ] elif g == "ALL": return list(ctx.dataset.contexts()) @@ -33,18 +42,21 @@ def _graphAll(ctx, g): return [ctx.dataset.get_context(g)] -def evalLoad(ctx, u): +def evalLoad(ctx: QueryContext, u: CompValue) -> None: """ http://www.w3.org/TR/sparql11-update/#load """ + if TYPE_CHECKING: + assert isinstance(u.iri, URIRef) + if u.graphiri: ctx.load(u.iri, default=False, publicID=u.graphiri) else: ctx.load(u.iri, default=True) -def evalCreate(ctx, u): +def evalCreate(ctx: QueryContext, u: CompValue) -> None: """ http://www.w3.org/TR/sparql11-update/#create """ @@ -54,16 +66,15 @@ def evalCreate(ctx, u): raise Exception("Create not implemented!") -def evalClear(ctx, u): +def evalClear(ctx: QueryContext, u: CompValue) -> None: """ http://www.w3.org/TR/sparql11-update/#clear """ - for g in _graphAll(ctx, u.graphiri): g.remove((None, None, None)) -def evalDrop(ctx, u): +def evalDrop(ctx: QueryContext, u: CompValue) -> None: """ http://www.w3.org/TR/sparql11-update/#drop """ @@ -74,22 +85,22 @@ def evalDrop(ctx, u): evalClear(ctx, u) -def evalInsertData(ctx, u): +def evalInsertData(ctx: QueryContext, u: CompValue) -> None: """ http://www.w3.org/TR/sparql11-update/#insertData """ # add triples g = ctx.graph g += u.triples - # add quads # u.quads is a dict of graphURI=>[triples] for g in u.quads: - cg = ctx.dataset.get_context(g) + # type error: Argument 1 to "get_context" of "ConjunctiveGraph" has incompatible type "Optional[Graph]"; expected "Union[IdentifiedNode, str, None]" + cg = ctx.dataset.get_context(g) # type: ignore[arg-type] cg += u.quads[g] -def evalDeleteData(ctx, u): +def evalDeleteData(ctx: QueryContext, u: CompValue) -> None: """ http://www.w3.org/TR/sparql11-update/#deleteData """ @@ -100,22 +111,24 @@ def evalDeleteData(ctx, u): # remove quads # u.quads is a dict of graphURI=>[triples] for g in u.quads: - cg = ctx.dataset.get_context(g) + # type error: Argument 1 to "get_context" of "ConjunctiveGraph" has incompatible type "Optional[Graph]"; expected "Union[IdentifiedNode, str, None]" + cg = ctx.dataset.get_context(g) # type: ignore[arg-type] cg -= u.quads[g] -def evalDeleteWhere(ctx, u): +def evalDeleteWhere(ctx: QueryContext, u: CompValue) -> None: """ http://www.w3.org/TR/sparql11-update/#deleteWhere """ - res = evalBGP(ctx, u.triples) + res: Iterator[FrozenDict] = evalBGP(ctx, u.triples) for g in u.quads: cg = ctx.dataset.get_context(g) c = ctx.pushGraph(cg) res = _join(res, list(evalBGP(c, u.quads[g]))) - for c in res: + # type error: Incompatible types in assignment (expression has type "FrozenBindings", variable has type "QueryContext") + for c in res: # type: ignore[assignment] g = ctx.graph g -= _fillTemplate(u.triples, c) @@ -124,10 +137,11 @@ def evalDeleteWhere(ctx, u): cg -= _fillTemplate(u.quads[g], c) -def evalModify(ctx, u): +def evalModify(ctx: QueryContext, u: CompValue) -> None: originalctx = ctx # Using replaces the dataset for evaluating the where-clause + dg: Optional[Graph] if u.using: otherDefault = False for d in u.using: @@ -169,21 +183,25 @@ def evalModify(ctx, u): for c in res: dg = ctx.graph if u.delete: - dg -= _fillTemplate(u.delete.triples, c) + # type error: Unsupported left operand type for - ("None") + # type error: Unsupported operand types for - ("Graph" and "Generator[Tuple[Identifier, Identifier, Identifier], None, None]") + dg -= _fillTemplate(u.delete.triples, c) # type: ignore[operator] for g, q in u.delete.quads.items(): cg = ctx.dataset.get_context(c.get(g)) cg -= _fillTemplate(q, c) if u.insert: - dg += _fillTemplate(u.insert.triples, c) + # type error: Unsupported left operand type for + ("None") + # type error: Unsupported operand types for + ("Graph" and "Generator[Tuple[Identifier, Identifier, Identifier], None, None]") + dg += _fillTemplate(u.insert.triples, c) # type: ignore[operator] for g, q in u.insert.quads.items(): cg = ctx.dataset.get_context(c.get(g)) cg += _fillTemplate(q, c) -def evalAdd(ctx, u): +def evalAdd(ctx: QueryContext, u: CompValue) -> None: """ add all triples from src to dst @@ -195,13 +213,15 @@ def evalAdd(ctx, u): srcg = _graphOrDefault(ctx, src) dstg = _graphOrDefault(ctx, dst) - if srcg.identifier == dstg.identifier: + # type error: Item "None" of "Optional[Graph]" has no attribute "identifier" + if srcg.identifier == dstg.identifier: # type: ignore[union-attr] return - dstg += srcg + # type error: Unsupported left operand type for + ("None") + dstg += srcg # type: ignore[operator] -def evalMove(ctx, u): +def evalMove(ctx: QueryContext, u: CompValue) -> None: """ remove all triples from dst @@ -216,20 +236,25 @@ def evalMove(ctx, u): srcg = _graphOrDefault(ctx, src) dstg = _graphOrDefault(ctx, dst) - if srcg.identifier == dstg.identifier: + # type error: Item "None" of "Optional[Graph]" has no attribute "identifier" + if srcg.identifier == dstg.identifier: # type: ignore[union-attr] return - dstg.remove((None, None, None)) + # type error: Item "None" of "Optional[Graph]" has no attribute "remove" + dstg.remove((None, None, None)) # type: ignore[union-attr] - dstg += srcg + # type error: Unsupported left operand type for + ("None") + dstg += srcg # type: ignore[operator] if ctx.dataset.store.graph_aware: - ctx.dataset.store.remove_graph(srcg) + # 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] else: - srcg.remove((None, None, None)) + # type error: Item "None" of "Optional[Graph]" has no attribute "remove" + srcg.remove((None, None, None)) # type: ignore[union-attr] -def evalCopy(ctx, u): +def evalCopy(ctx: QueryContext, u: CompValue) -> None: """ remove all triples from dst @@ -243,15 +268,20 @@ def evalCopy(ctx, u): srcg = _graphOrDefault(ctx, src) dstg = _graphOrDefault(ctx, dst) - if srcg.identifier == dstg.identifier: + # type error: Item "None" of "Optional[Graph]" has no attribute "remove" + if srcg.identifier == dstg.identifier: # type: ignore[union-attr] return - dstg.remove((None, None, None)) + # type error: Item "None" of "Optional[Graph]" has no attribute "remove" + dstg.remove((None, None, None)) # type: ignore[union-attr] - dstg += srcg + # type error: Unsupported left operand type for + ("None") + dstg += srcg # type: ignore[operator] -def evalUpdate(graph, update, initBindings={}): +def evalUpdate( + graph: Graph, update: Update, initBindings: Mapping[str, Identifier] = {} +) -> None: """ http://www.w3.org/TR/sparql11-update/#updateLanguage diff --git a/rdflib/plugins/stores/memory.py b/rdflib/plugins/stores/memory.py index 2e96a6e6b..13c15218a 100644 --- a/rdflib/plugins/stores/memory.py +++ b/rdflib/plugins/stores/memory.py @@ -30,7 +30,7 @@ ) from rdflib.plugins.sparql.sparql import Query, Update from rdflib.query import Result - from rdflib.term import Identifier, URIRef, Variable + from rdflib.term import Identifier, URIRef __all__ = ["SimpleMemory", "Memory"] @@ -246,7 +246,7 @@ def query( # type: ignore[return] self, query: Union["Query", str], initNs: Mapping[str, Any], # noqa: N803 - initBindings: Mapping["Variable", "Identifier"], # noqa: N803 + initBindings: Mapping["str", "Identifier"], # noqa: N803 queryGraph: "str", # noqa: N803 **kwargs: Any, ) -> "Result": @@ -258,7 +258,7 @@ def update( self, update: Union["Update", str], initNs: Mapping[str, Any], # noqa: N803 - initBindings: Mapping["Variable", "Identifier"], # noqa: N803 + initBindings: Mapping["str", "Identifier"], # noqa: N803 queryGraph: "str", # noqa: N803 **kwargs: Any, ) -> None: @@ -722,7 +722,7 @@ def query( # type: ignore[return] self, query: Union["Query", str], initNs: Mapping[str, Any], # noqa: N803 - initBindings: Mapping["Variable", "Identifier"], # noqa: N803 + initBindings: Mapping["str", "Identifier"], # noqa: N803 queryGraph: "str", **kwargs, ) -> "Result": @@ -732,7 +732,7 @@ def update( self, update: Union["Update", Any], initNs: Mapping[str, Any], # noqa: N803 - initBindings: Mapping["Variable", "Identifier"], # noqa: N803 + initBindings: Mapping["str", "Identifier"], # noqa: N803 queryGraph: "str", **kwargs, ) -> None: diff --git a/rdflib/plugins/stores/sparqlstore.py b/rdflib/plugins/stores/sparqlstore.py index a372de794..47bb57f97 100644 --- a/rdflib/plugins/stores/sparqlstore.py +++ b/rdflib/plugins/stores/sparqlstore.py @@ -193,7 +193,7 @@ def update( # type: ignore[override] self, query: Union["Update", str], initNs: Dict[str, Any] = {}, # noqa: N803 - initBindings: Dict["Variable", "Identifier"] = {}, + initBindings: Dict["str", "Identifier"] = {}, queryGraph: "Identifier" = None, DEBUG: bool = False, ) -> None: @@ -222,7 +222,7 @@ def query( # type: ignore[override] self, query: Union["Query", str], initNs: Optional[Mapping[str, Any]] = None, # noqa: N803 - initBindings: Optional[Mapping["Variable", "Identifier"]] = None, + initBindings: Optional[Mapping["str", "Identifier"]] = None, queryGraph: Optional["str"] = None, DEBUG: bool = False, ) -> "Result": @@ -826,7 +826,7 @@ def update( # type: ignore[override] self, query: Union["Update", str], initNs: Dict[str, Any] = {}, # noqa: N803 - initBindings: Dict["Variable", "Identifier"] = {}, + initBindings: Dict["str", "Identifier"] = {}, queryGraph: Optional[str] = None, DEBUG: bool = False, ): diff --git a/rdflib/query.py b/rdflib/query.py index 5211c3263..1cfaa1536 100644 --- a/rdflib/query.py +++ b/rdflib/query.py @@ -57,7 +57,7 @@ def __init__(self, graph: "Graph"): def query( # type: ignore[empty-body] self, strOrQuery: Union[str, "Query"], # noqa: N803 - initBindings: Mapping["Variable", "Identifier"] = {}, # noqa: N803 + initBindings: Mapping["str", "Identifier"] = {}, # noqa: N803 initNs: Mapping[str, Any] = {}, # noqa: N803 DEBUG: bool = False, ) -> Mapping[str, Any]: @@ -83,7 +83,7 @@ def __init__(self, graph: "Graph"): def update( self, strOrQuery: Union[str, "Update"], # noqa: N803 - initBindings: Mapping["Variable", "Identifier"] = {}, # noqa: N803 + initBindings: Mapping["str", "Identifier"] = {}, # noqa: N803 initNs: Mapping[str, Any] = {}, ) -> None: pass diff --git a/rdflib/store.py b/rdflib/store.py index f78441310..ca6f92611 100644 --- a/rdflib/store.py +++ b/rdflib/store.py @@ -31,7 +31,7 @@ ) from rdflib.plugins.sparql.sparql import Query, Update from rdflib.query import Result - from rdflib.term import Identifier, Node, URIRef, Variable + from rdflib.term import Identifier, Node, URIRef """ ============ @@ -392,7 +392,7 @@ def query( self, query: Union["Query", str], initNs: Mapping[str, Any], # noqa: N803 - initBindings: Mapping["Variable", "Identifier"], # noqa: N803 + initBindings: Mapping["str", "Identifier"], # noqa: N803 queryGraph: str, # noqa: N803 **kwargs: Any, ) -> "Result": @@ -415,7 +415,7 @@ def update( self, update: Union["Update", str], initNs: Mapping[str, Any], # noqa: N803 - initBindings: Mapping["Variable", "Identifier"], # noqa: N803 + initBindings: Mapping["str", "Identifier"], # noqa: N803 queryGraph: str, # noqa: N803 **kwargs: Any, ) -> None: