From fa4dcc01533e2cb21b4e1ee72b83128c5c2f0e30 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Fri, 29 May 2020 18:30:17 +0530 Subject: [PATCH 1/7] Add support to use SELECT query variables in sh:message --- pyshacl/constraints/constraint_component.py | 25 ++++++++++++++++--- .../sparql/sparql_based_constraints.py | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/pyshacl/constraints/constraint_component.py b/pyshacl/constraints/constraint_component.py index e811a3a..80f3aec 100644 --- a/pyshacl/constraints/constraint_component.py +++ b/pyshacl/constraints/constraint_component.py @@ -3,6 +3,7 @@ https://www.w3.org/TR/shacl/#core-components-value-type """ import abc +import re from rdflib import BNode from pyshacl.consts import * from pyshacl.rdfutil import stringify_node @@ -68,7 +69,7 @@ def recursion_triggers(self, _evaluation_path): return maybe_recursive - def make_v_result_description(self, datagraph, focus_node, severity, value_node=None, result_path=None, + def make_v_result_description(self, datagraph, focus_node, severity, value_node=None, result_path=None, bound_vars=None, constraint_component=None, source_constraint=None, extra_messages=None): """ :param datagraph: @@ -78,6 +79,7 @@ def make_v_result_description(self, datagraph, focus_node, severity, value_node= :param value_node: :type value_node: rdflib.term.Identifier | None :param result_path: + :param bound_vars: :param constraint_component: :param source_constraint: :param extra_messages: @@ -113,11 +115,17 @@ def make_v_result_description(self, datagraph, focus_node, severity, value_node= if m in self.shape.message: continue if isinstance(m, rdflib.Literal): - desc += "\tMessage: {}\n".format(str(m.value)) + msg = str(m.value) + if bound_vars is not None: + msg = self._format_sparql_based_result_message(msg, bound_vars) + desc += "\tMessage: {}\n".format(msg) else: # pragma: no cover desc += "\tMessage: {}\n".format(str(m)) for m in self.shape.message: if isinstance(m, rdflib.Literal): + msg = str(m.value) + if bound_vars is not None: + msg = self._format_sparql_based_result_message(msg, bound_vars) desc += "\tMessage: {}\n".format(str(m.value)) else: # pragma: no cover desc += "\tMessage: {}\n".format(str(m)) @@ -125,7 +133,7 @@ def make_v_result_description(self, datagraph, focus_node, severity, value_node= def make_v_result(self, datagraph, focus_node, value_node=None, result_path=None, constraint_component=None, source_constraint=None, - extra_messages=None): + extra_messages=None, bound_vars=None): """ :param datagraph: :type datagraph: rdflib.Graph | rdflib.Dataset @@ -137,6 +145,7 @@ def make_v_result(self, datagraph, focus_node, value_node=None, result_path=None :param constraint_component: :param source_constraint: :param extra_messages: + :param bound_vars: :return: """ constraint_component = constraint_component or self.shacl_constraint_class() @@ -151,7 +160,7 @@ def make_v_result(self, datagraph, focus_node, value_node=None, result_path=None desc = self.make_v_result_description( datagraph, focus_node, severity, value_node, result_path=result_path, constraint_component=constraint_component, - source_constraint=source_constraint, extra_messages=extra_messages) + source_constraint=source_constraint, extra_messages=extra_messages, bound_vars=bound_vars) if value_node: r_triples.append((r_node, SH_value, ('D', value_node))) if result_path is None and self.shape.is_property_shape: @@ -169,3 +178,11 @@ def make_v_result(self, datagraph, focus_node, value_node=None, result_path=None r_triples.append((r_node, SH_resultMessage, m)) self.shape.logger.debug(desc) return desc, r_node, r_triples + + def _format_sparql_based_result_message(self, msg, bound_vars): + if bound_vars is None: + return msg + msg = re.sub('{[?$]this}', str(bound_vars[0]), msg) + msg = re.sub('{[?$]path}', str(bound_vars[1]), msg) + msg = re.sub('{[?$]value}', str(bound_vars[2]), msg) + return msg diff --git a/pyshacl/constraints/sparql/sparql_based_constraints.py b/pyshacl/constraints/sparql/sparql_based_constraints.py index 3f19f95..7586d19 100644 --- a/pyshacl/constraints/sparql/sparql_based_constraints.py +++ b/pyshacl/constraints/sparql/sparql_based_constraints.py @@ -140,7 +140,7 @@ def _evaluate_sparql_constraint(self, sparql_constraint, v = result_val rept = self.make_v_result( target_graph, t or f, value_node=v, result_path=p, - **rept_kwargs) + (t, p, v), **rept_kwargs) else: rept = self.make_v_result( target_graph, f, value_node=v, **rept_kwargs) From f9ea7ab9ac3bcd4614ac03d4c890f41b055723b3 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Fri, 29 May 2020 18:45:11 +0530 Subject: [PATCH 2/7] Fix variable ordering --- pyshacl/constraints/constraint_component.py | 6 +++--- pyshacl/constraints/sparql/sparql_based_constraints.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyshacl/constraints/constraint_component.py b/pyshacl/constraints/constraint_component.py index 80f3aec..c56f4c3 100644 --- a/pyshacl/constraints/constraint_component.py +++ b/pyshacl/constraints/constraint_component.py @@ -131,9 +131,9 @@ def make_v_result_description(self, datagraph, focus_node, severity, value_node= desc += "\tMessage: {}\n".format(str(m)) return desc - def make_v_result(self, datagraph, focus_node, value_node=None, result_path=None, + def make_v_result(self, datagraph, focus_node, value_node=None, result_path=None, , bound_vars=None constraint_component=None, source_constraint=None, - extra_messages=None, bound_vars=None): + extra_messages=None): """ :param datagraph: :type datagraph: rdflib.Graph | rdflib.Dataset @@ -142,10 +142,10 @@ def make_v_result(self, datagraph, focus_node, value_node=None, result_path=None :param value_node: :type value_node: rdflib.term.Identifier | None :param result_path: + :param bound_vars: :param constraint_component: :param source_constraint: :param extra_messages: - :param bound_vars: :return: """ constraint_component = constraint_component or self.shacl_constraint_class() diff --git a/pyshacl/constraints/sparql/sparql_based_constraints.py b/pyshacl/constraints/sparql/sparql_based_constraints.py index 7586d19..878e7f8 100644 --- a/pyshacl/constraints/sparql/sparql_based_constraints.py +++ b/pyshacl/constraints/sparql/sparql_based_constraints.py @@ -140,7 +140,7 @@ def _evaluate_sparql_constraint(self, sparql_constraint, v = result_val rept = self.make_v_result( target_graph, t or f, value_node=v, result_path=p, - (t, p, v), **rept_kwargs) + bound_vars=(t, p, v), **rept_kwargs) else: rept = self.make_v_result( target_graph, f, value_node=v, **rept_kwargs) From 4d956c93083f53289a9617eed64d09440a70c17b Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Fri, 29 May 2020 22:00:01 +0530 Subject: [PATCH 3/7] Fix regex replacement --- pyshacl/constraints/constraint_component.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyshacl/constraints/constraint_component.py b/pyshacl/constraints/constraint_component.py index c56f4c3..7b6f7e3 100644 --- a/pyshacl/constraints/constraint_component.py +++ b/pyshacl/constraints/constraint_component.py @@ -182,7 +182,7 @@ def make_v_result(self, datagraph, focus_node, value_node=None, result_path=None def _format_sparql_based_result_message(self, msg, bound_vars): if bound_vars is None: return msg - msg = re.sub('{[?$]this}', str(bound_vars[0]), msg) - msg = re.sub('{[?$]path}', str(bound_vars[1]), msg) - msg = re.sub('{[?$]value}', str(bound_vars[2]), msg) + msg = re.sub(f'{{[?,$]{{1}}this}}', str(bound_vars[0]), msg) + msg = re.sub(f'{{[?,$]{{1}}path}}', str(bound_vars[1]), msg) + msg = re.sub(f'{{[?,$]{{1}}value}}', str(bound_vars[2]), msg) return msg From 24699183e9b59b982dda95a17d63ec8a268e5e4c Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Sat, 30 May 2020 17:16:33 +0530 Subject: [PATCH 4/7] Fix typing error --- pyshacl/constraints/constraint_component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyshacl/constraints/constraint_component.py b/pyshacl/constraints/constraint_component.py index 7b6f7e3..238cdde 100644 --- a/pyshacl/constraints/constraint_component.py +++ b/pyshacl/constraints/constraint_component.py @@ -131,7 +131,7 @@ def make_v_result_description(self, datagraph, focus_node, severity, value_node= desc += "\tMessage: {}\n".format(str(m)) return desc - def make_v_result(self, datagraph, focus_node, value_node=None, result_path=None, , bound_vars=None + def make_v_result(self, datagraph, focus_node, value_node=None, result_path=None, bound_vars=None, constraint_component=None, source_constraint=None, extra_messages=None): """ From 40211bd46cc73b4cc6739f88297a15535039646e Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Sat, 30 May 2020 17:29:48 +0530 Subject: [PATCH 5/7] Fix regular expressions --- pyshacl/constraints/constraint_component.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyshacl/constraints/constraint_component.py b/pyshacl/constraints/constraint_component.py index 238cdde..b28a8e7 100644 --- a/pyshacl/constraints/constraint_component.py +++ b/pyshacl/constraints/constraint_component.py @@ -182,7 +182,7 @@ def make_v_result(self, datagraph, focus_node, value_node=None, result_path=None def _format_sparql_based_result_message(self, msg, bound_vars): if bound_vars is None: return msg - msg = re.sub(f'{{[?,$]{{1}}this}}', str(bound_vars[0]), msg) - msg = re.sub(f'{{[?,$]{{1}}path}}', str(bound_vars[1]), msg) - msg = re.sub(f'{{[?,$]{{1}}value}}', str(bound_vars[2]), msg) + msg = re.sub('{[?$]this}', str(bound_vars[0]), msg) + msg = re.sub('{[?$]path}', str(bound_vars[1]), msg) + msg = re.sub('{[?$]value}', str(bound_vars[2]), msg) return msg From e0983f8d833ea11f2736df1b39447a6f549c154f Mon Sep 17 00:00:00 2001 From: Ashley Sommer Date: Wed, 26 May 2021 13:22:01 +1000 Subject: [PATCH 6/7] Update pyshacl/constraints/constraint_component.py Add another blank line after the new method, to keep the linter happy --- pyshacl/constraints/constraint_component.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyshacl/constraints/constraint_component.py b/pyshacl/constraints/constraint_component.py index fea7bde..ee14f15 100644 --- a/pyshacl/constraints/constraint_component.py +++ b/pyshacl/constraints/constraint_component.py @@ -276,6 +276,7 @@ def _format_sparql_based_result_message(self, msg, bound_vars): msg = re.sub('{[?$]value}', str(bound_vars[2]), msg) return msg + SH_nodeValidator = SH.term('nodeValidator') SH_propertyValidator = SH.term('propertyValidator') SH_validator = SH.term('validator') From a43e2afdea11f929bc0581ff24cf1634b8c816f0 Mon Sep 17 00:00:00 2001 From: Ashley Sommer Date: Wed, 26 May 2021 13:55:18 +1000 Subject: [PATCH 7/7] Update pyshacl/constraints/sparql/sparql_based_constraints.py Fix black formatting in sparql_based_constraints.py --- pyshacl/constraints/sparql/sparql_based_constraints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyshacl/constraints/sparql/sparql_based_constraints.py b/pyshacl/constraints/sparql/sparql_based_constraints.py index 4e0535d..0561d30 100644 --- a/pyshacl/constraints/sparql/sparql_based_constraints.py +++ b/pyshacl/constraints/sparql/sparql_based_constraints.py @@ -137,8 +137,8 @@ def _evaluate_sparql_constraint(self, sparql_constraint, target_graph, f_v_dict) if v is None: v = result_val rept = self.make_v_result( - target_graph, t or f, value_node=v, result_path=p, - bound_vars=(t, p, v), **rept_kwargs) + target_graph, t or f, value_node=v, result_path=p, bound_vars=(t, p, v), **rept_kwargs + ) else: rept = self.make_v_result(target_graph, f, value_node=v, **rept_kwargs) reports.append(rept)