Merge pull request #4873 from youknowone/method

Method overhaul with static PyMethodDef
This commit is contained in:
Jeong, YunWon
2023-05-05 18:53:01 +09:00
committed by GitHub
62 changed files with 1320 additions and 798 deletions

View File

@@ -6,7 +6,7 @@ use crate::util::{
};
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::str::FromStr;
use syn::{
parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Item, Meta, NestedMeta, Result,
@@ -62,7 +62,8 @@ impl FromStr for AttrName {
#[derive(Default)]
struct ImplContext {
impl_extend_items: ItemNursery,
attribute_items: ItemNursery,
method_items: MethodNursery,
getset_items: GetSetNursery,
member_items: MemberNursery,
extend_slots_items: ItemNursery,
@@ -92,6 +93,7 @@ fn extract_items_into_context<'a, Item>(
});
context.errors.ok_or_push(r);
}
context.errors.ok_or_push(context.method_items.validate());
context.errors.ok_or_push(context.getset_items.validate());
context.errors.ok_or_push(context.member_items.validate());
}
@@ -157,18 +159,28 @@ pub(crate) fn impl_pyclass_impl(attr: AttributeArgs, item: Item) -> Result<Token
let ExtractedImplAttrs {
payload: attr_payload,
with_impl,
flags,
with_impl,
with_method_defs,
with_slots,
} = extract_impl_attrs(attr, &impl_ty)?;
let payload_ty = attr_payload.unwrap_or(payload_guess);
let method_def = &context.method_items;
let getset_impl = &context.getset_items;
let member_impl = &context.member_items;
let extend_impl = context.impl_extend_items.validate()?;
let extend_impl = context.attribute_items.validate()?;
let slots_impl = context.extend_slots_items.validate()?;
let class_extensions = &context.class_extensions;
let extra_methods = iter_chain![
parse_quote! {
#[allow(clippy::ptr_arg)]
fn __extend_method_def(
method_defs: &mut Vec<::rustpython_vm::function::PyMethodDef>,
) {
#method_def
}
},
parse_quote! {
fn __extend_py_class(
ctx: &::rustpython_vm::Context,
@@ -202,6 +214,14 @@ pub(crate) fn impl_pyclass_impl(attr: AttributeArgs, item: Item) -> Result<Token
#with_impl
}
#[allow(clippy::ptr_arg)]
fn impl_extend_method_def(
method_defs: &mut Vec<::rustpython_vm::function::PyMethodDef>,
) {
#impl_ty::__extend_method_def(method_defs);
#with_method_defs
}
fn extend_slots(slots: &mut ::rustpython_vm::types::PyTypeSlots) {
#impl_ty::__extend_slots(slots);
#with_slots
@@ -235,9 +255,10 @@ pub(crate) fn impl_pyclass_impl(attr: AttributeArgs, item: Item) -> Result<Token
..
} = extract_impl_attrs(attr, &trai.ident)?;
let method_def = &context.method_items;
let getset_impl = &context.getset_items;
let member_impl = &context.member_items;
let extend_impl = &context.impl_extend_items.validate()?;
let extend_impl = &context.attribute_items.validate()?;
let slots_impl = &context.extend_slots_items.validate()?;
let class_extensions = &context.class_extensions;
let call_extend_slots = if has_extend_slots {
@@ -248,6 +269,14 @@ pub(crate) fn impl_pyclass_impl(attr: AttributeArgs, item: Item) -> Result<Token
quote! {}
};
let extra_methods = iter_chain![
parse_quote! {
#[allow(clippy::ptr_arg)]
fn __extend_method_def(
method_defs: &mut Vec<::rustpython_vm::function::PyMethodDef>,
) {
#method_def
}
},
parse_quote! {
fn __extend_py_class(
ctx: &::rustpython_vm::Context,
@@ -732,51 +761,14 @@ where
let py_name = item_meta.method_name()?;
let sig_doc = text_signature(func.sig(), &py_name);
let tokens = {
let doc = args.attrs.doc().map_or_else(TokenStream::new, |mut doc| {
doc = format_doc(&sig_doc, &doc);
quote!(.with_doc(#doc.to_owned(), ctx))
});
let build_func = match self.inner.attr_name {
AttrName::Method => quote!(.build_method(ctx, class)),
AttrName::ClassMethod => quote!(.build_classmethod(ctx, class)),
AttrName::StaticMethod => quote!(.build_staticmethod(ctx, class)),
other => unreachable!(
"Only 'method', 'classmethod' and 'staticmethod' are supported, got {:?}",
other
),
};
if py_name.starts_with("__") && py_name.ends_with("__") {
let name_ident = Ident::new(&py_name, ident.span());
quote_spanned! { ident.span() =>
class.set_attr(
ctx.names.#name_ident,
ctx.make_func_def(ctx.intern_str(#py_name), Self::#ident)
#doc
#build_func
.into(),
);
}
} else {
quote_spanned! { ident.span() =>
class.set_str_attr(
#py_name,
ctx.make_func_def(ctx.intern_str(#py_name), Self::#ident)
#doc
#build_func,
ctx,
);
}
}
};
args.context.impl_extend_items.add_item(
ident.clone(),
vec![py_name],
args.cfgs.to_vec(),
tokens,
5,
)?;
let doc = args.attrs.doc().map(|doc| format_doc(&sig_doc, &doc));
args.context.method_items.add_item(MethodNurseryItem {
py_name,
cfgs: args.cfgs.to_vec(),
ident: ident.to_owned(),
doc,
attr_name: self.inner.attr_name,
});
Ok(())
}
}
@@ -898,7 +890,7 @@ where
};
args.context
.impl_extend_items
.attribute_items
.add_item(ident.clone(), vec![py_name], cfgs, tokens, 1)?;
Ok(())
@@ -960,6 +952,78 @@ where
}
}
#[derive(Default)]
struct MethodNursery {
items: Vec<MethodNurseryItem>,
}
struct MethodNurseryItem {
py_name: String,
cfgs: Vec<Attribute>,
ident: Ident,
doc: Option<String>,
attr_name: AttrName,
}
impl MethodNursery {
fn add_item(&mut self, item: MethodNurseryItem) {
self.items.push(item);
}
fn validate(&mut self) -> Result<()> {
let mut name_set = HashSet::new();
for item in &self.items {
if !name_set.insert((&item.py_name, &item.cfgs)) {
bail_span!(item.ident, "duplicate method name `{}`", item.py_name);
}
}
Ok(())
}
}
impl ToTokens for MethodNursery {
fn to_tokens(&self, tokens: &mut TokenStream) {
for item in &self.items {
let py_name = &item.py_name;
let ident = &item.ident;
let cfgs = &item.cfgs;
let doc = if let Some(doc) = item.doc.as_ref() {
quote! { Some(#doc) }
} else {
quote! { None }
};
let flags = match &item.attr_name {
AttrName::Method => {
quote! { rustpython_vm::function::PyMethodFlags::METHOD }
}
AttrName::ClassMethod => {
quote! { rustpython_vm::function::PyMethodFlags::CLASS }
}
AttrName::StaticMethod => {
quote! { rustpython_vm::function::PyMethodFlags::STATIC }
}
_ => unreachable!(),
};
// TODO: intern
// let py_name = if py_name.starts_with("__") && py_name.ends_with("__") {
// let name_ident = Ident::new(&py_name, ident.span());
// quote_spanned! { ident.span() => ctx.names.#name_ident }
// } else {
// quote_spanned! { ident.span() => #py_name }
// };
tokens.extend(quote! {
#(#cfgs)*
method_defs.push(rustpython_vm::function::PyMethodDef {
name: #py_name,
func: rustpython_vm::function::IntoPyNativeFn::into_func(Self::#ident),
flags: #flags,
doc: #doc,
});
});
}
}
}
#[derive(Default)]
#[allow(clippy::type_complexity)]
struct GetSetNursery {
@@ -1367,13 +1431,15 @@ impl MemberItemMeta {
struct ExtractedImplAttrs {
payload: Option<Ident>,
with_impl: TokenStream,
with_slots: TokenStream,
flags: TokenStream,
with_impl: TokenStream,
with_method_defs: TokenStream,
with_slots: TokenStream,
}
fn extract_impl_attrs(attr: AttributeArgs, item: &Ident) -> Result<ExtractedImplAttrs> {
let mut withs = Vec::new();
let mut with_method_defs = Vec::new();
let mut with_slots = Vec::new();
let mut flags = vec![quote! {
{
@@ -1396,16 +1462,18 @@ fn extract_impl_attrs(attr: AttributeArgs, item: &Ident) -> Result<ExtractedImpl
let NestedMeta::Meta(Meta::Path(path)) = meta else {
bail_span!(meta, "#[pyclass(with(...))] arguments should be paths")
};
let (extend_class, extend_slots) =
let (extend_class, extend_method_def, extend_slots) =
if path.is_ident("PyRef") || path.is_ident("Py") {
// special handling for PyRef
(
quote!(#path::<Self>::__extend_py_class),
quote!(#path::<Self>::__extend_method_def),
quote!(#path::<Self>::__extend_slots),
)
} else {
(
quote!(<Self as #path>::__extend_py_class),
quote!(<Self as #path>::__extend_method_def),
quote!(<Self as #path>::__extend_slots),
)
};
@@ -1413,6 +1481,9 @@ fn extract_impl_attrs(attr: AttributeArgs, item: &Ident) -> Result<ExtractedImpl
withs.push(quote_spanned! { path.span() =>
#extend_class(ctx, class);
});
with_method_defs.push(quote_spanned! { path.span() =>
#extend_method_def(method_defs);
});
with_slots.push(quote_spanned! { item_span =>
#extend_slots(slots);
});
@@ -1450,11 +1521,14 @@ fn extract_impl_attrs(attr: AttributeArgs, item: &Ident) -> Result<ExtractedImpl
Ok(ExtractedImplAttrs {
payload,
flags: quote! {
#(#flags)*
},
with_impl: quote! {
#(#withs)*
},
flags: quote! {
#(#flags)*
with_method_defs: quote! {
#(#with_method_defs)*
},
with_slots: quote! {
#(#with_slots)*

View File

@@ -4,7 +4,7 @@ use crate::util::{
AttributeExt, ClassItemMeta, ContentItem, ContentItemInner, ErrorVec, ItemMeta, ItemNursery,
ModuleItemMeta, SimpleItemMeta, ALL_ALLOWED_NAMES,
};
use proc_macro2::{Span, TokenStream};
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned, ToTokens};
use std::{collections::HashSet, str::FromStr};
use syn::{parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Item, Result};
@@ -122,9 +122,16 @@ pub fn impl_pymodule(attr: AttributeArgs, module_item: Item) -> Result<TokenStre
ctx: &::rustpython_vm::Context,
) -> &'static ::rustpython_vm::builtins::PyModuleDef {
DEF.get_or_init(|| {
#[allow(clippy::ptr_arg)]
let method_defs = {
let mut method_defs = Vec::new();
extend_method_def(ctx, &mut method_defs);
method_defs
};
let mut def = ::rustpython_vm::builtins::PyModuleDef {
name: ctx.intern_str(MODULE_NAME),
doc: DOC.map(|doc| ctx.intern_str(doc)),
methods: Box::leak(method_defs.into_boxed_slice()),
slots: Default::default(),
};
def.slots.exec = Some(extend_module);
@@ -160,6 +167,18 @@ pub fn impl_pymodule(attr: AttributeArgs, module_item: Item) -> Result<TokenStre
pub(crate) static DEF: ::rustpython_vm::builtins::PyModuleDef;
}
},
parse_quote! {
#[allow(clippy::ptr_arg)]
pub(crate) fn extend_method_def(
ctx: &::rustpython_vm::Context,
method_defs: &mut Vec<::rustpython_vm::function::PyMethodDef>,
) {
#(
super::#withs::extend_method_def(ctx, method_defs);
)*
#function_items
}
},
parse_quote! {
pub(crate) fn __init_attributes(
vm: &::rustpython_vm::VirtualMachine,
@@ -177,23 +196,10 @@ pub fn impl_pymodule(attr: AttributeArgs, module_item: Item) -> Result<TokenStre
vm: &::rustpython_vm::VirtualMachine,
module: &::rustpython_vm::Py<::rustpython_vm::builtins::PyModule>,
) {
__init_methods(vm, module);
module.__init_methods(vm).unwrap();
__init_attributes(vm, module);
}
},
parse_quote! {
// TODO: remove once PyMethodDef done
pub(crate) fn __init_methods(
vm: &::rustpython_vm::VirtualMachine,
module: &::rustpython_vm::Py<::rustpython_vm::builtins::PyModule>,
) {
#(
super::#withs::__init_methods(vm, module);
)*
let ctx = &vm.ctx;
#function_items
}
},
parse_quote! {
pub(crate) fn __init_dict(
vm: &::rustpython_vm::VirtualMachine,
@@ -330,9 +336,7 @@ struct FunctionNurseryItem {
py_names: Vec<String>,
cfgs: Vec<Attribute>,
ident: Ident,
#[allow(dead_code)]
doc: String,
tokens: TokenStream,
}
impl FunctionNursery {
@@ -358,11 +362,23 @@ struct ValidatedFunctionNursery(FunctionNursery);
impl ToTokens for ValidatedFunctionNursery {
fn to_tokens(&self, tokens: &mut TokenStream) {
for item in &self.0.items {
let ident = &item.ident;
let cfgs = &item.cfgs;
let item_tokens = &item.tokens;
let py_names = &item.py_names;
let doc = &item.doc;
let flags = quote! { rustpython_vm::function::PyMethodFlags::empty() };
tokens.extend(quote! {
#(#cfgs)*
#item_tokens
{
let doc = Some(#doc);
#(method_defs.push(rustpython_vm::function::PyMethodDef::new(
(#py_names),
#ident,
#flags,
doc,
));)*
}
});
}
}
@@ -438,38 +454,23 @@ impl ModuleItem for FunctionItem {
let py_name = item_meta.simple_name()?;
let sig_doc = text_signature(func.sig(), &py_name);
let (tokens, py_names, doc) = {
let module = args.module_name();
let doc = args.attrs.doc().or_else(|| {
crate::doc::Database::shared()
.try_module_item(module, &py_name)
.ok() // TODO: doc must exist at least one of code or CPython
.flatten()
.map(str::to_owned)
});
let doc = if let Some(doc) = doc {
format_doc(&sig_doc, &doc)
} else {
sig_doc
};
let with_doc = quote!(.with_doc(#doc.to_owned(), &vm.ctx));
let new_func = quote_spanned!(ident.span()=>
vm.ctx.make_func_def(vm.ctx.intern_str(#py_name), #ident)
#with_doc
.into_function()
.with_module(vm.new_pyobj(#module.to_owned()))
.into_ref(&vm.ctx)
);
let module = args.module_name();
let doc = args.attrs.doc().or_else(|| {
crate::doc::Database::shared()
.try_module_item(module, &py_name)
.ok() // TODO: doc must exist at least one of code or CPython
.flatten()
.map(str::to_owned)
});
let doc = if let Some(doc) = doc {
format_doc(&sig_doc, &doc)
} else {
sig_doc
};
let py_names = {
if self.py_attrs.is_empty() {
(
quote_spanned! { ident.span() => {
let func = #new_func;
vm.__module_set_attr(module, #py_name, func).unwrap();
}},
vec![py_name],
doc,
)
vec![py_name]
} else {
let mut py_names = HashSet::new();
py_names.insert(py_name);
@@ -494,16 +495,7 @@ impl ModuleItem for FunctionItem {
args.context.errors.ok_or_push(r);
}
let py_names: Vec<_> = py_names.into_iter().collect();
(
quote_spanned! { ident.span().resolved_at(Span::call_site()) => {
let func = #new_func;
for name in [#(#py_names,)*] {
vm.__module_set_attr(module, name, func.clone()).unwrap();
}
}},
py_names,
doc,
)
py_names
}
};
@@ -512,7 +504,6 @@ impl ModuleItem for FunctionItem {
py_names,
cfgs: args.cfgs.to_vec(),
doc,
tokens,
});
Ok(())
}
@@ -585,12 +576,12 @@ impl ModuleItem for ClassItem {
1 => {
let py_name = &py_names[0];
quote! {
vm.__module_set_attr(&module, #py_name, new_class).unwrap();
vm.__module_set_attr(&module, vm.ctx.intern_str(#py_name), new_class).unwrap();
}
}
_ => quote! {
for name in [#(#py_names,)*] {
vm.__module_set_attr(&module, name, new_class.clone()).unwrap();
vm.__module_set_attr(&module, vm.ctx.intern_str(name), new_class.clone()).unwrap();
}
},
};
@@ -681,7 +672,7 @@ impl ModuleItem for AttributeItem {
ident.to_string()
};
let tokens = quote_spanned! { ident.span() =>
vm.__module_set_attr(module, #py_name, vm.new_pyobj(#ident)).unwrap();
vm.__module_set_attr(module, vm.ctx.intern_str(#py_name), vm.new_pyobj(#ident)).unwrap();
};
args.context.attribute_items.add_item(
ident.clone(),
@@ -705,7 +696,7 @@ impl ModuleItem for AttributeItem {
(
quote_spanned! { ident.span() => {
#let_obj
vm.__module_set_attr(module, #py_name, obj).unwrap();
vm.__module_set_attr(module, vm.ctx.intern_str(#py_name), obj).unwrap();
}},
vec![py_name],
)
@@ -738,7 +729,7 @@ impl ModuleItem for AttributeItem {
quote_spanned! { ident.span() => {
#let_obj
for name in [(#(#names,)*)] {
vm.__module_set_attr(module, name, obj.clone()).unwrap();
vm.__module_set_attr(module, vm.ctx.intern_str(name), obj.clone()).unwrap();
}
}},
names,

View File

@@ -320,6 +320,7 @@ impl ItemMeta for ClassItemMeta {
"base",
"metaclass",
"unhashable",
"ctx",
"impl",
"traverse",
];

View File

@@ -41,7 +41,7 @@ fn run(vm: &vm::VirtualMachine) -> vm::PyResult<()> {
// typing `quit()` is too long, let's make `on(False)` work instead.
scope
.globals
.set_item("on", vm.ctx.new_function("on", on).into(), vm)?;
.set_item("on", vm.new_function("on", on).into(), vm)?;
// let's include a fibonacci function, but let's be lazy and write it in Python
add_python_function!(

View File

@@ -1,3 +1,4 @@
import types
from testutils import assert_raises
@@ -256,7 +257,7 @@ assert dict.fromkeys.__qualname__ == 'dict.fromkeys'
assert object.__init_subclass__.__qualname__ == 'object.__init_subclass__'
# Dynamic with `#[extend_class]`:
assert bytearray.maketrans.__qualname__ == 'bytearray.maketrans'
assert bytearray.maketrans.__qualname__ == 'bytearray.maketrans', bytearray.maketrans.__qualname__
# Third-party:
@@ -560,3 +561,7 @@ def my_repr_func():
pass
assert repr(my_repr_func).startswith('<function my_repr_func at 0x')
# https://github.com/RustPython/RustPython/issues/3100
assert issubclass(types.BuiltinMethodType, types.BuiltinFunctionType)

View File

@@ -269,19 +269,19 @@ class A(type):
def __new__(mcls):
assert type(mcls.__new__).__name__ == 'function'
assert type(mcls.mro).__name__ == 'method_descriptor'
assert type(super().__new__).__name__ in ['builtin_function_or_method', 'method']
assert type(super().__new__).__name__ in ['builtin_function_or_method', 'builtin_method']
assert type(super().mro).__name__ == 'method_descriptor'
class B(type):
def x(self):
assert type(self.__new__).__name__ in ['builtin_function_or_method', 'method']
assert type(self.mro).__name__ in ['builtin_function_or_method', 'method']
assert type(super().__new__).__name__ in ['builtin_function_or_method', 'method']
assert type(super().mro).__name__ in ['builtin_function_or_method', 'method']
assert type(self.__new__).__name__ in ['builtin_function_or_method', 'builtin_method']
assert type(self.mro).__name__ in ['builtin_function_or_method', 'builtin_method']
assert type(super().__new__).__name__ in ['builtin_function_or_method', 'builtin_method']
assert type(super().mro).__name__ in ['builtin_function_or_method', 'builtin_method']
assert type(tuple.__new__).__name__ in ['builtin_function_or_method', 'method']
assert type(tuple.mro).__name__ in ['builtin_function_or_method', 'method']
assert type(type.__new__).__name__ in ['builtin_function_or_method', 'method'], type.__new__
assert type(tuple.__new__).__name__ in ['builtin_function_or_method', 'builtin_method']
assert type(tuple.mro).__name__ in ['builtin_function_or_method', 'builtin_method']
assert type(type.__new__).__name__ in ['builtin_function_or_method', 'builtin_method'], type.__new__
assert type(type.mro).__name__ == 'method_descriptor'
B('x', (int,), {}).x()

View File

@@ -61,8 +61,8 @@ mod array {
SliceableSequenceOp,
},
types::{
AsBuffer, AsMapping, AsSequence, Comparable, Constructor, IterNext,
IterNextIterable, Iterable, PyComparisonOp, Representable,
AsBuffer, AsMapping, AsSequence, Comparable, Constructor, IterNext, Iterable,
PyComparisonOp, Representable, SelfIter,
},
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
},
@@ -1405,7 +1405,7 @@ mod array {
internal: PyMutex<PositionIterInternal<PyArrayRef>>,
}
#[pyclass(with(IterNext), flags(HAS_DICT))]
#[pyclass(with(IterNext, Iterable), flags(HAS_DICT))]
impl PyArrayIter {
#[pymethod(magic)]
fn setstate(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
@@ -1422,7 +1422,7 @@ mod array {
}
}
impl IterNextIterable for PyArrayIter {}
impl SelfIter for PyArrayIter {}
impl IterNext for PyArrayIter {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.internal.lock().next(|array, pos| {

View File

@@ -8,7 +8,7 @@ mod _csv {
function::{ArgIterable, ArgumentError, FromArgs, FuncArgs},
match_class,
protocol::{PyIter, PyIterReturn},
types::{IterNext, IterNextIterable},
types::{IterNext, Iterable, SelfIter},
AsObject, Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine,
};
use itertools::{self, Itertools};
@@ -166,9 +166,9 @@ mod _csv {
}
}
#[pyclass(with(IterNext))]
#[pyclass(with(IterNext, Iterable))]
impl Reader {}
impl IterNextIterable for Reader {}
impl SelfIter for Reader {}
impl IterNext for Reader {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let string = match zelf.iter.next(vm)? {

View File

@@ -15,7 +15,7 @@ pub(crate) mod _struct {
function::{ArgBytesLike, ArgMemoryBuffer, PosArgs},
match_class,
protocol::PyIterReturn,
types::{Constructor, IterNext, IterNextIterable},
types::{Constructor, IterNext, Iterable, SelfIter},
AsObject, Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine,
};
use crossbeam_utils::atomic::AtomicCell;
@@ -194,14 +194,14 @@ pub(crate) mod _struct {
}
}
#[pyclass(with(IterNext))]
#[pyclass(with(IterNext, Iterable))]
impl UnpackIterator {
#[pymethod(magic)]
fn length_hint(&self) -> usize {
self.buffer.len().saturating_sub(self.offset.load()) / self.format_spec.size
}
}
impl IterNextIterable for UnpackIterator {}
impl SelfIter for UnpackIterator {}
impl IterNext for UnpackIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let size = zelf.format_spec.size;

View File

@@ -62,8 +62,8 @@ mod _sqlite {
protocol::{PyBuffer, PyIterReturn, PyMappingMethods, PySequence, PySequenceMethods},
sliceable::{SaturatedSliceIter, SliceableSequenceOp},
types::{
AsMapping, AsSequence, Callable, Comparable, Constructor, Hashable, IterNext,
IterNextIterable, Iterable, PyComparisonOp,
AsMapping, AsSequence, Callable, Comparable, Constructor, Hashable, IterNext, Iterable,
PyComparisonOp, SelfIter,
},
utils::ToCString,
AsObject, Py, PyAtomicRef, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
@@ -1377,7 +1377,7 @@ mod _sqlite {
statement: Option<PyRef<Statement>>,
}
#[pyclass(with(Constructor, IterNext), flags(BASETYPE))]
#[pyclass(with(Constructor, IterNext, Iterable), flags(BASETYPE))]
impl Cursor {
fn new(
connection: PyRef<Connection>,
@@ -1708,7 +1708,7 @@ mod _sqlite {
}
}
impl IterNextIterable for Cursor {}
impl SelfIter for Cursor {}
impl IterNext for Cursor {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let mut inner = zelf.inner(vm)?;

View File

@@ -6,7 +6,7 @@ use crate::{
frame::FrameRef,
function::OptionalArg,
protocol::PyIterReturn,
types::{Constructor, IterNext, IterNextIterable, Representable, Unconstructible},
types::{Constructor, IterNext, Iterable, Representable, SelfIter, Unconstructible},
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
@@ -195,7 +195,7 @@ impl PyPayload for PyAsyncGenASend {
}
}
#[pyclass(with(IterNext))]
#[pyclass(with(IterNext, Iterable))]
impl PyAsyncGenASend {
#[pymethod(name = "__await__")]
fn r#await(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
@@ -268,7 +268,7 @@ impl PyAsyncGenASend {
}
}
impl IterNextIterable for PyAsyncGenASend {}
impl SelfIter for PyAsyncGenASend {}
impl IterNext for PyAsyncGenASend {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
PyIterReturn::from_pyresult(zelf.send(vm.ctx.none(), vm), vm)
@@ -290,7 +290,7 @@ impl PyPayload for PyAsyncGenAThrow {
}
}
#[pyclass(with(IterNext))]
#[pyclass(with(IterNext, Iterable))]
impl PyAsyncGenAThrow {
#[pymethod(name = "__await__")]
fn r#await(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
@@ -414,7 +414,7 @@ impl PyAsyncGenAThrow {
}
}
impl IterNextIterable for PyAsyncGenAThrow {}
impl SelfIter for PyAsyncGenAThrow {}
impl IterNext for PyAsyncGenAThrow {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
PyIterReturn::from_pyresult(zelf.send(vm.ctx.none(), vm), vm)

View File

@@ -1,94 +1,42 @@
use super::{type_, PyClassMethod, PyStaticMethod, PyStr, PyStrInterned, PyStrRef, PyType};
use super::{type_, PyStrInterned, PyStrRef, PyType};
use crate::{
builtins::PyBoundMethod,
class::PyClassImpl,
function::{FuncArgs, IntoPyNativeFunc, PyNativeFunc},
types::{Callable, Constructor, GetDescriptor, Representable, Unconstructible},
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
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;
pub struct PyNativeFuncDef {
pub func: PyNativeFunc,
pub name: &'static PyStrInterned,
pub doc: Option<PyStrRef>,
}
impl PyNativeFuncDef {
pub fn new(func: PyNativeFunc, name: &'static PyStrInterned) -> Self {
Self {
func,
name,
doc: None,
}
}
pub fn with_doc(mut self, doc: String, ctx: &Context) -> Self {
self.doc = Some(PyStr::new_ref(doc, ctx));
self
}
pub fn into_function(self) -> PyBuiltinFunction {
self.into()
}
pub fn build_function(self, ctx: &Context) -> PyRef<PyBuiltinFunction> {
self.into_function().into_ref(ctx)
}
pub fn build_method(self, ctx: &Context, class: &'static Py<PyType>) -> PyRef<PyBuiltinMethod> {
PyRef::new_ref(
PyBuiltinMethod { value: self, class },
ctx.types.method_descriptor_type.to_owned(),
None,
)
}
pub fn build_classmethod(
self,
ctx: &Context,
class: &'static Py<PyType>,
) -> PyRef<PyClassMethod> {
// TODO: classmethod_descriptor
let callable = self.build_method(ctx, class).into();
PyClassMethod::new_ref(callable, ctx)
}
pub fn build_staticmethod(
self,
ctx: &Context,
class: &'static Py<PyType>,
) -> PyRef<PyStaticMethod> {
let callable = self.build_method(ctx, class).into();
PyStaticMethod::new_ref(callable, ctx)
}
}
// PyCFunctionObject in CPython
#[pyclass(name = "builtin_function_or_method", module = false)]
pub struct PyBuiltinFunction {
value: PyNativeFuncDef,
module: Option<PyObjectRef>,
pub struct PyNativeFunction {
pub(crate) value: &'static PyMethodDef,
pub(crate) zelf: Option<PyObjectRef>,
pub(crate) module: Option<&'static PyStrInterned>, // None for bound method
}
impl PyPayload for PyBuiltinFunction {
impl PyPayload for PyNativeFunction {
fn class(ctx: &Context) -> &'static Py<PyType> {
ctx.types.builtin_function_or_method_type
}
}
impl fmt::Debug for PyBuiltinFunction {
impl fmt::Debug for PyNativeFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "builtin function {}", self.value.name.as_str())
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 From<PyNativeFuncDef> for PyBuiltinFunction {
fn from(value: PyNativeFuncDef) -> Self {
Self {
value,
module: None,
}
}
}
impl PyBuiltinFunction {
pub fn with_module(mut self, module: PyObjectRef) -> Self {
impl PyNativeFunction {
pub fn with_module(mut self, module: &'static PyStrInterned) -> Self {
self.module = Some(module);
self
}
@@ -101,183 +49,202 @@ impl PyBuiltinFunction {
)
}
pub fn as_func(&self) -> &PyNativeFunc {
&self.value.func
pub fn as_func(&self) -> &'static PyNativeFn {
self.value.func
}
}
impl Callable for PyBuiltinFunction {
impl Callable for PyNativeFunction {
type Args = FuncArgs;
#[inline]
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
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 PyBuiltinFunction {
impl PyNativeFunction {
#[pygetset(magic)]
fn module(&self, vm: &VirtualMachine) -> PyObjectRef {
vm.unwrap_or_none(self.module.clone())
fn module(zelf: NativeFunctionOrMethod) -> Option<&'static PyStrInterned> {
zelf.0.module
}
#[pygetset(magic)]
fn name(&self) -> PyStrRef {
self.value.name.to_owned()
fn name(zelf: NativeFunctionOrMethod) -> &'static str {
zelf.0.value.name
}
#[pygetset(magic)]
fn qualname(&self) -> PyStrRef {
self.name()
fn qualname(zelf: NativeFunctionOrMethod, vm: &VirtualMachine) -> PyResult<PyStrRef> {
let zelf = zelf.0;
let flags = zelf.value.flags;
// if flags.contains(PyMethodFlags::CLASS) || flags.contains(PyMethodFlags::STATIC) {
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(&self) -> Option<PyStrRef> {
self.value.doc.clone()
fn doc(zelf: NativeFunctionOrMethod) -> Option<&'static str> {
zelf.0.value.doc
}
#[pygetset(name = "__self__")]
fn get_self(&self, vm: &VirtualMachine) -> PyObjectRef {
fn __self__(_zelf: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
vm.ctx.none()
}
#[pymethod(magic)]
fn reduce(&self) -> PyStrRef {
fn reduce(&self) -> &'static str {
// TODO: return (getattr, (self.object, self.name)) if this is a method
self.name()
self.value.name
}
#[pymethod(magic)]
fn reduce_ex(&self, _ver: PyObjectRef) -> PyStrRef {
self.name()
fn reduce_ex(zelf: PyObjectRef, _ver: PyObjectRef, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(&zelf, identifier!(vm, __reduce__), ())
}
#[pygetset(magic)]
fn text_signature(&self) -> Option<String> {
let doc = self.value.doc.as_ref()?;
let signature =
type_::get_text_signature_from_internal_doc(self.value.name.as_str(), doc.as_str())?;
Some(signature.to_owned())
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 PyBuiltinFunction {
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 PyBuiltinFunction {}
impl Unconstructible for PyNativeFunction {}
// `PyBuiltinMethod` is similar to both `PyMethodDescrObject` in
// https://github.com/python/cpython/blob/main/Include/descrobject.h
// https://github.com/python/cpython/blob/main/Objects/descrobject.c
// and `PyCMethodObject` in
// https://github.com/python/cpython/blob/main/Include/cpython/methodobject.h
// https://github.com/python/cpython/blob/main/Objects/methodobject.c
#[pyclass(module = false, name = "method_descriptor")]
pub struct PyBuiltinMethod {
value: PyNativeFuncDef,
class: &'static Py<PyType>,
// `PyCMethodObject` in CPython
#[pyclass(name = "builtin_method", module = false, base = "PyNativeFunction")]
pub struct PyNativeMethod {
pub(crate) func: PyNativeFunction,
pub(crate) class: &'static Py<PyType>, // TODO: the actual life is &'self
}
impl PyPayload for PyBuiltinMethod {
#[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))> {
// 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(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.method_descriptor_type
ctx.types.builtin_method_type
}
}
impl fmt::Debug for PyBuiltinMethod {
impl fmt::Debug for PyNativeMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "method descriptor for '{}'", self.value.name)
write!(
f,
"builtin method of {:?} with {:?}",
&*self.class.name(),
&self.func
)
}
}
impl GetDescriptor for PyBuiltinMethod {
fn descr_get(
zelf: PyObjectRef,
obj: Option<PyObjectRef>,
cls: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
let (_zelf, obj) = match Self::_check(&zelf, obj, vm) {
Some(obj) => obj,
None => return Ok(zelf),
};
let r = if vm.is_none(&obj) && !Self::_cls_is(&cls, obj.class()) {
zelf
} else {
PyBoundMethod::new_ref(obj, zelf, &vm.ctx).into()
};
Ok(r)
}
}
impl Callable for PyBuiltinMethod {
type Args = FuncArgs;
#[inline]
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
(zelf.value.func)(vm, args)
}
}
impl PyBuiltinMethod {
pub fn new_ref<F, FKind>(
name: &'static PyStrInterned,
class: &'static Py<PyType>,
f: F,
ctx: &Context,
) -> PyRef<Self>
where
F: IntoPyNativeFunc<FKind>,
{
ctx.make_func_def(name, f).build_method(ctx, class)
}
}
#[pyclass(
with(GetDescriptor, Callable, Constructor, Representable),
flags(METHOD_DESCR)
)]
impl PyBuiltinMethod {
#[pygetset(magic)]
fn name(&self) -> PyStrRef {
self.value.name.to_owned()
}
#[pygetset(magic)]
fn qualname(&self) -> String {
format!("{}.{}", self.class.name(), &self.value.name)
}
#[pygetset(magic)]
fn doc(&self) -> Option<PyStrRef> {
self.value.doc.clone()
}
#[pygetset(magic)]
fn text_signature(&self) -> Option<String> {
self.value.doc.as_ref().and_then(|doc| {
type_::get_text_signature_from_internal_doc(self.value.name.as_str(), doc.as_str())
.map(|signature| signature.to_string())
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)
}
})
}
#[pymethod(magic)]
fn reduce(
&self,
vm: &VirtualMachine,
) -> (Option<PyObjectRef>, (Option<PyObjectRef>, PyStrRef)) {
let builtins_getattr = vm.builtins.get_attr("getattr", vm).ok();
let classname = vm.builtins.get_attr(&self.class.__name__(vm), vm).ok();
(builtins_getattr, (classname, self.value.name.to_owned()))
}
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 PyBuiltinMethod {
impl Representable for PyNativeMethod {
#[inline]
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
Ok(format!(
"<method '{}' of '{}' objects>",
&zelf.value.name,
"<built-in method {} of {} object at ...>",
&zelf.func.value.name,
zelf.class.name()
))
}
}
impl Unconstructible for PyBuiltinMethod {}
impl Unconstructible for PyNativeMethod {}
pub fn init(context: &Context) {
PyBuiltinFunction::extend_class(context, context.types.builtin_function_or_method_type);
PyBuiltinMethod::extend_class(context, context.types.method_descriptor_type);
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))
}
}
}

View File

@@ -31,7 +31,7 @@ use crate::{
sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp},
types::{
AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Initializer,
IterNext, IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible,
IterNext, Iterable, PyComparisonOp, Representable, SelfIter, Unconstructible,
},
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
VirtualMachine,
@@ -891,7 +891,7 @@ impl PyPayload for PyByteArrayIterator {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl PyByteArrayIterator {
#[pymethod(magic)]
fn length_hint(&self) -> usize {
@@ -914,7 +914,7 @@ impl PyByteArrayIterator {
impl Unconstructible for PyByteArrayIterator {}
impl IterNextIterable for PyByteArrayIterator {}
impl SelfIter for PyByteArrayIterator {}
impl IterNext for PyByteArrayIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.internal.lock().next(|bytearray, pos| {

View File

@@ -21,7 +21,7 @@ use crate::{
sliceable::{SequenceIndex, SliceableSequenceOp},
types::{
AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Hashable,
IterNext, IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible,
IterNext, Iterable, PyComparisonOp, Representable, SelfIter, Unconstructible,
},
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
TryFromBorrowedObject, TryFromObject, VirtualMachine,
@@ -688,7 +688,7 @@ impl PyPayload for PyBytesIterator {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl PyBytesIterator {
#[pymethod(magic)]
fn length_hint(&self) -> usize {
@@ -711,7 +711,7 @@ impl PyBytesIterator {
}
impl Unconstructible for PyBytesIterator {}
impl IterNextIterable for PyBytesIterator {}
impl SelfIter for PyBytesIterator {}
impl IterNext for PyBytesIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.internal.lock().next(|bytes, pos| {

View File

@@ -5,7 +5,7 @@ use crate::{
frame::FrameRef,
function::OptionalArg,
protocol::PyIterReturn,
types::{Constructor, IterNext, IterNextIterable, Representable, Unconstructible},
types::{Constructor, IterNext, Iterable, Representable, SelfIter, Unconstructible},
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
@@ -108,7 +108,7 @@ impl Representable for PyCoroutine {
}
}
impl IterNextIterable for PyCoroutine {}
impl SelfIter for PyCoroutine {}
impl IterNext for PyCoroutine {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
Self::send(zelf, vm.ctx.none(), vm)
@@ -128,7 +128,7 @@ impl PyPayload for PyCoroutineWrapper {
}
}
#[pyclass(with(IterNext))]
#[pyclass(with(IterNext, Iterable))]
impl PyCoroutineWrapper {
#[pymethod]
fn send(&self, val: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
@@ -147,7 +147,7 @@ impl PyCoroutineWrapper {
}
}
impl IterNextIterable for PyCoroutineWrapper {}
impl SelfIter for PyCoroutineWrapper {}
impl IterNext for PyCoroutineWrapper {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
Self::send(zelf, vm.ctx.none(), vm)

View File

@@ -1,19 +1,155 @@
use super::{PyStr, PyStrInterned, PyType, PyTypeRef};
use super::{PyStr, PyStrInterned, PyType};
use crate::{
builtins::{builtin_func::PyNativeMethod, type_},
class::PyClassImpl,
function::PySetterValue,
types::{Constructor, GetDescriptor, Representable, Unconstructible},
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
function::{FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue},
types::{Callable, Constructor, GetDescriptor, Representable, Unconstructible},
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
use rustpython_common::lock::PyRwLock;
#[derive(Debug)]
pub struct DescrObject {
pub typ: PyTypeRef,
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,
// vectorcall: vectorcallfunc,
}
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,
}
}
}
impl PyPayload for PyMethodDescriptor {
fn class(ctx: &Context) -> &'static Py<PyType> {
ctx.types.method_descriptor_type
}
}
impl std::fmt::Debug for PyMethodDescriptor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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.map_or(false, |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 {
unimplemented!()
}
}
None if descr.method.flags.contains(PyMethodFlags::CLASS) => cls.unwrap(),
None => return Ok(zelf),
};
// Ok(descr.method.build_bound_method(&vm.ctx, bound, class).into())
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, Constructor, Representable),
flags(METHOD_DESCRIPTOR)
)]
impl PyMethodDescriptor {
#[pygetset(magic)]
fn name(&self) -> &'static PyStrInterned {
self.common.name
}
#[pygetset(magic)]
fn qualname(&self) -> String {
format!("{}.{}", self.common.typ.name(), &self.common.name)
}
#[pygetset(magic)]
fn doc(&self) -> Option<&'static str> {
self.method.doc
}
#[pygetset(magic)]
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())
})
}
#[pymethod(magic)]
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()
))
}
}
impl Unconstructible for PyMethodDescriptor {}
#[derive(Debug)]
pub enum MemberKind {
Bool = 14,
@@ -78,7 +214,7 @@ impl std::fmt::Debug for PyMemberDef {
#[pyclass(name = "member_descriptor", module = false)]
#[derive(Debug)]
pub struct PyMemberDescriptor {
pub common: DescrObject,
pub common: PyDescriptorOwned,
pub member: PyMemberDef,
}
@@ -88,8 +224,8 @@ impl PyPayload for PyMemberDescriptor {
}
}
fn calculate_qualname(descr: &DescrObject, vm: &VirtualMachine) -> PyResult<Option<String>> {
if let Some(qualname) = vm.get_attribute_opt(descr.typ.to_owned().into(), "__qualname__")? {
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".to_owned(),
@@ -217,7 +353,7 @@ impl GetDescriptor for PyMemberDescriptor {
}
}
pub fn init(context: &Context) {
let member_descriptor_type = &context.types.member_descriptor_type;
PyMemberDescriptor::extend_class(context, member_descriptor_type);
pub fn init(ctx: &Context) {
PyMemberDescriptor::extend_class(ctx, ctx.types.member_descriptor_type);
PyMethodDescriptor::extend_class(ctx, ctx.types.method_descriptor_type);
}

View File

@@ -20,7 +20,7 @@ use crate::{
recursion::ReprGuard,
types::{
AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Initializer, IterNext,
IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible,
Iterable, PyComparisonOp, Representable, SelfIter, Unconstructible,
},
vm::VirtualMachine,
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
@@ -839,7 +839,7 @@ macro_rules! dict_view {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl $iter_name {
fn new(dict: PyDictRef) -> Self {
$iter_name {
@@ -870,7 +870,7 @@ macro_rules! dict_view {
}
impl Unconstructible for $iter_name {}
impl IterNextIterable for $iter_name {}
impl SelfIter for $iter_name {}
impl IterNext for $iter_name {
#[allow(clippy::redundant_closure_call)]
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
@@ -912,7 +912,7 @@ macro_rules! dict_view {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl $reverse_iter_name {
fn new(dict: PyDictRef) -> Self {
let size = dict.size();
@@ -948,7 +948,7 @@ macro_rules! dict_view {
}
impl Unconstructible for $reverse_iter_name {}
impl IterNextIterable for $reverse_iter_name {}
impl SelfIter for $reverse_iter_name {}
impl IterNext for $reverse_iter_name {
#[allow(clippy::redundant_closure_call)]
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {

View File

@@ -7,7 +7,7 @@ use crate::{
convert::ToPyObject,
function::OptionalArg,
protocol::{PyIter, PyIterReturn},
types::{Constructor, IterNext, IterNextIterable},
types::{Constructor, IterNext, Iterable, SelfIter},
AsObject, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
};
use num_bigint::BigInt;
@@ -52,7 +52,7 @@ impl Constructor for PyEnumerate {
}
}
#[pyclass(with(Py, IterNext, Constructor), flags(BASETYPE))]
#[pyclass(with(Py, IterNext, Iterable, Constructor), flags(BASETYPE))]
impl PyEnumerate {
#[pyclassmethod(magic)]
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
@@ -71,7 +71,7 @@ impl Py<PyEnumerate> {
}
}
impl IterNextIterable for PyEnumerate {}
impl SelfIter for PyEnumerate {}
impl IterNext for PyEnumerate {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let next_obj = match zelf.iterator.next(vm)? {
@@ -97,7 +97,7 @@ impl PyPayload for PyReverseSequenceIterator {
}
}
#[pyclass(with(IterNext))]
#[pyclass(with(IterNext, Iterable))]
impl PyReverseSequenceIterator {
pub fn new(obj: PyObjectRef, len: usize) -> Self {
let position = len.saturating_sub(1);
@@ -130,7 +130,7 @@ impl PyReverseSequenceIterator {
}
}
impl IterNextIterable for PyReverseSequenceIterator {}
impl SelfIter for PyReverseSequenceIterator {}
impl IterNext for PyReverseSequenceIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.internal

View File

@@ -2,7 +2,7 @@ use super::{PyType, PyTypeRef};
use crate::{
class::PyClassImpl,
protocol::{PyIter, PyIterReturn},
types::{Constructor, IterNext, IterNextIterable},
types::{Constructor, IterNext, Iterable, SelfIter},
Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
};
@@ -32,7 +32,7 @@ impl Constructor for PyFilter {
}
}
#[pyclass(with(IterNext, Constructor), flags(BASETYPE))]
#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))]
impl PyFilter {
#[pymethod(magic)]
fn reduce(&self, vm: &VirtualMachine) -> (PyTypeRef, (PyObjectRef, PyIter)) {
@@ -43,7 +43,7 @@ impl PyFilter {
}
}
impl IterNextIterable for PyFilter {}
impl SelfIter for PyFilter {}
impl IterNext for PyFilter {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let predicate = &zelf.predicate;

View File

@@ -350,7 +350,7 @@ impl PyPayload for PyFunction {
#[pyclass(
with(GetDescriptor, Callable, Representable),
flags(HAS_DICT, METHOD_DESCR)
flags(HAS_DICT, METHOD_DESCRIPTOR)
)]
impl PyFunction {
#[pygetset(magic)]

View File

@@ -9,7 +9,7 @@ use crate::{
frame::FrameRef,
function::OptionalArg,
protocol::PyIterReturn,
types::{Constructor, IterNext, IterNextIterable, Representable, Unconstructible},
types::{Constructor, IterNext, Iterable, Representable, SelfIter, Unconstructible},
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
@@ -25,7 +25,7 @@ impl PyPayload for PyGenerator {
}
}
#[pyclass(with(Py, Constructor, IterNext))]
#[pyclass(with(Py, Constructor, IterNext, Iterable))]
impl PyGenerator {
pub fn as_coro(&self) -> &Coro {
&self.inner
@@ -104,7 +104,7 @@ impl Representable for PyGenerator {
}
}
impl IterNextIterable for PyGenerator {}
impl SelfIter for PyGenerator {}
impl IterNext for PyGenerator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.send(vm.ctx.none(), vm)

View File

@@ -8,7 +8,7 @@ use crate::{
function::ArgCallable,
object::{Traverse, TraverseFn},
protocol::{PyIterReturn, PySequence, PySequenceMethods},
types::{IterNext, IterNextIterable},
types::{IterNext, Iterable, SelfIter},
Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
};
use rustpython_common::{
@@ -189,7 +189,7 @@ impl PyPayload for PySequenceIterator {
}
}
#[pyclass(with(IterNext))]
#[pyclass(with(IterNext, Iterable))]
impl PySequenceIterator {
pub fn new(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
let seq = PySequence::try_protocol(&obj, vm)?;
@@ -226,7 +226,7 @@ impl PySequenceIterator {
}
}
impl IterNextIterable for PySequenceIterator {}
impl SelfIter for PySequenceIterator {}
impl IterNext for PySequenceIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.internal.lock().next(|obj, pos| {
@@ -252,7 +252,7 @@ impl PyPayload for PyCallableIterator {
}
}
#[pyclass(with(IterNext))]
#[pyclass(with(IterNext, Iterable))]
impl PyCallableIterator {
pub fn new(callable: ArgCallable, sentinel: PyObjectRef) -> Self {
Self {
@@ -262,7 +262,7 @@ impl PyCallableIterator {
}
}
impl IterNextIterable for PyCallableIterator {}
impl SelfIter for PyCallableIterator {}
impl IterNext for PyCallableIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let status = zelf.status.upgradable_read();

View File

@@ -13,8 +13,8 @@ use crate::{
sequence::{MutObjectSequenceOp, OptionalRangeArgs, SequenceExt, SequenceMutExt},
sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp},
types::{
AsMapping, AsSequence, Comparable, Constructor, Initializer, IterNext, IterNextIterable,
Iterable, PyComparisonOp, Representable, Unconstructible,
AsMapping, AsSequence, Comparable, Constructor, Initializer, IterNext, Iterable,
PyComparisonOp, Representable, SelfIter, Unconstructible,
},
utils::collection_repr,
vm::VirtualMachine,
@@ -543,7 +543,7 @@ impl PyPayload for PyListIterator {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl PyListIterator {
#[pymethod(magic)]
fn length_hint(&self) -> usize {
@@ -566,7 +566,7 @@ impl PyListIterator {
}
impl Unconstructible for PyListIterator {}
impl IterNextIterable for PyListIterator {}
impl SelfIter for PyListIterator {}
impl IterNext for PyListIterator {
fn next(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.internal.lock().next(|list, pos| {
@@ -588,7 +588,7 @@ impl PyPayload for PyListReverseIterator {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl PyListReverseIterator {
#[pymethod(magic)]
fn length_hint(&self) -> usize {
@@ -611,7 +611,7 @@ impl PyListReverseIterator {
}
impl Unconstructible for PyListReverseIterator {}
impl IterNextIterable for PyListReverseIterator {}
impl SelfIter for PyListReverseIterator {}
impl IterNext for PyListReverseIterator {
fn next(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.internal.lock().rev_next(|list, pos| {

View File

@@ -4,7 +4,7 @@ use crate::{
class::PyClassImpl,
function::PosArgs,
protocol::{PyIter, PyIterReturn},
types::{Constructor, IterNext, IterNextIterable},
types::{Constructor, IterNext, Iterable, SelfIter},
Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
};
@@ -32,7 +32,7 @@ impl Constructor for PyMap {
}
}
#[pyclass(with(IterNext, Constructor), flags(BASETYPE))]
#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))]
impl PyMap {
#[pymethod(magic)]
fn length_hint(&self, vm: &VirtualMachine) -> PyResult<usize> {
@@ -51,7 +51,7 @@ impl PyMap {
}
}
impl IterNextIterable for PyMap {}
impl SelfIter for PyMap {}
impl IterNext for PyMap {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let mut next_objs = Vec::new();

View File

@@ -21,8 +21,8 @@ use crate::{
},
sliceable::SequenceIndexOp,
types::{
AsBuffer, AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext,
IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible,
AsBuffer, AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable,
PyComparisonOp, Representable, SelfIter, Unconstructible,
},
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
TryFromBorrowedObject, TryFromObject, VirtualMachine,
@@ -1137,7 +1137,7 @@ impl PyPayload for PyMemoryViewIterator {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl PyMemoryViewIterator {
#[pymethod(magic)]
fn reduce(&self, vm: &VirtualMachine) -> PyTupleRef {
@@ -1148,7 +1148,7 @@ impl PyMemoryViewIterator {
}
impl Unconstructible for PyMemoryViewIterator {}
impl IterNextIterable for PyMemoryViewIterator {}
impl SelfIter for PyMemoryViewIterator {}
impl IterNext for PyMemoryViewIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.internal.lock().next(|mv, pos| {

View File

@@ -3,7 +3,7 @@ use crate::{
builtins::{pystr::AsPyStr, PyStrInterned},
class::PyClassImpl,
convert::ToPyObject,
function::FuncArgs,
function::{FuncArgs, PyMethodDef},
types::{GetAttr, Initializer, Representable},
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
@@ -15,7 +15,7 @@ pub struct PyModuleDef {
pub name: &'static PyStrInterned,
pub doc: Option<&'static PyStrInterned>,
// pub size: isize,
// pub methods: &'static [PyMethodDef],
pub methods: &'static [PyMethodDef],
pub slots: PyModuleSlots,
// traverse: traverseproc
// clear: inquiry
@@ -82,11 +82,23 @@ impl PyModule {
}
pub fn __init_dict_from_def(vm: &VirtualMachine, module: &Py<PyModule>) {
let doc = module.def.unwrap().doc.map(|doc| doc.to_owned());
module.init_module_dict(module.name.unwrap(), doc, vm);
module.init_dict(module.name.unwrap(), doc, vm);
}
}
impl Py<PyModule> {
pub fn __init_methods(&self, vm: &VirtualMachine) -> PyResult<()> {
debug_assert!(self.def.is_some());
for method in self.def.unwrap().methods {
let func = method
.to_function()
.with_module(self.name.unwrap())
.into_ref(&vm.ctx);
vm.__module_set_attr(self, vm.ctx.intern_str(method.name), func)?;
}
Ok(())
}
fn getattr_inner(&self, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
if let Some(attr) = self.as_object().generic_getattr_opt(name, None, vm)? {
return Ok(attr);
@@ -115,7 +127,7 @@ impl Py<PyModule> {
self.as_object().dict().unwrap()
}
// TODO: should be on PyModule, not Py<PyModule>
pub(crate) fn init_module_dict(
pub(crate) fn init_dict(
&self,
name: &'static PyStrInterned,
doc: Option<PyStrRef>,
@@ -176,7 +188,7 @@ impl Initializer for PyModule {
.slots
.flags
.has_feature(crate::types::PyTypeFlags::HAS_DICT));
zelf.init_module_dict(vm.ctx.intern_str(args.name.as_str()), args.doc, vm);
zelf.init_dict(vm.ctx.intern_str(args.name.as_str()), args.doc, vm);
Ok(())
}
}

View File

@@ -8,8 +8,8 @@ use crate::{
function::{ArgIndex, FuncArgs, OptionalArg, PyComparisonValue},
protocol::{PyIterReturn, PyMappingMethods, PySequenceMethods},
types::{
AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, IterNextIterable,
Iterable, PyComparisonOp, Representable, Unconstructible,
AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable,
PyComparisonOp, Representable, SelfIter, Unconstructible,
},
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
VirtualMachine,
@@ -537,7 +537,7 @@ impl PyPayload for PyLongRangeIterator {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl PyLongRangeIterator {
#[pymethod(magic)]
fn length_hint(&self) -> BigInt {
@@ -568,7 +568,7 @@ impl PyLongRangeIterator {
}
impl Unconstructible for PyLongRangeIterator {}
impl IterNextIterable for PyLongRangeIterator {}
impl SelfIter for PyLongRangeIterator {}
impl IterNext for PyLongRangeIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
// TODO: In pathological case (index == usize::MAX) this can wrap around
@@ -602,7 +602,7 @@ impl PyPayload for PyRangeIterator {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl PyRangeIterator {
#[pymethod(magic)]
fn length_hint(&self) -> usize {
@@ -634,7 +634,7 @@ impl PyRangeIterator {
}
impl Unconstructible for PyRangeIterator {}
impl IterNextIterable for PyRangeIterator {}
impl SelfIter for PyRangeIterator {}
impl IterNext for PyRangeIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
// TODO: In pathological case (index == usize::MAX) this can wrap around

View File

@@ -16,8 +16,8 @@ use crate::{
recursion::ReprGuard,
types::AsNumber,
types::{
AsSequence, Comparable, Constructor, Hashable, Initializer, IterNext, IterNextIterable,
Iterable, PyComparisonOp, Representable, Unconstructible,
AsSequence, Comparable, Constructor, Hashable, Initializer, IterNext, Iterable,
PyComparisonOp, Representable, SelfIter, Unconstructible,
},
utils::collection_repr,
vm::VirtualMachine,
@@ -1232,7 +1232,7 @@ impl PyPayload for PySetIterator {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl PySetIterator {
#[pymethod(magic)]
fn length_hint(&self) -> usize {
@@ -1257,7 +1257,7 @@ impl PySetIterator {
}
impl Unconstructible for PySetIterator {}
impl IterNextIterable for PySetIterator {}
impl SelfIter for PySetIterator {}
impl IterNext for PySetIterator {
fn next(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let mut internal = zelf.internal.lock();

View File

@@ -1,9 +1,8 @@
use super::{PyStr, PyStrInterned, PyType, PyTypeRef};
use super::{PyStr, PyType, PyTypeRef};
use crate::{
builtins::builtin_func::PyBuiltinMethod,
class::PyClassImpl,
common::lock::PyMutex,
function::{FuncArgs, IntoPyNativeFunc},
function::FuncArgs,
types::{Callable, Constructor, GetDescriptor, Initializer, Representable},
Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
@@ -73,27 +72,6 @@ impl PyStaticMethod {
}
}
impl PyStaticMethod {
pub fn new_builtin_ref<F, FKind>(
name: &'static PyStrInterned,
class: &'static Py<PyType>,
f: F,
ctx: &Context,
) -> PyRef<Self>
where
F: IntoPyNativeFunc<FKind>,
{
let callable = PyBuiltinMethod::new_ref(name, class, f, ctx).into();
PyRef::new_ref(
Self {
callable: PyMutex::new(callable),
},
ctx.types.staticmethod_type.to_owned(),
None,
)
}
}
impl Initializer for PyStaticMethod {
type Args = PyObjectRef;

View File

@@ -20,8 +20,8 @@ use crate::{
sequence::SequenceExt,
sliceable::{SequenceIndex, SliceableSequenceOp},
types::{
AsMapping, AsNumber, AsSequence, Comparable, Constructor, Hashable, IterNext,
IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible,
AsMapping, AsNumber, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable,
PyComparisonOp, Representable, SelfIter, Unconstructible,
},
AsObject, Context, Py, PyExact, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
TryFromBorrowedObject, VirtualMachine,
@@ -206,7 +206,7 @@ impl PyPayload for PyStrIterator {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl PyStrIterator {
#[pymethod(magic)]
fn length_hint(&self) -> usize {
@@ -232,7 +232,7 @@ impl PyStrIterator {
}
impl Unconstructible for PyStrIterator {}
impl IterNextIterable for PyStrIterator {}
impl SelfIter for PyStrIterator {}
impl IterNext for PyStrIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let mut internal = zelf.internal.lock();

View File

@@ -153,9 +153,8 @@ impl PySuper {
impl GetAttr for PySuper {
fn getattro(zelf: &Py<Self>, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
let skip = |zelf: &Py<Self>, name| zelf.as_object().generic_getattr(name, vm);
let obj = zelf.inner.read().obj.clone();
let (obj, start_type): (PyObjectRef, PyTypeRef) = match obj {
Some(o) => o,
let (obj, start_type): (PyObjectRef, PyTypeRef) = match &zelf.inner.read().obj {
Some(o) => o.clone(),
None => return skip(zelf, name),
};
// We want __class__ to return the class of the super object

View File

@@ -12,8 +12,8 @@ use crate::{
sequence::{OptionalRangeArgs, SequenceExt},
sliceable::{SequenceIndex, SliceableSequenceOp},
types::{
AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, IterNextIterable,
Iterable, PyComparisonOp, Representable, Unconstructible,
AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable,
PyComparisonOp, Representable, SelfIter, Unconstructible,
},
utils::collection_repr,
vm::VirtualMachine,
@@ -434,7 +434,7 @@ impl PyPayload for PyTupleIterator {
}
}
#[pyclass(with(Constructor, IterNext))]
#[pyclass(with(Constructor, IterNext, Iterable))]
impl PyTupleIterator {
#[pymethod(magic)]
fn length_hint(&self) -> usize {
@@ -457,7 +457,7 @@ impl PyTupleIterator {
}
impl Unconstructible for PyTupleIterator {}
impl IterNextIterable for PyTupleIterator {}
impl SelfIter for PyTupleIterator {}
impl IterNext for PyTupleIterator {
fn next(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.internal.lock().next(|tuple, pos| {

View File

@@ -1,11 +1,12 @@
use super::{
mappingproxy::PyMappingProxy, object, union_, PyClassMethod, PyDictRef, PyList, PyStaticMethod,
PyStr, PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak,
mappingproxy::PyMappingProxy, object, union_, PyClassMethod, PyDictRef, PyList, PyStr,
PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak,
};
use crate::{
builtins::{
descriptor::{
DescrObject, MemberGetter, MemberKind, MemberSetter, PyMemberDef, PyMemberDescriptor,
MemberGetter, MemberKind, MemberSetter, PyDescriptorOwned, PyMemberDef,
PyMemberDescriptor,
},
function::PyCellRef,
tuple::{IntoPyTuple, PyTupleTyped},
@@ -18,7 +19,7 @@ use crate::{
lock::{PyRwLock, PyRwLockReadGuard},
},
convert::ToPyResult,
function::{FuncArgs, KwArgs, OptionalArg, PySetterValue},
function::{FuncArgs, KwArgs, OptionalArg, PyMethodDef, PySetterValue},
identifier,
object::{Traverse, TraverseFn},
protocol::{PyIterReturn, PyMappingMethods, PyNumberMethods, PySequenceMethods},
@@ -426,6 +427,13 @@ impl Py<PyType> {
pub fn iter_base_chain(&self) -> impl Iterator<Item = &Py<PyType>> {
std::iter::successors(Some(self), |cls| cls.base.as_deref())
}
pub fn extend_methods(&'static self, method_defs: &'static [PyMethodDef], ctx: &Context) {
for method_def in method_defs {
let method = method_def.to_proper_method(self, ctx);
self.set_attr(ctx.intern_str(method_def.name), method);
}
}
}
#[pyclass(
@@ -696,11 +704,6 @@ impl PyType {
};
let mut attributes = dict.to_attributes(vm);
if let Some(f) = attributes.get_mut(identifier!(vm, __new__)) {
if f.class().is(vm.ctx.types.function_type) {
*f = PyStaticMethod::from(f.clone()).into_pyobject(vm);
}
}
if let Some(f) = attributes.get_mut(identifier!(vm, __init_subclass__)) {
if f.class().is(vm.ctx.types.function_type) {
@@ -813,7 +816,7 @@ impl PyType {
};
let member_descriptor: PyRef<PyMemberDescriptor> =
vm.ctx.new_pyref(PyMemberDescriptor {
common: DescrObject {
common: PyDescriptorOwned {
typ: typ.clone(),
name: vm.ctx.intern_str(member.as_str()),
qualname: PyRwLock::new(None),
@@ -977,11 +980,7 @@ fn get_signature(doc: &str) -> Option<&str> {
fn find_signature<'a>(name: &str, doc: &'a str) -> Option<&'a str> {
let name = name.rsplit('.').next().unwrap();
let doc = doc.strip_prefix(name)?;
if !doc.starts_with('(') {
None
} else {
Some(doc)
}
doc.starts_with('(').then_some(doc)
}
pub(crate) fn get_text_signature_from_internal_doc<'a>(

View File

@@ -4,7 +4,7 @@ use crate::{
class::PyClassImpl,
function::{ArgIntoBool, OptionalArg, PosArgs},
protocol::{PyIter, PyIterReturn},
types::{Constructor, IterNext, IterNextIterable},
types::{Constructor, IterNext, Iterable, SelfIter},
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
};
use rustpython_common::atomic::{self, PyAtomic, Radium};
@@ -41,7 +41,7 @@ impl Constructor for PyZip {
}
}
#[pyclass(with(IterNext, Constructor), flags(BASETYPE))]
#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))]
impl PyZip {
#[pymethod(magic)]
fn reduce(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
@@ -68,7 +68,7 @@ impl PyZip {
}
}
impl IterNextIterable for PyZip {}
impl SelfIter for PyZip {}
impl IterNext for PyZip {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
if zelf.iterators.is_empty() {

View File

@@ -1,7 +1,8 @@
//! Utilities to define a new Python class
use crate::{
builtins::{PyBaseObject, PyBoundMethod, PyType, PyTypeRef},
builtins::{PyBaseObject, PyType, PyTypeRef},
function::PyMethodDef,
identifier,
object::Py,
types::{hash_not_implemented, PyTypeFlags, PyTypeSlots},
@@ -69,8 +70,6 @@ pub trait PyClassDef {
pub trait PyClassImpl: PyClassDef {
const TP_FLAGS: PyTypeFlags = PyTypeFlags::DEFAULT;
fn impl_extend_class(ctx: &Context, class: &'static Py<PyType>);
fn extend_class(ctx: &Context, class: &'static Py<PyType>) {
#[cfg(debug_assertions)]
{
@@ -102,16 +101,21 @@ pub trait PyClassImpl: PyClassDef {
ctx.new_str(module_name).into(),
);
}
let bound_new = PyBoundMethod::new_ref(
class.to_owned().into(),
ctx.slot_new_wrapper.clone().into(),
ctx,
);
class.set_attr(identifier!(ctx, __new__), bound_new.into());
if class.slots.new.load().is_some() {
let bound_new = Context::genesis().slot_new_wrapper.build_bound_method(
ctx,
class.to_owned().into(),
class,
);
class.set_attr(identifier!(ctx, __new__), bound_new.into());
}
if class.slots.hash.load().map_or(0, |h| h as usize) == hash_not_implemented as usize {
class.set_attr(ctx.names.__hash__, ctx.none.clone().into());
}
class.extend_methods(class.slots.methods, ctx);
}
fn make_class(ctx: &Context) -> PyTypeRef
@@ -130,14 +134,20 @@ pub trait PyClassImpl: PyClassDef {
.to_owned()
}
fn impl_extend_class(ctx: &Context, class: &'static Py<PyType>);
fn impl_extend_method_def(method_defs: &mut Vec<PyMethodDef>);
fn extend_slots(slots: &mut PyTypeSlots);
fn make_slots() -> PyTypeSlots {
let mut method_defs = Vec::new();
Self::impl_extend_method_def(&mut method_defs);
let mut slots = PyTypeSlots {
flags: Self::TP_FLAGS,
name: Self::TP_NAME,
basicsize: Self::BASICSIZE,
doc: Self::DOC,
methods: Box::leak(method_defs.into_boxed_slice()),
..Default::default()
};

View File

@@ -2,6 +2,7 @@ use crate::{
builtins::{PyBaseExceptionRef, PyBytesRef, PyStr, PyStrRef, PyTuple, PyTupleRef},
common::{ascii, lock::PyRwLock},
convert::ToPyObject,
function::PyMethodDef,
AsObject, Context, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine,
};
use std::{borrow::Cow, collections::HashMap, fmt::Write, ops::Range};
@@ -142,33 +143,33 @@ impl ToPyObject for PyCodec {
impl CodecsRegistry {
pub(crate) fn new(ctx: &Context) -> Self {
::rustpython_vm::common::static_cell! {
static METHODS: Box<[PyMethodDef]>;
}
let methods = METHODS.get_or_init(|| {
crate::define_methods![
"strict_errors" => strict_errors as EMPTY,
"ignore_errors" => ignore_errors as EMPTY,
"replace_errors" => replace_errors as EMPTY,
"xmlcharrefreplace_errors" => xmlcharrefreplace_errors as EMPTY,
"backslashreplace_errors" => backslashreplace_errors as EMPTY,
"namereplace_errors" => namereplace_errors as EMPTY,
"surrogatepass_errors" => surrogatepass_errors as EMPTY,
"surrogateescape_errors" => surrogateescape_errors as EMPTY
]
.into_boxed_slice()
});
let errors = [
("strict", ctx.new_function("strict_errors", strict_errors)),
("ignore", ctx.new_function("ignore_errors", ignore_errors)),
(
"replace",
ctx.new_function("replace_errors", replace_errors),
),
(
"xmlcharrefreplace",
ctx.new_function("xmlcharrefreplace_errors", xmlcharrefreplace_errors),
),
(
"backslashreplace",
ctx.new_function("backslashreplace_errors", backslashreplace_errors),
),
(
"namereplace",
ctx.new_function("namereplace_errors", namereplace_errors),
),
(
"surrogatepass",
ctx.new_function("surrogatepass_errors", surrogatepass_errors),
),
(
"surrogateescape",
ctx.new_function("surrogateescape_errors", surrogateescape_errors),
),
("strict", methods[0].build_function(ctx)),
("ignore", methods[1].build_function(ctx)),
("replace", methods[2].build_function(ctx)),
("xmlcharrefreplace", methods[3].build_function(ctx)),
("backslashreplace", methods[4].build_function(ctx)),
("namereplace", methods[5].build_function(ctx)),
("surrogatepass", methods[6].build_function(ctx)),
("surrogateescape", methods[7].build_function(ctx)),
];
let errors = errors
.into_iter()

View File

@@ -3,8 +3,7 @@ use crate::common::{lock::PyRwLock, str::ReprOverflowError};
use crate::object::{Traverse, TraverseFn};
use crate::{
builtins::{
traceback::PyTracebackRef, tuple::IntoPyTuple, PyNone, PyStr, PyStrRef, PyTuple,
PyTupleRef, PyType, PyTypeRef,
traceback::PyTracebackRef, PyNone, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef,
},
class::{PyClassImpl, StaticType},
convert::{ToPyException, ToPyObject},
@@ -767,9 +766,8 @@ impl ExceptionZoo {
extend_exception!(PyLookupError, ctx, excs.lookup_error);
extend_exception!(PyIndexError, ctx, excs.index_error);
extend_exception!(PyKeyError, ctx, excs.key_error, {
"__str__" => ctx.new_method(identifier!(ctx, __str__), excs.key_error, key_error_str),
});
extend_exception!(PyKeyError, ctx, excs.key_error);
extend_exception!(PyMemoryError, ctx, excs.memory_error);
extend_exception!(PyNameError, ctx, excs.name_error, {
@@ -801,8 +799,6 @@ impl ExceptionZoo {
"filename" => ctx.none(),
// second exception filename
"filename2" => ctx.none(),
"__str__" => ctx.new_method(identifier!(ctx, __str__), excs.os_error, os_error_str),
"__reduce__" => ctx.new_method(identifier!(ctx, __reduce__), excs.os_error, os_error_reduce),
});
// TODO: this isn't really accurate
#[cfg(windows)]
@@ -895,84 +891,6 @@ fn make_arg_getter(idx: usize) -> impl Fn(PyBaseExceptionRef) -> Option<PyObject
move |exc| exc.get_arg(idx)
}
fn key_error_str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyStrRef {
let args = exc.args();
if args.len() == 1 {
vm.exception_args_as_string(args, false)
.into_iter()
.exactly_one()
.unwrap()
} else {
exc.str(vm)
}
}
fn os_error_str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<PyStrRef> {
let args = exc.args();
let obj = exc.as_object().to_owned();
if args.len() == 2 {
// SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic
let errno = exc.get_arg(0).unwrap().str(vm)?;
let msg = exc.get_arg(1).unwrap().str(vm)?;
let s = match obj.get_attr("filename", vm) {
Ok(filename) => match obj.get_attr("filename2", vm) {
Ok(filename2) => format!(
"[Errno {}] {}: '{}' -> '{}'",
errno,
msg,
filename.str(vm)?,
filename2.str(vm)?
),
Err(_) => format!("[Errno {}] {}: '{}'", errno, msg, filename.str(vm)?),
},
Err(_) => {
format!("[Errno {errno}] {msg}")
}
};
Ok(vm.ctx.new_str(s))
} else {
Ok(exc.str(vm))
}
}
fn os_error_reduce(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyTupleRef {
let args = exc.args();
let obj = exc.as_object().to_owned();
let mut result: Vec<PyObjectRef> = vec![obj.class().to_owned().into()];
if args.len() >= 2 && args.len() <= 5 {
// SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic
let errno = exc.get_arg(0).unwrap();
let msg = exc.get_arg(1).unwrap();
if let Ok(filename) = obj.get_attr("filename", vm) {
if !vm.is_none(&filename) {
let mut args_reduced: Vec<PyObjectRef> = vec![errno, msg, filename];
if let Ok(filename2) = obj.get_attr("filename2", vm) {
if !vm.is_none(&filename2) {
args_reduced.push(filename2);
}
}
result.push(args_reduced.into_pytuple(vm).into());
} else {
result.push(vm.new_tuple((errno, msg)).into());
}
} else {
result.push(vm.new_tuple((errno, msg)).into());
}
} else {
result.push(args.into());
}
if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) {
result.push(dict.into());
}
result.into_pytuple(vm)
}
fn system_exit_code(exc: PyBaseExceptionRef) -> Option<PyObjectRef> {
exc.args.read().first().map(|code| {
match_class!(match code {
@@ -1137,13 +1055,16 @@ pub(super) mod types {
use crate::common::lock::PyRwLock;
#[cfg_attr(target_arch = "wasm32", allow(unused_imports))]
use crate::{
builtins::{traceback::PyTracebackRef, PyInt, PyTupleRef, PyTypeRef},
builtins::{
traceback::PyTracebackRef, tuple::IntoPyTuple, PyInt, PyStrRef, PyTupleRef, PyTypeRef,
},
convert::ToPyResult,
function::FuncArgs,
types::{Constructor, Initializer},
PyObjectRef, PyRef, PyResult, VirtualMachine,
AsObject, PyObjectRef, PyRef, PyResult, VirtualMachine,
};
use crossbeam_utils::atomic::AtomicCell;
use itertools::Itertools;
// This module is designed to be used as `use builtins::*;`.
// Do not add any pub symbols not included in builtins module.
@@ -1275,10 +1196,26 @@ pub(super) mod types {
#[derive(Debug)]
pub struct PyIndexError {}
#[pyexception(name, base = "PyLookupError", ctx = "key_error", impl)]
#[pyexception(name, base = "PyLookupError", ctx = "key_error")]
#[derive(Debug)]
pub struct PyKeyError {}
#[pyexception]
impl PyKeyError {
#[pymethod(magic)]
fn str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyStrRef {
let args = exc.args();
if args.len() == 1 {
vm.exception_args_as_string(args, false)
.into_iter()
.exactly_one()
.unwrap()
} else {
exc.str(vm)
}
}
}
#[pyexception(name, base = "PyException", ctx = "memory_error", impl)]
#[derive(Debug)]
pub struct PyMemoryError {}
@@ -1347,6 +1284,74 @@ pub(super) mod types {
}
PyBaseException::slot_init(zelf, new_args, vm)
}
#[pymethod(magic)]
fn str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<PyStrRef> {
let args = exc.args();
let obj = exc.as_object().to_owned();
if args.len() == 2 {
// SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic
let errno = exc.get_arg(0).unwrap().str(vm)?;
let msg = exc.get_arg(1).unwrap().str(vm)?;
let s = match obj.get_attr("filename", vm) {
Ok(filename) => match obj.get_attr("filename2", vm) {
Ok(filename2) => format!(
"[Errno {}] {}: '{}' -> '{}'",
errno,
msg,
filename.str(vm)?,
filename2.str(vm)?
),
Err(_) => format!("[Errno {}] {}: '{}'", errno, msg, filename.str(vm)?),
},
Err(_) => {
format!("[Errno {errno}] {msg}")
}
};
Ok(vm.ctx.new_str(s))
} else {
Ok(exc.str(vm))
}
}
#[pymethod(magic)]
fn reduce(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyTupleRef {
let args = exc.args();
let obj = exc.as_object().to_owned();
let mut result: Vec<PyObjectRef> = vec![obj.class().to_owned().into()];
if args.len() >= 2 && args.len() <= 5 {
// SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic
let errno = exc.get_arg(0).unwrap();
let msg = exc.get_arg(1).unwrap();
if let Ok(filename) = obj.get_attr("filename", vm) {
if !vm.is_none(&filename) {
let mut args_reduced: Vec<PyObjectRef> = vec![errno, msg, filename];
if let Ok(filename2) = obj.get_attr("filename2", vm) {
if !vm.is_none(&filename2) {
args_reduced.push(filename2);
}
}
result.push(args_reduced.into_pytuple(vm).into());
} else {
result.push(vm.new_tuple((errno, msg)).into());
}
} else {
result.push(vm.new_tuple((errno, msg)).into());
}
} else {
result.push(args.into());
}
if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) {
result.push(dict.into());
}
result.into_pytuple(vm)
}
}
#[pyexception(name, base = "PyOSError", ctx = "blocking_io_error", impl)]

View File

@@ -1390,13 +1390,20 @@ impl ExecutingFrame<'_> {
let func = self.pop_value();
let is_method = self.pop_value().is(&vm.ctx.true_value);
let target = self.pop_value();
let method = if is_method {
PyMethod::Function { target, func }
// TODO: It was PyMethod before #4873. Check if it's correct.
let func = if is_method {
if let Some(descr_get) = func.class().mro_find_map(|cls| cls.slots.descr_get.load()) {
let cls = target.class().to_owned().into();
descr_get(func, Some(target), Some(cls), vm)?
} else {
func
}
} else {
drop(target); // should be None
PyMethod::Attribute(func)
func
};
let value = method.invoke(args, vm)?;
let value = func.call(args, vm)?;
self.push_value(value);
Ok(None)
}

View File

@@ -6,7 +6,8 @@ use crate::{
use std::marker::PhantomData;
/// A built-in Python function.
pub type PyNativeFunc = Box<py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyResult)>;
// PyCFunction in CPython
pub type PyNativeFn = py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyResult);
/// Implemented by types that are or can generate built-in functions.
///
@@ -19,29 +20,30 @@ pub type PyNativeFunc = Box<py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyRe
/// For example, anything from `Fn()` to `Fn(vm: &VirtualMachine) -> u32` to
/// `Fn(PyIntRef, PyIntRef) -> String` to
/// `Fn(&self, PyStrRef, FooOptions, vm: &VirtualMachine) -> PyResult<PyInt>`
/// is `IntoPyNativeFunc`. If you do want a really general function signature, e.g.
/// is `IntoPyNativeFn`. If you do want a really general function signature, e.g.
/// to forward the args to another function, you can define a function like
/// `Fn(FuncArgs [, &VirtualMachine]) -> ...`
///
/// Note that the `Kind` type parameter is meaningless and should be considered
/// an implementation detail; if you need to use `IntoPyNativeFunc` as a trait bound
/// an implementation detail; if you need to use `IntoPyNativeFn` as a trait bound
/// just pass an unconstrained generic type, e.g.
/// `fn foo<F, FKind>(f: F) where F: IntoPyNativeFunc<FKind>`
pub trait IntoPyNativeFunc<Kind>: Sized + PyThreadingConstraint + 'static {
/// `fn foo<F, FKind>(f: F) where F: IntoPyNativeFn<FKind>`
pub trait IntoPyNativeFn<Kind>: Sized + PyThreadingConstraint + 'static {
fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult;
/// `IntoPyNativeFunc::into_func()` generates a PyNativeFunc that performs the
/// `IntoPyNativeFn::into_func()` generates a PyNativeFn that performs the
/// appropriate type and arity checking, any requested conversions, and then if
/// successful calls the function with the extracted parameters.
fn into_func(self) -> PyNativeFunc {
Box::new(move |vm: &VirtualMachine, args| self.call(vm, args))
fn into_func(self) -> &'static PyNativeFn {
let boxed = Box::new(move |vm: &VirtualMachine, args| self.call(vm, args));
Box::leak(boxed)
}
}
// TODO: once higher-rank trait bounds are stabilized, remove the `Kind` type
// parameter and impl for F where F: for<T, R, VM> PyNativeFuncInternal<T, R, VM>
impl<F, T, R, VM> IntoPyNativeFunc<(T, R, VM)> for F
// parameter and impl for F where F: for<T, R, VM> PyNativeFnInternal<T, R, VM>
impl<F, T, R, VM> IntoPyNativeFn<(T, R, VM)> for F
where
F: PyNativeFuncInternal<T, R, VM>,
F: PyNativeFnInternal<T, R, VM>,
{
#[inline(always)]
fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult {
@@ -51,11 +53,11 @@ where
mod sealed {
use super::*;
pub trait PyNativeFuncInternal<T, R, VM>: Sized + PyThreadingConstraint + 'static {
pub trait PyNativeFnInternal<T, R, VM>: Sized + PyThreadingConstraint + 'static {
fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult;
}
}
use sealed::PyNativeFuncInternal;
use sealed::PyNativeFnInternal;
#[doc(hidden)]
pub struct OwnedParam<T>(PhantomData<T>);
@@ -68,9 +70,9 @@ pub struct RefParam<T>(PhantomData<T>);
// generate native python functions.
//
// Note that this could be done without a macro - it is simply to avoid repetition.
macro_rules! into_py_native_func_tuple {
macro_rules! into_py_native_fn_tuple {
($(($n:tt, $T:ident)),*) => {
impl<F, $($T,)* R> PyNativeFuncInternal<($(OwnedParam<$T>,)*), R, VirtualMachine> for F
impl<F, $($T,)* R> PyNativeFnInternal<($(OwnedParam<$T>,)*), R, VirtualMachine> for F
where
F: Fn($($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
$($T: FromArgs,)*
@@ -83,7 +85,7 @@ macro_rules! into_py_native_func_tuple {
}
}
impl<F, S, $($T,)* R> PyNativeFuncInternal<(BorrowedParam<S>, $(OwnedParam<$T>,)*), R, VirtualMachine> for F
impl<F, S, $($T,)* R> PyNativeFnInternal<(BorrowedParam<S>, $(OwnedParam<$T>,)*), R, VirtualMachine> for F
where
F: Fn(&Py<S>, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
S: PyPayload,
@@ -97,7 +99,7 @@ macro_rules! into_py_native_func_tuple {
}
}
impl<F, S, $($T,)* R> PyNativeFuncInternal<(RefParam<S>, $(OwnedParam<$T>,)*), R, VirtualMachine> for F
impl<F, S, $($T,)* R> PyNativeFnInternal<(RefParam<S>, $(OwnedParam<$T>,)*), R, VirtualMachine> for F
where
F: Fn(&S, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
S: PyPayload,
@@ -111,7 +113,7 @@ macro_rules! into_py_native_func_tuple {
}
}
impl<F, $($T,)* R> PyNativeFuncInternal<($(OwnedParam<$T>,)*), R, ()> for F
impl<F, $($T,)* R> PyNativeFnInternal<($(OwnedParam<$T>,)*), R, ()> for F
where
F: Fn($($T,)*) -> R + PyThreadingConstraint + 'static,
$($T: FromArgs,)*
@@ -124,7 +126,7 @@ macro_rules! into_py_native_func_tuple {
}
}
impl<F, S, $($T,)* R> PyNativeFuncInternal<(BorrowedParam<S>, $(OwnedParam<$T>,)*), R, ()> for F
impl<F, S, $($T,)* R> PyNativeFnInternal<(BorrowedParam<S>, $(OwnedParam<$T>,)*), R, ()> for F
where
F: Fn(&Py<S>, $($T,)*) -> R + PyThreadingConstraint + 'static,
S: PyPayload,
@@ -138,7 +140,7 @@ macro_rules! into_py_native_func_tuple {
}
}
impl<F, S, $($T,)* R> PyNativeFuncInternal<(RefParam<S>, $(OwnedParam<$T>,)*), R, ()> for F
impl<F, S, $($T,)* R> PyNativeFnInternal<(RefParam<S>, $(OwnedParam<$T>,)*), R, ()> for F
where
F: Fn(&S, $($T,)*) -> R + PyThreadingConstraint + 'static,
S: PyPayload,
@@ -154,14 +156,14 @@ macro_rules! into_py_native_func_tuple {
};
}
into_py_native_func_tuple!();
into_py_native_func_tuple!((v1, T1));
into_py_native_func_tuple!((v1, T1), (v2, T2));
into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3));
into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4));
into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5));
into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5), (v6, T6));
into_py_native_func_tuple!(
into_py_native_fn_tuple!();
into_py_native_fn_tuple!((v1, T1));
into_py_native_fn_tuple!((v1, T1), (v2, T2));
into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3));
into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4));
into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5));
into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5), (v6, T6));
into_py_native_fn_tuple!(
(v1, T1),
(v2, T2),
(v3, T3),
@@ -176,8 +178,8 @@ mod tests {
use super::*;
#[test]
fn test_intonativefunc_noalloc() {
let check_zst = |f: PyNativeFunc| assert_eq!(std::mem::size_of_val(f.as_ref()), 0);
fn test_into_native_fn_noalloc() {
let check_zst = |f: &'static PyNativeFn| assert_eq!(std::mem::size_of_val(f), 0);
fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 {
1
}

259
vm/src/function/method.rs Normal file
View File

@@ -0,0 +1,259 @@
use crate::{
builtins::{
builtin_func::{PyNativeFunction, PyNativeMethod},
descriptor::PyMethodDescriptor,
PyType,
},
function::{IntoPyNativeFn, PyNativeFn},
Context, Py, PyObjectRef, PyPayload, PyRef, VirtualMachine,
};
bitflags::bitflags! {
// METH_XXX flags in CPython
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::IntoPyNativeFn::into_func($func),
flags: $crate::function::PyMethodFlags::$flags,
doc: None,
}),+ ]
};
}
#[derive(Clone)]
pub struct PyMethodDef {
pub name: &'static str, // TODO: interned
pub func: &'static PyNativeFn,
pub flags: PyMethodFlags,
pub doc: Option<&'static str>, // TODO: interned
}
impl PyMethodDef {
#[inline]
pub fn new<Kind>(
name: &'static str,
func: impl IntoPyNativeFn<Kind>,
flags: PyMethodFlags,
doc: Option<&'static str>,
) -> Self {
Self {
name,
func: func.into_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!();
}
}
pub fn to_function(&'static self) -> PyNativeFunction {
PyNativeFunction {
zelf: None,
value: self,
module: None,
}
}
pub fn to_method(
&'static self,
class: &'static Py<PyType>,
ctx: &Context,
) -> PyMethodDescriptor {
PyMethodDescriptor::new(self, class, ctx)
}
pub fn to_bound_method(
&'static self,
obj: PyObjectRef,
class: &'static Py<PyType>,
) -> PyNativeMethod {
PyNativeMethod {
func: PyNativeFunction {
zelf: Some(obj),
value: self,
module: 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,
};
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));
PyRef::new_ref(
self.to_method(class, ctx),
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_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));
let func = self.to_function();
PyNativeMethod { func, class }.into_ref(ctx)
}
}
impl std::fmt::Debug for PyMethodDef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PyMethodDef")
.field("name", &self.name)
.field(
"func",
&(unsafe { std::mem::transmute::<_, [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 {
pub fn new(method: PyMethodDef) -> Self {
Self { method }
}
}
impl Py<HeapMethodDef> {
pub(crate) unsafe fn method(&self) -> &'static PyMethodDef {
&*(&self.method as *const _)
}
pub fn build_function(&self, vm: &VirtualMachine) -> PyRef<PyNativeFunction> {
let function = unsafe { self.method() }.to_function();
let dict = vm.ctx.new_dict();
dict.set_item("__method_def__", self.to_owned().into(), vm)
.unwrap();
PyRef::new_ref(
function,
vm.ctx.types.builtin_function_or_method_type.to_owned(),
Some(dict),
)
}
pub fn build_method(
&self,
class: &'static Py<PyType>,
vm: &VirtualMachine,
) -> PyRef<PyMethodDescriptor> {
let function = unsafe { self.method() }.to_method(class, &vm.ctx);
let dict = vm.ctx.new_dict();
dict.set_item("__method_def__", self.to_owned().into(), vm)
.unwrap();
PyRef::new_ref(
function,
vm.ctx.types.method_descriptor_type.to_owned(),
Some(dict),
)
}
}
#[pyclass]
impl HeapMethodDef {}

View File

@@ -5,6 +5,7 @@ mod builtin;
mod either;
mod fspath;
mod getset;
mod method;
mod number;
mod protocol;
@@ -15,11 +16,12 @@ pub use argument::{
pub use arithmetic::{PyArithmeticValue, PyComparisonValue};
pub use buffer::{ArgAsciiBuffer, ArgBytesLike, ArgMemoryBuffer, ArgStrOrBytesLike};
pub(self) use builtin::{BorrowedParam, OwnedParam, RefParam};
pub use builtin::{IntoPyNativeFunc, PyNativeFunc};
pub use builtin::{IntoPyNativeFn, PyNativeFn};
pub use either::Either;
pub use fspath::FsPath;
pub use getset::PySetterValue;
pub(super) use getset::{IntoPyGetterFunc, IntoPySetterFunc, PyGetterFunc, PySetterFunc};
pub use method::{HeapMethodDef, PyMethodDef, PyMethodFlags};
pub use number::{ArgIndex, ArgIntoBool, ArgIntoComplex, ArgIntoFloat, ArgPrimitiveIndex, ArgSize};
pub use protocol::{ArgCallable, ArgIterable, ArgMapping, ArgSequence};

View File

@@ -2,7 +2,7 @@
macro_rules! extend_module {
( $vm:expr, $module:expr, { $($name:expr => $value:expr),* $(,)? }) => {{
$(
$vm.__module_set_attr($module, $name, $value).unwrap();
$vm.__module_set_attr($module, $vm.ctx.intern_str($name), $value).unwrap();
)*
}};
}
@@ -218,12 +218,13 @@ macro_rules! named_function {
#[allow(unused_variables)] // weird lint, something to do with paste probably
let ctx: &$crate::Context = &$ctx;
$crate::__exports::paste::expr! {
ctx.make_func_def(
ctx.intern_str(stringify!($func)),
ctx.new_method_def(
stringify!($func),
[<$module _ $func>],
::rustpython_vm::function::PyMethodFlags::empty(),
)
.into_function()
.with_module(ctx.new_str(stringify!($module).to_owned()).into())
.to_function()
.with_module(ctx.intern_str(stringify!($module)).into())
.into_ref(ctx)
}
}};

View File

@@ -551,7 +551,12 @@ impl PyObjectRef {
/// T must be the exact payload type
#[inline(always)]
pub unsafe fn downcast_unchecked<T: PyObjectPayload>(self) -> PyRef<T> {
PyRef::from_obj_unchecked(self)
// PyRef::from_obj_unchecked(self)
// manual impl to avoid assertion
let obj = ManuallyDrop::new(self);
PyRef {
ptr: obj.ptr.cast(),
}
}
/// # Safety

View File

@@ -80,3 +80,7 @@ pub trait PyObjectPayload:
}
impl<T: PyPayload + 'static> PyObjectPayload for T {}
pub trait SlotOffset {
fn offset() -> usize;
}

View File

@@ -16,6 +16,7 @@ impl PyObject {
}
/// PyObject_Call*Arg* series
#[inline]
pub fn call(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult {
let args = args.into_args(vm);
self.call_with_args(args, vm)

View File

@@ -2,6 +2,7 @@
//!
//! Implements the list of [builtin Python functions](https://docs.python.org/3/library/builtins.html).
use crate::{builtins::PyModule, class::PyClassImpl, Py, VirtualMachine};
pub(crate) use builtins::{__module_def, DOC};
pub use builtins::{ascii, print};
#[pymodule]

View File

@@ -16,8 +16,8 @@ mod _collections {
sequence::{MutObjectSequenceOp, OptionalRangeArgs},
sliceable::SequenceIndexOp,
types::{
AsSequence, Comparable, Constructor, Initializer, IterNext, IterNextIterable, Iterable,
PyComparisonOp, Representable,
AsSequence, Comparable, Constructor, Initializer, IterNext, Iterable, PyComparisonOp,
Representable, SelfIter,
},
utils::collection_repr,
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
@@ -623,7 +623,7 @@ mod _collections {
}
}
#[pyclass(with(IterNext, Constructor))]
#[pyclass(with(IterNext, Iterable, Constructor))]
impl PyDequeIterator {
pub(crate) fn new(deque: PyDequeRef) -> Self {
PyDequeIterator {
@@ -654,7 +654,7 @@ mod _collections {
}
}
impl IterNextIterable for PyDequeIterator {}
impl SelfIter for PyDequeIterator {}
impl IterNext for PyDequeIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.internal.lock().next(|deque, pos| {
@@ -696,7 +696,7 @@ mod _collections {
}
}
#[pyclass(with(IterNext, Constructor))]
#[pyclass(with(IterNext, Iterable, Constructor))]
impl PyReverseDequeIterator {
#[pymethod(magic)]
fn length_hint(&self) -> usize {
@@ -720,7 +720,7 @@ mod _collections {
}
}
impl IterNextIterable for PyReverseDequeIterator {}
impl SelfIter for PyReverseDequeIterator {}
impl IterNext for PyReverseDequeIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.internal.lock().next(|deque, pos| {

View File

@@ -382,7 +382,7 @@ mod _io {
#[derive(Debug, PyPayload)]
pub struct _IOBase;
#[pyclass(with(IterNext, Destructor), flags(BASETYPE, HAS_DICT))]
#[pyclass(with(IterNext, Iterable, Destructor), flags(BASETYPE, HAS_DICT))]
impl _IOBase {
#[pymethod]
fn seek(

View File

@@ -16,7 +16,7 @@ mod decl {
identifier,
protocol::{PyIter, PyIterReturn, PyNumber},
stdlib::sys,
types::{Constructor, IterNext, IterNextIterable, Representable},
types::{Constructor, IterNext, Iterable, Representable, SelfIter},
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, PyWeakRef, TryFromObject,
VirtualMachine,
};
@@ -32,7 +32,7 @@ mod decl {
active: PyRwLock<Option<PyIter>>,
}
#[pyclass(with(IterNext), flags(BASETYPE, HAS_DICT))]
#[pyclass(with(IterNext, Iterable), flags(BASETYPE, HAS_DICT))]
impl PyItertoolsChain {
#[pyslot]
fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
@@ -110,7 +110,7 @@ mod decl {
Ok(())
}
}
impl IterNextIterable for PyItertoolsChain {}
impl SelfIter for PyItertoolsChain {}
impl IterNext for PyItertoolsChain {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let Some(source) = zelf.source.read().clone() else {
@@ -189,7 +189,7 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor), flags(BASETYPE))]
#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))]
impl PyItertoolsCompress {
#[pymethod(magic)]
fn reduce(zelf: PyRef<Self>) -> (PyTypeRef, (PyIter, PyIter)) {
@@ -200,7 +200,7 @@ mod decl {
}
}
impl IterNextIterable for PyItertoolsCompress {}
impl SelfIter for PyItertoolsCompress {}
impl IterNext for PyItertoolsCompress {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
loop {
@@ -249,7 +249,7 @@ mod decl {
return Err(vm.new_type_error("a number is required".to_owned()));
}
PyItertoolsCount {
Self {
cur: PyRwLock::new(start),
step,
}
@@ -258,7 +258,7 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor, Representable))]
#[pyclass(with(IterNext, Iterable, Constructor, Representable))]
impl PyItertoolsCount {
// TODO: Implement this
// if (lz->cnt == PY_SSIZE_T_MAX)
@@ -268,7 +268,7 @@ mod decl {
(zelf.class().to_owned(), (zelf.cur.read().clone(),))
}
}
impl IterNextIterable for PyItertoolsCount {}
impl SelfIter for PyItertoolsCount {}
impl IterNext for PyItertoolsCount {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let mut cur = zelf.cur.write();
@@ -314,9 +314,9 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor), flags(BASETYPE))]
#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))]
impl PyItertoolsCycle {}
impl IterNextIterable for PyItertoolsCycle {}
impl SelfIter for PyItertoolsCycle {}
impl IterNext for PyItertoolsCycle {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let item = if let PyIterReturn::Return(item) = zelf.iter.next(vm)? {
@@ -378,7 +378,7 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor, Representable), flags(BASETYPE))]
#[pyclass(with(IterNext, Iterable, Constructor, Representable), flags(BASETYPE))]
impl PyItertoolsRepeat {
#[pymethod(magic)]
fn length_hint(&self, vm: &VirtualMachine) -> PyResult<usize> {
@@ -400,7 +400,7 @@ mod decl {
}
}
impl IterNextIterable for PyItertoolsRepeat {}
impl SelfIter for PyItertoolsRepeat {}
impl IterNext for PyItertoolsRepeat {
fn next(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyIterReturn> {
if let Some(ref times) = zelf.times {
@@ -456,7 +456,7 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor), flags(BASETYPE))]
#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))]
impl PyItertoolsStarmap {
#[pymethod(magic)]
fn reduce(zelf: PyRef<Self>) -> (PyTypeRef, (PyObjectRef, PyIter)) {
@@ -466,7 +466,7 @@ mod decl {
)
}
}
impl IterNextIterable for PyItertoolsStarmap {}
impl SelfIter for PyItertoolsStarmap {}
impl IterNext for PyItertoolsStarmap {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let obj = zelf.iterable.next(vm)?;
@@ -519,7 +519,7 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor), flags(BASETYPE))]
#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))]
impl PyItertoolsTakewhile {
#[pymethod(magic)]
fn reduce(zelf: PyRef<Self>) -> (PyTypeRef, (PyObjectRef, PyIter), u32) {
@@ -537,7 +537,7 @@ mod decl {
Ok(())
}
}
impl IterNextIterable for PyItertoolsTakewhile {}
impl SelfIter for PyItertoolsTakewhile {}
impl IterNext for PyItertoolsTakewhile {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
if zelf.stop_flag.load() {
@@ -600,7 +600,7 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor), flags(BASETYPE))]
#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))]
impl PyItertoolsDropwhile {
#[pymethod(magic)]
fn reduce(zelf: PyRef<Self>) -> (PyTypeRef, (PyObjectRef, PyIter), u32) {
@@ -618,7 +618,7 @@ mod decl {
Ok(())
}
}
impl IterNextIterable for PyItertoolsDropwhile {}
impl SelfIter for PyItertoolsDropwhile {}
impl IterNext for PyItertoolsDropwhile {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let predicate = &zelf.predicate;
@@ -719,7 +719,7 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor))]
#[pyclass(with(IterNext, Iterable, Constructor))]
impl PyItertoolsGroupBy {
pub(super) fn advance(
&self,
@@ -737,7 +737,7 @@ mod decl {
Ok(PyIterReturn::Return((new_value, new_key)))
}
}
impl IterNextIterable for PyItertoolsGroupBy {}
impl SelfIter for PyItertoolsGroupBy {}
impl IterNext for PyItertoolsGroupBy {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let mut state = zelf.state.lock();
@@ -795,9 +795,9 @@ mod decl {
groupby: PyRef<PyItertoolsGroupBy>,
}
#[pyclass(with(IterNext))]
#[pyclass(with(IterNext, Iterable))]
impl PyItertoolsGrouper {}
impl IterNextIterable for PyItertoolsGrouper {}
impl SelfIter for PyItertoolsGrouper {}
impl IterNext for PyItertoolsGrouper {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let old_key = {
@@ -865,7 +865,7 @@ mod decl {
)))
}
#[pyclass(with(IterNext), flags(BASETYPE))]
#[pyclass(with(IterNext, Iterable), flags(BASETYPE))]
impl PyItertoolsIslice {
#[pyslot]
fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
@@ -959,7 +959,7 @@ mod decl {
}
}
impl IterNextIterable for PyItertoolsIslice {}
impl SelfIter for PyItertoolsIslice {}
impl IterNext for PyItertoolsIslice {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
while zelf.cur.load() < zelf.next.load() {
@@ -1023,7 +1023,7 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor), flags(BASETYPE))]
#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))]
impl PyItertoolsFilterFalse {
#[pymethod(magic)]
fn reduce(zelf: PyRef<Self>) -> (PyTypeRef, (PyObjectRef, PyIter)) {
@@ -1033,7 +1033,7 @@ mod decl {
)
}
}
impl IterNextIterable for PyItertoolsFilterFalse {}
impl SelfIter for PyItertoolsFilterFalse {}
impl IterNext for PyItertoolsFilterFalse {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let predicate = &zelf.predicate;
@@ -1091,10 +1091,10 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor))]
#[pyclass(with(IterNext, Iterable, Constructor))]
impl PyItertoolsAccumulate {}
impl IterNextIterable for PyItertoolsAccumulate {}
impl SelfIter for PyItertoolsAccumulate {}
impl IterNext for PyItertoolsAccumulate {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let iterable = &zelf.iterable;
@@ -1199,7 +1199,7 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor))]
#[pyclass(with(IterNext, Iterable, Constructor))]
impl PyItertoolsTee {
fn from_iter(iterator: PyIter, vm: &VirtualMachine) -> PyResult {
let class = PyItertoolsTee::class(&vm.ctx);
@@ -1222,7 +1222,7 @@ mod decl {
}
}
}
impl IterNextIterable for PyItertoolsTee {}
impl SelfIter for PyItertoolsTee {}
impl IterNext for PyItertoolsTee {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let value = match zelf.tee_data.get_item(vm, zelf.index.load())? {
@@ -1277,7 +1277,7 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor))]
#[pyclass(with(IterNext, Iterable, Constructor))]
impl PyItertoolsProduct {
fn update_idxs(&self, mut idxs: PyRwLockWriteGuard<'_, Vec<usize>>) {
if idxs.len() == 0 {
@@ -1302,7 +1302,7 @@ mod decl {
}
}
}
impl IterNextIterable for PyItertoolsProduct {}
impl SelfIter for PyItertoolsProduct {}
impl IterNext for PyItertoolsProduct {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
// stop signal
@@ -1370,7 +1370,7 @@ mod decl {
let n = pool.len();
PyItertoolsCombinations {
Self {
pool,
indices: PyRwLock::new((0..r).collect()),
result: PyRwLock::new(None),
@@ -1382,7 +1382,7 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor))]
#[pyclass(with(IterNext, Iterable, Constructor))]
impl PyItertoolsCombinations {
#[pymethod(magic)]
fn reduce(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyTupleRef {
@@ -1413,7 +1413,7 @@ mod decl {
}
}
impl IterNextIterable for PyItertoolsCombinations {}
impl SelfIter for PyItertoolsCombinations {}
impl IterNext for PyItertoolsCombinations {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
// stop signal
@@ -1512,10 +1512,10 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor))]
#[pyclass(with(IterNext, Iterable, Constructor))]
impl PyItertoolsCombinationsWithReplacement {}
impl IterNextIterable for PyItertoolsCombinationsWithReplacement {}
impl SelfIter for PyItertoolsCombinationsWithReplacement {}
impl IterNext for PyItertoolsCombinationsWithReplacement {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
// stop signal
@@ -1609,7 +1609,7 @@ mod decl {
None => n,
};
PyItertoolsPermutations {
Self {
pool,
indices: PyRwLock::new((0..n).collect()),
cycles: PyRwLock::new((0..r.min(n)).map(|i| n - i).collect()),
@@ -1622,9 +1622,9 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor))]
#[pyclass(with(IterNext, Iterable, Constructor))]
impl PyItertoolsPermutations {}
impl IterNextIterable for PyItertoolsPermutations {}
impl SelfIter for PyItertoolsPermutations {}
impl IterNext for PyItertoolsPermutations {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
// stop signal
@@ -1725,7 +1725,7 @@ mod decl {
fillvalue: PyRwLock<PyObjectRef>,
}
#[pyclass(with(IterNext, Constructor))]
#[pyclass(with(IterNext, Iterable, Constructor))]
impl PyItertoolsZipLongest {
#[pymethod(magic)]
fn reduce(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
@@ -1747,7 +1747,7 @@ mod decl {
Ok(())
}
}
impl IterNextIterable for PyItertoolsZipLongest {}
impl SelfIter for PyItertoolsZipLongest {}
impl IterNext for PyItertoolsZipLongest {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
if zelf.iterators.is_empty() {
@@ -1794,9 +1794,9 @@ mod decl {
}
}
#[pyclass(with(IterNext, Constructor))]
#[pyclass(with(IterNext, Iterable, Constructor))]
impl PyItertoolsPairwise {}
impl IterNextIterable for PyItertoolsPairwise {}
impl SelfIter for PyItertoolsPairwise {}
impl IterNext for PyItertoolsPairwise {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let old = match zelf.old.read().clone() {

View File

@@ -316,7 +316,7 @@ pub(super) mod _os {
function::{ArgBytesLike, Either, FsPath, FuncArgs, OptionalArg},
protocol::PyIterReturn,
recursion::ReprGuard,
types::{IterNext, IterNextIterable, PyStructSequence, Representable},
types::{IterNext, Iterable, PyStructSequence, Representable, SelfIter},
vm::VirtualMachine,
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
};
@@ -791,7 +791,7 @@ pub(super) mod _os {
mode: OutputMode,
}
#[pyclass(with(IterNext))]
#[pyclass(with(IterNext, Iterable))]
impl ScandirIterator {
#[pymethod]
fn close(&self) {
@@ -809,7 +809,7 @@ pub(super) mod _os {
zelf.close()
}
}
impl IterNextIterable for ScandirIterator {}
impl SelfIter for ScandirIterator {}
impl IterNext for ScandirIterator {
fn next(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
let entryref: &mut Option<fs::ReadDir> = &mut zelf.entries.write();

View File

@@ -1,6 +1,6 @@
use crate::{builtins::PyModule, convert::ToPyObject, Py, PyResult, VirtualMachine};
pub(crate) use sys::{UnraisableHookArgs, MAXSIZE, MULTIARCH};
pub(crate) use sys::{UnraisableHookArgs, __module_def, DOC, MAXSIZE, MULTIARCH};
#[pymodule]
mod sys {

View File

@@ -3,7 +3,9 @@ use crate::{
bytecode::ComparisonOperator,
common::hash::PyHash,
convert::{ToPyObject, ToPyResult},
function::{Either, FromArgs, FuncArgs, OptionalArg, PyComparisonValue, PySetterValue},
function::{
Either, FromArgs, FuncArgs, OptionalArg, PyComparisonValue, PyMethodDef, PySetterValue,
},
identifier,
protocol::{
PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyNumber, PyNumberMethods,
@@ -62,6 +64,8 @@ pub struct PyTypeSlots {
pub iter: AtomicCell<Option<IterFunc>>,
pub iternext: AtomicCell<Option<IterNextFunc>>,
pub methods: &'static [PyMethodDef],
// Flags to define presence of optional/expanded features
pub flags: PyTypeFlags,
@@ -119,7 +123,7 @@ bitflags! {
const IMMUTABLETYPE = 1 << 8;
const HEAPTYPE = 1 << 9;
const BASETYPE = 1 << 10;
const METHOD_DESCR = 1 << 17;
const METHOD_DESCRIPTOR = 1 << 17;
const HAS_DICT = 1 << 40;
#[cfg(debug_assertions)]
@@ -305,6 +309,11 @@ fn iter_wrapper(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(&zelf, identifier!(vm, __iter__), ())
}
// PyObject_SelfIter in CPython
fn self_iter(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult {
Ok(zelf)
}
fn iternext_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
PyIterReturn::from_pyresult(
vm.call_special_method(zelf, identifier!(vm, __next__), ()),
@@ -344,7 +353,7 @@ fn init_wrapper(obj: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResu
Ok(())
}
fn new_wrapper(cls: PyTypeRef, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
pub(crate) fn new_wrapper(cls: PyTypeRef, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let new = cls.get_attr(identifier!(vm, __new__)).unwrap();
args.prepend_arg(cls.into());
new.call(args, vm)
@@ -822,10 +831,15 @@ pub trait Callable: PyPayload {
#[inline]
#[pyslot]
fn slot_call(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let Some(zelf) = zelf.downcast_ref() else {
let err = vm.new_downcast_type_error(Self::class(&vm.ctx), zelf);
return Err(err);
};
let zelf = zelf.downcast_ref().ok_or_else(|| {
let repr = zelf.repr(vm);
let help = if let Ok(repr) = repr.as_ref() {
repr.as_str().to_owned()
} else {
zelf.class().name().to_owned()
};
vm.new_type_error(format!("unexpected payload for __call__ of {help}"))
})?;
let args = args.bind(vm)?;
Self::call(zelf, args, vm)
}
@@ -1229,7 +1243,6 @@ pub trait AsNumber: PyPayload {
#[pyclass]
pub trait Iterable: PyPayload {
#[pyslot]
#[pymethod(name = "__iter__")]
fn slot_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let zelf = zelf
.downcast()
@@ -1237,7 +1250,14 @@ pub trait Iterable: PyPayload {
Self::iter(zelf, vm)
}
#[pymethod]
fn __iter__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Self::slot_iter(zelf, vm)
}
fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult;
fn extend_slots(_slots: &mut PyTypeSlots) {}
}
// `Iterator` fits better, but to avoid confusion with rust std::iter::Iterator
@@ -1260,19 +1280,29 @@ pub trait IterNext: PyPayload + Iterable {
}
}
pub trait IterNextIterable: PyPayload {}
pub trait SelfIter: PyPayload {}
impl<T> Iterable for T
where
T: IterNextIterable,
T: SelfIter,
{
#[inline]
fn slot_iter(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult {
Ok(zelf)
#[cold]
fn slot_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let repr = zelf.repr(vm)?;
unreachable!("slot must be overriden for {}", repr.as_str());
}
fn __iter__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
self_iter(zelf, vm)
}
#[cold]
fn iter(_zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyResult {
unreachable!("slot_iter is implemented");
}
fn extend_slots(slots: &mut PyTypeSlots) {
let prev = slots.iter.swap(Some(self_iter));
debug_assert!(prev.is_some()); // slot_iter would be set
}
}

View File

@@ -74,6 +74,7 @@ pub struct TypeZoo {
pub zip_type: &'static Py<PyType>,
pub function_type: &'static Py<PyType>,
pub builtin_function_or_method_type: &'static Py<PyType>,
pub builtin_method_type: &'static Py<PyType>,
pub method_descriptor_type: &'static Py<PyType>,
pub property_type: &'static Py<PyType>,
pub getset_type: &'static Py<PyType>,
@@ -91,6 +92,9 @@ pub struct TypeZoo {
pub generic_alias_type: &'static Py<PyType>,
pub union_type: &'static Py<PyType>,
pub member_descriptor_type: &'static Py<PyType>,
// RustPython-original types
pub method_def: &'static Py<PyType>,
}
impl TypeZoo {
@@ -135,7 +139,8 @@ impl TypeZoo {
async_generator_wrapped_value:
asyncgenerator::PyAsyncGenWrappedValue::init_builtin_type(),
bound_method_type: function::PyBoundMethod::init_builtin_type(),
builtin_function_or_method_type: builtin_func::PyBuiltinFunction::init_builtin_type(),
builtin_function_or_method_type: builtin_func::PyNativeFunction::init_builtin_type(),
builtin_method_type: builtin_func::PyNativeMethod::init_builtin_type(),
bytearray_iterator_type: bytearray::PyByteArrayIterator::init_builtin_type(),
bytes_iterator_type: bytes::PyBytesIterator::init_builtin_type(),
callable_iterator: iter::PyCallableIterator::init_builtin_type(),
@@ -172,12 +177,14 @@ impl TypeZoo {
traceback_type: traceback::PyTraceback::init_builtin_type(),
tuple_iterator_type: tuple::PyTupleIterator::init_builtin_type(),
weakproxy_type: weakproxy::PyWeakProxy::init_builtin_type(),
method_descriptor_type: builtin_func::PyBuiltinMethod::init_builtin_type(),
method_descriptor_type: descriptor::PyMethodDescriptor::init_builtin_type(),
none_type: singletons::PyNone::init_builtin_type(),
not_implemented_type: singletons::PyNotImplemented::init_builtin_type(),
generic_alias_type: genericalias::PyGenericAlias::init_builtin_type(),
union_type: union_::PyUnion::init_builtin_type(),
member_descriptor_type: descriptor::PyMemberDescriptor::init_builtin_type(),
method_def: crate::function::HeapMethodDef::init_builtin_type(),
}
}

View File

@@ -1,11 +1,10 @@
use crate::{
builtins::{
builtin_func::{PyBuiltinFunction, PyBuiltinMethod, PyNativeFuncDef},
bytes,
code::{self, PyCode},
descriptor::{
DescrObject, MemberGetter, MemberKind, MemberSetter, MemberSetterFunc, PyMemberDef,
PyMemberDescriptor,
MemberGetter, MemberKind, MemberSetter, MemberSetterFunc, PyDescriptorOwned,
PyMemberDef, PyMemberDescriptor,
},
getset::PyGetSet,
object, pystr,
@@ -17,7 +16,10 @@ use crate::{
class::{PyClassImpl, StaticType},
common::rc::PyRc,
exceptions,
function::{IntoPyGetterFunc, IntoPyNativeFunc, IntoPySetterFunc},
function::{
HeapMethodDef, IntoPyGetterFunc, IntoPyNativeFn, IntoPySetterFunc, PyMethodDef,
PyMethodFlags,
},
intern::{InternableString, MaybeInternedString, StringPool},
object::{Py, PyObjectPayload, PyObjectRef, PyPayload, PyRef},
types::{PyTypeFlags, PyTypeSlots, TypeZoo},
@@ -45,7 +47,7 @@ pub struct Context {
pub int_cache_pool: Vec<PyIntRef>,
// there should only be exact objects of str in here, no non-str objects and no subclasses
pub(crate) string_pool: StringPool,
pub(crate) slot_new_wrapper: PyRef<PyBuiltinFunction>,
pub(crate) slot_new_wrapper: PyMethodDef,
pub names: ConstName,
}
@@ -287,14 +289,16 @@ impl Context {
let string_pool = StringPool::default();
let names = unsafe { ConstName::new(&string_pool, &types.str_type.to_owned()) };
let slot_new_wrapper = create_object(
PyNativeFuncDef::new(PyType::__new__.into_func(), names.__new__).into_function(),
types.builtin_function_or_method_type,
);
let slot_new_wrapper = PyMethodDef {
name: names.__new__.as_str(),
func: PyType::__new__.into_func(),
flags: PyMethodFlags::METHOD,
doc: None,
};
let empty_str = unsafe { string_pool.intern("", types.str_type.to_owned()) };
let empty_bytes = create_object(PyBytes::from(Vec::new()), types.bytes_type);
let context = Context {
Context {
true_value,
false_value,
none,
@@ -312,10 +316,7 @@ impl Context {
string_pool,
slot_new_wrapper,
names,
};
TypeZoo::extend(&context);
exceptions::ExceptionZoo::extend(&context);
context
}
}
pub fn intern_str<S: InternableString>(&self, s: S) -> &'static PyStrInterned {
@@ -484,12 +485,24 @@ impl Context {
.unwrap()
}
#[inline]
pub fn make_func_def<F, FKind>(&self, name: &'static PyStrInterned, f: F) -> PyNativeFuncDef
pub fn new_method_def<F, FKind>(
&self,
name: &'static str,
f: F,
flags: PyMethodFlags,
doc: Option<&'static str>,
) -> PyRef<HeapMethodDef>
where
F: IntoPyNativeFunc<FKind>,
F: IntoPyNativeFn<FKind>,
{
PyNativeFuncDef::new(f.into_func(), name)
let def = PyMethodDef {
name,
func: f.into_func(),
flags,
doc,
};
let payload = HeapMethodDef::new(def);
PyRef::new_ref(payload, self.types.method_def.to_owned(), None)
}
#[inline]
@@ -509,40 +522,14 @@ impl Context {
doc: None,
};
let member_descriptor = PyMemberDescriptor {
common: DescrObject {
common: PyDescriptorOwned {
typ: class.to_owned(),
name: self.intern_str(name),
qualname: PyRwLock::new(None),
},
member: member_def,
};
PyRef::new_ref(
member_descriptor,
self.types.member_descriptor_type.to_owned(),
None,
)
}
// #[deprecated]
pub fn new_function<F, FKind>(&self, name: &str, f: F) -> PyRef<PyBuiltinFunction>
where
F: IntoPyNativeFunc<FKind>,
{
self.make_func_def(self.intern_str(name), f)
.build_function(self)
}
pub fn new_method<F, FKind>(
&self,
name: &'static PyStrInterned,
class: &'static Py<PyType>,
f: F,
) -> PyRef<PyBuiltinMethod>
where
F: IntoPyNativeFunc<FKind>,
{
PyBuiltinMethod::new_ref(name, class, f, self)
member_descriptor.into_ref(self)
}
pub fn new_readonly_getset<F, T>(

View File

@@ -46,6 +46,8 @@ impl Interpreter {
F: FnOnce(&mut VirtualMachine),
{
let ctx = Context::genesis();
crate::types::TypeZoo::extend(ctx);
crate::exceptions::ExceptionZoo::extend(ctx);
let mut vm = VirtualMachine::new(settings, ctx.clone());
init(&mut vm);
vm.initialize();

View File

@@ -33,7 +33,11 @@ impl PyMethod {
let cls_attr = match interned_name.and_then(|name| cls.get_attr(name)) {
Some(descr) => {
let descr_cls = descr.class();
let descr_get = if descr_cls.slots.flags.has_feature(PyTypeFlags::METHOD_DESCR) {
let descr_get = if descr_cls
.slots
.flags
.has_feature(PyTypeFlags::METHOD_DESCRIPTOR)
{
is_method = true;
None
} else {
@@ -106,7 +110,7 @@ impl PyMethod {
.class()
.slots
.flags
.has_feature(PyTypeFlags::METHOD_DESCR)
.has_feature(PyTypeFlags::METHOD_DESCRIPTOR)
{
Self::Function {
target: obj.to_owned(),

View File

@@ -116,17 +116,17 @@ impl VirtualMachine {
// make a new module without access to the vm; doesn't
// set __spec__, __loader__, etc. attributes
let new_module = || {
let new_module = |def| {
PyRef::new_ref(
PyModule::new(),
PyModule::from_def(def),
ctx.types.module_type.to_owned(),
Some(ctx.new_dict()),
)
};
// Hard-core modules:
let builtins = new_module();
let sys_module = new_module();
let builtins = new_module(stdlib::builtins::__module_def(&ctx));
let sys_module = new_module(stdlib::sys::__module_def(&ctx));
let import_func = ctx.none();
let profile_func = RefCell::new(ctx.none());
@@ -199,11 +199,17 @@ impl VirtualMachine {
let frozen = frozen::core_frozen_inits().collect();
PyRc::get_mut(&mut vm.state).unwrap().frozen = frozen;
vm.builtins
.init_module_dict(vm.ctx.intern_str("builtins"), None, &vm);
vm.sys_module
.init_module_dict(vm.ctx.intern_str("sys"), None, &vm);
vm.builtins.init_dict(
vm.ctx.intern_str("builtins"),
Some(vm.ctx.intern_str(stdlib::builtins::DOC.unwrap()).to_owned()),
&vm,
);
vm.sys_module.init_dict(
vm.ctx.intern_str("sys"),
Some(vm.ctx.intern_str(stdlib::sys::DOC.unwrap()).to_owned()),
&vm,
);
// let name = vm.sys_module.get_attr("__name__", &vm).unwrap();
vm
}
@@ -251,7 +257,7 @@ impl VirtualMachine {
self.state_mut().settings.path_list.insert(0, "".to_owned());
stdlib::builtins::init_module(self, &self.builtins);
stdlib::sys::init_module(self, self.sys_module.as_ref(), self.builtins.as_ref());
stdlib::sys::init_module(self, &self.sys_module, &self.builtins);
let mut essential_init = || -> PyResult {
#[cfg(not(target_arch = "wasm32"))]
@@ -802,15 +808,13 @@ impl VirtualMachine {
pub fn __module_set_attr(
&self,
module: &Py<PyModule>,
attr_name: &str,
attr_name: &'static PyStrInterned,
attr_value: impl Into<PyObjectRef>,
) -> PyResult<()> {
let val = attr_value.into();
module.as_object().generic_setattr(
self.ctx.intern_str(attr_name),
PySetterValue::Assign(val),
self,
)
module
.as_object()
.generic_setattr(attr_name, PySetterValue::Assign(val), self)
}
pub fn insert_sys_path(&self, obj: PyObjectRef) -> PyResult<()> {

View File

@@ -1,9 +1,12 @@
use crate::{
builtins::{
builtin_func::PyNativeFunction,
descriptor::PyMethodDescriptor,
tuple::{IntoPyTuple, PyTupleRef},
PyBaseException, PyBaseExceptionRef, PyDictRef, PyModule, PyStrRef, PyType, PyTypeRef,
},
convert::ToPyObject,
function::{IntoPyNativeFn, PyMethodFlags},
scope::Scope,
vm::VirtualMachine,
AsObject, Py, PyObject, PyObjectRef, PyRef,
@@ -20,17 +23,18 @@ impl VirtualMachine {
value.into_pytuple(self)
}
pub fn new_module(&self, name: &str, dict: PyDictRef, doc: Option<&str>) -> PyRef<PyModule> {
pub fn new_module(
&self,
name: &str,
dict: PyDictRef,
doc: Option<PyStrRef>,
) -> PyRef<PyModule> {
let module = PyRef::new_ref(
PyModule::new(),
self.ctx.types.module_type.to_owned(),
Some(dict),
);
module.init_module_dict(
self.ctx.intern_str(name),
doc.map(|doc| self.ctx.new_str(doc)),
self,
);
module.init_dict(self.ctx.intern_str(name), doc, self);
module
}
@@ -38,6 +42,31 @@ impl VirtualMachine {
Scope::with_builtins(None, self.ctx.new_dict(), self)
}
pub fn new_function<F, FKind>(&self, name: &'static str, f: F) -> PyRef<PyNativeFunction>
where
F: IntoPyNativeFn<FKind>,
{
let def = self
.ctx
.new_method_def(name, f, PyMethodFlags::empty(), None);
def.build_function(self)
}
pub fn new_method<F, FKind>(
&self,
name: &'static str,
class: &'static Py<PyType>,
f: F,
) -> PyRef<PyMethodDescriptor>
where
F: IntoPyNativeFn<FKind>,
{
let def = self
.ctx
.new_method_def(name, f, PyMethodFlags::METHOD, None);
def.build_method(class, self)
}
/// Instantiate an exception with arguments.
/// This function should only be used with builtin exception types; if a user-defined exception
/// type is passed in, it may not be fully initialized; try using

View File

@@ -214,26 +214,25 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef {
}
} else if js_val.is_function() {
let func = js_sys::Function::from(js_val);
vm.ctx
.new_function(
String::from(func.name()).as_str(),
move |args: FuncArgs, vm: &VirtualMachine| -> PyResult {
let this = Object::new();
for (k, v) in args.kwargs {
Reflect::set(&this, &k.into(), &py_to_js(vm, v))
.expect("property to be settable");
}
let js_args = args
.args
.into_iter()
.map(|v| py_to_js(vm, v))
.collect::<Array>();
func.apply(&this, &js_args)
.map(|val| js_to_py(vm, val))
.map_err(|err| js_err_to_py_err(vm, &err))
},
)
.into()
vm.new_function(
vm.ctx.intern_str(String::from(func.name())).as_str(),
move |args: FuncArgs, vm: &VirtualMachine| -> PyResult {
let this = Object::new();
for (k, v) in args.kwargs {
Reflect::set(&this, &k.into(), &py_to_js(vm, v))
.expect("property to be settable");
}
let js_args = args
.args
.into_iter()
.map(|v| py_to_js(vm, v))
.collect::<Array>();
func.apply(&this, &js_args)
.map(|val| js_to_py(vm, val))
.map_err(|err| js_err_to_py_err(vm, &err))
},
)
.into()
} else if let Some(err) = js_val.dyn_ref::<js_sys::Error>() {
js_err_to_py_err(vm, err).into()
} else if js_val.is_undefined() {

View File

@@ -14,7 +14,7 @@ mod _js {
convert::{IntoObject, ToPyObject},
function::{ArgCallable, OptionalArg, OptionalOption, PosArgs},
protocol::PyIterReturn,
types::{IterNext, IterNextIterable, Representable},
types::{IterNext, Representable, SelfIter},
Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
};
use std::{cell, fmt, future};
@@ -423,8 +423,8 @@ mod _js {
};
let _ = then.call(
(
vm.ctx.new_function("resolve", resolve),
vm.ctx.new_function("reject", reject),
vm.new_function("resolve", resolve),
vm.new_function("reject", reject),
),
vm,
);
@@ -602,7 +602,7 @@ mod _js {
}
}
impl IterNextIterable for AwaitPromise {}
impl SelfIter for AwaitPromise {}
impl IterNext for AwaitPromise {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
zelf.send(None, vm)

View File

@@ -316,7 +316,7 @@ impl WASMVirtualMachine {
let (key, value) = entry?;
let key = Object::from(key).to_string();
extend_module!(vm, &py_module, {
&String::from(key) => convert::js_to_py(vm, value),
String::from(key) => convert::js_to_py(vm, value),
});
}

View File

@@ -29,14 +29,14 @@ pub fn make_stdout_object(
vm.ctx.types.object_type.to_owned(),
{}
));
let write_method = ctx.new_method(
ctx.intern_str("write"),
let write_method = vm.new_method(
"write",
cls,
move |_self: PyObjectRef, data: PyStrRef, vm: &VirtualMachine| -> PyResult<()> {
write_f(data.as_str(), vm)
},
);
let flush_method = ctx.new_method(ctx.intern_str("flush"), cls, |_self: PyObjectRef| {});
let flush_method = vm.new_method("flush", cls, |_self: PyObjectRef| {});
extend_class!(ctx, cls, {
"write" => write_method,
"flush" => flush_method,