Metadata-Version: 2.4
Name: shacl-sparql-extensions-engine
Version: 1.1.0
Summary: Reference implementation plumbing for the SHACL 1.2 SPARQL Extensions spec, built on pyshacl + rdflib
Project-URL: Homepage, https://gitlab.com/riphixel/shacl-sparql-extensions-engine
Project-URL: Repository, https://gitlab.com/riphixel/shacl-sparql-extensions-engine
Project-URL: Issues, https://gitlab.com/riphixel/shacl-sparql-extensions-engine/-/issues
Author-email: Pierre Gronlier <pierre@gronlier.fr>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: rdf,rdf-star,semantic-web,shacl,shapes,sparql,validation
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup
Requires-Python: >=3.14
Requires-Dist: pyoxigraph>=0.5.9
Requires-Dist: pyshacl>=0.40.1
Requires-Dist: rdflib>=7.6.0
Description-Content-Type: text/markdown

# SHACL + SPARQL engine

[![pipeline status](https://gitlab.com/riphixel/shacl-sparql-extensions-engine/badges/main/pipeline.svg)](https://gitlab.com/riphixel/shacl-sparql-extensions-engine/-/commits/main)
[![coverage report](https://gitlab.com/riphixel/shacl-sparql-extensions-engine/badges/main/coverage.svg)](https://gitlab.com/riphixel/shacl-sparql-extensions-engine/-/commits/main)
[![Latest Release](https://gitlab.com/riphixel/shacl-sparql-extensions-engine/-/badges/release.svg)](https://gitlab.com/riphixel/shacl-sparql-extensions-engine/-/releases)

The goal of this project is to provide a reference implementation of the [SHACL 1.2 SPARQL extension](https://www.w3.org/TR/shacl12-sparql/).

## Framework

The code uses:

- Python 3.14+.
- [pySHACL](https://github.com/rdflib/pyshacl)
- [RDFLib](https://rdflib.readthedocs.io/en/stable/intro_to_sparql/)
- [pyoxigraph](https://pyoxigraph.readthedocs.io/), scoped to the one gap rdflib can't cover (RDF-star) — see [./docs/rdf-star-backend.md](./docs/rdf-star-backend.md)

## Structure

- `shacl_sparql.validate` — typed wrapper around `pyshacl.validate()`.
- `shacl_sparql.conformance` — static, non-executing scan of a shapes graph for SHACL 1.2 constructs the default pipeline can't evaluate, plus Appendix A pre-binding lint.
- `shacl_sparql.extensions` — opt-in closures for the SHACL 1.2 gaps that neither pyshacl nor rdflib implement natively (§5 result annotations, §6 node expressions, §7 custom functions, §8 layer/runOnce/expectedPredicate, RDF-star `sh:tempTriple`). Nothing here is wired into `validate()` automatically; `conformance.check()` names the construct so you know which extension to reach for.

## Hello world

```python
from shacl_sparql.validate import validate

# str sources are treated as file paths/URLs by rdflib; use bytes for inline Turtle.
shapes = b"""
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix ex: <http://example.com/ns#> .

ex:PersonShape
    a sh:NodeShape ;
    sh:targetClass ex:Person ;
    sh:property [
        sh:path ex:name ;
        sh:minCount 1 ;
    ] .
"""

data = b"""
@prefix ex: <http://example.com/ns#> .

ex:Alice a ex:Person ;
    ex:name "Alice" .

ex:Bob a ex:Person .
"""

outcome = validate(data, shapes)
print(outcome.conforms)  # False — ex:Bob has no ex:name
print(outcome.report_text)
```

## Hello world: SHACL + SPARQL constraint

A `sh:sparql` constraint runs an arbitrary SPARQL `SELECT` per focus node —
any row it returns is a violation. Here it checks that `ex:endDate` comes
after `ex:startDate`:

```python
from shacl_sparql.validate import validate

shapes = b"""
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix ex: <http://example.com/ns#> .

ex:EventShape
    a sh:NodeShape ;
    sh:targetClass ex:Event ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "ex:endDate must be after ex:startDate." ;
        sh:select \"\"\"
            PREFIX ex: <http://example.com/ns#>
            SELECT $this
            WHERE {
                $this ex:startDate ?start ;
                      ex:endDate ?end .
                FILTER (?end <= ?start)
            }
        \"\"\" ;
    ] .
"""

data = b"""
@prefix ex: <http://example.com/ns#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:Conference a ex:Event ;
    ex:startDate "2026-09-01"^^xsd:date ;
    ex:endDate "2026-09-03"^^xsd:date .

ex:Workshop a ex:Event ;
    ex:startDate "2026-10-05"^^xsd:date ;
    ex:endDate "2026-10-01"^^xsd:date .
"""

outcome = validate(data, shapes)
print(outcome.conforms)  # False — ex:Workshop's endDate precedes its startDate
print(outcome.report_text)
```

## Conformance

Every push runs the vendored [W3C SHACL 1.2 SPARQL Extensions test suite](https://github.com/w3c/data-shapes)
and publishes the results as an [EARL](https://www.w3.org/TR/EARL10-Schema/) report —
the standard machine-readable format for implementation conformance, so results
can be aggregated alongside other implementations. Latest report (always
up to date with `main`): [w3c-earl-report.ttl](https://gitlab.com/riphixel/shacl-sparql-extensions-engine/-/jobs/artifacts/main/raw/reports/w3c-earl-report.ttl?job=w3c-earl-report).

## Specification

Specification reference: <https://www.w3.org/TR/shacl12-sparql/>

