operators.py 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255
  1. """
  2. This contains evaluation functions for expressions
  3. They get bound as instances-methods to the CompValue objects from parserutils
  4. using setEvalFn
  5. """
  6. from __future__ import annotations
  7. import datetime as py_datetime # naming conflict with function within this module
  8. import hashlib
  9. import math
  10. import operator as pyop # python operators
  11. import random
  12. import re
  13. import uuid
  14. import warnings
  15. from decimal import ROUND_HALF_DOWN, ROUND_HALF_UP, Decimal, InvalidOperation
  16. from functools import reduce
  17. from typing import Any, Callable, Dict, NoReturn, Optional, Tuple, Union, overload
  18. from urllib.parse import quote
  19. from pyparsing import ParseResults
  20. from rdflib.namespace import RDF, XSD
  21. from rdflib.plugins.sparql.datatypes import (
  22. XSD_DateTime_DTs,
  23. XSD_DTs,
  24. XSD_Duration_DTs,
  25. type_promotion,
  26. )
  27. from rdflib.plugins.sparql.parserutils import CompValue, Expr
  28. from rdflib.plugins.sparql.sparql import (
  29. FrozenBindings,
  30. QueryContext,
  31. SPARQLError,
  32. SPARQLTypeError,
  33. )
  34. from rdflib.term import (
  35. BNode,
  36. IdentifiedNode,
  37. Identifier,
  38. Literal,
  39. Node,
  40. URIRef,
  41. Variable,
  42. )
  43. from rdflib.xsd_datetime import Duration, parse_datetime # type: ignore[attr-defined]
  44. def Builtin_IRI(expr: Expr, ctx: FrozenBindings) -> URIRef:
  45. """
  46. http://www.w3.org/TR/sparql11-query/#func-iri
  47. """
  48. a = expr.arg
  49. if isinstance(a, URIRef):
  50. return a
  51. if isinstance(a, Literal):
  52. # type error: Item "None" of "Optional[Prologue]" has no attribute "absolutize"
  53. # type error: Incompatible return value type (got "Union[CompValue, str, None, Any]", expected "URIRef")
  54. return ctx.prologue.absolutize(URIRef(a)) # type: ignore[union-attr,return-value]
  55. raise SPARQLError("IRI function only accepts URIRefs or Literals/Strings!")
  56. def Builtin_isBLANK(expr: Expr, ctx: FrozenBindings) -> Literal:
  57. return Literal(isinstance(expr.arg, BNode))
  58. def Builtin_isLITERAL(expr, ctx) -> Literal:
  59. return Literal(isinstance(expr.arg, Literal))
  60. def Builtin_isIRI(expr, ctx) -> Literal:
  61. return Literal(isinstance(expr.arg, URIRef))
  62. def Builtin_isNUMERIC(expr, ctx) -> Literal:
  63. try:
  64. numeric(expr.arg)
  65. return Literal(True)
  66. except: # noqa: E722
  67. return Literal(False)
  68. def Builtin_BNODE(expr, ctx) -> BNode:
  69. """
  70. http://www.w3.org/TR/sparql11-query/#func-bnode
  71. """
  72. a = expr.arg
  73. if a is None:
  74. return BNode()
  75. if isinstance(a, Literal):
  76. return ctx.bnodes[a] # defaultdict does the right thing
  77. raise SPARQLError("BNode function only accepts no argument or literal/string")
  78. def Builtin_ABS(expr: Expr, ctx) -> Literal:
  79. """
  80. http://www.w3.org/TR/sparql11-query/#func-abs
  81. """
  82. return Literal(abs(numeric(expr.arg)))
  83. def Builtin_IF(expr: Expr, ctx):
  84. """
  85. http://www.w3.org/TR/sparql11-query/#func-if
  86. """
  87. return expr.arg2 if EBV(expr.arg1) else expr.arg3
  88. def Builtin_RAND(expr: Expr, ctx) -> Literal:
  89. """
  90. http://www.w3.org/TR/sparql11-query/#idp2133952
  91. """
  92. return Literal(random.random())
  93. def Builtin_UUID(expr: Expr, ctx) -> URIRef:
  94. """
  95. http://www.w3.org/TR/sparql11-query/#func-strdt
  96. """
  97. return URIRef(uuid.uuid4().urn)
  98. def Builtin_STRUUID(expr, ctx) -> Literal:
  99. """
  100. http://www.w3.org/TR/sparql11-query/#func-strdt
  101. """
  102. return Literal(str(uuid.uuid4()))
  103. def Builtin_MD5(expr: Expr, ctx) -> Literal:
  104. s = string(expr.arg).encode("utf-8")
  105. return Literal(hashlib.md5(s).hexdigest())
  106. def Builtin_SHA1(expr: Expr, ctx) -> Literal:
  107. s = string(expr.arg).encode("utf-8")
  108. return Literal(hashlib.sha1(s).hexdigest())
  109. def Builtin_SHA256(expr: Expr, ctx) -> Literal:
  110. s = string(expr.arg).encode("utf-8")
  111. return Literal(hashlib.sha256(s).hexdigest())
  112. def Builtin_SHA384(expr: Expr, ctx) -> Literal:
  113. s = string(expr.arg).encode("utf-8")
  114. return Literal(hashlib.sha384(s).hexdigest())
  115. def Builtin_SHA512(expr: Expr, ctx) -> Literal:
  116. s = string(expr.arg).encode("utf-8")
  117. return Literal(hashlib.sha512(s).hexdigest())
  118. def Builtin_COALESCE(expr: Expr, ctx):
  119. """
  120. http://www.w3.org/TR/sparql11-query/#func-coalesce
  121. """
  122. for x in expr.get("arg", variables=True):
  123. if x is not None and not isinstance(x, (SPARQLError, Variable)):
  124. return x
  125. raise SPARQLError("COALESCE got no arguments that did not evaluate to an error")
  126. def Builtin_CEIL(expr: Expr, ctx) -> Literal:
  127. """
  128. http://www.w3.org/TR/sparql11-query/#func-ceil
  129. """
  130. l_ = expr.arg
  131. return Literal(int(math.ceil(numeric(l_))), datatype=l_.datatype)
  132. def Builtin_FLOOR(expr: Expr, ctx) -> Literal:
  133. """
  134. http://www.w3.org/TR/sparql11-query/#func-floor
  135. """
  136. l_ = expr.arg
  137. return Literal(int(math.floor(numeric(l_))), datatype=l_.datatype)
  138. def Builtin_ROUND(expr: Expr, ctx) -> Literal:
  139. """
  140. http://www.w3.org/TR/sparql11-query/#func-round
  141. """
  142. # This used to be just math.bound
  143. # but in py3k bound was changed to
  144. # "round-to-even" behaviour
  145. # this is an ugly work-around
  146. l_ = expr.arg
  147. v = numeric(l_)
  148. v = int(Decimal(v).quantize(1, ROUND_HALF_UP if v > 0 else ROUND_HALF_DOWN))
  149. return Literal(v, datatype=l_.datatype)
  150. def Builtin_REGEX(expr: Expr, ctx) -> Literal:
  151. """
  152. http://www.w3.org/TR/sparql11-query/#func-regex
  153. Invokes the XPath fn:matches function to match text against a regular
  154. expression pattern.
  155. The regular expression language is defined in XQuery 1.0 and XPath 2.0
  156. Functions and Operators section 7.6.1 Regular Expression Syntax
  157. """
  158. text = string(expr.text)
  159. pattern = string(expr.pattern)
  160. flags = expr.flags
  161. cFlag = 0
  162. if flags:
  163. # Maps XPath REGEX flags (http://www.w3.org/TR/xpath-functions/#flags)
  164. # to Python's re flags
  165. flagMap = dict([("i", re.IGNORECASE), ("s", re.DOTALL), ("m", re.MULTILINE)])
  166. cFlag = reduce(pyop.or_, [flagMap.get(f, 0) for f in flags])
  167. return Literal(bool(re.search(str(pattern), text, cFlag)))
  168. def Builtin_REPLACE(expr: Expr, ctx) -> Literal:
  169. """
  170. http://www.w3.org/TR/sparql11-query/#func-substr
  171. """
  172. text = string(expr.arg)
  173. pattern = string(expr.pattern)
  174. replacement = string(expr.replacement)
  175. flags = expr.flags
  176. # python uses \1, xpath/sparql uses $1
  177. # type error: Incompatible types in assignment (expression has type "str", variable has type "Literal")
  178. replacement = re.sub("\\$([0-9]*)", r"\\\1", replacement) # type: ignore[assignment]
  179. cFlag = 0
  180. if flags:
  181. # Maps XPath REGEX flags (http://www.w3.org/TR/xpath-functions/#flags)
  182. # to Python's re flags
  183. flagMap = dict([("i", re.IGNORECASE), ("s", re.DOTALL), ("m", re.MULTILINE)])
  184. cFlag = reduce(pyop.or_, [flagMap.get(f, 0) for f in flags])
  185. # @@FIXME@@ either datatype OR lang, NOT both
  186. return Literal(
  187. re.sub(str(pattern), replacement, text, cFlag),
  188. datatype=text.datatype,
  189. lang=text.language,
  190. )
  191. def Builtin_STRDT(expr: Expr, ctx) -> Literal:
  192. """
  193. http://www.w3.org/TR/sparql11-query/#func-strdt
  194. """
  195. return Literal(str(expr.arg1), datatype=expr.arg2)
  196. def Builtin_STRLANG(expr: Expr, ctx) -> Literal:
  197. """
  198. http://www.w3.org/TR/sparql11-query/#func-strlang
  199. """
  200. s = string(expr.arg1)
  201. if s.language or s.datatype:
  202. raise SPARQLError("STRLANG expects a simple literal")
  203. # TODO: normalisation of lang tag to lower-case
  204. # should probably happen in literal __init__
  205. return Literal(str(s), lang=str(expr.arg2).lower())
  206. def Builtin_CONCAT(expr: Expr, ctx) -> Literal:
  207. """
  208. http://www.w3.org/TR/sparql11-query/#func-concat
  209. """
  210. # dt/lang passed on only if they all match
  211. dt = set(x.datatype for x in expr.arg if isinstance(x, Literal))
  212. # type error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "Set[Optional[str]]")
  213. dt = dt.pop() if len(dt) == 1 else None # type: ignore[assignment]
  214. lang = set(x.language for x in expr.arg if isinstance(x, Literal))
  215. # type error: error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "Set[Optional[str]]")
  216. lang = lang.pop() if len(lang) == 1 else None # type: ignore[assignment]
  217. # NOTE on type errors: this is because same variable is used for two incompatibel types
  218. # type error: Argument "datatype" to "Literal" has incompatible type "Set[Any]"; expected "Optional[str]" [arg-type]
  219. # type error: Argument "lang" to "Literal" has incompatible type "Set[Any]"; expected "Optional[str]"
  220. return Literal("".join(string(x) for x in expr.arg), datatype=dt, lang=lang) # type: ignore[arg-type]
  221. def _compatibleStrings(a: Literal, b: Literal) -> None:
  222. string(a)
  223. string(b)
  224. if b.language and a.language != b.language:
  225. raise SPARQLError("incompatible arguments to str functions")
  226. def Builtin_STRSTARTS(expr: Expr, ctx) -> Literal:
  227. """
  228. http://www.w3.org/TR/sparql11-query/#func-strstarts
  229. """
  230. a = expr.arg1
  231. b = expr.arg2
  232. _compatibleStrings(a, b)
  233. return Literal(a.startswith(b))
  234. def Builtin_STRENDS(expr: Expr, ctx) -> Literal:
  235. """
  236. http://www.w3.org/TR/sparql11-query/#func-strends
  237. """
  238. a = expr.arg1
  239. b = expr.arg2
  240. _compatibleStrings(a, b)
  241. return Literal(a.endswith(b))
  242. def Builtin_STRBEFORE(expr: Expr, ctx) -> Literal:
  243. """
  244. http://www.w3.org/TR/sparql11-query/#func-strbefore
  245. """
  246. a = expr.arg1
  247. b = expr.arg2
  248. _compatibleStrings(a, b)
  249. i = a.find(b)
  250. if i == -1:
  251. return Literal("")
  252. else:
  253. return Literal(a[:i], lang=a.language, datatype=a.datatype)
  254. def Builtin_STRAFTER(expr: Expr, ctx) -> Literal:
  255. """
  256. http://www.w3.org/TR/sparql11-query/#func-strafter
  257. """
  258. a = expr.arg1
  259. b = expr.arg2
  260. _compatibleStrings(a, b)
  261. i = a.find(b)
  262. if i == -1:
  263. return Literal("")
  264. else:
  265. return Literal(a[i + len(b) :], lang=a.language, datatype=a.datatype)
  266. def Builtin_CONTAINS(expr: Expr, ctx) -> Literal:
  267. """
  268. http://www.w3.org/TR/sparql11-query/#func-strcontains
  269. """
  270. a = expr.arg1
  271. b = expr.arg2
  272. _compatibleStrings(a, b)
  273. return Literal(b in a)
  274. def Builtin_ENCODE_FOR_URI(expr: Expr, ctx) -> Literal:
  275. return Literal(quote(string(expr.arg).encode("utf-8"), safe=""))
  276. def Builtin_SUBSTR(expr: Expr, ctx) -> Literal:
  277. """
  278. http://www.w3.org/TR/sparql11-query/#func-substr
  279. """
  280. a = string(expr.arg)
  281. start = numeric(expr.start) - 1
  282. length = expr.length
  283. if length is not None:
  284. length = numeric(length) + start
  285. return Literal(a[start:length], lang=a.language, datatype=a.datatype)
  286. def Builtin_STRLEN(e: Expr, ctx) -> Literal:
  287. l_ = string(e.arg)
  288. return Literal(len(l_))
  289. def Builtin_STR(e: Expr, ctx) -> Literal:
  290. arg = e.arg
  291. if isinstance(arg, SPARQLError):
  292. raise arg
  293. return Literal(str(arg)) # plain literal
  294. def Builtin_LCASE(e: Expr, ctx) -> Literal:
  295. l_ = string(e.arg)
  296. return Literal(l_.lower(), datatype=l_.datatype, lang=l_.language)
  297. def Builtin_LANGMATCHES(e: Expr, ctx) -> Literal:
  298. """
  299. http://www.w3.org/TR/sparql11-query/#func-langMatches
  300. """
  301. langTag = string(e.arg1)
  302. langRange = string(e.arg2)
  303. if str(langTag) == "":
  304. return Literal(False) # nothing matches empty!
  305. return Literal(_lang_range_check(langRange, langTag))
  306. def Builtin_NOW(e: Expr, ctx) -> Literal:
  307. """
  308. http://www.w3.org/TR/sparql11-query/#func-now
  309. """
  310. return Literal(ctx.now)
  311. def Builtin_YEAR(e: Expr, ctx) -> Literal:
  312. d = date(e.arg)
  313. return Literal(d.year)
  314. def Builtin_MONTH(e: Expr, ctx) -> Literal:
  315. d = date(e.arg)
  316. return Literal(d.month)
  317. def Builtin_DAY(e: Expr, ctx) -> Literal:
  318. d = date(e.arg)
  319. return Literal(d.day)
  320. def Builtin_HOURS(e: Expr, ctx) -> Literal:
  321. d = datetime(e.arg)
  322. return Literal(d.hour)
  323. def Builtin_MINUTES(e: Expr, ctx) -> Literal:
  324. d = datetime(e.arg)
  325. return Literal(d.minute)
  326. def Builtin_SECONDS(e: Expr, ctx) -> Literal:
  327. """
  328. http://www.w3.org/TR/sparql11-query/#func-seconds
  329. """
  330. d = datetime(e.arg)
  331. result_value = Decimal(d.second)
  332. if d.microsecond:
  333. result_value += Decimal(d.microsecond) / Decimal(1000000)
  334. return Literal(result_value, datatype=XSD.decimal)
  335. def Builtin_TIMEZONE(e: Expr, ctx) -> Literal:
  336. """
  337. http://www.w3.org/TR/sparql11-query/#func-timezone
  338. Returns:
  339. The timezone part of arg as an xsd:dayTimeDuration.
  340. Raises:
  341. An error if there is no timezone.
  342. """
  343. dt = datetime(e.arg)
  344. if not dt.tzinfo:
  345. raise SPARQLError("datatime has no timezone: %r" % dt)
  346. delta = dt.utcoffset()
  347. # type error: Item "None" of "Optional[timedelta]" has no attribute "days"
  348. d = delta.days # type: ignore[union-attr]
  349. # type error: Item "None" of "Optional[timedelta]" has no attribute "seconds"
  350. s = delta.seconds # type: ignore[union-attr]
  351. neg = ""
  352. if d < 0:
  353. s = -24 * 60 * 60 * d - s
  354. d = 0
  355. neg = "-"
  356. h = s / (60 * 60)
  357. m = (s - h * 60 * 60) / 60
  358. s = s - h * 60 * 60 - m * 60
  359. tzdelta = "%sP%sT%s%s%s" % (
  360. neg,
  361. "%dD" % d if d else "",
  362. "%dH" % h if h else "",
  363. "%dM" % m if m else "",
  364. "%dS" % s if not d and not h and not m else "",
  365. )
  366. return Literal(tzdelta, datatype=XSD.dayTimeDuration)
  367. def Builtin_TZ(e: Expr, ctx) -> Literal:
  368. d = datetime(e.arg)
  369. if not d.tzinfo:
  370. return Literal("")
  371. n = d.tzinfo.tzname(d)
  372. if n is None:
  373. n = ""
  374. elif n == "UTC":
  375. n = "Z"
  376. elif n.startswith("UTC"):
  377. # Replace tzname like "UTC-05:00" with simply "-05:00" to match Jena tz fn
  378. n = n[3:]
  379. return Literal(n)
  380. def Builtin_UCASE(e: Expr, ctx) -> Literal:
  381. l_ = string(e.arg)
  382. return Literal(l_.upper(), datatype=l_.datatype, lang=l_.language)
  383. def Builtin_LANG(e: Expr, ctx) -> Literal:
  384. """http://www.w3.org/TR/sparql11-query/#func-lang
  385. Returns the language tag of ltrl, if it has one. It returns "" if ltrl has
  386. no language tag. Note that the RDF data model does not include literals
  387. with an empty language tag.
  388. """
  389. l_ = literal(e.arg)
  390. return Literal(l_.language or "")
  391. def Builtin_DATATYPE(e: Expr, ctx) -> Optional[str]:
  392. l_ = e.arg
  393. if not isinstance(l_, Literal):
  394. raise SPARQLError("Can only get datatype of literal: %r" % l_)
  395. if l_.language:
  396. return RDF.langString
  397. if not l_.datatype and not l_.language:
  398. return XSD.string
  399. return l_.datatype
  400. def Builtin_sameTerm(e: Expr, ctx) -> Literal:
  401. a = e.arg1
  402. b = e.arg2
  403. return Literal(a == b)
  404. def Builtin_BOUND(e: Expr, ctx) -> Literal:
  405. """
  406. http://www.w3.org/TR/sparql11-query/#func-bound
  407. """
  408. n = e.get("arg", variables=True)
  409. return Literal(not isinstance(n, Variable))
  410. def Builtin_EXISTS(e: Expr, ctx: FrozenBindings) -> Literal:
  411. # damn...
  412. from rdflib.plugins.sparql.evaluate import evalPart
  413. exists = e.name == "Builtin_EXISTS"
  414. # type error: Incompatible types in assignment (expression has type "QueryContext", variable has type "FrozenBindings")
  415. ctx = ctx.ctx.thaw(ctx) # type: ignore[assignment] # hmm
  416. # type error: Argument 1 to "evalPart" has incompatible type "FrozenBindings"; expected "QueryContext"
  417. for x in evalPart(ctx, e.graph): # type: ignore[arg-type]
  418. return Literal(exists)
  419. return Literal(not exists)
  420. _CustomFunction = Callable[[Expr, FrozenBindings], Node]
  421. _CUSTOM_FUNCTIONS: Dict[URIRef, Tuple[_CustomFunction, bool]] = {}
  422. def register_custom_function(
  423. uri: URIRef, func: _CustomFunction, override: bool = False, raw: bool = False
  424. ) -> None:
  425. """Register a custom SPARQL function.
  426. By default, the function will be passed the RDF terms in the argument list.
  427. If raw is True, the function will be passed an Expression and a Context.
  428. The function must return an RDF term, or raise a SparqlError.
  429. """
  430. if not override and uri in _CUSTOM_FUNCTIONS:
  431. raise ValueError("A function is already registered as %s" % uri.n3())
  432. _CUSTOM_FUNCTIONS[uri] = (func, raw)
  433. def custom_function(
  434. uri: URIRef, override: bool = False, raw: bool = False
  435. ) -> Callable[[_CustomFunction], _CustomFunction]:
  436. """
  437. Decorator version of :func:`register_custom_function`.
  438. """
  439. def decorator(func: _CustomFunction) -> _CustomFunction:
  440. register_custom_function(uri, func, override=override, raw=raw)
  441. return func
  442. return decorator
  443. def unregister_custom_function(
  444. uri: URIRef, func: Optional[Callable[..., Any]] = None
  445. ) -> None:
  446. """
  447. The 'func' argument is included for compatibility with existing code.
  448. A previous implementation checked that the function associated with
  449. the given uri was actually 'func', but this is not necessary as the
  450. uri should uniquely identify the function.
  451. """
  452. if _CUSTOM_FUNCTIONS.get(uri):
  453. del _CUSTOM_FUNCTIONS[uri]
  454. else:
  455. warnings.warn("This function is not registered as %s" % uri.n3())
  456. def Function(e: Expr, ctx: FrozenBindings) -> Node:
  457. """
  458. Custom functions and casts
  459. """
  460. pair = _CUSTOM_FUNCTIONS.get(e.iri)
  461. if pair is None:
  462. # no such function is registered
  463. raise SPARQLError("Unknown function %r" % e.iri)
  464. func, raw = pair
  465. if raw:
  466. # function expects expression and context
  467. return func(e, ctx)
  468. else:
  469. # function expects the argument list
  470. try:
  471. return func(*e.expr)
  472. except TypeError as ex:
  473. # wrong argument number
  474. raise SPARQLError(*ex.args)
  475. @custom_function(XSD.string, raw=True)
  476. @custom_function(XSD.dateTime, raw=True)
  477. @custom_function(XSD.float, raw=True)
  478. @custom_function(XSD.double, raw=True)
  479. @custom_function(XSD.decimal, raw=True)
  480. @custom_function(XSD.integer, raw=True)
  481. @custom_function(XSD.boolean, raw=True)
  482. def default_cast(e: Expr, ctx: FrozenBindings) -> Literal: # type: ignore[return]
  483. if not e.expr:
  484. raise SPARQLError("Nothing given to cast.")
  485. if len(e.expr) > 1:
  486. raise SPARQLError("Cannot cast more than one thing!")
  487. x = e.expr[0]
  488. if e.iri == XSD.string:
  489. if isinstance(x, (URIRef, Literal)):
  490. return Literal(x, datatype=XSD.string)
  491. else:
  492. raise SPARQLError("Cannot cast term %r of type %r" % (x, type(x)))
  493. if not isinstance(x, Literal):
  494. raise SPARQLError("Can only cast Literals to non-string data-types")
  495. if x.datatype and not x.datatype in XSD_DTs: # noqa: E713
  496. raise SPARQLError("Cannot cast literal with unknown datatype: %r" % x.datatype)
  497. if e.iri == XSD.dateTime:
  498. if x.datatype and x.datatype not in (XSD.dateTime, XSD.string):
  499. raise SPARQLError("Cannot cast %r to XSD:dateTime" % x.datatype)
  500. try:
  501. return Literal(parse_datetime(x), datatype=e.iri)
  502. except: # noqa: E722
  503. raise SPARQLError("Cannot interpret '%r' as datetime" % x)
  504. if x.datatype == XSD.dateTime:
  505. raise SPARQLError("Cannot cast XSD.dateTime to %r" % e.iri)
  506. if e.iri in (XSD.float, XSD.double):
  507. try:
  508. return Literal(float(x), datatype=e.iri)
  509. except: # noqa: E722
  510. raise SPARQLError("Cannot interpret '%r' as float" % x)
  511. elif e.iri == XSD.decimal:
  512. if "e" in x or "E" in x: # SPARQL/XSD does not allow exponents in decimals
  513. raise SPARQLError("Cannot interpret '%r' as decimal" % x)
  514. try:
  515. return Literal(Decimal(x), datatype=e.iri)
  516. except: # noqa: E722
  517. raise SPARQLError("Cannot interpret '%r' as decimal" % x)
  518. elif e.iri == XSD.integer:
  519. try:
  520. return Literal(int(x), datatype=XSD.integer)
  521. except: # noqa: E722
  522. raise SPARQLError("Cannot interpret '%r' as int" % x)
  523. elif e.iri == XSD.boolean:
  524. # # I would argue that any number is True...
  525. # try:
  526. # return Literal(bool(int(x)), datatype=XSD.boolean)
  527. # except:
  528. if x.lower() in ("1", "true"):
  529. return Literal(True)
  530. if x.lower() in ("0", "false"):
  531. return Literal(False)
  532. raise SPARQLError("Cannot interpret '%r' as bool" % x)
  533. def UnaryNot(expr: Expr, ctx: FrozenBindings) -> Literal:
  534. return Literal(not EBV(expr.expr))
  535. def UnaryMinus(expr: Expr, ctx: FrozenBindings) -> Literal:
  536. return Literal(-numeric(expr.expr))
  537. def UnaryPlus(expr: Expr, ctx: FrozenBindings) -> Literal:
  538. return Literal(+numeric(expr.expr))
  539. def MultiplicativeExpression(
  540. e: Expr, ctx: Union[QueryContext, FrozenBindings]
  541. ) -> Literal:
  542. expr = e.expr
  543. other = e.other
  544. # because of the way the mul-expr production handled operator precedence
  545. # we sometimes have nothing to do
  546. if other is None:
  547. return expr
  548. try:
  549. res: Union[Decimal, float]
  550. res = Decimal(numeric(expr))
  551. for op, f in zip(e.op, other):
  552. f = numeric(f)
  553. if type(f) == float: # noqa: E721
  554. res = float(res)
  555. if op == "*":
  556. res *= f
  557. else:
  558. res /= f
  559. except (InvalidOperation, ZeroDivisionError):
  560. raise SPARQLError("divide by 0")
  561. return Literal(res)
  562. # type error: Missing return statement
  563. def AdditiveExpression(e: Expr, ctx: Union[QueryContext, FrozenBindings]) -> Literal: # type: ignore[return]
  564. expr = e.expr
  565. other = e.other
  566. # because of the way the add-expr production handled operator precedence
  567. # we sometimes have nothing to do
  568. if other is None:
  569. return expr
  570. # handling arithmetic(addition/subtraction) of dateTime, date, time
  571. # and duration datatypes (if any)
  572. if hasattr(expr, "datatype") and (
  573. expr.datatype in XSD_DateTime_DTs or expr.datatype in XSD_Duration_DTs
  574. ):
  575. res = dateTimeObjects(expr)
  576. dt = expr.datatype
  577. for op, term in zip(e.op, other):
  578. # check if operation is datetime,date,time operation over
  579. # another datetime,date,time datatype
  580. if dt in XSD_DateTime_DTs and dt == term.datatype and op == "-":
  581. # checking if there are more than one datetime operands -
  582. # in that case it doesn't make sense for example
  583. # ( dateTime1 - dateTime2 - dateTime3 ) is an invalid operation
  584. if len(other) > 1:
  585. error_message = "Can't evaluate multiple %r arguments"
  586. # type error: Too many arguments for "SPARQLError"
  587. raise SPARQLError(error_message, dt.datatype) # type: ignore[call-arg]
  588. else:
  589. n = dateTimeObjects(term)
  590. res = calculateDuration(res, n)
  591. return res
  592. # datetime,date,time +/- duration,dayTimeDuration,yearMonthDuration
  593. elif dt in XSD_DateTime_DTs and term.datatype in XSD_Duration_DTs:
  594. n = dateTimeObjects(term)
  595. res = calculateFinalDateTime(res, dt, n, term.datatype, op)
  596. return res
  597. # duration,dayTimeDuration,yearMonthDuration + datetime,date,time
  598. elif dt in XSD_Duration_DTs and term.datatype in XSD_DateTime_DTs:
  599. if op == "+":
  600. n = dateTimeObjects(term)
  601. res = calculateFinalDateTime(res, dt, n, term.datatype, op)
  602. return res
  603. # rest are invalid types
  604. else:
  605. raise SPARQLError("Invalid DateTime Operations")
  606. # handling arithmetic(addition/subtraction) of numeric datatypes (if any)
  607. else:
  608. res = numeric(expr)
  609. dt = expr.datatype
  610. for op, term in zip(e.op, other):
  611. n = numeric(term)
  612. if isinstance(n, Decimal) and isinstance(res, float):
  613. n = float(n)
  614. if isinstance(n, float) and isinstance(res, Decimal):
  615. res = float(res)
  616. dt = type_promotion(dt, term.datatype)
  617. if op == "+":
  618. res += n
  619. else:
  620. res -= n
  621. return Literal(res, datatype=dt)
  622. def RelationalExpression(e: Expr, ctx: Union[QueryContext, FrozenBindings]) -> Literal:
  623. expr = e.expr
  624. other = e.other
  625. op = e.op
  626. # because of the way the add-expr production handled operator precedence
  627. # we sometimes have nothing to do
  628. if other is None:
  629. return expr
  630. ops = dict(
  631. [
  632. (">", lambda x, y: x.__gt__(y)),
  633. ("<", lambda x, y: x.__lt__(y)),
  634. ("=", lambda x, y: x.eq(y)),
  635. ("!=", lambda x, y: x.neq(y)),
  636. (">=", lambda x, y: x.__ge__(y)),
  637. ("<=", lambda x, y: x.__le__(y)),
  638. ("IN", pyop.contains),
  639. ("NOT IN", lambda x, y: not pyop.contains(x, y)),
  640. ]
  641. )
  642. if op in ("IN", "NOT IN"):
  643. res = op == "NOT IN"
  644. error: Union[bool, SPARQLError] = False
  645. if other == RDF.nil:
  646. other = []
  647. for x in other:
  648. try:
  649. if x == expr:
  650. return Literal(True ^ res)
  651. except SPARQLError as e:
  652. error = e
  653. if not error:
  654. return Literal(False ^ res)
  655. else:
  656. # Note on type error: this is because variable is Union[bool, SPARQLError]
  657. # type error: Exception must be derived from BaseException
  658. raise error # type: ignore[misc]
  659. if op not in ("=", "!=", "IN", "NOT IN"):
  660. if not isinstance(expr, Literal):
  661. raise SPARQLError(
  662. "Compare other than =, != of non-literals is an error: %r" % expr
  663. )
  664. if not isinstance(other, Literal):
  665. raise SPARQLError(
  666. "Compare other than =, != of non-literals is an error: %r" % other
  667. )
  668. else:
  669. if not isinstance(expr, Node):
  670. raise SPARQLError("I cannot compare this non-node: %r" % expr)
  671. if not isinstance(other, Node):
  672. raise SPARQLError("I cannot compare this non-node: %r" % other)
  673. if isinstance(expr, Literal) and isinstance(other, Literal):
  674. if (
  675. expr.datatype is not None
  676. and expr.datatype not in XSD_DTs
  677. and other.datatype is not None
  678. and other.datatype not in XSD_DTs
  679. ):
  680. # in SPARQL for non-XSD DT Literals we can only do =,!=
  681. if op not in ("=", "!="):
  682. raise SPARQLError("Can only do =,!= comparisons of non-XSD Literals")
  683. try:
  684. r = ops[op](expr, other)
  685. if r == NotImplemented:
  686. raise SPARQLError("Error when comparing")
  687. except TypeError as te:
  688. raise SPARQLError(*te.args)
  689. return Literal(r)
  690. def ConditionalAndExpression(
  691. e: Expr, ctx: Union[QueryContext, FrozenBindings]
  692. ) -> Literal:
  693. # TODO: handle returned errors
  694. expr = e.expr
  695. other = e.other
  696. # because of the way the add-expr production handled operator precedence
  697. # we sometimes have nothing to do
  698. if other is None:
  699. return expr
  700. return Literal(all(EBV(x) for x in [expr] + other))
  701. def ConditionalOrExpression(
  702. e: Expr, ctx: Union[QueryContext, FrozenBindings]
  703. ) -> Literal:
  704. # TODO: handle errors
  705. expr = e.expr
  706. other = e.other
  707. # because of the way the add-expr production handled operator precedence
  708. # we sometimes have nothing to do
  709. if other is None:
  710. return expr
  711. # A logical-or that encounters an error on only one branch
  712. # will return TRUE if the other branch is TRUE and an error
  713. # if the other branch is FALSE.
  714. error = None
  715. for x in [expr] + other:
  716. try:
  717. if EBV(x):
  718. return Literal(True)
  719. except SPARQLError as e:
  720. error = e
  721. if error:
  722. raise error
  723. return Literal(False)
  724. def not_(arg) -> Expr:
  725. return Expr("UnaryNot", UnaryNot, expr=arg)
  726. def and_(*args: Expr) -> Expr:
  727. if len(args) == 1:
  728. return args[0]
  729. return Expr(
  730. "ConditionalAndExpression",
  731. ConditionalAndExpression,
  732. expr=args[0],
  733. other=list(args[1:]),
  734. )
  735. TrueFilter = Expr("TrueFilter", lambda _1, _2: Literal(True))
  736. def simplify(expr: Any) -> Any:
  737. if isinstance(expr, ParseResults) and len(expr) == 1:
  738. return simplify(expr[0])
  739. if isinstance(expr, (list, ParseResults)):
  740. return list(map(simplify, expr))
  741. if not isinstance(expr, CompValue):
  742. return expr
  743. if expr.name.endswith("Expression"):
  744. if expr.other is None:
  745. return simplify(expr.expr)
  746. for k in expr.keys():
  747. expr[k] = simplify(expr[k])
  748. # expr['expr']=simplify(expr.expr)
  749. # expr['other']=simplify(expr.other)
  750. return expr
  751. def literal(s: Literal) -> Literal:
  752. if not isinstance(s, Literal):
  753. raise SPARQLError("Non-literal passed as string: %r" % s)
  754. return s
  755. def datetime(e: Literal) -> py_datetime.datetime:
  756. if not isinstance(e, Literal):
  757. raise SPARQLError("Non-literal passed as datetime: %r" % e)
  758. if not e.datatype == XSD.dateTime:
  759. raise SPARQLError("Literal with wrong datatype passed as datetime: %r" % e)
  760. return e.toPython()
  761. def date(e: Literal) -> py_datetime.date:
  762. if not isinstance(e, Literal):
  763. raise SPARQLError("Non-literal passed as date: %r" % e)
  764. if e.datatype not in (XSD.date, XSD.dateTime):
  765. raise SPARQLError("Literal with wrong datatype passed as date: %r" % e)
  766. result = e.toPython()
  767. if isinstance(result, py_datetime.datetime):
  768. return result.date()
  769. return result
  770. def string(s: Literal) -> Literal:
  771. """
  772. Make sure the passed thing is a string literal
  773. i.e. plain literal, xsd:string literal or lang-tagged literal
  774. """
  775. if not isinstance(s, Literal):
  776. raise SPARQLError("Non-literal passes as string: %r" % s)
  777. if s.datatype and s.datatype != XSD.string:
  778. raise SPARQLError("Non-string datatype-literal passes as string: %r" % s)
  779. return s
  780. def numeric(expr: Literal) -> Any:
  781. """
  782. return a number from a literal
  783. http://www.w3.org/TR/xpath20/#promotion
  784. or TypeError
  785. """
  786. if not isinstance(expr, Literal):
  787. raise SPARQLTypeError("%r is not a literal!" % expr)
  788. if expr.datatype not in (
  789. XSD.float,
  790. XSD.double,
  791. XSD.decimal,
  792. XSD.integer,
  793. XSD.nonPositiveInteger,
  794. XSD.negativeInteger,
  795. XSD.nonNegativeInteger,
  796. XSD.positiveInteger,
  797. XSD.unsignedLong,
  798. XSD.unsignedInt,
  799. XSD.unsignedShort,
  800. XSD.unsignedByte,
  801. XSD.long,
  802. XSD.int,
  803. XSD.short,
  804. XSD.byte,
  805. ):
  806. raise SPARQLTypeError("%r does not have a numeric datatype!" % expr)
  807. return expr.toPython()
  808. def dateTimeObjects(expr: Literal) -> Any:
  809. """
  810. return a dataTime/date/time/duration/dayTimeDuration/yearMonthDuration python objects from a literal
  811. """
  812. return expr.toPython()
  813. # type error: Missing return statement
  814. def isCompatibleDateTimeDatatype( # type: ignore[return]
  815. obj1: Union[py_datetime.date, py_datetime.datetime],
  816. dt1: URIRef,
  817. obj2: Union[Duration, py_datetime.timedelta],
  818. dt2: URIRef,
  819. ) -> bool:
  820. """
  821. Returns a boolean indicating if first object is compatible
  822. with operation(+/-) over second object.
  823. """
  824. if dt1 == XSD.date:
  825. if dt2 == XSD.yearMonthDuration:
  826. return True
  827. elif dt2 == XSD.dayTimeDuration or dt2 == XSD.Duration:
  828. # checking if the dayTimeDuration has no Time Component
  829. # else it won't be compatible with Date Literal
  830. if "T" in str(obj2):
  831. return False
  832. else:
  833. return True
  834. if dt1 == XSD.time:
  835. if dt2 == XSD.yearMonthDuration:
  836. return False
  837. elif dt2 == XSD.dayTimeDuration or dt2 == XSD.Duration:
  838. # checking if the dayTimeDuration has no Date Component
  839. # (by checking if the format is "PT...." )
  840. # else it won't be compatible with Time Literal
  841. if "T" == str(obj2)[1]:
  842. return True
  843. else:
  844. return False
  845. if dt1 == XSD.dateTime:
  846. # compatible with all
  847. return True
  848. def calculateDuration(
  849. obj1: Union[py_datetime.date, py_datetime.datetime],
  850. obj2: Union[py_datetime.date, py_datetime.datetime],
  851. ) -> Literal:
  852. """
  853. returns the duration Literal between two datetime
  854. """
  855. date1 = obj1
  856. date2 = obj2
  857. # type error: No overload variant of "__sub__" of "datetime" matches argument type "date"
  858. difference = date1 - date2 # type: ignore[operator]
  859. return Literal(difference, datatype=XSD.duration)
  860. def calculateFinalDateTime(
  861. obj1: Union[py_datetime.date, py_datetime.datetime],
  862. dt1: URIRef,
  863. obj2: Union[Duration, py_datetime.timedelta],
  864. dt2: URIRef,
  865. operation: str,
  866. ) -> Literal:
  867. """
  868. Calculates the final dateTime/date/time resultant after addition/
  869. subtraction of duration/dayTimeDuration/yearMonthDuration
  870. """
  871. # checking compatibility of datatypes (duration types and date/time/dateTime)
  872. if isCompatibleDateTimeDatatype(obj1, dt1, obj2, dt2):
  873. # proceed
  874. if operation == "-":
  875. ans = obj1 - obj2
  876. return Literal(ans, datatype=dt1)
  877. else:
  878. ans = obj1 + obj2
  879. return Literal(ans, datatype=dt1)
  880. else:
  881. raise SPARQLError("Incompatible Data types to DateTime Operations")
  882. @overload
  883. def EBV(rt: Literal) -> bool: ...
  884. @overload
  885. def EBV(rt: Union[Variable, IdentifiedNode, SPARQLError, Expr]) -> NoReturn: ...
  886. @overload
  887. def EBV(rt: Union[Identifier, SPARQLError, Expr]) -> Union[bool, NoReturn]: ...
  888. def EBV(rt: Union[Identifier, SPARQLError, Expr]) -> bool:
  889. """Effective Boolean Value (EBV)
  890. * If the argument is a typed literal with a datatype of xsd:boolean,
  891. the EBV is the value of that argument.
  892. * If the argument is a plain literal or a typed literal with a
  893. datatype of xsd:string, the EBV is false if the operand value
  894. has zero length; otherwise the EBV is true.
  895. * If the argument is a numeric type or a typed literal with a datatype
  896. derived from a numeric type, the EBV is false if the operand value is
  897. NaN or is numerically equal to zero; otherwise the EBV is true.
  898. * All other arguments, including unbound arguments, produce a type error.
  899. """
  900. if isinstance(rt, Literal):
  901. if rt.datatype == XSD.boolean:
  902. return rt.toPython()
  903. elif rt.datatype == XSD.string or rt.datatype is None:
  904. return len(rt) > 0
  905. else:
  906. pyRT = rt.toPython()
  907. if isinstance(pyRT, Literal):
  908. # Type error, see: http://www.w3.org/TR/rdf-sparql-query/#ebv
  909. raise SPARQLTypeError(
  910. "http://www.w3.org/TR/rdf-sparql-query/#ebv - ' + \
  911. 'Could not determine the EBV for : %r"
  912. % rt
  913. )
  914. else:
  915. return bool(pyRT)
  916. else:
  917. raise SPARQLTypeError(
  918. "http://www.w3.org/TR/rdf-sparql-query/#ebv - ' + \
  919. 'Only literals have Boolean values! %r"
  920. % rt
  921. )
  922. def _lang_range_check(range: Literal, lang: Literal) -> bool:
  923. """
  924. Implementation of the extended filtering algorithm, as defined in point
  925. 3.3.2, of [RFC 4647](http://www.rfc-editor.org/rfc/rfc4647.txt), on
  926. matching language ranges and language tags.
  927. Needed to handle the `rdf:PlainLiteral` datatype.
  928. Args:
  929. range: language range
  930. lang: language tag
  931. Author: [Ivan Herman](http://www.w3.org/People/Ivan/)
  932. Taken from [`RDFClosure/RestrictedDatatype.py`](http://dev.w3.org/2004/PythonLib-IH/RDFClosure/RestrictedDatatype.py)
  933. """
  934. def _match(r: str, l_: str) -> bool:
  935. """
  936. Matching of a range and language item: either range is a wildcard
  937. or the two are equal
  938. Args:
  939. r: language range item
  940. l_: language tag item
  941. """
  942. return r == "*" or r == l_
  943. rangeList = range.strip().lower().split("-")
  944. langList = lang.strip().lower().split("-")
  945. if not _match(rangeList[0], langList[0]):
  946. return False
  947. if len(rangeList) > len(langList):
  948. return False
  949. return all(_match(*x) for x in zip(rangeList, langList))