1use super::{PyStr, PyType, PyTypeRef};
8use crate::{
9 AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
10 builtins::function::PyCell,
11 class::PyClassImpl,
12 function::{FuncArgs, IntoFuncArgs, OptionalArg},
13 object::PyAtomicRef,
14 types::{Callable, Constructor, GetAttr, GetDescriptor, Initializer, Representable},
15};
16
17#[pyclass(module = false, name = "super", traverse)]
18#[derive(Debug)]
19pub struct PySuper {
20 #[pymember(name = "__thisclass__")]
21 typ: PyAtomicRef<Option<PyType>>,
22 #[pymember(name = "__self__")]
23 obj: PyAtomicRef<Option<PyObject>>,
24 #[pymember(name = "__self_class__")]
25 obj_type: PyAtomicRef<Option<PyType>>,
26}
27
28fn bind_super(
29 typ: PyTypeRef,
30 obj: PyObjectRef,
31 vm: &VirtualMachine,
32) -> PyResult<(PyTypeRef, Option<PyObjectRef>, Option<PyTypeRef>)> {
33 if vm.is_none(&obj) {
34 return Ok((typ, None, None));
35 }
36 let obj_type = super_check(&typ, &obj, vm)?;
37 Ok((typ, Some(obj), Some(obj_type)))
38}
39
40impl PySuper {
41 fn empty() -> Self {
42 Self {
43 typ: PyAtomicRef::from(None),
44 obj: PyAtomicRef::from(None),
45 obj_type: PyAtomicRef::from(None),
46 }
47 }
48
49 fn store_bound(
50 &self,
51 typ: Option<PyTypeRef>,
52 obj: Option<PyObjectRef>,
53 obj_type: Option<PyTypeRef>,
54 ) {
55 drop(self.typ.store(typ));
56 drop(self.obj.store(obj));
57 drop(self.obj_type.store(obj_type));
58 }
59}
60
61impl PyPayload for PySuper {
62 #[inline]
63 fn class(ctx: &Context) -> &'static Py<PyType> {
64 ctx.types.super_type
65 }
66}
67
68impl Constructor for PySuper {
69 type Args = FuncArgs;
70
71 fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
72 Ok(Self::empty())
74 }
75}
76
77#[derive(FromArgs)]
78pub struct InitArgs {
79 #[pyarg(
80 positional,
81 optional,
82 name = "type",
83 error_msg = "super() argument 1 must be a type"
84 )]
85 py_type: OptionalArg<PyTypeRef>,
86 #[pyarg(positional, optional)]
87 object: OptionalArg<PyObjectRef>,
88}
89
90impl Initializer for PySuper {
91 type Args = InitArgs;
92
93 fn init(
94 zelf: &Py<Self>,
95 Self::Args { py_type, object }: Self::Args,
96 vm: &VirtualMachine,
97 ) -> PyResult<()> {
98 let typ = match py_type {
99 OptionalArg::Present(ty) => Some(ty),
100 OptionalArg::Missing => None,
101 };
102 let obj = match object {
103 OptionalArg::Present(obj) => Some(obj),
104 OptionalArg::Missing => None,
105 };
106 super_init_impl(zelf, typ, obj, vm)
107 }
108}
109
110fn super_init_impl(
112 zelf: &Py<PySuper>,
113 typ: Option<PyTypeRef>,
114 obj: Option<PyObjectRef>,
115 vm: &VirtualMachine,
116) -> PyResult<()> {
117 let (typ, obj) = match typ {
118 Some(typ) => (typ, obj.unwrap_or_else(|| vm.ctx.none())),
119 None => super_init_without_args(vm)?,
120 };
121 let (typ, obj, obj_type) = bind_super(typ, obj, vm)?;
122 zelf.store_bound(Some(typ), obj, obj_type);
123 Ok(())
124}
125
126fn super_init_without_args(vm: &VirtualMachine) -> PyResult<(PyTypeRef, PyObjectRef)> {
127 let iframe_ptr = crate::vm::thread::get_current_frame();
130 if iframe_ptr.is_null() {
131 return Err(vm.new_runtime_error("super(): no current frame"));
132 }
133 let iframe = unsafe { &*iframe_ptr };
134 let code = iframe.code();
135
136 if code.arg_count == 0 {
137 return Err(vm.new_runtime_error("super(): no arguments"));
138 }
139
140 use rustpython_compiler_core::bytecode::CO_FAST_CELL;
142 let fastlocals = iframe.localsplus.fastlocals();
143 let obj = fastlocals[0]
144 .clone()
145 .and_then(|val| {
146 if code
148 .localspluskinds
149 .first()
150 .is_some_and(|&k| k & CO_FAST_CELL != 0)
151 {
152 val.downcast_ref::<PyCell>().and_then(|c| c.get())
153 } else {
154 Some(val)
155 }
156 })
157 .ok_or_else(|| vm.new_runtime_error("super(): arg[0] deleted"))?;
158
159 let mut typ = None;
160 let nlocalsplus = code.localspluskinds.len();
162 let nfrees = code.freevars.len();
163 let free_start = nlocalsplus - nfrees;
164 for (i, var) in code.freevars.iter().enumerate() {
165 if var.as_bytes() == b"__class__" {
166 let class = fastlocals[free_start + i]
167 .as_ref()
168 .and_then(|v| v.downcast_ref::<PyCell>())
169 .and_then(|c| c.get())
170 .ok_or_else(|| vm.new_runtime_error("super(): empty __class__ cell"))?;
171 typ = Some(class.downcast().map_err(|o| {
172 vm.new_type_error(format!(
173 "super(): __class__ is not a type ({})",
174 o.class().name()
175 ))
176 })?);
177 break;
178 }
179 }
180 let typ = typ.ok_or_else(|| {
181 vm.new_type_error("super must be called with 1 argument or from inside class method")
182 })?;
183
184 Ok((typ, obj))
185}
186
187#[pyclass(
188 with(GetAttr, GetDescriptor, Constructor, Initializer, Representable),
189 flags(BASETYPE)
190)]
191impl PySuper {}
192
193impl GetAttr for PySuper {
194 fn getattro(zelf: &Py<Self>, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
195 let skip = |zelf: &Py<Self>, name| zelf.as_object().generic_getattr(name, vm);
196 let Some(obj) = zelf.obj.load_owned() else {
197 return skip(zelf, name);
198 };
199 let Some(start_type) = zelf.obj_type.load_owned() else {
200 return skip(zelf, name);
201 };
202
203 if name.as_bytes() == b"__class__" {
206 return skip(zelf, name);
207 }
208
209 if let Some(name) = vm.ctx.interned_str(name) {
210 let Some(su_type) = zelf.typ.load_owned() else {
217 return skip(zelf, name);
218 };
219 let descr = {
220 let mro = start_type.mro.read();
221 mro.iter()
222 .skip_while(|cls| !cls.is(&su_type))
223 .skip(1) .find_map(|cls| cls.get_direct_attr(name))
225 };
226 if let Some(descr) = descr {
227 return vm
228 .call_get_descriptor_specific(
229 &descr,
230 if obj.is(&start_type) {
232 None
233 } else {
234 Some(&obj)
235 },
236 Some(start_type.as_object()),
237 )
238 .unwrap_or(Ok(descr));
239 }
240 }
241 skip(zelf, name)
242 }
243}
244
245impl GetDescriptor for PySuper {
246 fn descr_get(
247 zelf_obj: &PyObject,
248 obj: Option<&PyObject>,
249 _cls: Option<&PyObject>,
250 vm: &VirtualMachine,
251 ) -> PyResult {
252 let (zelf, obj) = Self::_unwrap(zelf_obj, obj, vm)?;
253 if vm.is_none(obj) || zelf.obj.deref().is_some() {
254 return Ok(zelf_obj.to_owned());
255 }
256 let zelf_class = zelf.as_object().class();
257 let typ = zelf.typ.load_owned();
258 if zelf_class.is(vm.ctx.types.super_type) {
259 let newobj = Self::empty().into_ref(&vm.ctx);
260 super_init_impl(&newobj, typ, Some(obj.to_owned()), vm)?;
262 Ok(newobj.into())
263 } else {
264 let args = match typ {
266 Some(typ) => (typ, obj.to_owned()).into_args(vm),
267 None => FuncArgs::default(),
268 };
269 PyType::call(zelf.class(), args, vm)
270 }
271 }
272}
273
274impl Representable for PySuper {
275 #[inline]
276 fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
277 let type_name = match zelf.typ.load_owned() {
278 Some(ty) => ty.name().to_owned(),
279 None => "NULL".to_owned(),
280 };
281 let repr = match zelf.obj_type.load_owned() {
282 Some(ty) => format!("<super: <class '{}'>, <{} object>>", type_name, ty.name()),
283 None => format!("<super: <class '{type_name}'>, NULL>"),
284 };
285 Ok(repr)
286 }
287}
288
289fn super_check(ty: &Py<PyType>, obj: &PyObject, vm: &VirtualMachine) -> PyResult<PyTypeRef> {
290 let typ = match obj.to_owned().downcast::<PyType>() {
291 Ok(cls) if cls.fast_issubclass(ty) => return Ok(cls),
292 Ok(cls) => Some(cls),
293 Err(_) => None,
294 };
295
296 if obj.fast_isinstance(ty) {
297 return Ok(obj.class().to_owned());
298 }
299
300 let class_attr = obj.get_attr("__class__", vm)?;
301 if let Ok(cls) = class_attr.downcast::<PyType>()
302 && !cls.is(obj.class())
303 && cls.fast_issubclass(ty)
304 {
305 return Ok(cls);
306 }
307
308 let (type_or_instance, obj_str) = match typ {
309 Some(t) => ("type", t.name().to_owned()),
310 None => ("instance of", obj.class().name().to_owned()),
311 };
312
313 Err(vm.new_type_error(format!(
314 "super(type, obj): obj ({} {}) is not an instance or subtype of type ({}).",
315 type_or_instance,
316 obj_str,
317 ty.name(),
318 )))
319}
320
321pub(crate) fn init(context: &'static Context) {
322 let super_type = &context.types.super_type;
323 PySuper::extend_class(context, super_type);
324
325 const SUPER_DOC: &str = "\
326super() -> same as super(__class__, <first argument>)
327super(type) -> unbound super object
328super(type, obj) -> bound super object; requires isinstance(obj, type)
329super(type, type2) -> bound super object; requires issubclass(type2, type)
330Typical use to call a cooperative superclass method:
331class C(B):
332 def meth(self, arg):
333 super().meth(arg)
334This works for class methods too:
335class C(B):
336 @classmethod
337 def cmeth(cls, arg):
338 super().cmeth(arg)
339";
340
341 extend_class!(context, super_type, {
342 "__doc__" => context.new_str(SUPER_DOC),
343 });
344}