1
1
// Copyright (c) 2017-present PyO3 Project and Contributors
2
2
3
3
use crate :: pyfunction:: Argument ;
4
- use crate :: pyfunction:: PyFunctionAttr ;
4
+ use crate :: pyfunction:: { parse_name_attribute, PyFunctionAttr } ;
5
+ use crate :: utils;
5
6
use proc_macro2:: TokenStream ;
6
7
use quote:: quote;
7
8
use quote:: ToTokens ;
9
+ use syn:: ext:: IdentExt ;
8
10
use syn:: spanned:: Spanned ;
9
11
10
12
#[ derive( Clone , PartialEq , Debug ) ]
@@ -20,8 +22,8 @@ pub struct FnArg<'a> {
20
22
21
23
#[ derive( Clone , PartialEq , Debug ) ]
22
24
pub enum FnType {
23
- Getter ( Option < String > ) ,
24
- Setter ( Option < String > ) ,
25
+ Getter ,
26
+ Setter ,
25
27
Fn ,
26
28
FnNew ,
27
29
FnCall ,
@@ -33,9 +35,15 @@ pub enum FnType {
33
35
#[ derive( Clone , PartialEq , Debug ) ]
34
36
pub struct FnSpec < ' a > {
35
37
pub tp : FnType ,
38
+ // Rust function name
39
+ pub name : & ' a syn:: Ident ,
40
+ // Wrapped python name. This should not have any leading r#.
41
+ // r# can be removed by syn::ext::IdentExt::unraw()
42
+ pub python_name : syn:: Ident ,
36
43
pub attrs : Vec < Argument > ,
37
44
pub args : Vec < FnArg < ' a > > ,
38
45
pub output : syn:: Type ,
46
+ pub doc : syn:: LitStr ,
39
47
}
40
48
41
49
pub fn get_return_info ( output : & syn:: ReturnType ) -> syn:: Type {
@@ -48,11 +56,16 @@ pub fn get_return_info(output: &syn::ReturnType) -> syn::Type {
48
56
impl < ' a > FnSpec < ' a > {
49
57
/// Parser function signature and function attributes
50
58
pub fn parse (
51
- name : & ' a syn:: Ident ,
52
59
sig : & ' a syn:: Signature ,
53
60
meth_attrs : & mut Vec < syn:: Attribute > ,
61
+ allow_custom_name : bool ,
54
62
) -> syn:: Result < FnSpec < ' a > > {
55
- let ( mut fn_type, fn_attrs) = parse_attributes ( meth_attrs) ?;
63
+ let name = & sig. ident ;
64
+ let MethodAttributes {
65
+ ty : mut fn_type,
66
+ args : fn_attrs,
67
+ mut python_name,
68
+ } = parse_method_attributes ( meth_attrs, allow_custom_name) ?;
56
69
57
70
let mut has_self = false ;
58
71
let mut arguments = Vec :: new ( ) ;
@@ -112,11 +125,58 @@ impl<'a> FnSpec<'a> {
112
125
fn_type = FnType :: PySelf ( tp) ;
113
126
}
114
127
128
+ // "Tweak" getter / setter names: strip off set_ and get_ if needed
129
+ if let FnType :: Getter | FnType :: Setter = & fn_type {
130
+ if python_name. is_none ( ) {
131
+ let prefix = match & fn_type {
132
+ FnType :: Getter => "get_" ,
133
+ FnType :: Setter => "set_" ,
134
+ _ => unreachable ! ( ) ,
135
+ } ;
136
+
137
+ let ident = sig. ident . unraw ( ) . to_string ( ) ;
138
+ if ident. starts_with ( prefix) {
139
+ python_name = Some ( syn:: Ident :: new ( & ident[ prefix. len ( ) ..] , ident. span ( ) ) )
140
+ }
141
+ }
142
+ }
143
+
144
+ let python_name = python_name. unwrap_or_else ( || name. unraw ( ) ) ;
145
+
146
+ let mut parse_erroneous_text_signature = |error_msg : & str | {
147
+ // try to parse anyway to give better error messages
148
+ if let Some ( text_signature) =
149
+ utils:: parse_text_signature_attrs ( meth_attrs, & python_name) ?
150
+ {
151
+ Err ( syn:: Error :: new_spanned ( text_signature, error_msg) )
152
+ } else {
153
+ Ok ( None )
154
+ }
155
+ } ;
156
+
157
+ let text_signature = match & fn_type {
158
+ FnType :: Fn | FnType :: PySelf ( _) | FnType :: FnClass | FnType :: FnStatic => {
159
+ utils:: parse_text_signature_attrs ( & mut * meth_attrs, name) ?
160
+ }
161
+ FnType :: FnNew => parse_erroneous_text_signature (
162
+ "text_signature not allowed on __new__; if you want to add a signature on \
163
+ __new__, put it on the struct definition instead",
164
+ ) ?,
165
+ FnType :: FnCall | FnType :: Getter | FnType :: Setter => {
166
+ parse_erroneous_text_signature ( "text_signature not allowed with this attribute" ) ?
167
+ }
168
+ } ;
169
+
170
+ let doc = utils:: get_doc ( & meth_attrs, text_signature, true ) ?;
171
+
115
172
Ok ( FnSpec {
116
173
tp : fn_type,
174
+ name,
175
+ python_name,
117
176
attrs : fn_attrs,
118
177
args : arguments,
119
178
output : ty,
179
+ doc,
120
180
} )
121
181
}
122
182
@@ -279,10 +339,21 @@ pub fn check_arg_ty_and_optional<'a>(
279
339
}
280
340
}
281
341
282
- fn parse_attributes ( attrs : & mut Vec < syn:: Attribute > ) -> syn:: Result < ( FnType , Vec < Argument > ) > {
342
+ #[ derive( Clone , PartialEq , Debug ) ]
343
+ struct MethodAttributes {
344
+ ty : FnType ,
345
+ args : Vec < Argument > ,
346
+ python_name : Option < syn:: Ident > ,
347
+ }
348
+
349
+ fn parse_method_attributes (
350
+ attrs : & mut Vec < syn:: Attribute > ,
351
+ allow_custom_name : bool ,
352
+ ) -> syn:: Result < MethodAttributes > {
283
353
let mut new_attrs = Vec :: new ( ) ;
284
- let mut spec = Vec :: new ( ) ;
354
+ let mut args = Vec :: new ( ) ;
285
355
let mut res: Option < FnType > = None ;
356
+ let mut property_name = None ;
286
357
287
358
for attr in attrs. iter ( ) {
288
359
match attr. parse_meta ( ) ? {
@@ -302,15 +373,21 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> syn::Result<(FnType, Vec
302
373
res = Some ( FnType :: FnStatic )
303
374
} else if name. is_ident ( "setter" ) || name. is_ident ( "getter" ) {
304
375
if let syn:: AttrStyle :: Inner ( _) = attr. style {
305
- panic ! ( "Inner style attribute is not supported for setter and getter" ) ;
376
+ return Err ( syn:: Error :: new_spanned (
377
+ attr,
378
+ "Inner style attribute is not supported for setter and getter" ,
379
+ ) ) ;
306
380
}
307
381
if res != None {
308
- panic ! ( "setter/getter attribute can not be used mutiple times" ) ;
382
+ return Err ( syn:: Error :: new_spanned (
383
+ attr,
384
+ "setter/getter attribute can not be used mutiple times" ,
385
+ ) ) ;
309
386
}
310
387
if name. is_ident ( "setter" ) {
311
- res = Some ( FnType :: Setter ( None ) )
388
+ res = Some ( FnType :: Setter )
312
389
} else {
313
- res = Some ( FnType :: Getter ( None ) )
390
+ res = Some ( FnType :: Getter )
314
391
}
315
392
} else {
316
393
new_attrs. push ( attr. clone ( ) )
@@ -332,58 +409,105 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> syn::Result<(FnType, Vec
332
409
res = Some ( FnType :: FnCall )
333
410
} else if path. is_ident ( "setter" ) || path. is_ident ( "getter" ) {
334
411
if let syn:: AttrStyle :: Inner ( _) = attr. style {
335
- panic ! (
336
- "Inner style attribute is not
337
- supported for setter and getter"
338
- ) ;
412
+ return Err ( syn :: Error :: new_spanned (
413
+ attr ,
414
+ "Inner style attribute is not supported for setter and getter",
415
+ ) ) ;
339
416
}
340
417
if res != None {
341
- panic ! ( "setter/getter attribute can not be used mutiple times" ) ;
418
+ return Err ( syn:: Error :: new_spanned (
419
+ attr,
420
+ "setter/getter attribute can not be used mutiple times" ,
421
+ ) ) ;
342
422
}
343
423
if nested. len ( ) != 1 {
344
- panic ! ( "setter/getter requires one value" ) ;
424
+ return Err ( syn:: Error :: new_spanned (
425
+ attr,
426
+ "setter/getter requires one value" ,
427
+ ) ) ;
345
428
}
346
- match nested. first ( ) . unwrap ( ) {
347
- syn:: NestedMeta :: Meta ( syn:: Meta :: Path ( ref w) ) => {
348
- if path. is_ident ( "setter" ) {
349
- res = Some ( FnType :: Setter ( Some ( w. segments [ 0 ] . ident . to_string ( ) ) ) )
350
- } else {
351
- res = Some ( FnType :: Getter ( Some ( w. segments [ 0 ] . ident . to_string ( ) ) ) )
352
- }
429
+
430
+ res = if path. is_ident ( "setter" ) {
431
+ Some ( FnType :: Setter )
432
+ } else {
433
+ Some ( FnType :: Getter )
434
+ } ;
435
+
436
+ property_name = match nested. first ( ) . unwrap ( ) {
437
+ syn:: NestedMeta :: Meta ( syn:: Meta :: Path ( ref w) ) if w. segments . len ( ) == 1 => {
438
+ Some ( w. segments [ 0 ] . ident . clone ( ) )
353
439
}
354
440
syn:: NestedMeta :: Lit ( ref lit) => match * lit {
355
- syn:: Lit :: Str ( ref s) => {
356
- if path. is_ident ( "setter" ) {
357
- res = Some ( FnType :: Setter ( Some ( s. value ( ) ) ) )
358
- } else {
359
- res = Some ( FnType :: Getter ( Some ( s. value ( ) ) ) )
360
- }
361
- }
441
+ syn:: Lit :: Str ( ref s) => Some ( s. parse ( ) ?) ,
362
442
_ => {
363
- panic ! ( "setter/getter attribute requires str value" ) ;
443
+ return Err ( syn:: Error :: new_spanned (
444
+ lit,
445
+ "setter/getter attribute requires str value" ,
446
+ ) )
364
447
}
365
448
} ,
366
449
_ => {
367
- println ! ( "cannot parse {:?} attribute: {:?}" , path, nested) ;
450
+ return Err ( syn:: Error :: new_spanned (
451
+ nested. first ( ) . unwrap ( ) ,
452
+ "expected ident or string literal for property name" ,
453
+ ) )
368
454
}
369
- }
455
+ } ;
370
456
} else if path. is_ident ( "args" ) {
371
457
let attrs = PyFunctionAttr :: from_meta ( nested) ?;
372
- spec . extend ( attrs. arguments )
458
+ args . extend ( attrs. arguments )
373
459
} else {
374
460
new_attrs. push ( attr. clone ( ) )
375
461
}
376
462
}
377
463
syn:: Meta :: NameValue ( _) => new_attrs. push ( attr. clone ( ) ) ,
378
464
}
379
465
}
466
+
380
467
attrs. clear ( ) ;
381
468
attrs. extend ( new_attrs) ;
382
469
383
- match res {
384
- Some ( tp) => Ok ( ( tp, spec) ) ,
385
- None => Ok ( ( FnType :: Fn , spec) ) ,
470
+ let ty = res. unwrap_or ( FnType :: Fn ) ;
471
+ let python_name = if allow_custom_name {
472
+ parse_method_name_attribute ( & ty, attrs, property_name) ?
473
+ } else {
474
+ property_name
475
+ } ;
476
+
477
+ Ok ( MethodAttributes {
478
+ ty,
479
+ args,
480
+ python_name,
481
+ } )
482
+ }
483
+
484
+ fn parse_method_name_attribute (
485
+ ty : & FnType ,
486
+ attrs : & mut Vec < syn:: Attribute > ,
487
+ property_name : Option < syn:: Ident > ,
488
+ ) -> syn:: Result < Option < syn:: Ident > > {
489
+ let name = parse_name_attribute ( attrs) ?;
490
+
491
+ // Reject some invalid combinations
492
+ if let Some ( name) = & name {
493
+ match ty {
494
+ FnType :: FnNew | FnType :: FnCall | FnType :: Getter | FnType :: Setter => {
495
+ return Err ( syn:: Error :: new_spanned (
496
+ name,
497
+ "name not allowed with this attribute" ,
498
+ ) )
499
+ }
500
+ _ => { }
501
+ }
386
502
}
503
+
504
+ // Thanks to check above we can be sure that this generates the right python name
505
+ Ok ( match ty {
506
+ FnType :: FnNew => Some ( syn:: Ident :: new ( "__new__" , proc_macro2:: Span :: call_site ( ) ) ) ,
507
+ FnType :: FnCall => Some ( syn:: Ident :: new ( "__call__" , proc_macro2:: Span :: call_site ( ) ) ) ,
508
+ FnType :: Getter | FnType :: Setter => property_name,
509
+ _ => name,
510
+ } )
387
511
}
388
512
389
513
// Replace A<Self> with A<_>
0 commit comments