Upgrade to syn 2
This commit is contained in:
parent
0b4187a672
commit
afbb1d435c
|
@ -0,0 +1 @@
|
||||||
|
Switched from syn 1.x to syn 2.x
|
|
@ -18,7 +18,7 @@ quote = { version = "1", default-features = false }
|
||||||
proc-macro2 = { version = "1", default-features = false }
|
proc-macro2 = { version = "1", default-features = false }
|
||||||
|
|
||||||
[dependencies.syn]
|
[dependencies.syn]
|
||||||
version = "1.0.85"
|
version = "2"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-traits"]
|
features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-traits"]
|
||||||
|
|
||||||
|
|
|
@ -146,21 +146,13 @@ pub type FromPyWithAttribute = KeywordAttribute<kw::from_py_with, LitStrValue<Ex
|
||||||
pub type CrateAttribute = KeywordAttribute<Token![crate], LitStrValue<Path>>;
|
pub type CrateAttribute = KeywordAttribute<Token![crate], LitStrValue<Path>>;
|
||||||
|
|
||||||
pub fn get_pyo3_options<T: Parse>(attr: &syn::Attribute) -> Result<Option<Punctuated<T, Comma>>> {
|
pub fn get_pyo3_options<T: Parse>(attr: &syn::Attribute) -> Result<Option<Punctuated<T, Comma>>> {
|
||||||
if is_attribute_ident(attr, "pyo3") {
|
if attr.path().is_ident("pyo3") {
|
||||||
attr.parse_args_with(Punctuated::parse_terminated).map(Some)
|
attr.parse_args_with(Punctuated::parse_terminated).map(Some)
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
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.
|
/// Takes attributes from an attribute vector.
|
||||||
///
|
///
|
||||||
/// For each attribute in `attrs`, `extractor` is called. If `extractor` returns `Ok(true)`, then
|
/// For each attribute in `attrs`, `extractor` is called. If `extractor` returns `Ok(true)`, then
|
||||||
|
|
|
@ -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 mut lifetimes = generics.lifetimes();
|
||||||
let lifetime = lifetimes.next();
|
let lifetime = lifetimes.next();
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
attributes::{self, get_pyo3_options, is_attribute_ident, take_attributes, NameAttribute},
|
attributes::{self, get_pyo3_options, take_attributes, NameAttribute},
|
||||||
deprecations::Deprecations,
|
deprecations::Deprecations,
|
||||||
};
|
};
|
||||||
use proc_macro2::{Ident, TokenStream};
|
use proc_macro2::{Ident, TokenStream};
|
||||||
|
@ -64,9 +64,9 @@ impl ConstAttributes {
|
||||||
};
|
};
|
||||||
|
|
||||||
take_attributes(attrs, |attr| {
|
take_attributes(attrs, |attr| {
|
||||||
if is_attribute_ident(attr, "classattr") {
|
if attr.path().is_ident("classattr") {
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
attr.tokens.is_empty(),
|
matches!(attr.meta, syn::Meta::Path(..)),
|
||||||
attr.span() => "`#[classattr]` does not take any arguments"
|
attr.span() => "`#[classattr]` does not take any arguments"
|
||||||
);
|
);
|
||||||
attributes.is_class_attr = true;
|
attributes.is_class_attr = true;
|
||||||
|
|
|
@ -651,8 +651,8 @@ fn parse_method_attributes(
|
||||||
}
|
}
|
||||||
|
|
||||||
for attr in attrs.drain(..) {
|
for attr in attrs.drain(..) {
|
||||||
match attr.parse_meta() {
|
match attr.meta {
|
||||||
Ok(syn::Meta::Path(name)) => {
|
syn::Meta::Path(ref name) => {
|
||||||
if name.is_ident("new") || name.is_ident("__new__") {
|
if name.is_ident("new") || name.is_ident("__new__") {
|
||||||
set_compound_ty!(MethodTypeAttribute::New, name);
|
set_compound_ty!(MethodTypeAttribute::New, name);
|
||||||
} else if name.is_ident("init") || name.is_ident("__init__") {
|
} else if name.is_ident("init") || name.is_ident("__init__") {
|
||||||
|
@ -680,9 +680,7 @@ fn parse_method_attributes(
|
||||||
new_attrs.push(attr)
|
new_attrs.push(attr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(syn::Meta::List(syn::MetaList {
|
syn::Meta::List(ref ml @ syn::MetaList { ref path, .. }) => {
|
||||||
path, mut nested, ..
|
|
||||||
})) => {
|
|
||||||
if path.is_ident("new") {
|
if path.is_ident("new") {
|
||||||
set_ty!(MethodTypeAttribute::New, path);
|
set_ty!(MethodTypeAttribute::New, path);
|
||||||
} else if path.is_ident("init") {
|
} else if path.is_ident("init") {
|
||||||
|
@ -699,10 +697,6 @@ fn parse_method_attributes(
|
||||||
attr.span() => "inner attribute is not supported for setter and getter"
|
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") {
|
if path.is_ident("setter") {
|
||||||
set_ty!(MethodTypeAttribute::Setter, path);
|
set_ty!(MethodTypeAttribute::Setter, path);
|
||||||
|
@ -715,31 +709,21 @@ fn parse_method_attributes(
|
||||||
python_name.span() => "`name` may only be specified once"
|
python_name.span() => "`name` may only be specified once"
|
||||||
);
|
);
|
||||||
|
|
||||||
python_name = match nested.pop().unwrap().into_value() {
|
if let Ok(ident) = ml.parse_args::<syn::Ident>() {
|
||||||
syn::NestedMeta::Meta(syn::Meta::Path(w)) if w.segments.len() == 1 => {
|
python_name = Some(ident);
|
||||||
Some(w.segments[0].ident.clone())
|
} else if let Ok(syn::Lit::Str(s)) = ml.parse_args::<syn::Lit>() {
|
||||||
}
|
python_name = Some(s.parse()?);
|
||||||
syn::NestedMeta::Lit(lit) => match lit {
|
} else {
|
||||||
syn::Lit::Str(s) => Some(s.parse()?),
|
return Err(syn::Error::new_spanned(
|
||||||
_ => {
|
ml,
|
||||||
return Err(syn::Error::new_spanned(
|
"expected ident or string literal for property name",
|
||||||
lit,
|
));
|
||||||
"setter/getter attribute requires str value",
|
}
|
||||||
))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Err(syn::Error::new_spanned(
|
|
||||||
nested.first().unwrap(),
|
|
||||||
"expected ident or string literal for property name",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
new_attrs.push(attr)
|
new_attrs.push(attr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(syn::Meta::NameValue(_)) | Err(_) => new_attrs.push(attr),
|
syn::Meta::NameValue(_) => new_attrs.push(attr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
//! Code generation for the function that initializes a python module and adds classes and function.
|
//! Code generation for the function that initializes a python module and adds classes and function.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
attributes::{
|
attributes::{self, take_attributes, take_pyo3_options, CrateAttribute, NameAttribute},
|
||||||
self, is_attribute_ident, take_attributes, take_pyo3_options, CrateAttribute, NameAttribute,
|
|
||||||
},
|
|
||||||
pyfunction::{impl_wrap_pyfunction, PyFunctionOptions},
|
pyfunction::{impl_wrap_pyfunction, PyFunctionOptions},
|
||||||
utils::{get_pyo3_crate, PythonDoc},
|
utils::{get_pyo3_crate, PythonDoc},
|
||||||
};
|
};
|
||||||
|
@ -165,7 +163,7 @@ fn get_pyfn_attr(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Option<PyFnArgs
|
||||||
let mut pyfn_args: Option<PyFnArgs> = None;
|
let mut pyfn_args: Option<PyFnArgs> = None;
|
||||||
|
|
||||||
take_attributes(attrs, |attr| {
|
take_attributes(attrs, |attr| {
|
||||||
if is_attribute_ident(attr, "pyfn") {
|
if attr.path().is_ident("pyfn") {
|
||||||
ensure_spanned!(
|
ensure_spanned!(
|
||||||
pyfn_args.is_none(),
|
pyfn_args.is_none(),
|
||||||
attr.span() => "`#[pyfn] may only be specified once"
|
attr.span() => "`#[pyfn] may only be specified once"
|
||||||
|
|
|
@ -264,7 +264,7 @@ enum Annotated<X, Y> {
|
||||||
Struct(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 {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Self::Field(x) => x.span(),
|
Self::Field(x) => x.span(),
|
||||||
|
@ -410,7 +410,7 @@ impl<'a> PyClassEnum<'a> {
|
||||||
// "Under the default representation, the specified discriminant is interpreted as an isize
|
// "Under the default representation, the specified discriminant is interpreted as an isize
|
||||||
// value", so `isize` should be enough by default.
|
// value", so `isize` should be enough by default.
|
||||||
let mut repr_type = syn::Ident::new("isize", proc_macro2::Span::call_site());
|
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 =
|
let args =
|
||||||
attr.parse_args_with(Punctuated::<TokenStream, Token![!]>::parse_terminated)?;
|
attr.parse_args_with(Punctuated::<TokenStream, Token![!]>::parse_terminated)?;
|
||||||
if let Some(ident) = args
|
if let Some(ident) = args
|
||||||
|
@ -447,7 +447,7 @@ pub fn build_py_enum(
|
||||||
} else if let Some(subclass) = &args.options.subclass {
|
} else if let Some(subclass) = &args.options.subclass {
|
||||||
bail_spanned!(subclass.span() => "enums can't be inherited by other classes");
|
bail_spanned!(subclass.span() => "enums can't be inherited by other classes");
|
||||||
} else if enum_.variants.is_empty() {
|
} 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);
|
let doc = utils::get_doc(&enum_.attrs, None);
|
||||||
|
@ -518,7 +518,7 @@ fn impl_enum(
|
||||||
);
|
);
|
||||||
quote! { #cls::#variant_name => #repr, }
|
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 {
|
fn __pyo3__repr__(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
#(#variants_repr)*
|
#(#variants_repr)*
|
||||||
|
@ -537,7 +537,7 @@ fn impl_enum(
|
||||||
let variant_name = variant.ident;
|
let variant_name = variant.ident;
|
||||||
quote! { #cls::#variant_name => #cls::#variant_name as #repr_type, }
|
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 {
|
fn __pyo3__int__(&self) -> #repr_type {
|
||||||
match self {
|
match self {
|
||||||
#(#variants_to_int)*
|
#(#variants_to_int)*
|
||||||
|
@ -549,7 +549,7 @@ fn impl_enum(
|
||||||
};
|
};
|
||||||
|
|
||||||
let (default_richcmp, default_richcmp_slot) = {
|
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__(
|
fn __pyo3__richcmp__(
|
||||||
&self,
|
&self,
|
||||||
py: _pyo3::Python,
|
py: _pyo3::Python,
|
||||||
|
@ -623,7 +623,7 @@ fn impl_enum(
|
||||||
|
|
||||||
fn generate_default_protocol_slot(
|
fn generate_default_protocol_slot(
|
||||||
cls: &syn::Type,
|
cls: &syn::Type,
|
||||||
method: &mut syn::ImplItemMethod,
|
method: &mut syn::ImplItemFn,
|
||||||
slot: &SlotDef,
|
slot: &SlotDef,
|
||||||
) -> syn::Result<MethodAndSlotDef> {
|
) -> syn::Result<MethodAndSlotDef> {
|
||||||
let spec = FnSpec::parse(
|
let spec = FnSpec::parse(
|
||||||
|
|
|
@ -23,7 +23,7 @@ impl Parse for Signature {
|
||||||
let content;
|
let content;
|
||||||
let paren_token = syn::parenthesized!(content in input);
|
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 })
|
Ok(Signature { paren_token, items })
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ pub fn impl_methods(
|
||||||
|
|
||||||
for iimpl in impls.iter_mut() {
|
for iimpl in impls.iter_mut() {
|
||||||
match iimpl {
|
match iimpl {
|
||||||
syn::ImplItem::Method(meth) => {
|
syn::ImplItem::Fn(meth) => {
|
||||||
let mut fun_options = PyFunctionOptions::from_attrs(&mut meth.attrs)?;
|
let mut fun_options = PyFunctionOptions::from_attrs(&mut meth.attrs)?;
|
||||||
fun_options.krate = fun_options.krate.or_else(|| options.krate.clone());
|
fun_options.krate = fun_options.krate.or_else(|| options.krate.clone());
|
||||||
match pymethod::gen_py_method(ty, &mut meth.sig, &mut meth.attrs, fun_options)? {
|
match pymethod::gen_py_method(ty, &mut meth.sig, &mut meth.attrs, fun_options)? {
|
||||||
|
@ -299,6 +299,6 @@ fn submit_methods_inventory(
|
||||||
fn get_cfg_attributes(attrs: &[syn::Attribute]) -> Vec<&syn::Attribute> {
|
fn get_cfg_attributes(attrs: &[syn::Attribute]) -> Vec<&syn::Attribute> {
|
||||||
attrs
|
attrs
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|attr| attr.path.is_ident("cfg"))
|
.filter(|attr| attr.path().is_ident("cfg"))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
@ -541,7 +541,11 @@ pub fn impl_py_setter_def(
|
||||||
|
|
||||||
let mut cfg_attrs = TokenStream::new();
|
let mut cfg_attrs = TokenStream::new();
|
||||||
if let PropertyType::Descriptor { field, .. } = &property_type {
|
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);
|
attr.to_tokens(&mut cfg_attrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -667,7 +671,11 @@ pub fn impl_py_getter_def(
|
||||||
|
|
||||||
let mut cfg_attrs = TokenStream::new();
|
let mut cfg_attrs = TokenStream::new();
|
||||||
if let PropertyType::Descriptor { field, .. } = &property_type {
|
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);
|
attr.to_tokens(&mut cfg_attrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
let mut current_part = text_signature.unwrap_or_default();
|
||||||
|
|
||||||
for attr in attrs.iter() {
|
for attr in attrs.iter() {
|
||||||
if attr.path.is_ident("doc") {
|
if attr.path().is_ident("doc") {
|
||||||
if let Ok(DocArgs {
|
if let Ok(nv) = attr.meta.require_name_value() {
|
||||||
_eq_token,
|
|
||||||
token_stream,
|
|
||||||
}) = syn::parse2(attr.tokens.clone())
|
|
||||||
{
|
|
||||||
if !first {
|
if !first {
|
||||||
current_part.push('\n');
|
current_part.push('\n');
|
||||||
} else {
|
} else {
|
||||||
first = false;
|
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.
|
// Strip single left space from literal strings, if needed.
|
||||||
// e.g. `/// Hello world` expands to #[doc = " Hello world"]
|
// e.g. `/// Hello world` expands to #[doc = " Hello world"]
|
||||||
let doc_line = lit_str.value();
|
let doc_line = lit_str.value();
|
||||||
|
@ -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.
|
// Reset the string buffer, write that part, and then push this macro part too.
|
||||||
parts.push(current_part.to_token_stream());
|
parts.push(current_part.to_token_stream());
|
||||||
current_part.clear();
|
current_part.clear();
|
||||||
parts.push(token_stream);
|
parts.push(nv.value.to_token_stream());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ pub fn get_doc(attrs: &[syn::Attribute], mut text_signature: Option<String>) ->
|
||||||
let mut tokens = TokenStream::new();
|
let mut tokens = TokenStream::new();
|
||||||
|
|
||||||
syn::Ident::new("concat", Span::call_site()).to_tokens(&mut tokens);
|
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| {
|
syn::token::Bracket(Span::call_site()).surround(&mut tokens, |tokens| {
|
||||||
parts.to_tokens(tokens);
|
parts.to_tokens(tokens);
|
||||||
syn::token::Comma(Span::call_site()).to_tokens(tokens);
|
syn::token::Comma(Span::call_site()).to_tokens(tokens);
|
||||||
|
@ -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<()> {
|
pub fn ensure_not_async_fn(sig: &syn::Signature) -> syn::Result<()> {
|
||||||
if let Some(asyncness) = &sig.asyncness {
|
if let Some(asyncness) = &sig.asyncness {
|
||||||
bail_spanned!(
|
bail_spanned!(
|
||||||
|
|
|
@ -21,5 +21,5 @@ abi3 = ["pyo3-macros-backend/abi3"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
proc-macro2 = { version = "1", default-features = false }
|
proc-macro2 = { version = "1", default-features = false }
|
||||||
quote = "1"
|
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" }
|
pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.19.0" }
|
||||||
|
|
Loading…
Reference in New Issue