(zelf: &Py<Self>, state: PyObjectRef, vm: &VirtualMachine)
| 187 | |
| 188 | #[pymethod] |
| 189 | fn __setstate__(zelf: &Py<Self>, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { |
| 190 | let state_tuple = state |
| 191 | .downcast::<PyTuple>() |
| 192 | .map_err(|_| vm.new_type_error("argument to __setstate__ must be a tuple"))?; |
| 193 | |
| 194 | if state_tuple.len() != 4 { |
| 195 | return Err(vm.new_type_error(format!( |
| 196 | "expected 4 items in state, got {}", |
| 197 | state_tuple.len() |
| 198 | ))); |
| 199 | } |
| 200 | |
| 201 | let func = &state_tuple[0]; |
| 202 | let args = &state_tuple[1]; |
| 203 | let kwds = &state_tuple[2]; |
| 204 | let dict = &state_tuple[3]; |
| 205 | |
| 206 | if !func.is_callable() { |
| 207 | return Err(vm.new_type_error("invalid partial state")); |
| 208 | } |
| 209 | |
| 210 | // Validate that args is a tuple (or subclass) |
| 211 | if !args.fast_isinstance(vm.ctx.types.tuple_type) { |
| 212 | return Err(vm.new_type_error("invalid partial state")); |
| 213 | } |
| 214 | // Always convert to base tuple, even if it's a subclass |
| 215 | let args_tuple = match args.clone().downcast::<PyTuple>() { |
| 216 | Ok(tuple) if tuple.class().is(vm.ctx.types.tuple_type) => tuple, |
| 217 | _ => { |
| 218 | // It's a tuple subclass, convert to base tuple |
| 219 | let elements: Vec<PyObjectRef> = args.try_to_value(vm)?; |
| 220 | vm.ctx.new_tuple(elements) |
| 221 | } |
| 222 | }; |
| 223 | |
| 224 | let keywords_dict = if kwds.is(&vm.ctx.none) { |
| 225 | vm.ctx.new_dict() |
| 226 | } else { |
| 227 | // Always convert to base dict, even if it's a subclass |
| 228 | let dict = kwds |
| 229 | .clone() |
| 230 | .downcast::<PyDict>() |
| 231 | .map_err(|_| vm.new_type_error("invalid partial state"))?; |
| 232 | if dict.class().is(vm.ctx.types.dict_type) { |
| 233 | // It's already a base dict |
| 234 | dict |
| 235 | } else { |
| 236 | // It's a dict subclass, convert to base dict |
| 237 | let new_dict = vm.ctx.new_dict(); |
| 238 | for (key, value) in dict { |
| 239 | new_dict.set_item(&*key, value, vm)?; |
| 240 | } |
| 241 | new_dict |
| 242 | } |
| 243 | }; |
| 244 | |
| 245 | // Validate no trailing placeholders |
| 246 | let args_slice = args_tuple.as_slice(); |
nothing calls this directly
no test coverage detected