11use crate :: {
22 attributes:: { kw, KeywordAttribute } ,
3- method:: { FnArg , RegularArg } ,
3+ method:: FnArg ,
4+ utils:: expr_to_python,
45} ;
56use proc_macro2:: { Span , TokenStream } ;
67use quote:: ToTokens ;
@@ -9,7 +10,7 @@ use syn::{
910 parse:: { Parse , ParseStream } ,
1011 punctuated:: Punctuated ,
1112 spanned:: Spanned ,
12- Token ,
13+ Expr , Token ,
1314} ;
1415
1516#[ derive( Clone ) ]
@@ -270,10 +271,11 @@ impl ConstructorAttribute {
270271pub struct PythonSignature {
271272 pub positional_parameters : Vec < String > ,
272273 pub positional_only_parameters : usize ,
273- pub required_positional_parameters : usize ,
274+ /// Vector of expressions representing positional defaults
275+ pub default_positional_parameters : Vec < Expr > ,
274276 pub varargs : Option < String > ,
275- // Tuples of keyword name and whether it is required
276- pub keyword_only_parameters : Vec < ( String , bool ) > ,
277+ // Tuples of keyword name and optional default value
278+ pub keyword_only_parameters : Vec < ( String , Option < Expr > ) > ,
277279 pub kwargs : Option < String > ,
278280}
279281
@@ -284,6 +286,13 @@ impl PythonSignature {
284286 && self . varargs . is_none ( )
285287 && self . kwargs . is_none ( )
286288 }
289+
290+ pub fn required_positional_parameters ( & self ) -> usize {
291+ self . positional_parameters
292+ . len ( )
293+ . checked_sub ( self . default_positional_parameters . len ( ) )
294+ . expect ( "should always have positional defaults <= positional parameters" )
295+ }
287296}
288297
289298#[ derive( Clone ) ]
@@ -309,23 +318,24 @@ impl ParseState {
309318 & mut self ,
310319 signature : & mut PythonSignature ,
311320 name : String ,
312- required : bool ,
321+ default_value : Option < Expr > ,
313322 span : Span ,
314323 ) -> syn:: Result < ( ) > {
315324 match self {
316325 ParseState :: Positional | ParseState :: PositionalAfterPosargs => {
317326 signature. positional_parameters . push ( name) ;
318- if required {
319- signature. required_positional_parameters += 1 ;
320- ensure_spanned ! (
321- signature. required_positional_parameters == signature. positional_parameters. len( ) ,
322- span => "cannot have required positional parameter after an optional parameter"
323- ) ;
327+ if let Some ( default_value) = default_value {
328+ signature. default_positional_parameters . push ( default_value) ;
329+ // Now all subsequent positional parameters must also have defaults
330+ } else if !signature. default_positional_parameters . is_empty ( ) {
331+ bail_spanned ! ( span => "cannot have required positional parameter after an optional parameter" )
324332 }
325333 Ok ( ( ) )
326334 }
327335 ParseState :: Keywords => {
328- signature. keyword_only_parameters . push ( ( name, required) ) ;
336+ signature
337+ . keyword_only_parameters
338+ . push ( ( name, default_value) ) ;
329339 Ok ( ( ) )
330340 }
331341 ParseState :: Done => {
@@ -475,7 +485,9 @@ impl<'a> FunctionSignature<'a> {
475485 parse_state. add_argument (
476486 & mut python_signature,
477487 arg. ident . unraw ( ) . to_string ( ) ,
478- arg. eq_and_default . is_none ( ) ,
488+ arg. eq_and_default
489+ . as_ref ( )
490+ . map ( |( _, default) | default. clone ( ) ) ,
479491 arg. span ( ) ,
480492 ) ?;
481493 let FnArg :: Regular ( fn_arg) = fn_arg else {
@@ -577,17 +589,6 @@ impl<'a> FunctionSignature<'a> {
577589 continue ;
578590 }
579591
580- if let FnArg :: Regular ( RegularArg { .. } ) = arg {
581- // This argument is required, all previous arguments must also have been required
582- assert_eq ! (
583- python_signature. required_positional_parameters,
584- python_signature. positional_parameters. len( ) ,
585- ) ;
586-
587- python_signature. required_positional_parameters =
588- python_signature. positional_parameters . len ( ) + 1 ;
589- }
590-
591592 python_signature
592593 . positional_parameters
593594 . push ( arg. name ( ) . unraw ( ) . to_string ( ) ) ;
@@ -600,14 +601,6 @@ impl<'a> FunctionSignature<'a> {
600601 }
601602 }
602603
603- fn default_value_for_parameter ( & self , parameter : & str ) -> String {
604- if let Some ( fn_arg) = self . arguments . iter ( ) . find ( |arg| arg. name ( ) == parameter) {
605- fn_arg. default_value ( )
606- } else {
607- "..." . to_string ( )
608- }
609- }
610-
611604 pub fn text_signature ( & self , self_argument : Option < & str > ) -> String {
612605 let mut output = String :: new ( ) ;
613606 output. push ( '(' ) ;
@@ -630,14 +623,19 @@ impl<'a> FunctionSignature<'a> {
630623
631624 let py_sig = & self . python_signature ;
632625
633- for ( i, parameter) in py_sig. positional_parameters . iter ( ) . enumerate ( ) {
626+ let defaults = std:: iter:: repeat_n ( None , py_sig. required_positional_parameters ( ) )
627+ . chain ( py_sig. default_positional_parameters . iter ( ) . map ( Some ) ) ;
628+
629+ for ( i, ( parameter, default) ) in
630+ std:: iter:: zip ( & py_sig. positional_parameters , defaults) . enumerate ( )
631+ {
634632 maybe_push_comma ( & mut output) ;
635633
636634 output. push_str ( parameter) ;
637635
638- if i >= py_sig . required_positional_parameters {
636+ if let Some ( expr ) = default {
639637 output. push ( '=' ) ;
640- output. push_str ( & self . default_value_for_parameter ( parameter ) ) ;
638+ output. push_str ( & expr_to_python ( expr ) ) ;
641639 }
642640
643641 if py_sig. positional_only_parameters > 0 && i + 1 == py_sig. positional_only_parameters {
@@ -654,12 +652,12 @@ impl<'a> FunctionSignature<'a> {
654652 output. push ( '*' ) ;
655653 }
656654
657- for ( parameter, required ) in & py_sig. keyword_only_parameters {
655+ for ( parameter, default ) in & py_sig. keyword_only_parameters {
658656 maybe_push_comma ( & mut output) ;
659657 output. push_str ( parameter) ;
660- if !required {
658+ if let Some ( expr ) = default {
661659 output. push ( '=' ) ;
662- output. push_str ( & self . default_value_for_parameter ( parameter ) ) ;
660+ output. push_str ( & expr_to_python ( expr ) ) ;
663661 }
664662 }
665663
0 commit comments