Add sys.flags by creating a pystruct_sequence macro

This commit is contained in:
ben committed 2019-05-19 10:17:21 +12:00
1 parent 8359c6e031
commit 2ca96c8ae1
5 files changed
+162 -14

No files matched your search

+9
View File
@@ -1,3 +1,5 @@
#![recursion_limit = "128"]
extern crate proc_macro;
#[macro_use]
@@ -38,3 +40,10 @@ pub fn pyimpl(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as Item);
result_to_tokens(pyclass::impl_pyimpl(attr, item))
}
#[proc_macro_attribute]
pub fn pystruct_sequence(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr = parse_macro_input!(attr as AttributeArgs);
let item = parse_macro_input!(item as Item);
result_to_tokens(pyclass::impl_pystruct_sequence(attr, item))
}
+114 -13
View File
@@ -2,7 +2,9 @@ use super::Diagnostic;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use std::collections::HashMap;
use syn::{Attribute, AttributeArgs, Ident, ImplItem, Item, Lit, Meta, MethodSig, NestedMeta};
use syn::{
Attribute, AttributeArgs, Ident, ImplItem, Index, Item, Lit, Meta, MethodSig, NestedMeta,
};
enum ClassItem {
Method {
@@ -266,16 +268,12 @@ pub fn impl_pyimpl(_attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
Ok(ret)
}
pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
let (item, ident, attrs) = match item {
Item::Struct(struc) => (quote!(#struc), struc.ident, struc.attrs),
Item::Enum(enu) => (quote!(#enu), enu.ident, enu.attrs),
other => bail_span!(
other,
"#[pyclass] can only be on a struct or enum declaration"
),
};
fn generate_class_def(
ident: &Ident,
attr_name: &'static str,
attr: AttributeArgs,
attrs: &Vec<Attribute>,
) -> Result<TokenStream2, Diagnostic> {
let mut class_name = None;
for attr in attr {
if let NestedMeta::Meta(meta) = attr {
@@ -284,7 +282,11 @@ pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
if let Lit::Str(s) = name_value.lit {
class_name = Some(s.value());
} else {
bail_span!(name_value.lit, "#[pyclass(name = ...)] must be a string");
bail_span!(
name_value.lit,
"#[{}(name = ...)] must be a string",
attr_name
);
}
}
}
@@ -316,7 +318,6 @@ pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
};
let ret = quote! {
#item
impl ::rustpython_vm::pyobject::PyClassDef for #ident {
const NAME: &'static str = #class_name;
const DOC: Option<&'static str> = #doc;
@@ -324,3 +325,103 @@ pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
};
Ok(ret)
}
pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
let (item, ident, attrs) = match item {
Item::Struct(struc) => (quote!(#struc), struc.ident, struc.attrs),
Item::Enum(enu) => (quote!(#enu), enu.ident, enu.attrs),
other => bail_span!(
other,
"#[pyclass] can only be on a struct or enum declaration"
),
};
let class_def = generate_class_def(&ident, "pyclass", attr, &attrs)?;
let ret = quote! {
#item
#class_def
};
Ok(ret)
}
pub fn impl_pystruct_sequence(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
if let Item::Struct(struc) = item {
let class_def = generate_class_def(&struc.ident, "pystruct_sequence", attr, &struc.attrs)?;
let mut methods = Vec::new();
let mut method_references = Vec::new();
let mut field_names = Vec::new();
for (i, field) in struc.fields.iter().enumerate() {
let idx = Index::from(i);
if let Some(ref field_name) = field.ident {
let method_name = format!("get_{}", field_name);
let method_ident = Ident::new(&method_name, field_name.span());
let method = quote! {
fn #method_ident(
zelf: &::rustpython_vm::obj::objtuple::PyTuple,
_vm: &::rustpython_vm::vm::VirtualMachine)
-> ::rustpython_vm::pyobject::PyObjectRef {
zelf.fast_getitem(#idx)
}
};
methods.push(method);
let field_name_str = field_name.to_string();
let method_reference = quote! {
class.set_str_attr(
#field_name_str,
::rustpython_vm::obj::objproperty::PropertyBuilder::new(ctx)
.add_getter(Self::#method_ident)
.create(),
);
};
method_references.push(method_reference);
field_names.push(quote!(#field_name));
} else {
field_names.push(quote!(#idx));
}
}
let ty = &struc.ident;
let ret = quote! {
#struc
#class_def
impl #ty {
fn into_struct_sequence(&self,
vm: &::rustpython_vm::vm::VirtualMachine,
cls: ::rustpython_vm::obj::objtype::PyClassRef,
) -> ::rustpython_vm::pyobject::PyResult<::rustpython_vm::obj::objtuple::PyTupleRef> {
let tuple: ::rustpython_vm::obj::objtuple::PyTuple =
vec![#(::rustpython_vm::pyobject::IntoPyObject
::into_pyobject(self.#field_names, vm)?
),*].into();
::rustpython_vm::pyobject::PyValue::into_ref_with_type(tuple, vm, cls)
}
#(#methods)*
}
impl ::rustpython_vm::pyobject::PyClassImpl for #ty {
fn impl_extend_class(
ctx: &::rustpython_vm::pyobject::PyContext,
class: &::rustpython_vm::obj::objtype::PyClassRef,
) {
#(#method_references)*
}
fn make_class(
ctx: &::rustpython_vm::pyobject::PyContext
) -> ::rustpython_vm::obj::objtype::PyClassRef {
let py_class = ctx.new_class(<Self as ::rustpython_vm::pyobject::PyClassDef>::NAME, ctx.tuple_type());
Self::extend_class(ctx, &py_class);
py_class
}
}
};
Ok(ret)
} else {
bail_span!(
item,
"#[pystruct_sequence] can only be on a struct declaration"
)
}
}
+5
View File
@@ -7,3 +7,8 @@ assert sys.platform == "linux" or sys.platform == "darwin" or sys.platform == "w
assert isinstance(sys.builtin_module_names, tuple)
assert 'sys' in sys.builtin_module_names
assert isinstance(sys.flags, tuple)
assert type(sys.flags).__name__ == "flags"
assert type(sys.flags.optimize) is int
assert sys.flags[3] == sys.flags.optimize
+6
View File
@@ -40,6 +40,12 @@ impl PyValue for PyTuple {
}
}
impl PyTuple {
pub fn fast_getitem(&self, idx: usize) -> PyObjectRef {
self.elements.borrow()[idx].clone()
}
}
pub type PyTupleRef = PyRef<PyTuple>;
impl PyTupleRef {
+28 -1
View File
@@ -4,7 +4,7 @@ use std::{env, mem};
use crate::frame::FrameRef;
use crate::function::{OptionalArg, PyFuncArgs};
use crate::obj::objstr::PyStringRef;
use crate::pyobject::{IntoPyObject, ItemProtocol, PyContext, PyObjectRef, PyResult};
use crate::pyobject::{IntoPyObject, ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyResult};
use crate::vm::VirtualMachine;
/*
@@ -27,6 +27,26 @@ fn getframe(offset: OptionalArg<usize>, vm: &VirtualMachine) -> PyResult<FrameRe
Ok(frame.clone())
}
#[pystruct_sequence(name = "flags")]
#[derive(Default, Debug)]
struct SysFlags {
debug: bool,
inspect: bool,
interactive: bool,
optimize: u8,
dont_write_bytecode: bool,
no_user_site: bool,
no_site: bool,
ignore_environment: bool,
verbose: bool,
bytes_warning: bool,
quiet: bool,
hash_randomization: bool,
isolated: bool,
dev_mode: bool,
utf8_mode: bool,
}
fn sys_getrefcount(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(object, None)]);
let size = Rc::strong_count(&object);
@@ -48,6 +68,12 @@ fn sys_intern(value: PyStringRef, _vm: &VirtualMachine) -> PyStringRef {
pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectRef) {
let ctx = &vm.ctx;
let flags_type = SysFlags::make_class(ctx);
// TODO parse command line arguments and environment variables to populate SysFlags
let flags = SysFlags::default()
.into_struct_sequence(vm, flags_type)
.unwrap();
let path_list = match env::var_os("PYTHONPATH") {
Some(paths) => env::split_paths(&paths)
.map(|path| {
@@ -152,6 +178,7 @@ settrace() -- set the global debug tracing function
extend_module!(vm, module, {
"argv" => argv(ctx),
"builtin_module_names" => ctx.new_tuple(module_names.iter().map(|v| v.into_pyobject(vm).unwrap()).collect()),
"flags" => flags,
"getrefcount" => ctx.new_rustfunc(sys_getrefcount),
"getsizeof" => ctx.new_rustfunc(sys_getsizeof),
"intern" => ctx.new_rustfunc(sys_intern),