Merge pull request #1688 from RustPython/coolreader18/pyimpl-trait

Allow #[pyimpl] on traits
This commit is contained in:
Jeong YunWon
2020-01-23 01:01:40 +09:00
committed by GitHub
14 changed files with 319 additions and 246 deletions

7
Cargo.lock generated
View File

@@ -1312,11 +1312,11 @@ version = "0.1.1"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustpython-bytecode 0.1.1",
"rustpython-compiler 0.1.1",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1328,7 +1328,6 @@ dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unic-emoji-char 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unic-ucd-ident 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wtf8 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@@ -11,9 +11,9 @@ edition = "2018"
proc-macro = true
[dependencies]
syn = { version = "0.15.29", features = ["full"] }
quote = "0.6.11"
proc-macro2 = "0.4.27"
syn = { version = "1.0", features = ["full"] }
quote = "1.0"
proc-macro2 = "1.0"
rustpython-compiler = { path = "../compiler", version = "0.1.1" }
rustpython-bytecode = { path = "../bytecode", version = "0.1.1" }
maplit = "1.0"

View File

@@ -176,7 +176,11 @@ impl PyCompileInput {
for meta in &self.metas {
if let Meta::NameValue(name_value) = meta {
if name_value.ident == "mode" {
let ident = match name_value.path.get_ident() {
Some(ident) => ident,
None => continue,
};
if ident == "mode" {
match &name_value.lit {
Lit::Str(s) => match s.value().parse() {
Ok(mode_val) => mode = Some(mode_val),
@@ -184,12 +188,12 @@ impl PyCompileInput {
},
_ => bail_span!(name_value.lit, "mode must be a string"),
}
} else if name_value.ident == "module_name" {
} else if ident == "module_name" {
module_name = Some(match &name_value.lit {
Lit::Str(s) => s.value(),
_ => bail_span!(name_value.lit, "module_name must be string"),
})
} else if name_value.ident == "source" {
} else if ident == "source" {
assert_source_empty(&source)?;
let code = match &name_value.lit {
Lit::Str(s) => s.value(),
@@ -199,7 +203,7 @@ impl PyCompileInput {
kind: CompilationSourceKind::SourceCode(code),
span: extract_spans(&name_value).unwrap(),
});
} else if name_value.ident == "file" {
} else if ident == "file" {
assert_source_empty(&source)?;
let path = match &name_value.lit {
Lit::Str(s) => PathBuf::from(s.value()),
@@ -209,7 +213,7 @@ impl PyCompileInput {
kind: CompilationSourceKind::File(path),
span: extract_spans(&name_value).unwrap(),
});
} else if name_value.ident == "dir" {
} else if ident == "dir" {
assert_source_empty(&source)?;
let path = match &name_value.lit {
Lit::Str(s) => PathBuf::from(s.value()),

View File

@@ -1,3 +1,4 @@
use crate::util::path_eq;
use crate::Diagnostic;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
@@ -45,7 +46,9 @@ impl ArgAttribute {
err_span!(list, "There must be at least one argument to #[pyarg()]")
})?;
let kind = match first_arg {
NestedMeta::Meta(Meta::Word(ident)) => ParameterKind::from_ident(ident),
NestedMeta::Meta(Meta::Path(path)) => {
path.get_ident().and_then(ParameterKind::from_ident)
}
_ => None,
};
let kind = kind.ok_or_else(|| {
@@ -79,21 +82,21 @@ impl ArgAttribute {
fn parse_argument(&mut self, arg: &NestedMeta) -> Result<(), Diagnostic> {
match arg {
NestedMeta::Meta(Meta::Word(ident)) => {
if ident == "default" {
NestedMeta::Meta(Meta::Path(path)) => {
if path_eq(&path, "default") {
if self.default.is_some() {
bail_span!(ident, "Default already set");
bail_span!(path, "Default already set");
}
let expr = parse_quote!(Default::default());
self.default = Some(expr);
} else if ident == "optional" {
} else if path_eq(&path, "optional") {
self.optional = true;
} else {
bail_span!(ident, "Unrecognised pyarg attribute");
bail_span!(path, "Unrecognised pyarg attribute");
}
}
NestedMeta::Meta(Meta::NameValue(name_value)) => {
if name_value.ident == "default" {
if path_eq(&name_value.path, "default") {
if self.default.is_some() {
bail_span!(name_value, "Default already set");
}
@@ -107,7 +110,7 @@ impl ArgAttribute {
}
_ => bail_span!(name_value, "Expected string value for default argument"),
}
} else if name_value.ident == "optional" {
} else if path_eq(&name_value.path, "optional") {
match name_value.lit {
Lit::Bool(ref val) => {
self.optional = val.value;

View File

@@ -12,6 +12,7 @@ mod error;
mod compile_bytecode;
mod from_args;
mod pyclass;
mod util;
use error::{extract_spans, Diagnostic};
use proc_macro::TokenStream;

View File

@@ -1,15 +1,16 @@
use super::Diagnostic;
use crate::util::path_eq;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, quote_spanned};
use quote::{quote, quote_spanned, ToTokens};
use std::collections::{HashMap, HashSet};
use syn::{
spanned::Spanned, Attribute, AttributeArgs, Ident, ImplItem, Index, Item, Lit, Meta, MethodSig,
NestedMeta,
parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Index, Item, Lit, Meta,
NestedMeta, Signature,
};
fn meta_to_vec(meta: Meta) -> Result<Vec<NestedMeta>, Meta> {
match meta {
Meta::Word(_) => Ok(Vec::new()),
Meta::Path(_) => Ok(Vec::new()),
Meta::List(list) => Ok(list.nested.into_iter().collect()),
Meta::NameValue(_) => Err(meta),
}
@@ -53,10 +54,169 @@ impl Class {
}
}
fn extract_method(sig: &Signature, meta: Meta) -> Result<ClassItem, Diagnostic> {
let nesteds = meta_to_vec(meta).map_err(|meta| {
err_span!(
meta,
"#[pymethod = \"...\"] cannot be a name/value, you probably meant \
#[pymethod(name = \"...\")]",
)
})?;
let mut py_name = None;
for meta in nesteds {
let meta = match meta {
NestedMeta::Meta(meta) => meta,
NestedMeta::Lit(_) => continue,
};
if let Meta::NameValue(name_value) = meta {
if path_eq(&name_value.path, "name") {
if let Lit::Str(s) = &name_value.lit {
py_name = Some(s.value());
} else {
bail_span!(&sig.ident, "#[pymethod(name = ...)] must be a string");
}
}
}
}
Ok(ClassItem::Method {
item_ident: sig.ident.clone(),
py_name: py_name.unwrap_or_else(|| sig.ident.to_string()),
})
}
fn extract_classmethod(sig: &Signature, meta: Meta) -> Result<ClassItem, Diagnostic> {
let nesteds = meta_to_vec(meta).map_err(|meta| {
err_span!(
meta,
"#[pyclassmethod = \"...\"] cannot be a name/value, you probably meant \
#[pyclassmethod(name = \"...\")]",
)
})?;
let mut py_name = None;
for meta in nesteds {
let meta = match meta {
NestedMeta::Meta(meta) => meta,
NestedMeta::Lit(_) => continue,
};
if let Meta::NameValue(name_value) = meta {
if path_eq(&name_value.path, "name") {
if let Lit::Str(s) = &name_value.lit {
py_name = Some(s.value());
} else {
bail_span!(&sig.ident, "#[pyclassmethod(name = ...)] must be a string");
}
}
}
}
Ok(ClassItem::ClassMethod {
item_ident: sig.ident.clone(),
py_name: py_name.unwrap_or_else(|| sig.ident.to_string()),
})
}
fn extract_property(sig: &Signature, meta: Meta) -> Result<ClassItem, Diagnostic> {
let nesteds = meta_to_vec(meta).map_err(|meta| {
err_span!(
meta,
"#[pyproperty = \"...\"] cannot be a name/value, you probably meant \
#[pyproperty(name = \"...\")]"
)
})?;
let mut setter = false;
let mut py_name = None;
for meta in nesteds {
let meta = match meta {
NestedMeta::Meta(meta) => meta,
NestedMeta::Lit(_) => continue,
};
match meta {
Meta::NameValue(name_value) => {
if path_eq(&name_value.path, "name") {
if let Lit::Str(s) = &name_value.lit {
py_name = Some(s.value());
} else {
bail_span!(&sig.ident, "#[pyproperty(name = ...)] must be a string");
}
}
}
Meta::Path(path) => {
if path_eq(&path, "setter") {
setter = true;
}
}
_ => {}
}
}
let py_name = match py_name {
Some(py_name) => py_name,
None => {
let item_ident = sig.ident.to_string();
if setter {
if item_ident.starts_with("set_") {
let name = &item_ident["set_".len()..];
if name.is_empty() {
bail_span!(
&sig.ident,
"A #[pyproperty(setter)] fn with a set_* name must \
have something after \"set_\""
)
} else {
name.to_string()
}
} else {
bail_span!(
&sig.ident,
"A #[pyproperty(setter)] fn must either have a `name` \
parameter or a fn name along the lines of \"set_*\""
)
}
} else {
item_ident
}
}
};
Ok(ClassItem::Property {
py_name,
item_ident: sig.ident.clone(),
setter,
})
}
fn extract_slot(sig: &Signature, 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(&quote!(#(#nesteds)*), pyslot_err));
}
let slot_ident = if nesteds.is_empty() {
let ident_str = sig.ident.to_string();
if ident_str.starts_with("tp_") {
let slot_name = &ident_str[3..];
proc_macro2::Ident::new(slot_name, sig.ident.span())
} else {
sig.ident.clone()
}
} else {
match nesteds.into_iter().next().unwrap() {
NestedMeta::Meta(Meta::Path(path)) => path
.get_ident()
.cloned()
.ok_or_else(|| err_span!(path, "{}", pyslot_err))?,
bad => bail_span!(bad, "{}", pyslot_err),
}
};
Ok(ClassItem::Slot {
slot_ident,
item_ident: sig.ident.clone(),
})
}
fn extract_item_from_syn(
&mut self,
attrs: &mut Vec<Attribute>,
sig: &MethodSig,
sig: &Signature,
) -> Result<(), Diagnostic> {
let mut attr_idxs = Vec::new();
for (i, meta) in attrs
@@ -65,205 +225,61 @@ impl Class {
.enumerate()
{
let meta_span = meta.span();
let name = meta.name();
let name = match meta.path().get_ident() {
Some(name) => name,
None => continue,
};
if name == "pymethod" {
let nesteds = meta_to_vec(meta).map_err(|meta| {
err_span!(
meta,
"#[pymethod = \"...\"] cannot be a name/value, you probably meant \
#[pymethod(name = \"...\")]",
)
})?;
let mut py_name = None;
for meta in nesteds {
let meta = match meta {
NestedMeta::Meta(meta) => meta,
NestedMeta::Literal(_) => continue,
};
if let Meta::NameValue(name_value) = meta {
if name_value.ident == "name" {
if let Lit::Str(s) = &name_value.lit {
py_name = Some(s.value());
} else {
bail_span!(&sig.ident, "#[pymethod(name = ...)] must be a string");
}
}
}
}
self.add_item(
ClassItem::Method {
item_ident: sig.ident.clone(),
py_name: py_name.unwrap_or_else(|| sig.ident.to_string()),
},
meta_span,
)?;
self.add_item(Self::extract_method(sig, meta)?, meta_span)?;
attr_idxs.push(i);
} else if name == "pyclassmethod" {
let nesteds = meta_to_vec(meta).map_err(|meta| {
err_span!(
meta,
"#[pyclassmethod = \"...\"] cannot be a name/value, you probably meant \
#[pyclassmethod(name = \"...\")]",
)
})?;
let mut py_name = None;
for meta in nesteds {
let meta = match meta {
NestedMeta::Meta(meta) => meta,
NestedMeta::Literal(_) => continue,
};
if let Meta::NameValue(name_value) = meta {
if name_value.ident == "name" {
if let Lit::Str(s) = &name_value.lit {
py_name = Some(s.value());
} else {
bail_span!(
&sig.ident,
"#[pyclassmethod(name = ...)] must be a string"
);
}
}
}
}
self.add_item(
ClassItem::ClassMethod {
item_ident: sig.ident.clone(),
py_name: py_name.unwrap_or_else(|| sig.ident.to_string()),
},
meta_span,
)?;
self.add_item(Self::extract_classmethod(sig, meta)?, meta_span)?;
attr_idxs.push(i);
} else if name == "pyproperty" {
let nesteds = meta_to_vec(meta).map_err(|meta| {
err_span!(
meta,
"#[pyproperty = \"...\"] cannot be a name/value, you probably meant \
#[pyproperty(name = \"...\")]"
)
})?;
let mut setter = false;
let mut py_name = None;
for meta in nesteds {
let meta = match meta {
NestedMeta::Meta(meta) => meta,
NestedMeta::Literal(_) => continue,
};
match meta {
Meta::NameValue(name_value) => {
if name_value.ident == "name" {
if let Lit::Str(s) = &name_value.lit {
py_name = Some(s.value());
} else {
bail_span!(
&sig.ident,
"#[pyproperty(name = ...)] must be a string"
);
}
}
}
Meta::Word(ident) => {
if ident == "setter" {
setter = true;
}
}
_ => {}
}
}
let py_name = match py_name {
Some(py_name) => py_name,
None => {
let item_ident = sig.ident.to_string();
if setter {
if item_ident.starts_with("set_") {
let name = &item_ident["set_".len()..];
if name.is_empty() {
bail_span!(
&sig.ident,
"A #[pyproperty(setter)] fn with a set_* name must \
have something after \"set_\""
)
} else {
name.to_string()
}
} else {
bail_span!(
&sig.ident,
"A #[pyproperty(setter)] fn must either have a `name` \
parameter or a fn name along the lines of \"set_*\""
)
}
} else {
item_ident
}
}
};
self.add_item(
ClassItem::Property {
py_name,
item_ident: sig.ident.clone(),
setter,
},
meta_span,
)?;
self.add_item(Self::extract_property(sig, meta)?, meta_span)?;
attr_idxs.push(i);
} else if name == "pyslot" {
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(&quote!(#(#nesteds)*), pyslot_err));
}
let slot_ident = if nesteds.is_empty() {
let ident_str = sig.ident.to_string();
if ident_str.starts_with("tp_") {
let slot_name = &ident_str[3..];
proc_macro2::Ident::new(slot_name, sig.ident.span())
} else {
sig.ident.clone()
}
} else {
match nesteds.into_iter().next().unwrap() {
NestedMeta::Meta(Meta::Word(ident)) => ident,
bad => bail_span!(bad, "{}", pyslot_err),
}
};
self.add_item(
ClassItem::Slot {
slot_ident,
item_ident: sig.ident.clone(),
},
meta_span,
)?;
self.add_item(Self::extract_slot(sig, meta)?, meta_span)?;
attr_idxs.push(i);
}
}
for idx in attr_idxs {
attrs.remove(idx);
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(())
}
}
pub fn impl_pyimpl(_attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
let mut imp = if let Item::Impl(imp) = item {
imp
} else {
return Ok(quote!(#item));
};
struct ItemSig<'a> {
attrs: &'a mut Vec<Attribute>,
sig: &'a Signature,
}
fn extract_impl_items(
attr: AttributeArgs,
mut items: Vec<ItemSig>,
) -> Result<TokenStream2, Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = Vec::new();
let mut class = Class::default();
for item in imp.items.iter_mut() {
if let ImplItem::Method(meth) = item {
push_diag_result!(
diagnostics,
class.extract_item_from_syn(&mut meth.attrs, &meth.sig),
);
}
for item in items.iter_mut() {
push_diag_result!(
diagnostics,
class.extract_item_from_syn(&mut item.attrs, item.sig),
);
}
let ty = &imp.self_ty;
let mut properties: HashMap<&str, (Option<&Ident>, Option<&Ident>)> = HashMap::new();
for item in class.items.iter() {
if let ClassItem::Property {
@@ -346,19 +362,88 @@ pub fn impl_pyimpl(_attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
Diagnostic::from_vec(diagnostics)?;
let ret = quote! {
#imp
impl ::rustpython_vm::pyobject::PyClassImpl for #ty {
fn impl_extend_class(
ctx: &::rustpython_vm::pyobject::PyContext,
class: &::rustpython_vm::obj::objtype::PyClassRef,
) {
#(#methods)*
#(#properties)*
let mut withs = Vec::new();
for attr in attr {
match attr {
NestedMeta::Meta(Meta::List(syn::MetaList { path, nested, .. }))
if path_eq(&path, "with") =>
{
for meta in nested {
match meta {
NestedMeta::Meta(Meta::Path(path)) => {
withs.push(quote! {
<Self as #path>::__extend_py_class(ctx, class);
});
}
meta => bail_span!(meta, "#[pyimpl(with(...))] arguments should be paths"),
}
}
}
attr => bail_span!(attr, "Unknown pyimpl attribute"),
}
};
Ok(ret)
}
Ok(quote! {
#(#methods)*
#(#properties)*
#(#withs)*
})
}
pub fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
match item {
Item::Impl(mut imp) => {
let items = imp
.items
.iter_mut()
.filter_map(|item| match item {
syn::ImplItem::Method(syn::ImplItemMethod { attrs, sig, .. }) => {
Some(ItemSig { attrs, sig })
}
_ => None,
})
.collect();
let extend_impl = extract_impl_items(attr, items)?;
let ty = &imp.self_ty;
let ret = quote! {
#imp
impl ::rustpython_vm::pyobject::PyClassImpl for #ty {
fn impl_extend_class(
ctx: &::rustpython_vm::pyobject::PyContext,
class: &::rustpython_vm::obj::objtype::PyClassRef,
) {
#extend_impl
}
}
};
Ok(ret)
}
Item::Trait(mut trai) => {
let items = trai
.items
.iter_mut()
.filter_map(|item| match item {
syn::TraitItem::Method(syn::TraitItemMethod { attrs, sig, .. }) => {
Some(ItemSig { attrs, sig })
}
_ => None,
})
.collect();
let extend_impl = extract_impl_items(attr, items)?;
let item = parse_quote! {
fn __extend_py_class(
ctx: &::rustpython_vm::pyobject::PyContext,
class: &::rustpython_vm::obj::objtype::PyClassRef,
) {
#extend_impl
}
};
trai.items.push(item);
Ok(trai.into_token_stream())
}
item => Ok(quote!(#item)),
}
}
fn generate_class_def(
@@ -371,7 +456,7 @@ fn generate_class_def(
for attr in attr {
if let NestedMeta::Meta(meta) = attr {
if let Meta::NameValue(name_value) = meta {
if name_value.ident == "name" {
if path_eq(&name_value.path, "name") {
if let Lit::Str(s) = name_value.lit {
class_name = Some(s.value());
} else {

3
derive/src/util.rs Normal file
View File

@@ -0,0 +1,3 @@
pub fn path_eq(path: &syn::Path, s: &str) -> bool {
path.get_ident().map_or(false, |id| id == s)
}

View File

@@ -14,7 +14,6 @@ lalrpop="0.17"
[dependencies]
lalrpop-util="0.17"
log="0.4.1"
regex = "1"
num-bigint = "0.2"
num-traits = "0.2"
unic-emoji-char = "0.9"

View File

@@ -2,8 +2,10 @@ use crate::function::OptionalArg;
use crate::pyobject::{IdProtocol, PyObjectRef, PyRef, PyResult, PyValue};
use crate::VirtualMachine;
// TODO: pyimpl compose pyslot(descr_get) and pymethod(__get__) from this trait
#[pyimpl]
pub trait PyBuiltinDescriptor: PyValue {
#[pymethod(name = "__get__")]
#[pyslot(descr_get)]
fn get(
zelf: PyRef<Self>,
obj: PyObjectRef,

View File

@@ -87,7 +87,7 @@ impl PyBuiltinDescriptor for PyBuiltinMethod {
}
}
#[pyimpl]
#[pyimpl(with(PyBuiltinDescriptor))]
impl PyBuiltinMethod {
#[pymethod(name = "__call__")]
pub fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
@@ -98,8 +98,4 @@ impl PyBuiltinMethod {
pub fn init(context: &PyContext) {
PyBuiltinFunction::extend_class(context, &context.types.builtin_function_or_method_type);
PyBuiltinMethod::extend_class(context, &context.types.method_descriptor_type);
extend_class!(context, context.types.method_descriptor_type, {
"__get__" => context.new_method(PyBuiltinMethod::get),
(slot descr_get) => PyBuiltinMethod::get,
});
}

View File

@@ -59,7 +59,7 @@ impl PyBuiltinDescriptor for PyClassMethod {
}
}
#[pyimpl]
#[pyimpl(with(PyBuiltinDescriptor))]
impl PyClassMethod {
#[pyslot]
fn tp_new(
@@ -81,8 +81,4 @@ impl PyClassMethod {
pub(crate) fn init(context: &PyContext) {
PyClassMethod::extend_class(context, &context.types.classmethod_type);
extend_class!(context, context.types.classmethod_type, {
"__get__" => context.new_method(PyClassMethod::get),
(slot descr_get) => PyClassMethod::get,
});
}

View File

@@ -240,7 +240,7 @@ impl PyValue for PyFunction {
}
}
#[pyimpl]
#[pyimpl(with(PyBuiltinDescriptor))]
impl PyFunction {
#[pymethod(name = "__call__")]
fn call(zelf: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
@@ -289,10 +289,6 @@ impl PyValue for PyBoundMethod {
pub fn init(context: &PyContext) {
let function_type = &context.types.function_type;
PyFunction::extend_class(context, function_type);
extend_class!(context, function_type, {
"__get__" => context.new_method(PyFunction::get),
(slot descr_get) => PyFunction::get,
});
let method_type = &context.types.bound_method_type;
extend_class!(context, method_type, {

View File

@@ -46,7 +46,7 @@ impl PyBuiltinDescriptor for PyReadOnlyProperty {
}
}
#[pyimpl]
#[pyimpl(with(PyBuiltinDescriptor))]
impl PyReadOnlyProperty {}
/// Property attribute.
@@ -129,7 +129,7 @@ impl PyBuiltinDescriptor for PyProperty {
}
}
#[pyimpl]
#[pyimpl(with(PyBuiltinDescriptor))]
impl PyProperty {
#[pyslot]
fn tp_new(cls: PyClassRef, args: PropertyArgs, vm: &VirtualMachine) -> PyResult<PyPropertyRef> {
@@ -318,15 +318,8 @@ impl<'a> PropertyBuilder<'a> {
pub fn init(context: &PyContext) {
PyReadOnlyProperty::extend_class(context, &context.types.readonly_property_type);
extend_class!(context, context.types.readonly_property_type, {
"__get__" => context.new_method(PyReadOnlyProperty::get),
(slot descr_get) => PyReadOnlyProperty::get,
});
PyProperty::extend_class(context, &context.types.property_type);
extend_class!(context, context.types.property_type, {
"__get__" => context.new_method(PyProperty::get),
});
// This is a bit unfortunate, but this instance attribute overlaps with the
// class __doc__ string..

View File

@@ -28,7 +28,7 @@ impl PyBuiltinDescriptor for PyStaticMethod {
}
}
#[pyimpl]
#[pyimpl(with(PyBuiltinDescriptor))]
impl PyStaticMethod {
#[pyslot]
fn tp_new(
@@ -45,8 +45,4 @@ impl PyStaticMethod {
pub fn init(context: &PyContext) {
PyStaticMethod::extend_class(context, &context.types.staticmethod_type);
extend_class!(context, context.types.staticmethod_type, {
"__get__" => context.new_method(PyStaticMethod::get),
(slot descr_get) => PyStaticMethod::get,
});
}