lean-interact 0.4.1__tar.gz → 0.5.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 (22) hide show
  1. lean_interact-0.5.1/.github/workflows/ci.yml +62 -0
  2. {lean_interact-0.4.1 → lean_interact-0.5.1}/PKG-INFO +20 -3
  3. {lean_interact-0.4.1 → lean_interact-0.5.1}/README.md +19 -1
  4. {lean_interact-0.4.1 → lean_interact-0.5.1}/examples/beq_plus.py +26 -8
  5. {lean_interact-0.4.1 → lean_interact-0.5.1}/pyproject.toml +1 -2
  6. {lean_interact-0.4.1 → lean_interact-0.5.1}/src/lean_interact/config.py +98 -34
  7. {lean_interact-0.4.1 → lean_interact-0.5.1}/src/lean_interact/interface.py +1 -1
  8. {lean_interact-0.4.1 → lean_interact-0.5.1}/src/lean_interact/server.py +153 -33
  9. {lean_interact-0.4.1 → lean_interact-0.5.1}/src/lean_interact/utils.py +120 -37
  10. lean_interact-0.5.1/tests/test_concurrency.py +429 -0
  11. {lean_interact-0.4.1 → lean_interact-0.5.1}/tests/test_server.py +182 -78
  12. {lean_interact-0.4.1 → lean_interact-0.5.1}/uv.lock +1 -3
  13. lean_interact-0.4.1/.github/workflows/ci.yml +0 -25
  14. lean_interact-0.4.1/tests/test_concurrency.py +0 -163
  15. {lean_interact-0.4.1 → lean_interact-0.5.1}/.devcontainer/devcontainer.json +0 -0
  16. {lean_interact-0.4.1 → lean_interact-0.5.1}/.github/workflows/publish-to-pypi.yml +0 -0
  17. {lean_interact-0.4.1 → lean_interact-0.5.1}/.gitignore +0 -0
  18. {lean_interact-0.4.1 → lean_interact-0.5.1}/LICENSE +0 -0
  19. {lean_interact-0.4.1 → lean_interact-0.5.1}/examples/proof_generation_and_autoformalization.py +0 -0
  20. {lean_interact-0.4.1 → lean_interact-0.5.1}/examples/type_check.py +0 -0
  21. {lean_interact-0.4.1 → lean_interact-0.5.1}/src/lean_interact/__init__.py +0 -0
  22. {lean_interact-0.4.1 → lean_interact-0.5.1}/tests/test_utils.py +0 -0
@@ -0,0 +1,62 @@
1
+ name: CI
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ name: Test on ${{ matrix.os }}
8
+ runs-on: ${{ matrix.os }}
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ os: [ubuntu-latest, macos-latest, windows-latest]
13
+ python-version: ['3.10']
14
+
15
+ steps:
16
+ - name: Configure Windows for long paths
17
+ if: runner.os == 'Windows'
18
+ shell: pwsh
19
+ run: |
20
+ # Enable long paths in registry
21
+ New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force
22
+ # Configure Git for long paths
23
+ git config --system core.longpaths true
24
+
25
+ - name: Checkout
26
+ uses: actions/checkout@v3
27
+
28
+ - name: Set up Python ${{ matrix.python-version }}
29
+ uses: actions/setup-python@v4
30
+ with:
31
+ python-version: ${{ matrix.python-version }}
32
+
33
+ - name: Install dependencies
34
+ run: |
35
+ python -m pip install --upgrade pip
36
+ python -m pip install .
37
+
38
+ - name: Install Lean (Linux/macOS)
39
+ if: runner.os != 'Windows'
40
+ run: |
41
+ install-lean
42
+ echo "$HOME/.elan/bin" >> $GITHUB_PATH
43
+
44
+ - name: Install Lean (Windows)
45
+ if: runner.os == 'Windows'
46
+ shell: pwsh
47
+ run: |
48
+ install-lean
49
+ echo "$env:USERPROFILE\.elan\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
50
+
51
+ - name: Run tests (Linux/macOS)
52
+ if: runner.os != 'Windows'
53
+ run: python -m unittest discover -s ./tests
54
+ env:
55
+ PYTHONIOENCODING: utf-8
56
+
57
+ - name: Run tests (Windows)
58
+ if: runner.os == 'Windows'
59
+ shell: pwsh
60
+ run: python -m unittest discover -s ./tests
61
+ env:
62
+ PYTHONIOENCODING: utf-8
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lean-interact
3
- Version: 0.4.1
3
+ Version: 0.5.1
4
4
  Summary: LeanInteract is a Python package that allows you to interact with the Lean theorem prover.
5
5
  Author-email: Auguste Poiroux <auguste.poiroux@epfl.ch>
6
6
  License: MIT License
@@ -29,7 +29,6 @@ Keywords: Lean,REPL,autoformalization,theorem proving
29
29
  Requires-Python: >=3.10
30
30
  Requires-Dist: filelock>=3.18.0
31
31
  Requires-Dist: gitpython>=3.1.44
32
- Requires-Dist: pexpect>=4.9.0
33
32
  Requires-Dist: psutil>=6.1.0
34
33
  Requires-Dist: pydantic>=2.11.1
35
34
  Requires-Dist: requests>=2.32.3
@@ -38,7 +37,8 @@ Description-Content-Type: text/markdown
38
37
 
39
38
  # LeanInteract
40
39
 
41
- [![PyPI version](https://badge.fury.io/py/lean-interact.svg)](https://badge.fury.io/py/lean-interact)
40
+ [![PyPI version](https://img.shields.io/pypi/v/lean-interact.svg)](https://pypi.org/project/lean-interact/)
41
+ [![PyPI downloads](https://img.shields.io/pepy/dt/lean-interact.svg)](https://pypi.org/project/lean-interact/)
42
42
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
43
43
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
44
44
 
@@ -48,6 +48,7 @@ Description-Content-Type: text/markdown
48
48
 
49
49
  - **🔗 Interactivity**: Execute Lean code and files directly from Python.
50
50
  - **🚀 Ease of Use**: LeanInteract abstracts the complexities of Lean setup and interaction.
51
+ - **💻 Cross-platform**: Works on Windows, macOS, and Linux operating systems.
51
52
  - **🔧 Compatibility**: Supports all Lean versions between `v4.7.0-rc1` and `v4.19.0-rc2`.
52
53
  - We backport the latest features of Lean REPL to older versions of Lean.
53
54
  - **📦 Temporary Projects**: Easily instantiate temporary Lean environments.
@@ -480,6 +481,22 @@ Common issues and their solutions:
480
481
 
481
482
  3. **Long waiting times during first run**: This is expected as Lean REPL is being downloaded and built. Additionally, if you are importing Mathlib it will take even more time. Subsequent runs will be much faster.
482
483
 
484
+ 4. **(Windows) Path too long error**: Windows has a maximum path length limitation of 260 characters.
485
+ If you get an error similar to the following one, you are likely affected by this problem:
486
+
487
+ ```
488
+ error: external command 'git' exited with code 128
489
+ ERROR Failed during Lean project setup: Command '['lake', 'update']' returned non-zero exit status 1.
490
+ ```
491
+
492
+ To resolve this, you can enable long paths in Windows 10 and later versions. For more information, refer to the [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation).
493
+ Alternatively, run the following command in a terminal with administrator privileges:
494
+
495
+ ```bash
496
+ New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name LongPathsEnabled -Value 1 -PropertyType DWord -Force
497
+ git config --system core.longpaths true
498
+ ```
499
+
483
500
  ## Contributing
484
501
 
485
502
  Contributions are welcome! Here's how you can help:
@@ -1,6 +1,7 @@
1
1
  # LeanInteract
2
2
 
3
- [![PyPI version](https://badge.fury.io/py/lean-interact.svg)](https://badge.fury.io/py/lean-interact)
3
+ [![PyPI version](https://img.shields.io/pypi/v/lean-interact.svg)](https://pypi.org/project/lean-interact/)
4
+ [![PyPI downloads](https://img.shields.io/pepy/dt/lean-interact.svg)](https://pypi.org/project/lean-interact/)
4
5
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
5
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
7
 
@@ -10,6 +11,7 @@
10
11
 
11
12
  - **🔗 Interactivity**: Execute Lean code and files directly from Python.
12
13
  - **🚀 Ease of Use**: LeanInteract abstracts the complexities of Lean setup and interaction.
14
+ - **💻 Cross-platform**: Works on Windows, macOS, and Linux operating systems.
13
15
  - **🔧 Compatibility**: Supports all Lean versions between `v4.7.0-rc1` and `v4.19.0-rc2`.
14
16
  - We backport the latest features of Lean REPL to older versions of Lean.
15
17
  - **📦 Temporary Projects**: Easily instantiate temporary Lean environments.
@@ -442,6 +444,22 @@ Common issues and their solutions:
442
444
 
443
445
  3. **Long waiting times during first run**: This is expected as Lean REPL is being downloaded and built. Additionally, if you are importing Mathlib it will take even more time. Subsequent runs will be much faster.
444
446
 
447
+ 4. **(Windows) Path too long error**: Windows has a maximum path length limitation of 260 characters.
448
+ If you get an error similar to the following one, you are likely affected by this problem:
449
+
450
+ ```
451
+ error: external command 'git' exited with code 128
452
+ ERROR Failed during Lean project setup: Command '['lake', 'update']' returned non-zero exit status 1.
453
+ ```
454
+
455
+ To resolve this, you can enable long paths in Windows 10 and later versions. For more information, refer to the [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation).
456
+ Alternatively, run the following command in a terminal with administrator privileges:
457
+
458
+ ```bash
459
+ New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name LongPathsEnabled -Value 1 -PropertyType DWord -Force
460
+ git config --system core.longpaths true
461
+ ```
462
+
445
463
  ## Contributing
446
464
 
447
465
  Contributions are welcome! Here's how you can help:
@@ -156,14 +156,14 @@ def beql(
156
156
  except ValueError:
157
157
  if verbose:
158
158
  console.print("Invalid theorems encountered, skipping this pair.")
159
- continue
159
+ break
160
160
 
161
161
  formal_code = formal_1_code + formal_2_code
162
162
  # Preliminary check to ensure the formalization is well-typed.
163
163
  if check_proof_sub(server, formal_code, context_env, formal_2_start_line, "sorry", timeout_per_proof) is None:
164
164
  if verbose:
165
165
  console.print("Ill-typed formalization encountered, skipping this pair.")
166
- continue
166
+ break
167
167
 
168
168
  proof_exact = check_proof_sub(
169
169
  server, formal_code, context_env, formal_2_start_line, "exact?", timeout_per_proof
@@ -173,6 +173,8 @@ def beql(
173
173
  if verbose:
174
174
  console.print("Proof exact")
175
175
  console.print(Syntax(proof_exact, "lean4"))
176
+ else:
177
+ break
176
178
 
177
179
  return res[0] and res[1]
178
180
 
@@ -229,13 +231,13 @@ def beq_plus(
229
231
  except ValueError:
230
232
  if verbose:
231
233
  console.print("Invalid theorem encountered, skipping this pair.")
232
- continue
234
+ break
233
235
 
234
236
  formal_code = formal_1_code + formal_2_code
235
237
  if check_proof_sub(server, formal_code, context_env, formal_2_start_line, "sorry", timeout_per_proof) is None:
236
238
  if verbose:
237
239
  console.print("Ill-typed formalization encountered, skipping this pair.")
238
- continue
240
+ break
239
241
 
240
242
  # 1. Use BEqL
241
243
  proof_exact = check_proof_sub(
@@ -322,6 +324,9 @@ def beq_plus(
322
324
  console.print(Syntax(proof_convert, "lean4"))
323
325
  break
324
326
 
327
+ if not res[i]:
328
+ break
329
+
325
330
  return res[0] and res[1]
326
331
 
327
332
 
@@ -382,22 +387,35 @@ open scoped BigOperators"""
382
387
  ]
383
388
 
384
389
  console.print(f"{metric.__name__} metric on examples:")
390
+ counter_equivalent = 0
385
391
  for formalization_1, formalization_2 in formalization_pairs:
386
392
  console.print()
387
393
  console.rule()
388
394
  console.print("Comparing formalizations:")
389
395
  console.print(Syntax(formalization_1, "lean4"))
390
396
  console.print(Syntax(formalization_2, "lean4"))
391
- console.print(
392
- f"Proved equivalent: {metric(formalization_1, formalization_2, src_header, repl_config, timeout_per_proof=DEFAULT_TIMEOUT, verbose=True)}"
397
+ equivalent = metric(
398
+ formalization_1,
399
+ formalization_2,
400
+ src_header,
401
+ repl_config,
402
+ timeout_per_proof=DEFAULT_TIMEOUT,
403
+ verbose=True,
393
404
  )
405
+ counter_equivalent += equivalent
406
+ if equivalent:
407
+ console.print("[green]Proved equivalent[/]")
408
+ else:
409
+ console.print("[red]Equivalence not proven[/]")
410
+ console.print()
411
+ console.print(f"Total proved equivalent: {counter_equivalent}/{len(formalization_pairs)}")
394
412
 
395
413
 
396
- def proofnetverif(metric):
414
+ def proofnetverif(metric, n_samples=100):
397
415
  repl_config = LeanREPLConfig(lean_version="v4.8.0", project=TempRequireProject("mathlib"), verbose=True)
398
416
 
399
417
  dataset = load_dataset("PAug/ProofNetVerif", split="valid")
400
- dataset = dataset.shuffle(seed=42).select(range(100))
418
+ dataset = dataset.shuffle(seed=42).select(range(n_samples))
401
419
 
402
420
  metric_results = []
403
421
  for example in tqdm(dataset, desc=f"`{metric.__name__}` metric on ProofNetVerif dataset"):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lean-interact"
3
- version = "0.4.1"
3
+ version = "0.5.1"
4
4
  description = "LeanInteract is a Python package that allows you to interact with the Lean theorem prover."
5
5
  keywords = ["Lean", "theorem proving", "autoformalization", "REPL"]
6
6
  license = { file = "LICENSE" }
@@ -10,7 +10,6 @@ requires-python = ">=3.10"
10
10
  dependencies = [
11
11
  "filelock>=3.18.0",
12
12
  "gitpython>=3.1.44",
13
- "pexpect>=4.9.0",
14
13
  "psutil>=6.1.0",
15
14
  "pydantic>=2.11.1",
16
15
  "requests>=2.32.3",
@@ -58,8 +58,12 @@ class LocalProject(BaseProject):
58
58
  stderr = None if verbose else subprocess.DEVNULL
59
59
 
60
60
  with FileLock(f"{self.directory}.lock"):
61
- # check that the project is buildable
62
- subprocess.run(["lake", "build"], cwd=self.directory, check=True, stdout=stdout, stderr=stderr)
61
+ try:
62
+ subprocess.run(["lake", "exe", "cache", "get"], cwd=self.directory, check=False, stdout=stdout, stderr=stderr)
63
+ subprocess.run(["lake", "build"], cwd=self.directory, check=True, stdout=stdout, stderr=stderr)
64
+ except subprocess.CalledProcessError as e:
65
+ logger.error("Failed to build local project: %s", e)
66
+ raise
63
67
 
64
68
 
65
69
  @dataclass(frozen=True)
@@ -70,8 +74,15 @@ class GitProject(BaseProject):
70
74
  rev: str | None = None
71
75
 
72
76
  def _get_directory(self, cache_dir: str, lean_version: str | None = None) -> str:
73
- repo_name = "/".join(self.url.split("/")[-2:]).replace(".git", "")
74
- return os.path.join(cache_dir, "git_projects", repo_name, self.rev or "latest")
77
+ repo_parts = self.url.split("/")
78
+ if len(repo_parts) >= 2:
79
+ owner = repo_parts[-2]
80
+ repo = repo_parts[-1].replace(".git", "")
81
+ return os.path.join(cache_dir, "git_projects", owner, repo, self.rev or "latest")
82
+ else:
83
+ # Fallback for malformed URLs
84
+ repo_name = self.url.replace(".git", "").split("/")[-1]
85
+ return os.path.join(cache_dir, "git_projects", repo_name, self.rev or "latest")
75
86
 
76
87
  def _instantiate(self, cache_dir: str, lean_version: str, verbose: bool = True):
77
88
  """Instantiate the git project."""
@@ -91,8 +102,12 @@ class GitProject(BaseProject):
91
102
 
92
103
  repo.submodule_update(init=True, recursive=True)
93
104
 
94
- # check that the project is buildable
95
- subprocess.run(["lake", "build"], cwd=project_dir, check=True, stdout=stdout, stderr=stderr)
105
+ try:
106
+ subprocess.run(["lake", "exe", "cache", "get"], cwd=project_dir, check=False, stdout=stdout, stderr=stderr)
107
+ subprocess.run(["lake", "build"], cwd=project_dir, check=True, stdout=stdout, stderr=stderr)
108
+ except subprocess.CalledProcessError as e:
109
+ logger.error("Failed to build the git project: %s", e)
110
+ raise
96
111
 
97
112
 
98
113
  @dataclass(frozen=True)
@@ -117,8 +132,8 @@ class BaseTempProject(BaseProject):
117
132
 
118
133
  # Lock the temporary project directory during setup
119
134
  with FileLock(f"{tmp_project_dir}.lock"):
120
- # check if the Lean project is already built
121
- if not os.path.exists(os.path.join(tmp_project_dir, "lakefile.lean")):
135
+ # check if the Lean project already exists
136
+ if not os.path.exists(os.path.join(tmp_project_dir, "lake-manifest.json")):
122
137
  # clean the content of the folder in case of a previous aborted build
123
138
  shutil.rmtree(tmp_project_dir, ignore_errors=True)
124
139
  os.makedirs(tmp_project_dir, exist_ok=True)
@@ -127,16 +142,31 @@ class BaseTempProject(BaseProject):
127
142
  cmd_init = ["lake", f"+{lean_version}", "init", "dummy", "exe.lean"]
128
143
  if lean_version.startswith("v4") and int(lean_version.split(".")[1]) <= 7:
129
144
  cmd_init = ["lake", f"+{lean_version}", "init", "dummy", "exe"]
130
- subprocess.run(cmd_init, cwd=tmp_project_dir, check=True, stdout=stdout, stderr=stderr)
145
+
146
+ try:
147
+ subprocess.run(cmd_init, cwd=tmp_project_dir, check=True, stdout=stdout, stderr=stderr)
148
+ except subprocess.CalledProcessError as e:
149
+ logger.error("Failed to initialize Lean project: %s", e)
150
+ raise
131
151
 
132
152
  # Create or modify the lakefile
133
153
  self._modify_lakefile(tmp_project_dir, lean_version)
134
154
 
135
155
  logger.info("Preparing Lean environment with dependencies (may take a while the first time)...")
136
- subprocess.run(["lake", "update"], cwd=tmp_project_dir, check=True, stdout=stdout, stderr=stderr)
137
- # in case mathlib is used as a dependency, we try to get the cache
138
- subprocess.run(["lake", "exe", "cache", "get"], cwd=tmp_project_dir, stdout=stdout, stderr=stderr)
139
- subprocess.run(["lake", "build"], cwd=tmp_project_dir, check=True, stdout=stdout, stderr=stderr)
156
+
157
+ # Run lake commands with appropriate platform handling
158
+ try:
159
+ subprocess.run(["lake", "update"], cwd=tmp_project_dir, check=True, stdout=stdout, stderr=stderr)
160
+ # in case mathlib is used as a dependency, we try to get the cache
161
+ subprocess.run(
162
+ ["lake", "exe", "cache", "get"], cwd=tmp_project_dir, check=False, stdout=stdout, stderr=stderr
163
+ )
164
+ subprocess.run(["lake", "build"], cwd=tmp_project_dir, check=True, stdout=stdout, stderr=stderr)
165
+ except subprocess.CalledProcessError as e:
166
+ logger.error("Failed during Lean project setup: %s", e)
167
+ # delete the project directory to avoid conflicts
168
+ shutil.rmtree(tmp_project_dir, ignore_errors=True)
169
+ raise
140
170
 
141
171
  def _get_hash_content(self, lean_version: str) -> str:
142
172
  """Return a unique hash for the project content."""
@@ -159,7 +189,7 @@ class TemporaryProject(BaseTempProject):
159
189
 
160
190
  def _modify_lakefile(self, project_dir: str, lean_version: str) -> None:
161
191
  """Write the content to the lakefile."""
162
- with open(os.path.join(project_dir, "lakefile.lean"), "w") as f:
192
+ with open(os.path.join(project_dir, "lakefile.lean"), "w", encoding="utf-8") as f:
163
193
  f.write(self.content)
164
194
 
165
195
 
@@ -173,15 +203,26 @@ class TempRequireProject(BaseTempProject):
173
203
  [MiniF2F](https://github.com/yangky11/miniF2F-lean4)) without having to manually set up a Lean project.
174
204
  """
175
205
 
176
- require: Literal["mathlib"] | list[LeanRequire]
206
+ require: Literal["mathlib"] | LeanRequire | list[LeanRequire | Literal["mathlib"]]
177
207
 
178
208
  def _normalize_require(self, lean_version: str) -> list[LeanRequire]:
179
209
  """Normalize the require field to always be a list."""
180
210
  require = self.require
181
- if require == "mathlib":
182
- return [LeanRequire("mathlib", "https://github.com/leanprover-community/mathlib4.git", lean_version)]
183
- assert isinstance(require, list)
184
- return sorted(require, key=lambda x: x.name)
211
+ if not isinstance(require, list):
212
+ require = [require]
213
+
214
+ normalized_require: list[LeanRequire] = []
215
+ for req in require:
216
+ if req == "mathlib":
217
+ normalized_require.append(
218
+ LeanRequire("mathlib", "https://github.com/leanprover-community/mathlib4.git", lean_version)
219
+ )
220
+ elif isinstance(req, LeanRequire):
221
+ normalized_require.append(req)
222
+ else:
223
+ raise ValueError(f"Invalid requirement type: {type(req)}")
224
+
225
+ return sorted(normalized_require, key=lambda x: x.name)
185
226
 
186
227
  def _get_hash_content(self, lean_version: str) -> str:
187
228
  """Return a unique hash based on dependencies."""
@@ -191,7 +232,7 @@ class TempRequireProject(BaseTempProject):
191
232
  def _modify_lakefile(self, project_dir: str, lean_version: str) -> None:
192
233
  """Add requirements to the lakefile."""
193
234
  require = self._normalize_require(lean_version)
194
- with open(os.path.join(project_dir, "lakefile.lean"), "a") as f:
235
+ with open(os.path.join(project_dir, "lakefile.lean"), "a", encoding="utf-8") as f:
195
236
  for req in require:
196
237
  f.write(f'\n\nrequire {req.name} from git\n "{req.git}"' + (f' @ "{req.rev}"' if req.rev else ""))
197
238
 
@@ -208,16 +249,18 @@ class LeanREPLConfig:
208
249
  verbose: bool = False,
209
250
  ):
210
251
  """
211
- Configuration class used to specify the Lean environment we wish to use.
252
+ Initialize the Lean REPL configuration.
212
253
 
213
254
  Args:
214
- lean_version: Lean version you want to use with the REPL.
215
- If `None`, `LeanREPLConfig` will try to infer `lean_version` from `project`, otherwise
216
- the latest version available in the Lean REPL repository will be used.
217
- project: Source configuration for the Lean project. Use one of:
218
- - `LocalProject`: A directory where a local Lean project is stored.
219
- - `GitProject`: A git repository with a Lean project.
220
- - `TemporaryProject`: A temporary Lean project with a custom lakefile that will be created.
255
+ lean_version:
256
+ The Lean version you want to use.
257
+ Default is `None`, which means the latest version compatible with the project will be selected.
258
+ project:
259
+ The project you want to use. There are 4 options:
260
+ - `None`: The project will only depend on Lean and its standard library.
261
+ - `LocalProject`: An existing local Lean project.
262
+ - `GitProject`: A git repository with a Lean project that will be cloned.
263
+ - `TemporaryProject`: A temporary Lean project with a custom lakefile.lean that will be created.
221
264
  - `TempRequireProject`: A temporary Lean project with dependencies that will be created.
222
265
  repl_rev:
223
266
  The REPL version you want to use. It is not recommended to change this value unless you know what you are doing.
@@ -228,6 +271,7 @@ class LeanREPLConfig:
228
271
  Default is inside the package directory.
229
272
  memory_hard_limit_mb:
230
273
  The maximum memory usage in MB for the Lean server. Setting this value too low may lead to more command processing failures.
274
+ Only available on Linux platforms.
231
275
  Default is `None`, which means no limit.
232
276
  verbose:
233
277
  Whether to print additional information during the setup process.
@@ -236,14 +280,21 @@ class LeanREPLConfig:
236
280
  self.project = project
237
281
  self.repl_git = repl_git
238
282
  self.repl_rev = repl_rev
239
- self.cache_dir = cache_dir
283
+ self.cache_dir = os.path.normpath(cache_dir)
240
284
  self.memory_hard_limit_mb = memory_hard_limit_mb
241
285
 
242
286
  self.verbose = verbose
243
287
  self._stdout = None if self.verbose else subprocess.DEVNULL
244
288
  self._stderr = None if self.verbose else subprocess.DEVNULL
245
289
 
246
- self.repo_name = "/".join(self.repl_git.split("/")[-2:]).replace(".git", "")
290
+ repo_parts = self.repl_git.split("/")
291
+ if len(repo_parts) >= 2:
292
+ owner = repo_parts[-2]
293
+ repo = repo_parts[-1].replace(".git", "")
294
+ self.repo_name = os.path.join(owner, repo)
295
+ else:
296
+ self.repo_name = self.repl_git.replace(".git", "")
297
+
247
298
  self.cache_clean_repl_dir = os.path.join(self.cache_dir, self.repo_name, "repl_clean_copy")
248
299
 
249
300
  # check if lake is installed
@@ -322,10 +373,13 @@ class LeanREPLConfig:
322
373
  f"Please open an issue on GitHub if you think this is a bug."
323
374
  )
324
375
 
325
- # build the repl
326
- subprocess.run(
327
- ["lake", "build"], cwd=self._cache_repl_dir, check=True, stdout=self._stdout, stderr=self._stderr
328
- )
376
+ try:
377
+ subprocess.run(
378
+ ["lake", "build"], cwd=self._cache_repl_dir, check=True, stdout=self._stdout, stderr=self._stderr
379
+ )
380
+ except subprocess.CalledProcessError as e:
381
+ logger.error("Failed to build the REPL: %s", e)
382
+ raise
329
383
 
330
384
  def _get_available_lean_versions_sha(self) -> list[tuple[str, str]]:
331
385
  """
@@ -344,5 +398,15 @@ class LeanREPLConfig:
344
398
  """
345
399
  return [commit[0] for commit in self._get_available_lean_versions_sha()]
346
400
 
401
+ @property
402
+ def working_dir(self) -> str:
403
+ """Get the working directory for the Lean environment."""
404
+ return self._working_dir
405
+
406
+ @property
407
+ def cache_repl_dir(self) -> str:
408
+ """Get the cache directory for the Lean REPL."""
409
+ return self._cache_repl_dir
410
+
347
411
  def is_setup(self) -> bool:
348
412
  return hasattr(self, "_working_dir")
@@ -254,7 +254,7 @@ class CommandResponse(BaseREPLResponse):
254
254
 
255
255
  env: int
256
256
  tactics: list[Tactic] = Field(default_factory=list)
257
- infotree: dict | None = None
257
+ infotree: list | None = None
258
258
 
259
259
 
260
260
  class ProofStepResponse(BaseREPLResponse):