##// END OF EJS Templates
Improve typing and MIME hook API for inspector
krassowski -
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,55 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 """Make an object info dict with all fields present."""
123 infodict = {k:None for k in info_fields}
148 """DEPRECATED: Make an object info dict with all fields present."""
149 warnings.warn(
150 "IPython.core.oinspect.object_info has been deprecated since IPython 8.22 and will be removed in future versions",
151 DeprecationWarning,
152 stacklevel=2,
153 )
154 infodict = {k: None for k in info_fields}
124 155 infodict.update(kw)
125 156 return infodict
126 157
@@ -148,6 +179,7 b' def get_encoding(obj):'
148 179 encoding, _lines = openpy.detect_encoding(buffer.readline)
149 180 return encoding
150 181
182
151 183 def getdoc(obj) -> Union[str,None]:
152 184 """Stable wrapper around inspect.getdoc.
153 185
@@ -761,6 +793,7 b' class Inspector(Colorable):'
761 793 """
762 794
763 795 info_dict = self.info(obj, oname=oname, info=info, detail_level=detail_level)
796
764 797 bundle = self._make_info_unformatted(
765 798 obj,
766 799 info_dict,
@@ -768,10 +801,33 b' class Inspector(Colorable):'
768 801 detail_level=detail_level,
769 802 omit_sections=omit_sections,
770 803 )
771 for key, hook in self.mime_hooks.items():
772 res = hook(obj, info)
773 if res is not None:
774 bundle[key] = res
804 if self.mime_hooks:
805 hook_data = InspectorHookData(
806 obj=obj,
807 info=info,
808 info_dict=info_dict,
809 detail_level=detail_level,
810 omit_sections=omit_sections,
811 )
812 for key, hook in self.mime_hooks.items():
813 required_parameters = [
814 parameter
815 for parameter in inspect.signature(hook).parameters.values()
816 if parameter.default != inspect.Parameter.default
817 ]
818 if len(required_parameters) == 1:
819 res = hook(hook_data)
820 else:
821 warnings.warn(
822 "MIME hook format changed in IPython 8.22; hooks should now accept"
823 " a single parameter (InspectorHookData); support for hooks requiring"
824 " two-parameters (obj and info) will be removed in a future version",
825 DeprecationWarning,
826 stacklevel=2,
827 )
828 res = hook(obj, info)
829 if res is not None:
830 bundle[key] = res
775 831 return self.format_mime(bundle)
776 832
777 833 def pinfo(
@@ -830,7 +886,7 b' class Inspector(Colorable):'
830 886 )
831 887 return self.info(obj, oname=oname, info=info, detail_level=detail_level)
832 888
833 def info(self, obj, oname="", info=None, detail_level=0) -> Dict[str, Any]:
889 def info(self, obj, oname="", info=None, detail_level=0) -> InfoDict:
834 890 """Compute a dict with detailed information about an object.
835 891
836 892 Parameters
@@ -847,8 +903,7 b' class Inspector(Colorable):'
847 903
848 904 Returns
849 905 -------
850 An object info dict with known fields from `info_fields`. Keys are
851 strings, values are string or None.
906 An object info dict with known fields from `info_fields` (see `InfoDict`).
852 907 """
853 908
854 909 if info is None:
@@ -867,8 +922,18 b' class Inspector(Colorable):'
867 922 if info and info.parent is not None and hasattr(info.parent, HOOK_NAME):
868 923 parents_docs_dict = getattr(info.parent, HOOK_NAME)
869 924 parents_docs = parents_docs_dict.get(att_name, None)
870 out = dict(
871 name=oname, found=True, isalias=isalias, ismagic=ismagic, subclasses=None
925 out: InfoDict = cast(
926 InfoDict,
927 {
928 **{field: None for field in info_fields},
929 **{
930 "name": oname,
931 "found": True,
932 "isalias": isalias,
933 "ismagic": ismagic,
934 "subclasses": None,
935 },
936 },
872 937 )
873 938
874 939 if parents_docs:
@@ -914,12 +979,14 b' class Inspector(Colorable):'
914 979 if detail_level >= self.str_detail_level:
915 980 try:
916 981 ostr = str(obj)
917 str_head = 'string_form'
918 if not detail_level and len(ostr)>string_max:
982 if not detail_level and len(ostr) > string_max:
919 983 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
984 # TODO: `'string_form'.expandtabs()` seems wrong, but
985 # it was (nearly) like this since the first commit ever.
986 ostr = ("\n" + " " * len("string_form".expandtabs())).join(
987 q.strip() for q in ostr.split("\n")
988 )
989 out["string_form"] = ostr
923 990 except:
924 991 pass
925 992
@@ -1054,7 +1121,7 b' class Inspector(Colorable):'
1054 1121 if call_ds:
1055 1122 out['call_docstring'] = call_ds
1056 1123
1057 return object_info(**out)
1124 return out
1058 1125
1059 1126 @staticmethod
1060 1127 def _source_contains_docstring(src, doc):
General Comments 0
You need to be logged in to leave comments. Login now