368
368
// $
369
369
//
370
370
// PyArg_ParseTupleAndKeywords() only: Indicates that the remaining
371
- // arguments in the Python argument list are keyword-only. Currently,
372
- // all keyword-only arguments must also be optional arguments, so |
373
- // must always be specified before $ in the format string.
371
+ // arguments in the Python argument list are keyword-only.
374
372
//
375
373
// New in version 3.3.
376
374
//
@@ -416,17 +414,13 @@ func ParseTupleAndKeywords(args Tuple, kwargs StringDict, format string, kwlist
416
414
if kwlist != nil && len (results ) != len (kwlist ) {
417
415
return ExceptionNewf (TypeError , "Internal error: supply the same number of results and kwlist" )
418
416
}
419
- min , max , name , ops := parseFormat ( format )
420
- keywordOnly := false
421
- err := checkNumberOfArgs (name , len (args )+ len (kwargs ), len (results ), min , max )
417
+ var opsBuf [ 16 ] formatOp
418
+ min , name , kwOnly_i , ops := parseFormat ( format , opsBuf [: 0 ])
419
+ err := checkNumberOfArgs (name , len (args )+ len (kwargs ), len (results ), min , len ( ops ) )
422
420
if err != nil {
423
421
return err
424
422
}
425
423
426
- if len (ops ) > 0 && ops [0 ] == "$" {
427
- keywordOnly = true
428
- ops = ops [1 :]
429
- }
430
424
// Check all the kwargs are in kwlist
431
425
// O(N^2) Slow but kwlist is usually short
432
426
for kwargName := range kwargs {
@@ -439,46 +433,60 @@ func ParseTupleAndKeywords(args Tuple, kwargs StringDict, format string, kwlist
439
433
found:
440
434
}
441
435
442
- // Create args tuple with all the arguments we have in
443
- args = args .Copy ()
444
- for i , kw := range kwlist {
445
- if value , ok := kwargs [kw ]; ok {
446
- if len (args ) > i {
436
+ // Walk through all the results we want
437
+ for i , op := range ops {
438
+
439
+ var (
440
+ arg Object
441
+ kw string
442
+ )
443
+ if i < len (kwlist ) {
444
+ kw = kwlist [i ]
445
+ arg = kwargs [kw ]
446
+ }
447
+
448
+ // Consume ordered args first -- they should not require keyword only or also be specified via keyword
449
+ if i < len (args ) {
450
+ if i >= kwOnly_i {
451
+ return ExceptionNewf (TypeError , "%s() specifies argument '%s' that is keyword only" , name , kw )
452
+ }
453
+ if arg != nil {
447
454
return ExceptionNewf (TypeError , "%s() got multiple values for argument '%s'" , name , kw )
448
455
}
449
- args = append (args , value )
450
- } else if keywordOnly {
451
- args = append (args , nil )
456
+ arg = args [i ]
452
457
}
453
- }
454
- for i , arg := range args {
455
- op := ops [i ]
458
+
459
+ // Unspecified args retain their default value
460
+ if arg == nil {
461
+ continue
462
+ }
463
+
456
464
result := results [i ]
457
- switch op {
458
- case "O" :
465
+ switch op . code {
466
+ case 'O' :
459
467
* result = arg
460
- case "Z" , "z" :
468
+ case 'Z' , 'z' :
461
469
if _ , ok := arg .(NoneType ); ok {
462
470
* result = arg
463
471
break
464
472
}
465
473
fallthrough
466
- case "U" , "s" :
474
+ case 'U' , 's' :
467
475
if _ , ok := arg .(String ); ! ok {
468
476
return ExceptionNewf (TypeError , "%s() argument %d must be str, not %s" , name , i + 1 , arg .Type ().Name )
469
477
}
470
478
* result = arg
471
- case "i" :
479
+ case 'i' :
472
480
if _ , ok := arg .(Int ); ! ok {
473
481
return ExceptionNewf (TypeError , "%s() argument %d must be int, not %s" , name , i + 1 , arg .Type ().Name )
474
482
}
475
483
* result = arg
476
- case "p" :
484
+ case 'p' :
477
485
if _ , ok := arg .(Bool ); ! ok {
478
486
return ExceptionNewf (TypeError , "%s() argument %d must be bool, not %s" , name , i + 1 , arg .Type ().Name )
479
487
}
480
488
* result = arg
481
- case "d" :
489
+ case 'd' :
482
490
switch x := arg .(type ) {
483
491
case Int :
484
492
* result = Float (x )
@@ -500,30 +508,42 @@ func ParseTuple(args Tuple, format string, results ...*Object) error {
500
508
return ParseTupleAndKeywords (args , nil , format , nil , results ... )
501
509
}
502
510
511
+ type formatOp struct {
512
+ code byte
513
+ modifier byte
514
+ }
515
+
503
516
// Parse the format
504
- func parseFormat (format string ) (min , max int , name string , ops []string ) {
517
+ func parseFormat (format string , in [] formatOp ) (min int , name string , kwOnly_i int , ops []formatOp ) {
505
518
name = "function"
506
519
min = - 1
507
- for format != "" {
508
- op := string (format [0 ])
509
- format = format [1 :]
510
- if len (format ) > 1 && (format [1 ] == '*' || format [1 ] == '#' ) {
511
- op += string (format [0 ])
512
- format = format [1 :]
520
+ kwOnly_i = 0xFFFF
521
+ ops = in [:0 ]
522
+
523
+ N := len (format )
524
+ for i := 0 ; i < N ; {
525
+ op := formatOp {code : format [i ]}
526
+ i ++
527
+ if i < N {
528
+ if mod := format [i ]; mod == '*' || mod == '#' {
529
+ op .modifier = mod
530
+ i ++
531
+ }
513
532
}
514
- switch op {
515
- case ":" , ";" :
516
- name = format
517
- format = ""
518
- case "|" :
533
+ switch op .code {
534
+ case ':' , ';' :
535
+ name = format [i :]
536
+ i = N
537
+ case '$' :
538
+ kwOnly_i = len (ops )
539
+ case '|' :
519
540
min = len (ops )
520
541
default :
521
542
ops = append (ops , op )
522
543
}
523
544
}
524
- max = len (ops )
525
545
if min < 0 {
526
- min = max
546
+ min = len ( ops )
527
547
}
528
548
return
529
549
}
0 commit comments