Skip to main content

brokk_bifrost_python/
intrinsics.rs

1//! Python language-defined builtin names and the closed object member surface.
2//!
3//! Audited Python 3.0 through 3.15. Unknown/out-of-range versions do not select
4//! version-dependent names. Identity is independent of availability and version.
5//! Sources: https://docs.python.org/3.15/library/functions.html,
6//! https://docs.python.org/3.15/library/exceptions.html and
7//! https://docs.python.org/3.15/reference/datamodel.html.
8//! Site-installed conveniences and platform-only names are not language rules.
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
11pub struct PythonVersion {
12    pub major: u16,
13    pub minor: u16,
14}
15impl PythonVersion {
16    pub const fn new(major: u16, minor: u16) -> Self {
17        Self { major, minor }
18    }
19}
20const FIRST: PythonVersion = PythonVersion::new(3, 0);
21const END: PythonVersion = PythonVersion::new(3, 16);
22// Undeclared projects use 3.9: CPython 3.8 and older were end of life by 2024,
23// and large projects such as PyTorch still declare 3.9.
24const UNDECLARED_FLOOR: PythonVersion = PythonVersion::new(3, 9);
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum Availability {
28    Present,
29    Absent,
30    Unknown,
31}
32
33#[derive(Clone, Copy, Debug)]
34pub struct VersionRange {
35    pub since: PythonVersion,
36}
37impl VersionRange {
38    pub fn availability(self, version: Option<PythonVersion>) -> Availability {
39        let minimum = version.unwrap_or(UNDECLARED_FLOOR);
40        if !(FIRST..END).contains(&minimum) || self.since > minimum {
41            Availability::Unknown
42        } else {
43            Availability::Present
44        }
45    }
46}
47
48#[derive(Clone, Copy, Debug)]
49pub enum BuiltinKind {
50    Type,
51    Callable,
52    Value,
53}
54#[derive(Clone, Copy, Debug)]
55pub struct Builtin {
56    pub name: &'static str,
57    pub qualified_name: &'static str,
58    pub kind: BuiltinKind,
59    pub versions: VersionRange,
60}
61macro_rules! builtin {
62    ($name:ident, $kind:ident, $minor:literal) => {
63        Builtin {
64            name: stringify!($name),
65            qualified_name: concat!("builtins.", stringify!($name)),
66            kind: BuiltinKind::$kind,
67            versions: VersionRange {
68                since: PythonVersion::new(3, $minor),
69            },
70        }
71    };
72}
73
74pub const BUILTINS: &[Builtin] = &[
75    builtin!(ArithmeticError, Type, 0),
76    builtin!(AssertionError, Type, 0),
77    builtin!(AttributeError, Type, 0),
78    builtin!(BaseException, Type, 0),
79    builtin!(BaseExceptionGroup, Type, 11),
80    builtin!(BlockingIOError, Type, 3),
81    builtin!(BrokenPipeError, Type, 3),
82    builtin!(BufferError, Type, 0),
83    builtin!(BytesWarning, Type, 0),
84    builtin!(ChildProcessError, Type, 3),
85    builtin!(ConnectionAbortedError, Type, 3),
86    builtin!(ConnectionError, Type, 3),
87    builtin!(ConnectionRefusedError, Type, 3),
88    builtin!(ConnectionResetError, Type, 3),
89    builtin!(DeprecationWarning, Type, 0),
90    builtin!(EOFError, Type, 0),
91    builtin!(Ellipsis, Value, 0),
92    builtin!(EncodingWarning, Type, 10),
93    builtin!(EnvironmentError, Type, 0),
94    builtin!(Exception, Type, 0),
95    builtin!(ExceptionGroup, Type, 11),
96    builtin!(False, Value, 0),
97    builtin!(FileExistsError, Type, 3),
98    builtin!(FileNotFoundError, Type, 3),
99    builtin!(FloatingPointError, Type, 0),
100    builtin!(FutureWarning, Type, 0),
101    builtin!(GeneratorExit, Type, 0),
102    builtin!(IOError, Type, 0),
103    builtin!(ImportCycleError, Type, 15),
104    builtin!(ImportError, Type, 0),
105    builtin!(ImportWarning, Type, 0),
106    builtin!(IndentationError, Type, 0),
107    builtin!(IndexError, Type, 0),
108    builtin!(InterruptedError, Type, 3),
109    builtin!(IsADirectoryError, Type, 3),
110    builtin!(KeyError, Type, 0),
111    builtin!(KeyboardInterrupt, Type, 0),
112    builtin!(LookupError, Type, 0),
113    builtin!(MemoryError, Type, 0),
114    builtin!(ModuleNotFoundError, Type, 6),
115    builtin!(NameError, Type, 0),
116    builtin!(None, Value, 0),
117    builtin!(NotADirectoryError, Type, 3),
118    builtin!(NotImplemented, Value, 0),
119    builtin!(NotImplementedError, Type, 0),
120    builtin!(OSError, Type, 0),
121    builtin!(OverflowError, Type, 0),
122    builtin!(PendingDeprecationWarning, Type, 0),
123    builtin!(PermissionError, Type, 3),
124    builtin!(ProcessLookupError, Type, 3),
125    builtin!(PythonFinalizationError, Type, 13),
126    builtin!(RecursionError, Type, 5),
127    builtin!(ReferenceError, Type, 0),
128    builtin!(ResourceWarning, Type, 2),
129    builtin!(RuntimeError, Type, 0),
130    builtin!(RuntimeWarning, Type, 0),
131    builtin!(StopAsyncIteration, Type, 5),
132    builtin!(StopIteration, Type, 0),
133    builtin!(SyntaxError, Type, 0),
134    builtin!(SyntaxWarning, Type, 0),
135    builtin!(SystemError, Type, 0),
136    builtin!(SystemExit, Type, 0),
137    builtin!(TabError, Type, 0),
138    builtin!(TimeoutError, Type, 3),
139    builtin!(True, Value, 0),
140    builtin!(TypeError, Type, 0),
141    builtin!(UnboundLocalError, Type, 0),
142    builtin!(UnicodeDecodeError, Type, 0),
143    builtin!(UnicodeEncodeError, Type, 0),
144    builtin!(UnicodeError, Type, 0),
145    builtin!(UnicodeTranslateError, Type, 0),
146    builtin!(UnicodeWarning, Type, 0),
147    builtin!(UserWarning, Type, 0),
148    builtin!(ValueError, Type, 0),
149    builtin!(Warning, Type, 0),
150    builtin!(ZeroDivisionError, Type, 0),
151    builtin!(__build_class__, Callable, 0),
152    builtin!(__debug__, Value, 0),
153    builtin!(__import__, Callable, 0),
154    builtin!(abs, Callable, 0),
155    builtin!(aiter, Callable, 10),
156    builtin!(all, Callable, 0),
157    builtin!(anext, Callable, 10),
158    builtin!(any, Callable, 0),
159    builtin!(ascii, Callable, 0),
160    builtin!(bin, Callable, 0),
161    builtin!(bool, Type, 0),
162    builtin!(breakpoint, Callable, 7),
163    builtin!(bytearray, Type, 0),
164    builtin!(bytes, Type, 0),
165    builtin!(callable, Callable, 2),
166    builtin!(chr, Callable, 0),
167    builtin!(classmethod, Type, 0),
168    builtin!(compile, Callable, 0),
169    builtin!(complex, Type, 0),
170    builtin!(delattr, Callable, 0),
171    builtin!(dict, Type, 0),
172    builtin!(dir, Callable, 0),
173    builtin!(divmod, Callable, 0),
174    builtin!(enumerate, Type, 0),
175    builtin!(eval, Callable, 0),
176    builtin!(exec, Callable, 0),
177    builtin!(filter, Type, 0),
178    builtin!(float, Type, 0),
179    builtin!(format, Callable, 0),
180    builtin!(frozendict, Type, 15),
181    builtin!(frozenset, Type, 0),
182    builtin!(getattr, Callable, 0),
183    builtin!(globals, Callable, 0),
184    builtin!(hasattr, Callable, 0),
185    builtin!(hash, Callable, 0),
186    builtin!(hex, Callable, 0),
187    builtin!(id, Callable, 0),
188    builtin!(input, Callable, 0),
189    builtin!(int, Type, 0),
190    builtin!(isinstance, Callable, 0),
191    builtin!(issubclass, Callable, 0),
192    builtin!(iter, Callable, 0),
193    builtin!(len, Callable, 0),
194    builtin!(list, Type, 0),
195    builtin!(locals, Callable, 0),
196    builtin!(map, Type, 0),
197    builtin!(max, Callable, 0),
198    builtin!(memoryview, Type, 0),
199    builtin!(min, Callable, 0),
200    builtin!(next, Callable, 0),
201    builtin!(object, Type, 0),
202    builtin!(oct, Callable, 0),
203    builtin!(open, Callable, 0),
204    builtin!(ord, Callable, 0),
205    builtin!(pow, Callable, 0),
206    builtin!(print, Callable, 0),
207    builtin!(property, Type, 0),
208    builtin!(range, Type, 0),
209    builtin!(repr, Callable, 0),
210    builtin!(reversed, Type, 0),
211    builtin!(round, Callable, 0),
212    builtin!(sentinel, Type, 15),
213    builtin!(set, Type, 0),
214    builtin!(setattr, Callable, 0),
215    builtin!(slice, Type, 0),
216    builtin!(sorted, Callable, 0),
217    builtin!(staticmethod, Type, 0),
218    builtin!(str, Type, 0),
219    builtin!(sum, Callable, 0),
220    builtin!(super, Type, 0),
221    builtin!(tuple, Type, 0),
222    builtin!(type, Type, 0),
223    builtin!(vars, Callable, 0),
224    builtin!(zip, Type, 0),
225];
226
227/// The language rule is independent of the lexical binding named object.
228pub fn implicit_base(version: Option<PythonVersion>) -> Option<&'static Builtin> {
229    let object = BUILTINS
230        .iter()
231        .find(|row| row.name == "object")
232        .expect("object is a Python language root");
233    (object.versions.availability(version) == Availability::Present).then_some(object)
234}
235pub fn builtin(name: &str) -> Option<&'static Builtin> {
236    BUILTINS.iter().find(|row| row.name == name)
237}
238
239#[derive(Clone, Copy, Debug)]
240pub struct ObjectMember {
241    pub name: &'static str,
242    pub versions: VersionRange,
243}
244macro_rules! member {
245    ($name:literal, $minor:literal) => {
246        ObjectMember {
247            name: $name,
248            versions: VersionRange {
249                since: PythonVersion::new(3, $minor),
250            },
251        }
252    };
253}
254/// Complete for the audited version interval, including descriptors, not just methods.
255pub const OBJECT_MEMBERS: &[ObjectMember] = &[
256    member!("__class__", 0),
257    member!("__delattr__", 0),
258    member!("__dir__", 3),
259    member!("__doc__", 0),
260    member!("__eq__", 0),
261    member!("__format__", 0),
262    member!("__ge__", 0),
263    member!("__getattribute__", 0),
264    member!("__getstate__", 11),
265    member!("__gt__", 0),
266    member!("__hash__", 0),
267    member!("__init__", 0),
268    member!("__init_subclass__", 6),
269    member!("__le__", 0),
270    member!("__lt__", 0),
271    member!("__ne__", 0),
272    member!("__new__", 0),
273    member!("__reduce__", 0),
274    member!("__reduce_ex__", 0),
275    member!("__repr__", 0),
276    member!("__setattr__", 0),
277    member!("__sizeof__", 0),
278    member!("__str__", 0),
279    member!("__subclasshook__", 0),
280];
281
282pub fn object_member(name: &str, version: Option<PythonVersion>) -> Availability {
283    match OBJECT_MEMBERS.iter().find(|row| row.name == name) {
284        Some(row) => row.versions.availability(version),
285        None if version.is_none_or(|v| (FIRST..END).contains(&v)) => Availability::Absent,
286        None => Availability::Unknown,
287    }
288}
289
290/// Existing Python value-class policy shared by legacy and native member
291/// lookup. Their intrinsic dictionaries cannot acquire user-defined members;
292/// rebinding a name in the mutable builtins module is a separate operation.
293pub fn has_fixed_value_class_surface(qualified_name: &str) -> bool {
294    matches!(
295        qualified_name,
296        "types.NoneType"
297            | "builtins.bool"
298            | "builtins.int"
299            | "builtins.float"
300            | "builtins.str"
301            | "builtins.bytes"
302            | "builtins.list"
303            | "builtins.tuple"
304            | "builtins.dict"
305            | "builtins.set"
306            | "builtins.frozenset"
307            | "builtins.object"
308    )
309}