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::{builtins::PyModule, class::PyClassImpl, Py, VirtualMachine};
5pub(crate) use builtins::{__module_def, DOC};
6pub use builtins::{ascii, print, reversed};
7
8#[pymodule]
9mod builtins {
10    use crate::{
11        builtins::{
12            enumerate::PyReverseSequenceIterator,
13            function::{PyCellRef, PyFunction},
14            int::PyIntRef,
15            iter::PyCallableIterator,
16            list::{PyList, SortOptions},
17            PyByteArray, PyBytes, PyDictRef, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType,
18        },
19        common::{hash::PyHash, str::to_ascii},
20        function::{
21            ArgBytesLike, ArgCallable, ArgIndex, ArgIntoBool, ArgIterable, ArgMapping,
22            ArgStrOrBytesLike, Either, FsPath, FuncArgs, KwArgs, OptionalArg, OptionalOption,
23            PosArgs,
24        },
25        protocol::{PyIter, PyIterReturn},
26        py_io,
27        readline::{Readline, ReadlineResult},
28        stdlib::sys,
29        types::PyComparisonOp,
30        AsObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
31    };
32    use num_traits::{Signed, ToPrimitive};
33
34    #[cfg(not(feature = "rustpython-compiler"))]
35    const CODEGEN_NOT_SUPPORTED: &str =
36        "can't compile() to bytecode when the `codegen` feature of rustpython is disabled";
37
38    #[pyfunction]
39    fn abs(x: PyObjectRef, vm: &VirtualMachine) -> PyResult {
40        vm._abs(&x)
41    }
42
43    #[pyfunction]
44    fn all(iterable: ArgIterable<ArgIntoBool>, vm: &VirtualMachine) -> PyResult<bool> {
45        for item in iterable.iter(vm)? {
46            if !*item? {
47                return Ok(false);
48            }
49        }
50        Ok(true)
51    }
52
53    #[pyfunction]
54    fn any(iterable: ArgIterable<ArgIntoBool>, vm: &VirtualMachine) -> PyResult<bool> {
55        for item in iterable.iter(vm)? {
56            if *item? {
57                return Ok(true);
58            }
59        }
60        Ok(false)
61    }
62
63    #[pyfunction]
64    pub fn ascii(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<ascii::AsciiString> {
65        let repr = obj.repr(vm)?;
66        let ascii = to_ascii(repr.as_str());
67        Ok(ascii)
68    }
69
70    #[pyfunction]
71    fn bin(x: PyIntRef) -> String {
72        let x = x.as_bigint();
73        if x.is_negative() {
74            format!("-0b{:b}", x.abs())
75        } else {
76            format!("0b{x:b}")
77        }
78    }
79
80    #[pyfunction]
81    fn callable(obj: PyObjectRef) -> bool {
82        obj.is_callable()
83    }
84
85    #[pyfunction]
86    fn chr(i: PyIntRef, vm: &VirtualMachine) -> PyResult<String> {
87        let value = i
88            .try_to_primitive::<isize>(vm)?
89            .to_u32()
90            .and_then(char::from_u32)
91            .ok_or_else(|| vm.new_value_error("chr() arg not in range(0x110000)".to_owned()))?;
92        Ok(value.to_string())
93    }
94
95    #[derive(FromArgs)]
96    #[allow(dead_code)]
97    struct CompileArgs {
98        source: PyObjectRef,
99        filename: FsPath,
100        mode: PyStrRef,
101        #[pyarg(any, optional)]
102        flags: OptionalArg<PyIntRef>,
103        #[pyarg(any, optional)]
104        dont_inherit: OptionalArg<bool>,
105        #[pyarg(any, optional)]
106        optimize: OptionalArg<PyIntRef>,
107        #[pyarg(any, optional)]
108        _feature_version: OptionalArg<i32>,
109    }
110
111    #[cfg(any(feature = "rustpython-parser", feature = "rustpython-codegen"))]
112    #[pyfunction]
113    fn compile(args: CompileArgs, vm: &VirtualMachine) -> PyResult {
114        #[cfg(feature = "rustpython-ast")]
115        {
116            use crate::{class::PyClassImpl, stdlib::ast};
117
118            if args._feature_version.is_present() {
119                // TODO: add support for _feature_version
120            }
121
122            let mode_str = args.mode.as_str();
123
124            let optimize: i32 = args.optimize.map_or(Ok(-1), |v| v.try_to_primitive(vm))?;
125            let optimize: u8 = if optimize == -1 {
126                vm.state.settings.optimize
127            } else {
128                optimize.try_into().map_err(|_| {
129                    vm.new_value_error("compile() optimize value invalid".to_owned())
130                })?
131            };
132
133            if args
134                .source
135                .fast_isinstance(&ast::NodeAst::make_class(&vm.ctx))
136            {
137                #[cfg(not(feature = "rustpython-codegen"))]
138                {
139                    return Err(vm.new_type_error(CODEGEN_NOT_SUPPORTED.to_owned()));
140                }
141                #[cfg(feature = "rustpython-codegen")]
142                {
143                    let mode = mode_str
144                        .parse::<crate::compiler::Mode>()
145                        .map_err(|err| vm.new_value_error(err.to_string()))?;
146                    return ast::compile(
147                        vm,
148                        args.source,
149                        args.filename.as_str(),
150                        mode,
151                        Some(optimize),
152                    );
153                }
154            }
155
156            #[cfg(not(feature = "rustpython-parser"))]
157            {
158                const PARSER_NOT_SUPPORTED: &str =
159        "can't compile() source code when the `parser` feature of rustpython is disabled";
160                Err(vm.new_type_error(PARSER_NOT_SUPPORTED.to_owned()))
161            }
162            #[cfg(feature = "rustpython-parser")]
163            {
164                use crate::convert::ToPyException;
165                use num_traits::Zero;
166                use rustpython_parser as parser;
167
168                let source = ArgStrOrBytesLike::try_from_object(vm, args.source)?;
169                let source = source.borrow_bytes();
170
171                // TODO: compiler::compile should probably get bytes
172                let source = std::str::from_utf8(&source)
173                    .map_err(|e| vm.new_unicode_decode_error(e.to_string()))?;
174
175                let flags = args.flags.map_or(Ok(0), |v| v.try_to_primitive(vm))?;
176
177                if !(flags & !ast::PY_COMPILE_FLAGS_MASK).is_zero() {
178                    return Err(vm.new_value_error("compile() unrecognized flags".to_owned()));
179                }
180
181                if (flags & ast::PY_COMPILE_FLAG_AST_ONLY).is_zero() {
182                    #[cfg(not(feature = "rustpython-compiler"))]
183                    {
184                        Err(vm.new_value_error(CODEGEN_NOT_SUPPORTED.to_owned()))
185                    }
186                    #[cfg(feature = "rustpython-compiler")]
187                    {
188                        let mode = mode_str
189                            .parse::<crate::compiler::Mode>()
190                            .map_err(|err| vm.new_value_error(err.to_string()))?;
191
192                        let mut opts = vm.compile_opts();
193                        opts.optimize = optimize;
194
195                        let code = vm
196                            .compile_with_opts(
197                                source,
198                                mode,
199                                args.filename.as_str().to_owned(),
200                                opts,
201                            )
202                            .map_err(|err| (err, Some(source)).to_pyexception(vm))?;
203                        Ok(code.into())
204                    }
205                } else {
206                    let mode = mode_str
207                        .parse::<parser::Mode>()
208                        .map_err(|err| vm.new_value_error(err.to_string()))?;
209                    ast::parse(vm, source, mode).map_err(|e| (e, Some(source)).to_pyexception(vm))
210                }
211            }
212        }
213    }
214
215    #[pyfunction]
216    fn delattr(obj: PyObjectRef, attr: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
217        let attr = attr.try_to_ref::<PyStr>(vm).map_err(|_e| {
218            vm.new_type_error(format!(
219                "attribute name must be string, not '{}'",
220                attr.class().name()
221            ))
222        })?;
223        obj.del_attr(attr, vm)
224    }
225
226    #[pyfunction]
227    fn dir(obj: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<PyList> {
228        vm.dir(obj.into_option())
229    }
230
231    #[pyfunction]
232    fn divmod(a: PyObjectRef, b: PyObjectRef, vm: &VirtualMachine) -> PyResult {
233        vm._divmod(&a, &b)
234    }
235
236    #[derive(FromArgs)]
237    struct ScopeArgs {
238        #[pyarg(any, default)]
239        globals: Option<PyDictRef>,
240        #[pyarg(any, default)]
241        locals: Option<ArgMapping>,
242    }
243
244    impl ScopeArgs {
245        fn make_scope(self, vm: &VirtualMachine) -> PyResult<crate::scope::Scope> {
246            let (globals, locals) = match self.globals {
247                Some(globals) => {
248                    if !globals.contains_key(identifier!(vm, __builtins__), vm) {
249                        let builtins_dict = vm.builtins.dict().into();
250                        globals.set_item(identifier!(vm, __builtins__), builtins_dict, vm)?;
251                    }
252                    (
253                        globals.clone(),
254                        self.locals.unwrap_or_else(|| {
255                            ArgMapping::try_from_object(vm, globals.into()).unwrap()
256                        }),
257                    )
258                }
259                None => (
260                    vm.current_globals().clone(),
261                    if let Some(locals) = self.locals {
262                        locals
263                    } else {
264                        vm.current_locals()?
265                    },
266                ),
267            };
268
269            let scope = crate::scope::Scope::with_builtins(Some(locals), globals, vm);
270            Ok(scope)
271        }
272    }
273
274    #[pyfunction]
275    fn eval(
276        source: Either<ArgStrOrBytesLike, PyRef<crate::builtins::PyCode>>,
277        scope: ScopeArgs,
278        vm: &VirtualMachine,
279    ) -> PyResult {
280        // source as string
281        let code = match source {
282            Either::A(either) => {
283                let source: &[u8] = &either.borrow_bytes();
284                if source.contains(&0) {
285                    return Err(vm.new_exception_msg(
286                        vm.ctx.exceptions.syntax_error.to_owned(),
287                        "source code string cannot contain null bytes".to_owned(),
288                    ));
289                }
290
291                let source = std::str::from_utf8(source).map_err(|err| {
292                    let msg = format!(
293                        "(unicode error) 'utf-8' codec can't decode byte 0x{:x?} in position {}: invalid start byte",
294                        source[err.valid_up_to()],
295                        err.valid_up_to()
296                    );
297
298                    vm.new_exception_msg(vm.ctx.exceptions.syntax_error.to_owned(), msg)
299                })?;
300                Ok(Either::A(vm.ctx.new_str(source.trim_start())))
301            }
302            Either::B(code) => Ok(Either::B(code)),
303        }?;
304        run_code(vm, code, scope, crate::compiler::Mode::Eval, "eval")
305    }
306
307    #[pyfunction]
308    fn exec(
309        source: Either<PyStrRef, PyRef<crate::builtins::PyCode>>,
310        scope: ScopeArgs,
311        vm: &VirtualMachine,
312    ) -> PyResult {
313        run_code(vm, source, scope, crate::compiler::Mode::Exec, "exec")
314    }
315
316    fn run_code(
317        vm: &VirtualMachine,
318        source: Either<PyStrRef, PyRef<crate::builtins::PyCode>>,
319        scope: ScopeArgs,
320        #[allow(unused_variables)] mode: crate::compiler::Mode,
321        func: &str,
322    ) -> PyResult {
323        let scope = scope.make_scope(vm)?;
324
325        // Determine code object:
326        let code_obj = match source {
327            #[cfg(feature = "rustpython-compiler")]
328            Either::A(string) => vm
329                .compile(string.as_str(), mode, "<string>".to_owned())
330                .map_err(|err| vm.new_syntax_error(&err, Some(string.as_str())))?,
331            #[cfg(not(feature = "rustpython-compiler"))]
332            Either::A(_) => return Err(vm.new_type_error(CODEGEN_NOT_SUPPORTED.to_owned())),
333            Either::B(code_obj) => code_obj,
334        };
335
336        if !code_obj.freevars.is_empty() {
337            return Err(vm.new_type_error(format!(
338                "code object passed to {func}() may not contain free variables"
339            )));
340        }
341
342        // Run the code:
343        vm.run_code_obj(code_obj, scope)
344    }
345
346    #[pyfunction]
347    fn format(
348        value: PyObjectRef,
349        format_spec: OptionalArg<PyStrRef>,
350        vm: &VirtualMachine,
351    ) -> PyResult<PyStrRef> {
352        vm.format(&value, format_spec.unwrap_or(vm.ctx.new_str("")))
353    }
354
355    #[pyfunction]
356    fn getattr(
357        obj: PyObjectRef,
358        attr: PyObjectRef,
359        default: OptionalArg<PyObjectRef>,
360        vm: &VirtualMachine,
361    ) -> PyResult {
362        let attr = attr.try_to_ref::<PyStr>(vm).map_err(|_e| {
363            vm.new_type_error(format!(
364                "attribute name must be string, not '{}'",
365                attr.class().name()
366            ))
367        })?;
368
369        if let OptionalArg::Present(default) = default {
370            Ok(vm.get_attribute_opt(obj, attr)?.unwrap_or(default))
371        } else {
372            obj.get_attr(attr, vm)
373        }
374    }
375
376    #[pyfunction]
377    fn globals(vm: &VirtualMachine) -> PyDictRef {
378        vm.current_globals().clone()
379    }
380
381    #[pyfunction]
382    fn hasattr(obj: PyObjectRef, attr: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
383        let attr = attr.try_to_ref::<PyStr>(vm).map_err(|_e| {
384            vm.new_type_error(format!(
385                "attribute name must be string, not '{}'",
386                attr.class().name()
387            ))
388        })?;
389        Ok(vm.get_attribute_opt(obj, attr)?.is_some())
390    }
391
392    #[pyfunction]
393    fn hash(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyHash> {
394        obj.hash(vm)
395    }
396
397    #[pyfunction]
398    fn breakpoint(args: FuncArgs, vm: &VirtualMachine) -> PyResult {
399        match vm
400            .sys_module
401            .get_attr(vm.ctx.intern_str("breakpointhook"), vm)
402        {
403            Ok(hook) => hook.as_ref().call(args, vm),
404            Err(_) => Err(vm.new_runtime_error("lost sys.breakpointhook".to_owned())),
405        }
406    }
407
408    #[pyfunction]
409    fn hex(number: ArgIndex) -> String {
410        let n = number.as_bigint();
411        format!("{n:#x}")
412    }
413
414    #[pyfunction]
415    fn id(obj: PyObjectRef) -> usize {
416        obj.get_id()
417    }
418
419    #[pyfunction]
420    fn input(prompt: OptionalArg<PyStrRef>, vm: &VirtualMachine) -> PyResult {
421        let stdin = sys::get_stdin(vm)?;
422        let stdout = sys::get_stdout(vm)?;
423        let stderr = sys::get_stderr(vm)?;
424
425        let _ = vm.call_method(&stderr, "flush", ());
426
427        let fd_matches = |obj, expected| {
428            vm.call_method(obj, "fileno", ())
429                .and_then(|o| i64::try_from_object(vm, o))
430                .ok()
431                .map_or(false, |fd| fd == expected)
432        };
433
434        // everything is normalish, we can just rely on rustyline to use stdin/stdout
435        if fd_matches(&stdin, 0) && fd_matches(&stdout, 1) && atty::is(atty::Stream::Stdin) {
436            let prompt = prompt.as_ref().map_or("", |s| s.as_str());
437            let mut readline = Readline::new(());
438            match readline.readline(prompt) {
439                ReadlineResult::Line(s) => Ok(vm.ctx.new_str(s).into()),
440                ReadlineResult::Eof => {
441                    Err(vm.new_exception_empty(vm.ctx.exceptions.eof_error.to_owned()))
442                }
443                ReadlineResult::Interrupt => {
444                    Err(vm.new_exception_empty(vm.ctx.exceptions.keyboard_interrupt.to_owned()))
445                }
446                ReadlineResult::Io(e) => Err(vm.new_os_error(e.to_string())),
447                ReadlineResult::Other(e) => Err(vm.new_runtime_error(e.to_string())),
448            }
449        } else {
450            if let OptionalArg::Present(prompt) = prompt {
451                vm.call_method(&stdout, "write", (prompt,))?;
452            }
453            let _ = vm.call_method(&stdout, "flush", ());
454            py_io::file_readline(&stdin, None, vm)
455        }
456    }
457
458    #[pyfunction]
459    fn isinstance(obj: PyObjectRef, typ: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
460        obj.is_instance(&typ, vm)
461    }
462
463    #[pyfunction]
464    fn issubclass(subclass: PyObjectRef, typ: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
465        subclass.is_subclass(&typ, vm)
466    }
467
468    #[pyfunction]
469    fn iter(
470        iter_target: PyObjectRef,
471        sentinel: OptionalArg<PyObjectRef>,
472        vm: &VirtualMachine,
473    ) -> PyResult<PyIter> {
474        if let OptionalArg::Present(sentinel) = sentinel {
475            let callable = ArgCallable::try_from_object(vm, iter_target)?;
476            let iterator = PyCallableIterator::new(callable, sentinel)
477                .into_ref(&vm.ctx)
478                .into();
479            Ok(PyIter::new(iterator))
480        } else {
481            iter_target.get_iter(vm)
482        }
483    }
484
485    #[pyfunction]
486    fn aiter(iter_target: PyObjectRef, vm: &VirtualMachine) -> PyResult {
487        iter_target.get_aiter(vm)
488    }
489
490    #[pyfunction]
491    fn len(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
492        obj.length(vm)
493    }
494
495    #[pyfunction]
496    fn locals(vm: &VirtualMachine) -> PyResult<ArgMapping> {
497        vm.current_locals()
498    }
499
500    fn min_or_max(
501        mut args: FuncArgs,
502        vm: &VirtualMachine,
503        func_name: &str,
504        op: PyComparisonOp,
505    ) -> PyResult {
506        let default = args.take_keyword("default");
507        let key_func = args.take_keyword("key");
508
509        if let Some(err) = args.check_kwargs_empty(vm) {
510            return Err(err);
511        }
512
513        let candidates = match args.args.len().cmp(&1) {
514            std::cmp::Ordering::Greater => {
515                if default.is_some() {
516                    return Err(vm.new_type_error(format!(
517                        "Cannot specify a default for {func_name}() with multiple positional arguments"
518                    )));
519                }
520                args.args
521            }
522            std::cmp::Ordering::Equal => args.args[0].try_to_value(vm)?,
523            std::cmp::Ordering::Less => {
524                // zero arguments means type error:
525                return Err(
526                    vm.new_type_error(format!("{func_name} expected at least 1 argument, got 0"))
527                );
528            }
529        };
530
531        let mut candidates_iter = candidates.into_iter();
532        let mut x = match candidates_iter.next() {
533            Some(x) => x,
534            None => {
535                return default.ok_or_else(|| {
536                    vm.new_value_error(format!("{func_name}() arg is an empty sequence"))
537                })
538            }
539        };
540
541        let key_func = key_func.filter(|f| !vm.is_none(f));
542        if let Some(ref key_func) = key_func {
543            let mut x_key = key_func.call((x.clone(),), vm)?;
544            for y in candidates_iter {
545                let y_key = key_func.call((y.clone(),), vm)?;
546                if y_key.rich_compare_bool(&x_key, op, vm)? {
547                    x = y;
548                    x_key = y_key;
549                }
550            }
551        } else {
552            for y in candidates_iter {
553                if y.rich_compare_bool(&x, op, vm)? {
554                    x = y;
555                }
556            }
557        }
558
559        Ok(x)
560    }
561
562    #[pyfunction]
563    fn max(args: FuncArgs, vm: &VirtualMachine) -> PyResult {
564        min_or_max(args, vm, "max", PyComparisonOp::Gt)
565    }
566
567    #[pyfunction]
568    fn min(args: FuncArgs, vm: &VirtualMachine) -> PyResult {
569        min_or_max(args, vm, "min", PyComparisonOp::Lt)
570    }
571
572    #[pyfunction]
573    fn next(
574        iterator: PyObjectRef,
575        default_value: OptionalArg<PyObjectRef>,
576        vm: &VirtualMachine,
577    ) -> PyResult<PyIterReturn> {
578        if !PyIter::check(&iterator) {
579            return Err(vm.new_type_error(format!(
580                "{} object is not an iterator",
581                iterator.class().name()
582            )));
583        }
584        PyIter::new(iterator).next(vm).map(|iret| match iret {
585            PyIterReturn::Return(obj) => PyIterReturn::Return(obj),
586            PyIterReturn::StopIteration(v) => {
587                default_value.map_or(PyIterReturn::StopIteration(v), PyIterReturn::Return)
588            }
589        })
590    }
591
592    #[pyfunction]
593    fn oct(number: ArgIndex, vm: &VirtualMachine) -> PyResult {
594        let n = number.as_bigint();
595        let s = if n.is_negative() {
596            format!("-0o{:o}", n.abs())
597        } else {
598            format!("0o{n:o}")
599        };
600
601        Ok(vm.ctx.new_str(s).into())
602    }
603
604    #[pyfunction]
605    fn ord(string: Either<ArgBytesLike, PyStrRef>, vm: &VirtualMachine) -> PyResult<u32> {
606        match string {
607            Either::A(bytes) => bytes.with_ref(|bytes| {
608                let bytes_len = bytes.len();
609                if bytes_len != 1 {
610                    return Err(vm.new_type_error(format!(
611                        "ord() expected a character, but string of length {bytes_len} found"
612                    )));
613                }
614                Ok(u32::from(bytes[0]))
615            }),
616            Either::B(string) => {
617                let string = string.as_str();
618                let string_len = string.chars().count();
619                if string_len != 1 {
620                    return Err(vm.new_type_error(format!(
621                        "ord() expected a character, but string of length {string_len} found"
622                    )));
623                }
624                match string.chars().next() {
625                    Some(character) => Ok(character as u32),
626                    None => Err(vm.new_type_error(
627                        "ord() could not guess the integer representing this character".to_owned(),
628                    )),
629                }
630            }
631        }
632    }
633
634    #[derive(FromArgs)]
635    struct PowArgs {
636        base: PyObjectRef,
637        exp: PyObjectRef,
638        #[pyarg(any, optional, name = "mod")]
639        modulus: Option<PyObjectRef>,
640    }
641
642    #[pyfunction]
643    fn pow(args: PowArgs, vm: &VirtualMachine) -> PyResult {
644        let PowArgs {
645            base: x,
646            exp: y,
647            modulus,
648        } = args;
649        let modulus = modulus.as_ref().map_or(vm.ctx.none.as_object(), |m| m);
650        vm._pow(&x, &y, modulus)
651    }
652
653    #[pyfunction]
654    pub fn exit(exit_code_arg: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
655        let code = exit_code_arg.unwrap_or_else(|| vm.ctx.new_int(0).into());
656        Err(vm.new_exception(vm.ctx.exceptions.system_exit.to_owned(), vec![code]))
657    }
658
659    #[derive(Debug, Default, FromArgs)]
660    pub struct PrintOptions {
661        #[pyarg(named, default)]
662        sep: Option<PyStrRef>,
663        #[pyarg(named, default)]
664        end: Option<PyStrRef>,
665        #[pyarg(named, default = "ArgIntoBool::FALSE")]
666        flush: ArgIntoBool,
667        #[pyarg(named, default)]
668        file: Option<PyObjectRef>,
669    }
670
671    #[pyfunction]
672    pub fn print(objects: PosArgs, options: PrintOptions, vm: &VirtualMachine) -> PyResult<()> {
673        let file = match options.file {
674            Some(f) => f,
675            None => sys::get_stdout(vm)?,
676        };
677        let write = |obj: PyStrRef| vm.call_method(&file, "write", (obj,));
678
679        let sep = options
680            .sep
681            .unwrap_or_else(|| PyStr::from(" ").into_ref(&vm.ctx));
682
683        let mut first = true;
684        for object in objects {
685            if first {
686                first = false;
687            } else {
688                write(sep.clone())?;
689            }
690
691            write(object.str(vm)?)?;
692        }
693
694        let end = options
695            .end
696            .unwrap_or_else(|| PyStr::from("\n").into_ref(&vm.ctx));
697        write(end)?;
698
699        if *options.flush {
700            vm.call_method(&file, "flush", ())?;
701        }
702
703        Ok(())
704    }
705
706    #[pyfunction]
707    fn repr(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyStrRef> {
708        obj.repr(vm)
709    }
710
711    #[pyfunction]
712    pub fn reversed(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
713        if let Some(reversed_method) = vm.get_method(obj.clone(), identifier!(vm, __reversed__)) {
714            reversed_method?.call((), vm)
715        } else {
716            vm.get_method_or_type_error(obj.clone(), identifier!(vm, __getitem__), || {
717                "argument to reversed() must be a sequence".to_owned()
718            })?;
719            let len = obj.length(vm)?;
720            let obj_iterator = PyReverseSequenceIterator::new(obj, len);
721            Ok(obj_iterator.into_pyobject(vm))
722        }
723    }
724
725    #[derive(FromArgs)]
726    pub struct RoundArgs {
727        number: PyObjectRef,
728        #[pyarg(any, optional)]
729        ndigits: OptionalOption<PyObjectRef>,
730    }
731
732    #[pyfunction]
733    fn round(RoundArgs { number, ndigits }: RoundArgs, vm: &VirtualMachine) -> PyResult {
734        let meth = vm
735            .get_special_method(&number, identifier!(vm, __round__))?
736            .ok_or_else(|| {
737                vm.new_type_error(format!(
738                    "type {} doesn't define __round__",
739                    number.class().name()
740                ))
741            })?;
742        match ndigits.flatten() {
743            Some(obj) => {
744                let ndigits = obj.try_index(vm)?;
745                meth.invoke((ndigits,), vm)
746            }
747            None => {
748                // without a parameter, the result type is coerced to int
749                meth.invoke((), vm)
750            }
751        }
752    }
753
754    #[pyfunction]
755    fn setattr(
756        obj: PyObjectRef,
757        attr: PyObjectRef,
758        value: PyObjectRef,
759        vm: &VirtualMachine,
760    ) -> PyResult<()> {
761        let attr = attr.try_to_ref::<PyStr>(vm).map_err(|_e| {
762            vm.new_type_error(format!(
763                "attribute name must be string, not '{}'",
764                attr.class().name()
765            ))
766        })?;
767        obj.set_attr(attr, value, vm)?;
768        Ok(())
769    }
770
771    // builtin_slice
772
773    #[pyfunction]
774    fn sorted(iterable: PyObjectRef, opts: SortOptions, vm: &VirtualMachine) -> PyResult<PyList> {
775        let items: Vec<_> = iterable.try_to_value(vm)?;
776        let lst = PyList::from(items);
777        lst.sort(opts, vm)?;
778        Ok(lst)
779    }
780
781    #[derive(FromArgs)]
782    pub struct SumArgs {
783        #[pyarg(positional)]
784        iterable: ArgIterable,
785        #[pyarg(any, optional)]
786        start: OptionalArg<PyObjectRef>,
787    }
788
789    #[pyfunction]
790    fn sum(SumArgs { iterable, start }: SumArgs, vm: &VirtualMachine) -> PyResult {
791        // Start with zero and add at will:
792        let mut sum = start
793            .into_option()
794            .unwrap_or_else(|| vm.ctx.new_int(0).into());
795
796        match_class!(match sum {
797            PyStr =>
798                return Err(vm.new_type_error(
799                    "sum() can't sum strings [use ''.join(seq) instead]".to_owned()
800                )),
801            PyBytes =>
802                return Err(vm.new_type_error(
803                    "sum() can't sum bytes [use b''.join(seq) instead]".to_owned()
804                )),
805            PyByteArray =>
806                return Err(vm.new_type_error(
807                    "sum() can't sum bytearray [use b''.join(seq) instead]".to_owned()
808                )),
809            _ => (),
810        });
811
812        for item in iterable.iter(vm)? {
813            sum = vm._add(&sum, &*item?)?;
814        }
815        Ok(sum)
816    }
817
818    #[pyfunction]
819    fn __import__(args: FuncArgs, vm: &VirtualMachine) -> PyResult {
820        vm.import_func.call(args, vm)
821    }
822
823    #[pyfunction]
824    fn vars(obj: OptionalArg, vm: &VirtualMachine) -> PyResult {
825        if let OptionalArg::Present(obj) = obj {
826            obj.get_attr(identifier!(vm, __dict__), vm).map_err(|_| {
827                vm.new_type_error("vars() argument must have __dict__ attribute".to_owned())
828            })
829        } else {
830            Ok(vm.current_locals()?.into())
831        }
832    }
833
834    #[pyfunction]
835    pub fn __build_class__(
836        function: PyRef<PyFunction>,
837        qualified_name: PyStrRef,
838        bases: PosArgs,
839        mut kwargs: KwArgs,
840        vm: &VirtualMachine,
841    ) -> PyResult {
842        let name = qualified_name.as_str().split('.').next_back().unwrap();
843        let name_obj = vm.ctx.new_str(name);
844
845        // Update bases.
846        let mut new_bases: Option<Vec<PyObjectRef>> = None;
847        let bases = PyTuple::new_ref(bases.into_vec(), &vm.ctx);
848        for (i, base) in bases.iter().enumerate() {
849            if base.fast_isinstance(vm.ctx.types.type_type) {
850                if let Some(bases) = &mut new_bases {
851                    bases.push(base.clone());
852                }
853                continue;
854            }
855            let mro_entries =
856                vm.get_attribute_opt(base.clone(), identifier!(vm, __mro_entries__))?;
857            let entries = match mro_entries {
858                Some(meth) => meth.call((bases.clone(),), vm)?,
859                None => {
860                    if let Some(bases) = &mut new_bases {
861                        bases.push(base.clone());
862                    }
863                    continue;
864                }
865            };
866            let entries: PyTupleRef = entries
867                .downcast()
868                .map_err(|_| vm.new_type_error("__mro_entries__ must return a tuple".to_owned()))?;
869            let new_bases = new_bases.get_or_insert_with(|| bases[..i].to_vec());
870            new_bases.extend_from_slice(&entries);
871        }
872
873        let new_bases = new_bases.map(|v| PyTuple::new_ref(v, &vm.ctx));
874        let (orig_bases, bases) = match new_bases {
875            Some(new) => (Some(bases), new),
876            None => (None, bases),
877        };
878
879        // Use downcast_exact to keep ref to old object on error.
880        let metaclass = kwargs
881            .pop_kwarg("metaclass")
882            .map(|metaclass| {
883                metaclass
884                    .downcast_exact::<PyType>(vm)
885                    .map(|m| m.into_pyref())
886            })
887            .unwrap_or_else(|| Ok(vm.ctx.types.type_type.to_owned()));
888
889        let (metaclass, meta_name) = match metaclass {
890            Ok(mut metaclass) => {
891                for base in bases.iter() {
892                    let base_class = base.class();
893                    if base_class.fast_issubclass(&metaclass) {
894                        metaclass = base.class().to_owned();
895                    } else if !metaclass.fast_issubclass(base_class) {
896                        return Err(vm.new_type_error(
897                            "metaclass conflict: the metaclass of a derived class must be a (non-strict) \
898                            subclass of the metaclasses of all its bases"
899                                .to_owned(),
900                        ));
901                    }
902                }
903                let meta_name = metaclass.slot_name();
904                (metaclass.to_owned().into(), meta_name.to_owned())
905            }
906            Err(obj) => (obj, "<metaclass>".to_owned()),
907        };
908
909        let bases: PyObjectRef = bases.into();
910
911        // Prepare uses full __getattribute__ resolution chain.
912        let namespace = vm
913            .get_attribute_opt(metaclass.clone(), identifier!(vm, __prepare__))?
914            .map_or(Ok(vm.ctx.new_dict().into()), |prepare| {
915                let args =
916                    FuncArgs::new(vec![name_obj.clone().into(), bases.clone()], kwargs.clone());
917                prepare.call(args, vm)
918            })?;
919
920        // Accept any PyMapping as namespace.
921        let namespace = ArgMapping::try_from_object(vm, namespace.clone()).map_err(|_| {
922            vm.new_type_error(format!(
923                "{}.__prepare__() must return a mapping, not {}",
924                meta_name,
925                namespace.class()
926            ))
927        })?;
928
929        let classcell = function.invoke_with_locals(().into(), Some(namespace.clone()), vm)?;
930        let classcell = <Option<PyCellRef>>::try_from_object(vm, classcell)?;
931
932        if let Some(orig_bases) = orig_bases {
933            namespace.as_object().set_item(
934                identifier!(vm, __orig_bases__),
935                orig_bases.into(),
936                vm,
937            )?;
938        }
939
940        let args = FuncArgs::new(vec![name_obj.into(), bases, namespace.into()], kwargs);
941        let class = metaclass.call(args, vm)?;
942
943        if let Some(ref classcell) = classcell {
944            let classcell = classcell.get().ok_or_else(|| {
945                vm.new_type_error(format!(
946                    "__class__ not set defining {meta_name:?} as {class:?}. Was __classcell__ propagated to type.__new__?"
947                ))
948            })?;
949
950            if !classcell.is(&class) {
951                return Err(vm.new_type_error(format!(
952                    "__class__ set to {classcell:?} defining {meta_name:?} as {class:?}"
953                )));
954            }
955        }
956
957        Ok(class)
958    }
959}
960
961pub fn init_module(vm: &VirtualMachine, module: &Py<PyModule>) {
962    let ctx = &vm.ctx;
963
964    crate::protocol::VecBuffer::make_class(&vm.ctx);
965
966    builtins::extend_module(vm, module).unwrap();
967
968    let debug_mode: bool = vm.state.settings.optimize == 0;
969    extend_module!(vm, module, {
970        "__debug__" => ctx.new_bool(debug_mode),
971
972        "bool" => ctx.types.bool_type.to_owned(),
973        "bytearray" => ctx.types.bytearray_type.to_owned(),
974        "bytes" => ctx.types.bytes_type.to_owned(),
975        "classmethod" => ctx.types.classmethod_type.to_owned(),
976        "complex" => ctx.types.complex_type.to_owned(),
977        "dict" => ctx.types.dict_type.to_owned(),
978        "enumerate" => ctx.types.enumerate_type.to_owned(),
979        "float" => ctx.types.float_type.to_owned(),
980        "frozenset" => ctx.types.frozenset_type.to_owned(),
981        "filter" => ctx.types.filter_type.to_owned(),
982        "int" => ctx.types.int_type.to_owned(),
983        "list" => ctx.types.list_type.to_owned(),
984        "map" => ctx.types.map_type.to_owned(),
985        "memoryview" => ctx.types.memoryview_type.to_owned(),
986        "object" => ctx.types.object_type.to_owned(),
987        "property" => ctx.types.property_type.to_owned(),
988        "range" => ctx.types.range_type.to_owned(),
989        "set" => ctx.types.set_type.to_owned(),
990        "slice" => ctx.types.slice_type.to_owned(),
991        "staticmethod" => ctx.types.staticmethod_type.to_owned(),
992        "str" => ctx.types.str_type.to_owned(),
993        "super" => ctx.types.super_type.to_owned(),
994        "tuple" => ctx.types.tuple_type.to_owned(),
995        "type" => ctx.types.type_type.to_owned(),
996        "zip" => ctx.types.zip_type.to_owned(),
997
998        // Constants
999        "None" => ctx.none(),
1000        "True" => ctx.new_bool(true),
1001        "False" => ctx.new_bool(false),
1002        "NotImplemented" => ctx.not_implemented(),
1003        "Ellipsis" => vm.ctx.ellipsis.clone(),
1004
1005        // ordered by exception_hierarchy.txt
1006        // Exceptions:
1007        "BaseException" => ctx.exceptions.base_exception_type.to_owned(),
1008        "BaseExceptionGroup" => ctx.exceptions.base_exception_group.to_owned(),
1009        "ExceptionGroup" => ctx.exceptions.exception_group.to_owned(),
1010        "SystemExit" => ctx.exceptions.system_exit.to_owned(),
1011        "KeyboardInterrupt" => ctx.exceptions.keyboard_interrupt.to_owned(),
1012        "GeneratorExit" => ctx.exceptions.generator_exit.to_owned(),
1013        "Exception" => ctx.exceptions.exception_type.to_owned(),
1014        "StopIteration" => ctx.exceptions.stop_iteration.to_owned(),
1015        "StopAsyncIteration" => ctx.exceptions.stop_async_iteration.to_owned(),
1016        "ArithmeticError" => ctx.exceptions.arithmetic_error.to_owned(),
1017        "FloatingPointError" => ctx.exceptions.floating_point_error.to_owned(),
1018        "OverflowError" => ctx.exceptions.overflow_error.to_owned(),
1019        "ZeroDivisionError" => ctx.exceptions.zero_division_error.to_owned(),
1020        "AssertionError" => ctx.exceptions.assertion_error.to_owned(),
1021        "AttributeError" => ctx.exceptions.attribute_error.to_owned(),
1022        "BufferError" => ctx.exceptions.buffer_error.to_owned(),
1023        "EOFError" => ctx.exceptions.eof_error.to_owned(),
1024        "ImportError" => ctx.exceptions.import_error.to_owned(),
1025        "ModuleNotFoundError" => ctx.exceptions.module_not_found_error.to_owned(),
1026        "LookupError" => ctx.exceptions.lookup_error.to_owned(),
1027        "IndexError" => ctx.exceptions.index_error.to_owned(),
1028        "KeyError" => ctx.exceptions.key_error.to_owned(),
1029        "MemoryError" => ctx.exceptions.memory_error.to_owned(),
1030        "NameError" => ctx.exceptions.name_error.to_owned(),
1031        "UnboundLocalError" => ctx.exceptions.unbound_local_error.to_owned(),
1032        "OSError" => ctx.exceptions.os_error.to_owned(),
1033        // OSError alias
1034        "IOError" => ctx.exceptions.os_error.to_owned(),
1035        "EnvironmentError" => ctx.exceptions.os_error.to_owned(),
1036        "BlockingIOError" => ctx.exceptions.blocking_io_error.to_owned(),
1037        "ChildProcessError" => ctx.exceptions.child_process_error.to_owned(),
1038        "ConnectionError" => ctx.exceptions.connection_error.to_owned(),
1039        "BrokenPipeError" => ctx.exceptions.broken_pipe_error.to_owned(),
1040        "ConnectionAbortedError" => ctx.exceptions.connection_aborted_error.to_owned(),
1041        "ConnectionRefusedError" => ctx.exceptions.connection_refused_error.to_owned(),
1042        "ConnectionResetError" => ctx.exceptions.connection_reset_error.to_owned(),
1043        "FileExistsError" => ctx.exceptions.file_exists_error.to_owned(),
1044        "FileNotFoundError" => ctx.exceptions.file_not_found_error.to_owned(),
1045        "InterruptedError" => ctx.exceptions.interrupted_error.to_owned(),
1046        "IsADirectoryError" => ctx.exceptions.is_a_directory_error.to_owned(),
1047        "NotADirectoryError" => ctx.exceptions.not_a_directory_error.to_owned(),
1048        "PermissionError" => ctx.exceptions.permission_error.to_owned(),
1049        "ProcessLookupError" => ctx.exceptions.process_lookup_error.to_owned(),
1050        "TimeoutError" => ctx.exceptions.timeout_error.to_owned(),
1051        "ReferenceError" => ctx.exceptions.reference_error.to_owned(),
1052        "RuntimeError" => ctx.exceptions.runtime_error.to_owned(),
1053        "NotImplementedError" => ctx.exceptions.not_implemented_error.to_owned(),
1054        "RecursionError" => ctx.exceptions.recursion_error.to_owned(),
1055        "SyntaxError" =>  ctx.exceptions.syntax_error.to_owned(),
1056        "IndentationError" =>  ctx.exceptions.indentation_error.to_owned(),
1057        "TabError" =>  ctx.exceptions.tab_error.to_owned(),
1058        "SystemError" => ctx.exceptions.system_error.to_owned(),
1059        "TypeError" => ctx.exceptions.type_error.to_owned(),
1060        "ValueError" => ctx.exceptions.value_error.to_owned(),
1061        "UnicodeError" => ctx.exceptions.unicode_error.to_owned(),
1062        "UnicodeDecodeError" => ctx.exceptions.unicode_decode_error.to_owned(),
1063        "UnicodeEncodeError" => ctx.exceptions.unicode_encode_error.to_owned(),
1064        "UnicodeTranslateError" => ctx.exceptions.unicode_translate_error.to_owned(),
1065
1066        // Warnings
1067        "Warning" => ctx.exceptions.warning.to_owned(),
1068        "DeprecationWarning" => ctx.exceptions.deprecation_warning.to_owned(),
1069        "PendingDeprecationWarning" => ctx.exceptions.pending_deprecation_warning.to_owned(),
1070        "RuntimeWarning" => ctx.exceptions.runtime_warning.to_owned(),
1071        "SyntaxWarning" => ctx.exceptions.syntax_warning.to_owned(),
1072        "UserWarning" => ctx.exceptions.user_warning.to_owned(),
1073        "FutureWarning" => ctx.exceptions.future_warning.to_owned(),
1074        "ImportWarning" => ctx.exceptions.import_warning.to_owned(),
1075        "UnicodeWarning" => ctx.exceptions.unicode_warning.to_owned(),
1076        "BytesWarning" => ctx.exceptions.bytes_warning.to_owned(),
1077        "ResourceWarning" => ctx.exceptions.resource_warning.to_owned(),
1078        "EncodingWarning" => ctx.exceptions.encoding_warning.to_owned(),
1079    });
1080
1081    #[cfg(feature = "jit")]
1082    extend_module!(vm, module, {
1083        "JitError" => ctx.exceptions.jit_error.to_owned(),
1084    });
1085}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.