Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pip/_internal/exceptions/_base.py: 27%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

55 statements  

1"""Base pip exception classes.""" 

2 

3from __future__ import annotations 

4 

5import re 

6from typing import TYPE_CHECKING, Literal 

7 

8from pip._vendor.rich.text import Text 

9 

10if TYPE_CHECKING: 

11 from pip._vendor.rich.console import Console, ConsoleOptions, RenderResult 

12 

13 

14def _is_kebab_case(s: str) -> bool: 

15 return re.match(r"^[a-z]+(-[a-z]+)*$", s) is not None 

16 

17 

18def _prefix_with_indent( 

19 s: Text | str, 

20 console: Console, 

21 *, 

22 prefix: str, 

23 indent: str, 

24) -> Text: 

25 if isinstance(s, Text): 

26 text = s 

27 else: 

28 text = console.render_str(s) 

29 

30 return console.render_str(prefix, overflow="ignore") + console.render_str( 

31 f"\n{indent}", overflow="ignore" 

32 ).join(text.split(allow_blank=True)) 

33 

34 

35class PipError(Exception): 

36 """The base pip error.""" 

37 

38 

39class InstallationError(PipError): 

40 """General exception during installation""" 

41 

42 

43class DiagnosticPipError(PipError): 

44 """An error, that presents diagnostic information to the user. 

45 

46 This contains a bunch of logic, to enable pretty presentation of our error 

47 messages. Each error gets a unique reference. Each error can also include 

48 additional context, a hint and/or a note -- which are presented with the 

49 main error message in a consistent style. 

50 

51 This is adapted from the error output styling in `sphinx-theme-builder`. 

52 """ 

53 

54 reference: str 

55 

56 def __init__( 

57 self, 

58 *, 

59 kind: Literal["error", "warning"] = "error", 

60 reference: str | None = None, 

61 message: str | Text, 

62 context: str | Text | None, 

63 hint_stmt: str | Text | None, 

64 note_stmt: str | Text | None = None, 

65 link: str | None = None, 

66 ) -> None: 

67 # Ensure a proper reference is provided. 

68 if reference is None: 

69 assert hasattr(self, "reference"), "error reference not provided!" 

70 reference = self.reference 

71 assert _is_kebab_case(reference), "error reference must be kebab-case!" 

72 

73 self.kind = kind 

74 self.reference = reference 

75 

76 self.message = message 

77 self.context = context 

78 

79 self.note_stmt = note_stmt 

80 self.hint_stmt = hint_stmt 

81 

82 self.link = link 

83 

84 super().__init__(f"<{self.__class__.__name__}: {self.reference}>") 

85 

86 def __repr__(self) -> str: 

87 return ( 

88 f"<{self.__class__.__name__}(" 

89 f"reference={self.reference!r}, " 

90 f"message={self.message!r}, " 

91 f"context={self.context!r}, " 

92 f"note_stmt={self.note_stmt!r}, " 

93 f"hint_stmt={self.hint_stmt!r}" 

94 ")>" 

95 ) 

96 

97 def __rich_console__( 

98 self, 

99 console: Console, 

100 options: ConsoleOptions, 

101 ) -> RenderResult: 

102 colour = "red" if self.kind == "error" else "yellow" 

103 

104 yield f"[{colour} bold]{self.kind}[/]: [bold]{self.reference}[/]" 

105 yield "" 

106 

107 if not options.ascii_only: 

108 # Present the main message, with relevant context indented. 

109 if self.context is not None: 

110 yield _prefix_with_indent( 

111 self.message, 

112 console, 

113 prefix=f"[{colour}]×[/] ", 

114 indent=f"[{colour}]│[/] ", 

115 ) 

116 yield _prefix_with_indent( 

117 self.context, 

118 console, 

119 prefix=f"[{colour}]╰─>[/] ", 

120 indent=f"[{colour}] [/] ", 

121 ) 

122 else: 

123 yield _prefix_with_indent( 

124 self.message, 

125 console, 

126 prefix="[red]×[/] ", 

127 indent=" ", 

128 ) 

129 else: 

130 yield self.message 

131 if self.context is not None: 

132 yield "" 

133 yield self.context 

134 

135 if self.note_stmt is not None or self.hint_stmt is not None: 

136 yield "" 

137 

138 if self.note_stmt is not None: 

139 yield _prefix_with_indent( 

140 self.note_stmt, 

141 console, 

142 prefix="[magenta bold]note[/]: ", 

143 indent=" ", 

144 ) 

145 if self.hint_stmt is not None: 

146 yield _prefix_with_indent( 

147 self.hint_stmt, 

148 console, 

149 prefix="[cyan bold]hint[/]: ", 

150 indent=" ", 

151 ) 

152 

153 if self.link is not None: 

154 yield "" 

155 yield f"Link: {self.link}"