Spaces:
Sleeping
Sleeping
"""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: | |
https://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) | |
if use_latex == "svg": | |
extra_preamble = "\n\\special{color %s}" % forecolor | |
else: | |
extra_preamble = "" | |
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) | |
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', euler=euler, | |
outputbuffer=exprbuffer, extra_preamble=extra_preamble, | |
dvioptions=dvioptions, fontsize=fontsize) | |
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', euler=euler, | |
outputbuffer=exprbuffer, extra_preamble=extra_preamble, | |
dvioptions=dvioptions_svg, fontsize=fontsize) | |
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 (https://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 or int, 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) | |