1 # -*- coding: utf-8 -*- 2 """Top-level display functions for displaying object in different formats.""" 3 4 # Copyright (c) IPython Development Team. 5 # Distributed under the terms of the Modified BSD License. 6 7 8 from binascii import b2a_hex 9 import os 10 import sys 11 import warnings 12 13 __all__ = ['display', 'clear_output', 'publish_display_data', 'update_display', 'DisplayHandle'] 14 15 #----------------------------------------------------------------------------- 16 # utility functions 17 #----------------------------------------------------------------------------- 18 19 20 def _merge(d1, d2): 21 """Like update, but merges sub-dicts instead of clobbering at the top level. 22 23 Updates d1 in-place 24 """ 25 26 if not isinstance(d2, dict) or not isinstance(d1, dict): 27 return d2 28 for key, value in d2.items(): 29 d1[key] = _merge(d1.get(key), value) 30 return d1 31 32 33 #----------------------------------------------------------------------------- 34 # Main functions 35 #----------------------------------------------------------------------------- 36 37 class _Sentinel: 38 def __repr__(self): 39 return "<deprecated>" 40 41 42 _sentinel = _Sentinel() 43 44 # use * to indicate transient is keyword-only 45 def publish_display_data( 46 data, metadata=None, source=_sentinel, *, transient=None, **kwargs 47 ): 48 """Publish data and metadata to all frontends. 49 50 See the ``display_data`` message in the messaging documentation for 51 more details about this message type. 52 53 Keys of data and metadata can be any mime-type. 54 55 Parameters 56 ---------- 57 data : dict 58 A dictionary having keys that are valid MIME types (like 59 'text/plain' or 'image/svg+xml') and values that are the data for 60 that MIME type. The data itself must be a JSON'able data 61 structure. Minimally all data should have the 'text/plain' data, 62 which can be displayed by all frontends. If more than the plain 63 text is given, it is up to the frontend to decide which 64 representation to use. 65 metadata : dict 66 A dictionary for metadata related to the data. This can contain 67 arbitrary key, value pairs that frontends can use to interpret 68 the data. mime-type keys matching those in data can be used 69 to specify metadata about particular representations. 70 source : str, deprecated 71 Unused. 72 transient : dict, keyword-only 73 A dictionary of transient data, such as display_id. 74 """ 75 from IPython.core.interactiveshell import InteractiveShell 76 77 if source is not _sentinel: 78 warnings.warn( 79 "The `source` parameter emit a deprecation warning since" 80 " IPython 8.0, it had no effects for a long time and will " 81 " be removed in future versions.", 82 DeprecationWarning, 83 stacklevel=2, 84 ) 85 display_pub = InteractiveShell.instance().display_pub 86 87 # only pass transient if supplied, 88 # to avoid errors with older ipykernel. 89 # TODO: We could check for ipykernel version and provide a detailed upgrade message. 90 if transient: 91 kwargs['transient'] = transient 92 93 display_pub.publish( 94 data=data, 95 metadata=metadata, 96 **kwargs 97 ) 98 99 100 def _new_id(): 101 """Generate a new random text id with urandom""" 102 return b2a_hex(os.urandom(16)).decode('ascii') 103 104 105 def display( 106 *objs, 107 include=None, 108 exclude=None, 109 metadata=None, 110 transient=None, 111 display_id=None, 112 raw=False, 113 clear=False, 114 **kwargs, 115 ): 116 """Display a Python object in all frontends. 117 118 By default all representations will be computed and sent to the frontends. 119 Frontends can decide which representation is used and how. 120 121 In terminal IPython this will be similar to using :func:`print`, for use in richer 122 frontends see Jupyter notebook examples with rich display logic. 123 124 Parameters 125 ---------- 126 *objs : object 127 The Python objects to display. 128 raw : bool, optional 129 Are the objects to be displayed already mimetype-keyed dicts of raw display data, 130 or Python objects that need to be formatted before display? [default: False] 131 include : list, tuple or set, optional 132 A list of format type strings (MIME types) to include in the 133 format data dict. If this is set *only* the format types included 134 in this list will be computed. 135 exclude : list, tuple or set, optional 136 A list of format type strings (MIME types) to exclude in the format 137 data dict. If this is set all format types will be computed, 138 except for those included in this argument. 139 metadata : dict, optional 140 A dictionary of metadata to associate with the output. 141 mime-type keys in this dictionary will be associated with the individual 142 representation formats, if they exist. 143 transient : dict, optional 144 A dictionary of transient data to associate with the output. 145 Data in this dict should not be persisted to files (e.g. notebooks). 146 display_id : str, bool optional 147 Set an id for the display. 148 This id can be used for updating this display area later via update_display. 149 If given as `True`, generate a new `display_id` 150 clear : bool, optional 151 Should the output area be cleared before displaying anything? If True, 152 this will wait for additional output before clearing. [default: False] 153 **kwargs : additional keyword-args, optional 154 Additional keyword-arguments are passed through to the display publisher. 155 156 Returns 157 ------- 158 handle: DisplayHandle 159 Returns a handle on updatable displays for use with :func:`update_display`, 160 if `display_id` is given. Returns :any:`None` if no `display_id` is given 161 (default). 162 163 Examples 164 -------- 165 >>> class Json(object): 166 ... def __init__(self, json): 167 ... self.json = json 168 ... def _repr_pretty_(self, pp, cycle): 169 ... import json 170 ... pp.text(json.dumps(self.json, indent=2)) 171 ... def __repr__(self): 172 ... return str(self.json) 173 ... 174 175 >>> d = Json({1:2, 3: {4:5}}) 176 177 >>> print(d) 178 {1: 2, 3: {4: 5}} 179 180 >>> display(d) 181 { 182 "1": 2, 183 "3": { 184 "4": 5 185 } 186 } 187 188 >>> def int_formatter(integer, pp, cycle): 189 ... pp.text('I'*integer) 190 191 >>> plain = get_ipython().display_formatter.formatters['text/plain'] 192 >>> plain.for_type(int, int_formatter) 193 <function _repr_pprint at 0x...> 194 >>> display(7-5) 195 II 196 197 >>> del plain.type_printers[int] 198 >>> display(7-5) 199 2 200 201 See Also 202 -------- 203 :func:`update_display` 204 205 Notes 206 ----- 207 In Python, objects can declare their textual representation using the 208 `__repr__` method. IPython expands on this idea and allows objects to declare 209 other, rich representations including: 210 211 - HTML 212 - JSON 213 - PNG 214 - JPEG 215 - SVG 216 - LaTeX 217 218 A single object can declare some or all of these representations; all are 219 handled by IPython's display system. 220 221 The main idea of the first approach is that you have to implement special 222 display methods when you define your class, one for each representation you 223 want to use. Here is a list of the names of the special methods and the 224 values they must return: 225 226 - `_repr_html_`: return raw HTML as a string, or a tuple (see below). 227 - `_repr_json_`: return a JSONable dict, or a tuple (see below). 228 - `_repr_jpeg_`: return raw JPEG data, or a tuple (see below). 229 - `_repr_png_`: return raw PNG data, or a tuple (see below). 230 - `_repr_svg_`: return raw SVG data as a string, or a tuple (see below). 231 - `_repr_latex_`: return LaTeX commands in a string surrounded by "$", 232 or a tuple (see below). 233 - `_repr_mimebundle_`: return a full mimebundle containing the mapping 234 from all mimetypes to data. 235 Use this for any mime-type not listed above. 236 237 The above functions may also return the object's metadata alonside the 238 data. If the metadata is available, the functions will return a tuple 239 containing the data and metadata, in that order. If there is no metadata 240 available, then the functions will return the data only. 241 242 When you are directly writing your own classes, you can adapt them for 243 display in IPython by following the above approach. But in practice, you 244 often need to work with existing classes that you can't easily modify. 245 246 You can refer to the documentation on integrating with the display system in 247 order to register custom formatters for already existing types 248 (:ref:`integrating_rich_display`). 249 250 .. versionadded:: 5.4 display available without import 251 .. versionadded:: 6.1 display available without import 252 253 Since IPython 5.4 and 6.1 :func:`display` is automatically made available to 254 the user without import. If you are using display in a document that might 255 be used in a pure python context or with older version of IPython, use the 256 following import at the top of your file:: 257 258 from IPython.display import display 259 260 """ 261 from IPython.core.interactiveshell import InteractiveShell 262 263 if not InteractiveShell.initialized(): 264 # Directly print objects. 265 print(*objs) 266 return 267 268 if transient is None: 269 transient = {} 270 if metadata is None: 271 metadata={} 272 if display_id: 273 if display_id is True: 274 display_id = _new_id() 275 transient['display_id'] = display_id 276 if kwargs.get('update') and 'display_id' not in transient: 277 raise TypeError('display_id required for update_display') 278 if transient: 279 kwargs['transient'] = transient 280 281 if not objs and display_id: 282 # if given no objects, but still a request for a display_id, 283 # we assume the user wants to insert an empty output that 284 # can be updated later 285 objs = [{}] 286 raw = True 287 288 if not raw: 289 format = InteractiveShell.instance().display_formatter.format 290 291 if clear: 292 clear_output(wait=True) 293 294 for obj in objs: 295 if raw: 296 publish_display_data(data=obj, metadata=metadata, **kwargs) 297 else: 298 format_dict, md_dict = format(obj, include=include, exclude=exclude) 299 if not format_dict: 300 # nothing to display (e.g. _ipython_display_ took over) 301 continue 302 if metadata: 303 # kwarg-specified metadata gets precedence 304 _merge(md_dict, metadata) 305 publish_display_data(data=format_dict, metadata=md_dict, **kwargs) 306 if display_id: 307 return DisplayHandle(display_id) 308 309 310 # use * for keyword-only display_id arg 311 def update_display(obj, *, display_id, **kwargs): 312 """Update an existing display by id 313 314 Parameters 315 ---------- 316 obj 317 The object with which to update the display 318 display_id : keyword-only 319 The id of the display to update 320 321 See Also 322 -------- 323 :func:`display` 324 """ 325 kwargs['update'] = True 326 display(obj, display_id=display_id, **kwargs) 327 328 329 class DisplayHandle(object): 330 """A handle on an updatable display 331 332 Call `.update(obj)` to display a new object. 333 334 Call `.display(obj`) to add a new instance of this display, 335 and update existing instances. 336 337 See Also 338 -------- 339 340 :func:`display`, :func:`update_display` 341 342 """ 343 344 def __init__(self, display_id=None): 345 if display_id is None: 346 display_id = _new_id() 347 self.display_id = display_id 348 349 def __repr__(self): 350 return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id) 351 352 def display(self, obj, **kwargs): 353 """Make a new display with my id, updating existing instances. 354 355 Parameters 356 ---------- 357 obj 358 object to display 359 **kwargs 360 additional keyword arguments passed to display 361 """ 362 display(obj, display_id=self.display_id, **kwargs) 363 364 def update(self, obj, **kwargs): 365 """Update existing displays with my id 366 367 Parameters 368 ---------- 369 obj 370 object to display 371 **kwargs 372 additional keyword arguments passed to update_display 373 """ 374 update_display(obj, display_id=self.display_id, **kwargs) 375 376 377 def clear_output(wait=False): 378 """Clear the output of the current cell receiving output. 379 380 Parameters 381 ---------- 382 wait : bool [default: false] 383 Wait to clear the output until new output is available to replace it.""" 384 from IPython.core.interactiveshell import InteractiveShell 385 if InteractiveShell.initialized(): 386 InteractiveShell.instance().display_pub.clear_output(wait) 387 else: 388 print('\033[2K\r', end='') 389 sys.stdout.flush() 390 print('\033[2K\r', end='') 391 sys.stderr.flush() 392