Skip to content

Commit

Permalink
pyo3_path, part 2: add pyo3_path options and use them.
Browse files Browse the repository at this point in the history
  • Loading branch information
birkenfeld committed Nov 24, 2021
1 parent b8e4467 commit 88e4025
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 54 deletions.
16 changes: 15 additions & 1 deletion pyo3-macros-backend/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -17,6 +17,7 @@ pub mod kw {
syn::custom_keyword!(signature);
syn::custom_keyword!(text_signature);
syn::custom_keyword!(transparent);
syn::custom_keyword!(pyo3_path);
}

#[derive(Clone, Debug, PartialEq)]
Expand All @@ -43,6 +44,19 @@ impl Parse for NameAttribute {
}
}

/// For specifying the path to the pyo3 crate.
#[derive(Clone, Debug, PartialEq)]
pub struct PyO3PathAttribute(pub Path);

impl Parse for PyO3PathAttribute {
fn parse(input: ParseStream) -> Result<Self> {
let _: kw::pyo3_path = input.parse()?;
let _: Token![=] = input.parse()?;
let string_literal: LitStr = input.parse()?;
string_literal.parse().map(PyO3PathAttribute)
}
}

#[derive(Clone, Debug, PartialEq)]
pub struct TextSignatureAttribute {
pub kw: kw::text_signature,
Expand Down
20 changes: 14 additions & 6 deletions pyo3-macros-backend/src/from_pyobject.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::attributes::{self, get_pyo3_options, FromPyWithAttribute};
use proc_macro2::TokenStream;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
parenthesized,
Expand Down Expand Up @@ -519,12 +519,20 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
};

let ident = &tokens.ident;
let const_name = syn::Ident::new(
&format!("_PYO3__FROMPYOBJECT_FOR_{}", ident),
Span::call_site(),
);
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<Self> {
#derives
const #const_name: () = {
use ::pyo3 as _pyo3; // TODO

#[automatically_derived]
impl#trait_generics _pyo3::FromPyObject<#lt_param> for #ident#generics #where_clause {
fn extract(obj: &#lt_param _pyo3::PyAny) -> _pyo3::PyResult<Self> {
#derives
}
}
}
};
))
}
46 changes: 29 additions & 17 deletions pyo3-macros-backend/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ pub struct FnSpec<'a> {
pub deprecations: Deprecations,
pub convention: CallingConvention,
pub text_signature: Option<TextSignatureAttribute>,
pub pyo3_path: syn::Path,
}

pub fn get_return_info(output: &syn::ReturnType) -> syn::Type {
Expand All @@ -254,12 +255,14 @@ pub fn parse_method_receiver(arg: &syn::FnArg) -> Result<SelfType> {
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<syn::Attribute>,
options: PyFunctionOptions,
) -> Result<FnSpec<'a>> {
let PyFunctionOptions {
text_signature,
pyo3_path,
name,
mut deprecations,
..
Expand All @@ -278,6 +281,9 @@ 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 pyo3_path = pyo3_path
.map(|p| p.0)
.unwrap_or_else(|| syn::parse_str("::pyo3").unwrap());

let doc = utils::get_doc(
meth_attrs,
Expand Down Expand Up @@ -311,6 +317,7 @@ impl<'a> FnSpec<'a> {
doc,
deprecations,
text_signature,
pyo3_path,
})
}

Expand Down Expand Up @@ -472,14 +479,16 @@ impl<'a> FnSpec<'a> {
};
let rust_call =
quote! { _pyo3::callback::convert(#py, #rust_name(#self_arg #(#arg_names),*)) };
let pyo3_path = &self.pyo3_path;
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
pub(crate) unsafe extern "C" fn #ident (
_slf: *mut #pyo3_path::ffi::PyObject,
_args: *mut #pyo3_path::ffi::PyObject,
) -> *mut #pyo3_path::ffi::PyObject
{
use #pyo3_path as _pyo3;
#deprecations
_pyo3::callback::handle_panic(|#py| {
#self_conversion
Expand All @@ -491,12 +500,13 @@ impl<'a> FnSpec<'a> {
CallingConvention::Fastcall => {
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
pub(crate) unsafe extern "C" fn #ident (
_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
{
use #pyo3_path as _pyo3;
#deprecations
_pyo3::callback::handle_panic(|#py| {
#self_conversion
Expand All @@ -518,11 +528,12 @@ impl<'a> FnSpec<'a> {
CallingConvention::Varargs => {
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
pub(crate) unsafe extern "C" fn #ident (
_slf: *mut #pyo3_path::ffi::PyObject,
_args: *mut #pyo3_path::ffi::PyObject,
_kwargs: *mut #pyo3_path::ffi::PyObject) -> *mut #pyo3_path::ffi::PyObject
{
use #pyo3_path as _pyo3;
#deprecations
_pyo3::callback::handle_panic(|#py| {
#self_conversion
Expand All @@ -538,11 +549,12 @@ impl<'a> FnSpec<'a> {
let rust_call = quote! { #rust_name(#(#arg_names),*) };
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
pub(crate) unsafe extern "C" fn #ident (
subtype: *mut #pyo3_path::ffi::PyTypeObject,
_args: *mut #pyo3_path::ffi::PyObject,
_kwargs: *mut #pyo3_path::ffi::PyObject) -> *mut #pyo3_path::ffi::PyObject
{
use #pyo3_path as _pyo3;
#deprecations
use _pyo3::callback::IntoPyCallbackOutput;
_pyo3::callback::handle_panic(|#py| {
Expand Down
30 changes: 27 additions & 3 deletions pyo3-macros-backend/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
//! 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, NameAttribute,
PyO3PathAttribute,
},
pyfunction::{impl_wrap_pyfunction, PyFunctionOptions},
utils::PythonDoc,
};
Expand All @@ -16,17 +19,20 @@ use syn::{
Ident, Path, Result,
};

#[derive(Default)]
pub struct PyModuleOptions {
pyo3_path: Option<PyO3PathAttribute>,
name: Option<syn::Ident>,
}

impl PyModuleOptions {
pub fn from_attrs(attrs: &mut Vec<syn::Attribute>) -> Result<Self> {
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::PyO3Path(path) => options.set_pyo3_path(path)?,
}
}

Expand All @@ -42,20 +48,35 @@ impl PyModuleOptions {
self.name = Some(name);
Ok(())
}

fn set_pyo3_path(&mut self, path: PyO3PathAttribute) -> Result<()> {
ensure_spanned!(
self.pyo3_path.is_none(),
path.0.span() => "`pyo3_path` may only be specified once"
);

self.pyo3_path = 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 pyo3_path = options
.pyo3_path
.map(|p| p.0)
.unwrap_or_else(|| syn::parse_str("::pyo3").unwrap());
let cb_name = Ident::new(&format!("PyInit_{}", name), Span::call_site());

quote! {
#[no_mangle]
#[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 {
pub unsafe extern "C" fn #cb_name() -> *mut #pyo3_path::ffi::PyObject {
use #pyo3_path as _pyo3;
use _pyo3::derive_utils::ModuleDef;
static NAME: &str = concat!(stringify!(#name), "\0");
static DOC: &str = #doc;
Expand Down Expand Up @@ -143,6 +164,7 @@ fn get_pyfn_attr(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Option<PyFnArgs
}

enum PyModulePyO3Option {
PyO3Path(PyO3PathAttribute),
Name(NameAttribute),
}

Expand All @@ -151,6 +173,8 @@ impl Parse for PyModulePyO3Option {
let lookahead = input.lookahead1();
if lookahead.peek(attributes::kw::name) {
input.parse().map(PyModulePyO3Option::Name)
} else if lookahead.peek(attributes::kw::pyo3_path) {
input.parse().map(PyModulePyO3Option::PyO3Path)
} else {
Err(lookahead.error())
}
Expand Down
Loading

0 comments on commit 88e4025

Please sign in to comment.