tlaup 0.8.2__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.
- tlaup-0.8.2/LICENSE +21 -0
- tlaup-0.8.2/MANIFEST.in +4 -0
- tlaup-0.8.2/PKG-INFO +73 -0
- tlaup-0.8.2/README.md +43 -0
- tlaup-0.8.2/pyproject.toml +148 -0
- tlaup-0.8.2/setup.cfg +4 -0
- tlaup-0.8.2/src/tlaup/__init__.py +6 -0
- tlaup-0.8.2/src/tlaup/cache/__init__.py +0 -0
- tlaup-0.8.2/src/tlaup/cache/github.py +28 -0
- tlaup-0.8.2/src/tlaup/cli.py +62 -0
- tlaup-0.8.2/src/tlaup/cmd/__init__.py +0 -0
- tlaup-0.8.2/src/tlaup/cmd/check_java.py +19 -0
- tlaup-0.8.2/src/tlaup/cmd/config/__init__.py +6 -0
- tlaup-0.8.2/src/tlaup/cmd/config/edit.py +34 -0
- tlaup-0.8.2/src/tlaup/cmd/config/list.py +16 -0
- tlaup-0.8.2/src/tlaup/cmd/fetch_cache/__init__.py +5 -0
- tlaup-0.8.2/src/tlaup/cmd/fetch_cache/clear.py +11 -0
- tlaup-0.8.2/src/tlaup/cmd/modules/__init__.py +16 -0
- tlaup-0.8.2/src/tlaup/cmd/modules/add.py +140 -0
- tlaup-0.8.2/src/tlaup/cmd/modules/list.py +45 -0
- tlaup-0.8.2/src/tlaup/cmd/modules/path.py +68 -0
- tlaup-0.8.2/src/tlaup/cmd/modules/remove.py +22 -0
- tlaup-0.8.2/src/tlaup/cmd/tlc.py +291 -0
- tlaup-0.8.2/src/tlaup/cmd/tools/__init__.py +8 -0
- tlaup-0.8.2/src/tlaup/cmd/tools/dir.py +16 -0
- tlaup-0.8.2/src/tlaup/cmd/tools/install.py +79 -0
- tlaup-0.8.2/src/tlaup/cmd/tools/list.py +81 -0
- tlaup-0.8.2/src/tlaup/cmd/tools/meta/__init__.py +5 -0
- tlaup-0.8.2/src/tlaup/cmd/tools/meta/sync.py +34 -0
- tlaup-0.8.2/src/tlaup/cmd/tools/path.py +33 -0
- tlaup-0.8.2/src/tlaup/cmd/tools/pin.py +31 -0
- tlaup-0.8.2/src/tlaup/cmd/tools/uninstall.py +104 -0
- tlaup-0.8.2/src/tlaup/cmd/tools/upgrade.py +76 -0
- tlaup-0.8.2/src/tlaup/config/__init__.py +0 -0
- tlaup-0.8.2/src/tlaup/config/loader.py +87 -0
- tlaup-0.8.2/src/tlaup/config/schema.py +52 -0
- tlaup-0.8.2/src/tlaup/java/__init__.py +7 -0
- tlaup-0.8.2/src/tlaup/java/classpath.py +197 -0
- tlaup-0.8.2/src/tlaup/java/inspector.py +86 -0
- tlaup-0.8.2/src/tlaup/project/__init__.py +3 -0
- tlaup-0.8.2/src/tlaup/project/core.py +31 -0
- tlaup-0.8.2/src/tlaup/py.typed +0 -0
- tlaup-0.8.2/src/tlaup/resources/__init__.py +1 -0
- tlaup-0.8.2/src/tlaup/resources/default_config.yaml +28 -0
- tlaup-0.8.2/src/tlaup/tlc/__init__.py +0 -0
- tlaup-0.8.2/src/tlaup/tlc/codes.py +314 -0
- tlaup-0.8.2/src/tlaup/tlc/compiler.py +12 -0
- tlaup-0.8.2/src/tlaup/tlc/components.py +287 -0
- tlaup-0.8.2/src/tlaup/tlc/formatter.py +348 -0
- tlaup-0.8.2/src/tlaup/tlc/models.py +132 -0
- tlaup-0.8.2/src/tlaup/tlc/parser.py +258 -0
- tlaup-0.8.2/src/tlaup/tlc/run_cache.py +61 -0
- tlaup-0.8.2/src/tlaup/tlc/runner.py +285 -0
- tlaup-0.8.2/src/tlaup/tlc/sany.py +94 -0
- tlaup-0.8.2/src/tlaup/tlc/values.py +186 -0
- tlaup-0.8.2/src/tlaup/ui.py +21 -0
- tlaup-0.8.2/src/tlaup/versioning/__init__.py +61 -0
- tlaup-0.8.2/src/tlaup/versioning/api.py +113 -0
- tlaup-0.8.2/src/tlaup/versioning/app_version.py +62 -0
- tlaup-0.8.2/src/tlaup/versioning/downloader.py +90 -0
- tlaup-0.8.2/src/tlaup/versioning/metadata.py +92 -0
- tlaup-0.8.2/src/tlaup/versioning/paths.py +97 -0
- tlaup-0.8.2/src/tlaup/versioning/resolver.py +92 -0
- tlaup-0.8.2/src/tlaup/versioning/schema.py +27 -0
- tlaup-0.8.2/src/tlaup.egg-info/PKG-INFO +73 -0
- tlaup-0.8.2/src/tlaup.egg-info/SOURCES.txt +68 -0
- tlaup-0.8.2/src/tlaup.egg-info/dependency_links.txt +1 -0
- tlaup-0.8.2/src/tlaup.egg-info/entry_points.txt +2 -0
- tlaup-0.8.2/src/tlaup.egg-info/requires.txt +7 -0
- tlaup-0.8.2/src/tlaup.egg-info/top_level.txt +1 -0
tlaup-0.8.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Denis Nikolskiy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
tlaup-0.8.2/MANIFEST.in
ADDED
tlaup-0.8.2/PKG-INFO
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tlaup
|
|
3
|
+
Version: 0.8.2
|
|
4
|
+
Summary: TLA+ tools: download TLC, compile custom modules, run model checker
|
|
5
|
+
Author-email: Denis Nikolskiy <nikolskiy@users.noreply.github.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/nikolskiy/tlaup
|
|
8
|
+
Project-URL: Repository, https://github.com/nikolskiy/tlaup
|
|
9
|
+
Project-URL: Issues, https://github.com/nikolskiy/tlaup/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/nikolskiy/tlaup/blob/main/CHANGELOG.md
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
18
|
+
Classifier: Topic :: Utilities
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: typer<1.0.0,>=0.22.0
|
|
23
|
+
Requires-Dist: requests<3.0.0,>=2.31.0
|
|
24
|
+
Requires-Dist: pyyaml<7.0,>=6.0
|
|
25
|
+
Requires-Dist: platformdirs<5.0,>=4.0
|
|
26
|
+
Requires-Dist: pydantic<3.0.0,>=2.12.5
|
|
27
|
+
Requires-Dist: truststore<1.0.0,>=0.9.0
|
|
28
|
+
Requires-Dist: rich<14.0.0,>=13.7.0
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# tlaup
|
|
32
|
+
|
|
33
|
+
Command-line tool for working with TLA+ specifications and the TLC model checker.
|
|
34
|
+
|
|
35
|
+
> **Formerly `tlaplus-cli`.** The project, the PyPI package and the command were renamed to `tlaup` in 0.8.2.
|
|
36
|
+
> The old `tla` command is gone; use `tlaup` instead. Installed toolsets, modules and config are kept where they were.
|
|
37
|
+
|
|
38
|
+
## Project Goals
|
|
39
|
+
|
|
40
|
+
1. Simplify managing TLA+ tools such as TLC, community modules, custom modules, etc.
|
|
41
|
+
2. Run TLA tools in a single CLI interface.
|
|
42
|
+
3. Manage and display the tool outputs.
|
|
43
|
+
|
|
44
|
+
## Quick Start & Usage
|
|
45
|
+
|
|
46
|
+
### 1. Toolset Management (`tlaup tools`)
|
|
47
|
+
* `tlaup check-java`: Verify Java installation and compatibility.
|
|
48
|
+
* `tlaup tools list`: List available and installed toolset versions (`tla2tools.jar`).
|
|
49
|
+
* `tlaup tools install [vX.Y.Z]`: Download and install a toolset version.
|
|
50
|
+
* `tlaup tools pin <version>`: Set default toolset version for TLC runs.
|
|
51
|
+
|
|
52
|
+
### 2. Model Checking (`tlaup tlc`)
|
|
53
|
+
* `tlaup tlc <spec>`: Execute TLC model checker on a specification.
|
|
54
|
+
* `tlaup tlc <spec> --show-command`: Inspect exact JVM command before execution.
|
|
55
|
+
|
|
56
|
+
### 3. Custom Java Modules (`tlaup modules`)
|
|
57
|
+
* `tlaup modules add <path>`: Compile Java operator overrides and register module in CLI cache.
|
|
58
|
+
* `tlaup modules list`: Display cached custom modules.
|
|
59
|
+
* `tlaup modules remove <name>`: Remove custom module from cache.
|
|
60
|
+
|
|
61
|
+
For complete, runnable specifications and Java module examples, see [docs/examples](docs/examples/).
|
|
62
|
+
|
|
63
|
+
## Quick Installation
|
|
64
|
+
|
|
65
|
+
### Via `uv tool`
|
|
66
|
+
```bash
|
|
67
|
+
uv tool install tlaup
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
If you installed the old package, remove it first:
|
|
71
|
+
```bash
|
|
72
|
+
uv tool uninstall tlaplus-cli
|
|
73
|
+
```
|
tlaup-0.8.2/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# tlaup
|
|
2
|
+
|
|
3
|
+
Command-line tool for working with TLA+ specifications and the TLC model checker.
|
|
4
|
+
|
|
5
|
+
> **Formerly `tlaplus-cli`.** The project, the PyPI package and the command were renamed to `tlaup` in 0.8.2.
|
|
6
|
+
> The old `tla` command is gone; use `tlaup` instead. Installed toolsets, modules and config are kept where they were.
|
|
7
|
+
|
|
8
|
+
## Project Goals
|
|
9
|
+
|
|
10
|
+
1. Simplify managing TLA+ tools such as TLC, community modules, custom modules, etc.
|
|
11
|
+
2. Run TLA tools in a single CLI interface.
|
|
12
|
+
3. Manage and display the tool outputs.
|
|
13
|
+
|
|
14
|
+
## Quick Start & Usage
|
|
15
|
+
|
|
16
|
+
### 1. Toolset Management (`tlaup tools`)
|
|
17
|
+
* `tlaup check-java`: Verify Java installation and compatibility.
|
|
18
|
+
* `tlaup tools list`: List available and installed toolset versions (`tla2tools.jar`).
|
|
19
|
+
* `tlaup tools install [vX.Y.Z]`: Download and install a toolset version.
|
|
20
|
+
* `tlaup tools pin <version>`: Set default toolset version for TLC runs.
|
|
21
|
+
|
|
22
|
+
### 2. Model Checking (`tlaup tlc`)
|
|
23
|
+
* `tlaup tlc <spec>`: Execute TLC model checker on a specification.
|
|
24
|
+
* `tlaup tlc <spec> --show-command`: Inspect exact JVM command before execution.
|
|
25
|
+
|
|
26
|
+
### 3. Custom Java Modules (`tlaup modules`)
|
|
27
|
+
* `tlaup modules add <path>`: Compile Java operator overrides and register module in CLI cache.
|
|
28
|
+
* `tlaup modules list`: Display cached custom modules.
|
|
29
|
+
* `tlaup modules remove <name>`: Remove custom module from cache.
|
|
30
|
+
|
|
31
|
+
For complete, runnable specifications and Java module examples, see [docs/examples](docs/examples/).
|
|
32
|
+
|
|
33
|
+
## Quick Installation
|
|
34
|
+
|
|
35
|
+
### Via `uv tool`
|
|
36
|
+
```bash
|
|
37
|
+
uv tool install tlaup
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
If you installed the old package, remove it first:
|
|
41
|
+
```bash
|
|
42
|
+
uv tool uninstall tlaplus-cli
|
|
43
|
+
```
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tlaup"
|
|
7
|
+
version = "0.8.2"
|
|
8
|
+
description = "TLA+ tools: download TLC, compile custom modules, run model checker"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"typer>=0.22.0,<1.0.0",
|
|
13
|
+
"requests>=2.31.0,<3.0.0",
|
|
14
|
+
"pyyaml>=6.0,<7.0",
|
|
15
|
+
"platformdirs>=4.0,<5.0",
|
|
16
|
+
"pydantic>=2.12.5,<3.0.0",
|
|
17
|
+
"truststore>=0.9.0,<1.0.0",
|
|
18
|
+
"rich>=13.7.0,<14.0.0",
|
|
19
|
+
]
|
|
20
|
+
authors = [
|
|
21
|
+
{name = "Denis Nikolskiy", email = "nikolskiy@users.noreply.github.com"}
|
|
22
|
+
]
|
|
23
|
+
license = "MIT"
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 3 - Alpha",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Programming Language :: Python :: 3.13",
|
|
30
|
+
"Operating System :: OS Independent",
|
|
31
|
+
"Topic :: Software Development :: Build Tools",
|
|
32
|
+
"Topic :: Utilities",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/nikolskiy/tlaup"
|
|
37
|
+
Repository = "https://github.com/nikolskiy/tlaup"
|
|
38
|
+
Issues = "https://github.com/nikolskiy/tlaup/issues"
|
|
39
|
+
Changelog = "https://github.com/nikolskiy/tlaup/blob/main/CHANGELOG.md"
|
|
40
|
+
|
|
41
|
+
[project.scripts]
|
|
42
|
+
tlaup = "tlaup.cli:main"
|
|
43
|
+
|
|
44
|
+
[tool.setuptools.packages.find]
|
|
45
|
+
where = ["src"]
|
|
46
|
+
include = ["tlaup*"]
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.package-data]
|
|
49
|
+
tlaup = ["py.typed"]
|
|
50
|
+
"tlaup.resources" = ["*.yaml"]
|
|
51
|
+
|
|
52
|
+
[dependency-groups]
|
|
53
|
+
dev = [
|
|
54
|
+
"pytest>=9.0.2,<10.0.0",
|
|
55
|
+
"pytest-cov>=7.0.0,<8.0.0",
|
|
56
|
+
"pytest-mock>=3.15.1,<4.0.0",
|
|
57
|
+
"ruff>=0.9.0,<1.0.0",
|
|
58
|
+
"mypy>=1.0.0,<2.0.0",
|
|
59
|
+
"types-PyYAML>=6.0.12.12,<7.0.0.0",
|
|
60
|
+
"types-requests>=2.31.0.2,<3.0.0.0",
|
|
61
|
+
"poethepoet>=0.24.0,<1.0.0",
|
|
62
|
+
"detect-secrets>=1.5.0,<2.0.0",
|
|
63
|
+
"pre-commit>=3.5.0,<4.0.0",
|
|
64
|
+
"pre-commit-hooks>=6.0.0,<7.0.0",
|
|
65
|
+
"twine>=6.0.0,<7.0.0",
|
|
66
|
+
"bandit>=1.9.4",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[tool.pytest.ini_options]
|
|
70
|
+
addopts = "--cov=tlaup --cov-report=term-missing"
|
|
71
|
+
testpaths = ["tests/unit"]
|
|
72
|
+
|
|
73
|
+
[tool.ruff]
|
|
74
|
+
line-length = 120
|
|
75
|
+
target-version = "py311"
|
|
76
|
+
|
|
77
|
+
[tool.ruff.lint]
|
|
78
|
+
select = [
|
|
79
|
+
"F", # Pyflakes: logic errors
|
|
80
|
+
"E", # pycodestyle errors
|
|
81
|
+
"W", # pycodestyle warnings
|
|
82
|
+
"I", # isort: import sorting
|
|
83
|
+
"N", # pep8-naming: naming conventions
|
|
84
|
+
"UP", # pyupgrade: upgrade syntax for newer python versions
|
|
85
|
+
"PL", # Pylint: various checks (convention, refactor, warning, etc.)
|
|
86
|
+
"B", # flake8-bugbear: finds likely bugs and design problems
|
|
87
|
+
"SIM", # flake8-simplify: suggests simpler python constructs
|
|
88
|
+
"C4", # flake8-comprehensions: better list/set/dict comprehensions
|
|
89
|
+
"PIE", # flake8-pie: miscellaneous lints
|
|
90
|
+
"TCH", # flake8-type-checking: move imports to type-checking blocks
|
|
91
|
+
"PGH", # pygrep-hooks: regex based checks
|
|
92
|
+
"G", # flake8-logging-format: validate logging format strings
|
|
93
|
+
"RUF", # Ruff-specific rules
|
|
94
|
+
"PTH", # flake8-use-pathlib: replace os.path with pathlib
|
|
95
|
+
"ARG", # flake8-unused-arguments: find unused function arguments
|
|
96
|
+
"RET", # flake8-return: cleaner return logic
|
|
97
|
+
"TRY", # tryceratops: prevent exception handling anti-patterns
|
|
98
|
+
]
|
|
99
|
+
ignore = [
|
|
100
|
+
"PLR2004", # Magic value used in comparison
|
|
101
|
+
"PLW1510", # subprocess.run without explicit check
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
[tool.ruff.lint.per-file-ignores]
|
|
105
|
+
"tests/*" = [
|
|
106
|
+
"ARG001", # Unused arguments are common in tests (fixtures)
|
|
107
|
+
"PLR0913", # Many arguments are common in tests (fixtures)
|
|
108
|
+
"PLR0917", # Many positional arguments are common in tests (fixtures)
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
[tool.bandit]
|
|
112
|
+
skips = ["B404", "B603", "B607"]
|
|
113
|
+
|
|
114
|
+
[tool.poe.tasks]
|
|
115
|
+
check = "pre-commit run --all-files"
|
|
116
|
+
lint = ["lint-ruff", "lint-bandit", "type-check"]
|
|
117
|
+
lint-ruff = "ruff check ."
|
|
118
|
+
lint-bandit = "bandit -c pyproject.toml -r src/tlaup"
|
|
119
|
+
type-check = "mypy ."
|
|
120
|
+
fix = ["format", "lint-fix"]
|
|
121
|
+
format = "ruff format ."
|
|
122
|
+
lint-fix = "ruff check --fix ."
|
|
123
|
+
test = "pytest"
|
|
124
|
+
install-hooks = "pre-commit install --install-hooks"
|
|
125
|
+
audit = "detect-secrets audit .secrets.baseline"
|
|
126
|
+
build = "uv build"
|
|
127
|
+
clean = "rm -rf dist"
|
|
128
|
+
|
|
129
|
+
twine-check = "twine check dist/*.whl dist/*.tar.gz"
|
|
130
|
+
|
|
131
|
+
[tool.poe.tasks.check-release]
|
|
132
|
+
sequence = [
|
|
133
|
+
"check",
|
|
134
|
+
"clean",
|
|
135
|
+
"build",
|
|
136
|
+
"twine-check",
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
[tool.mypy]
|
|
140
|
+
python_version = "3.11"
|
|
141
|
+
strict = true
|
|
142
|
+
ignore_missing_imports = false
|
|
143
|
+
exclude = ["build", "tests/"]
|
|
144
|
+
|
|
145
|
+
# Per-module overrides for libraries without strict types
|
|
146
|
+
[[tool.mypy.overrides]]
|
|
147
|
+
module = "platformdirs.*"
|
|
148
|
+
ignore_missing_imports = true
|
tlaup-0.8.2/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from dataclasses import asdict
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from tlaup.ui import warn
|
|
6
|
+
from tlaup.versioning.schema import RemoteVersion
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def load_github_cache(cache_file: Path) -> list[RemoteVersion] | None:
|
|
10
|
+
"""Load the remote versions cache from disk."""
|
|
11
|
+
if cache_file.exists():
|
|
12
|
+
try:
|
|
13
|
+
with cache_file.open("r", encoding="utf-8") as f:
|
|
14
|
+
data = json.load(f)
|
|
15
|
+
return [RemoteVersion(**item) for item in data]
|
|
16
|
+
except (json.JSONDecodeError, OSError, KeyError, TypeError) as e:
|
|
17
|
+
warn(f"Failed to read cache: {e}")
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def save_github_cache(cache_file: Path, versions: list[RemoteVersion]) -> None:
|
|
22
|
+
"""Save the remote versions cache to disk."""
|
|
23
|
+
try:
|
|
24
|
+
cache_file.parent.mkdir(parents=True, exist_ok=True)
|
|
25
|
+
with cache_file.open("w", encoding="utf-8") as f:
|
|
26
|
+
json.dump([asdict(v) for v in versions], f)
|
|
27
|
+
except OSError as e:
|
|
28
|
+
warn(f"Failed to save cache: {e}")
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""TLA+ CLI tool - entry point."""
|
|
2
|
+
|
|
3
|
+
import importlib.metadata
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from tlaup.cmd.check_java import check_java
|
|
8
|
+
from tlaup.cmd.config import app as config_app
|
|
9
|
+
from tlaup.cmd.fetch_cache import app as fetch_cache_app
|
|
10
|
+
from tlaup.cmd.modules import app as modules_app
|
|
11
|
+
from tlaup.cmd.tlc import tlc as run_tlc_cmd
|
|
12
|
+
from tlaup.cmd.tools import app as tools_app
|
|
13
|
+
from tlaup.config.loader import load_config
|
|
14
|
+
from tlaup.versioning import get_app_version
|
|
15
|
+
|
|
16
|
+
app = typer.Typer(
|
|
17
|
+
name="tlaup",
|
|
18
|
+
help="TLA+ tools: download TLA+ toolset distribution, compile custom modules, run model checker.",
|
|
19
|
+
no_args_is_help=True,
|
|
20
|
+
add_completion=False,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def version_callback(value: bool) -> None:
|
|
25
|
+
if value:
|
|
26
|
+
meta = importlib.metadata.metadata("tlaup")
|
|
27
|
+
app_ver = get_app_version(base_version=meta["Version"])
|
|
28
|
+
typer.echo(f"{meta['Name']} v{app_ver}")
|
|
29
|
+
typer.echo(meta["Summary"])
|
|
30
|
+
raise typer.Exit()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@app.callback()
|
|
34
|
+
def root(
|
|
35
|
+
_: bool = typer.Option(
|
|
36
|
+
None,
|
|
37
|
+
"--version",
|
|
38
|
+
"-v",
|
|
39
|
+
help="Show the application's version and exit.",
|
|
40
|
+
callback=version_callback,
|
|
41
|
+
is_eager=True,
|
|
42
|
+
),
|
|
43
|
+
) -> None:
|
|
44
|
+
"""TLA+ CLI tool."""
|
|
45
|
+
# Load config early to trigger first-run copy
|
|
46
|
+
load_config()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# --- Subcommands ---
|
|
50
|
+
|
|
51
|
+
app.add_typer(modules_app, name="modules")
|
|
52
|
+
app.add_typer(tools_app, name="tools")
|
|
53
|
+
app.add_typer(fetch_cache_app, name="fetch-cache")
|
|
54
|
+
app.add_typer(config_app, name="config")
|
|
55
|
+
|
|
56
|
+
app.command(name="tlc")(run_tlc_cmd)
|
|
57
|
+
app.command(name="check-java")(check_java)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def main() -> None:
|
|
61
|
+
"""Entry point for [project.scripts]."""
|
|
62
|
+
app()
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
|
|
3
|
+
from tlaup.config.loader import load_config
|
|
4
|
+
from tlaup.java import get_java_version, validate_java_version
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def check_java() -> None:
|
|
8
|
+
"""Check if Java is installed and meets the minimum version requirement."""
|
|
9
|
+
config = load_config()
|
|
10
|
+
version = get_java_version()
|
|
11
|
+
if version:
|
|
12
|
+
typer.echo(f"Detected Java version: {version}")
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
validate_java_version(config.java.min_version)
|
|
16
|
+
typer.echo(f"Java version is compatible (>= {config.java.min_version}).")
|
|
17
|
+
except RuntimeError as e:
|
|
18
|
+
typer.echo(f"Error: {e}", err=True)
|
|
19
|
+
raise typer.Exit(1) from None
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from tlaup.cmd.config import app
|
|
8
|
+
from tlaup.config.loader import config_path, load_config
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.command(name="edit")
|
|
12
|
+
def edit_config(
|
|
13
|
+
editor: str | None = typer.Argument(None, help="The editor to use (defaults to $EDITOR or 'vim')."),
|
|
14
|
+
) -> None:
|
|
15
|
+
"""Open the configuration file in an editor."""
|
|
16
|
+
cp = config_path()
|
|
17
|
+
|
|
18
|
+
# Ensure config exists by loading it
|
|
19
|
+
load_config()
|
|
20
|
+
|
|
21
|
+
if not editor:
|
|
22
|
+
editor = os.environ.get("EDITOR", "vim")
|
|
23
|
+
|
|
24
|
+
editor_path = shutil.which(editor)
|
|
25
|
+
if not editor_path:
|
|
26
|
+
typer.echo(f"Error: Editor '{editor}' not found in PATH.", err=True)
|
|
27
|
+
raise typer.Exit(1)
|
|
28
|
+
|
|
29
|
+
typer.echo(f"Opening {cp} with {editor} ...")
|
|
30
|
+
try:
|
|
31
|
+
subprocess.run([editor_path, str(cp)], check=True)
|
|
32
|
+
except subprocess.CalledProcessError as e:
|
|
33
|
+
typer.echo(f"Editor exited with error: {e}", err=True)
|
|
34
|
+
raise typer.Exit(1) from None
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
|
|
3
|
+
from tlaup.cmd.config import app
|
|
4
|
+
from tlaup.config.loader import config_path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@app.command(name="list")
|
|
8
|
+
def list_config() -> None:
|
|
9
|
+
"""Display the current configuration file content."""
|
|
10
|
+
cp = config_path()
|
|
11
|
+
if not cp.exists():
|
|
12
|
+
typer.echo("Configuration file not found.", err=True)
|
|
13
|
+
raise typer.Exit(1)
|
|
14
|
+
|
|
15
|
+
with cp.open(encoding="utf-8") as f:
|
|
16
|
+
typer.echo(f.read())
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
|
|
3
|
+
from tlaup.cmd.fetch_cache import app
|
|
4
|
+
from tlaup.versioning import clear_cache
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@app.command(name="clear")
|
|
8
|
+
def cmd_clear_cache() -> None:
|
|
9
|
+
"""Clear the local cache of remote GitHub versions."""
|
|
10
|
+
clear_cache()
|
|
11
|
+
typer.echo("GitHub versions cache cleared.")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
|
|
3
|
+
app = typer.Typer(name="modules", help="Manage TLA+ Java modules.", no_args_is_help=True)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Import functions for new commands
|
|
7
|
+
from .add import add_module # noqa: E402
|
|
8
|
+
from .list import list_modules # noqa: E402
|
|
9
|
+
from .path import show_modules_path # noqa: E402
|
|
10
|
+
from .remove import remove_module # noqa: E402
|
|
11
|
+
|
|
12
|
+
# Register new commands with the typer app
|
|
13
|
+
app.command(name="add")(add_module)
|
|
14
|
+
app.command(name="list")(list_modules)
|
|
15
|
+
app.command(name="remove")(remove_module)
|
|
16
|
+
app.command(name="path")(show_modules_path)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
from tlaup.config.loader import load_config
|
|
12
|
+
from tlaup.java.classpath import ClasspathResolver, ResolveMode
|
|
13
|
+
from tlaup.tlc.compiler import get_tlc_jar_path
|
|
14
|
+
from tlaup.tlc.run_cache import clear_tlc_run_cache
|
|
15
|
+
from tlaup.versioning.paths import get_modules_dir
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def find_override_classes(java_files: list[Path]) -> list[str]:
|
|
19
|
+
"""Find all class names implementing ITLCOverrides in Java files."""
|
|
20
|
+
override_classes = []
|
|
21
|
+
package_pattern = re.compile(r"package\s+([a-zA-Z0-9_.]+)\s*;")
|
|
22
|
+
class_pattern = re.compile(
|
|
23
|
+
r"\bclass\s+([a-zA-Z0-9_]+)\b[^{]*\bimplements\b[^{]*\b(tlc2\.overrides\.)?ITLCOverrides\b"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
for jf in java_files:
|
|
27
|
+
try:
|
|
28
|
+
content = jf.read_text(encoding="utf-8", errors="ignore")
|
|
29
|
+
except OSError:
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
# Strip comments to prevent false matches
|
|
33
|
+
content_no_comments = re.sub(r"//.*", "", content)
|
|
34
|
+
content_no_comments = re.sub(r"/\*.*?\*/", "", content_no_comments, flags=re.DOTALL)
|
|
35
|
+
|
|
36
|
+
package_match = package_pattern.search(content_no_comments)
|
|
37
|
+
package_name = package_match.group(1) if package_match else ""
|
|
38
|
+
|
|
39
|
+
for match in class_pattern.finditer(content_no_comments):
|
|
40
|
+
class_name = match.group(1)
|
|
41
|
+
full_class_name = f"{package_name}.{class_name}" if package_name else class_name
|
|
42
|
+
override_classes.append(full_class_name)
|
|
43
|
+
|
|
44
|
+
return override_classes
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def add_module( # noqa: PLR0912, PLR0915
|
|
48
|
+
path: str = typer.Argument(..., help="Path to the module directory to add."),
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Compile custom Java overrides and add/update the module in the cache."""
|
|
51
|
+
source_dir = Path(path).resolve()
|
|
52
|
+
if not source_dir.is_dir():
|
|
53
|
+
typer.echo(f"Error: Path does not exist or is not a directory: {source_dir}", err=True)
|
|
54
|
+
raise typer.Exit(1)
|
|
55
|
+
|
|
56
|
+
config = load_config()
|
|
57
|
+
module_name = source_dir.name
|
|
58
|
+
target_dir = get_modules_dir() / module_name
|
|
59
|
+
|
|
60
|
+
# Overwrite if exists
|
|
61
|
+
if target_dir.exists():
|
|
62
|
+
shutil.rmtree(target_dir)
|
|
63
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
|
|
65
|
+
# 1. Copy directories (lib, modules, tlc2, and test/tests)
|
|
66
|
+
def copy_subfolder(subname: str) -> None:
|
|
67
|
+
src = source_dir / subname
|
|
68
|
+
if src.is_dir():
|
|
69
|
+
shutil.copytree(src, target_dir / subname)
|
|
70
|
+
|
|
71
|
+
typer.echo("Copying subfolders (lib, modules, test, tlc2)...")
|
|
72
|
+
copy_subfolder("lib")
|
|
73
|
+
copy_subfolder("modules")
|
|
74
|
+
copy_subfolder("tlc2")
|
|
75
|
+
|
|
76
|
+
if (source_dir / "test").is_dir():
|
|
77
|
+
copy_subfolder("test")
|
|
78
|
+
elif (source_dir / "tests").is_dir():
|
|
79
|
+
copy_subfolder("tests")
|
|
80
|
+
|
|
81
|
+
# 2. Compile Java overrides in cached version if tlc2/overrides exists
|
|
82
|
+
java_files = []
|
|
83
|
+
for candidate in [target_dir / "modules" / "tlc2" / "overrides", target_dir / "tlc2" / "overrides"]:
|
|
84
|
+
if candidate.is_dir():
|
|
85
|
+
java_files.extend(list(candidate.rglob("*.java")))
|
|
86
|
+
|
|
87
|
+
if java_files:
|
|
88
|
+
# Build classpath including tool_jar and local libs from the cached folder
|
|
89
|
+
cached_lib = target_dir / "lib"
|
|
90
|
+
extra_libs = []
|
|
91
|
+
if cached_lib.is_dir():
|
|
92
|
+
extra_libs.extend([str(j.absolute()) for j in cached_lib.glob("*.jar")])
|
|
93
|
+
|
|
94
|
+
tool_jar = get_tlc_jar_path()
|
|
95
|
+
if not tool_jar.exists():
|
|
96
|
+
typer.echo("Error: tla2tools.jar not found. Run 'tlaup tools install' first.", err=True)
|
|
97
|
+
shutil.rmtree(target_dir, ignore_errors=True)
|
|
98
|
+
raise typer.Exit(1)
|
|
99
|
+
|
|
100
|
+
resolver = ClasspathResolver(config, project_root=None, tool_jar=tool_jar, extra_libs=extra_libs)
|
|
101
|
+
classpath = os.pathsep.join(resolver.resolve(ResolveMode.COMPILE))
|
|
102
|
+
|
|
103
|
+
classes_dir = target_dir / "classes"
|
|
104
|
+
classes_dir.mkdir(parents=True, exist_ok=True)
|
|
105
|
+
|
|
106
|
+
cmd = ["javac", "-cp", classpath, "-d", str(classes_dir), *[str(f) for f in java_files]]
|
|
107
|
+
typer.echo("Compiling Java files in cached tlc2/overrides...")
|
|
108
|
+
try:
|
|
109
|
+
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
|
110
|
+
except FileNotFoundError as err:
|
|
111
|
+
typer.echo("Error: 'javac' not found. Ensure JDK is installed.", err=True)
|
|
112
|
+
shutil.rmtree(target_dir, ignore_errors=True)
|
|
113
|
+
raise typer.Exit(1) from err
|
|
114
|
+
except subprocess.CalledProcessError as err:
|
|
115
|
+
typer.echo("Compilation failed!", err=True)
|
|
116
|
+
typer.echo(err.stdout, err=True)
|
|
117
|
+
typer.echo(err.stderr, err=True)
|
|
118
|
+
shutil.rmtree(target_dir, ignore_errors=True)
|
|
119
|
+
raise typer.Exit(1) from err
|
|
120
|
+
|
|
121
|
+
# Write service file
|
|
122
|
+
meta_inf = classes_dir / "META-INF" / "services"
|
|
123
|
+
meta_inf.mkdir(parents=True, exist_ok=True)
|
|
124
|
+
service_file = meta_inf / "tlc2.overrides.ITLCOverrides"
|
|
125
|
+
|
|
126
|
+
override_classes = find_override_classes(java_files)
|
|
127
|
+
if not override_classes:
|
|
128
|
+
override_classes = [config.tlc.overrides_class]
|
|
129
|
+
|
|
130
|
+
with service_file.open("w") as f:
|
|
131
|
+
for cls in override_classes:
|
|
132
|
+
f.write(f"{cls}\n")
|
|
133
|
+
|
|
134
|
+
# 3. Create metadata.json
|
|
135
|
+
metadata = {"name": module_name, "built_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
|
|
136
|
+
with (target_dir / "metadata.json").open("w") as f:
|
|
137
|
+
json.dump(metadata, f, indent=2)
|
|
138
|
+
|
|
139
|
+
clear_tlc_run_cache()
|
|
140
|
+
typer.echo(f"Module '{module_name}' successfully added to cache.")
|