forked from astral-sh/ruff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
generate_builtin_modules.py
116 lines (95 loc) · 3.48 KB
/
generate_builtin_modules.py
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
"""Script to generate `crates/ruff_python_stdlib/src/builtin_modules.rs`.
This script requires the following executables to be callable via a subprocess:
- `python3.7`
- `python3.8`
- `python3.9`
- `python3.10`
- `python3.11`
- `python3.12`
- `python3.13`
"""
from __future__ import annotations
import builtins
import subprocess
import textwrap
from functools import partial
from pathlib import Path
MODULE_CRATE = "ruff_python_stdlib"
MODULE_PATH = Path("crates") / MODULE_CRATE / "src" / "sys" / "builtin_modules.rs"
type Version = tuple[int, int]
PYTHON_VERSIONS: list[Version] = [
(3, 7),
(3, 8),
(3, 9),
(3, 10),
(3, 11),
(3, 12),
(3, 13),
]
def builtin_modules_on_version(major_version: int, minor_version: int) -> set[str]:
executable = f"python{major_version}.{minor_version}"
try:
proc = subprocess.run(
[executable, "-c", "import sys; print(sys.builtin_module_names)"],
check=True,
text=True,
capture_output=True,
)
except subprocess.CalledProcessError as e:
print(e.stdout)
print(e.stderr)
raise
return set(eval(proc.stdout))
def generate_module(
script_destination: Path, crate_name: str, python_versions: list[Version]
) -> None:
with script_destination.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(major_version, minor_version)
for major_version, 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 _major_version, 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", crate_name], check=True)
if __name__ == "__main__":
generate_module(MODULE_PATH, MODULE_CRATE, PYTHON_VERSIONS)