RDFLib / pySHACL

A Python validator for SHACL
Apache License 2.0
241 stars 63 forks source link

Use sh:detail for sh:NodeConstraintComponent #185

Closed mgberg closed 1 year ago

mgberg commented 1 year ago

Overview

The SHACL spec states that sh:detail may be used to provide further details about a validation result. Previously, pySHACL would ignore (the word used in the comments) the failures generated for sh:node and create a new validation error such that the details never made it into the final validation report. This attaches those validation results to the newly generated error via sh:detail so they are available for use in the final validation report if it is desired to take advantage of them.

Specifically, this PR contains the following modifications:

Example

Below are examples of how the validation reports are changed by this PR as a diff compared to the report before these modifications are applied. The graph used to generate these reports is the one used in the added test.

Graph-Based Report

Note that some triples in the serialization have been reordered from rdflib's default serialization behavior to make it more readable for the sake of this example.

 @prefix ex: <http://example.org/> .
 @prefix sh: <http://www.w3.org/ns/shacl#> .
 @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

 [] a sh:ValidationReport ;
     sh:conforms false ;
     sh:result [ a sh:ValidationResult ;
             sh:focusNode ex:Person1 ;
-            sh:resultMessage "Value does not conform to Shape ex:Shape1" ;
+            sh:resultMessage "Value does not conform to Shape ex:Shape1. See details for more information." ;
             sh:resultSeverity sh:Violation ;
             sh:sourceConstraintComponent sh:NodeConstraintComponent ;
             sh:sourceShape ex:Person ;
-            sh:value ex:Person1 ] .
+            sh:value ex:Person1 ;
+            sh:detail [ a sh:ValidationResult ;
+                    sh:focusNode ex:Person1 ;
+                    sh:resultMessage "Less than 1 values on ex:Person1->ex:familyName" ;
+                    sh:resultPath ex:familyName ;
+                    sh:resultSeverity sh:Violation ;
+                    sh:sourceConstraintComponent sh:MinCountConstraintComponent ;
+                    sh:sourceShape [ a sh:PropertyShape ;
+                            sh:datatype xsd:string ;
+                            sh:minCount 1 ;
+                            sh:path ex:familyName ] ],
+                [ a sh:ValidationResult ;
+                    sh:focusNode ex:Person1 ;
+                    sh:resultMessage "Value does not conform to Shape ex:Shape2. See details for more information." ;
+                    sh:resultSeverity sh:Violation ;
+                    sh:sourceConstraintComponent sh:NodeConstraintComponent ;
+                    sh:sourceShape ex:Shape1 ;
+                    sh:value ex:Person1 ;
+                    sh:detail [ a sh:ValidationResult ;
+                            sh:focusNode ex:Person1 ;
+                            sh:resultMessage "Less than 1 values on ex:Person1->ex:givenName" ;
+                            sh:resultPath ex:givenName ;
+                            sh:resultSeverity sh:Violation ;
+                            sh:sourceConstraintComponent sh:MinCountConstraintComponent ;
+                            sh:sourceShape [ a sh:PropertyShape ;
+                                    sh:datatype xsd:string ;
+                                    sh:minCount 1 ;
+                                    sh:path ex:givenName ] ] ] ] .

Text-Based Report

 Validation Report
 Conforms: False
 Results (1):
 Constraint Violation in NodeConstraintComponent (http://www.w3.org/ns/shacl#NodeConstraintComponent):
         Severity: sh:Violation
         Source Shape: ex:Person
         Focus Node: ex:Person1
         Value Node: ex:Person1
-        Message: Value does not conform to Shape ex:Shape1
+        Message: Value does not conform to Shape ex:Shape1. See details for more information.
+        Details:
+                Constraint Violation in MinCountConstraintComponent (http://www.w3.org/ns/shacl#MinCountConstraintComponent):
+                        Severity: sh:Violation
+                        Source Shape: [ rdf:type sh:PropertyShape ; sh:datatype xsd:string ; sh:minCount Literal("1", datatype=xsd:integer) ; sh:path ex:familyName ]
+                        Focus Node: ex:Person1
+                        Result Path: ex:familyName
+                        Message: Less than 1 values on ex:Person1->ex:familyName
+                Constraint Violation in NodeConstraintComponent (http://www.w3.org/ns/shacl#NodeConstraintComponent):
+                        Severity: sh:Violation
+                        Source Shape: ex:Shape1
+                        Focus Node: ex:Person1
+                        Value Node: ex:Person1
+                        Message: Value does not conform to Shape ex:Shape2. See details for more information.
+                        Details:
+                                Constraint Violation in MinCountConstraintComponent (http://www.w3.org/ns/shacl#MinCountConstraintComponent):
+                                        Severity: sh:Violation
+                                        Source Shape: [ rdf:type sh:PropertyShape ; sh:datatype xsd:string ; sh:minCount Literal("1", datatype=xsd:integer) ; sh:path ex:givenName ]
+                                        Focus Node: ex:Person1
+                                        Result Path: ex:givenName
+                                        Message: Less than 1 values on ex:Person1->ex:givenName
ashleysommer commented 1 year ago

@mgberg Thank you for this amazing contribution. This is a very well written pull request, shows good understanding of the PySHACL codebase, and the W3C SHACL spec. I'm more than happy to merge this, and put out a new release in the next couple of days with this feature.

Quick note, I noticed you use graph.cbd() in your addition to clean_validation_reports(). This is fine, and is probably the correct tool for the job. cbd() is a (relatively) new feature in RDFLib, and was only released in v6.1.0. I have thus far avoided using it in PySHACL for compatibility reasons. However, PySHACL does now have a requirement on RDFLib v6.2.0+, so that shouldn't be an issue.

ashleysommer commented 1 year ago

@mgberg This is released now in PySHACL v0.23.0