Coverage for /usr/lib/python3/dist-packages/sympy/interactive/printing.py: 5%
234 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""Tools for setting up printing in interactive sessions. """
3from sympy.external.importtools import version_tuple
4from io import BytesIO
6from sympy.printing.latex import latex as default_latex
7from sympy.printing.preview import preview
8from sympy.utilities.misc import debug
9from sympy.printing.defaults import Printable
12def _init_python_printing(stringify_func, **settings):
13 """Setup printing in Python interactive session. """
14 import sys
15 import builtins
17 def _displayhook(arg):
18 """Python's pretty-printer display hook.
20 This function was adapted from:
22 https://www.python.org/dev/peps/pep-0217/
24 """
25 if arg is not None:
26 builtins._ = None
27 print(stringify_func(arg, **settings))
28 builtins._ = arg
30 sys.displayhook = _displayhook
33def _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor,
34 backcolor, fontsize, latex_mode, print_builtin,
35 latex_printer, scale, **settings):
36 """Setup printing in IPython interactive session. """
37 try:
38 from IPython.lib.latextools import latex_to_png
39 except ImportError:
40 pass
42 # Guess best font color if none was given based on the ip.colors string.
43 # From the IPython documentation:
44 # It has four case-insensitive values: 'nocolor', 'neutral', 'linux',
45 # 'lightbg'. The default is neutral, which should be legible on either
46 # dark or light terminal backgrounds. linux is optimised for dark
47 # backgrounds and lightbg for light ones.
48 if forecolor is None:
49 color = ip.colors.lower()
50 if color == 'lightbg':
51 forecolor = 'Black'
52 elif color == 'linux':
53 forecolor = 'White'
54 else:
55 # No idea, go with gray.
56 forecolor = 'Gray'
57 debug("init_printing: Automatic foreground color:", forecolor)
59 if use_latex == "svg":
60 extra_preamble = "\n\\special{color %s}" % forecolor
61 else:
62 extra_preamble = ""
64 imagesize = 'tight'
65 offset = "0cm,0cm"
66 resolution = round(150*scale)
67 dvi = r"-T %s -D %d -bg %s -fg %s -O %s" % (
68 imagesize, resolution, backcolor, forecolor, offset)
69 dvioptions = dvi.split()
71 svg_scale = 150/72*scale
72 dvioptions_svg = ["--no-fonts", "--scale={}".format(svg_scale)]
74 debug("init_printing: DVIOPTIONS:", dvioptions)
75 debug("init_printing: DVIOPTIONS_SVG:", dvioptions_svg)
77 latex = latex_printer or default_latex
79 def _print_plain(arg, p, cycle):
80 """caller for pretty, for use in IPython 0.11"""
81 if _can_print(arg):
82 p.text(stringify_func(arg))
83 else:
84 p.text(IPython.lib.pretty.pretty(arg))
86 def _preview_wrapper(o):
87 exprbuffer = BytesIO()
88 try:
89 preview(o, output='png', viewer='BytesIO', euler=euler,
90 outputbuffer=exprbuffer, extra_preamble=extra_preamble,
91 dvioptions=dvioptions, fontsize=fontsize)
92 except Exception as e:
93 # IPython swallows exceptions
94 debug("png printing:", "_preview_wrapper exception raised:",
95 repr(e))
96 raise
97 return exprbuffer.getvalue()
99 def _svg_wrapper(o):
100 exprbuffer = BytesIO()
101 try:
102 preview(o, output='svg', viewer='BytesIO', euler=euler,
103 outputbuffer=exprbuffer, extra_preamble=extra_preamble,
104 dvioptions=dvioptions_svg, fontsize=fontsize)
105 except Exception as e:
106 # IPython swallows exceptions
107 debug("svg printing:", "_preview_wrapper exception raised:",
108 repr(e))
109 raise
110 return exprbuffer.getvalue().decode('utf-8')
112 def _matplotlib_wrapper(o):
113 # mathtext can't render some LaTeX commands. For example, it can't
114 # render any LaTeX environments such as array or matrix. So here we
115 # ensure that if mathtext fails to render, we return None.
116 try:
117 try:
118 return latex_to_png(o, color=forecolor, scale=scale)
119 except TypeError: # Old IPython version without color and scale
120 return latex_to_png(o)
121 except ValueError as e:
122 debug('matplotlib exception caught:', repr(e))
123 return None
126 # Hook methods for builtin SymPy printers
127 printing_hooks = ('_latex', '_sympystr', '_pretty', '_sympyrepr')
130 def _can_print(o):
131 """Return True if type o can be printed with one of the SymPy printers.
133 If o is a container type, this is True if and only if every element of
134 o can be printed in this way.
135 """
137 try:
138 # If you're adding another type, make sure you add it to printable_types
139 # later in this file as well
141 builtin_types = (list, tuple, set, frozenset)
142 if isinstance(o, builtin_types):
143 # If the object is a custom subclass with a custom str or
144 # repr, use that instead.
145 if (type(o).__str__ not in (i.__str__ for i in builtin_types) or
146 type(o).__repr__ not in (i.__repr__ for i in builtin_types)):
147 return False
148 return all(_can_print(i) for i in o)
149 elif isinstance(o, dict):
150 return all(_can_print(i) and _can_print(o[i]) for i in o)
151 elif isinstance(o, bool):
152 return False
153 elif isinstance(o, Printable):
154 # types known to SymPy
155 return True
156 elif any(hasattr(o, hook) for hook in printing_hooks):
157 # types which add support themselves
158 return True
159 elif isinstance(o, (float, int)) and print_builtin:
160 return True
161 return False
162 except RuntimeError:
163 return False
164 # This is in case maximum recursion depth is reached.
165 # Since RecursionError is for versions of Python 3.5+
166 # so this is to guard against RecursionError for older versions.
168 def _print_latex_png(o):
169 """
170 A function that returns a png rendered by an external latex
171 distribution, falling back to matplotlib rendering
172 """
173 if _can_print(o):
174 s = latex(o, mode=latex_mode, **settings)
175 if latex_mode == 'plain':
176 s = '$\\displaystyle %s$' % s
177 try:
178 return _preview_wrapper(s)
179 except RuntimeError as e:
180 debug('preview failed with:', repr(e),
181 ' Falling back to matplotlib backend')
182 if latex_mode != 'inline':
183 s = latex(o, mode='inline', **settings)
184 return _matplotlib_wrapper(s)
186 def _print_latex_svg(o):
187 """
188 A function that returns a svg rendered by an external latex
189 distribution, no fallback available.
190 """
191 if _can_print(o):
192 s = latex(o, mode=latex_mode, **settings)
193 if latex_mode == 'plain':
194 s = '$\\displaystyle %s$' % s
195 try:
196 return _svg_wrapper(s)
197 except RuntimeError as e:
198 debug('preview failed with:', repr(e),
199 ' No fallback available.')
201 def _print_latex_matplotlib(o):
202 """
203 A function that returns a png rendered by mathtext
204 """
205 if _can_print(o):
206 s = latex(o, mode='inline', **settings)
207 return _matplotlib_wrapper(s)
209 def _print_latex_text(o):
210 """
211 A function to generate the latex representation of SymPy expressions.
212 """
213 if _can_print(o):
214 s = latex(o, mode=latex_mode, **settings)
215 if latex_mode == 'plain':
216 return '$\\displaystyle %s$' % s
217 return s
219 def _result_display(self, arg):
220 """IPython's pretty-printer display hook, for use in IPython 0.10
222 This function was adapted from:
224 ipython/IPython/hooks.py:155
226 """
227 if self.rc.pprint:
228 out = stringify_func(arg)
230 if '\n' in out:
231 print()
233 print(out)
234 else:
235 print(repr(arg))
237 import IPython
238 if version_tuple(IPython.__version__) >= version_tuple('0.11'):
240 # Printable is our own type, so we handle it with methods instead of
241 # the approach required by builtin types. This allows downstream
242 # packages to override the methods in their own subclasses of Printable,
243 # which avoids the effects of gh-16002.
244 printable_types = [float, tuple, list, set, frozenset, dict, int]
246 plaintext_formatter = ip.display_formatter.formatters['text/plain']
248 # Exception to the rule above: IPython has better dispatching rules
249 # for plaintext printing (xref ipython/ipython#8938), and we can't
250 # use `_repr_pretty_` without hitting a recursion error in _print_plain.
251 for cls in printable_types + [Printable]:
252 plaintext_formatter.for_type(cls, _print_plain)
254 svg_formatter = ip.display_formatter.formatters['image/svg+xml']
255 if use_latex in ('svg', ):
256 debug("init_printing: using svg formatter")
257 for cls in printable_types:
258 svg_formatter.for_type(cls, _print_latex_svg)
259 Printable._repr_svg_ = _print_latex_svg
260 else:
261 debug("init_printing: not using any svg formatter")
262 for cls in printable_types:
263 # Better way to set this, but currently does not work in IPython
264 #png_formatter.for_type(cls, None)
265 if cls in svg_formatter.type_printers:
266 svg_formatter.type_printers.pop(cls)
267 Printable._repr_svg_ = Printable._repr_disabled
269 png_formatter = ip.display_formatter.formatters['image/png']
270 if use_latex in (True, 'png'):
271 debug("init_printing: using png formatter")
272 for cls in printable_types:
273 png_formatter.for_type(cls, _print_latex_png)
274 Printable._repr_png_ = _print_latex_png
275 elif use_latex == 'matplotlib':
276 debug("init_printing: using matplotlib formatter")
277 for cls in printable_types:
278 png_formatter.for_type(cls, _print_latex_matplotlib)
279 Printable._repr_png_ = _print_latex_matplotlib
280 else:
281 debug("init_printing: not using any png formatter")
282 for cls in printable_types:
283 # Better way to set this, but currently does not work in IPython
284 #png_formatter.for_type(cls, None)
285 if cls in png_formatter.type_printers:
286 png_formatter.type_printers.pop(cls)
287 Printable._repr_png_ = Printable._repr_disabled
289 latex_formatter = ip.display_formatter.formatters['text/latex']
290 if use_latex in (True, 'mathjax'):
291 debug("init_printing: using mathjax formatter")
292 for cls in printable_types:
293 latex_formatter.for_type(cls, _print_latex_text)
294 Printable._repr_latex_ = _print_latex_text
295 else:
296 debug("init_printing: not using text/latex formatter")
297 for cls in printable_types:
298 # Better way to set this, but currently does not work in IPython
299 #latex_formatter.for_type(cls, None)
300 if cls in latex_formatter.type_printers:
301 latex_formatter.type_printers.pop(cls)
302 Printable._repr_latex_ = Printable._repr_disabled
304 else:
305 ip.set_hook('result_display', _result_display)
307def _is_ipython(shell):
308 """Is a shell instance an IPython shell?"""
309 # shortcut, so we don't import IPython if we don't have to
310 from sys import modules
311 if 'IPython' not in modules:
312 return False
313 try:
314 from IPython.core.interactiveshell import InteractiveShell
315 except ImportError:
316 # IPython < 0.11
317 try:
318 from IPython.iplib import InteractiveShell
319 except ImportError:
320 # Reaching this points means IPython has changed in a backward-incompatible way
321 # that we don't know about. Warn?
322 return False
323 return isinstance(shell, InteractiveShell)
325# Used by the doctester to override the default for no_global
326NO_GLOBAL = False
328def init_printing(pretty_print=True, order=None, use_unicode=None,
329 use_latex=None, wrap_line=None, num_columns=None,
330 no_global=False, ip=None, euler=False, forecolor=None,
331 backcolor='Transparent', fontsize='10pt',
332 latex_mode='plain', print_builtin=True,
333 str_printer=None, pretty_printer=None,
334 latex_printer=None, scale=1.0, **settings):
335 r"""
336 Initializes pretty-printer depending on the environment.
338 Parameters
339 ==========
341 pretty_print : bool, default=True
342 If ``True``, use :func:`~.pretty_print` to stringify or the provided pretty
343 printer; if ``False``, use :func:`~.sstrrepr` to stringify or the provided string
344 printer.
345 order : string or None, default='lex'
346 There are a few different settings for this parameter:
347 ``'lex'`` (default), which is lexographic order;
348 ``'grlex'``, which is graded lexographic order;
349 ``'grevlex'``, which is reversed graded lexographic order;
350 ``'old'``, which is used for compatibility reasons and for long expressions;
351 ``None``, which sets it to lex.
352 use_unicode : bool or None, default=None
353 If ``True``, use unicode characters;
354 if ``False``, do not use unicode characters;
355 if ``None``, make a guess based on the environment.
356 use_latex : string, bool, or None, default=None
357 If ``True``, use default LaTeX rendering in GUI interfaces (png and
358 mathjax);
359 if ``False``, do not use LaTeX rendering;
360 if ``None``, make a guess based on the environment;
361 if ``'png'``, enable LaTeX rendering with an external LaTeX compiler,
362 falling back to matplotlib if external compilation fails;
363 if ``'matplotlib'``, enable LaTeX rendering with matplotlib;
364 if ``'mathjax'``, enable LaTeX text generation, for example MathJax
365 rendering in IPython notebook or text rendering in LaTeX documents;
366 if ``'svg'``, enable LaTeX rendering with an external latex compiler,
367 no fallback
368 wrap_line : bool
369 If True, lines will wrap at the end; if False, they will not wrap
370 but continue as one line. This is only relevant if ``pretty_print`` is
371 True.
372 num_columns : int or None, default=None
373 If ``int``, number of columns before wrapping is set to num_columns; if
374 ``None``, number of columns before wrapping is set to terminal width.
375 This is only relevant if ``pretty_print`` is ``True``.
376 no_global : bool, default=False
377 If ``True``, the settings become system wide;
378 if ``False``, use just for this console/session.
379 ip : An interactive console
380 This can either be an instance of IPython,
381 or a class that derives from code.InteractiveConsole.
382 euler : bool, optional, default=False
383 Loads the euler package in the LaTeX preamble for handwritten style
384 fonts (https://www.ctan.org/pkg/euler).
385 forecolor : string or None, optional, default=None
386 DVI setting for foreground color. ``None`` means that either ``'Black'``,
387 ``'White'``, or ``'Gray'`` will be selected based on a guess of the IPython
388 terminal color setting. See notes.
389 backcolor : string, optional, default='Transparent'
390 DVI setting for background color. See notes.
391 fontsize : string or int, optional, default='10pt'
392 A font size to pass to the LaTeX documentclass function in the
393 preamble. Note that the options are limited by the documentclass.
394 Consider using scale instead.
395 latex_mode : string, optional, default='plain'
396 The mode used in the LaTeX printer. Can be one of:
397 ``{'inline'|'plain'|'equation'|'equation*'}``.
398 print_builtin : boolean, optional, default=True
399 If ``True`` then floats and integers will be printed. If ``False`` the
400 printer will only print SymPy types.
401 str_printer : function, optional, default=None
402 A custom string printer function. This should mimic
403 :func:`~.sstrrepr()`.
404 pretty_printer : function, optional, default=None
405 A custom pretty printer. This should mimic :func:`~.pretty()`.
406 latex_printer : function, optional, default=None
407 A custom LaTeX printer. This should mimic :func:`~.latex()`.
408 scale : float, optional, default=1.0
409 Scale the LaTeX output when using the ``'png'`` or ``'svg'`` backends.
410 Useful for high dpi screens.
411 settings :
412 Any additional settings for the ``latex`` and ``pretty`` commands can
413 be used to fine-tune the output.
415 Examples
416 ========
418 >>> from sympy.interactive import init_printing
419 >>> from sympy import Symbol, sqrt
420 >>> from sympy.abc import x, y
421 >>> sqrt(5)
422 sqrt(5)
423 >>> init_printing(pretty_print=True) # doctest: +SKIP
424 >>> sqrt(5) # doctest: +SKIP
425 ___
426 \/ 5
427 >>> theta = Symbol('theta') # doctest: +SKIP
428 >>> init_printing(use_unicode=True) # doctest: +SKIP
429 >>> theta # doctest: +SKIP
430 \u03b8
431 >>> init_printing(use_unicode=False) # doctest: +SKIP
432 >>> theta # doctest: +SKIP
433 theta
434 >>> init_printing(order='lex') # doctest: +SKIP
435 >>> str(y + x + y**2 + x**2) # doctest: +SKIP
436 x**2 + x + y**2 + y
437 >>> init_printing(order='grlex') # doctest: +SKIP
438 >>> str(y + x + y**2 + x**2) # doctest: +SKIP
439 x**2 + x + y**2 + y
440 >>> init_printing(order='grevlex') # doctest: +SKIP
441 >>> str(y * x**2 + x * y**2) # doctest: +SKIP
442 x**2*y + x*y**2
443 >>> init_printing(order='old') # doctest: +SKIP
444 >>> str(x**2 + y**2 + x + y) # doctest: +SKIP
445 x**2 + x + y**2 + y
446 >>> init_printing(num_columns=10) # doctest: +SKIP
447 >>> x**2 + x + y**2 + y # doctest: +SKIP
448 x + y +
449 x**2 + y**2
451 Notes
452 =====
454 The foreground and background colors can be selected when using ``'png'`` or
455 ``'svg'`` LaTeX rendering. Note that before the ``init_printing`` command is
456 executed, the LaTeX rendering is handled by the IPython console and not SymPy.
458 The colors can be selected among the 68 standard colors known to ``dvips``,
459 for a list see [1]_. In addition, the background color can be
460 set to ``'Transparent'`` (which is the default value).
462 When using the ``'Auto'`` foreground color, the guess is based on the
463 ``colors`` variable in the IPython console, see [2]_. Hence, if
464 that variable is set correctly in your IPython console, there is a high
465 chance that the output will be readable, although manual settings may be
466 needed.
469 References
470 ==========
472 .. [1] https://en.wikibooks.org/wiki/LaTeX/Colors#The_68_standard_colors_known_to_dvips
474 .. [2] https://ipython.readthedocs.io/en/stable/config/details.html#terminal-colors
476 See Also
477 ========
479 sympy.printing.latex
480 sympy.printing.pretty
482 """
483 import sys
484 from sympy.printing.printer import Printer
486 if pretty_print:
487 if pretty_printer is not None:
488 stringify_func = pretty_printer
489 else:
490 from sympy.printing import pretty as stringify_func
491 else:
492 if str_printer is not None:
493 stringify_func = str_printer
494 else:
495 from sympy.printing import sstrrepr as stringify_func
497 # Even if ip is not passed, double check that not in IPython shell
498 in_ipython = False
499 if ip is None:
500 try:
501 ip = get_ipython()
502 except NameError:
503 pass
504 else:
505 in_ipython = (ip is not None)
507 if ip and not in_ipython:
508 in_ipython = _is_ipython(ip)
510 if in_ipython and pretty_print:
511 try:
512 import IPython
513 # IPython 1.0 deprecates the frontend module, so we import directly
514 # from the terminal module to prevent a deprecation message from being
515 # shown.
516 if version_tuple(IPython.__version__) >= version_tuple('1.0'):
517 from IPython.terminal.interactiveshell import TerminalInteractiveShell
518 else:
519 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
520 from code import InteractiveConsole
521 except ImportError:
522 pass
523 else:
524 # This will be True if we are in the qtconsole or notebook
525 if not isinstance(ip, (InteractiveConsole, TerminalInteractiveShell)) \
526 and 'ipython-console' not in ''.join(sys.argv):
527 if use_unicode is None:
528 debug("init_printing: Setting use_unicode to True")
529 use_unicode = True
530 if use_latex is None:
531 debug("init_printing: Setting use_latex to True")
532 use_latex = True
534 if not NO_GLOBAL and not no_global:
535 Printer.set_global_settings(order=order, use_unicode=use_unicode,
536 wrap_line=wrap_line, num_columns=num_columns)
537 else:
538 _stringify_func = stringify_func
540 if pretty_print:
541 stringify_func = lambda expr, **settings: \
542 _stringify_func(expr, order=order,
543 use_unicode=use_unicode,
544 wrap_line=wrap_line,
545 num_columns=num_columns,
546 **settings)
547 else:
548 stringify_func = \
549 lambda expr, **settings: _stringify_func(
550 expr, order=order, **settings)
552 if in_ipython:
553 mode_in_settings = settings.pop("mode", None)
554 if mode_in_settings:
555 debug("init_printing: Mode is not able to be set due to internals"
556 "of IPython printing")
557 _init_ipython_printing(ip, stringify_func, use_latex, euler,
558 forecolor, backcolor, fontsize, latex_mode,
559 print_builtin, latex_printer, scale,
560 **settings)
561 else:
562 _init_python_printing(stringify_func, **settings)