fix(copilot): mirror transactional Python hooks

This commit is contained in:
ZacharyZhang-NY committed 2026-07-20 12:34:09 -04:00
1 parent af8d227b45
commit 9b67aad113
13 files changed
+2120 -583

No files matched your search

+56 -2
View File
@@ -5,7 +5,7 @@ from __future__ import annotations
from dataclasses import dataclass
import os
import tempfile
from typing import List, Optional, Sequence
from typing import List, Optional, Sequence, cast
from uuid import uuid4
from ._managed_files import (
@@ -30,6 +30,14 @@ class FileChange:
maximum_bytes: int
@dataclass(frozen=True)
class FilePrecondition:
file_path: str
label: str
original: Optional[FileSnapshot]
maximum_bytes: int = MAX_SOURCE_BYTES
@dataclass
class _StagedChange:
change: FileChange
@@ -39,6 +47,22 @@ class _StagedChange:
committed: bool = False
_EXPECTED_SNAPSHOT_UNSET = object()
def _same_snapshot(
current: Optional[FileSnapshot],
expected: Optional[FileSnapshot],
) -> bool:
if current is None or expected is None:
return current is expected
return (
current.contents == expected.contents
and current.device == expected.device
and current.inode == expected.inode
)
def read_optional(
file_path: str,
label: str,
@@ -55,6 +79,7 @@ def source_change(
next_source: Optional[str],
mode: int,
maximum_bytes: int = MAX_SOURCE_BYTES,
expected_snapshot: object = _EXPECTED_SNAPSHOT_UNSET,
) -> Optional[FileChange]:
if original == next_source:
return None
@@ -69,6 +94,10 @@ def source_change(
current = None if snapshot is None else snapshot.contents
if current != original:
raise OSError(f"{label} changed before staging: {file_path}")
if expected_snapshot is not _EXPECTED_SNAPSHOT_UNSET:
expected = cast(Optional[FileSnapshot], expected_snapshot)
if not _same_snapshot(snapshot, expected):
raise OSError(f"{label} changed before staging: {file_path}")
return FileChange(
file_path,
label,
@@ -170,6 +199,23 @@ def _assert_unchanged(change: FileChange) -> None:
raise OSError(f"{change.label} changed during installation: {change.file_path}")
def _assert_preconditions(
preconditions: Sequence[FilePrecondition],
operation: str,
) -> None:
for condition in preconditions:
current = read_physical_file(
condition.file_path,
condition.label,
condition.maximum_bytes,
)
if not _same_snapshot(current, condition.original):
raise OSError(
f"{condition.label} changed during {operation}: "
f"{condition.file_path}"
)
def _stage(change: FileChange) -> _StagedChange:
_assert_unchanged(change)
directory = os.path.dirname(change.file_path)
@@ -302,7 +348,11 @@ def _cleanup(staged: _StagedChange) -> None:
_remove_optional(staged.rollback_path)
def write_changes(changes: Sequence[FileChange], label: str) -> None:
def write_changes(
changes: Sequence[FileChange],
label: str,
preconditions: Sequence[FilePrecondition] = (),
) -> None:
filtered = [change for change in changes if change.original != change.next_source]
if not filtered:
return
@@ -311,10 +361,14 @@ def write_changes(changes: Sequence[FileChange], label: str) -> None:
raise ValueError(f"{label} contains duplicate file targets")
staged: List[_StagedChange] = []
try:
_assert_preconditions(preconditions, label)
for change in filtered:
staged.append(_stage(change))
_assert_preconditions(preconditions, label)
for item in staged:
_assert_preconditions(preconditions, label)
_commit(item)
_assert_preconditions(preconditions, label)
except Exception as error:
recovery_errors = []
for item in reversed(staged):