Thread pyo3's path through the builder functions (#3907)

* Thread pyo3's path through the builder functions

* preserve span of pyo3_path

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
This commit is contained in:
Bruno Kolenbrander 2024-03-04 08:54:04 +01:00 committed by GitHub
parent 70a7aa808d
commit 4114dcb1a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 708 additions and 513 deletions

View File

@ -1,3 +1,4 @@
use crate::utils::Ctx;
use proc_macro2::{Span, TokenStream};
use quote::{quote_spanned, ToTokens};
@ -14,12 +15,11 @@ impl Deprecation {
}
}
#[derive(Default)]
pub struct Deprecations(Vec<(Deprecation, Span)>);
pub struct Deprecations<'ctx>(Vec<(Deprecation, Span)>, &'ctx Ctx);
impl Deprecations {
pub fn new() -> Self {
Deprecations(Vec::new())
impl<'ctx> Deprecations<'ctx> {
pub fn new(ctx: &'ctx Ctx) -> Self {
Deprecations(Vec::new(), ctx)
}
pub fn push(&mut self, deprecation: Deprecation, span: Span) {
@ -27,15 +27,18 @@ impl Deprecations {
}
}
impl ToTokens for Deprecations {
impl<'ctx> ToTokens for Deprecations<'ctx> {
fn to_tokens(&self, tokens: &mut TokenStream) {
for (deprecation, span) in &self.0 {
let Self(deprecations, Ctx { pyo3_path }) = self;
for (deprecation, span) in deprecations {
let pyo3_path = pyo3_path.to_tokens_spanned(*span);
let ident = deprecation.ident(*span);
quote_spanned!(
*span =>
#[allow(clippy::let_unit_value)]
{
let _ = _pyo3::impl_::deprecations::#ident;
let _ = #pyo3_path::impl_::deprecations::#ident;
}
)
.to_tokens(tokens)

View File

@ -1,7 +1,5 @@
use crate::{
attributes::{self, get_pyo3_options, CrateAttribute, FromPyWithAttribute},
utils::get_pyo3_crate,
};
use crate::attributes::{self, get_pyo3_options, CrateAttribute, FromPyWithAttribute};
use crate::utils::Ctx;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{
@ -46,14 +44,15 @@ impl<'a> Enum<'a> {
}
/// Build derivation body for enums.
fn build(&self) -> TokenStream {
fn build(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
let mut var_extracts = Vec::new();
let mut variant_names = Vec::new();
let mut error_names = Vec::new();
for var in &self.variants {
let struct_derive = var.build();
let struct_derive = var.build(ctx);
let ext = quote!({
let maybe_ret = || -> _pyo3::PyResult<Self> {
let maybe_ret = || -> #pyo3_path::PyResult<Self> {
#struct_derive
}();
@ -73,7 +72,7 @@ impl<'a> Enum<'a> {
#(#var_extracts),*
];
::std::result::Result::Err(
_pyo3::impl_::frompyobject::failed_to_extract_enum(
#pyo3_path::impl_::frompyobject::failed_to_extract_enum(
obj.py(),
#ty_name,
&[#(#variant_names),*],
@ -239,16 +238,16 @@ impl<'a> Container<'a> {
}
/// Build derivation body for a struct.
fn build(&self) -> TokenStream {
fn build(&self, ctx: &Ctx) -> TokenStream {
match &self.ty {
ContainerType::StructNewtype(ident, from_py_with) => {
self.build_newtype_struct(Some(ident), from_py_with)
self.build_newtype_struct(Some(ident), from_py_with, ctx)
}
ContainerType::TupleNewtype(from_py_with) => {
self.build_newtype_struct(None, from_py_with)
self.build_newtype_struct(None, from_py_with, ctx)
}
ContainerType::Tuple(tups) => self.build_tuple_struct(tups),
ContainerType::Struct(tups) => self.build_struct(tups),
ContainerType::Tuple(tups) => self.build_tuple_struct(tups, ctx),
ContainerType::Struct(tups) => self.build_struct(tups, ctx),
}
}
@ -256,7 +255,9 @@ impl<'a> Container<'a> {
&self,
field_ident: Option<&Ident>,
from_py_with: &Option<FromPyWithAttribute>,
ctx: &Ctx,
) -> TokenStream {
let Ctx { pyo3_path } = ctx;
let self_ty = &self.path;
let struct_name = self.name();
if let Some(ident) = field_ident {
@ -264,32 +265,33 @@ impl<'a> Container<'a> {
match from_py_with {
None => quote! {
Ok(#self_ty {
#ident: _pyo3::impl_::frompyobject::extract_struct_field(obj, #struct_name, #field_name)?
#ident: #pyo3_path::impl_::frompyobject::extract_struct_field(obj, #struct_name, #field_name)?
})
},
Some(FromPyWithAttribute {
value: expr_path, ..
}) => quote! {
Ok(#self_ty {
#ident: _pyo3::impl_::frompyobject::extract_struct_field_with(#expr_path as fn(_) -> _, obj, #struct_name, #field_name)?
#ident: #pyo3_path::impl_::frompyobject::extract_struct_field_with(#expr_path as fn(_) -> _, obj, #struct_name, #field_name)?
})
},
}
} else {
match from_py_with {
None => quote!(
_pyo3::impl_::frompyobject::extract_tuple_struct_field(obj, #struct_name, 0).map(#self_ty)
#pyo3_path::impl_::frompyobject::extract_tuple_struct_field(obj, #struct_name, 0).map(#self_ty)
),
Some(FromPyWithAttribute {
value: expr_path, ..
}) => quote! (
_pyo3::impl_::frompyobject::extract_tuple_struct_field_with(#expr_path as fn(_) -> _, obj, #struct_name, 0).map(#self_ty)
#pyo3_path::impl_::frompyobject::extract_tuple_struct_field_with(#expr_path as fn(_) -> _, obj, #struct_name, 0).map(#self_ty)
),
}
}
}
fn build_tuple_struct(&self, struct_fields: &[TupleStructField]) -> TokenStream {
fn build_tuple_struct(&self, struct_fields: &[TupleStructField], ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
let self_ty = &self.path;
let struct_name = &self.name();
let field_idents: Vec<_> = (0..struct_fields.len())
@ -298,12 +300,12 @@ impl<'a> Container<'a> {
let fields = struct_fields.iter().zip(&field_idents).enumerate().map(|(index, (field, ident))| {
match &field.from_py_with {
None => quote!(
_pyo3::impl_::frompyobject::extract_tuple_struct_field(&#ident, #struct_name, #index)?
#pyo3_path::impl_::frompyobject::extract_tuple_struct_field(&#ident, #struct_name, #index)?
),
Some(FromPyWithAttribute {
value: expr_path, ..
}) => quote! (
_pyo3::impl_::frompyobject::extract_tuple_struct_field_with(#expr_path as fn(_) -> _, &#ident, #struct_name, #index)?
#pyo3_path::impl_::frompyobject::extract_tuple_struct_field_with(#expr_path as fn(_) -> _, &#ident, #struct_name, #index)?
),
}
});
@ -315,7 +317,8 @@ impl<'a> Container<'a> {
)
}
fn build_struct(&self, struct_fields: &[NamedStructField<'_>]) -> TokenStream {
fn build_struct(&self, struct_fields: &[NamedStructField<'_>], ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
let self_ty = &self.path;
let struct_name = &self.name();
let mut fields: Punctuated<TokenStream, syn::Token![,]> = Punctuated::new();
@ -324,27 +327,27 @@ impl<'a> Container<'a> {
let field_name = ident.to_string();
let getter = match field.getter.as_ref().unwrap_or(&FieldGetter::GetAttr(None)) {
FieldGetter::GetAttr(Some(name)) => {
quote!(getattr(_pyo3::intern!(obj.py(), #name)))
quote!(getattr(#pyo3_path::intern!(obj.py(), #name)))
}
FieldGetter::GetAttr(None) => {
quote!(getattr(_pyo3::intern!(obj.py(), #field_name)))
quote!(getattr(#pyo3_path::intern!(obj.py(), #field_name)))
}
FieldGetter::GetItem(Some(syn::Lit::Str(key))) => {
quote!(get_item(_pyo3::intern!(obj.py(), #key)))
quote!(get_item(#pyo3_path::intern!(obj.py(), #key)))
}
FieldGetter::GetItem(Some(key)) => quote!(get_item(#key)),
FieldGetter::GetItem(None) => {
quote!(get_item(_pyo3::intern!(obj.py(), #field_name)))
quote!(get_item(#pyo3_path::intern!(obj.py(), #field_name)))
}
};
let extractor = match &field.from_py_with {
None => {
quote!(_pyo3::impl_::frompyobject::extract_struct_field(&obj.#getter?, #struct_name, #field_name)?)
quote!(#pyo3_path::impl_::frompyobject::extract_struct_field(&obj.#getter?, #struct_name, #field_name)?)
}
Some(FromPyWithAttribute {
value: expr_path, ..
}) => {
quote! (_pyo3::impl_::frompyobject::extract_struct_field_with(#expr_path as fn(_) -> _, &obj.#getter?, #struct_name, #field_name)?)
quote! (#pyo3_path::impl_::frompyobject::extract_struct_field_with(#expr_path as fn(_) -> _, &obj.#getter?, #struct_name, #field_name)?)
}
};
@ -579,7 +582,9 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
.push(parse_quote!(#gen_ident: FromPyObject<#lt_param>))
}
let options = ContainerOptions::from_attrs(&tokens.attrs)?;
let krate = get_pyo3_crate(&options.krate);
let ctx = &Ctx::new(&options.krate);
let Ctx { pyo3_path } = &ctx;
let derives = match &tokens.data {
syn::Data::Enum(en) => {
if options.transparent || options.annotation.is_some() {
@ -587,7 +592,7 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
at top level for enums");
}
let en = Enum::new(en, &tokens.ident)?;
en.build()
en.build(ctx)
}
syn::Data::Struct(st) => {
if let Some(lit_str) = &options.annotation {
@ -595,7 +600,7 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
}
let ident = &tokens.ident;
let st = Container::new(&st.fields, parse_quote!(#ident), options)?;
st.build()
st.build(ctx)
}
syn::Data::Union(_) => bail_spanned!(
tokens.span() => "#[derive(FromPyObject)] is not supported for unions"
@ -607,12 +612,11 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
// FIXME https://github.com/PyO3/pyo3/issues/3903
#[allow(unknown_lints, non_local_definitions)]
const _: () = {
use #krate as _pyo3;
use _pyo3::prelude::PyAnyMethods;
use #pyo3_path::prelude::PyAnyMethods;
#[automatically_derived]
impl #trait_generics _pyo3::FromPyObject<#lt_param> for #ident #generics #where_clause {
fn extract_bound(obj: &_pyo3::Bound<#lt_param, _pyo3::PyAny>) -> _pyo3::PyResult<Self> {
impl #trait_generics #pyo3_path::FromPyObject<#lt_param> for #ident #generics #where_clause {
fn extract_bound(obj: &#pyo3_path::Bound<#lt_param, #pyo3_path::PyAny>) -> #pyo3_path::PyResult<Self> {
#derives
}
}

View File

@ -1,5 +1,6 @@
use std::borrow::Cow;
use crate::utils::Ctx;
use crate::{
attributes::{self, get_pyo3_options, take_attributes, NameAttribute},
deprecations::Deprecations,
@ -13,12 +14,12 @@ use syn::{
Result,
};
pub struct ConstSpec {
pub struct ConstSpec<'ctx> {
pub rust_ident: syn::Ident,
pub attributes: ConstAttributes,
pub attributes: ConstAttributes<'ctx>,
}
impl ConstSpec {
impl ConstSpec<'_> {
pub fn python_name(&self) -> Cow<'_, Ident> {
if let Some(name) = &self.attributes.name {
Cow::Borrowed(&name.value.0)
@ -34,10 +35,10 @@ impl ConstSpec {
}
}
pub struct ConstAttributes {
pub struct ConstAttributes<'ctx> {
pub is_class_attr: bool,
pub name: Option<NameAttribute>,
pub deprecations: Deprecations,
pub deprecations: Deprecations<'ctx>,
}
pub enum PyO3ConstAttribute {
@ -55,12 +56,12 @@ impl Parse for PyO3ConstAttribute {
}
}
impl ConstAttributes {
pub fn from_attrs(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Self> {
impl<'ctx> ConstAttributes<'ctx> {
pub fn from_attrs(attrs: &mut Vec<syn::Attribute>, ctx: &'ctx Ctx) -> syn::Result<Self> {
let mut attributes = ConstAttributes {
is_class_attr: false,
name: None,
deprecations: Deprecations::new(),
deprecations: Deprecations::new(ctx),
};
take_attributes(attrs, |attr| {

View File

@ -4,6 +4,7 @@ use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use syn::{ext::IdentExt, spanned::Spanned, Ident, Result};
use crate::utils::Ctx;
use crate::{
attributes::{TextSignatureAttribute, TextSignatureAttributeValue},
deprecations::{Deprecation, Deprecations},
@ -108,13 +109,16 @@ impl FnType {
cls: Option<&syn::Type>,
error_mode: ExtractErrorMode,
holders: &mut Vec<TokenStream>,
ctx: &Ctx,
) -> TokenStream {
let Ctx { pyo3_path } = ctx;
match self {
FnType::Getter(st) | FnType::Setter(st) | FnType::Fn(st) => {
let mut receiver = st.receiver(
cls.expect("no class given for Fn with a \"self\" receiver"),
error_mode,
holders,
ctx,
);
syn::Token![,](Span::call_site()).to_tokens(&mut receiver);
receiver
@ -125,22 +129,24 @@ impl FnType {
FnType::FnClass(span) | FnType::FnNewClass(span) => {
let py = syn::Ident::new("py", Span::call_site());
let slf: Ident = syn::Ident::new("_slf", Span::call_site());
let pyo3_path = pyo3_path.to_tokens_spanned(*span);
quote_spanned! { *span =>
#[allow(clippy::useless_conversion)]
::std::convert::Into::into(
_pyo3::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf.cast())
.downcast_unchecked::<_pyo3::types::PyType>()
#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf.cast())
.downcast_unchecked::<#pyo3_path::types::PyType>()
),
}
}
FnType::FnModule(span) => {
let py = syn::Ident::new("py", Span::call_site());
let slf: Ident = syn::Ident::new("_slf", Span::call_site());
let pyo3_path = pyo3_path.to_tokens_spanned(*span);
quote_spanned! { *span =>
#[allow(clippy::useless_conversion)]
::std::convert::Into::into(
_pyo3::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf.cast())
.downcast_unchecked::<_pyo3::types::PyModule>()
#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf.cast())
.downcast_unchecked::<#pyo3_path::types::PyModule>()
),
}
}
@ -161,13 +167,14 @@ pub enum ExtractErrorMode {
}
impl ExtractErrorMode {
pub fn handle_error(self, extract: TokenStream) -> TokenStream {
pub fn handle_error(self, extract: TokenStream, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
match self {
ExtractErrorMode::Raise => quote! { #extract? },
ExtractErrorMode::NotImplemented => quote! {
match #extract {
::std::result::Result::Ok(value) => value,
::std::result::Result::Err(_) => { return _pyo3::callback::convert(py, py.NotImplemented()); },
::std::result::Result::Err(_) => { return #pyo3_path::callback::convert(py, py.NotImplemented()); },
}
},
}
@ -180,11 +187,13 @@ impl SelfType {
cls: &syn::Type,
error_mode: ExtractErrorMode,
holders: &mut Vec<TokenStream>,
ctx: &Ctx,
) -> TokenStream {
// Due to use of quote_spanned in this function, need to bind these idents to the
// main macro callsite.
let py = syn::Ident::new("py", Span::call_site());
let slf = syn::Ident::new("_slf", Span::call_site());
let Ctx { pyo3_path } = ctx;
match self {
SelfType::Receiver { span, mutable } => {
let method = if *mutable {
@ -193,29 +202,35 @@ impl SelfType {
syn::Ident::new("extract_pyclass_ref", *span)
};
let holder = syn::Ident::new(&format!("holder_{}", holders.len()), *span);
let pyo3_path = pyo3_path.to_tokens_spanned(*span);
holders.push(quote_spanned! { *span =>
#[allow(clippy::let_unit_value)]
let mut #holder = _pyo3::impl_::extract_argument::FunctionArgumentHolder::INIT;
let mut #slf = _pyo3::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf);
let mut #holder = #pyo3_path::impl_::extract_argument::FunctionArgumentHolder::INIT;
let mut #slf = #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf);
});
error_mode.handle_error(quote_spanned! { *span =>
_pyo3::impl_::extract_argument::#method::<#cls>(
error_mode.handle_error(
quote_spanned! { *span =>
#pyo3_path::impl_::extract_argument::#method::<#cls>(
&#slf,
&mut #holder,
)
})
},
ctx,
)
}
SelfType::TryFromBoundRef(span) => {
let pyo3_path = pyo3_path.to_tokens_spanned(*span);
error_mode.handle_error(
quote_spanned! { *span =>
_pyo3::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf).downcast::<#cls>()
.map_err(::std::convert::Into::<_pyo3::PyErr>::into)
#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf).downcast::<#cls>()
.map_err(::std::convert::Into::<#pyo3_path::PyErr>::into)
.and_then(
#[allow(unknown_lints, clippy::unnecessary_fallible_conversions)] // In case slf is Py<Self> (unknown_lints can be removed when MSRV is 1.75+)
|bound| ::std::convert::TryFrom::try_from(bound).map_err(::std::convert::Into::into)
)
}
},
ctx
)
}
}
@ -264,7 +279,7 @@ pub struct FnSpec<'a> {
pub text_signature: Option<TextSignatureAttribute>,
pub asyncness: Option<syn::Token![async]>,
pub unsafety: Option<syn::Token![unsafe]>,
pub deprecations: Deprecations,
pub deprecations: Deprecations<'a>,
}
pub fn get_return_info(output: &syn::ReturnType) -> syn::Type {
@ -303,6 +318,7 @@ impl<'a> FnSpec<'a> {
sig: &'a mut syn::Signature,
meth_attrs: &mut Vec<syn::Attribute>,
options: PyFunctionOptions,
ctx: &'a Ctx,
) -> Result<FnSpec<'a>> {
let PyFunctionOptions {
text_signature,
@ -312,7 +328,7 @@ impl<'a> FnSpec<'a> {
} = options;
let mut python_name = name.map(|name| name.value.0);
let mut deprecations = Deprecations::new();
let mut deprecations = Deprecations::new(ctx);
let fn_type = Self::parse_fn_type(sig, meth_attrs, &mut python_name, &mut deprecations)?;
ensure_signatures_on_valid_method(&fn_type, signature.as_ref(), text_signature.as_ref())?;
@ -366,7 +382,7 @@ impl<'a> FnSpec<'a> {
sig: &syn::Signature,
meth_attrs: &mut Vec<syn::Attribute>,
python_name: &mut Option<syn::Ident>,
deprecations: &mut Deprecations,
deprecations: &mut Deprecations<'_>,
) -> Result<FnType> {
let mut method_attributes = parse_method_attributes(meth_attrs, deprecations)?;
@ -480,7 +496,9 @@ impl<'a> FnSpec<'a> {
&self,
ident: &proc_macro2::Ident,
cls: Option<&syn::Type>,
ctx: &Ctx,
) -> Result<TokenStream> {
let Ctx { pyo3_path } = ctx;
let mut cancel_handle_iter = self
.signature
.arguments
@ -495,7 +513,7 @@ impl<'a> FnSpec<'a> {
}
let rust_call = |args: Vec<TokenStream>, holders: &mut Vec<TokenStream>| {
let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise, holders);
let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise, holders, ctx);
let call = if self.asyncness.is_some() {
let throw_callback = if cancel_handle.is_some() {
@ -505,7 +523,7 @@ impl<'a> FnSpec<'a> {
};
let python_name = &self.python_name;
let qualname_prefix = match cls {
Some(cls) => quote!(Some(<#cls as _pyo3::PyTypeInfo>::NAME)),
Some(cls) => quote!(Some(<#cls as #pyo3_path::PyTypeInfo>::NAME)),
None => quote!(None),
};
let future = match self.tp {
@ -513,7 +531,7 @@ impl<'a> FnSpec<'a> {
holders.pop().unwrap(); // does not actually use holder created by `self_arg`
quote! {{
let __guard = _pyo3::impl_::coroutine::RefGuard::<#cls>::new(&_pyo3::impl_::pymethods::BoundRef::ref_from_ptr(py, &_slf))?;
let __guard = #pyo3_path::impl_::coroutine::RefGuard::<#cls>::new(&#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &_slf))?;
async move { function(&__guard, #(#args),*).await }
}}
}
@ -521,7 +539,7 @@ impl<'a> FnSpec<'a> {
holders.pop().unwrap(); // does not actually use holder created by `self_arg`
quote! {{
let mut __guard = _pyo3::impl_::coroutine::RefMutGuard::<#cls>::new(&_pyo3::impl_::pymethods::BoundRef::ref_from_ptr(py, &_slf))?;
let mut __guard = #pyo3_path::impl_::coroutine::RefMutGuard::<#cls>::new(&#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &_slf))?;
async move { function(&mut __guard, #(#args),*).await }
}}
}
@ -529,16 +547,16 @@ impl<'a> FnSpec<'a> {
};
let mut call = quote! {{
let future = #future;
_pyo3::impl_::coroutine::new_coroutine(
_pyo3::intern!(py, stringify!(#python_name)),
#pyo3_path::impl_::coroutine::new_coroutine(
#pyo3_path::intern!(py, stringify!(#python_name)),
#qualname_prefix,
#throw_callback,
async move { _pyo3::impl_::wrap::OkWrap::wrap(future.await) },
async move { #pyo3_path::impl_::wrap::OkWrap::wrap(future.await) },
)
}};
if cancel_handle.is_some() {
call = quote! {{
let __cancel_handle = _pyo3::coroutine::CancelHandle::new();
let __cancel_handle = #pyo3_path::coroutine::CancelHandle::new();
let __throw_callback = __cancel_handle.throw_callback();
#call
}};
@ -547,7 +565,7 @@ impl<'a> FnSpec<'a> {
} else {
quote! { function(#self_arg #(#args),*) }
};
quotes::map_result_into_ptr(quotes::ok_wrap(call))
quotes::map_result_into_ptr(quotes::ok_wrap(call, ctx), ctx)
};
let func_name = &self.name;
@ -578,9 +596,9 @@ impl<'a> FnSpec<'a> {
quote! {
unsafe fn #ident<'py>(
py: _pyo3::Python<'py>,
_slf: *mut _pyo3::ffi::PyObject,
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
py: #pyo3_path::Python<'py>,
_slf: *mut #pyo3_path::ffi::PyObject,
) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
let function = #rust_name; // Shadow the function name to avoid #3017
#( #holders )*
let result = #call;
@ -590,16 +608,16 @@ impl<'a> FnSpec<'a> {
}
CallingConvention::Fastcall => {
let mut holders = Vec::new();
let (arg_convert, args) = impl_arg_params(self, cls, true, &mut holders)?;
let (arg_convert, args) = impl_arg_params(self, cls, true, &mut holders, ctx)?;
let call = rust_call(args, &mut holders);
quote! {
unsafe fn #ident<'py>(
py: _pyo3::Python<'py>,
_slf: *mut _pyo3::ffi::PyObject,
_args: *const *mut _pyo3::ffi::PyObject,
_nargs: _pyo3::ffi::Py_ssize_t,
_kwnames: *mut _pyo3::ffi::PyObject
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
py: #pyo3_path::Python<'py>,
_slf: *mut #pyo3_path::ffi::PyObject,
_args: *const *mut #pyo3_path::ffi::PyObject,
_nargs: #pyo3_path::ffi::Py_ssize_t,
_kwnames: *mut #pyo3_path::ffi::PyObject
) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
let function = #rust_name; // Shadow the function name to avoid #3017
#arg_convert
#( #holders )*
@ -610,15 +628,15 @@ impl<'a> FnSpec<'a> {
}
CallingConvention::Varargs => {
let mut holders = Vec::new();
let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders)?;
let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders, ctx)?;
let call = rust_call(args, &mut holders);
quote! {
unsafe fn #ident<'py>(
py: _pyo3::Python<'py>,
_slf: *mut _pyo3::ffi::PyObject,
_args: *mut _pyo3::ffi::PyObject,
_kwargs: *mut _pyo3::ffi::PyObject
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
py: #pyo3_path::Python<'py>,
_slf: *mut #pyo3_path::ffi::PyObject,
_args: *mut #pyo3_path::ffi::PyObject,
_kwargs: *mut #pyo3_path::ffi::PyObject
) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
let function = #rust_name; // Shadow the function name to avoid #3017
#arg_convert
#( #holders )*
@ -629,23 +647,25 @@ impl<'a> FnSpec<'a> {
}
CallingConvention::TpNew => {
let mut holders = Vec::new();
let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders)?;
let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise, &mut holders);
let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders, ctx)?;
let self_arg = self
.tp
.self_arg(cls, ExtractErrorMode::Raise, &mut holders, ctx);
let call = quote! { #rust_name(#self_arg #(#args),*) };
quote! {
unsafe fn #ident(
py: _pyo3::Python<'_>,
_slf: *mut _pyo3::ffi::PyTypeObject,
_args: *mut _pyo3::ffi::PyObject,
_kwargs: *mut _pyo3::ffi::PyObject
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
use _pyo3::callback::IntoPyCallbackOutput;
py: #pyo3_path::Python<'_>,
_slf: *mut #pyo3_path::ffi::PyTypeObject,
_args: *mut #pyo3_path::ffi::PyObject,
_kwargs: *mut #pyo3_path::ffi::PyObject
) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
use #pyo3_path::callback::IntoPyCallbackOutput;
let function = #rust_name; // Shadow the function name to avoid #3017
#arg_convert
#( #holders )*
let result = #call;
let initializer: _pyo3::PyClassInitializer::<#cls> = result.convert(py)?;
_pyo3::impl_::pymethods::tp_new_impl(py, initializer, _slf)
let initializer: #pyo3_path::PyClassInitializer::<#cls> = result.convert(py)?;
#pyo3_path::impl_::pymethods::tp_new_impl(py, initializer, _slf)
}
}
}
@ -654,19 +674,20 @@ impl<'a> FnSpec<'a> {
/// Return a `PyMethodDef` constructor for this function, matching the selected
/// calling convention.
pub fn get_methoddef(&self, wrapper: impl ToTokens, doc: &PythonDoc) -> TokenStream {
pub fn get_methoddef(&self, wrapper: impl ToTokens, doc: &PythonDoc, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
let python_name = self.null_terminated_python_name();
match self.convention {
CallingConvention::Noargs => quote! {
_pyo3::impl_::pymethods::PyMethodDef::noargs(
#pyo3_path::impl_::pymethods::PyMethodDef::noargs(
#python_name,
_pyo3::impl_::pymethods::PyCFunction({
#pyo3_path::impl_::pymethods::PyCFunction({
unsafe extern "C" fn trampoline(
_slf: *mut _pyo3::ffi::PyObject,
_args: *mut _pyo3::ffi::PyObject,
) -> *mut _pyo3::ffi::PyObject
_slf: *mut #pyo3_path::ffi::PyObject,
_args: *mut #pyo3_path::ffi::PyObject,
) -> *mut #pyo3_path::ffi::PyObject
{
_pyo3::impl_::trampoline::noargs(
#pyo3_path::impl_::trampoline::noargs(
_slf,
_args,
#wrapper
@ -678,17 +699,17 @@ impl<'a> FnSpec<'a> {
)
},
CallingConvention::Fastcall => quote! {
_pyo3::impl_::pymethods::PyMethodDef::fastcall_cfunction_with_keywords(
#pyo3_path::impl_::pymethods::PyMethodDef::fastcall_cfunction_with_keywords(
#python_name,
_pyo3::impl_::pymethods::PyCFunctionFastWithKeywords({
#pyo3_path::impl_::pymethods::PyCFunctionFastWithKeywords({
unsafe extern "C" fn trampoline(
_slf: *mut _pyo3::ffi::PyObject,
_args: *const *mut _pyo3::ffi::PyObject,
_nargs: _pyo3::ffi::Py_ssize_t,
_kwnames: *mut _pyo3::ffi::PyObject
) -> *mut _pyo3::ffi::PyObject
_slf: *mut #pyo3_path::ffi::PyObject,
_args: *const *mut #pyo3_path::ffi::PyObject,
_nargs: #pyo3_path::ffi::Py_ssize_t,
_kwnames: *mut #pyo3_path::ffi::PyObject
) -> *mut #pyo3_path::ffi::PyObject
{
_pyo3::impl_::trampoline::fastcall_with_keywords(
#pyo3_path::impl_::trampoline::fastcall_with_keywords(
_slf,
_args,
_nargs,
@ -702,16 +723,16 @@ impl<'a> FnSpec<'a> {
)
},
CallingConvention::Varargs => quote! {
_pyo3::impl_::pymethods::PyMethodDef::cfunction_with_keywords(
#pyo3_path::impl_::pymethods::PyMethodDef::cfunction_with_keywords(
#python_name,
_pyo3::impl_::pymethods::PyCFunctionWithKeywords({
#pyo3_path::impl_::pymethods::PyCFunctionWithKeywords({
unsafe extern "C" fn trampoline(
_slf: *mut _pyo3::ffi::PyObject,
_args: *mut _pyo3::ffi::PyObject,
_kwargs: *mut _pyo3::ffi::PyObject,
) -> *mut _pyo3::ffi::PyObject
_slf: *mut #pyo3_path::ffi::PyObject,
_args: *mut #pyo3_path::ffi::PyObject,
_kwargs: *mut #pyo3_path::ffi::PyObject,
) -> *mut #pyo3_path::ffi::PyObject
{
_pyo3::impl_::trampoline::cfunction_with_keywords(
#pyo3_path::impl_::trampoline::cfunction_with_keywords(
_slf,
_args,
_kwargs,
@ -783,7 +804,7 @@ impl MethodTypeAttribute {
/// Otherwise will either return a parse error or the attribute.
fn parse_if_matching_attribute(
attr: &syn::Attribute,
deprecations: &mut Deprecations,
deprecations: &mut Deprecations<'_>,
) -> Result<Option<Self>> {
fn ensure_no_arguments(meta: &syn::Meta, ident: &str) -> syn::Result<()> {
match meta {
@ -869,7 +890,7 @@ impl Display for MethodTypeAttribute {
fn parse_method_attributes(
attrs: &mut Vec<syn::Attribute>,
deprecations: &mut Deprecations,
deprecations: &mut Deprecations<'_>,
) -> Result<Vec<MethodTypeAttribute>> {
let mut new_attrs = Vec::new();
let mut found_attrs = Vec::new();

View File

@ -1,10 +1,10 @@
//! Code generation for the function that initializes a python module and adds classes and function.
use crate::utils::Ctx;
use crate::{
attributes::{self, take_attributes, take_pyo3_options, CrateAttribute, NameAttribute},
get_doc,
pyfunction::{impl_wrap_pyfunction, PyFunctionOptions},
utils::get_pyo3_crate,
};
use proc_macro2::TokenStream;
use quote::quote;
@ -73,7 +73,8 @@ pub fn pymodule_module_impl(mut module: syn::ItemMod) -> Result<TokenStream> {
bail_spanned!(module.span() => "`#[pymodule]` can only be used on inline modules")
};
let options = PyModuleOptions::from_attrs(attrs)?;
let krate = get_pyo3_crate(&options.krate);
let ctx = &Ctx::new(&options.krate);
let Ctx { pyo3_path } = ctx;
let doc = get_doc(attrs, None);
let mut module_items = Vec::new();
@ -164,8 +165,8 @@ pub fn pymodule_module_impl(mut module: syn::ItemMod) -> Result<TokenStream> {
#initialization
impl MakeDef {
const fn make_def() -> #krate::impl_::pymodule::ModuleDef {
use #krate::impl_::pymodule as impl_;
const fn make_def() -> #pyo3_path::impl_::pymodule::ModuleDef {
use #pyo3_path::impl_::pymodule as impl_;
const INITIALIZER: impl_::ModuleInitializer = impl_::ModuleInitializer(__pyo3_pymodule);
unsafe {
impl_::ModuleDef::new(
@ -177,8 +178,8 @@ pub fn pymodule_module_impl(mut module: syn::ItemMod) -> Result<TokenStream> {
}
}
fn __pyo3_pymodule(module: &#krate::Bound<'_, #krate::types::PyModule>) -> #krate::PyResult<()> {
use #krate::impl_::pymodule::PyAddToModule;
fn __pyo3_pymodule(module: &#pyo3_path::Bound<'_, #pyo3_path::types::PyModule>) -> #pyo3_path::PyResult<()> {
use #pyo3_path::impl_::pymodule::PyAddToModule;
#(
#(#module_items_cfg_attrs)*
#module_items::add_to_module(module)?;
@ -195,7 +196,8 @@ pub fn pymodule_module_impl(mut module: syn::ItemMod) -> Result<TokenStream> {
pub fn pymodule_function_impl(mut function: syn::ItemFn) -> Result<TokenStream> {
let options = PyModuleOptions::from_attrs(&mut function.attrs)?;
process_functions_in_module(&options, &mut function)?;
let krate = get_pyo3_crate(&options.krate);
let ctx = &Ctx::new(&options.krate);
let Ctx { pyo3_path } = ctx;
let ident = &function.sig.ident;
let vis = &function.vis;
let doc = get_doc(&function.attrs, None);
@ -222,10 +224,10 @@ pub fn pymodule_function_impl(mut function: syn::ItemFn) -> Result<TokenStream>
// FIXME https://github.com/PyO3/pyo3/issues/3903
#[allow(unknown_lints, non_local_definitions)]
const _: () = {
use #krate::impl_::pymodule as impl_;
use #krate::impl_::pymethods::BoundRef;
use #pyo3_path::impl_::pymodule as impl_;
use #pyo3_path::impl_::pymethods::BoundRef;
fn __pyo3_pymodule(module: &#krate::Bound<'_, #krate::types::PyModule>) -> #krate::PyResult<()> {
fn __pyo3_pymodule(module: &#pyo3_path::Bound<'_, #pyo3_path::types::PyModule>) -> #pyo3_path::PyResult<()> {
#ident(#(#module_args),*)
}
@ -247,36 +249,37 @@ pub fn pymodule_function_impl(mut function: syn::ItemFn) -> Result<TokenStream>
fn module_initialization(options: PyModuleOptions, ident: &syn::Ident) -> TokenStream {
let name = options.name.unwrap_or_else(|| ident.unraw());
let krate = get_pyo3_crate(&options.krate);
let ctx = &Ctx::new(&options.krate);
let Ctx { pyo3_path } = ctx;
let pyinit_symbol = format!("PyInit_{}", name);
quote! {
pub const __PYO3_NAME: &'static str = concat!(stringify!(#name), "\0");
pub(super) struct MakeDef;
pub static DEF: #krate::impl_::pymodule::ModuleDef = MakeDef::make_def();
pub static DEF: #pyo3_path::impl_::pymodule::ModuleDef = MakeDef::make_def();
pub fn add_to_module(module: &#krate::Bound<'_, #krate::types::PyModule>) -> #krate::PyResult<()> {
use #krate::prelude::PyModuleMethods;
pub fn add_to_module(module: &#pyo3_path::Bound<'_, #pyo3_path::types::PyModule>) -> #pyo3_path::PyResult<()> {
use #pyo3_path::prelude::PyModuleMethods;
module.add_submodule(DEF.make_module(module.py())?.bind(module.py()))
}
/// This autogenerated function is called by the python interpreter when importing
/// the module.
#[export_name = #pyinit_symbol]
pub unsafe extern "C" fn __pyo3_init() -> *mut #krate::ffi::PyObject {
#krate::impl_::trampoline::module_init(|py| DEF.make_module(py))
pub unsafe extern "C" fn __pyo3_init() -> *mut #pyo3_path::ffi::PyObject {
#pyo3_path::impl_::trampoline::module_init(|py| DEF.make_module(py))
}
}
}
/// Finds and takes care of the #[pyfn(...)] in `#[pymodule]`
fn process_functions_in_module(options: &PyModuleOptions, func: &mut syn::ItemFn) -> Result<()> {
let krate = get_pyo3_crate(&options.krate);
let ctx = &Ctx::new(&options.krate);
let Ctx { pyo3_path } = ctx;
let mut stmts: Vec<syn::Stmt> = vec![syn::parse_quote!(
#[allow(unknown_lints, unused_imports, redundant_imports)]
use #krate::{PyNativeType, types::PyModuleMethods};
use #pyo3_path::{PyNativeType, types::PyModuleMethods};
)];
for mut stmt in func.block.stmts.drain(..) {
@ -287,7 +290,7 @@ fn process_functions_in_module(options: &PyModuleOptions, func: &mut syn::ItemFn
let name = &func.sig.ident;
let statements: Vec<syn::Stmt> = syn::parse_quote! {
#wrapped_function
#module_name.as_borrowed().add_function(#krate::wrap_pyfunction!(#name, #module_name.as_borrowed())?)?;
#module_name.as_borrowed().add_function(#pyo3_path::wrap_pyfunction!(#name, #module_name.as_borrowed())?)?;
};
stmts.extend(statements);
}

View File

@ -1,3 +1,4 @@
use crate::utils::Ctx;
use crate::{
method::{FnArg, FnSpec},
pyfunction::FunctionSignature,
@ -30,8 +31,10 @@ pub fn impl_arg_params(
self_: Option<&syn::Type>,
fastcall: bool,
holders: &mut Vec<TokenStream>,
ctx: &Ctx,
) -> Result<(TokenStream, Vec<TokenStream>)> {
let args_array = syn::Ident::new("output", Span::call_site());
let Ctx { pyo3_path } = ctx;
if !fastcall && is_forwarded_args(&spec.signature) {
// In the varargs convention, we can just pass though if the signature
@ -40,12 +43,12 @@ pub fn impl_arg_params(
.signature
.arguments
.iter()
.map(|arg| impl_arg_param(arg, &mut 0, &args_array, holders))
.map(|arg| impl_arg_param(arg, &mut 0, &args_array, holders, ctx))
.collect::<Result<_>>()?;
return Ok((
quote! {
let _args = _pyo3::impl_::pymethods::BoundRef::ref_from_ptr(py, &_args);
let _kwargs = _pyo3::impl_::pymethods::BoundRef::ref_from_ptr_or_opt(py, &_kwargs);
let _args = #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &_args);
let _kwargs = #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr_or_opt(py, &_kwargs);
},
arg_convert,
));
@ -64,7 +67,7 @@ pub fn impl_arg_params(
.iter()
.map(|(name, required)| {
quote! {
_pyo3::impl_::extract_argument::KeywordOnlyParameterDescription {
#pyo3_path::impl_::extract_argument::KeywordOnlyParameterDescription {
name: #name,
required: #required,
}
@ -78,22 +81,22 @@ pub fn impl_arg_params(
.signature
.arguments
.iter()
.map(|arg| impl_arg_param(arg, &mut option_pos, &args_array, holders))
.map(|arg| impl_arg_param(arg, &mut option_pos, &args_array, holders, ctx))
.collect::<Result<_>>()?;
let args_handler = if spec.signature.python_signature.varargs.is_some() {
quote! { _pyo3::impl_::extract_argument::TupleVarargs }
quote! { #pyo3_path::impl_::extract_argument::TupleVarargs }
} else {
quote! { _pyo3::impl_::extract_argument::NoVarargs }
quote! { #pyo3_path::impl_::extract_argument::NoVarargs }
};
let kwargs_handler = if spec.signature.python_signature.kwargs.is_some() {
quote! { _pyo3::impl_::extract_argument::DictVarkeywords }
quote! { #pyo3_path::impl_::extract_argument::DictVarkeywords }
} else {
quote! { _pyo3::impl_::extract_argument::NoVarkeywords }
quote! { #pyo3_path::impl_::extract_argument::NoVarkeywords }
};
let cls_name = if let Some(cls) = self_ {
quote! { ::std::option::Option::Some(<#cls as _pyo3::type_object::PyTypeInfo>::NAME) }
quote! { ::std::option::Option::Some(<#cls as #pyo3_path::type_object::PyTypeInfo>::NAME) }
} else {
quote! { ::std::option::Option::None }
};
@ -123,7 +126,7 @@ pub fn impl_arg_params(
// create array of arguments, and then parse
Ok((
quote! {
const DESCRIPTION: _pyo3::impl_::extract_argument::FunctionDescription = _pyo3::impl_::extract_argument::FunctionDescription {
const DESCRIPTION: #pyo3_path::impl_::extract_argument::FunctionDescription = #pyo3_path::impl_::extract_argument::FunctionDescription {
cls_name: #cls_name,
func_name: stringify!(#python_name),
positional_parameter_names: &[#(#positional_parameter_names),*],
@ -145,7 +148,11 @@ fn impl_arg_param(
option_pos: &mut usize,
args_array: &syn::Ident,
holders: &mut Vec<TokenStream>,
ctx: &Ctx,
) -> Result<TokenStream> {
let Ctx { pyo3_path } = ctx;
let pyo3_path = pyo3_path.to_tokens_spanned(arg.ty.span());
// Use this macro inside this function, to ensure that all code generated here is associated
// with the function argument
macro_rules! quote_arg_span {
@ -167,7 +174,7 @@ fn impl_arg_param(
let holder = syn::Ident::new(&format!("holder_{}", holders.len()), arg.ty.span());
holders.push(quote_arg_span! {
#[allow(clippy::let_unit_value)]
let mut #holder = _pyo3::impl_::extract_argument::FunctionArgumentHolder::INIT;
let mut #holder = #pyo3_path::impl_::extract_argument::FunctionArgumentHolder::INIT;
});
holder
};
@ -179,7 +186,7 @@ fn impl_arg_param(
);
let holder = push_holder();
return Ok(quote_arg_span! {
_pyo3::impl_::extract_argument::extract_argument(
#pyo3_path::impl_::extract_argument::extract_argument(
&_args,
&mut #holder,
#name_str
@ -192,7 +199,7 @@ fn impl_arg_param(
);
let holder = push_holder();
return Ok(quote_arg_span! {
_pyo3::impl_::extract_argument::extract_optional_argument(
#pyo3_path::impl_::extract_argument::extract_optional_argument(
_kwargs.as_deref(),
&mut #holder,
#name_str,
@ -209,14 +216,17 @@ fn impl_arg_param(
// Option<T> arguments have special treatment: the default should be specified _without_ the
// Some() wrapper. Maybe this should be changed in future?!
if arg.optional.is_some() {
default = Some(default.map_or_else(|| quote!(::std::option::Option::None), some_wrap));
default = Some(default.map_or_else(
|| quote!(::std::option::Option::None),
|tokens| some_wrap(tokens, ctx),
));
}
let tokens = if let Some(expr_path) = arg.attrs.from_py_with.as_ref().map(|attr| &attr.value) {
if let Some(default) = default {
quote_arg_span! {
#[allow(clippy::redundant_closure)]
_pyo3::impl_::extract_argument::from_py_with_with_default(
#pyo3_path::impl_::extract_argument::from_py_with_with_default(
#arg_value.as_deref(),
#name_str,
#expr_path as fn(_) -> _,
@ -225,8 +235,8 @@ fn impl_arg_param(
}
} else {
quote_arg_span! {
_pyo3::impl_::extract_argument::from_py_with(
&_pyo3::impl_::extract_argument::unwrap_required_argument(#arg_value),
#pyo3_path::impl_::extract_argument::from_py_with(
&#pyo3_path::impl_::extract_argument::unwrap_required_argument(#arg_value),
#name_str,
#expr_path as fn(_) -> _,
)?
@ -236,7 +246,7 @@ fn impl_arg_param(
let holder = push_holder();
quote_arg_span! {
#[allow(clippy::redundant_closure)]
_pyo3::impl_::extract_argument::extract_optional_argument(
#pyo3_path::impl_::extract_argument::extract_optional_argument(
#arg_value.as_deref(),
&mut #holder,
#name_str,
@ -247,7 +257,7 @@ fn impl_arg_param(
let holder = push_holder();
quote_arg_span! {
#[allow(clippy::redundant_closure)]
_pyo3::impl_::extract_argument::extract_argument_with_default(
#pyo3_path::impl_::extract_argument::extract_argument_with_default(
#arg_value.as_deref(),
&mut #holder,
#name_str,
@ -257,8 +267,8 @@ fn impl_arg_param(
} else {
let holder = push_holder();
quote_arg_span! {
_pyo3::impl_::extract_argument::extract_argument(
&_pyo3::impl_::extract_argument::unwrap_required_argument(#arg_value),
#pyo3_path::impl_::extract_argument::extract_argument(
&#pyo3_path::impl_::extract_argument::unwrap_required_argument(#arg_value),
&mut #holder,
#name_str
)?

View File

@ -13,7 +13,8 @@ use crate::pymethod::{
impl_py_getter_def, impl_py_setter_def, MethodAndMethodDef, MethodAndSlotDef, PropertyType,
SlotDef, __INT__, __REPR__, __RICHCMP__,
};
use crate::utils::{self, apply_renaming_rule, get_pyo3_crate, PythonDoc};
use crate::utils::Ctx;
use crate::utils::{self, apply_renaming_rule, PythonDoc};
use crate::PyFunctionOptions;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote};
@ -189,7 +190,8 @@ pub fn build_py_class(
) -> syn::Result<TokenStream> {
args.options.take_pyo3_options(&mut class.attrs)?;
let doc = utils::get_doc(&class.attrs, None);
let krate = get_pyo3_crate(&args.options.krate);
let ctx = &Ctx::new(&args.options.krate);
if let Some(lt) = class.generics.lifetimes().next() {
bail_spanned!(
@ -251,7 +253,7 @@ pub fn build_py_class(
}
}
impl_class(&class.ident, &args, doc, field_options, methods_type, krate)
impl_class(&class.ident, &args, doc, field_options, methods_type, ctx)
}
enum Annotated<X, Y> {
@ -342,9 +344,10 @@ fn impl_class(
doc: PythonDoc,
field_options: Vec<(&syn::Field, FieldPyO3Options)>,
methods_type: PyClassMethodsType,
krate: syn::Path,
ctx: &Ctx,
) -> syn::Result<TokenStream> {
let pytypeinfo_impl = impl_pytypeinfo(cls, args, None);
let Ctx { pyo3_path } = ctx;
let pytypeinfo_impl = impl_pytypeinfo(cls, args, None, ctx);
let py_class_impl = PyClassImplsBuilder::new(
cls,
@ -355,19 +358,18 @@ fn impl_class(
args.options.rename_all.as_ref(),
args.options.frozen,
field_options,
ctx,
)?,
vec![],
)
.doc(doc)
.impl_all()?;
.impl_all(ctx)?;
Ok(quote! {
// FIXME https://github.com/PyO3/pyo3/issues/3903
#[allow(unknown_lints, non_local_definitions)]
const _: () = {
use #krate as _pyo3;
impl _pyo3::types::DerefToPyAny for #cls {}
impl #pyo3_path::types::DerefToPyAny for #cls {}
#pytypeinfo_impl
@ -405,6 +407,7 @@ pub fn build_py_enum(
) -> syn::Result<TokenStream> {
args.options.take_pyo3_options(&mut enum_.attrs)?;
let ctx = &Ctx::new(&args.options.krate);
if let Some(extends) = &args.options.extends {
bail_spanned!(extends.span() => "enums can't extend from other classes");
} else if let Some(subclass) = &args.options.subclass {
@ -415,7 +418,7 @@ pub fn build_py_enum(
let doc = utils::get_doc(&enum_.attrs, None);
let enum_ = PyClassEnum::new(enum_)?;
impl_enum(enum_, &args, doc, method_type)
impl_enum(enum_, &args, doc, method_type, ctx)
}
struct PyClassSimpleEnum<'a> {
@ -665,11 +668,14 @@ fn impl_enum(
args: &PyClassArgs,
doc: PythonDoc,
methods_type: PyClassMethodsType,
ctx: &Ctx,
) -> Result<TokenStream> {
match enum_ {
PyClassEnum::Simple(simple_enum) => impl_simple_enum(simple_enum, args, doc, methods_type),
PyClassEnum::Simple(simple_enum) => {
impl_simple_enum(simple_enum, args, doc, methods_type, ctx)
}
PyClassEnum::Complex(complex_enum) => {
impl_complex_enum(complex_enum, args, doc, methods_type)
impl_complex_enum(complex_enum, args, doc, methods_type, ctx)
}
}
}
@ -679,12 +685,13 @@ fn impl_simple_enum(
args: &PyClassArgs,
doc: PythonDoc,
methods_type: PyClassMethodsType,
ctx: &Ctx,
) -> Result<TokenStream> {
let krate = get_pyo3_crate(&args.options.krate);
let Ctx { pyo3_path } = ctx;
let cls = simple_enum.ident;
let ty: syn::Type = syn::parse_quote!(#cls);
let variants = simple_enum.variants;
let pytypeinfo = impl_pytypeinfo(cls, args, None);
let pytypeinfo = impl_pytypeinfo(cls, args, None, ctx);
let (default_repr, default_repr_slot) = {
let variants_repr = variants.iter().map(|variant| {
@ -704,7 +711,8 @@ fn impl_simple_enum(
}
}
};
let repr_slot = generate_default_protocol_slot(&ty, &mut repr_impl, &__REPR__).unwrap();
let repr_slot =
generate_default_protocol_slot(&ty, &mut repr_impl, &__REPR__, ctx).unwrap();
(repr_impl, repr_slot)
};
@ -723,7 +731,7 @@ fn impl_simple_enum(
}
}
};
let int_slot = generate_default_protocol_slot(&ty, &mut int_impl, &__INT__).unwrap();
let int_slot = generate_default_protocol_slot(&ty, &mut int_impl, &__INT__, ctx).unwrap();
(int_impl, int_slot)
};
@ -731,30 +739,30 @@ fn impl_simple_enum(
let mut richcmp_impl: syn::ImplItemFn = syn::parse_quote! {
fn __pyo3__richcmp__(
&self,
py: _pyo3::Python,
other: &_pyo3::PyAny,
op: _pyo3::basic::CompareOp
) -> _pyo3::PyResult<_pyo3::PyObject> {
use _pyo3::conversion::ToPyObject;
py: #pyo3_path::Python,
other: &#pyo3_path::PyAny,
op: #pyo3_path::basic::CompareOp
) -> #pyo3_path::PyResult<#pyo3_path::PyObject> {
use #pyo3_path::conversion::ToPyObject;
use ::core::result::Result::*;
match op {
_pyo3::basic::CompareOp::Eq => {
#pyo3_path::basic::CompareOp::Eq => {
let self_val = self.__pyo3__int__();
if let Ok(i) = other.extract::<#repr_type>() {
return Ok((self_val == i).to_object(py));
}
if let Ok(other) = other.extract::<_pyo3::PyRef<Self>>() {
if let Ok(other) = other.extract::<#pyo3_path::PyRef<Self>>() {
return Ok((self_val == other.__pyo3__int__()).to_object(py));
}
return Ok(py.NotImplemented());
}
_pyo3::basic::CompareOp::Ne => {
#pyo3_path::basic::CompareOp::Ne => {
let self_val = self.__pyo3__int__();
if let Ok(i) = other.extract::<#repr_type>() {
return Ok((self_val != i).to_object(py));
}
if let Ok(other) = other.extract::<_pyo3::PyRef<Self>>() {
if let Ok(other) = other.extract::<#pyo3_path::PyRef<Self>>() {
return Ok((self_val != other.__pyo3__int__()).to_object(py));
}
@ -765,7 +773,7 @@ fn impl_simple_enum(
}
};
let richcmp_slot =
generate_default_protocol_slot(&ty, &mut richcmp_impl, &__RICHCMP__).unwrap();
generate_default_protocol_slot(&ty, &mut richcmp_impl, &__RICHCMP__, ctx).unwrap();
(richcmp_impl, richcmp_slot)
};
@ -778,18 +786,17 @@ fn impl_simple_enum(
simple_enum_default_methods(
cls,
variants.iter().map(|v| (v.ident, v.get_python_name(args))),
ctx,
),
default_slots,
)
.doc(doc)
.impl_all()?;
.impl_all(ctx)?;
Ok(quote! {
// FIXME https://github.com/PyO3/pyo3/issues/3903
#[allow(unknown_lints, non_local_definitions)]
const _: () = {
use #krate as _pyo3;
#pytypeinfo
#pyclass_impls
@ -810,7 +817,10 @@ fn impl_complex_enum(
args: &PyClassArgs,
doc: PythonDoc,
methods_type: PyClassMethodsType,
ctx: &Ctx,
) -> Result<TokenStream> {
let Ctx { pyo3_path } = ctx;
// Need to rig the enum PyClass options
let args = {
let mut rigged_args = args.clone();
@ -821,10 +831,10 @@ fn impl_complex_enum(
rigged_args
};
let krate = get_pyo3_crate(&args.options.krate);
let ctx = &Ctx::new(&args.options.krate);
let cls = complex_enum.ident;
let variants = complex_enum.variants;
let pytypeinfo = impl_pytypeinfo(cls, &args, None);
let pytypeinfo = impl_pytypeinfo(cls, &args, None, ctx);
let default_slots = vec![];
@ -837,6 +847,7 @@ fn impl_complex_enum(
variants
.iter()
.map(|v| (v.get_ident(), v.get_python_name(&args))),
ctx,
),
default_slots,
)
@ -851,17 +862,17 @@ fn impl_complex_enum(
let variant_cls = gen_complex_enum_variant_class_ident(cls, variant.get_ident());
quote! {
#cls::#variant_ident { .. } => {
let pyclass_init = _pyo3::PyClassInitializer::from(self).add_subclass(#variant_cls);
let variant_value = _pyo3::Py::new(py, pyclass_init).unwrap();
_pyo3::IntoPy::into_py(variant_value, py)
let pyclass_init = #pyo3_path::PyClassInitializer::from(self).add_subclass(#variant_cls);
let variant_value = #pyo3_path::Py::new(py, pyclass_init).unwrap();
#pyo3_path::IntoPy::into_py(variant_value, py)
}
}
})
.collect();
quote! {
impl _pyo3::IntoPy<_pyo3::PyObject> for #cls {
fn into_py(self, py: _pyo3::Python) -> _pyo3::PyObject {
impl #pyo3_path::IntoPy<#pyo3_path::PyObject> for #cls {
fn into_py(self, py: #pyo3_path::Python) -> #pyo3_path::PyObject {
match self {
#(#match_arms)*
}
@ -871,11 +882,11 @@ fn impl_complex_enum(
};
let pyclass_impls: TokenStream = vec![
impl_builder.impl_pyclass(),
impl_builder.impl_extractext(),
impl_builder.impl_pyclass(ctx),
impl_builder.impl_extractext(ctx),
enum_into_py_impl,
impl_builder.impl_pyclassimpl()?,
impl_builder.impl_freelist(),
impl_builder.impl_pyclassimpl(ctx)?,
impl_builder.impl_freelist(ctx),
]
.into_iter()
.collect();
@ -900,12 +911,12 @@ fn impl_complex_enum(
options: parse_quote!(extends = #cls, frozen),
};
let variant_cls_pytypeinfo = impl_pytypeinfo(&variant_cls, &variant_args, None);
let variant_cls_pytypeinfo = impl_pytypeinfo(&variant_cls, &variant_args, None, ctx);
variant_cls_pytypeinfos.push(variant_cls_pytypeinfo);
let variant_new = complex_enum_variant_new(cls, variant)?;
let variant_new = complex_enum_variant_new(cls, variant, ctx)?;
let (variant_cls_impl, field_getters) = impl_complex_enum_variant_cls(cls, variant)?;
let (variant_cls_impl, field_getters) = impl_complex_enum_variant_cls(cls, variant, ctx)?;
variant_cls_impls.push(variant_cls_impl);
let pyclass_impl = PyClassImplsBuilder::new(
@ -915,7 +926,7 @@ fn impl_complex_enum(
field_getters,
vec![variant_new],
)
.impl_all()?;
.impl_all(ctx)?;
variant_cls_pyclass_impls.push(pyclass_impl);
}
@ -924,8 +935,6 @@ fn impl_complex_enum(
// FIXME https://github.com/PyO3/pyo3/issues/3903
#[allow(unknown_lints, non_local_definitions)]
const _: () = {
use #krate as _pyo3;
#pytypeinfo
#pyclass_impls
@ -948,10 +957,11 @@ fn impl_complex_enum(
fn impl_complex_enum_variant_cls(
enum_name: &syn::Ident,
variant: &PyClassEnumVariant<'_>,
ctx: &Ctx,
) -> Result<(TokenStream, Vec<MethodAndMethodDef>)> {
match variant {
PyClassEnumVariant::Struct(struct_variant) => {
impl_complex_enum_struct_variant_cls(enum_name, struct_variant)
impl_complex_enum_struct_variant_cls(enum_name, struct_variant, ctx)
}
}
}
@ -959,7 +969,9 @@ fn impl_complex_enum_variant_cls(
fn impl_complex_enum_struct_variant_cls(
enum_name: &syn::Ident,
variant: &PyClassEnumStructVariant<'_>,
ctx: &Ctx,
) -> Result<(TokenStream, Vec<MethodAndMethodDef>)> {
let Ctx { pyo3_path } = ctx;
let variant_ident = &variant.ident;
let variant_cls = gen_complex_enum_variant_class_ident(enum_name, variant.ident);
let variant_cls_type = parse_quote!(#variant_cls);
@ -978,10 +990,11 @@ fn impl_complex_enum_struct_variant_cls(
field_name,
field_type,
field.span,
ctx,
)?;
let field_getter_impl = quote! {
fn #field_name(slf: _pyo3::PyRef<Self>) -> _pyo3::PyResult<#field_type> {
fn #field_name(slf: #pyo3_path::PyRef<Self>) -> #pyo3_path::PyResult<#field_type> {
match &*slf.into_super() {
#enum_name::#variant_ident { #field_name, .. } => Ok(#field_name.clone()),
_ => unreachable!("Wrong complex enum variant found in variant wrapper PyClass"),
@ -999,9 +1012,9 @@ fn impl_complex_enum_struct_variant_cls(
#[doc(hidden)]
#[allow(non_snake_case)]
impl #variant_cls {
fn __pymethod_constructor__(py: _pyo3::Python<'_>, #(#fields_with_types,)*) -> _pyo3::PyClassInitializer<#variant_cls> {
fn __pymethod_constructor__(py: #pyo3_path::Python<'_>, #(#fields_with_types,)*) -> #pyo3_path::PyClassInitializer<#variant_cls> {
let base_value = #enum_name::#variant_ident { #(#field_names,)* };
_pyo3::PyClassInitializer::from(base_value).add_subclass(#variant_cls)
#pyo3_path::PyClassInitializer::from(base_value).add_subclass(#variant_cls)
}
#(#field_getter_impls)*
@ -1019,11 +1032,13 @@ fn generate_default_protocol_slot(
cls: &syn::Type,
method: &mut syn::ImplItemFn,
slot: &SlotDef,
ctx: &Ctx,
) -> syn::Result<MethodAndSlotDef> {
let spec = FnSpec::parse(
&mut method.sig,
&mut Vec::new(),
PyFunctionOptions::default(),
ctx,
)
.unwrap();
let name = spec.name.to_string();
@ -1031,12 +1046,14 @@ fn generate_default_protocol_slot(
&syn::parse_quote!(#cls),
&spec,
&format!("__default_{}__", name),
ctx,
)
}
fn simple_enum_default_methods<'a>(
cls: &'a syn::Ident,
unit_variant_names: impl IntoIterator<Item = (&'a syn::Ident, Cow<'a, syn::Ident>)>,
ctx: &Ctx,
) -> Vec<MethodAndMethodDef> {
let cls_type = syn::parse_quote!(#cls);
let variant_to_attribute = |var_ident: &syn::Ident, py_ident: &syn::Ident| ConstSpec {
@ -1047,18 +1064,19 @@ fn simple_enum_default_methods<'a>(
kw: syn::parse_quote! { name },
value: NameLitStr(py_ident.clone()),
}),
deprecations: Default::default(),
deprecations: Deprecations::new(ctx),
},
};
unit_variant_names
.into_iter()
.map(|(var, py_name)| gen_py_const(&cls_type, &variant_to_attribute(var, &py_name)))
.map(|(var, py_name)| gen_py_const(&cls_type, &variant_to_attribute(var, &py_name), ctx))
.collect()
}
fn complex_enum_default_methods<'a>(
cls: &'a syn::Ident,
variant_names: impl IntoIterator<Item = (&'a syn::Ident, Cow<'a, syn::Ident>)>,
ctx: &Ctx,
) -> Vec<MethodAndMethodDef> {
let cls_type = syn::parse_quote!(#cls);
let variant_to_attribute = |var_ident: &syn::Ident, py_ident: &syn::Ident| ConstSpec {
@ -1069,13 +1087,13 @@ fn complex_enum_default_methods<'a>(
kw: syn::parse_quote! { name },
value: NameLitStr(py_ident.clone()),
}),
deprecations: Default::default(),
deprecations: Deprecations::new(ctx),
},
};
variant_names
.into_iter()
.map(|(var, py_name)| {
gen_complex_enum_variant_attr(cls, &cls_type, &variant_to_attribute(var, &py_name))
gen_complex_enum_variant_attr(cls, &cls_type, &variant_to_attribute(var, &py_name), ctx)
})
.collect()
}
@ -1083,8 +1101,10 @@ fn complex_enum_default_methods<'a>(
pub fn gen_complex_enum_variant_attr(
cls: &syn::Ident,
cls_type: &syn::Type,
spec: &ConstSpec,
spec: &ConstSpec<'_>,
ctx: &Ctx,
) -> MethodAndMethodDef {
let Ctx { pyo3_path } = ctx;
let member = &spec.rust_ident;
let wrapper_ident = format_ident!("__pymethod_variant_cls_{}__", member);
let deprecations = &spec.attributes.deprecations;
@ -1092,17 +1112,17 @@ pub fn gen_complex_enum_variant_attr(
let variant_cls = format_ident!("{}_{}", cls, member);
let associated_method = quote! {
fn #wrapper_ident(py: _pyo3::Python<'_>) -> _pyo3::PyResult<_pyo3::PyObject> {
fn #wrapper_ident(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::PyObject> {
#deprecations
::std::result::Result::Ok(py.get_type_bound::<#variant_cls>().into_any().unbind())
}
};
let method_def = quote! {
_pyo3::class::PyMethodDefType::ClassAttribute({
_pyo3::class::PyClassAttributeDef::new(
#pyo3_path::class::PyMethodDefType::ClassAttribute({
#pyo3_path::class::PyClassAttributeDef::new(
#python_name,
_pyo3::impl_::pymethods::PyClassAttributeFactory(#cls_type::#wrapper_ident)
#pyo3_path::impl_::pymethods::PyClassAttributeFactory(#cls_type::#wrapper_ident)
)
})
};
@ -1116,10 +1136,11 @@ pub fn gen_complex_enum_variant_attr(
fn complex_enum_variant_new<'a>(
cls: &'a syn::Ident,
variant: &'a PyClassEnumVariant<'a>,
ctx: &Ctx,
) -> Result<MethodAndSlotDef> {
match variant {
PyClassEnumVariant::Struct(struct_variant) => {
complex_enum_struct_variant_new(cls, struct_variant)
complex_enum_struct_variant_new(cls, struct_variant, ctx)
}
}
}
@ -1127,12 +1148,14 @@ fn complex_enum_variant_new<'a>(
fn complex_enum_struct_variant_new<'a>(
cls: &'a syn::Ident,
variant: &'a PyClassEnumStructVariant<'a>,
ctx: &Ctx,
) -> Result<MethodAndSlotDef> {
let Ctx { pyo3_path } = ctx;
let variant_cls = format_ident!("{}_{}", cls, variant.ident);
let variant_cls_type: syn::Type = parse_quote!(#variant_cls);
let arg_py_ident: syn::Ident = parse_quote!(py);
let arg_py_type: syn::Type = parse_quote!(_pyo3::Python<'_>);
let arg_py_type: syn::Type = parse_quote!(#pyo3_path::Python<'_>);
let args = {
let mut no_pyo3_attrs = vec![];
@ -1180,10 +1203,10 @@ fn complex_enum_struct_variant_new<'a>(
text_signature: None,
asyncness: None,
unsafety: None,
deprecations: Deprecations::default(),
deprecations: Deprecations::new(ctx),
};
crate::pymethod::impl_py_method_def_new(&variant_cls_type, &spec)
crate::pymethod::impl_py_method_def_new(&variant_cls_type, &spec, ctx)
}
fn complex_enum_variant_field_getter<'a>(
@ -1191,6 +1214,7 @@ fn complex_enum_variant_field_getter<'a>(
field_name: &'a syn::Ident,
field_type: &'a syn::Type,
field_span: Span,
ctx: &Ctx,
) -> Result<MethodAndMethodDef> {
let signature = crate::pyfunction::FunctionSignature::from_arguments(vec![])?;
@ -1206,7 +1230,7 @@ fn complex_enum_variant_field_getter<'a>(
text_signature: None,
asyncness: None,
unsafety: None,
deprecations: Deprecations::default(),
deprecations: Deprecations::new(ctx),
};
let property_type = crate::pymethod::PropertyType::Function {
@ -1215,7 +1239,7 @@ fn complex_enum_variant_field_getter<'a>(
doc: crate::get_doc(&[], None),
};
let getter = crate::pymethod::impl_py_getter_def(variant_cls_type, property_type)?;
let getter = crate::pymethod::impl_py_getter_def(variant_cls_type, property_type, ctx)?;
Ok(getter)
}
@ -1224,6 +1248,7 @@ fn descriptors_to_items(
rename_all: Option<&RenameAllAttribute>,
frozen: Option<frozen>,
field_options: Vec<(&syn::Field, FieldPyO3Options)>,
ctx: &Ctx,
) -> syn::Result<Vec<MethodAndMethodDef>> {
let ty = syn::parse_quote!(#cls);
let mut items = Vec::new();
@ -1246,6 +1271,7 @@ fn descriptors_to_items(
python_name: options.name.as_ref(),
renaming_rule: rename_all.map(|rename_all| rename_all.value.rule),
},
ctx,
)?;
items.push(getter);
}
@ -1260,6 +1286,7 @@ fn descriptors_to_items(
python_name: options.name.as_ref(),
renaming_rule: rename_all.map(|rename_all| rename_all.value.rule),
},
ctx,
)?;
items.push(setter);
};
@ -1270,8 +1297,10 @@ fn descriptors_to_items(
fn impl_pytypeinfo(
cls: &syn::Ident,
attr: &PyClassArgs,
deprecations: Option<&Deprecations>,
deprecations: Option<&Deprecations<'_>>,
ctx: &Ctx,
) -> TokenStream {
let Ctx { pyo3_path } = ctx;
let cls_name = get_class_python_name(cls, attr).to_string();
let module = if let Some(ModuleAttribute { value, .. }) = &attr.options.module {
@ -1282,20 +1311,20 @@ fn impl_pytypeinfo(
quote! {
#[allow(deprecated)]
unsafe impl _pyo3::type_object::HasPyGilRef for #cls {
type AsRefTarget = _pyo3::PyCell<Self>;
unsafe impl #pyo3_path::type_object::HasPyGilRef for #cls {
type AsRefTarget = #pyo3_path::PyCell<Self>;
}
unsafe impl _pyo3::type_object::PyTypeInfo for #cls {
unsafe impl #pyo3_path::type_object::PyTypeInfo for #cls {
const NAME: &'static str = #cls_name;
const MODULE: ::std::option::Option<&'static str> = #module;
#[inline]
fn type_object_raw(py: _pyo3::Python<'_>) -> *mut _pyo3::ffi::PyTypeObject {
use _pyo3::prelude::PyTypeMethods;
fn type_object_raw(py: #pyo3_path::Python<'_>) -> *mut #pyo3_path::ffi::PyTypeObject {
use #pyo3_path::prelude::PyTypeMethods;
#deprecations
<#cls as _pyo3::impl_::pyclass::PyClassImpl>::lazy_type_object()
<#cls as #pyo3_path::impl_::pyclass::PyClassImpl>::lazy_type_object()
.get_or_init(py)
.as_type_ptr()
}
@ -1342,82 +1371,85 @@ impl<'a> PyClassImplsBuilder<'a> {
}
}
fn impl_all(&self) -> Result<TokenStream> {
fn impl_all(&self, ctx: &Ctx) -> Result<TokenStream> {
let tokens = vec![
self.impl_pyclass(),
self.impl_extractext(),
self.impl_into_py(),
self.impl_pyclassimpl()?,
self.impl_freelist(),
self.impl_pyclass(ctx),
self.impl_extractext(ctx),
self.impl_into_py(ctx),
self.impl_pyclassimpl(ctx)?,
self.impl_freelist(ctx),
]
.into_iter()
.collect();
Ok(tokens)
}
fn impl_pyclass(&self) -> TokenStream {
fn impl_pyclass(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
let cls = self.cls;
let frozen = if self.attr.options.frozen.is_some() {
quote! { _pyo3::pyclass::boolean_struct::True }
quote! { #pyo3_path::pyclass::boolean_struct::True }
} else {
quote! { _pyo3::pyclass::boolean_struct::False }
quote! { #pyo3_path::pyclass::boolean_struct::False }
};
quote! {
impl _pyo3::PyClass for #cls {
impl #pyo3_path::PyClass for #cls {
type Frozen = #frozen;
}
}
}
fn impl_extractext(&self) -> TokenStream {
fn impl_extractext(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
let cls = self.cls;
if self.attr.options.frozen.is_some() {
quote! {
impl<'a, 'py> _pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a #cls
impl<'a, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a #cls
{
type Holder = ::std::option::Option<_pyo3::PyRef<'py, #cls>>;
type Holder = ::std::option::Option<#pyo3_path::PyRef<'py, #cls>>;
#[inline]
fn extract(obj: &'a _pyo3::Bound<'py, _pyo3::PyAny>, holder: &'a mut Self::Holder) -> _pyo3::PyResult<Self> {
_pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder)
fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'a mut Self::Holder) -> #pyo3_path::PyResult<Self> {
#pyo3_path::impl_::extract_argument::extract_pyclass_ref(obj, holder)
}
}
}
} else {
quote! {
impl<'a, 'py> _pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a #cls
impl<'a, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a #cls
{
type Holder = ::std::option::Option<_pyo3::PyRef<'py, #cls>>;
type Holder = ::std::option::Option<#pyo3_path::PyRef<'py, #cls>>;
#[inline]
fn extract(obj: &'a _pyo3::Bound<'py, _pyo3::PyAny>, holder: &'a mut Self::Holder) -> _pyo3::PyResult<Self> {
_pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder)
fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'a mut Self::Holder) -> #pyo3_path::PyResult<Self> {
#pyo3_path::impl_::extract_argument::extract_pyclass_ref(obj, holder)
}
}
impl<'a, 'py> _pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a mut #cls
impl<'a, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a mut #cls
{
type Holder = ::std::option::Option<_pyo3::PyRefMut<'py, #cls>>;
type Holder = ::std::option::Option<#pyo3_path::PyRefMut<'py, #cls>>;
#[inline]
fn extract(obj: &'a _pyo3::Bound<'py, _pyo3::PyAny>, holder: &'a mut Self::Holder) -> _pyo3::PyResult<Self> {
_pyo3::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder)
fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'a mut Self::Holder) -> #pyo3_path::PyResult<Self> {
#pyo3_path::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder)
}
}
}
}
}
fn impl_into_py(&self) -> TokenStream {
fn impl_into_py(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
let cls = self.cls;
let attr = self.attr;
// If #cls is not extended type, we allow Self->PyObject conversion
if attr.options.extends.is_none() {
quote! {
impl _pyo3::IntoPy<_pyo3::PyObject> for #cls {
fn into_py(self, py: _pyo3::Python) -> _pyo3::PyObject {
_pyo3::IntoPy::into_py(_pyo3::Py::new(py, self).unwrap(), py)
impl #pyo3_path::IntoPy<#pyo3_path::PyObject> for #cls {
fn into_py(self, py: #pyo3_path::Python) -> #pyo3_path::PyObject {
#pyo3_path::IntoPy::into_py(#pyo3_path::Py::new(py, self).unwrap(), py)
}
}
}
@ -1425,13 +1457,14 @@ impl<'a> PyClassImplsBuilder<'a> {
quote! {}
}
}
fn impl_pyclassimpl(&self) -> Result<TokenStream> {
fn impl_pyclassimpl(&self, ctx: &Ctx) -> Result<TokenStream> {
let Ctx { pyo3_path } = ctx;
let cls = self.cls;
let doc = self.doc.as_ref().map_or(quote! {"\0"}, |doc| quote! {#doc});
let is_basetype = self.attr.options.subclass.is_some();
let base = match &self.attr.options.extends {
Some(extends_attr) => extends_attr.value.clone(),
None => parse_quote! { _pyo3::PyAny },
None => parse_quote! { #pyo3_path::PyAny },
};
let is_subclass = self.attr.options.extends.is_some();
let is_mapping: bool = self.attr.options.mapping.is_some();
@ -1444,8 +1477,8 @@ impl<'a> PyClassImplsBuilder<'a> {
let dict_offset = if self.attr.options.dict.is_some() {
quote! {
fn dict_offset() -> ::std::option::Option<_pyo3::ffi::Py_ssize_t> {
::std::option::Option::Some(_pyo3::impl_::pyclass::dict_offset::<Self>())
fn dict_offset() -> ::std::option::Option<#pyo3_path::ffi::Py_ssize_t> {
::std::option::Option::Some(#pyo3_path::impl_::pyclass::dict_offset::<Self>())
}
}
} else {
@ -1455,8 +1488,8 @@ impl<'a> PyClassImplsBuilder<'a> {
// insert space for weak ref
let weaklist_offset = if self.attr.options.weakref.is_some() {
quote! {
fn weaklist_offset() -> ::std::option::Option<_pyo3::ffi::Py_ssize_t> {
::std::option::Option::Some(_pyo3::impl_::pyclass::weaklist_offset::<Self>())
fn weaklist_offset() -> ::std::option::Option<#pyo3_path::ffi::Py_ssize_t> {
::std::option::Option::Some(#pyo3_path::impl_::pyclass::weaklist_offset::<Self>())
}
}
} else {
@ -1464,9 +1497,9 @@ impl<'a> PyClassImplsBuilder<'a> {
};
let thread_checker = if self.attr.options.unsendable.is_some() {
quote! { _pyo3::impl_::pyclass::ThreadCheckerImpl }
quote! { #pyo3_path::impl_::pyclass::ThreadCheckerImpl }
} else {
quote! { _pyo3::impl_::pyclass::SendablePyClass<#cls> }
quote! { #pyo3_path::impl_::pyclass::SendablePyClass<#cls> }
};
let (pymethods_items, inventory, inventory_class) = match self.methods_type {
@ -1481,13 +1514,13 @@ impl<'a> PyClassImplsBuilder<'a> {
quote! {
::std::boxed::Box::new(
::std::iter::Iterator::map(
_pyo3::inventory::iter::<<Self as _pyo3::impl_::pyclass::PyClassImpl>::Inventory>(),
_pyo3::impl_::pyclass::PyClassInventory::items
#pyo3_path::inventory::iter::<<Self as #pyo3_path::impl_::pyclass::PyClassImpl>::Inventory>(),
#pyo3_path::impl_::pyclass::PyClassInventory::items
)
)
},
Some(quote! { type Inventory = #inventory_class_name; }),
Some(define_inventory_class(&inventory_class_name)),
Some(define_inventory_class(&inventory_class_name, ctx)),
)
}
};
@ -1504,7 +1537,7 @@ impl<'a> PyClassImplsBuilder<'a> {
let default_method_defs = self.default_methods.iter().map(|meth| &meth.method_def);
let default_slot_defs = self.default_slots.iter().map(|slot| &slot.slot_def);
let freelist_slots = self.freelist_slots();
let freelist_slots = self.freelist_slots(ctx);
let class_mutability = if self.attr.options.frozen.is_some() {
quote! {
@ -1519,26 +1552,26 @@ impl<'a> PyClassImplsBuilder<'a> {
let cls = self.cls;
let attr = self.attr;
let dict = if attr.options.dict.is_some() {
quote! { _pyo3::impl_::pyclass::PyClassDictSlot }
quote! { #pyo3_path::impl_::pyclass::PyClassDictSlot }
} else {
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
quote! { #pyo3_path::impl_::pyclass::PyClassDummySlot }
};
// insert space for weak ref
let weakref = if attr.options.weakref.is_some() {
quote! { _pyo3::impl_::pyclass::PyClassWeakRefSlot }
quote! { #pyo3_path::impl_::pyclass::PyClassWeakRefSlot }
} else {
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
quote! { #pyo3_path::impl_::pyclass::PyClassDummySlot }
};
let base_nativetype = if attr.options.extends.is_some() {
quote! { <Self::BaseType as _pyo3::impl_::pyclass::PyClassBaseType>::BaseNativeType }
quote! { <Self::BaseType as #pyo3_path::impl_::pyclass::PyClassBaseType>::BaseNativeType }
} else {
quote! { _pyo3::PyAny }
quote! { #pyo3_path::PyAny }
};
Ok(quote! {
impl _pyo3::impl_::pyclass::PyClassImpl for #cls {
impl #pyo3_path::impl_::pyclass::PyClassImpl for #cls {
const IS_BASETYPE: bool = #is_basetype;
const IS_SUBCLASS: bool = #is_subclass;
const IS_MAPPING: bool = #is_mapping;
@ -1547,13 +1580,13 @@ impl<'a> PyClassImplsBuilder<'a> {
type BaseType = #base;
type ThreadChecker = #thread_checker;
#inventory
type PyClassMutability = <<#base as _pyo3::impl_::pyclass::PyClassBaseType>::PyClassMutability as _pyo3::impl_::pycell::PyClassMutability>::#class_mutability;
type PyClassMutability = <<#base as #pyo3_path::impl_::pyclass::PyClassBaseType>::PyClassMutability as #pyo3_path::impl_::pycell::PyClassMutability>::#class_mutability;
type Dict = #dict;
type WeakRef = #weakref;
type BaseNativeType = #base_nativetype;
fn items_iter() -> _pyo3::impl_::pyclass::PyClassItemsIter {
use _pyo3::impl_::pyclass::*;
fn items_iter() -> #pyo3_path::impl_::pyclass::PyClassItemsIter {
use #pyo3_path::impl_::pyclass::*;
let collector = PyClassImplCollector::<Self>::new();
static INTRINSIC_ITEMS: PyClassItems = PyClassItems {
methods: &[#(#default_method_defs),*],
@ -1562,12 +1595,12 @@ impl<'a> PyClassImplsBuilder<'a> {
PyClassItemsIter::new(&INTRINSIC_ITEMS, #pymethods_items)
}
fn doc(py: _pyo3::Python<'_>) -> _pyo3::PyResult<&'static ::std::ffi::CStr> {
use _pyo3::impl_::pyclass::*;
static DOC: _pyo3::sync::GILOnceCell<::std::borrow::Cow<'static, ::std::ffi::CStr>> = _pyo3::sync::GILOnceCell::new();
fn doc(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<&'static ::std::ffi::CStr> {
use #pyo3_path::impl_::pyclass::*;
static DOC: #pyo3_path::sync::GILOnceCell<::std::borrow::Cow<'static, ::std::ffi::CStr>> = #pyo3_path::sync::GILOnceCell::new();
DOC.get_or_try_init(py, || {
let collector = PyClassImplCollector::<Self>::new();
build_pyclass_doc(<#cls as _pyo3::PyTypeInfo>::NAME, #doc, collector.new_text_signature())
build_pyclass_doc(<#cls as #pyo3_path::PyTypeInfo>::NAME, #doc, collector.new_text_signature())
}).map(::std::ops::Deref::deref)
}
@ -1575,8 +1608,8 @@ impl<'a> PyClassImplsBuilder<'a> {
#weaklist_offset
fn lazy_type_object() -> &'static _pyo3::impl_::pyclass::LazyTypeObject<Self> {
use _pyo3::impl_::pyclass::LazyTypeObject;
fn lazy_type_object() -> &'static #pyo3_path::impl_::pyclass::LazyTypeObject<Self> {
use #pyo3_path::impl_::pyclass::LazyTypeObject;
static TYPE_OBJECT: LazyTypeObject<#cls> = LazyTypeObject::new();
&TYPE_OBJECT
}
@ -1592,20 +1625,21 @@ impl<'a> PyClassImplsBuilder<'a> {
})
}
fn impl_freelist(&self) -> TokenStream {
fn impl_freelist(&self, ctx: &Ctx) -> TokenStream {
let cls = self.cls;
let Ctx { pyo3_path } = ctx;
self.attr.options.freelist.as_ref().map_or(quote!{}, |freelist| {
let freelist = &freelist.value;
quote! {
impl _pyo3::impl_::pyclass::PyClassWithFreeList for #cls {
impl #pyo3_path::impl_::pyclass::PyClassWithFreeList for #cls {
#[inline]
fn get_free_list(py: _pyo3::Python<'_>) -> &mut _pyo3::impl_::freelist::FreeList<*mut _pyo3::ffi::PyObject> {
static mut FREELIST: *mut _pyo3::impl_::freelist::FreeList<*mut _pyo3::ffi::PyObject> = 0 as *mut _;
fn get_free_list(py: #pyo3_path::Python<'_>) -> &mut #pyo3_path::impl_::freelist::FreeList<*mut #pyo3_path::ffi::PyObject> {
static mut FREELIST: *mut #pyo3_path::impl_::freelist::FreeList<*mut #pyo3_path::ffi::PyObject> = 0 as *mut _;
unsafe {
if FREELIST.is_null() {
FREELIST = ::std::boxed::Box::into_raw(::std::boxed::Box::new(
_pyo3::impl_::freelist::FreeList::with_capacity(#freelist)));
#pyo3_path::impl_::freelist::FreeList::with_capacity(#freelist)));
}
&mut *FREELIST
}
@ -1615,21 +1649,22 @@ impl<'a> PyClassImplsBuilder<'a> {
})
}
fn freelist_slots(&self) -> Vec<TokenStream> {
fn freelist_slots(&self, ctx: &Ctx) -> Vec<TokenStream> {
let Ctx { pyo3_path } = ctx;
let cls = self.cls;
if self.attr.options.freelist.is_some() {
vec![
quote! {
_pyo3::ffi::PyType_Slot {
slot: _pyo3::ffi::Py_tp_alloc,
pfunc: _pyo3::impl_::pyclass::alloc_with_freelist::<#cls> as *mut _,
#pyo3_path::ffi::PyType_Slot {
slot: #pyo3_path::ffi::Py_tp_alloc,
pfunc: #pyo3_path::impl_::pyclass::alloc_with_freelist::<#cls> as *mut _,
}
},
quote! {
_pyo3::ffi::PyType_Slot {
slot: _pyo3::ffi::Py_tp_free,
pfunc: _pyo3::impl_::pyclass::free_with_freelist::<#cls> as *mut _,
#pyo3_path::ffi::PyType_Slot {
slot: #pyo3_path::ffi::Py_tp_free,
pfunc: #pyo3_path::impl_::pyclass::free_with_freelist::<#cls> as *mut _,
}
},
]
@ -1639,25 +1674,26 @@ impl<'a> PyClassImplsBuilder<'a> {
}
}
fn define_inventory_class(inventory_class_name: &syn::Ident) -> TokenStream {
fn define_inventory_class(inventory_class_name: &syn::Ident, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
quote! {
#[doc(hidden)]
pub struct #inventory_class_name {
items: _pyo3::impl_::pyclass::PyClassItems,
items: #pyo3_path::impl_::pyclass::PyClassItems,
}
impl #inventory_class_name {
pub const fn new(items: _pyo3::impl_::pyclass::PyClassItems) -> Self {
pub const fn new(items: #pyo3_path::impl_::pyclass::PyClassItems) -> Self {
Self { items }
}
}
impl _pyo3::impl_::pyclass::PyClassInventory for #inventory_class_name {
fn items(&self) -> &_pyo3::impl_::pyclass::PyClassItems {
impl #pyo3_path::impl_::pyclass::PyClassInventory for #inventory_class_name {
fn items(&self) -> &#pyo3_path::impl_::pyclass::PyClassItems {
&self.items
}
}
_pyo3::inventory::collect!(#inventory_class_name);
#pyo3_path::inventory::collect!(#inventory_class_name);
}
}

View File

@ -1,3 +1,4 @@
use crate::utils::Ctx;
use crate::{
attributes::{
self, get_pyo3_options, take_attributes, take_pyo3_options, CrateAttribute,
@ -6,7 +7,6 @@ use crate::{
deprecations::Deprecations,
method::{self, CallingConvention, FnArg},
pymethod::check_generic,
utils::get_pyo3_crate,
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
@ -205,6 +205,9 @@ pub fn impl_wrap_pyfunction(
krate,
} = options;
let ctx = &Ctx::new(&krate);
let Ctx { pyo3_path } = &ctx;
let python_name = name.map_or_else(|| func.sig.ident.unraw(), |name| name.value.0);
let tp = if pass_module.is_some() {
@ -249,17 +252,15 @@ pub fn impl_wrap_pyfunction(
text_signature,
asyncness: func.sig.asyncness,
unsafety: func.sig.unsafety,
deprecations: Deprecations::new(),
deprecations: Deprecations::new(ctx),
};
let krate = get_pyo3_crate(&krate);
let vis = &func.vis;
let name = &func.sig.ident;
let wrapper_ident = format_ident!("__pyfunction_{}", spec.name);
let wrapper = spec.get_wrapper_function(&wrapper_ident, None)?;
let methoddef = spec.get_methoddef(wrapper_ident, &spec.get_doc(&func.attrs));
let wrapper = spec.get_wrapper_function(&wrapper_ident, None, ctx)?;
let methoddef = spec.get_methoddef(wrapper_ident, &spec.get_doc(&func.attrs), ctx);
let wrapped_pyfunction = quote! {
@ -268,12 +269,12 @@ pub fn impl_wrap_pyfunction(
#[doc(hidden)]
#vis mod #name {
pub(crate) struct MakeDef;
pub const DEF: #krate::impl_::pymethods::PyMethodDef = MakeDef::DEF;
pub const DEF: #pyo3_path::impl_::pymethods::PyMethodDef = MakeDef::DEF;
pub fn add_to_module(module: &#krate::Bound<'_, #krate::types::PyModule>) -> #krate::PyResult<()> {
use #krate::prelude::PyModuleMethods;
pub fn add_to_module(module: &#pyo3_path::Bound<'_, #pyo3_path::types::PyModule>) -> #pyo3_path::PyResult<()> {
use #pyo3_path::prelude::PyModuleMethods;
use ::std::convert::Into;
module.add_function(#krate::types::PyCFunction::internal_new(module.py(), &DEF, module.into())?)
module.add_function(#pyo3_path::types::PyCFunction::internal_new(module.py(), &DEF, module.into())?)
}
}
@ -284,10 +285,8 @@ pub fn impl_wrap_pyfunction(
// FIXME https://github.com/PyO3/pyo3/issues/3903
#[allow(unknown_lints, non_local_definitions)]
const _: () = {
use #krate as _pyo3;
impl #name::MakeDef {
const DEF: #krate::impl_::pymethods::PyMethodDef = #methoddef;
const DEF: #pyo3_path::impl_::pymethods::PyMethodDef = #methoddef;
}
#[allow(non_snake_case)]

View File

@ -1,11 +1,11 @@
use std::collections::HashSet;
use crate::utils::Ctx;
use crate::{
attributes::{take_pyo3_options, CrateAttribute},
konst::{ConstAttributes, ConstSpec},
pyfunction::PyFunctionOptions,
pymethod::{self, is_proto_method, MethodAndMethodDef, MethodAndSlotDef},
utils::get_pyo3_crate,
};
use proc_macro2::TokenStream;
use pymethod::GeneratedPyMethod;
@ -90,6 +90,7 @@ pub fn impl_methods(
methods_type: PyClassMethodsType,
options: PyImplOptions,
) -> syn::Result<TokenStream> {
let ctx = &Ctx::new(&options.krate);
let mut trait_impls = Vec::new();
let mut proto_impls = Vec::new();
let mut methods = Vec::new();
@ -102,7 +103,8 @@ pub fn impl_methods(
syn::ImplItem::Fn(meth) => {
let mut fun_options = PyFunctionOptions::from_attrs(&mut meth.attrs)?;
fun_options.krate = fun_options.krate.or_else(|| options.krate.clone());
match pymethod::gen_py_method(ty, &mut meth.sig, &mut meth.attrs, fun_options)? {
match pymethod::gen_py_method(ty, &mut meth.sig, &mut meth.attrs, fun_options, ctx)?
{
GeneratedPyMethod::Method(MethodAndMethodDef {
associated_method,
method_def,
@ -127,7 +129,7 @@ pub fn impl_methods(
}
}
syn::ImplItem::Const(konst) => {
let attributes = ConstAttributes::from_attrs(&mut konst.attrs)?;
let attributes = ConstAttributes::from_attrs(&mut konst.attrs, ctx)?;
if attributes.is_class_attr {
let spec = ConstSpec {
rust_ident: konst.ident.clone(),
@ -137,7 +139,7 @@ pub fn impl_methods(
let MethodAndMethodDef {
associated_method,
method_def,
} = gen_py_const(ty, &spec);
} = gen_py_const(ty, &spec, ctx);
methods.push(quote!(#(#attrs)* #method_def));
associated_methods.push(quote!(#(#attrs)* #associated_method));
if is_proto_method(&spec.python_name().to_string()) {
@ -158,21 +160,19 @@ pub fn impl_methods(
}
}
add_shared_proto_slots(ty, &mut proto_impls, implemented_proto_fragments);
add_shared_proto_slots(ty, &mut proto_impls, implemented_proto_fragments, ctx);
let krate = get_pyo3_crate(&options.krate);
let ctx = &Ctx::new(&options.krate);
let items = match methods_type {
PyClassMethodsType::Specialization => impl_py_methods(ty, methods, proto_impls),
PyClassMethodsType::Inventory => submit_methods_inventory(ty, methods, proto_impls),
PyClassMethodsType::Specialization => impl_py_methods(ty, methods, proto_impls, ctx),
PyClassMethodsType::Inventory => submit_methods_inventory(ty, methods, proto_impls, ctx),
};
Ok(quote! {
// FIXME https://github.com/PyO3/pyo3/issues/3903
#[allow(unknown_lints, non_local_definitions)]
const _: () = {
use #krate as _pyo3;
#(#trait_impls)*
#items
@ -186,24 +186,25 @@ pub fn impl_methods(
})
}
pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec) -> MethodAndMethodDef {
pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec<'_>, ctx: &Ctx) -> MethodAndMethodDef {
let member = &spec.rust_ident;
let wrapper_ident = format_ident!("__pymethod_{}__", member);
let deprecations = &spec.attributes.deprecations;
let python_name = &spec.null_terminated_python_name();
let Ctx { pyo3_path } = ctx;
let associated_method = quote! {
fn #wrapper_ident(py: _pyo3::Python<'_>) -> _pyo3::PyResult<_pyo3::PyObject> {
fn #wrapper_ident(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::PyObject> {
#deprecations
::std::result::Result::Ok(_pyo3::IntoPy::into_py(#cls::#member, py))
::std::result::Result::Ok(#pyo3_path::IntoPy::into_py(#cls::#member, py))
}
};
let method_def = quote! {
_pyo3::class::PyMethodDefType::ClassAttribute({
_pyo3::class::PyClassAttributeDef::new(
#pyo3_path::class::PyMethodDefType::ClassAttribute({
#pyo3_path::class::PyClassAttributeDef::new(
#python_name,
_pyo3::impl_::pymethods::PyClassAttributeFactory(#cls::#wrapper_ident)
#pyo3_path::impl_::pymethods::PyClassAttributeFactory(#cls::#wrapper_ident)
)
})
};
@ -218,13 +219,15 @@ fn impl_py_methods(
ty: &syn::Type,
methods: Vec<TokenStream>,
proto_impls: Vec<TokenStream>,
ctx: &Ctx,
) -> TokenStream {
let Ctx { pyo3_path } = ctx;
quote! {
impl _pyo3::impl_::pyclass::PyMethods<#ty>
for _pyo3::impl_::pyclass::PyClassImplCollector<#ty>
impl #pyo3_path::impl_::pyclass::PyMethods<#ty>
for #pyo3_path::impl_::pyclass::PyClassImplCollector<#ty>
{
fn py_methods(self) -> &'static _pyo3::impl_::pyclass::PyClassItems {
static ITEMS: _pyo3::impl_::pyclass::PyClassItems = _pyo3::impl_::pyclass::PyClassItems {
fn py_methods(self) -> &'static #pyo3_path::impl_::pyclass::PyClassItems {
static ITEMS: #pyo3_path::impl_::pyclass::PyClassItems = #pyo3_path::impl_::pyclass::PyClassItems {
methods: &[#(#methods),*],
slots: &[#(#proto_impls),*]
};
@ -238,13 +241,15 @@ fn add_shared_proto_slots(
ty: &syn::Type,
proto_impls: &mut Vec<TokenStream>,
mut implemented_proto_fragments: HashSet<String>,
ctx: &Ctx,
) {
let Ctx { pyo3_path } = ctx;
macro_rules! try_add_shared_slot {
($slot:ident, $($fragments:literal),*) => {{
let mut implemented = false;
$(implemented |= implemented_proto_fragments.remove($fragments));*;
if implemented {
proto_impls.push(quote! { _pyo3::impl_::pyclass::$slot!(#ty) })
proto_impls.push(quote! { #pyo3_path::impl_::pyclass::$slot!(#ty) })
}
}};
}
@ -294,11 +299,13 @@ fn submit_methods_inventory(
ty: &syn::Type,
methods: Vec<TokenStream>,
proto_impls: Vec<TokenStream>,
ctx: &Ctx,
) -> TokenStream {
let Ctx { pyo3_path } = ctx;
quote! {
_pyo3::inventory::submit! {
type Inventory = <#ty as _pyo3::impl_::pyclass::PyClassImpl>::Inventory;
Inventory::new(_pyo3::impl_::pyclass::PyClassItems { methods: &[#(#methods),*], slots: &[#(#proto_impls),*] })
#pyo3_path::inventory::submit! {
type Inventory = <#ty as #pyo3_path::impl_::pyclass::PyClassImpl>::Inventory;
Inventory::new(#pyo3_path::impl_::pyclass::PyClassItems { methods: &[#(#methods),*], slots: &[#(#proto_impls),*] })
}
}
}

View File

@ -2,6 +2,7 @@ use std::borrow::Cow;
use crate::attributes::{NameAttribute, RenamingRule};
use crate::method::{CallingConvention, ExtractErrorMode};
use crate::utils::Ctx;
use crate::utils::PythonDoc;
use crate::{
method::{FnArg, FnSpec, FnType, SelfType},
@ -160,8 +161,9 @@ impl<'a> PyMethod<'a> {
sig: &'a mut syn::Signature,
meth_attrs: &mut Vec<syn::Attribute>,
options: PyFunctionOptions,
ctx: &'a Ctx,
) -> Result<Self> {
let spec = FnSpec::parse(sig, meth_attrs, options)?;
let spec = FnSpec::parse(sig, meth_attrs, options, ctx)?;
let method_name = spec.python_name.to_string();
let kind = PyMethodKind::from_name(&method_name);
@ -186,33 +188,35 @@ pub fn gen_py_method(
sig: &mut syn::Signature,
meth_attrs: &mut Vec<syn::Attribute>,
options: PyFunctionOptions,
ctx: &Ctx,
) -> Result<GeneratedPyMethod> {
check_generic(sig)?;
ensure_function_options_valid(&options)?;
let method = PyMethod::parse(sig, meth_attrs, options)?;
let method = PyMethod::parse(sig, meth_attrs, options, ctx)?;
let spec = &method.spec;
let Ctx { pyo3_path } = ctx;
Ok(match (method.kind, &spec.tp) {
// Class attributes go before protos so that class attributes can be used to set proto
// method to None.
(_, FnType::ClassAttribute) => {
GeneratedPyMethod::Method(impl_py_class_attribute(cls, spec)?)
GeneratedPyMethod::Method(impl_py_class_attribute(cls, spec, ctx)?)
}
(PyMethodKind::Proto(proto_kind), _) => {
ensure_no_forbidden_protocol_attributes(&proto_kind, spec, &method.method_name)?;
match proto_kind {
PyMethodProtoKind::Slot(slot_def) => {
let slot = slot_def.generate_type_slot(cls, spec, &method.method_name)?;
let slot = slot_def.generate_type_slot(cls, spec, &method.method_name, ctx)?;
GeneratedPyMethod::Proto(slot)
}
PyMethodProtoKind::Call => {
GeneratedPyMethod::Proto(impl_call_slot(cls, method.spec)?)
GeneratedPyMethod::Proto(impl_call_slot(cls, method.spec, ctx)?)
}
PyMethodProtoKind::Traverse => {
GeneratedPyMethod::Proto(impl_traverse_slot(cls, spec)?)
GeneratedPyMethod::Proto(impl_traverse_slot(cls, spec, ctx)?)
}
PyMethodProtoKind::SlotFragment(slot_fragment_def) => {
let proto = slot_fragment_def.generate_pyproto_fragment(cls, spec)?;
let proto = slot_fragment_def.generate_pyproto_fragment(cls, spec, ctx)?;
GeneratedPyMethod::SlotTraitImpl(method.method_name, proto)
}
}
@ -223,22 +227,25 @@ pub fn gen_py_method(
spec,
&spec.get_doc(meth_attrs),
None,
ctx,
)?),
(_, FnType::FnClass(_)) => GeneratedPyMethod::Method(impl_py_method_def(
cls,
spec,
&spec.get_doc(meth_attrs),
Some(quote!(_pyo3::ffi::METH_CLASS)),
Some(quote!(#pyo3_path::ffi::METH_CLASS)),
ctx,
)?),
(_, FnType::FnStatic) => GeneratedPyMethod::Method(impl_py_method_def(
cls,
spec,
&spec.get_doc(meth_attrs),
Some(quote!(_pyo3::ffi::METH_STATIC)),
Some(quote!(#pyo3_path::ffi::METH_STATIC)),
ctx,
)?),
// special prototypes
(_, FnType::FnNew) | (_, FnType::FnNewClass(_)) => {
GeneratedPyMethod::Proto(impl_py_method_def_new(cls, spec)?)
GeneratedPyMethod::Proto(impl_py_method_def_new(cls, spec, ctx)?)
}
(_, FnType::Getter(self_type)) => GeneratedPyMethod::Method(impl_py_getter_def(
@ -248,6 +255,7 @@ pub fn gen_py_method(
spec,
doc: spec.get_doc(meth_attrs),
},
ctx,
)?),
(_, FnType::Setter(self_type)) => GeneratedPyMethod::Method(impl_py_setter_def(
cls,
@ -256,6 +264,7 @@ pub fn gen_py_method(
spec,
doc: spec.get_doc(meth_attrs),
},
ctx,
)?),
(_, FnType::FnModule(_)) => {
unreachable!("methods cannot be FnModule")
@ -305,18 +314,20 @@ pub fn impl_py_method_def(
spec: &FnSpec<'_>,
doc: &PythonDoc,
flags: Option<TokenStream>,
ctx: &Ctx,
) -> Result<MethodAndMethodDef> {
let Ctx { pyo3_path } = ctx;
let wrapper_ident = format_ident!("__pymethod_{}__", spec.python_name);
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls))?;
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls), ctx)?;
let add_flags = flags.map(|flags| quote!(.flags(#flags)));
let methoddef_type = match spec.tp {
FnType::FnStatic => quote!(Static),
FnType::FnClass(_) => quote!(Class),
_ => quote!(Method),
};
let methoddef = spec.get_methoddef(quote! { #cls::#wrapper_ident }, doc);
let methoddef = spec.get_methoddef(quote! { #cls::#wrapper_ident }, doc, ctx);
let method_def = quote! {
_pyo3::class::PyMethodDefType::#methoddef_type(#methoddef #add_flags)
#pyo3_path::class::PyMethodDefType::#methoddef_type(#methoddef #add_flags)
};
Ok(MethodAndMethodDef {
associated_method,
@ -325,9 +336,14 @@ pub fn impl_py_method_def(
}
/// Also used by pyclass.
pub fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<MethodAndSlotDef> {
pub fn impl_py_method_def_new(
cls: &syn::Type,
spec: &FnSpec<'_>,
ctx: &Ctx,
) -> Result<MethodAndSlotDef> {
let Ctx { pyo3_path } = ctx;
let wrapper_ident = syn::Ident::new("__pymethod___new____", Span::call_site());
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls))?;
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls), ctx)?;
// Use just the text_signature_call_signature() because the class' Python name
// isn't known to `#[pymethods]` - that has to be attached at runtime from the PyClassImpl
// trait implementation created by `#[pyclass]`.
@ -337,18 +353,18 @@ pub fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<Meth
);
let deprecations = &spec.deprecations;
let slot_def = quote! {
_pyo3::ffi::PyType_Slot {
slot: _pyo3::ffi::Py_tp_new,
#pyo3_path::ffi::PyType_Slot {
slot: #pyo3_path::ffi::Py_tp_new,
pfunc: {
unsafe extern "C" fn trampoline(
subtype: *mut _pyo3::ffi::PyTypeObject,
args: *mut _pyo3::ffi::PyObject,
kwargs: *mut _pyo3::ffi::PyObject,
) -> *mut _pyo3::ffi::PyObject
subtype: *mut #pyo3_path::ffi::PyTypeObject,
args: *mut #pyo3_path::ffi::PyObject,
kwargs: *mut #pyo3_path::ffi::PyObject,
) -> *mut #pyo3_path::ffi::PyObject
{
#deprecations
use _pyo3::impl_::pyclass::*;
use #pyo3_path::impl_::pyclass::*;
impl PyClassNewTextSignature<#cls> for PyClassImplCollector<#cls> {
#[inline]
fn new_text_signature(self) -> ::std::option::Option<&'static str> {
@ -356,7 +372,7 @@ pub fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<Meth
}
}
_pyo3::impl_::trampoline::newfunc(
#pyo3_path::impl_::trampoline::newfunc(
subtype,
args,
kwargs,
@ -364,7 +380,7 @@ pub fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<Meth
)
}
trampoline
} as _pyo3::ffi::newfunc as _
} as #pyo3_path::ffi::newfunc as _
}
};
Ok(MethodAndSlotDef {
@ -373,24 +389,26 @@ pub fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<Meth
})
}
fn impl_call_slot(cls: &syn::Type, mut spec: FnSpec<'_>) -> Result<MethodAndSlotDef> {
fn impl_call_slot(cls: &syn::Type, mut spec: FnSpec<'_>, ctx: &Ctx) -> Result<MethodAndSlotDef> {
let Ctx { pyo3_path } = ctx;
// HACK: __call__ proto slot must always use varargs calling convention, so change the spec.
// Probably indicates there's a refactoring opportunity somewhere.
spec.convention = CallingConvention::Varargs;
let wrapper_ident = syn::Ident::new("__pymethod___call____", Span::call_site());
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls))?;
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls), ctx)?;
let slot_def = quote! {
_pyo3::ffi::PyType_Slot {
slot: _pyo3::ffi::Py_tp_call,
#pyo3_path::ffi::PyType_Slot {
slot: #pyo3_path::ffi::Py_tp_call,
pfunc: {
unsafe extern "C" fn trampoline(
slf: *mut _pyo3::ffi::PyObject,
args: *mut _pyo3::ffi::PyObject,
kwargs: *mut _pyo3::ffi::PyObject,
) -> *mut _pyo3::ffi::PyObject
slf: *mut #pyo3_path::ffi::PyObject,
args: *mut #pyo3_path::ffi::PyObject,
kwargs: *mut #pyo3_path::ffi::PyObject,
) -> *mut #pyo3_path::ffi::PyObject
{
_pyo3::impl_::trampoline::ternaryfunc(
#pyo3_path::impl_::trampoline::ternaryfunc(
slf,
args,
kwargs,
@ -398,7 +416,7 @@ fn impl_call_slot(cls: &syn::Type, mut spec: FnSpec<'_>) -> Result<MethodAndSlot
)
}
trampoline
} as _pyo3::ffi::ternaryfunc as _
} as #pyo3_path::ffi::ternaryfunc as _
}
};
Ok(MethodAndSlotDef {
@ -407,7 +425,12 @@ fn impl_call_slot(cls: &syn::Type, mut spec: FnSpec<'_>) -> Result<MethodAndSlot
})
}
fn impl_traverse_slot(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<MethodAndSlotDef> {
fn impl_traverse_slot(
cls: &syn::Type,
spec: &FnSpec<'_>,
ctx: &Ctx,
) -> syn::Result<MethodAndSlotDef> {
let Ctx { pyo3_path } = ctx;
if let (Some(py_arg), _) = split_off_python_arg(&spec.signature.arguments) {
return Err(syn::Error::new_spanned(py_arg.ty, "__traverse__ may not take `Python`. \
Usually, an implementation of `__traverse__` should do nothing but calls to `visit.call`. \
@ -419,17 +442,17 @@ fn impl_traverse_slot(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<MethodA
let associated_method = quote! {
pub unsafe extern "C" fn __pymethod_traverse__(
slf: *mut _pyo3::ffi::PyObject,
visit: _pyo3::ffi::visitproc,
slf: *mut #pyo3_path::ffi::PyObject,
visit: #pyo3_path::ffi::visitproc,
arg: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int {
_pyo3::impl_::pymethods::_call_traverse::<#cls>(slf, #cls::#rust_fn_ident, visit, arg)
#pyo3_path::impl_::pymethods::_call_traverse::<#cls>(slf, #cls::#rust_fn_ident, visit, arg)
}
};
let slot_def = quote! {
_pyo3::ffi::PyType_Slot {
slot: _pyo3::ffi::Py_tp_traverse,
pfunc: #cls::__pymethod_traverse__ as _pyo3::ffi::traverseproc as _
#pyo3_path::ffi::PyType_Slot {
slot: #pyo3_path::ffi::Py_tp_traverse,
pfunc: #cls::__pymethod_traverse__ as #pyo3_path::ffi::traverseproc as _
}
};
Ok(MethodAndSlotDef {
@ -438,7 +461,12 @@ fn impl_traverse_slot(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<MethodA
})
}
fn impl_py_class_attribute(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<MethodAndMethodDef> {
fn impl_py_class_attribute(
cls: &syn::Type,
spec: &FnSpec<'_>,
ctx: &Ctx,
) -> syn::Result<MethodAndMethodDef> {
let Ctx { pyo3_path } = ctx;
let (py_arg, args) = split_off_python_arg(&spec.signature.arguments);
ensure_spanned!(
args.is_empty(),
@ -454,20 +482,20 @@ fn impl_py_class_attribute(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<Me
let wrapper_ident = format_ident!("__pymethod_{}__", name);
let python_name = spec.null_terminated_python_name();
let body = quotes::ok_wrap(fncall);
let body = quotes::ok_wrap(fncall, ctx);
let associated_method = quote! {
fn #wrapper_ident(py: _pyo3::Python<'_>) -> _pyo3::PyResult<_pyo3::PyObject> {
fn #wrapper_ident(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::PyObject> {
let function = #cls::#name; // Shadow the method name to avoid #3017
_pyo3::impl_::wrap::map_result_into_py(py, #body)
#pyo3_path::impl_::wrap::map_result_into_py(py, #body)
}
};
let method_def = quote! {
_pyo3::class::PyMethodDefType::ClassAttribute({
_pyo3::class::PyClassAttributeDef::new(
#pyo3_path::class::PyMethodDefType::ClassAttribute({
#pyo3_path::class::PyClassAttributeDef::new(
#python_name,
_pyo3::impl_::pymethods::PyClassAttributeFactory(#cls::#wrapper_ident)
#pyo3_path::impl_::pymethods::PyClassAttributeFactory(#cls::#wrapper_ident)
)
})
};
@ -483,9 +511,10 @@ fn impl_call_setter(
spec: &FnSpec<'_>,
self_type: &SelfType,
holders: &mut Vec<TokenStream>,
ctx: &Ctx,
) -> syn::Result<TokenStream> {
let (py_arg, args) = split_off_python_arg(&spec.signature.arguments);
let slf = self_type.receiver(cls, ExtractErrorMode::Raise, holders);
let slf = self_type.receiver(cls, ExtractErrorMode::Raise, holders, ctx);
if args.is_empty() {
bail_spanned!(spec.name.span() => "setter function expected to have one argument");
@ -510,7 +539,9 @@ fn impl_call_setter(
pub fn impl_py_setter_def(
cls: &syn::Type,
property_type: PropertyType<'_>,
ctx: &Ctx,
) -> Result<MethodAndMethodDef> {
let Ctx { pyo3_path } = ctx;
let python_name = property_type.null_terminated_python_name()?;
let doc = property_type.doc();
let mut holders = Vec::new();
@ -522,7 +553,7 @@ pub fn impl_py_setter_def(
mutable: true,
span: Span::call_site(),
}
.receiver(cls, ExtractErrorMode::Raise, &mut holders);
.receiver(cls, ExtractErrorMode::Raise, &mut holders, ctx);
if let Some(ident) = &field.ident {
// named struct field
quote!({ #slf.#ident = _val; })
@ -534,7 +565,7 @@ pub fn impl_py_setter_def(
}
PropertyType::Function {
spec, self_type, ..
} => impl_call_setter(cls, spec, self_type, &mut holders)?,
} => impl_call_setter(cls, spec, self_type, &mut holders, ctx)?,
};
let wrapper_ident = match property_type {
@ -568,27 +599,27 @@ pub fn impl_py_setter_def(
let associated_method = quote! {
#cfg_attrs
unsafe fn #wrapper_ident(
py: _pyo3::Python<'_>,
_slf: *mut _pyo3::ffi::PyObject,
_value: *mut _pyo3::ffi::PyObject,
) -> _pyo3::PyResult<::std::os::raw::c_int> {
py: #pyo3_path::Python<'_>,
_slf: *mut #pyo3_path::ffi::PyObject,
_value: *mut #pyo3_path::ffi::PyObject,
) -> #pyo3_path::PyResult<::std::os::raw::c_int> {
use ::std::convert::Into;
let _value = _pyo3::impl_::pymethods::BoundRef::ref_from_ptr_or_opt(py, &_value)
let _value = #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr_or_opt(py, &_value)
.ok_or_else(|| {
_pyo3::exceptions::PyAttributeError::new_err("can't delete attribute")
#pyo3_path::exceptions::PyAttributeError::new_err("can't delete attribute")
})?;
let _val = _pyo3::FromPyObject::extract_bound(_value.into())?;
let _val = #pyo3_path::FromPyObject::extract_bound(_value.into())?;
#( #holders )*
_pyo3::callback::convert(py, #setter_impl)
#pyo3_path::callback::convert(py, #setter_impl)
}
};
let method_def = quote! {
#cfg_attrs
_pyo3::class::PyMethodDefType::Setter(
_pyo3::class::PySetterDef::new(
#pyo3_path::class::PyMethodDefType::Setter(
#pyo3_path::class::PySetterDef::new(
#python_name,
_pyo3::impl_::pymethods::PySetter(#cls::#wrapper_ident),
#pyo3_path::impl_::pymethods::PySetter(#cls::#wrapper_ident),
#doc
)
)
@ -605,9 +636,10 @@ fn impl_call_getter(
spec: &FnSpec<'_>,
self_type: &SelfType,
holders: &mut Vec<TokenStream>,
ctx: &Ctx,
) -> syn::Result<TokenStream> {
let (py_arg, args) = split_off_python_arg(&spec.signature.arguments);
let slf = self_type.receiver(cls, ExtractErrorMode::Raise, holders);
let slf = self_type.receiver(cls, ExtractErrorMode::Raise, holders, ctx);
ensure_spanned!(
args.is_empty(),
args[0].ty.span() => "getter function can only have one argument (of type pyo3::Python)"
@ -627,7 +659,9 @@ fn impl_call_getter(
pub fn impl_py_getter_def(
cls: &syn::Type,
property_type: PropertyType<'_>,
ctx: &Ctx,
) -> Result<MethodAndMethodDef> {
let Ctx { pyo3_path } = ctx;
let python_name = property_type.null_terminated_python_name()?;
let doc = property_type.doc();
@ -640,7 +674,7 @@ pub fn impl_py_getter_def(
mutable: false,
span: Span::call_site(),
}
.receiver(cls, ExtractErrorMode::Raise, &mut holders);
.receiver(cls, ExtractErrorMode::Raise, &mut holders, ctx);
let field_token = if let Some(ident) = &field.ident {
// named struct field
ident.to_token_stream()
@ -648,17 +682,23 @@ pub fn impl_py_getter_def(
// tuple struct field
syn::Index::from(field_index).to_token_stream()
};
quotes::map_result_into_ptr(quotes::ok_wrap(quote! {
quotes::map_result_into_ptr(
quotes::ok_wrap(
quote! {
::std::clone::Clone::clone(&(#slf.#field_token))
}))
},
ctx,
),
ctx,
)
}
// Forward to `IntoPyCallbackOutput`, to handle `#[getter]`s returning results.
PropertyType::Function {
spec, self_type, ..
} => {
let call = impl_call_getter(cls, spec, self_type, &mut holders)?;
let call = impl_call_getter(cls, spec, self_type, &mut holders, ctx)?;
quote! {
_pyo3::callback::convert(py, #call)
#pyo3_path::callback::convert(py, #call)
}
}
};
@ -694,9 +734,9 @@ pub fn impl_py_getter_def(
let associated_method = quote! {
#cfg_attrs
unsafe fn #wrapper_ident(
py: _pyo3::Python<'_>,
_slf: *mut _pyo3::ffi::PyObject
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
py: #pyo3_path::Python<'_>,
_slf: *mut #pyo3_path::ffi::PyObject
) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
#( #holders )*
let result = #body;
result
@ -705,10 +745,10 @@ pub fn impl_py_getter_def(
let method_def = quote! {
#cfg_attrs
_pyo3::class::PyMethodDefType::Getter(
_pyo3::class::PyGetterDef::new(
#pyo3_path::class::PyMethodDefType::Getter(
#pyo3_path::class::PyGetterDef::new(
#python_name,
_pyo3::impl_::pymethods::PyGetter(#cls::#wrapper_ident),
#pyo3_path::impl_::pymethods::PyGetter(#cls::#wrapper_ident),
#doc
)
)
@ -786,7 +826,7 @@ pub const __REPR__: SlotDef = SlotDef::new("Py_tp_repr", "reprfunc");
const __HASH__: SlotDef = SlotDef::new("Py_tp_hash", "hashfunc")
.ret_ty(Ty::PyHashT)
.return_conversion(TokenGenerator(
|| quote! { _pyo3::callback::HashCallbackOutput },
|Ctx { pyo3_path }: &Ctx| quote! { #pyo3_path::callback::HashCallbackOutput },
));
pub const __RICHCMP__: SlotDef = SlotDef::new("Py_tp_richcompare", "richcmpfunc")
.extract_error_mode(ExtractErrorMode::NotImplemented)
@ -796,14 +836,16 @@ const __GET__: SlotDef = SlotDef::new("Py_tp_descr_get", "descrgetfunc")
const __ITER__: SlotDef = SlotDef::new("Py_tp_iter", "getiterfunc");
const __NEXT__: SlotDef = SlotDef::new("Py_tp_iternext", "iternextfunc")
.return_specialized_conversion(
TokenGenerator(|| quote! { IterBaseKind, IterOptionKind, IterResultOptionKind }),
TokenGenerator(|| quote! { iter_tag }),
TokenGenerator(|_| quote! { IterBaseKind, IterOptionKind, IterResultOptionKind }),
TokenGenerator(|_| quote! { iter_tag }),
);
const __AWAIT__: SlotDef = SlotDef::new("Py_am_await", "unaryfunc");
const __AITER__: SlotDef = SlotDef::new("Py_am_aiter", "unaryfunc");
const __ANEXT__: SlotDef = SlotDef::new("Py_am_anext", "unaryfunc").return_specialized_conversion(
TokenGenerator(|| quote! { AsyncIterBaseKind, AsyncIterOptionKind, AsyncIterResultOptionKind }),
TokenGenerator(|| quote! { async_iter_tag }),
TokenGenerator(
|_| quote! { AsyncIterBaseKind, AsyncIterOptionKind, AsyncIterResultOptionKind },
),
TokenGenerator(|_| quote! { async_iter_tag }),
);
const __LEN__: SlotDef = SlotDef::new("Py_mp_length", "lenfunc").ret_ty(Ty::PySsizeT);
const __CONTAINS__: SlotDef = SlotDef::new("Py_sq_contains", "objobjproc")
@ -905,16 +947,17 @@ enum Ty {
}
impl Ty {
fn ffi_type(self) -> TokenStream {
fn ffi_type(self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
match self {
Ty::Object | Ty::MaybeNullObject => quote! { *mut _pyo3::ffi::PyObject },
Ty::NonNullObject => quote! { ::std::ptr::NonNull<_pyo3::ffi::PyObject> },
Ty::IPowModulo => quote! { _pyo3::impl_::pymethods::IPowModulo },
Ty::Object | Ty::MaybeNullObject => quote! { *mut #pyo3_path::ffi::PyObject },
Ty::NonNullObject => quote! { ::std::ptr::NonNull<#pyo3_path::ffi::PyObject> },
Ty::IPowModulo => quote! { #pyo3_path::impl_::pymethods::IPowModulo },
Ty::Int | Ty::CompareOp => quote! { ::std::os::raw::c_int },
Ty::PyHashT => quote! { _pyo3::ffi::Py_hash_t },
Ty::PySsizeT => quote! { _pyo3::ffi::Py_ssize_t },
Ty::PyHashT => quote! { #pyo3_path::ffi::Py_hash_t },
Ty::PySsizeT => quote! { #pyo3_path::ffi::Py_ssize_t },
Ty::Void => quote! { () },
Ty::PyBuffer => quote! { *mut _pyo3::ffi::Py_buffer },
Ty::PyBuffer => quote! { *mut #pyo3_path::ffi::Py_buffer },
}
}
@ -924,14 +967,16 @@ impl Ty {
arg: &FnArg<'_>,
extract_error_mode: ExtractErrorMode,
holders: &mut Vec<TokenStream>,
ctx: &Ctx,
) -> TokenStream {
let Ctx { pyo3_path } = ctx;
let name_str = arg.name.unraw().to_string();
match self {
Ty::Object => extract_object(
extract_error_mode,
holders,
&name_str,
quote! { #ident },
quote! { #ident },ctx
),
Ty::MaybeNullObject => extract_object(
extract_error_mode,
@ -939,36 +984,36 @@ impl Ty {
&name_str,
quote! {
if #ident.is_null() {
_pyo3::ffi::Py_None()
#pyo3_path::ffi::Py_None()
} else {
#ident
}
},
},ctx
),
Ty::NonNullObject => extract_object(
extract_error_mode,
holders,
&name_str,
quote! { #ident.as_ptr() },
quote! { #ident.as_ptr() },ctx
),
Ty::IPowModulo => extract_object(
extract_error_mode,
holders,
&name_str,
quote! { #ident.as_ptr() },
quote! { #ident.as_ptr() },ctx
),
Ty::CompareOp => extract_error_mode.handle_error(
quote! {
_pyo3::class::basic::CompareOp::from_raw(#ident)
.ok_or_else(|| _pyo3::exceptions::PyValueError::new_err("invalid comparison operator"))
},
#pyo3_path::class::basic::CompareOp::from_raw(#ident)
.ok_or_else(|| #pyo3_path::exceptions::PyValueError::new_err("invalid comparison operator"))
},ctx
),
Ty::PySsizeT => {
let ty = arg.ty;
extract_error_mode.handle_error(
quote! {
::std::convert::TryInto::<#ty>::try_into(#ident).map_err(|e| _pyo3::exceptions::PyValueError::new_err(e.to_string()))
},
::std::convert::TryInto::<#ty>::try_into(#ident).map_err(|e| #pyo3_path::exceptions::PyValueError::new_err(e.to_string()))
},ctx
)
}
// Just pass other types through unmodified
@ -982,19 +1027,24 @@ fn extract_object(
holders: &mut Vec<TokenStream>,
name: &str,
source_ptr: TokenStream,
ctx: &Ctx,
) -> TokenStream {
let Ctx { pyo3_path } = ctx;
let holder = syn::Ident::new(&format!("holder_{}", holders.len()), Span::call_site());
holders.push(quote! {
#[allow(clippy::let_unit_value)]
let mut #holder = _pyo3::impl_::extract_argument::FunctionArgumentHolder::INIT;
let mut #holder = #pyo3_path::impl_::extract_argument::FunctionArgumentHolder::INIT;
});
extract_error_mode.handle_error(quote! {
_pyo3::impl_::extract_argument::extract_argument(
&_pyo3::impl_::pymethods::BoundRef::ref_from_ptr(py, &#source_ptr),
extract_error_mode.handle_error(
quote! {
#pyo3_path::impl_::extract_argument::extract_argument(
&#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &#source_ptr),
&mut #holder,
#name
)
})
},
ctx,
)
}
enum ReturnMode {
@ -1004,21 +1054,29 @@ enum ReturnMode {
}
impl ReturnMode {
fn return_call_output(&self, call: TokenStream) -> TokenStream {
fn return_call_output(&self, call: TokenStream, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
match self {
ReturnMode::Conversion(conversion) => quote! {
let _result: _pyo3::PyResult<#conversion> = _pyo3::callback::convert(py, #call);
_pyo3::callback::convert(py, _result)
},
ReturnMode::SpecializedConversion(traits, tag) => quote! {
ReturnMode::Conversion(conversion) => {
let conversion = TokenGeneratorCtx(*conversion, ctx);
quote! {
let _result: #pyo3_path::PyResult<#conversion> = #pyo3_path::callback::convert(py, #call);
#pyo3_path::callback::convert(py, _result)
}
}
ReturnMode::SpecializedConversion(traits, tag) => {
let traits = TokenGeneratorCtx(*traits, ctx);
let tag = TokenGeneratorCtx(*tag, ctx);
quote! {
let _result = #call;
use _pyo3::impl_::pymethods::{#traits};
use #pyo3_path::impl_::pymethods::{#traits};
(&_result).#tag().convert(py, _result)
},
}
}
ReturnMode::ReturnSelf => quote! {
let _result: _pyo3::PyResult<()> = _pyo3::callback::convert(py, #call);
let _result: #pyo3_path::PyResult<()> = #pyo3_path::callback::convert(py, #call);
_result?;
_pyo3::ffi::Py_XINCREF(_raw_slf);
#pyo3_path::ffi::Py_XINCREF(_raw_slf);
::std::result::Result::Ok(_raw_slf)
},
}
@ -1094,7 +1152,9 @@ impl SlotDef {
cls: &syn::Type,
spec: &FnSpec<'_>,
method_name: &str,
ctx: &Ctx,
) -> Result<MethodAndSlotDef> {
let Ctx { pyo3_path } = ctx;
let SlotDef {
slot,
func_ty,
@ -1110,12 +1170,12 @@ impl SlotDef {
spec.name.span() => format!("`{}` must be `unsafe fn`", method_name)
);
}
let arg_types: &Vec<_> = &arguments.iter().map(|arg| arg.ffi_type()).collect();
let arg_types: &Vec<_> = &arguments.iter().map(|arg| arg.ffi_type(ctx)).collect();
let arg_idents: &Vec<_> = &(0..arguments.len())
.map(|i| format_ident!("arg{}", i))
.collect();
let wrapper_ident = format_ident!("__pymethod_{}__", method_name);
let ret_ty = ret_ty.ffi_type();
let ret_ty = ret_ty.ffi_type(ctx);
let mut holders = Vec::new();
let body = generate_method_body(
cls,
@ -1124,14 +1184,15 @@ impl SlotDef {
*extract_error_mode,
&mut holders,
return_mode.as_ref(),
ctx,
)?;
let name = spec.name;
let associated_method = quote! {
unsafe fn #wrapper_ident(
py: _pyo3::Python<'_>,
_raw_slf: *mut _pyo3::ffi::PyObject,
py: #pyo3_path::Python<'_>,
_raw_slf: *mut #pyo3_path::ffi::PyObject,
#(#arg_idents: #arg_types),*
) -> _pyo3::PyResult<#ret_ty> {
) -> #pyo3_path::PyResult<#ret_ty> {
let function = #cls::#name; // Shadow the method name to avoid #3017
let _slf = _raw_slf;
#( #holders )*
@ -1140,20 +1201,20 @@ impl SlotDef {
};
let slot_def = quote! {{
unsafe extern "C" fn trampoline(
_slf: *mut _pyo3::ffi::PyObject,
_slf: *mut #pyo3_path::ffi::PyObject,
#(#arg_idents: #arg_types),*
) -> #ret_ty
{
_pyo3::impl_::trampoline:: #func_ty (
#pyo3_path::impl_::trampoline:: #func_ty (
_slf,
#(#arg_idents,)*
#cls::#wrapper_ident
)
}
_pyo3::ffi::PyType_Slot {
slot: _pyo3::ffi::#slot,
pfunc: trampoline as _pyo3::ffi::#func_ty as _
#pyo3_path::ffi::PyType_Slot {
slot: #pyo3_path::ffi::#slot,
pfunc: trampoline as #pyo3_path::ffi::#func_ty as _
}
}};
Ok(MethodAndSlotDef {
@ -1170,15 +1231,19 @@ fn generate_method_body(
extract_error_mode: ExtractErrorMode,
holders: &mut Vec<TokenStream>,
return_mode: Option<&ReturnMode>,
ctx: &Ctx,
) -> Result<TokenStream> {
let self_arg = spec.tp.self_arg(Some(cls), extract_error_mode, holders);
let Ctx { pyo3_path } = ctx;
let self_arg = spec
.tp
.self_arg(Some(cls), extract_error_mode, holders, ctx);
let rust_name = spec.name;
let args = extract_proto_arguments(spec, arguments, extract_error_mode, holders)?;
let args = extract_proto_arguments(spec, arguments, extract_error_mode, holders, ctx)?;
let call = quote! { #cls::#rust_name(#self_arg #(#args),*) };
Ok(if let Some(return_mode) = return_mode {
return_mode.return_call_output(call)
return_mode.return_call_output(call, ctx)
} else {
quote! { _pyo3::callback::convert(py, #call) }
quote! { #pyo3_path::callback::convert(py, #call) }
})
}
@ -1209,7 +1274,13 @@ impl SlotFragmentDef {
self
}
fn generate_pyproto_fragment(&self, cls: &syn::Type, spec: &FnSpec<'_>) -> Result<TokenStream> {
fn generate_pyproto_fragment(
&self,
cls: &syn::Type,
spec: &FnSpec<'_>,
ctx: &Ctx,
) -> Result<TokenStream> {
let Ctx { pyo3_path } = ctx;
let SlotFragmentDef {
fragment,
arguments,
@ -1219,7 +1290,7 @@ impl SlotFragmentDef {
let fragment_trait = format_ident!("PyClass{}SlotFragment", fragment);
let method = syn::Ident::new(fragment, Span::call_site());
let wrapper_ident = format_ident!("__pymethod_{}__", fragment);
let arg_types: &Vec<_> = &arguments.iter().map(|arg| arg.ffi_type()).collect();
let arg_types: &Vec<_> = &arguments.iter().map(|arg| arg.ffi_type(ctx)).collect();
let arg_idents: &Vec<_> = &(0..arguments.len())
.map(|i| format_ident!("arg{}", i))
.collect();
@ -1231,30 +1302,31 @@ impl SlotFragmentDef {
*extract_error_mode,
&mut holders,
None,
ctx,
)?;
let ret_ty = ret_ty.ffi_type();
let ret_ty = ret_ty.ffi_type(ctx);
Ok(quote! {
impl #cls {
unsafe fn #wrapper_ident(
py: _pyo3::Python,
_raw_slf: *mut _pyo3::ffi::PyObject,
py: #pyo3_path::Python,
_raw_slf: *mut #pyo3_path::ffi::PyObject,
#(#arg_idents: #arg_types),*
) -> _pyo3::PyResult<#ret_ty> {
) -> #pyo3_path::PyResult<#ret_ty> {
let _slf = _raw_slf;
#( #holders )*
#body
}
}
impl _pyo3::impl_::pyclass::#fragment_trait<#cls> for _pyo3::impl_::pyclass::PyClassImplCollector<#cls> {
impl #pyo3_path::impl_::pyclass::#fragment_trait<#cls> for #pyo3_path::impl_::pyclass::PyClassImplCollector<#cls> {
#[inline]
unsafe fn #method(
self,
py: _pyo3::Python,
_raw_slf: *mut _pyo3::ffi::PyObject,
py: #pyo3_path::Python,
_raw_slf: *mut #pyo3_path::ffi::PyObject,
#(#arg_idents: #arg_types),*
) -> _pyo3::PyResult<#ret_ty> {
) -> #pyo3_path::PyResult<#ret_ty> {
#cls::#wrapper_ident(py, _raw_slf, #(#arg_idents),*)
}
}
@ -1341,6 +1413,7 @@ fn extract_proto_arguments(
proto_args: &[Ty],
extract_error_mode: ExtractErrorMode,
holders: &mut Vec<TokenStream>,
ctx: &Ctx,
) -> Result<Vec<TokenStream>> {
let mut args = Vec::with_capacity(spec.signature.arguments.len());
let mut non_python_args = 0;
@ -1352,7 +1425,7 @@ fn extract_proto_arguments(
let ident = syn::Ident::new(&format!("arg{}", non_python_args), Span::call_site());
let conversions = proto_args.get(non_python_args)
.ok_or_else(|| err_spanned!(arg.ty.span() => format!("Expected at most {} non-python arguments", proto_args.len())))?
.extract(&ident, arg, extract_error_mode, holders);
.extract(&ident, arg, extract_error_mode, holders, ctx);
non_python_args += 1;
args.push(conversions);
}
@ -1372,10 +1445,14 @@ impl ToTokens for StaticIdent {
}
}
struct TokenGenerator(fn() -> TokenStream);
#[derive(Clone, Copy)]
struct TokenGenerator(fn(&Ctx) -> TokenStream);
impl ToTokens for TokenGenerator {
struct TokenGeneratorCtx<'ctx>(TokenGenerator, &'ctx Ctx);
impl ToTokens for TokenGeneratorCtx<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0().to_tokens(tokens)
let Self(TokenGenerator(gen), ctx) = self;
(gen)(ctx).to_tokens(tokens)
}
}

View File

@ -1,21 +1,25 @@
use crate::utils::Ctx;
use proc_macro2::TokenStream;
use quote::quote;
pub(crate) fn some_wrap(obj: TokenStream) -> TokenStream {
pub(crate) fn some_wrap(obj: TokenStream, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
quote! {
_pyo3::impl_::wrap::SomeWrap::wrap(#obj)
#pyo3_path::impl_::wrap::SomeWrap::wrap(#obj)
}
}
pub(crate) fn ok_wrap(obj: TokenStream) -> TokenStream {
pub(crate) fn ok_wrap(obj: TokenStream, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
quote! {
_pyo3::impl_::wrap::OkWrap::wrap(#obj)
.map_err(::core::convert::Into::<_pyo3::PyErr>::into)
#pyo3_path::impl_::wrap::OkWrap::wrap(#obj)
.map_err(::core::convert::Into::<#pyo3_path::PyErr>::into)
}
}
pub(crate) fn map_result_into_ptr(result: TokenStream) -> TokenStream {
pub(crate) fn map_result_into_ptr(result: TokenStream, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path } = ctx;
quote! {
_pyo3::impl_::wrap::map_result_into_ptr(py, #result)
#pyo3_path::impl_::wrap::map_result_into_ptr(py, #result)
}
}

View File

@ -144,11 +144,41 @@ pub fn unwrap_ty_group(mut ty: &syn::Type) -> &syn::Type {
ty
}
/// Extract the path to the pyo3 crate, or use the default (`::pyo3`).
pub(crate) fn get_pyo3_crate(attr: &Option<CrateAttribute>) -> syn::Path {
match attr {
Some(attr) => attr.value.0.clone(),
None => syn::parse_str("::pyo3").unwrap(),
pub struct Ctx {
pub pyo3_path: PyO3CratePath,
}
impl Ctx {
pub(crate) fn new(attr: &Option<CrateAttribute>) -> Self {
let pyo3_path = match attr {
Some(attr) => PyO3CratePath::Given(attr.value.0.clone()),
None => PyO3CratePath::Default,
};
Self { pyo3_path }
}
}
pub enum PyO3CratePath {
Given(syn::Path),
Default,
}
impl PyO3CratePath {
pub fn to_tokens_spanned(&self, span: Span) -> TokenStream {
match self {
Self::Given(path) => quote::quote_spanned! { span => #path },
Self::Default => quote::quote_spanned! { span => ::pyo3 },
}
}
}
impl quote::ToTokens for PyO3CratePath {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Self::Given(path) => path.to_tokens(tokens),
Self::Default => quote::quote! { ::pyo3 }.to_tokens(tokens),
}
}
}

View File

@ -31,4 +31,4 @@ error[E0592]: duplicate definitions with name `__pymethod___richcmp____`
| duplicate definitions for `__pymethod___richcmp____`
| other definition for `__pymethod___richcmp____`
|
= note: this error originates in the macro `_pyo3::impl_::pyclass::generate_pyclass_richcompare_slot` which comes from the expansion of the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)
= note: this error originates in the macro `::pyo3::impl_::pyclass::generate_pyclass_richcompare_slot` which comes from the expansion of the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)