forked from Rust-related/RustPython
Merge pull request #4873 from youknowone/method
Method overhaul with static PyMethodDef
This commit is contained in:
@@ -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)*
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -320,6 +320,7 @@ impl ItemMeta for ClassItemMeta {
|
||||
"base",
|
||||
"metaclass",
|
||||
"unhashable",
|
||||
"ctx",
|
||||
"impl",
|
||||
"traverse",
|
||||
];
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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)? {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
};
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
259
vm/src/function/method.rs
Normal 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 {}
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -80,3 +80,7 @@ pub trait PyObjectPayload:
|
||||
}
|
||||
|
||||
impl<T: PyPayload + 'static> PyObjectPayload for T {}
|
||||
|
||||
pub trait SlotOffset {
|
||||
fn offset() -> usize;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user