Thanks to visit codestin.com
Credit goes to GitHub.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 12 additions & 30 deletions crates/vm/src/builtins/builtin_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::{
use alloc::fmt;

// PyCFunctionObject in CPython
#[repr(C)]
#[pyclass(name = "builtin_function_or_method", module = false)]
pub struct PyNativeFunction {
pub(crate) value: &'static PyMethodDef,
Expand Down Expand Up @@ -146,7 +147,9 @@ impl Representable for PyNativeFunction {
}

// `PyCMethodObject` in CPython
#[pyclass(name = "builtin_method", module = false, base = PyNativeFunction, ctx = "builtin_method_type")]
// repr(C) ensures `func` is at offset 0, allowing safe cast from PyNativeMethod to PyNativeFunction
#[repr(C)]
#[pyclass(name = "builtin_function_or_method", module = false, base = PyNativeFunction, ctx = "builtin_function_or_method_type")]
pub struct PyNativeMethod {
pub(crate) func: PyNativeFunction,
pub(crate) class: &'static Py<PyType>, // TODO: the actual life is &'self
Expand All @@ -157,34 +160,8 @@ pub struct PyNativeMethod {
flags(HAS_DICT, DISALLOW_INSTANTIATION)
)]
impl PyNativeMethod {
#[pygetset]
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]
fn __reduce__(
&self,
vm: &VirtualMachine,
) -> PyResult<(PyObjectRef, (PyObjectRef, &'static str))> {
// TODO: return (getattr, (self.object, self.name)) if this is a method
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]
fn __self__(zelf: PyRef<Self>, _vm: &VirtualMachine) -> Option<PyObjectRef> {
zelf.func.zelf.clone()
}
// __qualname__, __self__, and __reduce__ are inherited from PyNativeFunction
// via NativeFunctionOrMethod wrapper since we share the same Python type.
}

impl fmt::Debug for PyNativeMethod {
Expand Down Expand Up @@ -246,15 +223,20 @@ impl Representable 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);
PyNativeMethod::extend_class(context, context.types.builtin_function_or_method_type);
}

/// Wrapper that provides access to the common PyNativeFunction data
/// for both PyNativeFunction and PyNativeMethod (which has func as its first field).
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) {
// Both PyNativeFunction and PyNativeMethod share the same type now.
// PyNativeMethod has `func: PyNativeFunction` as its first field,
// so we can safely treat the data pointer as PyNativeFunction for reading.
Ok(Self(unsafe { obj.downcast_unchecked() }))
} else {
Err(vm.new_downcast_type_error(class, &obj))
Expand Down
33 changes: 33 additions & 0 deletions crates/vm/src/builtins/capsule.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use super::PyType;
use crate::{Context, Py, PyPayload, PyResult, class::PyClassImpl, types::Representable};

/// PyCapsule - a container for C pointers.
/// In RustPython, this is a minimal implementation for compatibility.
#[pyclass(module = false, name = "PyCapsule")]
#[derive(Debug, Clone, Copy)]
pub struct PyCapsule {
// Capsules store opaque pointers; we don't expose the actual pointer functionality
// since RustPython doesn't have the same C extension model as CPython.
_private: (),
}

impl PyPayload for PyCapsule {
#[inline]
fn class(ctx: &Context) -> &'static Py<PyType> {
ctx.types.capsule_type
}
}

#[pyclass(with(Representable), flags(DISALLOW_INSTANTIATION))]
impl PyCapsule {}

impl Representable for PyCapsule {
#[inline]
fn repr_str(_zelf: &Py<Self>, _vm: &crate::VirtualMachine) -> PyResult<String> {
Ok("<capsule object>".to_string())
}
}

pub fn init(context: &Context) {
PyCapsule::extend_class(context, context.types.capsule_type);
}
2 changes: 2 additions & 0 deletions crates/vm/src/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub(crate) mod bytearray;
pub use bytearray::PyByteArray;
pub(crate) mod bytes;
pub use bytes::{PyBytes, PyBytesRef};
pub(crate) mod capsule;
pub use capsule::PyCapsule;
pub(crate) mod classmethod;
pub use classmethod::PyClassMethod;
pub(crate) mod code;
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/function/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl PyMethodDef {
) -> PyRef<PyNativeMethod> {
PyRef::new_ref(
self.to_bound_method(obj, class),
ctx.types.builtin_method_type.to_owned(),
ctx.types.builtin_function_or_method_type.to_owned(),
None,
)
}
Expand Down
157 changes: 157 additions & 0 deletions crates/vm/src/stdlib/_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//! Implementation of the `_types` module.
//!
//! This module exposes built-in types that are used by the `types` module.

pub(crate) use _types::module_def;

#[pymodule]
#[allow(non_snake_case)]
mod _types {
use crate::{PyObjectRef, VirtualMachine};

#[pyattr]
fn AsyncGeneratorType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.async_generator.to_owned().into()
}

#[pyattr]
fn BuiltinFunctionType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx
.types
.builtin_function_or_method_type
.to_owned()
.into()
}

#[pyattr]
fn BuiltinMethodType(vm: &VirtualMachine) -> PyObjectRef {
// Same as BuiltinFunctionType in CPython
vm.ctx
.types
.builtin_function_or_method_type
.to_owned()
.into()
}

#[pyattr]
fn CapsuleType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.capsule_type.to_owned().into()
}

#[pyattr]
fn CellType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.cell_type.to_owned().into()
}

#[pyattr]
fn CodeType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.code_type.to_owned().into()
}

#[pyattr]
fn CoroutineType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.coroutine_type.to_owned().into()
}

#[pyattr]
fn EllipsisType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.ellipsis_type.to_owned().into()
}

#[pyattr]
fn FrameType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.frame_type.to_owned().into()
}

#[pyattr]
fn FunctionType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.function_type.to_owned().into()
}

#[pyattr]
fn GeneratorType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.generator_type.to_owned().into()
}

#[pyattr]
fn GenericAlias(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.generic_alias_type.to_owned().into()
}

#[pyattr]
fn GetSetDescriptorType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.getset_type.to_owned().into()
}

#[pyattr]
fn LambdaType(vm: &VirtualMachine) -> PyObjectRef {
// Same as FunctionType in CPython
vm.ctx.types.function_type.to_owned().into()
}

#[pyattr]
fn MappingProxyType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.mappingproxy_type.to_owned().into()
}

#[pyattr]
fn MemberDescriptorType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.member_descriptor_type.to_owned().into()
}

#[pyattr]
fn MethodDescriptorType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.method_descriptor_type.to_owned().into()
}

#[pyattr]
fn ClassMethodDescriptorType(vm: &VirtualMachine) -> PyObjectRef {
// TODO: implement as separate type
vm.ctx.types.method_descriptor_type.to_owned().into()
}
Comment on lines +107 to +111
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

ClassMethodDescriptorType should not alias MethodDescriptorType.

On Line 109, this TODO means types.ClassMethodDescriptorType becomes identical to types.MethodDescriptorType, which diverges from CPython and can break code that relies on type identity checks. Please introduce a dedicated type (or avoid exposing this attribute until it exists).

🤖 Prompt for AI Agents
In `@crates/vm/src/stdlib/_types.rs` around lines 107 - 111, The
ClassMethodDescriptorType function currently returns
vm.ctx.types.method_descriptor_type, causing types.ClassMethodDescriptorType to
be identical to MethodDescriptorType; instead either register and return a
distinct type object for class method descriptors (e.g. create and expose a new
vm.ctx.types.class_method_descriptor_type via the same type-registration flow
used for other descriptor types and return that from ClassMethodDescriptorType)
or remove/withhold the pyattr until the dedicated type is implemented; update
the ClassMethodDescriptorType implementation to return the new unique type
symbol (class_method_descriptor_type) rather than method_descriptor_type so type
identity matches CPython.


#[pyattr]
fn MethodType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.bound_method_type.to_owned().into()
}

#[pyattr]
fn MethodWrapperType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.method_wrapper_type.to_owned().into()
}

#[pyattr]
fn ModuleType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.module_type.to_owned().into()
}

#[pyattr]
fn NoneType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.none_type.to_owned().into()
}

#[pyattr]
fn NotImplementedType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.not_implemented_type.to_owned().into()
}

#[pyattr]
fn SimpleNamespace(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.namespace_type.to_owned().into()
}

#[pyattr]
fn TracebackType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.traceback_type.to_owned().into()
}

#[pyattr]
fn UnionType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.union_type.to_owned().into()
}

#[pyattr]
fn WrapperDescriptorType(vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.types.wrapper_descriptor_type.to_owned().into()
}
}
2 changes: 2 additions & 0 deletions crates/vm/src/stdlib/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod _abc;
mod _types;
#[cfg(feature = "ast")]
pub(crate) mod ast;
pub mod atexit;
Expand Down Expand Up @@ -72,6 +73,7 @@ use crate::{Context, builtins::PyModuleDef};
pub fn builtin_module_defs(ctx: &Context) -> Vec<&'static PyModuleDef> {
vec![
_abc::module_def(ctx),
_types::module_def(ctx),
#[cfg(feature = "ast")]
ast::module_def(ctx),
atexit::module_def(ctx),
Expand Down
27 changes: 19 additions & 8 deletions crates/vm/src/types/zoo.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
Py,
builtins::{
asyncgenerator, bool_, builtin_func, bytearray, bytes, classmethod, code, complex,
asyncgenerator, bool_, builtin_func, bytearray, bytes, capsule, classmethod, code, complex,
coroutine, descriptor, dict, enumerate, filter, float, frame, function, generator,
genericalias, getset, int, interpolation, iter, list, map, mappingproxy, memory, module,
namespace, object, property, pystr, range, set, singletons, slice, staticmethod, super_,
Expand All @@ -28,6 +28,7 @@ pub struct TypeZoo {
pub bytearray_iterator_type: &'static Py<PyType>,
pub bool_type: &'static Py<PyType>,
pub callable_iterator: &'static Py<PyType>,
pub capsule_type: &'static Py<PyType>,
pub cell_type: &'static Py<PyType>,
pub classmethod_type: &'static Py<PyType>,
pub code_type: &'static Py<PyType>,
Expand Down Expand Up @@ -108,12 +109,20 @@ impl TypeZoo {
#[cold]
pub(crate) fn init() -> Self {
let (type_type, object_type, weakref_type) = crate::object::init_type_hierarchy();
// the order matters for type, object, weakref, and int - must be initialized first
let type_type = type_::PyType::init_manually(type_type);
let object_type = object::PyBaseObject::init_manually(object_type);
let weakref_type = weakref::PyWeak::init_manually(weakref_type);
let int_type = int::PyInt::init_builtin_type();

// builtin_function_or_method and builtin_method share the same type (CPython behavior)
let builtin_function_or_method_type = builtin_func::PyNativeFunction::init_builtin_type();

Self {
// the order matters for type, object, weakref, and int
type_type: type_::PyType::init_manually(type_type),
object_type: object::PyBaseObject::init_manually(object_type),
weakref_type: weakref::PyWeak::init_manually(weakref_type),
int_type: int::PyInt::init_builtin_type(),
type_type,
object_type,
weakref_type,
int_type,

// types exposed as builtins
bool_type: bool_::PyBool::init_builtin_type(),
Expand Down Expand Up @@ -147,11 +156,12 @@ impl TypeZoo {
asyncgenerator::PyAsyncGenWrappedValue::init_builtin_type(),
anext_awaitable: asyncgenerator::PyAnextAwaitable::init_builtin_type(),
bound_method_type: function::PyBoundMethod::init_builtin_type(),
builtin_function_or_method_type: builtin_func::PyNativeFunction::init_builtin_type(),
builtin_method_type: builtin_func::PyNativeMethod::init_builtin_type(),
builtin_function_or_method_type,
builtin_method_type: builtin_function_or_method_type,
bytearray_iterator_type: bytearray::PyByteArrayIterator::init_builtin_type(),
bytes_iterator_type: bytes::PyBytesIterator::init_builtin_type(),
callable_iterator: iter::PyCallableIterator::init_builtin_type(),
capsule_type: capsule::PyCapsule::init_builtin_type(),
cell_type: function::PyCell::init_builtin_type(),
code_type: code::PyCode::init_builtin_type(),
coroutine_type: coroutine::PyCoroutine::init_builtin_type(),
Expand Down Expand Up @@ -225,6 +235,7 @@ impl TypeZoo {
complex::init(context);
bytes::init(context);
bytearray::init(context);
capsule::init(context);
property::init(context);
getset::init(context);
memory::init(context);
Expand Down
Loading