Skip to main content

rustpython_vm/stdlib/
builtins.rs

1//! Builtin function definitions.
2//!
3//! Implements the list of [builtin Python functions](https://docs.python.org/3/library/builtins.html).
4use crate::{Py, VirtualMachine, builtins::PyModule, class::PyClassImpl};
5pub(crate) use builtins::{DOC, module_def};
6pub use builtins::{ascii, print, reversed};
7
8#[pymodule]
9mod builtins {
10    use crate::{
11        AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
12        builtins::{
13            PyByteArray, PyBytes, PyDictRef, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType,
14            PyUtf8StrRef,
15            enumerate::PyReverseSequenceIterator,
16            function::{PyCellRef, PyFunction},
17            int::PyIntRef,
18            iter::PyCallableIterator,
19            list::{PyList, SortOptions},
20        },
21        common::hash::PyHash,
22        function::{
23            ArgBytesLike, ArgCallable, ArgIndex, ArgIntoBool, ArgIterable, ArgMapping,
24            ArgStrOrBytesLike, Either, FsPath, FuncArgs, KwArgs, OptionalArg, OptionalOption,
25            PosArgs,
26        },
27        protocol::{PyIter, PyIterReturn},
28        py_io,
29        readline::{Readline, ReadlineResult},
30        stdlib::sys,
31        types::PyComparisonOp,
32    };
33    use itertools::Itertools;
34    use num_traits::{Signed, ToPrimitive, Zero};
35    use rustpython_common::wtf8::CodePoint;
36
37    #[cfg(not(feature = "rustpython-compiler"))]
38    const CODEGEN_NOT_SUPPORTED: &str =
39        "can't compile() to bytecode when the `codegen` feature of rustpython is disabled";
40
41    #[pyfunction]
42    fn abs(x: PyObjectRef, vm: &VirtualMachine) -> PyResult {
43        vm._abs(&x)
44    }
45
46    #[pyfunction]
47    fn all(iterable: ArgIterable<ArgIntoBool>, vm: &VirtualMachine) -> PyResult<bool> {
48        for item in iterable.iter(vm)? {
49            if !item?.into_bool() {
50                return Ok(false);
51            }
52        }
53        Ok(true)
54    }
55
56    #[pyfunction]
57    fn any(iterable: ArgIterable<ArgIntoBool>, vm: &VirtualMachine) -> PyResult<bool> {
58        for item in iterable.iter(vm)? {
59            if item?.into_bool() {
60                return Ok(true);
61            }
62        }
63        Ok(false)
64    }
65
66    #[pyfunction]
67    pub fn ascii(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyStrRef> {
68        obj.ascii(vm)
69    }
70
71    #[pyfunction]
72    fn bin(x: PyIntRef) -> String {
73        let x = x.as_bigint();
74        if x.is_negative() {
75            format!("-0b{:b}", x.abs())
76        } else {
77            format!("0b{x:b}")
78        }
79    }
80
81    #[pyfunction]
82    fn callable(obj: PyObjectRef) -> bool {
83        obj.is_callable()
84    }
85
86    #[pyfunction]
87    fn chr(i: PyIntRef, vm: &VirtualMachine) -> PyResult<CodePoint> {
88        let value = i
89            .as_bigint()
90            .to_u32()
91            .and_then(CodePoint::from_u32)
92            .ok_or_else(|| vm.new_value_error("chr() arg not in range(0x110000)"))?;
93        Ok(value)
94    }
95
96    #[derive(FromArgs)]
97    #[allow(dead_code)]
98    struct CompileArgs {
99        source: PyObjectRef,
100        filename: FsPath,
101        mode: PyUtf8StrRef,
102        #[pyarg(any, optional)]
103        flags: OptionalArg<PyIntRef>,
104        #[pyarg(any, optional)]
105        dont_inherit: OptionalArg<bool>,
106        #[pyarg(any, optional)]
107        optimize: OptionalArg<PyIntRef>,
108        #[pyarg(any, optional)]
109        _feature_version: OptionalArg<i32>,
110    }
111
112    /// Detect PEP 263 encoding cookie from source bytes.
113    /// Checks first two lines for `# coding[:=] <encoding>` pattern.
114    /// Returns the encoding name if found, or None for default (UTF-8).
115    #[cfg(feature = "parser")]
116    fn detect_source_encoding(source: &[u8]) -> Option<String> {
117        fn find_encoding_in_line(line: &[u8]) -> Option<String> {
118            // PEP 263: '#' must be preceded only by whitespace/formfeed
119            let hash_pos = line.iter().position(|&b| b == b'#')?;
120            if !line[..hash_pos]
121                .iter()
122                .all(|&b| b == b' ' || b == b'\t' || b == b'\x0c' || b == b'\r')
123            {
124                return None;
125            }
126            let after_hash = &line[hash_pos..];
127
128            // Find "coding" after the #
129            let coding_pos = after_hash.windows(6).position(|w| w == b"coding")?;
130            let after_coding = &after_hash[coding_pos + 6..];
131
132            // Next char must be ':' or '='
133            let rest = if after_coding.first() == Some(&b':') || after_coding.first() == Some(&b'=')
134            {
135                &after_coding[1..]
136            } else {
137                return None;
138            };
139
140            // Skip whitespace
141            let rest = rest
142                .iter()
143                .copied()
144                .skip_while(|&b| b == b' ' || b == b'\t')
145                .collect::<Vec<_>>();
146
147            // Read encoding name: [-\w.]+
148            let name: String = rest
149                .iter()
150                .take_while(|&&b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_' || b == b'.')
151                .map(|&b| b as char)
152                .collect();
153
154            if name.is_empty() { None } else { Some(name) }
155        }
156
157        // Split into lines (first two only)
158        let mut lines = source.splitn(3, |&b| b == b'\n');
159
160        if let Some(first) = lines.next() {
161            // Strip BOM if present
162            let first = first.strip_prefix(b"\xef\xbb\xbf").unwrap_or(first);
163            if let Some(enc) = find_encoding_in_line(first) {
164                return Some(enc);
165            }
166            // Only check second line if first line is blank or a comment
167            let trimmed = first
168                .iter()
169                .skip_while(|&&b| b == b' ' || b == b'\t' || b == b'\x0c' || b == b'\r')
170                .copied()
171                .collect::<Vec<_>>();
172            if !trimmed.is_empty() && trimmed[0] != b'#' {
173                return None;
174            }
175        }
176
177        lines.next().and_then(find_encoding_in_line)
178    }
179
180    /// Decode source bytes to a string, handling PEP 263 encoding declarations
181    /// and BOM. Raises SyntaxError for invalid UTF-8 without an encoding
182    /// declaration.
183    /// Check if an encoding name is a UTF-8 variant after normalization.
184    /// Matches: utf-8, utf_8, utf8, UTF-8, etc.
185    #[cfg(feature = "parser")]
186    fn is_utf8_encoding(name: &str) -> bool {
187        let normalized: String = name.chars().filter(|&c| c != '-' && c != '_').collect();
188        normalized.eq_ignore_ascii_case("utf8")
189    }
190
191    #[cfg(feature = "parser")]
192    fn decode_source_bytes(source: &[u8], filename: &str, vm: &VirtualMachine) -> PyResult<String> {
193        let has_bom = source.starts_with(b"\xef\xbb\xbf");
194        let encoding = detect_source_encoding(source);
195
196        let is_utf8 = encoding.as_deref().is_none_or(is_utf8_encoding);
197
198        // Validate BOM + encoding combination
199        if has_bom && !is_utf8 {
200            return Err(vm.new_exception_msg(
201                vm.ctx.exceptions.syntax_error.to_owned(),
202                format!("encoding problem for '{filename}': utf-8").into(),
203            ));
204        }
205
206        if is_utf8 {
207            let src = if has_bom { &source[3..] } else { source };
208            match core::str::from_utf8(src) {
209                Ok(s) => Ok(s.to_owned()),
210                Err(e) => {
211                    let bad_byte = src[e.valid_up_to()];
212                    let line = src[..e.valid_up_to()]
213                        .iter()
214                        .filter(|&&b| b == b'\n')
215                        .count()
216                        + 1;
217                    Err(vm.new_exception_msg(
218                        vm.ctx.exceptions.syntax_error.to_owned(),
219                        format!(
220                            "Non-UTF-8 code starting with '\\x{bad_byte:02x}' \
221                             on line {line}, but no encoding declared; \
222                             see https://peps.python.org/pep-0263/ for details \
223                             ({filename}, line {line})"
224                        )
225                        .into(),
226                    ))
227                }
228            }
229        } else {
230            // Use codec registry for non-UTF-8 encodings
231            let enc = encoding.as_deref().unwrap();
232            let bytes_obj = vm.ctx.new_bytes(source.to_vec());
233            let decoded = vm
234                .state
235                .codec_registry
236                .decode_text(bytes_obj.into(), enc, None, vm)
237                .map_err(|exc| {
238                    if exc.fast_isinstance(vm.ctx.exceptions.lookup_error) {
239                        vm.new_exception_msg(
240                            vm.ctx.exceptions.syntax_error.to_owned(),
241                            format!("unknown encoding for '{filename}': {enc}").into(),
242                        )
243                    } else {
244                        exc
245                    }
246                })?;
247            Ok(decoded.to_string_lossy().into_owned())
248        }
249    }
250
251    #[cfg(any(feature = "parser", feature = "compiler"))]
252    #[pyfunction]
253    fn compile(args: CompileArgs, vm: &VirtualMachine) -> PyResult {
254        #[cfg(not(feature = "ast"))]
255        {
256            _ = args; // to disable unused warning
257            return Err(vm.new_type_error("AST Not Supported"));
258        }
259        #[cfg(feature = "ast")]
260        {
261            use crate::{class::PyClassImpl, stdlib::_ast};
262
263            let feature_version = feature_version_from_arg(args._feature_version, vm)?;
264
265            let mode_str = args.mode.as_str();
266
267            let optimize: i32 = args.optimize.map_or(Ok(-1), |v| v.try_to_primitive(vm))?;
268            let optimize: u8 = if optimize == -1 {
269                vm.state.config.settings.optimize
270            } else {
271                optimize
272                    .try_into()
273                    .map_err(|_| vm.new_value_error("compile() optimize value invalid"))?
274            };
275
276            if args
277                .source
278                .fast_isinstance(&_ast::NodeAst::make_static_type())
279            {
280                let flags: i32 = args.flags.map_or(Ok(0), |v| v.try_to_primitive(vm))?;
281                let is_ast_only = !(flags & _ast::PY_CF_ONLY_AST).is_zero();
282
283                // func_type mode requires PyCF_ONLY_AST
284                if mode_str == "func_type" && !is_ast_only {
285                    return Err(vm.new_value_error(
286                        "compile() mode 'func_type' requires flag PyCF_ONLY_AST",
287                    ));
288                }
289
290                // compile(ast_node, ..., PyCF_ONLY_AST) returns the AST after validation
291                if is_ast_only {
292                    let (expected_type, expected_name) = _ast::mode_type_and_name(mode_str)
293                        .ok_or_else(|| {
294                            vm.new_value_error(
295                                "compile() mode must be 'exec', 'eval', 'single' or 'func_type'",
296                            )
297                        })?;
298                    if !args.source.fast_isinstance(&expected_type) {
299                        return Err(vm.new_type_error(format!(
300                            "expected {} node, got {}",
301                            expected_name,
302                            args.source.class().name()
303                        )));
304                    }
305                    _ast::validate_ast_object(vm, args.source.clone())?;
306                    return Ok(args.source);
307                }
308
309                #[cfg(not(feature = "rustpython-codegen"))]
310                {
311                    return Err(vm.new_type_error(CODEGEN_NOT_SUPPORTED.to_owned()));
312                }
313                #[cfg(feature = "rustpython-codegen")]
314                {
315                    let mode = mode_str
316                        .parse::<crate::compiler::Mode>()
317                        .map_err(|err| vm.new_value_error(err.to_string()))?;
318                    return _ast::compile(
319                        vm,
320                        args.source,
321                        &args.filename.to_string_lossy(),
322                        mode,
323                        Some(optimize),
324                    );
325                }
326            }
327
328            #[cfg(not(feature = "parser"))]
329            {
330                const PARSER_NOT_SUPPORTED: &str = "can't compile() source code when the `parser` feature of rustpython is disabled";
331                Err(vm.new_type_error(PARSER_NOT_SUPPORTED.to_owned()))
332            }
333            #[cfg(feature = "parser")]
334            {
335                use crate::convert::ToPyException;
336
337                use ruff_python_parser as parser;
338
339                let source = ArgStrOrBytesLike::try_from_object(vm, args.source)?;
340                let source = source.borrow_bytes();
341
342                let source = decode_source_bytes(&source, &args.filename.to_string_lossy(), vm)?;
343                let source = source.as_str();
344
345                let flags = args.flags.map_or(Ok(0), |v| v.try_to_primitive(vm))?;
346
347                if !(flags & !_ast::PY_COMPILE_FLAGS_MASK).is_zero() {
348                    return Err(vm.new_value_error("compile() unrecognized flags"));
349                }
350
351                let allow_incomplete = !(flags & _ast::PY_CF_ALLOW_INCOMPLETE_INPUT).is_zero();
352                let type_comments = !(flags & _ast::PY_CF_TYPE_COMMENTS).is_zero();
353
354                let optimize_level = optimize;
355
356                if (flags & _ast::PY_CF_ONLY_AST).is_zero() {
357                    #[cfg(not(feature = "compiler"))]
358                    {
359                        Err(vm.new_value_error(CODEGEN_NOT_SUPPORTED.to_owned()))
360                    }
361                    #[cfg(feature = "compiler")]
362                    {
363                        if let Some(feature_version) = feature_version {
364                            let mode = mode_str
365                                .parse::<parser::Mode>()
366                                .map_err(|err| vm.new_value_error(err.to_string()))?;
367                            let _ = _ast::parse(
368                                vm,
369                                source,
370                                mode,
371                                optimize_level,
372                                Some(feature_version),
373                                type_comments,
374                            )
375                            .map_err(|e| (e, Some(source), allow_incomplete).to_pyexception(vm))?;
376                        }
377
378                        let mode = mode_str
379                            .parse::<crate::compiler::Mode>()
380                            .map_err(|err| vm.new_value_error(err.to_string()))?;
381
382                        let mut opts = vm.compile_opts();
383                        opts.optimize = optimize;
384
385                        let code = vm
386                            .compile_with_opts(
387                                source,
388                                mode,
389                                args.filename.to_string_lossy().into_owned(),
390                                opts,
391                            )
392                            .map_err(|err| {
393                                (err, Some(source), allow_incomplete).to_pyexception(vm)
394                            })?;
395                        Ok(code.into())
396                    }
397                } else {
398                    if mode_str == "func_type" {
399                        return _ast::parse_func_type(vm, source, optimize_level, feature_version)
400                            .map_err(|e| (e, Some(source), allow_incomplete).to_pyexception(vm));
401                    }
402
403                    let mode = mode_str
404                        .parse::<parser::Mode>()
405                        .map_err(|err| vm.new_value_error(err.to_string()))?;
406                    let parsed = _ast::parse(
407                        vm,
408                        source,
409                        mode,
410                        optimize_level,
411                        feature_version,
412                        type_comments,
413                    )
414                    .map_err(|e| (e, Some(source), allow_incomplete).to_pyexception(vm))?;
415
416                    if mode_str == "single" {
417                        return _ast::wrap_interactive(vm, parsed);
418                    }
419
420                    Ok(parsed)
421                }
422            }
423        }
424    }
425
426    #[cfg(feature = "ast")]
427    fn feature_version_from_arg(
428        feature_version: OptionalArg<i32>,
429        vm: &VirtualMachine,
430    ) -> PyResult<Option<ruff_python_ast::PythonVersion>> {
431        let minor = match feature_version.into_option() {
432            Some(minor) => minor,
433            None => return Ok(None),
434        };
435
436        if minor < 0 {
437            return Ok(None);
438        }
439
440        let minor = u8::try_from(minor)
441            .map_err(|_| vm.new_value_error("compile() _feature_version out of range"))?;
442        Ok(Some(ruff_python_ast::PythonVersion { major: 3, minor }))
443    }
444
445    #[pyfunction]
446    fn delattr(obj: PyObjectRef, attr: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
447        let attr = attr.try_to_ref::<PyStr>(vm).map_err(|_e| {
448            vm.new_type_error(format!(
449                "attribute name must be string, not '{}'",
450                attr.class().name()
451            ))
452        })?;
453        obj.del_attr(attr, vm)
454    }
455
456    #[pyfunction]
457    fn dir(obj: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<PyList> {
458        vm.dir(obj.into_option())
459    }
460
461    #[pyfunction]
462    fn divmod(a: PyObjectRef, b: PyObjectRef, vm: &VirtualMachine) -> PyResult {
463        vm._divmod(&a, &b)
464    }
465
466    #[derive(FromArgs)]
467    struct ScopeArgs {
468        #[pyarg(any, default)]
469        globals: Option<PyObjectRef>,
470        #[pyarg(any, default)]
471        locals: Option<ArgMapping>,
472    }
473
474    impl ScopeArgs {
475        fn make_scope(
476            self,
477            vm: &VirtualMachine,
478            func_name: &'static str,
479        ) -> PyResult<crate::scope::Scope> {
480            fn validate_globals_dict(
481                globals: &PyObject,
482                vm: &VirtualMachine,
483                func_name: &'static str,
484            ) -> PyResult<()> {
485                if !globals.fast_isinstance(vm.ctx.types.dict_type) {
486                    return Err(match func_name {
487                        "eval" => {
488                            let is_mapping = globals.mapping_unchecked().check();
489                            vm.new_type_error(if is_mapping {
490                                "globals must be a real dict; try eval(expr, {}, mapping)"
491                                    .to_owned()
492                            } else {
493                                "globals must be a dict".to_owned()
494                            })
495                        }
496                        "exec" => vm.new_type_error(format!(
497                            "exec() globals must be a dict, not {}",
498                            globals.class().name()
499                        )),
500                        _ => vm.new_type_error("globals must be a dict"),
501                    });
502                }
503                Ok(())
504            }
505
506            let (globals, locals) = match self.globals {
507                Some(globals) => {
508                    validate_globals_dict(&globals, vm, func_name)?;
509
510                    let globals = PyDictRef::try_from_object(vm, globals)?;
511                    if !globals.contains_key(identifier!(vm, __builtins__), vm) {
512                        let builtins_dict = vm.builtins.dict().into();
513                        globals.set_item(identifier!(vm, __builtins__), builtins_dict, vm)?;
514                    }
515                    (
516                        globals.clone(),
517                        self.locals
518                            .unwrap_or_else(|| ArgMapping::from_dict_exact(globals.clone())),
519                    )
520                }
521                None => (
522                    vm.current_globals(),
523                    if let Some(locals) = self.locals {
524                        locals
525                    } else {
526                        vm.current_locals()?
527                    },
528                ),
529            };
530
531            let scope = crate::scope::Scope::with_builtins(Some(locals), globals, vm);
532            Ok(scope)
533        }
534    }
535
536    #[pyfunction]
537    fn eval(
538        source: Either<ArgStrOrBytesLike, PyRef<crate::builtins::PyCode>>,
539        scope: ScopeArgs,
540        vm: &VirtualMachine,
541    ) -> PyResult {
542        let scope = scope.make_scope(vm, "eval")?;
543
544        // source as string
545        let code = match source {
546            Either::A(either) => {
547                let source: &[u8] = &either.borrow_bytes();
548                if source.contains(&0) {
549                    return Err(vm.new_exception_msg(
550                        vm.ctx.exceptions.syntax_error.to_owned(),
551                        "source code string cannot contain null bytes".into(),
552                    ));
553                }
554
555                let source = core::str::from_utf8(source).map_err(|err| {
556                    let msg = format!(
557                        "(unicode error) 'utf-8' codec can't decode byte 0x{:x?} in position {}: invalid start byte",
558                        source[err.valid_up_to()],
559                        err.valid_up_to()
560                    );
561
562                    vm.new_exception_msg(vm.ctx.exceptions.syntax_error.to_owned(), msg.into())
563                })?;
564                Ok(Either::A(vm.ctx.new_utf8_str(source.trim_start())))
565            }
566            Either::B(code) => Ok(Either::B(code)),
567        }?;
568        run_code(vm, code, scope, crate::compiler::Mode::Eval, "eval")
569    }
570
571    #[pyfunction]
572    fn exec(
573        source: Either<PyUtf8StrRef, PyRef<crate::builtins::PyCode>>,
574        scope: ScopeArgs,
575        vm: &VirtualMachine,
576    ) -> PyResult {
577        let scope = scope.make_scope(vm, "exec")?;
578        run_code(vm, source, scope, crate::compiler::Mode::Exec, "exec")
579    }
580
581    fn run_code(
582        vm: &VirtualMachine,
583        source: Either<PyUtf8StrRef, PyRef<crate::builtins::PyCode>>,
584        scope: crate::scope::Scope,
585        #[allow(unused_variables)] mode: crate::compiler::Mode,
586        func: &str,
587    ) -> PyResult {
588        // Determine code object:
589        let code_obj = match source {
590            #[cfg(feature = "rustpython-compiler")]
591            Either::A(string) => {
592                let source = string.as_str();
593                vm.compile(source, mode, "<string>".to_owned())
594                    .map_err(|err| vm.new_syntax_error(&err, Some(source)))?
595            }
596            #[cfg(not(feature = "rustpython-compiler"))]
597            Either::A(_) => return Err(vm.new_type_error(CODEGEN_NOT_SUPPORTED.to_owned())),
598            Either::B(code_obj) => code_obj,
599        };
600
601        if !code_obj.freevars.is_empty() {
602            return Err(vm.new_type_error(format!(
603                "code object passed to {func}() may not contain free variables"
604            )));
605        }
606
607        // Run the code:
608        vm.run_code_obj(code_obj, scope)
609    }
610
611    #[pyfunction]
612    fn format(
613        value: PyObjectRef,
614        format_spec: OptionalArg<PyStrRef>,
615        vm: &VirtualMachine,
616    ) -> PyResult<PyStrRef> {
617        vm.format(&value, format_spec.unwrap_or(vm.ctx.new_str("")))
618    }
619
620    #[pyfunction]
621    fn getattr(
622        obj: PyObjectRef,
623        attr: PyObjectRef,
624        default: OptionalArg<PyObjectRef>,
625        vm: &VirtualMachine,
626    ) -> PyResult {
627        let attr = attr.try_to_ref::<PyStr>(vm).map_err(|_e| {
628            vm.new_type_error(format!(
629                "attribute name must be string, not '{}'",
630                attr.class().name()
631            ))
632        })?;
633
634        if let OptionalArg::Present(default) = default {
635            Ok(vm.get_attribute_opt(obj, attr)?.unwrap_or(default))
636        } else {
637            obj.get_attr(attr, vm)
638        }
639    }
640
641    #[pyfunction]
642    fn globals(vm: &VirtualMachine) -> PyDictRef {
643        vm.current_globals()
644    }
645
646    #[pyfunction]
647    fn hasattr(obj: PyObjectRef, attr: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
648        let attr = attr.try_to_ref::<PyStr>(vm).map_err(|_e| {
649            vm.new_type_error(format!(
650                "attribute name must be string, not '{}'",
651                attr.class().name()
652            ))
653        })?;
654        Ok(vm.get_attribute_opt(obj, attr)?.is_some())
655    }
656
657    #[pyfunction]
658    fn hash(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyHash> {
659        obj.hash(vm)
660    }
661
662    #[pyfunction]
663    fn breakpoint(args: FuncArgs, vm: &VirtualMachine) -> PyResult {
664        match vm
665            .sys_module
666            .get_attr(vm.ctx.intern_str("breakpointhook"), vm)
667        {
668            Ok(hook) => hook.as_ref().call(args, vm),
669            Err(_) => Err(vm.new_runtime_error("lost sys.breakpointhook")),
670        }
671    }
672
673    #[pyfunction]
674    fn hex(number: ArgIndex) -> String {
675        let number = number.into_int_ref();
676        let n = number.as_bigint();
677        format!("{n:#x}")
678    }
679
680    #[pyfunction]
681    fn id(obj: PyObjectRef) -> usize {
682        obj.get_id()
683    }
684
685    #[pyfunction]
686    fn input(prompt: OptionalArg<PyStrRef>, vm: &VirtualMachine) -> PyResult {
687        use std::io::IsTerminal;
688
689        let stdin = sys::get_stdin(vm)?;
690        let stdout = sys::get_stdout(vm)?;
691        let stderr = sys::get_stderr(vm)?;
692
693        let _ = vm.call_method(&stderr, "flush", ());
694
695        let fd_matches = |obj, expected| {
696            vm.call_method(obj, "fileno", ())
697                .and_then(|o| i64::try_from_object(vm, o))
698                .is_ok_and(|fd| fd == expected)
699        };
700
701        // Check if we should use rustyline (interactive terminal, not PTY child)
702        let use_rustyline = fd_matches(&stdin, 0)
703            && fd_matches(&stdout, 1)
704            && std::io::stdin().is_terminal()
705            && !is_pty_child();
706
707        // Disable rustyline if prompt contains surrogates (not valid UTF-8 for terminal)
708        let prompt_str = match &prompt {
709            OptionalArg::Present(s) => s.to_str(),
710            OptionalArg::Missing => Some(""),
711        };
712        let use_rustyline = use_rustyline && prompt_str.is_some();
713
714        if use_rustyline {
715            let prompt = prompt_str.unwrap();
716            let mut readline = Readline::new(());
717            match readline.readline(prompt) {
718                ReadlineResult::Line(s) => Ok(vm.ctx.new_str(s).into()),
719                ReadlineResult::Eof => {
720                    Err(vm.new_exception_empty(vm.ctx.exceptions.eof_error.to_owned()))
721                }
722                ReadlineResult::Interrupt => {
723                    Err(vm.new_exception_empty(vm.ctx.exceptions.keyboard_interrupt.to_owned()))
724                }
725                ReadlineResult::Io(e) => Err(vm.new_os_error(e.to_string())),
726                #[cfg(unix)]
727                ReadlineResult::OsError(num) => Err(vm.new_os_error(num.to_string())),
728                ReadlineResult::Other(e) => Err(vm.new_runtime_error(e.to_string())),
729            }
730        } else {
731            if let OptionalArg::Present(prompt) = prompt {
732                vm.call_method(&stdout, "write", (prompt,))?;
733            }
734            let _ = vm.call_method(&stdout, "flush", ());
735            py_io::file_readline(&stdin, None, vm)
736        }
737    }
738
739    /// Check if we're running in a PTY child process (e.g., after pty.fork()).
740    /// pty.fork() calls setsid(), making the child a session leader.
741    /// In this case, rustyline may hang because it uses raw mode.
742    #[cfg(unix)]
743    fn is_pty_child() -> bool {
744        use nix::unistd::{getpid, getsid};
745        // If this process is a session leader, we're likely in a PTY child
746        getsid(None) == Ok(getpid())
747    }
748
749    #[cfg(not(unix))]
750    fn is_pty_child() -> bool {
751        false
752    }
753
754    #[pyfunction]
755    fn isinstance(obj: PyObjectRef, typ: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
756        obj.is_instance(&typ, vm)
757    }
758
759    #[pyfunction]
760    fn issubclass(subclass: PyObjectRef, typ: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
761        subclass.is_subclass(&typ, vm)
762    }
763
764    #[pyfunction]
765    fn iter(
766        iter_target: PyObjectRef,
767        sentinel: OptionalArg<PyObjectRef>,
768        vm: &VirtualMachine,
769    ) -> PyResult<PyIter> {
770        if let OptionalArg::Present(sentinel) = sentinel {
771            let callable = ArgCallable::try_from_object(vm, iter_target)?;
772            let iterator = PyCallableIterator::new(callable, sentinel)
773                .into_ref(&vm.ctx)
774                .into();
775            Ok(PyIter::new(iterator))
776        } else {
777            iter_target.get_iter(vm)
778        }
779    }
780
781    #[pyfunction]
782    fn aiter(iter_target: PyObjectRef, vm: &VirtualMachine) -> PyResult {
783        iter_target.get_aiter(vm)
784    }
785
786    #[pyfunction]
787    fn anext(
788        aiter: PyObjectRef,
789        default_value: OptionalArg<PyObjectRef>,
790        vm: &VirtualMachine,
791    ) -> PyResult {
792        use crate::builtins::asyncgenerator::PyAnextAwaitable;
793
794        // Check if object is an async iterator (has __anext__ method)
795        if !aiter.class().has_attr(identifier!(vm, __anext__)) {
796            return Err(vm.new_type_error(format!(
797                "'{}' object is not an async iterator",
798                aiter.class().name()
799            )));
800        }
801
802        let awaitable = vm.call_method(&aiter, "__anext__", ())?;
803
804        if let OptionalArg::Present(default) = default_value {
805            Ok(PyAnextAwaitable::new(awaitable, default)
806                .into_ref(&vm.ctx)
807                .into())
808        } else {
809            Ok(awaitable)
810        }
811    }
812
813    #[pyfunction]
814    fn len(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
815        obj.length(vm)
816    }
817
818    #[pyfunction]
819    fn locals(vm: &VirtualMachine) -> PyResult<ArgMapping> {
820        vm.current_locals()
821    }
822
823    fn min_or_max(
824        mut args: FuncArgs,
825        vm: &VirtualMachine,
826        func_name: &str,
827        op: PyComparisonOp,
828    ) -> PyResult {
829        let default = args.take_keyword("default");
830        let key_func = args.take_keyword("key");
831
832        if let Some(err) = args.check_kwargs_empty(vm) {
833            return Err(err);
834        }
835
836        let candidates = match args.args.len().cmp(&1) {
837            core::cmp::Ordering::Greater => {
838                if default.is_some() {
839                    return Err(vm.new_type_error(format!(
840                        "Cannot specify a default for {func_name}() with multiple positional arguments"
841                    )));
842                }
843                args.args
844            }
845            core::cmp::Ordering::Equal => args.args[0].try_to_value(vm)?,
846            core::cmp::Ordering::Less => {
847                // zero arguments means type error:
848                return Err(
849                    vm.new_type_error(format!("{func_name} expected at least 1 argument, got 0"))
850                );
851            }
852        };
853
854        let mut candidates_iter = candidates.into_iter();
855        let mut x = match candidates_iter.next() {
856            Some(x) => x,
857            None => {
858                return default.ok_or_else(|| {
859                    vm.new_value_error(format!("{func_name}() iterable argument is empty"))
860                });
861            }
862        };
863
864        let key_func = key_func.filter(|f| !vm.is_none(f));
865        if let Some(ref key_func) = key_func {
866            let mut x_key = key_func.call((x.clone(),), vm)?;
867            for y in candidates_iter {
868                let y_key = key_func.call((y.clone(),), vm)?;
869                if y_key.rich_compare_bool(&x_key, op, vm)? {
870                    x = y;
871                    x_key = y_key;
872                }
873            }
874        } else {
875            for y in candidates_iter {
876                if y.rich_compare_bool(&x, op, vm)? {
877                    x = y;
878                }
879            }
880        }
881
882        Ok(x)
883    }
884
885    #[pyfunction]
886    fn max(args: FuncArgs, vm: &VirtualMachine) -> PyResult {
887        min_or_max(args, vm, "max", PyComparisonOp::Gt)
888    }
889
890    #[pyfunction]
891    fn min(args: FuncArgs, vm: &VirtualMachine) -> PyResult {
892        min_or_max(args, vm, "min", PyComparisonOp::Lt)
893    }
894
895    #[pyfunction]
896    fn next(
897        iterator: PyObjectRef,
898        default_value: OptionalArg<PyObjectRef>,
899        vm: &VirtualMachine,
900    ) -> PyResult<PyIterReturn> {
901        if !PyIter::check(&iterator) {
902            return Err(vm.new_type_error(format!(
903                "{} object is not an iterator",
904                iterator.class().name()
905            )));
906        }
907        PyIter::new(iterator)
908            .next(vm)
909            .map(|iter_ret| match iter_ret {
910                PyIterReturn::Return(obj) => PyIterReturn::Return(obj),
911                PyIterReturn::StopIteration(v) => {
912                    default_value.map_or(PyIterReturn::StopIteration(v), PyIterReturn::Return)
913                }
914            })
915    }
916
917    #[pyfunction]
918    fn oct(number: ArgIndex, vm: &VirtualMachine) -> PyResult {
919        let number = number.into_int_ref();
920        let n = number.as_bigint();
921        let s = if n.is_negative() {
922            format!("-0o{:o}", n.abs())
923        } else {
924            format!("0o{n:o}")
925        };
926
927        Ok(vm.ctx.new_str(s).into())
928    }
929
930    #[pyfunction]
931    fn ord(string: Either<ArgBytesLike, PyStrRef>, vm: &VirtualMachine) -> PyResult<u32> {
932        match string {
933            Either::A(bytes) => bytes.with_ref(|bytes| {
934                let bytes_len = bytes.len();
935                if bytes_len != 1 {
936                    return Err(vm.new_type_error(format!(
937                        "ord() expected a character, but string of length {bytes_len} found"
938                    )));
939                }
940                Ok(u32::from(bytes[0]))
941            }),
942            Either::B(string) => match string.as_wtf8().code_points().exactly_one() {
943                Ok(character) => Ok(character.to_u32()),
944                Err(_) => {
945                    let string_len = string.char_len();
946                    Err(vm.new_type_error(format!(
947                        "ord() expected a character, but string of length {string_len} found"
948                    )))
949                }
950            },
951        }
952    }
953
954    #[derive(FromArgs)]
955    struct PowArgs {
956        base: PyObjectRef,
957        exp: PyObjectRef,
958        #[pyarg(any, optional, name = "mod")]
959        modulus: Option<PyObjectRef>,
960    }
961
962    #[pyfunction]
963    fn pow(args: PowArgs, vm: &VirtualMachine) -> PyResult {
964        let PowArgs {
965            base: x,
966            exp: y,
967            modulus,
968        } = args;
969        let modulus = modulus.as_ref().map_or(vm.ctx.none.as_object(), |m| m);
970        vm._pow(&x, &y, modulus)
971    }
972
973    #[pyfunction]
974    pub fn exit(exit_code_arg: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
975        let code = exit_code_arg.unwrap_or_else(|| vm.ctx.new_int(0).into());
976        Err(vm.new_exception(vm.ctx.exceptions.system_exit.to_owned(), vec![code]))
977    }
978
979    #[derive(Debug, Default, FromArgs)]
980    pub struct PrintOptions {
981        #[pyarg(named, default)]
982        sep: Option<PyStrRef>,
983        #[pyarg(named, default)]
984        end: Option<PyStrRef>,
985        #[pyarg(named, default = ArgIntoBool::FALSE)]
986        flush: ArgIntoBool,
987        #[pyarg(named, default)]
988        file: Option<PyObjectRef>,
989    }
990
991    #[pyfunction]
992    pub fn print(objects: PosArgs, options: PrintOptions, vm: &VirtualMachine) -> PyResult<()> {
993        let file = match options.file {
994            Some(f) => f,
995            None => sys::get_stdout(vm)?,
996        };
997        let write = |obj: PyStrRef| vm.call_method(&file, "write", (obj,));
998
999        let sep = options.sep.unwrap_or_else(|| vm.ctx.new_str(" "));
1000
1001        let mut first = true;
1002        for object in objects {
1003            if first {
1004                first = false;
1005            } else {
1006                write(sep.clone())?;
1007            }
1008
1009            write(object.str(vm)?)?;
1010        }
1011
1012        let end = options.end.unwrap_or_else(|| vm.ctx.new_str("\n"));
1013        write(end)?;
1014
1015        if options.flush.into() {
1016            vm.call_method(&file, "flush", ())?;
1017        }
1018
1019        Ok(())
1020    }
1021
1022    #[pyfunction]
1023    fn repr(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyStrRef> {
1024        obj.repr(vm)
1025    }
1026
1027    #[pyfunction]
1028    pub fn reversed(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
1029        if let Some(reversed_method) = vm.get_method(obj.clone(), identifier!(vm, __reversed__)) {
1030            reversed_method?.call((), vm)
1031        } else {
1032            vm.get_method_or_type_error(obj.clone(), identifier!(vm, __getitem__), || {
1033                "argument to reversed() must be a sequence".to_owned()
1034            })?;
1035            let len = obj.length(vm)?;
1036            let obj_iterator = PyReverseSequenceIterator::new(obj, len);
1037            Ok(obj_iterator.into_pyobject(vm))
1038        }
1039    }
1040
1041    #[derive(FromArgs)]
1042    pub struct RoundArgs {
1043        number: PyObjectRef,
1044        #[pyarg(any, optional)]
1045        ndigits: OptionalOption<PyObjectRef>,
1046    }
1047
1048    #[pyfunction]
1049    fn round(RoundArgs { number, ndigits }: RoundArgs, vm: &VirtualMachine) -> PyResult {
1050        let meth = vm
1051            .get_special_method(&number, identifier!(vm, __round__))?
1052            .ok_or_else(|| {
1053                vm.new_type_error(format!(
1054                    "type {} doesn't define __round__",
1055                    number.class().name()
1056                ))
1057            })?;
1058        match ndigits.flatten() {
1059            Some(obj) => {
1060                let ndigits = obj.try_index(vm)?;
1061                meth.invoke((ndigits,), vm)
1062            }
1063            None => {
1064                // without a parameter, the result type is coerced to int
1065                meth.invoke((), vm)
1066            }
1067        }
1068    }
1069
1070    #[pyfunction]
1071    fn setattr(
1072        obj: PyObjectRef,
1073        attr: PyObjectRef,
1074        value: PyObjectRef,
1075        vm: &VirtualMachine,
1076    ) -> PyResult<()> {
1077        let attr = attr.try_to_ref::<PyStr>(vm).map_err(|_e| {
1078            vm.new_type_error(format!(
1079                "attribute name must be string, not '{}'",
1080                attr.class().name()
1081            ))
1082        })?;
1083        obj.set_attr(attr, value, vm)?;
1084        Ok(())
1085    }
1086
1087    // builtin_slice
1088
1089    #[pyfunction]
1090    fn sorted(iterable: PyObjectRef, opts: SortOptions, vm: &VirtualMachine) -> PyResult<PyList> {
1091        let items: Vec<_> = iterable.try_to_value(vm)?;
1092        let lst = PyList::from(items);
1093        lst.sort(opts, vm)?;
1094        Ok(lst)
1095    }
1096
1097    #[derive(FromArgs)]
1098    pub struct SumArgs {
1099        #[pyarg(positional)]
1100        iterable: ArgIterable,
1101        #[pyarg(any, optional)]
1102        start: OptionalArg<PyObjectRef>,
1103    }
1104
1105    #[pyfunction]
1106    fn sum(SumArgs { iterable, start }: SumArgs, vm: &VirtualMachine) -> PyResult {
1107        // Start with zero and add at will:
1108        let mut sum = start
1109            .into_option()
1110            .unwrap_or_else(|| vm.ctx.new_int(0).into());
1111
1112        match_class!(match sum {
1113            PyStr =>
1114                return Err(vm.new_type_error(
1115                    "sum() can't sum strings [use ''.join(seq) instead]".to_owned()
1116                )),
1117            PyBytes =>
1118                return Err(vm.new_type_error(
1119                    "sum() can't sum bytes [use b''.join(seq) instead]".to_owned()
1120                )),
1121            PyByteArray =>
1122                return Err(vm.new_type_error(
1123                    "sum() can't sum bytearray [use b''.join(seq) instead]".to_owned()
1124                )),
1125            _ => (),
1126        });
1127
1128        for item in iterable.iter(vm)? {
1129            sum = vm._add(&sum, &*item?)?;
1130        }
1131        Ok(sum)
1132    }
1133
1134    #[derive(FromArgs)]
1135    struct ImportArgs {
1136        #[pyarg(any)]
1137        name: PyStrRef,
1138        #[pyarg(any, default)]
1139        globals: Option<PyObjectRef>,
1140        #[allow(dead_code)]
1141        #[pyarg(any, default)]
1142        locals: Option<PyObjectRef>,
1143        #[pyarg(any, default)]
1144        fromlist: Option<PyObjectRef>,
1145        #[pyarg(any, default)]
1146        level: i32,
1147    }
1148
1149    #[pyfunction]
1150    fn __import__(args: ImportArgs, vm: &VirtualMachine) -> PyResult {
1151        crate::import::import_module_level(&args.name, args.globals, args.fromlist, args.level, vm)
1152    }
1153
1154    #[pyfunction]
1155    fn vars(obj: OptionalArg, vm: &VirtualMachine) -> PyResult {
1156        if let OptionalArg::Present(obj) = obj {
1157            obj.get_attr(identifier!(vm, __dict__), vm)
1158                .map_err(|_| vm.new_type_error("vars() argument must have __dict__ attribute"))
1159        } else {
1160            Ok(vm.current_locals()?.into())
1161        }
1162    }
1163
1164    #[pyfunction]
1165    pub fn __build_class__(
1166        function: PyRef<PyFunction>,
1167        name: PyStrRef,
1168        bases: PosArgs,
1169        mut kwargs: KwArgs,
1170        vm: &VirtualMachine,
1171    ) -> PyResult {
1172        let name_obj: PyObjectRef = name.clone().into();
1173
1174        // Update bases.
1175        let mut new_bases: Option<Vec<PyObjectRef>> = None;
1176        let bases = PyTuple::new_ref(bases.into_vec(), &vm.ctx);
1177        for (i, base) in bases.iter().enumerate() {
1178            if base.fast_isinstance(vm.ctx.types.type_type) {
1179                if let Some(bases) = &mut new_bases {
1180                    bases.push(base.clone());
1181                }
1182                continue;
1183            }
1184            let mro_entries =
1185                vm.get_attribute_opt(base.clone(), identifier!(vm, __mro_entries__))?;
1186            let entries = match mro_entries {
1187                Some(meth) => meth.call((bases.clone(),), vm)?,
1188                None => {
1189                    if let Some(bases) = &mut new_bases {
1190                        bases.push(base.clone());
1191                    }
1192                    continue;
1193                }
1194            };
1195            let entries: PyTupleRef = entries
1196                .downcast()
1197                .map_err(|_| vm.new_type_error("__mro_entries__ must return a tuple"))?;
1198            let new_bases = new_bases.get_or_insert_with(|| bases[..i].to_vec());
1199            new_bases.extend_from_slice(&entries);
1200        }
1201
1202        let new_bases = new_bases.map(|v| PyTuple::new_ref(v, &vm.ctx));
1203        let (orig_bases, bases) = match new_bases {
1204            Some(new) => (Some(bases), new),
1205            None => (None, bases),
1206        };
1207
1208        // Use downcast_exact to keep ref to old object on error.
1209        let metaclass = kwargs
1210            .pop_kwarg("metaclass")
1211            .map(|metaclass| {
1212                metaclass
1213                    .downcast_exact::<PyType>(vm)
1214                    .map(|m| m.into_pyref())
1215            })
1216            .unwrap_or_else(|| {
1217                // if there are no bases, use type; else get the type of the first base
1218                Ok(if bases.is_empty() {
1219                    vm.ctx.types.type_type.to_owned()
1220                } else {
1221                    bases.first().unwrap().class().to_owned()
1222                })
1223            });
1224
1225        let (metaclass, meta_name) = match metaclass {
1226            Ok(mut metaclass) => {
1227                for base in bases.iter() {
1228                    let base_class = base.class();
1229                    // if winner is subtype of tmptype, continue (winner is more derived)
1230                    if metaclass.fast_issubclass(base_class) {
1231                        continue;
1232                    }
1233                    // if tmptype is subtype of winner, update (tmptype is more derived)
1234                    if base_class.fast_issubclass(&metaclass) {
1235                        metaclass = base_class.to_owned();
1236                        continue;
1237                    }
1238                    // Metaclass conflict
1239                    return Err(vm.new_type_error(
1240                        "metaclass conflict: the metaclass of a derived class must be a (non-strict) \
1241                        subclass of the metaclasses of all its bases",
1242                    ));
1243                }
1244                let meta_name = metaclass.slot_name();
1245                (metaclass.to_owned().into(), meta_name.to_owned())
1246            }
1247            Err(obj) => (obj, "<metaclass>".to_owned()),
1248        };
1249
1250        let bases: PyObjectRef = bases.into();
1251
1252        // Prepare uses full __getattribute__ resolution chain.
1253        let namespace = vm
1254            .get_attribute_opt(metaclass.clone(), identifier!(vm, __prepare__))?
1255            .map_or(Ok(vm.ctx.new_dict().into()), |prepare| {
1256                let args = FuncArgs::new(vec![name_obj.clone(), bases.clone()], kwargs.clone());
1257                prepare.call(args, vm)
1258            })?;
1259
1260        // Accept any PyMapping as namespace.
1261        let namespace = ArgMapping::try_from_object(vm, namespace.clone()).map_err(|_| {
1262            vm.new_type_error(format!(
1263                "{}.__prepare__() must return a mapping, not {}",
1264                meta_name,
1265                namespace.class()
1266            ))
1267        })?;
1268
1269        // For PEP 695 classes, set .type_params in namespace before calling the function
1270        if let Ok(type_params) = function
1271            .as_object()
1272            .get_attr(identifier!(vm, __type_params__), vm)
1273            && let Some(type_params_tuple) = type_params.downcast_ref::<PyTuple>()
1274            && !type_params_tuple.is_empty()
1275        {
1276            // Set .type_params in namespace so the compiler-generated code can use it
1277            namespace
1278                .as_object()
1279                .set_item(vm.ctx.intern_str(".type_params"), type_params, vm)?;
1280        }
1281
1282        let classcell = function.invoke_with_locals(().into(), Some(namespace.clone()), vm)?;
1283        let classcell = <Option<PyCellRef>>::try_from_object(vm, classcell)?;
1284
1285        if let Some(orig_bases) = orig_bases {
1286            namespace.as_object().set_item(
1287                identifier!(vm, __orig_bases__),
1288                orig_bases.into(),
1289                vm,
1290            )?;
1291        }
1292
1293        // Remove .type_params from namespace before creating the class
1294        namespace
1295            .as_object()
1296            .del_item(vm.ctx.intern_str(".type_params"), vm)
1297            .ok();
1298
1299        let args = FuncArgs::new(vec![name_obj, bases, namespace.into()], kwargs);
1300        let class = metaclass.call(args, vm)?;
1301
1302        // For PEP 695 classes, set __type_params__ on the class from the function
1303        if let Ok(type_params) = function
1304            .as_object()
1305            .get_attr(identifier!(vm, __type_params__), vm)
1306            && let Some(type_params_tuple) = type_params.downcast_ref::<PyTuple>()
1307            && !type_params_tuple.is_empty()
1308        {
1309            class.set_attr(identifier!(vm, __type_params__), type_params.clone(), vm)?;
1310            // Also set __parameters__ for compatibility with typing module
1311            class.set_attr(identifier!(vm, __parameters__), type_params, vm)?;
1312        }
1313
1314        // only check cell if cls is a type and cell is a cell object
1315        if let Some(ref classcell) = classcell
1316            && class.fast_isinstance(vm.ctx.types.type_type)
1317        {
1318            let cell_value = classcell.get().ok_or_else(|| {
1319                vm.new_runtime_error(format!(
1320                    "__class__ not set defining {:?} as {:?}. Was __classcell__ propagated to type.__new__?",
1321                    name, class
1322                ))
1323            })?;
1324
1325            if !cell_value.is(&class) {
1326                return Err(vm.new_type_error(format!(
1327                    "__class__ set to {:?} defining {:?} as {:?}",
1328                    cell_value, name, class
1329                )));
1330            }
1331        }
1332
1333        Ok(class)
1334    }
1335}
1336
1337pub fn init_module(vm: &VirtualMachine, module: &Py<PyModule>) {
1338    let ctx = &vm.ctx;
1339
1340    crate::protocol::VecBuffer::make_static_type();
1341
1342    module.__init_methods(vm).unwrap();
1343    builtins::module_exec(vm, module).unwrap();
1344
1345    let debug_mode: bool = vm.state.config.settings.optimize == 0;
1346    // Create dynamic ExceptionGroup with multiple inheritance (BaseExceptionGroup + Exception)
1347    let exception_group = crate::exception_group::exception_group();
1348
1349    extend_module!(vm, module, {
1350        "__debug__" => ctx.new_bool(debug_mode),
1351
1352        "bool" => ctx.types.bool_type.to_owned(),
1353        "bytearray" => ctx.types.bytearray_type.to_owned(),
1354        "bytes" => ctx.types.bytes_type.to_owned(),
1355        "classmethod" => ctx.types.classmethod_type.to_owned(),
1356        "complex" => ctx.types.complex_type.to_owned(),
1357        "dict" => ctx.types.dict_type.to_owned(),
1358        "enumerate" => ctx.types.enumerate_type.to_owned(),
1359        "float" => ctx.types.float_type.to_owned(),
1360        "frozenset" => ctx.types.frozenset_type.to_owned(),
1361        "filter" => ctx.types.filter_type.to_owned(),
1362        "int" => ctx.types.int_type.to_owned(),
1363        "list" => ctx.types.list_type.to_owned(),
1364        "map" => ctx.types.map_type.to_owned(),
1365        "memoryview" => ctx.types.memoryview_type.to_owned(),
1366        "object" => ctx.types.object_type.to_owned(),
1367        "property" => ctx.types.property_type.to_owned(),
1368        "range" => ctx.types.range_type.to_owned(),
1369        "set" => ctx.types.set_type.to_owned(),
1370        "slice" => ctx.types.slice_type.to_owned(),
1371        "staticmethod" => ctx.types.staticmethod_type.to_owned(),
1372        "str" => ctx.types.str_type.to_owned(),
1373        "super" => ctx.types.super_type.to_owned(),
1374        "tuple" => ctx.types.tuple_type.to_owned(),
1375        "type" => ctx.types.type_type.to_owned(),
1376        "zip" => ctx.types.zip_type.to_owned(),
1377
1378        // Constants
1379        "None" => ctx.none(),
1380        "True" => ctx.new_bool(true),
1381        "False" => ctx.new_bool(false),
1382        "NotImplemented" => ctx.not_implemented(),
1383        "Ellipsis" => vm.ctx.ellipsis.clone(),
1384
1385        // ordered by exception_hierarchy.txt
1386        // Exceptions:
1387        "BaseException" => ctx.exceptions.base_exception_type.to_owned(),
1388        "BaseExceptionGroup" => ctx.exceptions.base_exception_group.to_owned(),
1389        "ExceptionGroup" => exception_group.to_owned(),
1390        "SystemExit" => ctx.exceptions.system_exit.to_owned(),
1391        "KeyboardInterrupt" => ctx.exceptions.keyboard_interrupt.to_owned(),
1392        "GeneratorExit" => ctx.exceptions.generator_exit.to_owned(),
1393        "Exception" => ctx.exceptions.exception_type.to_owned(),
1394        "StopIteration" => ctx.exceptions.stop_iteration.to_owned(),
1395        "StopAsyncIteration" => ctx.exceptions.stop_async_iteration.to_owned(),
1396        "ArithmeticError" => ctx.exceptions.arithmetic_error.to_owned(),
1397        "FloatingPointError" => ctx.exceptions.floating_point_error.to_owned(),
1398        "OverflowError" => ctx.exceptions.overflow_error.to_owned(),
1399        "ZeroDivisionError" => ctx.exceptions.zero_division_error.to_owned(),
1400        "AssertionError" => ctx.exceptions.assertion_error.to_owned(),
1401        "AttributeError" => ctx.exceptions.attribute_error.to_owned(),
1402        "BufferError" => ctx.exceptions.buffer_error.to_owned(),
1403        "EOFError" => ctx.exceptions.eof_error.to_owned(),
1404        "ImportError" => ctx.exceptions.import_error.to_owned(),
1405        "ModuleNotFoundError" => ctx.exceptions.module_not_found_error.to_owned(),
1406        "LookupError" => ctx.exceptions.lookup_error.to_owned(),
1407        "IndexError" => ctx.exceptions.index_error.to_owned(),
1408        "KeyError" => ctx.exceptions.key_error.to_owned(),
1409        "MemoryError" => ctx.exceptions.memory_error.to_owned(),
1410        "NameError" => ctx.exceptions.name_error.to_owned(),
1411        "UnboundLocalError" => ctx.exceptions.unbound_local_error.to_owned(),
1412        "OSError" => ctx.exceptions.os_error.to_owned(),
1413        // OSError alias
1414        "IOError" => ctx.exceptions.os_error.to_owned(),
1415        "EnvironmentError" => ctx.exceptions.os_error.to_owned(),
1416        "BlockingIOError" => ctx.exceptions.blocking_io_error.to_owned(),
1417        "ChildProcessError" => ctx.exceptions.child_process_error.to_owned(),
1418        "ConnectionError" => ctx.exceptions.connection_error.to_owned(),
1419        "BrokenPipeError" => ctx.exceptions.broken_pipe_error.to_owned(),
1420        "ConnectionAbortedError" => ctx.exceptions.connection_aborted_error.to_owned(),
1421        "ConnectionRefusedError" => ctx.exceptions.connection_refused_error.to_owned(),
1422        "ConnectionResetError" => ctx.exceptions.connection_reset_error.to_owned(),
1423        "FileExistsError" => ctx.exceptions.file_exists_error.to_owned(),
1424        "FileNotFoundError" => ctx.exceptions.file_not_found_error.to_owned(),
1425        "InterruptedError" => ctx.exceptions.interrupted_error.to_owned(),
1426        "IsADirectoryError" => ctx.exceptions.is_a_directory_error.to_owned(),
1427        "NotADirectoryError" => ctx.exceptions.not_a_directory_error.to_owned(),
1428        "PermissionError" => ctx.exceptions.permission_error.to_owned(),
1429        "ProcessLookupError" => ctx.exceptions.process_lookup_error.to_owned(),
1430        "TimeoutError" => ctx.exceptions.timeout_error.to_owned(),
1431        "ReferenceError" => ctx.exceptions.reference_error.to_owned(),
1432        "RuntimeError" => ctx.exceptions.runtime_error.to_owned(),
1433        "NotImplementedError" => ctx.exceptions.not_implemented_error.to_owned(),
1434        "RecursionError" => ctx.exceptions.recursion_error.to_owned(),
1435        "SyntaxError" =>  ctx.exceptions.syntax_error.to_owned(),
1436        "_IncompleteInputError" =>  ctx.exceptions.incomplete_input_error.to_owned(),
1437        "IndentationError" =>  ctx.exceptions.indentation_error.to_owned(),
1438        "TabError" =>  ctx.exceptions.tab_error.to_owned(),
1439        "SystemError" => ctx.exceptions.system_error.to_owned(),
1440        "TypeError" => ctx.exceptions.type_error.to_owned(),
1441        "ValueError" => ctx.exceptions.value_error.to_owned(),
1442        "UnicodeError" => ctx.exceptions.unicode_error.to_owned(),
1443        "UnicodeDecodeError" => ctx.exceptions.unicode_decode_error.to_owned(),
1444        "UnicodeEncodeError" => ctx.exceptions.unicode_encode_error.to_owned(),
1445        "UnicodeTranslateError" => ctx.exceptions.unicode_translate_error.to_owned(),
1446
1447        // Warnings
1448        "Warning" => ctx.exceptions.warning.to_owned(),
1449        "DeprecationWarning" => ctx.exceptions.deprecation_warning.to_owned(),
1450        "PendingDeprecationWarning" => ctx.exceptions.pending_deprecation_warning.to_owned(),
1451        "RuntimeWarning" => ctx.exceptions.runtime_warning.to_owned(),
1452        "SyntaxWarning" => ctx.exceptions.syntax_warning.to_owned(),
1453        "UserWarning" => ctx.exceptions.user_warning.to_owned(),
1454        "FutureWarning" => ctx.exceptions.future_warning.to_owned(),
1455        "ImportWarning" => ctx.exceptions.import_warning.to_owned(),
1456        "UnicodeWarning" => ctx.exceptions.unicode_warning.to_owned(),
1457        "BytesWarning" => ctx.exceptions.bytes_warning.to_owned(),
1458        "ResourceWarning" => ctx.exceptions.resource_warning.to_owned(),
1459        "EncodingWarning" => ctx.exceptions.encoding_warning.to_owned(),
1460    });
1461
1462    #[cfg(feature = "jit")]
1463    extend_module!(vm, module, {
1464        "JitError" => ctx.exceptions.jit_error.to_owned(),
1465    });
1466
1467    #[cfg(windows)]
1468    extend_module!(vm, module, {
1469        // OSError alias for Windows
1470        "WindowsError" => ctx.exceptions.os_error.to_owned(),
1471    });
1472}