Merge pull request #3613 from youknowone/pystructseq-fromobj

derive(TryIntoPyStructSequence)
This commit is contained in:
Jeong YunWon authored and GitHub committed 2022-04-19 16:20:35 +09:00
commit 16cedb1624
13 files changed
+164 -97

No files matched your search

+8
View File
@@ -102,6 +102,14 @@ pub fn pystruct_sequence(input: proc_macro::TokenStream) -> proc_macro::TokenStr
result_to_tokens(pystructseq::impl_pystruct_sequence(input))
}
#[proc_macro_derive(TryIntoPyStructSequence)]
pub fn pystruct_sequence_try_from_object(
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
result_to_tokens(pystructseq::impl_pystruct_sequence_try_from_object(input))
}
#[proc_macro]
pub fn py_compile(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
result_to_tokens(compile_bytecode::impl_py_compile(input.into()))
+28 -4
View File
@@ -1,8 +1,8 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{DeriveInput, Result};
use syn::{DeriveInput, Ident, Result};
pub(crate) fn impl_pystruct_sequence(input: DeriveInput) -> Result<TokenStream> {
fn field_names(input: &DeriveInput) -> Result<Vec<&Ident>> {
let fields = if let syn::Data::Struct(ref struc) = input.data {
&struc.fields
} else {
@@ -24,9 +24,14 @@ pub(crate) fn impl_pystruct_sequence(input: DeriveInput) -> Result<TokenStream>
),
};
Ok(field_names)
}
pub(crate) fn impl_pystruct_sequence(input: DeriveInput) -> Result<TokenStream> {
let field_names = field_names(&input)?;
let ty = &input.ident;
let ret = quote! {
impl ::rustpython_vm::PyStructSequence for #ty {
impl ::rustpython_vm::types::PyStructSequence for #ty {
const FIELD_NAMES: &'static [&'static str] = &[#(stringify!(#field_names)),*];
fn into_tuple(self, vm: &::rustpython_vm::VirtualMachine) -> ::rustpython_vm::builtins::PyTuple {
let items = vec![#(::rustpython_vm::convert::ToPyObject::to_pyobject(
@@ -38,7 +43,26 @@ pub(crate) fn impl_pystruct_sequence(input: DeriveInput) -> Result<TokenStream>
}
impl ::rustpython_vm::convert::ToPyObject for #ty {
fn to_pyobject(self, vm: &::rustpython_vm::VirtualMachine) -> ::rustpython_vm::PyObjectRef {
::rustpython_vm::PyStructSequence::into_struct_sequence(self, vm).into()
::rustpython_vm::types::PyStructSequence::into_struct_sequence(self, vm).into()
}
}
};
Ok(ret)
}
pub(crate) fn impl_pystruct_sequence_try_from_object(input: DeriveInput) -> Result<TokenStream> {
let field_names = field_names(&input)?;
let ty = &input.ident;
let ret = quote! {
impl ::rustpython_vm::TryFromObject for #ty {
fn try_from_object(vm: &::rustpython_vm::VirtualMachine, seq: ::rustpython_vm::PyObjectRef) -> ::rustpython_vm::PyResult<Self> {
const LEN: usize = #ty::FIELD_NAMES.len();
let seq = Self::try_elements_from::<LEN>(seq, vm)?;
// TODO: this is possible to be written without iterator
let mut iter = seq.into_iter();
Ok(Self {#(
#field_names: iter.next().unwrap().clone().try_into_value(vm)?
),*})
}
}
};
+2 -1
View File
@@ -5,7 +5,8 @@ mod resource {
use crate::vm::{
convert::{ToPyException, ToPyObject},
stdlib::os,
PyObject, PyObjectRef, PyResult, PyStructSequence, TryFromBorrowedObject, VirtualMachine,
types::PyStructSequence,
PyObject, PyObjectRef, PyResult, TryFromBorrowedObject, VirtualMachine,
};
use std::{io, mem};
+6
View File
@@ -77,6 +77,12 @@ impl PyStrKindData {
}
}
impl TryFromBorrowedObject for String {
fn try_from_borrowed_object(vm: &VirtualMachine, obj: &PyObject) -> PyResult<Self> {
obj.try_value_with(|pystr: &PyStr| Ok(pystr.as_str().to_owned()), vm)
}
}
/// str(object='') -> str
/// str(bytes_or_buffer[, encoding[, errors]]) -> str
///
+3 -3
View File
@@ -88,12 +88,12 @@ mod pyobject {
// pub use self::Executor;
pub use self::convert::{TryFromBorrowedObject, TryFromObject};
// pyobject items
pub use self::pyobject::{
AsObject, PyContext, PyMethod, PyRefExact, PyResult, PyStructSequence, PyValue,
};
pub use self::pyobject::{AsObject, PyContext, PyMethod, PyRefExact, PyResult, PyValue};
// pyobjectrc items
pub use self::pyobject::{PyObject, PyObjectRef, PyObjectView, PyObjectWeak, PyRef, PyWeakRef};
pub use self::types::PyStructSequence;
pub use self::vm::{InitParameter, Interpreter, PySettings, VirtualMachine};
pub use rustpython_bytecode as bytecode;
pub use rustpython_common as common;
#[cfg(feature = "rustpython-compiler")]
+13 -59
View File
@@ -622,66 +622,20 @@ pub trait PyObjectPayload: Any + fmt::Debug + PyThreadingConstraint + 'static {}
impl<T: PyValue + 'static> PyObjectPayload for T {}
#[pyimpl]
pub trait PyStructSequence: StaticType + PyClassImpl + Sized + 'static {
const FIELD_NAMES: &'static [&'static str];
pub trait PyObjectWrap
where
Self: AsObject,
{
fn into_object(self) -> PyObjectRef;
}
fn into_tuple(self, vm: &VirtualMachine) -> PyTuple;
fn into_struct_sequence(self, vm: &VirtualMachine) -> PyTupleRef {
self.into_tuple(vm)
.into_ref_with_type(vm, Self::static_type().clone())
.unwrap()
}
#[pymethod(magic)]
fn repr(zelf: PyRef<PyTuple>, vm: &VirtualMachine) -> PyResult<String> {
let format_field = |(value, name): (&PyObjectRef, _)| {
let s = value.repr(vm)?;
Ok(format!("{}={}", name, s))
};
let (body, suffix) =
if let Some(_guard) = rustpython_vm::vm::ReprGuard::enter(vm, zelf.as_object()) {
if Self::FIELD_NAMES.len() == 1 {
let value = zelf.as_slice().first().unwrap();
let formatted = format_field((value, Self::FIELD_NAMES[0]))?;
(formatted, ",")
} else {
let fields: PyResult<Vec<_>> = zelf
.as_slice()
.iter()
.zip(Self::FIELD_NAMES.iter().copied())
.map(format_field)
.collect();
(fields?.join(", "), "")
}
} else {
(String::new(), "...")
};
Ok(format!("{}({}{})", Self::TP_NAME, body, suffix))
}
#[pymethod(magic)]
fn reduce(zelf: PyRef<PyTuple>, vm: &VirtualMachine) -> PyTupleRef {
vm.new_tuple((
zelf.class().clone(),
(vm.ctx.new_tuple(zelf.as_slice().to_vec()),),
))
}
#[extend_class]
fn extend_pyclass(ctx: &PyContext, class: &PyTypeRef) {
for (i, &name) in Self::FIELD_NAMES.iter().enumerate() {
// cast i to a u8 so there's less to store in the getter closure.
// Hopefully there's not struct sequences with >=256 elements :P
let i = i as u8;
class.set_str_attr(
name,
ctx.new_readonly_getset(name, class.clone(), move |zelf: &PyTuple| {
zelf.fast_getitem(i.into())
}),
);
}
impl<T> From<T> for PyObjectRef
where
T: PyObjectWrap,
{
#[inline(always)]
fn from(py_ref: T) -> Self {
py_ref.into_object()
}
}
+2 -2
View File
@@ -413,10 +413,10 @@ pub(super) mod _os {
function::{ArgBytesLike, FuncArgs, OptionalArg},
protocol::PyIterReturn,
suppress_iph,
types::{IterNext, IterNextIterable},
types::{IterNext, IterNextIterable, PyStructSequence},
utils::Either,
vm::{ReprGuard, VirtualMachine},
AsObject, PyObjectRef, PyRef, PyResult, PyStructSequence, PyValue, TryFromObject,
AsObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
};
use crossbeam_utils::atomic::AtomicCell;
use itertools::Itertools;
+2 -1
View File
@@ -5,7 +5,8 @@ mod pwd {
use crate::{
builtins::{PyIntRef, PyStrRef},
convert::{ToPyException, ToPyObject},
AsObject, PyObjectRef, PyResult, PyStructSequence, VirtualMachine,
types::PyStructSequence,
AsObject, PyObjectRef, PyResult, VirtualMachine,
};
use nix::unistd::{self, User};
use std::ptr::NonNull;
+2 -1
View File
@@ -13,9 +13,10 @@ mod sys {
frame::FrameRef,
function::{FuncArgs, OptionalArg, PosArgs},
stdlib::builtins,
types::PyStructSequence,
version,
vm::{PySettings, VirtualMachine},
PyObjectRef, PyRef, PyRefExact, PyResult, PyStructSequence,
PyObjectRef, PyRef, PyRefExact, PyResult,
};
use num_traits::ToPrimitive;
use std::{env, mem, path};
+3 -25
View File
@@ -21,8 +21,9 @@ mod time {
use crate::{
builtins::{PyStrRef, PyTypeRef},
function::{FuncArgs, OptionalArg},
types::PyStructSequence,
utils::Either,
PyObjectRef, PyResult, PyStructSequence, TryFromObject, VirtualMachine,
PyObjectRef, PyResult, TryFromObject, VirtualMachine,
};
use chrono::{
naive::{NaiveDate, NaiveDateTime, NaiveTime},
@@ -315,7 +316,7 @@ mod time {
#[pyattr]
#[pyclass(name = "struct_time")]
#[derive(PyStructSequence)]
#[derive(PyStructSequence, TryIntoPyStructSequence)]
#[allow(dead_code)]
struct PyStructTime {
tm_year: PyObjectRef,
@@ -375,29 +376,6 @@ mod time {
}
}
impl TryFromObject for PyStructTime {
fn try_from_object(vm: &VirtualMachine, seq: PyObjectRef) -> PyResult<Self> {
let seq: Vec<_> = seq.try_to_value(vm)?;
if seq.len() != 9 {
return Err(
vm.new_type_error("time.struct_time() takes a sequence of length 9".to_owned())
);
}
let mut i = seq.into_iter();
Ok(PyStructTime {
tm_year: i.next().unwrap(),
tm_mon: i.next().unwrap(),
tm_mday: i.next().unwrap(),
tm_hour: i.next().unwrap(),
tm_min: i.next().unwrap(),
tm_sec: i.next().unwrap(),
tm_wday: i.next().unwrap(),
tm_yday: i.next().unwrap(),
tm_isdst: i.next().unwrap(),
})
}
}
#[allow(unused_imports)]
#[cfg(unix)]
use super::unix::*;
+2
View File
@@ -1,5 +1,7 @@
mod slot;
mod structseq;
mod zoo;
pub use slot::*;
pub use structseq::PyStructSequence;
pub(crate) use zoo::TypeZoo;
+92
View File
@@ -0,0 +1,92 @@
use crate::{
builtins::{PyTuple, PyTupleRef, PyTypeRef},
pyclass::{PyClassImpl, StaticType},
pyobject::PyContext,
AsObject, PyObjectRef, PyRef, PyResult, PyValue, VirtualMachine,
};
#[pyimpl]
pub trait PyStructSequence: StaticType + PyClassImpl + Sized + 'static {
const FIELD_NAMES: &'static [&'static str];
fn into_tuple(self, vm: &VirtualMachine) -> PyTuple;
fn into_struct_sequence(self, vm: &VirtualMachine) -> PyTupleRef {
self.into_tuple(vm)
.into_ref_with_type(vm, Self::static_type().clone())
.unwrap()
}
fn try_elements_from<const FIELD_LEN: usize>(
obj: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<[PyObjectRef; FIELD_LEN]> {
let typ = Self::static_type();
// if !obj.fast_isinstance(typ) {
// return Err(vm.new_type_error(format!(
// "{} is not a subclass of {}",
// obj.class().name(),
// typ.name(),
// )));
// }
let seq: Vec<PyObjectRef> = obj.try_into_value(vm)?;
let seq: [PyObjectRef; FIELD_LEN] = seq.try_into().map_err(|_| {
vm.new_type_error(format!(
"{} takes a sequence of length {}",
typ.name(),
FIELD_LEN
))
})?;
Ok(seq)
}
#[pymethod(magic)]
fn repr(zelf: PyRef<PyTuple>, vm: &VirtualMachine) -> PyResult<String> {
let format_field = |(value, name): (&PyObjectRef, _)| {
let s = value.repr(vm)?;
Ok(format!("{}={}", name, s))
};
let (body, suffix) =
if let Some(_guard) = rustpython_vm::vm::ReprGuard::enter(vm, zelf.as_object()) {
if Self::FIELD_NAMES.len() == 1 {
let value = zelf.as_slice().first().unwrap();
let formatted = format_field((value, Self::FIELD_NAMES[0]))?;
(formatted, ",")
} else {
let fields: PyResult<Vec<_>> = zelf
.as_slice()
.iter()
.zip(Self::FIELD_NAMES.iter().copied())
.map(format_field)
.collect();
(fields?.join(", "), "")
}
} else {
(String::new(), "...")
};
Ok(format!("{}({}{})", Self::TP_NAME, body, suffix))
}
#[pymethod(magic)]
fn reduce(zelf: PyRef<PyTuple>, vm: &VirtualMachine) -> PyTupleRef {
vm.new_tuple((
zelf.class().clone(),
(vm.ctx.new_tuple(zelf.as_slice().to_vec()),),
))
}
#[extend_class]
fn extend_pyclass(ctx: &PyContext, class: &PyTypeRef) {
for (i, &name) in Self::FIELD_NAMES.iter().enumerate() {
// cast i to a u8 so there's less to store in the getter closure.
// Hopefully there's not struct sequences with >=256 elements :P
let i = i as u8;
class.set_str_attr(
name,
ctx.new_readonly_getset(name, class.clone(), move |zelf: &PyTuple| {
zelf.fast_getitem(i.into())
}),
);
}
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
/* Several function to retrieve version information.
*/
use crate::PyStructSequence;
use crate::types::PyStructSequence;
use chrono::prelude::DateTime;
use chrono::Local;
use std::time::{Duration, UNIX_EPOCH};