mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
Merge pull request #1832 from youknowone/pymodule
pymodule derive macro
This commit is contained in:
11 files changed
+3029
-2866
No files matched your search
@@ -12,6 +12,7 @@ mod error;
|
||||
mod compile_bytecode;
|
||||
mod from_args;
|
||||
mod pyclass;
|
||||
mod pymodule;
|
||||
mod util;
|
||||
|
||||
use error::{extract_spans, Diagnostic};
|
||||
@@ -44,6 +45,13 @@ pub fn pyimpl(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
result_to_tokens(pyclass::impl_pyimpl(attr, item))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn pymodule(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let attr = parse_macro_input!(attr as AttributeArgs);
|
||||
let item = parse_macro_input!(item as Item);
|
||||
result_to_tokens(pymodule::impl_pymodule(attr, item))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn pystruct_sequence(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let attr = parse_macro_input!(attr as AttributeArgs);
|
||||
|
||||
+47
-257
@@ -1,11 +1,11 @@
|
||||
use super::Diagnostic;
|
||||
use crate::util::path_eq;
|
||||
use crate::util::{def_to_name, path_eq, strip_prefix, ItemIdent, ItemMeta};
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use syn::{
|
||||
parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Index, Item, Lit, Meta,
|
||||
NestedMeta, Signature,
|
||||
NestedMeta,
|
||||
};
|
||||
|
||||
fn meta_to_vec(meta: Meta) -> Result<Vec<NestedMeta>, Meta> {
|
||||
@@ -42,181 +42,6 @@ enum ClassItem {
|
||||
},
|
||||
}
|
||||
|
||||
struct ClassItemMeta<'a> {
|
||||
sig: &'a Signature,
|
||||
parent_type: &'static str,
|
||||
meta: HashMap<String, Option<Lit>>,
|
||||
}
|
||||
|
||||
impl<'a> ClassItemMeta<'a> {
|
||||
const METHOD_NAMES: &'static [&'static str] = &["name", "magic"];
|
||||
const PROPERTY_NAMES: &'static [&'static str] = &["name", "magic", "setter"];
|
||||
|
||||
fn from_nested_meta(
|
||||
parent_type: &'static str,
|
||||
sig: &'a Signature,
|
||||
nested_meta: &[NestedMeta],
|
||||
names: &[&'static str],
|
||||
) -> Result<Self, Diagnostic> {
|
||||
let mut extracted = Self {
|
||||
sig,
|
||||
parent_type,
|
||||
meta: HashMap::new(),
|
||||
};
|
||||
|
||||
let validate_name = |name: &str, extracted: &Self| -> Result<(), Diagnostic> {
|
||||
if names.contains(&name) {
|
||||
if extracted.meta.contains_key(name) {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"#[{}] must have only one '{}'",
|
||||
parent_type,
|
||||
name
|
||||
);
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"#[{}({})] is not one of allowed attributes {}",
|
||||
parent_type,
|
||||
name,
|
||||
names.join(", ")
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
for meta in nested_meta {
|
||||
let meta = match meta {
|
||||
NestedMeta::Meta(meta) => meta,
|
||||
NestedMeta::Lit(_) => continue,
|
||||
};
|
||||
|
||||
match meta {
|
||||
Meta::NameValue(name_value) => {
|
||||
if let Some(ident) = name_value.path.get_ident() {
|
||||
let name = ident.to_string();
|
||||
validate_name(&name, &extracted)?;
|
||||
extracted.meta.insert(name, Some(name_value.lit.clone()));
|
||||
}
|
||||
}
|
||||
Meta::Path(path) => {
|
||||
if let Some(ident) = path.get_ident() {
|
||||
let name = ident.to_string();
|
||||
validate_name(&name, &extracted)?;
|
||||
extracted.meta.insert(name, None);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(extracted)
|
||||
}
|
||||
|
||||
fn _str(&self, key: &str) -> Result<Option<String>, Diagnostic> {
|
||||
Ok(match self.meta.get(key) {
|
||||
Some(Some(lit)) => {
|
||||
if let Lit::Str(s) = lit {
|
||||
Some(s.value())
|
||||
} else {
|
||||
bail_span!(
|
||||
&self.sig.ident,
|
||||
"#[{}({} = ...)] must be a string",
|
||||
self.parent_type,
|
||||
key
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(None) => {
|
||||
bail_span!(
|
||||
&self.sig.ident,
|
||||
"#[{}({} = ...)] is expected",
|
||||
self.parent_type,
|
||||
key,
|
||||
);
|
||||
}
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn _bool(&self, key: &str) -> Result<bool, Diagnostic> {
|
||||
Ok(match self.meta.get(key) {
|
||||
Some(Some(_)) => {
|
||||
bail_span!(
|
||||
&self.sig.ident,
|
||||
"#[{}({})] is expected",
|
||||
self.parent_type,
|
||||
key,
|
||||
);
|
||||
}
|
||||
Some(None) => true,
|
||||
None => false,
|
||||
})
|
||||
}
|
||||
|
||||
fn method_name(&self) -> Result<String, Diagnostic> {
|
||||
let name = self._str("name")?;
|
||||
let magic = self._bool("magic")?;
|
||||
Ok(if let Some(name) = name {
|
||||
name
|
||||
} else {
|
||||
let name = self.sig.ident.to_string();
|
||||
if magic {
|
||||
format!("__{}__", name)
|
||||
} else {
|
||||
name
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn setter(&self) -> Result<bool, Diagnostic> {
|
||||
self._bool("setter")
|
||||
}
|
||||
|
||||
fn property_name(&self) -> Result<String, Diagnostic> {
|
||||
let magic = self._bool("magic")?;
|
||||
let setter = self._bool("setter")?;
|
||||
let name = self._str("name")?;
|
||||
|
||||
Ok(if let Some(name) = name {
|
||||
name
|
||||
} else {
|
||||
let sig_name = self.sig.ident.to_string();
|
||||
let name = if setter {
|
||||
if let Some(name) = strip_prefix(&sig_name, "set_") {
|
||||
if name.is_empty() {
|
||||
bail_span!(
|
||||
&self.sig.ident,
|
||||
"A #[{}(setter)] fn with a set_* name must \
|
||||
have something after \"set_\"",
|
||||
self.parent_type
|
||||
)
|
||||
}
|
||||
name.to_string()
|
||||
} else {
|
||||
bail_span!(
|
||||
&self.sig.ident,
|
||||
"A #[{}(setter)] fn must either have a `name` \
|
||||
parameter or a fn name along the lines of \"set_*\"",
|
||||
self.parent_type
|
||||
)
|
||||
}
|
||||
} else {
|
||||
sig_name
|
||||
};
|
||||
if magic {
|
||||
format!("__{}__", name)
|
||||
} else {
|
||||
name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Class {
|
||||
fn add_item(&mut self, item: ClassItem, span: Span) -> Result<(), Diagnostic> {
|
||||
if self.items.insert(item) {
|
||||
@@ -229,7 +54,7 @@ impl Class {
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_method(sig: &Signature, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
fn extract_method(ident: &Ident, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
@@ -238,19 +63,15 @@ impl Class {
|
||||
)
|
||||
})?;
|
||||
|
||||
let item_meta = ClassItemMeta::from_nested_meta(
|
||||
"pymethod",
|
||||
sig,
|
||||
&nesteds,
|
||||
ClassItemMeta::METHOD_NAMES,
|
||||
)?;
|
||||
let item_meta =
|
||||
ItemMeta::from_nested_meta("pymethod", &ident, &nesteds, ItemMeta::ATTRIBUTE_NAMES)?;
|
||||
Ok(ClassItem::Method {
|
||||
item_ident: sig.ident.clone(),
|
||||
item_ident: ident.clone(),
|
||||
py_name: item_meta.method_name()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_classmethod(sig: &Signature, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
fn extract_classmethod(ident: &Ident, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
@@ -258,19 +79,19 @@ impl Class {
|
||||
#[pyclassmethod(name = \"...\")]",
|
||||
)
|
||||
})?;
|
||||
let item_meta = ClassItemMeta::from_nested_meta(
|
||||
let item_meta = ItemMeta::from_nested_meta(
|
||||
"pyclassmethod",
|
||||
sig,
|
||||
&ident,
|
||||
&nesteds,
|
||||
ClassItemMeta::METHOD_NAMES,
|
||||
ItemMeta::ATTRIBUTE_NAMES,
|
||||
)?;
|
||||
Ok(ClassItem::ClassMethod {
|
||||
item_ident: sig.ident.clone(),
|
||||
item_ident: ident.clone(),
|
||||
py_name: item_meta.method_name()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_property(sig: &Signature, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
fn extract_property(ident: &Ident, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
@@ -278,31 +99,27 @@ impl Class {
|
||||
#[pyproperty(name = \"...\")]"
|
||||
)
|
||||
})?;
|
||||
let item_meta = ClassItemMeta::from_nested_meta(
|
||||
"pyproperty",
|
||||
sig,
|
||||
&nesteds,
|
||||
ClassItemMeta::PROPERTY_NAMES,
|
||||
)?;
|
||||
let item_meta =
|
||||
ItemMeta::from_nested_meta("pyproperty", &ident, &nesteds, ItemMeta::PROPERTY_NAMES)?;
|
||||
Ok(ClassItem::Property {
|
||||
py_name: item_meta.property_name()?,
|
||||
item_ident: sig.ident.clone(),
|
||||
item_ident: ident.clone(),
|
||||
setter: item_meta.setter()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_slot(sig: &Signature, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
fn extract_slot(ident: &Ident, meta: Meta) -> Result<ClassItem, Diagnostic> {
|
||||
let pyslot_err = "#[pyslot] must be of the form #[pyslot] or #[pyslot(slotname)]";
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| err_span!(meta, "{}", pyslot_err))?;
|
||||
if nesteds.len() > 1 {
|
||||
return Err(Diagnostic::spanned_error("e!(#(#nesteds)*), pyslot_err));
|
||||
}
|
||||
let slot_ident = if nesteds.is_empty() {
|
||||
let ident_str = sig.ident.to_string();
|
||||
let ident_str = ident.to_string();
|
||||
if let Some(stripped) = strip_prefix(&ident_str, "tp_") {
|
||||
proc_macro2::Ident::new(stripped, sig.ident.span())
|
||||
proc_macro2::Ident::new(stripped, ident.span())
|
||||
} else {
|
||||
sig.ident.clone()
|
||||
ident.clone()
|
||||
}
|
||||
} else {
|
||||
match nesteds.into_iter().next().unwrap() {
|
||||
@@ -315,14 +132,14 @@ impl Class {
|
||||
};
|
||||
Ok(ClassItem::Slot {
|
||||
slot_ident,
|
||||
item_ident: sig.ident.clone(),
|
||||
item_ident: ident.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_item_from_syn(
|
||||
&mut self,
|
||||
attrs: &mut Vec<Attribute>,
|
||||
sig: &Signature,
|
||||
ident: &Ident,
|
||||
) -> Result<(), Diagnostic> {
|
||||
let mut attr_idxs = Vec::new();
|
||||
for (i, meta) in attrs
|
||||
@@ -335,17 +152,16 @@ impl Class {
|
||||
Some(name) => name,
|
||||
None => continue,
|
||||
};
|
||||
if name == "pymethod" {
|
||||
self.add_item(Self::extract_method(sig, meta)?, meta_span)?;
|
||||
} else if name == "pyclassmethod" {
|
||||
self.add_item(Self::extract_classmethod(sig, meta)?, meta_span)?;
|
||||
} else if name == "pyproperty" {
|
||||
self.add_item(Self::extract_property(sig, meta)?, meta_span)?;
|
||||
} else if name == "pyslot" {
|
||||
self.add_item(Self::extract_slot(sig, meta)?, meta_span)?;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
let item = match name.to_string().as_str() {
|
||||
"pymethod" => Self::extract_method(ident, meta)?,
|
||||
"pyclassmethod" => Self::extract_classmethod(ident, meta)?,
|
||||
"pyproperty" => Self::extract_property(ident, meta)?,
|
||||
"pyslot" => Self::extract_slot(ident, meta)?,
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
self.add_item(item, meta_span)?;
|
||||
attr_idxs.push(i);
|
||||
}
|
||||
let mut i = 0;
|
||||
@@ -365,12 +181,7 @@ impl Class {
|
||||
}
|
||||
}
|
||||
|
||||
struct ItemSig<'a> {
|
||||
attrs: &'a mut Vec<Attribute>,
|
||||
sig: &'a Signature,
|
||||
}
|
||||
|
||||
fn extract_impl_items(mut items: Vec<ItemSig>) -> Result<TokenStream2, Diagnostic> {
|
||||
fn extract_impl_items(mut items: Vec<ItemIdent>) -> Result<TokenStream2, Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||
|
||||
let mut class = Class::default();
|
||||
@@ -378,7 +189,7 @@ fn extract_impl_items(mut items: Vec<ItemSig>) -> Result<TokenStream2, Diagnosti
|
||||
for item in items.iter_mut() {
|
||||
push_diag_result!(
|
||||
diagnostics,
|
||||
class.extract_item_from_syn(&mut item.attrs, item.sig),
|
||||
class.extract_item_from_syn(&mut item.attrs, &item.ident),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -544,7 +355,10 @@ pub fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diag
|
||||
.iter_mut()
|
||||
.filter_map(|item| match item {
|
||||
syn::ImplItem::Method(syn::ImplItemMethod { attrs, sig, .. }) => {
|
||||
Some(ItemSig { attrs, sig })
|
||||
Some(ItemIdent {
|
||||
attrs,
|
||||
ident: &sig.ident,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
@@ -574,7 +388,10 @@ pub fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diag
|
||||
.iter_mut()
|
||||
.filter_map(|item| match item {
|
||||
syn::TraitItem::Method(syn::TraitItemMethod { attrs, sig, .. }) => {
|
||||
Some(ItemSig { attrs, sig })
|
||||
Some(ItemIdent {
|
||||
attrs,
|
||||
ident: &sig.ident,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
@@ -597,30 +414,9 @@ pub fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diag
|
||||
|
||||
fn generate_class_def(
|
||||
ident: &Ident,
|
||||
attr_name: &'static str,
|
||||
attr: AttributeArgs,
|
||||
name: &str,
|
||||
attrs: &[Attribute],
|
||||
) -> Result<TokenStream2, Diagnostic> {
|
||||
let mut class_name = None;
|
||||
for attr in attr {
|
||||
if let NestedMeta::Meta(meta) = attr {
|
||||
if let Meta::NameValue(name_value) = meta {
|
||||
if path_eq(&name_value.path, "name") {
|
||||
if let Lit::Str(s) = name_value.lit {
|
||||
class_name = Some(s.value());
|
||||
} else {
|
||||
bail_span!(
|
||||
name_value.lit,
|
||||
"#[{}(name = ...)] must be a string",
|
||||
attr_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let class_name = class_name.unwrap_or_else(|| ident.to_string());
|
||||
|
||||
let mut doc: Option<Vec<String>> = None;
|
||||
for attr in attrs.iter() {
|
||||
if attr.path.is_ident("doc") {
|
||||
@@ -646,7 +442,7 @@ fn generate_class_def(
|
||||
|
||||
let ret = quote! {
|
||||
impl ::rustpython_vm::pyobject::PyClassDef for #ident {
|
||||
const NAME: &'static str = #class_name;
|
||||
const NAME: &'static str = #name;
|
||||
const DOC: Option<&'static str> = #doc;
|
||||
}
|
||||
};
|
||||
@@ -663,7 +459,8 @@ pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
|
||||
),
|
||||
};
|
||||
|
||||
let class_def = generate_class_def(&ident, "pyclass", attr, &attrs)?;
|
||||
let class_name = def_to_name(&ident, "pyclass", attr)?;
|
||||
let class_def = generate_class_def(&ident, &class_name, &attrs)?;
|
||||
|
||||
let ret = quote! {
|
||||
#item
|
||||
@@ -681,7 +478,8 @@ pub fn impl_pystruct_sequence(attr: AttributeArgs, item: Item) -> Result<TokenSt
|
||||
"#[pystruct_sequence] can only be on a struct declaration"
|
||||
)
|
||||
};
|
||||
let class_def = generate_class_def(&struc.ident, "pystruct_sequence", attr, &struc.attrs)?;
|
||||
let class_name = def_to_name(&struc.ident, "pystruct_sequence", attr)?;
|
||||
let class_def = generate_class_def(&struc.ident, &class_name, &struc.attrs)?;
|
||||
let mut properties = Vec::new();
|
||||
let mut field_names = Vec::new();
|
||||
for (i, field) in struc.fields.iter().enumerate() {
|
||||
@@ -745,11 +543,3 @@ pub fn impl_pystruct_sequence(attr: AttributeArgs, item: Item) -> Result<TokenSt
|
||||
};
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn strip_prefix<'a>(s: &'a str, prefix: &str) -> Option<&'a str> {
|
||||
if s.starts_with(prefix) {
|
||||
Some(&s[prefix.len()..])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
use super::Diagnostic;
|
||||
use crate::util::{def_to_name, ItemIdent, ItemMeta};
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{quote, quote_spanned};
|
||||
use std::collections::HashSet;
|
||||
use syn::{parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Item, Meta, NestedMeta};
|
||||
|
||||
fn meta_to_vec(meta: Meta) -> Result<Vec<NestedMeta>, Meta> {
|
||||
match meta {
|
||||
Meta::Path(_) => Ok(Vec::new()),
|
||||
Meta::List(list) => Ok(list.nested.into_iter().collect()),
|
||||
Meta::NameValue(_) => Err(meta),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Module {
|
||||
items: HashSet<ModuleItem>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
enum ModuleItem {
|
||||
Function { item_ident: Ident, py_name: String },
|
||||
Class { item_ident: Ident, py_name: String },
|
||||
}
|
||||
|
||||
impl Module {
|
||||
fn add_item(&mut self, item: ModuleItem, span: Span) -> Result<(), Diagnostic> {
|
||||
if self.items.insert(item) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Diagnostic::span_error(
|
||||
span,
|
||||
"Duplicate #[py*] attribute on pyimpl".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_function(ident: &Ident, meta: Meta) -> Result<ModuleItem, Diagnostic> {
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
"#[pyfunction = \"...\"] cannot be a name/value, you probably meant \
|
||||
#[pyfunction(name = \"...\")]",
|
||||
)
|
||||
})?;
|
||||
|
||||
let item_meta =
|
||||
ItemMeta::from_nested_meta("pyfunction", &ident, &nesteds, ItemMeta::SIMPLE_NAMES)?;
|
||||
Ok(ModuleItem::Function {
|
||||
item_ident: ident.clone(),
|
||||
py_name: item_meta.simple_name()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_class(ident: &Ident, meta: Meta) -> Result<ModuleItem, Diagnostic> {
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
"#[pyclass = \"...\"] cannot be a name/value, you probably meant \
|
||||
#[pyclass(name = \"...\")]",
|
||||
)
|
||||
})?;
|
||||
|
||||
let item_meta =
|
||||
ItemMeta::from_nested_meta("pyclass", &ident, &nesteds, ItemMeta::SIMPLE_NAMES)?;
|
||||
Ok(ModuleItem::Class {
|
||||
item_ident: ident.clone(),
|
||||
py_name: item_meta.simple_name()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_item_from_syn(
|
||||
&mut self,
|
||||
attrs: &mut Vec<Attribute>,
|
||||
ident: &Ident,
|
||||
) -> Result<(), Diagnostic> {
|
||||
let mut attr_idxs = Vec::new();
|
||||
for (i, meta) in attrs
|
||||
.iter()
|
||||
.filter_map(|attr| attr.parse_meta().ok())
|
||||
.enumerate()
|
||||
{
|
||||
let meta_span = meta.span();
|
||||
let name = match meta.path().get_ident() {
|
||||
Some(name) => name,
|
||||
None => continue,
|
||||
};
|
||||
let item = match name.to_string().as_str() {
|
||||
"pyfunction" => {
|
||||
attr_idxs.push(i);
|
||||
Self::extract_function(ident, meta)?
|
||||
}
|
||||
"pyclass" => Self::extract_class(ident, meta)?,
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
self.add_item(item, meta_span)?;
|
||||
}
|
||||
let mut i = 0;
|
||||
let mut attr_idxs = &*attr_idxs;
|
||||
attrs.retain(|_| {
|
||||
let drop = attr_idxs.first().copied() == Some(i);
|
||||
if drop {
|
||||
attr_idxs = &attr_idxs[1..];
|
||||
}
|
||||
i += 1;
|
||||
!drop
|
||||
});
|
||||
for (i, idx) in attr_idxs.iter().enumerate() {
|
||||
attrs.remove(idx - i);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_module_items(mut items: Vec<ItemIdent>) -> Result<TokenStream2, Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||
|
||||
let mut module = Module::default();
|
||||
|
||||
for item in items.iter_mut() {
|
||||
push_diag_result!(
|
||||
diagnostics,
|
||||
module.extract_item_from_syn(&mut item.attrs, item.ident),
|
||||
);
|
||||
}
|
||||
|
||||
let functions = module.items.into_iter().map(|item| match item {
|
||||
ModuleItem::Function {
|
||||
item_ident,
|
||||
py_name,
|
||||
} => {
|
||||
let new_func = quote_spanned!(item_ident.span() => .new_function(#item_ident));
|
||||
quote! {
|
||||
vm.__module_set_attr(&module, #py_name, vm.ctx#new_func).unwrap();
|
||||
}
|
||||
}
|
||||
ModuleItem::Class {
|
||||
item_ident,
|
||||
py_name,
|
||||
} => {
|
||||
let new_class = quote_spanned!(item_ident.span() => #item_ident::make_class(&vm.ctx));
|
||||
quote! {
|
||||
vm.__module_set_attr(&module, #py_name, #new_class).unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Diagnostic::from_vec(diagnostics)?;
|
||||
|
||||
Ok(quote! {
|
||||
#(#functions)*
|
||||
})
|
||||
}
|
||||
|
||||
pub fn impl_pymodule(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
|
||||
match item {
|
||||
Item::Mod(mut module) => {
|
||||
let module_name = def_to_name(&module.ident, "pymodule", attr)?;
|
||||
|
||||
if let Some(content) = module.content.as_mut() {
|
||||
let items = content
|
||||
.1
|
||||
.iter_mut()
|
||||
.filter_map(|item| match item {
|
||||
Item::Fn(syn::ItemFn { attrs, sig, .. }) => Some(ItemIdent {
|
||||
attrs,
|
||||
ident: &sig.ident,
|
||||
}),
|
||||
Item::Struct(syn::ItemStruct { attrs, ident, .. }) => {
|
||||
Some(ItemIdent { attrs, ident })
|
||||
}
|
||||
Item::Enum(syn::ItemEnum { attrs, ident, .. }) => {
|
||||
Some(ItemIdent { attrs, ident })
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let extend_mod = extract_module_items(items)?;
|
||||
content.1.push(parse_quote! {
|
||||
const MODULE_NAME: &str = #module_name;
|
||||
});
|
||||
content.1.push(parse_quote! {
|
||||
pub(crate) fn extend_module(
|
||||
vm: &::rustpython_vm::vm::VirtualMachine,
|
||||
module: &::rustpython_vm::pyobject::PyObjectRef,
|
||||
) {
|
||||
#extend_mod
|
||||
}
|
||||
});
|
||||
content.1.push(parse_quote! {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn make_module(
|
||||
vm: &::rustpython_vm::vm::VirtualMachine
|
||||
) -> ::rustpython_vm::pyobject::PyObjectRef {
|
||||
let module = vm.new_module(MODULE_NAME, vm.ctx.new_dict());
|
||||
extend_module(vm, &module);
|
||||
module
|
||||
}
|
||||
});
|
||||
|
||||
Ok(quote! {
|
||||
#module
|
||||
})
|
||||
} else {
|
||||
bail_span!(
|
||||
module,
|
||||
"#[pymodule] can only be on a module declaration with body"
|
||||
)
|
||||
}
|
||||
}
|
||||
other => bail_span!(other, "#[pymodule] can only be on a module declaration"),
|
||||
}
|
||||
}
|
||||
+214
-1
@@ -1,3 +1,216 @@
|
||||
pub fn path_eq(path: &syn::Path, s: &str) -> bool {
|
||||
use super::Diagnostic;
|
||||
use std::collections::HashMap;
|
||||
use syn::{Attribute, AttributeArgs, Ident, Lit, Meta, NestedMeta, Path};
|
||||
|
||||
pub fn path_eq(path: &Path, s: &str) -> bool {
|
||||
path.get_ident().map_or(false, |id| id == s)
|
||||
}
|
||||
|
||||
pub fn def_to_name(
|
||||
ident: &Ident,
|
||||
attr_name: &'static str,
|
||||
attr: AttributeArgs,
|
||||
) -> Result<String, Diagnostic> {
|
||||
let mut name = None;
|
||||
for attr in attr {
|
||||
if let NestedMeta::Meta(meta) = attr {
|
||||
if let Meta::NameValue(name_value) = meta {
|
||||
if path_eq(&name_value.path, "name") {
|
||||
if let Lit::Str(s) = name_value.lit {
|
||||
name = Some(s.value());
|
||||
} else {
|
||||
bail_span!(
|
||||
name_value.lit,
|
||||
"#[{}(name = ...)] must be a string",
|
||||
attr_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(name.unwrap_or_else(|| ident.to_string()))
|
||||
}
|
||||
|
||||
pub fn strip_prefix<'a>(s: &'a str, prefix: &str) -> Option<&'a str> {
|
||||
if s.starts_with(prefix) {
|
||||
Some(&s[prefix.len()..])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ItemIdent<'a> {
|
||||
pub attrs: &'a mut Vec<Attribute>,
|
||||
pub ident: &'a Ident,
|
||||
}
|
||||
|
||||
pub struct ItemMeta<'a> {
|
||||
ident: &'a Ident,
|
||||
parent_type: &'static str,
|
||||
meta: HashMap<String, Option<Lit>>,
|
||||
}
|
||||
|
||||
impl<'a> ItemMeta<'a> {
|
||||
pub const SIMPLE_NAMES: &'static [&'static str] = &["name"];
|
||||
pub const ATTRIBUTE_NAMES: &'static [&'static str] = &["name", "magic"];
|
||||
pub const PROPERTY_NAMES: &'static [&'static str] = &["name", "magic", "setter"];
|
||||
|
||||
pub fn from_nested_meta(
|
||||
parent_type: &'static str,
|
||||
ident: &'a Ident,
|
||||
nested_meta: &[NestedMeta],
|
||||
names: &[&'static str],
|
||||
) -> Result<Self, Diagnostic> {
|
||||
let mut extracted = Self {
|
||||
ident,
|
||||
parent_type,
|
||||
meta: HashMap::new(),
|
||||
};
|
||||
|
||||
let validate_name = |name: &str, extracted: &Self| -> Result<(), Diagnostic> {
|
||||
if names.contains(&name) {
|
||||
if extracted.meta.contains_key(name) {
|
||||
bail_span!(ident, "#[{}] must have only one '{}'", parent_type, name);
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
bail_span!(
|
||||
ident,
|
||||
"#[{}({})] is not one of allowed attributes {}",
|
||||
parent_type,
|
||||
name,
|
||||
names.join(", ")
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
for meta in nested_meta {
|
||||
let meta = match meta {
|
||||
NestedMeta::Meta(meta) => meta,
|
||||
NestedMeta::Lit(_) => continue,
|
||||
};
|
||||
|
||||
match meta {
|
||||
Meta::NameValue(name_value) => {
|
||||
if let Some(ident) = name_value.path.get_ident() {
|
||||
let name = ident.to_string();
|
||||
validate_name(&name, &extracted)?;
|
||||
extracted.meta.insert(name, Some(name_value.lit.clone()));
|
||||
}
|
||||
}
|
||||
Meta::Path(path) => {
|
||||
if let Some(ident) = path.get_ident() {
|
||||
let name = ident.to_string();
|
||||
validate_name(&name, &extracted)?;
|
||||
extracted.meta.insert(name, None);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(extracted)
|
||||
}
|
||||
|
||||
fn _str(&self, key: &str) -> Result<Option<String>, Diagnostic> {
|
||||
Ok(match self.meta.get(key) {
|
||||
Some(Some(lit)) => {
|
||||
if let Lit::Str(s) = lit {
|
||||
Some(s.value())
|
||||
} else {
|
||||
bail_span!(
|
||||
&self.ident,
|
||||
"#[{}({} = ...)] must be a string",
|
||||
self.parent_type,
|
||||
key
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(None) => {
|
||||
bail_span!(
|
||||
&self.ident,
|
||||
"#[{}({} = ...)] is expected",
|
||||
self.parent_type,
|
||||
key,
|
||||
);
|
||||
}
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn _bool(&self, key: &str) -> Result<bool, Diagnostic> {
|
||||
Ok(match self.meta.get(key) {
|
||||
Some(Some(_)) => {
|
||||
bail_span!(&self.ident, "#[{}({})] is expected", self.parent_type, key,);
|
||||
}
|
||||
Some(None) => true,
|
||||
None => false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn simple_name(&self) -> Result<String, Diagnostic> {
|
||||
Ok(self._str("name")?.unwrap_or_else(|| self.ident.to_string()))
|
||||
}
|
||||
|
||||
pub fn method_name(&self) -> Result<String, Diagnostic> {
|
||||
let name = self._str("name")?;
|
||||
let magic = self._bool("magic")?;
|
||||
Ok(if let Some(name) = name {
|
||||
name
|
||||
} else {
|
||||
let name = self.ident.to_string();
|
||||
if magic {
|
||||
format!("__{}__", name)
|
||||
} else {
|
||||
name
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn property_name(&self) -> Result<String, Diagnostic> {
|
||||
let magic = self._bool("magic")?;
|
||||
let setter = self._bool("setter")?;
|
||||
let name = self._str("name")?;
|
||||
|
||||
Ok(if let Some(name) = name {
|
||||
name
|
||||
} else {
|
||||
let sig_name = self.ident.to_string();
|
||||
let name = if setter {
|
||||
if let Some(name) = strip_prefix(&sig_name, "set_") {
|
||||
if name.is_empty() {
|
||||
bail_span!(
|
||||
&self.ident,
|
||||
"A #[{}(setter)] fn with a set_* name must \
|
||||
have something after \"set_\"",
|
||||
self.parent_type
|
||||
)
|
||||
}
|
||||
name.to_string()
|
||||
} else {
|
||||
bail_span!(
|
||||
&self.ident,
|
||||
"A #[{}(setter)] fn must either have a `name` \
|
||||
parameter or a fn name along the lines of \"set_*\"",
|
||||
self.parent_type
|
||||
)
|
||||
}
|
||||
} else {
|
||||
sig_name
|
||||
};
|
||||
if magic {
|
||||
format!("__{}__", name)
|
||||
} else {
|
||||
name
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn setter(&self) -> Result<bool, Diagnostic> {
|
||||
self._bool("setter")
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ use crate::pyobject::{
|
||||
PyArithmaticValue::{self, *},
|
||||
PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue, ThreadSafe,
|
||||
};
|
||||
use crate::sequence::{self, SimpleSeq};
|
||||
use crate::sequence;
|
||||
use crate::vm::{ReprGuard, VirtualMachine};
|
||||
|
||||
/// tuple() -> empty tuple
|
||||
|
||||
+127
-130
@@ -1,147 +1,144 @@
|
||||
use crate::function::OptionalArg;
|
||||
use crate::obj::objbytearray::{PyByteArray, PyByteArrayRef};
|
||||
use crate::obj::objbyteinner::PyBytesLike;
|
||||
use crate::obj::objbytes::{PyBytes, PyBytesRef};
|
||||
use crate::obj::objstr::{PyString, PyStringRef};
|
||||
use crate::pyobject::{PyObjectRef, PyResult, TryFromObject, TypeProtocol};
|
||||
use crate::vm::VirtualMachine;
|
||||
pub(crate) use decl::make_module;
|
||||
|
||||
use crc::{crc32, Hasher32};
|
||||
use itertools::Itertools;
|
||||
#[pymodule(name = "binascii")]
|
||||
mod decl {
|
||||
use crate::function::OptionalArg;
|
||||
use crate::obj::objbytearray::{PyByteArray, PyByteArrayRef};
|
||||
use crate::obj::objbyteinner::PyBytesLike;
|
||||
use crate::obj::objbytes::{PyBytes, PyBytesRef};
|
||||
use crate::obj::objstr::{PyString, PyStringRef};
|
||||
use crate::pyobject::{PyObjectRef, PyResult, TryFromObject, TypeProtocol};
|
||||
use crate::vm::VirtualMachine;
|
||||
use crc::{crc32, Hasher32};
|
||||
use itertools::Itertools;
|
||||
|
||||
enum SerializedData {
|
||||
Bytes(PyBytesRef),
|
||||
Buffer(PyByteArrayRef),
|
||||
Ascii(PyStringRef),
|
||||
}
|
||||
enum SerializedData {
|
||||
Bytes(PyBytesRef),
|
||||
Buffer(PyByteArrayRef),
|
||||
Ascii(PyStringRef),
|
||||
}
|
||||
|
||||
impl TryFromObject for SerializedData {
|
||||
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
|
||||
match_class!(match obj {
|
||||
b @ PyBytes => Ok(SerializedData::Bytes(b)),
|
||||
b @ PyByteArray => Ok(SerializedData::Buffer(b)),
|
||||
a @ PyString => {
|
||||
if a.as_str().is_ascii() {
|
||||
Ok(SerializedData::Ascii(a))
|
||||
} else {
|
||||
Err(vm.new_value_error(
|
||||
"string argument should contain only ASCII characters".to_owned(),
|
||||
))
|
||||
impl TryFromObject for SerializedData {
|
||||
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
|
||||
match_class!(match obj {
|
||||
b @ PyBytes => Ok(SerializedData::Bytes(b)),
|
||||
b @ PyByteArray => Ok(SerializedData::Buffer(b)),
|
||||
a @ PyString => {
|
||||
if a.as_str().is_ascii() {
|
||||
Ok(SerializedData::Ascii(a))
|
||||
} else {
|
||||
Err(vm.new_value_error(
|
||||
"string argument should contain only ASCII characters".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
obj => Err(vm.new_type_error(format!(
|
||||
"argument should be bytes, buffer or ASCII string, not '{}'",
|
||||
obj.class().name,
|
||||
))),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SerializedData {
|
||||
#[inline]
|
||||
pub fn with_ref<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
|
||||
match self {
|
||||
SerializedData::Bytes(b) => f(b.get_value()),
|
||||
SerializedData::Buffer(b) => f(&b.borrow_value().elements),
|
||||
SerializedData::Ascii(a) => f(a.as_str().as_bytes()),
|
||||
}
|
||||
obj => Err(vm.new_type_error(format!(
|
||||
"argument should be bytes, buffer or ASCII string, not '{}'",
|
||||
obj.class().name,
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn hex_nibble(n: u8) -> u8 {
|
||||
match n {
|
||||
0..=9 => b'0' + n,
|
||||
10..=15 => b'a' + n,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[pyfunction(name = "b2a_hex")]
|
||||
#[pyfunction]
|
||||
fn hexlify(data: PyBytesLike) -> Vec<u8> {
|
||||
data.with_ref(|bytes| {
|
||||
let mut hex = Vec::<u8>::with_capacity(bytes.len() * 2);
|
||||
for b in bytes.iter() {
|
||||
hex.push(hex_nibble(b >> 4));
|
||||
hex.push(hex_nibble(b & 0xf));
|
||||
}
|
||||
hex
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SerializedData {
|
||||
#[inline]
|
||||
pub fn with_ref<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
|
||||
match self {
|
||||
SerializedData::Bytes(b) => f(b.get_value()),
|
||||
SerializedData::Buffer(b) => f(&b.borrow_value().elements),
|
||||
SerializedData::Ascii(a) => f(a.as_str().as_bytes()),
|
||||
fn unhex_nibble(c: u8) -> Option<u8> {
|
||||
match c {
|
||||
b'0'..=b'9' => Some(c - b'0'),
|
||||
b'a'..=b'f' => Some(c - b'a' + 10),
|
||||
b'A'..=b'F' => Some(c - b'A' + 10),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hex_nibble(n: u8) -> u8 {
|
||||
match n {
|
||||
0..=9 => b'0' + n,
|
||||
10..=15 => b'a' + n,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn binascii_hexlify(data: PyBytesLike) -> Vec<u8> {
|
||||
data.with_ref(|bytes| {
|
||||
let mut hex = Vec::<u8>::with_capacity(bytes.len() * 2);
|
||||
for b in bytes.iter() {
|
||||
hex.push(hex_nibble(b >> 4));
|
||||
hex.push(hex_nibble(b & 0xf));
|
||||
}
|
||||
hex
|
||||
})
|
||||
}
|
||||
|
||||
fn unhex_nibble(c: u8) -> Option<u8> {
|
||||
match c {
|
||||
b'0'..=b'9' => Some(c - b'0'),
|
||||
b'a'..=b'f' => Some(c - b'a' + 10),
|
||||
b'A'..=b'F' => Some(c - b'A' + 10),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn binascii_unhexlify(data: SerializedData, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
|
||||
data.with_ref(|hex_bytes| {
|
||||
if hex_bytes.len() % 2 != 0 {
|
||||
return Err(vm.new_value_error("Odd-length string".to_owned()));
|
||||
}
|
||||
|
||||
let mut unhex = Vec::<u8>::with_capacity(hex_bytes.len() / 2);
|
||||
for (n1, n2) in hex_bytes.iter().tuples() {
|
||||
if let (Some(n1), Some(n2)) = (unhex_nibble(*n1), unhex_nibble(*n2)) {
|
||||
unhex.push(n1 << 4 | n2);
|
||||
} else {
|
||||
return Err(vm.new_value_error("Non-hexadecimal digit found".to_owned()));
|
||||
#[pyfunction(name = "a2b_hex")]
|
||||
#[pyfunction]
|
||||
fn unhexlify(data: SerializedData, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
|
||||
data.with_ref(|hex_bytes| {
|
||||
if hex_bytes.len() % 2 != 0 {
|
||||
return Err(vm.new_value_error("Odd-length string".to_owned()));
|
||||
}
|
||||
|
||||
let mut unhex = Vec::<u8>::with_capacity(hex_bytes.len() / 2);
|
||||
for (n1, n2) in hex_bytes.iter().tuples() {
|
||||
if let (Some(n1), Some(n2)) = (unhex_nibble(*n1), unhex_nibble(*n2)) {
|
||||
unhex.push(n1 << 4 | n2);
|
||||
} else {
|
||||
return Err(vm.new_value_error("Non-hexadecimal digit found".to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(unhex)
|
||||
})
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn crc32(data: SerializedData, value: OptionalArg<u32>, vm: &VirtualMachine) -> PyResult {
|
||||
let crc = value.unwrap_or(0);
|
||||
|
||||
let mut digest = crc32::Digest::new_with_initial(crc32::IEEE, crc);
|
||||
data.with_ref(|bytes| digest.write(&bytes));
|
||||
|
||||
Ok(vm.ctx.new_int(digest.sum32()))
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct NewlineArg {
|
||||
#[pyarg(keyword_only, default = "true")]
|
||||
newline: bool,
|
||||
}
|
||||
|
||||
/// trim a newline from the end of the bytestring, if it exists
|
||||
fn trim_newline(b: &[u8]) -> &[u8] {
|
||||
if b.ends_with(b"\n") {
|
||||
&b[..b.len() - 1]
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
|
||||
Ok(unhex)
|
||||
})
|
||||
}
|
||||
#[pyfunction]
|
||||
fn a2b_base64(s: SerializedData, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
|
||||
s.with_ref(|b| base64::decode(trim_newline(b)))
|
||||
.map_err(|err| vm.new_value_error(format!("error decoding base64: {}", err)))
|
||||
}
|
||||
|
||||
fn binascii_crc32(data: SerializedData, value: OptionalArg<u32>, vm: &VirtualMachine) -> PyResult {
|
||||
let crc = value.unwrap_or(0);
|
||||
|
||||
let mut digest = crc32::Digest::new_with_initial(crc32::IEEE, crc);
|
||||
data.with_ref(|bytes| digest.write(&bytes));
|
||||
|
||||
Ok(vm.ctx.new_int(digest.sum32()))
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct NewlineArg {
|
||||
#[pyarg(keyword_only, default = "true")]
|
||||
newline: bool,
|
||||
}
|
||||
|
||||
/// trim a newline from the end of the bytestring, if it exists
|
||||
fn trim_newline(b: &[u8]) -> &[u8] {
|
||||
if b.ends_with(b"\n") {
|
||||
&b[..b.len() - 1]
|
||||
} else {
|
||||
b
|
||||
#[pyfunction]
|
||||
fn b2a_base64(data: PyBytesLike, NewlineArg { newline }: NewlineArg) -> Vec<u8> {
|
||||
let mut encoded = data.with_ref(base64::encode).into_bytes();
|
||||
if newline {
|
||||
encoded.push(b'\n');
|
||||
}
|
||||
encoded
|
||||
}
|
||||
}
|
||||
|
||||
fn binascii_a2b_base64(s: SerializedData, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
|
||||
s.with_ref(|b| base64::decode(trim_newline(b)))
|
||||
.map_err(|err| vm.new_value_error(format!("error decoding base64: {}", err)))
|
||||
}
|
||||
|
||||
fn binascii_b2a_base64(data: PyBytesLike, NewlineArg { newline }: NewlineArg) -> Vec<u8> {
|
||||
let mut encoded = data.with_ref(base64::encode).into_bytes();
|
||||
if newline {
|
||||
encoded.push(b'\n');
|
||||
}
|
||||
encoded
|
||||
}
|
||||
|
||||
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
let ctx = &vm.ctx;
|
||||
|
||||
py_module!(vm, "binascii", {
|
||||
"hexlify" => ctx.new_function(binascii_hexlify),
|
||||
"b2a_hex" => ctx.new_function(binascii_hexlify),
|
||||
"unhexlify" => ctx.new_function(binascii_unhexlify),
|
||||
"a2b_hex" => ctx.new_function(binascii_unhexlify),
|
||||
"crc32" => ctx.new_function(binascii_crc32),
|
||||
"a2b_base64" => ctx.new_function(binascii_a2b_base64),
|
||||
"b2a_base64" => ctx.new_function(binascii_b2a_base64),
|
||||
})
|
||||
}
|
||||
+361
-358
@@ -1,395 +1,398 @@
|
||||
use crate::function::OptionalArg;
|
||||
use crate::obj::{objiter, objtype::PyClassRef};
|
||||
use crate::pyobject::{
|
||||
IdProtocol, PyArithmaticValue::*, PyClassImpl, PyComparisonValue, PyIterable, PyObjectRef,
|
||||
PyRef, PyResult, PyValue,
|
||||
};
|
||||
use crate::sequence::{self, SimpleSeq};
|
||||
use crate::vm::ReprGuard;
|
||||
use crate::VirtualMachine;
|
||||
use itertools::Itertools;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
pub(crate) use _collections::make_module;
|
||||
|
||||
#[pyclass(name = "deque")]
|
||||
#[derive(Debug, Clone)]
|
||||
struct PyDeque {
|
||||
deque: RefCell<VecDeque<PyObjectRef>>,
|
||||
maxlen: Cell<Option<usize>>,
|
||||
}
|
||||
type PyDequeRef = PyRef<PyDeque>;
|
||||
#[pymodule]
|
||||
mod _collections {
|
||||
use crate::function::OptionalArg;
|
||||
use crate::obj::{objiter, objtype::PyClassRef};
|
||||
use crate::pyobject::{
|
||||
IdProtocol, PyArithmaticValue::*, PyClassImpl, PyComparisonValue, PyIterable, PyObjectRef,
|
||||
PyRef, PyResult, PyValue,
|
||||
};
|
||||
use crate::sequence;
|
||||
use crate::vm::ReprGuard;
|
||||
use crate::VirtualMachine;
|
||||
use itertools::Itertools;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
impl PyValue for PyDeque {
|
||||
fn class(vm: &VirtualMachine) -> PyClassRef {
|
||||
vm.class("_collections", "deque")
|
||||
#[pyclass(name = "deque")]
|
||||
#[derive(Debug, Clone)]
|
||||
struct PyDeque {
|
||||
deque: RefCell<VecDeque<PyObjectRef>>,
|
||||
maxlen: Cell<Option<usize>>,
|
||||
}
|
||||
}
|
||||
type PyDequeRef = PyRef<PyDeque>;
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct PyDequeOptions {
|
||||
#[pyarg(positional_or_keyword, default = "None")]
|
||||
maxlen: Option<usize>,
|
||||
}
|
||||
|
||||
impl PyDeque {
|
||||
fn borrow_deque<'a>(&'a self) -> impl std::ops::Deref<Target = VecDeque<PyObjectRef>> + 'a {
|
||||
self.deque.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(flags(BASETYPE))]
|
||||
impl PyDeque {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
cls: PyClassRef,
|
||||
iter: OptionalArg<PyIterable>,
|
||||
PyDequeOptions { maxlen }: PyDequeOptions,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyRef<Self>> {
|
||||
let py_deque = PyDeque {
|
||||
deque: RefCell::default(),
|
||||
maxlen: maxlen.into(),
|
||||
};
|
||||
if let OptionalArg::Present(iter) = iter {
|
||||
py_deque.extend(iter, vm)?;
|
||||
impl PyValue for PyDeque {
|
||||
fn class(vm: &VirtualMachine) -> PyClassRef {
|
||||
vm.class("_collections", "deque")
|
||||
}
|
||||
py_deque.into_ref_with_type(vm, cls)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn append(&self, obj: PyObjectRef) {
|
||||
let mut deque = self.deque.borrow_mut();
|
||||
if self.maxlen.get() == Some(deque.len()) {
|
||||
deque.pop_front();
|
||||
#[derive(FromArgs)]
|
||||
struct PyDequeOptions {
|
||||
#[pyarg(positional_or_keyword, default = "None")]
|
||||
maxlen: Option<usize>,
|
||||
}
|
||||
|
||||
impl PyDeque {
|
||||
fn borrow_deque<'a>(&'a self) -> impl std::ops::Deref<Target = VecDeque<PyObjectRef>> + 'a {
|
||||
self.deque.borrow()
|
||||
}
|
||||
deque.push_back(obj);
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn appendleft(&self, obj: PyObjectRef) {
|
||||
let mut deque = self.deque.borrow_mut();
|
||||
if self.maxlen.get() == Some(deque.len()) {
|
||||
deque.pop_back();
|
||||
}
|
||||
deque.push_front(obj);
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn clear(&self) {
|
||||
self.deque.borrow_mut().clear()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn copy(&self) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn count(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
|
||||
let mut count = 0;
|
||||
for elem in self.deque.borrow().iter() {
|
||||
if vm.identical_or_equal(elem, &obj)? {
|
||||
count += 1;
|
||||
#[pyimpl(flags(BASETYPE))]
|
||||
impl PyDeque {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
cls: PyClassRef,
|
||||
iter: OptionalArg<PyIterable>,
|
||||
PyDequeOptions { maxlen }: PyDequeOptions,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyRef<Self>> {
|
||||
let py_deque = PyDeque {
|
||||
deque: RefCell::default(),
|
||||
maxlen: maxlen.into(),
|
||||
};
|
||||
if let OptionalArg::Present(iter) = iter {
|
||||
py_deque.extend(iter, vm)?;
|
||||
}
|
||||
py_deque.into_ref_with_type(vm, cls)
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn extend(&self, iter: PyIterable, vm: &VirtualMachine) -> PyResult<()> {
|
||||
// TODO: use length_hint here and for extendleft
|
||||
for elem in iter.iter(vm)? {
|
||||
self.append(elem?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn extendleft(&self, iter: PyIterable, vm: &VirtualMachine) -> PyResult<()> {
|
||||
for elem in iter.iter(vm)? {
|
||||
self.appendleft(elem?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn index(
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
start: OptionalArg<usize>,
|
||||
stop: OptionalArg<usize>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<usize> {
|
||||
let deque = self.deque.borrow();
|
||||
let start = start.unwrap_or(0);
|
||||
let stop = stop.unwrap_or_else(|| deque.len());
|
||||
for (i, elem) in deque.iter().skip(start).take(stop - start).enumerate() {
|
||||
if vm.identical_or_equal(elem, &obj)? {
|
||||
return Ok(i);
|
||||
#[pymethod]
|
||||
fn append(&self, obj: PyObjectRef) {
|
||||
let mut deque = self.deque.borrow_mut();
|
||||
if self.maxlen.get() == Some(deque.len()) {
|
||||
deque.pop_front();
|
||||
}
|
||||
}
|
||||
Err(vm.new_value_error(
|
||||
vm.to_repr(&obj)
|
||||
.map(|repr| format!("{} is not in deque", repr))
|
||||
.unwrap_or_else(|_| String::new()),
|
||||
))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn insert(&self, idx: i32, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let mut deque = self.deque.borrow_mut();
|
||||
|
||||
if self.maxlen.get() == Some(deque.len()) {
|
||||
return Err(vm.new_index_error("deque already at its maximum size".to_owned()));
|
||||
deque.push_back(obj);
|
||||
}
|
||||
|
||||
let idx = if idx < 0 {
|
||||
if -idx as usize > deque.len() {
|
||||
0
|
||||
#[pymethod]
|
||||
fn appendleft(&self, obj: PyObjectRef) {
|
||||
let mut deque = self.deque.borrow_mut();
|
||||
if self.maxlen.get() == Some(deque.len()) {
|
||||
deque.pop_back();
|
||||
}
|
||||
deque.push_front(obj);
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn clear(&self) {
|
||||
self.deque.borrow_mut().clear()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn copy(&self) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn count(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
|
||||
let mut count = 0;
|
||||
for elem in self.deque.borrow().iter() {
|
||||
if vm.identical_or_equal(elem, &obj)? {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn extend(&self, iter: PyIterable, vm: &VirtualMachine) -> PyResult<()> {
|
||||
// TODO: use length_hint here and for extendleft
|
||||
for elem in iter.iter(vm)? {
|
||||
self.append(elem?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn extendleft(&self, iter: PyIterable, vm: &VirtualMachine) -> PyResult<()> {
|
||||
for elem in iter.iter(vm)? {
|
||||
self.appendleft(elem?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn index(
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
start: OptionalArg<usize>,
|
||||
stop: OptionalArg<usize>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<usize> {
|
||||
let deque = self.deque.borrow();
|
||||
let start = start.unwrap_or(0);
|
||||
let stop = stop.unwrap_or_else(|| deque.len());
|
||||
for (i, elem) in deque.iter().skip(start).take(stop - start).enumerate() {
|
||||
if vm.identical_or_equal(elem, &obj)? {
|
||||
return Ok(i);
|
||||
}
|
||||
}
|
||||
Err(vm.new_value_error(
|
||||
vm.to_repr(&obj)
|
||||
.map(|repr| format!("{} is not in deque", repr))
|
||||
.unwrap_or_else(|_| String::new()),
|
||||
))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn insert(&self, idx: i32, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let mut deque = self.deque.borrow_mut();
|
||||
|
||||
if self.maxlen.get() == Some(deque.len()) {
|
||||
return Err(vm.new_index_error("deque already at its maximum size".to_owned()));
|
||||
}
|
||||
|
||||
let idx = if idx < 0 {
|
||||
if -idx as usize > deque.len() {
|
||||
0
|
||||
} else {
|
||||
deque.len() - ((-idx) as usize)
|
||||
}
|
||||
} else if idx as usize >= deque.len() {
|
||||
deque.len() - 1
|
||||
} else {
|
||||
deque.len() - ((-idx) as usize)
|
||||
idx as usize
|
||||
};
|
||||
|
||||
deque.insert(idx, obj);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn pop(&self, vm: &VirtualMachine) -> PyResult {
|
||||
self.deque
|
||||
.borrow_mut()
|
||||
.pop_back()
|
||||
.ok_or_else(|| vm.new_index_error("pop from an empty deque".to_owned()))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn popleft(&self, vm: &VirtualMachine) -> PyResult {
|
||||
self.deque
|
||||
.borrow_mut()
|
||||
.pop_front()
|
||||
.ok_or_else(|| vm.new_index_error("pop from an empty deque".to_owned()))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn remove(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
let mut deque = self.deque.borrow_mut();
|
||||
let mut idx = None;
|
||||
for (i, elem) in deque.iter().enumerate() {
|
||||
if vm.identical_or_equal(elem, &obj)? {
|
||||
idx = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if idx as usize >= deque.len() {
|
||||
deque.len() - 1
|
||||
} else {
|
||||
idx as usize
|
||||
};
|
||||
idx.map(|idx| deque.remove(idx).unwrap())
|
||||
.ok_or_else(|| vm.new_value_error("deque.remove(x): x not in deque".to_owned()))
|
||||
}
|
||||
|
||||
deque.insert(idx, obj);
|
||||
#[pymethod]
|
||||
fn reverse(&self) {
|
||||
self.deque
|
||||
.replace_with(|deque| deque.iter().cloned().rev().collect());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn pop(&self, vm: &VirtualMachine) -> PyResult {
|
||||
self.deque
|
||||
.borrow_mut()
|
||||
.pop_back()
|
||||
.ok_or_else(|| vm.new_index_error("pop from an empty deque".to_owned()))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn popleft(&self, vm: &VirtualMachine) -> PyResult {
|
||||
self.deque
|
||||
.borrow_mut()
|
||||
.pop_front()
|
||||
.ok_or_else(|| vm.new_index_error("pop from an empty deque".to_owned()))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn remove(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
let mut deque = self.deque.borrow_mut();
|
||||
let mut idx = None;
|
||||
for (i, elem) in deque.iter().enumerate() {
|
||||
if vm.identical_or_equal(elem, &obj)? {
|
||||
idx = Some(i);
|
||||
break;
|
||||
#[pymethod]
|
||||
fn rotate(&self, mid: OptionalArg<isize>) {
|
||||
let mut deque = self.deque.borrow_mut();
|
||||
let mid = mid.unwrap_or(1);
|
||||
if mid < 0 {
|
||||
deque.rotate_left(-mid as usize);
|
||||
} else {
|
||||
deque.rotate_right(mid as usize);
|
||||
}
|
||||
}
|
||||
idx.map(|idx| deque.remove(idx).unwrap())
|
||||
.ok_or_else(|| vm.new_value_error("deque.remove(x): x not in deque".to_owned()))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn reverse(&self) {
|
||||
self.deque
|
||||
.replace_with(|deque| deque.iter().cloned().rev().collect());
|
||||
}
|
||||
#[pyproperty]
|
||||
fn maxlen(&self) -> Option<usize> {
|
||||
self.maxlen.get()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn rotate(&self, mid: OptionalArg<isize>) {
|
||||
let mut deque = self.deque.borrow_mut();
|
||||
let mid = mid.unwrap_or(1);
|
||||
if mid < 0 {
|
||||
deque.rotate_left(-mid as usize);
|
||||
} else {
|
||||
deque.rotate_right(mid as usize);
|
||||
#[pyproperty(setter)]
|
||||
fn set_maxlen(&self, maxlen: Option<usize>) {
|
||||
self.maxlen.set(maxlen);
|
||||
}
|
||||
|
||||
#[pymethod(name = "__repr__")]
|
||||
fn repr(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let repr = if let Some(_guard) = ReprGuard::enter(zelf.as_object()) {
|
||||
let elements = zelf
|
||||
.deque
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|obj| vm.to_repr(obj))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let maxlen = zelf
|
||||
.maxlen
|
||||
.get()
|
||||
.map(|maxlen| format!(", maxlen={}", maxlen))
|
||||
.unwrap_or_default();
|
||||
format!("deque([{}]{})", elements.into_iter().format(", "), maxlen)
|
||||
} else {
|
||||
"[...]".to_owned()
|
||||
};
|
||||
Ok(repr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cmp<F>(
|
||||
&self,
|
||||
other: PyObjectRef,
|
||||
op: F,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue>
|
||||
where
|
||||
F: Fn(&VecDeque<PyObjectRef>, &VecDeque<PyObjectRef>) -> PyResult<bool>,
|
||||
{
|
||||
let r = if let Some(other) = other.payload_if_subclass::<PyDeque>(vm) {
|
||||
Implemented(op(&*self.borrow_deque(), &*other.borrow_deque())?)
|
||||
} else {
|
||||
NotImplemented
|
||||
};
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
#[pymethod(name = "__eq__")]
|
||||
fn eq(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
if zelf.as_object().is(&other) {
|
||||
Ok(Implemented(true))
|
||||
} else {
|
||||
zelf.cmp(other, |a, b| sequence::eq(vm, a, b), vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__ne__")]
|
||||
fn ne(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
Ok(PyDeque::eq(zelf, other, vm)?.map(|v| !v))
|
||||
}
|
||||
|
||||
#[pymethod(name = "__lt__")]
|
||||
fn lt(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
if zelf.as_object().is(&other) {
|
||||
Ok(Implemented(false))
|
||||
} else {
|
||||
zelf.cmp(other, |a, b| sequence::lt(vm, a, b), vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__gt__")]
|
||||
fn gt(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
if zelf.as_object().is(&other) {
|
||||
Ok(Implemented(false))
|
||||
} else {
|
||||
zelf.cmp(other, |a, b| sequence::gt(vm, a, b), vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__le__")]
|
||||
fn le(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
if zelf.as_object().is(&other) {
|
||||
Ok(Implemented(true))
|
||||
} else {
|
||||
zelf.cmp(other, |a, b| sequence::le(vm, a, b), vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__ge__")]
|
||||
fn ge(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
if zelf.as_object().is(&other) {
|
||||
Ok(Implemented(true))
|
||||
} else {
|
||||
zelf.cmp(other, |a, b| sequence::ge(vm, a, b), vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__mul__")]
|
||||
fn mul(&self, n: isize) -> Self {
|
||||
let deque: &VecDeque<_> = &self.deque.borrow();
|
||||
let mul = sequence::seq_mul(deque, n);
|
||||
let skipped = if let Some(maxlen) = self.maxlen.get() {
|
||||
mul.len() - maxlen
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let deque = mul.skip(skipped).cloned().collect();
|
||||
PyDeque {
|
||||
deque: RefCell::new(deque),
|
||||
maxlen: self.maxlen.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__len__")]
|
||||
fn len(&self) -> usize {
|
||||
self.deque.borrow().len()
|
||||
}
|
||||
|
||||
#[pymethod(name = "__iter__")]
|
||||
fn iter(zelf: PyRef<Self>) -> PyDequeIterator {
|
||||
PyDequeIterator {
|
||||
position: Cell::new(0),
|
||||
deque: zelf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyproperty]
|
||||
fn maxlen(&self) -> Option<usize> {
|
||||
self.maxlen.get()
|
||||
#[pyclass(name = "_deque_iterator")]
|
||||
#[derive(Debug)]
|
||||
struct PyDequeIterator {
|
||||
position: Cell<usize>,
|
||||
deque: PyDequeRef,
|
||||
}
|
||||
|
||||
#[pyproperty(setter)]
|
||||
fn set_maxlen(&self, maxlen: Option<usize>) {
|
||||
self.maxlen.set(maxlen);
|
||||
}
|
||||
|
||||
#[pymethod(name = "__repr__")]
|
||||
fn repr(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let repr = if let Some(_guard) = ReprGuard::enter(zelf.as_object()) {
|
||||
let elements = zelf
|
||||
.deque
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|obj| vm.to_repr(obj))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let maxlen = zelf
|
||||
.maxlen
|
||||
.get()
|
||||
.map(|maxlen| format!(", maxlen={}", maxlen))
|
||||
.unwrap_or_default();
|
||||
format!("deque([{}]{})", elements.into_iter().format(", "), maxlen)
|
||||
} else {
|
||||
"[...]".to_owned()
|
||||
};
|
||||
Ok(repr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cmp<F>(&self, other: PyObjectRef, op: F, vm: &VirtualMachine) -> PyResult<PyComparisonValue>
|
||||
where
|
||||
F: Fn(&VecDeque<PyObjectRef>, &VecDeque<PyObjectRef>) -> PyResult<bool>,
|
||||
{
|
||||
let r = if let Some(other) = other.payload_if_subclass::<PyDeque>(vm) {
|
||||
Implemented(op(&*self.borrow_deque(), &*other.borrow_deque())?)
|
||||
} else {
|
||||
NotImplemented
|
||||
};
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
#[pymethod(name = "__eq__")]
|
||||
fn eq(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
if zelf.as_object().is(&other) {
|
||||
Ok(Implemented(true))
|
||||
} else {
|
||||
zelf.cmp(other, |a, b| sequence::eq(vm, a, b), vm)
|
||||
impl PyValue for PyDequeIterator {
|
||||
fn class(vm: &VirtualMachine) -> PyClassRef {
|
||||
vm.class("_collections", "_deque_iterator")
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__ne__")]
|
||||
fn ne(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
Ok(PyDeque::eq(zelf, other, vm)?.map(|v| !v))
|
||||
}
|
||||
|
||||
#[pymethod(name = "__lt__")]
|
||||
fn lt(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
if zelf.as_object().is(&other) {
|
||||
Ok(Implemented(false))
|
||||
} else {
|
||||
zelf.cmp(other, |a, b| sequence::lt(vm, a, b), vm)
|
||||
#[pyimpl]
|
||||
impl PyDequeIterator {
|
||||
#[pymethod(name = "__next__")]
|
||||
fn next(&self, vm: &VirtualMachine) -> PyResult {
|
||||
if self.position.get() < self.deque.deque.borrow().len() {
|
||||
let ret = self.deque.deque.borrow()[self.position.get()].clone();
|
||||
self.position.set(self.position.get() + 1);
|
||||
Ok(ret)
|
||||
} else {
|
||||
Err(objiter::new_stop_iteration(vm))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__gt__")]
|
||||
fn gt(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
if zelf.as_object().is(&other) {
|
||||
Ok(Implemented(false))
|
||||
} else {
|
||||
zelf.cmp(other, |a, b| sequence::gt(vm, a, b), vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__le__")]
|
||||
fn le(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
if zelf.as_object().is(&other) {
|
||||
Ok(Implemented(true))
|
||||
} else {
|
||||
zelf.cmp(other, |a, b| sequence::le(vm, a, b), vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__ge__")]
|
||||
fn ge(
|
||||
zelf: PyRef<Self>,
|
||||
other: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
if zelf.as_object().is(&other) {
|
||||
Ok(Implemented(true))
|
||||
} else {
|
||||
zelf.cmp(other, |a, b| sequence::ge(vm, a, b), vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__mul__")]
|
||||
fn mul(&self, n: isize) -> Self {
|
||||
let deque: &VecDeque<_> = &self.deque.borrow();
|
||||
let mul = sequence::seq_mul(deque, n);
|
||||
let skipped = if let Some(maxlen) = self.maxlen.get() {
|
||||
mul.len() - maxlen
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let deque = mul.skip(skipped).cloned().collect();
|
||||
PyDeque {
|
||||
deque: RefCell::new(deque),
|
||||
maxlen: self.maxlen.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__len__")]
|
||||
fn len(&self) -> usize {
|
||||
self.deque.borrow().len()
|
||||
}
|
||||
|
||||
#[pymethod(name = "__iter__")]
|
||||
fn iter(zelf: PyRef<Self>) -> PyDequeIterator {
|
||||
PyDequeIterator {
|
||||
position: Cell::new(0),
|
||||
deque: zelf,
|
||||
#[pymethod(name = "__iter__")]
|
||||
fn iter(zelf: PyRef<Self>) -> PyRef<Self> {
|
||||
zelf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "_deque_iterator")]
|
||||
#[derive(Debug)]
|
||||
struct PyDequeIterator {
|
||||
position: Cell<usize>,
|
||||
deque: PyDequeRef,
|
||||
}
|
||||
|
||||
impl PyValue for PyDequeIterator {
|
||||
fn class(vm: &VirtualMachine) -> PyClassRef {
|
||||
vm.class("_collections", "_deque_iterator")
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
impl PyDequeIterator {
|
||||
#[pymethod(name = "__next__")]
|
||||
fn next(&self, vm: &VirtualMachine) -> PyResult {
|
||||
if self.position.get() < self.deque.deque.borrow().len() {
|
||||
let ret = self.deque.deque.borrow()[self.position.get()].clone();
|
||||
self.position.set(self.position.get() + 1);
|
||||
Ok(ret)
|
||||
} else {
|
||||
Err(objiter::new_stop_iteration(vm))
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__iter__")]
|
||||
fn iter(zelf: PyRef<Self>) -> PyRef<Self> {
|
||||
zelf
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
py_module!(vm, "_collections", {
|
||||
"deque" => PyDeque::make_class(&vm.ctx),
|
||||
"_deque_iterator" => PyDequeIterator::make_class(&vm.ctx),
|
||||
})
|
||||
}
|
||||
+1276
-1344
File diff suppressed because it is too large.
Load diff
+29
-31
@@ -1,39 +1,37 @@
|
||||
use crate::pyobject::PyObjectRef;
|
||||
use crate::version;
|
||||
use crate::vm::VirtualMachine;
|
||||
pub(crate) use decl::make_module;
|
||||
|
||||
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
let ctx = &vm.ctx;
|
||||
py_module!(vm, "platform", {
|
||||
"python_branch" => ctx.new_function(platform_python_branch),
|
||||
"python_build" => ctx.new_function(platform_python_build),
|
||||
"python_compiler" => ctx.new_function(platform_python_compiler),
|
||||
"python_implementation" => ctx.new_function(platform_python_implementation),
|
||||
"python_revision" => ctx.new_function(platform_python_revision),
|
||||
"python_version" => ctx.new_function(platform_python_version),
|
||||
})
|
||||
}
|
||||
#[pymodule(name = "platform")]
|
||||
mod decl {
|
||||
use crate::version;
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
fn platform_python_implementation(_vm: &VirtualMachine) -> String {
|
||||
"RustPython".to_owned()
|
||||
}
|
||||
#[pyfunction]
|
||||
fn python_implementation(_vm: &VirtualMachine) -> String {
|
||||
"RustPython".to_owned()
|
||||
}
|
||||
|
||||
fn platform_python_version(_vm: &VirtualMachine) -> String {
|
||||
version::get_version_number()
|
||||
}
|
||||
#[pyfunction]
|
||||
fn python_version(_vm: &VirtualMachine) -> String {
|
||||
version::get_version_number()
|
||||
}
|
||||
|
||||
fn platform_python_compiler(_vm: &VirtualMachine) -> String {
|
||||
version::get_compiler()
|
||||
}
|
||||
#[pyfunction]
|
||||
fn python_compiler(_vm: &VirtualMachine) -> String {
|
||||
version::get_compiler()
|
||||
}
|
||||
|
||||
fn platform_python_build(_vm: &VirtualMachine) -> (String, String) {
|
||||
(version::get_git_identifier(), version::get_git_datetime())
|
||||
}
|
||||
#[pyfunction]
|
||||
fn python_build(_vm: &VirtualMachine) -> (String, String) {
|
||||
(version::get_git_identifier(), version::get_git_datetime())
|
||||
}
|
||||
|
||||
fn platform_python_branch(_vm: &VirtualMachine) -> String {
|
||||
version::get_git_branch()
|
||||
}
|
||||
#[pyfunction]
|
||||
fn python_branch(_vm: &VirtualMachine) -> String {
|
||||
version::get_git_branch()
|
||||
}
|
||||
|
||||
fn platform_python_revision(_vm: &VirtualMachine) -> String {
|
||||
version::get_git_revision()
|
||||
#[pyfunction]
|
||||
fn python_revision(_vm: &VirtualMachine) -> String {
|
||||
version::get_git_revision()
|
||||
}
|
||||
}
|
||||
+646
-637
File diff suppressed because it is too large.
Load diff
+103
-107
@@ -1,127 +1,123 @@
|
||||
//! Random module.
|
||||
|
||||
use std::cell::RefCell;
|
||||
pub(crate) use _random::make_module;
|
||||
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use num_traits::Signed;
|
||||
use rand::RngCore;
|
||||
#[pymodule]
|
||||
mod _random {
|
||||
use crate::function::OptionalOption;
|
||||
use crate::obj::objint::PyIntRef;
|
||||
use crate::obj::objtype::PyClassRef;
|
||||
use crate::pyobject::{PyClassImpl, PyRef, PyResult, PyValue};
|
||||
use crate::VirtualMachine;
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use num_traits::Signed;
|
||||
use rand::RngCore;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::function::OptionalOption;
|
||||
use crate::obj::objint::PyIntRef;
|
||||
use crate::obj::objtype::PyClassRef;
|
||||
use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue};
|
||||
use crate::VirtualMachine;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PyRng {
|
||||
Std(rand::rngs::ThreadRng),
|
||||
MT(Box<mt19937::MT19937>),
|
||||
}
|
||||
|
||||
impl Default for PyRng {
|
||||
fn default() -> Self {
|
||||
PyRng::Std(rand::thread_rng())
|
||||
#[derive(Debug)]
|
||||
enum PyRng {
|
||||
Std(rand::rngs::ThreadRng),
|
||||
MT(Box<mt19937::MT19937>),
|
||||
}
|
||||
}
|
||||
|
||||
impl RngCore for PyRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
match self {
|
||||
Self::Std(s) => s.next_u32(),
|
||||
Self::MT(m) => m.next_u32(),
|
||||
impl Default for PyRng {
|
||||
fn default() -> Self {
|
||||
PyRng::Std(rand::thread_rng())
|
||||
}
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
match self {
|
||||
Self::Std(s) => s.next_u64(),
|
||||
Self::MT(m) => m.next_u64(),
|
||||
}
|
||||
}
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
match self {
|
||||
Self::Std(s) => s.fill_bytes(dest),
|
||||
Self::MT(m) => m.fill_bytes(dest),
|
||||
}
|
||||
}
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
|
||||
match self {
|
||||
Self::Std(s) => s.try_fill_bytes(dest),
|
||||
Self::MT(m) => m.try_fill_bytes(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "Random")]
|
||||
#[derive(Debug)]
|
||||
struct PyRandom {
|
||||
rng: RefCell<PyRng>,
|
||||
}
|
||||
|
||||
impl PyValue for PyRandom {
|
||||
fn class(vm: &VirtualMachine) -> PyClassRef {
|
||||
vm.class("_random", "Random")
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(flags(BASETYPE))]
|
||||
impl PyRandom {
|
||||
#[pyslot(new)]
|
||||
fn new(cls: PyClassRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
|
||||
PyRandom {
|
||||
rng: RefCell::new(PyRng::default()),
|
||||
}
|
||||
.into_ref_with_type(vm, cls)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn random(&self) -> f64 {
|
||||
mt19937::gen_res53(&mut *self.rng.borrow_mut())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn seed(&self, n: OptionalOption<PyIntRef>) {
|
||||
let new_rng = match n.flat_option() {
|
||||
None => PyRng::default(),
|
||||
Some(n) => {
|
||||
let (_, mut key) = n.as_bigint().abs().to_u32_digits();
|
||||
if cfg!(target_endian = "big") {
|
||||
key.reverse();
|
||||
}
|
||||
PyRng::MT(Box::new(mt19937::MT19937::new_with_slice_seed(&key)))
|
||||
impl RngCore for PyRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
match self {
|
||||
Self::Std(s) => s.next_u32(),
|
||||
Self::MT(m) => m.next_u32(),
|
||||
}
|
||||
};
|
||||
|
||||
*self.rng.borrow_mut() = new_rng;
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
match self {
|
||||
Self::Std(s) => s.next_u64(),
|
||||
Self::MT(m) => m.next_u64(),
|
||||
}
|
||||
}
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
match self {
|
||||
Self::Std(s) => s.fill_bytes(dest),
|
||||
Self::MT(m) => m.fill_bytes(dest),
|
||||
}
|
||||
}
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
|
||||
match self {
|
||||
Self::Std(s) => s.try_fill_bytes(dest),
|
||||
Self::MT(m) => m.try_fill_bytes(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn getrandbits(&self, mut k: usize) -> BigInt {
|
||||
let mut rng = self.rng.borrow_mut();
|
||||
#[pyclass(name = "Random")]
|
||||
#[derive(Debug)]
|
||||
struct PyRandom {
|
||||
rng: RefCell<PyRng>,
|
||||
}
|
||||
|
||||
let mut gen_u32 = |k| rng.next_u32() >> (32 - k) as u32;
|
||||
impl PyValue for PyRandom {
|
||||
fn class(vm: &VirtualMachine) -> PyClassRef {
|
||||
vm.class("_random", "Random")
|
||||
}
|
||||
}
|
||||
|
||||
if k <= 32 {
|
||||
return gen_u32(k).into();
|
||||
#[pyimpl(flags(BASETYPE))]
|
||||
impl PyRandom {
|
||||
#[pyslot(new)]
|
||||
fn new(cls: PyClassRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
|
||||
PyRandom {
|
||||
rng: RefCell::new(PyRng::default()),
|
||||
}
|
||||
.into_ref_with_type(vm, cls)
|
||||
}
|
||||
|
||||
let words = (k - 1) / 8 + 1;
|
||||
let mut wordarray = vec![0u32; words];
|
||||
|
||||
let it = wordarray.iter_mut();
|
||||
#[cfg(target_endian = "big")]
|
||||
let it = it.rev();
|
||||
for word in it {
|
||||
*word = gen_u32(k);
|
||||
k -= 32;
|
||||
#[pymethod]
|
||||
fn random(&self) -> f64 {
|
||||
mt19937::gen_res53(&mut *self.rng.borrow_mut())
|
||||
}
|
||||
|
||||
BigInt::from_slice(Sign::NoSign, &wordarray)
|
||||
#[pymethod]
|
||||
fn seed(&self, n: OptionalOption<PyIntRef>) {
|
||||
let new_rng = match n.flat_option() {
|
||||
None => PyRng::default(),
|
||||
Some(n) => {
|
||||
let (_, mut key) = n.as_bigint().abs().to_u32_digits();
|
||||
if cfg!(target_endian = "big") {
|
||||
key.reverse();
|
||||
}
|
||||
PyRng::MT(Box::new(mt19937::MT19937::new_with_slice_seed(&key)))
|
||||
}
|
||||
};
|
||||
|
||||
*self.rng.borrow_mut() = new_rng;
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn getrandbits(&self, mut k: usize) -> BigInt {
|
||||
let mut rng = self.rng.borrow_mut();
|
||||
|
||||
let mut gen_u32 = |k| rng.next_u32() >> (32 - k) as u32;
|
||||
|
||||
if k <= 32 {
|
||||
return gen_u32(k).into();
|
||||
}
|
||||
|
||||
let words = (k - 1) / 8 + 1;
|
||||
let mut wordarray = vec![0u32; words];
|
||||
|
||||
let it = wordarray.iter_mut();
|
||||
#[cfg(target_endian = "big")]
|
||||
let it = it.rev();
|
||||
for word in it {
|
||||
*word = gen_u32(k);
|
||||
k -= 32;
|
||||
}
|
||||
|
||||
BigInt::from_slice(Sign::NoSign, &wordarray)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
let ctx = &vm.ctx;
|
||||
py_module!(vm, "_random", {
|
||||
"Random" => PyRandom::make_class(ctx),
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user