##// END OF EJS Templates
Improve typing and MIME hook API for inspector (#14342)...
M Bussonnier -
r28661:2084e7f3 merge
parent child Browse files
Show More
@@ -24,9 +24,7 b' import os'
24 24 import types
25 25 import warnings
26 26
27 from typing import Any, Optional, Dict, Union, List, Tuple
28
29 from typing import TypeAlias
27 from typing import cast, Any, Optional, Dict, Union, List, TypedDict, TypeAlias, Tuple
30 28
31 29 import traitlets
32 30
@@ -42,7 +40,6 b' from IPython.utils.text import indent'
42 40 from IPython.utils.wildcard import list_namespace
43 41 from IPython.utils.wildcard import typestr2type
44 42 from IPython.utils.coloransi import TermColors
45 from IPython.utils.py3compat import cast_unicode
46 43 from IPython.utils.colorable import Colorable
47 44 from IPython.utils.decorators import undoc
48 45
@@ -106,21 +103,50 b' InspectColors = PyColorize.ANSICodeColors'
106 103 #****************************************************************************
107 104 # Auxiliary functions and objects
108 105
109 # See the messaging spec for the definition of all these fields. This list
110 # effectively defines the order of display
111 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
112 'length', 'file', 'definition', 'docstring', 'source',
113 'init_definition', 'class_docstring', 'init_docstring',
114 'call_def', 'call_docstring',
115 # These won't be printed but will be used to determine how to
116 # format the object
117 'ismagic', 'isalias', 'isclass', 'found', 'name'
118 ]
106
107 class InfoDict(TypedDict):
108 type_name: Optional[str]
109 base_class: Optional[str]
110 string_form: Optional[str]
111 namespace: Optional[str]
112 length: Optional[str]
113 file: Optional[str]
114 definition: Optional[str]
115 docstring: Optional[str]
116 source: Optional[str]
117 init_definition: Optional[str]
118 class_docstring: Optional[str]
119 init_docstring: Optional[str]
120 call_def: Optional[str]
121 call_docstring: Optional[str]
122 subclasses: Optional[str]
123 # These won't be printed but will be used to determine how to
124 # format the object
125 ismagic: bool
126 isalias: bool
127 isclass: bool
128 found: bool
129 name: str
130
131
132 info_fields = list(InfoDict.__annotations__.keys())
133
134
135 @dataclass
136 class InspectorHookData:
137 """Data passed to the mime hook"""
138
139 obj: Any
140 info: Optional[OInfo]
141 info_dict: InfoDict
142 detail_level: int
143 omit_sections: list[str]
119 144
120 145
146 @undoc
121 147 def object_info(**kw):
122 148 """Make an object info dict with all fields present."""
123 infodict = {k:None for k in info_fields}
149 infodict = {k: None for k in info_fields}
124 150 infodict.update(kw)
125 151 return infodict
126 152
@@ -148,6 +174,7 b' def get_encoding(obj):'
148 174 encoding, _lines = openpy.detect_encoding(buffer.readline)
149 175 return encoding
150 176
177
151 178 def getdoc(obj) -> Union[str,None]:
152 179 """Stable wrapper around inspect.getdoc.
153 180
@@ -761,6 +788,7 b' class Inspector(Colorable):'
761 788 """
762 789
763 790 info_dict = self.info(obj, oname=oname, info=info, detail_level=detail_level)
791
764 792 bundle = self._make_info_unformatted(
765 793 obj,
766 794 info_dict,
@@ -768,10 +796,33 b' class Inspector(Colorable):'
768 796 detail_level=detail_level,
769 797 omit_sections=omit_sections,
770 798 )
771 for key, hook in self.mime_hooks.items():
772 res = hook(obj, info)
773 if res is not None:
774 bundle[key] = res
799 if self.mime_hooks:
800 hook_data = InspectorHookData(
801 obj=obj,
802 info=info,
803 info_dict=info_dict,
804 detail_level=detail_level,
805 omit_sections=omit_sections,
806 )
807 for key, hook in self.mime_hooks.items():
808 required_parameters = [
809 parameter
810 for parameter in inspect.signature(hook).parameters.values()
811 if parameter.default != inspect.Parameter.default
812 ]
813 if len(required_parameters) == 1:
814 res = hook(hook_data)
815 else:
816 warnings.warn(
817 "MIME hook format changed in IPython 8.22; hooks should now accept"
818 " a single parameter (InspectorHookData); support for hooks requiring"
819 " two-parameters (obj and info) will be removed in a future version",
820 DeprecationWarning,
821 stacklevel=2,
822 )
823 res = hook(obj, info)
824 if res is not None:
825 bundle[key] = res
775 826 return self.format_mime(bundle)
776 827
777 828 def pinfo(
@@ -830,7 +881,7 b' class Inspector(Colorable):'
830 881 )
831 882 return self.info(obj, oname=oname, info=info, detail_level=detail_level)
832 883
833 def info(self, obj, oname="", info=None, detail_level=0) -> Dict[str, Any]:
884 def info(self, obj, oname="", info=None, detail_level=0) -> InfoDict:
834 885 """Compute a dict with detailed information about an object.
835 886
836 887 Parameters
@@ -847,8 +898,7 b' class Inspector(Colorable):'
847 898
848 899 Returns
849 900 -------
850 An object info dict with known fields from `info_fields`. Keys are
851 strings, values are string or None.
901 An object info dict with known fields from `info_fields` (see `InfoDict`).
852 902 """
853 903
854 904 if info is None:
@@ -867,8 +917,18 b' class Inspector(Colorable):'
867 917 if info and info.parent is not None and hasattr(info.parent, HOOK_NAME):
868 918 parents_docs_dict = getattr(info.parent, HOOK_NAME)
869 919 parents_docs = parents_docs_dict.get(att_name, None)
870 out = dict(
871 name=oname, found=True, isalias=isalias, ismagic=ismagic, subclasses=None
920 out: InfoDict = cast(
921 InfoDict,
922 {
923 **{field: None for field in info_fields},
924 **{
925 "name": oname,
926 "found": True,
927 "isalias": isalias,
928 "ismagic": ismagic,
929 "subclasses": None,
930 },
931 },
872 932 )
873 933
874 934 if parents_docs:
@@ -914,12 +974,14 b' class Inspector(Colorable):'
914 974 if detail_level >= self.str_detail_level:
915 975 try:
916 976 ostr = str(obj)
917 str_head = 'string_form'
918 if not detail_level and len(ostr)>string_max:
977 if not detail_level and len(ostr) > string_max:
919 978 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
920 ostr = ("\n" + " " * len(str_head.expandtabs())).\
921 join(q.strip() for q in ostr.split("\n"))
922 out[str_head] = ostr
979 # TODO: `'string_form'.expandtabs()` seems wrong, but
980 # it was (nearly) like this since the first commit ever.
981 ostr = ("\n" + " " * len("string_form".expandtabs())).join(
982 q.strip() for q in ostr.split("\n")
983 )
984 out["string_form"] = ostr
923 985 except:
924 986 pass
925 987
@@ -1054,7 +1116,7 b' class Inspector(Colorable):'
1054 1116 if call_ds:
1055 1117 out['call_docstring'] = call_ds
1056 1118
1057 return object_info(**out)
1119 return out
1058 1120
1059 1121 @staticmethod
1060 1122 def _source_contains_docstring(src, doc):
General Comments 0
You need to be logged in to leave comments. Login now