ddmodl 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.
ddmodl-0.1.0/.envrc ADDED
@@ -0,0 +1 @@
1
+ [[ -d .venv ]] && source .venv/bin/activate
@@ -0,0 +1,116 @@
1
+ # Model Ledger — Project Guidelines
2
+
3
+ ## Project Overview
4
+
5
+ `modl` is a CLI tool for maintaining an **append-only identity ledger** for evolving data models.
6
+ It tracks how model elements (entities and properties) change over time using four CSV tables:
7
+ `concepts`, `revisions`, `contracts`, `bindings`.
8
+
9
+ The tool is language-agnostic: language adapters produce a diff report in ModL's IR format, which feeds the sync engine. See [README.md](../README.md) for the full picture with examples.
10
+
11
+ ## Architecture
12
+
13
+ ```
14
+ Config YAML → BreakingChangeConfig (config.py)
15
+ Diff JSON → DiffReport (ir.py)
16
+
17
+ sync() [cli.py → ledger.py]
18
+
19
+ Four CSV files on disk
20
+ ```
21
+
22
+ | Module | Role |
23
+ |--------|------|
24
+ | `cli.py` | Entry point; `modl sync` command via `rich-click` |
25
+ | `config.py` | Pydantic models for the YAML config (namespace, breaking change rules) |
26
+ | `ir.py` | Intermediate representation: `DiffReport`, `EntityChanged`, `PropertyChanged`, `ChangeType` |
27
+ | `models.py` | Pydantic row models for the four ledger tables; `ElementStatus`, `ElementKind` enums (`ENTITY`, `PROPERTY`, `ENUMERATION_SET`, `ENUM_VALUE`) |
28
+ | `ledger.py` | Ledger I/O, multi-level CSV validation, base-36 URI serial encoding |
29
+
30
+ ## Domain Vocabulary
31
+
32
+ | Term | Meaning |
33
+ |------|---------|
34
+ | **Concept** | The agreed meaning of a model element (stable identity, independent of implementation) |
35
+ | **Revision** | An audit record of any detected change (breaking or not) |
36
+ | **Contract** | A versioned data contract for a concept — each contract captures a distinct variant of the concept's essential metadata |
37
+ | **Binding** | Maps a property contract to a runtime path via an instance label |
38
+ | **Entity** | Top-level model object (e.g., `Vehicle`, `Vehicle.Door`) — receives concepts/revisions/contracts; **no bindings** |
39
+ | **Property** | Field of an entity (e.g., `Vehicle.Speed`, `Vehicle.Door.IsOpen`) — receives concepts/revisions/contracts; **bindings** (one per instance, or one singleton if no instances) |
40
+ | **EnumerationSet** | Vocabulary entity (enum type, unit group, code list) — receives concepts/revisions/contracts but **no bindings**; set `kind: ENUMERATION_SET` in the diff event |
41
+ | **EnumValue** | Child of an `EnumerationSet` (e.g., `SpeedUnit.KMH`) — receives concepts/revisions/contracts but **no bindings**; set `kind: ENUM_VALUE` in the diff event |
42
+ | **Instance** | A concrete occurrence of a multi-instance entity (e.g., `Left`, `Right`) |
43
+ | **Breaking change** | Modification to an attribute listed in `essential_attributes` in the config |
44
+
45
+ ## Coding Conventions
46
+
47
+ - **Python ≥ 3.11**; all modules start with `from __future__ import annotations`
48
+ - **Type hints everywhere**: use `|` union syntax, `Path` for file paths, `dict[str, Any]` for flexible dicts
49
+ - **Pydantic v2** for all data models: `BaseModel` + `ConfigDict(extra="forbid")` on config models
50
+ - **Enums are `StrEnum`** subclasses with UPPERCASE string values
51
+ - **Field validators** use `@field_validator` + `@classmethod` (Pydantic v2 style)
52
+ - **Docstrings**: concise, meaning-first (e.g., "Indicates whether ..." not "This method checks whether ...")
53
+ - Ledger validation is **fail-fast**: stop and raise `LedgerValidationError` on the first violation
54
+ - URI serials are **base-36 encoded** (lowercase `0-9a-z`): `b36encode(serial)` must match the URI suffix
55
+
56
+ ## Build and Test
57
+
58
+ ```bash
59
+ # Install (with uv)
60
+ uv sync --dev
61
+
62
+ # Run tests
63
+ pytest
64
+
65
+ # Run tests with coverage
66
+ pytest --cov=modl
67
+
68
+ # Lint / format
69
+ ruff check src tests
70
+ ruff format src tests
71
+
72
+ # Type check
73
+ ty check src
74
+ ```
75
+
76
+ Line length is **120 characters** (ruff).
77
+
78
+ Lint rules enabled: `E, F, I, UP, PGH, B, SIM`.
79
+
80
+ ## Config File Format
81
+
82
+ ```yaml
83
+ namespace:
84
+ namespace: "https://myproject.org/model/" # required; absolute URI ending with / or #
85
+ prefix: "mp" # optional display alias
86
+ entity:
87
+ name.modified: false # renames are non-breaking; suppresses --strict warnings
88
+ instances.added: false # adding an instance is non-breaking
89
+ instances.removed: true # removing an instance is breaking
90
+ type: true # breaking — triggers a new contract
91
+ description: false # known, non-breaking; suppresses --strict warnings
92
+ property:
93
+ name.modified: false
94
+ output_type: true # breaking — triggers a new contract
95
+ unit: true # breaking — triggers a new contract
96
+ description: false # known, non-breaking; suppresses --strict warnings
97
+ enumeration_set:
98
+ name.modified: false
99
+ values.added: false
100
+ values.removed: true
101
+ enum_value:
102
+ name.modified: true
103
+ symbol: true
104
+ ```
105
+
106
+ All four sections (`entity`, `property`, `enumeration_set`, `enum_value`) are flat `dict[str, bool]` maps. Keys use a flat dotted form for per-op control (`unit.added`, `unit.modified`, `unit.removed`); a plain key is shorthand for all three ops. `true` = breaking, `false` = known non-breaking (silences `--strict`), absent = unknown (warns). Plain `name` is forbidden — use `name.modified`. The structural keys (`name.modified`, `instances.added`, `instances.removed`, `properties.added`, `properties.removed`, `values.added`, `values.removed`) are always recognised by the engine and never produce unknown-key warnings.
107
+
108
+ On entity `MODIFIED` events, instance-list changes are reported as a directional delta: `instances_added` and `instances_removed` (not the full `instances` list). The plain `instances` key is only valid on entity `ADDED` events.
109
+
110
+ ## Test Conventions
111
+
112
+ - Use `tmp_path` (pytest built-in) for temporary file I/O
113
+ - Use `caplog` to assert on log output
114
+ - CLI tests use `click.testing.CliRunner`
115
+ - Use `pytest.raises(ValidationError)` to test Pydantic schema violations
116
+ - Prefer round-trip tests (write → read → compare) for I/O code
@@ -0,0 +1,160 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/#use-with-ide
110
+ .pdm.toml
111
+
112
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
+ __pypackages__/
114
+
115
+ # Celery stuff
116
+ celerybeat-schedule
117
+ celerybeat.pid
118
+
119
+ # SageMath parsed files
120
+ *.sage.py
121
+
122
+ # Environments
123
+ .env
124
+ .venv
125
+ env/
126
+ venv/
127
+ ENV/
128
+ env.bak/
129
+ venv.bak/
130
+
131
+ # Spyder project settings
132
+ .spyderproject
133
+ .spyproject
134
+
135
+ # Rope project settings
136
+ .ropeproject
137
+
138
+ # mkdocs documentation
139
+ /site
140
+
141
+ # mypy
142
+ .mypy_cache/
143
+ .dmypy.json
144
+ dmypy.json
145
+
146
+ # Pyre type checker
147
+ .pyre/
148
+
149
+ # pytype static type analyzer
150
+ .pytype/
151
+
152
+ # Cython debug symbols
153
+ cython_debug/
154
+
155
+ # PyCharm
156
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
159
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
+ #.idea/
@@ -0,0 +1,39 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: check-ast
6
+ - id: check-builtin-literals
7
+ - id: check-case-conflict
8
+ - id: check-docstring-first
9
+ - id: check-illegal-windows-names
10
+ - id: check-merge-conflict
11
+ - id: check-json
12
+ - id: check-toml
13
+ - id: check-yaml
14
+ - id: end-of-file-fixer
15
+ - id: mixed-line-ending
16
+ - id: check-vcs-permalinks
17
+ - id: check-shebang-scripts-are-executable
18
+ - id: trailing-whitespace
19
+ - id: debug-statements
20
+
21
+ - repo: https://github.com/astral-sh/ruff-pre-commit
22
+ rev: v0.14.3
23
+ hooks:
24
+ - id: ruff
25
+ args: [--fix]
26
+ - id: ruff-format
27
+
28
+ - repo: https://github.com/astral-sh/uv-pre-commit
29
+ rev: 0.9.7
30
+ hooks:
31
+ - id: uv-lock
32
+
33
+ - repo: local
34
+ hooks:
35
+ - id: ty
36
+ name: ty check
37
+ entry: uv run ty check
38
+ language: system
39
+ pass_filenames: false
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0] - 2026-05-20
9
+
10
+ ### Added
11
+
12
+ - Everything (init)
@@ -0,0 +1,73 @@
1
+ # Contributing to Model Ledger
2
+
3
+ ## Development Environment
4
+
5
+ Model Ledger uses [uv](https://docs.astral.sh/uv/) for packaging and
6
+ dependency management. To start developing with Model Ledger, install `uv`
7
+ using the [recommended method](https://docs.astral.sh/uv/#getting-started).
8
+
9
+ Once `uv` is installed, install the dependencies with the following command:
10
+
11
+ ```
12
+ uv sync
13
+ ```
14
+
15
+ It will create a `.venv` in the root of the project.
16
+
17
+ If you want to have a shell in the virtual environment you can activate it with (for Linux/MacOS):
18
+
19
+ ```
20
+ . .venv/bin/activate
21
+ ```
22
+
23
+ The package is linked in editable mode so you will not need to reinstall the package when changing something.
24
+
25
+ Alternatively you can run things in the virtual environment by using a `uv run` prefix for commands, e.g.:
26
+
27
+ ```
28
+ uv run pytest
29
+ ```
30
+
31
+ **The following section commands assume you are in the virtual environment by either activating or prefixing commands with `uv run`!**
32
+
33
+ ### Pre-Commit-Hooks
34
+
35
+ Pre commit hooks can be setup with:
36
+
37
+ ```
38
+ pre-commit install
39
+ ```
40
+
41
+ ### Tests
42
+
43
+ Run tests with the following command:
44
+
45
+ ```
46
+ pytest --cov-report term-missing --cov=modl -vv
47
+ ```
48
+
49
+ New code should ideally have tests and not break existing tests.
50
+
51
+ ### Type Checking
52
+
53
+ Model Ledger uses type annotations throughout, and `ty` to do the checking. Run the following to type check Model Ledger:
54
+
55
+ ```
56
+ ty check
57
+ ```
58
+
59
+ ### Code Formatting
60
+
61
+ Model Ledger uses [`ruff`](https://docs.astral.sh/ruff/) for code formatting.
62
+ Since it is very fast it makes sense to setup your editor to format on save.
63
+
64
+ Use `ruff format` to format all files in the current directory
65
+
66
+ ### Versioning
67
+
68
+ - This tool is using [semantic versioning](https://semver.org/spec/v2.0.0.html). [CHANGELOG.md](./CHANGELOG.md) should be updated on every source code change.
69
+ - A new version can be bumped with the support of [bump-my-version](https://github.com/callowayproject/bump-my-version), which is a `dev` dependency:
70
+ ```bash
71
+ # major,minor,patch
72
+ bump-my-version bump minor
73
+ ```