lean-runtime 2.0.1__tar.gz → 2.0.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.
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/CHANGELOG.md +9 -0
- {lean_runtime-2.0.1/lean_runtime.egg-info → lean_runtime-2.0.2}/PKG-INFO +1 -1
- lean_runtime-2.0.2/lean_runtime/_paths.py +38 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/bundles.py +2 -1
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/cli.py +14 -1
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/environments.py +7 -4
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/programs.py +4 -4
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/store.py +3 -2
- {lean_runtime-2.0.1 → lean_runtime-2.0.2/lean_runtime.egg-info}/PKG-INFO +1 -1
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime.egg-info/SOURCES.txt +2 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/pyproject.toml +1 -1
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_cli.py +22 -1
- lean_runtime-2.0.2/tests/test_paths.py +25 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_store.py +4 -2
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/LICENSE +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/MANIFEST.in +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/README.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/compatibility/README.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/compatibility/mathlib-4.32.2.json +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/compatibility/mathlib-4.32.2.toml +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/architecture.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/captures.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/case-study-v1.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/cli.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/development.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/environments.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/getting-started.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/index.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/local-projects.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/portable-copies.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/python-api.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/ready-programs.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/standalone-files.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/trust-and-limitations.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/docs/v1-precision.md +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/examples/mathlib.toml +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/__init__.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/__main__.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/_git.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/backends.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/comparison.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/decisions.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/diagnostics.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/errors.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/events.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/facade.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/frontmatter.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/health.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/lake.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/lockfiles.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/locking.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/matrix.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/models.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/oci.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/policies.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/profiling.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/projects.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/publisher_verification.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/py.typed +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/references.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/resolver.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/run_cli.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/runtime.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/schema_resources.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/serialization.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/specs.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/timings.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/toolchains.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/verification.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime/wire.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime.egg-info/dependency_links.txt +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime.egg-info/entry_points.txt +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime.egg-info/requires.txt +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/lean_runtime.egg-info/top_level.txt +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/mkdocs.yml +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/schemas/cleanup-v1.schema.json +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/schemas/comparison-v1.schema.json +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/schemas/execution-v1.schema.json +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/schemas/inspect-v1.schema.json +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/schemas/matrix-v1.schema.json +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/schemas/profile-v1.schema.json +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/schemas/verify-v1.schema.json +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/scripts/run_compatibility.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/scripts/run_v1_case_study.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/scripts/smoke_wheel.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/setup.cfg +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/conftest.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_backend.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_bundles.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_diagnostics.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_environment_integration.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_events_health.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_facade.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_frontmatter.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_git.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_interactive.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_lockfiles.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_multifile.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_prebuilt_policy.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_programs.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_projects.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_references.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_run_cli.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_runtime.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_schema_resources.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_schemas.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_signatures.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_specs.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_toolchains.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_v1_precision.py +0 -0
- {lean_runtime-2.0.1 → lean_runtime-2.0.2}/tests/test_verification_inventory.py +0 -0
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.2
|
|
4
|
+
|
|
5
|
+
- Remove staged environments safely when Git-for-Windows leaves source pack
|
|
6
|
+
files read-only or briefly locked.
|
|
7
|
+
- Preserve the primary environment-build diagnostic when staging cleanup also
|
|
8
|
+
fails.
|
|
9
|
+
- Show the failed phase, command, exit code, and captured tool output when a
|
|
10
|
+
verbose CLI materialization fails.
|
|
11
|
+
|
|
3
12
|
## 2.0.1
|
|
4
13
|
|
|
5
14
|
- Enable Git's long-path support for every Runtime-managed source operation so
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Internal cross-platform filesystem operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import stat
|
|
8
|
+
import time
|
|
9
|
+
from collections.abc import Callable
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from types import TracebackType
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _retry_writable(
|
|
16
|
+
function: Callable[..., Any],
|
|
17
|
+
path: str,
|
|
18
|
+
_error: tuple[type[BaseException], BaseException, TracebackType | None],
|
|
19
|
+
) -> None:
|
|
20
|
+
"""Clear Git-for-Windows read-only attributes and retry one removal."""
|
|
21
|
+
|
|
22
|
+
os.chmod(path, stat.S_IWRITE)
|
|
23
|
+
function(path)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def remove_tree(path: Path) -> None:
|
|
27
|
+
"""Remove a tree containing Git packs, tolerating transient Windows locks."""
|
|
28
|
+
|
|
29
|
+
for attempt in range(4):
|
|
30
|
+
try:
|
|
31
|
+
shutil.rmtree(path, onerror=_retry_writable)
|
|
32
|
+
return
|
|
33
|
+
except PermissionError:
|
|
34
|
+
if not path.exists():
|
|
35
|
+
return
|
|
36
|
+
if attempt == 3:
|
|
37
|
+
raise
|
|
38
|
+
time.sleep(0.05 * (2**attempt))
|
|
@@ -20,6 +20,7 @@ from pathlib import Path, PurePosixPath
|
|
|
20
20
|
from typing import Any
|
|
21
21
|
|
|
22
22
|
from ._git import git_command
|
|
23
|
+
from ._paths import remove_tree
|
|
23
24
|
from .backends import Backend
|
|
24
25
|
from .errors import EnvironmentError
|
|
25
26
|
from .events import EventEmitter
|
|
@@ -667,7 +668,7 @@ class EnvironmentBundles:
|
|
|
667
668
|
stage.replace(destination)
|
|
668
669
|
finally:
|
|
669
670
|
if stage.exists():
|
|
670
|
-
|
|
671
|
+
remove_tree(stage)
|
|
671
672
|
if name:
|
|
672
673
|
self.store.set_alias(name, environment_id)
|
|
673
674
|
self.events.emit(
|
|
@@ -50,6 +50,19 @@ def _progress(event: RuntimeEvent) -> None:
|
|
|
50
50
|
print(f"lean-runtime: {event.kind}{package}: {event.message}", file=sys.stderr)
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
def _print_operation_failure(exc: ResolutionError | MaterializationError, *, verbose: bool) -> None:
|
|
54
|
+
print(f"lean-runtime: {exc}", file=sys.stderr)
|
|
55
|
+
if not verbose:
|
|
56
|
+
return
|
|
57
|
+
print(f"phase: {exc.phase}", file=sys.stderr)
|
|
58
|
+
if exc.command:
|
|
59
|
+
print("command: " + " ".join(exc.command), file=sys.stderr)
|
|
60
|
+
if exc.exit_code is not None:
|
|
61
|
+
print(f"exit code: {exc.exit_code}", file=sys.stderr)
|
|
62
|
+
if exc.output:
|
|
63
|
+
print(exc.output.rstrip(), file=sys.stderr)
|
|
64
|
+
|
|
65
|
+
|
|
53
66
|
def _cli_source_name(path: Path) -> str:
|
|
54
67
|
if path.is_absolute():
|
|
55
68
|
return path.name
|
|
@@ -643,7 +656,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
643
656
|
)
|
|
644
657
|
)
|
|
645
658
|
else:
|
|
646
|
-
|
|
659
|
+
_print_operation_failure(exc, verbose=args.verbose)
|
|
647
660
|
return 2
|
|
648
661
|
except (LeanRuntimeError, OSError, UnicodeError, ValueError, json.JSONDecodeError) as exc:
|
|
649
662
|
if getattr(args, "json", False):
|
|
@@ -6,7 +6,6 @@ import asyncio
|
|
|
6
6
|
import json
|
|
7
7
|
import os
|
|
8
8
|
import re
|
|
9
|
-
import shutil
|
|
10
9
|
import subprocess
|
|
11
10
|
import tempfile
|
|
12
11
|
import threading
|
|
@@ -21,6 +20,7 @@ from pathlib import Path, PurePosixPath
|
|
|
21
20
|
from typing import Any, Generic, TextIO, TypeVar, cast
|
|
22
21
|
|
|
23
22
|
from ._git import git_command
|
|
23
|
+
from ._paths import remove_tree
|
|
24
24
|
from .backends import Backend, BackendResult, InteractiveProcess, InteractiveTextReader
|
|
25
25
|
from .diagnostics import error_diagnostic, parse_diagnostics
|
|
26
26
|
from .errors import EnvironmentError, MaterializationError, PolicyError
|
|
@@ -559,7 +559,7 @@ class Environment:
|
|
|
559
559
|
|
|
560
560
|
def cleanup() -> None:
|
|
561
561
|
if instance.exists():
|
|
562
|
-
|
|
562
|
+
remove_tree(instance)
|
|
563
563
|
with suppress(OSError):
|
|
564
564
|
job_parent.rmdir()
|
|
565
565
|
|
|
@@ -847,7 +847,7 @@ class Environment:
|
|
|
847
847
|
return result
|
|
848
848
|
finally:
|
|
849
849
|
if instance.exists():
|
|
850
|
-
|
|
850
|
+
remove_tree(instance)
|
|
851
851
|
with suppress(OSError):
|
|
852
852
|
job_parent.rmdir()
|
|
853
853
|
|
|
@@ -1107,7 +1107,10 @@ class EnvironmentManager:
|
|
|
1107
1107
|
)
|
|
1108
1108
|
finally:
|
|
1109
1109
|
if stage.exists():
|
|
1110
|
-
|
|
1110
|
+
# Preserve a build/materialization diagnostic if cleanup of
|
|
1111
|
+
# Git's read-only pack files also fails on Windows.
|
|
1112
|
+
with suppress(OSError):
|
|
1113
|
+
remove_tree(stage)
|
|
1111
1114
|
|
|
1112
1115
|
def _ensure_sources(self, lock: EnvironmentLock) -> None:
|
|
1113
1116
|
"""Acquire exact locked sources without invoking Lake resolution."""
|
|
@@ -10,7 +10,6 @@ from __future__ import annotations
|
|
|
10
10
|
import hashlib
|
|
11
11
|
import os
|
|
12
12
|
import re
|
|
13
|
-
import shutil
|
|
14
13
|
import tempfile
|
|
15
14
|
import uuid
|
|
16
15
|
from collections.abc import Mapping, Sequence
|
|
@@ -20,6 +19,7 @@ from datetime import datetime, timezone
|
|
|
20
19
|
from pathlib import Path, PurePosixPath
|
|
21
20
|
from typing import Any, cast
|
|
22
21
|
|
|
22
|
+
from ._paths import remove_tree
|
|
23
23
|
from .backends import Backend, BackendResult, InteractiveProcess
|
|
24
24
|
from .bundles import (
|
|
25
25
|
INDEX_MEDIA_TYPE,
|
|
@@ -238,7 +238,7 @@ class ReadyProgram:
|
|
|
238
238
|
|
|
239
239
|
def cleanup() -> None:
|
|
240
240
|
if instance.exists():
|
|
241
|
-
|
|
241
|
+
remove_tree(instance)
|
|
242
242
|
with suppress(OSError):
|
|
243
243
|
job_parent.rmdir()
|
|
244
244
|
|
|
@@ -347,7 +347,7 @@ class ProgramManager:
|
|
|
347
347
|
stage.replace(destination)
|
|
348
348
|
finally:
|
|
349
349
|
if stage.exists():
|
|
350
|
-
|
|
350
|
+
remove_tree(stage)
|
|
351
351
|
self.events.emit(
|
|
352
352
|
"program.created", "Created executable program", program_id=manifest.program_id
|
|
353
353
|
)
|
|
@@ -534,7 +534,7 @@ class ProgramManager:
|
|
|
534
534
|
stage.replace(destination)
|
|
535
535
|
finally:
|
|
536
536
|
if stage.exists():
|
|
537
|
-
|
|
537
|
+
remove_tree(stage)
|
|
538
538
|
return self.open(manifest.program_id)
|
|
539
539
|
|
|
540
540
|
|
|
@@ -18,6 +18,7 @@ from pathlib import Path
|
|
|
18
18
|
from typing import Any
|
|
19
19
|
|
|
20
20
|
from ._git import git_command
|
|
21
|
+
from ._paths import remove_tree
|
|
21
22
|
from .errors import EnvironmentError
|
|
22
23
|
from .lockfiles import EnvironmentLock
|
|
23
24
|
from .locking import FileLock
|
|
@@ -348,7 +349,7 @@ class EnvironmentStore:
|
|
|
348
349
|
# Failed Git checkouts can leave locked pack files on
|
|
349
350
|
# Windows. Cleanup must not mask the actionable Git error.
|
|
350
351
|
with suppress(OSError):
|
|
351
|
-
|
|
352
|
+
remove_tree(stage)
|
|
352
353
|
return destination
|
|
353
354
|
|
|
354
355
|
def environment_path(self, environment_id: str) -> Path:
|
|
@@ -556,7 +557,7 @@ class EnvironmentStore:
|
|
|
556
557
|
):
|
|
557
558
|
retained.append(path.name)
|
|
558
559
|
continue
|
|
559
|
-
|
|
560
|
+
remove_tree(path)
|
|
560
561
|
usage.unlink(missing_ok=True)
|
|
561
562
|
removed.append(path.name)
|
|
562
563
|
return CleanupReport(tuple(candidates), tuple(removed), tuple(retained), dry_run)
|
|
@@ -26,6 +26,7 @@ examples/mathlib.toml
|
|
|
26
26
|
lean_runtime/__init__.py
|
|
27
27
|
lean_runtime/__main__.py
|
|
28
28
|
lean_runtime/_git.py
|
|
29
|
+
lean_runtime/_paths.py
|
|
29
30
|
lean_runtime/backends.py
|
|
30
31
|
lean_runtime/bundles.py
|
|
31
32
|
lean_runtime/cli.py
|
|
@@ -91,6 +92,7 @@ tests/test_git.py
|
|
|
91
92
|
tests/test_interactive.py
|
|
92
93
|
tests/test_lockfiles.py
|
|
93
94
|
tests/test_multifile.py
|
|
95
|
+
tests/test_paths.py
|
|
94
96
|
tests/test_prebuilt_policy.py
|
|
95
97
|
tests/test_programs.py
|
|
96
98
|
tests/test_projects.py
|
|
@@ -4,7 +4,8 @@ import json
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
from lean_runtime.bundles import PortableCopyInfo
|
|
7
|
-
from lean_runtime.cli import main, parser
|
|
7
|
+
from lean_runtime.cli import _print_operation_failure, main, parser
|
|
8
|
+
from lean_runtime.errors import MaterializationError
|
|
8
9
|
from lean_runtime.models import ExecutionResult
|
|
9
10
|
from lean_runtime.verification import VerificationCheck, VerificationReport
|
|
10
11
|
|
|
@@ -34,6 +35,26 @@ def test_removed_v1_commands_are_absent() -> None:
|
|
|
34
35
|
assert commands.isdisjoint(removed)
|
|
35
36
|
|
|
36
37
|
|
|
38
|
+
def test_verbose_materialization_failure_includes_tool_output(capsys) -> None:
|
|
39
|
+
failure = MaterializationError(
|
|
40
|
+
"environment build failed",
|
|
41
|
+
phase="build",
|
|
42
|
+
command=("lake", "build"),
|
|
43
|
+
exit_code=1,
|
|
44
|
+
output="specific compiler failure\n",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
_print_operation_failure(failure, verbose=True)
|
|
48
|
+
|
|
49
|
+
assert capsys.readouterr().err == (
|
|
50
|
+
"lean-runtime: environment build failed\n"
|
|
51
|
+
"phase: build\n"
|
|
52
|
+
"command: lake build\n"
|
|
53
|
+
"exit code: 1\n"
|
|
54
|
+
"specific compiler failure\n"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
37
58
|
def test_check_file_cli_json_result(monkeypatch, tmp_path: Path, capsys) -> None:
|
|
38
59
|
source = tmp_path / "Main.lean"
|
|
39
60
|
source.write_text("example : True := by trivial")
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from lean_runtime._paths import remove_tree
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_remove_tree_retries_transient_permission_errors(tmp_path: Path, monkeypatch) -> None:
|
|
7
|
+
target = tmp_path / "tree"
|
|
8
|
+
target.mkdir()
|
|
9
|
+
calls = 0
|
|
10
|
+
|
|
11
|
+
def flaky_rmtree(path: Path, *, onerror) -> None:
|
|
12
|
+
nonlocal calls
|
|
13
|
+
calls += 1
|
|
14
|
+
if calls == 1:
|
|
15
|
+
raise PermissionError("Git pack is briefly locked")
|
|
16
|
+
assert onerror is not None
|
|
17
|
+
path.rmdir()
|
|
18
|
+
|
|
19
|
+
monkeypatch.setattr("lean_runtime._paths.shutil.rmtree", flaky_rmtree)
|
|
20
|
+
monkeypatch.setattr("lean_runtime._paths.time.sleep", lambda _seconds: None)
|
|
21
|
+
|
|
22
|
+
remove_tree(target)
|
|
23
|
+
|
|
24
|
+
assert calls == 2
|
|
25
|
+
assert not target.exists()
|
|
@@ -217,11 +217,13 @@ def test_source_snapshot_preserves_git_error_when_cleanup_also_fails(
|
|
|
217
217
|
Path(command[-1]).mkdir()
|
|
218
218
|
return subprocess.CompletedProcess(command, 1, "", "Filename too long")
|
|
219
219
|
|
|
220
|
-
def failed_cleanup(_path: Path) -> None:
|
|
220
|
+
def failed_cleanup(_path: Path, *, onerror) -> None:
|
|
221
|
+
assert onerror is not None
|
|
221
222
|
raise PermissionError("pack file is locked")
|
|
222
223
|
|
|
223
224
|
monkeypatch.setattr("lean_runtime.store.subprocess.run", failed_clone)
|
|
224
|
-
monkeypatch.setattr("lean_runtime.
|
|
225
|
+
monkeypatch.setattr("lean_runtime._paths.shutil.rmtree", failed_cleanup)
|
|
226
|
+
monkeypatch.setattr("lean_runtime._paths.time.sleep", lambda _seconds: None)
|
|
225
227
|
|
|
226
228
|
with pytest.raises(EnvironmentError, match="Filename too long"):
|
|
227
229
|
store.publish_source(checkout, source_id, metadata)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|