Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BREAKING CHANGE: Make Literal more standards compliant #2460

Draft
wants to merge 3 commits into
base: 8.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
3 changes: 3 additions & 0 deletions rdflib/plugins/parsers/trig.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ def graph(self, argstr: str, i: int) -> int:

j = i + 1

if self._context is not None:
self.BadSyntax(argstr, i, "Nested graphs are not allowed")

oldParentContext = self._parentContext
self._parentContext = self._context
reason2 = self._reason2
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