Repository navigation
Expand file tree
/
Copy pathcheck-scripts.py
More file actions
executable file
·125 lines (99 loc) · 3.7 KB
/
Copy pathcheck-scripts.py
File metadata and controls
executable file
·125 lines (99 loc) · 3.7 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
#!/usr/bin/env -S uv run --script
#
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "pathspec>=1.1.1",
# ]
#
# [tool.ty.rules]
# truthiness-test-of-none-union = "warn"
# blanket-ignore-comment = "warn"
# missing-type-argument = "warn"
# possibly-unresolved-reference = "warn"
# unsound-return-statement = "warn"
# unsound-yield = "warn"
# unsupported-dynamic-base = "warn"
# division-by-zero = "warn"
# dynamic-function-decorator-return = "warn"
# unsound-assignment = "warn"
# redundant-condition-strict = "warn"
# disjoint-cast = "warn"
# missing-direct-dependency = "warn"
# ///
"""Update script lockfiles.
Run `uv run scripts/check-scripts.py` to update the lockfiles.
Use `--write-ty-args-file <path>` to write the discovered scripts to an argument file for ty
instead of updating lockfiles.
"""
from __future__ import annotations
import argparse
import subprocess
import sys
from collections.abc import Iterator
from pathlib import Path
from pathspec import GitIgnoreSpec
ROOT = Path(__file__).resolve().parent.parent
# Crate resources and completion fixtures are inputs, not project scripts.
COMPLETION_FIXTURES = Path("crates/ty_completion_eval/truth")
def scripts() -> Iterator[Path]:
"""Yield paths to Python scripts containing PEP 723 metadata.
The paths are relative to the repository root. Files ignored by the root
`.gitignore`, symlinks, crate resources, and completion fixtures are
excluded. Scripts with invalid metadata are included so `uv lock` can
report the error.
"""
gitignore = GitIgnoreSpec.from_lines((ROOT / ".gitignore").read_text().splitlines())
# uv's script listing skips invalid metadata, so find candidates before
# asking uv to validate and lock them.
#
# We use `.walk()` here rather than `.rglob()` because some of our ignored directories are
# huge; `.walk()` allows us to lazily inspect whether they're gitignored before recursively
# descending into them.
for directory, dirnames, filenames in ROOT.walk():
dirnames[:] = [
dirname
for dirname in dirnames
if (path := (directory / dirname).relative_to(ROOT)) != Path(".git")
and not gitignore.match_file(f"{path.as_posix()}/")
]
for filename in filenames:
if not filename.endswith(".py"):
continue
full_path = directory / filename
path = full_path.relative_to(ROOT)
if path.is_relative_to(COMPLETION_FIXTURES):
continue
match full_path.relative_to(ROOT).parts:
case ("crates", _, "resources", *_):
continue
if (
full_path.is_symlink()
or not full_path.is_file()
or gitignore.match_file(path)
):
continue
if b"# /// script" in full_path.read_bytes().splitlines():
yield path
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__, allow_abbrev=False)
parser.add_argument(
"--write-ty-args-file",
type=Path,
help="Write script paths to an argument file for ty",
)
args = parser.parse_args()
if args.write_ty_args_file is not None:
args.write_ty_args_file.write_text(
"".join(f"{script}\n" for script in scripts()), encoding="utf-8"
)
return 0
failed = False
for script in scripts():
print(script, flush=True)
command = ["uv", "lock", "--script", str(script), "--refresh", "--no-locked"]
result = subprocess.run(command, cwd=ROOT, check=False)
failed |= result.returncode != 0
return int(failed)
if __name__ == "__main__":
sys.exit(main())