123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 |
- """Tools for setting up printing in interactive sessions. """
- from sympy.external.importtools import version_tuple
- from io import BytesIO
- from sympy.printing.latex import latex as default_latex
- from sympy.printing.preview import preview
- from sympy.utilities.misc import debug
- from sympy.printing.defaults import Printable
- def _init_python_printing(stringify_func, **settings):
- """Setup printing in Python interactive session. """
- import sys
- import builtins
- def _displayhook(arg):
- """Python's pretty-printer display hook.
- This function was adapted from:
- http://www.python.org/dev/peps/pep-0217/
- """
- if arg is not None:
- builtins._ = None
- print(stringify_func(arg, **settings))
- builtins._ = arg
- sys.displayhook = _displayhook
- def _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor,
- backcolor, fontsize, latex_mode, print_builtin,
- latex_printer, scale, **settings):
- """Setup printing in IPython interactive session. """
- try:
- from IPython.lib.latextools import latex_to_png
- except ImportError:
- pass
- # Guess best font color if none was given based on the ip.colors string.
- # From the IPython documentation:
- # It has four case-insensitive values: 'nocolor', 'neutral', 'linux',
- # 'lightbg'. The default is neutral, which should be legible on either
- # dark or light terminal backgrounds. linux is optimised for dark
- # backgrounds and lightbg for light ones.
- if forecolor is None:
- color = ip.colors.lower()
- if color == 'lightbg':
- forecolor = 'Black'
- elif color == 'linux':
- forecolor = 'White'
- else:
- # No idea, go with gray.
- forecolor = 'Gray'
- debug("init_printing: Automatic foreground color:", forecolor)
- preamble = "\\documentclass[varwidth,%s]{standalone}\n" \
- "\\usepackage{amsmath,amsfonts}%s\\begin{document}"
- if euler:
- addpackages = '\\usepackage{euler}'
- else:
- addpackages = ''
- if use_latex == "svg":
- addpackages = addpackages + "\n\\special{color %s}" % forecolor
- preamble = preamble % (fontsize, addpackages)
- imagesize = 'tight'
- offset = "0cm,0cm"
- resolution = round(150*scale)
- dvi = r"-T %s -D %d -bg %s -fg %s -O %s" % (
- imagesize, resolution, backcolor, forecolor, offset)
- dvioptions = dvi.split()
- svg_scale = 150/72*scale
- dvioptions_svg = ["--no-fonts", "--scale={}".format(svg_scale)]
- debug("init_printing: DVIOPTIONS:", dvioptions)
- debug("init_printing: DVIOPTIONS_SVG:", dvioptions_svg)
- debug("init_printing: PREAMBLE:", preamble)
- latex = latex_printer or default_latex
- def _print_plain(arg, p, cycle):
- """caller for pretty, for use in IPython 0.11"""
- if _can_print(arg):
- p.text(stringify_func(arg))
- else:
- p.text(IPython.lib.pretty.pretty(arg))
- def _preview_wrapper(o):
- exprbuffer = BytesIO()
- try:
- preview(o, output='png', viewer='BytesIO',
- outputbuffer=exprbuffer, preamble=preamble,
- dvioptions=dvioptions)
- except Exception as e:
- # IPython swallows exceptions
- debug("png printing:", "_preview_wrapper exception raised:",
- repr(e))
- raise
- return exprbuffer.getvalue()
- def _svg_wrapper(o):
- exprbuffer = BytesIO()
- try:
- preview(o, output='svg', viewer='BytesIO',
- outputbuffer=exprbuffer, preamble=preamble,
- dvioptions=dvioptions_svg)
- except Exception as e:
- # IPython swallows exceptions
- debug("svg printing:", "_preview_wrapper exception raised:",
- repr(e))
- raise
- return exprbuffer.getvalue().decode('utf-8')
- def _matplotlib_wrapper(o):
- # mathtext can't render some LaTeX commands. For example, it can't
- # render any LaTeX environments such as array or matrix. So here we
- # ensure that if mathtext fails to render, we return None.
- try:
- try:
- return latex_to_png(o, color=forecolor, scale=scale)
- except TypeError: # Old IPython version without color and scale
- return latex_to_png(o)
- except ValueError as e:
- debug('matplotlib exception caught:', repr(e))
- return None
- # Hook methods for builtin SymPy printers
- printing_hooks = ('_latex', '_sympystr', '_pretty', '_sympyrepr')
- def _can_print(o):
- """Return True if type o can be printed with one of the SymPy printers.
- If o is a container type, this is True if and only if every element of
- o can be printed in this way.
- """
- try:
- # If you're adding another type, make sure you add it to printable_types
- # later in this file as well
- builtin_types = (list, tuple, set, frozenset)
- if isinstance(o, builtin_types):
- # If the object is a custom subclass with a custom str or
- # repr, use that instead.
- if (type(o).__str__ not in (i.__str__ for i in builtin_types) or
- type(o).__repr__ not in (i.__repr__ for i in builtin_types)):
- return False
- return all(_can_print(i) for i in o)
- elif isinstance(o, dict):
- return all(_can_print(i) and _can_print(o[i]) for i in o)
- elif isinstance(o, bool):
- return False
- elif isinstance(o, Printable):
- # types known to SymPy
- return True
- elif any(hasattr(o, hook) for hook in printing_hooks):
- # types which add support themselves
- return True
- elif isinstance(o, (float, int)) and print_builtin:
- return True
- return False
- except RuntimeError:
- return False
- # This is in case maximum recursion depth is reached.
- # Since RecursionError is for versions of Python 3.5+
- # so this is to guard against RecursionError for older versions.
- def _print_latex_png(o):
- """
- A function that returns a png rendered by an external latex
- distribution, falling back to matplotlib rendering
- """
- if _can_print(o):
- s = latex(o, mode=latex_mode, **settings)
- if latex_mode == 'plain':
- s = '$\\displaystyle %s$' % s
- try:
- return _preview_wrapper(s)
- except RuntimeError as e:
- debug('preview failed with:', repr(e),
- ' Falling back to matplotlib backend')
- if latex_mode != 'inline':
- s = latex(o, mode='inline', **settings)
- return _matplotlib_wrapper(s)
- def _print_latex_svg(o):
- """
- A function that returns a svg rendered by an external latex
- distribution, no fallback available.
- """
- if _can_print(o):
- s = latex(o, mode=latex_mode, **settings)
- if latex_mode == 'plain':
- s = '$\\displaystyle %s$' % s
- try:
- return _svg_wrapper(s)
- except RuntimeError as e:
- debug('preview failed with:', repr(e),
- ' No fallback available.')
- def _print_latex_matplotlib(o):
- """
- A function that returns a png rendered by mathtext
- """
- if _can_print(o):
- s = latex(o, mode='inline', **settings)
- return _matplotlib_wrapper(s)
- def _print_latex_text(o):
- """
- A function to generate the latex representation of SymPy expressions.
- """
- if _can_print(o):
- s = latex(o, mode=latex_mode, **settings)
- if latex_mode == 'plain':
- return '$\\displaystyle %s$' % s
- return s
- def _result_display(self, arg):
- """IPython's pretty-printer display hook, for use in IPython 0.10
- This function was adapted from:
- ipython/IPython/hooks.py:155
- """
- if self.rc.pprint:
- out = stringify_func(arg)
- if '\n' in out:
- print()
- print(out)
- else:
- print(repr(arg))
- import IPython
- if version_tuple(IPython.__version__) >= version_tuple('0.11'):
- # Printable is our own type, so we handle it with methods instead of
- # the approach required by builtin types. This allows downstream
- # packages to override the methods in their own subclasses of Printable,
- # which avoids the effects of gh-16002.
- printable_types = [float, tuple, list, set, frozenset, dict, int]
- plaintext_formatter = ip.display_formatter.formatters['text/plain']
- # Exception to the rule above: IPython has better dispatching rules
- # for plaintext printing (xref ipython/ipython#8938), and we can't
- # use `_repr_pretty_` without hitting a recursion error in _print_plain.
- for cls in printable_types + [Printable]:
- plaintext_formatter.for_type(cls, _print_plain)
- svg_formatter = ip.display_formatter.formatters['image/svg+xml']
- if use_latex in ('svg', ):
- debug("init_printing: using svg formatter")
- for cls in printable_types:
- svg_formatter.for_type(cls, _print_latex_svg)
- Printable._repr_svg_ = _print_latex_svg
- else:
- debug("init_printing: not using any svg formatter")
- for cls in printable_types:
- # Better way to set this, but currently does not work in IPython
- #png_formatter.for_type(cls, None)
- if cls in svg_formatter.type_printers:
- svg_formatter.type_printers.pop(cls)
- Printable._repr_svg_ = Printable._repr_disabled
- png_formatter = ip.display_formatter.formatters['image/png']
- if use_latex in (True, 'png'):
- debug("init_printing: using png formatter")
- for cls in printable_types:
- png_formatter.for_type(cls, _print_latex_png)
- Printable._repr_png_ = _print_latex_png
- elif use_latex == 'matplotlib':
- debug("init_printing: using matplotlib formatter")
- for cls in printable_types:
- png_formatter.for_type(cls, _print_latex_matplotlib)
- Printable._repr_png_ = _print_latex_matplotlib
- else:
- debug("init_printing: not using any png formatter")
- for cls in printable_types:
- # Better way to set this, but currently does not work in IPython
- #png_formatter.for_type(cls, None)
- if cls in png_formatter.type_printers:
- png_formatter.type_printers.pop(cls)
- Printable._repr_png_ = Printable._repr_disabled
- latex_formatter = ip.display_formatter.formatters['text/latex']
- if use_latex in (True, 'mathjax'):
- debug("init_printing: using mathjax formatter")
- for cls in printable_types:
- latex_formatter.for_type(cls, _print_latex_text)
- Printable._repr_latex_ = _print_latex_text
- else:
- debug("init_printing: not using text/latex formatter")
- for cls in printable_types:
- # Better way to set this, but currently does not work in IPython
- #latex_formatter.for_type(cls, None)
- if cls in latex_formatter.type_printers:
- latex_formatter.type_printers.pop(cls)
- Printable._repr_latex_ = Printable._repr_disabled
- else:
- ip.set_hook('result_display', _result_display)
- def _is_ipython(shell):
- """Is a shell instance an IPython shell?"""
- # shortcut, so we don't import IPython if we don't have to
- from sys import modules
- if 'IPython' not in modules:
- return False
- try:
- from IPython.core.interactiveshell import InteractiveShell
- except ImportError:
- # IPython < 0.11
- try:
- from IPython.iplib import InteractiveShell
- except ImportError:
- # Reaching this points means IPython has changed in a backward-incompatible way
- # that we don't know about. Warn?
- return False
- return isinstance(shell, InteractiveShell)
- # Used by the doctester to override the default for no_global
- NO_GLOBAL = False
- def init_printing(pretty_print=True, order=None, use_unicode=None,
- use_latex=None, wrap_line=None, num_columns=None,
- no_global=False, ip=None, euler=False, forecolor=None,
- backcolor='Transparent', fontsize='10pt',
- latex_mode='plain', print_builtin=True,
- str_printer=None, pretty_printer=None,
- latex_printer=None, scale=1.0, **settings):
- r"""
- Initializes pretty-printer depending on the environment.
- Parameters
- ==========
- pretty_print : bool, default=True
- If ``True``, use :func:`~.pretty_print` to stringify or the provided pretty
- printer; if ``False``, use :func:`~.sstrrepr` to stringify or the provided string
- printer.
- order : string or None, default='lex'
- There are a few different settings for this parameter:
- ``'lex'`` (default), which is lexographic order;
- ``'grlex'``, which is graded lexographic order;
- ``'grevlex'``, which is reversed graded lexographic order;
- ``'old'``, which is used for compatibility reasons and for long expressions;
- ``None``, which sets it to lex.
- use_unicode : bool or None, default=None
- If ``True``, use unicode characters;
- if ``False``, do not use unicode characters;
- if ``None``, make a guess based on the environment.
- use_latex : string, bool, or None, default=None
- If ``True``, use default LaTeX rendering in GUI interfaces (png and
- mathjax);
- if ``False``, do not use LaTeX rendering;
- if ``None``, make a guess based on the environment;
- if ``'png'``, enable LaTeX rendering with an external LaTeX compiler,
- falling back to matplotlib if external compilation fails;
- if ``'matplotlib'``, enable LaTeX rendering with matplotlib;
- if ``'mathjax'``, enable LaTeX text generation, for example MathJax
- rendering in IPython notebook or text rendering in LaTeX documents;
- if ``'svg'``, enable LaTeX rendering with an external latex compiler,
- no fallback
- wrap_line : bool
- If True, lines will wrap at the end; if False, they will not wrap
- but continue as one line. This is only relevant if ``pretty_print`` is
- True.
- num_columns : int or None, default=None
- If ``int``, number of columns before wrapping is set to num_columns; if
- ``None``, number of columns before wrapping is set to terminal width.
- This is only relevant if ``pretty_print`` is ``True``.
- no_global : bool, default=False
- If ``True``, the settings become system wide;
- if ``False``, use just for this console/session.
- ip : An interactive console
- This can either be an instance of IPython,
- or a class that derives from code.InteractiveConsole.
- euler : bool, optional, default=False
- Loads the euler package in the LaTeX preamble for handwritten style
- fonts (http://www.ctan.org/pkg/euler).
- forecolor : string or None, optional, default=None
- DVI setting for foreground color. ``None`` means that either ``'Black'``,
- ``'White'``, or ``'Gray'`` will be selected based on a guess of the IPython
- terminal color setting. See notes.
- backcolor : string, optional, default='Transparent'
- DVI setting for background color. See notes.
- fontsize : string, optional, default='10pt'
- A font size to pass to the LaTeX documentclass function in the
- preamble. Note that the options are limited by the documentclass.
- Consider using scale instead.
- latex_mode : string, optional, default='plain'
- The mode used in the LaTeX printer. Can be one of:
- ``{'inline'|'plain'|'equation'|'equation*'}``.
- print_builtin : boolean, optional, default=True
- If ``True`` then floats and integers will be printed. If ``False`` the
- printer will only print SymPy types.
- str_printer : function, optional, default=None
- A custom string printer function. This should mimic
- :func:`~.sstrrepr()`.
- pretty_printer : function, optional, default=None
- A custom pretty printer. This should mimic :func:`~.pretty()`.
- latex_printer : function, optional, default=None
- A custom LaTeX printer. This should mimic :func:`~.latex()`.
- scale : float, optional, default=1.0
- Scale the LaTeX output when using the ``'png'`` or ``'svg'`` backends.
- Useful for high dpi screens.
- settings :
- Any additional settings for the ``latex`` and ``pretty`` commands can
- be used to fine-tune the output.
- Examples
- ========
- >>> from sympy.interactive import init_printing
- >>> from sympy import Symbol, sqrt
- >>> from sympy.abc import x, y
- >>> sqrt(5)
- sqrt(5)
- >>> init_printing(pretty_print=True) # doctest: +SKIP
- >>> sqrt(5) # doctest: +SKIP
- ___
- \/ 5
- >>> theta = Symbol('theta') # doctest: +SKIP
- >>> init_printing(use_unicode=True) # doctest: +SKIP
- >>> theta # doctest: +SKIP
- \u03b8
- >>> init_printing(use_unicode=False) # doctest: +SKIP
- >>> theta # doctest: +SKIP
- theta
- >>> init_printing(order='lex') # doctest: +SKIP
- >>> str(y + x + y**2 + x**2) # doctest: +SKIP
- x**2 + x + y**2 + y
- >>> init_printing(order='grlex') # doctest: +SKIP
- >>> str(y + x + y**2 + x**2) # doctest: +SKIP
- x**2 + x + y**2 + y
- >>> init_printing(order='grevlex') # doctest: +SKIP
- >>> str(y * x**2 + x * y**2) # doctest: +SKIP
- x**2*y + x*y**2
- >>> init_printing(order='old') # doctest: +SKIP
- >>> str(x**2 + y**2 + x + y) # doctest: +SKIP
- x**2 + x + y**2 + y
- >>> init_printing(num_columns=10) # doctest: +SKIP
- >>> x**2 + x + y**2 + y # doctest: +SKIP
- x + y +
- x**2 + y**2
- Notes
- =====
- The foreground and background colors can be selected when using ``'png'`` or
- ``'svg'`` LaTeX rendering. Note that before the ``init_printing`` command is
- executed, the LaTeX rendering is handled by the IPython console and not SymPy.
- The colors can be selected among the 68 standard colors known to ``dvips``,
- for a list see [1]_. In addition, the background color can be
- set to ``'Transparent'`` (which is the default value).
- When using the ``'Auto'`` foreground color, the guess is based on the
- ``colors`` variable in the IPython console, see [2]_. Hence, if
- that variable is set correctly in your IPython console, there is a high
- chance that the output will be readable, although manual settings may be
- needed.
- References
- ==========
- .. [1] https://en.wikibooks.org/wiki/LaTeX/Colors#The_68_standard_colors_known_to_dvips
- .. [2] https://ipython.readthedocs.io/en/stable/config/details.html#terminal-colors
- See Also
- ========
- sympy.printing.latex
- sympy.printing.pretty
- """
- import sys
- from sympy.printing.printer import Printer
- if pretty_print:
- if pretty_printer is not None:
- stringify_func = pretty_printer
- else:
- from sympy.printing import pretty as stringify_func
- else:
- if str_printer is not None:
- stringify_func = str_printer
- else:
- from sympy.printing import sstrrepr as stringify_func
- # Even if ip is not passed, double check that not in IPython shell
- in_ipython = False
- if ip is None:
- try:
- ip = get_ipython()
- except NameError:
- pass
- else:
- in_ipython = (ip is not None)
- if ip and not in_ipython:
- in_ipython = _is_ipython(ip)
- if in_ipython and pretty_print:
- try:
- import IPython
- # IPython 1.0 deprecates the frontend module, so we import directly
- # from the terminal module to prevent a deprecation message from being
- # shown.
- if version_tuple(IPython.__version__) >= version_tuple('1.0'):
- from IPython.terminal.interactiveshell import TerminalInteractiveShell
- else:
- from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
- from code import InteractiveConsole
- except ImportError:
- pass
- else:
- # This will be True if we are in the qtconsole or notebook
- if not isinstance(ip, (InteractiveConsole, TerminalInteractiveShell)) \
- and 'ipython-console' not in ''.join(sys.argv):
- if use_unicode is None:
- debug("init_printing: Setting use_unicode to True")
- use_unicode = True
- if use_latex is None:
- debug("init_printing: Setting use_latex to True")
- use_latex = True
- if not NO_GLOBAL and not no_global:
- Printer.set_global_settings(order=order, use_unicode=use_unicode,
- wrap_line=wrap_line, num_columns=num_columns)
- else:
- _stringify_func = stringify_func
- if pretty_print:
- stringify_func = lambda expr, **settings: \
- _stringify_func(expr, order=order,
- use_unicode=use_unicode,
- wrap_line=wrap_line,
- num_columns=num_columns,
- **settings)
- else:
- stringify_func = \
- lambda expr, **settings: _stringify_func(
- expr, order=order, **settings)
- if in_ipython:
- mode_in_settings = settings.pop("mode", None)
- if mode_in_settings:
- debug("init_printing: Mode is not able to be set due to internals"
- "of IPython printing")
- _init_ipython_printing(ip, stringify_func, use_latex, euler,
- forecolor, backcolor, fontsize, latex_mode,
- print_builtin, latex_printer, scale,
- **settings)
- else:
- _init_python_printing(stringify_func, **settings)
|