MCPcopy Index your code
hub / github.com/RustPython/RustPython / unpack_sequence

Method unpack_sequence

crates/vm/src/frame.rs:7124–7203  ·  view source on GitHub ↗

_PyEval_UnpackIterableStackRef

(&mut self, size: u32, vm: &VirtualMachine)

Source from the content-addressed store, hash-verified

7122
7123 /// _PyEval_UnpackIterableStackRef
7124 fn unpack_sequence(&mut self, size: u32, vm: &VirtualMachine) -> FrameResult {
7125 let value = self.pop_value();
7126 let size = size as usize;
7127
7128 // Fast path for exact tuple/list types (not subclasses) — push
7129 // elements directly from the slice without intermediate Vec allocation,
7130 // matching UNPACK_SEQUENCE_TUPLE / UNPACK_SEQUENCE_LIST specializations.
7131 let cls = value.class();
7132 if cls.is(vm.ctx.types.tuple_type) {
7133 let tuple = value.downcast_ref::<PyTuple>().unwrap();
7134 return self.unpack_fast(tuple.as_slice(), size, vm);
7135 }
7136 if cls.is(vm.ctx.types.list_type) {
7137 let list = value.downcast_ref::<PyList>().unwrap();
7138 let borrowed = list.borrow_vec();
7139 return self.unpack_fast(&borrowed, size, vm);
7140 }
7141
7142 // General path — iterate up to `size + 1` elements to avoid
7143 // consuming the entire iterator (fixes hang on infinite sequences).
7144 let not_iterable = value.class().slots.iter.load().is_none()
7145 && value
7146 .get_class_attr(vm.ctx.intern_str("__getitem__"))
7147 .is_none();
7148 let iter = PyIter::try_from_object(vm, value.clone()).map_err(|e| {
7149 if not_iterable && e.class().is(vm.ctx.exceptions.type_error) {
7150 vm.new_type_error(format!(
7151 "cannot unpack non-iterable {} object",
7152 value.class().name()
7153 ))
7154 } else {
7155 e
7156 }
7157 })?;
7158
7159 let mut elements = Vec::with_capacity(size);
7160 for _ in 0..size {
7161 match iter.next(vm)? {
7162 PyIterReturn::Return(item) => elements.push(item),
7163 PyIterReturn::StopIteration(_) => {
7164 return Err(vm.new_value_error(format!(
7165 "not enough values to unpack (expected {size}, got {})",
7166 elements.len()
7167 )));
7168 }
7169 }
7170 }
7171
7172 // Check that the iterator is exhausted.
7173 match iter.next(vm)? {
7174 PyIterReturn::Return(_) => {
7175 // For exact dict types, show "got N" using the container's
7176 // size (PyDict_Size). Exact tuple/list are handled by the
7177 // fast path above and never reach here.
7178 let msg = if value.class().is(vm.ctx.types.dict_type) {
7179 if let Ok(got) = value.length(vm) {
7180 if got > size {
7181 format!("too many values to unpack (expected {size}, got {got})")

Callers 1

execute_instructionMethod · 0.80

Calls 15

pop_valueMethod · 0.80
isMethod · 0.80
unpack_fastMethod · 0.80
borrow_vecMethod · 0.80
get_class_attrMethod · 0.80
intern_strMethod · 0.80
stack_extendMethod · 0.80
ErrClass · 0.50
SomeClass · 0.50
classMethod · 0.45
unwrapMethod · 0.45
as_sliceMethod · 0.45

Tested by

no test coverage detected