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

143 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1from __future__ import absolute_import 

2 

3import inspect 

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

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

6 

7from .console import Group, RenderableType 

8from .control import escape_control_codes 

9from .highlighter import ReprHighlighter 

10from .jupyter import JupyterMixin 

11from .panel import Panel 

12from .pretty import Pretty 

13from .table import Table 

14from .text import Text, TextType 

15 

16 

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

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

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

20 return paragraph 

21 

22 

23class Inspect(JupyterMixin): 

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

25 

26 Args: 

27 obj (Any): An object to inspect. 

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

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

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

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

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

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

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

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

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

37 """ 

38 

39 def __init__( 

40 self, 

41 obj: Any, 

42 *, 

43 title: Optional[TextType] = None, 

44 help: bool = False, 

45 methods: bool = False, 

46 docs: bool = True, 

47 private: bool = False, 

48 dunder: bool = False, 

49 sort: bool = True, 

50 all: bool = True, 

51 value: bool = True, 

52 ) -> None: 

53 self.highlighter = ReprHighlighter() 

54 self.obj = obj 

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

56 if all: 

57 methods = private = dunder = True 

58 self.help = help 

59 self.methods = methods 

60 self.docs = docs or help 

61 self.private = private or dunder 

62 self.dunder = dunder 

63 self.sort = sort 

64 self.value = value 

65 

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

67 """Make a default title.""" 

68 title_str = ( 

69 str(obj) 

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

71 else str(type(obj)) 

72 ) 

73 title_text = self.highlighter(title_str) 

74 return title_text 

75 

76 def __rich__(self) -> Panel: 

77 return Panel.fit( 

78 Group(*self._render()), 

79 title=self.title, 

80 border_style="scope.border", 

81 padding=(0, 1), 

82 ) 

83 

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

85 """Get a signature for a callable.""" 

86 try: 

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

88 except ValueError: 

89 _signature = "(...)" 

90 except TypeError: 

91 return None 

92 

93 source_filename: Optional[str] = None 

94 try: 

95 source_filename = getfile(obj) 

96 except (OSError, TypeError): 

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

98 pass 

99 

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

101 if source_filename: 

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

103 signature_text = self.highlighter(_signature) 

104 

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

106 

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

108 if inspect.isclass(obj): 

109 prefix = "class" 

110 elif inspect.iscoroutinefunction(obj): 

111 prefix = "async def" 

112 else: 

113 prefix = "def" 

114 

115 qual_signature = Text.assemble( 

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

117 (qualname, "inspect.callable"), 

118 signature_text, 

119 ) 

120 

121 return qual_signature 

122 

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

124 """Render object.""" 

125 

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

127 key, (_error, value) = item 

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

129 

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

131 """Get attribute or any exception.""" 

132 try: 

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

134 except Exception as error: 

135 return (error, None) 

136 

137 obj = self.obj 

138 keys = dir(obj) 

139 total_items = len(keys) 

140 if not self.dunder: 

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

142 if not self.private: 

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

144 not_shown_count = total_items - len(keys) 

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

146 if self.sort: 

147 items.sort(key=sort_items) 

148 

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

150 items_table.add_column(justify="right") 

151 add_row = items_table.add_row 

152 highlighter = self.highlighter 

153 

154 if callable(obj): 

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

156 if signature is not None: 

157 yield signature 

158 yield "" 

159 

160 if self.docs: 

161 _doc = self._get_formatted_doc(obj) 

162 if _doc is not None: 

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

164 doc_text = highlighter(doc_text) 

165 yield doc_text 

166 yield "" 

167 

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

169 yield Panel( 

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

171 border_style="inspect.value.border", 

172 ) 

173 yield "" 

174 

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

176 key_text = Text.assemble( 

177 ( 

178 key, 

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

180 ), 

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

182 ) 

183 if error is not None: 

184 warning = key_text.copy() 

185 warning.stylize("inspect.error") 

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

187 continue 

188 

189 if callable(value): 

190 if not self.methods: 

191 continue 

192 

193 _signature_text = self._get_signature(key, value) 

194 if _signature_text is None: 

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

196 else: 

197 if self.docs: 

198 docs = self._get_formatted_doc(value) 

199 if docs is not None: 

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

201 doc = highlighter(docs) 

202 doc.stylize("inspect.doc") 

203 _signature_text.append(doc) 

204 

205 add_row(key_text, _signature_text) 

206 else: 

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

208 if items_table.row_count: 

209 yield items_table 

210 elif not_shown_count: 

211 yield Text.from_markup( 

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

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

214 ) 

215 

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

217 """ 

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

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

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

221 and escape its control codes. 

222 

223 Args: 

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

225 

226 Returns: 

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

228 """ 

229 docs = getdoc(object_) 

230 if docs is None: 

231 return None 

232 docs = cleandoc(docs).strip() 

233 if not self.help: 

234 docs = _first_paragraph(docs) 

235 return escape_control_codes(docs) 

236 

237 

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

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

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

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

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

243 obj = type(obj) 

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

245 

246 

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

248 """ 

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

250 

251 Examples: 

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

253 """ 

254 return [ 

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

256 for type_ in get_object_types_mro(obj) 

257 ] 

258 

259 

260def is_object_one_of_types( 

261 obj: object, fully_qualified_types_names: Collection[str] 

262) -> bool: 

263 """ 

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

265 fully qualified names in its MRO. 

266 """ 

267 for type_name in get_object_types_mro_as_strings(obj): 

268 if type_name in fully_qualified_types_names: 

269 return True 

270 return False