Skip to content

Commit

Permalink
BREAKING CHANGE: Make Literal more standards compliant
Browse files Browse the repository at this point in the history
TBD

checkpoint

aucampia/20230609T2355-literal_datatype: checkpoint 20230817T000443
  • Loading branch information
aucampia committed Aug 16, 2023
1 parent 04f679a commit 376c034
Show file tree
Hide file tree
Showing 21 changed files with 316 additions and 220 deletions.
15 changes: 6 additions & 9 deletions rdflib/plugins/parsers/notation3.py
Original file line number Diff line number Diff line change
Expand Up @@ -1506,7 +1506,7 @@ def object(

j, s = self.strconst(argstr, i, delim)

res.append(self._store.newLiteral(s)) # type: ignore[call-arg] # TODO FIXME
res.append(Literal(s))
return j
else:
return -1
Expand Down Expand Up @@ -1570,11 +1570,14 @@ def nodeOrLiteral(self, argstr: str, i: int, res: MutableSequence[Any]) -> int:
i = m.end()
lang = argstr[j + 1 : i]
j = i
if argstr[j : j + 2] == "^^":
res.append(Literal(s, lang=lang))
elif argstr[j : j + 2] == "^^":
res2: typing.List[Any] = []
j = self.uri_ref2(argstr, j + 2, res2) # Read datatype URI
dt = res2[0]
res.append(self._store.newLiteral(s, dt, lang))
res.append(Literal(s, datatype=dt))
else:
res.append(Literal(s))
return j
else:
return -1
Expand Down Expand Up @@ -1852,12 +1855,6 @@ def newBlankNode(
bn = BNode(str(arg[0]).split("#").pop().replace("_", "b"))
return bn

def newLiteral(self, s: str, dt: Optional[URIRef], lang: Optional[str]) -> Literal:
if dt:
return Literal(s, datatype=dt)
else:
return Literal(s, lang=lang)

def newList(self, n: typing.List[Any], f: Optional[Formula]) -> IdentifiedNode:
nil = self.newSymbol("http://www.w3.org/1999/02/22-rdf-syntax-ns#nil")
if not n:
Expand Down
15 changes: 7 additions & 8 deletions rdflib/plugins/parsers/ntriples.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,18 +316,17 @@ def nodeid(

def literal(self) -> Union["te.Literal[False]", Literal]:
if self.peek('"'):
lit, lang, dtype = self.eat(r_literal).groups()
if lang:
lit, lang, dtype = self.eat(r_literal).groups(default=None)
if TYPE_CHECKING:
# The pattern does not allow lit to be none.
assert lit is not None
if lang is not None:
lang = lang
else:
lang = None
if dtype:
if dtype is not None:
dtype = unquote(dtype)
dtype = uriquote(dtype)
dtype = URI(dtype)
else:
dtype = None
if lang and dtype:
if lang is not None and dtype is not None:
raise ParseError("Can't have both a language and a datatype")
lit = unquote(lit)
return Literal(lit, lang, dtype)
Expand Down
4 changes: 3 additions & 1 deletion rdflib/plugins/sparql/aggregates.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ def update(self, row: FrozenBindings, aggregator: "Aggregator") -> None:
pass

def get_value(self) -> Literal:
return Literal(self.value, datatype=self.datatype)
return Literal(
self.value, datatype=XSD.integer if self.datatype is None else self.datatype
)


class Average(Accumulator):
Expand Down
24 changes: 12 additions & 12 deletions rdflib/plugins/sparql/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,18 +290,18 @@ def Builtin_CONCAT(expr: Expr, ctx) -> Literal:

# dt/lang passed on only if they all match

dt = set(x.datatype for x in expr.arg if isinstance(x, Literal))
# type error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "Set[Optional[str]]")
dt = dt.pop() if len(dt) == 1 else None # type: ignore[assignment]

lang = set(x.language for x in expr.arg if isinstance(x, Literal))
# type error: error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "Set[Optional[str]]")
lang = lang.pop() if len(lang) == 1 else None # type: ignore[assignment]

# NOTE on type errors: this is because same variable is used for two incompatibel types
# type error: Argument "datatype" to "Literal" has incompatible type "Set[Any]"; expected "Optional[str]" [arg-type]
# type error: Argument "lang" to "Literal" has incompatible type "Set[Any]"; expected "Optional[str]"
return Literal("".join(string(x) for x in expr.arg), datatype=dt, lang=lang) # type: ignore[arg-type]
args = [x for x in expr.arg if isinstance(x, Literal)]

if not args:
return Literal("")

dt_set = set(x.datatype for x in args)
dt = dt_set.pop()

lang_set = set(x.language for x in args)
lang = lang_set.pop()

return Literal("".join(string(x) for x in expr.arg), datatype=dt, lang=lang)


def _compatibleStrings(a: Literal, b: Literal) -> None:
Expand Down
8 changes: 3 additions & 5 deletions rdflib/plugins/sparql/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@

import re
import sys
from typing import Any, BinaryIO, List
from typing import Optional as OptionalType
from typing import TextIO, Tuple, Union
from typing import Any, BinaryIO, List, TextIO, Tuple, Union

from pyparsing import CaselessKeyword as Keyword # watch out :)
from pyparsing import (
Expand Down Expand Up @@ -45,11 +43,11 @@ def neg(literal: rdflib.Literal) -> rdflib.Literal:
return rdflib.Literal(-literal, datatype=literal.datatype)


def setLanguage(terms: Tuple[Any, OptionalType[str]]) -> rdflib.Literal:
def setLanguage(terms: Tuple[Any, str]) -> rdflib.Literal:
return rdflib.Literal(terms[0], lang=terms[1])


def setDataType(terms: Tuple[Any, OptionalType[str]]) -> rdflib.Literal:
def setDataType(terms: Tuple[Any, str]) -> rdflib.Literal:
return rdflib.Literal(terms[0], datatype=terms[1])


Expand Down
5 changes: 4 additions & 1 deletion rdflib/plugins/sparql/results/jsonresults.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
from typing import IO, Any, Dict, Mapping, MutableSequence, Optional

from rdflib.namespace import XSD
from rdflib.query import Result, ResultException, ResultParser, ResultSerializer
from rdflib.term import BNode, Identifier, Literal, URIRef, Variable

Expand Down Expand Up @@ -101,7 +102,9 @@ def parseJsonTerm(d: Dict[str, str]) -> Identifier:
if t == "uri":
return URIRef(d["value"])
elif t == "literal":
return Literal(d["value"], datatype=d.get("datatype"), lang=d.get("xml:lang"))
return Literal(
d["value"], datatype=d.get("datatype", XSD.string), lang=d.get("xml:lang")
)
elif t == "typed-literal":
return Literal(d["value"], datatype=URIRef(d["datatype"]))
elif t == "bnode":
Expand Down
3 changes: 2 additions & 1 deletion rdflib/plugins/sparql/results/xmlresults.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from xml.sax.saxutils import XMLGenerator
from xml.sax.xmlreader import AttributesNSImpl

from rdflib.namespace import XSD
from rdflib.query import Result, ResultException, ResultParser, ResultSerializer
from rdflib.term import BNode, Identifier, Literal, URIRef, Variable

Expand Down Expand Up @@ -131,7 +132,7 @@ def parseTerm(element: xml_etree.Element) -> Union[URIRef, Literal, BNode]:
if tag == RESULTS_NS_ET + "literal":
if text is None:
text = ""
datatype = None
datatype = XSD.string
lang = None
if element.get("datatype", None):
# type error: Argument 1 to "URIRef" has incompatible type "Optional[str]"; expected "str"
Expand Down
Loading

0 comments on commit 376c034

Please sign in to comment.