Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
608 lines (482 sloc) 21.6 KB
# -*- coding: utf-8 -*-
"""
IPython Backend for the Sage Rich Output System
This module defines the IPython backends for
:mod:`sage.repl.rich_output`.
"""
#*****************************************************************************
# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com>
#
# Distributed under the terms of the GNU General Public License (GPL)
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
# http://www.gnu.org/licenses/
#*****************************************************************************
import os
import sys
from IPython.display import publish_display_data
from sage.repl.rich_output.backend_base import BackendBase
from sage.repl.rich_output.output_catalog import *
class BackendIPython(BackendBase):
"""
Common base for the IPython UIs
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPython
sage: BackendIPython()._repr_()
Traceback (most recent call last):
NotImplementedError: derived classes must implement this method
"""
def install(self, **kwds):
"""
Switch the Sage rich output to the IPython backend
INPUT:
- ``shell`` -- keyword argument. The IPython shell.
No tests since switching away from the doctest rich output
backend will break the doctests.
EXAMPLES::
sage: from sage.repl.interpreter import get_test_shell
sage: from sage.repl.rich_output.backend_ipython import BackendIPython
sage: backend = BackendIPython()
sage: shell = get_test_shell()
sage: backend.install(shell=shell)
sage: shell.run_cell('1+1')
2
"""
shell = kwds['shell']
from sage.repl.display.formatter import SageDisplayFormatter
shell.display_formatter = SageDisplayFormatter(parent=shell)
shell.configurables.append(shell.display_formatter)
def set_underscore_variable(self, obj):
"""
Set the ``_`` builtin variable.
Since IPython handles the history itself, this does nothing.
INPUT:
- ``obj`` -- anything.
EXAMPLES::
sage: from sage.repl.interpreter import get_test_shell
sage: from sage.repl.rich_output.backend_ipython import BackendIPython
sage: backend = BackendIPython()
sage: backend.set_underscore_variable(123)
sage: _
0
"""
pass
def display_immediately(self, plain_text, rich_output):
"""
Show output immediately.
This method is similar to the rich output :meth:`displayhook`,
except that it can be invoked at any time.
INPUT:
Same as :meth:`displayhook`.
OUTPUT:
This method does not return anything.
EXAMPLES::
sage: from sage.repl.rich_output.output_basic import OutputPlainText
sage: plain_text = OutputPlainText.example()
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
sage: backend = BackendIPythonNotebook()
sage: _ = backend.display_immediately(plain_text, plain_text)
Example plain text output
"""
formatted, metadata = self.displayhook(plain_text, rich_output)
if not formatted:
return
publish_display_data(data=formatted, metadata=metadata)
class BackendIPythonCommandline(BackendIPython):
"""
Backend for the IPython Command Line
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
sage: BackendIPythonCommandline()
IPython command line
"""
def default_preferences(self):
"""
Return the backend's display preferences
The default for the commandline is to not plot graphs since
the launching of an external viewer is considered too
disruptive.
OUTPUT:
Instance of
:class:`~sage.repl.rich_output.preferences.DisplayPreferences`.
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
sage: backend = BackendIPythonCommandline()
sage: backend.default_preferences()
Display preferences:
* graphics is not specified
* supplemental_plot = never
* text is not specified
"""
from sage.repl.rich_output.preferences import DisplayPreferences
return DisplayPreferences(supplemental_plot='never')
def _repr_(self):
"""
Return a string representation
OUTPUT:
String.
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
sage: backend = BackendIPythonCommandline()
sage: backend._repr_()
'IPython command line'
"""
return 'IPython command line'
def supported_output(self):
"""
Return the outputs that are supported by the IPython commandline backend.
OUTPUT:
Iterable of output container classes, that is, subclass of
:class:`~sage.repl.rich_output.output_basic.OutputBase`).
The order is ignored.
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
sage: backend = BackendIPythonCommandline()
sage: supp = backend.supported_output(); supp # random output
set([<class 'sage.repl.rich_output.output_graphics.OutputImageGif'>,
...,
<class 'sage.repl.rich_output.output_graphics.OutputImagePng'>])
sage: from sage.repl.rich_output.output_basic import OutputLatex
sage: OutputLatex in supp
True
"""
return set([
OutputPlainText, OutputAsciiArt, OutputUnicodeArt, OutputLatex,
OutputImagePng, OutputImageGif,
OutputImagePdf, OutputImageDvi,
OutputSceneJmol, OutputSceneWavefront, OutputSceneThreejs,
])
def displayhook(self, plain_text, rich_output):
"""
Backend implementation of the displayhook
INPUT:
- ``plain_text`` -- instance of
:class:`~sage.repl.rich_output.output_basic.OutputPlainText`. The
plain text version of the output.
- ``rich_output`` -- instance of an output container class
(subclass of
:class:`~sage.repl.rich_output.output_basic.OutputBase`). Guaranteed
to be one of the output containers returned from
:meth:`supported_output`, possibly the same as
``plain_text``.
OUTPUT:
The IPython commandline display hook returns the IPython
display data, a pair of dictionaries. The first dictionary
contains mime types as keys and the respective output as
value. The second dictionary is metadata.
EXAMPLES::
sage: from sage.repl.rich_output.output_basic import OutputPlainText
sage: plain_text = OutputPlainText.example()
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
sage: backend = BackendIPythonCommandline()
sage: backend.displayhook(plain_text, plain_text)
({u'text/plain': u'Example plain text output'}, {})
TESTS:
We verify that unicode strings work::
sage: class Foo(sage.structure.sage_object.SageObject):
....: def _rich_repr_(self, dm):
....: return dm.types.OutputPlainText(u'Motörhead')
sage: from sage.repl.rich_output import get_display_manager
sage: dm = get_display_manager()
sage: dm.displayhook(Foo())
({u'text/plain': u'Mot\xf6rhead'}, {})
"""
if isinstance(rich_output, OutputPlainText):
return ({u'text/plain': rich_output.text.get_unicode()}, {})
elif isinstance(rich_output, OutputAsciiArt):
return ({u'text/plain': rich_output.ascii_art.get_unicode()}, {})
elif isinstance(rich_output, OutputUnicodeArt):
return ({u'text/plain': rich_output.unicode_art.get_unicode()}, {})
elif isinstance(rich_output, OutputLatex):
return ({u'text/plain': rich_output.latex.get_unicode()}, {})
elif isinstance(rich_output, OutputImagePng):
msg = self.launch_viewer(
rich_output.png.filename(ext='png'), plain_text.text.get_unicode())
return ({u'text/plain': msg}, {})
elif isinstance(rich_output, OutputImageGif):
msg = self.launch_viewer(
rich_output.gif.filename(ext='gif'), plain_text.text.get_unicode())
return ({u'text/plain': msg}, {})
elif isinstance(rich_output, OutputImagePdf):
msg = self.launch_viewer(
rich_output.pdf.filename(ext='pdf'), plain_text.text.get_unicode())
return ({u'text/plain': msg}, {})
elif isinstance(rich_output, OutputImageDvi):
msg = self.launch_viewer(
rich_output.dvi.filename(ext='dvi'), plain_text.text.get_unicode())
return ({u'text/plain': msg}, {})
elif isinstance(rich_output, OutputSceneJmol):
msg = self.launch_jmol(rich_output, plain_text.text.get_unicode())
return ({u'text/plain': msg}, {})
elif isinstance(rich_output, OutputSceneWavefront):
msg = self.launch_sage3d(rich_output, plain_text.text.get_unicode())
return ({u'text/plain': msg}, {})
elif isinstance(rich_output, OutputSceneThreejs):
msg = self.launch_viewer(
rich_output.html.filename(ext='html'), plain_text.text.get_unicode())
return ({u'text/plain': msg}, {})
else:
raise TypeError('rich_output type not supported')
def display_immediately(self, plain_text, rich_output):
"""
Show output without going back to the command line prompt.
This method is similar to the rich output :meth:`displayhook`,
except that it can be invoked at any time. On the Sage command
line it launches viewers just like :meth:`displayhook`.
INPUT:
Same as :meth:`displayhook`.
OUTPUT:
This method does not return anything.
EXAMPLES::
sage: from sage.repl.rich_output.output_basic import OutputPlainText
sage: plain_text = OutputPlainText.example()
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
sage: backend = BackendIPythonCommandline()
sage: backend.display_immediately(plain_text, plain_text)
Example plain text output
"""
formatdata, metadata = self.displayhook(plain_text, rich_output)
print(formatdata[u'text/plain'])
def launch_viewer(self, image_file, plain_text):
"""
Launch external viewer for the graphics file.
INPUT:
- ``image_file`` -- string. File name of the image file.
- ``plain_text`` -- string. The plain text representation of
the image file.
OUTPUT:
String. Human-readable message indicating whether the viewer
was launched successfully.
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
sage: backend = BackendIPythonCommandline()
sage: backend.launch_viewer('/path/to/foo.bar', 'Graphics object')
'Launched bar viewer for Graphics object'
"""
base, dot_ext = os.path.splitext(image_file)
ext = dot_ext.lstrip(os.path.extsep)
from sage.misc.viewer import viewer
command = viewer(ext)
if not command:
command = viewer.browser()
from sage.doctest import DOCTEST_MODE
if not DOCTEST_MODE:
os.system('{0} {1} 2>/dev/null 1>/dev/null &'
.format(command, image_file))
return 'Launched {0} viewer for {1}'.format(ext, plain_text)
def launch_jmol(self, output_jmol, plain_text):
"""
Launch the stand-alone jmol viewer
INPUT:
- ``output_jmol`` --
:class:`~sage.repl.rich_output.output_graphics3d.OutputSceneJmol`. The
scene to launch Jmol with.
- ``plain_text`` -- string. The plain text representation.
OUTPUT:
String. Human-readable message indicating that the viewer was launched.
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
sage: backend = BackendIPythonCommandline()
sage: from sage.repl.rich_output.output_graphics3d import OutputSceneJmol
sage: backend.launch_jmol(OutputSceneJmol.example(), 'Graphics3d object')
'Launched jmol viewer for Graphics3d object'
"""
from sage.doctest import DOCTEST_MODE
from sage.interfaces.jmoldata import JmolData
jdata = JmolData()
if not jdata.is_jvm_available() and not DOCTEST_MODE:
raise RuntimeError('jmol cannot run, no suitable java version found')
launch_script = output_jmol.launch_script_filename()
jmol_cmd = 'jmol'
if not DOCTEST_MODE:
os.system('{0} {1} 2>/dev/null 1>/dev/null &'
.format(jmol_cmd, launch_script))
return 'Launched jmol viewer for {0}'.format(plain_text)
def is_in_terminal(self):
"""
Test whether the UI is meant to run in a terminal
See
:meth:`sage.repl.rich_output.display_manager.DisplayManager.is_in_terminal`
for details.
OUTPUT:
``True`` for the IPython commandline.
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
sage: backend = BackendIPythonCommandline()
sage: backend.is_in_terminal()
True
"""
return True
def threejs_offline_scripts(self):
"""
Three.js scripts for the IPython command line
OUTPUT:
String containing script tags
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
sage: backend = BackendIPythonCommandline()
sage: backend.threejs_offline_scripts()
'...<script ...</script>...'
"""
from sage.env import SAGE_SHARE
scripts = [os.path.join(SAGE_SHARE, 'threejs', script)
for script in ['three.min.js', 'OrbitControls.js']]
if sys.platform == 'cygwin':
import cygwin
scripts = [cygwin.cygpath(script, 'w') for script in scripts]
return '\n'.join('<script src="{0}"></script>'.format(script)
for script in scripts)
IFRAME_TEMPLATE = \
"""
<iframe srcdoc="{escaped_html}"
width="{width}"
height="{height}"
style="border: 0;">
</iframe>
"""
class BackendIPythonNotebook(BackendIPython):
"""
Backend for the IPython Notebook
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
sage: BackendIPythonNotebook()
IPython notebook
"""
def _repr_(self):
"""
Return string representation of the backend
OUTPUT:
String.
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
sage: backend = BackendIPythonNotebook()
sage: backend._repr_()
'IPython notebook'
"""
return 'IPython notebook'
def supported_output(self):
"""
Return the outputs that are supported by the IPython notebook backend.
OUTPUT:
Iterable of output container classes, that is, subclass of
:class:`~sage.repl.rich_output.output_basic.OutputBase`).
The order is ignored.
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
sage: backend = BackendIPythonNotebook()
sage: supp = backend.supported_output(); supp # random output
set([<class 'sage.repl.rich_output.output_graphics.OutputPlainText'>,
...,
<class 'sage.repl.rich_output.output_graphics.OutputImagePdf'>])
sage: from sage.repl.rich_output.output_basic import OutputLatex
sage: OutputLatex in supp
True
sage: from sage.repl.rich_output.output_graphics import OutputImageGif
sage: OutputImageGif in supp
True
"""
return set([
OutputPlainText, OutputAsciiArt, OutputUnicodeArt, OutputLatex,
OutputHtml,
OutputImagePng, OutputImageGif, OutputImageJpg,
OutputImageSvg, OutputImagePdf,
OutputSceneJmol, OutputSceneThreejs,
])
def displayhook(self, plain_text, rich_output):
"""
Backend implementation of the displayhook
INPUT:
- ``plain_text`` -- instance of
:class:`~sage.repl.rich_output.output_basic.OutputPlainText`. The
plain text version of the output.
- ``rich_output`` -- instance of an output container class
(subclass of
:class:`~sage.repl.rich_output.output_basic.OutputBase`). Guaranteed
to be one of the output containers returned from
:meth:`supported_output`, possibly the same as
``plain_text``.
OUTPUT:
The IPython notebook display hook returns the IPython
display data, a pair of dictionaries. The first dictionary
contains mime types as keys and the respective output as
value. The second dictionary is metadata.
EXAMPLES::
sage: from sage.repl.rich_output.output_basic import OutputPlainText
sage: plain_text = OutputPlainText.example()
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
sage: backend = BackendIPythonNotebook()
sage: backend.displayhook(plain_text, plain_text)
({u'text/plain': u'Example plain text output'}, {})
"""
if isinstance(rich_output, OutputPlainText):
return ({u'text/plain': rich_output.text.get_unicode()}, {})
elif isinstance(rich_output, OutputAsciiArt):
return ({u'text/plain': rich_output.ascii_art.get_unicode()}, {})
elif isinstance(rich_output, OutputUnicodeArt):
return ({u'text/plain': rich_output.unicode_art.get_unicode()}, {})
elif isinstance(rich_output, OutputLatex):
return ({u'text/html': rich_output.mathjax(),
u'text/plain': plain_text.text.get_unicode(),
}, {})
elif isinstance(rich_output, OutputHtml):
return ({u'text/html': rich_output.html.get_unicode(),
u'text/plain': plain_text.text.get_unicode(),
}, {})
elif isinstance(rich_output, OutputImagePng):
return ({u'image/png': rich_output.png.get(),
u'text/plain': plain_text.text.get_unicode(),
}, {})
elif isinstance(rich_output, OutputImageGif):
return ({u'text/html': rich_output.html_fragment(),
u'text/plain': plain_text.text.get_unicode(),
}, {})
elif isinstance(rich_output, OutputImageJpg):
return ({u'image/jpeg': rich_output.jpg.get(),
u'text/plain': plain_text.text.get_unicode(),
}, {})
elif isinstance(rich_output, OutputImageSvg):
return ({u'image/svg+xml': rich_output.svg.get(),
u'text/plain': plain_text.text.get_unicode(),
}, {})
elif isinstance(rich_output, OutputImagePdf):
return ({u'image/png': rich_output.png.get(),
u'text/plain': plain_text.text.get_unicode(),
}, {})
elif isinstance(rich_output, OutputSceneJmol):
from sage.repl.display.jsmol_iframe import JSMolHtml
jsmol = JSMolHtml(rich_output, height=500)
return ({u'text/html': jsmol.iframe(),
u'text/plain': plain_text.text.get_unicode(),
}, {})
elif isinstance(rich_output, OutputSceneThreejs):
escaped_html = rich_output.html.get_unicode().replace('"', '&quot;')
iframe = IFRAME_TEMPLATE.format(
escaped_html=escaped_html,
width='100%',
height=400,
)
return ({u'text/html': iframe,
u'text/plain': plain_text.text.get_unicode(),
}, {})
else:
raise TypeError('rich_output type not supported')
def threejs_offline_scripts(self):
"""
Three.js scripts for the IPython notebook
OUTPUT:
String containing script tags
EXAMPLES::
sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
sage: backend = BackendIPythonNotebook()
sage: backend.threejs_offline_scripts()
'...<script src="/nbextensions/threejs/three.min...<\\/script>...'
"""
from sage.repl.rich_output import get_display_manager
CDN_scripts = get_display_manager().threejs_scripts(online=True)
return """
<script src="/nbextensions/threejs/three.min.js"></script>
<script src="/nbextensions/threejs/OrbitControls.js"></script>
<script>
if ( !window.THREE ) document.write('{}');
</script>
""".format(CDN_scripts.replace('</script>', r'<\/script>').replace('\n', ' \\\n'))
You can’t perform that action at this time.