3737)
3838from griffe .docstrings .utils import parse_annotation , warning
3939from griffe .expressions import ExprName
40+ from griffe .logger import LogLevel
4041
4142if TYPE_CHECKING :
4243 from typing import Any , Literal , Pattern
@@ -245,12 +246,7 @@ def _read_parameters_section(
245246 ** options : Any ,
246247) -> tuple [DocstringSectionParameters | None , int ]:
247248 parameters , new_offset = _read_parameters (docstring , offset = offset , ** options )
248-
249- if parameters :
250- return DocstringSectionParameters (parameters ), new_offset
251-
252- _warn (docstring , new_offset , f"Empty parameters section at line { offset } " )
253- return None , new_offset
249+ return DocstringSectionParameters (parameters ), new_offset
254250
255251
256252def _read_other_parameters_section (
@@ -261,12 +257,7 @@ def _read_other_parameters_section(
261257 ** options : Any ,
262258) -> tuple [DocstringSectionOtherParameters | None , int ]:
263259 parameters , new_offset = _read_parameters (docstring , offset = offset , warn_unknown_params = False , ** options )
264-
265- if parameters :
266- return DocstringSectionOtherParameters (parameters ), new_offset
267-
268- _warn (docstring , new_offset , f"Empty other parameters section at line { offset } " )
269- return None , new_offset
260+ return DocstringSectionOtherParameters (parameters ), new_offset
270261
271262
272263def _read_attributes_section (
@@ -302,11 +293,7 @@ def _read_attributes_section(
302293
303294 attributes .append (DocstringAttribute (name = name , annotation = annotation , description = description ))
304295
305- if attributes :
306- return DocstringSectionAttributes (attributes ), new_offset
307-
308- _warn (docstring , new_offset , f"Empty attributes section at line { offset } " )
309- return None , new_offset
296+ return DocstringSectionAttributes (attributes ), new_offset
310297
311298
312299def _read_functions_section (
@@ -337,11 +324,7 @@ def _read_functions_section(
337324
338325 functions .append (DocstringFunction (name = name , annotation = signature , description = description ))
339326
340- if functions :
341- return DocstringSectionFunctions (functions ), new_offset
342-
343- _warn (docstring , new_offset , f"Empty functions/methods section at line { offset } " )
344- return None , new_offset
327+ return DocstringSectionFunctions (functions ), new_offset
345328
346329
347330def _read_classes_section (
@@ -372,11 +355,7 @@ def _read_classes_section(
372355
373356 classes .append (DocstringClass (name = name , annotation = signature , description = description ))
374357
375- if classes :
376- return DocstringSectionClasses (classes ), new_offset
377-
378- _warn (docstring , new_offset , f"Empty classes section at line { offset } " )
379- return None , new_offset
358+ return DocstringSectionClasses (classes ), new_offset
380359
381360
382361def _read_modules_section (
@@ -397,11 +376,7 @@ def _read_modules_section(
397376 description = "\n " .join ([description .lstrip (), * module_lines [1 :]]).rstrip ("\n " )
398377 modules .append (DocstringModule (name = name , description = description ))
399378
400- if modules :
401- return DocstringSectionModules (modules ), new_offset
402-
403- _warn (docstring , new_offset , f"Empty modules section at line { offset } " )
404- return None , new_offset
379+ return DocstringSectionModules (modules ), new_offset
405380
406381
407382def _read_raises_section (
@@ -425,11 +400,7 @@ def _read_raises_section(
425400 annotation = parse_annotation (annotation , docstring )
426401 exceptions .append (DocstringRaise (annotation = annotation , description = description ))
427402
428- if exceptions :
429- return DocstringSectionRaises (exceptions ), new_offset
430-
431- _warn (docstring , new_offset , f"Empty exceptions section at line { offset } " )
432- return None , new_offset
403+ return DocstringSectionRaises (exceptions ), new_offset
433404
434405
435406def _read_warns_section (
@@ -450,11 +421,7 @@ def _read_warns_section(
450421 description = "\n " .join ([description .lstrip (), * warning_lines [1 :]]).rstrip ("\n " )
451422 warns .append (DocstringWarn (annotation = annotation , description = description ))
452423
453- if warns :
454- return DocstringSectionWarns (warns ), new_offset
455-
456- _warn (docstring , new_offset , f"Empty warns section at line { offset } " )
457- return None , new_offset
424+ return DocstringSectionWarns (warns ), new_offset
458425
459426
460427def _read_returns_section (
@@ -516,11 +483,7 @@ def _read_returns_section(
516483
517484 returns .append (DocstringReturn (name = name or "" , annotation = annotation , description = description ))
518485
519- if returns :
520- return DocstringSectionReturns (returns ), new_offset
521-
522- _warn (docstring , new_offset , f"Empty returns section at line { offset } " )
523- return None , new_offset
486+ return DocstringSectionReturns (returns ), new_offset
524487
525488
526489def _read_yields_section (
@@ -567,11 +530,7 @@ def _read_yields_section(
567530
568531 yields .append (DocstringYield (name = name or "" , annotation = annotation , description = description ))
569532
570- if yields :
571- return DocstringSectionYields (yields ), new_offset
572-
573- _warn (docstring , new_offset , f"Empty yields section at line { offset } " )
574- return None , new_offset
533+ return DocstringSectionYields (yields ), new_offset
575534
576535
577536def _read_receives_section (
@@ -614,11 +573,7 @@ def _read_receives_section(
614573
615574 receives .append (DocstringReceive (name = name or "" , annotation = annotation , description = description ))
616575
617- if receives :
618- return DocstringSectionReceives (receives ), new_offset
619-
620- _warn (docstring , new_offset , f"Empty receives section at line { offset } " )
621- return None , new_offset
576+ return DocstringSectionReceives (receives ), new_offset
622577
623578
624579def _read_examples_section (
@@ -677,11 +632,7 @@ def _read_examples_section(
677632 elif current_example :
678633 sub_sections .append ((DocstringSectionKind .examples , "\n " .join (current_example )))
679634
680- if sub_sections :
681- return DocstringSectionExamples (sub_sections ), new_offset
682-
683- _warn (docstring , new_offset , f"Empty examples section at line { offset } " )
684- return None , new_offset
635+ return DocstringSectionExamples (sub_sections ), new_offset
685636
686637
687638def _read_deprecated_section (
@@ -692,11 +643,6 @@ def _read_deprecated_section(
692643) -> tuple [DocstringSectionDeprecated | None , int ]:
693644 text , new_offset = _read_block (docstring , offset = offset , ** options )
694645
695- # early exit if there is no text in the yield section
696- if not text :
697- _warn (docstring , new_offset , f"Empty deprecated section at line { offset } " )
698- return None , new_offset
699-
700646 # check the presence of a name and description, separated by a semi-colon
701647 try :
702648 version , text = text .split (":" , 1 )
@@ -733,6 +679,8 @@ def _is_empty_line(line: str) -> bool:
733679 DocstringSectionKind .deprecated : _read_deprecated_section ,
734680}
735681
682+ _sentinel = object ()
683+
736684
737685def parse (
738686 docstring : Docstring ,
@@ -800,7 +748,41 @@ def parse(
800748 groups = match .groupdict ()
801749 title = groups ["title" ]
802750 admonition_type = groups ["type" ]
803- if admonition_type .lower () in _section_kind :
751+ is_section = admonition_type .lower () in _section_kind
752+
753+ has_previous_line = offset > 0
754+ blank_line_above = not has_previous_line or _is_empty_line (lines [offset - 1 ])
755+ has_next_line = offset < len (lines ) - 1
756+ has_next_lines = offset < len (lines ) - 2
757+ blank_line_below = has_next_line and _is_empty_line (lines [offset + 1 ])
758+ blank_lines_below = has_next_lines and _is_empty_line (lines [offset + 2 ])
759+ indented_line_below = has_next_line and not blank_line_below and lines [offset + 1 ].startswith (" " )
760+ indented_lines_below = has_next_lines and not blank_lines_below and lines [offset + 2 ].startswith (" " )
761+ if not (indented_line_below or indented_lines_below ):
762+ # Do not warn when there are no contents,
763+ # this is most probably not a section or admonition.
764+ current_section .append (lines [offset ])
765+ offset += 1
766+ continue
767+ reasons = []
768+ kind = "section" if is_section else "admonition"
769+ if (indented_line_below or indented_lines_below ) and not blank_line_above :
770+ reasons .append (f"Missing blank line above { kind } " )
771+ if indented_lines_below and blank_line_below :
772+ reasons .append (f"Extraneous blank line below { kind } title" )
773+ if reasons :
774+ reasons_string = "; " .join (reasons )
775+ _warn (
776+ docstring ,
777+ offset ,
778+ f"Possible { kind } skipped, reasons: { reasons_string } " ,
779+ LogLevel .debug ,
780+ )
781+ current_section .append (lines [offset ])
782+ offset += 1
783+ continue
784+
785+ if is_section :
804786 if current_section :
805787 if any (current_section ):
806788 sections .append (DocstringSectionText ("\n " .join (current_section ).rstrip ("\n " )))
0 commit comments