semantikon 0.post0.dev117__tar.gz

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,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, Max-Planck-Institut für Nachhaltige Materialien GmbH - Computational Materials Design (CM) Department
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ * Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ include LICENSE
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.1
2
+ Name: semantikon
3
+ Version: 0.post0.dev117
4
+ Summary: semantikon - Ontological type system
5
+ Author-email: Sam Waseda <waseda@mpie.de>
6
+ License: BSD 3-Clause License
7
+
8
+ Copyright (c) 2024, Max-Planck-Institut für Nachhaltige Materialien GmbH - Computational Materials Design (CM) Department
9
+ All rights reserved.
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions are met:
13
+
14
+ * Redistributions of source code must retain the above copyright notice, this
15
+ list of conditions and the following disclaimer.
16
+
17
+ * Redistributions in binary form must reproduce the above copyright notice,
18
+ this list of conditions and the following disclaimer in the documentation
19
+ and/or other materials provided with the distribution.
20
+
21
+ * Neither the name of the copyright holder nor the names of its
22
+ contributors may be used to endorse or promote products derived from
23
+ this software without specific prior written permission.
24
+
25
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
+
36
+ Project-URL: Homepage, https://pyiron.org/
37
+ Project-URL: Documentation, https://semantikon.readthedocs.io
38
+ Project-URL: Repository, https://github.com/pyiron/semantikon
39
+ Keywords: pyiron
40
+ Classifier: Development Status :: 3 - Alpha
41
+ Classifier: Topic :: Scientific/Engineering
42
+ Classifier: License :: OSI Approved :: BSD License
43
+ Classifier: Intended Audience :: Science/Research
44
+ Classifier: Operating System :: OS Independent
45
+ Classifier: Programming Language :: Python :: 3.9
46
+ Classifier: Programming Language :: Python :: 3.10
47
+ Classifier: Programming Language :: Python :: 3.11
48
+ Classifier: Programming Language :: Python :: 3.12
49
+ Requires-Python: <3.13,>=3.9
50
+ Description-Content-Type: text/markdown
51
+ License-File: LICENSE
52
+ Requires-Dist: numpy==2.1.3
53
+ Requires-Dist: pint==0.24.4
54
+
55
+ # semantikon
56
+
57
+ ## Overview
58
+
59
+ In the realm of the workflow management systems, there are well defined inputs and outputs for each node. `semantikon` is a Python package to give scientific context to node inputs and outputs by providing type hinting and interpreters. Therefore, it consists of two **fully** separate parts: type hinting and interpreters.
60
+
61
+ ### **Type hinting**
62
+
63
+ `semantikon` provides a way to define types for any number of input parameters and any number of output values for function via type hinting, in particular: data type, unit and ontological type. Type hinting is done with the function `u`, which **requires** the type, and **optionally** you can define the units and the ontological type. The type hinting is done in the following way:
64
+
65
+ ```python
66
+ from semantikon.typing import u
67
+
68
+ def my_function(
69
+ a: u(int, units="meter"),
70
+ b: u(int, units="second")
71
+ ) -> u(int, units="meter/second", label="speed"):
72
+ return a / b
73
+ ```
74
+
75
+ `semantikon`'s type hinting does not require to follow any particular standard. It only needs to be compatible with the interpreter applied.
76
+
77
+ There are two possible ways to store the data for `semantikon`. The standard way is to do it by converting all arguments except for the data type as a string, which is the default behaviour. The other way is to store the data as a list, which is turned on by setting `use_list=True`. In most cases, the default behaviour is the safest option; in some cases, especially when the data cannot be represented as a string, you might want to switch on `use_list`, but `semantikon` is still under intensive development, and therefore there is no guarantee that you can retrieve the data across different versions correctly.
78
+
79
+
80
+ ### **Interpreters**
81
+
82
+ #### General interpreter
83
+
84
+ In order to extract argument information, you can use the functions `parse_input_args` and `parse_output_args`. `parse_input_args` parses the input variables and return a dictionary with the variable names as keys and the variable information as values. `parse_output_args` parses the output variables and return a dictionary with the variable information as values if there is one output variable, or a list of dictionaries if it is a tuple.
85
+
86
+ Example:
87
+
88
+ ```python
89
+ from semantikon.typing import u
90
+ from semantikon.converter import parse_input_args, parse_output_args
91
+
92
+ def my_function(
93
+ a: u(int, units="meter"),
94
+ b: u(int, units="second")
95
+ ) -> u(int, units="meter/second", label="speed"):
96
+ return a / b
97
+
98
+ print(parse_input_args(my_function))
99
+ print(parse_output_args(my_function))
100
+ ```
101
+
102
+ Output:
103
+
104
+ ```python
105
+ {'distance': {'units': 'meter', 'label': None, 'uri': None, 'shape': None, 'dtype': <class 'float'>}, 'time': {'units': 'second', 'label': None, 'uri': None, 'shape': None, 'dtype': <class 'float'>}}
106
+ {'units': 'meter/second', 'label': 'speed', 'uri': None, 'shape': None, 'dtype': <class 'float'>}
107
+ ```
108
+
109
+ Here the output is the same whether `use_list` is set to `True` or `False`. When `use_list` is `False`, you can use additionally any tag that you want to store. When `use_list` is `True`, you can store only the data type, `units`, `label`, `uri`, `shape` and `dtype`.
110
+
111
+ Future announcement: There will be no distrinction between `use_list=True` and `use_list=False` when the official support of python 3.10 is dropped (i.e. around autumn 2026).
112
+
113
+ #### Unit conversion with `pint`
114
+
115
+ `semantikon` provides a way to interpret the types of inputs and outputs of a function via a decorator, in order to check consistency of the types and to convert them if necessary. Currently, `semantikon` provides an interpreter for `pint.UnitRegistry` objects. The interpreter is applied in the following way:
116
+
117
+ ```python
118
+ from semantikon.typing import u
119
+ from semantikon.converters import units
120
+ from pint import UnitRegistry
121
+
122
+ @units
123
+ def my_function(
124
+ a: u(int, units="meter"),
125
+ b: u(int, units="second")
126
+ ) -> u(int, units="meter/second", label="speed"):
127
+ return a / b
128
+
129
+
130
+ ureg = UnitRegistry()
131
+
132
+ print(my_function(1 * ureg.meter, 1 * ureg.second))
133
+ ```
134
+
135
+ Output: `1.0 meter / second`
136
+
137
+
138
+ The interpreters check all types and, if necessary, convert them to the expected types **before** the function is executed, in order for all possible errors would be raised before the function execution. The interpreters convert the types in the way that the underlying function would receive the raw values.
139
+
140
+ In case there are multiple outputs, the type hints are to be passed as a tuple (e.g. `(u(int, "meter"), u(int, "second"))`).
141
+
142
+ Interpreters can distinguish between annotated arguments and non-anotated arguments. If the argument is annotated, the interpreter will try to convert the argument to the expected type. If the argument is not annotated, the interpreter will pass the argument as is.
143
+
144
+ Regardless of type hints are given or not, the interpreter acts only when the input values contain units and ontological types. If the input values do not contain units and ontological types, the interpreter will pass the input values to the function as is.
145
+
146
+
@@ -0,0 +1,92 @@
1
+ # semantikon
2
+
3
+ ## Overview
4
+
5
+ In the realm of the workflow management systems, there are well defined inputs and outputs for each node. `semantikon` is a Python package to give scientific context to node inputs and outputs by providing type hinting and interpreters. Therefore, it consists of two **fully** separate parts: type hinting and interpreters.
6
+
7
+ ### **Type hinting**
8
+
9
+ `semantikon` provides a way to define types for any number of input parameters and any number of output values for function via type hinting, in particular: data type, unit and ontological type. Type hinting is done with the function `u`, which **requires** the type, and **optionally** you can define the units and the ontological type. The type hinting is done in the following way:
10
+
11
+ ```python
12
+ from semantikon.typing import u
13
+
14
+ def my_function(
15
+ a: u(int, units="meter"),
16
+ b: u(int, units="second")
17
+ ) -> u(int, units="meter/second", label="speed"):
18
+ return a / b
19
+ ```
20
+
21
+ `semantikon`'s type hinting does not require to follow any particular standard. It only needs to be compatible with the interpreter applied.
22
+
23
+ There are two possible ways to store the data for `semantikon`. The standard way is to do it by converting all arguments except for the data type as a string, which is the default behaviour. The other way is to store the data as a list, which is turned on by setting `use_list=True`. In most cases, the default behaviour is the safest option; in some cases, especially when the data cannot be represented as a string, you might want to switch on `use_list`, but `semantikon` is still under intensive development, and therefore there is no guarantee that you can retrieve the data across different versions correctly.
24
+
25
+
26
+ ### **Interpreters**
27
+
28
+ #### General interpreter
29
+
30
+ In order to extract argument information, you can use the functions `parse_input_args` and `parse_output_args`. `parse_input_args` parses the input variables and return a dictionary with the variable names as keys and the variable information as values. `parse_output_args` parses the output variables and return a dictionary with the variable information as values if there is one output variable, or a list of dictionaries if it is a tuple.
31
+
32
+ Example:
33
+
34
+ ```python
35
+ from semantikon.typing import u
36
+ from semantikon.converter import parse_input_args, parse_output_args
37
+
38
+ def my_function(
39
+ a: u(int, units="meter"),
40
+ b: u(int, units="second")
41
+ ) -> u(int, units="meter/second", label="speed"):
42
+ return a / b
43
+
44
+ print(parse_input_args(my_function))
45
+ print(parse_output_args(my_function))
46
+ ```
47
+
48
+ Output:
49
+
50
+ ```python
51
+ {'distance': {'units': 'meter', 'label': None, 'uri': None, 'shape': None, 'dtype': <class 'float'>}, 'time': {'units': 'second', 'label': None, 'uri': None, 'shape': None, 'dtype': <class 'float'>}}
52
+ {'units': 'meter/second', 'label': 'speed', 'uri': None, 'shape': None, 'dtype': <class 'float'>}
53
+ ```
54
+
55
+ Here the output is the same whether `use_list` is set to `True` or `False`. When `use_list` is `False`, you can use additionally any tag that you want to store. When `use_list` is `True`, you can store only the data type, `units`, `label`, `uri`, `shape` and `dtype`.
56
+
57
+ Future announcement: There will be no distrinction between `use_list=True` and `use_list=False` when the official support of python 3.10 is dropped (i.e. around autumn 2026).
58
+
59
+ #### Unit conversion with `pint`
60
+
61
+ `semantikon` provides a way to interpret the types of inputs and outputs of a function via a decorator, in order to check consistency of the types and to convert them if necessary. Currently, `semantikon` provides an interpreter for `pint.UnitRegistry` objects. The interpreter is applied in the following way:
62
+
63
+ ```python
64
+ from semantikon.typing import u
65
+ from semantikon.converters import units
66
+ from pint import UnitRegistry
67
+
68
+ @units
69
+ def my_function(
70
+ a: u(int, units="meter"),
71
+ b: u(int, units="second")
72
+ ) -> u(int, units="meter/second", label="speed"):
73
+ return a / b
74
+
75
+
76
+ ureg = UnitRegistry()
77
+
78
+ print(my_function(1 * ureg.meter, 1 * ureg.second))
79
+ ```
80
+
81
+ Output: `1.0 meter / second`
82
+
83
+
84
+ The interpreters check all types and, if necessary, convert them to the expected types **before** the function is executed, in order for all possible errors would be raised before the function execution. The interpreters convert the types in the way that the underlying function would receive the raw values.
85
+
86
+ In case there are multiple outputs, the type hints are to be passed as a tuple (e.g. `(u(int, "meter"), u(int, "second"))`).
87
+
88
+ Interpreters can distinguish between annotated arguments and non-anotated arguments. If the argument is annotated, the interpreter will try to convert the argument to the expected type. If the argument is not annotated, the interpreter will pass the argument as is.
89
+
90
+ Regardless of type hints are given or not, the interpreter acts only when the input values contain units and ontological types. If the input values do not contain units and ontological types, the interpreter will pass the input values to the function as is.
91
+
92
+
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools",
4
+ "versioneer[toml]==0.29",
5
+ ]
6
+ build-backend = "setuptools.build_meta"
7
+
8
+ [project]
9
+ name = "semantikon"
10
+ description = "semantikon - Ontological type system"
11
+ readme = "docs/README.md"
12
+ keywords = [ "pyiron",]
13
+ requires-python = ">=3.9, <3.13"
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Topic :: Scientific/Engineering",
17
+ "License :: OSI Approved :: BSD License",
18
+ "Intended Audience :: Science/Research",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ ]
25
+ dependencies = [
26
+ "numpy==2.1.3",
27
+ "pint==0.24.4"
28
+ ]
29
+ dynamic = [ "version",]
30
+ authors = [
31
+ { name = "Sam Waseda", email = "waseda@mpie.de" },
32
+ ]
33
+ license = {file = "LICENSE"}
34
+
35
+ [project.urls]
36
+ Homepage = "https://pyiron.org/"
37
+ Documentation = "https://semantikon.readthedocs.io"
38
+ Repository = "https://github.com/pyiron/semantikon"
39
+
40
+ [tool.versioneer]
41
+ VCS = "git"
42
+ style = "pep440-pre"
43
+ versionfile_source = "semantikon/_version.py"
44
+ parentdir_prefix = "semantikon"
45
+ tag_prefix = "semantikon-"
46
+
47
+ [tool.setuptools.packages.find]
48
+ include = [ "semantikon*",]
49
+
50
+ [tool.setuptools.dynamic.version]
51
+ attr = "semantikon.__version__"
@@ -0,0 +1,3 @@
1
+ from . import _version
2
+
3
+ __version__ = _version.get_versions()["version"]
@@ -0,0 +1,21 @@
1
+
2
+ # This file was generated by 'versioneer.py' (0.29) from
3
+ # revision-control system data, or from the parent directory name of an
4
+ # unpacked source archive. Distribution tarballs contain a pre-generated copy
5
+ # of this file.
6
+
7
+ import json
8
+
9
+ version_json = '''
10
+ {
11
+ "date": "2024-12-09T21:22:15+0100",
12
+ "dirty": true,
13
+ "error": null,
14
+ "full-revisionid": "3278605f1428382dd40683a6a9daa993088f7e47",
15
+ "version": "0.post0.dev117"
16
+ }
17
+ ''' # END VERSION_JSON
18
+
19
+
20
+ def get_versions():
21
+ return json.loads(version_json)
@@ -0,0 +1,208 @@
1
+ # coding: utf-8
2
+ # Copyright (c) Max-Planck-Institut für Eisenforschung GmbH - Computational Materials Design (CM) Department
3
+ # Distributed under the terms of "New BSD License", see the LICENSE file.
4
+
5
+ from pint import Quantity
6
+ import inspect
7
+ from functools import wraps
8
+ from pint.registry_helpers import (
9
+ _apply_defaults,
10
+ _parse_wrap_args,
11
+ _to_units_container,
12
+ _replace_units,
13
+ )
14
+ from ast import literal_eval
15
+
16
+ __author__ = "Sam Waseda"
17
+ __copyright__ = (
18
+ "Copyright 2021, Max-Planck-Institut für Eisenforschung GmbH "
19
+ "- Computational Materials Design (CM) Department"
20
+ )
21
+ __version__ = "1.0"
22
+ __maintainer__ = "Sam Waseda"
23
+ __email__ = "waseda@mpie.de"
24
+ __status__ = "development"
25
+ __date__ = "Aug 21, 2021"
26
+
27
+
28
+ def _get_ureg(args, kwargs):
29
+ for arg in args + tuple(kwargs.values()):
30
+ if isinstance(arg, Quantity):
31
+ return arg._REGISTRY
32
+ return None
33
+
34
+
35
+ def parse_metadata(value):
36
+ """
37
+ Parse the metadata of a Quantity object.
38
+
39
+ Args:
40
+ value: Quantity object
41
+
42
+ Returns:
43
+ dictionary of the metadata. Available keys are `units`, `otype`,
44
+ `shape`, and `dtype`. See `semantikon.typing.u` for more details.
45
+ """
46
+ # When there is only one metadata `use_list=False` must have been used
47
+ if len(value.__metadata__) == 1:
48
+ return literal_eval(value.__metadata__[0])
49
+ else:
50
+ return dict(zip(["units", "label", "uri", "shape"], value.__metadata__))
51
+
52
+
53
+ def _meta_to_dict(value):
54
+ if hasattr(value, "__metadata__"):
55
+ result = parse_metadata(value)
56
+ result["dtype"] = value.__args__[0]
57
+ return result
58
+ elif value is not inspect.Parameter.empty:
59
+ return {
60
+ "units": None,
61
+ "label": None,
62
+ "uri": None,
63
+ "shape": None,
64
+ "dtype": value,
65
+ }
66
+ else:
67
+ return None
68
+
69
+
70
+ def parse_input_args(func: callable):
71
+ """
72
+ Parse the input arguments of a function.
73
+
74
+ Args:
75
+ func: function to be parsed
76
+
77
+ Returns:
78
+ dictionary of the input arguments. Available keys are `units`, `otype`,
79
+ and `shape`. See `semantikon.typing.u` for more details.
80
+ """
81
+ return {
82
+ key: _meta_to_dict(value.annotation)
83
+ for key, value in inspect.signature(func).parameters.items()
84
+ }
85
+
86
+
87
+ def parse_output_args(func: callable):
88
+ """
89
+ Parse the output arguments of a function.
90
+
91
+ Args:
92
+ func: function to be parsed
93
+
94
+ Returns:
95
+ dictionary of the output arguments if there is only one output. Otherwise,
96
+ a list of dictionaries is returned. Available keys are `units`, `otype`,
97
+ and `shape`. See `semantikon.typing.u` for more details.
98
+ """
99
+ sig = inspect.signature(func)
100
+ if isinstance(sig.return_annotation, tuple):
101
+ return tuple([_meta_to_dict(ann) for ann in sig.return_annotation])
102
+ else:
103
+ return _meta_to_dict(sig.return_annotation)
104
+
105
+
106
+ def _get_converter(func):
107
+ args = []
108
+ for value in parse_input_args(func).values():
109
+ if value is not None:
110
+ args.append(value["units"])
111
+ else:
112
+ args.append(None)
113
+ if any([arg is not None for arg in args]):
114
+ return _parse_wrap_args(args)
115
+ else:
116
+ return None
117
+
118
+
119
+ def _get_ret_units(output, ureg, names):
120
+ if output is None:
121
+ return None
122
+ ret = _to_units_container(output["units"], ureg)
123
+ return ureg.Quantity(1, _replace_units(ret[0], names) if ret[1] else ret[0])
124
+
125
+
126
+ def _get_output_units(output, ureg, names):
127
+ if isinstance(output, tuple):
128
+ return tuple([_get_ret_units(oo, ureg, names) for oo in output])
129
+ else:
130
+ return _get_ret_units(output, ureg, names)
131
+
132
+
133
+ def units(func):
134
+ """
135
+ Decorator to convert the output of a function to a Quantity object with
136
+ the specified units.
137
+
138
+ Args:
139
+ func: function to be decorated
140
+
141
+ Returns:
142
+ decorated function
143
+ """
144
+ sig = inspect.signature(func)
145
+ converter = _get_converter(func)
146
+
147
+ @wraps(func)
148
+ def wrapper(*args, **kwargs):
149
+ ureg = _get_ureg(args, kwargs)
150
+ if converter is None or ureg is None:
151
+ return func(*args, **kwargs)
152
+ args, kwargs = _apply_defaults(sig, args, kwargs)
153
+ args, kwargs, names = converter(ureg, sig, args, kwargs, strict=False)
154
+ try:
155
+ output_units = _get_output_units(parse_output_args(func), ureg, names)
156
+ except AttributeError:
157
+ output_units = None
158
+ if output_units is None:
159
+ return func(*args, **kwargs)
160
+ elif isinstance(output_units, tuple):
161
+ return tuple(
162
+ [oo * ff for oo, ff in zip(output_units, func(*args, **kwargs))]
163
+ )
164
+ else:
165
+ return output_units * func(*args, **kwargs)
166
+
167
+ return wrapper
168
+
169
+
170
+ def semantikon_class(cls: type):
171
+ """
172
+ A class decorator to append type hints to class attributes.
173
+
174
+ Args:
175
+ cls: class to be decorated
176
+
177
+ Returns:
178
+ The modified class with type hints appended to its attributes.
179
+
180
+ Comments:
181
+
182
+ >>> from typing import Annotated
183
+ >>> from semantikon.converter import semantikon_class
184
+
185
+ >>> @semantikon_class
186
+ >>> class Pizza:
187
+ >>> price: Annotated[float, "money"]
188
+ >>> size: Annotated[float, "dimension"]
189
+
190
+ >>> class Topping:
191
+ >>> sauce: Annotated[str, "matter"]
192
+
193
+ >>> append_types(Pizza)
194
+ >>> print(Pizza)
195
+ >>> print(Pizza.Topping)
196
+ >>> print(Pizza.size)
197
+ >>> print(Pizza.price)
198
+ >>> print(Pizza.Topping.sauce)
199
+ """
200
+ for key, value in cls.__dict__.items():
201
+ if isinstance(value, type):
202
+ semantikon_class(getattr(cls, key)) # Recursively apply to nested classes
203
+ try:
204
+ for key, value in cls.__annotations__.items():
205
+ setattr(cls, key, value) # Append type hints to attributes
206
+ except AttributeError:
207
+ pass
208
+ return cls
@@ -0,0 +1,32 @@
1
+ from typing import Annotated, Any
2
+
3
+ __author__ = "Sam Waseda"
4
+ __copyright__ = (
5
+ "Copyright 2021, Max-Planck-Institut für Eisenforschung GmbH "
6
+ "- Computational Materials Design (CM) Department"
7
+ )
8
+ __version__ = "1.0"
9
+ __maintainer__ = "Sam Waseda"
10
+ __email__ = "waseda@mpie.de"
11
+ __status__ = "development"
12
+ __date__ = "Aug 21, 2021"
13
+
14
+
15
+ def u(
16
+ type_,
17
+ /,
18
+ units: str | None = None,
19
+ label: str | None = None,
20
+ uri: str | None = None,
21
+ shape: tuple[int] | None = None,
22
+ use_list: bool = False,
23
+ **kwargs,
24
+ ):
25
+ if use_list:
26
+ if len(kwargs) > 0:
27
+ raise ValueError("kwargs are not allowed when use_list=True")
28
+ return Annotated[type_, units, label, uri, shape]
29
+ else:
30
+ result = {"units": units, "label": label, "uri": uri, "shape": shape}
31
+ result.update(kwargs)
32
+ return Annotated[type_, str(result)]
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.1
2
+ Name: semantikon
3
+ Version: 0.post0.dev117
4
+ Summary: semantikon - Ontological type system
5
+ Author-email: Sam Waseda <waseda@mpie.de>
6
+ License: BSD 3-Clause License
7
+
8
+ Copyright (c) 2024, Max-Planck-Institut für Nachhaltige Materialien GmbH - Computational Materials Design (CM) Department
9
+ All rights reserved.
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions are met:
13
+
14
+ * Redistributions of source code must retain the above copyright notice, this
15
+ list of conditions and the following disclaimer.
16
+
17
+ * Redistributions in binary form must reproduce the above copyright notice,
18
+ this list of conditions and the following disclaimer in the documentation
19
+ and/or other materials provided with the distribution.
20
+
21
+ * Neither the name of the copyright holder nor the names of its
22
+ contributors may be used to endorse or promote products derived from
23
+ this software without specific prior written permission.
24
+
25
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
+
36
+ Project-URL: Homepage, https://pyiron.org/
37
+ Project-URL: Documentation, https://semantikon.readthedocs.io
38
+ Project-URL: Repository, https://github.com/pyiron/semantikon
39
+ Keywords: pyiron
40
+ Classifier: Development Status :: 3 - Alpha
41
+ Classifier: Topic :: Scientific/Engineering
42
+ Classifier: License :: OSI Approved :: BSD License
43
+ Classifier: Intended Audience :: Science/Research
44
+ Classifier: Operating System :: OS Independent
45
+ Classifier: Programming Language :: Python :: 3.9
46
+ Classifier: Programming Language :: Python :: 3.10
47
+ Classifier: Programming Language :: Python :: 3.11
48
+ Classifier: Programming Language :: Python :: 3.12
49
+ Requires-Python: <3.13,>=3.9
50
+ Description-Content-Type: text/markdown
51
+ License-File: LICENSE
52
+ Requires-Dist: numpy==2.1.3
53
+ Requires-Dist: pint==0.24.4
54
+
55
+ # semantikon
56
+
57
+ ## Overview
58
+
59
+ In the realm of the workflow management systems, there are well defined inputs and outputs for each node. `semantikon` is a Python package to give scientific context to node inputs and outputs by providing type hinting and interpreters. Therefore, it consists of two **fully** separate parts: type hinting and interpreters.
60
+
61
+ ### **Type hinting**
62
+
63
+ `semantikon` provides a way to define types for any number of input parameters and any number of output values for function via type hinting, in particular: data type, unit and ontological type. Type hinting is done with the function `u`, which **requires** the type, and **optionally** you can define the units and the ontological type. The type hinting is done in the following way:
64
+
65
+ ```python
66
+ from semantikon.typing import u
67
+
68
+ def my_function(
69
+ a: u(int, units="meter"),
70
+ b: u(int, units="second")
71
+ ) -> u(int, units="meter/second", label="speed"):
72
+ return a / b
73
+ ```
74
+
75
+ `semantikon`'s type hinting does not require to follow any particular standard. It only needs to be compatible with the interpreter applied.
76
+
77
+ There are two possible ways to store the data for `semantikon`. The standard way is to do it by converting all arguments except for the data type as a string, which is the default behaviour. The other way is to store the data as a list, which is turned on by setting `use_list=True`. In most cases, the default behaviour is the safest option; in some cases, especially when the data cannot be represented as a string, you might want to switch on `use_list`, but `semantikon` is still under intensive development, and therefore there is no guarantee that you can retrieve the data across different versions correctly.
78
+
79
+
80
+ ### **Interpreters**
81
+
82
+ #### General interpreter
83
+
84
+ In order to extract argument information, you can use the functions `parse_input_args` and `parse_output_args`. `parse_input_args` parses the input variables and return a dictionary with the variable names as keys and the variable information as values. `parse_output_args` parses the output variables and return a dictionary with the variable information as values if there is one output variable, or a list of dictionaries if it is a tuple.
85
+
86
+ Example:
87
+
88
+ ```python
89
+ from semantikon.typing import u
90
+ from semantikon.converter import parse_input_args, parse_output_args
91
+
92
+ def my_function(
93
+ a: u(int, units="meter"),
94
+ b: u(int, units="second")
95
+ ) -> u(int, units="meter/second", label="speed"):
96
+ return a / b
97
+
98
+ print(parse_input_args(my_function))
99
+ print(parse_output_args(my_function))
100
+ ```
101
+
102
+ Output:
103
+
104
+ ```python
105
+ {'distance': {'units': 'meter', 'label': None, 'uri': None, 'shape': None, 'dtype': <class 'float'>}, 'time': {'units': 'second', 'label': None, 'uri': None, 'shape': None, 'dtype': <class 'float'>}}
106
+ {'units': 'meter/second', 'label': 'speed', 'uri': None, 'shape': None, 'dtype': <class 'float'>}
107
+ ```
108
+
109
+ Here the output is the same whether `use_list` is set to `True` or `False`. When `use_list` is `False`, you can use additionally any tag that you want to store. When `use_list` is `True`, you can store only the data type, `units`, `label`, `uri`, `shape` and `dtype`.
110
+
111
+ Future announcement: There will be no distrinction between `use_list=True` and `use_list=False` when the official support of python 3.10 is dropped (i.e. around autumn 2026).
112
+
113
+ #### Unit conversion with `pint`
114
+
115
+ `semantikon` provides a way to interpret the types of inputs and outputs of a function via a decorator, in order to check consistency of the types and to convert them if necessary. Currently, `semantikon` provides an interpreter for `pint.UnitRegistry` objects. The interpreter is applied in the following way:
116
+
117
+ ```python
118
+ from semantikon.typing import u
119
+ from semantikon.converters import units
120
+ from pint import UnitRegistry
121
+
122
+ @units
123
+ def my_function(
124
+ a: u(int, units="meter"),
125
+ b: u(int, units="second")
126
+ ) -> u(int, units="meter/second", label="speed"):
127
+ return a / b
128
+
129
+
130
+ ureg = UnitRegistry()
131
+
132
+ print(my_function(1 * ureg.meter, 1 * ureg.second))
133
+ ```
134
+
135
+ Output: `1.0 meter / second`
136
+
137
+
138
+ The interpreters check all types and, if necessary, convert them to the expected types **before** the function is executed, in order for all possible errors would be raised before the function execution. The interpreters convert the types in the way that the underlying function would receive the raw values.
139
+
140
+ In case there are multiple outputs, the type hints are to be passed as a tuple (e.g. `(u(int, "meter"), u(int, "second"))`).
141
+
142
+ Interpreters can distinguish between annotated arguments and non-anotated arguments. If the argument is annotated, the interpreter will try to convert the argument to the expected type. If the argument is not annotated, the interpreter will pass the argument as is.
143
+
144
+ Regardless of type hints are given or not, the interpreter acts only when the input values contain units and ontological types. If the input values do not contain units and ontological types, the interpreter will pass the input values to the function as is.
145
+
146
+
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ pyproject.toml
4
+ setup.py
5
+ docs/README.md
6
+ semantikon/__init__.py
7
+ semantikon/_version.py
8
+ semantikon/converter.py
9
+ semantikon/typing.py
10
+ semantikon.egg-info/PKG-INFO
11
+ semantikon.egg-info/SOURCES.txt
12
+ semantikon.egg-info/dependency_links.txt
13
+ semantikon.egg-info/requires.txt
14
+ semantikon.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ numpy==2.1.3
2
+ pint==0.24.4
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,8 @@
1
+ from setuptools import setup
2
+
3
+ import versioneer
4
+
5
+ setup(
6
+ version=versioneer.get_version(),
7
+ cmdclass=versioneer.get_cmdclass(),
8
+ )