| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- """RDF4J utility functions."""
- from __future__ import annotations
- import io
- import typing as t
- from rdflib.graph import DATASET_DEFAULT_GRAPH_ID, Dataset, Graph
- from rdflib.plugins.sparql.processor import prepareQuery
- from rdflib.term import BNode, IdentifiedNode, URIRef
- if t.TYPE_CHECKING:
- from rdflib.contrib.rdf4j.client import ObjectType, PredicateType, SubjectType
- def build_context_param(
- params: dict[str, str],
- graph_name: IdentifiedNode | t.Iterable[IdentifiedNode] | str | None = None,
- ) -> None:
- """Build the RDF4J http context query parameters dictionary.
- !!! Note
- This mutates the params dictionary key `context`.
- Parameters:
- params: The `httpx.Request` parameter dictionary.
- graph_name: The graph name or iterable of graph names.
- This is the `context` query parameter value.
- """
- if graph_name is not None and isinstance(graph_name, IdentifiedNode):
- if graph_name == DATASET_DEFAULT_GRAPH_ID:
- # Special RDF4J null value for context-less statements.
- params["context"] = "null"
- else:
- params["context"] = graph_name.n3()
- elif graph_name is not None and isinstance(graph_name, str):
- params["context"] = URIRef(graph_name).n3()
- elif graph_name is not None and isinstance(graph_name, t.Iterable):
- # type error: "str" has no attribute "n3"
- graph_names = ",".join([x.n3() for x in graph_name]) # type: ignore[attr-defined]
- params["context"] = graph_names
- def build_spo_param(
- params: dict[str, str],
- subj: SubjectType = None,
- pred: PredicateType = None,
- obj: ObjectType = None,
- ) -> None:
- """Build the RDF4J http subj, predicate, and object query parameters dictionary.
- !!! Note
- This mutates the params dictionary key `subj`, `pred`, and `obj`.
- Parameters:
- params: The `httpx.Request` parameter dictionary.
- subj: The `subj` query parameter value.
- pred: The `pred` query parameter value.
- obj: The `obj` query parameter value.
- """
- if subj is not None:
- params["subj"] = subj.n3()
- if pred is not None:
- params["pred"] = pred.n3()
- if obj is not None:
- params["obj"] = obj.n3()
- def build_infer_param(
- params: dict[str, str],
- infer: bool = True,
- ) -> None:
- """Build the RDF4J http infer query parameters dictionary.
- !!! Note
- This mutates the params dictionary key `infer`.
- Parameters:
- params: The `httpx.Request` parameter dictionary.
- infer: The `infer` query parameter value.
- """
- if not infer:
- params["infer"] = "false"
- def rdf_payload_to_stream(
- data: str | bytes | t.BinaryIO | Graph | Dataset,
- ) -> tuple[t.BinaryIO, bool]:
- """Convert an RDF payload into a file-like object.
- Parameters:
- data: The RDF payload.
- This can be a python `str`, `bytes`, `BinaryIO`, or a
- [`Graph`][rdflib.graph.Graph] or [`Dataset`][rdflib.graph.Dataset].
- Returns:
- A tuple containing the file-like object and a boolean indicating whether the
- immediate caller should close the stream.
- """
- stream: t.BinaryIO
- if isinstance(data, str):
- # Check if it looks like a file path. Assumes file path length is less than 260.
- if "\n" not in data and len(data) < 260:
- try:
- stream = open(data, "rb")
- should_close = True
- except (FileNotFoundError, OSError):
- # Treat as raw string content
- stream = io.BytesIO(data.encode("utf-8"))
- should_close = False
- else:
- # Treat as raw string content
- stream = io.BytesIO(data.encode("utf-8"))
- should_close = False
- elif isinstance(data, bytes):
- stream = io.BytesIO(data)
- should_close = False
- elif isinstance(data, (Graph, Dataset)):
- if data.context_aware:
- stream = io.BytesIO(
- data.serialize(format="application/n-quads", encoding="utf-8")
- )
- else:
- stream = io.BytesIO(
- data.serialize(format="application/n-triples", encoding="utf-8")
- )
- should_close = True
- else:
- # Assume it's already a file-like object
- stream = data
- should_close = False
- return stream, should_close
- def build_sparql_query_accept_header(query: str, headers: dict[str, str]):
- """Build the SPARQL query accept header.
- !!! Note
- This mutates the headers dictionary key `Accept`.
- Parameters:
- query: The SPARQL query.
- """
- prepared_query = prepareQuery(query)
- if prepared_query.algebra.name in ("SelectQuery", "AskQuery"):
- headers["Accept"] = "application/sparql-results+json"
- elif prepared_query.algebra.name in ("ConstructQuery", "DescribeQuery"):
- headers["Accept"] = "application/n-triples"
- else:
- raise ValueError(f"Unsupported query type: {prepared_query.algebra.name}")
- def validate_graph_name(graph_name: URIRef | t.Iterable[URIRef] | str | None):
- if (
- isinstance(graph_name, BNode)
- or isinstance(graph_name, t.Iterable)
- and any(isinstance(x, BNode) for x in graph_name)
- ):
- raise ValueError("Graph name must not be a BNode.")
- def validate_no_bnodes(
- subj: SubjectType,
- pred: PredicateType,
- obj: ObjectType,
- graph_name: URIRef | t.Iterable[URIRef] | str | None,
- ) -> None:
- """Validate that the subject, predicate, and object are not BNodes."""
- if (
- isinstance(subj, BNode)
- or isinstance(pred, BNode)
- or isinstance(obj, BNode)
- or isinstance(graph_name, BNode)
- ):
- raise ValueError(
- "Subject, predicate, and object must not be a BNode: "
- f"{subj}, {pred}, {obj}"
- )
- validate_graph_name(graph_name)
|