evalutils.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. from __future__ import annotations
  2. import collections
  3. from typing import (
  4. Any,
  5. DefaultDict,
  6. Generator,
  7. Iterable,
  8. Mapping,
  9. Set,
  10. Tuple,
  11. TypeVar,
  12. Union,
  13. overload,
  14. )
  15. from rdflib.plugins.sparql.operators import EBV
  16. from rdflib.plugins.sparql.parserutils import CompValue, Expr
  17. from rdflib.plugins.sparql.sparql import (
  18. FrozenBindings,
  19. FrozenDict,
  20. NotBoundError,
  21. QueryContext,
  22. SPARQLError,
  23. )
  24. from rdflib.term import BNode, Identifier, Literal, URIRef, Variable
  25. _ContextType = Union[FrozenBindings, QueryContext]
  26. _FrozenDictT = TypeVar("_FrozenDictT", bound=FrozenDict)
  27. def _diff(
  28. a: Iterable[_FrozenDictT], b: Iterable[_FrozenDictT], expr
  29. ) -> Set[_FrozenDictT]:
  30. res = set()
  31. for x in a:
  32. if all(not x.compatible(y) or not _ebv(expr, x.merge(y)) for y in b):
  33. res.add(x)
  34. return res
  35. def _minus(
  36. a: Iterable[_FrozenDictT], b: Iterable[_FrozenDictT]
  37. ) -> Generator[_FrozenDictT, None, None]:
  38. for x in a:
  39. if all((not x.compatible(y)) or x.disjointDomain(y) for y in b):
  40. yield x
  41. @overload
  42. def _join(
  43. a: Iterable[FrozenBindings], b: Iterable[Mapping[Identifier, Identifier]]
  44. ) -> Generator[FrozenBindings, None, None]: ...
  45. @overload
  46. def _join(
  47. a: Iterable[FrozenDict], b: Iterable[Mapping[Identifier, Identifier]]
  48. ) -> Generator[FrozenDict, None, None]: ...
  49. def _join(
  50. a: Iterable[FrozenDict], b: Iterable[Mapping[Identifier, Identifier]]
  51. ) -> Generator[FrozenDict, None, None]:
  52. for x in a:
  53. for y in b:
  54. if x.compatible(y):
  55. yield x.merge(y)
  56. def _ebv(expr: Union[Literal, Variable, Expr], ctx: FrozenDict) -> bool:
  57. """
  58. Return true/false for the given expr
  59. Either the expr is itself true/false
  60. or evaluates to something, with the given ctx
  61. an error is false
  62. """
  63. try:
  64. return EBV(expr)
  65. except SPARQLError:
  66. pass
  67. if isinstance(expr, Expr):
  68. try:
  69. return EBV(expr.eval(ctx))
  70. except SPARQLError:
  71. return False # filter error == False
  72. # type error: Subclass of "Literal" and "CompValue" cannot exist: would have incompatible method signatures
  73. elif isinstance(expr, CompValue): # type: ignore[unreachable]
  74. raise Exception("Weird - filter got a CompValue without evalfn! %r" % expr)
  75. elif isinstance(expr, Variable):
  76. try:
  77. return EBV(ctx[expr])
  78. except: # noqa: E722
  79. return False
  80. return False
  81. @overload
  82. def _eval(
  83. expr: Union[Literal, URIRef],
  84. ctx: FrozenBindings,
  85. raise_not_bound_error: bool = ...,
  86. ) -> Union[Literal, URIRef]: ...
  87. @overload
  88. def _eval(
  89. expr: Union[Variable, Expr],
  90. ctx: FrozenBindings,
  91. raise_not_bound_error: bool = ...,
  92. ) -> Union[Any, SPARQLError]: ...
  93. def _eval(
  94. expr: Union[Literal, URIRef, Variable, Expr],
  95. ctx: FrozenBindings,
  96. raise_not_bound_error: bool = True,
  97. ) -> Any:
  98. if isinstance(expr, (Literal, URIRef)):
  99. return expr
  100. if isinstance(expr, Expr):
  101. return expr.eval(ctx)
  102. elif isinstance(expr, Variable):
  103. try:
  104. return ctx[expr]
  105. except KeyError:
  106. if raise_not_bound_error:
  107. raise NotBoundError("Variable %s is not bound" % expr)
  108. else:
  109. return None
  110. elif isinstance(expr, CompValue): # type: ignore[unreachable]
  111. raise Exception("Weird - _eval got a CompValue without evalfn! %r" % expr)
  112. else:
  113. raise Exception("Cannot eval thing: %s (%s)" % (expr, type(expr)))
  114. def _filter(
  115. a: Iterable[FrozenDict], expr: Union[Literal, Variable, Expr]
  116. ) -> Generator[FrozenDict, None, None]:
  117. for c in a:
  118. if _ebv(expr, c):
  119. yield c
  120. def _fillTemplate(
  121. template: Iterable[Tuple[Identifier, Identifier, Identifier]],
  122. solution: _ContextType,
  123. ) -> Generator[Tuple[Identifier, Identifier, Identifier], None, None]:
  124. """
  125. For construct/deleteWhere and friends
  126. Fill a triple template with instantiated variables
  127. """
  128. bnodeMap: DefaultDict[BNode, BNode] = collections.defaultdict(BNode)
  129. for t in template:
  130. s, p, o = t
  131. _s = solution.get(s)
  132. _p = solution.get(p)
  133. _o = solution.get(o)
  134. # instantiate new bnodes for each solution
  135. _s, _p, _o = [
  136. bnodeMap[x] if isinstance(x, BNode) else y for x, y in zip(t, (_s, _p, _o))
  137. ]
  138. if _s is not None and _p is not None and _o is not None:
  139. yield (_s, _p, _o)
  140. _ValueT = TypeVar("_ValueT", Variable, BNode, URIRef, Literal)
  141. def _val(v: _ValueT) -> Tuple[int, _ValueT]:
  142. """utilitity for ordering things"""
  143. if isinstance(v, Variable):
  144. return (0, v)
  145. elif isinstance(v, BNode):
  146. return (1, v)
  147. elif isinstance(v, URIRef):
  148. return (2, v)
  149. elif isinstance(v, Literal):
  150. return (3, v)