Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to syn 2 #3239

Merged
merged 1 commit into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragments/3239.packaging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Switched from syn 1.x to syn 2.x
2 changes: 1 addition & 1 deletion pyo3-macros-backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ quote = { version = "1", default-features = false }
proc-macro2 = { version = "1", default-features = false }

[dependencies.syn]
version = "1.0.85"
version = "2"
default-features = false
features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-traits"]

Expand Down
10 changes: 1 addition & 9 deletions pyo3-macros-backend/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,21 +146,13 @@ pub type FromPyWithAttribute = KeywordAttribute<kw::from_py_with, LitStrValue<Ex
pub type CrateAttribute = KeywordAttribute<Token![crate], LitStrValue<Path>>;

pub fn get_pyo3_options<T: Parse>(attr: &syn::Attribute) -> Result<Option<Punctuated<T, Comma>>> {
if is_attribute_ident(attr, "pyo3") {
if attr.path().is_ident("pyo3") {
attr.parse_args_with(Punctuated::parse_terminated).map(Some)
} else {
Ok(None)
}
}

pub fn is_attribute_ident(attr: &syn::Attribute, name: &str) -> bool {
if let Some(path_segment) = attr.path.segments.last() {
attr.path.segments.len() == 1 && path_segment.ident == name
} else {
false
}
}

/// Takes attributes from an attribute vector.
///
/// For each attribute in `attrs`, `extractor` is called. If `extractor` returns `Ok(true)`, then
Expand Down
2 changes: 1 addition & 1 deletion pyo3-macros-backend/src/frompyobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ impl FieldPyO3Attributes {
}
}

fn verify_and_get_lifetime(generics: &syn::Generics) -> Result<Option<&syn::LifetimeDef>> {
fn verify_and_get_lifetime(generics: &syn::Generics) -> Result<Option<&syn::LifetimeParam>> {
let mut lifetimes = generics.lifetimes();
let lifetime = lifetimes.next();
ensure_spanned!(
Expand Down
6 changes: 3 additions & 3 deletions pyo3-macros-backend/src/konst.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::borrow::Cow;

use crate::{
attributes::{self, get_pyo3_options, is_attribute_ident, take_attributes, NameAttribute},
attributes::{self, get_pyo3_options, take_attributes, NameAttribute},
deprecations::Deprecations,
};
use proc_macro2::{Ident, TokenStream};
Expand Down Expand Up @@ -64,9 +64,9 @@ impl ConstAttributes {
};

take_attributes(attrs, |attr| {
if is_attribute_ident(attr, "classattr") {
if attr.path().is_ident("classattr") {
ensure_spanned!(
attr.tokens.is_empty(),
matches!(attr.meta, syn::Meta::Path(..)),
attr.span() => "`#[classattr]` does not take any arguments"
);
attributes.is_class_attr = true;
Expand Down
44 changes: 14 additions & 30 deletions pyo3-macros-backend/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,8 +651,8 @@ fn parse_method_attributes(
}

for attr in attrs.drain(..) {
match attr.parse_meta() {
Ok(syn::Meta::Path(name)) => {
match attr.meta {
syn::Meta::Path(ref name) => {
if name.is_ident("new") || name.is_ident("__new__") {
set_compound_ty!(MethodTypeAttribute::New, name);
} else if name.is_ident("init") || name.is_ident("__init__") {
Expand Down Expand Up @@ -680,9 +680,7 @@ fn parse_method_attributes(
new_attrs.push(attr)
}
}
Ok(syn::Meta::List(syn::MetaList {
path, mut nested, ..
})) => {
syn::Meta::List(ref ml @ syn::MetaList { ref path, .. }) => {
if path.is_ident("new") {
set_ty!(MethodTypeAttribute::New, path);
} else if path.is_ident("init") {
Expand All @@ -699,10 +697,6 @@ fn parse_method_attributes(
attr.span() => "inner attribute is not supported for setter and getter"
);
}
ensure_spanned!(
nested.len() == 1,
attr.span() => "setter/getter requires one value"
);

if path.is_ident("setter") {
set_ty!(MethodTypeAttribute::Setter, path);
Expand All @@ -715,31 +709,21 @@ fn parse_method_attributes(
python_name.span() => "`name` may only be specified once"
);

python_name = match nested.pop().unwrap().into_value() {
syn::NestedMeta::Meta(syn::Meta::Path(w)) if w.segments.len() == 1 => {
Some(w.segments[0].ident.clone())
}
syn::NestedMeta::Lit(lit) => match lit {
syn::Lit::Str(s) => Some(s.parse()?),
_ => {
return Err(syn::Error::new_spanned(
lit,
"setter/getter attribute requires str value",
))
}
},
_ => {
return Err(syn::Error::new_spanned(
nested.first().unwrap(),
"expected ident or string literal for property name",
))
}
};
if let Ok(ident) = ml.parse_args::<syn::Ident>() {
python_name = Some(ident);
} else if let Ok(syn::Lit::Str(s)) = ml.parse_args::<syn::Lit>() {
python_name = Some(s.parse()?);
} else {
return Err(syn::Error::new_spanned(
ml,
"expected ident or string literal for property name",
));
}
} else {
new_attrs.push(attr)
}
}
Ok(syn::Meta::NameValue(_)) | Err(_) => new_attrs.push(attr),
syn::Meta::NameValue(_) => new_attrs.push(attr),
}
}

Expand Down
6 changes: 2 additions & 4 deletions pyo3-macros-backend/src/module.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
//! 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, CrateAttribute, NameAttribute,
},
attributes::{self, take_attributes, take_pyo3_options, CrateAttribute, NameAttribute},
pyfunction::{impl_wrap_pyfunction, PyFunctionOptions},
utils::{get_pyo3_crate, PythonDoc},
};
Expand Down Expand Up @@ -165,7 +163,7 @@ fn get_pyfn_attr(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Option<PyFnArgs
let mut pyfn_args: Option<PyFnArgs> = None;

take_attributes(attrs, |attr| {
if is_attribute_ident(attr, "pyfn") {
if attr.path().is_ident("pyfn") {
ensure_spanned!(
pyfn_args.is_none(),
attr.span() => "`#[pyfn] may only be specified once"
Expand Down
14 changes: 7 additions & 7 deletions pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ enum Annotated<X, Y> {
Struct(Y),
}

impl<X: Spanned, Y: Spanned> Spanned for Annotated<X, Y> {
impl<X: Spanned, Y: Spanned> Annotated<X, Y> {
fn span(&self) -> Span {
match self {
Self::Field(x) => x.span(),
Expand Down Expand Up @@ -410,7 +410,7 @@ impl<'a> PyClassEnum<'a> {
// "Under the default representation, the specified discriminant is interpreted as an isize
// value", so `isize` should be enough by default.
let mut repr_type = syn::Ident::new("isize", proc_macro2::Span::call_site());
if let Some(attr) = enum_.attrs.iter().find(|attr| attr.path.is_ident("repr")) {
if let Some(attr) = enum_.attrs.iter().find(|attr| attr.path().is_ident("repr")) {
let args =
attr.parse_args_with(Punctuated::<TokenStream, Token![!]>::parse_terminated)?;
if let Some(ident) = args
Expand Down Expand Up @@ -447,7 +447,7 @@ pub fn build_py_enum(
} else if let Some(subclass) = &args.options.subclass {
bail_spanned!(subclass.span() => "enums can't be inherited by other classes");
} else if enum_.variants.is_empty() {
bail_spanned!(enum_.brace_token.span => "#[pyclass] can't be used on enums without any variants");
bail_spanned!(enum_.brace_token.span.join() => "#[pyclass] can't be used on enums without any variants");
}

let doc = utils::get_doc(&enum_.attrs, None);
Expand Down Expand Up @@ -518,7 +518,7 @@ fn impl_enum(
);
quote! { #cls::#variant_name => #repr, }
});
let mut repr_impl: syn::ImplItemMethod = syn::parse_quote! {
let mut repr_impl: syn::ImplItemFn = syn::parse_quote! {
fn __pyo3__repr__(&self) -> &'static str {
match self {
#(#variants_repr)*
Expand All @@ -537,7 +537,7 @@ fn impl_enum(
let variant_name = variant.ident;
quote! { #cls::#variant_name => #cls::#variant_name as #repr_type, }
});
let mut int_impl: syn::ImplItemMethod = syn::parse_quote! {
let mut int_impl: syn::ImplItemFn = syn::parse_quote! {
fn __pyo3__int__(&self) -> #repr_type {
match self {
#(#variants_to_int)*
Expand All @@ -549,7 +549,7 @@ fn impl_enum(
};

let (default_richcmp, default_richcmp_slot) = {
let mut richcmp_impl: syn::ImplItemMethod = syn::parse_quote! {
let mut richcmp_impl: syn::ImplItemFn = syn::parse_quote! {
fn __pyo3__richcmp__(
&self,
py: _pyo3::Python,
Expand Down Expand Up @@ -623,7 +623,7 @@ fn impl_enum(

fn generate_default_protocol_slot(
cls: &syn::Type,
method: &mut syn::ImplItemMethod,
method: &mut syn::ImplItemFn,
slot: &SlotDef,
) -> syn::Result<MethodAndSlotDef> {
let spec = FnSpec::parse(
Expand Down
2 changes: 1 addition & 1 deletion pyo3-macros-backend/src/pyfunction/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl Parse for Signature {
let content;
let paren_token = syn::parenthesized!(content in input);

let items = content.parse_terminated(SignatureItem::parse)?;
let items = content.parse_terminated(SignatureItem::parse, Token![,])?;

Ok(Signature { paren_token, items })
}
Expand Down
4 changes: 2 additions & 2 deletions pyo3-macros-backend/src/pyimpl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub fn impl_methods(

for iimpl in impls.iter_mut() {
match iimpl {
syn::ImplItem::Method(meth) => {
syn::ImplItem::Fn(meth) => {
let mut fun_options = PyFunctionOptions::from_attrs(&mut meth.attrs)?;
fun_options.krate = fun_options.krate.or_else(|| options.krate.clone());
match pymethod::gen_py_method(ty, &mut meth.sig, &mut meth.attrs, fun_options)? {
Expand Down Expand Up @@ -299,6 +299,6 @@ fn submit_methods_inventory(
fn get_cfg_attributes(attrs: &[syn::Attribute]) -> Vec<&syn::Attribute> {
attrs
.iter()
.filter(|attr| attr.path.is_ident("cfg"))
.filter(|attr| attr.path().is_ident("cfg"))
.collect()
}
12 changes: 10 additions & 2 deletions pyo3-macros-backend/src/pymethod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,11 @@ pub fn impl_py_setter_def(

let mut cfg_attrs = TokenStream::new();
if let PropertyType::Descriptor { field, .. } = &property_type {
for attr in field.attrs.iter().filter(|attr| attr.path.is_ident("cfg")) {
for attr in field
.attrs
.iter()
.filter(|attr| attr.path().is_ident("cfg"))
{
attr.to_tokens(&mut cfg_attrs);
}
}
Expand Down Expand Up @@ -667,7 +671,11 @@ pub fn impl_py_getter_def(

let mut cfg_attrs = TokenStream::new();
if let PropertyType::Descriptor { field, .. } = &property_type {
for attr in field.attrs.iter().filter(|attr| attr.path.is_ident("cfg")) {
for attr in field
.attrs
.iter()
.filter(|attr| attr.path().is_ident("cfg"))
{
attr.to_tokens(&mut cfg_attrs);
}
}
Expand Down
34 changes: 9 additions & 25 deletions pyo3-macros-backend/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,18 @@ pub fn get_doc(attrs: &[syn::Attribute], mut text_signature: Option<String>) ->
let mut current_part = text_signature.unwrap_or_default();

for attr in attrs.iter() {
if attr.path.is_ident("doc") {
if let Ok(DocArgs {
_eq_token,
token_stream,
}) = syn::parse2(attr.tokens.clone())
{
if attr.path().is_ident("doc") {
if let Ok(nv) = attr.meta.require_name_value() {
if !first {
current_part.push('\n');
} else {
first = false;
}
if let Ok(syn::Lit::Str(lit_str)) = syn::parse2(token_stream.clone()) {
if let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit_str),
..
}) = &nv.value
{
// Strip single left space from literal strings, if needed.
// e.g. `/// Hello world` expands to #[doc = " Hello world"]
let doc_line = lit_str.value();
Expand All @@ -101,7 +101,7 @@ pub fn get_doc(attrs: &[syn::Attribute], mut text_signature: Option<String>) ->
// Reset the string buffer, write that part, and then push this macro part too.
parts.push(current_part.to_token_stream());
current_part.clear();
parts.push(token_stream);
parts.push(nv.value.to_token_stream());
}
}
}
Expand All @@ -116,7 +116,7 @@ pub fn get_doc(attrs: &[syn::Attribute], mut text_signature: Option<String>) ->
let mut tokens = TokenStream::new();

syn::Ident::new("concat", Span::call_site()).to_tokens(&mut tokens);
syn::token::Bang(Span::call_site()).to_tokens(&mut tokens);
syn::token::Not(Span::call_site()).to_tokens(&mut tokens);
syn::token::Bracket(Span::call_site()).surround(&mut tokens, |tokens| {
parts.to_tokens(tokens);
syn::token::Comma(Span::call_site()).to_tokens(tokens);
Expand All @@ -137,22 +137,6 @@ impl quote::ToTokens for PythonDoc {
}
}

struct DocArgs {
_eq_token: syn::Token![=],
token_stream: TokenStream,
}

impl syn::parse::Parse for DocArgs {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let this = Self {
_eq_token: input.parse()?,
token_stream: input.parse()?,
};
ensure_spanned!(input.is_empty(), input.span() => "expected end of doc attribute");
Ok(this)
}
}

pub fn ensure_not_async_fn(sig: &syn::Signature) -> syn::Result<()> {
if let Some(asyncness) = &sig.asyncness {
bail_spanned!(
Expand Down
2 changes: 1 addition & 1 deletion pyo3-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ abi3 = ["pyo3-macros-backend/abi3"]
[dependencies]
proc-macro2 = { version = "1", default-features = false }
quote = "1"
syn = { version = "1.0.85", features = ["full", "extra-traits"] }
syn = { version = "2", features = ["full", "extra-traits"] }
pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.19.0" }