Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/rich/_inspect.py: 4%

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

142 statements  

1import inspect 

2from inspect import cleandoc, getdoc, getfile, isclass, ismodule, signature 

3from typing import Any, Collection, Iterable, Optional, Tuple, Type, Union 

4 

5from .console import Group, RenderableType 

6from .control import escape_control_codes 

7from .highlighter import ReprHighlighter 

8from .jupyter import JupyterMixin 

9from .panel import Panel 

10from .pretty import Pretty 

11from .table import Table 

12from .text import Text, TextType 

13 

14 

15def _first_paragraph(doc: str) -> str: 

16 """Get the first paragraph from a docstring.""" 

17 paragraph, _, _ = doc.partition("\n\n") 

18 return paragraph 

19 

20 

21class Inspect(JupyterMixin): 

22 """A renderable to inspect any Python Object. 

23 

24 Args: 

25 obj (Any): An object to inspect. 

26 title (str, optional): Title to display over inspect result, or None use type. Defaults to None. 

27 help (bool, optional): Show full help text rather than just first paragraph. Defaults to False. 

28 methods (bool, optional): Enable inspection of callables. Defaults to False. 

29 docs (bool, optional): Also render doc strings. Defaults to True. 

30 private (bool, optional): Show private attributes (beginning with underscore). Defaults to False. 

31 dunder (bool, optional): Show attributes starting with double underscore. Defaults to False. 

32 sort (bool, optional): Sort attributes alphabetically. Defaults to True. 

33 all (bool, optional): Show all attributes. Defaults to False. 

34 value (bool, optional): Pretty print value of object. Defaults to True. 

35 """ 

36 

37 def __init__( 

38 self, 

39 obj: Any, 

40 *, 

41 title: Optional[TextType] = None, 

42 help: bool = False, 

43 methods: bool = False, 

44 docs: bool = True, 

45 private: bool = False, 

46 dunder: bool = False, 

47 sort: bool = True, 

48 all: bool = True, 

49 value: bool = True, 

50 ) -> None: 

51 self.highlighter = ReprHighlighter() 

52 self.obj = obj 

53 self.title = title or self._make_title(obj) 

54 if all: 

55 methods = private = dunder = True 

56 self.help = help 

57 self.methods = methods 

58 self.docs = docs or help 

59 self.private = private or dunder 

60 self.dunder = dunder 

61 self.sort = sort 

62 self.value = value 

63 

64 def _make_title(self, obj: Any) -> Text: 

65 """Make a default title.""" 

66 title_str = ( 

67 str(obj) 

68 if (isclass(obj) or callable(obj) or ismodule(obj)) 

69 else str(type(obj)) 

70 ) 

71 title_text = self.highlighter(title_str) 

72 return title_text 

73 

74 def __rich__(self) -> Panel: 

75 return Panel.fit( 

76 Group(*self._render()), 

77 title=self.title, 

78 border_style="scope.border", 

79 padding=(0, 1), 

80 ) 

81 

82 def _get_signature(self, name: str, obj: Any) -> Optional[Text]: 

83 """Get a signature for a callable.""" 

84 try: 

85 _signature = str(signature(obj)) + ":" 

86 except ValueError: 

87 _signature = "(...)" 

88 except TypeError: 

89 return None 

90 

91 source_filename: Optional[str] = None 

92 try: 

93 source_filename = getfile(obj) 

94 except (OSError, TypeError): 

95 # OSError is raised if obj has no source file, e.g. when defined in REPL. 

96 pass 

97 

98 callable_name = Text(name, style="inspect.callable") 

99 if source_filename: 

100 callable_name.stylize(f"link file://{source_filename}") 

101 signature_text = self.highlighter(_signature) 

102 

103 qualname = name or getattr(obj, "__qualname__", name) 

104 

105 # If obj is a module, there may be classes (which are callable) to display 

106 if inspect.isclass(obj): 

107 prefix = "class" 

108 elif inspect.iscoroutinefunction(obj): 

109 prefix = "async def" 

110 else: 

111 prefix = "def" 

112 

113 qual_signature = Text.assemble( 

114 (f"{prefix} ", f"inspect.{prefix.replace(' ', '_')}"), 

115 (qualname, "inspect.callable"), 

116 signature_text, 

117 ) 

118 

119 return qual_signature 

120 

121 def _render(self) -> Iterable[RenderableType]: 

122 """Render object.""" 

123 

124 def sort_items(item: Tuple[str, Any]) -> Tuple[bool, str]: 

125 key, (_error, value) = item 

126 return (callable(value), key.strip("_").lower()) 

127 

128 def safe_getattr(attr_name: str) -> Tuple[Any, Any]: 

129 """Get attribute or any exception.""" 

130 try: 

131 return (None, getattr(obj, attr_name)) 

132 except Exception as error: 

133 return (error, None) 

134 

135 obj = self.obj 

136 keys = dir(obj) 

137 total_items = len(keys) 

138 if not self.dunder: 

139 keys = [key for key in keys if not key.startswith("__")] 

140 if not self.private: 

141 keys = [key for key in keys if not key.startswith("_")] 

142 not_shown_count = total_items - len(keys) 

143 items = [(key, safe_getattr(key)) for key in keys] 

144 if self.sort: 

145 items.sort(key=sort_items) 

146 

147 items_table = Table.grid(padding=(0, 1), expand=False) 

148 items_table.add_column(justify="right") 

149 add_row = items_table.add_row 

150 highlighter = self.highlighter 

151 

152 if callable(obj): 

153 signature = self._get_signature("", obj) 

154 if signature is not None: 

155 yield signature 

156 yield "" 

157 

158 if self.docs: 

159 _doc = self._get_formatted_doc(obj) 

160 if _doc is not None: 

161 doc_text = Text(_doc, style="inspect.help") 

162 doc_text = highlighter(doc_text) 

163 yield doc_text 

164 yield "" 

165 

166 if self.value and not (isclass(obj) or callable(obj) or ismodule(obj)): 

167 yield Panel( 

168 Pretty(obj, indent_guides=True, max_length=10, max_string=60), 

169 border_style="inspect.value.border", 

170 ) 

171 yield "" 

172 

173 for key, (error, value) in items: 

174 key_text = Text.assemble( 

175 ( 

176 key, 

177 "inspect.attr.dunder" if key.startswith("__") else "inspect.attr", 

178 ), 

179 (" =", "inspect.equals"), 

180 ) 

181 if error is not None: 

182 warning = key_text.copy() 

183 warning.stylize("inspect.error") 

184 add_row(warning, highlighter(repr(error))) 

185 continue 

186 

187 if callable(value): 

188 if not self.methods: 

189 continue 

190 

191 _signature_text = self._get_signature(key, value) 

192 if _signature_text is None: 

193 add_row(key_text, Pretty(value, highlighter=highlighter)) 

194 else: 

195 if self.docs: 

196 docs = self._get_formatted_doc(value) 

197 if docs is not None: 

198 _signature_text.append("\n" if "\n" in docs else " ") 

199 doc = highlighter(docs) 

200 doc.stylize("inspect.doc") 

201 _signature_text.append(doc) 

202 

203 add_row(key_text, _signature_text) 

204 else: 

205 add_row(key_text, Pretty(value, highlighter=highlighter)) 

206 if items_table.row_count: 

207 yield items_table 

208 elif not_shown_count: 

209 yield Text.from_markup( 

210 f"[b cyan]{not_shown_count}[/][i] attribute(s) not shown.[/i] " 

211 f"Run [b][magenta]inspect[/]([not b]inspect[/])[/b] for options." 

212 ) 

213 

214 def _get_formatted_doc(self, object_: Any) -> Optional[str]: 

215 """ 

216 Extract the docstring of an object, process it and returns it. 

217 The processing consists in cleaning up the doctring's indentation, 

218 taking only its 1st paragraph if `self.help` is not True, 

219 and escape its control codes. 

220 

221 Args: 

222 object_ (Any): the object to get the docstring from. 

223 

224 Returns: 

225 Optional[str]: the processed docstring, or None if no docstring was found. 

226 """ 

227 docs = getdoc(object_) 

228 if docs is None: 

229 return None 

230 docs = cleandoc(docs).strip() 

231 if not self.help: 

232 docs = _first_paragraph(docs) 

233 return escape_control_codes(docs) 

234 

235 

236def get_object_types_mro(obj: Union[object, Type[Any]]) -> Tuple[type, ...]: 

237 """Returns the MRO of an object's class, or of the object itself if it's a class.""" 

238 if not hasattr(obj, "__mro__"): 

239 # N.B. we cannot use `if type(obj) is type` here because it doesn't work with 

240 # some types of classes, such as the ones that use abc.ABCMeta. 

241 obj = type(obj) 

242 return getattr(obj, "__mro__", ()) 

243 

244 

245def get_object_types_mro_as_strings(obj: object) -> Collection[str]: 

246 """ 

247 Returns the MRO of an object's class as full qualified names, or of the object itself if it's a class. 

248 

249 Examples: 

250 `object_types_mro_as_strings(JSONDecoder)` will return `['json.decoder.JSONDecoder', 'builtins.object']` 

251 """ 

252 return [ 

253 f'{getattr(type_, "__module__", "")}.{getattr(type_, "__qualname__", "")}' 

254 for type_ in get_object_types_mro(obj) 

255 ] 

256 

257 

258def is_object_one_of_types( 

259 obj: object, fully_qualified_types_names: Collection[str] 

260) -> bool: 

261 """ 

262 Returns `True` if the given object's class (or the object itself, if it's a class) has one of the 

263 fully qualified names in its MRO. 

264 """ 

265 for type_name in get_object_types_mro_as_strings(obj): 

266 if type_name in fully_qualified_types_names: 

267 return True 

268 return False