tlaplus-cli 0.7.0__tar.gz → 0.8.1__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.
- {tlaplus_cli-0.7.0/src/tlaplus_cli.egg-info → tlaplus_cli-0.8.1}/PKG-INFO +30 -11
- tlaplus_cli-0.8.1/README.md +40 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/pyproject.toml +1 -1
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cli.py +3 -1
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/modules/add.py +2 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/modules/remove.py +2 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tlc.py +37 -7
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/list.py +1 -0
- tlaplus_cli-0.8.1/src/tlaplus_cli/tlc/run_cache.py +61 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/runner.py +62 -4
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/__init__.py +8 -0
- tlaplus_cli-0.8.1/src/tlaplus_cli/versioning/app_version.py +62 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/paths.py +8 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1/src/tlaplus_cli.egg-info}/PKG-INFO +30 -11
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli.egg-info/SOURCES.txt +2 -0
- tlaplus_cli-0.7.0/README.md +0 -21
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/LICENSE +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/MANIFEST.in +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/setup.cfg +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cache/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cache/github.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/check_java.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/config/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/config/edit.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/config/list.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/fetch_cache/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/fetch_cache/clear.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/modules/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/modules/list.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/modules/path.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/dir.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/install.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/meta/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/meta/sync.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/path.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/pin.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/uninstall.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/upgrade.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/config/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/config/loader.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/config/schema.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/java/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/java/classpath.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/java/inspector.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/project/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/project/core.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/py.typed +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/resources/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/resources/default_config.yaml +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/__init__.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/codes.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/compiler.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/components.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/formatter.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/models.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/parser.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/sany.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/values.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/ui.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/api.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/downloader.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/metadata.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/resolver.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/schema.py +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli.egg-info/dependency_links.txt +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli.egg-info/entry_points.txt +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli.egg-info/requires.txt +0 -0
- {tlaplus_cli-0.7.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tlaplus-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: TLA+ tools: download TLC, compile custom modules, run model checker
|
|
5
5
|
Author-email: Denis Nikolskiy <codeomatics@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -32,20 +32,39 @@ Dynamic: license-file
|
|
|
32
32
|
|
|
33
33
|
Command-line tool for working with TLA+ specifications and the TLC model checker.
|
|
34
34
|
|
|
35
|
-
## Project
|
|
35
|
+
## Project Goals
|
|
36
36
|
|
|
37
37
|
1. Simplify managing TLA+ tools such as TLC, community modules, custom modules, etc.
|
|
38
38
|
2. Run TLA tools in a single CLI interface.
|
|
39
|
-
3. Manage and display the
|
|
39
|
+
3. Manage and display the tool outputs.
|
|
40
40
|
|
|
41
|
-
##
|
|
41
|
+
## Quick Start & Usage
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
*
|
|
45
|
-
*
|
|
43
|
+
### 1. Toolset Management (`tla tools`)
|
|
44
|
+
* `tla check-java`: Verify Java installation and compatibility.
|
|
45
|
+
* `tla tools list`: List available and installed toolset versions (`tla2tools.jar`).
|
|
46
|
+
* `tla tools install [vX.Y.Z]`: Download and install a toolset version.
|
|
47
|
+
* `tla tools pin <version>`: Set default toolset version for TLC runs.
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
### 2. Model Checking (`tla tlc`)
|
|
50
|
+
* `tla tlc <spec>`: Execute TLC model checker on a specification.
|
|
51
|
+
* `tla tlc <spec> --show-command`: Inspect exact JVM command before execution.
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
*
|
|
51
|
-
*
|
|
53
|
+
### 3. Custom Java Modules (`tla modules`)
|
|
54
|
+
* `tla modules add <path>`: Compile Java operator overrides and register module in CLI cache.
|
|
55
|
+
* `tla modules list`: Display cached custom modules.
|
|
56
|
+
* `tla modules remove <name>`: Remove custom module from cache.
|
|
57
|
+
|
|
58
|
+
For complete, runnable specifications and Java module examples, see [docs/examples](docs/examples/).
|
|
59
|
+
|
|
60
|
+
## Quick Installation
|
|
61
|
+
|
|
62
|
+
### Via `uv tool`
|
|
63
|
+
```bash
|
|
64
|
+
uv tool install tlaplus-cli
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Via Nix Flakes
|
|
68
|
+
```bash
|
|
69
|
+
nix profile install github:nikolskiy/tlaplus-cli
|
|
70
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# TLA+ CLI
|
|
2
|
+
|
|
3
|
+
Command-line tool for working with TLA+ specifications and the TLC model checker.
|
|
4
|
+
|
|
5
|
+
## Project Goals
|
|
6
|
+
|
|
7
|
+
1. Simplify managing TLA+ tools such as TLC, community modules, custom modules, etc.
|
|
8
|
+
2. Run TLA tools in a single CLI interface.
|
|
9
|
+
3. Manage and display the tool outputs.
|
|
10
|
+
|
|
11
|
+
## Quick Start & Usage
|
|
12
|
+
|
|
13
|
+
### 1. Toolset Management (`tla tools`)
|
|
14
|
+
* `tla check-java`: Verify Java installation and compatibility.
|
|
15
|
+
* `tla tools list`: List available and installed toolset versions (`tla2tools.jar`).
|
|
16
|
+
* `tla tools install [vX.Y.Z]`: Download and install a toolset version.
|
|
17
|
+
* `tla tools pin <version>`: Set default toolset version for TLC runs.
|
|
18
|
+
|
|
19
|
+
### 2. Model Checking (`tla tlc`)
|
|
20
|
+
* `tla tlc <spec>`: Execute TLC model checker on a specification.
|
|
21
|
+
* `tla tlc <spec> --show-command`: Inspect exact JVM command before execution.
|
|
22
|
+
|
|
23
|
+
### 3. Custom Java Modules (`tla modules`)
|
|
24
|
+
* `tla modules add <path>`: Compile Java operator overrides and register module in CLI cache.
|
|
25
|
+
* `tla modules list`: Display cached custom modules.
|
|
26
|
+
* `tla modules remove <name>`: Remove custom module from cache.
|
|
27
|
+
|
|
28
|
+
For complete, runnable specifications and Java module examples, see [docs/examples](docs/examples/).
|
|
29
|
+
|
|
30
|
+
## Quick Installation
|
|
31
|
+
|
|
32
|
+
### Via `uv tool`
|
|
33
|
+
```bash
|
|
34
|
+
uv tool install tlaplus-cli
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Via Nix Flakes
|
|
38
|
+
```bash
|
|
39
|
+
nix profile install github:nikolskiy/tlaplus-cli
|
|
40
|
+
```
|
|
@@ -11,6 +11,7 @@ from tlaplus_cli.cmd.modules import app as modules_app
|
|
|
11
11
|
from tlaplus_cli.cmd.tlc import tlc as run_tlc_cmd
|
|
12
12
|
from tlaplus_cli.cmd.tools import app as tools_app
|
|
13
13
|
from tlaplus_cli.config.loader import load_config
|
|
14
|
+
from tlaplus_cli.versioning import get_app_version
|
|
14
15
|
|
|
15
16
|
app = typer.Typer(
|
|
16
17
|
name="tla",
|
|
@@ -23,7 +24,8 @@ app = typer.Typer(
|
|
|
23
24
|
def version_callback(value: bool) -> None:
|
|
24
25
|
if value:
|
|
25
26
|
meta = importlib.metadata.metadata("tlaplus-cli")
|
|
26
|
-
|
|
27
|
+
app_ver = get_app_version(base_version=meta["Version"])
|
|
28
|
+
typer.echo(f"{meta['Name']} v{app_ver}")
|
|
27
29
|
typer.echo(meta["Summary"])
|
|
28
30
|
raise typer.Exit()
|
|
29
31
|
|
|
@@ -11,6 +11,7 @@ import typer
|
|
|
11
11
|
from tlaplus_cli.config.loader import load_config
|
|
12
12
|
from tlaplus_cli.java.classpath import ClasspathResolver, ResolveMode
|
|
13
13
|
from tlaplus_cli.tlc.compiler import get_tlc_jar_path
|
|
14
|
+
from tlaplus_cli.tlc.run_cache import clear_tlc_run_cache
|
|
14
15
|
from tlaplus_cli.versioning.paths import get_modules_dir
|
|
15
16
|
|
|
16
17
|
|
|
@@ -135,4 +136,5 @@ def add_module( # noqa: PLR0912, PLR0915
|
|
|
135
136
|
with (target_dir / "metadata.json").open("w") as f:
|
|
136
137
|
json.dump(metadata, f, indent=2)
|
|
137
138
|
|
|
139
|
+
clear_tlc_run_cache()
|
|
138
140
|
typer.echo(f"Module '{module_name}' successfully added to cache.")
|
|
@@ -2,6 +2,7 @@ import shutil
|
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
4
|
|
|
5
|
+
from tlaplus_cli.tlc.run_cache import clear_tlc_run_cache
|
|
5
6
|
from tlaplus_cli.versioning.paths import get_modules_dir
|
|
6
7
|
|
|
7
8
|
|
|
@@ -14,6 +15,7 @@ def remove_module(module_name: str = typer.Argument(..., help="Name of the modul
|
|
|
14
15
|
|
|
15
16
|
try:
|
|
16
17
|
shutil.rmtree(target_dir)
|
|
18
|
+
clear_tlc_run_cache()
|
|
17
19
|
typer.echo(f"Module '{module_name}' successfully deleted from cache.")
|
|
18
20
|
except OSError as e:
|
|
19
21
|
typer.echo(f"Error: Failed to delete module: {e}", err=True)
|
|
@@ -12,9 +12,11 @@ from tlaplus_cli.tlc.compiler import get_tlc_jar_path
|
|
|
12
12
|
from tlaplus_cli.tlc.components import StatsTableFormatter, StatusFooterRenderable
|
|
13
13
|
from tlaplus_cli.tlc.formatter import TlcFormatter
|
|
14
14
|
from tlaplus_cli.tlc.models import CheckState, ModelCheckResult
|
|
15
|
+
from tlaplus_cli.tlc.run_cache import clear_tlc_run_cache
|
|
15
16
|
from tlaplus_cli.tlc.runner import (
|
|
16
17
|
build_tlc_command,
|
|
17
18
|
get_tlc_version,
|
|
19
|
+
resolve_cfg_file,
|
|
18
20
|
resolve_spec_file,
|
|
19
21
|
run_tlc,
|
|
20
22
|
)
|
|
@@ -62,9 +64,13 @@ def _show_diagnostic_info(spec: str, spec_name: str) -> None:
|
|
|
62
64
|
|
|
63
65
|
# PLR0912: This command handler orchestrates multiple output types (errors, warnings, results, progress);
|
|
64
66
|
# splitting it would fragment the CLI's primary user-facing logic.
|
|
67
|
+
# PLR0913: CLI command function accepts options as parameters.
|
|
65
68
|
# PLR0915: Too many statements.
|
|
66
|
-
def tlc( # noqa: PLR0912, PLR0915
|
|
67
|
-
spec: str = typer.Argument(
|
|
69
|
+
def tlc( # noqa: PLR0912, PLR0913, PLR0915
|
|
70
|
+
spec: str | None = typer.Argument(
|
|
71
|
+
None,
|
|
72
|
+
help="Name of the TLA+ specification (without .tla extension).",
|
|
73
|
+
),
|
|
68
74
|
# Typer requires the parameter in the signature to register the CLI option;
|
|
69
75
|
# since the logic is in the eager callback, the value is not used here.
|
|
70
76
|
version: bool | None = typer.Option( # noqa: ARG001
|
|
@@ -75,6 +81,7 @@ def tlc( # noqa: PLR0912, PLR0915
|
|
|
75
81
|
is_eager=True,
|
|
76
82
|
),
|
|
77
83
|
show_command: bool = typer.Option(False, "--show-command", help="Print the command instead of executing it."),
|
|
84
|
+
cleanup: bool = typer.Option(False, "--cleanup", help="Purge all cached TLC run artifacts from cache."),
|
|
78
85
|
_refresh_interval: float | None = typer.Option(
|
|
79
86
|
None,
|
|
80
87
|
"--refresh-interval",
|
|
@@ -82,17 +89,39 @@ def tlc( # noqa: PLR0912, PLR0915
|
|
|
82
89
|
),
|
|
83
90
|
coverage: bool = typer.Option(False, "--coverage", help="Enable code coverage visualization."),
|
|
84
91
|
raw: bool = typer.Option(False, "--raw", help="Run TLC in raw mode without structured tool mode formatting."),
|
|
92
|
+
cfg: str | None = typer.Option(
|
|
93
|
+
None,
|
|
94
|
+
"--cfg",
|
|
95
|
+
help="Custom TLC configuration file name or path (e.g. spec-v1 or spec-v1.cfg).",
|
|
96
|
+
),
|
|
85
97
|
) -> None:
|
|
86
98
|
"""Run TLC model checker on a TLA+ specification."""
|
|
99
|
+
if cleanup:
|
|
100
|
+
clear_tlc_run_cache()
|
|
101
|
+
ui.success("TLC run cache cleared successfully.")
|
|
102
|
+
raise typer.Exit(0)
|
|
103
|
+
|
|
104
|
+
if not spec:
|
|
105
|
+
ui.error("Missing argument 'SPEC'.")
|
|
106
|
+
raise typer.Exit(1)
|
|
107
|
+
|
|
87
108
|
# version argument is handled by version_callback (is_eager=True)
|
|
88
109
|
try:
|
|
89
|
-
|
|
110
|
+
spec_file, _spec_name = resolve_spec_file(spec)
|
|
111
|
+
if cfg:
|
|
112
|
+
config_obj = load_config()
|
|
113
|
+
project_root = find_project_root(
|
|
114
|
+
spec_file,
|
|
115
|
+
modules_dir=config_obj.workspace.modules_dir,
|
|
116
|
+
classes_dir=config_obj.workspace.classes_dir,
|
|
117
|
+
)
|
|
118
|
+
resolve_cfg_file(cfg, spec_file, project_root=project_root)
|
|
90
119
|
except FileNotFoundError as e:
|
|
91
120
|
ui.error(str(e))
|
|
92
121
|
raise typer.Exit(1) from None
|
|
93
122
|
|
|
94
123
|
if show_command:
|
|
95
|
-
cmd = build_tlc_command(spec)
|
|
124
|
+
cmd = build_tlc_command(spec, cfg=cfg)
|
|
96
125
|
if coverage:
|
|
97
126
|
cmd.extend(["-coverage", "1"])
|
|
98
127
|
if not cmd:
|
|
@@ -101,9 +130,10 @@ def tlc( # noqa: PLR0912, PLR0915
|
|
|
101
130
|
raise typer.Exit(0)
|
|
102
131
|
|
|
103
132
|
if raw:
|
|
104
|
-
exit_code = run_tlc(spec, coverage=coverage, raw=True)
|
|
133
|
+
exit_code = run_tlc(spec, coverage=coverage, raw=True, cfg=cfg)
|
|
105
134
|
raise typer.Exit(exit_code)
|
|
106
135
|
|
|
136
|
+
|
|
107
137
|
tlc_version = get_tlc_version() or "unknown"
|
|
108
138
|
formatter = TlcFormatter(tlc_version=tlc_version)
|
|
109
139
|
layout = formatter.create_layout()
|
|
@@ -164,7 +194,7 @@ def tlc( # noqa: PLR0912, PLR0915
|
|
|
164
194
|
live.console.print(table_formatter.get_border())
|
|
165
195
|
stats_border_printed = True
|
|
166
196
|
|
|
167
|
-
exit_code = run_tlc(spec, callback=wrapped_callback, coverage=coverage)
|
|
197
|
+
exit_code = run_tlc(spec, callback=wrapped_callback, coverage=coverage, cfg=cfg)
|
|
168
198
|
else:
|
|
169
199
|
# Non-TTY mode: no Live block
|
|
170
200
|
table_formatter = StatsTableFormatter()
|
|
@@ -207,7 +237,7 @@ def tlc( # noqa: PLR0912, PLR0915
|
|
|
207
237
|
print(table_formatter.get_border())
|
|
208
238
|
stats_border_printed = True
|
|
209
239
|
|
|
210
|
-
exit_code = run_tlc(spec, callback=wrapped_callback, coverage=coverage)
|
|
240
|
+
exit_code = run_tlc(spec, callback=wrapped_callback, coverage=coverage, cfg=cfg)
|
|
211
241
|
|
|
212
242
|
# Fallback print of border if not printed yet
|
|
213
243
|
if stats_header_printed and not stats_border_printed:
|
|
@@ -15,6 +15,7 @@ from tlaplus_cli.versioning import (
|
|
|
15
15
|
|
|
16
16
|
@app.command(name="list")
|
|
17
17
|
def list_versions() -> None:
|
|
18
|
+
"""List available and installed TLC versions."""
|
|
18
19
|
config = load_config()
|
|
19
20
|
versions, status = fetch_remote_versions(config.tla.urls.tags, config.tla.urls.releases, config.tla.urls.per_page)
|
|
20
21
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import hashlib
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from tlaplus_cli.versioning.paths import get_tlc_run_dir
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def compute_spec_hash(spec_file: Path) -> str:
|
|
10
|
+
"""Compute an 8-character SHA-256 short hash for a TLA+ specification and its configuration file."""
|
|
11
|
+
hasher = hashlib.sha256()
|
|
12
|
+
|
|
13
|
+
# Include absolute path string
|
|
14
|
+
hasher.update(str(spec_file.absolute()).encode("utf-8"))
|
|
15
|
+
|
|
16
|
+
# Include spec file contents if it exists
|
|
17
|
+
with contextlib.suppress(OSError):
|
|
18
|
+
hasher.update(spec_file.read_bytes())
|
|
19
|
+
|
|
20
|
+
# Include .cfg file contents if present
|
|
21
|
+
cfg_file = spec_file.with_suffix(".cfg")
|
|
22
|
+
if cfg_file.is_file():
|
|
23
|
+
with contextlib.suppress(OSError):
|
|
24
|
+
hasher.update(cfg_file.read_bytes())
|
|
25
|
+
|
|
26
|
+
return hasher.hexdigest()[:8]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _cleanup_stale_spec_dirs(tlc_run_dir: Path, spec_stem: str, current_hash: str) -> None:
|
|
30
|
+
"""Remove older cached run directories for the spec stem that do not match the current hash."""
|
|
31
|
+
if not tlc_run_dir.is_dir():
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
prefix = f"{spec_stem}_"
|
|
35
|
+
current_dir_name = f"{spec_stem}_{current_hash}"
|
|
36
|
+
|
|
37
|
+
for item in tlc_run_dir.iterdir():
|
|
38
|
+
if item.is_dir() and item.name.startswith(prefix) and item.name != current_dir_name:
|
|
39
|
+
with contextlib.suppress(OSError):
|
|
40
|
+
shutil.rmtree(item)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_spec_run_dir(spec_file: Path) -> Path:
|
|
44
|
+
"""Get or create the isolated run directory for a spec and purge stale runs for the same spec."""
|
|
45
|
+
spec_file = spec_file.absolute()
|
|
46
|
+
short_hash = compute_spec_hash(spec_file)
|
|
47
|
+
tlc_run_dir = get_tlc_run_dir()
|
|
48
|
+
|
|
49
|
+
_cleanup_stale_spec_dirs(tlc_run_dir, spec_file.stem, short_hash)
|
|
50
|
+
|
|
51
|
+
target_dir = tlc_run_dir / f"{spec_file.stem}_{short_hash}"
|
|
52
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
return target_dir
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def clear_tlc_run_cache() -> None:
|
|
57
|
+
"""Purge all cached TLC run state directories."""
|
|
58
|
+
tlc_run_dir = get_tlc_run_dir()
|
|
59
|
+
if tlc_run_dir.exists():
|
|
60
|
+
with contextlib.suppress(OSError):
|
|
61
|
+
shutil.rmtree(tlc_run_dir)
|
|
@@ -12,6 +12,7 @@ from tlaplus_cli.project import find_project_root
|
|
|
12
12
|
from tlaplus_cli.tlc.compiler import get_tlc_jar_path
|
|
13
13
|
from tlaplus_cli.tlc.models import CheckState, ModelCheckResult
|
|
14
14
|
from tlaplus_cli.tlc.parser import TlcParser
|
|
15
|
+
from tlaplus_cli.tlc.run_cache import get_spec_run_dir
|
|
15
16
|
from tlaplus_cli.tlc.sany import SanyParser
|
|
16
17
|
|
|
17
18
|
|
|
@@ -36,6 +37,51 @@ def resolve_spec_file(spec: str) -> tuple[Path, str]:
|
|
|
36
37
|
return spec_file.absolute(), spec_file.name
|
|
37
38
|
|
|
38
39
|
|
|
40
|
+
def resolve_cfg_file(cfg: str, spec_file: Path, project_root: Path | None = None) -> tuple[Path, str]:
|
|
41
|
+
"""Resolve a .cfg configuration file from a string name or path.
|
|
42
|
+
|
|
43
|
+
Returns (absolute_path, cfg_arg_for_tlc).
|
|
44
|
+
Raises FileNotFoundError if not found.
|
|
45
|
+
"""
|
|
46
|
+
cfg_path = Path(cfg)
|
|
47
|
+
cfg_names: list[str] = [cfg_path.name]
|
|
48
|
+
if cfg_path.suffix != ".cfg":
|
|
49
|
+
cfg_names.append(cfg_path.name + ".cfg")
|
|
50
|
+
|
|
51
|
+
candidates: list[Path] = []
|
|
52
|
+
if cfg_path.is_absolute():
|
|
53
|
+
candidates.append(cfg_path)
|
|
54
|
+
if cfg_path.suffix != ".cfg":
|
|
55
|
+
candidates.append(cfg_path.with_suffix(".cfg"))
|
|
56
|
+
else:
|
|
57
|
+
search_dirs: list[Path] = []
|
|
58
|
+
base_parent = cfg_path.parent
|
|
59
|
+
search_dirs.append(spec_file.parent / base_parent)
|
|
60
|
+
search_dirs.append(Path.cwd() / base_parent)
|
|
61
|
+
if project_root:
|
|
62
|
+
search_dirs.append(project_root / base_parent)
|
|
63
|
+
|
|
64
|
+
seen_dirs: set[Path] = set()
|
|
65
|
+
for sdir in search_dirs:
|
|
66
|
+
resolved_dir = sdir.resolve()
|
|
67
|
+
if resolved_dir not in seen_dirs:
|
|
68
|
+
seen_dirs.add(resolved_dir)
|
|
69
|
+
for cname in cfg_names:
|
|
70
|
+
candidates.append(sdir / cname)
|
|
71
|
+
|
|
72
|
+
cfg_file = next((c for c in candidates if c.is_file()), None)
|
|
73
|
+
if not cfg_file:
|
|
74
|
+
msg = f"Could not find TLC configuration file '{cfg}'"
|
|
75
|
+
raise FileNotFoundError(msg)
|
|
76
|
+
|
|
77
|
+
cfg_arg = cfg_file.name
|
|
78
|
+
if project_root:
|
|
79
|
+
with contextlib.suppress(ValueError):
|
|
80
|
+
cfg_arg = str(cfg_file.relative_to(project_root))
|
|
81
|
+
|
|
82
|
+
return cfg_file.absolute(), cfg_arg
|
|
83
|
+
|
|
84
|
+
|
|
39
85
|
def get_override_classes(classpath: list[str]) -> list[str]:
|
|
40
86
|
"""Inspect all classpath directories and locate override classes."""
|
|
41
87
|
classes = []
|
|
@@ -63,7 +109,7 @@ def get_override_classes(classpath: list[str]) -> list[str]:
|
|
|
63
109
|
return deduped
|
|
64
110
|
|
|
65
111
|
|
|
66
|
-
def build_tlc_command(spec: str) -> list[str]:
|
|
112
|
+
def build_tlc_command(spec: str, cfg: str | None = None) -> list[str]:
|
|
67
113
|
"""Build the java command for running TLC."""
|
|
68
114
|
config = load_config()
|
|
69
115
|
|
|
@@ -95,29 +141,41 @@ def build_tlc_command(spec: str) -> list[str]:
|
|
|
95
141
|
with contextlib.suppress(ValueError):
|
|
96
142
|
spec_arg = str(spec_file.relative_to(project_root))
|
|
97
143
|
|
|
98
|
-
|
|
144
|
+
run_dir = get_spec_run_dir(spec_file)
|
|
145
|
+
|
|
146
|
+
cmd = [
|
|
99
147
|
"java",
|
|
100
148
|
*config.java.opts,
|
|
101
149
|
*extra_jvm_opts,
|
|
102
150
|
"-cp",
|
|
103
151
|
classpath,
|
|
104
152
|
config.tlc.java_class,
|
|
105
|
-
|
|
153
|
+
"-metadir",
|
|
154
|
+
str(run_dir),
|
|
106
155
|
]
|
|
107
156
|
|
|
157
|
+
if cfg:
|
|
158
|
+
_, cfg_arg = resolve_cfg_file(cfg, spec_file, project_root=project_root)
|
|
159
|
+
cmd.extend(["-config", cfg_arg])
|
|
160
|
+
|
|
161
|
+
cmd.append(spec_arg)
|
|
162
|
+
return cmd
|
|
163
|
+
|
|
108
164
|
|
|
109
165
|
def run_tlc( # noqa: PLR0912, PLR0915
|
|
110
166
|
spec: str,
|
|
111
167
|
callback: Callable[[ModelCheckResult], None] | None = None,
|
|
112
168
|
coverage: bool = False,
|
|
113
169
|
raw: bool = False,
|
|
170
|
+
cfg: str | None = None,
|
|
114
171
|
) -> int:
|
|
115
172
|
"""Run TLC model checker on a TLA+ specification. Returns exit code."""
|
|
116
173
|
config = load_config()
|
|
117
174
|
validate_java_version(config.java.min_version)
|
|
118
175
|
|
|
119
176
|
spec_file, _ = resolve_spec_file(spec)
|
|
120
|
-
cmd = build_tlc_command(spec)
|
|
177
|
+
cmd = build_tlc_command(spec, cfg=cfg)
|
|
178
|
+
|
|
121
179
|
|
|
122
180
|
# Ensure tool mode is enabled for structured output (unless in raw mode)
|
|
123
181
|
if not raw and "-tool" not in cmd:
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
from tlaplus_cli.versioning.api import fetch_remote_versions
|
|
2
|
+
from tlaplus_cli.versioning.app_version import (
|
|
3
|
+
get_app_version,
|
|
4
|
+
get_git_commit_hash,
|
|
5
|
+
is_editable_install,
|
|
6
|
+
)
|
|
2
7
|
from tlaplus_cli.versioning.downloader import (
|
|
3
8
|
download_version,
|
|
4
9
|
download_version_from_url,
|
|
@@ -39,10 +44,13 @@ __all__ = [
|
|
|
39
44
|
"download_version_from_url",
|
|
40
45
|
"extract_version_from_url",
|
|
41
46
|
"fetch_remote_versions",
|
|
47
|
+
"get_app_version",
|
|
48
|
+
"get_git_commit_hash",
|
|
42
49
|
"get_github_cache_file",
|
|
43
50
|
"get_pinned_path",
|
|
44
51
|
"get_pinned_version_dir",
|
|
45
52
|
"get_tools_dir",
|
|
53
|
+
"is_editable_install",
|
|
46
54
|
"is_url",
|
|
47
55
|
"list_local_versions",
|
|
48
56
|
"read_version_metadata",
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""App version discovery and git commit resolution for development installations."""
|
|
2
|
+
|
|
3
|
+
import importlib.metadata
|
|
4
|
+
import json
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def is_editable_install() -> bool:
|
|
10
|
+
"""Check if the tlaplus-cli package is installed in editable / development mode."""
|
|
11
|
+
try:
|
|
12
|
+
dist = importlib.metadata.distribution("tlaplus-cli")
|
|
13
|
+
raw = dist.read_text("direct_url.json")
|
|
14
|
+
if raw:
|
|
15
|
+
data = json.loads(raw)
|
|
16
|
+
if isinstance(data, dict) and data.get("dir_info", {}).get("editable") is True:
|
|
17
|
+
return True
|
|
18
|
+
|
|
19
|
+
origin = getattr(dist, "origin", None)
|
|
20
|
+
if origin is not None:
|
|
21
|
+
dir_info = getattr(origin, "dir_info", None)
|
|
22
|
+
if dir_info is not None and getattr(dir_info, "editable", None) is True:
|
|
23
|
+
return True
|
|
24
|
+
except (importlib.metadata.PackageNotFoundError, json.JSONDecodeError, TypeError, KeyError, AttributeError):
|
|
25
|
+
pass
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_git_commit_hash(base_dir: Path | None = None) -> str | None:
|
|
30
|
+
"""Attempt to retrieve short git commit hash using git rev-parse --short HEAD."""
|
|
31
|
+
cmd = ["git", "rev-parse", "--short", "HEAD"]
|
|
32
|
+
cwd = base_dir if (base_dir and base_dir.is_dir()) else Path(__file__).parent
|
|
33
|
+
try:
|
|
34
|
+
res = subprocess.run(cmd, capture_output=True, text=True, check=False, cwd=cwd)
|
|
35
|
+
if res.returncode == 0:
|
|
36
|
+
commit = res.stdout.strip()
|
|
37
|
+
if commit and len(commit) >= 7:
|
|
38
|
+
return commit
|
|
39
|
+
except (subprocess.SubprocessError, OSError):
|
|
40
|
+
pass
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_app_version(base_version: str | None = None, base_dir: Path | None = None) -> str:
|
|
45
|
+
"""Return the application version string.
|
|
46
|
+
|
|
47
|
+
Appends short git commit hash if running in an editable/development installation.
|
|
48
|
+
"""
|
|
49
|
+
if not base_version:
|
|
50
|
+
try:
|
|
51
|
+
meta = importlib.metadata.metadata("tlaplus-cli")
|
|
52
|
+
base_version = meta["Version"]
|
|
53
|
+
except (importlib.metadata.PackageNotFoundError, KeyError):
|
|
54
|
+
base_version = "0.0.0"
|
|
55
|
+
|
|
56
|
+
git_hash = get_git_commit_hash(base_dir)
|
|
57
|
+
repo_git = (Path(__file__).parent / "../../..").resolve() / ".git"
|
|
58
|
+
is_dev = is_editable_install() or base_dir is not None or repo_git.exists()
|
|
59
|
+
if git_hash and is_dev:
|
|
60
|
+
return f"{base_version}+{git_hash}"
|
|
61
|
+
|
|
62
|
+
return base_version
|
|
@@ -17,6 +17,14 @@ def get_modules_dir() -> Path:
|
|
|
17
17
|
return cache_dir() / "modules"
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def get_run_dir() -> Path:
|
|
21
|
+
return cache_dir() / "run"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_tlc_run_dir() -> Path:
|
|
25
|
+
return get_run_dir() / "tlc"
|
|
26
|
+
|
|
27
|
+
|
|
20
28
|
def get_pinned_path() -> Path:
|
|
21
29
|
"""Returns path to the pin marker file."""
|
|
22
30
|
return get_tools_dir() / "tools-pinned-version.txt"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tlaplus-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: TLA+ tools: download TLC, compile custom modules, run model checker
|
|
5
5
|
Author-email: Denis Nikolskiy <codeomatics@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -32,20 +32,39 @@ Dynamic: license-file
|
|
|
32
32
|
|
|
33
33
|
Command-line tool for working with TLA+ specifications and the TLC model checker.
|
|
34
34
|
|
|
35
|
-
## Project
|
|
35
|
+
## Project Goals
|
|
36
36
|
|
|
37
37
|
1. Simplify managing TLA+ tools such as TLC, community modules, custom modules, etc.
|
|
38
38
|
2. Run TLA tools in a single CLI interface.
|
|
39
|
-
3. Manage and display the
|
|
39
|
+
3. Manage and display the tool outputs.
|
|
40
40
|
|
|
41
|
-
##
|
|
41
|
+
## Quick Start & Usage
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
*
|
|
45
|
-
*
|
|
43
|
+
### 1. Toolset Management (`tla tools`)
|
|
44
|
+
* `tla check-java`: Verify Java installation and compatibility.
|
|
45
|
+
* `tla tools list`: List available and installed toolset versions (`tla2tools.jar`).
|
|
46
|
+
* `tla tools install [vX.Y.Z]`: Download and install a toolset version.
|
|
47
|
+
* `tla tools pin <version>`: Set default toolset version for TLC runs.
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
### 2. Model Checking (`tla tlc`)
|
|
50
|
+
* `tla tlc <spec>`: Execute TLC model checker on a specification.
|
|
51
|
+
* `tla tlc <spec> --show-command`: Inspect exact JVM command before execution.
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
*
|
|
51
|
-
*
|
|
53
|
+
### 3. Custom Java Modules (`tla modules`)
|
|
54
|
+
* `tla modules add <path>`: Compile Java operator overrides and register module in CLI cache.
|
|
55
|
+
* `tla modules list`: Display cached custom modules.
|
|
56
|
+
* `tla modules remove <name>`: Remove custom module from cache.
|
|
57
|
+
|
|
58
|
+
For complete, runnable specifications and Java module examples, see [docs/examples](docs/examples/).
|
|
59
|
+
|
|
60
|
+
## Quick Installation
|
|
61
|
+
|
|
62
|
+
### Via `uv tool`
|
|
63
|
+
```bash
|
|
64
|
+
uv tool install tlaplus-cli
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Via Nix Flakes
|
|
68
|
+
```bash
|
|
69
|
+
nix profile install github:nikolskiy/tlaplus-cli
|
|
70
|
+
```
|
|
@@ -54,11 +54,13 @@ src/tlaplus_cli/tlc/components.py
|
|
|
54
54
|
src/tlaplus_cli/tlc/formatter.py
|
|
55
55
|
src/tlaplus_cli/tlc/models.py
|
|
56
56
|
src/tlaplus_cli/tlc/parser.py
|
|
57
|
+
src/tlaplus_cli/tlc/run_cache.py
|
|
57
58
|
src/tlaplus_cli/tlc/runner.py
|
|
58
59
|
src/tlaplus_cli/tlc/sany.py
|
|
59
60
|
src/tlaplus_cli/tlc/values.py
|
|
60
61
|
src/tlaplus_cli/versioning/__init__.py
|
|
61
62
|
src/tlaplus_cli/versioning/api.py
|
|
63
|
+
src/tlaplus_cli/versioning/app_version.py
|
|
62
64
|
src/tlaplus_cli/versioning/downloader.py
|
|
63
65
|
src/tlaplus_cli/versioning/metadata.py
|
|
64
66
|
src/tlaplus_cli/versioning/paths.py
|
tlaplus_cli-0.7.0/README.md
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# TLA+ CLI
|
|
2
|
-
|
|
3
|
-
Command-line tool for working with TLA+ specifications and the TLC model checker.
|
|
4
|
-
|
|
5
|
-
## Project goals
|
|
6
|
-
|
|
7
|
-
1. Simplify managing TLA+ tools such as TLC, community modules, custom modules, etc.
|
|
8
|
-
2. Run TLA tools in a single CLI interface.
|
|
9
|
-
3. Manage and display the tools outputs.
|
|
10
|
-
|
|
11
|
-
## Features
|
|
12
|
-
|
|
13
|
-
* [Managing TLA+ Tools](docs/managing-tlaplus-tools/managing-tla2tools.md): Verify Java requirements, manage/pin `tla2tools.jar` versions, and handle cache settings.
|
|
14
|
-
* [Running TLC](docs/tlc/tlc.md): Run the TLC model checker, inspect commands, and execute model checking with custom modules.
|
|
15
|
-
* [Custom Java Modules](docs/java-modules-for-tlc/README.md): Compile, register, and manage custom Java operator overrides for TLC using automated CLI commands.
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
For detailed installation instructions, please refer to:
|
|
20
|
-
* [Installation via uv tool](docs/installation/uv-tool.md)
|
|
21
|
-
* [Nix Flakes Installation & Development](docs/installation/nix.md)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|