wunderspec 0.129.3__tar.gz → 0.129.5__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.
- {wunderspec-0.129.3 → wunderspec-0.129.5}/PKG-INFO +35 -14
- wunderspec-0.129.5/README.md +92 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/cheatsheet.html +64 -61
- wunderspec-0.129.5/docs/user-references/wunderspec-five-minutes.md +114 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/payment.py +3 -2
- {wunderspec-0.129.3 → wunderspec-0.129.5}/pyproject.toml +1 -1
- {wunderspec-0.129.3 → wunderspec-0.129.5}/tests/README.md +4 -4
- {wunderspec-0.129.3 → wunderspec-0.129.5}/uv.lock +1 -1
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/__init__.py +1 -1
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/cli.py +10 -4
- wunderspec-0.129.3/README.md +0 -71
- {wunderspec-0.129.3 → wunderspec-0.129.5}/.devcontainer/Dockerfile +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/.devcontainer/devcontainer.json +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/.devcontainer/post-create.sh +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/.flake8 +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/.github/workflows/build.yml +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/.github/workflows/convert-to-tla.yml +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/.github/workflows/lint.yml +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/.github/workflows/publish.yml +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/.github/workflows/run-examples.yml +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/.gitignore +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/CONTRIBUTING.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/LICENSE +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/README.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-avatar-github-1024.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-avatar-github-256.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-avatar-github-512.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-icon-circle-dark-1024.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-icon-circle-dark-512.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-icon-circle-transparent-1024.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-lockup-horizontal-dark-1600.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-lockup-horizontal-dark-800.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-mark-transparent-1024.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-readme-header-dark-1200x480.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-readme-header-dark-1600x640.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-social-preview-1280x640.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/png/wunderspec-wordmark-transparent-900.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/svg/wunderspec-avatar-github.svg +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/svg/wunderspec-icon-circle-dark.svg +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/svg/wunderspec-icon-circle-transparent.svg +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/svg/wunderspec-lockup-horizontal-dark.svg +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/svg/wunderspec-lockup-horizontal-transparent.svg +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/svg/wunderspec-mark-transparent.svg +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/svg/wunderspec-readme-header-dark.svg +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/assets/design/svg/wunderspec-wordmark-transparent.svg +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/basedpyrightconfig.json +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/references/from-quint-llms.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/references/from-tla-llms.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/booleans.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/comprehensions.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/decorators.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/enums.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/flow.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/integers.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/lists.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/maps.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/records.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/sets.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/state-machine.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/strings.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/temporal.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/tuples.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-references/unions.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-stories/bobs_log.md +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-stories/bobs_log.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/docs/user-stories/img/bob.png +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/bags.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/bakery.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/bakery_walk.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/ben_or.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/channel.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/dekker.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/epfd.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/examples.yaml +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/fifo.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/fpaxos.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/kv_store.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/lamport_mutex.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/ledger.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/minimmit.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/producer_consumer.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/readers_writers.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/readers_writers_walk.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/simple_ponzi.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/simple_ponzi_machine.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/simple_ponzi_machine_walk.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/simple_wal1.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/simple_wal2.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/sliding_puzzles.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/sloppy_counter.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/test_simple_ponzi_machine.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/tiny_workers.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/examples/two_phase.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/pyrightconfig.json +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/scripts/__init__.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/scripts/_spec_utils.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/scripts/convert_examples_to_tla.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/scripts/run_examples.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/__main__.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/_edition.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/apalache.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/api.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/__init__.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/action_ast.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/ast.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/list_ast.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/map_ast.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/record_ast.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/serialization.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/set_ast.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/sorts.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/temporal_ast.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/terms.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/tuple_ast.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/ast/union_ast.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/dev_debug.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/doc_format.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/errors.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/exec/__init__.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/exec/action_exec.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/exec/context.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/exec/scheduler.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/explain.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/expr.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/flow.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/fuzzer.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/interpreter.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/interpreter_sampling.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/interpreter_value.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/itf_trace.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/lang.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/linter.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/machine.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/model_checker.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/permutation.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/petnames.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/pretty.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/py.typed +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/quint_convert.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/random_walk.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/source_tracking.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/submachine.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/sym_context.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/tla.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/tlc.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/tlc_trace.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/trace_output.py +0 -0
- {wunderspec-0.129.3 → wunderspec-0.129.5}/wunderspec/uniq_names.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wunderspec
|
|
3
|
-
Version: 0.129.
|
|
3
|
+
Version: 0.129.5
|
|
4
4
|
Summary: Protocol specifications as Python code
|
|
5
5
|
Author: Igor Konnov, Thomas Pani
|
|
6
6
|
License-File: LICENSE
|
|
@@ -18,6 +18,12 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
|
|
19
19
|
# Wunderspec
|
|
20
20
|
|
|
21
|
+
[](https://pypi.org/project/wunderspec/)
|
|
22
|
+
[](https://github.com/wunderspec/wunderspec/actions/workflows/lint.yml)
|
|
23
|
+
[](https://github.com/wunderspec/wunderspec/actions/workflows/build.yml)
|
|
24
|
+
[](https://github.com/wunderspec/wunderspec/actions/workflows/run-examples.yml)
|
|
25
|
+
[](https://github.com/wunderspec/wunderspec/actions/workflows/convert-to-tla.yml)
|
|
26
|
+
|
|
21
27
|
Wunderspec is a Python DSL for writing and checking executable specifications.
|
|
22
28
|
|
|
23
29
|
Distributed systems often fail due to unforeseen issues: message reordering,
|
|
@@ -29,21 +35,24 @@ they become production incidents.
|
|
|
29
35
|
|
|
30
36
|
This is **the open-core distribution**.
|
|
31
37
|
|
|
32
|
-
> 📋 **
|
|
33
|
-
>
|
|
38
|
+
> 📋 **Wunderspec in 5 minutes:** the [Wunderspec in Five Minutes][five-minutes]
|
|
39
|
+
> gives you a quick overview of the core concepts.
|
|
40
|
+
|
|
41
|
+
> 📋 **Cheatsheet:** the [Wunderspec cheatsheet][cheatsheet]
|
|
42
|
+
> summarizes the DSL and CLI in a few pages — keep it open while you work.
|
|
34
43
|
|
|
35
44
|
## 1. Wunderspec in Action
|
|
36
45
|
|
|
37
|
-
Follow Bob in [his Wunderspec adventure]
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
Follow Bob in [his Wunderspec adventure][bobs_log]. There, Bob writes a
|
|
47
|
+
specification of a write-ahead log, finds a bug, replays the counterexample and
|
|
48
|
+
fixes the specification.
|
|
40
49
|
|
|
41
50
|
## 2. Installation
|
|
42
51
|
|
|
43
|
-
Using [uv]
|
|
52
|
+
Using [uv][]:
|
|
44
53
|
|
|
45
54
|
```sh
|
|
46
|
-
uv
|
|
55
|
+
uv tool install wunderspec
|
|
47
56
|
```
|
|
48
57
|
|
|
49
58
|
After installation, the CLI is available:
|
|
@@ -52,6 +61,13 @@ After installation, the CLI is available:
|
|
|
52
61
|
wunderspec --help
|
|
53
62
|
```
|
|
54
63
|
|
|
64
|
+
If you want to add Wunderspec as a dependency, just type:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
uv add wunderspec
|
|
68
|
+
uv sync
|
|
69
|
+
```
|
|
70
|
+
|
|
55
71
|
## 3. Features
|
|
56
72
|
|
|
57
73
|
This open-core package includes the features marked ✅ in the **Open Core**
|
|
@@ -74,14 +90,19 @@ commands are not included in this package.
|
|
|
74
90
|
|
|
75
91
|
## 4. Release Provenance
|
|
76
92
|
|
|
77
|
-
- Release tag: `v0.129.
|
|
78
|
-
- Source commit: `
|
|
93
|
+
- Release tag: `v0.129.5`
|
|
94
|
+
- Source commit: `8561aecba08feb12e97117b0c7632ad83e9d8076`
|
|
79
95
|
|
|
80
|
-
See [tests/README.md]
|
|
81
|
-
for the development test log captured at release time.
|
|
96
|
+
See [tests/README.md][] for the development test log captured at release time.
|
|
82
97
|
|
|
83
98
|
## 5. License
|
|
84
99
|
|
|
85
100
|
Wunderspec is distributed under the Functional Source License, Version 1.1, with
|
|
86
|
-
an Apache 2.0 future license (FSL-1.1-ALv2). See
|
|
87
|
-
|
|
101
|
+
an Apache 2.0 future license (FSL-1.1-ALv2). See [LICENSE][].
|
|
102
|
+
|
|
103
|
+
[cheatsheet]: https://github.com/wunderspec/wunderspec/blob/main/docs/user-references/cheatsheet.html
|
|
104
|
+
[Wunderspec in Five Minutes]: https://github.com/wunderspec/wunderspec/blob/main/docs/user-references/wunderspec-one-pager.md
|
|
105
|
+
[bobs_log]: https://github.com/wunderspec/wunderspec/blob/main/docs/user-stories/bobs_log.md
|
|
106
|
+
[uv]: https://docs.astral.sh/uv/
|
|
107
|
+
[tests/README.md]: https://github.com/wunderspec/wunderspec/blob/main/tests/README.md
|
|
108
|
+
[LICENSE]: https://github.com/wunderspec/wunderspec/blob/main/LICENSE
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# Wunderspec
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/wunderspec/)
|
|
6
|
+
[](https://github.com/wunderspec/wunderspec/actions/workflows/lint.yml)
|
|
7
|
+
[](https://github.com/wunderspec/wunderspec/actions/workflows/build.yml)
|
|
8
|
+
[](https://github.com/wunderspec/wunderspec/actions/workflows/run-examples.yml)
|
|
9
|
+
[](https://github.com/wunderspec/wunderspec/actions/workflows/convert-to-tla.yml)
|
|
10
|
+
|
|
11
|
+
Wunderspec is a Python DSL for writing and checking executable specifications.
|
|
12
|
+
|
|
13
|
+
Distributed systems often fail due to unforeseen issues: message reordering,
|
|
14
|
+
race condition, stale observations, retry behavior, crashes and restarts,
|
|
15
|
+
network outages, vibe code. Adding more example tests is often not enough to
|
|
16
|
+
find these bugs before they make it to production. Specifications written in
|
|
17
|
+
Wunderspec surface these issues early, so that teams find design flaws before
|
|
18
|
+
they become production incidents.
|
|
19
|
+
|
|
20
|
+
This is **the open-core distribution**.
|
|
21
|
+
|
|
22
|
+
> 📋 **Wunderspec in 5 minutes:** the [Wunderspec in Five Minutes][five-minutes]
|
|
23
|
+
> gives you a quick overview of the core concepts.
|
|
24
|
+
|
|
25
|
+
> 📋 **Cheatsheet:** the [Wunderspec cheatsheet][cheatsheet]
|
|
26
|
+
> summarizes the DSL and CLI in a few pages — keep it open while you work.
|
|
27
|
+
|
|
28
|
+
## 1. Wunderspec in Action
|
|
29
|
+
|
|
30
|
+
Follow Bob in [his Wunderspec adventure][bobs_log]. There, Bob writes a
|
|
31
|
+
specification of a write-ahead log, finds a bug, replays the counterexample and
|
|
32
|
+
fixes the specification.
|
|
33
|
+
|
|
34
|
+
## 2. Installation
|
|
35
|
+
|
|
36
|
+
Using [uv][]:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
uv tool install wunderspec
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
After installation, the CLI is available:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
wunderspec --help
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If you want to add Wunderspec as a dependency, just type:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
uv add wunderspec
|
|
52
|
+
uv sync
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 3. Features
|
|
56
|
+
|
|
57
|
+
This open-core package includes the features marked ✅ in the **Open Core**
|
|
58
|
+
column. Premium-only command names stay visible for discoverability, but those
|
|
59
|
+
commands are not included in this package.
|
|
60
|
+
|
|
61
|
+
| Feature | Open Core | Premium |
|
|
62
|
+
| ----------------------------- | :-------: | :-----: |
|
|
63
|
+
| Symbolic expression evaluator | ✅ | ✅ |
|
|
64
|
+
| `wunderspec lint` | ✅ | ✅ |
|
|
65
|
+
| `wunderspec run` | ✅ | ✅ |
|
|
66
|
+
| `wunderspec replay` | ✅ | ✅ |
|
|
67
|
+
| `wunderspec convert` | ✅ | ✅ |
|
|
68
|
+
| `wunderspec check` | ✅ | ✅ |
|
|
69
|
+
| `wunderspec with-tlc` | ✅ | ✅ |
|
|
70
|
+
| `wunderspec with-apalache` | ✅ | ✅ |
|
|
71
|
+
| `wunderspec fuzz` | ❌ | ✅ |
|
|
72
|
+
| `wunderspec rust` | ❌ | ✅ |
|
|
73
|
+
| `wunderspec lean` | ❌ | ✅ |
|
|
74
|
+
|
|
75
|
+
## 4. Release Provenance
|
|
76
|
+
|
|
77
|
+
- Release tag: `v0.129.5`
|
|
78
|
+
- Source commit: `8561aecba08feb12e97117b0c7632ad83e9d8076`
|
|
79
|
+
|
|
80
|
+
See [tests/README.md][] for the development test log captured at release time.
|
|
81
|
+
|
|
82
|
+
## 5. License
|
|
83
|
+
|
|
84
|
+
Wunderspec is distributed under the Functional Source License, Version 1.1, with
|
|
85
|
+
an Apache 2.0 future license (FSL-1.1-ALv2). See [LICENSE][].
|
|
86
|
+
|
|
87
|
+
[cheatsheet]: https://github.com/wunderspec/wunderspec/blob/main/docs/user-references/cheatsheet.html
|
|
88
|
+
[Wunderspec in Five Minutes]: https://github.com/wunderspec/wunderspec/blob/main/docs/user-references/wunderspec-one-pager.md
|
|
89
|
+
[bobs_log]: https://github.com/wunderspec/wunderspec/blob/main/docs/user-stories/bobs_log.md
|
|
90
|
+
[uv]: https://docs.astral.sh/uv/
|
|
91
|
+
[tests/README.md]: https://github.com/wunderspec/wunderspec/blob/main/tests/README.md
|
|
92
|
+
[LICENSE]: https://github.com/wunderspec/wunderspec/blob/main/LICENSE
|
|
@@ -547,15 +547,13 @@
|
|
|
547
547
|
</div>
|
|
548
548
|
<div class="desc">Boolean equality / inequality</div>
|
|
549
549
|
</div>
|
|
550
|
-
<div class="item">
|
|
551
|
-
<div class="sig">Ite(cond, then_expr, else_expr)</div>
|
|
552
|
-
<div class="desc">If-then-else expression; all three arguments are expressions</div>
|
|
553
|
-
</div>
|
|
554
550
|
<div class="item">
|
|
555
551
|
<div class="alt">
|
|
552
|
+
<span class="sig">Ite(cond, then_expr, else_expr)</span>
|
|
553
|
+
<span class="sep">or</span>
|
|
556
554
|
<span class="sig">p.if_(cond).else_(q)</span>
|
|
557
|
-
|
|
558
|
-
<div class="desc">
|
|
555
|
+
</div>
|
|
556
|
+
<div class="desc">If-then-else expression; all three arguments are expressions</div>
|
|
559
557
|
</div>
|
|
560
558
|
</div>
|
|
561
559
|
</section>
|
|
@@ -575,13 +573,17 @@
|
|
|
575
573
|
<div class="sig">x == y x != y x < y x <= y x > y x >= y</div>
|
|
576
574
|
<div class="desc">Comparison operators; return <span class="sig">BoolExpr</span></div>
|
|
577
575
|
</div>
|
|
576
|
+
<div class="item">
|
|
577
|
+
<span class="sig">Set(lo, ..., hi)</span>
|
|
578
|
+
<div class="desc">Inclusive integer set <span class="sig">{lo, …, hi}</span>; distinct from list <span class="sig">Range(start, end)</span></div>
|
|
579
|
+
</div>
|
|
578
580
|
<div class="item">
|
|
579
581
|
<div class="alt">
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
582
|
+
<span class="sig">Ints</span>
|
|
583
|
+
<span class="sep">or</span>
|
|
584
|
+
<span class="sig">UnsignedInts</span>
|
|
583
585
|
</div>
|
|
584
|
-
<div class="desc">
|
|
586
|
+
<div class="desc">All signed / unsigned integers (infinite sets)</div>
|
|
585
587
|
</div>
|
|
586
588
|
</div>
|
|
587
589
|
</section>
|
|
@@ -594,16 +596,16 @@
|
|
|
594
596
|
</header>
|
|
595
597
|
<div class="content">
|
|
596
598
|
<div class="item">
|
|
597
|
-
<div class="sig">Set(
|
|
598
|
-
<div class="desc">New set with the given elements</div>
|
|
599
|
+
<div class="sig">Set(2, 5, 8, 8)</div>
|
|
600
|
+
<div class="desc">New set with the given elements, duplicates ignored</div>
|
|
599
601
|
</div>
|
|
600
602
|
<div class="item">
|
|
601
|
-
<div class="sig">Set(
|
|
602
|
-
<div class="desc">
|
|
603
|
+
<div class="sig">Set(lo, ..., hi)</div>
|
|
604
|
+
<div class="desc">Inclusive integer set <span class="sig">{lo, …, hi}</span></div>
|
|
603
605
|
</div>
|
|
604
606
|
<div class="item">
|
|
605
607
|
<div class="sig">Set(E)</div>
|
|
606
|
-
<div class="desc">Empty set whose elements have
|
|
608
|
+
<div class="desc">Empty set whose elements have sort <span class="sig">E</span></div>
|
|
607
609
|
</div>
|
|
608
610
|
<div class="item">
|
|
609
611
|
<div class="alt">
|
|
@@ -746,12 +748,12 @@
|
|
|
746
748
|
</header>
|
|
747
749
|
<div class="content">
|
|
748
750
|
<div class="item">
|
|
749
|
-
<div class="sig">List(
|
|
750
|
-
<div class="desc">New list with the given elements in order</div>
|
|
751
|
+
<div class="sig">List(2, 3, 3, 4)</div>
|
|
752
|
+
<div class="desc">New list with the given elements in order, duplicates allowed</div>
|
|
751
753
|
</div>
|
|
752
754
|
<div class="item">
|
|
753
755
|
<div class="sig">List(E)</div>
|
|
754
|
-
<div class="desc">Empty list with elements of
|
|
756
|
+
<div class="desc">Empty list with elements of sort <span class="sig">E</span></div>
|
|
755
757
|
</div>
|
|
756
758
|
<div class="item">
|
|
757
759
|
<div class="sig">Range(start, end)</div>
|
|
@@ -835,7 +837,7 @@
|
|
|
835
837
|
</div>
|
|
836
838
|
<div class="item">
|
|
837
839
|
<div class="sig">M[key]</div>
|
|
838
|
-
<div class="desc">Look up value for <span class="sig">key</span
|
|
840
|
+
<div class="desc">Look up value for <span class="sig">key</span> (<span class="sig">key</span> must exist)</div>
|
|
839
841
|
</div>
|
|
840
842
|
<div class="item">
|
|
841
843
|
<div class="sig">M.insert(k, v)</div>
|
|
@@ -1069,6 +1071,49 @@ class Flag:
|
|
|
1069
1071
|
</div>
|
|
1070
1072
|
</section>
|
|
1071
1073
|
|
|
1074
|
+
<!-- Temporal -->
|
|
1075
|
+
<section class="card" data-title="temporal always eventually enabled weakfair strongfair liveness safety action_expr forall exists fairness">
|
|
1076
|
+
<header>
|
|
1077
|
+
<h2>Temporal expressions</h2>
|
|
1078
|
+
<span class="hint">TemporalExpr — used in @temporal properties</span>
|
|
1079
|
+
</header>
|
|
1080
|
+
<div class="content">
|
|
1081
|
+
<div class="item">
|
|
1082
|
+
<div class="sig">Always(P)</div>
|
|
1083
|
+
<div class="desc">□P — <span class="sig">P</span> holds in every state of the execution</div>
|
|
1084
|
+
</div>
|
|
1085
|
+
<div class="item">
|
|
1086
|
+
<div class="sig">Eventually(P)</div>
|
|
1087
|
+
<div class="desc">◇P — <span class="sig">P</span> holds in some future state</div>
|
|
1088
|
+
</div>
|
|
1089
|
+
<div class="item">
|
|
1090
|
+
<div class="sig">AndT(t1, t2) OrT(t1, t2) NotT(t) ImpliesT(t1, t2)</div>
|
|
1091
|
+
<div class="desc">Temporal connectives; Boolean arguments are lifted to <span class="sig">TemporalExpr</span></div>
|
|
1092
|
+
</div>
|
|
1093
|
+
<div class="item">
|
|
1094
|
+
<div class="sig">Enabled(my_action, arg)</div>
|
|
1095
|
+
<div class="desc">True if <span class="sig">my_action</span> can fire; requires <span class="sig">@action(inline=False)</span></div>
|
|
1096
|
+
</div>
|
|
1097
|
+
<div class="item">
|
|
1098
|
+
<div class="sig">Enabled(action_expr)</div>
|
|
1099
|
+
<div class="desc">Direct action-expression form of <span class="sig">ENABLED A</span></div>
|
|
1100
|
+
</div>
|
|
1101
|
+
<div class="item">
|
|
1102
|
+
<div class="sig">WeakFair(my_action, arg, vars=("x",))</div>
|
|
1103
|
+
<div class="desc">WF — if action is continuously enabled it must eventually fire</div>
|
|
1104
|
+
</div>
|
|
1105
|
+
<div class="item">
|
|
1106
|
+
<div class="sig">StrongFair(my_action, arg, vars=("x",))</div>
|
|
1107
|
+
<div class="desc">SF — if action is enabled infinitely often it must eventually fire</div>
|
|
1108
|
+
</div>
|
|
1109
|
+
<div class="item">
|
|
1110
|
+
<div class="sig">Forall(WeakFair(send, p, vars=("msgs",)) for p in PROCS)</div>
|
|
1111
|
+
<div class="desc">Temporal quantification over a parameter set</div>
|
|
1112
|
+
</div>
|
|
1113
|
+
</div>
|
|
1114
|
+
</section>
|
|
1115
|
+
|
|
1116
|
+
|
|
1072
1117
|
<!-- State machine -->
|
|
1073
1118
|
<section class="card" style="grid-column:span 12" data-title="state machine action context one_of alternatives split assume cache Param StateVar PARAMETER StateFieldType UPSERT direct assignment nested editing atomic">
|
|
1074
1119
|
<header>
|
|
@@ -1277,48 +1322,6 @@ InChan = SubMachine(Data="Message", chan="cin")
|
|
|
1277
1322
|
</div>
|
|
1278
1323
|
</section>
|
|
1279
1324
|
|
|
1280
|
-
<!-- Temporal -->
|
|
1281
|
-
<section class="card" data-title="temporal always eventually enabled weakfair strongfair liveness safety action_expr forall exists fairness">
|
|
1282
|
-
<header>
|
|
1283
|
-
<h2>Temporal expressions</h2>
|
|
1284
|
-
<span class="hint">TemporalExpr — used in @temporal properties</span>
|
|
1285
|
-
</header>
|
|
1286
|
-
<div class="content">
|
|
1287
|
-
<div class="item">
|
|
1288
|
-
<div class="sig">Always(P)</div>
|
|
1289
|
-
<div class="desc">□P — <span class="sig">P</span> holds in every state of the execution</div>
|
|
1290
|
-
</div>
|
|
1291
|
-
<div class="item">
|
|
1292
|
-
<div class="sig">Eventually(P)</div>
|
|
1293
|
-
<div class="desc">◇P — <span class="sig">P</span> holds in some future state</div>
|
|
1294
|
-
</div>
|
|
1295
|
-
<div class="item">
|
|
1296
|
-
<div class="sig">AndT(t1, t2) OrT(t1, t2) NotT(t) ImpliesT(t1, t2)</div>
|
|
1297
|
-
<div class="desc">Temporal connectives; Boolean arguments are lifted to <span class="sig">TemporalExpr</span></div>
|
|
1298
|
-
</div>
|
|
1299
|
-
<div class="item">
|
|
1300
|
-
<div class="sig">Enabled(my_action, arg)</div>
|
|
1301
|
-
<div class="desc">True if <span class="sig">my_action</span> can fire; requires <span class="sig">@action(inline=False)</span></div>
|
|
1302
|
-
</div>
|
|
1303
|
-
<div class="item">
|
|
1304
|
-
<div class="sig">Enabled(action_expr)</div>
|
|
1305
|
-
<div class="desc">Direct action-expression form of <span class="sig">ENABLED A</span></div>
|
|
1306
|
-
</div>
|
|
1307
|
-
<div class="item">
|
|
1308
|
-
<div class="sig">WeakFair(my_action, arg, vars=("x",))</div>
|
|
1309
|
-
<div class="desc">WF — if action is continuously enabled it must eventually fire</div>
|
|
1310
|
-
</div>
|
|
1311
|
-
<div class="item">
|
|
1312
|
-
<div class="sig">StrongFair(my_action, arg, vars=("x",))</div>
|
|
1313
|
-
<div class="desc">SF — if action is enabled infinitely often it must eventually fire</div>
|
|
1314
|
-
</div>
|
|
1315
|
-
<div class="item">
|
|
1316
|
-
<div class="sig">Forall(WeakFair(send, p, vars=("msgs",)) for p in PROCS)</div>
|
|
1317
|
-
<div class="desc">Temporal quantification over a parameter set</div>
|
|
1318
|
-
</div>
|
|
1319
|
-
</div>
|
|
1320
|
-
</section>
|
|
1321
|
-
|
|
1322
1325
|
<!-- Imperative flow -->
|
|
1323
1326
|
<section class="card" data-title="flow with_flow if else return end imperative conditional">
|
|
1324
1327
|
<header>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Wunderspec in Five Minutes
|
|
2
|
+
|
|
3
|
+
Since nobody has time to read docs, here is a one-page summary of what you want
|
|
4
|
+
to know about Wunderspec. For more details, see the [Cheatsheet][cheatsheet].
|
|
5
|
+
|
|
6
|
+
**States and actions**. The most important definitions are annotated with
|
|
7
|
+
`@state` and `@action`. Wunderspec uses these definitions to define a *state
|
|
8
|
+
machine*. This state machine starts in a state defined with
|
|
9
|
+
`@action(init=True)` and evolves via one or more actions labeled with `@action`.
|
|
10
|
+
Normally, the top-level actions are grouped with an action called `step`. All
|
|
11
|
+
actions are atomic -- either an action executes (when all assumptions are met),
|
|
12
|
+
and its effects are applied, or it doesn't execute at all. For a simple example,
|
|
13
|
+
see the [sloppy counter][sloppy_counter].
|
|
14
|
+
|
|
15
|
+
**Assumptions and assignments**. Actions normally contain assumptions about
|
|
16
|
+
the current state and assignments to the next state. Here is a typical example
|
|
17
|
+
from the [ledger][ledger]:
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
@action
|
|
21
|
+
def start_transaction(c: Context[LedgerState], company: Annotated[Expr, str]):
|
|
22
|
+
s = c.state
|
|
23
|
+
c.assume(s.pending_seq_no[company] == -1)
|
|
24
|
+
s.pending_seq_no[company] = s.registered_seq_no[company]
|
|
25
|
+
s.registered_seq_no[company] += 1
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The Wunderspec transpiler collects the assumptions and assignments, when the
|
|
29
|
+
Python code is executed. They are used to define the state machine.
|
|
30
|
+
|
|
31
|
+
**Every variable of a state machine may be assigned at most once in an action**.
|
|
32
|
+
Use local Python variables to compute intermediate values.
|
|
33
|
+
|
|
34
|
+
**Value generation**. State machines in Wunderspec often use
|
|
35
|
+
non-deterministically generated values. These values are produced with `with
|
|
36
|
+
c.one_of(...)`. We can see an example in the [sloppy counter][sloppy_counter]:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
@action
|
|
40
|
+
def step(c: Context[SloppyCounterState]):
|
|
41
|
+
with c.one_of(cpus(c.state), "cpu") as cpu:
|
|
42
|
+
increment(c, cpu)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
In the code above, `cpu` receive a value from the set `cpus(c.state)`. The
|
|
46
|
+
second argument to `one_of` is a convenient alias for the intermediate
|
|
47
|
+
representation and generated code. When it's omitted, the name is generated
|
|
48
|
+
automatically. Wunderspec commands interpret this construct differently:
|
|
49
|
+
|
|
50
|
+
- `wunderspec run` generates values randomly,
|
|
51
|
+
|
|
52
|
+
- `wunderspec check` generates values systematically to explore all possible
|
|
53
|
+
executions,
|
|
54
|
+
|
|
55
|
+
- `wunderspec with-apalache` produces values with the SMT solver Z3.
|
|
56
|
+
|
|
57
|
+
**Control non-determinism.** Wunderspec does not have explicit processes,
|
|
58
|
+
threads, processors, or other computing devices. Instead, is uses control
|
|
59
|
+
non-determinism to model choice of actions. Non-deterministic choice is done
|
|
60
|
+
with `with c.alternatives(...)`. For example, he is how [simple
|
|
61
|
+
WAL][simple_wal1] defines `step` with alternatives:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
@action
|
|
65
|
+
def step(c: Context[KvStoreState]):
|
|
66
|
+
run, crash = c.alternatives("Run", "Restart")
|
|
67
|
+
with run:
|
|
68
|
+
kv_step(c)
|
|
69
|
+
with crash:
|
|
70
|
+
s = c.state
|
|
71
|
+
s.pc = Val(PC.Start)
|
|
72
|
+
s.pending = Command.Nop()
|
|
73
|
+
s.kv_mem = s.KEYS.map_to(lambda _: Val(0))
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
In the above code, the call to `c.alternatives` introduces two branching
|
|
77
|
+
alternatives, "Run" and "Restart". Wunderspec commands interpret these
|
|
78
|
+
alternatives differently:
|
|
79
|
+
|
|
80
|
+
- `wunderspec run` chooses one of the alternatives randomly,
|
|
81
|
+
|
|
82
|
+
- `wunderspec check` explores all possible alternatives systematically,
|
|
83
|
+
|
|
84
|
+
- `wunderspec with-apalache` explores the alternatives symbolically.
|
|
85
|
+
|
|
86
|
+
Similar to `c.alternatives`, we have a special version `c.split(condition)` that
|
|
87
|
+
introduces two alternatives, one where `condition` holds and another where it
|
|
88
|
+
doesn't hold.
|
|
89
|
+
|
|
90
|
+
**Properties.** Properties are the targets for analysis tools. Wunderspec
|
|
91
|
+
currently has three kinds of properties:
|
|
92
|
+
|
|
93
|
+
- state `@invariant` is a Boolean condition over a state that must hold in all
|
|
94
|
+
states reachable from the initial states. All tools check state invariants. If
|
|
95
|
+
they find a state that violates the invariant, they report a counterexample
|
|
96
|
+
execution.
|
|
97
|
+
|
|
98
|
+
- state `@example` is a Boolean condition over a state that maybe holds in some
|
|
99
|
+
execution of the state machine. Tools report one or more executions that lead
|
|
100
|
+
to a state that satisfies the example.
|
|
101
|
+
|
|
102
|
+
- `@temporal` properties must hold in all executions. They are typically used
|
|
103
|
+
to check complex safety properties and liveness properties. Currently, only
|
|
104
|
+
`with-tlc` and `with-apalache` check temporal properties.
|
|
105
|
+
|
|
106
|
+
**Immutability.** While the Wunderspec state machines mutate the state,
|
|
107
|
+
the expressions operate over immutable data structures. For the data
|
|
108
|
+
structures, see the [Cheatsheet][cheatsheet].
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
[sloppy_counter]: https://github.com/wunderspec/wunderspec/blob/main/examples/sloppy_counter.py
|
|
112
|
+
[ledger]: https://github.com/wunderspec/wunderspec/blob/main/examples/ledger.py
|
|
113
|
+
[simple_wal1]: https://github.com/wunderspec/wunderspec/blob/main/examples/simple_wal1.py
|
|
114
|
+
[cheatsheet]: https://wunderspec.com/cheatsheet
|
|
@@ -15,6 +15,7 @@ from wunderspec import (
|
|
|
15
15
|
Exists,
|
|
16
16
|
Expr,
|
|
17
17
|
Field,
|
|
18
|
+
Forall,
|
|
18
19
|
List,
|
|
19
20
|
Map,
|
|
20
21
|
Param,
|
|
@@ -394,12 +395,12 @@ def type_invariant(s: PaymentState) -> BoolExpr:
|
|
|
394
395
|
|
|
395
396
|
@invariant
|
|
396
397
|
def no_overdraft(s: PaymentState) -> BoolExpr:
|
|
397
|
-
return
|
|
398
|
+
return Forall(s.balance[account] >= 0 for account in accounts(s))
|
|
398
399
|
|
|
399
400
|
|
|
400
401
|
@invariant
|
|
401
402
|
def no_double_charge(s: PaymentState) -> BoolExpr:
|
|
402
|
-
return
|
|
403
|
+
return Forall(event_count(s, txn, CHARGED) <= Val(1) for txn in s.Txn)
|
|
403
404
|
|
|
404
405
|
|
|
405
406
|
@invariant
|
|
@@ -6,9 +6,9 @@ public distribution to keep the package focused. The full development
|
|
|
6
6
|
|
|
7
7
|
## Release Provenance
|
|
8
8
|
|
|
9
|
-
- Release tag: `v0.129.
|
|
10
|
-
- Source commit: `
|
|
11
|
-
- Test log captured at: `2026-06-
|
|
9
|
+
- Release tag: `v0.129.5`
|
|
10
|
+
- Source commit: `8561aecba08feb12e97117b0c7632ad83e9d8076`
|
|
11
|
+
- Test log captured at: `2026-06-16T19:14:56Z`
|
|
12
12
|
- `make test` exit code: `0`
|
|
13
13
|
|
|
14
14
|
## Full Test Log From the Development Repository
|
|
@@ -124,5 +124,5 @@ tests/test_tla.py ...................................................... [ 94%]
|
|
|
124
124
|
tests/test_tlc_trace.py .... [ 98%]
|
|
125
125
|
tests/test_trace_output.py ......................... [100%]
|
|
126
126
|
|
|
127
|
-
====================== 2343 passed, 11 skipped in
|
|
127
|
+
====================== 2343 passed, 11 skipped in 22.31s =======================
|
|
128
128
|
```
|
|
@@ -500,19 +500,23 @@ def main() -> None:
|
|
|
500
500
|
|
|
501
501
|
convert_parser = subparsers.add_parser(
|
|
502
502
|
"convert",
|
|
503
|
-
help="Convert
|
|
503
|
+
help="Convert Wunderspec to TLA+ or Quint to Wunderspec",
|
|
504
|
+
description=(
|
|
505
|
+
"Convert a Wunderspec .py module to TLA+ .tla, "
|
|
506
|
+
"or a Quint .qnt module to Wunderspec .py."
|
|
507
|
+
),
|
|
504
508
|
)
|
|
505
509
|
convert_parser.add_argument(
|
|
506
510
|
"--from",
|
|
507
511
|
dest="source",
|
|
508
512
|
required=True,
|
|
509
|
-
help="Source
|
|
513
|
+
help="Source specification file: Wunderspec .py or Quint .qnt",
|
|
510
514
|
)
|
|
511
515
|
convert_parser.add_argument(
|
|
512
516
|
"--to",
|
|
513
517
|
dest="output",
|
|
514
518
|
required=True,
|
|
515
|
-
help="Output file path
|
|
519
|
+
help="Output file path: .tla for Wunderspec input, .py for Quint input",
|
|
516
520
|
)
|
|
517
521
|
convert_parser.add_argument(
|
|
518
522
|
"--defs",
|
|
@@ -524,7 +528,9 @@ def main() -> None:
|
|
|
524
528
|
"--instance",
|
|
525
529
|
default=None,
|
|
526
530
|
help="Name of a partially specialized state object in the source module "
|
|
527
|
-
"(e.g., proto5).
|
|
531
|
+
"(e.g., proto5). Applies only to Wunderspec-to-TLA+ conversion; "
|
|
532
|
+
"ignored for Quint input. Generates an MC*_<Base> wrapper with fixed "
|
|
533
|
+
"constants and INSTANCE.",
|
|
528
534
|
)
|
|
529
535
|
convert_parser.add_argument(
|
|
530
536
|
"--text-width",
|