Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/misc/displayhook.py
8814 views
1
# -*- coding: utf-8 -*-
2
r"""
3
Implements a displayhook for Sage.
4
5
This displayhook has two new facilities, by default the displayhook contains a
6
new facility for displaying lists of matrices in an easier to read format::
7
8
sage: [identity_matrix(i) for i in range(2,5)]
9
[
10
[1 0 0 0]
11
[1 0 0] [0 1 0 0]
12
[1 0] [0 1 0] [0 0 1 0]
13
[0 1], [0 0 1], [0 0 0 1]
14
]
15
16
This facility uses :meth:`_repr_` (and a simple string) to try do a nice read
17
format (see :meth:`sage.structure.parent._repr_option` for details).
18
19
With this displayhook there exists an other way for displaying object and more
20
generally, all sage expression as an ASCII art object::
21
22
sage: from sage.misc.interpreter import get_test_shell
23
sage: shell = get_test_shell()
24
sage: shell.run_cell('%display ascii_art')
25
sage: shell.run_cell('integral(x^2/pi^x, x)')
26
/ 2 2 \ -x*log(pi)
27
-\x *log (pi) + 2*x*log(pi) + 2/*e
28
---------------------------------------------
29
3
30
log (pi)
31
sage: shell.run_cell("i = var('i')")
32
sage: shell.run_cell('sum(i*x^i, i, 0, 10)')
33
10 9 8 7 6 5 4 3 2
34
10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x
35
sage: shell.run_cell('StandardTableaux(4).list()')
36
[
37
[ 1 4 1 3
38
[ 1 3 4 1 2 4 1 2 3 1 3 1 2 2 2
39
[ 1 2 3 4, 2 , 3 , 4 , 2 4, 3 4, 3 , 4
40
<BLANKLINE>
41
1 ]
42
1 2 2 ]
43
3 3 ]
44
, 4 , 4 ]
45
sage: shell.run_cell('%display simple')
46
47
This other facility uses a simple `AsciiArt` object
48
(see :class:`sage.misc.ascii_art.AsciiArt` and
49
:meth:`sage.structure.parent._ascii_art_`).
50
51
AUTHORS:
52
53
- Bill Cauchois (2009): initial version
54
- Jean-Baptiste Priez <[email protected]> (2013): ASCII art
55
- Volker Braun (2013): refactored into DisplayHookBase
56
"""
57
58
import sys, __builtin__
59
60
class ListFormatter(object):
61
62
# This is used to wrap lines when printing "tall" lists.
63
MAX_COLUMN = 70
64
65
def _check_tall_list_and_format(self, the_list):
66
"""
67
First check whether a list is "tall" -- whether the reprs of the
68
elements of the list will span multiple lines and cause the list
69
to be printed awkwardly. If not, this function returns ``None`` and
70
does nothing; you should revert back to the normal method for
71
printing an object (its repr). If so, return the string in the
72
special format. Note that the special format isn't just for
73
matrices. Any object with a multiline repr will be formatted.
74
75
INPUT:
76
77
- ``the_list`` - The list (or a tuple).
78
79
TESTS::
80
81
sage: from sage.misc.displayhook import DisplayHookBase
82
sage: dhb = DisplayHookBase()
83
84
We test :meth:`_check_tall_list_and_format` indirectly by
85
calling :meth:`simple_format_obj` on a list of matrices::
86
87
sage: print dhb.simple_format_obj(
88
....: [matrix([[1, 2, 3, 4], [5, 6, 7, 8]]) for i in xrange(7)])
89
[
90
[1 2 3 4] [1 2 3 4] [1 2 3 4] [1 2 3 4] [1 2 3 4] [1 2 3 4]
91
[5 6 7 8], [5 6 7 8], [5 6 7 8], [5 6 7 8], [5 6 7 8], [5 6 7 8],
92
<BLANKLINE>
93
[1 2 3 4]
94
[5 6 7 8]
95
]
96
97
We return ``None`` if we don't have anything special to do::
98
99
sage: dhb.simple_format_obj('one-line string')
100
sage: dhb.simple_format_obj(matrix([[1,2,3]]))
101
"""
102
# For every object to be printed, split its repr on newlines and store the
103
# result in this list.
104
split_reprs = []
105
tall = False
106
for elem in the_list:
107
split_reprs.append(repr(elem).split('\n'))
108
if len(split_reprs[-1]) > 1:
109
# Meanwhile, check to make sure the list is actually "tall".
110
tall = True
111
if not tall:
112
return None
113
# Figure out which type of parenthesis to use, based on the type of the_list.
114
if isinstance(the_list, tuple):
115
parens = '()'
116
elif isinstance(the_list, list):
117
parens = '[]'
118
else:
119
raise TypeError('expected list or tuple')
120
121
# running_lines is a list of lines, which are stored as lists of strings
122
# to be joined later. For each split repr, we add its lines to the
123
# running_lines array. When current_column exceeds MAX_COLUMN, process
124
# and output running_lines using _print_tall_list_row.
125
running_lines = [[]]
126
current_column = 0
127
s = [parens[0]]
128
for split_repr in split_reprs:
129
width = max(len(x) for x in split_repr)
130
if current_column + width > self.MAX_COLUMN and not (width > self.MAX_COLUMN):
131
s.extend(self._tall_list_row(running_lines))
132
running_lines = [[]]
133
current_column = 0
134
current_column += width + 2
135
# Add the lines from split_repr to the running_lines array. It may
136
# be necessary to add or remove lines from either one so that the
137
# number of lines matches up.
138
for i in xrange(len(running_lines), len(split_repr)):
139
running_lines.insert(0, [' ' * len(x) for x in running_lines[-1]])
140
line_diff = len(running_lines) - len(split_repr)
141
for i, x in enumerate(split_repr):
142
running_lines[i + line_diff].append(x.ljust(width))
143
for i in xrange(line_diff):
144
running_lines[i].append(' ' * width)
145
# Output any remaining entries.
146
if len(running_lines[0]) > 0:
147
s.extend(self._tall_list_row(running_lines, True))
148
s.append(parens[1])
149
return "\n".join(s)
150
151
def _tall_list_row(self, running_lines, last_row=False):
152
"""
153
Helper for :meth:`_check_tall_list_and_format`
154
155
This helper function processes and outputs the contents of the
156
running_lines array.
157
158
TESTS::
159
160
sage: from sage.misc.displayhook import format_list
161
sage: format_list._tall_list_row(['a b', 'b c', 'c'])
162
['a b', 'b c', 'c,', '']
163
"""
164
s=[]
165
for i, line in enumerate(running_lines):
166
if i + 1 != len(running_lines):
167
sep, tail = ' ', ''
168
else:
169
# The commas go on the bottom line of this row.
170
sep, tail = ', ', '' if last_row else ','
171
s.append(sep.join(line) + tail)
172
# Separate rows with a newline to make them stand out.
173
if not last_row:
174
s.append("")
175
return s
176
177
def try_format_list(self, obj):
178
"""
179
Format list/tuple.
180
181
OUTPUT:
182
183
A string representation or ``None``. The latter means that no
184
Sage-specific formatting is defined and the default should be
185
used.
186
187
EXAMPLES::
188
189
sage: from sage.misc.displayhook import format_list
190
sage: format_list.try_format_list('Hello, world!')
191
sage: format_list('Hello, world!')
192
"'Hello, world!'"
193
"""
194
ascii_art_repr = False
195
for o in obj:
196
try:
197
ascii_art_repr = ascii_art_repr or o.parent()._repr_option('element_ascii_art')
198
except (AttributeError, TypeError):
199
pass
200
if ascii_art_repr:
201
return self._check_tall_list_and_format(obj)
202
else:
203
return None
204
205
def __call__(self, obj):
206
"""
207
Return a string formatting.
208
209
This method is like :meth:`try_format_list` except that it
210
will always return a string.
211
212
OUTPUT:
213
214
String.
215
216
EXAMPLES::
217
218
sage: from sage.misc.displayhook import format_list
219
sage: format_list.try_format_list('Hello, world!')
220
sage: format_list('Hello, world!')
221
"'Hello, world!'"
222
"""
223
s = self.try_format_list(obj)
224
if s is None:
225
return repr(obj)
226
else:
227
return s
228
229
format_list = ListFormatter()
230
231
232
class DisplayHookBase(object):
233
234
def simple_format_obj(self, obj):
235
"""This function is used internally by the displayhook.
236
237
We attempt to keep ascii art of list/tuple members intact as we
238
print them. See :meth:`sage.structure.parent._repr_option` for
239
details.
240
241
OUTPUT:
242
243
Return a string if we want to print it in a special way;
244
otherwise, return ``None``.
245
246
EXAMPLES::
247
248
sage: from sage.misc.displayhook import DisplayHookBase
249
sage: dhb = DisplayHookBase()
250
251
For most objects, nothing is done (``None`` is returned):
252
253
sage: dhb.simple_format_obj('Hello, world!')
254
sage: dhb.simple_format_obj((1, 2, 3, 4))
255
256
We demonstrate the special format for lists of matrices::
257
258
sage: dhb.simple_format_obj(
259
....: [matrix([[1], [2]]), matrix([[3], [4]])])
260
'[\n[1] [3]\n[2], [4]\n]'
261
262
TESTS:
263
264
In :trac:`14466` we override IPython's special printing of
265
``type`` objects and revert it to Python's standard string
266
representation::
267
268
sage: shell=sage.misc.interpreter.get_test_shell()
269
sage: shell.displayhook(type)
270
<type 'type'>
271
"""
272
if isinstance(obj, type):
273
return repr(obj)
274
if isinstance(obj, (tuple, list)) and len(obj) > 0:
275
return format_list.try_format_list(obj)
276
else:
277
return None
278
279
def set_display(self, mode):
280
r"""
281
Select the text formatting method.
282
283
INPUT:
284
285
- ``mode`` -- string. One of ``simple``, ``ascii_art``, or ``typeset``.
286
287
See :func:`simple_format_obj` or :func:`sage.misc.ascii_art.ascii_art`.
288
289
TESTS::
290
291
sage: [identity_matrix(i) for i in range(3,7)]
292
[
293
[1 0 0 0 0 0]
294
[1 0 0 0 0] [0 1 0 0 0 0]
295
[1 0 0 0] [0 1 0 0 0] [0 0 1 0 0 0]
296
[1 0 0] [0 1 0 0] [0 0 1 0 0] [0 0 0 1 0 0]
297
[0 1 0] [0 0 1 0] [0 0 0 1 0] [0 0 0 0 1 0]
298
[0 0 1], [0 0 0 1], [0 0 0 0 1], [0 0 0 0 0 1]
299
]
300
sage: from sage.misc.interpreter import get_test_shell
301
sage: shell = get_test_shell()
302
sage: shell.run_cell('%display ascii_art') # indirect doctest
303
sage: shell.run_cell("i = var('i')")
304
sage: shell.run_cell('sum(i*x^i, i, 0, 10)')
305
10 9 8 7 6 5 4 3 2
306
10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x
307
sage: shell.run_cell('%display simple')
308
"""
309
if mode not in ['simple', 'ascii_art', 'typeset']:
310
raise ValueError('invalid mode set')
311
self.mode = mode
312
313
mode = 'simple'
314
315
@property
316
def simple(self):
317
"""
318
Whether the mode is the "simple" (default) display.
319
320
EXAMPLES::
321
322
sage: sys.displayhook.simple
323
True
324
sage: sys.displayhook.ascii_art
325
False
326
"""
327
return self.mode == 'simple'
328
329
@property
330
def ascii_art(self):
331
"""
332
Whether the mode is the ascii art display.
333
334
EXAMPLES::
335
336
sage: sys.displayhook.simple
337
True
338
sage: sys.displayhook.ascii_art
339
False
340
"""
341
return self.mode == 'ascii_art'
342
343
@property
344
def typeset(self):
345
"""
346
Whether the mode is the notebook "Typeset" display.
347
348
EXAMPLES::
349
350
sage: sys.displayhook.simple
351
True
352
sage: sys.displayhook.typeset
353
False
354
"""
355
return self.mode == 'typeset'
356
357
def try_format_obj(self, obj):
358
"""
359
Format non-graphics object.
360
361
OUTPUT:
362
363
A string representation or ``None``. The latter means that no
364
Sage-specific formatting is defined and the default should be
365
used.
366
367
TESTS::
368
369
sage: from sage.misc.displayhook import DisplayHookBase
370
sage: dhb = DisplayHookBase()
371
sage: dhb.try_format_obj('Hello, world!')
372
"""
373
if self.simple:
374
return self.simple_format_obj(obj)
375
if self.ascii_art:
376
from sage.misc.ascii_art import ascii_art
377
return ascii_art(obj)
378
if self.typeset:
379
from sage.misc.latex import pretty_print
380
pretty_print(obj)
381
return ''
382
assert(False)
383
384
def try_format_graphics(self, obj):
385
"""
386
Format graphics object.
387
388
OUTPUT:
389
390
Boolean. Whether the object is graphics and was successfully
391
displayed.
392
393
TESTS::
394
395
sage: from sage.misc.displayhook import DisplayHookBase
396
sage: dhb = DisplayHookBase()
397
sage: dhb.try_format_graphics('Hello, world!')
398
False
399
"""
400
from sage.structure.sage_object import SageObject
401
if isinstance(obj, SageObject) and hasattr(obj, '_graphics_'):
402
return obj._graphics_()
403
return False
404
405
406
class DisplayHook(DisplayHookBase):
407
"""
408
Display hook for Sage.
409
410
This is not used directly in interactive Sage (where we use the
411
IPython system for display hooks). This class provides a way to
412
use the Sage display formatting when not using interactive Sage.
413
"""
414
def __init__(self, oldhook=sys.__displayhook__):
415
"""
416
Set the old display hook (default to repr)
417
418
EXAMPLES::
419
420
sage: from sage.misc.displayhook import DisplayHook
421
sage: def f(o): print repr(o)[:5], "..."
422
sage: d = DisplayHook(f)
423
sage: d(range(10))
424
[0, 1 ...
425
"""
426
self.oldhook = oldhook
427
428
def __call__(self, obj):
429
"""
430
Format the object using Sage's formatting, or format it using the old
431
display hook if Sage does not want to handle the object.
432
433
EXAMPLES::
434
435
sage: from sage.misc.displayhook import DisplayHook
436
sage: d = DisplayHook()
437
sage: d((identity_matrix(3), identity_matrix(3)))
438
(
439
[1 0 0] [1 0 0]
440
[0 1 0] [0 1 0]
441
[0 0 1], [0 0 1]
442
)
443
"""
444
if self.try_format_graphics(obj):
445
return
446
s = self.try_format_obj(obj)
447
if s is not None:
448
print s
449
__builtin__._ = obj
450
else:
451
self.oldhook(obj)
452
453
454
from IPython.core.formatters import PlainTextFormatter
455
class SagePlainTextFormatter(DisplayHookBase, PlainTextFormatter):
456
r"""
457
A replacement for the plain text formatter which can use two facilities:
458
459
- correctly print lists of matrices or other objects (see
460
:meth:`sage.structure.parent._repr_option`),
461
- print ASCII art objects (like expressions) (see
462
:meth:`sage.structure.parent._ascii_art_`).
463
464
EXAMPLES::
465
466
sage: from sage.misc.interpreter import get_test_shell
467
sage: shell = get_test_shell()
468
sage: shell.display_formatter.formatters['text/plain']
469
<...displayhook.SagePlainTextFormatter object at 0x...>
470
sage: shell.run_cell('a = identity_matrix(ZZ, 2); [a,a]')
471
[
472
[1 0] [1 0]
473
[0 1], [0 1]
474
]
475
"""
476
def __call__(self, obj):
477
r"""
478
Computes the format data of ``result``. If the
479
:func:`sage.misc.displayhook.simple_format_obj` writes a string, then
480
we override IPython's :class:`DisplayHook` formatting.
481
482
EXAMPLES::
483
484
sage: from sage.misc.interpreter import get_test_shell
485
sage: shell = get_test_shell()
486
sage: fmt = shell.display_formatter.formatters['text/plain']
487
sage: fmt
488
<...displayhook.SagePlainTextFormatter object at 0x...>
489
sage: shell.displayhook.compute_format_data(2)
490
({u'text/plain': '2'}, {})
491
sage: a = identity_matrix(ZZ, 2)
492
sage: shell.displayhook.compute_format_data([a,a])
493
({u'text/plain': '[\n[1 0] [1 0]\n[0 1], [0 1]\n]'}, {})
494
sage: fmt.set_display('ascii_art')
495
sage: shell.displayhook.compute_format_data([a,a])
496
({u'text/plain': [ [1 0] [1 0] ]
497
[ [0 1], [0 1] ]}, {})
498
499
sage: i = var('i')
500
sage: shell.displayhook.compute_format_data(sum(i*x^i, i, 0, 10))
501
({u'text/plain': 10 9 8 7 6 5 4 3 2
502
10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x}, {})
503
sage: fmt.set_display('simple')
504
"""
505
if self.try_format_graphics(obj):
506
return ''
507
s = self.try_format_obj(obj)
508
if s is None:
509
s = super(SagePlainTextFormatter, self).__call__(obj)
510
return s
511
512
513
SPTextFormatter = None
514
515