Add non-iterability guard, deduplicate dict flush and missing args format

This commit is contained in:
Jeong, YunWon committed 2026-02-12 14:14:38 +09:00
1 parent 11275507b3
commit 64cd3db977
3 files changed
+68 -74

No files matched your search

+21 -18
View File
@@ -6670,6 +6670,23 @@ impl Compiler {
let mut have_dict = false;
let mut elements: u32 = 0;
// Flush pending regular pairs as a BUILD_MAP, merging into the
// accumulator dict via DICT_UPDATE when one already exists.
macro_rules! flush_pending {
() => {
#[allow(unused_assignments)]
if elements > 0 {
emit!(self, Instruction::BuildMap { size: elements });
if have_dict {
emit!(self, Instruction::DictUpdate { index: 1 });
} else {
have_dict = true;
}
elements = 0;
}
};
}
for item in items {
if let Some(key) = &item.key {
// Regular key: value pair
@@ -6678,17 +6695,7 @@ impl Compiler {
elements += 1;
} else {
// ** unpacking entry
if elements > 0 {
// Flush pending regular pairs
if !have_dict {
emit!(self, Instruction::BuildMap { size: elements });
have_dict = true;
} else {
emit!(self, Instruction::BuildMap { size: elements });
emit!(self, Instruction::DictUpdate { index: 1 });
}
elements = 0;
}
flush_pending!();
if !have_dict {
emit!(self, Instruction::BuildMap { size: 0 });
have_dict = true;
@@ -6698,13 +6705,9 @@ impl Compiler {
}
}
if elements > 0 || !have_dict {
if !have_dict {
emit!(self, Instruction::BuildMap { size: elements });
} else {
emit!(self, Instruction::BuildMap { size: elements });
emit!(self, Instruction::DictUpdate { index: 1 });
}
flush_pending!();
if !have_dict {
emit!(self, Instruction::BuildMap { size: 0 });
}
Ok(())
+37 -54
View File
@@ -25,6 +25,37 @@ use itertools::Itertools;
#[cfg(feature = "jit")]
use rustpython_jit::CompiledCode;
fn format_missing_args(
qualname: impl core::fmt::Display,
kind: &str,
missing: &mut Vec<impl core::fmt::Display>,
) -> String {
let count = missing.len();
let last = if missing.len() > 1 {
missing.pop()
} else {
None
};
let (and, right): (&str, String) = if let Some(last) = last {
(
if missing.len() == 1 {
"' and '"
} else {
"', and '"
},
format!("{last}"),
)
} else {
("", String::new())
};
format!(
"{qualname}() missing {count} required {kind} argument{}: '{}{}{right}'",
if count == 1 { "" } else { "s" },
missing.iter().join("', '"),
and,
)
}
#[pyclass(module = false, name = "function", traverse = "manual")]
#[derive(Debug)]
pub struct PyFunction {
@@ -343,36 +374,12 @@ impl PyFunction {
}
})
.collect();
let missing_args_len = missing.len();
if !missing.is_empty() {
let last = if missing.len() > 1 {
missing.pop()
} else {
None
};
let (and, right) = if let Some(last) = last {
(
if missing.len() == 1 {
"' and '"
} else {
"', and '"
},
last.as_str(),
)
} else {
("", "")
};
return Err(vm.new_type_error(format!(
"{}() missing {} required positional argument{}: '{}{}{}'",
return Err(vm.new_type_error(format_missing_args(
self.__qualname__(),
missing_args_len,
if missing_args_len == 1 { "" } else { "s" },
missing.iter().join("', '"),
and,
right,
"positional",
&mut missing,
)));
}
@@ -413,34 +420,10 @@ impl PyFunction {
}
if !missing.is_empty() {
let missing_len = missing.len();
let last = if missing.len() > 1 {
missing.pop()
} else {
None
};
let (and, right) = if let Some(last) = last {
(
if missing.len() == 1 {
"' and '"
} else {
"', and '"
},
last.as_str(),
)
} else {
("", "")
};
return Err(vm.new_type_error(format!(
"{}() missing {} required keyword-only argument{}: '{}{}{}'",
return Err(vm.new_type_error(format_missing_args(
self.__qualname__(),
missing_len,
if missing_len == 1 { "" } else { "s" },
missing.iter().join("', '"),
and,
right,
"keyword-only",
&mut missing,
)));
}
}
+10 -2
View File
@@ -2803,8 +2803,12 @@ impl ExecutingFrame<'_> {
fn execute_unpack_ex(&mut self, vm: &VirtualMachine, before: u8, after: u8) -> FrameResult {
let (before, after) = (before as usize, after as usize);
let value = self.pop_value();
let not_iterable = value.class().slots.iter.load().is_none()
&& value
.get_class_attr(vm.ctx.intern_str("__getitem__"))
.is_none();
let elements: Vec<_> = value.try_to_value(vm).map_err(|e| {
if e.class().is(vm.ctx.exceptions.type_error) {
if not_iterable && e.class().is(vm.ctx.exceptions.type_error) {
vm.new_type_error(format!(
"cannot unpack non-iterable {} object",
value.class().name()
@@ -3013,8 +3017,12 @@ impl ExecutingFrame<'_> {
fn unpack_sequence(&mut self, size: u32, vm: &VirtualMachine) -> FrameResult {
let value = self.pop_value();
let not_iterable = value.class().slots.iter.load().is_none()
&& value
.get_class_attr(vm.ctx.intern_str("__getitem__"))
.is_none();
let elements: Vec<_> = value.try_to_value(vm).map_err(|e| {
if e.class().is(vm.ctx.exceptions.type_error) {
if not_iterable && e.class().is(vm.ctx.exceptions.type_error) {
vm.new_type_error(format!(
"cannot unpack non-iterable {} object",
value.class().name()