def _run_pre_inference(self, target_graph, inference_option): try: if inference_option == 'rdfs': inferencer = owl_rl.DeductiveClosure(CustomRDFSSemantics) elif inference_option == 'owlrl': inferencer = owl_rl.DeductiveClosure(owl_rl.OWLRL_Semantics) elif inference_option == 'both' or inference_option == 'all'\ or inference_option == 'rdfsowlrl': inferencer = owl_rl.DeductiveClosure(CustomRDFSOWLRLSemantics) else: raise ReportableRuntimeError( "Don't know how to do '{}' type inferencing.".format( inference_option)) except Exception as e: self.logger.error( "Error during creation of OWL-RL Deductive Closure") raise ReportableRuntimeError( "Error during creation of OWL-RL Deductive Closure\n" "{}".format(str(e.args[0]))) try: inferencer.expand(target_graph) except Exception as e: self.logger.error("Error while running OWL-RL Deductive Closure") raise ReportableRuntimeError( "Error while running OWL-RL Deductive Closure\n" "{}".format(str(e.args[0])))
def _evaluate_xone_constraint(xone_c): nonlocal self, shape, target_graph, focus_value_nodes, _evaluation_path _reports = [] _non_conformant = False sg = shape.sg.graph xone_list = list(sg.items(xone_c)) if len(xone_list) < 1: raise ReportableRuntimeError("The list associated with sh:xone is not " "a valid RDF list.") xone_shapes = list() for x in xone_list: xone_shape = shape.get_other_shape(x) if not xone_shape: raise ReportableRuntimeError( "Shape pointed to by sh:xone does not exist " "or is not a well-formed SHACL Shape." ) xone_shapes.append(xone_shape) for f, value_nodes in focus_value_nodes.items(): for v in value_nodes: passed_count = 0 for xone_shape in xone_shapes: try: _is_conform, _r = xone_shape.validate( target_graph, focus=v, _evaluation_path=_evaluation_path[:] ) except ValidationFailure as e: raise e if _is_conform: passed_count += 1 if not (passed_count == 1): _non_conformant = True rept = self.make_v_result(target_graph, f, value_node=v) _reports.append(rept) return _non_conformant, _reports
def _evaluate_or_constraint(or_c): nonlocal self, shape, target_graph, focus_value_nodes, _evaluation_path _reports = [] _non_conformant = False sg = shape.sg.graph or_list = set(sg.items(or_c)) if len(or_list) < 1: raise ReportableRuntimeError("The list associated with sh:or " "is not a valid RDF list.") or_shapes = set() for o in or_list: or_shape = shape.get_other_shape(o) if not or_shape: raise ReportableRuntimeError( "Shape pointed to by sh:or does not exist or " "is not a well-formed SHACL Shape." ) or_shapes.add(or_shape) for f, value_nodes in focus_value_nodes.items(): for v in value_nodes: passed_any = False for or_shape in or_shapes: try: _is_conform, _r = or_shape.validate( target_graph, focus=v, _evaluation_path=_evaluation_path[:] ) except ValidationFailure as e: raise e passed_any = passed_any or _is_conform if not passed_any: _non_conformant = True rept = self.make_v_result(target_graph, f, value_node=v) _reports.append(rept) return _non_conformant, _reports
def _evaluate_and_constraint(self, and_c, target_graph, f_v_dict): reports = [] non_conformant = False sg = self.shape.sg.graph and_list = set(sg.items(and_c)) if len(and_list) < 1: raise ReportableRuntimeError( "The list associated with sh:and is not a " "valid RDF list.") and_shapes = set() for a in and_list: and_shape = self.shape.get_other_shape(a) if not and_shape: raise ReportableRuntimeError( "Shape pointed to by sh:and does not exist or " "is not a well-formed SHACL Shape.") and_shapes.add(and_shape) for f, value_nodes in f_v_dict.items(): for v in value_nodes: passed_all = True for and_shape in and_shapes: try: _is_conform, _r = and_shape.validate(target_graph, focus=v) except ValidationFailure as e: raise e passed_all = passed_all and _is_conform if not passed_all: non_conformant = True rept = self.make_v_result(target_graph, f, value_node=v) reports.append(rept) return non_conformant, reports
def compare_inferencing_reports(data_graph: GraphLike, expected_graph: GraphLike, expected_result: GraphLike): expected_object = set(expected_graph.objects(expected_result, RDF_object)) if len(expected_object) < 1: raise ReportableRuntimeError( "Cannot check the expected result, the given expectedResult does not have an rdf:object." ) expected_object = next(iter(expected_object)) expected_subject = set(expected_graph.objects(expected_result, RDF_subject)) if len(expected_subject) < 1: raise ReportableRuntimeError( "Cannot check the expected result, the given expectedResult does not have an rdf:subject." ) expected_subject = next(iter(expected_subject)) expected_predicate = set( expected_graph.objects(expected_result, RDF_predicate)) if len(expected_predicate) < 1: raise ReportableRuntimeError( "Cannot check the expected result, the given expectedResult does not have an rdf:predicate." ) expected_predicate = next(iter(expected_predicate)) if isinstance(expected_object, Literal): found_objs = set( data_graph.objects(expected_subject, expected_predicate)) if len(found_objs) < 1: return False found = False for o in found_objs: if isinstance(o, Literal): found = 0 == order_graph_literal(expected_graph, expected_object, data_graph, o) return found elif isinstance(expected_object, BNode): found_objs = set( data_graph.objects(expected_subject, expected_predicate)) if len(found_objs) < 1: return False found = False for o in found_objs: if isinstance(o, BNode): found = 0 == compare_blank_node(expected_graph, expected_object, data_graph, o) return found else: found_triples = set( data_graph.triples( (expected_subject, expected_predicate, expected_object))) if len(found_triples) < 1: return False return True
def apply_to_shape_via_constraint(self, constraint, shape, **kwargs): """ :param constraint: :type constraint: SPARQLConstraintComponent :param shape: :type shape: pyshacl.shape.Shape :param kwargs: :return: """ must_be_ask_val = kwargs.pop('must_be_ask_val', False) if must_be_ask_val and not (isinstance(self, AskConstraintValidator)): # TODO:coverage: No test for this case, do we need to test this? raise ConstraintLoadError( "Validator not for NodeShape or a PropertyShape must be of type SPARQLAskValidator.", "https://www.w3.org/TR/shacl/#ConstraintComponent", ) must_be_select_val = kwargs.pop('must_be_select_val', False) if must_be_select_val and not (isinstance(self, SelectConstraintValidator)): # TODO:coverage: No test for this case, do we need to test this? raise ConstraintLoadError( "Validator for a NodeShape or a PropertyShape must be of type SPARQLSelectValidator.", "https://www.w3.org/TR/shacl/#ConstraintComponent", ) bind_map = {} for m in constraint.mandatory_parameters: name = constraint.parameter_name(m) if name in invalid_parameter_names: # TODO:coverage: No test for this case raise ReportableRuntimeError( "Parameter name {} cannot be used.".format(name)) shape_params = set(shape.objects(m.path())) if len(shape_params) < 1: # TODO:coverage: No test for this case raise ReportableRuntimeError( "Shape does not have mandatory parameter {}.".format( str(m.path()))) # TODO: Can shapes have more than one value for the predicate? # Just use one for now. bind_map[name] = next(iter(shape_params)) for o in constraint.optional_parameters: name = constraint.parameter_name(o) if name in invalid_parameter_names: # TODO:coverage: No test for this case raise ReportableRuntimeError( "Parameter name {} cannot be used.".format(name)) shape_params = set(shape.objects(o.path())) if len(shape_params) > 0: # TODO: Can shapes have more than one value for the predicate? # Just use one for now. bind_map[name] = next(iter(shape_params)) return BoundShapeValidatorComponent(constraint, shape, self, bind_map)
def compare_validation_reports(report_graph, expected_graph, expected_result): expected_conforms = expected_graph.objects(expected_result, SH_conforms) expected_conforms = set(expected_conforms) if len(expected_conforms) < 1: # pragma: no cover raise ReportableRuntimeError( "Cannot check the expected result, the given expectedResult does not have an sh:conforms." ) expected_conforms = next(iter(expected_conforms)) expected_result_nodes = expected_graph.objects(expected_result, SH_result) expected_result_nodes = set(expected_result_nodes) expected_result_node_count = len(expected_result_nodes) validation_reports = report_graph.subjects(RDF_type, SH_ValidationReport) validation_reports = set(validation_reports) if len(validation_reports) < 1: # pragma: no cover raise ReportableRuntimeError( "Cannot check the validation report, the report graph does not contain a ValidationReport" ) validation_report = next(iter(validation_reports)) clean_validation_reports(report_graph, validation_report, expected_graph, expected_result) eq = compare_blank_node(report_graph, validation_report, expected_graph, expected_result) if eq != 0: return False report_conforms = report_graph.objects(validation_report, SH_conforms) report_conforms = set(report_conforms) if len(report_conforms) < 1: # pragma: no cover raise ReportableRuntimeError( "Cannot check the validation report, the report graph does not have an sh:conforms." ) report_conforms = next(iter(report_conforms)) if bool(expected_conforms.value) != bool(report_conforms.value): # TODO:coverage: write a test for this log.error( "Expected Result Conforms value is different from Validation Report's Conforms value." ) return False report_result_nodes = report_graph.objects(validation_report, SH_result) report_result_nodes = set(report_result_nodes) report_result_node_count = len(report_result_nodes) if expected_result_node_count != report_result_node_count: # TODO:coverage: write a test for this log.error( "Number of expected result's sh:result entries is different from Validation Report's sh:result entries.\n" "Expected {}, got {}.".format(expected_result_node_count, report_result_node_count)) return False return True
def compare_validation_reports(report_graph, expected_graph, expected_result): expected_conforms = expected_graph.objects(expected_result, SH_conforms) expected_conforms = set(expected_conforms) if len(expected_conforms) < 1: raise ReportableRuntimeError( "Cannot check the expected result, the given expectedResult does not have an sh:conforms." ) expected_conforms = next(iter(expected_conforms)) expected_result_nodes = expected_graph.objects(expected_result, SH_result) expected_result_nodes = set(expected_result_nodes) expected_result_node_count = len(expected_result_nodes) validation_reports = report_graph.subjects(RDF_type, SH_ValidationReport) validation_reports = set(validation_reports) if len(validation_reports) < 1: raise ReportableRuntimeError( "Cannot check the validation report, the report graph does not contain a ValidationReport" ) validation_report = next(iter(validation_reports)) clean_validation_reports(report_graph, validation_report, expected_graph, expected_result) eq = compare_blank_node(report_graph, validation_report, expected_graph, expected_result) if eq != 0: return False report_conforms = report_graph.objects(validation_report, SH_conforms) report_conforms = set(report_conforms) if len(report_conforms) < 1: raise ReportableRuntimeError( "Cannot check the validation report, the report graph does not have an sh:conforms." ) report_conforms = next(iter(report_conforms)) if bool(expected_conforms.value) != bool(report_conforms.value): log.error( "Expected Result Conforms value is different from Validation Report's Conforms value." ) return False report_result_nodes = report_graph.objects(validation_report, SH_result) report_result_nodes = set(report_result_nodes) report_result_node_count = len(report_result_nodes) if expected_result_node_count != report_result_node_count: log.error( "Number of expected result's sh:result entries is different from Validation Report's sh:result entries.\n" "Expected {}, got {}.".format(expected_result_node_count, report_result_node_count)) return False # Note it is not easily achievable with this method to compare actual result entries, because they are all blank nodes. return True
def _run_pre_inference(cls, target_graph: GraphLike, inference_option: str, logger: Optional[logging.Logger] = None): """ Note, this is the OWL/RDFS pre-inference, it is not the Advanced Spec SHACL-Rule inferencing step. :param target_graph: :param inference_option: :return: """ if logger is None: logger = logging.getLogger(__name__) try: if inference_option == 'rdfs': inferencer = owlrl.DeductiveClosure(CustomRDFSSemantics) elif inference_option == 'owlrl': inferencer = owlrl.DeductiveClosure(owlrl.OWLRL_Semantics) elif inference_option == 'both' or inference_option == 'all' or inference_option == 'rdfsowlrl': inferencer = owlrl.DeductiveClosure(CustomRDFSOWLRLSemantics) else: raise ReportableRuntimeError( "Don't know how to do '{}' type inferencing.".format( inference_option)) except Exception as e: # pragma: no cover logger.error("Error during creation of OWL-RL Deductive Closure") if isinstance(e, ReportableRuntimeError): raise e raise ReportableRuntimeError( "Error during creation of OWL-RL Deductive Closure\n" "{}".format(str(e.args[0]))) if isinstance(target_graph, (rdflib.Dataset, rdflib.ConjunctiveGraph)): named_graphs = [ rdflib.Graph(target_graph.store, i, namespace_manager=target_graph.namespace_manager) if not isinstance(i, rdflib.Graph) else i for i in target_graph.store.contexts(None) ] else: named_graphs = [target_graph] try: for g in named_graphs: inferencer.expand(g) except Exception as e: # pragma: no cover logger.error("Error while running OWL-RL Deductive Closure") raise ReportableRuntimeError( "Error while running OWL-RL Deductive Closure\n" "{}".format(str(e.args[0])))
def check_dash_result(data_graph: GraphLike, report_graph: GraphLike, expected_result_graph: GraphLike): DASH = rdflib.namespace.Namespace('http://datashapes.org/dash#') DASH_GraphValidationTestCase = DASH.term('GraphValidationTestCase') DASH_InferencingTestCase = DASH.term('InferencingTestCase') DASH_expectedResult = DASH.term('expectedResult') gv_test_cases = expected_result_graph.subjects( RDF_type, DASH_GraphValidationTestCase) gv_test_cases = set(gv_test_cases) inf_test_cases = expected_result_graph.subjects(RDF_type, DASH_InferencingTestCase) inf_test_cases = set(inf_test_cases) if len(gv_test_cases) > 0: test_case = next(iter(gv_test_cases)) expected_results = expected_result_graph.objects( test_case, DASH_expectedResult) expected_results = set(expected_results) if len(expected_results) < 1: # pragma: no cover raise ReportableRuntimeError( "Cannot check the expected result, the given GraphValidationTestCase does not have an expectedResult." ) expected_result = next(iter(expected_results)) gv_res = compare_validation_reports(report_graph, expected_result_graph, expected_result) else: gv_res = True if len(inf_test_cases) > 0: test_case = next(iter(inf_test_cases)) expected_results = expected_result_graph.objects( test_case, DASH_expectedResult) expected_results = set(expected_results) if len(expected_results) < 1: # pragma: no cover raise ReportableRuntimeError( "Cannot check the expected result, the given InferencingTestCase does not have an expectedResult." ) expected_result = next(iter(expected_results)) inf_res = compare_inferencing_reports(data_graph, expected_result_graph, expected_result) else: inf_res = True if gv_res is None and inf_res is None: # pragma: no cover raise ReportableRuntimeError( "Cannot check the expected result, the given expected result graph does not have a GraphValidationTestCase or InferencingTestCase." ) return gv_res and inf_res
def _evaluate_ltoe(self, lt, target_graph, f_v_dict): reports = [] non_conformant = False for f, value_nodes in f_v_dict.items(): value_node_set = set(value_nodes) compare_values = set(target_graph.objects(f, lt)) for value_node in iter(value_node_set): if isinstance(value_node, rdflib.BNode): raise ReportableRuntimeError( "Cannot use sh:lessThanOrEquals to compare a BlankNode." ) value_is_string = False orig_value_node = value_node if isinstance(value_node, rdflib.URIRef): value_node = str(value_node) value_is_string = True elif isinstance(value_node, rdflib.Literal) and isinstance( value_node.value, str): value_node = value_node.value value_is_string = True for compare_value in compare_values: if isinstance(compare_value, rdflib.BNode): raise ReportableRuntimeError( "Cannot use sh:lessThanOrEquals to compare a BlankNode." ) compare_is_string = False if isinstance(compare_value, rdflib.URIRef): compare_value = str(compare_value) compare_is_string = True elif isinstance(compare_value, rdflib.Literal) and isinstance( compare_value.value, str): compare_value = compare_value.value compare_is_string = True if (value_is_string and not compare_is_string) or ( compare_is_string and not value_is_string): non_conformant = True elif not value_node <= compare_value: non_conformant = True else: continue rept = self.make_v_result(target_graph, f, value_node=orig_value_node) reports.append(rept) return non_conformant, reports
def _evaluate_max_rule(self, m_val, f_v_dict): reports = [] non_conformant = False assert isinstance(m_val, rdflib.Literal) max_is_string = isinstance(m_val.value, str) for f, value_nodes in f_v_dict.items(): for v in value_nodes: flag = False if isinstance(v, rdflib.BNode): # blank nodes cannot pass val comparison pass elif isinstance(v, rdflib.URIRef): # TODO: Don't know if URIRefs can be compared here pass elif isinstance(v, rdflib.Literal): v_is_string = isinstance(v.value, str) if max_is_string and not v_is_string: pass elif v_is_string and not max_is_string: pass else: flag = bool(m_val >= v) else: raise ReportableRuntimeError( "Not sure how to compare anything else.") if not flag: non_conformant = True rept = self.make_v_result(f, value_node=v) reports.append(rept) return non_conformant, reports
def _evaluate_not_constraint(self, not_c, datagraph, focus_value_nodes, potentially_recursive, _evaluation_path): """ :type not_c: List[Node] :type datagraph: rdflib.Graph :type focus_value_nodes: dict :type potentially_recursive: List :type _evaluation_path: List """ _reports = [] _non_conformant = False not_shape = self.shape.get_other_shape(not_c) if not not_shape: raise ReportableRuntimeError( "Shape pointed to by sh:not does not exist or is not a well-formed SHACL Shape." ) if not_shape in potentially_recursive: warn(ShapeRecursionWarning(_evaluation_path)) return _non_conformant, _reports for f, value_nodes in focus_value_nodes.items(): for v in value_nodes: try: _is_conform, _r = not_shape.validate( datagraph, focus=v, _evaluation_path=_evaluation_path[:]) except ValidationFailure as e: raise e if _is_conform: # in this case, we _dont_ want to conform! _non_conformant = True rept = self.make_v_result(datagraph, f, value_node=v) _reports.append(rept) return _non_conformant, _reports
def _evaluate_not_constraint(not_c): nonlocal self, shape, target_graph, focus_value_nodes, _evaluation_path _reports = [] _non_conformant = False not_shape = shape.get_other_shape(not_c) if not not_shape: raise ReportableRuntimeError( "Shape pointed to by sh:not does not exist or is not " "a well-formed SHACL Shape." ) if not_shape in potentially_recursive: warn(ShapeRecursionWarning(_evaluation_path)) return _non_conformant, _reports for f, value_nodes in focus_value_nodes.items(): for v in value_nodes: try: _is_conform, _r = not_shape.validate( target_graph, focus=v, _evaluation_path=_evaluation_path[:] ) except ValidationFailure as e: raise e if _is_conform: # in this case, we _dont_ want to conform! _non_conformant = True rept = self.make_v_result(target_graph, f, value_node=v) _reports.append(rept) return _non_conformant, _reports
def apply(self, data_graph: 'GraphLike') -> int: focus_nodes = self.shape.focus_nodes(data_graph) # uses target nodes to find focus nodes applicable_nodes = self.filter_conditions(focus_nodes, data_graph) all_added = 0 iterate_limit = 100 while True: if iterate_limit < 1: raise ReportableRuntimeError("sh:rule iteration exceeded iteration limit of 100.") iterate_limit -= 1 added = 0 to_add = [] for a in applicable_nodes: s_set = nodes_from_node_expression(self.s, a, data_graph, self.shape.sg) p_set = nodes_from_node_expression(self.p, a, data_graph, self.shape.sg) o_set = nodes_from_node_expression(self.o, a, data_graph, self.shape.sg) new_triples = itertools.product(s_set, p_set, o_set) this_added = False for i in iter(new_triples): if not this_added and i not in data_graph: this_added = True to_add.append(i) if this_added: added += 1 if added > 0: for i in to_add: data_graph.add(cast(Tuple['Node', 'Node', 'Node'], i)) all_added += added if self.iterate: continue # Jump up to iterate else: break # Don't iterate break return all_added
def apply_rules(shapes_rules: Dict, data_graph: GraphLike, iterate=False) -> int: # short the shapes dict by shapes sh:order before execution sorted_shapes_rules: List[Tuple[Any, Any]] = sorted(shapes_rules.items(), key=lambda x: x[0].order) total_modified = 0 for shape, rules in sorted_shapes_rules: # sort the rules by the sh:order before execution rules = sorted(rules, key=lambda x: x.order) iterate_limit = 100 while True: if iterate_limit < 1: raise ReportableRuntimeError("SHACL Shape Rule iteration exceeded iteration limit of 100.") iterate_limit -= 1 this_modified = 0 for r in rules: if r.deactivated: continue n_modified = r.apply(data_graph) this_modified += n_modified if this_modified > 0: total_modified += this_modified if iterate: continue else: break break return total_modified
def _evaluate_node_shape(node_shape): nonlocal self, target_graph, shape, focus_value_nodes, _evaluation_path _reports = [] _non_conformant = False node_shape = shape.get_other_shape(node_shape) if node_shape in potentially_recursive: warn(ShapeRecursionWarning(_evaluation_path)) return _non_conformant, _reports if not node_shape or node_shape.is_property_shape: raise ReportableRuntimeError( "Shape pointed to by sh:node does not exist or " "is not a well-formed SHACL NodeShape.") for f, value_nodes in focus_value_nodes.items(): for v in value_nodes: _is_conform, _r = node_shape.validate( target_graph, focus=v, _evaluation_path=_evaluation_path[:]) # ignore the fails from the node, create our own fail if (not _is_conform) or len(_r) > 0: _non_conformant = True rept = self.make_v_result(target_graph, f, value_node=v) _reports.append(rept) return _non_conformant, _reports
def _evaluate_min_rule(self, m_val, target_graph, f_v_dict): reports = [] non_conformant = False assert isinstance(m_val, rdflib.Literal) min_is_string = isinstance(m_val.value, str) for f, value_nodes in f_v_dict.items(): for v in value_nodes: flag = False if isinstance(v, rdflib.BNode): # blank nodes cannot pass val comparison pass elif isinstance(v, rdflib.URIRef): # TODO: Don't know if URIRefs can be compared here pass elif isinstance(v, rdflib.Literal): v_is_string = isinstance(v.value, str) if min_is_string and not v_is_string: pass elif v_is_string and not min_is_string: pass else: try: # pass if v > m_val cmp = compare_literal(v, m_val) flag = cmp > 0 except (TypeError, NotImplementedError): flag = False else: raise ReportableRuntimeError( "Not sure how to compare anything else.") if not flag: non_conformant = True rept = self.make_v_result(target_graph, f, value_node=v) reports.append(rept) return non_conformant, reports
def check_dash_result(report_graph, expected_result_graph): DASH = rdflib.namespace.Namespace('http://datashapes.org/dash#') DASH_TestCase = DASH.term('GraphValidationTestCase') DASH_expectedResult = DASH.term('expectedResult') test_cases = expected_result_graph.subjects(RDF_type, DASH_TestCase) test_cases = set(test_cases) if len(test_cases) < 1: # pragma: no cover raise ReportableRuntimeError("Cannot check the expected result, the given expected result graph does not have a GraphValidationTestCase.") test_case = next(iter(test_cases)) expected_results = expected_result_graph.objects(test_case, DASH_expectedResult) expected_results = set(expected_results) if len(expected_results) < 1: # pragma: no cover raise ReportableRuntimeError("Cannot check the expected result, the given GraphValidationTestCase does not have an expectedResult.") expected_result = next(iter(expected_results)) return compare_validation_reports(report_graph, expected_result_graph, expected_result)
def _evaluate_value_shape(self, v_shape, target_graph, f_v_dict): reports = [] non_conformant = False other_shape = self.shape.get_other_shape(v_shape) if not other_shape: raise ReportableRuntimeError( "Shape pointed to by sh:property does not " "exist or is not a well-formed SHACL Shape.") if self.is_disjoint: # Textual Definition of Sibling Shapes: # Let Q be a shape in shapes graph G that declares a qualified cardinality constraint (by having values for sh:qualifiedValueShape and at least one of sh:qualifiedMinCount or sh:qualifiedMaxCount). Let ps be the set of shapes in G that have Q as a value of sh:property. If Q has true as a value for sh:qualifiedValueShapesDisjoint then the set of sibling shapes for Q is defined as the set of all values of the SPARQL property path sh:property/sh:qualifiedValueShape for any shape in ps minus the value of sh:qualifiedValueShape of Q itself. The set of sibling shapes is empty otherwise. sibling_shapes = set() parent_shapes = set( self.shape.sg.subjects(SH_property, self.shape.node)) for p in iter(parent_shapes): found_siblings = set(self.shape.sg.objects(p, SH_property)) for s in iter(found_siblings): if s == self.shape.node: continue sibling_shapes.update( self.shape.sg.objects(s, SH_qualifiedValueShape)) sibling_shapes = set( self.shape.get_other_shape(s) for s in sibling_shapes) else: sibling_shapes = set() for f, value_nodes in f_v_dict.items(): number_conforms = 0 for v in value_nodes: try: _is_conform, _r = other_shape.validate(target_graph, focus=v) if _is_conform: _conforms_to_sibling = False for sibling_shape in sibling_shapes: _c2, _r = sibling_shape.validate(target_graph, focus=v) _conforms_to_sibling = _conforms_to_sibling or _c2 if not _conforms_to_sibling: number_conforms += 1 except ValidationFailure as v: raise v if self.max_count is not None and number_conforms > self.max_count: non_conformant = True _r = self.make_v_result( target_graph, f, constraint_component=SH_QualifiedMaxCountConstraintComponent ) reports.append(_r) if self.min_count is not None and number_conforms < self.min_count: non_conformant = True _r = self.make_v_result( target_graph, f, constraint_component=SH_QualifiedMinCountConstraintComponent ) reports.append(_r) return non_conformant, reports
def _evaluate_string_rule(self, r, target_graph, f_v_dict): reports = [] non_conformant = False languages_need = set() sg = self.shape.sg.graph try: for lang_in in iter(sg.items(r)): try: if not isinstance(lang_in, rdflib.Literal) or not isinstance( lang_in.value, str): raise ReportableRuntimeError( "All languages in sh:LanugageIn must be a Literal with type xsd:string" ) except (AssertionError, AttributeError): raise ReportableRuntimeError( "All languages in sh:LanugageIn must be a Literal with type xsd:string" ) languages_need.add(str(lang_in.value).lower()) except (KeyError, AttributeError, ValueError): raise ReportableRuntimeError( "Value of sh:LanguageIn must be a RDF List") wildcard = False if '*' in languages_need: wildcard = True for f, value_nodes in f_v_dict.items(): for v in value_nodes: flag = False if isinstance(v, rdflib.Literal): lang = v.language if lang: if wildcard: flag = True elif str(lang).lower() in languages_need: flag = True else: lang_parts = str(lang).split('-') first_part = lang_parts[0] if str(first_part).lower() in languages_need: flag = True if not flag: non_conformant = True rept = self.make_v_result(target_graph, f, value_node=v) reports.append(rept) return non_conformant, reports
def execute(self, g, *args): params = self.get_params_in_order() if len(args) != len(params): raise ReportableRuntimeError( "Got incorrect number of arguments for JSFunction {}.".format( self.node)) args_map = {} for i, p in enumerate(params): arg = args[i] ln = p.localname if arg is None and p.optional is False: raise ReportableRuntimeError( "Got NoneType for Non-optional argument {}.".format(ln)) args_map[ln] = arg results = self.js_exe.execute(g, args_map, mode="function", return_type=self.rtype) res = results['_result'] return res
def apply(self, data_graph: 'GraphLike') -> int: focus_nodes = self.shape.focus_nodes(data_graph) # uses target nodes to find focus nodes all_added = 0 SPARQLQueryHelper = get_query_helper_cls() iterate_limit = 100 while True: if iterate_limit < 1: raise ReportableRuntimeError("Local rule iteration exceeded iteration limit of 100.") iterate_limit -= 1 added = 0 applicable_nodes = self.filter_conditions(focus_nodes, data_graph) construct_graphs = set() for a in applicable_nodes: for c in self._constructs: init_bindings = {} found_this = SPARQLQueryHelper.bind_this_regex.search(c) if found_this: init_bindings['this'] = a c = self._qh.apply_prefixes(c) results = data_graph.query(c, initBindings=init_bindings) if results.type != "CONSTRUCT": raise ReportableRuntimeError("Query executed by a SHACL SPARQLRule must be CONSTRUCT query.") this_added = False for i in results.graph: if not this_added and i not in data_graph: this_added = True # We only need to know at least one triple was added, then break! break if this_added: added += 1 construct_graphs.add(results.graph) if added > 0: for g in construct_graphs: data_graph = clone_graph(g, target_graph=data_graph) all_added += added if self.iterate: continue # Jump up to iterate else: break # Don't iterate break # We've reached a local steady state return all_added
def parameter_name(cls, parameter): path = str(parameter.path()) hash_index = path.find('#') if hash_index > 0: ending = path[hash_index + 1:] return ending right_slash_index = path.rfind('/') if right_slash_index > 0: ending = path[right_slash_index + 1:] return ending raise ReportableRuntimeError( "Cannot get a local name for {}".format(path))
def bind_params(self): bind_map = {} shape = self.shape for p in self.constraint.parameters: name = p.localname if name in self.invalid_parameter_names: # TODO:coverage: No test for this case raise ReportableRuntimeError( "Parameter name {} cannot be used.".format(name)) shape_params = set(shape.objects(p.path())) if len(shape_params) < 1: if not p.optional: # TODO:coverage: No test for this case raise ReportableRuntimeError( "Shape does not have mandatory parameter {}.".format( str(p.path()))) continue # TODO: Can shapes have more than one value for the predicate? # Just use one for now. # TODO: Check for sh:class and sh:nodeKind on the found param value bind_map[name] = next(iter(shape_params)) self.param_bind_map = bind_map
def parameter_name(cls, parameter): path = str(parameter.path()) hash_index = path.find('#') if hash_index > 0: ending = path[hash_index + 1:] return ending right_slash_index = path.rfind('/') if right_slash_index > 0: # TODO:coverage: No test for this case where path has a right slash ending = path[right_slash_index + 1:] return ending raise ReportableRuntimeError( "Cannot get a local name for {}".format(path))
def get_fn_args(self, fn_name, args_map): c = self.context try: fn = c.get_global(fn_name) except BaseException as e: print(e) raise if fn is None: raise ReportableRuntimeError( "JS Function {} cannot be found in the loaded files.".format( fn_name)) if fn_name not in self.fns: raise ReportableRuntimeError( "JS Function {} args cannot be determined. Bad JS structure?". format(fn_name)) known_fn_args = self.fns[fn_name] # type: tuple needed_args = [] for k, v in args_map.items(): look_for = "$" + str(k) positions = [] start = 0 while True: try: pos = known_fn_args.index(look_for, start) positions.append(pos) except ValueError: break start = pos + 1 if not positions: continue for p in positions: needed_args.append((p, k, v)) for i, a in enumerate(known_fn_args): if a.startswith("$"): a = a[1:] if a not in args_map: needed_args.append((i, a, None)) needed_args = [v for p, k, v in sorted(needed_args)] return needed_args
def _evaluate_property_shape(self, prop_shape, target_graph, f_v_dict): reports = [] non_conformant = False prop_shape = self.shape.get_other_shape(prop_shape) if not prop_shape or not prop_shape.is_property_shape: raise ReportableRuntimeError( "Shape pointed to by sh:property does not exist " "or is not a well-formed SHACL PropertyShape.") for f, value_nodes in f_v_dict.items(): for v in value_nodes: _is_conform, _r = prop_shape.validate(target_graph, focus=v) non_conformant = non_conformant or (not _is_conform) reports.extend(_r) return non_conformant, reports
def _load_into_graph(target, rdf_format=None): if isinstance(target, rdflib.Graph): return target target_is_open = False target_is_file = False target_is_text = False filename = None if isinstance(target, IOBase) and hasattr(target, 'read'): target_is_file = True target_is_open = True filename = target.name elif isinstance(target, str): if target.startswith('file://'): target_is_file = True filename = target[7:] elif len(target) < 240: target_is_file = True filename = target if not target_is_file: target_is_text = True else: raise ReportableRuntimeError( "Cannot determine the format of the input graph") if filename: if filename.endswith('.ttl'): rdf_format = rdf_format or 'turtle' if filename.endswith('.nt'): rdf_format = rdf_format or 'nt' elif filename.endswith('.xml'): rdf_format = rdf_format or 'xml' elif filename.endswith('.json'): rdf_format = rdf_format or 'json-ld' g = rdflib.Graph() if target_is_file and filename and not target_is_open: import os filename = os.path.abspath(filename) target = open(filename, mode='rb') target_is_open = True if target_is_open: g.parse(source=None, publicID=None, format=rdf_format, location=None, file=target) target.close() elif target_is_text: g.parse(data=target, format=rdf_format) return g
def evaluate(self, target_graph: GraphLike, focus_value_nodes: Dict, _evaluation_path: List): """ :type target_graph: rdflib.Graph :type focus_value_nodes: dict :type _evaluation_path: list """ reports = [] non_conformant = False for lt in iter(self.property_compare_set): if isinstance(lt, rdflib.Literal) or isinstance(lt, rdflib.BNode): raise ReportableRuntimeError("Value of sh:lessThanOrEquals MUST be a URI Identifier.") _nc, _r = self._evaluate_ltoe(lt, target_graph, focus_value_nodes) non_conformant = non_conformant or _nc reports.extend(_r) return (not non_conformant), reports