tlaplus-cli 0.6.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.
Files changed (95) hide show
  1. tlaplus_cli-0.8.1/PKG-INFO +70 -0
  2. tlaplus_cli-0.8.1/README.md +40 -0
  3. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/pyproject.toml +2 -2
  4. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cli.py +3 -1
  5. tlaplus_cli-0.8.1/src/tlaplus_cli/cmd/modules/__init__.py +16 -0
  6. tlaplus_cli-0.8.1/src/tlaplus_cli/cmd/modules/add.py +140 -0
  7. tlaplus_cli-0.8.1/src/tlaplus_cli/cmd/modules/list.py +45 -0
  8. tlaplus_cli-0.8.1/src/tlaplus_cli/cmd/modules/path.py +68 -0
  9. tlaplus_cli-0.8.1/src/tlaplus_cli/cmd/modules/remove.py +22 -0
  10. tlaplus_cli-0.8.1/src/tlaplus_cli/cmd/tlc.py +291 -0
  11. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/list.py +1 -0
  12. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/java/classpath.py +27 -4
  13. tlaplus_cli-0.8.1/src/tlaplus_cli/tlc/compiler.py +12 -0
  14. tlaplus_cli-0.8.1/src/tlaplus_cli/tlc/components.py +287 -0
  15. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/formatter.py +19 -80
  16. tlaplus_cli-0.8.1/src/tlaplus_cli/tlc/run_cache.py +61 -0
  17. tlaplus_cli-0.8.1/src/tlaplus_cli/tlc/runner.py +285 -0
  18. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/__init__.py +8 -0
  19. tlaplus_cli-0.8.1/src/tlaplus_cli/versioning/app_version.py +62 -0
  20. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/paths.py +8 -0
  21. tlaplus_cli-0.8.1/src/tlaplus_cli.egg-info/PKG-INFO +70 -0
  22. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli.egg-info/SOURCES.txt +7 -18
  23. tlaplus_cli-0.6.0/PKG-INFO +0 -296
  24. tlaplus_cli-0.6.0/README.md +0 -266
  25. tlaplus_cli-0.6.0/src/tlaplus_cli/cmd/modules/__init__.py +0 -5
  26. tlaplus_cli-0.6.0/src/tlaplus_cli/cmd/modules/build.py +0 -41
  27. tlaplus_cli-0.6.0/src/tlaplus_cli/cmd/modules/install.py +0 -75
  28. tlaplus_cli-0.6.0/src/tlaplus_cli/cmd/modules/lib.py +0 -46
  29. tlaplus_cli-0.6.0/src/tlaplus_cli/cmd/modules/path.py +0 -72
  30. tlaplus_cli-0.6.0/src/tlaplus_cli/cmd/tlc.py +0 -159
  31. tlaplus_cli-0.6.0/src/tlaplus_cli/tlc/compiler.py +0 -81
  32. tlaplus_cli-0.6.0/src/tlaplus_cli/tlc/runner.py +0 -151
  33. tlaplus_cli-0.6.0/src/tlaplus_cli.egg-info/PKG-INFO +0 -296
  34. tlaplus_cli-0.6.0/tests/test_build_both.py +0 -33
  35. tlaplus_cli-0.6.0/tests/test_build_modules_path.py +0 -50
  36. tlaplus_cli-0.6.0/tests/test_cli.py +0 -39
  37. tlaplus_cli-0.6.0/tests/test_config.py +0 -59
  38. tlaplus_cli-0.6.0/tests/test_config_suite.py +0 -74
  39. tlaplus_cli-0.6.0/tests/test_modules_build_lib.py +0 -88
  40. tlaplus_cli-0.6.0/tests/test_modules_build_show_command.py +0 -27
  41. tlaplus_cli-0.6.0/tests/test_modules_install_community.py +0 -86
  42. tlaplus_cli-0.6.0/tests/test_modules_lib.py +0 -106
  43. tlaplus_cli-0.6.0/tests/test_modules_path.py +0 -101
  44. tlaplus_cli-0.6.0/tests/test_modules_path_resolved.py +0 -33
  45. tlaplus_cli-0.6.0/tests/test_settings.py +0 -38
  46. tlaplus_cli-0.6.0/tests/test_tlc_modules_path.py +0 -50
  47. tlaplus_cli-0.6.0/tests/test_tlc_show_command.py +0 -25
  48. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/LICENSE +0 -0
  49. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/MANIFEST.in +0 -0
  50. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/setup.cfg +0 -0
  51. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/__init__.py +0 -0
  52. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cache/__init__.py +0 -0
  53. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cache/github.py +0 -0
  54. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/__init__.py +0 -0
  55. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/check_java.py +0 -0
  56. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/config/__init__.py +0 -0
  57. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/config/edit.py +0 -0
  58. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/config/list.py +0 -0
  59. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/fetch_cache/__init__.py +0 -0
  60. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/fetch_cache/clear.py +0 -0
  61. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/__init__.py +0 -0
  62. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/dir.py +0 -0
  63. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/install.py +0 -0
  64. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/meta/__init__.py +0 -0
  65. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/meta/sync.py +0 -0
  66. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/path.py +0 -0
  67. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/pin.py +0 -0
  68. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/uninstall.py +0 -0
  69. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/cmd/tools/upgrade.py +0 -0
  70. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/config/__init__.py +0 -0
  71. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/config/loader.py +0 -0
  72. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/config/schema.py +0 -0
  73. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/java/__init__.py +0 -0
  74. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/java/inspector.py +0 -0
  75. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/project/__init__.py +0 -0
  76. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/project/core.py +0 -0
  77. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/py.typed +0 -0
  78. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/resources/__init__.py +0 -0
  79. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/resources/default_config.yaml +0 -0
  80. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/__init__.py +0 -0
  81. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/codes.py +0 -0
  82. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/models.py +0 -0
  83. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/parser.py +0 -0
  84. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/sany.py +0 -0
  85. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/tlc/values.py +0 -0
  86. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/ui.py +0 -0
  87. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/api.py +0 -0
  88. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/downloader.py +0 -0
  89. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/metadata.py +0 -0
  90. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/resolver.py +0 -0
  91. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli/versioning/schema.py +0 -0
  92. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli.egg-info/dependency_links.txt +0 -0
  93. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli.egg-info/entry_points.txt +0 -0
  94. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli.egg-info/requires.txt +0 -0
  95. {tlaplus_cli-0.6.0 → tlaplus_cli-0.8.1}/src/tlaplus_cli.egg-info/top_level.txt +0 -0
@@ -0,0 +1,70 @@
1
+ Metadata-Version: 2.4
2
+ Name: tlaplus-cli
3
+ Version: 0.8.1
4
+ Summary: TLA+ tools: download TLC, compile custom modules, run model checker
5
+ Author-email: Denis Nikolskiy <codeomatics@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/nikolskiy/tlaplus-cli
8
+ Project-URL: Repository, https://github.com/nikolskiy/tlaplus-cli
9
+ Project-URL: Issues, https://github.com/nikolskiy/tlaplus-cli/issues
10
+ Project-URL: Changelog, https://github.com/nikolskiy/tlaplus-cli/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
+ # TLA+ CLI
32
+
33
+ Command-line tool for working with TLA+ specifications and the TLC model checker.
34
+
35
+ ## Project Goals
36
+
37
+ 1. Simplify managing TLA+ tools such as TLC, community modules, custom modules, etc.
38
+ 2. Run TLA tools in a single CLI interface.
39
+ 3. Manage and display the tool outputs.
40
+
41
+ ## Quick Start & Usage
42
+
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.
48
+
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.
52
+
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
+ ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tlaplus-cli"
7
- version = "0.6.0"
7
+ version = "0.8.1"
8
8
  description = "TLA+ tools: download TLC, compile custom modules, run model checker"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -68,7 +68,7 @@ dev = [
68
68
 
69
69
  [tool.pytest.ini_options]
70
70
  addopts = "--cov=tlaplus_cli --cov-report=term-missing"
71
- testpaths = ["tests"]
71
+ testpaths = ["tests/unit"]
72
72
 
73
73
  [tool.ruff]
74
74
  line-length = 120
@@ -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
- typer.echo(f"{meta['Name']} v{meta['Version']}")
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
 
@@ -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 tlaplus_cli.config.loader import load_config
12
+ from tlaplus_cli.java.classpath import ClasspathResolver, ResolveMode
13
+ from tlaplus_cli.tlc.compiler import get_tlc_jar_path
14
+ from tlaplus_cli.tlc.run_cache import clear_tlc_run_cache
15
+ from tlaplus_cli.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 'tla 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.")
@@ -0,0 +1,45 @@
1
+ import json
2
+
3
+ import typer
4
+ from rich.console import Console
5
+ from rich.table import Table
6
+
7
+ from tlaplus_cli.versioning.paths import get_modules_dir
8
+
9
+
10
+ def list_modules() -> None:
11
+ """List all custom TLA+ modules added to the cache."""
12
+ modules_dir = get_modules_dir()
13
+
14
+ if not modules_dir.is_dir():
15
+ typer.echo("No modules currently added.")
16
+ return
17
+
18
+ modules_data = []
19
+ for item in sorted(modules_dir.iterdir()):
20
+ if item.is_dir():
21
+ metadata_file = item / "metadata.json"
22
+ if metadata_file.exists():
23
+ try:
24
+ meta = json.loads(metadata_file.read_text())
25
+ name = meta.get("name", item.name)
26
+ built_at = meta.get("built_at", "Unknown")
27
+ modules_data.append((name, built_at))
28
+ except Exception:
29
+ modules_data.append((item.name, "Corrupt Metadata"))
30
+ else:
31
+ modules_data.append((item.name, "Unknown"))
32
+
33
+ if not modules_data:
34
+ typer.echo("No modules currently added.")
35
+ return
36
+
37
+ table = Table()
38
+ table.add_column("Module Name", style="cyan")
39
+ table.add_column("Last Built/Modified", style="magenta")
40
+
41
+ for name, built_at in modules_data:
42
+ table.add_row(name, built_at)
43
+
44
+ console = Console()
45
+ console.print(table)
@@ -0,0 +1,68 @@
1
+ import os
2
+ from pathlib import Path
3
+
4
+ import typer
5
+
6
+ from tlaplus_cli.config.loader import load_config
7
+ from tlaplus_cli.java.classpath import ClasspathResolver, ResolveMode, SubdirType
8
+ from tlaplus_cli.tlc.compiler import get_tlc_jar_path
9
+ from tlaplus_cli.versioning.paths import get_modules_dir
10
+
11
+
12
+ def show_modules_path() -> None: # noqa: PLR0912
13
+ """Display the active paths that will be included when running TLC."""
14
+ config_obj = load_config()
15
+ tool_jar = get_tlc_jar_path()
16
+
17
+ resolver = ClasspathResolver(config_obj, project_root=None, tool_jar=tool_jar)
18
+
19
+ # 1. Source Paths (-DTLA-Library)
20
+ tla_library = resolver.get_tla_library_property()
21
+ typer.echo("Source Paths (-DTLA-Library):")
22
+ if tla_library:
23
+ for p in tla_library.split(os.pathsep):
24
+ typer.echo(f" - {p}")
25
+ else:
26
+ typer.echo(" None")
27
+
28
+ # 2. Classes Paths
29
+ classes_paths = []
30
+ project_classes = resolver.get_project_path(SubdirType.CLASSES)
31
+ if project_classes:
32
+ classes_paths.append(str(project_classes.absolute()))
33
+
34
+ managed_dir = get_modules_dir()
35
+ if managed_dir.is_dir():
36
+ for item in sorted(managed_dir.iterdir()):
37
+ if item.is_dir():
38
+ mod_classes = item / "classes"
39
+ if mod_classes.is_dir():
40
+ classes_paths.append(str(mod_classes.absolute()))
41
+
42
+ typer.echo("\nClasses Paths:")
43
+ if classes_paths:
44
+ for p in classes_paths:
45
+ typer.echo(f" - {p}")
46
+ else:
47
+ typer.echo(" None")
48
+
49
+ # 3. Library Paths (JARs)
50
+ libs = resolver.resolve(ResolveMode.RUNTIME)
51
+ tool_jar_abs = str(tool_jar.absolute()) if tool_jar.exists() else ""
52
+
53
+ # Filter out classes directories and tool_jar to show only external library JARs
54
+ only_libs = []
55
+ for p in libs:
56
+ if p == tool_jar_abs:
57
+ continue
58
+ p_path = Path(p)
59
+ if p_path.name == "classes" and p_path.is_dir():
60
+ continue
61
+ only_libs.append(p)
62
+
63
+ typer.echo("\nLibrary Paths (JARs):")
64
+ if only_libs:
65
+ for p in only_libs:
66
+ typer.echo(f" - {p}")
67
+ else:
68
+ typer.echo(" None")
@@ -0,0 +1,22 @@
1
+ import shutil
2
+
3
+ import typer
4
+
5
+ from tlaplus_cli.tlc.run_cache import clear_tlc_run_cache
6
+ from tlaplus_cli.versioning.paths import get_modules_dir
7
+
8
+
9
+ def remove_module(module_name: str = typer.Argument(..., help="Name of the module to remove from cache.")) -> None:
10
+ """Remove a custom module from the cache."""
11
+ target_dir = get_modules_dir() / module_name
12
+ if not target_dir.is_dir():
13
+ typer.echo(f"Error: Module '{module_name}' not found in cache.", err=True)
14
+ raise typer.Exit(1)
15
+
16
+ try:
17
+ shutil.rmtree(target_dir)
18
+ clear_tlc_run_cache()
19
+ typer.echo(f"Module '{module_name}' successfully deleted from cache.")
20
+ except OSError as e:
21
+ typer.echo(f"Error: Failed to delete module: {e}", err=True)
22
+ raise typer.Exit(1) from e