Skip to content

Commit

Permalink
Add typing for rdflib/plugins/sparql
Browse files Browse the repository at this point in the history
This patch adds typing to two files and changes imports so that they are
more specific to the module in which classes are defined.

This patch contains no runtime changes.

I'm adding this to make it easier to spot bugs in new PRs to SPARQL
code.
  • Loading branch information
aucampia committed May 13, 2022
1 parent e5f3f1f commit a1959ca
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 91 deletions.
92 changes: 62 additions & 30 deletions rdflib/plugins/sparql/evaluate.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
import itertools
import json as j
import re
from typing import Any, Deque, Dict, List, Union
from typing import Any, Deque, Dict, Generator, Iterable, List, Tuple, Union
from urllib.parse import urlencode
from urllib.request import Request, urlopen

from pyparsing import ParseException

from rdflib import BNode, Graph, Literal, URIRef, Variable
from rdflib.graph import Graph
from rdflib.plugins.sparql import CUSTOM_EVALS, parser
from rdflib.plugins.sparql.aggregates import Aggregator
from rdflib.plugins.sparql.evalutils import (
Expand All @@ -42,13 +42,19 @@
AlreadyBound,
Bindings,
FrozenBindings,
FrozenDict,
Query,
QueryContext,
SPARQLError,
)
from rdflib.term import Identifier
from rdflib.term import BNode, Identifier, Literal, URIRef, Variable

_Triple = Tuple[Identifier, Identifier, Identifier]

def evalBGP(ctx: QueryContext, bgp: List[Any]):

def evalBGP(
ctx: QueryContext, bgp: List[_Triple]
) -> Generator[FrozenBindings, None, None]:
"""
A basic graph pattern
"""
Expand All @@ -63,7 +69,8 @@ def evalBGP(ctx: QueryContext, bgp: List[Any]):
_p = ctx[p]
_o = ctx[o]

for ss, sp, so in ctx.graph.triples((_s, _p, _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]
if None in (_s, _p, _o):
c = ctx.push()
else:
Expand All @@ -88,7 +95,9 @@ def evalBGP(ctx: QueryContext, bgp: List[Any]):
yield x


def evalExtend(ctx: QueryContext, extend: CompValue):
def evalExtend(
ctx: QueryContext, extend: CompValue
) -> Generator[FrozenBindings, None, None]:
# TODO: Deal with dict returned from evalPart from GROUP BY

for c in evalPart(ctx, extend.p):
Expand All @@ -103,7 +112,9 @@ def evalExtend(ctx: QueryContext, extend: CompValue):
yield c


def evalLazyJoin(ctx: QueryContext, join: CompValue):
def evalLazyJoin(
ctx: QueryContext, join: CompValue
) -> Generator[FrozenBindings, None, None]:
"""
A lazy join will push the variables bound
in the first part to the second part,
Expand All @@ -116,7 +127,7 @@ def evalLazyJoin(ctx: QueryContext, join: CompValue):
yield b.merge(a) # merge, as some bindings may have been forgotten


def evalJoin(ctx: QueryContext, join: CompValue):
def evalJoin(ctx: QueryContext, join: CompValue) -> Generator[FrozenDict, None, None]:

# TODO: Deal with dict returned from evalPart from GROUP BY
# only ever for join.p1
Expand All @@ -129,7 +140,7 @@ def evalJoin(ctx: QueryContext, join: CompValue):
return _join(a, b)


def evalUnion(ctx: QueryContext, union: CompValue):
def evalUnion(ctx: QueryContext, union: CompValue) -> Iterable[FrozenBindings]:
branch1_branch2 = []
for x in evalPart(ctx, union.p1):
branch1_branch2.append(x)
Expand All @@ -138,13 +149,15 @@ def evalUnion(ctx: QueryContext, union: CompValue):
return branch1_branch2


def evalMinus(ctx: QueryContext, minus: CompValue):
def evalMinus(ctx: QueryContext, minus: CompValue) -> Generator[FrozenDict, None, None]:
a = evalPart(ctx, minus.p1)
b = set(evalPart(ctx, minus.p2))
return _minus(a, b)


def evalLeftJoin(ctx: QueryContext, join: CompValue):
def evalLeftJoin(
ctx: QueryContext, join: CompValue
) -> Generator[FrozenBindings, None, None]:
# import pdb; pdb.set_trace()
for a in evalPart(ctx, join.p1):
ok = False
Expand All @@ -168,7 +181,9 @@ def evalLeftJoin(ctx: QueryContext, join: CompValue):
yield a


def evalFilter(ctx: QueryContext, part: CompValue):
def evalFilter(
ctx: QueryContext, part: CompValue
) -> Generator[FrozenBindings, None, None]:
# TODO: Deal with dict returned from evalPart!
for c in evalPart(ctx, part.p):
if _ebv(
Expand All @@ -178,7 +193,9 @@ def evalFilter(ctx: QueryContext, part: CompValue):
yield c


def evalGraph(ctx: QueryContext, part: CompValue):
def evalGraph(
ctx: QueryContext, part: CompValue
) -> Generator[FrozenBindings, None, None]:

if ctx.dataset is None:
raise Exception(
Expand Down Expand Up @@ -211,7 +228,9 @@ def evalGraph(ctx: QueryContext, part: CompValue):
yield x


def evalValues(ctx: QueryContext, part):
def evalValues(
ctx: QueryContext, part: CompValue
) -> Generator[FrozenBindings, None, None]:
for r in part.p.res:
c = ctx.push()
try:
Expand Down Expand Up @@ -337,7 +356,8 @@ def evalServiceQuery(ctx: QueryContext, part):
res = json["results"]["bindings"]
if len(res) > 0:
for r in res:
for bound in _yieldBindingsFromServiceCallResult(ctx, r, variables):
# type error: Argument 2 to "_yieldBindingsFromServiceCallResult" has incompatible type "str"; expected "Dict[str, Dict[str, str]]"
for bound in _yieldBindingsFromServiceCallResult(ctx, r, variables): # type: ignore[arg-type]
yield bound
else:
raise Exception(
Expand All @@ -353,18 +373,20 @@ def evalServiceQuery(ctx: QueryContext, part):
"""


def _buildQueryStringForServiceCall(ctx: QueryContext, match):
def _buildQueryStringForServiceCall(ctx: QueryContext, match: re.Match) -> str:

service_query = match.group(2)
try:
parser.parseQuery(service_query)
except ParseException:
# This could be because we don't have a select around the service call.
service_query = "SELECT REDUCED * WHERE {" + service_query + "}"
for p in ctx.prologue.namespace_manager.store.namespaces():
# type error: Item "None" of "Optional[Prologue]" has no attribute "namespace_manager"
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
base = ctx.prologue.base
# type error: Item "None" of "Optional[Prologue]" has no attribute "base" [union-attr]
base = ctx.prologue.base # type: ignore[union-attr]
if base is not None and len(base) > 0:
service_query = "BASE <" + base + "> " + service_query
sol = ctx.solution()
Expand All @@ -377,7 +399,9 @@ def _buildQueryStringForServiceCall(ctx: QueryContext, match):
return service_query


def _yieldBindingsFromServiceCallResult(ctx: QueryContext, r, variables):
def _yieldBindingsFromServiceCallResult(
ctx: QueryContext, r: Dict[str, Dict[str, str]], variables: List[str]
) -> Generator[FrozenBindings, None, None]:
res_dict: Dict[Variable, Identifier] = {}
for var in variables:
if var in r and r[var]:
Expand All @@ -396,15 +420,17 @@ def _yieldBindingsFromServiceCallResult(ctx: QueryContext, r, variables):
yield FrozenBindings(ctx, res_dict)


def evalGroup(ctx: QueryContext, group):
def evalGroup(ctx: QueryContext, group: CompValue):
"""
http://www.w3.org/TR/sparql11-query/#defn_algGroup
"""
# grouping should be implemented by evalAggregateJoin
return evalPart(ctx, group.p)


def evalAggregateJoin(ctx: QueryContext, agg):
def evalAggregateJoin(
ctx: QueryContext, agg: CompValue
) -> Generator[FrozenBindings, None, None]:
# import pdb ; pdb.set_trace()
p = evalPart(ctx, agg.p)
# p is always a Group, we always get a dict back
Expand Down Expand Up @@ -435,7 +461,9 @@ def evalAggregateJoin(ctx: QueryContext, agg):
yield FrozenBindings(ctx)


def evalOrderBy(ctx: QueryContext, part):
def evalOrderBy(
ctx: QueryContext, part: CompValue
) -> Generator[FrozenBindings, None, None]:

res = evalPart(ctx, part.p)

Expand All @@ -449,7 +477,7 @@ def evalOrderBy(ctx: QueryContext, part):
return res


def evalSlice(ctx: QueryContext, slice):
def evalSlice(ctx: QueryContext, slice: CompValue):
res = evalPart(ctx, slice.p)

return itertools.islice(
Expand All @@ -459,7 +487,9 @@ def evalSlice(ctx: QueryContext, slice):
)


def evalReduced(ctx: QueryContext, part):
def evalReduced(
ctx: QueryContext, part: CompValue
) -> Generator[FrozenBindings, None, None]:
"""apply REDUCED to result
REDUCED is not as strict as DISTINCT, but if the incoming rows were sorted
Expand Down Expand Up @@ -497,7 +527,9 @@ def evalReduced(ctx: QueryContext, part):
mru_queue.appendleft(row)


def evalDistinct(ctx: QueryContext, part):
def evalDistinct(
ctx: QueryContext, part: CompValue
) -> Generator[FrozenBindings, None, None]:
res = evalPart(ctx, part.p)

done = set()
Expand All @@ -507,13 +539,13 @@ def evalDistinct(ctx: QueryContext, part):
done.add(x)


def evalProject(ctx: QueryContext, project):
def evalProject(ctx: QueryContext, project: CompValue):
res = evalPart(ctx, project.p)

return (row.project(project.PV) for row in res)


def evalSelectQuery(ctx: QueryContext, query):
def evalSelectQuery(ctx: QueryContext, query: CompValue):

res = {}
res["type_"] = "SELECT"
Expand All @@ -522,7 +554,7 @@ def evalSelectQuery(ctx: QueryContext, query):
return res


def evalAskQuery(ctx: QueryContext, query):
def evalAskQuery(ctx: QueryContext, query: CompValue):
res: Dict[str, Union[bool, str]] = {}
res["type_"] = "ASK"
res["askAnswer"] = False
Expand All @@ -533,7 +565,7 @@ def evalAskQuery(ctx: QueryContext, query):
return res


def evalConstructQuery(ctx: QueryContext, query):
def evalConstructQuery(ctx: QueryContext, query) -> Dict[str, Union[str, Graph]]:
template = query.template

if not template:
Expand All @@ -552,7 +584,7 @@ def evalConstructQuery(ctx: QueryContext, query):
return res


def evalQuery(graph, query, initBindings, base=None):
def evalQuery(graph: Graph, query: Query, initBindings, base=None):

initBindings = dict((Variable(k), v) for k, v in initBindings.items())

Expand Down

0 comments on commit a1959ca

Please sign in to comment.