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

Added support for xsd:nonNegativeInteger literals #178

Merged
merged 5 commits into from
Mar 1, 2024
Merged
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
8 changes: 8 additions & 0 deletions examples/workflow-mappings/workflow_mappings.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
"""Workflow example"""

import warnings

from tripper import Triplestore
from tripper.mappings import MappingStep, mapping_routes

warnings.filterwarnings(
action="ignore",
message="Function and module name for function '[^']*' is not provided",
category=UserWarning,
)

ts = Triplestore(backend="rdflib")
EX = ts.bind("ex", "http://example.com/generic_example#")
ts.add_function(
Expand Down
53 changes: 52 additions & 1 deletion tests/test_literals.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ def test_string_lang() -> None:
assert literal.n3() == '"Hello world!"@en'


def test_cannot_combine_datatype_and_lang() -> None:
"""Test that combining datatype and lang raises TypeError."""
import pytest

from tripper import XSD, Literal

with pytest.raises(TypeError):
Literal("1", datatype=XSD.string, lang="en")


def test_en() -> None:
"""Test creating a string literal through `en()`."""
from tripper.utils import en
Expand All @@ -38,6 +48,8 @@ def test_en() -> None:

def test_integer() -> None:
"""Test creating an integer literal."""
import pytest

from tripper import XSD, Literal

literal = Literal(42)
Expand All @@ -46,6 +58,32 @@ def test_integer() -> None:
assert literal.value == 42
assert literal.n3() == f'"42"^^{XSD.integer}'

with pytest.raises(TypeError):
Literal(42, datatype=XSD.nonPositiveInteger)

with pytest.raises(TypeError):
Literal(-42, datatype=XSD.nonNegativeInteger)

with pytest.raises(TypeError):
Literal(-42, datatype=XSD.unsignedInt)


def test_hexbinary() -> None:
"""Test creating hexbinary literal."""
from tripper import XSD, Literal

literal = Literal(b"hi")
assert literal.lang is None
assert literal.datatype == XSD.hexBinary
assert literal.value == "6869"
assert literal.n3() == f'"6869"^^{XSD.hexBinary}'

literal = Literal("1f", datatype=XSD.hexBinary)
assert literal.lang is None
assert literal.datatype == XSD.hexBinary
assert literal.value == "1f"
assert literal.n3() == f'"1f"^^{XSD.hexBinary}'


def test_float_through_datatype() -> None:
"""Test creating a float literal from an int through datatype."""
Expand All @@ -58,6 +96,16 @@ def test_float_through_datatype() -> None:
assert literal.n3() == f'"42"^^{XSD.double}'


def test_repr() -> None:
"""Test repr formatting."""
from tripper import Literal

literal = Literal(42, datatype=float)
assert repr(literal) == (
"Literal('42', datatype='http://www.w3.org/2001/XMLSchema#double')"
)


def test_split_iri() -> None:
"""Test parse n3-encoded literal value."""
from tripper import DCTERMS, RDFS
Expand All @@ -76,6 +124,8 @@ def test_parse_literal() -> None:
"""Test parse n3-encoded literal value."""
from datetime import datetime

import pytest

from tripper import RDF, XSD, Literal
from tripper.utils import parse_literal

Expand Down Expand Up @@ -150,7 +200,8 @@ def test_parse_literal() -> None:
assert literal.lang is None
assert literal.datatype == RDF.HTML

literal = parse_literal('"value"^^http://example.com/vocab#mytype')
with pytest.warns(UserWarning, match="unknown datatype"):
literal = parse_literal('"value"^^http://example.com/vocab#mytype')
assert literal.value == "value"
assert literal.lang is None
assert literal.datatype == "http://example.com/vocab#mytype"
Expand Down
10 changes: 6 additions & 4 deletions tests/test_triplestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ def test_backend_rdflib_graph(
assert graph.value(URIRef(":Nils"), URIRef(RDF.type)) == URIRef(FAM.Father)


@pytest.mark.filterwarnings("ignore:adding new IRI to ontology:UserWarning")
def test_backend_ontopy(get_ontology_path: "Callable[[str], Path]") -> None:
"""Specifically test the ontopy backend Triplestore.

Expand Down Expand Up @@ -245,10 +246,11 @@ def test_backend_sparqlwrapper() -> None:
"csiro_international-chronostratigraphic-chart_geologic-"
"time-scale-2020",
)
for s, p, o in ts.triples(predicate=SKOS.notation):
assert s
assert p
assert o
with pytest.warns(UserWarning, match="unknown datatype"):
for s, p, o in ts.triples(predicate=SKOS.notation):
assert s
assert p
assert o


@pytest.mark.skip(
Expand Down
41 changes: 34 additions & 7 deletions tripper/literal.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
XSD.int,
XSD.short,
XSD.long,
XSD.nonNegativeInteger,
XSD.nonPositiveInteger,
XSD.negativeInteger,
XSD.unsignedInt,
Expand Down Expand Up @@ -134,6 +135,38 @@
# TODO:
# - XSD.base64Binary
# - XSD.byte, XSD.unsignedByte

# Some consistency checking
if (
string.datatype == XSD.nonPositiveInteger
and int(value) > 0 # type: ignore[arg-type]
):
raise TypeError(f"not a xsd:nonPositiveInteger: '{string}'")
if (
string.datatype == XSD.nonNegativeInteger
and int(value) < 0 # type: ignore[arg-type]
):
raise TypeError(f"not a xsd:nonNegativeInteger: '{string}'")
if (
string.datatype
in (
XSD.unsignedInt,
XSD.unsignedShort,
XSD.unsignedLong,
XSD.unsignedByte,
)
and int(value) < 0 # type: ignore[arg-type]
):
raise TypeError(f"not an unsigned integer: '{string}'")

# Check if datatype is known
if string.datatype and not any(
string.datatype in types for types in cls.datatypes.values()
):
warnings.warn(
f"unknown datatype: {string.datatype} - assuming xsd:string"
)

return string

def __hash__(self):
Expand Down Expand Up @@ -174,14 +207,8 @@
value = int(self)
elif self.datatype in self.datatypes[float]:
value = float(self)
elif self.datatype == XSD.hexBinary:
value = self.encode()
elif self.datatype == XSD.dateTime:
value = datetime.fromisoformat(self)
elif self.datatype and self.datatype not in self.datatypes[str]:
warnings.warn(
f"unknown datatype: {self.datatype} - assuming string"
)

return value

Expand All @@ -191,4 +218,4 @@
return f'"{self}"@{self.lang}'
if self.datatype:
return f'"{self}"^^{self.datatype}'
return f'"{self}"'
assert False, "should never be reached" # nosec

Check warning on line 221 in tripper/literal.py

View check run for this annotation

Codecov / codecov/patch

tripper/literal.py#L221

Added line #L221 was not covered by tests
Loading