forked from leanprover/lean4
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfix_expected.py
More file actions
executable file
·164 lines (134 loc) · 4.51 KB
/
Copy pathfix_expected.py
File metadata and controls
executable file
·164 lines (134 loc) · 4.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#!/usr/bin/env python3
from dataclasses import dataclass
from argparse import Namespace
import argparse
import os
import shutil
import subprocess
from pathlib import Path
# Run in repo root
os.chdir(Path(__file__).parent.parent)
@dataclass
class Args(Namespace):
force: bool
nomeld: bool
parser = argparse.ArgumentParser(
description="Interactively create, fix, and remove *.out.expected files "
"based on their corresponding *.out.produced files."
)
parser.add_argument(
"--force",
"-f",
action="store_true",
help="automatically accept all suggested changes without user interaction",
)
parser.add_argument(
"--nomeld",
"-M",
action="store_true",
help="diff files instead of using meld to merge them",
)
args = parser.parse_args(namespace=Args)
use_meld = not args.nomeld
try:
subprocess.run(["meld", "--version"], check=True, stdout=subprocess.DEVNULL)
except (subprocess.CalledProcessError, FileNotFoundError):
print("meld not found, falling back to --nomeld behavior")
use_meld = False
def prompt(message: str, options: str = "Yn") -> str:
default: str | None = None
for c in options:
if c.isupper():
default = c.lower()
break
options = options.lower()
options_display = "/".join(c.upper() if c == default else c for c in options)
if args.force and default:
print(f"{message} [{options_display}]: selected {default} due to --force")
return default
while True:
response = input(f"{message} [{options_display}]: ").strip().lower()
if not response and default:
return default
elif response in options.lower():
return response
else:
print(f"Please enter {options_display}.")
def remove_file(file: Path, reason: str) -> None:
print()
print()
print(f"{reason} but {file} exists.")
if prompt(f"Remove {file}?") == "y":
file.unlink()
def compare_and_merge(
produced_file: Path,
expected_file: Path,
) -> None:
produced = produced_file.read_bytes()
expected = expected_file.read_bytes()
if produced == expected:
return
print()
print()
print(f"{produced_file} differs from {expected_file}")
if args.force:
print(f"Automatically updating {expected_file} due to --force")
shutil.copy(produced_file, expected_file)
return
if not use_meld:
subprocess.run(
[
"diff",
"-au",
"--strip-trailing-cr",
"--color=always",
expected_file,
produced_file,
]
)
if prompt(f"Apply diff to {expected_file}?") == "y":
shutil.copy(produced_file, expected_file)
return
# This is the opposite direction of the tests' diff output, but meld puts
# the cursor into the right file by default, and only saves the file with
# the cursor when pressing Ctrl+S, so this order is more convenient for
# quickly fixing many files.
subprocess.run(["meld", produced_file, expected_file])
def create_or_ignore(
produced_file: Path,
expected_file: Path,
ignored_file: Path,
) -> None:
print()
print()
print(f"{produced_file} is not empty.")
answer = prompt("Create expected file, ignore, or do nothing?", "Ein")
if answer == "e":
shutil.copy(produced_file, expected_file)
elif answer == "i":
ignored_file.touch()
for produced_file in sorted(Path().rglob("*.out.produced")):
expected_file = produced_file.with_suffix(".expected")
ignored_file = produced_file.with_suffix(".ignored")
no_test_file = produced_file.with_suffix("").with_suffix(".no_test")
if no_test_file.exists():
if expected_file.exists():
remove_file(expected_file, f"{no_test_file} exists")
if ignored_file.exists():
remove_file(ignored_file, f"{no_test_file} exists")
continue
produced = produced_file.read_bytes()
if not produced:
if expected_file.exists():
remove_file(expected_file, f"{produced_file} is empty")
if ignored_file.exists():
remove_file(ignored_file, f"{produced_file} is empty")
continue
if ignored_file.exists():
if expected_file.exists():
remove_file(expected_file, f"{ignored_file} exists")
continue
if expected_file.exists():
compare_and_merge(produced_file, expected_file)
else:
create_or_ignore(produced_file, expected_file, ignored_file)