Merge pull request #2234 from davidhewitt/pyclass-args-refactor
pyclass: unify pyclass with its pyo3 arguments
This commit is contained in:
commit
87c79c0319
|
@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Allow `#[pyo3(crate = "...", text_signature = "...")]` options to be used directly in `#[pyclass(crate = "...", text_signature = "...")]`. [#2234](https://github.com/PyO3/pyo3/pull/2234)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Considered `PYTHONFRAMEWORK` when cross compiling in order that on macos cross compiling against a [Framework bundle](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html) is considered shared. [#2233](https://github.com/PyO3/pyo3/pull/2233)
|
- Considered `PYTHONFRAMEWORK` when cross compiling in order that on macos cross compiling against a [Framework bundle](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html) is considered shared. [#2233](https://github.com/PyO3/pyo3/pull/2233)
|
||||||
|
|
|
@ -195,22 +195,16 @@ Python::with_gil(|py|{
|
||||||
|
|
||||||
## Customizing the class
|
## Customizing the class
|
||||||
|
|
||||||
The `#[pyclass]` macro accepts the following parameters:
|
{{#include ../../pyo3-macros/docs/pyclass_parameters.md}}
|
||||||
|
|
||||||
* `name="XXX"` - Set the class name shown in Python code. By default, the struct name is used as the class name.
|
[params-1]: {{#PYO3_DOCS_URL}}/pyo3/prelude/struct.PyAny.html
|
||||||
* `freelist=XXX` - The `freelist` parameter adds support of free allocation list to custom class.
|
[params-2]: https://en.wikipedia.org/wiki/Free_list
|
||||||
The performance improvement applies to types that are often created and deleted in a row,
|
[params-3]: https://doc.rust-lang.org/stable/std/marker/trait.Send.html
|
||||||
so that they can benefit from a freelist. `XXX` is a number of items for the free list.
|
[params-4]: https://doc.rust-lang.org/stable/std/rc/struct.Rc.html
|
||||||
* `gc` - Classes with the `gc` parameter participate in Python garbage collection.
|
[params-5]: https://doc.rust-lang.org/stable/std/sync/struct.Rc.html
|
||||||
If a custom class contains references to other Python objects that can be collected, the [`PyGCProtocol`]({{#PYO3_DOCS_URL}}/pyo3/class/gc/trait.PyGCProtocol.html) trait has to be implemented.
|
[params-6]: https://docs.python.org/3/library/weakref.html
|
||||||
* `weakref` - Adds support for Python weak references.
|
|
||||||
* `extends=BaseType` - Use a custom base class. The base `BaseType` must implement `PyTypeInfo`. `enum` pyclasses can't use a custom base class.
|
These parameters are covered in various sections of this guide.
|
||||||
* `subclass` - Allows Python classes to inherit from this class. `enum` pyclasses can't be inherited from.
|
|
||||||
* `dict` - Adds `__dict__` support, so that the instances of this type have a dictionary containing arbitrary instance variables.
|
|
||||||
* `unsendable` - Making it safe to expose `!Send` structs to Python, where all object can be accessed
|
|
||||||
by multiple threads. A class marked with `unsendable` panics when accessed by another thread.
|
|
||||||
* `module="XXX"` - Set the name of the module the class will be shown as defined in. If not given, the class
|
|
||||||
will be a virtual member of the `builtins` module.
|
|
||||||
|
|
||||||
### Return type
|
### Return type
|
||||||
|
|
||||||
|
@ -716,7 +710,7 @@ num=-1
|
||||||
|
|
||||||
## Making class method signatures available to Python
|
## Making class method signatures available to Python
|
||||||
|
|
||||||
The [`#[pyo3(text_signature = "...")]`](./function.md#text_signature) option for `#[pyfunction]` also works for classes and methods:
|
The [`text_signature = "..."`](./function.md#text_signature) option for `#[pyfunction]` also works for classes and methods:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #![allow(dead_code)]
|
# #![allow(dead_code)]
|
||||||
|
@ -724,8 +718,7 @@ use pyo3::prelude::*;
|
||||||
use pyo3::types::PyType;
|
use pyo3::types::PyType;
|
||||||
|
|
||||||
// it works even if the item is not documented:
|
// it works even if the item is not documented:
|
||||||
#[pyclass]
|
#[pyclass(text_signature = "(c, d, /)")]
|
||||||
#[pyo3(text_signature = "(c, d, /)")]
|
|
||||||
struct MyClass {}
|
struct MyClass {}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
|
|
|
@ -1,77 +1,107 @@
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::ToTokens;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
punctuated::Punctuated,
|
punctuated::Punctuated,
|
||||||
|
spanned::Spanned,
|
||||||
token::Comma,
|
token::Comma,
|
||||||
Attribute, ExprPath, Ident, LitStr, Path, Result, Token,
|
Attribute, Expr, ExprPath, Ident, LitStr, Path, Result, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod kw {
|
pub mod kw {
|
||||||
syn::custom_keyword!(annotation);
|
syn::custom_keyword!(annotation);
|
||||||
syn::custom_keyword!(attribute);
|
syn::custom_keyword!(attribute);
|
||||||
|
syn::custom_keyword!(dict);
|
||||||
|
syn::custom_keyword!(extends);
|
||||||
|
syn::custom_keyword!(freelist);
|
||||||
syn::custom_keyword!(from_py_with);
|
syn::custom_keyword!(from_py_with);
|
||||||
|
syn::custom_keyword!(gc);
|
||||||
syn::custom_keyword!(get);
|
syn::custom_keyword!(get);
|
||||||
syn::custom_keyword!(item);
|
syn::custom_keyword!(item);
|
||||||
syn::custom_keyword!(pass_module);
|
syn::custom_keyword!(module);
|
||||||
syn::custom_keyword!(name);
|
syn::custom_keyword!(name);
|
||||||
|
syn::custom_keyword!(pass_module);
|
||||||
syn::custom_keyword!(set);
|
syn::custom_keyword!(set);
|
||||||
syn::custom_keyword!(signature);
|
syn::custom_keyword!(signature);
|
||||||
|
syn::custom_keyword!(subclass);
|
||||||
syn::custom_keyword!(text_signature);
|
syn::custom_keyword!(text_signature);
|
||||||
syn::custom_keyword!(transparent);
|
syn::custom_keyword!(transparent);
|
||||||
|
syn::custom_keyword!(unsendable);
|
||||||
|
syn::custom_keyword!(weakref);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct FromPyWithAttribute(pub ExprPath);
|
pub struct KeywordAttribute<K, V> {
|
||||||
|
pub kw: K,
|
||||||
|
pub value: V,
|
||||||
|
}
|
||||||
|
|
||||||
impl Parse for FromPyWithAttribute {
|
/// A helper type which parses the inner type via a literal string
|
||||||
|
/// e.g. LitStrValue<Path> -> parses "some::path" in quotes.
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct LitStrValue<T>(pub T);
|
||||||
|
|
||||||
|
impl<T: Parse> Parse for LitStrValue<T> {
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
let _: kw::from_py_with = input.parse()?;
|
let lit_str: LitStr = input.parse()?;
|
||||||
let _: Token![=] = input.parse()?;
|
lit_str.parse().map(LitStrValue)
|
||||||
let string_literal: LitStr = input.parse()?;
|
|
||||||
string_literal.parse().map(FromPyWithAttribute)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
impl<T: ToTokens> ToTokens for LitStrValue<T> {
|
||||||
pub struct NameAttribute(pub Ident);
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
self.0.to_tokens(tokens)
|
||||||
impl Parse for NameAttribute {
|
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
|
||||||
let _: kw::name = input.parse()?;
|
|
||||||
let _: Token![=] = input.parse()?;
|
|
||||||
let string_literal: LitStr = input.parse()?;
|
|
||||||
string_literal.parse().map(NameAttribute)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A helper type which parses a name via a literal string
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct NameLitStr(pub Ident);
|
||||||
|
|
||||||
|
impl Parse for NameLitStr {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let string_literal: LitStr = input.parse()?;
|
||||||
|
if let Ok(ident) = string_literal.parse() {
|
||||||
|
Ok(NameLitStr(ident))
|
||||||
|
} else {
|
||||||
|
bail_spanned!(string_literal.span() => "expected a single identifier in double quotes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for NameLitStr {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
self.0.to_tokens(tokens)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ExtendsAttribute = KeywordAttribute<kw::extends, Path>;
|
||||||
|
pub type FreelistAttribute = KeywordAttribute<kw::freelist, Box<Expr>>;
|
||||||
|
pub type ModuleAttribute = KeywordAttribute<kw::module, LitStr>;
|
||||||
|
pub type NameAttribute = KeywordAttribute<kw::name, NameLitStr>;
|
||||||
|
pub type TextSignatureAttribute = KeywordAttribute<kw::text_signature, LitStr>;
|
||||||
|
|
||||||
|
impl<K: Parse + std::fmt::Debug, V: Parse> Parse for KeywordAttribute<K, V> {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let kw: K = input.parse()?;
|
||||||
|
let _: Token![=] = input.parse()?;
|
||||||
|
let value = input.parse()?;
|
||||||
|
Ok(KeywordAttribute { kw, value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: ToTokens, V: ToTokens> ToTokens for KeywordAttribute<K, V> {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
self.kw.to_tokens(tokens);
|
||||||
|
Token![=](self.kw.span()).to_tokens(tokens);
|
||||||
|
self.value.to_tokens(tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type FromPyWithAttribute = KeywordAttribute<kw::from_py_with, LitStrValue<ExprPath>>;
|
||||||
|
|
||||||
/// For specifying the path to the pyo3 crate.
|
/// For specifying the path to the pyo3 crate.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
pub type CrateAttribute = KeywordAttribute<Token![crate], LitStrValue<Path>>;
|
||||||
pub struct CrateAttribute(pub Path);
|
|
||||||
|
|
||||||
impl Parse for CrateAttribute {
|
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
|
||||||
let _: Token![crate] = input.parse()?;
|
|
||||||
let _: Token![=] = input.parse()?;
|
|
||||||
let string_literal: LitStr = input.parse()?;
|
|
||||||
string_literal.parse().map(CrateAttribute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct TextSignatureAttribute {
|
|
||||||
pub kw: kw::text_signature,
|
|
||||||
pub eq_token: Token![=],
|
|
||||||
pub lit: LitStr,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for TextSignatureAttribute {
|
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
|
||||||
Ok(TextSignatureAttribute {
|
|
||||||
kw: input.parse()?,
|
|
||||||
eq_token: input.parse()?,
|
|
||||||
lit: input.parse()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_pyo3_options<T: Parse>(attr: &syn::Attribute) -> Result<Option<Punctuated<T, Comma>>> {
|
pub fn get_pyo3_options<T: Parse>(attr: &syn::Attribute) -> Result<Option<Punctuated<T, Comma>>> {
|
||||||
if is_attribute_ident(attr, "pyo3") {
|
if is_attribute_ident(attr, "pyo3") {
|
||||||
|
|
|
@ -252,7 +252,9 @@ impl<'a> Container<'a> {
|
||||||
None => quote!(
|
None => quote!(
|
||||||
obj.get_item(#index)?.extract()
|
obj.get_item(#index)?.extract()
|
||||||
),
|
),
|
||||||
Some(FromPyWithAttribute(expr_path)) => quote! (
|
Some(FromPyWithAttribute {
|
||||||
|
value: expr_path, ..
|
||||||
|
}) => quote! (
|
||||||
#expr_path(obj.get_item(#index)?)
|
#expr_path(obj.get_item(#index)?)
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
@ -308,7 +310,9 @@ impl<'a> Container<'a> {
|
||||||
new_err.set_cause(py, ::std::option::Option::Some(inner));
|
new_err.set_cause(py, ::std::option::Option::Some(inner));
|
||||||
new_err
|
new_err
|
||||||
})?),
|
})?),
|
||||||
Some(FromPyWithAttribute(expr_path)) => quote! (
|
Some(FromPyWithAttribute {
|
||||||
|
value: expr_path, ..
|
||||||
|
}) => quote! (
|
||||||
#expr_path(#get_field).map_err(|inner| {
|
#expr_path(#get_field).map_err(|inner| {
|
||||||
let py = _pyo3::PyNativeType::py(obj);
|
let py = _pyo3::PyNativeType::py(obj);
|
||||||
let new_err = _pyo3::exceptions::PyTypeError::new_err(#conversion_error_msg);
|
let new_err = _pyo3::exceptions::PyTypeError::new_err(#conversion_error_msg);
|
||||||
|
@ -388,7 +392,7 @@ impl ContainerOptions {
|
||||||
ContainerPyO3Attribute::Crate(path) => {
|
ContainerPyO3Attribute::Crate(path) => {
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
options.krate.is_none(),
|
options.krate.is_none(),
|
||||||
path.0.span() => "`crate` may only be provided once"
|
path.span() => "`crate` may only be provided once"
|
||||||
);
|
);
|
||||||
options.krate = Some(path);
|
options.krate = Some(path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub struct ConstSpec {
|
||||||
impl ConstSpec {
|
impl ConstSpec {
|
||||||
pub fn python_name(&self) -> Cow<Ident> {
|
pub fn python_name(&self) -> Cow<Ident> {
|
||||||
if let Some(name) = &self.attributes.name {
|
if let Some(name) = &self.attributes.name {
|
||||||
Cow::Borrowed(&name.0)
|
Cow::Borrowed(&name.value.0)
|
||||||
} else {
|
} else {
|
||||||
Cow::Owned(self.rust_ident.unraw())
|
Cow::Owned(self.rust_ident.unraw())
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ impl ConstAttributes {
|
||||||
fn set_name(&mut self, name: NameAttribute) -> Result<()> {
|
fn set_name(&mut self, name: NameAttribute) -> Result<()> {
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
self.name.is_none(),
|
self.name.is_none(),
|
||||||
name.0.span() => "`name` may only be specified once"
|
name.span() => "`name` may only be specified once"
|
||||||
);
|
);
|
||||||
self.name = Some(name);
|
self.name = Some(name);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -14,7 +14,7 @@ use syn::ext::IdentExt;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::Result;
|
use syn::Result;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct FnArg<'a> {
|
pub struct FnArg<'a> {
|
||||||
pub name: &'a syn::Ident,
|
pub name: &'a syn::Ident,
|
||||||
pub by_ref: &'a Option<syn::token::Ref>,
|
pub by_ref: &'a Option<syn::token::Ref>,
|
||||||
|
@ -273,7 +273,7 @@ impl<'a> FnSpec<'a> {
|
||||||
ty: fn_type_attr,
|
ty: fn_type_attr,
|
||||||
args: fn_attrs,
|
args: fn_attrs,
|
||||||
mut python_name,
|
mut python_name,
|
||||||
} = parse_method_attributes(meth_attrs, name.map(|name| name.0), &mut deprecations)?;
|
} = parse_method_attributes(meth_attrs, name.map(|name| name.value.0), &mut deprecations)?;
|
||||||
|
|
||||||
let (fn_type, skip_first_arg, fixed_convention) =
|
let (fn_type, skip_first_arg, fixed_convention) =
|
||||||
Self::parse_fn_type(sig, fn_type_attr, &mut python_name)?;
|
Self::parse_fn_type(sig, fn_type_attr, &mut python_name)?;
|
||||||
|
|
|
@ -31,7 +31,7 @@ impl PyModuleOptions {
|
||||||
|
|
||||||
for option in take_pyo3_options(attrs)? {
|
for option in take_pyo3_options(attrs)? {
|
||||||
match option {
|
match option {
|
||||||
PyModulePyO3Option::Name(name) => options.set_name(name.0)?,
|
PyModulePyO3Option::Name(name) => options.set_name(name.value.0)?,
|
||||||
PyModulePyO3Option::Crate(path) => options.set_crate(path)?,
|
PyModulePyO3Option::Crate(path) => options.set_crate(path)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ impl PyModuleOptions {
|
||||||
fn set_crate(&mut self, path: CrateAttribute) -> Result<()> {
|
fn set_crate(&mut self, path: CrateAttribute) -> Result<()> {
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
self.krate.is_none(),
|
self.krate.is_none(),
|
||||||
path.0.span() => "`crate` may only be specified once"
|
path.span() => "`crate` may only be specified once"
|
||||||
);
|
);
|
||||||
|
|
||||||
self.krate = Some(path);
|
self.krate = Some(path);
|
||||||
|
|
|
@ -231,7 +231,9 @@ fn impl_arg_param(
|
||||||
let arg_value = quote_arg_span!(#args_array[#option_pos]);
|
let arg_value = quote_arg_span!(#args_array[#option_pos]);
|
||||||
*option_pos += 1;
|
*option_pos += 1;
|
||||||
|
|
||||||
let arg_value_or_default = if let Some(FromPyWithAttribute(expr_path)) = &arg.attrs.from_py_with
|
let arg_value_or_default = if let Some(FromPyWithAttribute {
|
||||||
|
value: expr_path, ..
|
||||||
|
}) = &arg.attrs.from_py_with
|
||||||
{
|
{
|
||||||
match (spec.default_value(name), arg.optional.is_some()) {
|
match (spec.default_value(name), arg.optional.is_some()) {
|
||||||
(Some(default), true) if default.to_string() != "None" => {
|
(Some(default), true) if default.to_string() != "None" => {
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||||
|
|
||||||
use crate::attributes::{
|
use crate::attributes::{
|
||||||
self, take_pyo3_options, CrateAttribute, NameAttribute, TextSignatureAttribute,
|
self, kw, take_pyo3_options, CrateAttribute, ExtendsAttribute, FreelistAttribute,
|
||||||
|
ModuleAttribute, NameAttribute, NameLitStr, TextSignatureAttribute,
|
||||||
};
|
};
|
||||||
use crate::deprecations::{Deprecation, Deprecations};
|
use crate::deprecations::{Deprecation, Deprecations};
|
||||||
use crate::konst::{ConstAttributes, ConstSpec};
|
use crate::konst::{ConstAttributes, ConstSpec};
|
||||||
use crate::pyimpl::{gen_default_items, gen_py_const, PyClassMethodsType};
|
use crate::pyimpl::{gen_default_items, gen_py_const, PyClassMethodsType};
|
||||||
use crate::pymethod::{impl_py_getter_def, impl_py_setter_def, PropertyType};
|
use crate::pymethod::{impl_py_getter_def, impl_py_setter_def, PropertyType};
|
||||||
use crate::utils::{self, get_pyo3_crate, unwrap_group, PythonDoc};
|
use crate::utils::{self, get_pyo3_crate, PythonDoc};
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::ext::IdentExt;
|
use syn::ext::IdentExt;
|
||||||
use syn::parse::{Parse, ParseStream};
|
use syn::parse::{Parse, ParseStream};
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::{parse_quote, spanned::Spanned, Expr, Result, Token}; //unraw
|
use syn::{parse_quote, spanned::Spanned, Result, Token};
|
||||||
|
|
||||||
/// If the class is derived from a Rust `struct` or `enum`.
|
/// If the class is derived from a Rust `struct` or `enum`.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -24,27 +25,18 @@ pub enum PyClassKind {
|
||||||
|
|
||||||
/// The parsed arguments of the pyclass macro
|
/// The parsed arguments of the pyclass macro
|
||||||
pub struct PyClassArgs {
|
pub struct PyClassArgs {
|
||||||
pub freelist: Option<syn::Expr>,
|
|
||||||
pub name: Option<syn::Ident>,
|
|
||||||
pub base: syn::TypePath,
|
|
||||||
pub has_dict: bool,
|
|
||||||
pub has_weaklist: bool,
|
|
||||||
pub is_basetype: bool,
|
|
||||||
pub has_extends: bool,
|
|
||||||
pub has_unsendable: bool,
|
|
||||||
pub module: Option<syn::LitStr>,
|
|
||||||
pub class_kind: PyClassKind,
|
pub class_kind: PyClassKind,
|
||||||
|
pub options: PyClassPyO3Options,
|
||||||
pub deprecations: Deprecations,
|
pub deprecations: Deprecations,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PyClassArgs {
|
impl PyClassArgs {
|
||||||
fn parse(input: ParseStream, kind: PyClassKind) -> Result<Self> {
|
fn parse(input: ParseStream, kind: PyClassKind) -> Result<Self> {
|
||||||
let mut slf = PyClassArgs::new(kind);
|
Ok(PyClassArgs {
|
||||||
let vars = Punctuated::<Expr, Token![,]>::parse_terminated(input)?;
|
class_kind: kind,
|
||||||
for expr in vars {
|
options: PyClassPyO3Options::parse(input)?,
|
||||||
slf.add_expr(&expr)?;
|
deprecations: Deprecations::new(),
|
||||||
}
|
})
|
||||||
Ok(slf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_stuct_args(input: ParseStream) -> syn::Result<Self> {
|
pub fn parse_stuct_args(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
@ -54,155 +46,64 @@ impl PyClassArgs {
|
||||||
pub fn parse_enum_args(input: ParseStream) -> syn::Result<Self> {
|
pub fn parse_enum_args(input: ParseStream) -> syn::Result<Self> {
|
||||||
Self::parse(input, PyClassKind::Enum)
|
Self::parse(input, PyClassKind::Enum)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(class_kind: PyClassKind) -> Self {
|
|
||||||
PyClassArgs {
|
|
||||||
freelist: None,
|
|
||||||
name: None,
|
|
||||||
module: None,
|
|
||||||
base: parse_quote! { _pyo3::PyAny },
|
|
||||||
has_dict: false,
|
|
||||||
has_weaklist: false,
|
|
||||||
is_basetype: false,
|
|
||||||
has_extends: false,
|
|
||||||
has_unsendable: false,
|
|
||||||
class_kind,
|
|
||||||
deprecations: Deprecations::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a single expression from the comma separated list in the attribute, which is
|
|
||||||
/// either a single word or an assignment expression
|
|
||||||
fn add_expr(&mut self, expr: &Expr) -> Result<()> {
|
|
||||||
match expr {
|
|
||||||
syn::Expr::Path(exp) if exp.path.segments.len() == 1 => self.add_path(exp),
|
|
||||||
syn::Expr::Assign(assign) => self.add_assign(assign),
|
|
||||||
_ => bail_spanned!(expr.span() => "failed to parse arguments"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Match a key/value flag
|
|
||||||
fn add_assign(&mut self, assign: &syn::ExprAssign) -> syn::Result<()> {
|
|
||||||
let syn::ExprAssign { left, right, .. } = assign;
|
|
||||||
let key = match &**left {
|
|
||||||
syn::Expr::Path(exp) if exp.path.segments.len() == 1 => {
|
|
||||||
exp.path.segments.first().unwrap().ident.to_string()
|
|
||||||
}
|
|
||||||
_ => bail_spanned!(assign.span() => "failed to parse arguments"),
|
|
||||||
};
|
|
||||||
|
|
||||||
macro_rules! expected {
|
|
||||||
($expected: literal) => {
|
|
||||||
expected!($expected, right.span())
|
|
||||||
};
|
|
||||||
($expected: literal, $span: expr) => {
|
|
||||||
bail_spanned!($span => concat!("expected ", $expected))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
match key.as_str() {
|
|
||||||
"freelist" => {
|
|
||||||
// We allow arbitrary expressions here so you can e.g. use `8*64`
|
|
||||||
self.freelist = Some(syn::Expr::clone(right));
|
|
||||||
}
|
|
||||||
"name" => match unwrap_group(&**right) {
|
|
||||||
syn::Expr::Lit(syn::ExprLit {
|
|
||||||
lit: syn::Lit::Str(lit),
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
self.name = Some(lit.parse().map_err(|_| {
|
|
||||||
err_spanned!(
|
|
||||||
lit.span() => "expected a single identifier in double-quotes")
|
|
||||||
})?);
|
|
||||||
}
|
|
||||||
syn::Expr::Path(exp) if exp.path.segments.len() == 1 => {
|
|
||||||
bail_spanned!(
|
|
||||||
exp.span() => format!(
|
|
||||||
"since PyO3 0.13 a pyclass name should be in double-quotes, \
|
|
||||||
e.g. \"{}\"",
|
|
||||||
exp.path.get_ident().expect("path has 1 segment")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => expected!("type name (e.g. \"Name\")"),
|
|
||||||
},
|
|
||||||
"extends" => match unwrap_group(&**right) {
|
|
||||||
syn::Expr::Path(exp) => {
|
|
||||||
if self.class_kind == PyClassKind::Enum {
|
|
||||||
bail_spanned!( assign.span() => "enums cannot extend from other classes" );
|
|
||||||
}
|
|
||||||
self.base = syn::TypePath {
|
|
||||||
path: exp.path.clone(),
|
|
||||||
qself: None,
|
|
||||||
};
|
|
||||||
self.has_extends = true;
|
|
||||||
}
|
|
||||||
_ => expected!("type path (e.g., my_mod::BaseClass)"),
|
|
||||||
},
|
|
||||||
"module" => match unwrap_group(&**right) {
|
|
||||||
syn::Expr::Lit(syn::ExprLit {
|
|
||||||
lit: syn::Lit::Str(lit),
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
self.module = Some(lit.clone());
|
|
||||||
}
|
|
||||||
_ => expected!(r#"string literal (e.g., "my_mod")"#),
|
|
||||||
},
|
|
||||||
_ => expected!("one of freelist/name/extends/module", left.span()),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Match a single flag
|
|
||||||
fn add_path(&mut self, exp: &syn::ExprPath) -> syn::Result<()> {
|
|
||||||
let flag = exp.path.segments.first().unwrap().ident.to_string();
|
|
||||||
match flag.as_str() {
|
|
||||||
"gc" => self
|
|
||||||
.deprecations
|
|
||||||
.push(Deprecation::PyClassGcOption, exp.span()),
|
|
||||||
"weakref" => {
|
|
||||||
self.has_weaklist = true;
|
|
||||||
}
|
|
||||||
"subclass" => {
|
|
||||||
if self.class_kind == PyClassKind::Enum {
|
|
||||||
bail_spanned!(exp.span() => "enums can't be inherited by other classes");
|
|
||||||
}
|
|
||||||
self.is_basetype = true;
|
|
||||||
}
|
|
||||||
"dict" => {
|
|
||||||
self.has_dict = true;
|
|
||||||
}
|
|
||||||
"unsendable" => {
|
|
||||||
self.has_unsendable = true;
|
|
||||||
}
|
|
||||||
_ => bail_spanned!(
|
|
||||||
exp.path.span() => "expected one of gc/weakref/subclass/dict/unsendable"
|
|
||||||
),
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PyClassPyO3Options {
|
pub struct PyClassPyO3Options {
|
||||||
pub text_signature: Option<TextSignatureAttribute>,
|
|
||||||
pub deprecations: Deprecations,
|
|
||||||
pub krate: Option<CrateAttribute>,
|
pub krate: Option<CrateAttribute>,
|
||||||
|
pub dict: Option<kw::dict>,
|
||||||
|
pub extends: Option<ExtendsAttribute>,
|
||||||
|
pub freelist: Option<FreelistAttribute>,
|
||||||
|
pub module: Option<ModuleAttribute>,
|
||||||
|
pub name: Option<NameAttribute>,
|
||||||
|
pub subclass: Option<kw::subclass>,
|
||||||
|
pub text_signature: Option<TextSignatureAttribute>,
|
||||||
|
pub unsendable: Option<kw::unsendable>,
|
||||||
|
pub weakref: Option<kw::weakref>,
|
||||||
|
|
||||||
|
pub deprecations: Deprecations,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PyClassPyO3Option {
|
enum PyClassPyO3Option {
|
||||||
TextSignature(TextSignatureAttribute),
|
|
||||||
Crate(CrateAttribute),
|
Crate(CrateAttribute),
|
||||||
|
Dict(kw::dict),
|
||||||
|
Extends(ExtendsAttribute),
|
||||||
|
Freelist(FreelistAttribute),
|
||||||
|
Module(ModuleAttribute),
|
||||||
|
Name(NameAttribute),
|
||||||
|
Subclass(kw::subclass),
|
||||||
|
TextSignature(TextSignatureAttribute),
|
||||||
|
Unsendable(kw::unsendable),
|
||||||
|
Weakref(kw::weakref),
|
||||||
|
|
||||||
|
DeprecatedGC(kw::gc),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for PyClassPyO3Option {
|
impl Parse for PyClassPyO3Option {
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
let lookahead = input.lookahead1();
|
let lookahead = input.lookahead1();
|
||||||
if lookahead.peek(attributes::kw::text_signature) {
|
if lookahead.peek(Token![crate]) {
|
||||||
input.parse().map(PyClassPyO3Option::TextSignature)
|
|
||||||
} else if lookahead.peek(Token![crate]) {
|
|
||||||
input.parse().map(PyClassPyO3Option::Crate)
|
input.parse().map(PyClassPyO3Option::Crate)
|
||||||
|
} else if lookahead.peek(kw::dict) {
|
||||||
|
input.parse().map(PyClassPyO3Option::Dict)
|
||||||
|
} else if lookahead.peek(kw::extends) {
|
||||||
|
input.parse().map(PyClassPyO3Option::Extends)
|
||||||
|
} else if lookahead.peek(attributes::kw::freelist) {
|
||||||
|
input.parse().map(PyClassPyO3Option::Freelist)
|
||||||
|
} else if lookahead.peek(attributes::kw::module) {
|
||||||
|
input.parse().map(PyClassPyO3Option::Module)
|
||||||
|
} else if lookahead.peek(kw::name) {
|
||||||
|
input.parse().map(PyClassPyO3Option::Name)
|
||||||
|
} else if lookahead.peek(attributes::kw::subclass) {
|
||||||
|
input.parse().map(PyClassPyO3Option::Subclass)
|
||||||
|
} else if lookahead.peek(attributes::kw::text_signature) {
|
||||||
|
input.parse().map(PyClassPyO3Option::TextSignature)
|
||||||
|
} else if lookahead.peek(attributes::kw::unsendable) {
|
||||||
|
input.parse().map(PyClassPyO3Option::Unsendable)
|
||||||
|
} else if lookahead.peek(attributes::kw::weakref) {
|
||||||
|
input.parse().map(PyClassPyO3Option::Weakref)
|
||||||
|
} else if lookahead.peek(attributes::kw::gc) {
|
||||||
|
input.parse().map(PyClassPyO3Option::DeprecatedGC)
|
||||||
} else {
|
} else {
|
||||||
Err(lookahead.error())
|
Err(lookahead.error())
|
||||||
}
|
}
|
||||||
|
@ -210,57 +111,69 @@ impl Parse for PyClassPyO3Option {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PyClassPyO3Options {
|
impl PyClassPyO3Options {
|
||||||
pub fn take_pyo3_options(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let mut options: PyClassPyO3Options = Default::default();
|
let mut options: PyClassPyO3Options = Default::default();
|
||||||
for option in take_pyo3_options(attrs)? {
|
|
||||||
match option {
|
for option in Punctuated::<PyClassPyO3Option, syn::Token![,]>::parse_terminated(input)? {
|
||||||
PyClassPyO3Option::TextSignature(text_signature) => {
|
options.set_option(option)?;
|
||||||
options.set_text_signature(text_signature)?;
|
|
||||||
}
|
|
||||||
PyClassPyO3Option::Crate(path) => {
|
|
||||||
options.set_crate(path)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(options)
|
Ok(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_text_signature(
|
pub fn take_pyo3_options(&mut self, attrs: &mut Vec<syn::Attribute>) -> syn::Result<()> {
|
||||||
&mut self,
|
take_pyo3_options(attrs)?
|
||||||
text_signature: TextSignatureAttribute,
|
.into_iter()
|
||||||
) -> syn::Result<()> {
|
.try_for_each(|option| self.set_option(option))
|
||||||
ensure_spanned!(
|
|
||||||
self.text_signature.is_none(),
|
|
||||||
text_signature.kw.span() => "`text_signature` may only be specified once"
|
|
||||||
);
|
|
||||||
self.text_signature = Some(text_signature);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_crate(&mut self, path: CrateAttribute) -> syn::Result<()> {
|
fn set_option(&mut self, option: PyClassPyO3Option) -> syn::Result<()> {
|
||||||
ensure_spanned!(
|
macro_rules! set_option {
|
||||||
self.krate.is_none(),
|
($key:ident) => {
|
||||||
path.0.span() => "`text_signature` may only be specified once"
|
{
|
||||||
);
|
ensure_spanned!(
|
||||||
self.krate = Some(path);
|
self.$key.is_none(),
|
||||||
|
$key.span() => concat!("`", stringify!($key), "` may only be specified once")
|
||||||
|
);
|
||||||
|
self.$key = Some($key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
match option {
|
||||||
|
PyClassPyO3Option::Crate(krate) => set_option!(krate),
|
||||||
|
PyClassPyO3Option::Dict(dict) => set_option!(dict),
|
||||||
|
PyClassPyO3Option::Extends(extends) => set_option!(extends),
|
||||||
|
PyClassPyO3Option::Freelist(freelist) => set_option!(freelist),
|
||||||
|
PyClassPyO3Option::Module(module) => set_option!(module),
|
||||||
|
PyClassPyO3Option::Name(name) => set_option!(name),
|
||||||
|
PyClassPyO3Option::Subclass(subclass) => set_option!(subclass),
|
||||||
|
PyClassPyO3Option::TextSignature(text_signature) => set_option!(text_signature),
|
||||||
|
PyClassPyO3Option::Unsendable(unsendable) => set_option!(unsendable),
|
||||||
|
PyClassPyO3Option::Weakref(weakref) => set_option!(weakref),
|
||||||
|
|
||||||
|
PyClassPyO3Option::DeprecatedGC(gc) => self
|
||||||
|
.deprecations
|
||||||
|
.push(Deprecation::PyClassGcOption, gc.span()),
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_py_class(
|
pub fn build_py_class(
|
||||||
class: &mut syn::ItemStruct,
|
class: &mut syn::ItemStruct,
|
||||||
args: &PyClassArgs,
|
mut args: PyClassArgs,
|
||||||
methods_type: PyClassMethodsType,
|
methods_type: PyClassMethodsType,
|
||||||
) -> syn::Result<TokenStream> {
|
) -> syn::Result<TokenStream> {
|
||||||
let options = PyClassPyO3Options::take_pyo3_options(&mut class.attrs)?;
|
args.options.take_pyo3_options(&mut class.attrs)?;
|
||||||
let doc = utils::get_doc(
|
let doc = utils::get_doc(
|
||||||
&class.attrs,
|
&class.attrs,
|
||||||
options
|
args.options
|
||||||
.text_signature
|
.text_signature
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|attr| (get_class_python_name(&class.ident, args), attr)),
|
.map(|attr| (get_class_python_name(&class.ident, &args), attr)),
|
||||||
);
|
);
|
||||||
let krate = get_pyo3_crate(&options.krate);
|
let krate = get_pyo3_crate(&args.options.krate);
|
||||||
|
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
class.generics.params.is_empty(),
|
class.generics.params.is_empty(),
|
||||||
|
@ -290,15 +203,7 @@ pub fn build_py_class(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
impl_class(
|
impl_class(&class.ident, &args, doc, field_options, methods_type, krate)
|
||||||
&class.ident,
|
|
||||||
args,
|
|
||||||
doc,
|
|
||||||
field_options,
|
|
||||||
methods_type,
|
|
||||||
options.deprecations,
|
|
||||||
krate,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `#[pyo3()]` options for pyclass fields
|
/// `#[pyo3()]` options for pyclass fields
|
||||||
|
@ -356,7 +261,7 @@ impl FieldPyO3Options {
|
||||||
FieldPyO3Option::Name(name) => {
|
FieldPyO3Option::Name(name) => {
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
options.name.is_none(),
|
options.name.is_none(),
|
||||||
name.0.span() => "`name` may only be specified once"
|
name.span() => "`name` may only be specified once"
|
||||||
);
|
);
|
||||||
options.name = Some(name);
|
options.name = Some(name);
|
||||||
}
|
}
|
||||||
|
@ -367,24 +272,27 @@ impl FieldPyO3Options {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_class_python_name<'a>(cls: &'a syn::Ident, attr: &'a PyClassArgs) -> &'a syn::Ident {
|
fn get_class_python_name<'a>(cls: &'a syn::Ident, args: &'a PyClassArgs) -> &'a syn::Ident {
|
||||||
attr.name.as_ref().unwrap_or(cls)
|
args.options
|
||||||
|
.name
|
||||||
|
.as_ref()
|
||||||
|
.map(|name_attr| &name_attr.value.0)
|
||||||
|
.unwrap_or(cls)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_class(
|
fn impl_class(
|
||||||
cls: &syn::Ident,
|
cls: &syn::Ident,
|
||||||
attr: &PyClassArgs,
|
args: &PyClassArgs,
|
||||||
doc: PythonDoc,
|
doc: PythonDoc,
|
||||||
field_options: Vec<(&syn::Field, FieldPyO3Options)>,
|
field_options: Vec<(&syn::Field, FieldPyO3Options)>,
|
||||||
methods_type: PyClassMethodsType,
|
methods_type: PyClassMethodsType,
|
||||||
deprecations: Deprecations,
|
|
||||||
krate: syn::Path,
|
krate: syn::Path,
|
||||||
) -> syn::Result<TokenStream> {
|
) -> syn::Result<TokenStream> {
|
||||||
let pytypeinfo_impl = impl_pytypeinfo(cls, attr, Some(&deprecations));
|
let pytypeinfo_impl = impl_pytypeinfo(cls, args, Some(&args.options.deprecations));
|
||||||
|
|
||||||
let py_class_impl = PyClassImplsBuilder::new(
|
let py_class_impl = PyClassImplsBuilder::new(
|
||||||
cls,
|
cls,
|
||||||
attr,
|
args,
|
||||||
methods_type,
|
methods_type,
|
||||||
descriptors_to_items(cls, field_options)?,
|
descriptors_to_items(cls, field_options)?,
|
||||||
vec![],
|
vec![],
|
||||||
|
@ -458,23 +366,28 @@ impl<'a> PyClassEnum<'a> {
|
||||||
|
|
||||||
pub fn build_py_enum(
|
pub fn build_py_enum(
|
||||||
enum_: &mut syn::ItemEnum,
|
enum_: &mut syn::ItemEnum,
|
||||||
args: &PyClassArgs,
|
mut args: PyClassArgs,
|
||||||
method_type: PyClassMethodsType,
|
method_type: PyClassMethodsType,
|
||||||
) -> syn::Result<TokenStream> {
|
) -> syn::Result<TokenStream> {
|
||||||
let options = PyClassPyO3Options::take_pyo3_options(&mut enum_.attrs)?;
|
args.options.take_pyo3_options(&mut enum_.attrs)?;
|
||||||
|
|
||||||
if enum_.variants.is_empty() {
|
if let Some(extends) = &args.options.extends {
|
||||||
bail_spanned!(enum_.brace_token.span => "Empty enums can't be #[pyclass].");
|
bail_spanned!(extends.span() => "enums can't extend from other classes");
|
||||||
|
} else if let Some(subclass) = &args.options.subclass {
|
||||||
|
bail_spanned!(subclass.span() => "enums can't be inherited by other classes");
|
||||||
|
} else if enum_.variants.is_empty() {
|
||||||
|
bail_spanned!(enum_.brace_token.span => "#[pyclass] can't be used on enums without any variants");
|
||||||
}
|
}
|
||||||
|
|
||||||
let doc = utils::get_doc(
|
let doc = utils::get_doc(
|
||||||
&enum_.attrs,
|
&enum_.attrs,
|
||||||
options
|
args.options
|
||||||
.text_signature
|
.text_signature
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|attr| (get_class_python_name(&enum_.ident, args), attr)),
|
.map(|attr| (get_class_python_name(&enum_.ident, &args), attr)),
|
||||||
);
|
);
|
||||||
let enum_ = PyClassEnum::new(enum_)?;
|
let enum_ = PyClassEnum::new(enum_)?;
|
||||||
Ok(impl_enum(enum_, args, doc, method_type, options))
|
Ok(impl_enum(enum_, &args, doc, method_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_enum(
|
fn impl_enum(
|
||||||
|
@ -482,9 +395,8 @@ fn impl_enum(
|
||||||
args: &PyClassArgs,
|
args: &PyClassArgs,
|
||||||
doc: PythonDoc,
|
doc: PythonDoc,
|
||||||
methods_type: PyClassMethodsType,
|
methods_type: PyClassMethodsType,
|
||||||
options: PyClassPyO3Options,
|
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let krate = get_pyo3_crate(&options.krate);
|
let krate = get_pyo3_crate(&args.options.krate);
|
||||||
impl_enum_class(enum_, args, doc, methods_type, krate)
|
impl_enum_class(enum_, args, doc, methods_type, krate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,7 +525,10 @@ fn enum_default_methods<'a>(
|
||||||
rust_ident: ident.clone(),
|
rust_ident: ident.clone(),
|
||||||
attributes: ConstAttributes {
|
attributes: ConstAttributes {
|
||||||
is_class_attr: true,
|
is_class_attr: true,
|
||||||
name: Some(NameAttribute(ident.clone())),
|
name: Some(NameAttribute {
|
||||||
|
kw: syn::parse_quote! { name },
|
||||||
|
value: NameLitStr(ident.clone()),
|
||||||
|
}),
|
||||||
deprecations: Default::default(),
|
deprecations: Default::default(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -649,7 +564,7 @@ fn descriptors_to_items(
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.flat_map(|(field_index, (field, options))| {
|
.flat_map(|(field_index, (field, options))| {
|
||||||
let name_err = if options.name.is_some() && !options.get && !options.set {
|
let name_err = if options.name.is_some() && !options.get && !options.set {
|
||||||
Some(Err(err_spanned!(options.name.as_ref().unwrap().0.span() => "`name` is useless without `get` or `set`")))
|
Some(Err(err_spanned!(options.name.as_ref().unwrap().span() => "`name` is useless without `get` or `set`")))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -686,8 +601,8 @@ fn impl_pytypeinfo(
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let cls_name = get_class_python_name(cls, attr).to_string();
|
let cls_name = get_class_python_name(cls, attr).to_string();
|
||||||
|
|
||||||
let module = if let Some(m) = &attr.module {
|
let module = if let Some(ModuleAttribute { value, .. }) = &attr.options.module {
|
||||||
quote! { ::core::option::Option::Some(#m) }
|
quote! { ::core::option::Option::Some(#value) }
|
||||||
} else {
|
} else {
|
||||||
quote! { ::core::option::Option::None }
|
quote! { ::core::option::Option::None }
|
||||||
};
|
};
|
||||||
|
@ -765,20 +680,20 @@ impl<'a> PyClassImplsBuilder<'a> {
|
||||||
fn impl_pyclass(&self) -> TokenStream {
|
fn impl_pyclass(&self) -> TokenStream {
|
||||||
let cls = self.cls;
|
let cls = self.cls;
|
||||||
let attr = self.attr;
|
let attr = self.attr;
|
||||||
let dict = if attr.has_dict {
|
let dict = if attr.options.dict.is_some() {
|
||||||
quote! { _pyo3::impl_::pyclass::PyClassDictSlot }
|
quote! { _pyo3::impl_::pyclass::PyClassDictSlot }
|
||||||
} else {
|
} else {
|
||||||
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
|
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
|
||||||
};
|
};
|
||||||
|
|
||||||
// insert space for weak ref
|
// insert space for weak ref
|
||||||
let weakref = if attr.has_weaklist {
|
let weakref = if attr.options.weakref.is_some() {
|
||||||
quote! { _pyo3::impl_::pyclass::PyClassWeakRefSlot }
|
quote! { _pyo3::impl_::pyclass::PyClassWeakRefSlot }
|
||||||
} else {
|
} else {
|
||||||
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
|
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
|
||||||
};
|
};
|
||||||
|
|
||||||
let base_nativetype = if attr.has_extends {
|
let base_nativetype = if attr.options.extends.is_some() {
|
||||||
quote! { <Self::BaseType as _pyo3::impl_::pyclass::PyClassBaseType>::BaseNativeType }
|
quote! { <Self::BaseType as _pyo3::impl_::pyclass::PyClassBaseType>::BaseNativeType }
|
||||||
} else {
|
} else {
|
||||||
quote! { _pyo3::PyAny }
|
quote! { _pyo3::PyAny }
|
||||||
|
@ -810,7 +725,7 @@ impl<'a> PyClassImplsBuilder<'a> {
|
||||||
let cls = self.cls;
|
let cls = self.cls;
|
||||||
let attr = self.attr;
|
let attr = self.attr;
|
||||||
// If #cls is not extended type, we allow Self->PyObject conversion
|
// If #cls is not extended type, we allow Self->PyObject conversion
|
||||||
if !attr.has_extends {
|
if attr.options.extends.is_none() {
|
||||||
quote! {
|
quote! {
|
||||||
impl _pyo3::IntoPy<_pyo3::PyObject> for #cls {
|
impl _pyo3::IntoPy<_pyo3::PyObject> for #cls {
|
||||||
fn into_py(self, py: _pyo3::Python) -> _pyo3::PyObject {
|
fn into_py(self, py: _pyo3::Python) -> _pyo3::PyObject {
|
||||||
|
@ -825,11 +740,17 @@ impl<'a> PyClassImplsBuilder<'a> {
|
||||||
fn impl_pyclassimpl(&self) -> TokenStream {
|
fn impl_pyclassimpl(&self) -> TokenStream {
|
||||||
let cls = self.cls;
|
let cls = self.cls;
|
||||||
let doc = self.doc.as_ref().map_or(quote! {"\0"}, |doc| quote! {#doc});
|
let doc = self.doc.as_ref().map_or(quote! {"\0"}, |doc| quote! {#doc});
|
||||||
let is_basetype = self.attr.is_basetype;
|
let is_basetype = self.attr.options.subclass.is_some();
|
||||||
let base = &self.attr.base;
|
let base = self
|
||||||
let is_subclass = self.attr.has_extends;
|
.attr
|
||||||
|
.options
|
||||||
|
.extends
|
||||||
|
.as_ref()
|
||||||
|
.map(|extends_attr| extends_attr.value.clone())
|
||||||
|
.unwrap_or_else(|| parse_quote! { _pyo3::PyAny });
|
||||||
|
let is_subclass = self.attr.options.extends.is_some();
|
||||||
|
|
||||||
let dict_offset = if self.attr.has_dict {
|
let dict_offset = if self.attr.options.dict.is_some() {
|
||||||
quote! {
|
quote! {
|
||||||
fn dict_offset() -> ::std::option::Option<_pyo3::ffi::Py_ssize_t> {
|
fn dict_offset() -> ::std::option::Option<_pyo3::ffi::Py_ssize_t> {
|
||||||
::std::option::Option::Some(_pyo3::impl_::pyclass::dict_offset::<Self>())
|
::std::option::Option::Some(_pyo3::impl_::pyclass::dict_offset::<Self>())
|
||||||
|
@ -840,7 +761,7 @@ impl<'a> PyClassImplsBuilder<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// insert space for weak ref
|
// insert space for weak ref
|
||||||
let weaklist_offset = if self.attr.has_weaklist {
|
let weaklist_offset = if self.attr.options.weakref.is_some() {
|
||||||
quote! {
|
quote! {
|
||||||
fn weaklist_offset() -> ::std::option::Option<_pyo3::ffi::Py_ssize_t> {
|
fn weaklist_offset() -> ::std::option::Option<_pyo3::ffi::Py_ssize_t> {
|
||||||
::std::option::Option::Some(_pyo3::impl_::pyclass::weaklist_offset::<Self>())
|
::std::option::Option::Some(_pyo3::impl_::pyclass::weaklist_offset::<Self>())
|
||||||
|
@ -850,9 +771,9 @@ impl<'a> PyClassImplsBuilder<'a> {
|
||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let thread_checker = if self.attr.has_unsendable {
|
let thread_checker = if self.attr.options.unsendable.is_some() {
|
||||||
quote! { _pyo3::impl_::pyclass::ThreadCheckerImpl<#cls> }
|
quote! { _pyo3::impl_::pyclass::ThreadCheckerImpl<#cls> }
|
||||||
} else if self.attr.has_extends {
|
} else if self.attr.options.extends.is_some() {
|
||||||
quote! {
|
quote! {
|
||||||
_pyo3::impl_::pyclass::ThreadCheckerInherited<#cls, <#cls as _pyo3::impl_::pyclass::PyClassImpl>::BaseType>
|
_pyo3::impl_::pyclass::ThreadCheckerInherited<#cls, <#cls as _pyo3::impl_::pyclass::PyClassImpl>::BaseType>
|
||||||
}
|
}
|
||||||
|
@ -940,7 +861,8 @@ impl<'a> PyClassImplsBuilder<'a> {
|
||||||
fn impl_freelist(&self) -> TokenStream {
|
fn impl_freelist(&self) -> TokenStream {
|
||||||
let cls = self.cls;
|
let cls = self.cls;
|
||||||
|
|
||||||
self.attr.freelist.as_ref().map_or(quote!{}, |freelist| {
|
self.attr.options.freelist.as_ref().map_or(quote!{}, |freelist| {
|
||||||
|
let freelist = &freelist.value;
|
||||||
quote! {
|
quote! {
|
||||||
impl _pyo3::impl_::pyclass::PyClassWithFreeList for #cls {
|
impl _pyo3::impl_::pyclass::PyClassWithFreeList for #cls {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -962,7 +884,7 @@ impl<'a> PyClassImplsBuilder<'a> {
|
||||||
fn freelist_slots(&self) -> Vec<TokenStream> {
|
fn freelist_slots(&self) -> Vec<TokenStream> {
|
||||||
let cls = self.cls;
|
let cls = self.cls;
|
||||||
|
|
||||||
if self.attr.freelist.is_some() {
|
if self.attr.options.freelist.is_some() {
|
||||||
vec![
|
vec![
|
||||||
quote! {
|
quote! {
|
||||||
_pyo3::ffi::PyType_Slot {
|
_pyo3::ffi::PyType_Slot {
|
||||||
|
|
|
@ -40,7 +40,7 @@ pub struct PyFunctionSignature {
|
||||||
has_kwargs: bool,
|
has_kwargs: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PyFunctionArgPyO3Attributes {
|
pub struct PyFunctionArgPyO3Attributes {
|
||||||
pub from_py_with: Option<FromPyWithAttribute>,
|
pub from_py_with: Option<FromPyWithAttribute>,
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ impl PyFunctionArgPyO3Attributes {
|
||||||
PyFunctionArgPyO3Attribute::FromPyWith(from_py_with) => {
|
PyFunctionArgPyO3Attribute::FromPyWith(from_py_with) => {
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
attributes.from_py_with.is_none(),
|
attributes.from_py_with.is_none(),
|
||||||
from_py_with.0.span() => "`from_py_with` may only be specified once per argument"
|
from_py_with.span() => "`from_py_with` may only be specified once per argument"
|
||||||
);
|
);
|
||||||
attributes.from_py_with = Some(from_py_with);
|
attributes.from_py_with = Some(from_py_with);
|
||||||
}
|
}
|
||||||
|
@ -339,7 +339,7 @@ impl PyFunctionOptions {
|
||||||
PyFunctionOption::Crate(path) => {
|
PyFunctionOption::Crate(path) => {
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
self.krate.is_none(),
|
self.krate.is_none(),
|
||||||
path.0.span() => "`crate` may only be specified once"
|
path.span() => "`crate` may only be specified once"
|
||||||
);
|
);
|
||||||
self.krate = Some(path);
|
self.krate = Some(path);
|
||||||
}
|
}
|
||||||
|
@ -351,7 +351,7 @@ impl PyFunctionOptions {
|
||||||
pub fn set_name(&mut self, name: NameAttribute) -> Result<()> {
|
pub fn set_name(&mut self, name: NameAttribute) -> Result<()> {
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
self.name.is_none(),
|
self.name.is_none(),
|
||||||
name.0.span() => "`name` may only be specified once"
|
name.span() => "`name` may only be specified once"
|
||||||
);
|
);
|
||||||
self.name = Some(name);
|
self.name = Some(name);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -377,7 +377,7 @@ pub fn impl_wrap_pyfunction(
|
||||||
|
|
||||||
let python_name = options
|
let python_name = options
|
||||||
.name
|
.name
|
||||||
.map_or_else(|| func.sig.ident.unraw(), |name| name.0);
|
.map_or_else(|| func.sig.ident.unraw(), |name| name.value.0);
|
||||||
|
|
||||||
let signature = options.signature.unwrap_or_default();
|
let signature = options.signature.unwrap_or_default();
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ impl PyImplOptions {
|
||||||
fn set_crate(&mut self, path: CrateAttribute) -> Result<()> {
|
fn set_crate(&mut self, path: CrateAttribute) -> Result<()> {
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
self.krate.is_none(),
|
self.krate.is_none(),
|
||||||
path.0.span() => "`crate` may only be specified once"
|
path.span() => "`crate` may only be specified once"
|
||||||
);
|
);
|
||||||
|
|
||||||
self.krate = Some(path);
|
self.krate = Some(path);
|
||||||
|
|
|
@ -544,7 +544,7 @@ impl PropertyType<'_> {
|
||||||
field, python_name, ..
|
field, python_name, ..
|
||||||
} => {
|
} => {
|
||||||
let name = match (python_name, &field.ident) {
|
let name = match (python_name, &field.ident) {
|
||||||
(Some(name), _) => name.0.to_string(),
|
(Some(name), _) => name.value.0.to_string(),
|
||||||
(None, Some(field_name)) => format!("{}\0", field_name.unraw()),
|
(None, Some(field_name)) => format!("{}\0", field_name.unraw()),
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
bail_spanned!(field.span() => "`get` and `set` with tuple struct fields require `name`");
|
bail_spanned!(field.span() => "`get` and `set` with tuple struct fields require `name`");
|
||||||
|
|
|
@ -77,7 +77,8 @@ pub fn get_doc(
|
||||||
syn::token::Bracket(Span::call_site()).surround(&mut tokens, |tokens| {
|
syn::token::Bracket(Span::call_site()).surround(&mut tokens, |tokens| {
|
||||||
if let Some((python_name, text_signature)) = text_signature {
|
if let Some((python_name, text_signature)) = text_signature {
|
||||||
// create special doc string lines to set `__text_signature__`
|
// create special doc string lines to set `__text_signature__`
|
||||||
let signature_lines = format!("{}{}\n--\n\n", python_name, text_signature.lit.value());
|
let signature_lines =
|
||||||
|
format!("{}{}\n--\n\n", python_name, text_signature.value.value());
|
||||||
signature_lines.to_tokens(tokens);
|
signature_lines.to_tokens(tokens);
|
||||||
comma.to_tokens(tokens);
|
comma.to_tokens(tokens);
|
||||||
}
|
}
|
||||||
|
@ -154,13 +155,6 @@ pub fn ensure_not_async_fn(sig: &syn::Signature) -> syn::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_group(mut expr: &syn::Expr) -> &syn::Expr {
|
|
||||||
while let syn::Expr::Group(g) = expr {
|
|
||||||
expr = &*g.expr;
|
|
||||||
}
|
|
||||||
expr
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwrap_ty_group(mut ty: &syn::Type) -> &syn::Type {
|
pub fn unwrap_ty_group(mut ty: &syn::Type) -> &syn::Type {
|
||||||
while let syn::Type::Group(g) = ty {
|
while let syn::Type::Group(g) = ty {
|
||||||
ty = &*g.elem;
|
ty = &*g.elem;
|
||||||
|
@ -193,6 +187,6 @@ pub(crate) fn replace_self(ty: &mut syn::Type, cls: &syn::Type) {
|
||||||
/// Extract the path to the pyo3 crate, or use the default (`::pyo3`).
|
/// Extract the path to the pyo3 crate, or use the default (`::pyo3`).
|
||||||
pub(crate) fn get_pyo3_crate(attr: &Option<CrateAttribute>) -> syn::Path {
|
pub(crate) fn get_pyo3_crate(attr: &Option<CrateAttribute>) -> syn::Path {
|
||||||
attr.as_ref()
|
attr.as_ref()
|
||||||
.map(|p| p.0.clone())
|
.map(|p| p.value.0.clone())
|
||||||
.unwrap_or_else(|| syn::parse_str("::pyo3").unwrap())
|
.unwrap_or_else(|| syn::parse_str("::pyo3").unwrap())
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
`#[pyclass]` can be used with the following parameters:
|
||||||
|
|
||||||
|
| Parameter | Description |
|
||||||
|
| :- | :- |
|
||||||
|
| <span style="white-space: pre">`crate = "some::path"`</span> | Path to import the `pyo3` crate, if it's not accessible at `::pyo3`. |
|
||||||
|
| `dict` | Gives instances of this class an empty `__dict__` to store custom attributes. |
|
||||||
|
| <span style="white-space: pre">`extends = BaseType`</span> | Use a custom baseclass. Defaults to [`PyAny`][params-1] |
|
||||||
|
| <span style="white-space: pre">`freelist = N`</span> | Implements a [free list][params-2] of size N. This can improve performance for types that are often created and deleted in quick succession. Profile your code to see whether `freelist` is right for you. |
|
||||||
|
| <span style="white-space: pre">`module = "module_name"`</span> | Python code will see the class as being defined in this module. Defaults to `builtins`. |
|
||||||
|
| <span style="white-space: pre">`name = "python_name"`</span> | Sets the name that Python sees this class as. Defaults to the name of the Rust struct. |
|
||||||
|
| <span style="white-space: pre">`text_signature = "(arg1, arg2, ...)"`</span> | Sets the text signature for the Python class' `__new__` method. |
|
||||||
|
| `subclass` | Allows other Python classes and `#[pyclass]` to inherit from this class. Enums cannot be subclassed. |
|
||||||
|
| `unsendable` | Required if your struct is not [`Send`][params-3]. Rather than using `unsendable`, consider implementing your struct in a threadsafe way by e.g. substituting [`Rc`][params-4] with [`Arc`][params-5]. By using `unsendable`, your class will panic when accessed by another thread.|
|
||||||
|
| `weakref` | Allows this class to be [weakly referenceable][params-6]. |
|
||||||
|
|
||||||
|
All of these parameters can either be passed directly on the `#[pyclass(...)]` annotation, or as one or
|
||||||
|
more accompanying `#[pyo3(...)]` annotations, e.g.:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
// Argument supplied directly to the `#[pyclass]` annotation.
|
||||||
|
#[pyclass(name = "SomeName", subclass)]
|
||||||
|
struct MyClass { }
|
||||||
|
|
||||||
|
// Argument supplied as a separate annotation.
|
||||||
|
#[pyclass]
|
||||||
|
#[pyo3(name = "SomeName", subclass)]
|
||||||
|
struct MyClass { }
|
||||||
|
```
|
|
@ -81,30 +81,18 @@ pub fn pyproto(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
/// A proc macro used to expose Rust structs and fieldless enums as Python objects.
|
/// A proc macro used to expose Rust structs and fieldless enums as Python objects.
|
||||||
///
|
///
|
||||||
/// `#[pyclass]` accepts the following [parameters][2]:
|
#[cfg_attr(docsrs, cfg_attr(docsrs, doc = include_str!("../docs/pyclass_parameters.md")))]
|
||||||
///
|
|
||||||
/// | Parameter | Description |
|
|
||||||
/// | :- | :- |
|
|
||||||
/// | <span style="white-space: pre">`name = "python_name"`</span> | Sets the name that Python sees this class as. Defaults to the name of the Rust struct. |
|
|
||||||
/// | <span style="white-space: pre">`freelist = N`</span> | Implements a [free list][9] of size N. This can improve performance for types that are often created and deleted in quick succession. Profile your code to see whether `freelist` is right for you. |
|
|
||||||
/// | `weakref` | Allows this class to be [weakly referenceable][6]. |
|
|
||||||
/// | <span style="white-space: pre">`extends = BaseType`</span> | Use a custom baseclass. Defaults to [`PyAny`][4] |
|
|
||||||
/// | `subclass` | Allows other Python classes and `#[pyclass]` to inherit from this class. Enums cannot be subclassed. |
|
|
||||||
/// | `unsendable` | Required if your struct is not [`Send`][3]. Rather than using `unsendable`, consider implementing your struct in a threadsafe way by e.g. substituting [`Rc`][7] with [`Arc`][8]. By using `unsendable`, your class will panic when accessed by another thread.|
|
|
||||||
/// | <span style="white-space: pre">`module = "module_name"`</span> | Python code will see the class as being defined in this module. Defaults to `builtins`. |
|
|
||||||
///
|
///
|
||||||
/// For more on creating Python classes,
|
/// For more on creating Python classes,
|
||||||
/// see the [class section of the guide][1].
|
/// see the [class section of the guide][1].
|
||||||
///
|
///
|
||||||
/// [1]: https://pyo3.rs/latest/class.html
|
/// [1]: https://pyo3.rs/latest/class.html
|
||||||
/// [2]: https://pyo3.rs/latest/class.html#customizing-the-class
|
/// [params-1]: ../prelude/struct.PyAny.html
|
||||||
/// [3]: std::marker::Send
|
/// [params-2]: https://en.wikipedia.org/wiki/Free_list
|
||||||
/// [4]: ../prelude/struct.PyAny.html
|
/// [params-3]: std::marker::Send
|
||||||
/// [5]: https://pyo3.rs/latest/class/protocols.html#garbage-collector-integration
|
/// [params-4]: std::rc::Rc
|
||||||
/// [6]: https://docs.python.org/3/library/weakref.html
|
/// [params-5]: std::sync::Arc
|
||||||
/// [7]: std::rc::Rc
|
/// [params-6]: https://docs.python.org/3/library/weakref.html
|
||||||
/// [8]: std::sync::Arc
|
|
||||||
/// [9]: https://en.wikipedia.org/wiki/Free_list
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn pyclass(attr: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn pyclass(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
use syn::Item;
|
use syn::Item;
|
||||||
|
@ -230,7 +218,7 @@ fn pyclass_impl(
|
||||||
methods_type: PyClassMethodsType,
|
methods_type: PyClassMethodsType,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let args = parse_macro_input!(attrs with PyClassArgs::parse_stuct_args);
|
let args = parse_macro_input!(attrs with PyClassArgs::parse_stuct_args);
|
||||||
let expanded = build_py_class(&mut ast, &args, methods_type).unwrap_or_compile_error();
|
let expanded = build_py_class(&mut ast, args, methods_type).unwrap_or_compile_error();
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
#ast
|
#ast
|
||||||
|
@ -245,7 +233,7 @@ fn pyclass_enum_impl(
|
||||||
methods_type: PyClassMethodsType,
|
methods_type: PyClassMethodsType,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let args = parse_macro_input!(attrs with PyClassArgs::parse_enum_args);
|
let args = parse_macro_input!(attrs with PyClassArgs::parse_enum_args);
|
||||||
let expanded = build_py_enum(&mut ast, &args, methods_type).unwrap_or_compile_error();
|
let expanded = build_py_enum(&mut ast, args, methods_type).unwrap_or_compile_error();
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
#ast
|
#ast
|
||||||
|
|
|
@ -35,13 +35,13 @@ error: `set` may only be specified once
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error: `name` may only be specified once
|
error: `name` may only be specified once
|
||||||
--> tests/ui/invalid_property_args.rs:37:49
|
--> tests/ui/invalid_property_args.rs:37:42
|
||||||
|
|
|
|
||||||
37 | struct MultipleName(#[pyo3(name = "foo", name = "bar")] i32);
|
37 | struct MultipleName(#[pyo3(name = "foo", name = "bar")] i32);
|
||||||
| ^^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: `name` is useless without `get` or `set`
|
error: `name` is useless without `get` or `set`
|
||||||
--> tests/ui/invalid_property_args.rs:40:40
|
--> tests/ui/invalid_property_args.rs:40:33
|
||||||
|
|
|
|
||||||
40 | struct NameWithoutGetSet(#[pyo3(name = "value")] i32);
|
40 | struct NameWithoutGetSet(#[pyo3(name = "value")] i32);
|
||||||
| ^^^^^^^
|
| ^^^^
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
error: expected one of freelist/name/extends/module
|
error: expected one of: `crate`, `dict`, `extends`, `freelist`, `module`, `name`, `subclass`, `text_signature`, `unsendable`, `weakref`, `gc`
|
||||||
--> tests/ui/invalid_pyclass_args.rs:3:11
|
--> tests/ui/invalid_pyclass_args.rs:3:11
|
||||||
|
|
|
|
||||||
3 | #[pyclass(extend=pyo3::types::PyDict)]
|
3 | #[pyclass(extend=pyo3::types::PyDict)]
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
||||||
error: expected type path (e.g., my_mod::BaseClass)
|
error: expected identifier
|
||||||
--> tests/ui/invalid_pyclass_args.rs:6:21
|
--> tests/ui/invalid_pyclass_args.rs:6:21
|
||||||
|
|
|
|
||||||
6 | #[pyclass(extends = "PyDict")]
|
6 | #[pyclass(extends = "PyDict")]
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
||||||
error: expected type name (e.g. "Name")
|
error: expected string literal
|
||||||
--> tests/ui/invalid_pyclass_args.rs:9:18
|
--> tests/ui/invalid_pyclass_args.rs:9:18
|
||||||
|
|
|
|
||||||
9 | #[pyclass(name = m::MyClass)]
|
9 | #[pyclass(name = m::MyClass)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: expected a single identifier in double-quotes
|
error: expected a single identifier in double quotes
|
||||||
--> tests/ui/invalid_pyclass_args.rs:12:18
|
--> tests/ui/invalid_pyclass_args.rs:12:18
|
||||||
|
|
|
|
||||||
12 | #[pyclass(name = "Custom Name")]
|
12 | #[pyclass(name = "Custom Name")]
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: since PyO3 0.13 a pyclass name should be in double-quotes, e.g. "CustomName"
|
error: expected string literal
|
||||||
--> tests/ui/invalid_pyclass_args.rs:15:18
|
--> tests/ui/invalid_pyclass_args.rs:15:18
|
||||||
|
|
|
|
||||||
15 | #[pyclass(name = CustomName)]
|
15 | #[pyclass(name = CustomName)]
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
||||||
error: expected string literal (e.g., "my_mod")
|
error: expected string literal
|
||||||
--> tests/ui/invalid_pyclass_args.rs:18:20
|
--> tests/ui/invalid_pyclass_args.rs:18:20
|
||||||
|
|
|
|
||||||
18 | #[pyclass(module = my_module)]
|
18 | #[pyclass(module = my_module)]
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^
|
||||||
|
|
||||||
error: expected one of gc/weakref/subclass/dict/unsendable
|
error: expected one of: `crate`, `dict`, `extends`, `freelist`, `module`, `name`, `subclass`, `text_signature`, `unsendable`, `weakref`, `gc`
|
||||||
--> tests/ui/invalid_pyclass_args.rs:21:11
|
--> tests/ui/invalid_pyclass_args.rs:21:11
|
||||||
|
|
|
|
||||||
21 | #[pyclass(weakrev)]
|
21 | #[pyclass(weakrev)]
|
||||||
|
|
|
@ -2,14 +2,14 @@ use pyo3::prelude::*;
|
||||||
|
|
||||||
#[pyclass(subclass)]
|
#[pyclass(subclass)]
|
||||||
enum NotBaseClass {
|
enum NotBaseClass {
|
||||||
x,
|
X,
|
||||||
y,
|
Y,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass(extends = PyList)]
|
#[pyclass(extends = PyList)]
|
||||||
enum NotDrivedClass {
|
enum NotDrivedClass {
|
||||||
x,
|
X,
|
||||||
y,
|
Y,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
|
|
@ -4,13 +4,13 @@ error: enums can't be inherited by other classes
|
||||||
3 | #[pyclass(subclass)]
|
3 | #[pyclass(subclass)]
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
||||||
error: enums cannot extend from other classes
|
error: enums can't extend from other classes
|
||||||
--> tests/ui/invalid_pyclass_enum.rs:9:11
|
--> tests/ui/invalid_pyclass_enum.rs:9:11
|
||||||
|
|
|
|
||||||
9 | #[pyclass(extends = PyList)]
|
9 | #[pyclass(extends = PyList)]
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
||||||
error: Empty enums can't be #[pyclass].
|
error: #[pyclass] can't be used on enums without any variants
|
||||||
--> tests/ui/invalid_pyclass_enum.rs:16:18
|
--> tests/ui/invalid_pyclass_enum.rs:16:18
|
||||||
|
|
|
|
||||||
16 | enum NoEmptyEnum {}
|
16 | enum NoEmptyEnum {}
|
||||||
|
|
|
@ -5,10 +5,10 @@ error: `name` may only be specified once
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: `name` may only be specified once
|
error: `name` may only be specified once
|
||||||
--> tests/ui/invalid_pymethod_names.rs:18:19
|
--> tests/ui/invalid_pymethod_names.rs:18:12
|
||||||
|
|
|
|
||||||
18 | #[pyo3(name = "bar")]
|
18 | #[pyo3(name = "bar")]
|
||||||
| ^^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: `name` not allowed with `#[new]`
|
error: `name` not allowed with `#[new]`
|
||||||
--> tests/ui/invalid_pymethod_names.rs:24:19
|
--> tests/ui/invalid_pymethod_names.rs:24:19
|
||||||
|
|
Loading…
Reference in New Issue