lean-interact 0.5.0__tar.gz → 0.5.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.
Files changed (20) hide show
  1. {lean_interact-0.5.0 → lean_interact-0.5.2}/PKG-INFO +2 -2
  2. {lean_interact-0.5.0 → lean_interact-0.5.2}/README.md +1 -1
  3. {lean_interact-0.5.0 → lean_interact-0.5.2}/examples/beq_plus.py +26 -8
  4. {lean_interact-0.5.0 → lean_interact-0.5.2}/pyproject.toml +1 -1
  5. {lean_interact-0.5.0 → lean_interact-0.5.2}/src/lean_interact/config.py +2 -0
  6. {lean_interact-0.5.0 → lean_interact-0.5.2}/src/lean_interact/server.py +19 -9
  7. {lean_interact-0.5.0 → lean_interact-0.5.2}/src/lean_interact/utils.py +2 -2
  8. {lean_interact-0.5.0 → lean_interact-0.5.2}/tests/test_server.py +27 -1
  9. {lean_interact-0.5.0 → lean_interact-0.5.2}/.devcontainer/devcontainer.json +0 -0
  10. {lean_interact-0.5.0 → lean_interact-0.5.2}/.github/workflows/ci.yml +0 -0
  11. {lean_interact-0.5.0 → lean_interact-0.5.2}/.github/workflows/publish-to-pypi.yml +0 -0
  12. {lean_interact-0.5.0 → lean_interact-0.5.2}/.gitignore +0 -0
  13. {lean_interact-0.5.0 → lean_interact-0.5.2}/LICENSE +0 -0
  14. {lean_interact-0.5.0 → lean_interact-0.5.2}/examples/proof_generation_and_autoformalization.py +0 -0
  15. {lean_interact-0.5.0 → lean_interact-0.5.2}/examples/type_check.py +0 -0
  16. {lean_interact-0.5.0 → lean_interact-0.5.2}/src/lean_interact/__init__.py +0 -0
  17. {lean_interact-0.5.0 → lean_interact-0.5.2}/src/lean_interact/interface.py +0 -0
  18. {lean_interact-0.5.0 → lean_interact-0.5.2}/tests/test_concurrency.py +0 -0
  19. {lean_interact-0.5.0 → lean_interact-0.5.2}/tests/test_utils.py +0 -0
  20. {lean_interact-0.5.0 → lean_interact-0.5.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lean-interact
3
- Version: 0.5.0
3
+ Version: 0.5.2
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
@@ -49,7 +49,7 @@ Description-Content-Type: text/markdown
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
51
  - **💻 Cross-platform**: Works on Windows, macOS, and Linux operating systems.
52
- - **🔧 Compatibility**: Supports all Lean versions between `v4.7.0-rc1` and `v4.19.0-rc2`.
52
+ - **🔧 Compatibility**: Supports all Lean versions between `v4.7.0-rc1` and `v4.19.0`.
53
53
  - We backport the latest features of Lean REPL to older versions of Lean.
54
54
  - **📦 Temporary Projects**: Easily instantiate temporary Lean environments.
55
55
  - Useful for experimenting with benchmarks depending on [Mathlib](https://github.com/leanprover-community/mathlib4) like [ProofNet#](https://huggingface.co/datasets/PAug/ProofNetSharp) and [MiniF2F](https://github.com/yangky11/miniF2F-lean4).
@@ -12,7 +12,7 @@
12
12
  - **🔗 Interactivity**: Execute Lean code and files directly from Python.
13
13
  - **🚀 Ease of Use**: LeanInteract abstracts the complexities of Lean setup and interaction.
14
14
  - **💻 Cross-platform**: Works on Windows, macOS, and Linux operating systems.
15
- - **🔧 Compatibility**: Supports all Lean versions between `v4.7.0-rc1` and `v4.19.0-rc2`.
15
+ - **🔧 Compatibility**: Supports all Lean versions between `v4.7.0-rc1` and `v4.19.0`.
16
16
  - We backport the latest features of Lean REPL to older versions of Lean.
17
17
  - **📦 Temporary Projects**: Easily instantiate temporary Lean environments.
18
18
  - Useful for experimenting with benchmarks depending on [Mathlib](https://github.com/leanprover-community/mathlib4) like [ProofNet#](https://huggingface.co/datasets/PAug/ProofNetSharp) and [MiniF2F](https://github.com/yangky11/miniF2F-lean4).
@@ -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.5.0"
3
+ version = "0.5.2"
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" }
@@ -59,6 +59,7 @@ class LocalProject(BaseProject):
59
59
 
60
60
  with FileLock(f"{self.directory}.lock"):
61
61
  try:
62
+ subprocess.run(["lake", "exe", "cache", "get"], cwd=self.directory, check=False, stdout=stdout, stderr=stderr)
62
63
  subprocess.run(["lake", "build"], cwd=self.directory, check=True, stdout=stdout, stderr=stderr)
63
64
  except subprocess.CalledProcessError as e:
64
65
  logger.error("Failed to build local project: %s", e)
@@ -102,6 +103,7 @@ class GitProject(BaseProject):
102
103
  repo.submodule_update(init=True, recursive=True)
103
104
 
104
105
  try:
106
+ subprocess.run(["lake", "exe", "cache", "get"], cwd=project_dir, check=False, stdout=stdout, stderr=stderr)
105
107
  subprocess.run(["lake", "build"], cwd=project_dir, check=True, stdout=stdout, stderr=stderr)
106
108
  except subprocess.CalledProcessError as e:
107
109
  logger.error("Failed to build the git project: %s", e)
@@ -71,6 +71,7 @@ class LeanServer:
71
71
  encoding="utf-8",
72
72
  text=True,
73
73
  bufsize=1,
74
+ start_new_session=True,
74
75
  preexec_fn=None
75
76
  if platform.system() != "Linux"
76
77
  else lambda: _limit_memory(self.config.memory_hard_limit_mb),
@@ -86,15 +87,24 @@ class LeanServer:
86
87
 
87
88
  def kill(self) -> None:
88
89
  if self._proc:
89
- # Close file handles before killing the process
90
- if self._proc.stdin:
91
- self._proc.stdin.close()
92
- if self._proc.stdout:
93
- self._proc.stdout.close()
94
- if self._proc.stderr:
95
- self._proc.stderr.close()
96
- self._proc.kill()
97
- self._proc.wait()
90
+ try:
91
+ proc = psutil.Process(self._proc.pid)
92
+ # Terminate the process tree
93
+ children = proc.children(recursive=True)
94
+ for child in children:
95
+ try:
96
+ child.terminate()
97
+ except Exception:
98
+ pass
99
+ proc.terminate()
100
+ _, alive = psutil.wait_procs([proc] + children, timeout=1)
101
+ for p in alive:
102
+ try:
103
+ p.kill()
104
+ except Exception:
105
+ pass
106
+ except Exception:
107
+ pass
98
108
  self._proc = None
99
109
  gc.collect()
100
110
 
@@ -20,7 +20,7 @@ logger.addHandler(handler)
20
20
  ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
21
21
  DEFAULT_CACHE_DIR = os.path.join(ROOT_DIR, "cache")
22
22
  DEFAULT_REPL_GIT_URL = "https://github.com/augustepoiroux/repl"
23
- DEFAULT_REPL_VERSION = "v1.0.6"
23
+ DEFAULT_REPL_VERSION = "v1.0.8"
24
24
 
25
25
  os.makedirs(DEFAULT_CACHE_DIR, exist_ok=True)
26
26
 
@@ -334,7 +334,7 @@ def clean_theorem_string(theorem_string: str, new_theorem_name: str = "dummy", a
334
334
  clean_formal = re.sub(r"^[^\s:({\[]+", "", clean_formal).strip()
335
335
  clean_formal = f"theorem {new_theorem_name} " + clean_formal
336
336
  if add_sorry:
337
- clean_formal += " := sorry"
337
+ clean_formal += " := by sorry"
338
338
  return clean_formal
339
339
  except Exception:
340
340
  return None
@@ -295,7 +295,7 @@ lean_exe "dummy" where
295
295
  ],
296
296
  ),
297
297
  )
298
- result = server.run(ProofStep(tactic="apply irrational_add_rat_iff.mpr", proof_state=0), verbose=True)
298
+ result = server.run(ProofStep(tactic="apply irrational_add_ratCast_iff.mpr", proof_state=0), verbose=True)
299
299
  self.assertEqual(result, ProofStepResponse(proof_state=1, goals=[], proof_status="Completed"))
300
300
 
301
301
  def test_restart_with_env(self):
@@ -416,6 +416,32 @@ lean_exe "dummy" where
416
416
  with self.assertRaises(ChildProcessError):
417
417
  server.run(Command(cmd="def z := 3"), verbose=True)
418
418
 
419
+ def test_timeout_respected(self):
420
+ if platform.system() == "Windows":
421
+ self.skipTest("(Temporary) Skipping test on Windows due to long path issues in the CI")
422
+
423
+ config = LeanREPLConfig(project=TempRequireProject("mathlib"))
424
+ server = AutoLeanServer(config)
425
+
426
+ response = server.run(
427
+ Command(cmd="import Mathlib\nset_option maxHeartbeats 0\nset_option maxRecDepth 100000"),
428
+ add_to_session_cache=True,
429
+ )
430
+ assert isinstance(response, CommandResponse)
431
+ root_env = response.env
432
+
433
+ # check that the next command takes less than 3 seconds
434
+ start = time.time()
435
+ with self.assertRaises(TimeoutError):
436
+ server.run(
437
+ Command(
438
+ cmd="theorem amc12a_2003_p1 (u v : ℕ → ℕ) (h₀ : ∀ n, u n = 2 * n + 2) (h₁ : ∀ n, v n = 2 * n + 1) :\n ((∑ k in Finset.range 2003, u k) - ∑ k in Finset.range 2003, v k) = 2003 := by simp only [h₀, h₁, Finset.sum_range_succ, Finset.sum_range_zero]; rfl",
439
+ env=root_env,
440
+ ),
441
+ timeout=2,
442
+ )
443
+ self.assertLess(time.time() - start, 3)
444
+
419
445
  # def test_run_proof(self):
420
446
  # server = AutoLeanServer(config=LeanREPLConfig(verbose=True))
421
447
  # result = server.run(
File without changes
File without changes
File without changes