use super::{type_, PyStrInterned, PyStrRef, PyType};
use crate::{
class::PyClassImpl,
convert::TryFromObject,
function::{FuncArgs, PyComparisonValue, PyMethodDef, PyMethodFlags, PyNativeFn},
types::{Callable, Comparable, Constructor, PyComparisonOp, Representable, Unconstructible},
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
use std::fmt;
#[pyclass(name = "builtin_function_or_method", module = false)]
pub struct PyNativeFunction {
pub(crate) value: &'static PyMethodDef,
pub(crate) zelf: Option<PyObjectRef>,
pub(crate) module: Option<&'static PyStrInterned>, }
impl PyPayload for PyNativeFunction {
fn class(ctx: &Context) -> &'static Py<PyType> {
ctx.types.builtin_function_or_method_type
}
}
impl fmt::Debug for PyNativeFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"builtin function {}.{} ({:?}) self as instance of {:?}",
self.module.map_or("<unknown>", |m| m.as_str()),
self.value.name,
self.value.flags,
self.zelf.as_ref().map(|z| z.class().name().to_owned())
)
}
}
impl PyNativeFunction {
pub fn with_module(mut self, module: &'static PyStrInterned) -> Self {
self.module = Some(module);
self
}
pub fn into_ref(self, ctx: &Context) -> PyRef<Self> {
PyRef::new_ref(
self,
ctx.types.builtin_function_or_method_type.to_owned(),
None,
)
}
pub fn as_func(&self) -> &'static PyNativeFn {
self.value.func
}
}
impl Callable for PyNativeFunction {
type Args = FuncArgs;
#[inline]
fn call(zelf: &Py<Self>, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
if let Some(z) = &zelf.zelf {
args.prepend_arg(z.clone());
}
(zelf.value.func)(vm, args)
}
}
#[pyclass(with(Callable, Constructor), flags(HAS_DICT))]
impl PyNativeFunction {
#[pygetset(magic)]
fn module(zelf: NativeFunctionOrMethod) -> Option<&'static PyStrInterned> {
zelf.0.module
}
#[pygetset(magic)]
fn name(zelf: NativeFunctionOrMethod) -> &'static str {
zelf.0.value.name
}
#[pygetset(magic)]
fn qualname(zelf: NativeFunctionOrMethod, vm: &VirtualMachine) -> PyResult<PyStrRef> {
let zelf = zelf.0;
let flags = zelf.value.flags;
let qualname = if let Some(bound) = &zelf.zelf {
let prefix = if flags.contains(PyMethodFlags::CLASS) {
bound
.get_attr("__qualname__", vm)
.unwrap()
.str(vm)
.unwrap()
.to_string()
} else {
bound.class().name().to_string()
};
vm.ctx.new_str(format!("{}.{}", prefix, &zelf.value.name))
} else {
vm.ctx.intern_str(zelf.value.name).to_owned()
};
Ok(qualname)
}
#[pygetset(magic)]
fn doc(zelf: NativeFunctionOrMethod) -> Option<&'static str> {
zelf.0.value.doc
}
#[pygetset(name = "__self__")]
fn __self__(_zelf: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.none()
}
#[pymethod(magic)]
fn reduce(&self) -> &'static str {
self.value.name
}
#[pymethod(magic)]
fn reduce_ex(zelf: PyObjectRef, _ver: PyObjectRef, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(&zelf, identifier!(vm, __reduce__), ())
}
#[pygetset(magic)]
fn text_signature(zelf: NativeFunctionOrMethod) -> Option<&'static str> {
let doc = zelf.0.value.doc?;
let signature = type_::get_text_signature_from_internal_doc(zelf.0.value.name, doc)?;
Some(signature)
}
}
impl Representable for PyNativeFunction {
#[inline]
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
Ok(format!("<built-in function {}>", zelf.value.name))
}
}
impl Unconstructible for PyNativeFunction {}
#[pyclass(name = "builtin_method", module = false, base = "PyNativeFunction")]
pub struct PyNativeMethod {
pub(crate) func: PyNativeFunction,
pub(crate) class: &'static Py<PyType>, }
#[pyclass(with(Callable, Comparable, Representable), flags(HAS_DICT))]
impl PyNativeMethod {
#[pygetset(magic)]
fn qualname(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
let prefix = zelf.class.name().to_string();
Ok(vm
.ctx
.new_str(format!("{}.{}", prefix, &zelf.func.value.name)))
}
#[pymethod(magic)]
fn reduce(&self, vm: &VirtualMachine) -> PyResult<(PyObjectRef, (PyObjectRef, &'static str))> {
let getattr = vm.builtins.get_attr("getattr", vm)?;
let target = self
.func
.zelf
.clone()
.unwrap_or_else(|| self.class.to_owned().into());
let name = self.func.value.name;
Ok((getattr, (target, name)))
}
#[pygetset(name = "__self__")]
fn __self__(zelf: PyRef<Self>, _vm: &VirtualMachine) -> Option<PyObjectRef> {
zelf.func.zelf.clone()
}
}
impl PyPayload for PyNativeMethod {
fn class(ctx: &Context) -> &'static Py<PyType> {
ctx.types.builtin_method_type
}
}
impl fmt::Debug for PyNativeMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"builtin method of {:?} with {:?}",
&*self.class.name(),
&self.func
)
}
}
impl Comparable for PyNativeMethod {
fn cmp(
zelf: &Py<Self>,
other: &PyObject,
op: PyComparisonOp,
_vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
op.eq_only(|| {
if let Some(other) = other.payload::<Self>() {
let eq = match (zelf.func.zelf.as_ref(), other.func.zelf.as_ref()) {
(Some(z), Some(o)) => z.is(o),
(None, None) => true,
_ => false,
};
let eq = eq && std::ptr::eq(zelf.func.value, other.func.value);
Ok(eq.into())
} else {
Ok(PyComparisonValue::NotImplemented)
}
})
}
}
impl Callable for PyNativeMethod {
type Args = FuncArgs;
#[inline]
fn call(zelf: &Py<Self>, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
if let Some(zelf) = &zelf.func.zelf {
args.prepend_arg(zelf.clone());
}
(zelf.func.value.func)(vm, args)
}
}
impl Representable for PyNativeMethod {
#[inline]
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
Ok(format!(
"<built-in method {} of {} object at ...>",
&zelf.func.value.name,
zelf.class.name()
))
}
}
impl Unconstructible for PyNativeMethod {}
pub fn init(context: &Context) {
PyNativeFunction::extend_class(context, context.types.builtin_function_or_method_type);
PyNativeMethod::extend_class(context, context.types.builtin_method_type);
}
struct NativeFunctionOrMethod(PyRef<PyNativeFunction>);
impl TryFromObject for NativeFunctionOrMethod {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let class = vm.ctx.types.builtin_function_or_method_type;
if obj.fast_isinstance(class) {
Ok(NativeFunctionOrMethod(unsafe { obj.downcast_unchecked() }))
} else {
Err(vm.new_downcast_type_error(class, &obj))
}
}
}