def matches(cntxt: Context, T: RDFGraph, expr: ShExJ.tripleExpr) -> bool:
    """
    **matches**: asserts that a triple expression is matched by a set of triples that come from the neighbourhood of a
    node in an RDF graph. The expression `matches(T, expr, m)` indicates that a set of triples `T` can satisfy these
    rules:

    * expr has semActs and `matches(T, expr, m)` by the remaining rules in this list and the evaluation
      of semActs succeeds according to the section below on Semantic Actions.
    * expr has a cardinality of min and/or max not equal to 1, where a max of -1 is treated as unbounded, and T
      can be partitioned into k subsets T1, T2,…Tk such that min ≤ k ≤ max and for each Tn,
      `matches(Tn, expr, m)` by the remaining rules in this list.
    * expr is a OneOf and there is some shape expression se2 in shapeExprs such that a matches(T, se2, m).
    * expr is an EachOf and there is some partition of T into T1, T2,… such that for every expression
      expr1, expr2,… in shapeExprs, matches(Tn, exprn, m).
    * expr is a TripleConstraint and:
        * T is a set of one triple. Let t be the soul triple in T.
        * t's predicate equals expr's predicate. Let value be t's subject if inverse is true, else t's object.
        * if inverse is true, t is in arcsIn, else t is in `arcsOut`.
        * either
            * expr has no valueExpr
            * or `satisfies(value, valueExpr, G, m).
    """
    if isinstance_(expr, ShExJ.tripleExprLabel):
        return matchesExpr(cntxt, T, expr)
    else:
        return matchesCardinality(
            cntxt, T, expr) and (expr.semActs is None
                                 or semActsSatisfied(expr.semActs, cntxt))
예제 #2
0
    def end_class(self, cls: ClassDefinition) -> None:
        if cls.is_a:
            self._add_constraint(self.namespaces.uri_for(camelcase(cls.is_a) + "_t"))
        for mixin in cls.mixins:
            if self._class_has_expressions(mixin):
                self._add_constraint(self.namespaces.uri_for(camelcase(mixin) + "_t"))
        if cls.name in self.synopsis.applytorefs:
            for applyto in self.synopsis.applytorefs[cls.name].classrefs:
                if self._class_has_expressions(applyto):
                    self._add_constraint(self.namespaces.uri_for(camelcase(applyto) + '_t'))

        self.shape.closed = True
        self.shape.extra = [RDF.type]
        if self.shape.expression:
            # TODO: Figure out how to label a single triple expression
            if isinstance_(self.shape.expression, tripleExprLabel):
                self.shape.expression = EachOf(expressions=[self.shape.expression, wildcard(None)])
            self.shape.expression.id = self.namespaces.uri_for(camelcase(cls.name) + "_t")
        else:
            self.shape.expression = wildcard(self.namespaces.uri_for(camelcase(cls.name) + "_t"))

        if self.class_identifier(cls):
            self.shape.extra = [RDF.type]
            type_constraint = TripleConstraint()
            type_constraint.predicate = RDF.type
            type_constraint.valueExpr = NodeConstraint(values=[IRIREF(self.namespaces.uri_for(cls.class_uri))])
            if not self.shape.expression:
                self.shape.expression = type_constraint
            else:
                self.shape.expression = EachOf(expressions=[self.shape.expression, type_constraint])

        shapeExpr = self.shape
        shapeExpr.id = self._shape_iri(cls.name)
        self.shapes.append(shapeExpr)
예제 #3
0
def _nodeSatisfiesValue(cntxt: Context, n: Node,
                        vsv: ShExJ.valueSetValue) -> bool:
    """
    A term matches a valueSetValue if:
        * vsv is an objectValue and n = vsv.
        * vsv is a Language with langTag lt and n is a language-tagged string with a language tag l and l = lt.
        * vsv is a IriStem, LiteralStem or LanguageStem with stem st and nodeIn(n, st).
        * vsv is a IriStemRange, LiteralStemRange or LanguageStemRange with stem st and exclusions excls and
          nodeIn(n, st) and there is no x in excls such that nodeIn(n, excl).
        * vsv is a Wildcard with exclusions excls and there is no x in excls such that nodeIn(n, excl).

    Note that ObjectLiteral is *not* typed in ShExJ.jsg, so we identify it by a lack of a 'type' variable

    .. note:: Mismatch with spec
        This won't work correctly if the stem value is passed in to nodeIn, as there will be no way to know whether
        we're matching an IRI or other type

    ... note:: Language issue
        The stem range spec shouldn't have the first element in the exclusions

    """
    vsv = map_object_literal(vsv)
    if isinstance_(vsv, ShExJ.objectValue):
        return objectValueMatches(n, vsv)

    if isinstance(vsv, ShExJ.Language):
        if vsv.languageTag is not None and isinstance(
                n, Literal) and n.language is not None:
            return n.language == vsv.languageTag
        else:
            return False

    if isinstance(vsv, ShExJ.IriStem):
        return nodeInIriStem(cntxt, n, vsv.stem)

    if isinstance(vsv, ShExJ.IriStemRange):
        exclusions = vsv.exclusions if vsv.exclusions is not None else []
        return nodeInIriStem(cntxt, n, vsv.stem) and not any(
            (uriref_matches_iriref(n, excl) if isinstance(excl, ShExJ.IRIREF)
             else uriref_startswith_iriref(n, excl.stem))
            for excl in exclusions)

    if isinstance(vsv, ShExJ.LiteralStem):
        return nodeInLiteralStem(cntxt, n, vsv.stem)

    if isinstance(vsv, ShExJ.LiteralStemRange):
        exclusions = vsv.exclusions if vsv.exclusions is not None else []
        return nodeInLiteralStem(cntxt, n, vsv.stem) and not any(
            str(n) == excl for excl in exclusions)

    if isinstance(vsv, ShExJ.LanguageStem):
        return nodeInLanguageStem(cntxt, n, vsv.stem)

    if isinstance(vsv, ShExJ.LanguageStemRange):
        exclusions = vsv.exclusions if vsv.exclusions is not None else []
        return nodeInLanguageStem(cntxt, n, vsv.stem) and not any(
            str(n) == str(excl) for excl in exclusions)

    return False
예제 #4
0
 def test_issue_with_shexj(self):
     from pyjsg.parser_impl.generate_python import generate
     data_root = os.path.join(
         os.path.split(os.path.abspath(__file__))[0], '..', 'test_basics')
     jsg_path = os.path.relpath(os.path.join(data_root, 'jsg', 'ShExJ.jsg'))
     py_path = os.path.abspath(os.path.join(data_root, 'py', 'ShExJ.py'))
     self.assertEqual(0, generate([jsg_path, "-o", py_path, "-e", "-nh"]))
     from tests.test_basics.py import ShExJ
     self.assertTrue(
         isinstance_(ShExJ.IRIREF("http://foo.bar"), ShExJ.shapeExprLabel))
예제 #5
0
 def test_isinstance_issue(self):
     from pyjsg.jsglib.loader import isinstance_
     x = Union[int, str]
     # Typing library seems to support Unions as of python 3.10
     if sys.version_info < (3, 10):
         with self.assertRaises(TypeError):
             isinstance(17, x)
     else:
         self.assertTrue(isinstance(17, x))
     self.assertTrue(isinstance_(17, x))
예제 #6
0
파일: p5_context.py 프로젝트: egonw/PyShEx
    def visit_shapes(self,
                     expr: ShExJ.shapeExpr,
                     f: Callable[[Any, ShExJ.shapeExpr, "Context"], None],
                     arg_cntxt: Any,
                     visit_center: _VisitorCenter = None,
                     follow_inner_shapes: bool = True) -> None:
        """
        Visit expr and all of its "descendant" shapes.

        :param expr: root shape expression
        :param f: visitor function
        :param arg_cntxt: accompanying context for the visitor function
        :param visit_center: Recursive visit context.  (Not normally supplied on an external call)
        :param follow_inner_shapes: Follow nested shapes or just visit on outer level
        """
        if visit_center is None:
            visit_center = _VisitorCenter(f, arg_cntxt)
        has_id = getattr(expr, 'id', None) is not None
        if not has_id or not (visit_center.already_seen_shape(expr.id) or
                              visit_center.actively_visiting_shape(expr.id)):

            # Visit the root expression
            if has_id:
                visit_center.start_visiting_shape(expr.id)
            f(arg_cntxt, expr, self)

            # Traverse the expression and visit its components
            if isinstance(expr, (ShExJ.ShapeOr, ShExJ.ShapeAnd)):
                for expr2 in expr.shapeExprs:
                    self.visit_shapes(expr2,
                                      f,
                                      arg_cntxt,
                                      visit_center,
                                      follow_inner_shapes=follow_inner_shapes)
            elif isinstance(expr, ShExJ.ShapeNot):
                self.visit_shapes(expr.shapeExpr,
                                  f,
                                  arg_cntxt,
                                  visit_center,
                                  follow_inner_shapes=follow_inner_shapes)
            elif isinstance(expr, ShExJ.Shape):
                if expr.expression is not None and follow_inner_shapes:
                    self.visit_triple_expressions(
                        expr.expression,
                        lambda ac, te, cntxt: self._visit_shape_te(
                            te, visit_center), arg_cntxt, visit_center)
            elif isinstance_(expr, ShExJ.shapeExprLabel):
                if not visit_center.actively_visiting_shape(
                        str(expr)) and follow_inner_shapes:
                    visit_center.start_visiting_shape(str(expr))
                    self.visit_shapes(self.shapeExprFor(expr), f, arg_cntxt,
                                      visit_center)
                    visit_center.done_visiting_shape(str(expr))
            if has_id:
                visit_center.done_visiting_shape(expr.id)
예제 #7
0
 def n3(self, node: Union[URIRef, BNode, Literal, Triple, str]) -> str:
     if isinstance_(node, Triple):
         return f"{self.n3(node[0])} {self.n3(node[1])} {self.n3(node[2])} ."
     elif isinstance(node, BNode):
         if node not in self._bnode_map:
             self._bnode_map[node] = self._next_bnode
         return self._bnode_map[node]
     else:
         if not isinstance(node, (URIRef, Literal)):
             node = URIRef(str(node))
         return node.n3(self.namespace_manager)
예제 #8
0
    def visit_triple_expressions(self,
                                 expr: ShExJ.tripleExpr,
                                 f: Callable[
                                     [Any, ShExJ.tripleExpr, "Context"], None],
                                 arg_cntxt: Any,
                                 visit_center: _VisitorCenter = None) -> None:
        if visit_center is None:
            visit_center = _VisitorCenter(f, arg_cntxt)
        if expr is None:
            return f(arg_cntxt, None, self)
        has_id = not isinstance_(
            expr,
            ShExJ.tripleExprLabel) and 'id' in expr and expr.id is not None
        if not has_id or not visit_center.already_seen_te(expr.id):

            # Visit the root expression
            if has_id:
                visit_center.start_visiting_te(expr.id)
            f(arg_cntxt, expr, self)

            # Visit all of the references
            if isinstance(expr, (ShExJ.EachOf, ShExJ.OneOf)):
                for expr2 in expr.expressions:
                    self.visit_triple_expressions(expr2, f, arg_cntxt,
                                                  visit_center)
            elif isinstance(expr, ShExJ.TripleConstraint):
                if expr.valueExpr is not None:
                    self.visit_shapes(
                        expr.valueExpr,
                        lambda ac, te, cntxt: self._visit_shape_te(
                            te, visit_center), arg_cntxt, visit_center)
            elif isinstance_(expr, ShExJ.tripleExprLabel):
                if not visit_center.actively_visiting_te(str(expr)):
                    visit_center.start_visiting_te(str(expr))
                    self.visit_triple_expressions(self.tripleExprFor(expr), f,
                                                  arg_cntxt, visit_center)
                    visit_center.done_visiting_te(str(expr))
            if has_id:
                visit_center.done_visiting_te(expr.id)
예제 #9
0
 def __init__(
         self,
         function: Callable[["Context", Union[RDFGraph, Node], JSGObject],
                            bool], expr: JSGObject,
         obj: Union[RDFGraph, Node], cntxt: "Context"):
     self.function = function
     self.expr = expr
     self.graph = obj if isinstance(obj, RDFGraph) else None
     self.node = obj if isinstance_(obj, Node) else None
     self.result = None
     self._fail_reason = None
     self.reason_stack = []
     self.nodes = []
     self.n3m = cntxt.n3_mapper
예제 #10
0
def isValid(cntxt: Context, m: FixedShapeMap) -> Tuple[bool, List[str]]:
    """`5.2 Validation Definition <http://shex.io/shex-semantics/#validation>`_

    The expression isValid(G, m) indicates that for every nodeSelector/shapeLabel pair (n, s) in m, s has a
        corresponding shape expression se and satisfies(n, se, G, m). satisfies is defined below for each form
        of shape expression

    :param cntxt: evaluation context - includes graph and schema
    :param m: list of NodeShape pairs to test
    :return: Success/failure indicator and, if fail, a list of failure reasons
    """
    if not cntxt.is_valid:
        return False, cntxt.error_list
    parse_nodes = []
    for nodeshapepair in m:
        n = nodeshapepair.nodeSelector
        if not isinstance_(n, Node):
            #return False, [f"{n}: Triple patterns are not implemented"]
            return False, [n + ":Triple patterns are not implemented"]
        # The third test below is because the spec asserts that completely empty graphs pass in certain circumstances
        elif not (next(
                cntxt.graph.predicate_objects(nodeshapepair.nodeSelector),
                None) or next(
                    cntxt.graph.subject_predicates(nodeshapepair.nodeSelector),
                    None) or not next(cntxt.graph.triples(
                        (None, None, None)), None)):
            #return False, [f"Focus: {nodeshapepair.nodeSelector} not in graph"]
            return False, [
                "Focus: " + nodeshapepair.nodeSelector + " not in graph"
            ]
        else:
            s = cntxt.shapeExprFor(START if nodeshapepair.shapeLabel is None
                                   or nodeshapepair.shapeLabel is START else
                                   nodeshapepair.shapeLabel)
            cntxt.current_node = ParseNode(satisfies, s, n, cntxt)
            if not s:
                if nodeshapepair.shapeLabel is START or nodeshapepair.shapeLabel is None:
                    cntxt.fail_reason = "START node is not specified or is invalid"
                else:
                    #cntxt.fail_reason = f"Shape: {nodeshapepair.shapeLabel} not found in Schema"
                    cntxt.fail.reason = "Shape: " + nodeshapepair.shapeLabel + " not found in Schema"
                return False, cntxt.process_reasons()
            parse_nodes.append(cntxt.current_node)
            if not satisfies(cntxt, n, s):
                cntxt.current_node.result = False
                return False, cntxt.process_reasons()
            else:
                cntxt.current_node.result = True
    return True, []
예제 #11
0
 def __init__(
         self,
         function: Callable[["Context", Union[RDFGraph, Node], JSGObject],
                            bool], expr: JSGObject,
         obj: Union[RDFGraph, Node], cntxt: "Context"):
     self.function = function
     self.expr = expr
     self.graph = obj if isinstance(obj, RDFGraph) else None
     self.node = obj if isinstance_(obj, Node) else None
     self.result: bool = None
     self._fail_reason: Optional[str] = None
     self.reason_stack: List[Tuple[Union[BNode, URIRef],
                                   Optional[str]]] = []
     self.nodes: List[ParseNode] = []
     self.n3m = cntxt.n3_mapper
예제 #12
0
def matchesExpr(cntxt: Context, T: RDFGraph, expr: ShExJ.tripleExpr, _: DebugContext) -> bool:
    """ Evaluate the expression

    """

    if isinstance(expr, ShExJ.OneOf):
        return matchesOneOf(cntxt, T, expr)
    elif isinstance(expr, ShExJ.EachOf):
        return matchesEachOf(cntxt, T, expr)
    elif isinstance(expr, ShExJ.TripleConstraint):
        return matchesCardinality(cntxt, T, expr)
    elif isinstance_(expr, ShExJ.tripleExprLabel):
        return matchesTripleExprRef(cntxt, T, expr)
    else:
        raise Exception("Unknown expression")
예제 #13
0
def satisfies(cntxt: Context, n: Node, se: ShExJ.shapeExpr) -> bool:
    """ `5.3 Shape Expressions <http://shex.io/shex-semantics/#node-constraint-semantics>`_

          satisfies: The expression satisfies(n, se, G, m) indicates that a node n and graph G satisfy a shape
                      expression se with shapeMap m.

           satisfies(n, se, G, m) is true if and only if:

            * Se is a NodeConstraint and satisfies2(n, se) as described below in Node Constraints.
                      Note that testing if a node satisfies a node constraint does not require a graph or shapeMap.
            * Se is a Shape and satisfies(n, se) as defined below in Shapes and Triple Expressions.
            * Se is a ShapeOr and there is some shape expression se2 in shapeExprs such that
            satisfies(n, se2, G, m).
            * Se is a ShapeAnd and for every shape expression se2 in shapeExprs, satisfies(n, se2, G, m).
            * Se is a ShapeNot and for the shape expression se2 at shapeExpr, notSatisfies(n, se2, G, m).
            * Se is a ShapeExternal and implementation-specific mechansims not defined in this specification
            indicate success.
            * Se is a shapeExprRef and there exists in the schema a shape expression se2 with that id and
                      satisfies(n, se2, G, m).

          .. note:: Where is the documentation on recursion?  All I can find is
           `5.9.4 Recursion Example <http://shex.io/shex-semantics/#example-recursion>`_
          """
    if isinstance(se, ShExJ.NodeConstraint):
        rval = satisfiesNodeConstraint(cntxt, n, se)
    elif isinstance(se, ShExJ.Shape):
        rval = satisfiesShape(cntxt, n, se)
    elif isinstance(se, ShExJ.ShapeOr):
        rval = satisifesShapeOr(cntxt, n, se)
    elif isinstance(se, ShExJ.ShapeAnd):
        rval = satisfiesShapeAnd(cntxt, n, se)
    elif isinstance(se, ShExJ.ShapeNot):
        rval = satisfiesShapeNot(cntxt, n, se)
    elif isinstance(se, ShExJ.ShapeExternal):
        rval = satisfiesExternal(cntxt, n, se)
    elif isinstance_(se, ShExJ.shapeExprLabel):
        rval = satisfiesShapeExprRef(cntxt, n, se)
    else:
        #raise NotImplementedError(f"Unrecognized shapeExpr: {type(se)}")
        raise NotImplementedError("Unrecognized shapeExpr: " + type(se))
    return rval
예제 #14
0
파일: p5_context.py 프로젝트: egonw/PyShEx
    def _gen_te_xref(
            self, expr: Union[ShExJ.tripleExpr,
                              ShExJ.tripleExprLabel]) -> None:
        """
        Generate the triple expression map (te_id_map)

        :param expr: root triple expression

        """
        if expr is not None and not isinstance_(
                expr, ShExJ.tripleExprLabel
        ) and 'id' in expr and expr.id is not None:
            if expr.id in self.te_id_map:
                return
            else:
                self.te_id_map[self._resolve_relative_uri(expr.id)] = expr
        if isinstance(expr, (ShExJ.OneOf, ShExJ.EachOf)):
            for expr2 in expr.expressions:
                self._gen_te_xref(expr2)
        elif isinstance(expr, ShExJ.TripleConstraint):
            if expr.valueExpr is not None:
                self._gen_schema_xref(expr.valueExpr)
예제 #15
0
파일: p5_context.py 프로젝트: egonw/PyShEx
    def _gen_schema_xref(
            self, expr: Optional[Union[ShExJ.shapeExprLabel,
                                       ShExJ.shapeExpr]]) -> None:
        """
        Generate the schema_id_map

        :param expr: root shape expression
        """
        if expr is not None and not isinstance_(
                expr,
                ShExJ.shapeExprLabel) and 'id' in expr and expr.id is not None:
            abs_id = self._resolve_relative_uri(expr.id)
            if abs_id not in self.schema_id_map:
                self.schema_id_map[abs_id] = expr
                if isinstance(expr, (ShExJ.ShapeOr, ShExJ.ShapeAnd)):
                    for expr2 in expr.shapeExprs:
                        self._gen_schema_xref(expr2)
                elif isinstance(expr, ShExJ.ShapeNot):
                    self._gen_schema_xref(expr.shapeExpr)
                elif isinstance(expr, ShExJ.Shape):
                    if expr.expression is not None:
                        self._gen_te_xref(expr.expression)
예제 #16
0
파일: p5_context.py 프로젝트: egonw/PyShEx
    def __init__(
        self,
        g: Optional[Graph],
        s: Schema,
        external_shape_resolver: Optional[Callable[
            [ShExJ.IRIREF], Optional[ShExJ.Shape]]] = None,
        base_namespace: Optional[Namespace] = None,
        shape_importer: Optional[Callable[[ShExJ.IRIREF],
                                          Optional[ShExJ.Schema]]] = None
    ) -> None:
        """
        Create a context consisting of an RDF Graph and a ShEx Schema and generate a identifier to
        item map.

        :param g: RDF graph
        :param s: ShExJ Schema instance
        :param external_shape_resolver: External resolution function
        :param base_namespace:
        """
        self.is_valid: bool = True
        self.error_list: List[str] = []
        self.graph: Graph = g
        self.n3_mapper = N3Mapper(g)
        self.schema: ShExJ.Schema = s
        self.schema_id_map: Dict[ShExJ.shapeExprLabel, ShExJ.shapeExpr] = {}
        self.te_id_map: Dict[ShExJ.tripleExprLabel, ShExJ.tripleExpr] = {}
        self.external_shape_for = external_shape_resolver if external_shape_resolver \
            else default_external_shape_resolver
        self.base_namespace = base_namespace if isinstance(base_namespace, Namespace) \
            else Namespace(base_namespace) if base_namespace else None
        self.shape_importer = shape_importer if shape_importer else default_shape_importer

        # For SPARQL API's, true means pull ALL predicate objects for a given subject, false means only the
        # predicates that are needed
        self.over_slurp = True

        # A list of node selectors/shape expressions that are being evaluated.  If we attempt to evaluate
        # an entry for a second time, we, instead, put the entry into the assumptions table.  We start with 'true'
        # and, if the result is 'true' then we count it as success.  If not, we switch to false and try again
        self.evaluating: Set[Tuple[Node, ShExJ.shapeExprLabel]] = set()
        self.assumptions: Dict[Tuple[Node, ShExJ.shapeExprLabel], bool] = {}

        # Known results -- a cache of existing evaluation results
        self.known_results: Dict[Tuple[Node, ShExJ.shapeExprLabel], bool] = {}

        # Debugging options
        self.debug_context = DebugContext()

        # Process imports
        if self.schema.imports is not None:
            for uri in self.schema.imports:
                imp_shape = self.shape_importer(uri, self)
                if not imp_shape:
                    # TODO: what to do on import failure
                    self.is_valid = False
                    self.error_list.append(f"Import failure on {uri}")

        if self.schema.start is not None:
            if not isinstance_(self.schema.start, ShExJ.shapeExprLabel) and\
                    'id' in self.schema.start and self.schema.start.id is None:
                self.schema.start.id = "_:start"
            self._gen_schema_xref(self.schema.start)
            # TODO: The logic below really belongs in the parser.  We shouldn't be messing with the schema here...
            if not isinstance_(self.schema.start, ShExJ.shapeExprLabel):
                if self.schema.shapes is None:
                    self.schema.shapes = [self.schema.start]
                else:
                    self.schema.shapes.append(self.schema.start)
                self.schema.start = self.schema.start.id
        if self.schema.shapes is not None:
            for e in self.schema.shapes:
                self._gen_schema_xref(e)

        self.current_node: ParseNode = None
        self.evaluate_stack: List[Tuple[Union[BNode, URIRef],
                                        Optional[str]]] = [
                                        ]  # Node / shape evaluation stacks
        self.bnode_map: Dict[BNode, str] = {}  # Map for prettifying bnodes