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.
- lean_interact-0.5.1/.github/workflows/ci.yml +62 -0
- {lean_interact-0.4.1 → lean_interact-0.5.1}/PKG-INFO +20 -3
- {lean_interact-0.4.1 → lean_interact-0.5.1}/README.md +19 -1
- {lean_interact-0.4.1 → lean_interact-0.5.1}/examples/beq_plus.py +26 -8
- {lean_interact-0.4.1 → lean_interact-0.5.1}/pyproject.toml +1 -2
- {lean_interact-0.4.1 → lean_interact-0.5.1}/src/lean_interact/config.py +98 -34
- {lean_interact-0.4.1 → lean_interact-0.5.1}/src/lean_interact/interface.py +1 -1
- {lean_interact-0.4.1 → lean_interact-0.5.1}/src/lean_interact/server.py +153 -33
- {lean_interact-0.4.1 → lean_interact-0.5.1}/src/lean_interact/utils.py +120 -37
- lean_interact-0.5.1/tests/test_concurrency.py +429 -0
- {lean_interact-0.4.1 → lean_interact-0.5.1}/tests/test_server.py +182 -78
- {lean_interact-0.4.1 → lean_interact-0.5.1}/uv.lock +1 -3
- lean_interact-0.4.1/.github/workflows/ci.yml +0 -25
- lean_interact-0.4.1/tests/test_concurrency.py +0 -163
- {lean_interact-0.4.1 → lean_interact-0.5.1}/.devcontainer/devcontainer.json +0 -0
- {lean_interact-0.4.1 → lean_interact-0.5.1}/.github/workflows/publish-to-pypi.yml +0 -0
- {lean_interact-0.4.1 → lean_interact-0.5.1}/.gitignore +0 -0
- {lean_interact-0.4.1 → lean_interact-0.5.1}/LICENSE +0 -0
- {lean_interact-0.4.1 → lean_interact-0.5.1}/examples/proof_generation_and_autoformalization.py +0 -0
- {lean_interact-0.4.1 → lean_interact-0.5.1}/examples/type_check.py +0 -0
- {lean_interact-0.4.1 → lean_interact-0.5.1}/src/lean_interact/__init__.py +0 -0
- {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.
|
|
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
|
-
[](https://pypi.org/project/lean-interact/)
|
|
41
|
+
[](https://pypi.org/project/lean-interact/)
|
|
42
42
|
[](https://www.python.org/downloads/)
|
|
43
43
|
[](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
|
-
[](https://pypi.org/project/lean-interact/)
|
|
4
|
+
[](https://pypi.org/project/lean-interact/)
|
|
4
5
|
[](https://www.python.org/downloads/)
|
|
5
6
|
[](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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
392
|
-
|
|
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(
|
|
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.
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|
|
121
|
-
if not os.path.exists(os.path.join(tmp_project_dir, "
|
|
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
|
-
|
|
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
|
-
|
|
137
|
-
#
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
252
|
+
Initialize the Lean REPL configuration.
|
|
212
253
|
|
|
213
254
|
Args:
|
|
214
|
-
lean_version:
|
|
215
|
-
|
|
216
|
-
the latest version
|
|
217
|
-
project:
|
|
218
|
-
|
|
219
|
-
- `
|
|
220
|
-
- `
|
|
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
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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")
|