##// END OF EJS Templates
Improve typing and MIME hook API for inspector
krassowski -
Show More
@@ -24,9 +24,7 b' import os'
24 import types
24 import types
25 import warnings
25 import warnings
26
26
27 from typing import Any, Optional, Dict, Union, List, Tuple
27 from typing import cast, Any, Optional, Dict, Union, List, TypedDict, TypeAlias, Tuple
28
29 from typing import TypeAlias
30
28
31 import traitlets
29 import traitlets
32
30
@@ -42,7 +40,6 b' from IPython.utils.text import indent'
42 from IPython.utils.wildcard import list_namespace
40 from IPython.utils.wildcard import list_namespace
43 from IPython.utils.wildcard import typestr2type
41 from IPython.utils.wildcard import typestr2type
44 from IPython.utils.coloransi import TermColors
42 from IPython.utils.coloransi import TermColors
45 from IPython.utils.py3compat import cast_unicode
46 from IPython.utils.colorable import Colorable
43 from IPython.utils.colorable import Colorable
47 from IPython.utils.decorators import undoc
44 from IPython.utils.decorators import undoc
48
45
@@ -106,21 +103,55 b' InspectColors = PyColorize.ANSICodeColors'
106 #****************************************************************************
103 #****************************************************************************
107 # Auxiliary functions and objects
104 # Auxiliary functions and objects
108
105
109 # See the messaging spec for the definition of all these fields. This list
106
110 # effectively defines the order of display
107 class InfoDict(TypedDict):
111 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
108 type_name: Optional[str]
112 'length', 'file', 'definition', 'docstring', 'source',
109 base_class: Optional[str]
113 'init_definition', 'class_docstring', 'init_docstring',
110 string_form: Optional[str]
114 'call_def', 'call_docstring',
111 namespace: Optional[str]
115 # These won't be printed but will be used to determine how to
112 length: Optional[str]
116 # format the object
113 file: Optional[str]
117 'ismagic', 'isalias', 'isclass', 'found', 'name'
114 definition: Optional[str]
118 ]
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 def object_info(**kw):
147 def object_info(**kw):
122 """Make an object info dict with all fields present."""
148 """DEPRECATED: Make an object info dict with all fields present."""
123 infodict = {k:None for k in info_fields}
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 infodict.update(kw)
155 infodict.update(kw)
125 return infodict
156 return infodict
126
157
@@ -148,6 +179,7 b' def get_encoding(obj):'
148 encoding, _lines = openpy.detect_encoding(buffer.readline)
179 encoding, _lines = openpy.detect_encoding(buffer.readline)
149 return encoding
180 return encoding
150
181
182
151 def getdoc(obj) -> Union[str,None]:
183 def getdoc(obj) -> Union[str,None]:
152 """Stable wrapper around inspect.getdoc.
184 """Stable wrapper around inspect.getdoc.
153
185
@@ -761,6 +793,7 b' class Inspector(Colorable):'
761 """
793 """
762
794
763 info_dict = self.info(obj, oname=oname, info=info, detail_level=detail_level)
795 info_dict = self.info(obj, oname=oname, info=info, detail_level=detail_level)
796
764 bundle = self._make_info_unformatted(
797 bundle = self._make_info_unformatted(
765 obj,
798 obj,
766 info_dict,
799 info_dict,
@@ -768,10 +801,33 b' class Inspector(Colorable):'
768 detail_level=detail_level,
801 detail_level=detail_level,
769 omit_sections=omit_sections,
802 omit_sections=omit_sections,
770 )
803 )
771 for key, hook in self.mime_hooks.items():
804 if self.mime_hooks:
772 res = hook(obj, info)
805 hook_data = InspectorHookData(
773 if res is not None:
806 obj=obj,
774 bundle[key] = res
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 return self.format_mime(bundle)
831 return self.format_mime(bundle)
776
832
777 def pinfo(
833 def pinfo(
@@ -830,7 +886,7 b' class Inspector(Colorable):'
830 )
886 )
831 return self.info(obj, oname=oname, info=info, detail_level=detail_level)
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 """Compute a dict with detailed information about an object.
890 """Compute a dict with detailed information about an object.
835
891
836 Parameters
892 Parameters
@@ -847,8 +903,7 b' class Inspector(Colorable):'
847
903
848 Returns
904 Returns
849 -------
905 -------
850 An object info dict with known fields from `info_fields`. Keys are
906 An object info dict with known fields from `info_fields` (see `InfoDict`).
851 strings, values are string or None.
852 """
907 """
853
908
854 if info is None:
909 if info is None:
@@ -867,8 +922,18 b' class Inspector(Colorable):'
867 if info and info.parent is not None and hasattr(info.parent, HOOK_NAME):
922 if info and info.parent is not None and hasattr(info.parent, HOOK_NAME):
868 parents_docs_dict = getattr(info.parent, HOOK_NAME)
923 parents_docs_dict = getattr(info.parent, HOOK_NAME)
869 parents_docs = parents_docs_dict.get(att_name, None)
924 parents_docs = parents_docs_dict.get(att_name, None)
870 out = dict(
925 out: InfoDict = cast(
871 name=oname, found=True, isalias=isalias, ismagic=ismagic, subclasses=None
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 if parents_docs:
939 if parents_docs:
@@ -914,12 +979,14 b' class Inspector(Colorable):'
914 if detail_level >= self.str_detail_level:
979 if detail_level >= self.str_detail_level:
915 try:
980 try:
916 ostr = str(obj)
981 ostr = str(obj)
917 str_head = 'string_form'
982 if not detail_level and len(ostr) > string_max:
918 if not detail_level and len(ostr)>string_max:
919 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
983 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
920 ostr = ("\n" + " " * len(str_head.expandtabs())).\
984 # TODO: `'string_form'.expandtabs()` seems wrong, but
921 join(q.strip() for q in ostr.split("\n"))
985 # it was (nearly) like this since the first commit ever.
922 out[str_head] = ostr
986 ostr = ("\n" + " " * len("string_form".expandtabs())).join(
987 q.strip() for q in ostr.split("\n")
988 )
989 out["string_form"] = ostr
923 except:
990 except:
924 pass
991 pass
925
992
@@ -1054,7 +1121,7 b' class Inspector(Colorable):'
1054 if call_ds:
1121 if call_ds:
1055 out['call_docstring'] = call_ds
1122 out['call_docstring'] = call_ds
1056
1123
1057 return object_info(**out)
1124 return out
1058
1125
1059 @staticmethod
1126 @staticmethod
1060 def _source_contains_docstring(src, doc):
1127 def _source_contains_docstring(src, doc):
General Comments 0
You need to be logged in to leave comments. Login now