Skip to content

Commit ef887ad

Browse files
authored
Merge pull request RustPython#4208 from moreal/pymember-tbool
* Support `T_BOOL` member type * Configure `pymember`'s MemberKind manually * Provide setter in `pymember` * Implement `Frame.f_trace_lines`
2 parents 6eb4e57 + 91fca47 commit ef887ad

File tree

5 files changed

+211
-21
lines changed

5 files changed

+211
-21
lines changed

derive/src/pyclass.rs

Lines changed: 151 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ impl FromStr for AttrName {
6666
struct ImplContext {
6767
impl_extend_items: ItemNursery,
6868
getset_items: GetSetNursery,
69+
member_items: MemberNursery,
6970
extend_slots_items: ItemNursery,
7071
class_extensions: Vec<TokenStream>,
7172
errors: Vec<syn::Error>,
@@ -94,6 +95,7 @@ fn extract_items_into_context<'a, Item>(
9495
context.errors.ok_or_push(r);
9596
}
9697
context.errors.ok_or_push(context.getset_items.validate());
98+
context.errors.ok_or_push(context.member_items.validate());
9799
}
98100

99101
pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream> {
@@ -110,6 +112,7 @@ pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream
110112
} = extract_impl_attrs(attr, &Ident::new(&quote!(ty).to_string(), ty.span()))?;
111113

112114
let getset_impl = &context.getset_items;
115+
let member_impl = &context.member_items;
113116
let extend_impl = context.impl_extend_items.validate()?;
114117
let slots_impl = context.extend_slots_items.validate()?;
115118
let class_extensions = &context.class_extensions;
@@ -123,6 +126,7 @@ pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream
123126
class: &'static ::rustpython_vm::Py<::rustpython_vm::builtins::PyType>,
124127
) {
125128
#getset_impl
129+
#member_impl
126130
#extend_impl
127131
#with_impl
128132
#(#class_extensions)*
@@ -146,6 +150,7 @@ pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream
146150
} = extract_impl_attrs(attr, &trai.ident)?;
147151

148152
let getset_impl = &context.getset_items;
153+
let member_impl = &context.member_items;
149154
let extend_impl = &context.impl_extend_items.validate()?;
150155
let slots_impl = &context.extend_slots_items.validate()?;
151156
let class_extensions = &context.class_extensions;
@@ -156,6 +161,7 @@ pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream
156161
class: &'static ::rustpython_vm::Py<::rustpython_vm::builtins::PyType>,
157162
) {
158163
#getset_impl
164+
#member_impl
159165
#extend_impl
160166
#with_impl
161167
#(#class_extensions)*
@@ -689,23 +695,20 @@ where
689695
let item_attr = args.attrs.remove(self.index());
690696
let item_meta = MemberItemMeta::from_attr(ident.clone(), &item_attr)?;
691697

692-
let py_name = item_meta.member_name()?;
693-
let tokens = {
694-
quote_spanned! { ident.span() =>
695-
class.set_str_attr(
696-
#py_name,
697-
ctx.new_member(#py_name, Self::#ident, None, class),
698-
ctx,
699-
);
700-
}
698+
let (py_name, member_item_kind) = item_meta.member_name()?;
699+
let member_kind = match item_meta.member_kind()? {
700+
Some(s) => match s.as_str() {
701+
"bool" => MemberKind::Bool,
702+
_ => unreachable!(),
703+
},
704+
_ => MemberKind::ObjectEx,
701705
};
702706

703-
args.context.impl_extend_items.add_item(
707+
args.context.member_items.add_item(
708+
py_name,
709+
member_item_kind,
710+
member_kind,
704711
ident.clone(),
705-
vec![py_name],
706-
args.cfgs.to_vec(),
707-
tokens,
708-
5,
709712
)?;
710713
Ok(())
711714
}
@@ -805,6 +808,95 @@ impl ToTokens for GetSetNursery {
805808
}
806809
}
807810

811+
#[derive(Default)]
812+
#[allow(clippy::type_complexity)]
813+
struct MemberNursery {
814+
map: HashMap<(String, MemberKind), (Option<Ident>, Option<Ident>)>,
815+
validated: bool,
816+
}
817+
818+
enum MemberItemKind {
819+
Get,
820+
Set,
821+
}
822+
823+
#[derive(Eq, PartialEq, Hash)]
824+
enum MemberKind {
825+
Bool,
826+
ObjectEx,
827+
}
828+
829+
impl MemberNursery {
830+
fn add_item(
831+
&mut self,
832+
name: String,
833+
kind: MemberItemKind,
834+
member_kind: MemberKind,
835+
item_ident: Ident,
836+
) -> Result<()> {
837+
assert!(!self.validated, "new item is not allowed after validation");
838+
let entry = self.map.entry((name.clone(), member_kind)).or_default();
839+
let func = match kind {
840+
MemberItemKind::Get => &mut entry.0,
841+
MemberItemKind::Set => &mut entry.1,
842+
};
843+
if func.is_some() {
844+
return Err(syn::Error::new_spanned(
845+
item_ident,
846+
format!("Multiple member accessors with name '{}'", name),
847+
));
848+
}
849+
*func = Some(item_ident);
850+
Ok(())
851+
}
852+
853+
fn validate(&mut self) -> Result<()> {
854+
let mut errors = Vec::new();
855+
for ((name, _), (getter, setter)) in &self.map {
856+
if getter.is_none() {
857+
errors.push(syn::Error::new_spanned(
858+
setter.as_ref().unwrap(),
859+
format!("Member '{}' is missing a getter", name),
860+
));
861+
};
862+
}
863+
errors.into_result()?;
864+
self.validated = true;
865+
Ok(())
866+
}
867+
}
868+
869+
impl ToTokens for MemberNursery {
870+
fn to_tokens(&self, tokens: &mut TokenStream) {
871+
assert!(self.validated, "Call `validate()` before token generation");
872+
let properties = self
873+
.map
874+
.iter()
875+
.map(|((name, member_kind), (getter, setter))| {
876+
let setter = match setter {
877+
Some(setter) => quote_spanned! { setter.span() => Some(Self::#setter)},
878+
None => quote! { None },
879+
};
880+
let member_kind = match member_kind {
881+
MemberKind::Bool => {
882+
quote!(::rustpython_vm::builtins::descriptor::MemberKind::Bool)
883+
}
884+
MemberKind::ObjectEx => {
885+
quote!(::rustpython_vm::builtins::descriptor::MemberKind::ObjectEx)
886+
}
887+
};
888+
quote_spanned! { getter.span() =>
889+
class.set_str_attr(
890+
#name,
891+
ctx.new_member(#name, #member_kind, Self::#getter, #setter, class),
892+
ctx,
893+
);
894+
}
895+
});
896+
tokens.extend(properties);
897+
}
898+
}
899+
808900
struct MethodItemMeta(ItemMetaInner);
809901

810902
impl ItemMeta for MethodItemMeta {
@@ -983,7 +1075,7 @@ impl SlotItemMeta {
9831075
struct MemberItemMeta(ItemMetaInner);
9841076

9851077
impl ItemMeta for MemberItemMeta {
986-
const ALLOWED_NAMES: &'static [&'static str] = &["magic"];
1078+
const ALLOWED_NAMES: &'static [&'static str] = &["magic", "type", "setter"];
9871079

9881080
fn from_inner(inner: ItemMetaInner) -> Self {
9891081
Self(inner)
@@ -994,11 +1086,52 @@ impl ItemMeta for MemberItemMeta {
9941086
}
9951087

9961088
impl MemberItemMeta {
997-
fn member_name(&self) -> Result<String> {
1089+
fn member_name(&self) -> Result<(String, MemberItemKind)> {
9981090
let inner = self.inner();
1091+
let sig_name = inner.item_name();
1092+
let extract_prefix_name = |prefix, item_typ| {
1093+
if let Some(name) = sig_name.strip_prefix(prefix) {
1094+
if name.is_empty() {
1095+
Err(syn::Error::new_spanned(
1096+
&inner.meta_ident,
1097+
format!(
1098+
"A #[{}({typ})] fn with a {prefix}* name must \
1099+
have something after \"{prefix}\"",
1100+
inner.meta_name(),
1101+
typ = item_typ,
1102+
prefix = prefix
1103+
),
1104+
))
1105+
} else {
1106+
Ok(name.to_owned())
1107+
}
1108+
} else {
1109+
Err(syn::Error::new_spanned(
1110+
&inner.meta_ident,
1111+
format!(
1112+
"A #[{}(setter)] fn must either have a `name` \
1113+
parameter or a fn name along the lines of \"set_*\"",
1114+
inner.meta_name()
1115+
),
1116+
))
1117+
}
1118+
};
9991119
let magic = inner._bool("magic")?;
1000-
let name = inner.item_name();
1001-
Ok(if magic { format!("__{}__", name) } else { name })
1120+
let kind = if inner._bool("setter")? {
1121+
MemberItemKind::Set
1122+
} else {
1123+
MemberItemKind::Get
1124+
};
1125+
let name = match kind {
1126+
MemberItemKind::Get => sig_name,
1127+
MemberItemKind::Set => extract_prefix_name("set_", "setter")?,
1128+
};
1129+
Ok((if magic { format!("__{}__", name) } else { name }, kind))
1130+
}
1131+
1132+
fn member_kind(&self) -> Result<Option<String>> {
1133+
let inner = self.inner();
1134+
inner._optional_str("type")
10021135
}
10031136
}
10041137

vm/src/builtins/descriptor.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct DescrObject {
1616

1717
#[derive(Debug)]
1818
pub enum MemberKind {
19+
Bool = 14,
1920
ObjectEx = 16,
2021
}
2122

@@ -148,6 +149,9 @@ fn get_slot_from_object(
148149
vm: &VirtualMachine,
149150
) -> PyResult {
150151
let slot = match member.kind {
152+
MemberKind::Bool => obj
153+
.get_slot(offset)
154+
.unwrap_or_else(|| vm.ctx.new_bool(false).into()),
151155
MemberKind::ObjectEx => obj.get_slot(offset).ok_or_else(|| {
152156
vm.new_attribute_error(format!(
153157
"'{}' object has no attribute '{}'",
@@ -165,9 +169,23 @@ fn set_slot_at_object(
165169
offset: usize,
166170
member: &MemberDef,
167171
value: PySetterValue,
168-
_vm: &VirtualMachine,
172+
vm: &VirtualMachine,
169173
) -> PyResult<()> {
170174
match member.kind {
175+
MemberKind::Bool => {
176+
match value {
177+
PySetterValue::Assign(v) => {
178+
if !v.class().is(vm.ctx.types.bool_type) {
179+
return Err(
180+
vm.new_type_error("attribute value type must be bool".to_owned())
181+
);
182+
}
183+
184+
obj.set_slot(offset, Some(v))
185+
}
186+
PySetterValue::Delete => obj.set_slot(offset, None),
187+
};
188+
}
171189
MemberKind::ObjectEx => match value {
172190
PySetterValue::Assign(v) => obj.set_slot(offset, Some(v)),
173191
PySetterValue::Delete => obj.set_slot(offset, None),

vm/src/builtins/frame.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
33
*/
44

5-
use super::{PyCode, PyDictRef};
5+
use super::{PyCode, PyDictRef, PyIntRef};
66
use crate::{
77
class::PyClassImpl,
88
frame::{Frame, FrameRef},
99
function::PySetterValue,
1010
types::{Constructor, Unconstructible},
1111
AsObject, Context, PyObjectRef, PyRef, PyResult, VirtualMachine,
1212
};
13+
use num_traits::Zero;
1314

1415
pub fn init(context: &Context) {
1516
FrameRef::extend_class(context, context.types.frame_type);
@@ -82,4 +83,37 @@ impl FrameRef {
8283
let mut storage = self.trace.lock();
8384
*storage = value.unwrap_or_none(vm);
8485
}
86+
87+
#[pymember(type = "bool")]
88+
fn f_trace_lines(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
89+
let zelf: FrameRef = zelf.downcast().unwrap_or_else(|_| unreachable!());
90+
91+
let boxed = zelf.trace_lines.lock();
92+
Ok(vm.ctx.new_bool(*boxed).into())
93+
}
94+
95+
#[pymember(type = "bool", setter)]
96+
fn set_f_trace_lines(
97+
vm: &VirtualMachine,
98+
zelf: PyObjectRef,
99+
value: PySetterValue,
100+
) -> PyResult<()> {
101+
match value {
102+
PySetterValue::Assign(value) => {
103+
let zelf: FrameRef = zelf.downcast().unwrap_or_else(|_| unreachable!());
104+
105+
let value: PyIntRef = value.downcast().map_err(|_| {
106+
vm.new_type_error("attribute value type must be bool".to_owned())
107+
})?;
108+
109+
let mut trace_lines = zelf.trace_lines.lock();
110+
*trace_lines = !value.as_bigint().is_zero();
111+
112+
Ok(())
113+
}
114+
PySetterValue::Delete => {
115+
Err(vm.new_type_error("can't delete numeric/char attribute".to_owned()))
116+
}
117+
}
118+
}
85119
}

vm/src/frame.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ pub struct Frame {
111111
/// tracer function for this frame (usually is None)
112112
pub trace: PyMutex<PyObjectRef>,
113113
state: PyMutex<FrameState>,
114+
115+
// member
116+
pub trace_lines: PyMutex<bool>,
114117
}
115118

116119
impl PyPayload for Frame {
@@ -158,6 +161,7 @@ impl Frame {
158161
lasti: Lasti::new(0),
159162
state: PyMutex::new(state),
160163
trace: PyMutex::new(vm.ctx.none()),
164+
trace_lines: PyMutex::new(true),
161165
}
162166
}
163167
}

vm/src/vm/context.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,13 +464,14 @@ impl Context {
464464
pub fn new_member(
465465
&self,
466466
name: &str,
467+
member_kind: MemberKind,
467468
getter: fn(&VirtualMachine, PyObjectRef) -> PyResult,
468469
setter: MemberSetterFunc,
469470
class: &'static Py<PyType>,
470471
) -> PyRef<MemberDescrObject> {
471472
let member_def = MemberDef {
472473
name: name.to_owned(),
473-
kind: MemberKind::ObjectEx,
474+
kind: member_kind,
474475
getter: MemberGetter::Getter(getter),
475476
setter: MemberSetter::Setter(setter),
476477
doc: None,

0 commit comments

Comments
 (0)