diff --git a/crates/vm/src/builtins/descriptor.rs b/crates/vm/src/builtins/descriptor.rs index 55c2275f5bb..e8329fbb572 100644 --- a/crates/vm/src/builtins/descriptor.rs +++ b/crates/vm/src/builtins/descriptor.rs @@ -2062,6 +2062,16 @@ impl Callable for PyWrapper { flags(DISALLOW_INSTANTIATION) )] impl Py { + #[pymethod] + fn __reduce__(&self, vm: &VirtualMachine) -> PyResult { + let getattr = vm.builtins.get_attr("getattr", vm)?; + let args = vm.ctx.new_tuple(vec![ + self.typ.to_owned().into(), + self.name.to_owned().into(), + ]); + Ok(vm.ctx.new_tuple(vec![getattr, args.into()]).into()) + } + #[pygetset] fn __qualname__(&self) -> String { format!("{}.{}", self.typ.name(), self.name) diff --git a/extra_tests/snippets/stdlib_pickle.py b/extra_tests/snippets/stdlib_pickle.py index 23d137df43b..855bd79691e 100644 --- a/extra_tests/snippets/stdlib_pickle.py +++ b/extra_tests/snippets/stdlib_pickle.py @@ -9,3 +9,7 @@ assert list(memo) == [1, 7] assert memo[1] is left assert memo[7] is left + +# Slot wrapper descriptors reduce to lookup on their defining class. +assert object.__str__.__reduce__() == (getattr, (object, "__str__")) +assert _pickle.loads(_pickle.dumps(object.__str__)) is object.__str__