Coverage for src/_griffe/docstrings/utils.py: 87.88%

26 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-15 16:47 +0200

1# This module contains utilities for docstrings parsers. 

2 

3from __future__ import annotations 

4 

5from ast import PyCF_ONLY_AST 

6from contextlib import suppress 

7from typing import TYPE_CHECKING 

8 

9from _griffe.enumerations import LogLevel 

10from _griffe.exceptions import BuiltinModuleError 

11from _griffe.expressions import safe_get_annotation 

12from _griffe.logger import logger 

13 

14if TYPE_CHECKING: 

15 from _griffe.expressions import Expr 

16 from _griffe.models import Docstring 

17 

18 

19def docstring_warning( 

20 docstring: Docstring, 

21 offset: int, 

22 message: str, 

23 log_level: LogLevel = LogLevel.warning, 

24) -> None: 

25 """Log a warning when parsing a docstring. 

26 

27 This function logs a warning message by prefixing it with the filepath and line number. 

28 

29 Parameters: 

30 docstring: The docstring object. 

31 offset: The offset in the docstring lines. 

32 message: The message to log. 

33 

34 Returns: 

35 A function used to log parsing warnings if `name` was passed, else none. 

36 """ 

37 

38 def warn(docstring: Docstring, offset: int, message: str, log_level: LogLevel = LogLevel.warning) -> None: 

39 try: 

40 prefix = docstring.parent.relative_filepath # type: ignore[union-attr] 

41 except (AttributeError, ValueError): 41 ↛ 43line 41 didn't jump to line 43

42 prefix = "<module>" 

43 except BuiltinModuleError: 

44 prefix = f"<module: {docstring.parent.module.name}>" # type: ignore[union-attr] 

45 log = getattr(logger, log_level.value) 

46 log(f"{prefix}:{(docstring.lineno or 0)+offset}: {message}") 

47 

48 warn(docstring, offset, message, log_level) 

49 

50 

51def parse_docstring_annotation( 

52 annotation: str, 

53 docstring: Docstring, 

54 log_level: LogLevel = LogLevel.error, 

55) -> str | Expr: 

56 """Parse a string into a true name or expression that can be resolved later. 

57 

58 Parameters: 

59 annotation: The annotation to parse. 

60 docstring: The docstring in which the annotation appears. 

61 The docstring's parent is accessed to bind a resolver to the resulting name/expression. 

62 log_level: Log level to use to log a message. 

63 

64 Returns: 

65 The string unchanged, or a new name or expression. 

66 """ 

67 with suppress( 

68 AttributeError, # docstring has no parent that can be used to resolve names 

69 SyntaxError, # annotation contains syntax errors 

70 ): 

71 code = compile(annotation, mode="eval", filename="", flags=PyCF_ONLY_AST, optimize=2) 

72 if code.body: # type: ignore[attr-defined] 72 ↛ 67line 72 didn't jump to line 67

73 name_or_expr = safe_get_annotation( 

74 code.body, # type: ignore[attr-defined] 

75 parent=docstring.parent, # type: ignore[arg-type] 

76 log_level=log_level, 

77 ) 

78 return name_or_expr or annotation 

79 return annotation