@@ -442,6 +442,76 @@ def _read_warns_section(
442
442
return DocstringSectionWarns (warns ), new_offset
443
443
444
444
445
+ def _read_block_items_maybe (
446
+ docstring : Docstring ,
447
+ * ,
448
+ offset : int ,
449
+ multiple : bool = True ,
450
+ ** options : Any ,
451
+ ) -> _ItemsBlock :
452
+ if multiple :
453
+ return _read_block_items (docstring , offset = offset , ** options )
454
+ one_block , new_offset = _read_block (docstring , offset = offset , ** options )
455
+ return [(new_offset , one_block .splitlines ())], new_offset
456
+
457
+
458
+ def _get_name_annotation_description (
459
+ docstring : Docstring ,
460
+ line_number : int ,
461
+ lines : list [str ],
462
+ * ,
463
+ named : bool = True ,
464
+ ) -> tuple [str | None , Any , str ]:
465
+ if named :
466
+ match = _RE_NAME_ANNOTATION_DESCRIPTION .match (lines [0 ])
467
+ if not match :
468
+ docstring_warning (
469
+ docstring ,
470
+ line_number ,
471
+ f"Failed to get name, annotation or description from '{ lines [0 ]} '" ,
472
+ )
473
+ raise ValueError
474
+ name , annotation , description = match .groups ()
475
+ else :
476
+ name = None
477
+ if ":" in lines [0 ]:
478
+ annotation , description = lines [0 ].split (":" , 1 )
479
+ annotation = annotation .lstrip ("(" ).rstrip (")" )
480
+ else :
481
+ annotation = None
482
+ description = lines [0 ]
483
+ description = "\n " .join ([description .lstrip (), * lines [1 :]]).rstrip ("\n " )
484
+ return name , annotation , description
485
+
486
+
487
+ def _unpack_generators (
488
+ annotation : Any ,
489
+ generator_pos : int ,
490
+ * ,
491
+ mandatory : bool = False ,
492
+ ) -> Any :
493
+ if annotation .is_generator :
494
+ return annotation .slice .elements [generator_pos ]
495
+ if annotation .is_iterator :
496
+ return annotation .slice
497
+ if mandatory :
498
+ raise ValueError (f"must be a Generator: { annotation !r} " )
499
+ return annotation
500
+
501
+
502
+ def _maybe_destructure_annotation (
503
+ annotation : Any ,
504
+ index : int ,
505
+ * ,
506
+ multiple : bool = True ,
507
+ ) -> Any :
508
+ if isinstance (annotation , ExprName ):
509
+ return annotation
510
+ if multiple and annotation .is_tuple :
511
+ return annotation .slice .elements [index ]
512
+ return annotation
513
+
514
+
445
515
def _read_returns_section (
446
516
docstring : Docstring ,
447
517
* ,
@@ -452,32 +522,23 @@ def _read_returns_section(
452
522
) -> tuple [DocstringSectionReturns | None , int ]:
453
523
returns = []
454
524
455
- if returns_multiple_items :
456
- block , new_offset = _read_block_items (docstring , offset = offset , ** options )
457
- else :
458
- one_block , new_offset = _read_block (docstring , offset = offset , ** options )
459
- block = [(new_offset , one_block .splitlines ())]
525
+ block , new_offset = _read_block_items_maybe (
526
+ docstring ,
527
+ offset = offset ,
528
+ multiple = returns_multiple_items ,
529
+ ** options ,
530
+ )
460
531
461
532
for index , (line_number , return_lines ) in enumerate (block ):
462
- if returns_named_value :
463
- match = _RE_NAME_ANNOTATION_DESCRIPTION .match (return_lines [0 ])
464
- if not match :
465
- docstring_warning (
466
- docstring ,
467
- line_number ,
468
- f"Failed to get name, annotation or description from '{ return_lines [0 ]} '" ,
469
- )
470
- continue
471
- name , annotation , description = match .groups ()
472
- else :
473
- name = None
474
- if ":" in return_lines [0 ]:
475
- annotation , description = return_lines [0 ].split (":" , 1 )
476
- annotation = annotation .lstrip ("(" ).rstrip (")" )
477
- else :
478
- annotation = None
479
- description = return_lines [0 ]
480
- description = "\n " .join ([description .lstrip (), * return_lines [1 :]]).rstrip ("\n " )
533
+ try :
534
+ name , annotation , description = _get_name_annotation_description (
535
+ docstring ,
536
+ line_number ,
537
+ return_lines ,
538
+ named = returns_named_value ,
539
+ )
540
+ except ValueError :
541
+ continue
481
542
482
543
if annotation :
483
544
# try to compile the annotation to transform it into an expression
@@ -491,22 +552,11 @@ def _read_returns_section(
491
552
annotation = docstring .parent .annotation # type: ignore[union-attr]
492
553
else :
493
554
raise ValueError
494
- if len (block ) > 1 :
495
- if annotation .is_tuple :
496
- annotation = annotation .slice .elements [index ]
497
- else :
498
- if annotation .is_iterator :
499
- return_item = annotation .slice
500
- elif annotation .is_generator :
501
- return_item = annotation .slice .elements [2 ]
502
- else :
503
- raise ValueError
504
- if isinstance (return_item , ExprName ):
505
- annotation = return_item
506
- elif return_item .is_tuple :
507
- annotation = return_item .slice .elements [index ]
508
- else :
509
- annotation = return_item
555
+ annotation = _maybe_destructure_annotation (
556
+ _unpack_generators (annotation , 2 ),
557
+ index ,
558
+ multiple = returns_multiple_items ,
559
+ )
510
560
511
561
if annotation is None :
512
562
returned_value = repr (name ) if name else index + 1
@@ -527,32 +577,23 @@ def _read_yields_section(
527
577
) -> tuple [DocstringSectionYields | None , int ]:
528
578
yields = []
529
579
530
- if returns_multiple_items :
531
- block , new_offset = _read_block_items (docstring , offset = offset , ** options )
532
- else :
533
- one_block , new_offset = _read_block (docstring , offset = offset , ** options )
534
- block = [(new_offset , one_block .splitlines ())]
580
+ block , new_offset = _read_block_items_maybe (
581
+ docstring ,
582
+ offset = offset ,
583
+ multiple = returns_multiple_items ,
584
+ ** options ,
585
+ )
535
586
536
587
for index , (line_number , yield_lines ) in enumerate (block ):
537
- if returns_named_value :
538
- match = _RE_NAME_ANNOTATION_DESCRIPTION .match (yield_lines [0 ])
539
- if not match :
540
- docstring_warning (
541
- docstring ,
542
- line_number ,
543
- f"Failed to get name, annotation or description from '{ yield_lines [0 ]} '" ,
544
- )
545
- continue
546
- name , annotation , description = match .groups ()
547
- else :
548
- name = None
549
- if ":" in yield_lines [0 ]:
550
- annotation , description = yield_lines [0 ].split (":" , 1 )
551
- annotation = annotation .lstrip ("(" ).rstrip (")" )
552
- else :
553
- annotation = None
554
- description = yield_lines [0 ]
555
- description = "\n " .join ([description .lstrip (), * yield_lines [1 :]]).rstrip ("\n " )
588
+ try :
589
+ name , annotation , description = _get_name_annotation_description (
590
+ docstring ,
591
+ line_number ,
592
+ yield_lines ,
593
+ named = returns_named_value ,
594
+ )
595
+ except ValueError :
596
+ continue
556
597
557
598
if annotation :
558
599
# try to compile the annotation to transform it into an expression
@@ -561,18 +602,11 @@ def _read_yields_section(
561
602
# try to retrieve the annotation from the docstring parent
562
603
with suppress (AttributeError , IndexError , KeyError , ValueError ):
563
604
annotation = docstring .parent .annotation # type: ignore[union-attr]
564
- if annotation .is_iterator :
565
- yield_item = annotation .slice
566
- elif annotation .is_generator :
567
- yield_item = annotation .slice .elements [0 ]
568
- else :
569
- raise ValueError
570
- if isinstance (yield_item , ExprName ):
571
- annotation = yield_item
572
- elif yield_item .is_tuple and returns_multiple_items :
573
- annotation = yield_item .slice .elements [index ]
574
- else :
575
- annotation = yield_item
605
+ annotation = _maybe_destructure_annotation (
606
+ _unpack_generators (annotation , 0 , mandatory = True ),
607
+ index ,
608
+ multiple = returns_multiple_items ,
609
+ )
576
610
577
611
if annotation is None :
578
612
yielded_value = repr (name ) if name else index + 1
@@ -593,32 +627,23 @@ def _read_receives_section(
593
627
) -> tuple [DocstringSectionReceives | None , int ]:
594
628
receives = []
595
629
596
- if receives_multiple_items :
597
- block , new_offset = _read_block_items (docstring , offset = offset , ** options )
598
- else :
599
- one_block , new_offset = _read_block (docstring , offset = offset , ** options )
600
- block = [(new_offset , one_block .splitlines ())]
630
+ block , new_offset = _read_block_items_maybe (
631
+ docstring ,
632
+ offset = offset ,
633
+ multiple = receives_multiple_items ,
634
+ ** options ,
635
+ )
601
636
602
637
for index , (line_number , receive_lines ) in enumerate (block ):
603
- if receives_multiple_items :
604
- match = _RE_NAME_ANNOTATION_DESCRIPTION .match (receive_lines [0 ])
605
- if not match :
606
- docstring_warning (
607
- docstring ,
608
- line_number ,
609
- f"Failed to get name, annotation or description from '{ receive_lines [0 ]} '" ,
610
- )
611
- continue
612
- name , annotation , description = match .groups ()
613
- else :
614
- name = None
615
- if ":" in receive_lines [0 ]:
616
- annotation , description = receive_lines [0 ].split (":" , 1 )
617
- annotation = annotation .lstrip ("(" ).rstrip (")" )
618
- else :
619
- annotation = None
620
- description = receive_lines [0 ]
621
- description = "\n " .join ([description .lstrip (), * receive_lines [1 :]]).rstrip ("\n " )
638
+ try :
639
+ name , annotation , description = _get_name_annotation_description (
640
+ docstring ,
641
+ line_number ,
642
+ receive_lines ,
643
+ named = receives_named_value ,
644
+ )
645
+ except ValueError :
646
+ continue
622
647
623
648
if annotation :
624
649
# try to compile the annotation to transform it into an expression
@@ -627,14 +652,11 @@ def _read_receives_section(
627
652
# try to retrieve the annotation from the docstring parent
628
653
with suppress (AttributeError , KeyError ):
629
654
annotation = docstring .parent .returns # type: ignore[union-attr]
630
- if annotation .is_generator :
631
- receives_item = annotation .slice .elements [1 ]
632
- if isinstance (receives_item , ExprName ):
633
- annotation = receives_item
634
- elif receives_item .is_tuple and receives_multiple_items :
635
- annotation = receives_item .slice .elements [index ]
636
- else :
637
- annotation = receives_item
655
+ annotation = _maybe_destructure_annotation (
656
+ _unpack_generators (annotation , 1 , mandatory = True ),
657
+ index ,
658
+ multiple = receives_multiple_items ,
659
+ )
638
660
639
661
if annotation is None :
640
662
received_value = repr (name ) if name else index + 1
0 commit comments