diff --git a/CHANGELOG.md b/CHANGELOG.md index 39b85f6b..de1f6233 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `Py::setattr` method. [#2009](https://github.com/PyO3/pyo3/pull/2009) - Add `PyCapsule`, exposing the [Capsule API](https://docs.python.org/3/c-api/capsule.html#capsules). [#1980](https://github.com/PyO3/pyo3/pull/1980) +- All PyO3 proc-macros except the deprecated `#[pyproto]` now accept a supplemental attribute `#[pyo3(crate = "some::path")]` that specifies + where to find the `pyo3` crate, in case it has been renamed or is re-exported and not found at the crate root. [#2022](https://github.com/PyO3/pyo3/pull/2022) ### Changed diff --git a/guide/src/faq.md b/guide/src/faq.md index 4f2786aa..42cb5b27 100644 --- a/guide/src/faq.md +++ b/guide/src/faq.md @@ -139,3 +139,23 @@ a: b: ``` The downside to this approach is that any Rust code working on the `Outer` struct now has to acquire the GIL to do anything with its field. + +## I want to use the `pyo3` crate re-exported from from dependency but the proc-macros fail! + +All PyO3 proc-macros (`#[pyclass]`, `#[pyfunction]`, `#[derive(FromPyObject)]` +and so on) expect the `pyo3` crate to be available under that name in your crate +root, which is the normal situation when `pyo3` is a direct dependency of your +crate. + +However, when the dependency is renamed, or your crate only indirectly depends +on `pyo3`, you need to let the macro code know where to find the crate. This is +done with the `crate` attribute: + +```rust +# use pyo3::prelude::*; +# pub extern crate pyo3; +# mod reexported { pub use ::pyo3; } +#[pyclass] +#[pyo3(crate = "reexported::pyo3")] +struct MyClass; +``` diff --git a/pyo3-macros-backend/src/attributes.rs b/pyo3-macros-backend/src/attributes.rs index a0569848..e0566076 100644 --- a/pyo3-macros-backend/src/attributes.rs +++ b/pyo3-macros-backend/src/attributes.rs @@ -2,7 +2,7 @@ use syn::{ parse::{Parse, ParseStream}, punctuated::Punctuated, token::Comma, - Attribute, ExprPath, Ident, LitStr, Result, Token, + Attribute, ExprPath, Ident, LitStr, Path, Result, Token, }; pub mod kw { @@ -43,6 +43,19 @@ impl Parse for NameAttribute { } } +/// For specifying the path to the pyo3 crate. +#[derive(Clone, Debug, PartialEq)] +pub struct CrateAttribute(pub Path); + +impl Parse for CrateAttribute { + fn parse(input: ParseStream) -> Result { + let _: Token![crate] = input.parse()?; + let _: Token![=] = input.parse()?; + let string_literal: LitStr = input.parse()?; + string_literal.parse().map(CrateAttribute) + } +} + #[derive(Clone, Debug, PartialEq)] pub struct TextSignatureAttribute { pub kw: kw::text_signature, diff --git a/pyo3-macros-backend/src/deprecations.rs b/pyo3-macros-backend/src/deprecations.rs index 4bf3d624..3d5fe878 100644 --- a/pyo3-macros-backend/src/deprecations.rs +++ b/pyo3-macros-backend/src/deprecations.rs @@ -33,7 +33,7 @@ impl ToTokens for Deprecations { let ident = deprecation.ident(*span); quote_spanned!( *span => - let _ = ::pyo3::impl_::deprecations::#ident; + let _ = _pyo3::impl_::deprecations::#ident; ) .to_tokens(tokens) } diff --git a/pyo3-macros-backend/src/from_pyobject.rs b/pyo3-macros-backend/src/from_pyobject.rs index de6fedc1..c0560fb8 100644 --- a/pyo3-macros-backend/src/from_pyobject.rs +++ b/pyo3-macros-backend/src/from_pyobject.rs @@ -1,4 +1,7 @@ -use crate::attributes::{self, get_pyo3_options, FromPyWithAttribute}; +use crate::{ + attributes::{self, get_pyo3_options, CrateAttribute, FromPyWithAttribute}, + utils::get_pyo3_crate, +}; use proc_macro2::TokenStream; use quote::quote; use syn::{ @@ -55,14 +58,14 @@ impl<'a> Enum<'a> { for (i, var) in self.variants.iter().enumerate() { let struct_derive = var.build(); let ext = quote!( - let maybe_ret = || -> ::pyo3::PyResult { + let maybe_ret = || -> _pyo3::PyResult { #struct_derive }(); match maybe_ret { ok @ ::std::result::Result::Ok(_) => return ok, ::std::result::Result::Err(err) => { - let py = ::pyo3::PyNativeType::py(obj); + let py = _pyo3::PyNativeType::py(obj); err_reasons.push_str(&::std::format!("{}\n", err.value(py).str()?)); } } @@ -82,7 +85,7 @@ impl<'a> Enum<'a> { #ty_name, #error_names, &err_reasons); - ::std::result::Result::Err(::pyo3::exceptions::PyTypeError::new_err(err_msg)) + ::std::result::Result::Err(_pyo3::exceptions::PyTypeError::new_err(err_msg)) ) } } @@ -207,8 +210,8 @@ impl<'a> Container<'a> { ); quote!( ::std::result::Result::Ok(#self_ty{#ident: obj.extract().map_err(|inner| { - let py = ::pyo3::PyNativeType::py(obj); - let new_err = ::pyo3::exceptions::PyTypeError::new_err(#error_msg); + let py = _pyo3::PyNativeType::py(obj); + let new_err = _pyo3::exceptions::PyTypeError::new_err(#error_msg); new_err.set_cause(py, ::std::option::Option::Some(inner)); new_err })?}) @@ -222,11 +225,11 @@ impl<'a> Container<'a> { }; quote!( ::std::result::Result::Ok(#self_ty(obj.extract().map_err(|err| { - let py = ::pyo3::PyNativeType::py(obj); + let py = _pyo3::PyNativeType::py(obj); let err_msg = ::std::format!("{}: {}", #error_msg, err.value(py).str().unwrap()); - ::pyo3::exceptions::PyTypeError::new_err(err_msg) + _pyo3::exceptions::PyTypeError::new_err(err_msg) })?)) ) } @@ -238,9 +241,9 @@ impl<'a> Container<'a> { for i in 0..len { let error_msg = format!("failed to extract field {}.{}", quote!(#self_ty), i); fields.push(quote!( - s.get_item(#i).and_then(::pyo3::types::PyAny::extract).map_err(|inner| { - let py = ::pyo3::PyNativeType::py(obj); - let new_err = ::pyo3::exceptions::PyTypeError::new_err(#error_msg); + s.get_item(#i).and_then(_pyo3::types::PyAny::extract).map_err(|inner| { + let py = _pyo3::PyNativeType::py(obj); + let new_err = _pyo3::exceptions::PyTypeError::new_err(#error_msg); new_err.set_cause(py, ::std::option::Option::Some(inner)); new_err })?)); @@ -255,9 +258,9 @@ impl<'a> Container<'a> { quote!("") }; quote!( - let s = <::pyo3::types::PyTuple as ::pyo3::conversion::PyTryFrom>::try_from(obj)?; + let s = <_pyo3::types::PyTuple as _pyo3::conversion::PyTryFrom>::try_from(obj)?; if s.len() != #len { - return ::std::result::Result::Err(::pyo3::exceptions::PyValueError::new_err(#msg)) + return ::std::result::Result::Err(_pyo3::exceptions::PyValueError::new_err(#msg)) } ::std::result::Result::Ok(#self_ty(#fields)) ) @@ -279,15 +282,15 @@ impl<'a> Container<'a> { let extractor = match &attrs.from_py_with { None => quote!( #get_field.extract().map_err(|inner| { - let py = ::pyo3::PyNativeType::py(obj); - let new_err = ::pyo3::exceptions::PyTypeError::new_err(#conversion_error_msg); + let py = _pyo3::PyNativeType::py(obj); + let new_err = _pyo3::exceptions::PyTypeError::new_err(#conversion_error_msg); new_err.set_cause(py, ::std::option::Option::Some(inner)); new_err })?), Some(FromPyWithAttribute(expr_path)) => quote! ( #expr_path(#get_field).map_err(|inner| { - let py = ::pyo3::PyNativeType::py(obj); - let new_err = ::pyo3::exceptions::PyTypeError::new_err(#conversion_error_msg); + let py = _pyo3::PyNativeType::py(obj); + let new_err = _pyo3::exceptions::PyTypeError::new_err(#conversion_error_msg); new_err.set_cause(py, ::std::option::Option::Some(inner)); new_err })? @@ -300,20 +303,25 @@ impl<'a> Container<'a> { } } +#[derive(Default)] struct ContainerOptions { /// Treat the Container as a Wrapper, directly extract its fields from the input object. transparent: bool, /// Change the name of an enum variant in the generated error message. annotation: Option, + /// Change the path for the pyo3 crate + krate: Option, } /// Attributes for deriving FromPyObject scoped on containers. -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug)] enum ContainerPyO3Attribute { /// Treat the Container as a Wrapper, directly extract its fields from the input object. Transparent(attributes::kw::transparent), /// Change the name of an enum variant in the generated error message. ErrorAnnotation(LitStr), + /// Change the path for the pyo3 crate + Crate(CrateAttribute), } impl Parse for ContainerPyO3Attribute { @@ -326,6 +334,8 @@ impl Parse for ContainerPyO3Attribute { let _: attributes::kw::annotation = input.parse()?; let _: Token![=] = input.parse()?; input.parse().map(ContainerPyO3Attribute::ErrorAnnotation) + } else if lookahead.peek(Token![crate]) { + input.parse().map(ContainerPyO3Attribute::Crate) } else { Err(lookahead.error()) } @@ -334,10 +344,8 @@ impl Parse for ContainerPyO3Attribute { impl ContainerOptions { fn from_attrs(attrs: &[Attribute]) -> Result { - let mut options = ContainerOptions { - transparent: false, - annotation: None, - }; + let mut options = ContainerOptions::default(); + for attr in attrs { if let Some(pyo3_attrs) = get_pyo3_options(attr)? { for pyo3_attr in pyo3_attrs { @@ -356,6 +364,13 @@ impl ContainerOptions { ); options.annotation = Some(lit_str); } + ContainerPyO3Attribute::Crate(path) => { + ensure_spanned!( + options.krate.is_none(), + path.0.span() => "`crate` may only be provided once" + ); + options.krate = Some(path); + } } } } @@ -499,13 +514,18 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result { .predicates .push(parse_quote!(#gen_ident: FromPyObject<#lt_param>)) } + let options = ContainerOptions::from_attrs(&tokens.attrs)?; + let krate = get_pyo3_crate(&options.krate); let derives = match &tokens.data { syn::Data::Enum(en) => { + if options.transparent || options.annotation.is_some() { + bail_spanned!(tokens.span() => "`transparent` or `annotation` is not supported \ + at top level for enums"); + } let en = Enum::new(en, &tokens.ident)?; en.build() } syn::Data::Struct(st) => { - let options = ContainerOptions::from_attrs(&tokens.attrs)?; if let Some(lit_str) = &options.annotation { bail_spanned!(lit_str.span() => "`annotation` is unsupported for structs"); } @@ -520,11 +540,15 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result { let ident = &tokens.ident; Ok(quote!( - #[automatically_derived] - impl#trait_generics ::pyo3::FromPyObject<#lt_param> for #ident#generics #where_clause { - fn extract(obj: &#lt_param ::pyo3::PyAny) -> ::pyo3::PyResult { - #derives + const _: () = { + use #krate as _pyo3; + + #[automatically_derived] + impl#trait_generics _pyo3::FromPyObject<#lt_param> for #ident#generics #where_clause { + fn extract(obj: &#lt_param _pyo3::PyAny) -> _pyo3::PyResult { + #derives + } } - } + }; )) } diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index 6a7b8432..dda829be 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -5,7 +5,7 @@ use crate::deprecations::Deprecation; use crate::params::{accept_args_kwargs, impl_arg_params}; use crate::pyfunction::PyFunctionOptions; use crate::pyfunction::{PyFunctionArgPyO3Attributes, PyFunctionSignature}; -use crate::utils::{self, PythonDoc}; +use crate::utils::{self, get_pyo3_crate, PythonDoc}; use crate::{deprecations::Deprecations, pyfunction::Argument}; use proc_macro2::{Span, TokenStream}; use quote::ToTokens; @@ -106,12 +106,12 @@ impl FnType { } FnType::FnClass => { quote! { - let _slf = ::pyo3::types::PyType::from_type_ptr(_py, _slf as *mut ::pyo3::ffi::PyTypeObject); + let _slf = _pyo3::types::PyType::from_type_ptr(_py, _slf as *mut _pyo3::ffi::PyTypeObject); } } FnType::FnModule => { quote! { - let _slf = _py.from_borrowed_ptr::<::pyo3::types::PyModule>(_slf); + let _slf = _py.from_borrowed_ptr::<_pyo3::types::PyModule>(_slf); } } } @@ -141,13 +141,13 @@ impl SelfType { pub fn receiver(&self, cls: &syn::Type, error_mode: ExtractErrorMode) -> TokenStream { let cell = match error_mode { ExtractErrorMode::Raise => { - quote! { _py.from_borrowed_ptr::<::pyo3::PyAny>(_slf).downcast::<::pyo3::PyCell<#cls>>()? } + quote! { _py.from_borrowed_ptr::<_pyo3::PyAny>(_slf).downcast::<_pyo3::PyCell<#cls>>()? } } ExtractErrorMode::NotImplemented => { quote! { - match _py.from_borrowed_ptr::<::pyo3::PyAny>(_slf).downcast::<::pyo3::PyCell<#cls>>() { + match _py.from_borrowed_ptr::<_pyo3::PyAny>(_slf).downcast::<_pyo3::PyCell<#cls>>() { ::std::result::Result::Ok(cell) => cell, - ::std::result::Result::Err(_) => return ::pyo3::callback::convert(_py, _py.NotImplemented()), + ::std::result::Result::Err(_) => return _pyo3::callback::convert(_py, _py.NotImplemented()), } } } @@ -228,6 +228,7 @@ pub struct FnSpec<'a> { pub deprecations: Deprecations, pub convention: CallingConvention, pub text_signature: Option, + pub krate: syn::Path, } pub fn get_return_info(output: &syn::ReturnType) -> syn::Type { @@ -254,12 +255,14 @@ pub fn parse_method_receiver(arg: &syn::FnArg) -> Result { impl<'a> FnSpec<'a> { /// Parser function signature and function attributes pub fn parse( + // Signature is mutable to remove the `Python` argument. sig: &'a mut syn::Signature, meth_attrs: &mut Vec, options: PyFunctionOptions, ) -> Result> { let PyFunctionOptions { text_signature, + krate, name, mut deprecations, .. @@ -278,6 +281,7 @@ impl<'a> FnSpec<'a> { let name = &sig.ident; let ty = get_return_info(&sig.output); let python_name = python_name.as_ref().unwrap_or(name).unraw(); + let krate = get_pyo3_crate(&krate); let doc = utils::get_doc( meth_attrs, @@ -311,6 +315,7 @@ impl<'a> FnSpec<'a> { doc, deprecations, text_signature, + krate, }) } @@ -471,17 +476,19 @@ impl<'a> FnSpec<'a> { quote!(#func_name) }; let rust_call = - quote! { ::pyo3::callback::convert(#py, #rust_name(#self_arg #(#arg_names),*)) }; + quote! { _pyo3::callback::convert(#py, #rust_name(#self_arg #(#arg_names),*)) }; + let krate = &self.krate; Ok(match self.convention { CallingConvention::Noargs => { quote! { unsafe extern "C" fn #ident ( - _slf: *mut ::pyo3::ffi::PyObject, - _args: *mut ::pyo3::ffi::PyObject, - ) -> *mut ::pyo3::ffi::PyObject + _slf: *mut #krate::ffi::PyObject, + _args: *mut #krate::ffi::PyObject, + ) -> *mut #krate::ffi::PyObject { + use #krate as _pyo3; #deprecations - ::pyo3::callback::handle_panic(|#py| { + _pyo3::callback::handle_panic(|#py| { #self_conversion #rust_call }) @@ -492,17 +499,18 @@ impl<'a> FnSpec<'a> { let arg_convert_and_rust_call = impl_arg_params(self, cls, rust_call, &py, true)?; quote! { unsafe extern "C" fn #ident ( - _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 #krate::ffi::PyObject, + _args: *const *mut #krate::ffi::PyObject, + _nargs: #krate::ffi::Py_ssize_t, + _kwnames: *mut #krate::ffi::PyObject) -> *mut #krate::ffi::PyObject { + use #krate as _pyo3; #deprecations - ::pyo3::callback::handle_panic(|#py| { + _pyo3::callback::handle_panic(|#py| { #self_conversion - let _kwnames: ::std::option::Option<&::pyo3::types::PyTuple> = #py.from_borrowed_ptr_or_opt(_kwnames); + let _kwnames: ::std::option::Option<&_pyo3::types::PyTuple> = #py.from_borrowed_ptr_or_opt(_kwnames); // Safety: &PyAny has the same memory layout as `*mut ffi::PyObject` - let _args = _args as *const &::pyo3::PyAny; + let _args = _args as *const &_pyo3::PyAny; let _kwargs = if let ::std::option::Option::Some(kwnames) = _kwnames { ::std::slice::from_raw_parts(_args.offset(_nargs), kwnames.len()) } else { @@ -519,15 +527,16 @@ impl<'a> FnSpec<'a> { let arg_convert_and_rust_call = impl_arg_params(self, cls, rust_call, &py, false)?; quote! { unsafe extern "C" fn #ident ( - _slf: *mut ::pyo3::ffi::PyObject, - _args: *mut ::pyo3::ffi::PyObject, - _kwargs: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject + _slf: *mut #krate::ffi::PyObject, + _args: *mut #krate::ffi::PyObject, + _kwargs: *mut #krate::ffi::PyObject) -> *mut #krate::ffi::PyObject { + use #krate as _pyo3; #deprecations - ::pyo3::callback::handle_panic(|#py| { + _pyo3::callback::handle_panic(|#py| { #self_conversion - let _args = #py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args); - let _kwargs: ::std::option::Option<&::pyo3::types::PyDict> = #py.from_borrowed_ptr_or_opt(_kwargs); + let _args = #py.from_borrowed_ptr::<_pyo3::types::PyTuple>(_args); + let _kwargs: ::std::option::Option<&_pyo3::types::PyDict> = #py.from_borrowed_ptr_or_opt(_kwargs); #arg_convert_and_rust_call }) @@ -539,20 +548,21 @@ impl<'a> FnSpec<'a> { let arg_convert_and_rust_call = impl_arg_params(self, cls, rust_call, &py, false)?; quote! { unsafe extern "C" fn #ident ( - subtype: *mut ::pyo3::ffi::PyTypeObject, - _args: *mut ::pyo3::ffi::PyObject, - _kwargs: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject + subtype: *mut #krate::ffi::PyTypeObject, + _args: *mut #krate::ffi::PyObject, + _kwargs: *mut #krate::ffi::PyObject) -> *mut #krate::ffi::PyObject { + use #krate as _pyo3; #deprecations - use ::pyo3::callback::IntoPyCallbackOutput; - ::pyo3::callback::handle_panic(|#py| { - let _args = #py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args); - let _kwargs: ::std::option::Option<&::pyo3::types::PyDict> = #py.from_borrowed_ptr_or_opt(_kwargs); + use _pyo3::callback::IntoPyCallbackOutput; + _pyo3::callback::handle_panic(|#py| { + let _args = #py.from_borrowed_ptr::<_pyo3::types::PyTuple>(_args); + let _kwargs: ::std::option::Option<&_pyo3::types::PyDict> = #py.from_borrowed_ptr_or_opt(_kwargs); let result = #arg_convert_and_rust_call; - let initializer: ::pyo3::PyClassInitializer::<#cls> = result.convert(#py)?; + let initializer: _pyo3::PyClassInitializer::<#cls> = result.convert(#py)?; let cell = initializer.create_cell_from_subtype(#py, subtype)?; - ::std::result::Result::Ok(cell as *mut ::pyo3::ffi::PyObject) + ::std::result::Result::Ok(cell as *mut _pyo3::ffi::PyObject) }) } } @@ -567,23 +577,23 @@ impl<'a> FnSpec<'a> { let doc = &self.doc; match self.convention { CallingConvention::Noargs => quote! { - ::pyo3::class::methods::PyMethodDef::noargs( + _pyo3::class::methods::PyMethodDef::noargs( #python_name, - ::pyo3::class::methods::PyCFunction(#wrapper), + _pyo3::class::methods::PyCFunction(#wrapper), #doc, ) }, CallingConvention::Fastcall => quote! { - ::pyo3::class::methods::PyMethodDef::fastcall_cfunction_with_keywords( + _pyo3::class::methods::PyMethodDef::fastcall_cfunction_with_keywords( #python_name, - ::pyo3::class::methods::PyCFunctionFastWithKeywords(#wrapper), + _pyo3::class::methods::PyCFunctionFastWithKeywords(#wrapper), #doc, ) }, CallingConvention::Varargs => quote! { - ::pyo3::class::methods::PyMethodDef::cfunction_with_keywords( + _pyo3::class::methods::PyMethodDef::cfunction_with_keywords( #python_name, - ::pyo3::class::methods::PyCFunctionWithKeywords(#wrapper), + _pyo3::class::methods::PyCFunctionWithKeywords(#wrapper), #doc, ) }, diff --git a/pyo3-macros-backend/src/module.rs b/pyo3-macros-backend/src/module.rs index 5adc02d4..1e6fe24a 100644 --- a/pyo3-macros-backend/src/module.rs +++ b/pyo3-macros-backend/src/module.rs @@ -2,9 +2,11 @@ //! Code generation for the function that initializes a python module and adds classes and function. use crate::{ - attributes::{self, is_attribute_ident, take_attributes, take_pyo3_options, NameAttribute}, + attributes::{ + self, is_attribute_ident, take_attributes, take_pyo3_options, CrateAttribute, NameAttribute, + }, pyfunction::{impl_wrap_pyfunction, PyFunctionOptions}, - utils::PythonDoc, + utils::{get_pyo3_crate, PythonDoc}, }; use proc_macro2::{Span, TokenStream}; use quote::quote; @@ -16,17 +18,20 @@ use syn::{ Ident, Path, Result, }; +#[derive(Default)] pub struct PyModuleOptions { + krate: Option, name: Option, } impl PyModuleOptions { pub fn from_attrs(attrs: &mut Vec) -> Result { - let mut options: PyModuleOptions = PyModuleOptions { name: None }; + let mut options: PyModuleOptions = Default::default(); for option in take_pyo3_options(attrs)? { match option { PyModulePyO3Option::Name(name) => options.set_name(name.0)?, + PyModulePyO3Option::Crate(path) => options.set_crate(path)?, } } @@ -42,12 +47,23 @@ impl PyModuleOptions { self.name = Some(name); Ok(()) } + + fn set_crate(&mut self, path: CrateAttribute) -> Result<()> { + ensure_spanned!( + self.krate.is_none(), + path.0.span() => "`crate` may only be specified once" + ); + + self.krate = Some(path); + Ok(()) + } } /// Generates the function that is called by the python interpreter to initialize the native /// module pub fn py_init(fnname: &Ident, options: PyModuleOptions, doc: PythonDoc) -> TokenStream { let name = options.name.unwrap_or_else(|| fnname.unraw()); + let krate = get_pyo3_crate(&options.krate); let cb_name = Ident::new(&format!("PyInit_{}", name), Span::call_site()); quote! { @@ -55,13 +71,14 @@ pub fn py_init(fnname: &Ident, options: PyModuleOptions, doc: PythonDoc) -> Toke #[allow(non_snake_case)] /// This autogenerated function is called by the python interpreter when importing /// the module. - pub unsafe extern "C" fn #cb_name() -> *mut ::pyo3::ffi::PyObject { - use ::pyo3::derive_utils::ModuleDef; + pub unsafe extern "C" fn #cb_name() -> *mut #krate::ffi::PyObject { + use #krate as _pyo3; + use _pyo3::derive_utils::ModuleDef; static NAME: &str = concat!(stringify!(#name), "\0"); static DOC: &str = #doc; static MODULE_DEF: ModuleDef = unsafe { ModuleDef::new(NAME, DOC) }; - ::pyo3::callback::handle_panic(|_py| { MODULE_DEF.make_module(_py, #fnname) }) + _pyo3::callback::handle_panic(|_py| { MODULE_DEF.make_module(_py, #fnname) }) } } } @@ -143,6 +160,7 @@ fn get_pyfn_attr(attrs: &mut Vec) -> syn::Result::NAME) } + quote! { ::std::option::Option::Some(<#cls as _pyo3::type_object::PyTypeInfo>::NAME) } } else { quote! { ::std::option::Option::None } }; @@ -163,7 +163,7 @@ pub fn impl_arg_params( // create array of arguments, and then parse Ok(quote! {{ - const DESCRIPTION: ::pyo3::derive_utils::FunctionDescription = ::pyo3::derive_utils::FunctionDescription { + const DESCRIPTION: _pyo3::derive_utils::FunctionDescription = _pyo3::derive_utils::FunctionDescription { cls_name: #cls_name, func_name: stringify!(#python_name), positional_parameter_names: &[#(#positional_parameter_names),*], @@ -214,7 +214,7 @@ fn impl_arg_param( let ty = arg.ty; let name = arg.name; let transform_error = quote! { - |e| ::pyo3::derive_utils::argument_extraction_error(#py, stringify!(#name), e) + |e| _pyo3::derive_utils::argument_extraction_error(#py, stringify!(#name), e) }; if is_args(&spec.attrs, name) { @@ -283,7 +283,7 @@ fn impl_arg_param( let (target_ty, borrow_tmp) = if arg.optional.is_some() { // Get Option<&T> from Option> ( - quote_arg_span! { ::std::option::Option<<#tref as ::pyo3::derive_utils::ExtractExt<'_>>::Target> }, + quote_arg_span! { ::std::option::Option<<#tref as _pyo3::derive_utils::ExtractExt<'_>>::Target> }, if mut_.is_some() { quote_arg_span! { _tmp.as_deref_mut() } } else { @@ -293,7 +293,7 @@ fn impl_arg_param( } else { // Get &T from PyRef ( - quote_arg_span! { <#tref as ::pyo3::derive_utils::ExtractExt<'_>>::Target }, + quote_arg_span! { <#tref as _pyo3::derive_utils::ExtractExt<'_>>::Target }, quote_arg_span! { &#mut_ *_tmp }, ) }; diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 63d5087b..2760571e 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -1,11 +1,13 @@ // Copyright (c) 2017-present PyO3 Project and Contributors -use crate::attributes::{self, take_pyo3_options, NameAttribute, TextSignatureAttribute}; +use crate::attributes::{ + self, take_pyo3_options, CrateAttribute, NameAttribute, TextSignatureAttribute, +}; use crate::deprecations::Deprecations; use crate::konst::{ConstAttributes, ConstSpec}; use crate::pyimpl::{gen_default_slot_impls, gen_py_const, PyClassMethodsType}; use crate::pymethod::{impl_py_getter_def, impl_py_setter_def, PropertyType}; -use crate::utils::{self, unwrap_group, PythonDoc}; +use crate::utils::{self, get_pyo3_crate, unwrap_group, PythonDoc}; use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::ext::IdentExt; @@ -58,7 +60,7 @@ impl PyClassArgs { freelist: None, name: None, module: None, - base: parse_quote! { ::pyo3::PyAny }, + base: parse_quote! { _pyo3::PyAny }, has_dict: false, has_weaklist: false, is_gc: false, @@ -186,10 +188,12 @@ impl PyClassArgs { pub struct PyClassPyO3Options { pub text_signature: Option, pub deprecations: Deprecations, + pub krate: Option, } enum PyClassPyO3Option { TextSignature(TextSignatureAttribute), + Crate(CrateAttribute), } impl Parse for PyClassPyO3Option { @@ -197,6 +201,8 @@ impl Parse for PyClassPyO3Option { let lookahead = input.lookahead1(); if lookahead.peek(attributes::kw::text_signature) { input.parse().map(PyClassPyO3Option::TextSignature) + } else if lookahead.peek(Token![crate]) { + input.parse().map(PyClassPyO3Option::Crate) } else { Err(lookahead.error()) } @@ -211,6 +217,9 @@ impl PyClassPyO3Options { PyClassPyO3Option::TextSignature(text_signature) => { options.set_text_signature(text_signature)?; } + PyClassPyO3Option::Crate(path) => { + options.set_crate(path)?; + } } } Ok(options) @@ -227,6 +236,15 @@ impl PyClassPyO3Options { self.text_signature = Some(text_signature); Ok(()) } + + pub fn set_crate(&mut self, path: CrateAttribute) -> syn::Result<()> { + ensure_spanned!( + self.krate.is_none(), + path.0.span() => "`text_signature` may only be specified once" + ); + self.krate = Some(path); + Ok(()) + } } pub fn build_py_class( @@ -242,6 +260,7 @@ pub fn build_py_class( .as_ref() .map(|attr| (get_class_python_name(&class.ident, args), attr)), ); + let krate = get_pyo3_crate(&options.krate); ensure_spanned!( class.generics.params.is_empty(), @@ -278,6 +297,7 @@ pub fn build_py_class( field_options, methods_type, options.deprecations, + krate, ) } @@ -358,6 +378,7 @@ fn impl_class( field_options: Vec<(&syn::Field, FieldPyO3Options)>, methods_type: PyClassMethodsType, deprecations: Deprecations, + krate: syn::Path, ) -> syn::Result { let pytypeinfo_impl = impl_pytypeinfo(cls, attr, Some(&deprecations)); @@ -368,11 +389,15 @@ fn impl_class( let descriptors = impl_descriptors(cls, field_options)?; Ok(quote! { - #pytypeinfo_impl + const _: () = { + use #krate as _pyo3; - #py_class_impl + #pytypeinfo_impl - #descriptors + #py_class_impl + + #descriptors + }; }) } @@ -382,10 +407,12 @@ struct PyClassEnumVariant<'a> { } pub fn build_py_enum( - enum_: &syn::ItemEnum, - args: PyClassArgs, + enum_: &mut syn::ItemEnum, + args: &PyClassArgs, method_type: PyClassMethodsType, ) -> syn::Result { + let options = PyClassPyO3Options::take_pyo3_options(&mut enum_.attrs)?; + if enum_.variants.is_empty() { bail_spanned!(enum_.brace_token.span => "Empty enums can't be #[pyclass]."); } @@ -394,33 +421,38 @@ pub fn build_py_enum( .iter() .map(extract_variant_data) .collect::>()?; - impl_enum(enum_, args, variants, method_type) + impl_enum(enum_, args, variants, method_type, options) } fn impl_enum( enum_: &syn::ItemEnum, - attrs: PyClassArgs, + args: &PyClassArgs, variants: Vec, methods_type: PyClassMethodsType, + options: PyClassPyO3Options, ) -> syn::Result { let enum_name = &enum_.ident; - let doc = utils::get_doc(&enum_.attrs, None); - let enum_cls = impl_enum_class(enum_name, &attrs, variants, doc, methods_type)?; - - Ok(quote! { - #enum_cls - }) + let doc = utils::get_doc( + &enum_.attrs, + options + .text_signature + .as_ref() + .map(|attr| (get_class_python_name(&enum_.ident, args), attr)), + ); + let krate = get_pyo3_crate(&options.krate); + impl_enum_class(enum_name, args, variants, doc, methods_type, krate) } fn impl_enum_class( cls: &syn::Ident, - attr: &PyClassArgs, + args: &PyClassArgs, variants: Vec, doc: PythonDoc, methods_type: PyClassMethodsType, + krate: syn::Path, ) -> syn::Result { - let pytypeinfo = impl_pytypeinfo(cls, attr, None); - let pyclass_impls = PyClassImplsBuilder::new(cls, attr, methods_type) + let pytypeinfo = impl_pytypeinfo(cls, args, None); + let pyclass_impls = PyClassImplsBuilder::new(cls, args, methods_type) .doc(doc) .impl_all(); let descriptors = unit_variants_as_descriptors(cls, variants.iter().map(|v| v.ident)); @@ -447,15 +479,17 @@ fn impl_enum_class( let default_impls = gen_default_slot_impls(cls, vec![default_repr_impl]); Ok(quote! { + const _: () = { + use #krate as _pyo3; - #pytypeinfo + #pytypeinfo - #pyclass_impls + #pyclass_impls - #descriptors - - #default_impls + #default_impls + #descriptors + }; }) } @@ -477,11 +511,11 @@ fn unit_variants_as_descriptors<'a>( .map(|var| gen_py_const(&cls_type, &variant_to_attribute(var))); quote! { - impl ::pyo3::class::impl_::PyClassDescriptors<#cls> - for ::pyo3::class::impl_::PyClassImplCollector<#cls> + impl _pyo3::class::impl_::PyClassDescriptors<#cls> + for _pyo3::class::impl_::PyClassImplCollector<#cls> { - fn py_class_descriptors(self) -> &'static [::pyo3::class::methods::PyMethodDefType] { - static METHODS: &[::pyo3::class::methods::PyMethodDefType] = &[#(#py_methods),*]; + fn py_class_descriptors(self) -> &'static [_pyo3::class::methods::PyMethodDefType] { + static METHODS: &[_pyo3::class::methods::PyMethodDefType] = &[#(#py_methods),*]; METHODS } } @@ -540,11 +574,11 @@ fn impl_descriptors( .collect::>()?; Ok(quote! { - impl ::pyo3::class::impl_::PyClassDescriptors<#cls> - for ::pyo3::class::impl_::PyClassImplCollector<#cls> + impl _pyo3::class::impl_::PyClassDescriptors<#cls> + for _pyo3::class::impl_::PyClassImplCollector<#cls> { - fn py_class_descriptors(self) -> &'static [::pyo3::class::methods::PyMethodDefType] { - static METHODS: &[::pyo3::class::methods::PyMethodDefType] = &[#(#py_methods),*]; + fn py_class_descriptors(self) -> &'static [_pyo3::class::methods::PyMethodDefType] { + static METHODS: &[_pyo3::class::methods::PyMethodDefType] = &[#(#py_methods),*]; METHODS } } @@ -565,17 +599,17 @@ fn impl_pytypeinfo( }; quote! { - unsafe impl ::pyo3::type_object::PyTypeInfo for #cls { - type AsRefTarget = ::pyo3::PyCell; + unsafe impl _pyo3::type_object::PyTypeInfo for #cls { + type AsRefTarget = _pyo3::PyCell; 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 { + fn type_object_raw(py: _pyo3::Python<'_>) -> *mut _pyo3::ffi::PyTypeObject { #deprecations - use ::pyo3::type_object::LazyStaticType; + use _pyo3::type_object::LazyStaticType; static TYPE_OBJECT: LazyStaticType = LazyStaticType::new(); TYPE_OBJECT.get_or_init::(py) } @@ -629,29 +663,29 @@ impl<'a> PyClassImplsBuilder<'a> { let cls = self.cls; let attr = self.attr; let dict = if attr.has_dict { - quote! { ::pyo3::pyclass_slots::PyClassDictSlot } + quote! { _pyo3::pyclass_slots::PyClassDictSlot } } else if attr.has_extends { - quote! { ::Dict } + quote! { ::Dict } } else { - quote! { ::pyo3::pyclass_slots::PyClassDummySlot } + quote! { _pyo3::pyclass_slots::PyClassDummySlot } }; // insert space for weak ref let weakref = if attr.has_weaklist { - quote! { ::pyo3::pyclass_slots::PyClassWeakRefSlot } + quote! { _pyo3::pyclass_slots::PyClassWeakRefSlot } } else if attr.has_extends { - quote! { ::WeakRef } + quote! { ::WeakRef } } else { - quote! { ::pyo3::pyclass_slots::PyClassDummySlot } + quote! { _pyo3::pyclass_slots::PyClassDummySlot } }; let base_nativetype = if attr.has_extends { - quote! { ::BaseNativeType } + quote! { ::BaseNativeType } } else { - quote! { ::pyo3::PyAny } + quote! { _pyo3::PyAny } }; quote! { - impl ::pyo3::PyClass for #cls { + impl _pyo3::PyClass for #cls { type Dict = #dict; type WeakRef = #weakref; type BaseNativeType = #base_nativetype; @@ -661,14 +695,14 @@ impl<'a> PyClassImplsBuilder<'a> { fn impl_extractext(&self) -> TokenStream { let cls = self.cls; quote! { - impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a #cls + impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a #cls { - type Target = ::pyo3::PyRef<'a, #cls>; + type Target = _pyo3::PyRef<'a, #cls>; } - impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a mut #cls + impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a mut #cls { - type Target = ::pyo3::PyRefMut<'a, #cls>; + type Target = _pyo3::PyRefMut<'a, #cls>; } } } @@ -679,9 +713,9 @@ impl<'a> PyClassImplsBuilder<'a> { // If #cls is not extended type, we allow Self->PyObject conversion if !attr.has_extends { 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::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) } } } @@ -698,13 +732,13 @@ impl<'a> PyClassImplsBuilder<'a> { let is_subclass = self.attr.has_extends; let thread_checker = if self.attr.has_unsendable { - quote! { ::pyo3::class::impl_::ThreadCheckerImpl<#cls> } + quote! { _pyo3::class::impl_::ThreadCheckerImpl<#cls> } } else if self.attr.has_extends { quote! { - ::pyo3::class::impl_::ThreadCheckerInherited<#cls, <#cls as ::pyo3::class::impl_::PyClassImpl>::BaseType> + _pyo3::class::impl_::ThreadCheckerInherited<#cls, <#cls as _pyo3::class::impl_::PyClassImpl>::BaseType> } } else { - quote! { ::pyo3::class::impl_::ThreadCheckerStub<#cls> } + quote! { _pyo3::class::impl_::ThreadCheckerStub<#cls> } }; let (for_each_py_method, methods_protos, inventory, inventory_class) = match self @@ -724,13 +758,13 @@ impl<'a> PyClassImplsBuilder<'a> { ); ( quote! { - for inventory in ::pyo3::inventory::iter::<::Inventory>() { - visitor(::pyo3::class::impl_::PyClassInventory::methods(inventory)); + for inventory in _pyo3::inventory::iter::<::Inventory>() { + visitor(_pyo3::class::impl_::PyClassInventory::methods(inventory)); } }, quote! { - for inventory in ::pyo3::inventory::iter::<::Inventory>() { - visitor(::pyo3::class::impl_::PyClassInventory::slots(inventory)); + for inventory in _pyo3::inventory::iter::<::Inventory>() { + visitor(_pyo3::class::impl_::PyClassInventory::slots(inventory)); } }, Some(quote! { type Inventory = #inventory_class_name; }), @@ -739,19 +773,19 @@ impl<'a> PyClassImplsBuilder<'a> { } }; quote! { - impl ::pyo3::class::impl_::PyClassImpl for #cls { + impl _pyo3::class::impl_::PyClassImpl for #cls { const DOC: &'static str = #doc; const IS_GC: bool = #is_gc; const IS_BASETYPE: bool = #is_basetype; const IS_SUBCLASS: bool = #is_subclass; - type Layout = ::pyo3::PyCell; + type Layout = _pyo3::PyCell; type BaseType = #base; type ThreadChecker = #thread_checker; #inventory - fn for_each_method_def(visitor: &mut dyn ::std::ops::FnMut(&[::pyo3::class::PyMethodDefType])) { - use ::pyo3::class::impl_::*; + fn for_each_method_def(visitor: &mut dyn ::std::ops::FnMut(&[_pyo3::class::PyMethodDefType])) { + use _pyo3::class::impl_::*; let collector = PyClassImplCollector::::new(); #for_each_py_method; visitor(collector.py_class_descriptors()); @@ -761,25 +795,25 @@ impl<'a> PyClassImplsBuilder<'a> { visitor(collector.mapping_protocol_methods()); visitor(collector.number_protocol_methods()); } - fn get_new() -> ::std::option::Option<::pyo3::ffi::newfunc> { - use ::pyo3::class::impl_::*; + fn get_new() -> ::std::option::Option<_pyo3::ffi::newfunc> { + use _pyo3::class::impl_::*; let collector = PyClassImplCollector::::new(); collector.new_impl() } - fn get_alloc() -> ::std::option::Option<::pyo3::ffi::allocfunc> { - use ::pyo3::class::impl_::*; + fn get_alloc() -> ::std::option::Option<_pyo3::ffi::allocfunc> { + use _pyo3::class::impl_::*; let collector = PyClassImplCollector::::new(); collector.alloc_impl() } - fn get_free() -> ::std::option::Option<::pyo3::ffi::freefunc> { - use ::pyo3::class::impl_::*; + fn get_free() -> ::std::option::Option<_pyo3::ffi::freefunc> { + use _pyo3::class::impl_::*; let collector = PyClassImplCollector::::new(); collector.free_impl() } - fn for_each_proto_slot(visitor: &mut dyn ::std::ops::FnMut(&[::pyo3::ffi::PyType_Slot])) { + fn for_each_proto_slot(visitor: &mut dyn ::std::ops::FnMut(&[_pyo3::ffi::PyType_Slot])) { // Implementation which uses dtolnay specialization to load all slots. - use ::pyo3::class::impl_::*; + use _pyo3::class::impl_::*; let collector = PyClassImplCollector::::new(); // This depends on Python implementation detail; // an old slot entry will be overriden by newer ones. @@ -796,8 +830,8 @@ impl<'a> PyClassImplsBuilder<'a> { #methods_protos } - fn get_buffer() -> ::std::option::Option<&'static ::pyo3::class::impl_::PyBufferProcs> { - use ::pyo3::class::impl_::*; + fn get_buffer() -> ::std::option::Option<&'static _pyo3::class::impl_::PyBufferProcs> { + use _pyo3::class::impl_::*; let collector = PyClassImplCollector::::new(); collector.buffer_procs() } @@ -812,31 +846,31 @@ impl<'a> PyClassImplsBuilder<'a> { self.attr.freelist.as_ref().map_or(quote!{}, |freelist| { quote! { - impl ::pyo3::class::impl_::PyClassWithFreeList for #cls { + impl _pyo3::class::impl_::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::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 _; unsafe { if FREELIST.is_null() { FREELIST = ::std::boxed::Box::into_raw(::std::boxed::Box::new( - ::pyo3::impl_::freelist::FreeList::with_capacity(#freelist))); + _pyo3::impl_::freelist::FreeList::with_capacity(#freelist))); } &mut *FREELIST } } } - impl ::pyo3::class::impl_::PyClassAllocImpl<#cls> for ::pyo3::class::impl_::PyClassImplCollector<#cls> { + impl _pyo3::class::impl_::PyClassAllocImpl<#cls> for _pyo3::class::impl_::PyClassImplCollector<#cls> { #[inline] - fn alloc_impl(self) -> ::std::option::Option<::pyo3::ffi::allocfunc> { - ::std::option::Option::Some(::pyo3::class::impl_::alloc_with_freelist::<#cls>) + fn alloc_impl(self) -> ::std::option::Option<_pyo3::ffi::allocfunc> { + ::std::option::Option::Some(_pyo3::class::impl_::alloc_with_freelist::<#cls>) } } - impl ::pyo3::class::impl_::PyClassFreeImpl<#cls> for ::pyo3::class::impl_::PyClassImplCollector<#cls> { + impl _pyo3::class::impl_::PyClassFreeImpl<#cls> for _pyo3::class::impl_::PyClassImplCollector<#cls> { #[inline] - fn free_impl(self) -> ::std::option::Option<::pyo3::ffi::freefunc> { - ::std::option::Option::Some(::pyo3::class::impl_::free_with_freelist::<#cls>) + fn free_impl(self) -> ::std::option::Option<_pyo3::ffi::freefunc> { + ::std::option::Option::Some(_pyo3::class::impl_::free_with_freelist::<#cls>) } } } @@ -851,9 +885,9 @@ impl<'a> PyClassImplsBuilder<'a> { let closure_token = syn::Ident::new(&closure_name, Span::call_site()); quote! { fn #closure_token() { - use ::pyo3::class; + use _pyo3::class; - fn _assert_implements_protocol<'p, T: ::pyo3::class::PyGCProtocol<'p>>() {} + fn _assert_implements_protocol<'p, T: _pyo3::class::PyGCProtocol<'p>>() {} _assert_implements_protocol::<#cls>(); } } @@ -867,23 +901,23 @@ fn define_inventory_class(inventory_class_name: &syn::Ident) -> TokenStream { quote! { #[doc(hidden)] pub struct #inventory_class_name { - methods: &'static [::pyo3::class::PyMethodDefType], - slots: &'static [::pyo3::ffi::PyType_Slot], + methods: &'static [_pyo3::class::PyMethodDefType], + slots: &'static [_pyo3::ffi::PyType_Slot], } impl #inventory_class_name { const fn new( - methods: &'static [::pyo3::class::PyMethodDefType], - slots: &'static [::pyo3::ffi::PyType_Slot], + methods: &'static [_pyo3::class::PyMethodDefType], + slots: &'static [_pyo3::ffi::PyType_Slot], ) -> Self { Self { methods, slots } } } - impl ::pyo3::class::impl_::PyClassInventory for #inventory_class_name { - fn methods(&'static self) -> &'static [::pyo3::class::PyMethodDefType] { + impl _pyo3::class::impl_::PyClassInventory for #inventory_class_name { + fn methods(&'static self) -> &'static [_pyo3::class::PyMethodDefType] { self.methods } - fn slots(&'static self) -> &'static [::pyo3::ffi::PyType_Slot] { + fn slots(&'static self) -> &'static [_pyo3::ffi::PyType_Slot] { self.slots } } @@ -892,6 +926,6 @@ fn define_inventory_class(inventory_class_name: &syn::Ident) -> TokenStream { unsafe impl ::std::marker::Send for #inventory_class_name {} unsafe impl ::std::marker::Sync for #inventory_class_name {} - ::pyo3::inventory::collect!(#inventory_class_name); + _pyo3::inventory::collect!(#inventory_class_name); } } diff --git a/pyo3-macros-backend/src/pyfunction.rs b/pyo3-macros-backend/src/pyfunction.rs index 87e8ffda..28577bfb 100644 --- a/pyo3-macros-backend/src/pyfunction.rs +++ b/pyo3-macros-backend/src/pyfunction.rs @@ -2,13 +2,13 @@ use crate::{ attributes::{ - self, get_pyo3_options, take_attributes, take_pyo3_options, FromPyWithAttribute, - NameAttribute, TextSignatureAttribute, + self, get_pyo3_options, take_attributes, take_pyo3_options, CrateAttribute, + FromPyWithAttribute, NameAttribute, TextSignatureAttribute, }, deprecations::Deprecations, method::{self, CallingConvention, FnArg}, pymethod::check_generic, - utils::{self, ensure_not_async_fn}, + utils::{self, ensure_not_async_fn, get_pyo3_crate}, }; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; @@ -239,17 +239,12 @@ pub struct PyFunctionOptions { pub signature: Option, pub text_signature: Option, pub deprecations: Deprecations, + pub krate: Option, } impl Parse for PyFunctionOptions { fn parse(input: ParseStream) -> Result { - let mut options = PyFunctionOptions { - pass_module: None, - name: None, - signature: None, - text_signature: None, - deprecations: Deprecations::new(), - }; + let mut options = PyFunctionOptions::default(); while !input.is_empty() { let lookahead = input.lookahead1(); @@ -262,6 +257,9 @@ impl Parse for PyFunctionOptions { if !input.is_empty() { let _: Comma = input.parse()?; } + } else if lookahead.peek(syn::Token![crate]) { + // TODO needs duplicate check? + options.krate = Some(input.parse()?); } else { // If not recognised attribute, this is "legacy" pyfunction syntax #[pyfunction(a, b)] // @@ -280,6 +278,7 @@ pub enum PyFunctionOption { PassModule(attributes::kw::pass_module), Signature(PyFunctionSignature), TextSignature(TextSignatureAttribute), + Crate(CrateAttribute), } impl Parse for PyFunctionOption { @@ -293,6 +292,8 @@ impl Parse for PyFunctionOption { input.parse().map(PyFunctionOption::Signature) } else if lookahead.peek(attributes::kw::text_signature) { input.parse().map(PyFunctionOption::TextSignature) + } else if lookahead.peek(syn::Token![crate]) { + input.parse().map(PyFunctionOption::Crate) } else { Err(lookahead.error()) } @@ -335,6 +336,13 @@ impl PyFunctionOptions { ); self.text_signature = Some(text_signature); } + PyFunctionOption::Crate(path) => { + ensure_spanned!( + self.krate.is_none(), + path.0.span() => "`crate` may only be specified once" + ); + self.krate = Some(path); + } } } Ok(()) @@ -410,6 +418,7 @@ pub fn impl_wrap_pyfunction( ); let function_wrapper_ident = function_wrapper_ident(&func.sig.ident); + let krate = get_pyo3_crate(&options.krate); let spec = method::FnSpec { tp: if options.pass_module.is_some() { @@ -426,6 +435,7 @@ pub fn impl_wrap_pyfunction( doc, deprecations: options.deprecations, text_signature: options.text_signature, + krate: krate.clone(), }; let wrapper_ident = format_ident!("__pyo3_raw_{}", spec.name); @@ -434,10 +444,12 @@ pub fn impl_wrap_pyfunction( let wrapped_pyfunction = quote! { #wrapper + pub(crate) fn #function_wrapper_ident<'a>( - args: impl ::std::convert::Into<::pyo3::derive_utils::PyFunctionArguments<'a>> - ) -> ::pyo3::PyResult<&'a ::pyo3::types::PyCFunction> { - ::pyo3::types::PyCFunction::internal_new(#methoddef, args.into()) + args: impl ::std::convert::Into<#krate::derive_utils::PyFunctionArguments<'a>> + ) -> #krate::PyResult<&'a #krate::types::PyCFunction> { + use #krate as _pyo3; + _pyo3::types::PyCFunction::internal_new(#methoddef, args.into()) } }; Ok((function_wrapper_ident, wrapped_pyfunction)) diff --git a/pyo3-macros-backend/src/pyimpl.rs b/pyo3-macros-backend/src/pyimpl.rs index fd0d1f87..46fe05b9 100644 --- a/pyo3-macros-backend/src/pyimpl.rs +++ b/pyo3-macros-backend/src/pyimpl.rs @@ -3,14 +3,20 @@ use std::collections::HashSet; use crate::{ + attributes::{take_pyo3_options, CrateAttribute}, konst::{ConstAttributes, ConstSpec}, pyfunction::PyFunctionOptions, pymethod::{self, is_proto_method}, + utils::get_pyo3_crate, }; use proc_macro2::TokenStream; use pymethod::GeneratedPyMethod; use quote::quote; -use syn::spanned::Spanned; +use syn::{ + parse::{Parse, ParseStream}, + spanned::Spanned, + Result, +}; /// The mechanism used to collect `#[pymethods]` into the type object #[derive(Copy, Clone)] @@ -19,6 +25,50 @@ pub enum PyClassMethodsType { Inventory, } +enum PyImplPyO3Option { + Crate(CrateAttribute), +} + +impl Parse for PyImplPyO3Option { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![crate]) { + input.parse().map(PyImplPyO3Option::Crate) + } else { + Err(lookahead.error()) + } + } +} + +#[derive(Default)] +pub struct PyImplOptions { + krate: Option, +} + +impl PyImplOptions { + pub fn from_attrs(attrs: &mut Vec) -> Result { + let mut options: PyImplOptions = Default::default(); + + for option in take_pyo3_options(attrs)? { + match option { + PyImplPyO3Option::Crate(path) => options.set_crate(path)?, + } + } + + Ok(options) + } + + fn set_crate(&mut self, path: CrateAttribute) -> Result<()> { + ensure_spanned!( + self.krate.is_none(), + path.0.span() => "`crate` may only be specified once" + ); + + self.krate = Some(path); + Ok(()) + } +} + pub fn build_py_methods( ast: &mut syn::ItemImpl, methods_type: PyClassMethodsType, @@ -31,7 +81,8 @@ pub fn build_py_methods( "#[pymethods] cannot be used with lifetime parameters or generics" ); } else { - impl_methods(&ast.self_ty, &mut ast.items, methods_type) + let options = PyImplOptions::from_attrs(&mut ast.attrs)?; + impl_methods(&ast.self_ty, &mut ast.items, methods_type, options) } } @@ -39,6 +90,7 @@ pub fn impl_methods( ty: &syn::Type, impls: &mut Vec, methods_type: PyClassMethodsType, + options: PyImplOptions, ) -> syn::Result { let mut trait_impls = Vec::new(); let mut proto_impls = Vec::new(); @@ -49,8 +101,9 @@ pub fn impl_methods( for iimpl in impls.iter_mut() { match iimpl { syn::ImplItem::Method(meth) => { - let options = PyFunctionOptions::from_attrs(&mut meth.attrs)?; - match pymethod::gen_py_method(ty, &mut meth.sig, &mut meth.attrs, options)? { + 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)? { GeneratedPyMethod::Method(token_stream) => { let attrs = get_cfg_attributes(&meth.attrs); methods.push(quote!(#(#attrs)* #token_stream)); @@ -95,25 +148,35 @@ pub fn impl_methods( add_shared_proto_slots(ty, &mut proto_impls, implemented_proto_fragments); + let krate = get_pyo3_crate(&options.krate); + Ok(match methods_type { PyClassMethodsType::Specialization => { let methods_registration = impl_py_methods(ty, methods); let protos_registration = impl_protos(ty, proto_impls); quote! { - #(#trait_impls)* + const _: () = { + use #krate as _pyo3; - #protos_registration + #(#trait_impls)* - #methods_registration + #protos_registration + + #methods_registration + }; } } PyClassMethodsType::Inventory => { let inventory = submit_methods_inventory(ty, methods, proto_impls); quote! { - #(#trait_impls)* + const _: () = { + use #krate as _pyo3; - #inventory + #(#trait_impls)* + + #inventory + }; } } }) @@ -124,13 +187,13 @@ pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec) -> TokenStream { let deprecations = &spec.attributes.deprecations; let python_name = &spec.null_terminated_python_name(); quote! { - ::pyo3::class::PyMethodDefType::ClassAttribute({ - ::pyo3::class::PyClassAttributeDef::new( + _pyo3::class::PyMethodDefType::ClassAttribute({ + _pyo3::class::PyClassAttributeDef::new( #python_name, - ::pyo3::class::methods::PyClassAttributeFactory({ - fn __wrap(py: ::pyo3::Python<'_>) -> ::pyo3::PyObject { + _pyo3::class::methods::PyClassAttributeFactory({ + fn __wrap(py: _pyo3::Python<'_>) -> _pyo3::PyObject { #deprecations - ::pyo3::IntoPy::into_py(#cls::#member, py) + _pyo3::IntoPy::into_py(#cls::#member, py) } __wrap }) @@ -182,11 +245,11 @@ pub fn gen_default_slot_impls(cls: &syn::Ident, method_defs: Vec) - fn impl_py_methods(ty: &syn::Type, methods: Vec) -> TokenStream { quote! { - impl ::pyo3::class::impl_::PyMethods<#ty> - for ::pyo3::class::impl_::PyClassImplCollector<#ty> + impl _pyo3::class::impl_::PyMethods<#ty> + for _pyo3::class::impl_::PyClassImplCollector<#ty> { - fn py_methods(self) -> &'static [::pyo3::class::methods::PyMethodDefType] { - static METHODS: &[::pyo3::class::methods::PyMethodDefType] = &[#(#methods),*]; + fn py_methods(self) -> &'static [_pyo3::class::methods::PyMethodDefType] { + static METHODS: &[_pyo3::class::methods::PyMethodDefType] = &[#(#methods),*]; METHODS } } @@ -203,7 +266,7 @@ fn add_shared_proto_slots( let first_implemented = implemented_proto_fragments.remove($first); let second_implemented = implemented_proto_fragments.remove($second); if first_implemented || second_implemented { - proto_impls.push(quote! { ::pyo3::$slot!(#ty) }) + proto_impls.push(quote! { _pyo3::class::impl_::$slot!(#ty) }) } }}; } @@ -235,10 +298,10 @@ fn add_shared_proto_slots( fn impl_protos(ty: &syn::Type, proto_impls: Vec) -> TokenStream { quote! { - impl ::pyo3::class::impl_::PyMethodsProtocolSlots<#ty> - for ::pyo3::class::impl_::PyClassImplCollector<#ty> + impl _pyo3::class::impl_::PyMethodsProtocolSlots<#ty> + for _pyo3::class::impl_::PyClassImplCollector<#ty> { - fn methods_protocol_slots(self) -> &'static [::pyo3::ffi::PyType_Slot] { + fn methods_protocol_slots(self) -> &'static [_pyo3::ffi::PyType_Slot] { &[#(#proto_impls),*] } } @@ -251,8 +314,8 @@ fn submit_methods_inventory( proto_impls: Vec, ) -> TokenStream { quote! { - ::pyo3::inventory::submit! { - type Inventory = <#ty as ::pyo3::class::impl_::PyClassImpl>::Inventory; + _pyo3::inventory::submit! { + type Inventory = <#ty as _pyo3::class::impl_::PyClassImpl>::Inventory; Inventory::new(&[#(#methods),*], &[#(#proto_impls),*]) } } diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index 1b5951a4..c8b4d607 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -120,12 +120,12 @@ pub fn gen_py_method( (_, FnType::FnClass) => GeneratedPyMethod::Method(impl_py_method_def( cls, spec, - Some(quote!(::pyo3::ffi::METH_CLASS)), + Some(quote!(_pyo3::ffi::METH_CLASS)), )?), (_, FnType::FnStatic) => GeneratedPyMethod::Method(impl_py_method_def( cls, spec, - Some(quote!(::pyo3::ffi::METH_STATIC)), + Some(quote!(_pyo3::ffi::METH_STATIC)), )?), // special prototypes (_, FnType::FnNew) => GeneratedPyMethod::TraitImpl(impl_py_method_def_new(cls, spec)?), @@ -186,7 +186,7 @@ pub fn impl_py_method_def( }; let methoddef = spec.get_methoddef(quote! {{ #wrapper_def #wrapper_ident }}); Ok(quote! { - ::pyo3::class::PyMethodDefType::#methoddef_type(#methoddef #add_flags) + _pyo3::class::PyMethodDefType::#methoddef_type(#methoddef #add_flags) }) } @@ -194,8 +194,8 @@ fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec) -> Result let wrapper_ident = syn::Ident::new("__wrap", Span::call_site()); let wrapper = spec.get_wrapper_function(&wrapper_ident, Some(cls))?; Ok(quote! { - impl ::pyo3::class::impl_::PyClassNewImpl<#cls> for ::pyo3::class::impl_::PyClassImplCollector<#cls> { - fn new_impl(self) -> ::std::option::Option<::pyo3::ffi::newfunc> { + impl _pyo3::class::impl_::PyClassNewImpl<#cls> for _pyo3::class::impl_::PyClassImplCollector<#cls> { + fn new_impl(self) -> ::std::option::Option<_pyo3::ffi::newfunc> { ::std::option::Option::Some({ #wrapper #wrapper_ident @@ -214,9 +214,9 @@ fn impl_call_slot(cls: &syn::Type, mut spec: FnSpec) -> Result { let wrapper = spec.get_wrapper_function(&wrapper_ident, Some(cls))?; Ok(quote! {{ #wrapper - ::pyo3::ffi::PyType_Slot { - slot: ::pyo3::ffi::Py_tp_call, - pfunc: __wrap as ::pyo3::ffi::ternaryfunc as _ + _pyo3::ffi::PyType_Slot { + slot: _pyo3::ffi::Py_tp_call, + pfunc: __wrap as _pyo3::ffi::ternaryfunc as _ } }}) } @@ -226,13 +226,13 @@ fn impl_py_class_attribute(cls: &syn::Type, spec: &FnSpec) -> TokenStream { let deprecations = &spec.deprecations; let python_name = spec.null_terminated_python_name(); quote! { - ::pyo3::class::PyMethodDefType::ClassAttribute({ - ::pyo3::class::PyClassAttributeDef::new( + _pyo3::class::PyMethodDefType::ClassAttribute({ + _pyo3::class::PyClassAttributeDef::new( #python_name, - ::pyo3::class::methods::PyClassAttributeFactory({ - fn __wrap(py: ::pyo3::Python<'_>) -> ::pyo3::PyObject { + _pyo3::class::methods::PyClassAttributeFactory({ + fn __wrap(py: _pyo3::Python<'_>) -> _pyo3::PyObject { #deprecations - ::pyo3::IntoPy::into_py(#cls::#name(), py) + _pyo3::IntoPy::into_py(#cls::#name(), py) } __wrap }) @@ -295,26 +295,26 @@ pub fn impl_py_setter_def(cls: &syn::Type, property_type: PropertyType) -> Resul } }; Ok(quote! { - ::pyo3::class::PyMethodDefType::Setter({ + _pyo3::class::PyMethodDefType::Setter({ #deprecations - ::pyo3::class::PySetterDef::new( + _pyo3::class::PySetterDef::new( #python_name, - ::pyo3::class::methods::PySetter({ + _pyo3::class::methods::PySetter({ unsafe extern "C" fn __wrap( - _slf: *mut ::pyo3::ffi::PyObject, - _value: *mut ::pyo3::ffi::PyObject, + _slf: *mut _pyo3::ffi::PyObject, + _value: *mut _pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void ) -> ::std::os::raw::c_int { - ::pyo3::callback::handle_panic(|_py| { + _pyo3::callback::handle_panic(|_py| { #slf let _value = _py .from_borrowed_ptr_or_opt(_value) .ok_or_else(|| { - ::pyo3::exceptions::PyAttributeError::new_err("can't delete attribute") + _pyo3::exceptions::PyAttributeError::new_err("can't delete attribute") })?; - let _val = ::pyo3::FromPyObject::extract(_value)?; + let _val = _pyo3::FromPyObject::extract(_value)?; - ::pyo3::callback::convert(_py, #setter_impl) + _pyo3::callback::convert(_py, #setter_impl) }) } __wrap @@ -375,18 +375,18 @@ pub fn impl_py_getter_def(cls: &syn::Type, property_type: PropertyType) -> Resul } }; Ok(quote! { - ::pyo3::class::PyMethodDefType::Getter({ + _pyo3::class::PyMethodDefType::Getter({ #deprecations - ::pyo3::class::PyGetterDef::new( + _pyo3::class::PyGetterDef::new( #python_name, - ::pyo3::class::methods::PyGetter({ + _pyo3::class::methods::PyGetter({ unsafe extern "C" fn __wrap( - _slf: *mut ::pyo3::ffi::PyObject, + _slf: *mut _pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void - ) -> *mut ::pyo3::ffi::PyObject { - ::pyo3::callback::handle_panic(|_py| { + ) -> *mut _pyo3::ffi::PyObject { + _pyo3::callback::handle_panic(|_py| { #slf - ::pyo3::callback::convert(_py, #getter_impl) + _pyo3::callback::convert(_py, #getter_impl) }) } __wrap @@ -459,10 +459,10 @@ const __GETATTR__: SlotDef = SlotDef::new("Py_tp_getattro", "getattrofunc") quote! { // Behave like python's __getattr__ (as opposed to __getattribute__) and check // for existing fields and methods first - let existing = ::pyo3::ffi::PyObject_GenericGetAttr(_slf, arg0); + let existing = _pyo3::ffi::PyObject_GenericGetAttr(_slf, arg0); if existing.is_null() { // PyObject_HasAttr also tries to get an object and clears the error if it fails - ::pyo3::ffi::PyErr_Clear(); + _pyo3::ffi::PyErr_Clear(); } else { return existing; } @@ -473,7 +473,7 @@ 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 }, + || quote! { _pyo3::callback::HashCallbackOutput }, )); const __RICHCMP__: SlotDef = SlotDef::new("Py_tp_richcompare", "richcmpfunc") .extract_error_mode(ExtractErrorMode::NotImplemented) @@ -482,12 +482,12 @@ const __GET__: SlotDef = SlotDef::new("Py_tp_descr_get", "descrgetfunc") .arguments(&[Ty::MaybeNullObject, Ty::MaybeNullObject]); const __ITER__: SlotDef = SlotDef::new("Py_tp_iter", "getiterfunc"); const __NEXT__: SlotDef = SlotDef::new("Py_tp_iternext", "iternextfunc").return_conversion( - TokenGenerator(|| quote! { ::pyo3::class::iter::IterNextOutput::<_, _> }), + TokenGenerator(|| quote! { _pyo3::class::iter::IterNextOutput::<_, _> }), ); 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_conversion( - TokenGenerator(|| quote! { ::pyo3::class::pyasync::IterANextOutput::<_, _> }), + TokenGenerator(|| quote! { _pyo3::class::pyasync::IterANextOutput::<_, _> }), ); const __LEN__: SlotDef = SlotDef::new("Py_mp_length", "lenfunc").ret_ty(Ty::PySsizeT); const __CONTAINS__: SlotDef = SlotDef::new("Py_sq_contains", "objobjproc") @@ -613,11 +613,11 @@ enum Ty { impl Ty { fn ffi_type(self) -> TokenStream { match self { - Ty::Object | Ty::MaybeNullObject => quote! { *mut ::pyo3::ffi::PyObject }, - Ty::NonNullObject => quote! { ::std::ptr::NonNull<::pyo3::ffi::PyObject> }, + Ty::Object | Ty::MaybeNullObject => quote! { *mut _pyo3::ffi::PyObject }, + Ty::NonNullObject => quote! { ::std::ptr::NonNull<_pyo3::ffi::PyObject> }, 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::ffi::Py_hash_t }, + Ty::PySsizeT => quote! { _pyo3::ffi::Py_ssize_t }, Ty::Void => quote! { () }, } } @@ -636,7 +636,7 @@ impl Ty { extract_error_mode, py, quote! { - #py.from_borrowed_ptr::<::pyo3::PyAny>(#ident).extract() + #py.from_borrowed_ptr::<_pyo3::PyAny>(#ident).extract() }, ); extract_object(cls, arg.ty, ident, extract) @@ -646,9 +646,9 @@ impl Ty { extract_error_mode, py, quote! { - #py.from_borrowed_ptr::<::pyo3::PyAny>( + #py.from_borrowed_ptr::<_pyo3::PyAny>( if #ident.is_null() { - ::pyo3::ffi::Py_None() + _pyo3::ffi::Py_None() } else { #ident } @@ -662,7 +662,7 @@ impl Ty { extract_error_mode, py, quote! { - #py.from_borrowed_ptr::<::pyo3::PyAny>(#ident.as_ptr()).extract() + #py.from_borrowed_ptr::<_pyo3::PyAny>(#ident.as_ptr()).extract() }, ); extract_object(cls, arg.ty, ident, extract) @@ -672,8 +672,8 @@ impl Ty { extract_error_mode, py, quote! { - ::pyo3::class::basic::CompareOp::from_raw(#ident) - .ok_or_else(|| ::pyo3::exceptions::PyValueError::new_err("invalid comparison operator")) + _pyo3::class::basic::CompareOp::from_raw(#ident) + .ok_or_else(|| _pyo3::exceptions::PyValueError::new_err("invalid comparison operator")) }, ); quote! { @@ -695,7 +695,7 @@ fn handle_error( 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::callback::convert(#py, #py.NotImplemented()); }, } }, } @@ -712,7 +712,7 @@ fn extract_object( replace_self(&mut tref.elem, cls); let mut_ = tref.mutability; quote! { - let #mut_ #ident: <#tref as ::pyo3::derive_utils::ExtractExt<'_>>::Target = #extract; + let #mut_ #ident: <#tref as _pyo3::derive_utils::ExtractExt<'_>>::Target = #extract; let #ident = &#mut_ *#ident; } } else { @@ -731,13 +731,13 @@ impl ReturnMode { fn return_call_output(&self, py: &syn::Ident, call: TokenStream) -> TokenStream { match self { ReturnMode::Conversion(conversion) => quote! { - let _result: ::pyo3::PyResult<#conversion> = #call; - ::pyo3::callback::convert(#py, _result) + let _result: _pyo3::PyResult<#conversion> = #call; + _pyo3::callback::convert(#py, _result) }, ReturnMode::ReturnSelf => quote! { - let _result: ::pyo3::PyResult<()> = #call; + let _result: _pyo3::PyResult<()> = #call; _result?; - ::pyo3::ffi::Py_XINCREF(_raw_slf); + _pyo3::ffi::Py_XINCREF(_raw_slf); ::std::result::Result::Ok(_raw_slf) }, } @@ -821,16 +821,16 @@ impl SlotDef { return_mode.as_ref(), )?; Ok(quote!({ - unsafe extern "C" fn __wrap(_raw_slf: *mut ::pyo3::ffi::PyObject, #(#method_arguments),*) -> #ret_ty { + unsafe extern "C" fn __wrap(_raw_slf: *mut _pyo3::ffi::PyObject, #(#method_arguments),*) -> #ret_ty { let _slf = _raw_slf; #before_call_method - ::pyo3::callback::handle_panic(|#py| { + _pyo3::callback::handle_panic(|#py| { #body }) } - ::pyo3::ffi::PyType_Slot { - slot: ::pyo3::ffi::#slot, - pfunc: __wrap as ::pyo3::ffi::#func_ty as _ + _pyo3::ffi::PyType_Slot { + slot: _pyo3::ffi::#slot, + pfunc: __wrap as _pyo3::ffi::#func_ty as _ } })) } @@ -858,7 +858,7 @@ fn generate_method_body( let rust_name = spec.name; let (arg_idents, conversions) = extract_proto_arguments(cls, py, &spec.args, arguments, extract_error_mode)?; - let call = quote! { ::pyo3::callback::convert(#py, #cls::#rust_name(_slf, #(#arg_idents),*)) }; + let call = quote! { _pyo3::callback::convert(#py, #cls::#rust_name(_slf, #(#arg_idents),*)) }; let body = if let Some(return_mode) = return_mode { return_mode.return_call_output(py, call) } else { @@ -912,15 +912,15 @@ impl SlotFragmentDef { let body = generate_method_body(cls, spec, &py, arguments, *extract_error_mode, None)?; let ret_ty = ret_ty.ffi_type(); Ok(quote! { - impl ::pyo3::class::impl_::#fragment_trait<#cls> for ::pyo3::class::impl_::PyClassImplCollector<#cls> { + impl _pyo3::class::impl_::#fragment_trait<#cls> for _pyo3::class::impl_::PyClassImplCollector<#cls> { #[inline] unsafe fn #method( self, - #py: ::pyo3::Python, - _raw_slf: *mut ::pyo3::ffi::PyObject, + #py: _pyo3::Python, + _raw_slf: *mut _pyo3::ffi::PyObject, #(#method_arguments),* - ) -> ::pyo3::PyResult<#ret_ty> { + ) -> _pyo3::PyResult<#ret_ty> { let _slf = _raw_slf; #body } diff --git a/pyo3-macros-backend/src/pyproto.rs b/pyo3-macros-backend/src/pyproto.rs index 779cce25..08b8527c 100644 --- a/pyo3-macros-backend/src/pyproto.rs +++ b/pyo3-macros-backend/src/pyproto.rs @@ -67,7 +67,7 @@ fn impl_proto_impl( let flags = if m.can_coexist { // We need METH_COEXIST here to prevent __add__ from overriding __radd__ - Some(quote!(::pyo3::ffi::METH_COEXIST)) + Some(quote!(_pyo3::ffi::METH_COEXIST)) } else { None }; @@ -86,10 +86,14 @@ fn impl_proto_impl( } let normal_methods = impl_normal_methods(py_methods, ty, proto); let protocol_methods = impl_proto_methods(method_names, ty, proto); + Ok(quote! { - #trait_impls - #normal_methods - #protocol_methods + const _: () = { + use ::pyo3 as _pyo3; // pyproto doesn't support specifying #[pyo3(crate)] + #trait_impls + #normal_methods + #protocol_methods + }; }) } @@ -105,11 +109,11 @@ fn impl_normal_methods( let methods_trait = proto.methods_trait(); let methods_trait_methods = proto.methods_trait_methods(); quote! { - impl ::pyo3::class::impl_::#methods_trait<#ty> - for ::pyo3::class::impl_::PyClassImplCollector<#ty> + impl _pyo3::class::impl_::#methods_trait<#ty> + for _pyo3::class::impl_::PyClassImplCollector<#ty> { - fn #methods_trait_methods(self) -> &'static [::pyo3::class::methods::PyMethodDefType] { - static METHODS: &[::pyo3::class::methods::PyMethodDefType] = + fn #methods_trait_methods(self) -> &'static [_pyo3::class::methods::PyMethodDefType] { + static METHODS: &[_pyo3::class::methods::PyMethodDefType] = &[#(#py_methods),*]; METHODS } @@ -138,16 +142,16 @@ fn impl_proto_methods( if build_config.version <= PY39 && proto.name == "Buffer" { maybe_buffer_methods = Some(quote! { - impl ::pyo3::class::impl_::PyBufferProtocolProcs<#ty> - for ::pyo3::class::impl_::PyClassImplCollector<#ty> + impl _pyo3::class::impl_::PyBufferProtocolProcs<#ty> + for _pyo3::class::impl_::PyClassImplCollector<#ty> { fn buffer_procs( self - ) -> ::std::option::Option<&'static ::pyo3::class::impl_::PyBufferProcs> { - static PROCS: ::pyo3::class::impl_::PyBufferProcs - = ::pyo3::class::impl_::PyBufferProcs { - bf_getbuffer: ::std::option::Option::Some(::pyo3::class::buffer::getbuffer::<#ty>), - bf_releasebuffer: ::std::option::Option::Some(::pyo3::class::buffer::releasebuffer::<#ty>), + ) -> ::std::option::Option<&'static _pyo3::class::impl_::PyBufferProcs> { + static PROCS: _pyo3::class::impl_::PyBufferProcs + = _pyo3::class::impl_::PyBufferProcs { + bf_getbuffer: ::std::option::Option::Some(_pyo3::class::buffer::getbuffer::<#ty>), + bf_releasebuffer: ::std::option::Option::Some(_pyo3::class::buffer::releasebuffer::<#ty>), }; ::std::option::Option::Some(&PROCS) } @@ -161,8 +165,8 @@ fn impl_proto_methods( let slot = syn::Ident::new(def.slot, Span::call_site()); let slot_impl = syn::Ident::new(def.slot_impl, Span::call_site()); quote! {{ - ::pyo3::ffi::PyType_Slot { - slot: ::pyo3::ffi::#slot, + _pyo3::ffi::PyType_Slot { + slot: _pyo3::ffi::#slot, pfunc: #module::#slot_impl::<#ty> as _ } }} @@ -176,10 +180,10 @@ fn impl_proto_methods( quote! { #maybe_buffer_methods - impl ::pyo3::class::impl_::#slots_trait<#ty> - for ::pyo3::class::impl_::PyClassImplCollector<#ty> + impl _pyo3::class::impl_::#slots_trait<#ty> + for _pyo3::class::impl_::PyClassImplCollector<#ty> { - fn #slots_trait_slots(self) -> &'static [::pyo3::ffi::PyType_Slot] { + fn #slots_trait_slots(self) -> &'static [_pyo3::ffi::PyType_Slot] { &[#(#tokens),*] } } diff --git a/pyo3-macros-backend/src/utils.rs b/pyo3-macros-backend/src/utils.rs index b25eb9a2..9c75239e 100644 --- a/pyo3-macros-backend/src/utils.rs +++ b/pyo3-macros-backend/src/utils.rs @@ -3,7 +3,7 @@ use proc_macro2::{Span, TokenStream}; use quote::ToTokens; use syn::spanned::Spanned; -use crate::attributes::TextSignatureAttribute; +use crate::attributes::{CrateAttribute, TextSignatureAttribute}; /// Macro inspired by `anyhow::anyhow!` to create a compiler error with the given span. macro_rules! err_spanned { @@ -189,3 +189,10 @@ pub(crate) fn replace_self(ty: &mut syn::Type, cls: &syn::Type) { _ => {} } } + +/// Extract the path to the pyo3 crate, or use the default (`::pyo3`). +pub(crate) fn get_pyo3_crate(attr: &Option) -> syn::Path { + attr.as_ref() + .map(|p| p.0.clone()) + .unwrap_or_else(|| syn::parse_str("::pyo3").unwrap()) +} diff --git a/pyo3-macros/src/lib.rs b/pyo3-macros/src/lib.rs index f08317d1..a6f6725c 100644 --- a/pyo3-macros/src/lib.rs +++ b/pyo3-macros/src/lib.rs @@ -216,16 +216,16 @@ fn pyclass_impl( } fn pyclass_enum_impl( - attr: TokenStream, - enum_: syn::ItemEnum, + attrs: TokenStream, + mut ast: syn::ItemEnum, methods_type: PyClassMethodsType, ) -> TokenStream { - let args = parse_macro_input!(attr with PyClassArgs::parse_enum_args); + let args = parse_macro_input!(attrs with PyClassArgs::parse_enum_args); let expanded = - build_py_enum(&enum_, args, methods_type).unwrap_or_else(|e| e.into_compile_error()); + build_py_enum(&mut ast, &args, methods_type).unwrap_or_else(|e| e.into_compile_error()); quote!( - #enum_ + #ast #expanded ) .into() diff --git a/src/class/impl_.rs b/src/class/impl_.rs index 0ab37e2a..7f1488a6 100644 --- a/src/class/impl_.rs +++ b/src/class/impl_.rs @@ -183,6 +183,7 @@ macro_rules! define_pyclass_setattr_slot { } }}; } + pub use $generate_macro; }; } @@ -292,6 +293,7 @@ macro_rules! define_pyclass_binary_operator_slot { } }}; } + pub use $generate_macro; }; } @@ -484,6 +486,7 @@ macro_rules! generate_pyclass_pow_slot { } }}; } +pub use generate_pyclass_pow_slot; pub trait PyClassAllocImpl { fn alloc_impl(self) -> Option; diff --git a/src/lib.rs b/src/lib.rs index 7d7d44b7..e8ebffcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -360,6 +360,11 @@ pub use pyo3_macros::{pyclass, pyfunction, pymethods, pymodule, pyproto, FromPyO #[macro_use] mod macros; +/// Test macro hygiene - this is in the crate since we won't have +/// `pyo3` available in the crate root. +#[cfg(all(test, feature = "macros"))] +mod test_hygiene; + /// Test readme and user guide #[cfg(doctest)] pub mod doc_test { diff --git a/src/test_hygiene/misc.rs b/src/test_hygiene/misc.rs new file mode 100644 index 00000000..eb49718e --- /dev/null +++ b/src/test_hygiene/misc.rs @@ -0,0 +1,29 @@ +#![no_implicit_prelude] + +#[derive(crate::FromPyObject)] +#[pyo3(crate = "crate")] +struct Derive1(i32); // newtype case + +#[derive(crate::FromPyObject)] +#[pyo3(crate = "crate")] +#[allow(dead_code)] +struct Derive2(i32, i32); // tuple case + +#[derive(crate::FromPyObject)] +#[pyo3(crate = "crate")] +#[allow(dead_code)] +struct Derive3 { + f: i32, + g: i32, +} // struct case + +#[derive(crate::FromPyObject)] +#[pyo3(crate = "crate")] +#[allow(dead_code)] +enum Derive4 { + A(i32), + B { f: i32 }, +} // enum case + +crate::create_exception!(mymodule, CustomError, crate::exceptions::PyException); +crate::import_exception!(socket, gaierror); diff --git a/src/test_hygiene/mod.rs b/src/test_hygiene/mod.rs new file mode 100644 index 00000000..d907d2e8 --- /dev/null +++ b/src/test_hygiene/mod.rs @@ -0,0 +1,5 @@ +mod misc; +mod pyclass; +mod pyfunction; +mod pymethods; +mod pymodule; diff --git a/tests/hygiene/pyclass.rs b/src/test_hygiene/pyclass.rs similarity index 61% rename from tests/hygiene/pyclass.rs rename to src/test_hygiene/pyclass.rs index 985d05dc..adadc08c 100644 --- a/tests/hygiene/pyclass.rs +++ b/src/test_hygiene/pyclass.rs @@ -1,27 +1,30 @@ #![no_implicit_prelude] #![allow(unused_variables)] -#[::pyo3::pyclass] +#[crate::pyclass] +#[pyo3(crate = "crate")] #[derive(::std::clone::Clone)] pub struct Foo; -#[::pyo3::pyclass] +#[crate::pyclass] +#[pyo3(crate = "crate")] pub struct Foo2; -#[::pyo3::pyclass( +#[crate::pyclass( name = "ActuallyBar", freelist = 8, weakref, unsendable, subclass, - extends = ::pyo3::types::PyAny, + extends = crate::types::PyAny, module = "Spam" )] +#[pyo3(crate = "crate")] pub struct Bar { #[pyo3(get, set)] a: u8, #[pyo3(get, set)] b: Foo, #[pyo3(get, set)] - c: ::std::option::Option<::pyo3::Py>, + c: ::std::option::Option>, } diff --git a/src/test_hygiene/pyfunction.rs b/src/test_hygiene/pyfunction.rs new file mode 100644 index 00000000..9a7d515e --- /dev/null +++ b/src/test_hygiene/pyfunction.rs @@ -0,0 +1,16 @@ +#![no_implicit_prelude] +#![allow(unused_variables)] + +#[crate::pyfunction] +#[pyo3(crate = "crate")] +fn do_something(x: i32) -> crate::PyResult { + ::std::result::Result::Ok(x) +} + +#[test] +fn invoke_wrap_pyfunction() { + crate::Python::with_gil(|py| { + let func = crate::wrap_pyfunction!(do_something)(py).unwrap(); + crate::py_run!(py, func, r#"func(5)"#); + }); +} diff --git a/tests/hygiene/pymethods.rs b/src/test_hygiene/pymethods.rs similarity index 67% rename from tests/hygiene/pymethods.rs rename to src/test_hygiene/pymethods.rs index 37a916f9..832e7239 100644 --- a/tests/hygiene/pymethods.rs +++ b/src/test_hygiene/pymethods.rs @@ -1,13 +1,16 @@ #![no_implicit_prelude] #![allow(unused_variables)] -#[::pyo3::pyclass] +#[crate::pyclass] +#[pyo3(crate = "crate")] pub struct Dummy; -#[::pyo3::pyclass] +#[crate::pyclass] +#[pyo3(crate = "crate")] pub struct DummyIter; -#[::pyo3::pymethods] +#[crate::pymethods] +#[pyo3(crate = "crate")] impl Dummy { ////////////////////// // Basic customization @@ -20,8 +23,8 @@ impl Dummy { "Dummy" } - fn __bytes__<'py>(&self, py: ::pyo3::Python<'py>) -> &'py ::pyo3::types::PyBytes { - ::pyo3::types::PyBytes::new(py, &[0]) + fn __bytes__<'py>(&self, py: crate::Python<'py>) -> &'py crate::types::PyBytes { + crate::types::PyBytes::new(py, &[0]) } fn __format__(&self, format_spec: ::std::string::String) -> ::std::string::String { @@ -60,11 +63,11 @@ impl Dummy { // Customizing attribute access ////////////////////// - fn __getattr__(&self, name: ::std::string::String) -> &::pyo3::PyAny { + fn __getattr__(&self, name: ::std::string::String) -> &crate::PyAny { ::std::panic!("unimplemented isn't hygienic before 1.50") } - fn __getattribute__(&self, name: ::std::string::String) -> &::pyo3::PyAny { + fn __getattribute__(&self, name: ::std::string::String) -> &crate::PyAny { ::std::panic!("unimplemented isn't hygienic before 1.50") } @@ -72,8 +75,8 @@ impl Dummy { fn __delattr__(&mut self, name: ::std::string::String) {} - fn __dir__<'py>(&self, py: ::pyo3::Python<'py>) -> &'py ::pyo3::types::PyList { - ::pyo3::types::PyList::new(py, ::std::vec![0_u8]) + fn __dir__<'py>(&self, py: crate::Python<'py>) -> &'py crate::types::PyList { + crate::types::PyList::new(py, ::std::vec![0_u8]) } ////////////////////// @@ -82,17 +85,17 @@ impl Dummy { fn __get__( &self, - instance: &::pyo3::PyAny, - owner: &::pyo3::PyAny, - ) -> ::pyo3::PyResult<&::pyo3::PyAny> { + instance: &crate::PyAny, + owner: &crate::PyAny, + ) -> crate::PyResult<&crate::PyAny> { ::std::panic!("unimplemented isn't hygienic before 1.50") } - fn __set__(&self, instance: &::pyo3::PyAny, owner: &::pyo3::PyAny) {} + fn __set__(&self, instance: &crate::PyAny, owner: &crate::PyAny) {} - fn __delete__(&self, instance: &::pyo3::PyAny) {} + fn __delete__(&self, instance: &crate::PyAny) {} - fn __set_name__(&self, owner: &::pyo3::PyAny, name: &::pyo3::PyAny) {} + fn __set_name__(&self, owner: &crate::PyAny, name: &crate::PyAny) {} ////////////////////// // Implementing Descriptors @@ -102,24 +105,24 @@ impl Dummy { 0 } - fn __getitem__(&self, key: u32) -> ::pyo3::PyResult { - ::std::result::Result::Err(::pyo3::exceptions::PyKeyError::new_err("boo")) + fn __getitem__(&self, key: u32) -> crate::PyResult { + ::std::result::Result::Err(crate::exceptions::PyKeyError::new_err("boo")) } fn __setitem__(&self, key: u32, value: u32) {} fn __delitem__(&self, key: u32) {} - fn __iter__(_: ::pyo3::pycell::PyRef, py: ::pyo3::Python) -> ::pyo3::Py { - ::pyo3::Py::new(py, DummyIter {}).unwrap() + fn __iter__(_: crate::pycell::PyRef, py: crate::Python) -> crate::Py { + crate::Py::new(py, DummyIter {}).unwrap() } fn __next__(&mut self) -> ::std::option::Option<()> { ::std::option::Option::None } - fn __reversed__(slf: ::pyo3::pycell::PyRef, py: ::pyo3::Python) -> ::pyo3::Py { - ::pyo3::Py::new(py, DummyIter {}).unwrap() + fn __reversed__(slf: crate::pycell::PyRef, py: crate::Python) -> crate::Py { + crate::Py::new(py, DummyIter {}).unwrap() } fn __contains__(&self, item: u32) -> bool { @@ -142,12 +145,12 @@ impl Dummy { Dummy {} } - fn __truediv__(&self, _other: &Self) -> ::pyo3::PyResult<()> { - ::std::result::Result::Err(::pyo3::exceptions::PyZeroDivisionError::new_err("boo")) + fn __truediv__(&self, _other: &Self) -> crate::PyResult<()> { + ::std::result::Result::Err(crate::exceptions::PyZeroDivisionError::new_err("boo")) } - fn __floordiv__(&self, _other: &Self) -> ::pyo3::PyResult<()> { - ::std::result::Result::Err(::pyo3::exceptions::PyZeroDivisionError::new_err("boo")) + fn __floordiv__(&self, _other: &Self) -> crate::PyResult<()> { + ::std::result::Result::Err(crate::exceptions::PyZeroDivisionError::new_err("boo")) } fn __mod__(&self, _other: &Self) -> u32 { @@ -194,12 +197,12 @@ impl Dummy { Dummy {} } - fn __rtruediv__(&self, _other: &Self) -> ::pyo3::PyResult<()> { - ::std::result::Result::Err(::pyo3::exceptions::PyZeroDivisionError::new_err("boo")) + fn __rtruediv__(&self, _other: &Self) -> crate::PyResult<()> { + ::std::result::Result::Err(crate::exceptions::PyZeroDivisionError::new_err("boo")) } - fn __rfloordiv__(&self, _other: &Self) -> ::pyo3::PyResult<()> { - ::std::result::Result::Err(::pyo3::exceptions::PyZeroDivisionError::new_err("boo")) + fn __rfloordiv__(&self, _other: &Self) -> crate::PyResult<()> { + ::std::result::Result::Err(crate::exceptions::PyZeroDivisionError::new_err("boo")) } fn __rmod__(&self, _other: &Self) -> u32 { @@ -258,24 +261,24 @@ impl Dummy { fn __ior__(&mut self, other: &Self) {} - fn __neg__(slf: ::pyo3::pycell::PyRef) -> ::pyo3::pycell::PyRef { + fn __neg__(slf: crate::pycell::PyRef) -> crate::pycell::PyRef { slf } - fn __pos__(slf: ::pyo3::pycell::PyRef) -> ::pyo3::pycell::PyRef { + fn __pos__(slf: crate::pycell::PyRef) -> crate::pycell::PyRef { slf } - fn __abs__(slf: ::pyo3::pycell::PyRef) -> ::pyo3::pycell::PyRef { + fn __abs__(slf: crate::pycell::PyRef) -> crate::pycell::PyRef { slf } - fn __invert__(slf: ::pyo3::pycell::PyRef) -> ::pyo3::pycell::PyRef { + fn __invert__(slf: crate::pycell::PyRef) -> crate::pycell::PyRef { slf } - fn __complex__<'py>(&self, py: ::pyo3::Python<'py>) -> &'py ::pyo3::types::PyComplex { - ::pyo3::types::PyComplex::from_doubles(py, 0.0, 0.0) + fn __complex__<'py>(&self, py: crate::Python<'py>) -> &'py crate::types::PyComplex { + crate::types::PyComplex::from_doubles(py, 0.0, 0.0) } fn __int__(&self) -> u32 { @@ -314,9 +317,9 @@ impl Dummy { fn __exit__( &mut self, - exc_type: &::pyo3::PyAny, - exc_value: &::pyo3::PyAny, - traceback: &::pyo3::PyAny, + exc_type: &crate::PyAny, + exc_value: &crate::PyAny, + traceback: &crate::PyAny, ) { } @@ -324,7 +327,7 @@ impl Dummy { // Awaitable Objects ////////////////////// - fn __await__(slf: ::pyo3::pycell::PyRef) -> ::pyo3::pycell::PyRef { + fn __await__(slf: crate::pycell::PyRef) -> crate::pycell::PyRef { slf } @@ -333,8 +336,8 @@ impl Dummy { // Asynchronous Iterators ////////////////////// - fn __aiter__(slf: ::pyo3::pycell::PyRef, py: ::pyo3::Python) -> ::pyo3::Py { - ::pyo3::Py::new(py, DummyIter {}).unwrap() + fn __aiter__(slf: crate::pycell::PyRef, py: crate::Python) -> crate::Py { + crate::Py::new(py, DummyIter {}).unwrap() } fn __anext__(&mut self) -> ::std::option::Option<()> { @@ -349,9 +352,9 @@ impl Dummy { fn __aexit__( &mut self, - exc_type: &::pyo3::PyAny, - exc_value: &::pyo3::PyAny, - traceback: &::pyo3::PyAny, + exc_type: &crate::PyAny, + exc_value: &crate::PyAny, + traceback: &crate::PyAny, ) { } @@ -362,13 +365,13 @@ impl Dummy { #[staticmethod] fn staticmethod() {} #[classmethod] - fn clsmethod(_: &::pyo3::types::PyType) {} + fn clsmethod(_: &crate::types::PyType) {} #[args(args = "*", kwds = "**")] fn __call__( &self, - _args: &::pyo3::types::PyTuple, - _kwds: ::std::option::Option<&::pyo3::types::PyDict>, - ) -> ::pyo3::PyResult { + _args: &crate::types::PyTuple, + _kwds: ::std::option::Option<&crate::types::PyDict>, + ) -> crate::PyResult { ::std::panic!("unimplemented isn't hygienic before 1.50") } #[new] @@ -391,8 +394,8 @@ impl Dummy { fn __richcmp__( &self, other: &Self, - op: ::pyo3::class::basic::CompareOp, - ) -> ::pyo3::PyResult { + op: crate::class::basic::CompareOp, + ) -> crate::PyResult { ::std::result::Result::Ok(false) } // PyGcProtocol diff --git a/src/test_hygiene/pymodule.rs b/src/test_hygiene/pymodule.rs new file mode 100644 index 00000000..478cea3a --- /dev/null +++ b/src/test_hygiene/pymodule.rs @@ -0,0 +1,23 @@ +#![no_implicit_prelude] +#![allow(unused_variables)] + +#[crate::pyfunction] +#[pyo3(crate = "crate")] +fn do_something(x: i32) -> crate::PyResult { + ::std::result::Result::Ok(x) +} + +#[crate::pymodule] +#[pyo3(crate = "crate")] +fn foo(_py: crate::Python, _m: &crate::types::PyModule) -> crate::PyResult<()> { + ::std::result::Result::Ok(()) +} + +#[crate::pymodule] +#[pyo3(crate = "crate")] +fn my_module(_py: crate::Python, m: &crate::types::PyModule) -> crate::PyResult<()> { + m.add_function(crate::wrap_pyfunction!(do_something, m)?)?; + m.add_wrapped(crate::wrap_pymodule!(foo))?; + + ::std::result::Result::Ok(()) +} diff --git a/tests/hygiene/misc.rs b/tests/hygiene/misc.rs deleted file mode 100644 index abd0cc6a..00000000 --- a/tests/hygiene/misc.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![no_implicit_prelude] - -#[derive(::pyo3::FromPyObject)] -struct Derive1(i32); // newtype case - -#[derive(::pyo3::FromPyObject)] -#[allow(dead_code)] -struct Derive2(i32, i32); // tuple case - -#[derive(::pyo3::FromPyObject)] -#[allow(dead_code)] -struct Derive3 { - f: i32, - g: i32, -} // struct case - -#[derive(::pyo3::FromPyObject)] -#[allow(dead_code)] -enum Derive4 { - A(i32), - B { f: i32 }, -} // enum case - -::pyo3::create_exception!(mymodule, CustomError, ::pyo3::exceptions::PyException); -::pyo3::import_exception!(socket, gaierror); diff --git a/tests/hygiene/pyfunction.rs b/tests/hygiene/pyfunction.rs deleted file mode 100644 index ba1402cd..00000000 --- a/tests/hygiene/pyfunction.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![no_implicit_prelude] -#![allow(unused_variables)] - -#[::pyo3::pyfunction] -fn do_something(x: i32) -> ::pyo3::PyResult { - ::std::result::Result::Ok(x) -} - -#[test] -fn invoke_wrap_pyfunction() { - ::pyo3::Python::with_gil(|py| { - let func = ::pyo3::wrap_pyfunction!(do_something)(py).unwrap(); - ::pyo3::py_run!(py, func, r#"func(5)"#); - }); -} diff --git a/tests/hygiene/pymodule.rs b/tests/hygiene/pymodule.rs deleted file mode 100644 index 8bb5e19e..00000000 --- a/tests/hygiene/pymodule.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![no_implicit_prelude] -#![allow(unused_variables)] - -#[::pyo3::pyfunction] -fn do_something(x: i32) -> ::pyo3::PyResult { - ::std::result::Result::Ok(x) -} - -#[::pyo3::pymodule] -fn foo(_py: ::pyo3::Python, _m: &::pyo3::types::PyModule) -> ::pyo3::PyResult<()> { - ::std::result::Result::Ok(()) -} - -#[::pyo3::pymodule] -fn my_module(_py: ::pyo3::Python, m: &::pyo3::types::PyModule) -> ::pyo3::PyResult<()> { - m.add_function(::pyo3::wrap_pyfunction!(do_something, m)?)?; - m.add_wrapped(::pyo3::wrap_pymodule!(foo))?; - - ::std::result::Result::Ok(()) -} diff --git a/tests/hygiene/pyproto.rs b/tests/hygiene/pyproto.rs deleted file mode 100644 index 9f6b0af8..00000000 --- a/tests/hygiene/pyproto.rs +++ /dev/null @@ -1,58 +0,0 @@ -#![no_implicit_prelude] -#![allow(unused_variables)] - -#[::pyo3::pyclass] -#[derive(::std::clone::Clone)] -pub struct Foo; - -#[::pyo3::pyclass] -pub struct Foo2; - -#[::pyo3::pyclass( - name = "ActuallyBar", - freelist = 8, - weakref, - unsendable, - gc, - subclass, - extends = ::pyo3::types::PyAny, - module = "Spam" -)] -pub struct Bar { - #[pyo3(get, set)] - a: u8, - #[pyo3(get, set)] - b: Foo, - #[pyo3(get, set)] - c: ::std::option::Option<::pyo3::Py>, -} - -#[::pyo3::pyproto] -impl ::pyo3::class::gc::PyGCProtocol for Bar { - fn __traverse__( - &self, - visit: ::pyo3::class::gc::PyVisit, - ) -> ::std::result::Result<(), ::pyo3::class::gc::PyTraverseError> { - if let ::std::option::Option::Some(obj) = &self.c { - visit.call(obj)? - } - ::std::result::Result::Ok(()) - } - - fn __clear__(&mut self) { - self.c = ::std::option::Option::None; - } -} - -#[cfg(not(Py_LIMITED_API))] -#[::pyo3::pyproto] -impl ::pyo3::class::PyBufferProtocol for Bar { - fn bf_getbuffer( - _s: ::pyo3::PyRefMut, - _v: *mut ::pyo3::ffi::Py_buffer, - _f: ::std::os::raw::c_int, - ) -> ::pyo3::PyResult<()> { - ::std::panic!("unimplemented isn't hygienic before 1.50") - } - fn bf_releasebuffer(_s: ::pyo3::PyRefMut, _v: *mut ::pyo3::ffi::Py_buffer) {} -} diff --git a/tests/test_hygiene.rs b/tests/test_hygiene.rs deleted file mode 100644 index 09289457..00000000 --- a/tests/test_hygiene.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![cfg(feature = "macros")] - -mod hygiene { - mod misc; - mod pyclass; - mod pyfunction; - mod pymethods; - mod pymodule; - mod pyproto; -} diff --git a/tests/ui/invalid_frompy_derive.stderr b/tests/ui/invalid_frompy_derive.stderr index d16c082f..04280d73 100644 --- a/tests/ui/invalid_frompy_derive.stderr +++ b/tests/ui/invalid_frompy_derive.stderr @@ -132,7 +132,7 @@ error: only one of `attribute` or `item` can be provided 118 | #[pyo3(item, attribute)] | ^ -error: expected `transparent` or `annotation` +error: expected one of: `transparent`, `annotation`, `crate` --> tests/ui/invalid_frompy_derive.rs:123:8 | 123 | #[pyo3(unknown = "should not work")]