xandergraph 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Package definitions for the `xandergraph` library.
6
+ see copyright/license https://github.com/DerwenAI/xandergraph/README.md
7
+ """
8
+
9
+ from .kg import KnowledgeGraph
10
+ from .ottr import OttrGenerator, OttrInstances
11
+
xandergraph/kg.py ADDED
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ KnowledgeGraph definitions.
6
+ see copyright/license https://github.com/DerwenAI/xandergraph/README.md
7
+ """
8
+
9
+ import pathlib
10
+
11
+ import pyshacl
12
+ import rdflib
13
+
14
+ from .ottr import OttrGenerator, OttrInstances
15
+
16
+
17
+ class KnowledgeGraph:
18
+ """
19
+ Represents a knowledge graph, with accessors for both the `RDFlib`
20
+ semantic graph and the `NetworkX` property graph.
21
+ """
22
+ def __init__ (
23
+ self,
24
+ *,
25
+ ns: dict[ str, str ] = {},
26
+ ) -> None:
27
+ """
28
+ Constructor.
29
+ """
30
+ self.graph: rdflib.Graph = rdflib.Graph()
31
+
32
+ for prefix, ns_uri in ns.items():
33
+ self.graph.bind(prefix, rdflib.Namespace(ns_uri))
34
+
35
+ self.ottr_generator: OttrGenerator = OttrGenerator()
36
+
37
+
38
+ def load_stottr (
39
+ self,
40
+ stottr_path: pathlib.Path,
41
+ ) -> rdflib.Graph:
42
+ """
43
+ Define and load the OTTR templates.
44
+ """
45
+ with open(stottr_path, "r", encoding = "utf-8") as fp:
46
+ stottr_template: str = fp.read().strip()
47
+
48
+ self.ottr_generator.load_templates(
49
+ stottr_template,
50
+ format = "stottr",
51
+ )
52
+
53
+
54
+ def gen_ottr_rdf (
55
+ self,
56
+ rdf_data: str,
57
+ ) -> rdflib.Graph:
58
+ """
59
+ Generate RDF triples based on applying the given text data to the
60
+ loaded OTTR templates.
61
+ """
62
+ instances: OttrInstances = self.ottr_generator.instanciate(
63
+ rdf_data,
64
+ format = "stottr",
65
+ )
66
+
67
+ for s, p, o in instances.execute(as_nt = False):
68
+ self.graph.add((s, p, o))
69
+
70
+
71
+ def run_shacl (
72
+ self,
73
+ data_file: str,
74
+ shacl_file: str,
75
+ onto_file: str,
76
+ *,
77
+ inference: str = "rdfs",
78
+ debug: bool = False,
79
+ ) -> tuple[ bool, rdflib.Graph, str ]:
80
+ """
81
+ This wrapper calls `pySHACL`, a pure Python module which allows for
82
+ validating RDF graphs against Shapes Constraint Language (SHACL) shape
83
+ constraint rules.
84
+ """
85
+ return pyshacl.validate(
86
+ data_file,
87
+ shacl_graph = shacl_file,
88
+ ont_graph = onto_file,
89
+ inference = inference,
90
+ debug = debug,
91
+ abort_on_first = False,
92
+ allow_infos = False,
93
+ allow_warnings = False,
94
+ meta_shacl = False,
95
+ advanced = False,
96
+ js = False,
97
+ )
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Thomas Minier
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,92 @@
1
+ # pyOTTR
2
+ [![Build Status](https://travis-ci.com/Callidon/pyOTTR.svg?branch=master)](https://travis-ci.com/Callidon/pyOTTR)
3
+
4
+ Manipulate [OTTR Reasonable Ontology Templates](http://ottr.xyz/) in Python.
5
+
6
+ [Package documentation](https://callidon.github.io/pyOTTR)
7
+
8
+ [OTTR documentation](http://ottr.xyz/)
9
+
10
+ :white_check_mark: **Supported features:**
11
+ * [Definition and execution of templates](http://spec.ottr.xyz/pOTTR/0.1/01-basics.html#2_Templates_and_Instances) in the [stOTTR syntax](http://spec.ottr.xyz/stOTTR/0.1/)
12
+ * [Nesting templates](http://spec.ottr.xyz/pOTTR/0.1/01-basics.html#3_Nesting_templates)
13
+ * [Type checking](http://spec.ottr.xyz/pOTTR/0.1/01-basics.html#4_Types)
14
+ * [Non blank](http://spec.ottr.xyz/pOTTR/0.1/01-basics.html#5_NonBlank), [Optional](http://spec.ottr.xyz/pOTTR/0.1/01-basics.html#6_Optionals_and_None) and [default values](http://spec.ottr.xyz/pOTTR/0.1/01-basics.html#7_Default_values) for template parameters.
15
+ * *RDF and RDFS templates* from the [OTTR template library](http://tpl.ottr.xyz/) are loaded by default.
16
+
17
+ :wrench: **In development:**
18
+ * [Expansion modes](http://spec.ottr.xyz/pOTTR/0.1/01-basics.html#8_Expansion_modes)
19
+ * Support for [OWL templates](http://tpl.ottr.xyz/owl/) from the template library
20
+
21
+ # Installation
22
+
23
+ ## Using pip (recommended)
24
+
25
+ ```
26
+ pip install ottr
27
+ ```
28
+
29
+ ## Manual installation
30
+
31
+ **Requirement:** [poetry](https://python-poetry.org/) (v0.12 or higher).
32
+
33
+ ```
34
+ git clone https://github.com/Callidon/pyOTTR.git
35
+ cd pyOTTR/
36
+ poetry install
37
+ ```
38
+
39
+ # Getting started
40
+
41
+ The main class to manipulate is `OttrGenerator`, which is used to load OTTR templates and expand template instances.
42
+ So, in practice, you only need to create a new generator, load some templates and then execute your instances to produce RDF triples.
43
+ Otherwise, everything else is done using classic OTTR syntax!
44
+
45
+ By default, **all templates** from the [OTTR template library](http://tpl.ottr.xyz/) are loaded when the generator is created.
46
+
47
+ ```python
48
+ # an OttrGenerator is used to load templates and expand instances
49
+
50
+ import ottr
51
+
52
+ template: str = """
53
+ @prefix ex: <http://example.org#> .
54
+
55
+ ex:FirstName [ ottr:IRI ?uri, ?firstName ] :: {
56
+ ottr:Triple ( ?uri, foaf:firstName, ?firstName )
57
+ } .
58
+
59
+ ex:Person[ ?firstName ] :: {
60
+ ottr:Triple ( _:person, rdf:type, foaf:Person ),
61
+ ex:FirstName ( _:person, ?firstName )
62
+ } .
63
+ """.strip()
64
+
65
+ # load a simple OTTR template definition
66
+
67
+ generator: ottr.OttrGenerator = ottr.OttrGenerator()
68
+ generator.load_templates(template)
69
+
70
+ # parse and prepare an instance for execution
71
+
72
+ rdf_data: str = """
73
+ @prefix ex: <http://example.org#> .
74
+
75
+ ex:Person("Ann") .
76
+ """.strip()
77
+
78
+ instances: ottr.generator.OttrInstances = generator.instanciate(rdf_data)
79
+
80
+ # execute the instance, which yield RDF triples
81
+ # the following prints (_:person0, rdf:type, foaf:Person) and (_:person0, foaf:firstName, "Ann")
82
+
83
+ for s, p, o in instances.execute(as_nt = True):
84
+ print("# ----- RDF triple ----- #")
85
+ print((s, p, o)
86
+ ```
87
+
88
+
89
+ ## Addendum
90
+
91
+ Updated for more recent releases of `RDFlib` by [Derwen](https://derwen.ai)
92
+ since the source did not appear to have been maintained for ~7 years.
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Package definitions for the `pyOTTR` library.
6
+ see copyright/license https://github.com/DerwenAI/xandergraph/README.md
7
+ """
8
+
9
+ from .generator import OttrGenerator, OttrInstances
File without changes
@@ -0,0 +1,145 @@
1
+ # argument.py
2
+ # Author: Thomas MINIER - MIT License 2019-2020
3
+
4
+ from abc import ABC, abstractmethod
5
+ from typing import Iterable, Tuple
6
+
7
+ from rdflib import BNode, URIRef
8
+
9
+ from ... ottr.base.utils import OTTR
10
+ from ... ottr.types import ExpansionResults, InputBindings, Term
11
+
12
+
13
+ class InstanceArgument(ABC):
14
+ """An abstract instance argument, which corresponds to the parameter of a template.
15
+
16
+ Args:
17
+ * value: Argument's value.
18
+ * position: Argument's position in the template's parameters list.
19
+ """
20
+
21
+ def __init__(self, value: Term, position: int):
22
+ super(InstanceArgument, self).__init__()
23
+ self._value = value
24
+ self._position = position
25
+
26
+ def __str__(self) -> str:
27
+ return f"InstanceArgument({self._value}, {self._position})"
28
+
29
+ def __repr__(self) -> str:
30
+ return self.__str__()
31
+
32
+ @property
33
+ def value(self) -> Term:
34
+ """The argument's value"""
35
+ return self._value
36
+
37
+ @property
38
+ def position(self) -> int:
39
+ """The argument's position in the template's parameters list"""
40
+ return self._position
41
+
42
+ @property
43
+ def is_bound(self) -> bool:
44
+ """Return True if the argument is bound (it is not a Variable), False otherwise"""
45
+ return False
46
+
47
+ @abstractmethod
48
+ def evaluate(self, bindings: InputBindings = dict(), bnode_suffix: Tuple[int, int] = (0, 0), as_nt: bool = False) -> Iterable[ExpansionResults]:
49
+ """Evaluate the argument using an optional set of bindings.
50
+
51
+ Args:
52
+ * bindings: set of bindings used for evaluation.
53
+ * bnode_suffix: Pair of suffixes used for creating unique blank nodes.
54
+ * as_nt: True if the RDF triples produced should be in n-triples format, False to use the rdflib format.
55
+
56
+ Yields:
57
+ RDF triples, in rdflib or n-triples format.
58
+ """
59
+ pass
60
+
61
+
62
+ class ConcreteArgument(InstanceArgument):
63
+ """An argument that evaluates to a constant RDF term, i.e., a RDF term.
64
+
65
+ Args:
66
+ * value: Argument's value.
67
+ * position: Argument's position in the template's parameters list.
68
+ """
69
+
70
+ def __init__(self, value: Term, position: int):
71
+ super(ConcreteArgument, self).__init__(value, position)
72
+
73
+ def __str__(self) -> str:
74
+ return f"ConcreteArgument({self._value}, {self._position})"
75
+
76
+ @property
77
+ def is_bound(self) -> bool:
78
+ return True
79
+
80
+ def evaluate(self, bindings: InputBindings = dict(), bnode_suffix: Tuple[int, int] = (0, 0), as_nt: bool = False) -> Iterable[ExpansionResults]:
81
+ """Evaluate the argument using an optional set of bindings.
82
+
83
+ Args:
84
+ * bindings: set of bindings used for evaluation.
85
+ * bnode_suffix: Pair of suffixes used for creating unique blank nodes.
86
+ * as_nt: True if the RDF triples produced should be in n-triples format, False to use the rdflib format.
87
+
88
+ Yields:
89
+ RDF triples, in rdflib or n-triples format.
90
+ """
91
+ term = self._value
92
+ if type(term) == BNode and bnode_suffix is not None:
93
+ term = BNode(f"{term}_{bnode_suffix[0]}_{bnode_suffix[1]}")
94
+ return term.n3() if as_nt else term
95
+
96
+
97
+ class URIArgument(ConcreteArgument):
98
+ """A ConcreteArgument that always evaluates to an URI.
99
+
100
+ Args:
101
+ * value: Argument's value (an URI).
102
+ * position: Argument's position in the template's parameters list.
103
+ """
104
+
105
+ def __init__(self, uri: URIRef, position: int):
106
+ super(URIArgument, self).__init__(URIRef(uri), position)
107
+
108
+ def __str__(self) -> str:
109
+ return f"URIArgument({self._value}, {self._position})"
110
+
111
+
112
+ class VariableArgument(InstanceArgument):
113
+ """A variable argument, i.e., a SPARQL variable.
114
+
115
+ Args:
116
+ * value: Argument's value (a SPARQL variable).
117
+ * position: Argument's position in the template's parameters list.
118
+ """
119
+
120
+ def __init__(self, value: Term, position: int):
121
+ super(VariableArgument, self).__init__(value, position)
122
+
123
+ def __str__(self) -> str:
124
+ return f"VariableArgument({self._value}, {self._position})"
125
+
126
+ def evaluate(self, bindings: InputBindings = dict(), bnode_suffix: Tuple[int, int] = (0, 0), as_nt: bool = False) -> Iterable[ExpansionResults]:
127
+ """Evaluate the argument using an optional set of bindings.
128
+
129
+ Args:
130
+ * bindings: set of bindings used for evaluation.
131
+ * bnode_suffix: Pair of suffixes used for creating unique blank nodes.
132
+ * as_nt: True if the RDF triples produced should be in n-triples format, False to use the rdflib format.
133
+
134
+ Yields:
135
+ RDF triples, in rdflib or n-triples format.
136
+ """
137
+ if self._value in bindings:
138
+ term = bindings[self._value]
139
+
140
+ if type(term) == BNode and bnode_suffix is not None:
141
+ term = BNode(f"{term}_{bnode_suffix[0]}_{bnode_suffix[1]}")
142
+
143
+ return term.n3() if as_nt else term
144
+
145
+ return OTTR.none
@@ -0,0 +1,54 @@
1
+ # base_templates.py
2
+ # Author: Thomas MINIER - MIT License 2019-2020
3
+ from typing import Dict, Iterable, Tuple
4
+
5
+ from rdflib import URIRef
6
+
7
+ from ... ottr.base.argument import InstanceArgument
8
+ from ... ottr.base.template import AbstractTemplate
9
+ from ... ottr.base.utils import OTTR_TRIPLE_URI
10
+ from ... ottr.types import ExpansionResults, InputBindings
11
+
12
+
13
+ class OttrTriple(AbstractTemplate):
14
+ """The default ottr:Triple base template, which expand to a RDF triple.
15
+
16
+ Args:
17
+ * subject_arg: Subject argument of the template instance.
18
+ * predicate_arg: Predicate argument of the template instance.
19
+ * object_arg: Object argument of the template instance.
20
+ """
21
+
22
+ def __init__(self, subject_arg: InstanceArgument, predicate_arg: InstanceArgument, object_arg: InstanceArgument):
23
+ super(OttrTriple, self).__init__(OTTR_TRIPLE_URI)
24
+ self._subject_arg = subject_arg
25
+ self._predicate_arg = predicate_arg
26
+ self._object_arg = object_arg
27
+
28
+ def __str__(self) -> str:
29
+ return f"ottr:Triple ({self._subject_arg}, {self._predicate_arg}, {self._object_arg}) :: BASE ."
30
+
31
+ def __repr__(self) -> str:
32
+ return self.__str__()
33
+
34
+ def is_base(self) -> bool:
35
+ """Returns True if the template is a base template, False otherwise"""
36
+ return True
37
+
38
+ def expand(self, arguments: InputBindings, all_templates: Dict[URIRef, AbstractTemplate], bnode_suffix: Tuple[int, int] = (0, 0), as_nt: bool = False) -> Iterable[ExpansionResults]:
39
+ """Expands the template and yields a single RDF triple.
40
+
41
+ Args:
42
+ * arguments: Template instantation arguments.
43
+ * all_templates: Map of all templates known at expansion times.
44
+ * bnode_suffix: Pair of suffixes used for creating unique blank nodes.
45
+ * as_nt: True if the RDF triples produced should be in n-triples format, False to use the rdflib format.
46
+
47
+ Yields:
48
+ A RDF triple, in rdflib or n-triples format.
49
+ """
50
+ yield (
51
+ self._subject_arg.evaluate(bindings=arguments, bnode_suffix=bnode_suffix, as_nt=as_nt),
52
+ self._predicate_arg.evaluate(bindings=arguments, bnode_suffix=bnode_suffix, as_nt=as_nt),
53
+ self._object_arg.evaluate(bindings=arguments, bnode_suffix=bnode_suffix, as_nt=as_nt)
54
+ )
@@ -0,0 +1,50 @@
1
+ # expansion.py
2
+ # Author: Thomas MINIER - MIT License 2019-2020
3
+
4
+ from typing import Dict, Iterable, Tuple
5
+
6
+ from rdflib import URIRef, Variable
7
+
8
+ from ... ottr.base.template import AbstractTemplate
9
+ from ... ottr.types import ExpansionResults, InputBindings
10
+
11
+
12
+ class CrossTemplate(AbstractTemplate):
13
+ """A CrossTemplate expands a template instance using the 'cross' expansion mode.
14
+
15
+ Args:
16
+ * name: Template's name.
17
+ * instance: Template instance to expand with the 'cross' expansion mode.
18
+ * cross_variable: Variable which binds to the list of arguments for the cross-operator.
19
+ """
20
+
21
+ def __init__(self, name: URIRef, instance: AbstractTemplate, cross_variable: Variable):
22
+ super(CrossTemplate, self).__init__(name)
23
+ self._inner_instance = instance
24
+ self._cross_variable = cross_variable
25
+
26
+ def expand(self, arguments: InputBindings, all_templates: Dict[URIRef, AbstractTemplate], bnode_suffix: Tuple[int, int] = (0, 0), as_nt: bool = False) -> Iterable[ExpansionResults]:
27
+ """Expands the template and yields RDF triples.
28
+
29
+ Args:
30
+ * arguments: Template instantation arguments.
31
+ * all_templates: Map of all templates known at expansion times.
32
+ * bnode_suffix: Pair of suffixes used for creating unique blank nodes.
33
+ * as_nt: True if the RDF triples produced should be in n-triples format, False to use the rdflib format.
34
+
35
+ Yields:
36
+ RDF triples, in rdflib or n-triples format.
37
+ """
38
+ # assert that the cross variable is found in the arguments
39
+ if self._cross_variable in arguments:
40
+ # invoke inner instance with each value of the list variable
41
+ for value in arguments[self._cross_variable]:
42
+ # copy arguments and inject the local value for the cross variable
43
+ local_args: InputBindings = dict()
44
+ for k, v in arguments.items():
45
+ if k == self._cross_variable:
46
+ local_args[k] = value
47
+ else:
48
+ local_args[k] = v
49
+ # recursively invoke the inner instance with the new set of arguments
50
+ yield from self._inner_instance.expand(local_args, all_templates, bnode_suffix=bnode_suffix, as_nt=as_nt)