onto-lang 0.1.0__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.
- onto_lang-0.1.0/PKG-INFO +94 -0
- onto_lang-0.1.0/README.md +87 -0
- onto_lang-0.1.0/pyproject.toml +31 -0
- onto_lang-0.1.0/setup.cfg +4 -0
- onto_lang-0.1.0/src/py/onto/__init__.py +215 -0
- onto_lang-0.1.0/src/py/onto/__main__.py +12 -0
- onto_lang-0.1.0/src/py/onto/binding/__init__.py +19 -0
- onto_lang-0.1.0/src/py/onto/binding/coverage.py +174 -0
- onto_lang-0.1.0/src/py/onto/binding/formatter.py +122 -0
- onto_lang-0.1.0/src/py/onto/binding/model.py +99 -0
- onto_lang-0.1.0/src/py/onto/binding/operations.py +364 -0
- onto_lang-0.1.0/src/py/onto/binding/parser.py +291 -0
- onto_lang-0.1.0/src/py/onto/cli/__init__.py +164 -0
- onto_lang-0.1.0/src/py/onto/cli/commands.py +436 -0
- onto_lang-0.1.0/src/py/onto/cli/ui.py +168 -0
- onto_lang-0.1.0/src/py/onto/cli/usage.py +382 -0
- onto_lang-0.1.0/src/py/onto/cli/workspace.py +555 -0
- onto_lang-0.1.0/src/py/onto/coverage.py +350 -0
- onto_lang-0.1.0/src/py/onto/expressions.py +264 -0
- onto_lang-0.1.0/src/py/onto/formats/__init__.py +12 -0
- onto_lang-0.1.0/src/py/onto/formats/formatter.py +25 -0
- onto_lang-0.1.0/src/py/onto/formats/model.py +53 -0
- onto_lang-0.1.0/src/py/onto/formats/operations.py +40 -0
- onto_lang-0.1.0/src/py/onto/formats/parser.py +80 -0
- onto_lang-0.1.0/src/py/onto/functions/__init__.py +19 -0
- onto_lang-0.1.0/src/py/onto/functions/formatter.py +100 -0
- onto_lang-0.1.0/src/py/onto/functions/model.py +110 -0
- onto_lang-0.1.0/src/py/onto/functions/operations.py +177 -0
- onto_lang-0.1.0/src/py/onto/functions/parser.py +221 -0
- onto_lang-0.1.0/src/py/onto/functions/runtime.py +320 -0
- onto_lang-0.1.0/src/py/onto/integrations/__init__.py +5 -0
- onto_lang-0.1.0/src/py/onto/integrations/hexa/__init__.py +44 -0
- onto_lang-0.1.0/src/py/onto/integrations/hexa/compiler.py +1014 -0
- onto_lang-0.1.0/src/py/onto/integrations/hexa/model.py +164 -0
- onto_lang-0.1.0/src/py/onto/integrations/hexa/operations.py +244 -0
- onto_lang-0.1.0/src/py/onto/integrations/hexa/runs.py +331 -0
- onto_lang-0.1.0/src/py/onto/integrations/hexa/stages.py +435 -0
- onto_lang-0.1.0/src/py/onto/integrations/hexa/unfold.py +545 -0
- onto_lang-0.1.0/src/py/onto/integrations/hexa/vocabulary.py +743 -0
- onto_lang-0.1.0/src/py/onto/integrations/hexa/writing.py +464 -0
- onto_lang-0.1.0/src/py/onto/inventory/__init__.py +15 -0
- onto_lang-0.1.0/src/py/onto/inventory/formatter.py +21 -0
- onto_lang-0.1.0/src/py/onto/inventory/model.py +43 -0
- onto_lang-0.1.0/src/py/onto/inventory/operations.py +166 -0
- onto_lang-0.1.0/src/py/onto/inventory/parser.py +60 -0
- onto_lang-0.1.0/src/py/onto/main.py +14 -0
- onto_lang-0.1.0/src/py/onto/mapping/__init__.py +20 -0
- onto_lang-0.1.0/src/py/onto/mapping/coverage.py +131 -0
- onto_lang-0.1.0/src/py/onto/mapping/formatter.py +133 -0
- onto_lang-0.1.0/src/py/onto/mapping/model.py +101 -0
- onto_lang-0.1.0/src/py/onto/mapping/operations.py +375 -0
- onto_lang-0.1.0/src/py/onto/mapping/parser.py +247 -0
- onto_lang-0.1.0/src/py/onto/mapping/reversal.py +234 -0
- onto_lang-0.1.0/src/py/onto/observations/__init__.py +84 -0
- onto_lang-0.1.0/src/py/onto/observations/formatter.py +322 -0
- onto_lang-0.1.0/src/py/onto/observations/model.py +237 -0
- onto_lang-0.1.0/src/py/onto/observations/operations.py +374 -0
- onto_lang-0.1.0/src/py/onto/observations/parser.py +717 -0
- onto_lang-0.1.0/src/py/onto/ontology/__init__.py +142 -0
- onto_lang-0.1.0/src/py/onto/ontology/formatter.py +400 -0
- onto_lang-0.1.0/src/py/onto/ontology/model.py +482 -0
- onto_lang-0.1.0/src/py/onto/ontology/operations.py +424 -0
- onto_lang-0.1.0/src/py/onto/ontology/parser.py +640 -0
- onto_lang-0.1.0/src/py/onto/referential/__init__.py +17 -0
- onto_lang-0.1.0/src/py/onto/referential/formatter.py +70 -0
- onto_lang-0.1.0/src/py/onto/referential/model.py +46 -0
- onto_lang-0.1.0/src/py/onto/referential/operations.py +205 -0
- onto_lang-0.1.0/src/py/onto/referential/parser.py +142 -0
- onto_lang-0.1.0/src/py/onto/text.py +279 -0
- onto_lang-0.1.0/src/py/onto_lang.egg-info/PKG-INFO +94 -0
- onto_lang-0.1.0/src/py/onto_lang.egg-info/SOURCES.txt +72 -0
- onto_lang-0.1.0/src/py/onto_lang.egg-info/dependency_links.txt +1 -0
- onto_lang-0.1.0/src/py/onto_lang.egg-info/entry_points.txt +2 -0
- onto_lang-0.1.0/src/py/onto_lang.egg-info/top_level.txt +1 -0
onto_lang-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: onto-lang
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Compact text formats for ontologies, observations and mappings
|
|
5
|
+
Requires-Python: >=3.14
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄
|
|
10
|
+
▄▀░▓░ ▀▄ █ ▓░ ▀▄ █ ░▓░ ▓ ▄▀░▓░ ▀▄
|
|
11
|
+
▐▌ ▒ ▐▌ ▓ ▒ ▐▌ █ ▒ ▒ ▐▌ ▒ ▐▌
|
|
12
|
+
█ ░ ░ █ ▒ ░ ▓ █▄▄▄ ░ ▄▄▄▄░ █ ░ ░ █
|
|
13
|
+
█ ▓ █ ░ ▒ █ █ █ ▓ █
|
|
14
|
+
▓ █ █ ░ ░ ░ ▓ █ ▓ █ █
|
|
15
|
+
░ █ ▓ ░ ▒ ░▒ █ █ ░ █ ▓
|
|
16
|
+
▒░ ▓ ▒ ▒░ ▓ ░▒░ ▒░ █ ▒░ ▓ ▒
|
|
17
|
+
▐▓▒░ ▀ ▐▌ ▓▒░ ░█ ░▒▓▒ ░▓▒░ █ ▐▓▒░ ▀ ▐▌
|
|
18
|
+
▀▓▒░ ▄▀ █▓▒░ ▒█ ░▒▓█▓ ██▓▒░ █ ▀▓▒░ ▄▀
|
|
19
|
+
▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
*onto* is a format (JSON & text) to represent conceptual models (ontologies)
|
|
23
|
+
and instance data (observations), as well the mapping between systems and
|
|
24
|
+
the records they describe.
|
|
25
|
+
|
|
26
|
+
*onto*'s goal is to:
|
|
27
|
+
- Provide a format to describe data that is compact and expressive
|
|
28
|
+
- Can we read and edited by humans and machines/agents efficiently
|
|
29
|
+
- Facilitate data management and systems integration
|
|
30
|
+
|
|
31
|
+
Onto is implemented in Python.
|
|
32
|
+
|
|
33
|
+
Onto offers the following formats, each with a text and a JSON form:
|
|
34
|
+
|
|
35
|
+
- `.onto` for ontologies ([spec-01](docs/specs/spec-01-ontology.md))
|
|
36
|
+
- `.obs` for observations ([spec-02](docs/specs/spec-02-observations.md))
|
|
37
|
+
- `.bind`, `.map` and `.fn` for bindings, mappings and function libraries
|
|
38
|
+
([spec-04](docs/specs/spec-04-mapping.md))
|
|
39
|
+
- `.ref` for identity referentials ([spec-05](docs/specs/spec-05-identities.md))
|
|
40
|
+
|
|
41
|
+
## Get started
|
|
42
|
+
|
|
43
|
+
*onto* needs Python 3.14 or later and has no runtime dependencies. The
|
|
44
|
+
distribution is named `onto-lang`; the command and Python import are both
|
|
45
|
+
`onto`.
|
|
46
|
+
|
|
47
|
+
From the repository root, run the CLI without installing:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
./bin/onto --help
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or install the checkout into your Python environment:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
pip install .
|
|
57
|
+
onto --help
|
|
58
|
+
python -m onto --help
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The package has not been published to PyPI yet. Once released, you can
|
|
62
|
+
install it with `pip install onto-lang`.
|
|
63
|
+
|
|
64
|
+
For publishing, see the [release instructions](docs/releasing.md).
|
|
65
|
+
|
|
66
|
+
## In a nutshell
|
|
67
|
+
|
|
68
|
+
Files name each other by namespace (`@use Finance as F`), so `onto` indexes
|
|
69
|
+
the files of your project by their header and finds what each one needs:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
$ onto ls ontology mapping
|
|
73
|
+
mapping Exchange->Finance finance/exchange-finance.map
|
|
74
|
+
ontology Exchange 2026-11-10 finance/exchange.onto
|
|
75
|
+
ontology Finance 2026-11-10 finance/finance.onto
|
|
76
|
+
|
|
77
|
+
$ onto check finance/exchange-finance.map # loads Exchange, Finance, Markets, MarketIds
|
|
78
|
+
no problems in 1 file (5 read)
|
|
79
|
+
|
|
80
|
+
$ onto fmt --check # lists files whose layout would change
|
|
81
|
+
|
|
82
|
+
$ onto coverage finance/exchange-finance.map
|
|
83
|
+
source target status note
|
|
84
|
+
Dividend.Amount Dividend.Amount direct
|
|
85
|
+
Dividend.Currency condition
|
|
86
|
+
Instrument.Class Instrument.AssetClass transformed
|
|
87
|
+
|
|
88
|
+
$ onto reverse finance/exchange-finance.map > finance-exchange.map
|
|
89
|
+
finance/exchange-finance.map:19: warning map.lossy: ... 'AssetClass' is not
|
|
90
|
+
reversed: @map 'AssetClassFromCode' is not exact and one-to-one
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`onto help` lists every command; [spec-07](docs/specs/spec-07-cli.md)
|
|
94
|
+
describes them.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
```
|
|
2
|
+
▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄
|
|
3
|
+
▄▀░▓░ ▀▄ █ ▓░ ▀▄ █ ░▓░ ▓ ▄▀░▓░ ▀▄
|
|
4
|
+
▐▌ ▒ ▐▌ ▓ ▒ ▐▌ █ ▒ ▒ ▐▌ ▒ ▐▌
|
|
5
|
+
█ ░ ░ █ ▒ ░ ▓ █▄▄▄ ░ ▄▄▄▄░ █ ░ ░ █
|
|
6
|
+
█ ▓ █ ░ ▒ █ █ █ ▓ █
|
|
7
|
+
▓ █ █ ░ ░ ░ ▓ █ ▓ █ █
|
|
8
|
+
░ █ ▓ ░ ▒ ░▒ █ █ ░ █ ▓
|
|
9
|
+
▒░ ▓ ▒ ▒░ ▓ ░▒░ ▒░ █ ▒░ ▓ ▒
|
|
10
|
+
▐▓▒░ ▀ ▐▌ ▓▒░ ░█ ░▒▓▒ ░▓▒░ █ ▐▓▒░ ▀ ▐▌
|
|
11
|
+
▀▓▒░ ▄▀ █▓▒░ ▒█ ░▒▓█▓ ██▓▒░ █ ▀▓▒░ ▄▀
|
|
12
|
+
▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
*onto* is a format (JSON & text) to represent conceptual models (ontologies)
|
|
16
|
+
and instance data (observations), as well the mapping between systems and
|
|
17
|
+
the records they describe.
|
|
18
|
+
|
|
19
|
+
*onto*'s goal is to:
|
|
20
|
+
- Provide a format to describe data that is compact and expressive
|
|
21
|
+
- Can we read and edited by humans and machines/agents efficiently
|
|
22
|
+
- Facilitate data management and systems integration
|
|
23
|
+
|
|
24
|
+
Onto is implemented in Python.
|
|
25
|
+
|
|
26
|
+
Onto offers the following formats, each with a text and a JSON form:
|
|
27
|
+
|
|
28
|
+
- `.onto` for ontologies ([spec-01](docs/specs/spec-01-ontology.md))
|
|
29
|
+
- `.obs` for observations ([spec-02](docs/specs/spec-02-observations.md))
|
|
30
|
+
- `.bind`, `.map` and `.fn` for bindings, mappings and function libraries
|
|
31
|
+
([spec-04](docs/specs/spec-04-mapping.md))
|
|
32
|
+
- `.ref` for identity referentials ([spec-05](docs/specs/spec-05-identities.md))
|
|
33
|
+
|
|
34
|
+
## Get started
|
|
35
|
+
|
|
36
|
+
*onto* needs Python 3.14 or later and has no runtime dependencies. The
|
|
37
|
+
distribution is named `onto-lang`; the command and Python import are both
|
|
38
|
+
`onto`.
|
|
39
|
+
|
|
40
|
+
From the repository root, run the CLI without installing:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
./bin/onto --help
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Or install the checkout into your Python environment:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
pip install .
|
|
50
|
+
onto --help
|
|
51
|
+
python -m onto --help
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The package has not been published to PyPI yet. Once released, you can
|
|
55
|
+
install it with `pip install onto-lang`.
|
|
56
|
+
|
|
57
|
+
For publishing, see the [release instructions](docs/releasing.md).
|
|
58
|
+
|
|
59
|
+
## In a nutshell
|
|
60
|
+
|
|
61
|
+
Files name each other by namespace (`@use Finance as F`), so `onto` indexes
|
|
62
|
+
the files of your project by their header and finds what each one needs:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
$ onto ls ontology mapping
|
|
66
|
+
mapping Exchange->Finance finance/exchange-finance.map
|
|
67
|
+
ontology Exchange 2026-11-10 finance/exchange.onto
|
|
68
|
+
ontology Finance 2026-11-10 finance/finance.onto
|
|
69
|
+
|
|
70
|
+
$ onto check finance/exchange-finance.map # loads Exchange, Finance, Markets, MarketIds
|
|
71
|
+
no problems in 1 file (5 read)
|
|
72
|
+
|
|
73
|
+
$ onto fmt --check # lists files whose layout would change
|
|
74
|
+
|
|
75
|
+
$ onto coverage finance/exchange-finance.map
|
|
76
|
+
source target status note
|
|
77
|
+
Dividend.Amount Dividend.Amount direct
|
|
78
|
+
Dividend.Currency condition
|
|
79
|
+
Instrument.Class Instrument.AssetClass transformed
|
|
80
|
+
|
|
81
|
+
$ onto reverse finance/exchange-finance.map > finance-exchange.map
|
|
82
|
+
finance/exchange-finance.map:19: warning map.lossy: ... 'AssetClass' is not
|
|
83
|
+
reversed: @map 'AssetClassFromCode' is not exact and one-to-one
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`onto help` lists every command; [spec-07](docs/specs/spec-07-cli.md)
|
|
87
|
+
describes them.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=75"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "onto-lang"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Compact text formats for ontologies, observations and mappings"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.14"
|
|
11
|
+
dependencies = []
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
onto = "onto.main:main"
|
|
15
|
+
|
|
16
|
+
[dependency-groups]
|
|
17
|
+
dev = ["pytest>=8", "jsonschema>=4.18"]
|
|
18
|
+
|
|
19
|
+
[tool.setuptools]
|
|
20
|
+
package-dir = { "" = "src/py" }
|
|
21
|
+
|
|
22
|
+
[tool.setuptools.packages.find]
|
|
23
|
+
where = ["src/py"]
|
|
24
|
+
include = ["onto*"]
|
|
25
|
+
|
|
26
|
+
[tool.pytest.ini_options]
|
|
27
|
+
python_files = ["T*.py"]
|
|
28
|
+
addopts = "--import-mode=importlib"
|
|
29
|
+
testpaths = ["tests"]
|
|
30
|
+
# NOTE: The optional hexa integration is tested against the sibling checkout when present.
|
|
31
|
+
pythonpath = ["src/py", "../hexa/src/py"]
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Module: onto — ontology/observation formats, mapping declarations and identity policies."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .text import Diagnostic, FormatError, Source
|
|
6
|
+
from .ontology import (
|
|
7
|
+
Operator as OntologyOperator,
|
|
8
|
+
Model,
|
|
9
|
+
Ontology,
|
|
10
|
+
formatOntology,
|
|
11
|
+
loadOntology,
|
|
12
|
+
normaliseOntology,
|
|
13
|
+
ontologyFromJson,
|
|
14
|
+
ontologyToJson,
|
|
15
|
+
parseOntology,
|
|
16
|
+
printOntology,
|
|
17
|
+
validateOntology,
|
|
18
|
+
)
|
|
19
|
+
from .observations import (
|
|
20
|
+
Operator as ObservationsOperator,
|
|
21
|
+
Gap,
|
|
22
|
+
Observations,
|
|
23
|
+
ObservationsDocument,
|
|
24
|
+
formatObservations,
|
|
25
|
+
loadObservations,
|
|
26
|
+
observationsFromJson,
|
|
27
|
+
observationsToJson,
|
|
28
|
+
parseObservations,
|
|
29
|
+
printObservationsLog,
|
|
30
|
+
printObservationsState,
|
|
31
|
+
reconcileObservations,
|
|
32
|
+
resolveObservations,
|
|
33
|
+
)
|
|
34
|
+
from .coverage import CoverageItem, CoverageReport, CoverageRow, observationsCoverage, printCoverageTable
|
|
35
|
+
from .expressions import Expression, expressionFromJson, parseExpression, printExpression
|
|
36
|
+
from .functions import (
|
|
37
|
+
Operator as FunctionsOperator,
|
|
38
|
+
Function,
|
|
39
|
+
FunctionDeclaration,
|
|
40
|
+
FunctionEvaluator,
|
|
41
|
+
FunctionExample,
|
|
42
|
+
FunctionLibrary,
|
|
43
|
+
FunctionResult,
|
|
44
|
+
FunctionsDocument,
|
|
45
|
+
SignatureType,
|
|
46
|
+
formatFunctions,
|
|
47
|
+
functionsFromJson,
|
|
48
|
+
functionsToJson,
|
|
49
|
+
loadFunctions,
|
|
50
|
+
parseFunctions,
|
|
51
|
+
printFunctions,
|
|
52
|
+
resolveFunctions,
|
|
53
|
+
)
|
|
54
|
+
from .binding import (
|
|
55
|
+
Operator as BindingOperator,
|
|
56
|
+
Binding,
|
|
57
|
+
BindingDocument,
|
|
58
|
+
BindingExclusion,
|
|
59
|
+
BindingLine,
|
|
60
|
+
BindingScope,
|
|
61
|
+
bindingFromJson,
|
|
62
|
+
bindingToJson,
|
|
63
|
+
formatBinding,
|
|
64
|
+
loadBinding,
|
|
65
|
+
parseBinding,
|
|
66
|
+
printBinding,
|
|
67
|
+
resolveBinding,
|
|
68
|
+
bindingCoverage,
|
|
69
|
+
)
|
|
70
|
+
from .mapping import (
|
|
71
|
+
Operator as MappingOperator,
|
|
72
|
+
Mapping,
|
|
73
|
+
MappingDocument,
|
|
74
|
+
MappingExclusion,
|
|
75
|
+
MappingLine,
|
|
76
|
+
MappingScope,
|
|
77
|
+
formatMapping,
|
|
78
|
+
loadMapping,
|
|
79
|
+
mappingFromJson,
|
|
80
|
+
mappingToJson,
|
|
81
|
+
parseMapping,
|
|
82
|
+
printMapping,
|
|
83
|
+
resolveMapping,
|
|
84
|
+
LossyLine,
|
|
85
|
+
mappingCoverage,
|
|
86
|
+
mappingReverse,
|
|
87
|
+
)
|
|
88
|
+
from .inventory import Inventory, InventoryField, defaultBinding, documentFields, inventoryFromDocuments, parseInventory, printInventory
|
|
89
|
+
from .formats import FormatChange, Formats, FormatsEntry, formatsUpdate, parseFormats, printFormats
|
|
90
|
+
from .referential import (
|
|
91
|
+
Operator as ReferentialOperator,
|
|
92
|
+
Policy as ReferentialPolicy,
|
|
93
|
+
Referential,
|
|
94
|
+
formatReferential,
|
|
95
|
+
loadReferential,
|
|
96
|
+
parseReferential,
|
|
97
|
+
printReferential,
|
|
98
|
+
referentialFromJson,
|
|
99
|
+
referentialImplicit,
|
|
100
|
+
referentialPolicy,
|
|
101
|
+
referentialToJson,
|
|
102
|
+
validateReferential,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
__all__ = [
|
|
106
|
+
"Binding",
|
|
107
|
+
"BindingDocument",
|
|
108
|
+
"BindingExclusion",
|
|
109
|
+
"BindingLine",
|
|
110
|
+
"BindingScope",
|
|
111
|
+
"BindingOperator",
|
|
112
|
+
"CoverageItem",
|
|
113
|
+
"CoverageReport",
|
|
114
|
+
"CoverageRow",
|
|
115
|
+
"FormatChange",
|
|
116
|
+
"Formats",
|
|
117
|
+
"FormatsEntry",
|
|
118
|
+
"Inventory",
|
|
119
|
+
"InventoryField",
|
|
120
|
+
"LossyLine",
|
|
121
|
+
"Diagnostic",
|
|
122
|
+
"Expression",
|
|
123
|
+
"FormatError",
|
|
124
|
+
"Function",
|
|
125
|
+
"FunctionDeclaration",
|
|
126
|
+
"FunctionEvaluator",
|
|
127
|
+
"FunctionExample",
|
|
128
|
+
"FunctionLibrary",
|
|
129
|
+
"FunctionResult",
|
|
130
|
+
"FunctionsDocument",
|
|
131
|
+
"FunctionsOperator",
|
|
132
|
+
"Gap",
|
|
133
|
+
"Model",
|
|
134
|
+
"Mapping",
|
|
135
|
+
"MappingDocument",
|
|
136
|
+
"MappingExclusion",
|
|
137
|
+
"MappingLine",
|
|
138
|
+
"MappingScope",
|
|
139
|
+
"MappingOperator",
|
|
140
|
+
"Ontology",
|
|
141
|
+
"OntologyOperator",
|
|
142
|
+
"Observations",
|
|
143
|
+
"ObservationsDocument",
|
|
144
|
+
"ObservationsOperator",
|
|
145
|
+
"Referential",
|
|
146
|
+
"ReferentialOperator",
|
|
147
|
+
"ReferentialPolicy",
|
|
148
|
+
"Source",
|
|
149
|
+
"SignatureType",
|
|
150
|
+
"expressionFromJson",
|
|
151
|
+
"bindingFromJson",
|
|
152
|
+
"bindingToJson",
|
|
153
|
+
"formatBinding",
|
|
154
|
+
"formatOntology",
|
|
155
|
+
"formatObservations",
|
|
156
|
+
"formatFunctions",
|
|
157
|
+
"formatMapping",
|
|
158
|
+
"formatReferential",
|
|
159
|
+
"functionsFromJson",
|
|
160
|
+
"functionsToJson",
|
|
161
|
+
"loadOntology",
|
|
162
|
+
"loadObservations",
|
|
163
|
+
"loadFunctions",
|
|
164
|
+
"loadBinding",
|
|
165
|
+
"loadMapping",
|
|
166
|
+
"loadReferential",
|
|
167
|
+
"mappingFromJson",
|
|
168
|
+
"mappingToJson",
|
|
169
|
+
"normaliseOntology",
|
|
170
|
+
"ontologyFromJson",
|
|
171
|
+
"ontologyToJson",
|
|
172
|
+
"observationsFromJson",
|
|
173
|
+
"observationsCoverage",
|
|
174
|
+
"observationsToJson",
|
|
175
|
+
"parseOntology",
|
|
176
|
+
"parseExpression",
|
|
177
|
+
"parseFunctions",
|
|
178
|
+
"parseBinding",
|
|
179
|
+
"parseMapping",
|
|
180
|
+
"parseReferential",
|
|
181
|
+
"parseObservations",
|
|
182
|
+
"printOntology",
|
|
183
|
+
"printExpression",
|
|
184
|
+
"printFunctions",
|
|
185
|
+
"printBinding",
|
|
186
|
+
"printMapping",
|
|
187
|
+
"printReferential",
|
|
188
|
+
"printObservationsLog",
|
|
189
|
+
"printObservationsState",
|
|
190
|
+
"reconcileObservations",
|
|
191
|
+
"referentialFromJson",
|
|
192
|
+
"referentialImplicit",
|
|
193
|
+
"referentialPolicy",
|
|
194
|
+
"referentialToJson",
|
|
195
|
+
"resolveObservations",
|
|
196
|
+
"resolveFunctions",
|
|
197
|
+
"resolveBinding",
|
|
198
|
+
"resolveMapping",
|
|
199
|
+
"validateOntology",
|
|
200
|
+
"validateReferential",
|
|
201
|
+
"bindingCoverage",
|
|
202
|
+
"defaultBinding",
|
|
203
|
+
"documentFields",
|
|
204
|
+
"formatsUpdate",
|
|
205
|
+
"inventoryFromDocuments",
|
|
206
|
+
"mappingCoverage",
|
|
207
|
+
"mappingReverse",
|
|
208
|
+
"parseFormats",
|
|
209
|
+
"parseInventory",
|
|
210
|
+
"printCoverageTable",
|
|
211
|
+
"printFormats",
|
|
212
|
+
"printInventory",
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
# EOF
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Module: binding — spec-04 binding declarations and interchange, without execution."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .model import Binding, BindingDocument, BindingExclusion, BindingLine, BindingScope, Condition
|
|
6
|
+
from .parser import parseBinding
|
|
7
|
+
from .formatter import bindingToJson, printBinding
|
|
8
|
+
from .operations import Operator, bindingFromJson, formatBinding, loadBinding, resolveBinding
|
|
9
|
+
from .coverage import bindingCoverage
|
|
10
|
+
|
|
11
|
+
BindingOperator = Operator
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"Binding", "BindingDocument", "BindingExclusion", "BindingLine", "BindingScope", "Condition", "Operator", "BindingOperator",
|
|
15
|
+
"parseBinding", "resolveBinding", "loadBinding", "formatBinding", "printBinding", "bindingToJson", "bindingFromJson",
|
|
16
|
+
"bindingCoverage",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
# EOF
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Module: binding.coverage — static coverage of a binding: ontology side and document side (spec-04)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from ..coverage import CoverageReport, CoverageRow, Elements, coverageRows
|
|
8
|
+
from ..expressions import Expression, printExpression
|
|
9
|
+
from ..inventory.model import CONTAINER_TYPES, Inventory
|
|
10
|
+
from ..ontology.model import Model, Ontology
|
|
11
|
+
from .model import Binding, BindingScope
|
|
12
|
+
|
|
13
|
+
# -----------------------------------------------------------------------------
|
|
14
|
+
#
|
|
15
|
+
# DEFINITIONS
|
|
16
|
+
#
|
|
17
|
+
# -----------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
type Steps = list[Any]
|
|
21
|
+
|
|
22
|
+
EACH = {"each": True}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# -----------------------------------------------------------------------------
|
|
26
|
+
#
|
|
27
|
+
# UTILITIES
|
|
28
|
+
#
|
|
29
|
+
# -----------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def documentPaths(expr: Expression | None) -> list[Steps]:
|
|
33
|
+
"""Document paths an expression reads; the type argument of `ref` is not one."""
|
|
34
|
+
if expr is None:
|
|
35
|
+
return []
|
|
36
|
+
if "path" in expr:
|
|
37
|
+
return [expr["path"]]
|
|
38
|
+
if "call" in expr:
|
|
39
|
+
args = expr["args"][1:] if expr["call"] == "ref" else expr["args"]
|
|
40
|
+
return [path for arg in args for path in documentPaths(arg)]
|
|
41
|
+
if "coalesce" in expr:
|
|
42
|
+
return [path for item in expr["coalesce"] for path in documentPaths(item)]
|
|
43
|
+
return []
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def absolute(scope: Steps | None, steps: Steps) -> Steps:
|
|
47
|
+
"""Steps of a path read from a record at `scope`, from the document root, indexes as `[*]`.
|
|
48
|
+
|
|
49
|
+
`^` climbs to the enclosing list element; `$` restarts at the root. A JSON root
|
|
50
|
+
object is implicit (`asOf`); a root list keeps its `$[*]`.
|
|
51
|
+
"""
|
|
52
|
+
first = steps[0] if steps else None
|
|
53
|
+
if isinstance(first, dict) and "root" in first:
|
|
54
|
+
base, steps = [], steps[1:]
|
|
55
|
+
elif isinstance(first, dict) and "up" in first:
|
|
56
|
+
eaches = [index for index, step in enumerate(scope or []) if step == EACH]
|
|
57
|
+
count = first["up"]
|
|
58
|
+
base = list(scope[:eaches[-1 - count] + 1]) if len(eaches) > count else []
|
|
59
|
+
steps = steps[1:]
|
|
60
|
+
else:
|
|
61
|
+
base = list(scope or [])
|
|
62
|
+
result = base + [EACH if isinstance(step, dict) and "index" in step else step for step in steps]
|
|
63
|
+
if result and isinstance(result[0], dict) and "root" in result[0] and (len(result) < 2 or result[1] != EACH):
|
|
64
|
+
result = result[1:]
|
|
65
|
+
if not result:
|
|
66
|
+
return []
|
|
67
|
+
if result[0] == EACH:
|
|
68
|
+
result = [{"root": True}] + result
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def pathText(steps: Steps) -> str:
|
|
73
|
+
return printExpression({"path": steps}, binding=True) if steps else "$"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# -----------------------------------------------------------------------------
|
|
77
|
+
#
|
|
78
|
+
# OPERATIONS
|
|
79
|
+
#
|
|
80
|
+
# -----------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class BindingCoverage:
|
|
84
|
+
"""Collect the fields each scope reads and the elements its lines assign."""
|
|
85
|
+
|
|
86
|
+
def __init__(self, ontology: Ontology, binding: Binding):
|
|
87
|
+
self.binding = binding
|
|
88
|
+
self.elements = Elements(Model(ontology), binding.ontology)
|
|
89
|
+
self.reads: dict[str, list[tuple[str | None, str, str | None]]] = {}
|
|
90
|
+
self.assigned: set[str] = set()
|
|
91
|
+
|
|
92
|
+
def record(self, scope: Steps | None, paths: list[Steps], target: str | None, status: str, note: str | None = None) -> None:
|
|
93
|
+
for steps in paths:
|
|
94
|
+
path = absolute(scope, steps)
|
|
95
|
+
if path:
|
|
96
|
+
self.reads.setdefault(pathText(path), []).append((target, status, note))
|
|
97
|
+
|
|
98
|
+
def enclosing(self, scope: BindingScope, up: int) -> BindingScope | None:
|
|
99
|
+
"""The scope line whose records enclose this scope's, `up` lists above."""
|
|
100
|
+
steps = absolute(None, scope.path["path"])
|
|
101
|
+
eaches = [index for index, step in enumerate(steps) if step == EACH]
|
|
102
|
+
if len(eaches) <= up:
|
|
103
|
+
return None
|
|
104
|
+
prefix = steps[:eaches[-1 - up] + 1]
|
|
105
|
+
return next((item for item in self.binding.scopes if absolute(None, item.path["path"]) == prefix), None)
|
|
106
|
+
|
|
107
|
+
def scope(self, scope: BindingScope) -> None:
|
|
108
|
+
steps = absolute(None, scope.path["path"])
|
|
109
|
+
typeName = self.elements.display(scope.type)
|
|
110
|
+
self.record(None, [scope.path["path"]], typeName, "direct", scope.doc)
|
|
111
|
+
conditions = documentPaths(scope.id) + documentPaths(scope.valid) + documentPaths(scope.ctx)
|
|
112
|
+
conditions += [scope.deleted["path"]] if scope.deleted else []
|
|
113
|
+
self.record(steps, conditions, None, "condition")
|
|
114
|
+
for line in scope.lines:
|
|
115
|
+
key = self.elements.assigned(scope.type, line.target, line.facet, line.verb)
|
|
116
|
+
if key:
|
|
117
|
+
self.assigned.add(key)
|
|
118
|
+
member = f"[{line.facet}]" if line.facet else f".{line.target or line.verb}"
|
|
119
|
+
expr = line.expr
|
|
120
|
+
if expr is not None and "path" in expr and len(expr["path"]) == 1 and isinstance(expr["path"][0], dict) and "up" in expr["path"][0]:
|
|
121
|
+
# `^` reads the enclosing record's key.
|
|
122
|
+
parent = self.enclosing(scope, expr["path"][0]["up"])
|
|
123
|
+
if parent is not None:
|
|
124
|
+
self.record(absolute(None, parent.path["path"]), documentPaths(parent.id), None, "condition")
|
|
125
|
+
continue
|
|
126
|
+
status = "direct" if expr is not None and "path" in expr else "transformed"
|
|
127
|
+
self.record(steps, documentPaths(expr), typeName + member, status, line.doc)
|
|
128
|
+
if line.when:
|
|
129
|
+
self.record(steps, [line.when["path"]], None, "condition")
|
|
130
|
+
|
|
131
|
+
def report(self, inventory: Inventory | None) -> CoverageReport:
|
|
132
|
+
if self.binding.at is not None:
|
|
133
|
+
self.record(None, documentPaths(self.binding.at), None, "condition")
|
|
134
|
+
for scope in self.binding.scopes:
|
|
135
|
+
self.scope(scope)
|
|
136
|
+
keys = [key for decl in self.elements.instanceTypes() for key in self.elements.declared(decl.path)]
|
|
137
|
+
keys += [f"{decl.path}.{decl.arrow.verb}" for decl in self.elements.model.ontology.decls if decl.kind == "relation" and decl.arrow and f"{decl.participants[0].type}.{decl.arrow.verb}" not in keys]
|
|
138
|
+
unassigned = sorted(self.elements.display(key) for key in dict.fromkeys(keys)
|
|
139
|
+
if key not in self.assigned and not any(key.startswith(item + ".") for item in self.assigned))
|
|
140
|
+
if inventory is None:
|
|
141
|
+
return CoverageReport([], unassigned, [])
|
|
142
|
+
types = inventory.types()
|
|
143
|
+
excluded: dict[str, tuple[str, str | None]] = {}
|
|
144
|
+
for exclusion in self.binding.exclusions:
|
|
145
|
+
prefix = pathText(absolute(None, exclusion.path["path"]))
|
|
146
|
+
for path in types:
|
|
147
|
+
if path == prefix or path.startswith(prefix + ".") or path.startswith(prefix + "["):
|
|
148
|
+
excluded.setdefault(path, (exclusion.reason, exclusion.doc))
|
|
149
|
+
# NOTE: Containers (objects, lists) are reported only when read or excluded themselves; their fields carry the rest.
|
|
150
|
+
fields = [path for path, kind in types.items() if kind not in CONTAINER_TYPES or path in self.reads or path in excluded]
|
|
151
|
+
rows: list[CoverageRow] = coverageRows(fields, self.reads, excluded)
|
|
152
|
+
return CoverageReport(rows, unassigned, sorted(path for path in self.reads if path not in types and path != "$"))
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# -----------------------------------------------------------------------------
|
|
156
|
+
#
|
|
157
|
+
# HIGH-LEVEL API
|
|
158
|
+
#
|
|
159
|
+
# -----------------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def bindingCoverage(ontology: Ontology, binding: Binding, inventory: Inventory | None = None) -> CoverageReport:
|
|
163
|
+
"""Both sides of a binding's coverage.
|
|
164
|
+
|
|
165
|
+
`unassigned` is the ontology side: elements no line assigns, needing no data. With an
|
|
166
|
+
`inventory`, `rows` give each document field's status and `uninventoried` the paths
|
|
167
|
+
the binding reads that the inventory does not list.
|
|
168
|
+
"""
|
|
169
|
+
return BindingCoverage(ontology, binding).report(inventory)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
__all__ = ["bindingCoverage", "absolute", "documentPaths"]
|
|
173
|
+
|
|
174
|
+
# EOF
|