mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
* Honor close unraisable and deallocator messages A failed close lookup on a yield-from target is unraisable. Ignored GeneratorExit from finalize gets a traceback and the closing-generator err_msg. __del__ uses the deallocator message. Assisted-by: Grok:grok-4.6 * address review: claim, ag_running, frame steal Keep the running claim through close() cleanup. ag_running reads running_async. aclose ignore-yield does not mark the generator closed. Steal the frame slot before uniqueness and expose it with try_to_owned. Assisted-by: Grok:grok-4.6 * Honor wrapper docs, yield assign, throw context method-wrapper exposes the slot __doc__. Parenthesized yield assignment uses the invalid-target messages. throw() restores without overwriting __context__, then chains only from the generator's own exc_info slot. Assisted-by: Grok:grok-4.6 * Remove passing expectedFailure markers method-wrapper docs, deallocator messages, and help pager output now match, so drop the leftover markers. Assisted-by: Grok:grok-4.6 * Honor wrapper signatures and assignment diagnostics Slot docs include text signatures. method-wrapper compares and hashes by identity. Keyword genexp and if/while assignment use the invalid-target messages. Assisted-by: Grok:grok-4.6 * address review: aclose, unraisable, frame publish aclose ignore-yield does not close the generator. Format unraisable object is None. close() leaves traceback to the caller; finalize attaches the generator frame. Publish the iframe before the lock-free slot. Assisted-by: Grok:grok-4.6 * Honor member descriptors and wrapper annotations Assisted-by: Grok:grok-4.6 * address review: getitem, bool doc, if assign Assisted-by: Grok:grok-4.6 * Honor eval-mode assignment invalid syntax Assisted-by: Grok:grok-4.6 * Supply getset and member docs from rustpython-doc Assisted-by: Grok:grok-4.6 * Enable rustls SSL on WASI targets Compile the rustls _ssl module for WASI and stub socket waits plus the native certificate store. Assisted-by: Grok:grok-4.6 * Add browser TLS _ssl in rustpython-wasm Register rustls _ssl and a MemoryBIO-oriented _socket from rustpython-wasm. Use rustls-rustcrypto because ring cannot compile for wasm32-unknown-unknown. Assisted-by: Grok:grok-4.6 * Define SSL protocol and cert constants as enums Keep OpenSSL bitmask defines as integer constants. Assisted-by: Grok:grok-4.6 * Ignore rustls deps cargo-shear cannot see The rustpython-wasm _ssl sources are compiled through a symlink, so shear does not count their crate uses. Assisted-by: Grok:grok-4.6 * Replace wasm ssl symlink with a BIO module Drop the stdlib ssl.rs link and keep a rustpython-wasm _ssl that only covers MemoryBIO wrap_bio TLS. Assisted-by: Grok:grok-4.6
343 lines
9.6 KiB
Rust
343 lines
9.6 KiB
Rust
use crate::{
|
|
Context, Py, PyObjectRef, PyPayload, PyRef, VirtualMachine,
|
|
builtins::{
|
|
PyType,
|
|
builtin_func::{PyNativeFunction, PyNativeMethod},
|
|
descriptor::PyMethodDescriptor,
|
|
},
|
|
class::PyClassDef,
|
|
function::{IntoPyNativeFn, PyNativeFn},
|
|
};
|
|
|
|
bitflags::bitflags! {
|
|
// METH_XXX flags in CPython
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub struct PyMethodFlags: u32 {
|
|
const VARARGS = 0x0001;
|
|
const KEYWORDS = 0x0002;
|
|
// METH_NOARGS and METH_O must not be combined with the flags above.
|
|
const NOARGS = 0x0004;
|
|
const O = 0x0008;
|
|
|
|
// METH_CLASS and METH_STATIC are a little different; these control
|
|
// the construction of methods for a class. These cannot be used for
|
|
// functions in modules.
|
|
const CLASS = 0x0010;
|
|
const STATIC = 0x0020;
|
|
|
|
// METH_COEXIST allows a method to be entered even though a slot has
|
|
// already filled the entry. When defined, the flag allows a separate
|
|
// method, "__contains__" for example, to coexist with a defined
|
|
// slot like sq_contains.
|
|
const COEXIST = 0x0040;
|
|
|
|
// if not Py_LIMITED_API
|
|
const FASTCALL = 0x0080;
|
|
|
|
// This bit is preserved for Stackless Python
|
|
// const STACKLESS = 0x0100;
|
|
|
|
// METH_METHOD means the function stores an
|
|
// additional reference to the class that defines it;
|
|
// both self and class are passed to it.
|
|
// It uses PyCMethodObject instead of PyCFunctionObject.
|
|
// May not be combined with METH_NOARGS, METH_O, METH_CLASS or METH_STATIC.
|
|
const METHOD = 0x0200;
|
|
}
|
|
}
|
|
|
|
impl PyMethodFlags {
|
|
// FIXME: macro temp
|
|
pub const EMPTY: Self = Self::empty();
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! define_methods {
|
|
// TODO: more flexible syntax
|
|
($($name:literal => $func:ident as $flags:ident),+) => {
|
|
vec![ $( $crate::function::PyMethodDef {
|
|
name: $name,
|
|
func: $crate::function::static_func($func),
|
|
flags: $crate::function::PyMethodFlags::$flags,
|
|
doc: None,
|
|
}),+ ]
|
|
};
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct PyMethodDef {
|
|
pub name: &'static str, // TODO: interned
|
|
pub func: &'static dyn PyNativeFn,
|
|
pub flags: PyMethodFlags,
|
|
pub doc: Option<&'static str>, // TODO: interned
|
|
}
|
|
|
|
impl PyMethodDef {
|
|
#[inline]
|
|
pub const fn new_const<Kind>(
|
|
name: &'static str,
|
|
func: impl IntoPyNativeFn<Kind>,
|
|
flags: PyMethodFlags,
|
|
doc: Option<&'static str>,
|
|
) -> Self {
|
|
Self {
|
|
name,
|
|
func: super::static_func(func),
|
|
flags,
|
|
doc,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub const fn new_raw_const(
|
|
name: &'static str,
|
|
func: impl PyNativeFn,
|
|
flags: PyMethodFlags,
|
|
doc: Option<&'static str>,
|
|
) -> Self {
|
|
Self {
|
|
name,
|
|
func: super::static_raw_func(func),
|
|
flags,
|
|
doc,
|
|
}
|
|
}
|
|
|
|
pub fn to_proper_method(
|
|
&'static self,
|
|
class: &'static Py<PyType>,
|
|
ctx: &Context,
|
|
) -> PyObjectRef {
|
|
if self.flags.contains(PyMethodFlags::METHOD) {
|
|
self.build_method(ctx, class).into()
|
|
} else if self.flags.contains(PyMethodFlags::CLASS) {
|
|
self.build_classmethod(ctx, class).into()
|
|
} else if self.flags.contains(PyMethodFlags::STATIC) {
|
|
self.build_staticmethod(ctx, class).into()
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub const fn to_function(&'static self) -> PyNativeFunction {
|
|
PyNativeFunction {
|
|
zelf: None,
|
|
value: self,
|
|
module: None,
|
|
_method_def_owner: None,
|
|
}
|
|
}
|
|
|
|
pub fn to_method(
|
|
&'static self,
|
|
class: &'static Py<PyType>,
|
|
ctx: &Context,
|
|
) -> PyMethodDescriptor {
|
|
PyMethodDescriptor::new(self, class, ctx)
|
|
}
|
|
|
|
pub const fn to_bound_method(
|
|
&'static self,
|
|
obj: PyObjectRef,
|
|
class: &'static Py<PyType>,
|
|
) -> PyNativeMethod {
|
|
PyNativeMethod {
|
|
func: PyNativeFunction {
|
|
zelf: Some(obj),
|
|
value: self,
|
|
module: None,
|
|
_method_def_owner: None,
|
|
},
|
|
class,
|
|
}
|
|
}
|
|
|
|
pub fn build_function(&'static self, ctx: &Context) -> PyRef<PyNativeFunction> {
|
|
self.to_function().into_ref(ctx)
|
|
}
|
|
|
|
pub fn build_bound_function(
|
|
&'static self,
|
|
ctx: &Context,
|
|
obj: PyObjectRef,
|
|
) -> PyRef<PyNativeFunction> {
|
|
let function = PyNativeFunction {
|
|
zelf: Some(obj),
|
|
value: self,
|
|
module: None,
|
|
_method_def_owner: None,
|
|
};
|
|
PyRef::new_ref(
|
|
function,
|
|
ctx.types.builtin_function_or_method_type.to_owned(),
|
|
None,
|
|
)
|
|
}
|
|
|
|
pub fn build_method(
|
|
&'static self,
|
|
ctx: &Context,
|
|
class: &'static Py<PyType>,
|
|
) -> PyRef<PyMethodDescriptor> {
|
|
debug_assert!(self.flags.contains(PyMethodFlags::METHOD));
|
|
let method = self.to_method(class, ctx);
|
|
PyRef::new_ref(method, ctx.types.method_descriptor_type.to_owned(), None)
|
|
}
|
|
|
|
pub fn build_bound_method(
|
|
&'static self,
|
|
ctx: &Context,
|
|
obj: PyObjectRef,
|
|
class: &'static Py<PyType>,
|
|
) -> PyRef<PyNativeMethod> {
|
|
PyRef::new_ref(
|
|
self.to_bound_method(obj, class),
|
|
ctx.types.builtin_function_or_method_type.to_owned(),
|
|
None,
|
|
)
|
|
}
|
|
|
|
pub fn build_classmethod(
|
|
&'static self,
|
|
ctx: &Context,
|
|
class: &'static Py<PyType>,
|
|
) -> PyRef<PyMethodDescriptor> {
|
|
PyRef::new_ref(
|
|
self.to_method(class, ctx),
|
|
ctx.types.method_descriptor_type.to_owned(),
|
|
None,
|
|
)
|
|
}
|
|
|
|
pub fn build_staticmethod(
|
|
&'static self,
|
|
ctx: &Context,
|
|
class: &'static Py<PyType>,
|
|
) -> PyRef<PyNativeMethod> {
|
|
debug_assert!(self.flags.contains(PyMethodFlags::STATIC));
|
|
// Set zelf to the class (m_self = type for static methods).
|
|
// Callable::call skips prepending when STATIC flag is set.
|
|
let func = PyNativeFunction {
|
|
zelf: Some(class.to_owned().into()),
|
|
value: self,
|
|
module: None,
|
|
_method_def_owner: None,
|
|
};
|
|
PyNativeMethod { func, class }.into_ref(ctx)
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
#[must_use]
|
|
pub const fn __const_concat_arrays<const SUM_LEN: usize>(
|
|
method_groups: &[&[Self]],
|
|
) -> [Self; SUM_LEN] {
|
|
const NULL_METHOD: PyMethodDef = PyMethodDef {
|
|
name: "",
|
|
func: &|_, _, _| unreachable!(),
|
|
flags: PyMethodFlags::empty(),
|
|
doc: None,
|
|
};
|
|
let mut all_methods = [NULL_METHOD; SUM_LEN];
|
|
let mut all_idx = 0;
|
|
let mut group_idx = 0;
|
|
while group_idx < method_groups.len() {
|
|
let group = method_groups[group_idx];
|
|
let mut method_idx = 0;
|
|
while method_idx < group.len() {
|
|
all_methods[all_idx] = group[method_idx].const_copy();
|
|
method_idx += 1;
|
|
all_idx += 1;
|
|
}
|
|
group_idx += 1;
|
|
}
|
|
all_methods
|
|
}
|
|
|
|
#[must_use]
|
|
const fn const_copy(&self) -> Self {
|
|
Self {
|
|
name: self.name,
|
|
func: self.func,
|
|
flags: self.flags,
|
|
doc: self.doc,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl core::fmt::Debug for PyMethodDef {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
f.debug_struct("PyMethodDef")
|
|
.field("name", &self.name)
|
|
.field(
|
|
"func",
|
|
&(unsafe {
|
|
core::mem::transmute::<&dyn PyNativeFn, [usize; 2]>(self.func)[1] as *const u8
|
|
}),
|
|
)
|
|
.field("flags", &self.flags)
|
|
.field("doc", &self.doc)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
// This is not a part of CPython API.
|
|
// But useful to support dynamically generated methods
|
|
#[pyclass(name, module = false, ctx = "method_def")]
|
|
#[derive(Debug)]
|
|
pub struct HeapMethodDef {
|
|
method: PyMethodDef,
|
|
}
|
|
|
|
impl HeapMethodDef {
|
|
#[must_use]
|
|
pub const fn new(method: PyMethodDef) -> Self {
|
|
Self { method }
|
|
}
|
|
}
|
|
|
|
impl Py<HeapMethodDef> {
|
|
pub(crate) unsafe fn method(&self) -> &'static PyMethodDef {
|
|
unsafe { &*(&self.method as *const _) }
|
|
}
|
|
|
|
pub fn build_function(
|
|
&self,
|
|
vm: &VirtualMachine,
|
|
zelf: Option<PyObjectRef>,
|
|
) -> PyRef<PyNativeFunction> {
|
|
let mut function = unsafe { self.method() }.to_function();
|
|
function._method_def_owner = Some(self.to_owned().into());
|
|
function.zelf = zelf;
|
|
PyRef::new_ref(
|
|
function,
|
|
vm.ctx.types.builtin_function_or_method_type.to_owned(),
|
|
None,
|
|
)
|
|
}
|
|
|
|
pub fn build_method(
|
|
&self,
|
|
class: &'static Py<PyType>,
|
|
vm: &VirtualMachine,
|
|
) -> PyRef<PyMethodDescriptor> {
|
|
let mut function = unsafe { self.method() }.to_method(class, &vm.ctx);
|
|
function._method_def_owner = Some(self.to_owned().into());
|
|
PyRef::new_ref(
|
|
function,
|
|
vm.ctx.types.method_descriptor_type.to_owned(),
|
|
None,
|
|
)
|
|
}
|
|
}
|
|
|
|
#[pyclass]
|
|
impl HeapMethodDef {}
|
|
|
|
pub(crate) fn init(ctx: &'static Context) {
|
|
// TODO: Should we extend the class instead of interning only the name?
|
|
// HeapMethodDef::extend_class(ctx, ctx.types.method_def);
|
|
|
|
let _ = ctx.intern_str(HeapMethodDef::NAME);
|
|
}
|