mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Merge pull request #1684 from youknowone/refactor-things
Refactor small things
This commit is contained in:
@@ -145,7 +145,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_empty() {
|
||||
let parse_ast = parse_program(&String::from(""));
|
||||
let parse_ast = parse_program("");
|
||||
assert_eq!(parse_ast, Ok(ast::Program { statements: vec![] }))
|
||||
}
|
||||
|
||||
|
||||
@@ -951,11 +951,11 @@ pub fn builtin_build_class_(
|
||||
let cells = vm.ctx.new_dict();
|
||||
|
||||
let scope = function
|
||||
.scope
|
||||
.scope()
|
||||
.new_child_scope_with_locals(cells.clone())
|
||||
.new_child_scope_with_locals(namespace.clone());
|
||||
|
||||
vm.invoke_python_function_with_scope(&function, vec![].into(), &scope)?;
|
||||
function.invoke_with_scope(vec![].into(), &scope, vm)?;
|
||||
|
||||
let class = vm.call_method(
|
||||
metaclass.as_object(),
|
||||
|
||||
@@ -369,29 +369,16 @@ impl DictKey for &str {
|
||||
}
|
||||
|
||||
impl DictKey for &String {
|
||||
fn do_hash(self, _vm: &VirtualMachine) -> PyResult<HashValue> {
|
||||
// follow a similar route as the hashing of PyStringRef
|
||||
let raw_hash = pyhash::hash_value(self).to_bigint().unwrap();
|
||||
let raw_hash = pyhash::hash_bigint(&raw_hash);
|
||||
let mut hasher = DefaultHasher::new();
|
||||
raw_hash.hash(&mut hasher);
|
||||
Ok(hasher.finish() as HashValue)
|
||||
fn do_hash(self, vm: &VirtualMachine) -> PyResult<HashValue> {
|
||||
self.as_str().do_hash(vm)
|
||||
}
|
||||
|
||||
fn do_is(self, _other: &PyObjectRef) -> bool {
|
||||
// No matter who the other pyobject is, we are never the same thing, since
|
||||
// we are a str, not a pyobject.
|
||||
false
|
||||
fn do_is(self, other: &PyObjectRef) -> bool {
|
||||
self.as_str().do_is(other)
|
||||
}
|
||||
|
||||
fn do_eq(self, vm: &VirtualMachine, other_key: &PyObjectRef) -> PyResult<bool> {
|
||||
if let Some(py_str_value) = other_key.payload::<PyString>() {
|
||||
Ok(py_str_value.as_str() == self)
|
||||
} else {
|
||||
// Fall back to PyString implementation.
|
||||
let s = vm.new_str(self.to_string());
|
||||
s.do_eq(vm, other_key)
|
||||
}
|
||||
self.as_str().do_eq(vm, other_key)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,16 @@ use crate::vm::VirtualMachine;
|
||||
#[pyclass]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PyClassMethod {
|
||||
pub callable: PyObjectRef,
|
||||
callable: PyObjectRef,
|
||||
}
|
||||
pub type PyClassMethodRef = PyRef<PyClassMethod>;
|
||||
|
||||
impl PyClassMethod {
|
||||
pub fn new(value: PyObjectRef) -> Self {
|
||||
Self { callable: value }
|
||||
}
|
||||
}
|
||||
|
||||
impl PyValue for PyClassMethod {
|
||||
const HAVE_DICT: bool = true;
|
||||
|
||||
@@ -73,7 +79,7 @@ impl PyClassMethod {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
pub(crate) fn init(context: &PyContext) {
|
||||
PyClassMethod::extend_class(context, &context.types.classmethod_type);
|
||||
extend_class!(context, context.types.classmethod_type, {
|
||||
"__get__" => context.new_method(PyClassMethod::get),
|
||||
|
||||
@@ -3,21 +3,28 @@ use super::objdict::PyDictRef;
|
||||
use super::objstr::PyStringRef;
|
||||
use super::objtuple::PyTupleRef;
|
||||
use super::objtype::PyClassRef;
|
||||
use crate::bytecode;
|
||||
use crate::descriptor::PyBuiltinDescriptor;
|
||||
use crate::frame::Frame;
|
||||
use crate::function::{OptionalArg, PyFuncArgs};
|
||||
use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol};
|
||||
use crate::obj::objcoroutine::PyCoroutine;
|
||||
use crate::obj::objgenerator::PyGenerator;
|
||||
use crate::pyobject::{
|
||||
IdProtocol, ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
|
||||
TypeProtocol,
|
||||
};
|
||||
use crate::scope::Scope;
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
pub type PyFunctionRef = PyRef<PyFunction>;
|
||||
|
||||
#[pyclass]
|
||||
#[derive(Debug)]
|
||||
pub struct PyFunction {
|
||||
// TODO: these shouldn't be public
|
||||
pub code: PyCodeRef,
|
||||
pub scope: Scope,
|
||||
pub defaults: Option<PyTupleRef>,
|
||||
pub kw_only_defaults: Option<PyDictRef>,
|
||||
code: PyCodeRef,
|
||||
scope: Scope,
|
||||
defaults: Option<PyTupleRef>,
|
||||
kw_only_defaults: Option<PyDictRef>,
|
||||
}
|
||||
|
||||
impl PyBuiltinDescriptor for PyFunction {
|
||||
@@ -49,6 +56,182 @@ impl PyFunction {
|
||||
kw_only_defaults,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scope(&self) -> &Scope {
|
||||
&self.scope
|
||||
}
|
||||
|
||||
fn fill_locals_from_args(
|
||||
&self,
|
||||
code_object: &bytecode::CodeObject,
|
||||
locals: &PyDictRef,
|
||||
func_args: PyFuncArgs,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
let nargs = func_args.args.len();
|
||||
let nexpected_args = code_object.arg_names.len();
|
||||
|
||||
// This parses the arguments from args and kwargs into
|
||||
// the proper variables keeping into account default values
|
||||
// and starargs and kwargs.
|
||||
// See also: PyEval_EvalCodeWithName in cpython:
|
||||
// https://github.com/python/cpython/blob/master/Python/ceval.c#L3681
|
||||
|
||||
let n = if nargs > nexpected_args {
|
||||
nexpected_args
|
||||
} else {
|
||||
nargs
|
||||
};
|
||||
|
||||
// Copy positional arguments into local variables
|
||||
for i in 0..n {
|
||||
let arg_name = &code_object.arg_names[i];
|
||||
let arg = &func_args.args[i];
|
||||
locals.set_item(arg_name, arg.clone(), vm)?;
|
||||
}
|
||||
|
||||
// Pack other positional arguments in to *args:
|
||||
match code_object.varargs {
|
||||
bytecode::Varargs::Named(ref vararg_name) => {
|
||||
let mut last_args = vec![];
|
||||
for i in n..nargs {
|
||||
let arg = &func_args.args[i];
|
||||
last_args.push(arg.clone());
|
||||
}
|
||||
let vararg_value = vm.ctx.new_tuple(last_args);
|
||||
|
||||
locals.set_item(vararg_name, vararg_value, vm)?;
|
||||
}
|
||||
bytecode::Varargs::Unnamed | bytecode::Varargs::None => {
|
||||
// Check the number of positional arguments
|
||||
if nargs > nexpected_args {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Expected {} arguments (got: {})",
|
||||
nexpected_args, nargs
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do we support `**kwargs` ?
|
||||
let kwargs = match code_object.varkeywords {
|
||||
bytecode::Varargs::Named(ref kwargs_name) => {
|
||||
let d = vm.ctx.new_dict();
|
||||
locals.set_item(kwargs_name, d.as_object().clone(), vm)?;
|
||||
Some(d)
|
||||
}
|
||||
bytecode::Varargs::Unnamed => Some(vm.ctx.new_dict()),
|
||||
bytecode::Varargs::None => None,
|
||||
};
|
||||
|
||||
// Handle keyword arguments
|
||||
for (name, value) in func_args.kwargs {
|
||||
// Check if we have a parameter with this name:
|
||||
if code_object.arg_names.contains(&name) || code_object.kwonlyarg_names.contains(&name)
|
||||
{
|
||||
if locals.contains_key(&name, vm) {
|
||||
return Err(
|
||||
vm.new_type_error(format!("Got multiple values for argument '{}'", name))
|
||||
);
|
||||
}
|
||||
|
||||
locals.set_item(&name, value, vm)?;
|
||||
} else if let Some(d) = &kwargs {
|
||||
d.set_item(&name, value, vm)?;
|
||||
} else {
|
||||
return Err(
|
||||
vm.new_type_error(format!("Got an unexpected keyword argument '{}'", name))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add missing positional arguments, if we have fewer positional arguments than the
|
||||
// function definition calls for
|
||||
if nargs < nexpected_args {
|
||||
let num_defaults_available = self.defaults.as_ref().map_or(0, |d| d.as_slice().len());
|
||||
|
||||
// Given the number of defaults available, check all the arguments for which we
|
||||
// _don't_ have defaults; if any are missing, raise an exception
|
||||
let required_args = nexpected_args - num_defaults_available;
|
||||
let mut missing = vec![];
|
||||
for i in 0..required_args {
|
||||
let variable_name = &code_object.arg_names[i];
|
||||
if !locals.contains_key(variable_name, vm) {
|
||||
missing.push(variable_name)
|
||||
}
|
||||
}
|
||||
if !missing.is_empty() {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Missing {} required positional arguments: {:?}",
|
||||
missing.len(),
|
||||
missing
|
||||
)));
|
||||
}
|
||||
if let Some(defaults) = &self.defaults {
|
||||
let defaults = defaults.as_slice();
|
||||
// We have sufficient defaults, so iterate over the corresponding names and use
|
||||
// the default if we don't already have a value
|
||||
for (default_index, i) in (required_args..nexpected_args).enumerate() {
|
||||
let arg_name = &code_object.arg_names[i];
|
||||
if !locals.contains_key(arg_name, vm) {
|
||||
locals.set_item(arg_name, defaults[default_index].clone(), vm)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Check if kw only arguments are all present:
|
||||
for arg_name in &code_object.kwonlyarg_names {
|
||||
if !locals.contains_key(arg_name, vm) {
|
||||
if let Some(kw_only_defaults) = &self.kw_only_defaults {
|
||||
if let Some(default) = kw_only_defaults.get_item_option(arg_name, vm)? {
|
||||
locals.set_item(arg_name, default, vm)?;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// No default value and not specified.
|
||||
return Err(
|
||||
vm.new_type_error(format!("Missing required kw only argument: '{}'", arg_name))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn invoke_with_scope(
|
||||
&self,
|
||||
func_args: PyFuncArgs,
|
||||
scope: &Scope,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
let code = &self.code;
|
||||
|
||||
let scope = if self.code.flags.contains(bytecode::CodeFlags::NEW_LOCALS) {
|
||||
scope.new_child_scope(&vm.ctx)
|
||||
} else {
|
||||
scope.clone()
|
||||
};
|
||||
|
||||
self.fill_locals_from_args(&code, &scope.get_locals(), func_args, vm)?;
|
||||
|
||||
// Construct frame:
|
||||
let frame = Frame::new(code.clone(), scope).into_ref(vm);
|
||||
|
||||
// If we have a generator, create a new generator
|
||||
if code.flags.contains(bytecode::CodeFlags::IS_GENERATOR) {
|
||||
Ok(PyGenerator::new(frame, vm).into_object())
|
||||
} else if code.flags.contains(bytecode::CodeFlags::IS_COROUTINE) {
|
||||
Ok(PyCoroutine::new(frame, vm).into_object())
|
||||
} else {
|
||||
vm.run_frame_full(frame)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke(&self, func_args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
self.invoke_with_scope(func_args, &self.scope, vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl PyValue for PyFunction {
|
||||
@@ -57,20 +240,25 @@ impl PyValue for PyFunction {
|
||||
}
|
||||
}
|
||||
|
||||
impl PyFunctionRef {
|
||||
fn call(func: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
vm.invoke(&func, args)
|
||||
#[pyimpl]
|
||||
impl PyFunction {
|
||||
#[pymethod(name = "__call__")]
|
||||
fn call(zelf: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
vm.invoke(&zelf, args)
|
||||
}
|
||||
|
||||
fn code(self, _vm: &VirtualMachine) -> PyCodeRef {
|
||||
#[pyproperty(name = "__code__")]
|
||||
fn code(&self, _vm: &VirtualMachine) -> PyCodeRef {
|
||||
self.code.clone()
|
||||
}
|
||||
|
||||
fn defaults(self, _vm: &VirtualMachine) -> Option<PyTupleRef> {
|
||||
#[pyproperty(name = "__defaults__")]
|
||||
fn defaults(&self, _vm: &VirtualMachine) -> Option<PyTupleRef> {
|
||||
self.defaults.clone()
|
||||
}
|
||||
|
||||
fn kwdefaults(self, _vm: &VirtualMachine) -> Option<PyDictRef> {
|
||||
#[pyproperty(name = "__kwdefaults__")]
|
||||
fn kwdefaults(&self, _vm: &VirtualMachine) -> Option<PyDictRef> {
|
||||
self.kw_only_defaults.clone()
|
||||
}
|
||||
}
|
||||
@@ -100,13 +288,10 @@ impl PyValue for PyBoundMethod {
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let function_type = &context.types.function_type;
|
||||
PyFunction::extend_class(context, function_type);
|
||||
extend_class!(context, function_type, {
|
||||
"__get__" => context.new_method(PyFunction::get),
|
||||
(slot descr_get) => PyFunction::get,
|
||||
"__call__" => context.new_method(PyFunctionRef::call),
|
||||
"__code__" => context.new_property(PyFunctionRef::code),
|
||||
"__defaults__" => context.new_property(PyFunctionRef::defaults),
|
||||
"__kwdefaults__" => context.new_property(PyFunctionRef::kwdefaults),
|
||||
});
|
||||
|
||||
let method_type = &context.types.bound_method_type;
|
||||
|
||||
@@ -29,7 +29,7 @@ impl PyBuiltinDescriptor for PyStaticMethod {
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
impl PyStaticMethodRef {
|
||||
impl PyStaticMethod {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
cls: PyClassRef,
|
||||
@@ -44,7 +44,7 @@ impl PyStaticMethodRef {
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
PyStaticMethodRef::extend_class(context, &context.types.staticmethod_type);
|
||||
PyStaticMethod::extend_class(context, &context.types.staticmethod_type);
|
||||
extend_class!(context, context.types.staticmethod_type, {
|
||||
"__get__" => context.new_method(PyStaticMethod::get),
|
||||
(slot descr_get) => PyStaticMethod::get,
|
||||
|
||||
@@ -7,8 +7,9 @@ use super::objtype::PyClassRef;
|
||||
use crate::function::OptionalArg;
|
||||
use crate::pyhash;
|
||||
use crate::pyobject::{
|
||||
IntoPyObject, PyArithmaticValue::*, PyClassImpl, PyComparisonValue, PyContext, PyObjectRef,
|
||||
PyRef, PyResult, PyValue,
|
||||
IntoPyObject,
|
||||
PyArithmaticValue::{self, *},
|
||||
PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
|
||||
};
|
||||
use crate::sequence::{self, SimpleSeq};
|
||||
use crate::vm::{ReprGuard, VirtualMachine};
|
||||
@@ -111,17 +112,17 @@ impl PyTuple {
|
||||
}
|
||||
|
||||
#[pymethod(name = "__add__")]
|
||||
fn add(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
fn add(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyArithmaticValue<PyTuple> {
|
||||
if let Some(other) = other.payload_if_subclass::<PyTuple>(vm) {
|
||||
let elements = self
|
||||
let elements: Vec<_> = self
|
||||
.elements
|
||||
.iter()
|
||||
.chain(other.as_slice().iter())
|
||||
.cloned()
|
||||
.collect();
|
||||
Ok(vm.ctx.new_tuple(elements))
|
||||
Implemented(elements.into())
|
||||
} else {
|
||||
Ok(vm.ctx.not_implemented())
|
||||
NotImplemented
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,15 +191,15 @@ impl PyTuple {
|
||||
}
|
||||
|
||||
#[pymethod(name = "__mul__")]
|
||||
fn mul(&self, counter: isize, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let new_elements = sequence::seq_mul(&self.elements, counter)
|
||||
fn mul(&self, counter: isize, _vm: &VirtualMachine) -> PyTuple {
|
||||
let new_elements: Vec<_> = sequence::seq_mul(&self.elements, counter)
|
||||
.cloned()
|
||||
.collect();
|
||||
vm.ctx.new_tuple(new_elements)
|
||||
new_elements.into()
|
||||
}
|
||||
|
||||
#[pymethod(name = "__rmul__")]
|
||||
fn rmul(&self, counter: isize, vm: &VirtualMachine) -> PyObjectRef {
|
||||
fn rmul(&self, counter: isize, vm: &VirtualMachine) -> PyTuple {
|
||||
self.mul(counter, vm)
|
||||
}
|
||||
|
||||
|
||||
@@ -492,9 +492,7 @@ impl PyContext {
|
||||
F: IntoPyNativeFunc<T, R, VM>,
|
||||
{
|
||||
PyObject::new(
|
||||
PyClassMethod {
|
||||
callable: self.new_method(f),
|
||||
},
|
||||
PyClassMethod::new(self.new_method(f)),
|
||||
self.classmethod_type(),
|
||||
None,
|
||||
)
|
||||
@@ -692,12 +690,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPyObject for PyRef<T> {
|
||||
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
|
||||
Ok(self.obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyValue> From<&'a PyRef<T>> for &'a PyObjectRef {
|
||||
fn from(obj: &'a PyRef<T>) -> Self {
|
||||
obj.as_object()
|
||||
@@ -747,12 +739,6 @@ impl TryFromObject for PyCallable {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPyObject for PyCallable {
|
||||
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
|
||||
Ok(self.into_object())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IdProtocol {
|
||||
fn get_id(&self) -> usize;
|
||||
fn is<T>(&self, other: &T) -> bool
|
||||
@@ -1019,6 +1005,18 @@ pub trait IntoPyObject {
|
||||
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult;
|
||||
}
|
||||
|
||||
impl<T> IntoPyObject for PyRef<T> {
|
||||
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
|
||||
Ok(self.obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPyObject for PyCallable {
|
||||
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
|
||||
Ok(self.into_object())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPyObject for PyObjectRef {
|
||||
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
|
||||
Ok(self)
|
||||
|
||||
186
vm/src/vm.rs
186
vm/src/vm.rs
@@ -28,16 +28,14 @@ use crate::import;
|
||||
use crate::obj::objbool;
|
||||
use crate::obj::objbuiltinfunc::{PyBuiltinFunction, PyBuiltinMethod};
|
||||
use crate::obj::objcode::{PyCode, PyCodeRef};
|
||||
use crate::obj::objcoroutine::PyCoroutine;
|
||||
use crate::obj::objdict::PyDictRef;
|
||||
use crate::obj::objfunction::{PyBoundMethod, PyFunction};
|
||||
use crate::obj::objgenerator::PyGenerator;
|
||||
use crate::obj::objint::PyInt;
|
||||
use crate::obj::objiter;
|
||||
use crate::obj::objlist::PyList;
|
||||
use crate::obj::objmodule::{self, PyModule};
|
||||
use crate::obj::objstr::{PyString, PyStringRef};
|
||||
use crate::obj::objtuple::{PyTuple, PyTupleRef};
|
||||
use crate::obj::objtuple::PyTuple;
|
||||
use crate::obj::objtype::{self, PyClassRef};
|
||||
use crate::pyhash;
|
||||
use crate::pyobject::{
|
||||
@@ -661,9 +659,9 @@ impl VirtualMachine {
|
||||
fn _invoke(&self, func_ref: &PyObjectRef, args: PyFuncArgs) -> PyResult {
|
||||
vm_trace!("Invoke: {:?} {:?}", func_ref, args);
|
||||
|
||||
if let Some(py_func) = func_ref.payload() {
|
||||
if let Some(py_func) = func_ref.payload::<PyFunction>() {
|
||||
self.trace_event(TraceEvent::Call)?;
|
||||
let res = self.invoke_python_function(py_func, args);
|
||||
let res = py_func.invoke(args, self);
|
||||
self.trace_event(TraceEvent::Return)?;
|
||||
res
|
||||
} else if let Some(PyBoundMethod {
|
||||
@@ -725,184 +723,6 @@ impl VirtualMachine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn invoke_python_function(&self, func: &PyFunction, func_args: PyFuncArgs) -> PyResult {
|
||||
self.invoke_python_function_with_scope(func, func_args, &func.scope)
|
||||
}
|
||||
|
||||
pub fn invoke_python_function_with_scope(
|
||||
&self,
|
||||
func: &PyFunction,
|
||||
func_args: PyFuncArgs,
|
||||
scope: &Scope,
|
||||
) -> PyResult {
|
||||
let code = &func.code;
|
||||
|
||||
let scope = if func.code.flags.contains(bytecode::CodeFlags::NEW_LOCALS) {
|
||||
scope.new_child_scope(&self.ctx)
|
||||
} else {
|
||||
scope.clone()
|
||||
};
|
||||
|
||||
self.fill_locals_from_args(
|
||||
&code,
|
||||
&scope.get_locals(),
|
||||
func_args,
|
||||
&func.defaults,
|
||||
&func.kw_only_defaults,
|
||||
)?;
|
||||
|
||||
// Construct frame:
|
||||
let frame = Frame::new(code.clone(), scope).into_ref(self);
|
||||
|
||||
// If we have a generator, create a new generator
|
||||
if code.flags.contains(bytecode::CodeFlags::IS_GENERATOR) {
|
||||
Ok(PyGenerator::new(frame, self).into_object())
|
||||
} else if code.flags.contains(bytecode::CodeFlags::IS_COROUTINE) {
|
||||
Ok(PyCoroutine::new(frame, self).into_object())
|
||||
} else {
|
||||
self.run_frame_full(frame)
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_locals_from_args(
|
||||
&self,
|
||||
code_object: &bytecode::CodeObject,
|
||||
locals: &PyDictRef,
|
||||
func_args: PyFuncArgs,
|
||||
defaults: &Option<PyTupleRef>,
|
||||
kw_only_defaults: &Option<PyDictRef>,
|
||||
) -> PyResult<()> {
|
||||
let nargs = func_args.args.len();
|
||||
let nexpected_args = code_object.arg_names.len();
|
||||
|
||||
// This parses the arguments from args and kwargs into
|
||||
// the proper variables keeping into account default values
|
||||
// and starargs and kwargs.
|
||||
// See also: PyEval_EvalCodeWithName in cpython:
|
||||
// https://github.com/python/cpython/blob/master/Python/ceval.c#L3681
|
||||
|
||||
let n = if nargs > nexpected_args {
|
||||
nexpected_args
|
||||
} else {
|
||||
nargs
|
||||
};
|
||||
|
||||
// Copy positional arguments into local variables
|
||||
for i in 0..n {
|
||||
let arg_name = &code_object.arg_names[i];
|
||||
let arg = &func_args.args[i];
|
||||
locals.set_item(arg_name, arg.clone(), self)?;
|
||||
}
|
||||
|
||||
// Pack other positional arguments in to *args:
|
||||
match code_object.varargs {
|
||||
bytecode::Varargs::Named(ref vararg_name) => {
|
||||
let mut last_args = vec![];
|
||||
for i in n..nargs {
|
||||
let arg = &func_args.args[i];
|
||||
last_args.push(arg.clone());
|
||||
}
|
||||
let vararg_value = self.ctx.new_tuple(last_args);
|
||||
|
||||
locals.set_item(vararg_name, vararg_value, self)?;
|
||||
}
|
||||
bytecode::Varargs::Unnamed | bytecode::Varargs::None => {
|
||||
// Check the number of positional arguments
|
||||
if nargs > nexpected_args {
|
||||
return Err(self.new_type_error(format!(
|
||||
"Expected {} arguments (got: {})",
|
||||
nexpected_args, nargs
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do we support `**kwargs` ?
|
||||
let kwargs = match code_object.varkeywords {
|
||||
bytecode::Varargs::Named(ref kwargs_name) => {
|
||||
let d = self.ctx.new_dict();
|
||||
locals.set_item(kwargs_name, d.as_object().clone(), self)?;
|
||||
Some(d)
|
||||
}
|
||||
bytecode::Varargs::Unnamed => Some(self.ctx.new_dict()),
|
||||
bytecode::Varargs::None => None,
|
||||
};
|
||||
|
||||
// Handle keyword arguments
|
||||
for (name, value) in func_args.kwargs {
|
||||
// Check if we have a parameter with this name:
|
||||
if code_object.arg_names.contains(&name) || code_object.kwonlyarg_names.contains(&name)
|
||||
{
|
||||
if locals.contains_key(&name, self) {
|
||||
return Err(
|
||||
self.new_type_error(format!("Got multiple values for argument '{}'", name))
|
||||
);
|
||||
}
|
||||
|
||||
locals.set_item(&name, value, self)?;
|
||||
} else if let Some(d) = &kwargs {
|
||||
d.set_item(&name, value, self)?;
|
||||
} else {
|
||||
return Err(
|
||||
self.new_type_error(format!("Got an unexpected keyword argument '{}'", name))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add missing positional arguments, if we have fewer positional arguments than the
|
||||
// function definition calls for
|
||||
if nargs < nexpected_args {
|
||||
let num_defaults_available = defaults.as_ref().map_or(0, |d| d.as_slice().len());
|
||||
|
||||
// Given the number of defaults available, check all the arguments for which we
|
||||
// _don't_ have defaults; if any are missing, raise an exception
|
||||
let required_args = nexpected_args - num_defaults_available;
|
||||
let mut missing = vec![];
|
||||
for i in 0..required_args {
|
||||
let variable_name = &code_object.arg_names[i];
|
||||
if !locals.contains_key(variable_name, self) {
|
||||
missing.push(variable_name)
|
||||
}
|
||||
}
|
||||
if !missing.is_empty() {
|
||||
return Err(self.new_type_error(format!(
|
||||
"Missing {} required positional arguments: {:?}",
|
||||
missing.len(),
|
||||
missing
|
||||
)));
|
||||
}
|
||||
if let Some(defaults) = defaults {
|
||||
let defaults = defaults.as_slice();
|
||||
// We have sufficient defaults, so iterate over the corresponding names and use
|
||||
// the default if we don't already have a value
|
||||
for (default_index, i) in (required_args..nexpected_args).enumerate() {
|
||||
let arg_name = &code_object.arg_names[i];
|
||||
if !locals.contains_key(arg_name, self) {
|
||||
locals.set_item(arg_name, defaults[default_index].clone(), self)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Check if kw only arguments are all present:
|
||||
for arg_name in &code_object.kwonlyarg_names {
|
||||
if !locals.contains_key(arg_name, self) {
|
||||
if let Some(kw_only_defaults) = kw_only_defaults {
|
||||
if let Some(default) = kw_only_defaults.get_item_option(arg_name, self)? {
|
||||
locals.set_item(arg_name, default, self)?;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// No default value and not specified.
|
||||
return Err(self
|
||||
.new_type_error(format!("Missing required kw only argument: '{}'", arg_name)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn extract_elements<T: TryFromObject>(&self, value: &PyObjectRef) -> PyResult<Vec<T>> {
|
||||
// Extract elements from item, if possible:
|
||||
let cls = value.class();
|
||||
|
||||
Reference in New Issue
Block a user