예제 #1
0
 def _find_custom_constraints(self):
     g = self.graph
     constraint_component_set = set(
         g.subjects(RDF_type, SH_ConstraintComponent))
     constraint_subclasses = set(
         g.subjects(RDFS_subClassOf, SH_ConstraintComponent))
     for sc in iter(constraint_subclasses):
         subclass_components = set(g.subjects(RDF_type, sc))
         constraint_component_set.update(subclass_components)
     components = set()
     for c in iter(constraint_component_set):
         optional_params = []
         mandatory_params = []
         param_nodes = set(g.objects(c, SH_parameter))
         if len(param_nodes) < 1:
             raise ConstraintLoadError(
                 "A sh:ConstraintComponent must have at least one value for sh:parameter",
                 "https://www.w3.org/TR/shacl/#constraint-components-parameters"
             )
         for p in iter(param_nodes):
             path_nodes = set(g.objects(p, SH_path))
             if len(path_nodes) < 1:
                 raise ConstraintLoadError(
                     "A sh:ConstraintComponent parameter value must have at least one value for sh:path",
                     "https://www.w3.org/TR/shacl/#constraint-components-parameters"
                 )
             elif len(path_nodes) > 1:
                 raise ConstraintLoadError(
                     "A sh:ConstraintComponent parameter value must have at most one value for sh:path",
                     "https://www.w3.org/TR/shacl/#constraint-components-parameters"
                 )
             path = next(iter(path_nodes))
             is_optional = False
             optional = set(g.objects(p, SH_optional))
             for o in iter(optional):
                 if not (isinstance(o, rdflib.Literal)
                         and isinstance(o.value, bool)):
                     raise ConstraintLoadError(
                         "A sh:Parameter value for sh:optional must be a valid RDF Literal of type xsd:boolean.",
                         "https://www.w3.org/TR/shacl/#constraint-components-parameters"
                     )
                 is_optional = o.value
             parameter = Shape(self,
                               p=True,
                               node=p,
                               path=path,
                               logger=self.logger)
             if is_optional:
                 optional_params.append(parameter)
             else:
                 mandatory_params.append(parameter)
         if len(mandatory_params) < 1:
             raise ConstraintLoadError(
                 "A sh:ConstraintComponent must have at least one non-optional parameter.",
                 "https://www.w3.org/TR/shacl/#constraint-components-parameters"
             )
         component = SPARQLConstraintComponent(self, c, mandatory_params,
                                               optional_params)
         components.add(component)
     return components
예제 #2
0
    def _build_node_shape_cache(self):
        """
        :returns: [Shape]
        :rtype: list(pyshacl.shape.Shape)
        """
        g = self.graph
        defined_node_shapes = set(g.subjects(RDF_type, SH_NodeShape))
        for s in defined_node_shapes:
            path_vals = list(g.objects(s, SH_path))
            if len(path_vals) > 0:
                # TODO:coverage: we don't have any tests for invalid shapes
                raise ShapeLoadError(
                    "A shape defined as a NodeShape cannot be the subject of a 'sh:path' predicate.",
                    "https://www.w3.org/TR/shacl/#node-shapes")

        defined_prop_shapes = set(g.subjects(RDF_type, SH_PropertyShape))
        found_prop_shapes_paths = dict()
        for s in defined_prop_shapes:
            if s in defined_node_shapes:
                # TODO:coverage: we don't have any tests for invalid shapes
                raise ShapeLoadError(
                    "A shape defined as a NodeShape cannot also be defined as a PropertyShape.",
                    "https://www.w3.org/TR/shacl/#node-shapes")
            path_vals = list(g.objects(s, SH_path))
            if len(path_vals) < 1:
                # TODO:coverage: we don't have any tests for invalid shapes
                raise ShapeLoadError(
                    "A shape defined as a PropertyShape must be the subject of a 'sh:path' predicate.",
                    "https://www.w3.org/TR/shacl/#property-shapes")
            elif len(path_vals) > 1:
                # TODO:coverage: we don't have any tests for invalid shapes
                raise ShapeLoadError(
                    "A shape defined as a PropertyShape cannot have more than one 'sh:path' predicate.",
                    "https://www.w3.org/TR/shacl/#property-shapes")
            found_prop_shapes_paths[s] = path_vals[0]

        has_target_class = {s for s, o in g.subject_objects(SH_targetClass)}
        has_target_node = {s for s, o in g.subject_objects(SH_targetNode)}
        has_target_objects_of = {s for s, o in g.subject_objects(SH_targetObjectsOf)}
        has_target_subjects_of = {s for s, o in g.subject_objects(SH_targetSubjectsOf)}
        subject_shapes = set(has_target_class).union(
            set(has_target_node).union(
                set(has_target_objects_of).union(
                    set(has_target_subjects_of))))

        # implicit shapes: their subjects must be shapes
        subject_of_property = {s for s, o in g.subject_objects(SH_property)}
        subject_of_node = {s for s, o in g.subject_objects(SH_node)}
        subject_shapes = subject_shapes.union(
            set(subject_of_property).union(
                set(subject_of_node)))

        # shape-expecting properties, their values must be shapes.
        value_of_property = {o for s, o in g.subject_objects(SH_property)}
        value_of_node = {o for s, o in g.subject_objects(SH_node)}
        value_of_not = {o for s, o in g.subject_objects(SH_not)}
        value_of_qvs = {o for s, o in g.subject_objects(SH_qualifiedValueShape)}
        value_of_shape_expecting = set(value_of_property).union(
            set(value_of_node).union(
                set(value_of_not).union(
                    set(value_of_qvs))))

        value_of_and = {o for s, o in g.subject_objects(SH_and)}
        value_of_or = {o for s, o in g.subject_objects(SH_or)}
        value_of_xone = {o for s, o in g.subject_objects(SH_xone)}
        value_of_s_list_expecting = set(value_of_and).union(
            set(value_of_or).union(
                set(value_of_xone)))

        for l in value_of_s_list_expecting:
            list_contents = set(g.items(l))
            if len(list_contents) < 1:
                # TODO:coverage: we don't have any tests for invalid shape lists
                raise ShapeLoadError(
                    "A Shape-Expecting & List-Expecting predicate should get a well-formed RDF list with 1 or more members.",
                    "https://www.w3.org/TR/shacl/#shapes-recursion")
            for s in list_contents:
                value_of_shape_expecting.add(s)

        found_node_shapes = set()
        found_prop_shapes = set()
        for s in subject_shapes:
            if s in defined_node_shapes or s in defined_prop_shapes:
                continue
            path_vals = list(g.objects(s, SH_path))
            if len(path_vals) < 1:
                found_node_shapes.add(s)
            elif len(path_vals) > 1:
                # TODO:coverage: we don't have any tests for invalid implicit shapes
                raise ShapeLoadError(
                    "An implicit PropertyShape cannot have more than one 'sh:path' predicate.",
                    "https://www.w3.org/TR/shacl/#property-shapes")
            else:
                # TODO:coverage: we don't have this case where an implicit shape is a property shape.
                found_prop_shapes.add(s)
                found_prop_shapes_paths[s] = path_vals[0]
        for s in value_of_shape_expecting:
            if s in defined_node_shapes or s in defined_prop_shapes or \
                    s in found_prop_shapes or s in found_node_shapes:
                continue
            path_vals = list(g.objects(s, SH_path))
            if len(path_vals) < 1:
                found_node_shapes.add(s)
            elif len(path_vals) > 1:
                # TODO:coverage: we don't have any tests for invalid implicit shapes
                raise ShapeLoadError(
                    "An implicit PropertyShape cannot have more than one 'sh:path' predicate.",
                    "https://www.w3.org/TR/shacl/#property-shapes")
            else:
                found_prop_shapes.add(s)
                found_prop_shapes_paths[s] = path_vals[0]
        for node_shape in defined_node_shapes.union(found_node_shapes):
            if node_shape in self._node_shape_cache:
                # TODO:coverage: we don't have any tests where a shape is loaded twice
                raise ShapeLoadError("That shape has already been loaded!",
                                     "None")
            s = Shape(self, node_shape, p=False, logger=self.logger)
            self._node_shape_cache[node_shape] = s
        for prop_shape in defined_prop_shapes.union(found_prop_shapes):
            if prop_shape in self._node_shape_cache:
                # TODO:coverage: we don't have any tests where a shape is loaded twice
                raise ShapeLoadError("That shape has already been loaded!",
                                     "None")
            prop_shape_path = found_prop_shapes_paths[prop_shape]
            s = Shape(self, prop_shape, p=True, path=prop_shape_path, logger=self.logger)
            self._node_shape_cache[prop_shape] = s
예제 #3
0
    def _find_shapes(self):
        """
        :returns: [Shape]
        :rtype: list(pyshacl.shape.Shape)
        """
        g = self.graph
        defined_node_shapes = set(g.subjects(RDF_type, SH_NodeShape))
        for s in defined_node_shapes:
            path_vals = list(g.objects(s, SH_path))
            if len(path_vals) > 0:
                raise ShapeLoadError(
                    "A shape defined as a NodeShape cannot be the subject of a 'sh:path' predicate.",
                    "https://www.w3.org/TR/shacl/#node-shapes")

        defined_prop_shapes = set(g.subjects(RDF_type, SH_PropertyShape))
        found_prop_shapes_paths = dict()
        for s in defined_prop_shapes:
            if s in defined_node_shapes:
                raise ShapeLoadError(
                    "A shape defined as a NodeShape cannot also be defined as a PropertyShape.",
                    "https://www.w3.org/TR/shacl/#node-shapes")
            path_vals = list(g.objects(s, SH_path))
            if len(path_vals) < 1:
                raise ShapeLoadError(
                    "A shape defined as a PropertyShape must be the subject of a 'sh:path' predicate.",
                    "https://www.w3.org/TR/shacl/#property-shapes")
            elif len(path_vals) > 1:
                raise ShapeLoadError(
                    "A shape defined as a PropertyShape cannot have more than one 'sh:path' predicate.",
                    "https://www.w3.org/TR/shacl/#property-shapes")
            found_prop_shapes_paths[s] = path_vals[0]

        has_target_class = {s for s, o in g.subject_objects(SH_targetClass)}
        has_target_node = {s for s, o in g.subject_objects(SH_targetNode)}
        has_target_objects_of = {
            s
            for s, o in g.subject_objects(SH_targetObjectsOf)
        }
        has_target_subjects_of = {
            s
            for s, o in g.subject_objects(SH_targetSubjectsOf)
        }
        subject_shapes = set(has_target_class).union(
            set(has_target_node).union(
                set(has_target_objects_of).union(set(has_target_subjects_of))))

        value_of_property = {o for s, o in g.subject_objects(SH_property)}
        value_of_node = {o for s, o in g.subject_objects(SH_node)}
        value_of_not = {o for s, o in g.subject_objects(SH_not)}
        value_of_qvs = {
            o
            for s, o in g.subject_objects(SH_qualifiedValueShape)
        }
        value_of_shape_expecting = set(value_of_property).union(
            set(value_of_node).union(
                set(value_of_not).union(set(value_of_qvs))))

        value_of_and = {o for s, o in g.subject_objects(SH_and)}
        value_of_or = {o for s, o in g.subject_objects(SH_or)}
        value_of_xone = {o for s, o in g.subject_objects(SH_xone)}
        value_of_s_list_expecting = set(value_of_and).union(
            set(value_of_or).union(set(value_of_xone)))

        for l in value_of_s_list_expecting:
            list_contents = set(g.items(l))
            if len(list_contents) < 1:
                raise ShapeLoadError(
                    "A Shape-Expecting & List-Expecting predicate should get a well-formed RDF list with 1 or more members.",
                    "https://www.w3.org/TR/shacl/#shapes-recursion")
            for s in list_contents:
                value_of_shape_expecting.add(s)

        found_node_shapes = set()
        found_prop_shapes = set()
        for s in subject_shapes:
            if s in defined_node_shapes or s in defined_prop_shapes:
                continue
            path_vals = list(g.objects(s, SH_path))
            if len(path_vals) < 1:
                found_node_shapes.add(s)
            elif len(path_vals) > 1:
                raise ShapeLoadError(
                    "An implicit PropertyShape cannot have more than one 'sh:path' predicate.",
                    "https://www.w3.org/TR/shacl/#property-shapes")
            else:
                found_prop_shapes.add(s)
                found_prop_shapes_paths[s] = path_vals[0]
        for s in value_of_shape_expecting:
            if s in defined_node_shapes or s in defined_prop_shapes or \
                    s in found_prop_shapes or s in found_node_shapes:
                continue
            path_vals = list(g.objects(s, SH_path))
            if len(path_vals) < 1:
                found_node_shapes.add(s)
            elif len(path_vals) > 1:
                raise ShapeLoadError(
                    "An implicit PropertyShape cannot have more than one 'sh:path' predicate.",
                    "https://www.w3.org/TR/shacl/#property-shapes")
            else:
                found_prop_shapes.add(s)
                found_prop_shapes_paths[s] = path_vals[0]
        created_node_shapes = set()
        for node_shape in defined_node_shapes.union(found_node_shapes):
            s = Shape(self, node_shape, False, logger=self.logger)
            created_node_shapes.add(s)
        created_prop_shapes = set()
        for prop_shape in defined_prop_shapes.union(found_prop_shapes):
            prop_shape_path = found_prop_shapes_paths[prop_shape]
            s = Shape(self,
                      prop_shape,
                      True,
                      path=prop_shape_path,
                      logger=self.logger)
            created_prop_shapes.add(s)
        return list(created_node_shapes.union(created_prop_shapes))
예제 #4
0
 def __init__(self, shape: Shape) -> None:
     super().__init__(shape)
     self.has_value = next(shape.objects(SH_hasValue))