Repository navigation
Expand file tree
/
Copy pathgenerate_builtin_modules.py
More file actions
134 lines (112 loc) · 3.77 KB
/
Copy pathgenerate_builtin_modules.py
File metadata and controls
134 lines (112 loc) · 3.77 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
# /// script
# requires-python = ">=3.12"
# dependencies = []
#
# [tool.ty.rules]
# 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"
#
# [tool.uv]
# no-build = true
# exclude-newer = "P7D"
# ///
"""Script to generate `crates/ruff_python_stdlib/src/sys/builtin_modules.rs`.
This script requires `uvx` to be available on PATH.
"""
from __future__ import annotations
import builtins
import subprocess
import textwrap
from functools import partial
from pathlib import Path
from typing import Final
MODULE_CRATE = "ruff_python_stdlib"
MODULE_PATH = Path("crates") / MODULE_CRATE / "src" / "sys" / "builtin_modules.rs"
PYTHON_VERSIONS: Final = range(8, 16) # Python 3.8 through 3.15
def run(command: list[str]) -> str:
try:
proc = subprocess.run(
command,
check=True,
text=True,
capture_output=True,
)
except subprocess.CalledProcessError as e:
print(e.stdout)
print(e.stderr)
raise
return proc.stdout
def builtin_modules_on_version(minor_version: int) -> set[str]:
command_1 = [
"uv",
"--managed-python",
"python",
"install",
f"python3.{minor_version}",
"--upgrade",
]
run(command_1)
command_2 = [
"uvx",
"--managed-python",
f"python3.{minor_version}",
"-c",
"import sys; print(sys.builtin_module_names)",
]
return set(eval(run(command_2)))
def generate_module() -> None:
with MODULE_PATH.open("w") as f:
print = partial(builtins.print, file=f)
print(
textwrap.dedent(
"""\
//! This file is generated by `scripts/generate_builtin_modules.py`
/// Return `true` if `module` is a [builtin module] on the given
/// Python 3 version.
///
/// "Builtin modules" are modules that are compiled directly into the
/// Python interpreter. These can never be shadowed by first-party
/// modules; the normal rules of module resolution do not apply to these
/// modules.
///
/// [builtin module]: https://docs.python.org/3/library/sys.html#sys.builtin_module_names
#[allow(clippy::unnested_or_patterns)]
pub fn is_builtin_module(minor_version: u8, module: &str) -> bool {
matches!((minor_version, module),
""",
)
)
modules_by_version = {
minor_version: builtin_modules_on_version(minor_version)
for minor_version in PYTHON_VERSIONS
}
# First, add a case for the modules that are in all versions.
ubiquitous_modules = set.intersection(*modules_by_version.values())
print("(_, ")
for i, module in enumerate(sorted(ubiquitous_modules)):
if i > 0:
print(" | ", end="")
print(f'"{module}"')
print(")")
# Next, add any version-specific modules.
for minor_version in PYTHON_VERSIONS:
version_modules = set.difference(
modules_by_version[minor_version],
ubiquitous_modules,
)
print(" | ")
print(f"({minor_version}, ")
for i, module in enumerate(sorted(version_modules)):
if i > 0:
print(" | ", end="")
print(f'"{module}"')
print(")")
print(")}")
subprocess.run(["cargo", "fmt", "--package", MODULE_CRATE], check=True)
if __name__ == "__main__":
generate_module()