1pub const MEMBER_PRESERVING_CLASS_DECORATORS: &[(&str, &str)] = &[
13 ("dataclasses", "dataclass"),
14 ("typing", "final"),
15 ("functools", "total_ordering"),
16 ("enum", "unique"),
17 ("typing", "runtime_checkable"),
18 ("unittest", "skip"),
19 ("unittest", "skipIf"),
20 ("unittest", "skipUnless"),
21 ("unittest", "expectedFailure"),
22];
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
25pub struct PythonVersion {
26 pub major: u16,
27 pub minor: u16,
28}
29impl PythonVersion {
30 pub const fn new(major: u16, minor: u16) -> Self {
31 Self { major, minor }
32 }
33}
34const FIRST: PythonVersion = PythonVersion::new(3, 0);
35const END: PythonVersion = PythonVersion::new(3, 16);
36const UNDECLARED_FLOOR: PythonVersion = PythonVersion::new(3, 9);
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum Availability {
42 Present,
43 Absent,
44 Unknown,
45}
46
47#[derive(Clone, Copy, Debug)]
48pub struct VersionRange {
49 pub since: PythonVersion,
50}
51impl VersionRange {
52 pub fn availability(self, version: Option<PythonVersion>) -> Availability {
53 let minimum = version.unwrap_or(UNDECLARED_FLOOR);
54 if !(FIRST..END).contains(&minimum) || self.since > minimum {
55 Availability::Unknown
56 } else {
57 Availability::Present
58 }
59 }
60}
61
62#[derive(Clone, Copy, Debug)]
63pub enum BuiltinKind {
64 Type,
65 Callable,
66 Value,
67}
68#[derive(Clone, Copy, Debug)]
69pub struct Builtin {
70 pub name: &'static str,
71 pub qualified_name: &'static str,
72 pub kind: BuiltinKind,
73 pub versions: VersionRange,
74 pub allocator_may_return_subclass: bool,
76}
77macro_rules! builtin {
78 ($name:ident, $kind:ident, $minor:literal) => {
79 builtin!($name, $kind, $minor, false)
80 };
81 ($name:ident, $kind:ident, $minor:literal, $subclass:literal) => {
82 Builtin {
83 name: stringify!($name),
84 qualified_name: concat!("builtins.", stringify!($name)),
85 kind: BuiltinKind::$kind,
86 allocator_may_return_subclass: $subclass,
87 versions: VersionRange {
88 since: PythonVersion::new(3, $minor),
89 },
90 }
91 };
92}
93
94pub const BUILTINS: &[Builtin] = &[
95 builtin!(ArithmeticError, Type, 0),
96 builtin!(AssertionError, Type, 0),
97 builtin!(AttributeError, Type, 0),
98 builtin!(BaseException, Type, 0),
99 builtin!(BaseExceptionGroup, Type, 11, true),
100 builtin!(BlockingIOError, Type, 3),
101 builtin!(BrokenPipeError, Type, 3),
102 builtin!(BufferError, Type, 0),
103 builtin!(BytesWarning, Type, 0),
104 builtin!(ChildProcessError, Type, 3),
105 builtin!(ConnectionAbortedError, Type, 3),
106 builtin!(ConnectionError, Type, 3),
107 builtin!(ConnectionRefusedError, Type, 3),
108 builtin!(ConnectionResetError, Type, 3),
109 builtin!(DeprecationWarning, Type, 0),
110 builtin!(EOFError, Type, 0),
111 builtin!(Ellipsis, Value, 0),
112 builtin!(EncodingWarning, Type, 10),
113 builtin!(EnvironmentError, Type, 0, true),
114 builtin!(Exception, Type, 0),
115 builtin!(ExceptionGroup, Type, 11),
116 builtin!(False, Value, 0),
117 builtin!(FileExistsError, Type, 3),
118 builtin!(FileNotFoundError, Type, 3),
119 builtin!(FloatingPointError, Type, 0),
120 builtin!(FutureWarning, Type, 0),
121 builtin!(GeneratorExit, Type, 0),
122 builtin!(IOError, Type, 0, true),
123 builtin!(ImportCycleError, Type, 15),
124 builtin!(ImportError, Type, 0),
125 builtin!(ImportWarning, Type, 0),
126 builtin!(IndentationError, Type, 0),
127 builtin!(IndexError, Type, 0),
128 builtin!(InterruptedError, Type, 3),
129 builtin!(IsADirectoryError, Type, 3),
130 builtin!(KeyError, Type, 0),
131 builtin!(KeyboardInterrupt, Type, 0),
132 builtin!(LookupError, Type, 0),
133 builtin!(MemoryError, Type, 0),
134 builtin!(ModuleNotFoundError, Type, 6),
135 builtin!(NameError, Type, 0),
136 builtin!(None, Value, 0),
137 builtin!(NotADirectoryError, Type, 3),
138 builtin!(NotImplemented, Value, 0),
139 builtin!(NotImplementedError, Type, 0),
140 builtin!(OSError, Type, 0, true),
141 builtin!(OverflowError, Type, 0),
142 builtin!(PendingDeprecationWarning, Type, 0),
143 builtin!(PermissionError, Type, 3),
144 builtin!(ProcessLookupError, Type, 3),
145 builtin!(PythonFinalizationError, Type, 13),
146 builtin!(RecursionError, Type, 5),
147 builtin!(ReferenceError, Type, 0),
148 builtin!(ResourceWarning, Type, 2),
149 builtin!(RuntimeError, Type, 0),
150 builtin!(RuntimeWarning, Type, 0),
151 builtin!(StopAsyncIteration, Type, 5),
152 builtin!(StopIteration, Type, 0),
153 builtin!(SyntaxError, Type, 0),
154 builtin!(SyntaxWarning, Type, 0),
155 builtin!(SystemError, Type, 0),
156 builtin!(SystemExit, Type, 0),
157 builtin!(TabError, Type, 0),
158 builtin!(TimeoutError, Type, 3),
159 builtin!(True, Value, 0),
160 builtin!(TypeError, Type, 0),
161 builtin!(UnboundLocalError, Type, 0),
162 builtin!(UnicodeDecodeError, Type, 0),
163 builtin!(UnicodeEncodeError, Type, 0),
164 builtin!(UnicodeError, Type, 0),
165 builtin!(UnicodeTranslateError, Type, 0),
166 builtin!(UnicodeWarning, Type, 0),
167 builtin!(UserWarning, Type, 0),
168 builtin!(ValueError, Type, 0),
169 builtin!(Warning, Type, 0),
170 builtin!(ZeroDivisionError, Type, 0),
171 builtin!(__build_class__, Callable, 0),
172 builtin!(__debug__, Value, 0),
173 builtin!(__import__, Callable, 0),
174 builtin!(abs, Callable, 0),
175 builtin!(aiter, Callable, 10),
176 builtin!(all, Callable, 0),
177 builtin!(anext, Callable, 10),
178 builtin!(any, Callable, 0),
179 builtin!(ascii, Callable, 0),
180 builtin!(bin, Callable, 0),
181 builtin!(bool, Type, 0),
182 builtin!(breakpoint, Callable, 7),
183 builtin!(bytearray, Type, 0),
184 builtin!(bytes, Type, 0),
185 builtin!(callable, Callable, 2),
186 builtin!(chr, Callable, 0),
187 builtin!(classmethod, Type, 0),
188 builtin!(compile, Callable, 0),
189 builtin!(complex, Type, 0),
190 builtin!(delattr, Callable, 0),
191 builtin!(dict, Type, 0),
192 builtin!(dir, Callable, 0),
193 builtin!(divmod, Callable, 0),
194 builtin!(enumerate, Type, 0),
195 builtin!(eval, Callable, 0),
196 builtin!(exec, Callable, 0),
197 builtin!(filter, Type, 0),
198 builtin!(float, Type, 0),
199 builtin!(format, Callable, 0),
200 builtin!(frozendict, Type, 15),
201 builtin!(frozenset, Type, 0),
202 builtin!(getattr, Callable, 0),
203 builtin!(globals, Callable, 0),
204 builtin!(hasattr, Callable, 0),
205 builtin!(hash, Callable, 0),
206 builtin!(hex, Callable, 0),
207 builtin!(id, Callable, 0),
208 builtin!(input, Callable, 0),
209 builtin!(int, Type, 0),
210 builtin!(isinstance, Callable, 0),
211 builtin!(issubclass, Callable, 0),
212 builtin!(iter, Callable, 0),
213 builtin!(len, Callable, 0),
214 builtin!(list, Type, 0),
215 builtin!(locals, Callable, 0),
216 builtin!(map, Type, 0),
217 builtin!(max, Callable, 0),
218 builtin!(memoryview, Type, 0),
219 builtin!(min, Callable, 0),
220 builtin!(next, Callable, 0),
221 builtin!(object, Type, 0),
222 builtin!(oct, Callable, 0),
223 builtin!(open, Callable, 0),
224 builtin!(ord, Callable, 0),
225 builtin!(pow, Callable, 0),
226 builtin!(print, Callable, 0),
227 builtin!(property, Type, 0),
228 builtin!(range, Type, 0),
229 builtin!(repr, Callable, 0),
230 builtin!(reversed, Type, 0),
231 builtin!(round, Callable, 0),
232 builtin!(sentinel, Type, 15),
233 builtin!(set, Type, 0),
234 builtin!(setattr, Callable, 0),
235 builtin!(slice, Type, 0),
236 builtin!(sorted, Callable, 0),
237 builtin!(staticmethod, Type, 0),
238 builtin!(str, Type, 0),
239 builtin!(sum, Callable, 0),
240 builtin!(super, Type, 0),
241 builtin!(tuple, Type, 0),
242 builtin!(type, Type, 0),
243 builtin!(vars, Callable, 0),
244 builtin!(zip, Type, 0),
245];
246
247pub fn implicit_base(version: Option<PythonVersion>) -> Option<&'static Builtin> {
249 let object = BUILTINS
250 .iter()
251 .find(|row| row.name == "object")
252 .expect("object is a Python language root");
253 (object.versions.availability(version) == Availability::Present).then_some(object)
254}
255pub fn builtin(name: &str) -> Option<&'static Builtin> {
256 BUILTINS.iter().find(|row| row.name == name)
257}
258
259#[derive(Clone, Copy, Debug)]
260pub struct ObjectMember {
261 pub name: &'static str,
262 pub versions: VersionRange,
263}
264macro_rules! member {
265 ($name:literal, $minor:literal) => {
266 ObjectMember {
267 name: $name,
268 versions: VersionRange {
269 since: PythonVersion::new(3, $minor),
270 },
271 }
272 };
273}
274pub const OBJECT_MEMBERS: &[ObjectMember] = &[
276 member!("__class__", 0),
277 member!("__delattr__", 0),
278 member!("__dir__", 3),
279 member!("__doc__", 0),
280 member!("__eq__", 0),
281 member!("__format__", 0),
282 member!("__ge__", 0),
283 member!("__getattribute__", 0),
284 member!("__getstate__", 11),
285 member!("__gt__", 0),
286 member!("__hash__", 0),
287 member!("__init__", 0),
288 member!("__init_subclass__", 6),
289 member!("__le__", 0),
290 member!("__lt__", 0),
291 member!("__ne__", 0),
292 member!("__new__", 0),
293 member!("__reduce__", 0),
294 member!("__reduce_ex__", 0),
295 member!("__repr__", 0),
296 member!("__setattr__", 0),
297 member!("__sizeof__", 0),
298 member!("__str__", 0),
299 member!("__subclasshook__", 0),
300];
301
302pub fn object_member(name: &str, version: Option<PythonVersion>) -> Availability {
303 match OBJECT_MEMBERS.iter().find(|row| row.name == name) {
304 Some(row) => row.versions.availability(version),
305 None if version.is_none_or(|v| (FIRST..END).contains(&v)) => Availability::Absent,
306 None => Availability::Unknown,
307 }
308}
309
310pub fn has_fixed_value_class_surface(qualified_name: &str) -> bool {
314 matches!(
315 qualified_name,
316 "types.NoneType"
317 | "builtins.bool"
318 | "builtins.int"
319 | "builtins.float"
320 | "builtins.str"
321 | "builtins.bytes"
322 | "builtins.list"
323 | "builtins.tuple"
324 | "builtins.dict"
325 | "builtins.set"
326 | "builtins.frozenset"
327 | "builtins.object"
328 )
329}
330
331pub const NONE_TYPE_MEMBERS: &[ObjectMember] = &[member!("__bool__", 0)];
336
337pub fn none_type_member(name: &str, version: Option<PythonVersion>) -> Availability {
338 match NONE_TYPE_MEMBERS.iter().find(|row| row.name == name) {
339 Some(row) => row.versions.availability(version),
340 None => object_member(name, version),
341 }
342}
343
344#[cfg(test)]
345mod allocator_review_tests {
346 #[test]
347 fn builtin_allocator_result_bounds() {
348 for name in [
349 "BaseExceptionGroup",
350 "OSError",
351 "EnvironmentError",
352 "IOError",
353 ] {
354 assert!(
355 super::builtin(name).unwrap().allocator_may_return_subclass,
356 "{name}"
357 );
358 }
359 for name in [
360 "object",
361 "dict",
362 "list",
363 "set",
364 "tuple",
365 "str",
366 "int",
367 "float",
368 "bytes",
369 "bool",
370 "ExceptionGroup",
371 "FileNotFoundError",
372 ] {
373 assert!(
374 !super::builtin(name).unwrap().allocator_may_return_subclass,
375 "{name}"
376 );
377 }
378 }
379}