use super::{PyStr, PyStrInterned, PyType};
use crate::{
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
builtins::{PyTypeRef, builtin_func::PyNativeMethod, type_},
class::PyClassImpl,
common::hash::PyHash,
convert::{ToPyObject, ToPyResult},
function::{ArgSize, FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue},
protocol::{PyNumberBinaryFunc, PyNumberTernaryFunc, PyNumberUnaryFunc},
types::{
Callable, Comparable, DelFunc, DescrGetFunc, DescrSetFunc, GenericMethod, GetDescriptor,
GetattroFunc, HashFunc, Hashable, InitFunc, IterFunc, IterNextFunc, MapAssSubscriptFunc,
MapLenFunc, MapSubscriptFunc, PyComparisonOp, Representable, RichCompareFunc,
SeqAssItemFunc, SeqConcatFunc, SeqContainsFunc, SeqItemFunc, SeqLenFunc, SeqRepeatFunc,
SetattroFunc, StringifyFunc,
},
};
use rustpython_common::lock::PyRwLock;
#[derive(Debug)]
pub struct PyDescriptor {
pub typ: &'static Py<PyType>,
pub name: &'static PyStrInterned,
pub qualname: PyRwLock<Option<String>>,
}
#[derive(Debug)]
pub struct PyDescriptorOwned {
pub typ: PyRef<PyType>,
pub name: &'static PyStrInterned,
pub qualname: PyRwLock<Option<String>>,
}
#[pyclass(name = "method_descriptor", module = false)]
pub struct PyMethodDescriptor {
pub common: PyDescriptor,
pub method: &'static PyMethodDef,
pub objclass: &'static Py<PyType>, pub(crate) _method_def_owner: Option<PyObjectRef>,
}
impl PyMethodDescriptor {
pub fn new(method: &'static PyMethodDef, typ: &'static Py<PyType>, ctx: &Context) -> Self {
Self {
common: PyDescriptor {
typ,
name: ctx.intern_str(method.name),
qualname: PyRwLock::new(None),
},
method,
objclass: typ,
_method_def_owner: None,
}
}
}
impl PyPayload for PyMethodDescriptor {
fn class(ctx: &Context) -> &'static Py<PyType> {
ctx.types.method_descriptor_type
}
}
impl core::fmt::Debug for PyMethodDescriptor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "method descriptor for '{}'", self.common.name)
}
}
impl GetDescriptor for PyMethodDescriptor {
fn descr_get(
zelf: PyObjectRef,
obj: Option<PyObjectRef>,
cls: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
let descr = Self::_as_pyref(&zelf, vm).unwrap();
let bound = match obj {
Some(obj) => {
if descr.method.flags.contains(PyMethodFlags::METHOD) {
if cls.is_some_and(|c| c.fast_isinstance(vm.ctx.types.type_type)) {
obj
} else {
return Err(vm.new_type_error(format!(
"descriptor '{}' needs a type, not '{}', as arg 2",
descr.common.name.as_str(),
obj.class().name()
)));
}
} else if descr.method.flags.contains(PyMethodFlags::CLASS) {
obj.class().to_owned().into()
} else {
obj
}
}
None if descr.method.flags.contains(PyMethodFlags::CLASS) => cls.unwrap(),
None => return Ok(zelf),
};
Ok(descr.bind(bound, &vm.ctx).into())
}
}
impl Callable for PyMethodDescriptor {
type Args = FuncArgs;
#[inline]
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
(zelf.method.func)(vm, args)
}
}
impl PyMethodDescriptor {
pub fn bind(&self, obj: PyObjectRef, ctx: &Context) -> PyRef<PyNativeMethod> {
self.method.build_bound_method(ctx, obj, self.common.typ)
}
}
#[pyclass(
with(GetDescriptor, Callable, Representable),
flags(METHOD_DESCRIPTOR, DISALLOW_INSTANTIATION)
)]
impl PyMethodDescriptor {
#[pygetset]
const fn __name__(&self) -> &'static PyStrInterned {
self.common.name
}
#[pygetset]
fn __qualname__(&self) -> String {
format!("{}.{}", self.common.typ.name(), &self.common.name)
}
#[pygetset]
const fn __doc__(&self) -> Option<&'static str> {
self.method.doc
}
#[pygetset]
fn __text_signature__(&self) -> Option<String> {
self.method.doc.and_then(|doc| {
type_::get_text_signature_from_internal_doc(self.method.name, doc)
.map(|signature| signature.to_string())
})
}
#[pygetset]
fn __objclass__(&self) -> PyTypeRef {
self.objclass.to_owned()
}
#[pymethod]
fn __reduce__(
&self,
vm: &VirtualMachine,
) -> (Option<PyObjectRef>, (Option<PyObjectRef>, &'static str)) {
let builtins_getattr = vm.builtins.get_attr("getattr", vm).ok();
let classname = vm.builtins.get_attr(&self.common.typ.__name__(vm), vm).ok();
(builtins_getattr, (classname, self.method.name))
}
}
impl Representable for PyMethodDescriptor {
#[inline]
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
Ok(format!(
"<method '{}' of '{}' objects>",
&zelf.method.name,
zelf.common.typ.name()
))
}
}
#[derive(Debug)]
pub enum MemberKind {
Object = 6,
Bool = 14,
ObjectEx = 16,
}
pub type MemberSetterFunc = Option<fn(&VirtualMachine, PyObjectRef, PySetterValue) -> PyResult<()>>;
pub enum MemberGetter {
Getter(fn(&VirtualMachine, PyObjectRef) -> PyResult),
Offset(usize),
}
pub enum MemberSetter {
Setter(MemberSetterFunc),
Offset(usize),
}
pub struct PyMemberDef {
pub name: String,
pub kind: MemberKind,
pub getter: MemberGetter,
pub setter: MemberSetter,
pub doc: Option<String>,
}
impl PyMemberDef {
fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
match self.getter {
MemberGetter::Getter(getter) => (getter)(vm, obj),
MemberGetter::Offset(offset) => get_slot_from_object(obj, offset, self, vm),
}
}
fn set(
&self,
obj: PyObjectRef,
value: PySetterValue<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
match self.setter {
MemberSetter::Setter(setter) => match setter {
Some(setter) => (setter)(vm, obj, value),
None => Err(vm.new_attribute_error("readonly attribute")),
},
MemberSetter::Offset(offset) => set_slot_at_object(obj, offset, self, value, vm),
}
}
}
impl core::fmt::Debug for PyMemberDef {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PyMemberDef")
.field("name", &self.name)
.field("kind", &self.kind)
.field("doc", &self.doc)
.finish()
}
}
#[pyclass(name = "member_descriptor", module = false)]
#[derive(Debug)]
pub struct PyMemberDescriptor {
pub common: PyDescriptorOwned,
pub member: PyMemberDef,
}
impl PyPayload for PyMemberDescriptor {
fn class(ctx: &Context) -> &'static Py<PyType> {
ctx.types.member_descriptor_type
}
}
fn calculate_qualname(descr: &PyDescriptorOwned, vm: &VirtualMachine) -> PyResult<Option<String>> {
if let Some(qualname) = vm.get_attribute_opt(descr.typ.clone().into(), "__qualname__")? {
let str = qualname.downcast::<PyStr>().map_err(|_| {
vm.new_type_error("<descriptor>.__objclass__.__qualname__ is not a unicode object")
})?;
Ok(Some(format!("{}.{}", str, descr.name)))
} else {
Ok(None)
}
}
#[pyclass(with(GetDescriptor, Representable), flags(DISALLOW_INSTANTIATION))]
impl PyMemberDescriptor {
#[pymember]
fn __objclass__(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
let zelf: &Py<Self> = zelf.try_to_value(vm)?;
Ok(zelf.common.typ.clone().into())
}
#[pymember]
fn __name__(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
let zelf: &Py<Self> = zelf.try_to_value(vm)?;
Ok(zelf.common.name.to_owned().into())
}
#[pygetset]
fn __doc__(&self) -> Option<String> {
self.member.doc.to_owned()
}
#[pygetset]
fn __qualname__(&self, vm: &VirtualMachine) -> PyResult<Option<String>> {
let qualname = self.common.qualname.read();
Ok(if qualname.is_none() {
drop(qualname);
let calculated = calculate_qualname(&self.common, vm)?;
calculated.clone_into(&mut self.common.qualname.write());
calculated
} else {
qualname.to_owned()
})
}
#[pymethod]
fn __reduce__(&self, vm: &VirtualMachine) -> PyResult {
let builtins_getattr = vm.builtins.get_attr("getattr", vm)?;
Ok(vm
.ctx
.new_tuple(vec![
builtins_getattr,
vm.ctx
.new_tuple(vec![
self.common.typ.clone().into(),
vm.ctx.new_str(self.common.name.as_str()).into(),
])
.into(),
])
.into())
}
#[pyslot]
fn descr_set(
zelf: &PyObject,
obj: PyObjectRef,
value: PySetterValue<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
let zelf = Self::_as_pyref(zelf, vm)?;
if !obj.class().fast_issubclass(&zelf.common.typ) {
return Err(vm.new_type_error(format!(
"descriptor '{}' for '{}' objects doesn't apply to a '{}' object",
zelf.common.name,
zelf.common.typ.name(),
obj.class().name()
)));
}
zelf.member.set(obj, value, vm)
}
}
fn get_slot_from_object(
obj: PyObjectRef,
offset: usize,
member: &PyMemberDef,
vm: &VirtualMachine,
) -> PyResult {
let slot = match member.kind {
MemberKind::Object => obj.get_slot(offset).unwrap_or_else(|| vm.ctx.none()),
MemberKind::Bool => obj
.get_slot(offset)
.unwrap_or_else(|| vm.ctx.new_bool(false).into()),
MemberKind::ObjectEx => obj.get_slot(offset).ok_or_else(|| {
vm.new_no_attribute_error(obj.clone(), vm.ctx.new_str(member.name.clone()))
})?,
};
Ok(slot)
}
fn set_slot_at_object(
obj: PyObjectRef,
offset: usize,
member: &PyMemberDef,
value: PySetterValue,
vm: &VirtualMachine,
) -> PyResult<()> {
match member.kind {
MemberKind::Object => match value {
PySetterValue::Assign(v) => {
obj.set_slot(offset, Some(v));
}
PySetterValue::Delete => {
obj.set_slot(offset, None);
}
},
MemberKind::Bool => {
match value {
PySetterValue::Assign(v) => {
if !v.class().is(vm.ctx.types.bool_type) {
return Err(vm.new_type_error("attribute value type must be bool"));
}
obj.set_slot(offset, Some(v))
}
PySetterValue::Delete => {
return Err(vm.new_type_error("can't delete numeric/char attribute"));
}
};
}
MemberKind::ObjectEx => match value {
PySetterValue::Assign(v) => {
obj.set_slot(offset, Some(v));
}
PySetterValue::Delete => {
if obj.get_slot(offset).is_none() {
return Err(vm.new_attribute_error(member.name.clone()));
}
obj.set_slot(offset, None);
}
},
}
Ok(())
}
impl Representable for PyMemberDescriptor {
#[inline]
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
Ok(format!(
"<member '{}' of '{}' objects>",
zelf.common.name,
zelf.common.typ.name(),
))
}
}
impl GetDescriptor for PyMemberDescriptor {
fn descr_get(
zelf: PyObjectRef,
obj: Option<PyObjectRef>,
_cls: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
let descr = Self::_as_pyref(&zelf, vm)?;
match obj {
Some(x) => {
if !x.class().fast_issubclass(&descr.common.typ) {
return Err(vm.new_type_error(format!(
"descriptor '{}' for '{}' objects doesn't apply to a '{}' object",
descr.common.name,
descr.common.typ.name(),
x.class().name()
)));
}
descr.member.get(x, vm)
}
None => Ok(zelf),
}
}
}
fn vectorcall_method_descriptor(
zelf_obj: &PyObject,
args: Vec<PyObjectRef>,
nargs: usize,
kwnames: Option<&[PyObjectRef]>,
vm: &VirtualMachine,
) -> PyResult {
let zelf: &Py<PyMethodDescriptor> = zelf_obj.downcast_ref().unwrap();
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
(zelf.method.func)(vm, func_args)
}
fn vectorcall_wrapper(
zelf_obj: &PyObject,
mut args: Vec<PyObjectRef>,
nargs: usize,
kwnames: Option<&[PyObjectRef]>,
vm: &VirtualMachine,
) -> PyResult {
let zelf: &Py<PyWrapper> = zelf_obj.downcast_ref().unwrap();
if nargs == 0 {
return Err(vm.new_type_error(format!(
"descriptor '{}' of '{}' object needs an argument",
zelf.name.as_str(),
zelf.typ.name()
)));
}
let obj = args.remove(0);
if !obj.fast_isinstance(zelf.typ) {
return Err(vm.new_type_error(format!(
"descriptor '{}' requires a '{}' object but received a '{}'",
zelf.name.as_str(),
zelf.typ.name(),
obj.class().name()
)));
}
let rest = FuncArgs::from_vectorcall_owned(args, nargs - 1, kwnames);
zelf.wrapped.call(obj, rest, vm)
}
pub fn init(ctx: &'static Context) {
PyMemberDescriptor::extend_class(ctx, ctx.types.member_descriptor_type);
PyMethodDescriptor::extend_class(ctx, ctx.types.method_descriptor_type);
ctx.types
.method_descriptor_type
.slots
.vectorcall
.store(Some(vectorcall_method_descriptor));
PyWrapper::extend_class(ctx, ctx.types.wrapper_descriptor_type);
ctx.types
.wrapper_descriptor_type
.slots
.vectorcall
.store(Some(vectorcall_wrapper));
PyMethodWrapper::extend_class(ctx, ctx.types.method_wrapper_type);
}
#[derive(Clone, Copy)]
pub enum SlotFunc {
Init(InitFunc),
Hash(HashFunc),
Str(StringifyFunc),
Repr(StringifyFunc),
Iter(IterFunc),
IterNext(IterNextFunc),
Call(GenericMethod),
Del(DelFunc),
GetAttro(GetattroFunc),
SetAttro(SetattroFunc), DelAttro(SetattroFunc),
RichCompare(RichCompareFunc, PyComparisonOp),
DescrGet(DescrGetFunc),
DescrSet(DescrSetFunc), DescrDel(DescrSetFunc),
SeqLength(SeqLenFunc),
SeqConcat(SeqConcatFunc),
SeqRepeat(SeqRepeatFunc),
SeqItem(SeqItemFunc),
SeqSetItem(SeqAssItemFunc), SeqDelItem(SeqAssItemFunc), SeqContains(SeqContainsFunc),
MapLength(MapLenFunc),
MapSubscript(MapSubscriptFunc),
MapSetSubscript(MapAssSubscriptFunc), MapDelSubscript(MapAssSubscriptFunc),
NumBoolean(PyNumberUnaryFunc<bool>), NumUnary(PyNumberUnaryFunc), NumBinary(PyNumberBinaryFunc), NumBinaryRight(PyNumberBinaryFunc), NumTernary(PyNumberTernaryFunc), NumTernaryRight(PyNumberTernaryFunc), }
impl core::fmt::Debug for SlotFunc {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
SlotFunc::Init(_) => write!(f, "SlotFunc::Init(...)"),
SlotFunc::Hash(_) => write!(f, "SlotFunc::Hash(...)"),
SlotFunc::Str(_) => write!(f, "SlotFunc::Str(...)"),
SlotFunc::Repr(_) => write!(f, "SlotFunc::Repr(...)"),
SlotFunc::Iter(_) => write!(f, "SlotFunc::Iter(...)"),
SlotFunc::IterNext(_) => write!(f, "SlotFunc::IterNext(...)"),
SlotFunc::Call(_) => write!(f, "SlotFunc::Call(...)"),
SlotFunc::Del(_) => write!(f, "SlotFunc::Del(...)"),
SlotFunc::GetAttro(_) => write!(f, "SlotFunc::GetAttro(...)"),
SlotFunc::SetAttro(_) => write!(f, "SlotFunc::SetAttro(...)"),
SlotFunc::DelAttro(_) => write!(f, "SlotFunc::DelAttro(...)"),
SlotFunc::RichCompare(_, op) => write!(f, "SlotFunc::RichCompare(..., {:?})", op),
SlotFunc::DescrGet(_) => write!(f, "SlotFunc::DescrGet(...)"),
SlotFunc::DescrSet(_) => write!(f, "SlotFunc::DescrSet(...)"),
SlotFunc::DescrDel(_) => write!(f, "SlotFunc::DescrDel(...)"),
SlotFunc::SeqLength(_) => write!(f, "SlotFunc::SeqLength(...)"),
SlotFunc::SeqConcat(_) => write!(f, "SlotFunc::SeqConcat(...)"),
SlotFunc::SeqRepeat(_) => write!(f, "SlotFunc::SeqRepeat(...)"),
SlotFunc::SeqItem(_) => write!(f, "SlotFunc::SeqItem(...)"),
SlotFunc::SeqSetItem(_) => write!(f, "SlotFunc::SeqSetItem(...)"),
SlotFunc::SeqDelItem(_) => write!(f, "SlotFunc::SeqDelItem(...)"),
SlotFunc::SeqContains(_) => write!(f, "SlotFunc::SeqContains(...)"),
SlotFunc::MapLength(_) => write!(f, "SlotFunc::MapLength(...)"),
SlotFunc::MapSubscript(_) => write!(f, "SlotFunc::MapSubscript(...)"),
SlotFunc::MapSetSubscript(_) => write!(f, "SlotFunc::MapSetSubscript(...)"),
SlotFunc::MapDelSubscript(_) => write!(f, "SlotFunc::MapDelSubscript(...)"),
SlotFunc::NumBoolean(_) => write!(f, "SlotFunc::NumBoolean(...)"),
SlotFunc::NumUnary(_) => write!(f, "SlotFunc::NumUnary(...)"),
SlotFunc::NumBinary(_) => write!(f, "SlotFunc::NumBinary(...)"),
SlotFunc::NumBinaryRight(_) => write!(f, "SlotFunc::NumBinaryRight(...)"),
SlotFunc::NumTernary(_) => write!(f, "SlotFunc::NumTernary(...)"),
SlotFunc::NumTernaryRight(_) => write!(f, "SlotFunc::NumTernaryRight(...)"),
}
}
}
impl SlotFunc {
pub fn call(&self, obj: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
match self {
SlotFunc::Init(func) => {
func(obj, args, vm)?;
Ok(vm.ctx.none())
}
SlotFunc::Hash(func) => {
if !args.args.is_empty() || !args.kwargs.is_empty() {
return Err(vm.new_type_error("__hash__() takes no arguments (1 given)"));
}
let hash = func(&obj, vm)?;
Ok(vm.ctx.new_int(hash).into())
}
SlotFunc::Repr(func) | SlotFunc::Str(func) => {
if !args.args.is_empty() || !args.kwargs.is_empty() {
let name = match self {
SlotFunc::Repr(_) => "__repr__",
SlotFunc::Str(_) => "__str__",
_ => unreachable!(),
};
return Err(vm.new_type_error(format!("{name}() takes no arguments (1 given)")));
}
let s = func(&obj, vm)?;
Ok(s.into())
}
SlotFunc::Iter(func) => {
if !args.args.is_empty() || !args.kwargs.is_empty() {
return Err(vm.new_type_error("__iter__() takes no arguments (1 given)"));
}
func(obj, vm)
}
SlotFunc::IterNext(func) => {
if !args.args.is_empty() || !args.kwargs.is_empty() {
return Err(vm.new_type_error("__next__() takes no arguments (1 given)"));
}
func(&obj, vm).to_pyresult(vm)
}
SlotFunc::Call(func) => func(&obj, args, vm),
SlotFunc::Del(func) => {
if !args.args.is_empty() || !args.kwargs.is_empty() {
return Err(vm.new_type_error("__del__() takes no arguments (1 given)"));
}
func(&obj, vm)?;
Ok(vm.ctx.none())
}
SlotFunc::GetAttro(func) => {
let (name,): (PyRef<PyStr>,) = args.bind(vm)?;
func(&obj, &name, vm)
}
SlotFunc::SetAttro(func) => {
let (name, value): (PyRef<PyStr>, PyObjectRef) = args.bind(vm)?;
func(&obj, &name, PySetterValue::Assign(value), vm)?;
Ok(vm.ctx.none())
}
SlotFunc::DelAttro(func) => {
let (name,): (PyRef<PyStr>,) = args.bind(vm)?;
func(&obj, &name, PySetterValue::Delete, vm)?;
Ok(vm.ctx.none())
}
SlotFunc::RichCompare(func, op) => {
let (other,): (PyObjectRef,) = args.bind(vm)?;
func(&obj, &other, *op, vm).map(|r| match r {
crate::function::Either::A(obj) => obj,
crate::function::Either::B(cmp_val) => cmp_val.to_pyobject(vm),
})
}
SlotFunc::DescrGet(func) => {
let (instance, owner): (PyObjectRef, crate::function::OptionalArg<PyObjectRef>) =
args.bind(vm)?;
let owner = owner.into_option();
let instance_opt = if vm.is_none(&instance) {
None
} else {
Some(instance)
};
func(obj, instance_opt, owner, vm)
}
SlotFunc::DescrSet(func) => {
let (instance, value): (PyObjectRef, PyObjectRef) = args.bind(vm)?;
func(&obj, instance, PySetterValue::Assign(value), vm)?;
Ok(vm.ctx.none())
}
SlotFunc::DescrDel(func) => {
let (instance,): (PyObjectRef,) = args.bind(vm)?;
func(&obj, instance, PySetterValue::Delete, vm)?;
Ok(vm.ctx.none())
}
SlotFunc::SeqLength(func) => {
args.bind::<()>(vm)?;
let len = func(obj.sequence_unchecked(), vm)?;
Ok(vm.ctx.new_int(len).into())
}
SlotFunc::SeqConcat(func) => {
let (other,): (PyObjectRef,) = args.bind(vm)?;
func(obj.sequence_unchecked(), &other, vm)
}
SlotFunc::SeqRepeat(func) => {
let (n,): (ArgSize,) = args.bind(vm)?;
func(obj.sequence_unchecked(), n.into(), vm)
}
SlotFunc::SeqItem(func) => {
let (index,): (isize,) = args.bind(vm)?;
func(obj.sequence_unchecked(), index, vm)
}
SlotFunc::SeqSetItem(func) => {
let (index, value): (isize, PyObjectRef) = args.bind(vm)?;
func(obj.sequence_unchecked(), index, Some(value), vm)?;
Ok(vm.ctx.none())
}
SlotFunc::SeqDelItem(func) => {
let (index,): (isize,) = args.bind(vm)?;
func(obj.sequence_unchecked(), index, None, vm)?;
Ok(vm.ctx.none())
}
SlotFunc::SeqContains(func) => {
let (item,): (PyObjectRef,) = args.bind(vm)?;
let result = func(obj.sequence_unchecked(), &item, vm)?;
Ok(vm.ctx.new_bool(result).into())
}
SlotFunc::MapLength(func) => {
args.bind::<()>(vm)?;
let len = func(obj.mapping_unchecked(), vm)?;
Ok(vm.ctx.new_int(len).into())
}
SlotFunc::MapSubscript(func) => {
let (key,): (PyObjectRef,) = args.bind(vm)?;
func(obj.mapping_unchecked(), &key, vm)
}
SlotFunc::MapSetSubscript(func) => {
let (key, value): (PyObjectRef, PyObjectRef) = args.bind(vm)?;
func(obj.mapping_unchecked(), &key, Some(value), vm)?;
Ok(vm.ctx.none())
}
SlotFunc::MapDelSubscript(func) => {
let (key,): (PyObjectRef,) = args.bind(vm)?;
func(obj.mapping_unchecked(), &key, None, vm)?;
Ok(vm.ctx.none())
}
SlotFunc::NumBoolean(func) => {
args.bind::<()>(vm)?;
let result = func(obj.number(), vm)?;
Ok(vm.ctx.new_bool(result).into())
}
SlotFunc::NumUnary(func) => {
args.bind::<()>(vm)?;
func(obj.number(), vm)
}
SlotFunc::NumBinary(func) => {
let (other,): (PyObjectRef,) = args.bind(vm)?;
func(&obj, &other, vm)
}
SlotFunc::NumBinaryRight(func) => {
let (other,): (PyObjectRef,) = args.bind(vm)?;
func(&other, &obj, vm) }
SlotFunc::NumTernary(func) => {
let (y, z): (PyObjectRef, crate::function::OptionalArg<PyObjectRef>) =
args.bind(vm)?;
let z = z.unwrap_or_else(|| vm.ctx.none());
func(&obj, &y, &z, vm)
}
SlotFunc::NumTernaryRight(func) => {
let (y, z): (PyObjectRef, crate::function::OptionalArg<PyObjectRef>) =
args.bind(vm)?;
let z = z.unwrap_or_else(|| vm.ctx.none());
func(&y, &obj, &z, vm) }
}
}
}
#[pyclass(name = "wrapper_descriptor", module = false)]
#[derive(Debug)]
pub struct PyWrapper {
pub typ: &'static Py<PyType>,
pub name: &'static PyStrInterned,
pub wrapped: SlotFunc,
pub doc: Option<&'static str>,
}
impl PyPayload for PyWrapper {
fn class(ctx: &Context) -> &'static Py<PyType> {
ctx.types.wrapper_descriptor_type
}
}
impl GetDescriptor for PyWrapper {
fn descr_get(
zelf: PyObjectRef,
obj: Option<PyObjectRef>,
_cls: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
match obj {
None => Ok(zelf),
Some(obj) => {
let zelf = zelf.downcast::<Self>().unwrap();
Ok(PyMethodWrapper { wrapper: zelf, obj }.into_pyobject(vm))
}
}
}
}
impl Callable for PyWrapper {
type Args = FuncArgs;
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let (obj, rest): (PyObjectRef, FuncArgs) = args.bind(vm)?;
if !obj.fast_isinstance(zelf.typ) {
return Err(vm.new_type_error(format!(
"descriptor '{}' requires a '{}' object but received a '{}'",
zelf.name.as_str(),
zelf.typ.name(),
obj.class().name()
)));
}
zelf.wrapped.call(obj, rest, vm)
}
}
#[pyclass(
with(GetDescriptor, Callable, Representable),
flags(DISALLOW_INSTANTIATION)
)]
impl PyWrapper {
#[pygetset]
fn __name__(&self) -> &'static PyStrInterned {
self.name
}
#[pygetset]
fn __qualname__(&self) -> String {
format!("{}.{}", self.typ.name(), self.name)
}
#[pygetset]
fn __objclass__(&self) -> PyTypeRef {
self.typ.to_owned()
}
#[pygetset]
fn __doc__(&self) -> Option<&'static str> {
self.doc
}
}
impl Representable for PyWrapper {
#[inline]
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
Ok(format!(
"<slot wrapper '{}' of '{}' objects>",
zelf.name.as_str(),
zelf.typ.name()
))
}
}
#[pyclass(name = "method-wrapper", module = false, traverse)]
#[derive(Debug)]
pub struct PyMethodWrapper {
pub wrapper: PyRef<PyWrapper>,
#[pytraverse(skip)]
pub obj: PyObjectRef,
}
impl PyPayload for PyMethodWrapper {
fn class(ctx: &Context) -> &'static Py<PyType> {
ctx.types.method_wrapper_type
}
}
impl Callable for PyMethodWrapper {
type Args = FuncArgs;
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
if !zelf.obj.fast_isinstance(zelf.wrapper.typ) {
return Err(vm.new_type_error(format!(
"descriptor '{}' requires a '{}' object but received a '{}'",
zelf.wrapper.name.as_str(),
zelf.wrapper.typ.name(),
zelf.obj.class().name()
)));
}
zelf.wrapper.wrapped.call(zelf.obj.clone(), args, vm)
}
}
#[pyclass(
with(Callable, Representable, Hashable, Comparable),
flags(DISALLOW_INSTANTIATION)
)]
impl PyMethodWrapper {
#[pygetset]
fn __self__(&self) -> PyObjectRef {
self.obj.clone()
}
#[pygetset]
fn __name__(&self) -> &'static PyStrInterned {
self.wrapper.name
}
#[pygetset]
fn __objclass__(&self) -> PyTypeRef {
self.wrapper.typ.to_owned()
}
#[pymethod]
fn __reduce__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
let builtins_getattr = vm.builtins.get_attr("getattr", vm)?;
Ok(vm
.ctx
.new_tuple(vec![
builtins_getattr,
vm.ctx
.new_tuple(vec![
zelf.obj.clone(),
vm.ctx.new_str(zelf.wrapper.name.as_str()).into(),
])
.into(),
])
.into())
}
}
impl Representable for PyMethodWrapper {
#[inline]
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
Ok(format!(
"<method-wrapper '{}' of {} object at {:#x}>",
zelf.wrapper.name.as_str(),
zelf.obj.class().name(),
zelf.obj.get_id()
))
}
}
impl Hashable for PyMethodWrapper {
fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
let obj_hash = zelf.obj.hash(vm)?;
let wrapper_hash = zelf.wrapper.as_object().get_id() as PyHash;
Ok(obj_hash ^ wrapper_hash)
}
}
impl Comparable for PyMethodWrapper {
fn cmp(
zelf: &Py<Self>,
other: &PyObject,
op: PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<crate::function::PyComparisonValue> {
op.eq_only(|| {
let other = class_or_notimplemented!(Self, other);
let eq = zelf.wrapper.is(&other.wrapper) && vm.bool_eq(&zelf.obj, &other.obj)?;
Ok(eq.into())
})
}
}