core/sage-mathematics/ipython3.patch
2015-04-13 07:59:57 +00:00

11326 lines
405 KiB
Diff

diff --git a/src/bin/sage-ipython b/src/bin/sage-ipython
index 2a66e09..060212e 100755
--- a/src/bin/sage-ipython
+++ b/src/bin/sage-ipython
@@ -3,6 +3,7 @@
"""
Sage IPython startup script.
"""
+
from sage.repl.interpreter import SageTerminalApp
# installs the extra readline commands before the IPython initialization begins.
diff --git a/src/bin/sage-notebook b/src/bin/sage-notebook
index e75e190..bdc41b7 100755
--- a/src/bin/sage-notebook
+++ b/src/bin/sage-notebook
@@ -74,13 +74,14 @@ class NotebookIPython(object):
""")
def __init__(self, argv):
- from sage.repl.notebook_ipython import have_prerequisites, SageNotebookApp
+ from sage.repl.ipython_kernel.install import \
+ SageKernelSpec, have_prerequisites
+ SageKernelSpec.update()
if not have_prerequisites():
print(self.PREREQUISITE_ERROR)
raise SystemExit(1)
- app = SageNotebookApp.instance()
- app.initialize(argv)
- app.start()
+ from IPython import start_ipython
+ start_ipython(['notebook'] + argv)
description = \
diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst
index 607121f..44119a8 100644
--- a/src/doc/en/reference/index.rst
+++ b/src/doc/en/reference/index.rst
@@ -10,7 +10,7 @@ Enjoy Sage!
Table of Contents
=================
-* :doc:`The Sage Command Line <repl/index>`
+* :doc:`The Sage Command Line (REPL) <repl/index>`
* :doc:`The Sage Notebook <notebook/index>`
Calculus, Plotting
diff --git a/src/doc/en/reference/repl/index.rst b/src/doc/en/reference/repl/index.rst
index 5271140..7a98831 100644
--- a/src/doc/en/reference/repl/index.rst
+++ b/src/doc/en/reference/repl/index.rst
@@ -20,7 +20,8 @@ tutorial.
sage/repl/readline_extra_commands
sage/repl/interpreter
sage/repl/ipython_extension
- sage/repl/notebook_ipython
+ sage/repl/ipython_kernel/install
+ sage/repl/ipython_kernel/kernel
Preparsing
@@ -63,7 +64,26 @@ this works using a modified displayhook in Python.
sage/repl/display/pretty_print
sage/repl/display/fancy_repr
sage/repl/display/util
- sage/repl/display/python_hook
+Display Backend Infrastructure
+------------------------------
+
+.. toctree::
+ :maxdepth: 2
+
+ sage/repl/rich_output/display_manager
+ sage/repl/rich_output/preferences
+ sage/repl/rich_output/buffer
+ sage/repl/rich_output/output_basic
+ sage/repl/rich_output/output_graphics
+ sage/repl/rich_output/output_graphics3d
+ sage/repl/rich_output/output_catalog
+
+ sage/repl/rich_output/backend_base
+ sage/repl/rich_output/backend_test
+ sage/repl/rich_output/backend_doctest
+ sage/repl/rich_output/backend_ipython
+
+
.. include:: ../footer.txt
diff --git a/src/doc/en/reference/structure/index.rst b/src/doc/en/reference/structure/index.rst
index 7cb978a..f7149a8 100644
--- a/src/doc/en/reference/structure/index.rst
+++ b/src/doc/en/reference/structure/index.rst
@@ -23,7 +23,6 @@ Basic Structures
sage/structure/element_wrapper
sage/structure/indexed_generators
sage/structure/global_options
- sage/structure/graphics_file
sage/sets/cartesian_product
sage/sets/family
diff --git a/src/doc/en/thematic_tutorials/polytutorial.rst b/src/doc/en/thematic_tutorials/polytutorial.rst
index c731c10..e60370f 100644
--- a/src/doc/en/thematic_tutorials/polytutorial.rst
+++ b/src/doc/en/thematic_tutorials/polytutorial.rst
@@ -36,7 +36,7 @@ Of course, you want to know what this object looks like:
::
- sage: show(P1)
+ sage: P1.plot()
Graphics object consisting of 6 graphics primitives
.. end of output
@@ -70,7 +70,7 @@ translate each inequality into a vector. For example,
::
sage: altP1 = Polyhedron(ieqs=[(12, -4, 1), (26, 1, 7),(5,1,0), (28, 2, -9)])
- sage: show(altP1)
+ sage: altP1.plot()
Graphics object consisting of 6 graphics primitives
.. end of output
@@ -119,7 +119,7 @@ to a rational\-lattice polytope. Let's look at that.
::
- sage: show(P1dual)
+ sage: P1dual.plot()
Graphics object consisting of 6 graphics primitives
@@ -127,7 +127,7 @@ to a rational\-lattice polytope. Let's look at that.
::
- sage: show(P1)+show(P1dual)
+ sage: P1.plot() + P1dual.plot()
Graphics object consisting of 12 graphics primitives
@@ -139,7 +139,7 @@ very different size. Let's rescale.
::
- sage: show((1/4)*P1)+show(4*P1dual)
+ sage: ((1/4)*P1).plot() + (4*P1dual).plot()
Graphics object consisting of 12 graphics primitives
.. end of output
@@ -155,7 +155,7 @@ example that makes the issue a bit clearer.
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 5 vertices
sage: P2dual = P2.polar(); P2dual
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 5 vertices
- sage: show(P2)+show(P2dual)
+ sage: P2.plot() + P2dual.plot()
Graphics object consisting of 14 graphics primitives
.. end of output
@@ -166,7 +166,7 @@ at this...
::
- sage: show(P2)+show(-1*P2dual)
+ sage: P2.plot() + (-1*P2dual).plot()
Graphics object consisting of 14 graphics primitives
.. end of output
@@ -223,14 +223,14 @@ visualization software such as Javaview and Jmol installed.)
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 5 vertices
sage: P4 = Polyhedron(vertices=[(-1,1,0),(1,1,0),(-1,0,1), (1,0,1),(0,-1,1),(0,1,1)]); P4
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 6 vertices
- sage: show(P3)+show(P4)
+ sage: P3.plot() + P4.plot()
Graphics3d Object
.. end of output
::
- sage: show(P3+P4)
+ sage: (P3+P4).plot()
Graphics3d Object
.. end of output
@@ -241,14 +241,14 @@ syntaxes!
::
- sage: int12 = P1.intersection(P2*.5); show(int12)
+ sage: int12 = P1.intersection(P2*.5); int12.plot()
Graphics object consisting of 7 graphics primitives
.. end of output
::
- sage: int34 = P3 & P4; show(int34)
+ sage: int34 = P3 & P4; int34.plot()
Graphics3d Object
.. end of output
@@ -259,7 +259,7 @@ Should one wish to translate, one can.
::
sage: transP2 = P2.translation([2,1])
- sage: show(P2)+show(transP2)
+ sage: P2.plot() + transP2.plot()
Graphics object consisting of 14 graphics primitives
.. end of output
@@ -269,21 +269,21 @@ Then of course we can take prisms, pyramids, and bipyramids of polytopes...
::
- sage: show(P2.prism())
+ sage: P2.prism().plot()
Graphics3d Object
.. end of output
::
- sage: show(P1.pyramid())
+ sage: P1.pyramid().plot()
Graphics3d Object
.. end of output
::
- sage: show(P2dual.bipyramid())
+ sage: P2dual.bipyramid().plot()
Graphics3d Object
.. end of output
@@ -308,7 +308,7 @@ Let's look at a 4\-dimensional polytope.
::
sage: P8 = polytopes.n_cube(4)
- sage: P8.show()
+ sage: P8.plot()
Graphics3d Object
.. end of output
diff --git a/src/ext/doctest/invalid/syntax_error.tachyon b/src/ext/doctest/invalid/syntax_error.tachyon
new file mode 100644
index 0000000..1cdd7a5
--- /dev/null
+++ b/src/ext/doctest/invalid/syntax_error.tachyon
@@ -0,0 +1,91 @@
+# This is NOT a valid tachyon input file
+#
+# We use it to test that tachyon errors are diagnosed in the Sage
+# interface to tachyon.
+
+
+begin_scene
+resolution 400 400
+
+ camera
+ zoom 1.0
+ aspectratio 1.0
+ antialiasing 8
+ raydepth 8
+ center 2.3 2.4 2.0
+ viewdir -2.3 -2.4 -2.0
+ updir 0.0 0.0 1.0
+ end_camera
+
+
+ light center 4.0 3.0 2.0
+ rad 0.2
+ color 1.0 1.0 1.0
+
+ plane
+ center -2000 -1000 -500
+ normal 2.3 2.4 2.0
+ TEXTURE
+ AMBIENT 1.0 DIFFUSE 1.0 SPECULAR 1.0 OPACITY 1.0
+ COLOR 1.0 1.0 1.0
+ TEXFUNC 0
+
+ Texdef texture239
+ Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1
+ Color 0.0 0.0 0.0
+ TexFunc 0
+Texdef texture240
+ Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1/2
+ Color 0.0 0.0 0.0
+ TexFunc 0
+
+ TRI V0 0.5 -0.5 0.5 V1 -0.5 -0.5 0.5 V2 -0.5 0.5 0.5
+texture239
+TRI V0 0.5 -0.5 0.5 V1 -0.5 0.5 0.5 V2 0.5 0.5 0.5
+texture239
+TRI V0 0.5 -0.5 0.5 V1 0.5 -0.5 -0.5 V2 -0.5 -0.5 -0.5
+texture239
+TRI V0 0.5 -0.5 0.5 V1 -0.5 -0.5 -0.5 V2 -0.5 -0.5 0.5
+texture239
+TRI V0 0.5 -0.5 0.5 V1 0.5 0.5 0.5 V2 0.5 0.5 -0.5
+texture239
+TRI V0 0.5 -0.5 0.5 V1 0.5 0.5 -0.5 V2 0.5 -0.5 -0.5
+texture239
+TRI V0 -0.5 -0.5 -0.5 V1 0.5 -0.5 -0.5 V2 0.5 0.5 -0.5
+texture239
+TRI V0 -0.5 -0.5 -0.5 V1 0.5 0.5 -0.5 V2 -0.5 0.5 -0.5
+texture239
+TRI V0 0.5 0.5 -0.5 V1 0.5 0.5 0.5 V2 -0.5 0.5 0.5
+texture239
+TRI V0 0.5 0.5 -0.5 V1 -0.5 0.5 0.5 V2 -0.5 0.5 -0.5
+texture239
+TRI V0 -0.5 0.5 0.5 V1 -0.5 -0.5 0.5 V2 -0.5 -0.5 -0.5
+texture239
+TRI V0 -0.5 0.5 0.5 V1 -0.5 -0.5 -0.5 V2 -0.5 0.5 -0.5
+texture239
+TRI V0 3 -0.5 0.5 V1 2 -0.5 0.5 V2 2 0.5 0.5
+texture240
+TRI V0 3 -0.5 0.5 V1 2 0.5 0.5 V2 3 0.5 0.5
+texture240
+TRI V0 3 -0.5 0.5 V1 3 -0.5 -0.5 V2 2 -0.5 -0.5
+texture240
+TRI V0 3 -0.5 0.5 V1 2 -0.5 -0.5 V2 2 -0.5 0.5
+texture240
+TRI V0 3 -0.5 0.5 V1 3 0.5 0.5 V2 3 0.5 -0.5
+texture240
+TRI V0 3 -0.5 0.5 V1 3 0.5 -0.5 V2 3 -0.5 -0.5
+texture240
+TRI V0 2 -0.5 -0.5 V1 3 -0.5 -0.5 V2 3 0.5 -0.5
+texture240
+TRI V0 2 -0.5 -0.5 V1 3 0.5 -0.5 V2 2 0.5 -0.5
+texture240
+TRI V0 3 0.5 -0.5 V1 3 0.5 0.5 V2 2 0.5 0.5
+texture240
+TRI V0 3 0.5 -0.5 V1 2 0.5 0.5 V2 2 0.5 -0.5
+texture240
+TRI V0 2 0.5 0.5 V1 2 -0.5 0.5 V2 2 -0.5 -0.5
+texture240
+TRI V0 2 0.5 0.5 V1 2 -0.5 -0.5 V2 2 0.5 -0.5
+texture240
+
+end_scene
\ No newline at end of file
diff --git a/src/ext/doctest/rich_output/example.canvas3d b/src/ext/doctest/rich_output/example.canvas3d
new file mode 100644
index 0000000..43b93e9
--- /dev/null
+++ b/src/ext/doctest/rich_output/example.canvas3d
@@ -0,0 +1 @@
+[{vertices:[{x:1,y:1,z:1},{x:-1,y:1,z:1},{x:-1,y:-1,z:1},{x:1,y:-1,z:1}],faces:[[0,1,2,3]],color:'008000'},{vertices:[{x:1,y:1,z:1},{x:-1,y:1,z:1},{x:-1,y:1,z:-1},{x:1,y:1,z:-1}],faces:[[0,1,2,3]],color:'008000'},{vertices:[{x:1,y:1,z:1},{x:1,y:-1,z:1},{x:1,y:-1,z:-1},{x:1,y:1,z:-1}],faces:[[0,1,2,3]],color:'008000'},{vertices:[{x:-1,y:1,z:1},{x:-1,y:-1,z:1},{x:-1,y:-1,z:-1},{x:-1,y:1,z:-1}],faces:[[0,1,2,3]],color:'008000'},{vertices:[{x:1,y:1,z:-1},{x:-1,y:1,z:-1},{x:-1,y:-1,z:-1},{x:1,y:-1,z:-1}],faces:[[0,1,2,3]],color:'008000'},{vertices:[{x:1,y:-1,z:1},{x:-1,y:-1,z:1},{x:-1,y:-1,z:-1},{x:1,y:-1,z:-1}],faces:[[0,1,2,3]],color:'008000'}]
\ No newline at end of file
diff --git a/src/ext/doctest/rich_output/example.dvi b/src/ext/doctest/rich_output/example.dvi
new file mode 100644
index 0000000..6c2f385
--- /dev/null
+++ b/src/ext/doctest/rich_output/example.dvi
Binary files differ
diff --git a/src/ext/doctest/rich_output/example.gif b/src/ext/doctest/rich_output/example.gif
new file mode 100644
index 0000000..12b3275
--- /dev/null
+++ b/src/ext/doctest/rich_output/example.gif
Binary files differ
diff --git a/src/ext/doctest/rich_output/example.jpg b/src/ext/doctest/rich_output/example.jpg
new file mode 100644
index 0000000..5e4439d
--- /dev/null
+++ b/src/ext/doctest/rich_output/example.jpg
Binary files differ
diff --git a/src/ext/doctest/rich_output/example.pdf b/src/ext/doctest/rich_output/example.pdf
new file mode 100644
index 0000000..e47041b
--- /dev/null
+++ b/src/ext/doctest/rich_output/example.pdf
Binary files differ
diff --git a/src/ext/doctest/rich_output/example.png b/src/ext/doctest/rich_output/example.png
new file mode 100644
index 0000000..3b8e082
--- /dev/null
+++ b/src/ext/doctest/rich_output/example.png
Binary files differ
diff --git a/src/ext/doctest/rich_output/example.svg b/src/ext/doctest/rich_output/example.svg
new file mode 100644
index 0000000..f736828
--- /dev/null
+++ b/src/ext/doctest/rich_output/example.svg
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Created with matplotlib (http://matplotlib.org/) -->
+<svg height="420pt" version="1.1" viewBox="0 0 420 420" width="420pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <style type="text/css">
+*{stroke-linecap:butt;stroke-linejoin:round;}
+ </style>
+ </defs>
+ <g id="figure_1">
+ <g id="patch_1">
+ <path d="
+M0 420.48
+L420.48 420.48
+L420.48 0
+L0 0
+z
+" style="fill:#ffffff;"/>
+ </g>
+ <g id="axes_1">
+ <g id="patch_2">
+ <path d="
+M7.2 413.28
+L413.28 413.28
+L413.28 7.2
+L7.2 7.2
+z
+" style="fill:#ffffff;"/>
+ </g>
+ <g id="matplotlib.axis_1"/>
+ <g id="matplotlib.axis_2"/>
+ <g id="patch_3">
+ <path clip-path="url(#p85c0e9ffdb)" d="
+M210.24 405.471
+C262.016 405.471 311.678 384.9 348.289 348.289
+C384.9 311.678 405.471 262.016 405.471 210.24
+C405.471 158.464 384.9 108.802 348.289 72.191
+C311.678 35.58 262.016 15.0092 210.24 15.0092
+C158.464 15.0092 108.802 35.58 72.191 72.191
+C35.58 108.802 15.0092 158.464 15.0092 210.24
+C15.0092 262.016 35.58 311.678 72.191 348.289
+C108.802 384.9 158.464 405.471 210.24 405.471
+z
+" style="fill:none;stroke:#0000ff;"/>
+ </g>
+ </g>
+ </g>
+ <defs>
+ <clipPath id="p85c0e9ffdb">
+ <rect height="406.08" width="406.08" x="7.2" y="7.2"/>
+ </clipPath>
+ </defs>
+</svg>
diff --git a/src/ext/doctest/rich_output/example_jmol.spt.zip b/src/ext/doctest/rich_output/example_jmol.spt.zip
new file mode 100644
index 0000000..f9748e1
--- /dev/null
+++ b/src/ext/doctest/rich_output/example_jmol.spt.zip
Binary files differ
diff --git a/src/ext/doctest/rich_output/example_wavefront/scene.mtl b/src/ext/doctest/rich_output/example_wavefront/scene.mtl
new file mode 100644
index 0000000..a2b7d31
--- /dev/null
+++ b/src/ext/doctest/rich_output/example_wavefront/scene.mtl
@@ -0,0 +1,7 @@
+newmtl texture177
+Ka 0.2 0.2 0.5
+Kd 0.4 0.4 1.0
+Ks 0.0 0.0 0.0
+illum 1
+Ns 1
+d 1
diff --git a/src/ext/doctest/rich_output/example_wavefront/scene.obj b/src/ext/doctest/rich_output/example_wavefront/scene.obj
new file mode 100644
index 0000000..1ece4f1
--- /dev/null
+++ b/src/ext/doctest/rich_output/example_wavefront/scene.obj
@@ -0,0 +1,17 @@
+mtllib scene.mtl
+g obj_1
+usemtl texture177
+v 0.5 0.5 0.5
+v -0.5 0.5 0.5
+v -0.5 -0.5 0.5
+v 0.5 -0.5 0.5
+v 0.5 0.5 -0.5
+v -0.5 0.5 -0.5
+v 0.5 -0.5 -0.5
+v -0.5 -0.5 -0.5
+f 1 2 3 4
+f 1 5 6 2
+f 1 4 7 5
+f 6 5 7 8
+f 7 4 3 8
+f 3 2 6 8
diff --git a/src/ext/notebook-ipython/logo-64x64.png b/src/ext/notebook-ipython/logo-64x64.png
new file mode 100644
index 0000000..4b6da1f
--- /dev/null
+++ b/src/ext/notebook-ipython/logo-64x64.png
Binary files differ
diff --git a/src/ext/notebook-ipython/logo.svg b/src/ext/notebook-ipython/logo.svg
new file mode 100644
index 0000000..7d85809
--- /dev/null
+++ b/src/ext/notebook-ipython/logo.svg
@@ -0,0 +1,352 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100"
+ height="100"
+ viewBox="0 0 99.999997 99.999997"
+ id="svg5182"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="sage-logo.svg">
+ <defs
+ id="defs5184" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.8"
+ inkscape:cx="4.5104621"
+ inkscape:cy="-33.017238"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ units="px"
+ inkscape:window-width="2560"
+ inkscape:window-height="1534"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata5187">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-184.28571,-313.79078)">
+ <g
+ transform="translate(-817.28326,-402.32598)"
+ id="g12802"
+ inkscape:export-filename="/home/sirius/Images/SAGE Logos/icons_strip.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <rect
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="/home/sirius/Images/SAGE Logos/sage_webpage_icons.png"
+ ry="11.531985"
+ rx="11.531984"
+ y="716.11676"
+ x="1001.569"
+ height="100"
+ width="100"
+ id="rect12804"
+ style="opacity:0.98999999;fill:#9393ff;fill-opacity:0.99216464;stroke:none;stroke-width:0.80000001;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.98431684" />
+ <g
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="/home/sirius/Images/SAGE Logos/sage_webpage_icons.png"
+ transform="matrix(0.4795482,0,0,0.4795482,301.82045,-349.76993)"
+ id="g12806">
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path12808"
+ transform="matrix(0.9672595,0,0,0.9672595,235.69886,548.04605)" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ transform="matrix(0.9672595,0,0,0.9672595,308.74604,539.35166)"
+ id="path12810"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path12812"
+ transform="matrix(1.2053547,0,0,1.2053547,-25.196551,182.19849)" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path12814"
+ transform="matrix(0.7953719,0,0,0.7953719,449.34462,925.45441)" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ transform="matrix(0.7953719,0,0,0.7953719,499.30379,973.24771)"
+ id="path12816"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path12818"
+ transform="matrix(0.7953719,0,0,0.7953719,556.29908,956.69427)" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ transform="matrix(0.7953719,0,0,0.7953719,581.00221,902.90894)"
+ id="path12820"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path12822"
+ transform="matrix(0.6234843,0,0,0.6234843,800.65848,1180.6657)" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ transform="matrix(0.6234843,0,0,0.6234843,766.33259,1139.9519)"
+ id="path12824"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path12826"
+ transform="matrix(0.6234843,0,0,0.6234843,664.75027,1230.1938)" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ transform="matrix(0.6234843,0,0,0.6234843,701.58781,1267.5497)"
+ id="path12828"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ transform="matrix(0.7953719,0,0,0.7953719,440.41431,869.81028)"
+ id="path12830"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path12832"
+ transform="matrix(0.7953719,0,0,0.7953719,484.50773,829.39633)" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path12834"
+ transform="matrix(0.6234843,0,0,0.6234843,699.00639,1196.6154)" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ transform="matrix(0.6234843,0,0,0.6234843,749.0301,1256.3969)"
+ id="path12836"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1590.0065,2271.6035 -18.6281,72.6731"
+ id="path12838" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1589.0995,2273.5223 -72.5588,8.4545"
+ id="path12840" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1571.3087,2343.4372 -54.6982,-61.8801"
+ id="path12842" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1571.3087,2343.8569 -18.07,55.944"
+ id="path12844" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1516.4012,2281.3172 21.0002,-24.3443"
+ id="path12846" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1493.9359,2295.4681 21.7676,-14.3307"
+ id="path12848" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1503.0057,2350.6326 13.3955,-69.9749"
+ id="path12850" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1552.1922,2398.5417 -49.5353,-46.59"
+ id="path12852" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1610.0997,2381.3328 -58.1168,16.969"
+ id="path12854" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1634.2395,2328.3869 -24.1398,53.1257"
+ id="path12856" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1570.5412,2343.4372 62.8611,-14.6306"
+ id="path12858" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1590.1461,2273.0426 43.1166,55.1644"
+ id="path12860" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1501.7987,2350.6326 66.9774,-7.1954"
+ id="path12862" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1551.4038,2398.062 -24.4188,-13.1316"
+ id="path12864" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1489.38,2347.2747 36.3492,37.296"
+ id="path12866" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1493.2173,2295.9478 -2.721,51.3269"
+ id="path12868" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1537.3805,2254.8743 54.0702,1.8588"
+ id="path12870" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1627.172,2298.5261 -34.2562,-41.9729"
+ id="path12872" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1635.4744,2328.207 -9.0001,-30.1006"
+ id="path12874" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1589.4972,2272.2631 3.2093,-15.2902"
+ id="path12876" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1503.6825,2351.2305 -14.1629,-2.6983"
+ id="path12878" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1526.985,2385.4084 46.7446,-11.4526"
+ id="path12880" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1574.9157,2373.8958 33.6282,7.2554"
+ id="path12882" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1492.5121,2295.941 61.203,-6.8478"
+ id="path12884" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1553.7151,2287.7387 36.4241,-13.5452"
+ id="path12886" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path12888"
+ transform="matrix(0.6234843,0,0,0.6234843,726.86911,1170.9368)" />
+ <circle
+ r="7.3256478"
+ cy="1792.4095"
+ cx="1324.929"
+ transform="matrix(0.5833906,0,0,0.5833906,837.2333,1298.2147)"
+ id="path12890"
+ style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1609.6576,2343.739 16.404,-46.0639"
+ id="path12892" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 1609.4298,2381.481 0.5696,-38.77"
+ id="path12894" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path12896"
+ d="m 1502.1919,2350.0723 23.4339,-35.9573"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path12898"
+ d="M 1537.8572,2255.3651 1524.94,2314.115"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/src/ext/notebook-ipython/templates/404.html b/src/ext/notebook-ipython/templates/404.html
deleted file mode 100644
index 7335051..0000000
--- a/src/ext/notebook-ipython/templates/404.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% extends "error.html" %}
-{% block error_detail %}
-<p>You are requesting a page that does not exist!</p>
-{% endblock %}
-
diff --git a/src/ext/notebook-ipython/templates/error.html b/src/ext/notebook-ipython/templates/error.html
deleted file mode 100644
index bedf06c..0000000
--- a/src/ext/notebook-ipython/templates/error.html
+++ /dev/null
@@ -1,31 +0,0 @@
-{% extends "page.html" %}
-
-{% block login_widget %}
-{% endblock %}
-
-{% block stylesheet %}
-{{super()}}
-<style type="text/css">
-/* disable initial hide */
-div#header, div#site {
- display: block;
-}
-</style>
-{% endblock %}
-{% block site %}
-
-<div class="error">
- {% block h1_error %}
- <h1>{{status_code}} : {{status_message}}</h1>
- {% endblock h1_error %}
- {% block error_detail %}
- {% if message %}
- <p>The error was:</p>
- <div class="traceback-wrapper">
- <pre class="traceback">{{message}}</pre>
- </div>
- {% endif %}
- {% endblock %}
-</header>
-
-{% endblock %}
diff --git a/src/ext/notebook-ipython/templates/login.html b/src/ext/notebook-ipython/templates/login.html
deleted file mode 100644
index 7244bee..0000000
--- a/src/ext/notebook-ipython/templates/login.html
+++ /dev/null
@@ -1,52 +0,0 @@
-{% extends "page.html" %}
-
-
-{% block stylesheet %}
-{{super()}}
-<link rel="stylesheet" href="{{ static_url("auth/css/override.css") }}" type="text/css" />
-{% endblock %}
-
-{% block login_widget %}
-{% endblock %}
-
-{% block site %}
-
-<div id="ipython-main-app" class="container">
-
- {% if login_available %}
- <div class="row">
- <div class="navbar span8 offset2">
- <div class="navbar-inner">
- <div class="container">
- <div class="center-nav">
- <p class="navbar-text nav">Password:</p>
- <form action="{{base_url}}login?next={{next}}" method="post" class="navbar-form pull-left">
- <input type="password" name="password" id="password_input">
- <button type="submit" id="login_submit">Log in</button>
- </form>
- </div>
- </div>
- </div>
- </div>
- </div>
- {% endif %}
- {% if message %}
- <div class="row">
- {% for key in message %}
- <div class="message {{key}}">
- {{message[key]}}
- </div>
- {% endfor %}
- </div>
- {% endif %}
-
-<div/>
-
-{% endblock %}
-
-
-{% block script %}
-
-<script src="{{static_url("auth/js/loginmain.js") }}" type="text/javascript" charset="utf-8"></script>
-
-{% endblock %}
diff --git a/src/ext/notebook-ipython/templates/logout.html b/src/ext/notebook-ipython/templates/logout.html
deleted file mode 100644
index 35c65c5..0000000
--- a/src/ext/notebook-ipython/templates/logout.html
+++ /dev/null
@@ -1,38 +0,0 @@
-{% extends "page.html" %}
-
-{% block stylesheet %}
-{{super()}}
-<link rel="stylesheet" href="{{ static_url("auth/css/override.css") }}" type="text/css" />
-{% endblock %}
-
-{% block login_widget %}
-{% endblock %}
-
-{% block site %}
-
-<div id="ipython-main-app" class="container">
-
- {% if message %}
- {% for key in message %}
- <div class="message {{key}}">
- {{message[key]}}
- </div>
- {% endfor %}
- {% endif %}
-
- {% if not login_available %}
- Proceed to the <a href="{{base_url}}">dashboard</a>.
- {% else %}
- Proceed to the <a href="{{base_url}}login">login page</a>.
- {% endif %}
-
-
-<div/>
-
-{% endblock %}
-
-{% block script %}
-
-<script src="{{static_url("auth/js/logoutmain.js") }}" type="text/javascript" charset="utf-8"></script>
-
-{% endblock %}
diff --git a/src/ext/notebook-ipython/templates/notebook.html b/src/ext/notebook-ipython/templates/notebook.html
deleted file mode 100644
index 8a4377b..0000000
--- a/src/ext/notebook-ipython/templates/notebook.html
+++ /dev/null
@@ -1,371 +0,0 @@
-{% extends "page.html" %}
-
-{% block stylesheet %}
-
-{% if mathjax_url %}
-<script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
-{% endif %}
-<script type="text/javascript">
-// MathJax disabled, set as null to distingish from *missing* MathJax,
-// where it will be undefined, and should prompt a dialog later.
-window.mathjax_url = "{{mathjax_url}}";
-</script>
-
-<link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
-<link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
-
-{{super()}}
-
-<link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
-
-{% endblock %}
-
-{% block params %}
-
-data-project="{{project}}"
-data-base-url="{{base_url}}"
-data-notebook-name="{{notebook_name}}"
-data-notebook-path="{{notebook_path}}"
-class="notebook_app"
-
-{% endblock %}
-
-
-{% block header %}
-
-<span id="save_widget" class="nav pull-left">
- <span id="notebook_name"></span>
- <span id="checkpoint_status"></span>
- <span id="autosave_status"></span>
-</span>
-
-{% endblock %}
-
-
-{% block site %}
-
-<div id="menubar-container" class="container">
-<div id="menubar">
-<div class="navbar">
- <div class="navbar-inner">
- <div class="container">
- <ul id="menus" class="nav">
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
- <ul id="file_menu" class="dropdown-menu">
- <li id="new_notebook"
- title="Make a new notebook (Opens a new window)">
- <a href="#">New</a></li>
- <li id="open_notebook"
- title="Opens a new window with the Dashboard view">
- <a href="#">Open...</a></li>
- <!-- <hr/> -->
- <li class="divider"></li>
- <li id="copy_notebook"
- title="Open a copy of this notebook's contents and start a new kernel">
- <a href="#">Make a Copy...</a></li>
- <li id="rename_notebook"><a href="#">Rename...</a></li>
- <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
- <!-- <hr/> -->
- <li class="divider"></li>
- <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
- <ul class="dropdown-menu">
- <li><a href="#"></a></li>
- <li><a href="#"></a></li>
- <li><a href="#"></a></li>
- <li><a href="#"></a></li>
- <li><a href="#"></a></li>
- </ul>
- </li>
- <li class="divider"></li>
- <li id="print_preview"><a href="#">Print Preview</a></li>
- <li class="dropdown-submenu"><a href="#">Download as</a>
- <ul class="dropdown-menu">
- <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
- <li id="download_py"><a href="#">Python (.py)</a></li>
- <li id="download_html"><a href="#">HTML (.html)</a></li>
- <li id="download_rst"><a href="#">reST (.rst)</a></li>
- </ul>
- </li>
- <li class="divider"></li>
- <li id="trust_notebook"
- title="Trust the output of this notebook">
- <a href="#" >Trust Notebook</a></li>
- <li class="divider"></li>
- <li id="kill_and_exit"
- title="Shutdown this notebook's kernel, and close this window">
- <a href="#" >Close and halt</a></li>
- </ul>
- </li>
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
- <ul id="edit_menu" class="dropdown-menu">
- <li id="cut_cell"><a href="#">Cut Cell</a></li>
- <li id="copy_cell"><a href="#">Copy Cell</a></li>
- <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
- <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
- <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
- <li id="delete_cell"><a href="#">Delete Cell</a></li>
- <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
- <li class="divider"></li>
- <li id="split_cell"><a href="#">Split Cell</a></li>
- <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
- <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
- <li class="divider"></li>
- <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
- <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
- <li class="divider"></li>
- <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
- </ul>
- </li>
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
- <ul id="view_menu" class="dropdown-menu">
- <li id="toggle_header"
- title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
- <a href="#">Toggle Header</a></li>
- <li id="toggle_toolbar"
- title="Show/Hide the action icons (below menu bar)">
- <a href="#">Toggle Toolbar</a></li>
- </ul>
- </li>
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
- <ul id="insert_menu" class="dropdown-menu">
- <li id="insert_cell_above"
- title="Insert an empty Code cell above the currently active cell">
- <a href="#">Insert Cell Above</a></li>
- <li id="insert_cell_below"
- title="Insert an empty Code cell below the currently active cell">
- <a href="#">Insert Cell Below</a></li>
- </ul>
- </li>
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
- <ul id="cell_menu" class="dropdown-menu">
- <li id="run_cell" title="Run this cell, and move cursor to the next one">
- <a href="#">Run</a></li>
- <li id="run_cell_select_below" title="Run this cell, select below">
- <a href="#">Run and Select Below</a></li>
- <li id="run_cell_insert_below" title="Run this cell, insert below">
- <a href="#">Run and Insert Below</a></li>
- <li id="run_all_cells" title="Run all cells in the notebook">
- <a href="#">Run All</a></li>
- <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
- <a href="#">Run All Above</a></li>
- <li id="run_all_cells_below" title="Run this cell and all cells below it">
- <a href="#">Run All Below</a></li>
- <li class="divider"></li>
- <li id="change_cell_type" class="dropdown-submenu"
- title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
- <a href="#">Cell Type</a>
- <ul class="dropdown-menu">
- <li id="to_code"
- title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
- <a href="#">Code</a></li>
- <li id="to_markdown"
- title="Contents will be rendered as HTML and serve as explanatory text">
- <a href="#">Markdown</a></li>
- <li id="to_raw"
- title="Contents will pass through nbconvert unmodified">
- <a href="#">Raw NBConvert</a></li>
- <li id="to_heading1"><a href="#">Heading 1</a></li>
- <li id="to_heading2"><a href="#">Heading 2</a></li>
- <li id="to_heading3"><a href="#">Heading 3</a></li>
- <li id="to_heading4"><a href="#">Heading 4</a></li>
- <li id="to_heading5"><a href="#">Heading 5</a></li>
- <li id="to_heading6"><a href="#">Heading 6</a></li>
- </ul>
- </li>
- <li class="divider"></li>
- <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
- <ul class="dropdown-menu">
- <li id="toggle_current_output"
- title="Hide/Show the output of the current cell">
- <a href="#">Toggle</a>
- </li>
- <li id="toggle_current_output_scroll"
- title="Scroll the output of the current cell">
- <a href="#">Toggle Scrolling</a>
- </li>
- <li id="clear_current_output"
- title="Clear the output of the current cell">
- <a href="#">Clear</a>
- </li>
- </ul>
- </li>
- <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
- <ul class="dropdown-menu">
- <li id="toggle_all_output"
- title="Hide/Show the output of all cells">
- <a href="#">Toggle</a>
- </li>
- <li id="toggle_all_output_scroll"
- title="Scroll the output of all cells">
- <a href="#">Toggle Scrolling</a>
- </li>
- <li id="clear_all_output"
- title="Clear the output of all cells">
- <a href="#">Clear</a>
- </li>
- </ul>
- </li>
- </ul>
- </li>
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
- <ul id="kernel_menu" class="dropdown-menu">
- <li id="int_kernel"
- title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
- <a href="#">Interrupt</a></li>
- <li id="restart_kernel"
- title="Restart the Kernel">
- <a href="#">Restart</a></li>
- </ul>
- </li>
- <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
- <ul id="help_menu" class="dropdown-menu">
- <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
- <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
- <li class="divider"></li>
- {% set
- sections = (
- (
- (static_url("a_tour_of_sage/index.html"), "A Tour of Sage", True),
- (static_url("tutorial/index.html"), "Sage Tutorial", True),
- (static_url("thematic_tutorials/index.html"), "Thematic Tutorials", True),
- (static_url("constructions/index.html"), "Sage Constructions", True),
- (static_url("developer/index.html"), "Developer Guide", True),
- (static_url("reference/index.html"), "Sage Reference", True),
- ),
- (
- ("http://www.singular.uni-kl.de/Manual/latest/index.htm", "Singular", True),
- ("http://gap-system.org/Manuals/doc/ref/chap0.html", "GAP", True),
- ("http://docs.scipy.org/doc/numpy/reference/", "NumPy",True),
- ("http://docs.scipy.org/doc/scipy/reference/", "SciPy",True),
- ("http://docs.sympy.org/latest/index.html", "SymPy",True),
- ),
- (
- ("http://ipython.org/documentation.html","IPython Help",True),
- ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
- ("http://docs.python.org","Python",True),
- ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
- ("http://matplotlib.org/contents.html","Matplotlib",True),
- )
- )
- %}
-
- {% for helplinks in sections %}
- {% for link in helplinks %}
- <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
- {{'<i class="icon-external-link menu-icon pull-right"></i>' if link[2]}}
- {{link[1]}}
- </a></li>
- {% endfor %}
- {% if not loop.last %}
- <li class="divider"></li>
- {% endif %}
- {% endfor %}
- </li>
- </ul>
- </li>
- </ul>
- <div id="kernel_indicator" class="indicator_area pull-right">
- <i id="kernel_indicator_icon"></i>
- </div>
- <div id="modal_indicator" class="indicator_area pull-right">
- <i id="modal_indicator_icon"></i>
- </div>
- <div id="notification_area"></div>
- </div>
- </div>
-</div>
-</div>
-<div id="maintoolbar" class="navbar">
- <div class="toolbar-inner navbar-inner navbar-nobg">
- <div id="maintoolbar-container" class="container"></div>
- </div>
-</div>
-</div>
-
-<div id="ipython-main-app">
-
- <div id="notebook_panel">
- <div id="notebook"></div>
- <div id="pager_splitter"></div>
- <div id="pager">
- <div id='pager_button_area'>
- </div>
- <div id="pager-container" class="container"></div>
- </div>
- </div>
-
-</div>
-<div id='tooltip' class='ipython_tooltip' style='display:none'></div>
-
-
-{% endblock %}
-
-
-{% block script %}
-
-{{super()}}
-
-<script src="{{ static_url("components/google-caja/html-css-sanitizer-minified.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
-<script type="text/javascript">
- CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js", include_version=False) }}";
-</script>
-<script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/addon/edit/closebrackets.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/mode/gfm/gfm.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
-
-<script src="{{ static_url("components/highlight.js/build/highlight.pack.js") }}" charset="utf-8"></script>
-
-<script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
-
-<script src="{{ static_url("base/js/events.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("base/js/keyboard.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("base/js/security.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("services/kernels/js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("services/kernels/js/comm.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("services/sessions/js/session.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/celltoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/keyboardmanager.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/tour.js") }}" type="text/javascript" charset="utf-8"></script>
-
-<script src="{{ static_url("notebook/js/config.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
-
-<script src="{{ static_url("notebook/js/contexthint.js") }}" charset="utf-8"></script>
-
-<script src="{{ static_url("notebook/js/celltoolbarpresets/default.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/celltoolbarpresets/rawcell.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{ static_url("notebook/js/celltoolbarpresets/slideshow.js") }}" type="text/javascript" charset="utf-8"></script>
-
-{% endblock %}
diff --git a/src/ext/notebook-ipython/templates/page.html b/src/ext/notebook-ipython/templates/page.html
deleted file mode 100644
index f971174..0000000
--- a/src/ext/notebook-ipython/templates/page.html
+++ /dev/null
@@ -1,94 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-
-<head>
- <meta charset="utf-8">
-
- <title>{% block title %}Sage IPython Notebook{% endblock %}</title>
- <link rel="shortcut icon" type="image/x-icon" href="{{static_url("base/images/favicon.ico") }}">
- <meta http-equiv="X-UA-Compatible" content="chrome=1">
- <link rel="stylesheet" href="{{static_url("components/jquery-ui/themes/smoothness/jquery-ui.min.css") }}" type="text/css" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-
- {% block stylesheet %}
- <link rel="stylesheet" href="{{ static_url("style/style.min.css") }}" type="text/css"/>
- {% endblock %}
- <link rel="stylesheet" href="{{ static_url("custom/custom.css") }}" type="text/css" />
- <script src="{{static_url("components/requirejs/require.js") }}" type="text/javascript" charset="utf-8"></script>
- <script>
- require.config({
- baseUrl: '{{static_url("", include_version=False)}}',
- paths: {
- nbextensions : '{{ base_url }}nbextensions',
- underscore : '{{static_url("components/underscore/underscore-min.js")}}',
- backbone : '{{static_url("components/backbone/backbone-min.js")}}',
- },
- shim: {
- underscore: {
- exports: '_'
- },
- backbone: {
- deps: ["underscore", "jquery"],
- exports: "Backbone"
- }
- }
- });
- </script>
-
- {% block meta %}
- {% endblock %}
-
-</head>
-
-<body {% block params %}{% endblock %}>
-
-<noscript>
- <div id='noscript'>
- IPython Notebook requires JavaScript.<br>
- Please enable it to proceed.
- </div>
-</noscript>
-
-<div id="header" class="navbar navbar-static-top">
- <div class="navbar-inner navbar-nobg">
- <div class="container">
- <div id="ipython_notebook" class="nav brand pull-left"><a href="{{base_url}}tree/{{notebook_path}}" alt='dashboard'><img src='{{static_url("sage-logo.png") }}' alt='Sage IPython Notebook'/></a></div>
-
- {% block login_widget %}
-
- <span id="login_widget">
- {% if logged_in %}
- <button id="logout">Logout</button>
- {% elif login_available and not logged_in %}
- <button id="login">Login</button>
- {% endif %}
- </span>
-
- {% endblock %}
-
- {% block header %}
- {% endblock %}
- </div>
- </div>
-</div>
-
-<div id="site">
-{% block site %}
-{% endblock %}
-</div>
-
-<script src="{{static_url("components/jquery/jquery.min.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{static_url("components/jquery-ui/ui/minified/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{static_url("components/bootstrap/bootstrap/js/bootstrap.min.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{static_url("base/js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{static_url("base/js/page.js") }}" type="text/javascript" charset="utf-8"></script>
-<script src="{{static_url("auth/js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
-
-{% block script %}
-{% endblock %}
-
-<script src="{{static_url("custom/custom.js") }}" type="text/javascript" charset="utf-8"></script>
-
-</body>
-
-</html>
diff --git a/src/ext/notebook-ipython/templates/tree.html b/src/ext/notebook-ipython/templates/tree.html
deleted file mode 100644
index 540c828..0000000
--- a/src/ext/notebook-ipython/templates/tree.html
+++ /dev/null
@@ -1,121 +0,0 @@
-{% extends "page.html" %}
-
-{% block title %}{{page_title}}{% endblock %}
-
-
-{% block stylesheet %}
-{{super()}}
-<link rel="stylesheet" href="{{ static_url("tree/css/override.css") }}" type="text/css" />
-{% endblock %}
-
-{% block params %}
-
-data-project="{{project}}"
-data-base-url="{{base_url}}"
-data-notebook-path="{{notebook_path}}"
-
-{% endblock %}
-
-
-{% block site %}
-
-<div id="ipython-main-app" class="container">
-
-<div id="tab_content" class="tabbable">
- <ul id="tabs" class="nav nav-tabs">
- <li class="active"><a href="#notebooks" data-toggle="tab">Notebooks</a></li>
- <li><a href="#running" data-toggle="tab">Running</a></li>
- <li><a href="#clusters" data-toggle="tab">Clusters</a></li>
- </ul>
-
- <div class="tab-content">
- <div id="notebooks" class="tab-pane active">
- <div id="notebook_toolbar" class="row-fluid">
- <div class="span8">
- <form id='alternate_upload' class='alternate_upload' >
- <span id="notebook_list_info" style="position:absolute" >
- To import a notebook, drag the file onto the listing below or <strong>click here</strong>.
- </span>
- <input type="file" name="datafile" class="fileinput" multiple='multiple'>
- </form>
- </div>
- <div class="span4 clearfix">
- <span id="notebook_buttons" class="pull-right">
- <button id="new_notebook" title="Create new notebook" class="btn btn-small">New Notebook</button>
- <button id="refresh_notebook_list" title="Refresh notebook list" class="btn btn-small"><i class="icon-refresh"></i></button>
- </span>
- </div>
- </div>
-
- <div id="notebook_list">
- <div id="notebook_list_header" class="row-fluid list_header">
- <div id="project_name">
- <ul class="breadcrumb">
- <li><a href="{{breadcrumbs[0][0]}}"><i class="icon-home"></i></a><span>/</span></li>
- {% for crumb in breadcrumbs[1:] %}
- <li><a href="{{crumb[0]}}">{{crumb[1]}}</a> <span>/</span></li>
- {% endfor %}
- </ul>
- </div>
- </div>
- </div>
- </div>
-
- <div id="running" class="tab-pane">
-
- <div id="running_toolbar" class="row-fluid">
- <div class="span8">
- <span id="running_list_info">Currently running IPython notebooks</span>
- </div>
- <div class="span4" class="clearfix">
- <span id="running_buttons" class="pull-right">
- <button id="refresh_running_list" title="Refresh running list" class="btn btn-small"><i class="icon-refresh"></i></button>
- </span>
- </div>
- </div>
-
- <div id="running_list">
- <div id="running_list_header" class="row-fluid list_header">
- <div> There are no notebooks running. </div>
- </div>
- </div>
- </div>
-
- <div id="clusters" class="tab-pane">
-
- <div id="cluster_toolbar" class="row-fluid">
- <div class="span8">
- <span id="cluster_list_info">IPython parallel computing clusters</span>
- </div>
- <div class="span4" class="clearfix">
- <span id="cluster_buttons" class="pull-right">
- <button id="refresh_cluster_list" title="Refresh cluster list" class="btn btn-small"><i class="icon-refresh"></i></button>
- </span>
- </div>
- </div>
-
- <div id="cluster_list">
- <div id="cluster_list_header" class="row-fluid list_header">
- <div class="profile_col span4">profile</div>
- <div class="status_col span3">status</div>
- <div class="engines_col span3" title="Enter the number of engines to start or empty for default"># of engines</div>
- <div class="action_col span2">action</div>
- </div>
- </div>
- </div>
-</div>
-
-</div>
-
-{% endblock %}
-
-{% block script %}
- {{super()}}
- <script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
- <script src="{{static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
- <script src="{{static_url("tree/js/sessionlist.js") }}" type="text/javascript" charset="utf-8"></script>
- <script src="{{static_url("tree/js/notebooklist.js") }}" type="text/javascript" charset="utf-8"></script>
- <script src="{{static_url("tree/js/kernellist.js") }}" type="text/javascript" charset="utf-8"></script>
- <script src="{{static_url("tree/js/clusterlist.js") }}" type="text/javascript" charset="utf-8"></script>
- <script src="{{static_url("tree/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
-{% endblock %}
diff --git a/src/sage/combinat/root_system/plot.py b/src/sage/combinat/root_system/plot.py
index 9faff6f..e1d06d4 100644
--- a/src/sage/combinat/root_system/plot.py
+++ b/src/sage/combinat/root_system/plot.py
@@ -731,7 +731,7 @@ class PlotOptions:
sage: options = RootSystem(["B",3]).root_lattice().plot_parse_options()
sage: print options.text("coucou", [0,1,2]).x3d_str()
<Transform translation='0 1 2'>
- <Shape><Text string='coucou' solid='true'/><Appearance><Material diffuseColor='0.0 0.0 0.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
+ <Shape><Text string='coucou' solid='true'/><Appearance><Material diffuseColor='0.0 0.0 0.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
<BLANKLINE>
</Transform>
"""
diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py
index c87b07d..6eb2ec5 100644
--- a/src/sage/combinat/tiling.py
+++ b/src/sage/combinat/tiling.py
@@ -177,17 +177,16 @@ Animation of the solutions::
sage: from sage.combinat.tiling import Polyomino, TilingSolver
sage: Y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='yellow')
sage: T = TilingSolver([Y], box=(15,15), reusable=True, reflection=True)
- sage: a = T.animate(stop=40) # long time
- sage: a # long time
+ sage: a = T.animate(stop=40) # long time # optional -- ImageMagick
+ sage: a # long time # optional -- ImageMagick
Animation with 40 frames
- sage: a.show() # not tested - requires convert command
Incremental animation of the solutions (one piece is removed/added at a time)::
- sage: a = T.animate('incremental', stop=40) # long time
- sage: a # long time
+ sage: a = T.animate('incremental', stop=40) # long time # optional -- ImageMagick
+ sage: a # long time # optional -- ImageMagick
Animation with 40 frames
- sage: a.show(delay=50, iterations=1) # not tested - requires convert command
+ sage: a.show(delay=50, iterations=1) # long time # optional -- ImageMagick
5d Easy Example
---------------
@@ -1650,20 +1649,20 @@ class TilingSolver(SageObject):
sage: y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='cyan')
sage: T = TilingSolver([y], box=(5,10), reusable=True, reflection=True)
sage: a = T.animate()
- sage: a
+ sage: a # optional -- ImageMagick
Animation with 10 frames
Include partial solutions (common prefix between two consecutive
solutions)::
sage: a = T.animate('common_prefix')
- sage: a
+ sage: a # optional -- ImageMagick
Animation with 19 frames
Incremental solutions (one piece removed or added at a time)::
sage: a = T.animate('incremental') # long time (2s)
- sage: a # long time (2s)
+ sage: a # long time (2s) # optional -- ImageMagick
Animation with 123 frames
::
diff --git a/src/sage/combinat/words/paths.py b/src/sage/combinat/words/paths.py
index d1dfa32..c43426e 100644
--- a/src/sage/combinat/words/paths.py
+++ b/src/sage/combinat/words/paths.py
@@ -1625,16 +1625,16 @@ class FiniteWordPath_2d(FiniteWordPath_all):
sage: P = WordPaths('abAB')
sage: p = P('aaababbb')
- sage: a = p.animate(); a
+ sage: a = p.animate(); a # optional -- ImageMagick
Animation with 9 frames
- sage: show(a) # optional -- ImageMagick
- sage: a.gif(delay=35, iterations=3) # optional
+ sage: show(a) # optional -- ImageMagick
+ sage: a.gif(delay=35, iterations=3) # optional -- ImageMagick
::
sage: P = WordPaths('abcdef',steps='triangle')
sage: p = P('abcdef')
- sage: p.animate()
+ sage: p.animate() # optional -- ImageMagick
Animation with 8 frames
If the path is closed, the plain polygon is added at the end of the
@@ -1642,7 +1642,7 @@ class FiniteWordPath_2d(FiniteWordPath_all):
sage: P = WordPaths('abAB')
sage: p = P('ababAbABABaB')
- sage: a = p.animate(); a
+ sage: a = p.animate(); a # optional -- ImageMagick
Animation with 14 frames
Another example illustrating a Fibonacci tile::
diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py
index 5e8d37f..fde65c3 100644
--- a/src/sage/doctest/forker.py
+++ b/src/sage/doctest/forker.py
@@ -101,8 +101,12 @@ def init_sage():
sage.doctest.DOCTEST_MODE=True
import sage.all_cmdline
sage.interfaces.quit.invalidate_all()
- import sage.repl.display.python_hook
- sys.displayhook = sage.repl.display.python_hook.DoctestDisplayHook()
+
+ # Use the rich output backend for doctest
+ from sage.repl.rich_output import get_display_manager
+ dm = get_display_manager()
+ from sage.repl.rich_output.backend_doctest import BackendDoctest
+ dm.switch_backend(BackendDoctest())
# Switch on extra debugging
from sage.structure.debug_options import debug
diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py
index c1e8e6c..b6b78e2 100644
--- a/src/sage/geometry/polyhedron/base.py
+++ b/src/sage/geometry/polyhedron/base.py
@@ -679,7 +679,33 @@ class Polyhedron_base(Element):
.format(self.ambient_dim()))
return plot_method(*opts)
- show = plot
+ def show(self, **kwds):
+ """
+ Display graphics immediately
+
+ This method attempts to display the graphics immediately,
+ without waiting for the currently running code (if any) to
+ return to the command line. Be careful, calling it from within
+ a loop will potentially launch a large number of external
+ viewer programs.
+
+ INPUT:
+
+ - ``kwds`` -- optional keyword arguments. See :meth:`plot` for
+ the description of available options.
+
+ OUTPUT:
+
+ This method does not return anything. Use :meth:`plot` if you
+ want to generate a graphics object that can be saved or
+ further transformed.
+
+ EXAMPLES::
+
+ sage: square = polytopes.n_cube(2)
+ sage: square.show(point='red')
+ """
+ self.plot(**kwds).show()
def _repr_(self):
"""
diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py
index ca8c03c..14ae9bb 100644
--- a/src/sage/geometry/polyhedron/library.py
+++ b/src/sage/geometry/polyhedron/library.py
@@ -595,7 +595,7 @@ class Polytopes():
sage: perm4 = polytopes.permutahedron(4)
sage: perm4
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 24 vertices
- sage: polytopes.permutahedron(5).show() # long time
+ sage: polytopes.permutahedron(5).plot() # long time
Graphics3d Object
"""
verts = range(1,n+1)
diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py
index d88fcd7..ee838d9 100644
--- a/src/sage/geometry/polyhedron/plot.py
+++ b/src/sage/geometry/polyhedron/plot.py
@@ -726,20 +726,6 @@ class Projection(SageObject):
deprecation(16625, 'use Projection.plot instead')
return self.plot(*args, **kwds)
- def _graphics_(self, **kwds):
- """
- Display projection graphically on the Sage command line.
-
- See :meth:`~sage.plot.graphics.Graphics._graphics_`.
-
- EXAMPLES::
-
- sage: polytopes.n_cube(3).projection()._graphics_(
- ....: mime_types={'image/png'})
- Graphics file image/png
- """
- return self.plot()._graphics_(**kwds)
-
def _init_from_2d(self, polyhedron):
"""
Internal function: Initialize from polyhedron in
@@ -762,7 +748,6 @@ class Projection(SageObject):
self._init_lines_arrows(polyhedron)
self._init_area_2d(polyhedron)
-
def _init_from_3d(self, polyhedron):
"""
Internal function: Initialize from polyhedron in
diff --git a/src/sage/interfaces/mathematica.py b/src/sage/interfaces/mathematica.py
index 8121167..e56675e 100644
--- a/src/sage/interfaces/mathematica.py
+++ b/src/sage/interfaces/mathematica.py
@@ -365,6 +365,7 @@ AUTHORS:
import os
import re
+from sage.misc.cachefunc import cached_method
from expect import (Expect, ExpectElement, ExpectFunction,
FunctionElement, AsciiArtString)
@@ -819,9 +820,94 @@ class MathematicaElement(ExpectElement):
"""
return self.Length()
- def show(self, filename=None, ImageSize=600):
+ @cached_method
+ def _is_graphics(self):
+ """
+ Test whether the mathematica expression is graphics
+
+ OUTPUT:
+
+ Boolean.
+
+ EXAMPLES::
+
+ sage: P = mathematica('Plot[Sin[x],{x,-2Pi,4Pi}]') # optional - mathematica
+ sage: P._is_graphics() # optional - mathematica
+ True
+ """
+ P = self._check_valid()
+ return P.eval('InputForm[%s]' % self.name()).strip().startswith('Graphics[')
+
+ def save_image(self, filename, ImageSize=600):
r"""
- Show a mathematica expression or plot in the Sage notebook.
+ Save a mathematica graphics
+
+ INPUT:
+
+ - ``filename`` -- string. The filename to save as. The
+ extension determines the image file format.
+
+ - ``ImageSize`` -- integer. The size of the resulting image.
+
+ EXAMPLES::
+
+ sage: P = mathematica('Plot[Sin[x],{x,-2Pi,4Pi}]') # optional - mathematica
+ sage: filename = tmp_filename() # optional - mathematica
+ sage: P.save(filename, ImageSize=800) # optional - mathematica
+ """
+ P = self._check_valid()
+ if not self._is_graphics():
+ raise ValueError('mathematica expression is not graphics')
+ filename = os.path.abspath(filename)
+ s = 'Export["%s", %s, ImageSize->%s]'%(filename, self.name(), ImageSize)
+ P.eval(s)
+
+ def _rich_repr_(self, display_manager, **kwds):
+ """
+ Rich Output Magic Method
+
+ See :mod:`sage.repl.rich_output` for details.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: P = mathematica('Plot[Sin[x],{x,-2Pi,4Pi}]') # optional - mathematica
+ sage: P._rich_repr_(dm) # optional - mathematica
+ OutputImagePng container
+ """
+ if self._is_graphics():
+ OutputImagePng = display_manager.types.OutputImagePng
+ if display_manager.preferences.graphics == 'disable':
+ return
+ if OutputImagePng in display_manager.supported_output():
+ return display_manager.graphics_from_save(
+ self.save, kwds, '.png', OutputImagePng)
+ else:
+ OutputLatex = display_manager.types.OutputLatex
+ if display_manager.preferences.text == 'plain':
+ return
+ if OutputLatex in display_manager.supported_output():
+ return OutputLatex(self._latex_())
+
+ def show(self, ImageSize=600):
+ r"""
+ Show a mathematica expression immediately.
+
+ This method attempts to display the graphics immediately,
+ without waiting for the currently running code (if any) to
+ return to the command line. Be careful, calling it from within
+ a loop will potentially launch a large number of external
+ viewer programs.
+
+ INPUT:
+
+ - ``ImageSize`` -- integer. The size of the resulting image.
+
+ OUTPUT:
+
+ This method does not return anything. Use :meth:`save` if you
+ want to save the figure as an image.
EXAMPLES::
@@ -832,19 +918,10 @@ class MathematicaElement(ExpectElement):
sage: show(Q) # optional - mathematica
<html><div class="math">\frac{\sin (x \cos (y))}{\sqrt{1-x^2}}</div></html>
"""
- P = self._check_valid()
- if P.eval('InputForm[%s]' % self.name()).strip().startswith('Graphics['):
- if filename is None:
- from sage.misc.temporary_file import graphics_filename
- filename = graphics_filename()
- orig_dir = P.eval('Directory[]').strip()
- P.chdir(os.path.abspath("."))
- s = 'Export["%s", %s, ImageSize->%s]'%(filename, self.name(), ImageSize)
- P.eval(s)
- P.chdir(orig_dir)
- else:
- print '<html><div class="math">%s</div></html>' % self._latex_()
-
+ from sage.repl.rich_output import get_display_manager
+ dm = get_display_manager()
+ dm.display_immediately(self, ImageSize=ImageSize)
+
def str(self):
return str(self)
diff --git a/src/sage/interfaces/sage0.py b/src/sage/interfaces/sage0.py
index 0e1a50b..8401dc8 100644
--- a/src/sage/interfaces/sage0.py
+++ b/src/sage/interfaces/sage0.py
@@ -426,17 +426,18 @@ class Sage(Expect):
class SageElement(ExpectElement):
- def _graphics_(self, **kwds):
+ def _rich_repr_(self, display_manager, **kwds):
"""
- Disable graphical output.
+ Disable rich output
This is necessary because otherwise our :meth:`__getattr__`
would be called.
EXAMPLES::
+ sage: from sage.repl.rich_output import get_display_manager
sage: m = sage0(4)
- sage: m._graphics_() is None
+ sage: m._rich_repr_(get_display_manager()) is None
True
"""
return None
diff --git a/src/sage/interfaces/tachyon.py b/src/sage/interfaces/tachyon.py
index 8930468..b7da4ae 100644
--- a/src/sage/interfaces/tachyon.py
+++ b/src/sage/interfaces/tachyon.py
@@ -15,13 +15,16 @@ AUTHOR:
# http://www.gnu.org/licenses/
#*****************************************************************************
+import os
+import sys
+
from sage.misc.pager import pager
from sage.misc.temporary_file import tmp_filename
from sage.misc.sagedoc import format
-import os
-import sys
+from sage.structure.sage_object import SageObject
+
-class TachyonRT:
+class TachyonRT(SageObject):
"""
The Tachyon Ray Tracer
@@ -64,7 +67,6 @@ class TachyonRT:
OUTPUT:
-
- Some text may be displayed onscreen.
- The file outfile is created.
@@ -72,11 +74,10 @@ class TachyonRT:
EXAMPLES:
- AUTHORS:
-
- - John E. Stone
+
+ .. automethod:: __call__
"""
- def __repr__(self):
+ def _repr_(self):
"""
Returns a brief description of this interface object (the Tachyon raytracer written by John Stone).
@@ -89,13 +90,23 @@ class TachyonRT:
"""
return "John Stone's Tachyon Ray Tracer"
- def __call__(self, model, outfile='sage.png',
- verbose=1, block=True, extra_opts=''):
+ def __call__(self, model, outfile='sage.png', verbose=1, extra_opts=''):
"""
This executes the tachyon program, given a scene file input.
- The default is to return the result as a PNG file called 'sage.png'.
- TESTS::
+ INPUT:
+
+ - ``model`` -- string. The tachyon model.
+
+ - ``outfile`` -- string, default ``'sage.png'``. The filename
+ to save the model to.
+
+ - ``verbose`` -- 0, 1, (default) or 2. The verbosity level.
+
+ - ``extra_opts`` -- string (default: empty string). Extra
+ options that will be appended to the tachyon commandline.
+
+ EXAMPLES::
sage: from sage.interfaces.tachyon import TachyonRT
sage: tgen = Tachyon()
@@ -105,45 +116,51 @@ class TachyonRT:
'resolution'
sage: t = TachyonRT()
sage: import os
- sage: t(tgen.str(), outfile = os.devnull)
+ sage: t(tgen.str(), outfile=os.devnull)
tachyon ...
Tachyon Parallel/Multiprocessor Ray Tracer...
+
+ TESTS::
+
+ sage: from sage.env import SAGE_EXTCODE
+ sage: filename = os.path.join(SAGE_EXTCODE, 'doctest', 'invalid', 'syntax_error.tachyon')
+ sage: syntax_error = open(filename, 'r').read()
+ sage: t(syntax_error, outfile=os.devnull)
+ Traceback (most recent call last):
+ ...
+ RuntimeError: Tachyon Parallel/Multiprocessor Ray Tracer Version 0.98.9
+ ...
+ Parser failed due to an input file syntax error.
+ Aborting render.
"""
modelfile = tmp_filename(ext='.dat')
open(modelfile,'w').write(model)
- opts = ''
+ cmd = ['tachyon', modelfile]
ext = outfile[-4:].lower()
if ext == '.png':
- opts += ' -format PNG '
+ cmd += ['-format', 'PNG']
elif ext == '.tga':
- opts += ' -format TARGA '
+ cmd += ['-format', 'TARGA']
elif ext == '.bmp':
- opts += ' -format BMP '
+ cmd += ['-format', 'BMP']
elif ext == '.ppm':
- opts += ' -format PPM '
+ cmd += ['-format', 'PPM']
elif ext == '.rgb':
- opts += ' -format RGB '
-
- opts += ' -o %s '%outfile
-
- opts += ' ' + extra_opts + ' '
-
+ cmd += ['-format', 'RGB']
+ cmd += ['-o', outfile]
+ cmd += extra_opts.split()
if verbose >= 2:
- opts += ' +V '
- elif verbose == 0:
- opts += ' 1>/dev/null'
-
- cmd = 'tachyon %s %s; rm -f "%s"'%(modelfile,opts, modelfile)
-
- if not block:
- cmd = '( ' + cmd + ' ) &'
-
+ cmd += ['+V']
if verbose:
- print cmd
- # One should always flush before system()
- sys.stdout.flush()
- sys.stderr.flush()
- os.system(cmd)
+ print(' '.join(cmd))
+ import subprocess
+ out = subprocess.check_output(cmd)
+ if verbose >= 1:
+ print(out)
+ if out.rstrip().endswith('Aborting render.'):
+ raise RuntimeError(out)
+ if outfile != os.devnull and os.stat(outfile).st_size == 0:
+ raise RuntimeError('tachyon did not abort but output file is empty')
def usage(self, use_pager=True):
"""
diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx
index 45454ee..27daca9 100644
--- a/src/sage/matrix/matrix2.pyx
+++ b/src/sage/matrix/matrix2.pyx
@@ -8310,11 +8310,9 @@ cdef class Matrix(matrix1.Matrix):
from sage.combinat.permutation import bistochastic_as_sum_of_permutations
return bistochastic_as_sum_of_permutations(self)
-
- def visualize_structure(self, filename=None, maxsize=512):
- """
- Write a PNG image to 'filename' which visualizes self by putting
- black pixels in those positions which have nonzero entries.
+ def visualize_structure(self, maxsize=512):
+ r"""
+ Visualize the non-zero entries
White pixels are put at positions with zero entries. If 'maxsize'
is given, then the maximal dimension in either x or y direction is
@@ -8326,62 +8324,56 @@ cdef class Matrix(matrix1.Matrix):
INPUT:
+ - ``maxsize`` - integer (default: ``512``). Maximal dimension
+ in either x or y direction of the resulting image. If
+ ``None`` or a maxsize larger than
+ ``max(self.nrows(),self.ncols())`` is given the image will
+ have the same pixelsize as the matrix dimensions.
- - ``filename`` - either a path or None in which case a
- filename in the current directory is chosen automatically
- (default:None)
-
+ OUTPUT:
- maxsize - maximal dimension in either x or y direction of the
- resulting image. If None or a maxsize larger than
- max(self.nrows(),self.ncols()) is given the image will have the
- same pixelsize as the matrix dimensions (default: 512)
+ Bitmap image as an instance of
+ :class:`~sage.repl.image.Image`.
EXAMPLE::
- sage: M = random_matrix(CC, 4)
- sage: M.visualize_structure()
- """
- import gd
- import os
+ sage: M = random_matrix(CC, 5, 7)
+ sage: for i in range(5): M[i,i] = 0
+ sage: M[4, 0] = M[0, 6] = M[4, 6] = 0
+ sage: img = M.visualize_structure(); img
+ 7x5px 24-bit RGB image
+ You can use :meth:`~sage.repl.image.Image.save` to save the
+ resulting image::
+
+ sage: filename = tmp_filename(ext='.png')
+ sage: img.save(filename)
+ sage: open(filename).read().startswith('\x89PNG')
+ True
+ """
cdef int x, y, _x, _y, v, bi, bisq
cdef int ir,ic
cdef float b, fct
-
mr, mc = self.nrows(), self.ncols()
-
if maxsize is None:
-
ir = mc
ic = mr
b = 1.0
-
elif max(mr,mc) > maxsize:
-
maxsize = float(maxsize)
ir = int(mc * maxsize/max(mr,mc))
ic = int(mr * maxsize/max(mr,mc))
b = max(mr,mc)/maxsize
-
else:
-
ir = mc
ic = mr
b = 1.0
-
bi = round(b)
bisq = bi*bi
fct = 255.0/bisq
-
- im = gd.image((ir,ic),1)
- white = im.colorExact((255,255,255))
- im.fill((0,0),white)
-
- # these speed things up a bit
- colorExact = im.colorExact
- setPixel = im.setPixel
-
+ from sage.repl.image import Image
+ img = Image('RGB', (ir, ic))
+ pixel = img.pixels()
for x from 0 <= x < ic:
for y from 0 <= y < ir:
v = bisq
@@ -8389,16 +8381,9 @@ cdef class Matrix(matrix1.Matrix):
for _y from 0 <= _y < bi:
if not self.get_unsafe(<int>(x*b + _x), <int>(y*b + _y)).is_zero():
v-=1 #increase darkness
-
- v = round(v*fct)
- val = colorExact((v,v,v))
- setPixel((y,x), val)
-
- if filename is None:
- from sage.misc.temporary_file import graphics_filename
- filename = graphics_filename()
-
- im.writePng(filename)
+ v = round(v*fct)
+ pixel[y, x] = (v, v, v)
+ return img
def density(self):
"""
diff --git a/src/sage/matrix/matrix_modn_sparse.pyx b/src/sage/matrix/matrix_modn_sparse.pyx
index f30b01b..be1cee5 100644
--- a/src/sage/matrix/matrix_modn_sparse.pyx
+++ b/src/sage/matrix/matrix_modn_sparse.pyx
@@ -563,7 +563,7 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse):
return nzp
def visualize_structure(self, filename=None, maxsize=512):
- """
+ r"""
Write a PNG image to 'filename' which visualizes self by putting
black pixels in those positions which have nonzero entries.
@@ -577,78 +577,63 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse):
INPUT:
+ - ``filename`` -- String. Name of the filename to save the
+ resulting image.
- - ``filename`` - either a path or None in which case a
- filename in the current directory is chosen automatically
- (default:None)
-
- - ``maxsize`` - maximal dimension in either x or y
- direction of the resulting image. If None or a maxsize larger than
- max(self.nrows(),self.ncols()) is given the image will have the
- same pixelsize as the matrix dimensions (default: 512)
+ - ``maxsize`` - integer (default: ``512``). Maximal dimension
+ in either x or y direction of the resulting image. If
+ ``None`` or a maxsize larger than
+ ``max(self.nrows(),self.ncols())`` is given the image will
+ have the same pixelsize as the matrix dimensions.
EXAMPLES::
sage: M = Matrix(GF(7), [[0,0,0,1,0,0,0,0],[0,1,0,0,0,0,1,0]], sparse=True); M
[0 0 0 1 0 0 0 0]
[0 1 0 0 0 0 1 0]
- sage: M.visualize_structure()
- """
- import gd
- import os
+ sage: img = M.visualize_structure(); img
+ 8x2px 24-bit RGB image
+ You can use :meth:`~sage.repl.image.Image.save` to save the
+ resulting image::
+
+ sage: filename = tmp_filename(ext='.png')
+ sage: img.save(filename)
+ sage: open(filename).read().startswith('\x89PNG')
+ True
+ """
cdef Py_ssize_t i, j, k
cdef float blk,invblk
cdef int delta
cdef int x,y,r,g,b
-
mr, mc = self.nrows(), self.ncols()
-
if maxsize is None:
-
ir = mc
ic = mr
blk = 1.0
invblk = 1.0
-
elif max(mr,mc) > maxsize:
-
maxsize = float(maxsize)
ir = int(mc * maxsize/max(mr,mc))
ic = int(mr * maxsize/max(mr,mc))
blk = max(mr,mc)/maxsize
invblk = maxsize/max(mr,mc)
-
else:
-
ir = mc
ic = mr
blk = 1.0
invblk = 1.0
-
delta = <int>(255.0 / blk*blk)
-
- im = gd.image((ir,ic),1)
- white = im.colorExact((255,255,255))
- im.fill((0,0),white)
-
- colorComponents = im.colorComponents
- getPixel = im.getPixel
- setPixel = im.setPixel
- colorExact = im.colorExact
-
+ from sage.repl.image import Image
+ img = Image('RGB', (ir, ic), (255, 255, 255))
+ pixel = img.pixels()
for i from 0 <= i < self._nrows:
for j from 0 <= j < self.rows[i].num_nonzero:
x = <int>(invblk * self.rows[i].positions[j])
y = <int>(invblk * i)
- r,g,b = colorComponents( getPixel((x,y)))
- setPixel( (x,y), colorExact((r-delta,g-delta,b-delta)) )
-
- if filename is None:
- from sage.misc.temporary_file import graphics_filename
- filename = graphics_filename()
-
- im.writePng(filename)
+ r, g, b = pixel[x, y]
+ pixel[x, y] = (r-delta, g-delta, b-delta)
+ return img
def density(self):
"""
diff --git a/src/sage/misc/ascii_art.py b/src/sage/misc/ascii_art.py
index 39a2298..2c5efd0 100644
--- a/src/sage/misc/ascii_art.py
+++ b/src/sage/misc/ascii_art.py
@@ -65,6 +65,7 @@ manager activated by the magic function: ``%display ascii_art``::
]
***** ]
sage: shell.run_cell('%display simple')
+ sage: shell.quit()
::
@@ -150,9 +151,12 @@ manager activated by the magic function: ``%display ascii_art``::
#
# http://www.gnu.org/licenses/
#*******************************************************************************
+
+import os, sys
from sage.structure.sage_object import SageObject
from sage.rings.integer import Integer
+
################################################################################
### Global variable use to compute the maximal length allows for ascii art
### object.
@@ -244,8 +248,6 @@ class AsciiArt(SageObject):
sage: repr(p5)
' * \n * * \n*****'
"""
- #return "\n".join(self._matrix)
- import os
# Compute the max length of a draw
global MAX_WIDTH
if MAX_WIDTH is not None:
@@ -352,6 +354,34 @@ class AsciiArt(SageObject):
"""
return self._breakpoints
+ def _isatty(self):
+ """
+ Test whether stdout is a TTY
+
+ If this test succeeds, you can assume that stdout is directly
+ connected to a terminal. Otherwise you should treat stdout as
+ being redirected to a file.
+
+ OUTPUT:
+
+ Boolean
+
+ EXAMPLES::
+
+ sage: from sage.misc.ascii_art import empty_ascii_art
+ sage: empty_ascii_art._isatty()
+ False
+ """
+ from sage.doctest import DOCTEST_MODE
+ if DOCTEST_MODE:
+ return False
+ try:
+ return os.isatty(sys.stdout.fileno())
+ except Exception:
+ # The IPython zeromq kernel uses a fake stdout that does
+ # not support fileno()
+ return False
+
def _terminal_width(self):
"""
Compute the width size of the terminal.
@@ -362,10 +392,7 @@ class AsciiArt(SageObject):
sage: empty_ascii_art._terminal_width()
80
"""
- import os, sys
- from sage.doctest.__init__ import DOCTEST_MODE
- isatty = os.isatty(sys.stdout.fileno())
- if DOCTEST_MODE or not isatty:
+ if not self._isatty():
return 80
import fcntl, termios, struct
rc = fcntl.ioctl(int(0), termios.TIOCGWINSZ,
diff --git a/src/sage/misc/displayhook.py b/src/sage/misc/displayhook.py
index 9e86dfe..88edfe4 100644
--- a/src/sage/misc/displayhook.py
+++ b/src/sage/misc/displayhook.py
@@ -1,28 +1,49 @@
+# -*- encoding: utf-8 -*-
"""
-Old Displayhook
+Entrypoint for the SageNB Display System
Just for compatibility with the notebook, you should not use this any
-more. Look into ``sage/repl/display`` instead.
+more. Look into ``sage/repl/`` instead.
"""
-from sage.repl.display.python_hook import DoctestDisplayHook
-from sage.repl.display.formatter import SageNBTextFormatter
+#*****************************************************************************
+# 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/
+#*****************************************************************************
+from sage.repl.rich_output import get_display_manager
+from sage.repl.rich_output.backend_sagenb import BackendSageNB
-class DisplayHook(DoctestDisplayHook):
- def __init__(self):
- """
- Python constructor
+def DisplayHook():
+ """
+ This function is called by SageNB to set up its displayhook.
- EXAMPLES::
+ OUTPUT:
+
+ The new displayhook that will be used by SageNB
+
+ EXAMPLES::
+
+ sage: from sage.misc.displayhook import DisplayHook
+ sage: d = DisplayHook()
+ sage: d
+ <bound method DisplayManager.displayhook of The
+ Sage display manager using the SageNB backend>
+
+ sage: d(set([1, 2, 3])) # Sage commandline output
+ {1, 2, 3}
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: get_display_manager()
+ The Sage display manager using the SageNB backend
+ """
+ display_manager = get_display_manager()
+ backend = BackendSageNB()
+ display_manager.switch_backend(backend)
+ return display_manager.displayhook
- sage: from sage.repl.display.python_hook import DoctestDisplayHook
- sage: d = DoctestDisplayHook()
- sage: d(set([1, 2, 3])) # Sage commandline output
- {1, 2, 3}
- sage: print(set([1, 2, 3])) # Plain Python output
- set([1, 2, 3])
- """
- # do not call super, we set our own formatter
- self.formatter = SageNBTextFormatter()
diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py
index 8d95c0f..00e85c9 100644
--- a/src/sage/misc/latex.py
+++ b/src/sage/misc/latex.py
@@ -59,7 +59,7 @@ import random
import subprocess
import types
-from sage.misc.temporary_file import tmp_dir, graphics_filename
+from sage.misc.temporary_file import tmp_dir
import sage_eval
from sage.misc.sage_ostools import have_program
from sage.misc.cachefunc import cached_function, cached_method
@@ -1917,8 +1917,10 @@ class MathJax:
- ``locals`` - extra local variables used when
evaluating Sage code in ``x``.
- - ``mode`` - string (optional, default ``'display'``): ``'display'``
- for displaymath or ``'inline'`` for inline math
+ - ``mode`` - string (optional, default ``'display'``):
+ ``'display'`` for displaymath, ``'inline'`` for inline
+ math, or ``'plain'`` for just the LaTeX code without the
+ surrounding html and script tags.
- ``combine_all`` - boolean (Default: ``False``): If ``combine_all`` is
``True`` and the input is a tuple, then it does not return a tuple
@@ -1987,24 +1989,21 @@ class MathJax:
subparts.append(spacer % "x")
subparts.append(part[closing + 1:])
parts[i] = "".join(subparts)
- x = "".join(parts)
-
- # In MathJax:
- # inline math: <script type="math/tex">...</script>
- # displaymath: <script type="math/tex; mode=display">...</script>
from sage.misc.latex_macros import sage_configurable_latex_macros
+ latex_string = ''.join(
+ sage_configurable_latex_macros +
+ [_Latex_prefs._option['macros']] +
+ parts
+ )
if mode == 'display':
- modecode = '; mode=display'
+ html = '<html><script type="math/tex; mode=display">{0}</script></html>'
elif mode == 'inline':
- modecode = ''
+ html = '<html><script type="math/tex">{0}</script></html>'
+ elif mode == 'plain':
+ return latex_string
else:
- # what happened here?
- raise ValueError("mode must be either 'display' or 'inline'")
-
- return MathJaxExpr('<html><script type="math/tex{}">'.format(modecode)
- + ''.join(sage_configurable_latex_macros)
- + _Latex_prefs._option['macros']
- + '{}</script></html>'.format(x))
+ raise ValueError("mode must be either 'display', 'inline', or 'plain'")
+ return MathJaxExpr(html.format(latex_string))
def view(objects, title='Sage', debug=False, sep='', tiny=False,
pdflatex=None, engine=None, viewer = None, tightpage = None,
@@ -2177,6 +2176,7 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False,
print(MathJax().eval(objects, mode=mode, combine_all=combine_all))
else:
base_dir = os.path.abspath("")
+ from sage.misc.temporary_file import graphics_filename
png_file = graphics_filename()
png_link = "cell://" + png_file
png(objects, os.path.join(base_dir, png_file),
@@ -2478,13 +2478,14 @@ def pretty_print_default(enable=True):
sage: pretty_print_default(True)
sage: 'foo'
- <html><script type="math/tex">\newcommand{\Bold}[1]{\mathbf{#1}}\verb|foo|</script></html>
+ \newcommand{\Bold}[1]{\mathbf{#1}}\verb|foo|
sage: pretty_print_default(False)
sage: 'foo'
'foo'
"""
- import sys
- sys.displayhook.formatter.set_display('typeset' if enable else 'simple')
+ from sage.repl.rich_output import get_display_manager
+ dm = get_display_manager()
+ dm.preferences.text = 'latex' if enable else None
common_varnames = ['alpha',
diff --git a/src/sage/misc/sage_input.py b/src/sage/misc/sage_input.py
index 157625d..130d4d2 100644
--- a/src/sage/misc/sage_input.py
+++ b/src/sage/misc/sage_input.py
@@ -1474,19 +1474,20 @@ class SageInputExpression(object):
"""
return SIE_getattr(self._sie_builder, self, attr)
- def _graphics_(self, **kwds):
+ def _rich_repr_(self, display_manager, **kwds):
"""
- Disable graphical output.
+ Disable rich output.
This is necessary because otherwise our :meth:`__getattr__`
would be called.
EXAMPLES::
+ sage: from sage.repl.rich_output import get_display_manager
sage: from sage.misc.sage_input import SageInputBuilder
sage: sib = SageInputBuilder()
sage: sie = sib.name('x')
- sage: sie._graphics_() is None
+ sage: sie._rich_repr_(get_display_manager()) is None
True
"""
return None
diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py
index 1e936a7..6424a73 100644
--- a/src/sage/misc/temporary_file.py
+++ b/src/sage/misc/temporary_file.py
@@ -149,6 +149,10 @@ def tmp_filename(name="tmp_", ext=""):
def graphics_filename(ext='.png'):
"""
+ Deprecated SageNB graphics filename
+
+ You should just use :meth:`tmp_filename`.
+
When run from the Sage notebook, return the next available canonical
filename for a plot/graphics file in the current working directory.
Otherwise, return a temporary file inside ``SAGE_TMP``.
@@ -191,11 +195,11 @@ def graphics_filename(ext='.png'):
sage: fn.endswith('.jpeg')
True
"""
+ import sage.plot.plot
+ from sage.misc.superseded import deprecation
if ext[0] not in '.-':
- from sage.misc.superseded import deprecation
deprecation(16640, "extension must now include the dot")
ext = '.' + ext
- import sage.plot.plot
if sage.plot.plot.EMBEDDED_MODE:
# Don't use this unsafe function except in the notebook, #15515
i = 0
@@ -204,6 +208,7 @@ def graphics_filename(ext='.png'):
filename = 'sage%d%s'%(i,ext)
return filename
else:
+ deprecation(17234,'use tmp_filename instead')
return tmp_filename(ext=ext)
diff --git a/src/sage/misc/viewer.py b/src/sage/misc/viewer.py
index b012cc8..1a1fec3 100644
--- a/src/sage/misc/viewer.py
+++ b/src/sage/misc/viewer.py
@@ -25,6 +25,9 @@ Functions and classes
---------------------
"""
+from sage.structure.sage_object import SageObject
+
+
VIEWERS = ['browser', 'dvi_viewer', 'pdf_viewer', 'png_viewer']
def default_viewer(viewer=None):
@@ -136,12 +139,10 @@ def default_viewer(viewer=None):
raise ValueError('Unknown type of viewer: {}.'.format(viewer))
-from sage.structure.sage_object import SageObject
-
# _viewer_prefs: a dictionary holding global preferences for viewers.
_viewer_prefs = {}
-class Viewer:
+class Viewer(SageObject):
"""
Set defaults for various viewing applications: a web browser, a
dvi viewer, a pdf viewer, and a png viewer.
diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py
index 75db15e..d502221 100644
--- a/src/sage/plot/animate.py
+++ b/src/sage/plot/animate.py
@@ -31,7 +31,7 @@ The sine function::
sage: sines = [plot(c*sin(x), (-2*pi,2*pi), color=Color(c,0,0), ymin=-1, ymax=1) for c in sxrange(0,1,.2)]
sage: a = animate(sines)
- sage: a
+ sage: a # optional -- ImageMagick
Animation with 5 frames
sage: a.show() # optional -- ImageMagick
@@ -43,12 +43,14 @@ Animate using FFmpeg_ instead of ImageMagick::
Animate as an APNG_::
sage: a.apng() # long time
+ doctest:...: DeprecationWarning: use tmp_filename instead
+ See http://trac.sagemath.org/17234 for details.
An animated :class:`sage.plot.graphics.GraphicsArray` of rotating ellipses::
sage: E = animate((graphics_array([[ellipse((0,0),a,b,angle=t,xmin=-3,xmax=3)+circle((0,0),3,color='blue') for a in range(1,3)] for b in range(2,4)]) for t in sxrange(0,pi/4,.15)))
- sage: E # animations produced from a generator do not have a known length
- Animation with unknown number of frames
+ sage: str(E) # animations produced from a generator do not have a known length
+ 'Animation with unknown number of frames'
sage: E.show() # optional -- ImageMagick
A simple animation of a circle shooting up to the right::
@@ -116,7 +118,7 @@ import struct
import zlib
from sage.structure.sage_object import SageObject
-from sage.misc.temporary_file import tmp_dir, graphics_filename
+from sage.misc.temporary_file import tmp_dir, tmp_filename, graphics_filename
import plot
import sage.misc.misc
import sage.misc.viewer
@@ -159,9 +161,9 @@ class Animation(SageObject):
sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.3)],
....: xmin=0, xmax=2*pi, figsize=[2,1])
- sage: a
+ sage: a # optional -- ImageMagick
Animation with 21 frames
- sage: a[:5]
+ sage: a[:5] # optional -- ImageMagick
Animation with 5 frames
sage: a.show() # optional -- ImageMagick
sage: a[:5].show() # optional -- ImageMagick
@@ -203,8 +205,9 @@ class Animation(SageObject):
Do not convert input iterator to a list, but ensure that
the frame count is known after rendering the frames::
- sage: a = animate((plot(x^p, (x,0,2)) for p in sxrange(1,2,.1))); a
- Animation with unknown number of frames
+ sage: a = animate((plot(x^p, (x,0,2)) for p in sxrange(1,2,.1)))
+ sage: str(a)
+ 'Animation with unknown number of frames'
sage: a.png() # long time
'.../'
sage: len(a) # long time
@@ -223,7 +226,7 @@ class Animation(SageObject):
sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.3)],
....: xmin=0, xmax=2*pi, figsize=[2,1]) # indirect doctest
- sage: a
+ sage: a # optional -- ImageMagick
Animation with 21 frames
"""
self._frames = v
@@ -273,12 +276,12 @@ class Animation(SageObject):
sage: a = animate([circle((i,-i), 1-1/(i+1), hue=i/10) for i in srange(0,2,0.2)],
....: xmin=0,ymin=-2,xmax=2,ymax=0,figsize=[2,2])
- sage: a
+ sage: a # optional -- ImageMagick
Animation with 10 frames
sage: frame2 = a[2] # indirect doctest
sage: frame2.show()
sage: a.show() # optional -- ImageMagick
- sage: a[3:7] # indirect doctest
+ sage: a[3:7] # optional -- ImageMagick # indirect doctest
Animation with 4 frames
sage: a[3:7].show() # optional -- ImageMagick
"""
@@ -295,7 +298,7 @@ class Animation(SageObject):
sage: a = animate([circle((i,-i), 1-1/(i+1), hue=i/10) for i in srange(0,2,0.2)],
....: xmin=0,ymin=-2,xmax=2,ymax=0,figsize=[2,2])
- sage: a
+ sage: a # optional -- ImageMagick
Animation with 10 frames
sage: a._repr_()
'Animation with 10 frames'
@@ -478,7 +481,7 @@ class Animation(SageObject):
sage: E = EllipticCurve('37a')
sage: v = [E.change_ring(GF(p)).plot(pointsize=30) for p in [97, 101, 103, 107]]
sage: a = animate(v, xmin=0, ymin=0, axes=False)
- sage: a
+ sage: a # optional -- ImageMagick
Animation with 4 frames
sage: a.show() # optional -- ImageMagick
@@ -618,19 +621,48 @@ the animate command can be saved in PNG image format.
See www.imagemagick.org and www.ffmpeg.org for more information."""
raise OSError(msg)
+ def _rich_repr_(self, display_manager, **kwds):
+ """
+ Rich Output Magic Method
+
+ See :mod:`sage.repl.rich_output` for details.
+
+ EXAMPLES::
+
+ sage: a = animate([plot(x^2 + n) for n in range(4)], ymin=0, ymax=4)
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: a._rich_repr_(dm) # optional -- ImageMagick
+ OutputImageGif container
+ """
+ OutputImageGif = display_manager.types.OutputImageGif
+ if OutputImageGif not in display_manager.supported_output():
+ return
+ return display_manager.graphics_from_save(
+ self.save, kwds, '.gif', OutputImageGif)
+
def show(self, delay=20, iterations=0):
r"""
- Show this animation.
+ Show this animation immediately.
- INPUT:
+ This method attempts to display the graphics immediately,
+ without waiting for the currently running code (if any) to
+ return to the command line. Be careful, calling it from within
+ a loop will potentially launch a large number of external
+ viewer programs.
+ INPUT:
- - ``delay`` - (default: 20) delay in hundredths of a
+ - ``delay`` -- (default: 20) delay in hundredths of a
second between frames
- - ``iterations`` - integer (default: 0); number of
+ - ``iterations`` -- integer (default: 0); number of
iterations of animation. If 0, loop forever.
+ OUTPUT:
+
+ This method does not return anything. Use :meth:`save` if you
+ want to save the figure as an image.
.. note::
@@ -667,11 +699,9 @@ See www.imagemagick.org and www.ffmpeg.org for more information."""
See www.imagemagick.org and www.ffmpeg.org for more information.
"""
- filename = graphics_filename(ext='.gif')
- self.gif(savefile=filename, delay=delay, iterations=iterations)
- if not (sage.doctest.DOCTEST_MODE or plot.EMBEDDED_MODE):
- os.system('%s %s 2>/dev/null 1>/dev/null &'%(
- sage.misc.viewer.browser(), filename))
+ from sage.repl.rich_output import get_display_manager
+ dm = get_display_manager()
+ dm.display_immediately(self, delay=delay, iterations=iterations)
def _have_ffmpeg(self):
"""
diff --git a/src/sage/plot/circle.py b/src/sage/plot/circle.py
index 9fb6e93..9a331e0 100644
--- a/src/sage/plot/circle.py
+++ b/src/sage/plot/circle.py
@@ -190,7 +190,7 @@ class Circle(GraphicPrimitive):
sage: c = C[0]
sage: d = c.plot3d(z=2)
sage: d.texture.opacity
- 0.300000000000000
+ 0.3
::
diff --git a/src/sage/plot/disk.py b/src/sage/plot/disk.py
index 7908d2c..3e6715e 100644
--- a/src/sage/plot/disk.py
+++ b/src/sage/plot/disk.py
@@ -202,7 +202,7 @@ class Disk(GraphicPrimitive):
sage: D = disk((2,3), 1, (pi/4,pi/3), hue=.8, alpha=.3, fill=True)
sage: d = D[0]
sage: d.plot3d(z=2).texture.opacity
- 0.300000000000000
+ 0.3
::
diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py
index e0fef74..0c8f61a 100644
--- a/src/sage/plot/graphics.py
+++ b/src/sage/plot/graphics.py
@@ -29,9 +29,8 @@ AUTHORS:
import os
import sage.misc.misc
from sage.misc.html import html
-from sage.misc.temporary_file import tmp_filename, graphics_filename
+from sage.misc.temporary_file import tmp_filename
from sage.structure.sage_object import SageObject
-from sage.structure.graphics_file import GraphicsFile
from sage.misc.decorators import suboptions
from colors import rgbcolor
@@ -132,7 +131,7 @@ class Graphics(SageObject):
sage: isinstance(g2, Graphics)
True
- .. automethod:: _graphics_
+ .. automethod:: _rich_repr_
"""
def __init__(self):
@@ -822,55 +821,44 @@ class Graphics(SageObject):
"""
return self.__str__()
- def _graphics_(self, mime_types=None, figsize=None, dpi=None):
+ def _rich_repr_(self, display_manager, **kwds):
"""
- Magic graphics method.
+ Rich Output Magic Method
- The presence of this method is used by the displayhook to
- decide that we want to see a graphical output by default.
-
- INPUT:
-
- - ``mime_types`` -- set of mime types (as strings).
-
- - ``figsize`` -- pair of integers (optional). The desired
- graphics size in pixels. Suggested, but need not be
- respected by the output.
-
- - ``dpi`` -- integer (optional). The desired resolution in
- dots per inch. Suggested, but need not be respected by the
- output.
-
- OUTPUT:
-
- Return an instance of
- :class:`sage.structure.graphics_file.GraphicsFile`
- encapsulating a suitable image file. If ``mime_types`` is
- specified, the resulting file format must match one of these.
-
- Alternatively, this method can return ``None`` to indicate
- that textual representation is preferable and/or no graphics
- with the desired mime type can be generated.
+ See :mod:`sage.repl.rich_output` for details.
EXAMPLES::
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
sage: g = Graphics()
- sage: g._graphics_()
- Graphics file image/png
-
- sage: [g, g]
- [Graphics object consisting of 0 graphics primitives,
- Graphics object consisting of 0 graphics primitives]
-
- sage: g._graphics_(mime_types={'foo/bar'}) is None
- True
- """
- from sage.structure.graphics_file import Mime, graphics_from_save
- preference = [Mime.PNG, Mime.SVG, Mime.JPG, Mime.PDF]
- return graphics_from_save(self.save, preference,
- allowed_mime_types=mime_types,
- figsize=figsize, dpi=dpi)
-
+ sage: g._rich_repr_(dm)
+ OutputImagePng container
+ """
+ types = display_manager.types
+ prefer_raster = (
+ ('.png', types.OutputImagePng),
+ ('.jpg', types.OutputImageJpg),
+ ('.gif', types.OutputImageGif),
+ )
+ prefer_vector = (
+ ('.svg', types.OutputImageSvg),
+ ('.pdf', types.OutputImagePdf),
+ )
+ graphics = display_manager.preferences.graphics
+ if graphics == 'disable':
+ return
+ elif graphics == 'raster' or graphics is None:
+ preferred = prefer_raster + prefer_vector
+ elif graphics == 'vector':
+ preferred = prefer_vector + prefer_raster
+ else:
+ raise ValueError('unknown graphics output preference')
+ for file_ext, output_container in preferred:
+ if output_container in display_manager.supported_output():
+ return display_manager.graphics_from_save(
+ self.save, kwds, file_ext, output_container)
+
def __str__(self):
r"""
Return string representation of this plot.
@@ -1266,8 +1254,7 @@ class Graphics(SageObject):
# NOTE: If you intend to use a new parameter in show(), you should update
# this dictionary to contain the default value for that parameter.
- SHOW_OPTIONS = dict(filename=None,
- # axes options
+ SHOW_OPTIONS = dict(# axes options
axes=None, axes_labels=None, axes_pad=None,
base=None, scale=None,
xmin=None, xmax=None, ymin=None, ymax=None,
@@ -1298,11 +1285,15 @@ class Graphics(SageObject):
shadow=False, title=None)
def show(self, filename=None, linkmode=False, **kwds):
r"""
- Show this graphics image with the default image viewer.
+ Show this graphics image immediately.
- OPTIONAL INPUT:
+ This method attempts to display the graphics immediately,
+ without waiting for the currently running code (if any) to
+ return to the command line. Be careful, calling it from within
+ a loop will potentially launch a large number of external
+ viewer programs.
- - ``filename`` - (default: None) string
+ OPTIONAL INPUT:
- ``dpi`` - (default: 100) dots per inch
@@ -1368,9 +1359,6 @@ class Graphics(SageObject):
rendering of the grid lines, the horizontal grid lines or the
vertical grid lines, respectively.
- - ``linkmode`` - (default: False) If True a string containing a link
- to the produced file is returned.
-
- ``transparent`` - (default: False) If True, make the background transparent.
- ``axes_pad`` - (default: 0.02 on ``"linear"`` scale, 1 on
@@ -1532,6 +1520,11 @@ class Graphics(SageObject):
in the figure. This requires LaTeX, dvipng and Ghostscript to
be installed.
+ OUTPUT:
+
+ This method does not return anything. Use :meth:`save` if you
+ want to save the figure as an image.
+
EXAMPLES::
sage: c = circle((1,1), 1, color='red')
@@ -1950,21 +1943,34 @@ class Graphics(SageObject):
...
ValueError: figsize should be a positive number or a list of two positive numbers, not [2, 3, 4]
sage: P.show(figsize=[sqrt(2),sqrt(3)])
- """
- if filename is None:
- filename = graphics_filename()
- self.save(filename, **kwds)
+ TESTS::
- if sage.plot.plot.EMBEDDED_MODE:
+ sage: P = plot(x^2,(x,0,1))
+ sage: P.show(linkmode=True)
+ doctest:...: DeprecationWarning: the filename and linkmode arguments are deprecated, use save() to save
+ See http://trac.sagemath.org/17234 for details.
+ doctest:...: DeprecationWarning: use tmp_filename instead
+ See http://trac.sagemath.org/17234 for details.
+ "<img src='cell:///...png'>"
+ """
+ if filename or linkmode:
+ from sage.misc.superseded import deprecation
+ deprecation(17234,'the filename and linkmode arguments are deprecated, '
+ 'use save() to save')
+ if filename is None:
+ from sage.misc.temporary_file import graphics_filename
+ filename = graphics_filename()
+ self.save(filename, **kwds)
if linkmode:
return "<img src='cell://%s'>" % filename
else:
html("<img src='cell://%s'>" % filename)
return
-
- gfx = GraphicsFile(filename, mime_type='image/png')
- gfx.launch_viewer()
+
+ from sage.repl.rich_output import get_display_manager
+ dm = get_display_manager()
+ dm.display_immediately(self, **kwds)
def xmin(self, xmin=None):
"""
@@ -2418,9 +2424,10 @@ class Graphics(SageObject):
``typeset`` must not be set to an arbitrary string::
sage: plot(x, typeset='garbage')
- doctest:...: FormatterWarning: Exception in text/plain formatter:
- typeset must be set to one of 'default', 'latex', or 'type1'; got 'garbage'.
- None
+ doctest:...: RichReprWarning: Exception in _rich_repr_ while
+ displaying object: typeset must be set to one of 'default',
+ 'latex', or 'type1'; got 'garbage'.
+ Graphics object consisting of 1 graphics primitive
We verify that numerical options are changed to float before saving (:trac:`14741`).
By default, Sage 5.10 changes float objects to the `RealLiteral` type.
@@ -2948,9 +2955,8 @@ class Graphics(SageObject):
INPUT:
- - ``filename`` -- a string (default: autogenerated), the filename and
- the image format given by the extension, which can be one of the
- following:
+ - ``filename`` -- string. The filename and the image format
+ given by the extension, which can be one of the following:
* ``.eps``,
@@ -3036,12 +3042,10 @@ class Graphics(SageObject):
fig_tight = options.pop('fig_tight')
if filename is None:
- try:
- filename = options.pop('filename')
- except KeyError:
- # Put this in except (not in pop()) such that the file is
- # only created when needed.
- filename = graphics_filename()
+ from sage.misc.superseded import deprecation
+ deprecation(17234,'the filename argument is now mandatory')
+ from sage.misc.temporary_file import graphics_filename
+ filename = graphics_filename()
ext = os.path.splitext(filename)[1].lower()
if ext not in ALLOWED_EXTENSIONS:
@@ -3113,7 +3117,7 @@ class GraphicsArray(SageObject):
GraphicsArray takes a (`m` x `n`) list of lists of
graphics objects and plots them all on one canvas.
- .. automethod:: _graphics_
+ .. automethod:: _rich_repr_
"""
def __init__(self, array):
"""
@@ -3186,26 +3190,43 @@ class GraphicsArray(SageObject):
"""
return self.__str__()
- def _graphics_(self, mime_types=None, figsize=None, dpi=None):
+ def _rich_repr_(self, display_manager, **kwds):
"""
- Magic graphics method.
+ Rich Output Magic Method
- See :meth:`sage.plot.graphics.Graphics._graphics_` for details.
+ See :mod:`sage.repl.rich_output` for details.
EXAMPLES::
- sage: from sage.plot.graphics import GraphicsArray
- sage: g = GraphicsArray([])
- sage: g._graphics_()
- Graphics file image/png
- sage: g._graphics_(mime_types={'foo/bar'}) is None
- True
- """
- from sage.structure.graphics_file import Mime, graphics_from_save
- preference = [Mime.PNG, Mime.SVG, Mime.JPG, Mime.PDF]
- return graphics_from_save(self.save, preference,
- allowed_mime_types=mime_types,
- figsize=figsize, dpi=dpi)
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: g = graphics_array([Graphics(), Graphics()], 1, 2)
+ sage: g._rich_repr_(dm)
+ OutputImagePng container
+ """
+ types = display_manager.types
+ prefer_raster = (
+ ('.png', types.OutputImagePng),
+ ('.jpg', types.OutputImageJpg),
+ ('.gif', types.OutputImageGif),
+ )
+ prefer_vector = (
+ ('.svg', types.OutputImageSvg),
+ ('.pdf', types.OutputImagePdf),
+ )
+ graphics = display_manager.preferences.graphics
+ if graphics == 'disable':
+ return
+ elif graphics == 'raster' or graphics is None:
+ preferred = prefer_raster + prefer_vector
+ elif graphics == 'vector':
+ preferred = prefer_vector + prefer_raster
+ else:
+ raise ValueError('unknown graphics output preference')
+ for file_ext, output_container in preferred:
+ if output_container in display_manager.supported_output():
+ return display_manager.graphics_from_save(
+ self.save, kwds, file_ext, output_container)
def __str__(self):
"""
@@ -3362,17 +3383,6 @@ class GraphicsArray(SageObject):
"""
return len(self._glist)
-# This does not work, and can never have worked!
-# To make this work, one would also change the
-# dimensions of the array, but it's not clear there
-# is a canonical way to do this.
-#
-# def append(self, g):
-# """
-# Appends a graphic to the array.
-# """
-# self._glist.append(g)
-
def append(self, g):
"""
Appends a graphic to the array. Currently
@@ -3387,20 +3397,32 @@ class GraphicsArray(SageObject):
...
NotImplementedError: Appending to a graphics array is not yet implemented
"""
+ # Not clear if there is a way to do this
raise NotImplementedError('Appending to a graphics array is not yet implemented')
-
def save(self, filename=None, dpi=DEFAULT_DPI, figsize=None, axes=None,
**kwds):
r"""
- Save the ``graphics_array`` to a png called ``filename``.
-
- We loop over all graphics objects in the array and add them to
- a subplot and then render that.
+ Save the graphics array.
INPUT:
- - ``filename`` - (default: None) string
+ - ``filename`` -- string. The filename and the image format
+ given by the extension, which can be one of the following:
+
+ * ``.eps``,
+
+ * ``.pdf``,
+
+ * ``.png``,
+
+ * ``.ps``,
+
+ * ``.sobj`` (for a Sage object you can load later),
+
+ * ``.svg``,
+
+ * empty extension will be treated as ``.sobj``.
- ``dpi`` - dots per inch
@@ -3418,12 +3440,15 @@ class GraphicsArray(SageObject):
TESTS::
- sage: graphics_array([]).save()
- sage: graphics_array([[]]).save()
+ sage: graphics_array([]).save(F)
+ sage: graphics_array([[]]).save(F)
"""
if figsize is not None:
self._set_figsize_(figsize)
if filename is None:
+ from sage.misc.superseded import deprecation
+ deprecation(17234,'the filename argument is now mandatory')
+ from sage.misc.temporary_file import graphics_filename
filename = graphics_filename()
#glist is a list of Graphics objects:
@@ -3444,7 +3469,7 @@ class GraphicsArray(SageObject):
g.matplotlib(filename, figure=figure, sub=subplot,
verify=do_verify, axes = axes, **kwds)
g.save(filename, dpi=dpi, figure=figure, sub=subplot,
- verify=do_verify, axes = axes, **kwds)
+ verify=do_verify, axes=axes, **kwds)
def save_image(self, filename=None, *args, **kwds):
r"""
@@ -3464,19 +3489,21 @@ class GraphicsArray(SageObject):
sage: plots = [[plot(m*cos(x + n*pi/4), (x,0, 2*pi)) for n in range(3)] for m in range(1,3)]
sage: G = graphics_array(plots)
- sage: G.save_image(tmp_filename()+'.png')
+ sage: G.save_image(tmp_filename(ext='.png'))
"""
self.save(filename, *args, **kwds)
-
- def show(self, filename=None, dpi=DEFAULT_DPI, figsize=None,
- axes=None, **kwds):
+ def show(self, **kwds):
r"""
- Show this graphics array using the default viewer.
+ Show this graphics array immediately.
- OPTIONAL INPUT:
+ This method attempts to display the graphics immediately,
+ without waiting for the currently running code (if any) to
+ return to the command line. Be careful, calling it from within
+ a loop will potentially launch a large number of external
+ viewer programs.
- - ``filename`` - (default: None) string
+ OPTIONAL INPUT:
- ``dpi`` - dots per inch
@@ -3491,6 +3518,11 @@ class GraphicsArray(SageObject):
- ``frame`` - (default: False) draw a frame around the
image
+ OUTPUT:
+
+ This method does not return anything. Use :meth:`save` if you
+ want to save the figure as an image.
+
EXAMPLES:
This draws a graphics array with four trig plots and no
@@ -3499,8 +3531,6 @@ class GraphicsArray(SageObject):
sage: G = graphics_array([[plot(sin), plot(cos)], [plot(tan), plot(sec)]])
sage: G.show(axes=False)
"""
- if filename is None:
- filename = graphics_filename()
- self.save(filename, dpi=dpi, figsize=figsize, axes=axes, **kwds)
- gfx = GraphicsFile(filename, mime_type='image/png')
- gfx.launch_viewer()
+ from sage.repl.rich_output import get_display_manager
+ dm = get_display_manager()
+ dm.display_immediately(self, **kwds)
diff --git a/src/sage/plot/line.py b/src/sage/plot/line.py
index 61694b1..e164e0c 100644
--- a/src/sage/plot/line.py
+++ b/src/sage/plot/line.py
@@ -99,7 +99,7 @@ class Line(GraphicPrimitive_xydata):
Line defined by 4 points
sage: m=l.plot3d(z=2)
sage: m.texture.opacity
- 0.500000000000000
+ 0.5
sage: m.thickness
10
sage: L = line([(1,1), (1,2), (2,2), (2,1)], linestyle=":")
diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx
index 335bb43..8372346 100644
--- a/src/sage/plot/plot3d/base.pyx
+++ b/src/sage/plot/plot3d/base.pyx
@@ -40,11 +40,12 @@ from cStringIO import StringIO
from sage.misc.misc import sage_makedirs
from sage.env import SAGE_LOCAL
+from sage.doctest import DOCTEST_MODE
from sage.modules.free_module_element import vector
from sage.rings.real_double import RDF
-from sage.misc.temporary_file import tmp_filename, graphics_filename
+from sage.misc.temporary_file import tmp_filename
from texture import Texture, is_Texture
from transform cimport Transformation, point_c, face_c
include "point_c.pxi"
@@ -63,8 +64,8 @@ cdef class Graphics3d(SageObject):
"""
This is the baseclass for all 3d graphics objects.
- .. automethod:: _graphics_
.. automethod:: __add__
+ .. automethod:: _rich_repr_
"""
def __cinit__(self):
"""
@@ -94,34 +95,32 @@ cdef class Graphics3d(SageObject):
"""
return str(self)
- def _graphics_(self, mime_types=None, figsize=None, dpi=None):
+ def _rich_repr_(self, display_manager, **kwds):
"""
- Magic graphics method.
+ Rich Output Magic Method
- The presence of this method is used by the displayhook to
- decide that we want to see a graphical output by default.
-
- INPUT/OUTPUT:
-
- See :meth:`sage.plot.graphics.Graphics._graphics_` for details.
+ See :mod:`sage.repl.rich_output` for details.
EXAMPLES::
- sage: S = sphere((0, 0, 0), 1)
- sage: S._graphics_(mime_types={'image/png'})
- Graphics file image/png
- sage: S # also productes graphics
- Graphics3d Object
- sage: [S, S]
- [Graphics3d Object, Graphics3d Object]
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: g = sphere()
+ sage: g._rich_repr_(dm)
+ OutputSceneJmol container
"""
- from sage.structure.graphics_file import (
- Mime, graphics_from_save, GraphicsFile)
### First, figure out the best graphics format
- can_view_jmol = (mime_types is None) or (Mime.JMOL in mime_types)
- viewer = self._extra_kwds.get('viewer', None)
+ types = display_manager.types
+ can_view_jmol = (types.OutputSceneJmol in display_manager.supported_output())
+ can_view_canvas3d = (types.OutputSceneCanvas3d in display_manager.supported_output())
+ can_view_wavefront = (types.OutputSceneWavefront in display_manager.supported_output())
+ opts = self._process_viewing_options(kwds)
+ viewer = opts.get('viewer', None)
+ if viewer == 'java3d':
+ from sage.misc.superseded import deprecation
+ deprecation(17234, 'use viewer="wavefront" instead of "java3d"')
# make sure viewer is one of the supported options
- if viewer not in [None, 'jmol', 'tachyon']:
+ if viewer not in [None, 'jmol', 'tachyon', 'canvas3d', 'wavefront']:
import warnings
warnings.warn('viewer={0} is not supported'.format(viewer))
viewer = None
@@ -129,24 +128,198 @@ cdef class Graphics3d(SageObject):
if viewer is None:
viewer = 'jmol'
# fall back to 2d image if necessary
- if viewer == 'jmol' and not can_view_jmol:
- viewer = 'tachyon'
+ if viewer == 'canvas3d' and not can_view_canvas3d: viewer = 'jmol'
+ if viewer == 'wavefront' and not can_view_wavefront: viewer = 'jmol'
+ if viewer == 'jmol' and not can_view_jmol: viewer = 'tachyon'
### Second, return the corresponding graphics file
if viewer == 'jmol':
- filename = tmp_filename(
- ext=os.path.extsep + Mime.extension(Mime.JMOL))
- self.save(filename)
- return GraphicsFile(filename, Mime.JMOL)
+ return self._rich_repr_jmol(**kwds)
elif viewer == 'tachyon':
- preference = [Mime.PNG, Mime.JPG]
- figsize = self._extra_kwds.get('figsize', figsize)
- dpi = self._extra_kwds.get('dpi', dpi)
- return graphics_from_save(self.save, preference,
- allowed_mime_types=mime_types,
- figsize=figsize, dpi=dpi)
+ preferred = (
+ types.OutputImagePng,
+ types.OutputImageJpg,
+ types.OutputImageGif,
+ )
+ for output_container in preferred:
+ if output_container in display_manager.supported_output():
+ return self._rich_repr_tachyon(output_container, **kwds)
+ elif viewer == 'canvas3d':
+ return self._rich_repr_canvas3d(**kwds)
+ elif viewer == 'wavefront':
+ return self._rich_repr_wavefront(**kwds)
else:
assert False # unreachable
+ def _rich_repr_tachyon(self, output_container, **kwds):
+ """
+ Rich Representation using Tachyon.
+
+ INPUT:
+
+ - ``output_container`` -- the rich output container to contain
+ the rendered image (can be png, gif, or jpg). Determines the
+ type of the output.
+
+ - ``**kwds`` -- Optional keyword arguments are passed to the
+ Tachyon raytracer.
+
+ OUTPUT:
+
+ Instance of
+ :class:`~sage.repl.rich_output.output_graphics.OutputImagePng`,
+ :class:`~sage.repl.rich_output.output_graphics.OutputImageGif`, or
+ :class:`~sage.repl.rich_output.output_graphics.OutputImageJpg`.
+
+ EXAMPLES::
+
+ sage: import sage.repl.rich_output.output_catalog as catalog
+ sage: sphere()._rich_repr_tachyon(catalog.OutputImagePng)
+ OutputImagePng container
+ sage: import sage.repl.rich_output.output_catalog as catalog
+ sage: sphere()._rich_repr_tachyon(catalog.OutputImageJpg) # optional -- libjpeg
+ OutputImageJpg container
+ """
+ filename = tmp_filename(ext='.png')
+ opts = self._process_viewing_options(kwds)
+ T = self._prepare_for_tachyon(
+ opts['frame'], opts['axes'], opts['frame_aspect_ratio'],
+ opts['aspect_ratio'], opts['zoom']
+ )
+ x, y = opts['figsize'][0]*100, opts['figsize'][1]*100
+ if DOCTEST_MODE:
+ x, y = 10, 10
+ tachyon_rt(T.tachyon(), filename, opts['verbosity'],
+ '-res %s %s' % (x, y))
+ from sage.repl.rich_output.buffer import OutputBuffer
+ import sage.repl.rich_output.output_catalog as catalog
+ import PIL.Image as Image
+ if output_container is catalog.OutputImagePng:
+ buf = OutputBuffer.from_file(filename)
+ elif output_container is catalog.OutputImageGif:
+ gif = tmp_filename(ext='.gif')
+ Image.open(filename).save(gif)
+ buf = OutputBuffer.from_file(gif)
+ elif output_container is catalog.OutputImageJpg:
+ jpg = tmp_filename(ext='.jpg')
+ Image.open(filename).save(jpg)
+ buf = OutputBuffer.from_file(jpg)
+ else:
+ raise ValueError('output_container not supported')
+ return output_container(buf)
+
+ def _rich_repr_jmol(self, **kwds):
+ """
+ Rich Representation as JMol scene
+
+ INPUT:
+
+ Optional keyword arguments are passed to JMol.
+
+ OUTPUT:
+
+ Instance of
+ :class:`sage.repl.rich_output.output_graphics3d.OutputSceneJmol`.
+
+ EXAMPLES::
+
+ sage: sphere()._rich_repr_jmol()
+ OutputSceneJmol container
+ """
+ from sage.misc.temporary_file import tmp_dir
+ root_dir = os.path.abspath(tmp_dir())
+ scene_zip = os.path.join(root_dir, 'scene.spt.zip')
+ preview_png = os.path.join(root_dir, 'preview.png')
+ opts = self._process_viewing_options(kwds)
+ zoom = opts['zoom']
+ T = self._prepare_for_jmol(
+ opts['frame'],
+ opts['axes'],
+ opts['frame_aspect_ratio'],
+ opts['aspect_ratio'],
+ zoom,
+ )
+ T.export_jmol(scene_zip, zoom=zoom*100, **kwds)
+ from sage.interfaces.jmoldata import JmolData
+ jdata = JmolData()
+ if not jdata.is_jvm_available():
+ # We can only use JMol to generate preview if a jvm is installed
+ from sage.repl.rich_output.output_graphics import OutputImagePng
+ tachyon = self._rich_repr_tachyon(OutputImagePng, **kwds)
+ tachyon.png.save_as(preview_png)
+ else:
+ # Java needs absolute paths
+ script = '''set defaultdirectory "%s"\nscript SCRIPT\n''' % scene_zip
+ jdata.export_image(targetfile=preview_png, datafile=script,
+ image_type="PNG",
+ figsize=opts['figsize'])
+ from sage.repl.rich_output.output_graphics3d import OutputSceneJmol
+ from sage.repl.rich_output.buffer import OutputBuffer
+ scene_zip = OutputBuffer.from_file(scene_zip)
+ preview_png = OutputBuffer.from_file(preview_png)
+ return OutputSceneJmol(scene_zip, preview_png)
+
+ def _rich_repr_wavefront(self, **kwds):
+ r"""
+ Rich Representation as Wavefront (obj + mtl) Scene
+
+ INPUT:
+
+ Optional keyword arguments are ignored.
+
+ OUTPUT:
+
+ Instance of
+ :class:`sage.repl.rich_output.output_graphics3d.OutputSceneWavefront`.
+
+ EXAMPLES::
+
+ sage: line = line3d([(0,0,0), (1,1,1)])
+ sage: out = line._rich_repr_wavefront()
+ sage: out
+ OutputSceneWavefront container
+ sage: out.obj.get()
+ 'mtllib ... 6 2 7 11\nf 7 8 12\nf 8 9 12\nf 9 10 12\nf 10 11 12\nf 11 7 12\n'
+ sage: out.mtl.get()
+ 'newmtl texture...\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1.0\nd 1.0\n'
+ """
+ from sage.repl.rich_output.output_graphics3d import OutputSceneWavefront
+ from sage.repl.rich_output.buffer import OutputBuffer
+ obj = OutputBuffer('mtllib scene.mtl\n' + self.obj())
+ return OutputSceneWavefront(obj, self.mtl_str())
+
+ def _rich_repr_canvas3d(self, **kwds):
+ r"""
+ Rich Representation as Canvas3D Scene
+
+ INPUT:
+
+ Optional keyword arguments.
+
+ OUTPUT:
+
+ Instance of
+ :class:`sage.repl.rich_output.output_graphics3d.OutputSceneCanvas3d`.
+
+ EXAMPLES::
+
+ sage: out = sphere()._rich_repr_canvas3d()
+ sage: out
+ OutputSceneCanvas3d container
+ sage: out.canvas3d.get()
+ "[{vertices:[{x:0,y:0,z:-1},...,color:'#6666ff'}]"
+ """
+ opts = self._process_viewing_options(kwds)
+ aspect_ratio = opts['aspect_ratio'] # this necessarily has a value now
+ frame_aspect_ratio = opts['frame_aspect_ratio']
+ zoom = opts['zoom']
+ frame = opts['frame']
+ axes = opts['axes']
+ T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom)
+ data = flatten_list(T.json_repr(T.default_render_params()))
+ canvas3d = '[' + ','.join(data) + ']'
+ from sage.repl.rich_output.output_catalog import OutputSceneCanvas3d
+ return OutputSceneCanvas3d(canvas3d)
+
def __str__(self):
"""
EXAMPLES::
@@ -505,7 +678,7 @@ cdef class Graphics3d(SageObject):
<Scene>
<Viewpoint position='0 0 6'/>
<Transform translation='1 2 3'>
- <Shape><Sphere radius='5.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
+ <Shape><Sphere radius='5.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
</Transform>
</Scene>
</X3D>
@@ -522,9 +695,9 @@ cdef class Graphics3d(SageObject):
<IndexedFaceSet coordIndex='...'>
<Coordinate point='...'/>
</IndexedFaceSet>
- <Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
+ <Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
<Transform translation='0 0 0'>
- <Shape><Sphere radius='0.5'/><Appearance><Material diffuseColor='1.0 0.0 0.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
+ <Shape><Sphere radius='0.5'/><Appearance><Material diffuseColor='1.0 0.0 0.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
</Transform>
</Scene>
</X3D>
@@ -561,7 +734,7 @@ cdef class Graphics3d(SageObject):
COLOR 1.0 1.0 1.0
TEXFUNC 0
Texdef texture...
- Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1
+ Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1.0
Color 1.0 1.0 0.0
TexFunc 0
Sphere center 1.0 -2.0 3.0 Rad 5.0 texture...
@@ -573,14 +746,13 @@ cdef class Graphics3d(SageObject):
begin_scene
...
Texdef texture...
- Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1
+ Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 1.0
Color 1.0 1.0 0.0
TexFunc 0
TRI V0 ...
Sphere center 1.0 -2.0 3.0 Rad 0.5 texture...
end_scene
"""
-
render_params = self.default_render_params()
# switch from LH to RH coords to be consistent with java rendition
render_params.push_transform(Transformation(scale=[1,-1,1]))
@@ -888,15 +1060,15 @@ end_scene""" % (render_params.antialiasing,
Kd 1.0 1e-05 1e-05
Ks 0.0 0.0 0.0
illum 1
- Ns 1
- d 1
+ Ns 1.0
+ d 1.0
newmtl ...
Ka 0.5 0.5 5e-06
Kd 1.0 1.0 1e-05
Ks 0.0 0.0 0.0
illum 1
- Ns 1
- d 0.500000000000000
+ Ns 1.0
+ d 0.5
"""
return "\n\n".join(sorted([t.mtl_str() for t in self.texture_set()])) + "\n"
@@ -1087,8 +1259,16 @@ end_scene""" % (render_params.antialiasing,
return opts
- def show(self, *, filename=None, **kwds):
+ def show(self, **kwds):
"""
+ Display graphics immediately
+
+ This method attempts to display the graphics immediately,
+ without waiting for the currently running code (if any) to
+ return to the command line. Be careful, calling it from within
+ a loop will potentially launch a large number of external
+ viewer programs.
+
INPUT:
- ``viewer`` -- string (default: 'jmol'), how to view
@@ -1103,9 +1283,6 @@ end_scene""" % (render_params.antialiasing,
* 'canvas3d': Web-based 3D viewer powered by JavaScript and
<canvas> (notebook only)
- - ``filename`` -- string (default: a temp file); filename
- without extension to save the image file(s) to
-
- ``verbosity`` -- display information about rendering
the figure
@@ -1132,6 +1309,11 @@ end_scene""" % (render_params.antialiasing,
- ``**kwds`` -- other options, which make sense for particular
rendering engines
+ OUTPUT:
+
+ This method does not return anything. Use :meth:`save` if you
+ want to save the figure as an image.
+
CHANGING DEFAULTS: Defaults can be uniformly changed by importing a
dictionary and changing it. For example, here we change the default
so images display without a frame instead of with one::
@@ -1177,159 +1359,79 @@ end_scene""" % (render_params.antialiasing,
the plot rendered inline using HTML canvas::
sage: p.show(viewer='canvas3d')
+ """
+ from sage.repl.rich_output import get_display_manager
+ dm = get_display_manager()
+ dm.display_immediately(self, **kwds)
- From the command line, you probably want to specify an explicit
- filename::
+ def _save_image_png(self, filename, **kwds):
+ r"""
+ Save a PNG rendering.
+
+ This private method is only for use by :meth:`save_image`.
- sage: basedir = tmp_dir()
- sage: basename = os.path.join(basedir, "xyz")
- sage: p.show(filename=basename)
+ EXAMPLES::
- In this doctest, we see many files because we test various
- viewers::
+ sage: s = sphere()
+ sage: filename = tmp_filename(ext='.png')
+ sage: s._save_image_png(filename)
+ sage: open(filename).read().startswith('\x89PNG')
+ True
- sage: sorted(os.listdir(basedir))
- ['xyz-size500.spt', 'xyz-size500.spt.zip', 'xyz.mtl', 'xyz.obj', 'xyz.png']
+ sage: s._save_image_png('/path/to/foo.bar')
+ Traceback (most recent call last):
+ ...
+ AssertionError
"""
+ assert filename.endswith('.png')
opts = self._process_viewing_options(kwds)
-
viewer = opts['viewer']
- verbosity = opts['verbosity']
- figsize = opts['figsize']
- aspect_ratio = opts['aspect_ratio'] # this necessarily has a value now
- frame_aspect_ratio = opts['frame_aspect_ratio']
- zoom = opts['zoom']
- frame = opts['frame']
- axes = opts['axes']
-
- basename = filename # Filename without extension
- filename = None # Filename with extension of the plot result
-
- def makename(ext):
- if basename is not None:
- return basename + ext
- else:
- return graphics_filename(ext=ext)
-
- from sage.plot.plot import EMBEDDED_MODE
- from sage.doctest import DOCTEST_MODE
-
- # Tachyon resolution options
- if DOCTEST_MODE:
- tachyon_opts = '-res 10 10'
+ if viewer == 'tachyon':
+ from sage.repl.rich_output.output_catalog import OutputImagePng
+ render = self._rich_repr_tachyon(OutputImagePng, **kwds)
+ render.png.save_as(filename)
+ elif viewer == 'jmol':
+ scene = self._rich_repr_jmol(**kwds)
+ scene.preview_png.save_as(filename)
else:
- tachyon_opts = '-res %s %s'%(figsize[0]*100, figsize[1]*100)
-
- if DOCTEST_MODE or viewer=='tachyon' or (viewer=='java3d' and EMBEDDED_MODE):
- T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom)
- filename = makename(".png")
- tachyon_rt(T.tachyon(), filename, verbosity, True, tachyon_opts)
- import sage.misc.viewer
- viewer_app = sage.misc.viewer.png_viewer()
-
- if DOCTEST_MODE or viewer=='java3d':
- mtl = makename(".mtl")
- filename = makename(".obj")
- with open(filename, "w") as f:
- f.write("mtllib %s\n" % mtl)
- f.write(self.obj())
- with open(mtl, "w") as f:
- f.write(self.mtl_str())
- viewer_app = os.path.join(SAGE_LOCAL, "bin", "sage3d")
-
- if DOCTEST_MODE or viewer=='jmol':
- # Temporary hack: encode the desired applet size in the end of the filename:
- # (This will be removed once we have dynamic resizing of applets in the browser.)
- fg = figsize[0]
- sizedname = lambda ext: makename("-size{}{}".format(fg*100, ext))
-
- if EMBEDDED_MODE:
- # jmol doesn't seem to correctly parse the ?params part of a URL
- archive_name = sizedname(
- "-{}.jmol.zip".format(randint(0, 1 << 30)))
- filename = sizedname(".jmol")
- else:
- archive_name = sizedname(".spt.zip".format(fg))
- filename = sizedname(".spt")
- with open(filename, 'w') as f:
- f.write('set defaultdirectory "{0}"\n'.format(archive_name))
- f.write('script SCRIPT\n')
-
- T = self._prepare_for_jmol(frame, axes, frame_aspect_ratio, aspect_ratio, zoom)
- T.export_jmol(archive_name, force_reload=EMBEDDED_MODE, zoom=zoom*100, **kwds)
- viewer_app = os.path.join(SAGE_LOCAL, "bin", "jmol")
-
- # If the server has a Java installation we can make better static images with Jmol
- # Test for Java then make image with Jmol or Tachyon if no JavaVM
- if EMBEDDED_MODE:
- # We need a script for the Notebook.
- # When the notebook sees this file, it will know to
- # display the static file and the "Make Interactive"
- # button.
- import sagenb
- path = "cells/%s/%s" %(sagenb.notebook.interact.SAGE_CELL_ID, archive_name)
- with open(filename, 'w') as f:
- f.write('set defaultdirectory "%s"\n' % path)
- f.write('script SCRIPT\n')
-
- # Filename for the static image
- png_path = '.jmol_images'
- sage_makedirs(png_path)
- png_name = os.path.join(png_path, filename + ".png")
-
- from sage.interfaces.jmoldata import JmolData
- jdata = JmolData()
- if jdata.is_jvm_available():
- # Java needs absolute paths
- archive_name = os.path.abspath(archive_name)
- png_name = os.path.abspath(png_name)
- script = '''set defaultdirectory "%s"\nscript SCRIPT\n''' % archive_name
- jdata.export_image(targetfile=png_name, datafile=script, image_type="PNG", figsize=fg)
- else:
- # Render the image with tachyon
- T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom)
- tachyon_rt(T.tachyon(), png_name, verbosity, True, tachyon_opts)
-
- if viewer == 'canvas3d':
- if not EMBEDDED_MODE and not DOCTEST_MODE:
- raise RuntimeError("canvas3d viewer is only available from the Notebook")
- T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom)
- data = flatten_list(T.json_repr(T.default_render_params()))
- filename = makename('.canvas3d')
- with open(filename, 'w') as f:
- f.write('[%s]' % ','.join(data))
-
- if filename is None:
- raise ValueError("Unknown 3d plot type: %s" % viewer)
-
- if not DOCTEST_MODE and not EMBEDDED_MODE:
- if verbosity:
- pipes = "2>&1"
- else:
- pipes = "2>/dev/null 1>/dev/null &"
- os.system('%s "%s" %s' % (viewer_app, filename, pipes))
+ raise ValueError('cannot use viewer={0} to render image'.format(viewer))
- def save_image(self, filename=None, *args, **kwds):
+ def save_image(self, filename, **kwds):
r"""
- Save an image representation of self. The image type is
- determined by the extension of the filename. For example,
- this could be ``.png``, ``.jpg``, ``.gif``, ``.pdf``,
- ``.svg``. Currently this is implemented by calling the
- :meth:`save` method of self, passing along all arguments and
- keywords.
+ Save a 2-D image rendering.
- .. Note::
+ The image type is determined by the extension of the filename.
+ For example, this could be ``.png``, ``.jpg``, ``.gif``,
+ ``.pdf``, ``.svg``.
+
+ INPUT:
- Not all image types are necessarily implemented for all
- graphics types. See :meth:`save` for more details.
+ - ``filename`` -- string. The file name under which to save
+ the image.
+
+ Any further keyword arguments are passed to the renderer.
EXAMPLES::
- sage: f = tmp_filename() + '.png'
sage: G = sphere()
- sage: G.save_image(f)
+ sage: png = tmp_filename(ext='.png')
+ sage: G.save_image(png)
+ sage: assert open(png).read().startswith('\x89PNG')
+
+ sage: gif = tmp_filename(ext='.gif')
+ sage: G.save_image(gif)
+ sage: assert open(gif).read().startswith('GIF')
"""
- self.save(filename, *args, **kwds)
+ ext = os.path.splitext(filename)[1].lower()
+ if ext not in ['.bmp', '.png', '.gif', '.ppm', '.tiff', '.tif', '.jpg', '.jpeg']:
+ raise ValueError('unknown image file type: {0}'.format(ext))
+ if ext == '.png':
+ self._save_image_png(filename, **kwds)
+ else:
+ png = tmp_filename(ext='.png')
+ self._save_image_png(png, **kwds)
+ import PIL.Image as Image
+ Image.open(png).save(filename)
def save(self, filename, **kwds):
"""
@@ -1341,7 +1443,7 @@ end_scene""" % (render_params.antialiasing,
INPUT:
- - ``filename`` -- Specify where to save the image or object.
+ - ``filename`` -- string. Where to save the image or object.
- ``**kwds`` -- When specifying an image file to be rendered by Tachyon,
any of the viewing options accepted by show() are valid as keyword
@@ -1361,7 +1463,7 @@ end_scene""" % (render_params.antialiasing,
sage: G.save(f, zoom=2, figsize=[5, 10])
- But some extra parameters don't make since (like ``viewer``, since
+ But some extra parameters don't make sense (like ``viewer``, since
rendering is done using Tachyon only). They will be ignored::
sage: G.save(f, viewer='jmol') # Looks the same
@@ -1375,39 +1477,14 @@ end_scene""" % (render_params.antialiasing,
if ext == '' or ext == '.sobj':
SageObject.save(self, filename)
elif ext in ['.bmp', '.png', '.gif', '.ppm', '.tiff', '.tif', '.jpg', '.jpeg']:
- opts = self._process_viewing_options(kwds)
- T = self._prepare_for_tachyon(
- opts['frame'], opts['axes'], opts['frame_aspect_ratio'],
- opts['aspect_ratio'], opts['zoom']
- )
-
- if ext == '.png':
- # No conversion is necessary
- out_filename = filename
- else:
- # Save to a temporary file, and then convert using PIL
- out_filename = tmp_filename(ext=ext)
- tachyon_rt(T.tachyon(), out_filename, opts['verbosity'], True,
- '-res %s %s' % (opts['figsize'][0]*100, opts['figsize'][1]*100))
- if ext != '.png':
- import PIL.Image as Image
- Image.open(out_filename).save(filename)
+ self.save_image(filename)
elif filename.endswith('.spt.zip'):
- # Jmol zip archive
- opts = self._process_viewing_options(kwds)
- zoom = opts['zoom']
- T = self._prepare_for_jmol(
- opts['frame'],
- opts['axes'],
- opts['frame_aspect_ratio'],
- opts['aspect_ratio'],
- zoom)
- T.export_jmol(filename, zoom=zoom*100, **kwds)
+ scene = self._rich_repr_jmol(**kwds)
+ scene.jmol.save(filename)
else:
raise ValueError('filetype not supported by save()')
-
# if you add any default parameters you must update some code below
SHOW_DEFAULTS = {'viewer':'jmol',
'verbosity':0,
@@ -1563,10 +1640,10 @@ class Graphics3dGroup(Graphics3d):
sage: G = sphere() + sphere((1,2,3))
sage: print G.x3d_str()
<Transform translation='0 0 0'>
- <Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
+ <Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
</Transform>
<Transform translation='1 2 3'>
- <Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
+ <Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
</Transform>
"""
return "\n".join([g.x3d_str() for g in self.all])
@@ -1730,7 +1807,7 @@ class TransformGroup(Graphics3dGroup):
EXAMPLES::
sage: sphere((1,2,3)).x3d_str()
- "<Transform translation='1 2 3'>\n<Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>\n\n</Transform>"
+ "<Transform translation='1 2 3'>\n<Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>\n\n</Transform>"
"""
s = "<Transform"
if self._rot is not None:
@@ -1965,7 +2042,7 @@ cdef class PrimitiveObject(Graphics3d):
EXAMPLES::
sage: sphere().flatten().x3d_str()
- "<Transform>\n<Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>\n\n</Transform>"
+ "<Transform>\n<Shape><Sphere radius='1.0'/><Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>\n\n</Transform>"
"""
return "<Shape>" + self.x3d_geometry() + self.texture.x3d_str() + "</Shape>\n"
diff --git a/src/sage/plot/plot3d/shapes.pyx b/src/sage/plot/plot3d/shapes.pyx
index 152d8fe..6ec5cb0 100644
--- a/src/sage/plot/plot3d/shapes.pyx
+++ b/src/sage/plot/plot3d/shapes.pyx
@@ -182,7 +182,7 @@ def ColorCube(size, colors, opacity=1, **kwds):
sage: c = ColorCube([1,2,3], ['red', 'blue', 'green', 'black', 'white', 'orange'], opacity=0.5)
sage: c.show()
sage: list(c.texture_set())[0].opacity
- 0.500000000000000
+ 0.5
If you omit the last 3 colors then the first three are repeated (with
repeated colors on opposing faces)::
diff --git a/src/sage/plot/plot3d/tachyon.py b/src/sage/plot/plot3d/tachyon.py
index 079c748..a8ef1aa 100644
--- a/src/sage/plot/plot3d/tachyon.py
+++ b/src/sage/plot/plot3d/tachyon.py
@@ -144,7 +144,7 @@ from sage.structure.sage_object import SageObject
from sage.misc.misc import SAGE_TMP
from sage.misc.misc import get_verbose
from sage.misc.viewer import png_viewer
-from sage.misc.temporary_file import tmp_filename, graphics_filename
+from sage.misc.temporary_file import tmp_filename
#from sage.ext import fast_tachyon_routines
@@ -418,14 +418,16 @@ class Tachyon(SageObject):
....: return q
sage: a = animate([tw_cubic(t) for t in srange(-1,1,.3)])
- sage: a
+ sage: a # optional -- ImageMagick
Animation with 7 frames
sage: a.show() # optional -- ImageMagick
"""
self.save(filename, *args, **kwds)
- def save(self, filename='sage.png', verbose=None, block=True, extra_opts=''):
+ def save(self, filename='sage.png', verbose=None, extra_opts=''):
r"""
+ Save rendering of the tachyon scene
+
INPUT:
- ``filename`` - (default: 'sage.png') output
@@ -452,9 +454,6 @@ class Tachyon(SageObject):
- ``2`` - very verbose output
- - ``block`` - bool (default: True); if False, run the
- rendering command in the background.
-
- ``extra_opts`` - passed directly to tachyon command
line. Use tachyon_rt.usage() to see some of the possibilities.
@@ -466,18 +465,52 @@ class Tachyon(SageObject):
sage: q.sphere((0,0,0),1,'s')
sage: tempname = tmp_filename()
sage: q.save(tempname)
- sage: os.system('rm ' + tempname)
- 0
"""
if verbose is None:
verbose = get_verbose()
+ tachyon_rt(self.str(), filename, verbose, extra_opts)
+
+ def _rich_repr_(self, display_manager, **kwds):
+ """
+ Rich Output Magic Method
+
+ See :mod:`sage.repl.rich_output` for details.
- tachyon_rt(self.str(), filename, verbose, block, extra_opts)
+ EXAMPLES::
- def show(self, verbose=None, extra_opts=''):
+ sage: q = Tachyon()
+ sage: q.light((1,1,11), 1,(1,1,1))
+ sage: q.texture('s')
+ sage: q.sphere((0,0,0),1,'s')
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: q._rich_repr_(dm)
+ OutputImagePng container
+ """
+ OutputImagePng = display_manager.types.OutputImagePng
+ if OutputImagePng not in display_manager.supported_output():
+ return
+ filename = tmp_filename(ext='.png')
+ self.save(filename, **kwds)
+ from sage.repl.rich_output.buffer import OutputBuffer
+ buf = OutputBuffer.from_file(filename)
+ return OutputImagePng(buf)
+
+ def show(self, **kwds):
r"""
Create a PNG file of the scene.
+ This method attempts to display the graphics immediately,
+ without waiting for the currently running code (if any) to
+ return to the command line. Be careful, calling it from within
+ a loop will potentially launch a large number of external
+ viewer programs.
+
+ OUTPUT:
+
+ This method does not return anything. Use :meth:`save` if you
+ want to save the figure as an image.
+
EXAMPLES:
This example demonstrates how the global Sage verbosity setting
@@ -535,13 +568,9 @@ class Tachyon(SageObject):
Scene contains 1 non-gridded objects
...
"""
- filename = graphics_filename()
- self.save(filename, verbose=verbose, extra_opts=extra_opts)
-
- from sage.doctest import DOCTEST_MODE
- from sage.plot.plot import EMBEDDED_MODE
- if not DOCTEST_MODE and not EMBEDDED_MODE:
- os.system('%s %s 2>/dev/null 1>/dev/null &'%(png_viewer(), filename))
+ from sage.repl.rich_output import get_display_manager
+ dm = get_display_manager()
+ dm.display_immediately(self, **kwds)
def _res(self):
r"""
@@ -611,8 +640,8 @@ class Tachyon(SageObject):
sage: t.sphere((0,0.5,0), 0.2, 't2')
sage: t.sphere((0.5,0,0), 0.2, 't3')
sage: t.sphere((0,0,0.5), 0.2, 't4')
- sage: t.str().find('PLASTIC')
- 595
+ sage: 'PLASTIC' in t.str()
+ True
"""
return r"""
begin_scene
@@ -738,7 +767,7 @@ class Tachyon(SageObject):
sage: t.texture('s')
sage: q = t.texture_recolor('s',[(0,0,1)])
sage: t._objects[1]._color
- (0, 0, 1)
+ (0.0, 0.0, 1.0)
"""
base_tex = None
names = []
@@ -782,7 +811,7 @@ class Tachyon(SageObject):
sage: t = Tachyon()
sage: t.ring([0,0,0], [0,0,1], 1.0, 2.0, 's')
sage: t._objects[0]._center
- [0, 0, 0]
+ (0.0, 0.0, 0.0)
"""
self._objects.append(Ring(center, normal, inner, outer, texture))
@@ -991,32 +1020,25 @@ class Tachyon(SageObject):
Scene contains 514 objects.
...
"""
+ self._objects.append(
+ ParametricPlot(
+ f, t_0, t_f, tex, r=r, cylinders=cylinders,
+ min_depth=min_depth, max_depth=max_depth,
+ e_rel=.01,e_abs=.01
+ )
+ )
+
- self._objects.append(ParametricPlot(f, t_0, t_f, tex, r=r, cylinders=cylinders,min_depth=min_depth,max_depth=max_depth,e_rel=.01,e_abs=.01))
-
-#Doesn't seem to be used:
-# def collect(self, objects):
-# """
-# Add a set of objects to the scene from a collection.
-#
-# EXAMPLES::
-#
-# sage: t = Tachyon()
-# sage: t.texture('s')
-# sage: for i in range(10): t.sphere((0,0,i),i,'s')
-# """
-# self._objects.extend(objects)
-
-class Light:
+class Light(object):
r"""
Represents lighting objects.
EXAMPLES::
sage: from sage.plot.plot3d.tachyon import Light
- sage: q = Light((1,1,1),1,(1,1,1))
+ sage: q = Light((1,1,1), 1, (1,1,1))
sage: q._center
- (1, 1, 1)
+ (1.0, 1.0, 1.0)
"""
def __init__(self, center, radius, color):
r"""
@@ -1025,13 +1047,15 @@ class Light:
EXAMPLES::
sage: from sage.plot.plot3d.tachyon import Light
- sage: q = Light((1,1,1),1,(1,1,1))
+ sage: q = Light((1,1,1), 1, (1,1,1))
sage: q._color
- (1, 1, 1)
+ (1.0, 1.0, 1.0)
"""
- self._center = center
- self._radius = radius
- self._color = color
+ x, y, z = center
+ self._center = (float(x), float(y), float(z))
+ self._radius = float(radius)
+ r, g, b = color
+ self._color = (float(r), float(g), float(b))
def str(self):
r"""
@@ -1040,19 +1064,21 @@ class Light:
EXAMPLES::
sage: from sage.plot.plot3d.tachyon import Light
- sage: q = Light((1,1,1),1,(1,1,1))
+ sage: q = Light((1,1,1), 1, (1,1,1))
sage: q._radius
- 1
+ 1.0
"""
return r"""
light center %s
rad %s
color %s
- """%(tostr(self._center), float(self._radius),
+ """%(tostr(self._center), self._radius,
tostr(self._color))
-class Texfunc:
- def __init__(self, ttype=0,center=(0,0,0), rotate=(0,0,0), scale=(1,1,1), imagefile=''):
+
+class Texfunc(object):
+
+ def __init__(self, ttype=0, center=(0,0,0), rotate=(0,0,0), scale=(1,1,1), imagefile=''):
r"""
Creates a texture function.
@@ -1064,9 +1090,12 @@ class Texfunc:
0
"""
self._ttype = ttype
- self._center = center
- self._rotate = rotate
- self._scale = scale
+ x, y, z = center
+ self._center = (float(x), float(y), float(z))
+ x, y, z = rotate
+ self._rotate = (float(x), float(y), float(z))
+ x, y, z = scale
+ self._scale = (float(x), float(y), float(z))
self._imagefile = imagefile
def str(self):
@@ -1083,25 +1112,33 @@ class Texfunc:
if self._ttype == 0:
return "0"
elif self._ttype < 7 and self._ttype > 0:
- return r"""%d center %s rotate %s scale %s"""%(self._ttype,
- tostr(self._center),
- tostr(self._rotate),
- tostr(self._scale))
+ return r"""%d center %s rotate %s scale %s"""%(
+ self._ttype,
+ tostr(self._center),
+ tostr(self._rotate),
+ tostr(self._scale))
elif self._ttype < 9:
- return r"""%d %s center %s rotate %s scale %s"""%(self._ttype, self._imagefile,tostr(self._center),tostr(self._rotate),tostr(self._scale))
+ return r"""%d %s center %s rotate %s scale %s"""%(
+ self._ttype,
+ self._imagefile,
+ tostr(self._center),
+ tostr(self._rotate),
+ tostr(self._scale))
elif self._ttype == 9:
return r"""%d %s center %s rotate %s scale %s
uaxis 1.0 0.0 0.0
- vaxis 0.0 1.0 0.0"""%(self._ttype,
- self._imagefile,
- tostr(self._center),
- tostr(self._rotate),
- tostr(self._scale))
+ vaxis 0.0 1.0 0.0"""%(
+ self._ttype,
+ self._imagefile,
+ tostr(self._center),
+ tostr(self._rotate),
+ tostr(self._scale))
else:
raise ValueError
-class Texture:
+class Texture(object):
+
def __init__(self, name, ambient=0.2, diffuse=0.8,
specular=0.0, opacity=1.0,
color=(1.0,0.0, 0.5), texfunc=0,
@@ -1116,17 +1153,18 @@ class Texture:
sage: t.str().split()[2:6]
['ambient', '0.2', 'diffuse', '0.8']
"""
- self._name = name
- self._ambient = ambient
- self._diffuse = diffuse
- self._specular = specular
- self._opacity = opacity
- self._color = color
+ self._name = str(name)
+ self._ambient = float(ambient)
+ self._diffuse = float(diffuse)
+ self._specular = float(specular)
+ self._opacity = float(opacity)
+ r, g, b = color
+ self._color = (float(r), float(g), float(b))
self._texfunc = texfunc
- self._phong = phong
- self._phongsize = phongsize
- self._phongtype = phongtype
- self._imagefile = imagefile
+ self._phong = float(phong)
+ self._phongsize = float(phongsize)
+ self._phongtype = str(phongtype)
+ self._imagefile = str(imagefile)
def recolor(self, name, color):
r"""
@@ -1155,7 +1193,6 @@ class Texture:
sage: t = Texture('w')
sage: t.str().split()[2:6]
['ambient', '0.2', 'diffuse', '0.8']
-
"""
return r"""
texdef %s ambient %s diffuse %s specular %s opacity %s
@@ -1172,7 +1209,8 @@ class Texture:
tostr(self._color),
self._texfunc)
-class Sphere:
+
+class Sphere(object):
r"""
A class for creating spheres in tachyon.
"""
@@ -1184,13 +1222,14 @@ class Sphere:
sage: t = Tachyon()
sage: from sage.plot.plot3d.tachyon import Sphere
- sage: t.texture('r', color=(.8,0,0), ambient = .1)
- sage: s = Sphere((1,1,1),1,'r')
+ sage: t.texture('r', color=(.8,0,0), ambient=.1)
+ sage: s = Sphere((1,1,1), 1, 'r')
sage: s._radius
- 1
+ 1.0
"""
- self._center = center
- self._radius = radius
+ x, y, z = center
+ self._center = (float(x), float(y), float(z))
+ self._radius = float(radius)
self._texture = texture
def str(self):
@@ -1202,15 +1241,16 @@ class Sphere:
sage: t = Tachyon()
sage: from sage.plot.plot3d.tachyon import Sphere
sage: t.texture('r', color=(.8,0,0), ambient = .1)
- sage: s = Sphere((1,1,1),1,'r')
+ sage: s = Sphere((1,1,1), 1, 'r')
sage: s.str()
'\n sphere center 1.0 1.0 1.0 rad 1.0 r\n '
"""
return r"""
sphere center %s rad %s %s
- """%(tostr(self._center), float(self._radius), self._texture)
+ """%(tostr(self._center), self._radius, self._texture)
-class Ring:
+
+class Ring(object):
r"""
An annulus of zero thickness.
"""
@@ -1224,12 +1264,14 @@ class Ring:
sage: from sage.plot.plot3d.tachyon import Ring
sage: r = Ring((1,1,1), (1,1,0), 1.0, 2.0, 's')
sage: r._center
- (1, 1, 1)
+ (1.0, 1.0, 1.0)
"""
- self._center = center
- self._normal = normal
- self._inner = inner
- self._outer = outer
+ x, y, z = center
+ self._center = (float(x), float(y), float(z))
+ x, y, z = normal
+ self._normal = (float(x), float(y), float(z))
+ self._inner = float(inner)
+ self._outer = float(outer)
self._texture = texture
def str(self):
@@ -1245,9 +1287,11 @@ class Ring:
"""
return r"""
ring center %s normal %s inner %s outer %s %s
- """%(tostr(self._center), tostr(self._normal), float(self._inner), float(self._outer), self._texture)
+ """%(tostr(self._center), tostr(self._normal),
+ self._inner, self._outer, self._texture)
-class FractalLandscape:
+
+class FractalLandscape(object):
r"""
Axis-aligned fractal landscape.
Does not seem very useful at the moment, but perhaps will be improved in the future.
@@ -1261,11 +1305,14 @@ class FractalLandscape:
sage: from sage.plot.plot3d.tachyon import FractalLandscape
sage: fl = FractalLandscape([20,20],[30,30],[1,2,3],'s')
sage: fl._center
- [1, 2, 3]
+ (1.0, 2.0, 3.0)
"""
- self._res = res
- self._scale = scale
- self._center = center
+ x, y = res
+ self._res = (int(x), int(y))
+ x, y = scale
+ self._scale = (int(x), int(y))
+ x, y, z = center
+ self._center = (float(x), float(y), float(z))
self._texture = texture
def str(self):
@@ -1283,7 +1330,8 @@ class FractalLandscape:
scape res %s scale %s center %s %s
"""%(tostr(self._res, 2, int), tostr(self._scale, 2, int), tostr(self._center), self._texture)
-class Cylinder:
+
+class Cylinder(object):
r"""
An infinite cylinder.
"""
@@ -1299,9 +1347,11 @@ class Cylinder:
sage: c.str()
'\n cylinder center 0.0 0.0 0.0 axis 1.0 1.0 1.0 rad 0.1 s\n '
"""
- self._center = center
- self._axis = axis
- self._radius = radius
+ x, y, z = center
+ self._center = (float(x), float(y), float(z))
+ x, y, z = axis
+ self._axis = (float(x), float(y), float(z))
+ self._radius = float(radius)
self._texture = texture
def str(self):
@@ -1318,9 +1368,10 @@ class Cylinder:
"""
return r"""
cylinder center %s axis %s rad %s %s
- """%(tostr(self._center), tostr(self._axis), float(self._radius), self._texture)
+ """%(tostr(self._center), tostr(self._axis), self._radius, self._texture)
+
-class Plane:
+class Plane(object):
r"""
An infinite plane.
"""
@@ -1331,12 +1382,14 @@ class Plane:
EXAMPLES::
sage: from sage.plot.plot3d.tachyon import Plane
- sage: p = Plane((1,2,3),(1,2,4),'s')
+ sage: p = Plane((1,2,3), (1,2,4), 's')
sage: p.str()
'\n plane center 1.0 2.0 3.0 normal 1.0 2.0 4.0 s\n '
"""
- self._center = center
- self._normal = normal
+ x, y, z = center
+ self._center = (float(x), float(y), float(z))
+ x, y, z = normal
+ self._normal = (float(x), float(y), float(z))
self._texture = texture
def str(self):
@@ -1354,7 +1407,8 @@ class Plane:
plane center %s normal %s %s
"""%(tostr(self._center), tostr(self._normal), self._texture)
-class FCylinder:
+
+class FCylinder(object):
r"""
A finite cylinder.
"""
@@ -1369,9 +1423,11 @@ class FCylinder:
sage: fc.str()
'\n fcylinder base 0.0 0.0 0.0 apex 1.0 1.0 1.0 rad 0.1 s\n '
"""
- self._center = base
- self._axis = apex
- self._radius = radius
+ x, y, z = base
+ self._center = (float(x), float(y), float(z))
+ x, y, z = apex
+ self._axis = (float(x), float(y), float(z))
+ self._radius = float(radius)
self._texture = texture
def str(self):
@@ -1387,9 +1443,10 @@ class FCylinder:
"""
return r"""
fcylinder base %s apex %s rad %s %s
- """%(tostr(self._center), tostr(self._axis), float(self._radius), self._texture)
+ """%(tostr(self._center), tostr(self._axis), self._radius, self._texture)
+
-class Axis_aligned_box():
+class Axis_aligned_box(object):
r"""
Box with axis-aligned edges with the given min and max coordinates.
"""
@@ -1404,8 +1461,10 @@ class Axis_aligned_box():
sage: aab.str()
'\n box min 0.0 0.0 0.0 max 1.0 1.0 1.0 s\n '
"""
- self._min_p = min_p
- self._max_p = max_p
+ x, y, z = min_p
+ self._min_p = (float(x), float(y), float(z))
+ x, y, z = max_p
+ self._max_p = (float(x), float(y), float(z))
self._texture = texture
def str(self):
@@ -1423,6 +1482,7 @@ class Axis_aligned_box():
box min %s max %s %s
"""%(tostr(self._min_p), tostr(self._max_p), self._texture)
+
class TachyonTriangle(Triangle):
r"""
Basic triangle class.
@@ -1441,7 +1501,7 @@ class TachyonTriangle(Triangle):
return r"""
TRI V0 %s V1 %s V2 %s
%s
- """%(tostr(self._a), tostr(self._b),tostr(self._c), self._color)
+ """%(tostr(self._a), tostr(self._b), tostr(self._c), self._color)
class TachyonSmoothTriangle(SmoothTriangle):
@@ -1535,254 +1595,13 @@ class TachyonTriangleFactory(TriangleFactory):
sage: t = Tachyon()
sage: t.texture('s')
sage: ttf = TachyonTriangleFactory(t, 's')
- sage: ttf.get_colors([1])
+ sage: ttf.get_colors([(1,1,1)])
['SAGETEX1_0']
"""
return self._tachyon.texture_recolor(self._texture, list)
-# following classes TachyonPlot and PlotBlock seems broken and not used anywhere, so commented out. Please write to the sage-devel google-group if you are the author of these classes to comment.
-
-#class TachyonPlot:
- #Recursively plots a function of two variables by building squares of 4 triangles, checking at
- # every stage whether or not each square should be split into four more squares. This way,
- # more planar areas get fewer triangles, and areas with higher curvature get more triangles
-
-# def str(self):
-# return "".join([o.str() for o in self._objects])
-
-# def __init__(self, tachyon, f, (min_x, max_x), (min_y, max_y), tex, g = None,
-# min_depth=4, max_depth=8, e_rel = .01, e_abs = .01, num_colors = None):
-# self._tachyon = tachyon
-# self._f = f
-# self._g = g
-# self._tex = tex
-# self._min_depth = min_depth
-# self._max_depth = max_depth
-# self._e_rel = e_rel
-# self._e_abs = e_abs
-# self._objects = []
-# self._eps = min(max_x - min_x, max_y - min_y)/(2**max_depth)
-# if self._eps == 0:
-# raise ValueError, 'Plot rectangle is really a line. Make sure min_x != #max_x and min_y != max_y.'
-# self._num_colors = num_colors
-# if g is None:
-# def fcn(x,y):
-# return [self._f(x,y)]
-# else:
-# def fcn(x,y):
-# return [self._f(x,y), self._g(x,y)]
-
-# self._fcn = fcn
-
-
-# # generate the necessary data to kick-start the recursion
-# mid_x = (min_x + max_x)/2
-# mid_y = (min_y + max_y)/2
-# sw_z = fcn(min_x,min_y)
-# nw_z = fcn(min_x,max_y)
-# se_z = fcn(max_x,min_y)
-# ne_z = fcn(max_x,max_y)
-# mid_z = fcn(mid_x,mid_y)
-
-# self._min = min(sw_z[0], nw_z[0], se_z[0], ne_z[0], mid_z[0])
-# self._max = max(sw_z[0], nw_z[0], se_z[0], ne_z[0], mid_z[0])
-
-# # jump in and start building blocks
-# outer = self.plot_block(min_x, mid_x, max_x, min_y, mid_y, max_y, sw_z, nw_z, se_z, ne_z, mid_z, 0)
-#
-# # build the boundary triangles
-# self.triangulate(outer.left, outer.left_c)
-# self.triangulate(outer.top, outer.top_c)
-# self.triangulate(outer.right, outer.right_c)
-# self.triangulate(outer.bottom, outer.bottom_c)
-
-# zrange = self._max - self._min
-# if num_colors is not None and zrange != 0:
-# colors = tachyon.texture_recolor(tex, [hue(float(i/num_colors)) for i in range(num_colors)])
-
-# for o in self._objects:
-# avg_z = (o._vertex_1[2] + o._vertex_2[2] + o._vertex_3[2])/3
-# o._texture = colors[int(num_colors * (avg_z - self._min) / zrange)]
-
-# def plot_block(self, min_x, mid_x, max_x, min_y, mid_y, max_y, sw_z, nw_z, se_z, ne_z, mid_z, depth):
-
-# if depth < self._max_depth:
-# # recursion is still an option -- step in one last level if we're within tolerance
-# # and just keep going if we're not.
-# # assumption: it's cheap to build triangles, so we might as well use all the data
-# # we calculate
-
-# # big square boundary midpoints
-# mid_w_z = self._fcn(min_x, mid_y)
-# mid_n_z = self._fcn(mid_x, max_y)
-# mid_e_z = self._fcn(max_x, mid_y)
-# mid_s_z = self._fcn(mid_x, min_y)
-
-# # midpoints locations of sub_squares
-# qtr1_x = (min_x + mid_x)/2
-# qtr1_y = (min_y + mid_y)/2
-# qtr3_x = (mid_x + max_x)/2
-# qtr3_y = (mid_y + max_y)/2
-
-# # function evaluated at these midpoints
-# mid_sw_z = self._fcn(qtr1_x,qtr1_y)
-# mid_nw_z = self._fcn(qtr1_x,qtr3_y)
-# mid_se_z = self._fcn(qtr3_x,qtr1_y)
-# mid_ne_z = self._fcn(qtr3_x,qtr3_y)
-
-# # linearization estimates of midpoints
-# est_sw_z = (mid_z[0] + sw_z[0])/2
-# est_nw_z = (mid_z[0] + nw_z[0])/2
-# est_se_z = (mid_z[0] + se_z[0])/2
-# est_ne_z = (mid_z[0] + ne_z[0])/2
-
-# self.extrema([mid_w_z[0], mid_n_z[0], mid_e_z[0], mid_s_z[0], mid_sw_z[0], mid_se_z[0], mid_nw_z[0], mid_sw_z[0]])
-
-# tol_check = [(est_sw_z, mid_sw_z[0]), (est_nw_z, mid_nw_z[0]), (est_se_z, mid_se_z[0]), (est_ne_z, mid_ne_z[0])]
-
-# if depth < self._min_depth or not self.tol_list(tol_check):
-# next_depth = depth + 1
-# else:
-# #lie about the depth to halt recursion
-# next_depth = self._max_depth
-
-# # recurse into the sub-squares
-# sw = self.plot_block(min_x, qtr1_x, mid_x, min_y, qtr1_y, mid_y, sw_z, mid_w_z, mid_s_z, mid_z, mid_sw_z, next_depth)
-# nw = self.plot_block(min_x, qtr1_x, mid_x, mid_y, qtr3_y, max_y, mid_w_z, nw_z, mid_z, mid_n_z, mid_nw_z, next_depth)
-# se = self.plot_block(mid_x, qtr3_x, max_x, min_y, qtr1_y, mid_y, mid_s_z, mid_z, se_z, mid_e_z, mid_se_z, next_depth)
-# ne = self.plot_block(mid_x, qtr3_x, max_x, mid_y, qtr3_y, max_y, mid_z, mid_n_z, mid_e_z, ne_z, mid_ne_z, next_depth)
-
-# # join the sub-squares
-# self.interface(1, sw.right, sw.right_c, se.left, se.left_c)
-# self.interface(1, nw.right, nw.right_c, ne.left, ne.left_c)
-# self.interface(0, sw.top, sw.top_c, nw.bottom, nw.bottom_c)
-# self.interface(0, se.top, se.top_c, ne.bottom, ne.bottom_c)
-
-# #get the boundary information about the subsquares
-# left = sw.left + nw.left[1:]
-# left_c = sw.left_c + nw.left_c
-# right = se.right + ne.right[1:]
-# right_c = se.right_c + ne.right_c
-# top = nw.top + ne.top[1:]
-# top_c = nw.top_c + ne.top_c
-# bottom = sw.bottom + se.bottom[1:]
-# bottom_c = sw.bottom_c + se.bottom_c
-
-# else:
-# # just build the square we're in
-# if self._g is None:
-# sw = [(min_x,min_y,sw_z[0])]
-# nw = [(min_x,max_y,nw_z[0])]
-# se = [(max_x,min_y,se_z[0])]
-# ne = [(max_x,max_y,ne_z[0])]
-# c = [[(mid_x,mid_y,mid_z[0])]]
-# else:
-# sw = [(min_x,min_y,sw_z[0]),sw_z[1]]
-# nw = [(min_x,max_y,nw_z[0]),nw_z[1]]
-# se = [(max_x,min_y,se_z[0]),se_z[1]]
-# ne = [(max_x,max_y,ne_z[0]),ne_z[1]]
-# c = [[(mid_x,mid_y,mid_z[0]),mid_z[1]]]
-
-
-# left = [sw,nw]
-# left_c = c
-# top = [nw,ne]
-# top_c = c
-# right = [se,ne]
-# right_c = c
-# bottom = [sw,se]
-# bottom_c = c
-
-# return PlotBlock(left, left_c, top, top_c, right, right_c, bottom, bottom_c)
-
-# def tol(self, (est, val)):
-# # Check relative, then absolute tolerance. If both fail, return False
-# # This is a zero-safe error checker
-
-# if abs(est - val) < self._e_rel*abs(val):
-# return True
-# if abs(est - val) < self._e_abs:
-# return True
-# return False
-
-# def tol_list(self, l):
-# # Pass in a list of pairs of numbers, (est, val) to be passed to self.tol
-# # returns False if any pair does not fall within tolerance level
-
-# for p in l:
-# if not self.tol(p):
-# return False
-# return True
-
-# def interface(self, n, p, p_c, q, q_c):
-# # Takes a pair of lists of points, and compares the (n)th coordinate, and
-# # "zips" the lists together into one. The "centers", supplied in p_c and
-# # q_c are matched up such that the lists describe triangles whose sides
-# # are "perfectly" aligned. This algorithm assumes that p and q start and
-# # end at the same point, and are sorted smallest to largest.
-
-# m = [p[0]] # a sorted union of p and q
-# mpc = [p_c[0]] # centers from p_c corresponding to m
-# mqc = [q_c[0]] # centers from q_c corresponding to m
-
-# i = 1
-# j = 1
-
-# while i < len(p_c) or j < len(q_c):
-# if abs(p[i][0][n] - q[j][0][n]) < self._eps:
-# m.append(p[i])
-# mpc.append(p_c[i])
-# mqc.append(q_c[j])
-# i += 1
-# j += 1
-# elif p[i][0][n] < q[j][0][n]:
-# m.append(p[i])
-# mpc.append(p_c[i])
-# mqc.append(mqc[-1])
-# i += 1
-# else:
-# m.append(q[j])
-# mpc.append(mpc[-1])
-# mqc.append(q_c[j])
-# j += 1
-
-# m.append(p[-1])
-
-# self.triangulate(m, mpc)
-# self.triangulate(m, mqc)
-
-
-# def triangulate(self, p, c):
-# # Pass in a list of edge points (p) and center points (c).
-# # Triangles will be rendered between consecutive edge points and the
-# # center point with the same index number as the earlier edge point.
-
-# if self._g is None:
-# for i in range(0,len(p)-1):
-# self._objects.append(Triangle(p[i][0], p[i+1][0], c[i][0], self._tex))
-# else:
-# for i in range(0,len(p)-1):
-# self._objects.append(SmoothTriangle(p[i][0], p[i+1][0], c[i][0],p[i][1], p[i+1][1], c[i][1], self._tex))
-
-
-# def extrema(self, list):
-# if self._num_colors is not None:
-# self._min = min(list+[self._min])
-# self._max = max(list+[self._max])
-
-
-#class PlotBlock:
-# def __init__(self, left, left_c, top, top_c, right, right_c, bottom, bottom_c):
-# self.left = left
-# self.left_c = left_c
-# self.top = top
-# self.top_c = top_c
-# self.right = right
-# self.right_c = right_c
-# self.bottom = bottom
-# self.bottom_c = bottom_c
-
-class ParametricPlot:
+
+class ParametricPlot(object):
r"""
Parametric plotting routines.
"""
@@ -1886,7 +1705,8 @@ class ParametricPlot:
return False
-def tostr(s, length = 3, out_type = float):
+
+def tostr(s, length=3, out_type=float):
r"""
Converts vector information to a space-separated string.
diff --git a/src/sage/plot/plot3d/texture.py b/src/sage/plot/plot3d/texture.py
index 8a1f73d..66952d2 100644
--- a/src/sage/plot/plot3d/texture.py
+++ b/src/sage/plot/plot3d/texture.py
@@ -26,7 +26,7 @@ And the Texture objects keep track of all their data::
sage: T = tetrahedron(color='red', opacity=0.5)
sage: t = T.get_texture()
sage: t.opacity
- 0.500000000000000
+ 0.5
sage: T # should be translucent
Graphics3d Object
@@ -254,13 +254,18 @@ class Texture_class(SageObject):
sage: t
Texture(texture..., 6666ff)
sage: t.opacity
- 0.600000000000000
+ 0.6
sage: t.jmol_str('obj')
'color obj translucent 0.4 [102,102,255]'
sage: t.mtl_str()
- 'newmtl texture...\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1\nd 0.600000000000000'
+ 'newmtl texture...\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1.0\nd 0.6'
sage: t.x3d_str()
- "<Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance>"
+ "<Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance>"
+
+ TESTS::
+
+ sage: Texture(opacity=1/3).opacity
+ 0.3333333333333333
"""
def __init__(self, id, color=(.4, .4, 1), opacity=1, ambient=0.5, diffuse=1, specular=0, shininess=1, name=None, **kwds):
r"""
@@ -288,8 +293,8 @@ class Texture_class(SageObject):
color = (float(color[0]), float(color[1]), float(color[2]))
self.color = color
- self.opacity = opacity
- self.shininess = shininess
+ self.opacity = float(opacity)
+ self.shininess = float(shininess)
if not isinstance(ambient, tuple):
ambient = parse_color(ambient, color)
@@ -341,7 +346,7 @@ class Texture_class(SageObject):
sage: from sage.plot.plot3d.texture import Texture
sage: t = Texture(opacity=0.6)
sage: t.tachyon_str()
- 'Texdef texture...\n Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 0.600000000000000\n Color 0.4 0.4 1.0\n TexFunc 0'
+ 'Texdef texture...\n Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 0.6\n Color 0.4 0.4 1.0\n TexFunc 0'
"""
total_color = float(sum(self.ambient) + sum(self.diffuse) + sum(self.specular))
if total_color == 0:
@@ -364,7 +369,7 @@ class Texture_class(SageObject):
sage: from sage.plot.plot3d.texture import Texture
sage: t = Texture(opacity=0.6)
sage: t.x3d_str()
- "<Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance>"
+ "<Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance>"
"""
return "<Appearance><Material diffuseColor='%s %s %s' shininess='%s' specularColor='%s %s %s'/></Appearance>" % \
(self.color[0], self.color[1], self.color[2], self.shininess, self.specular[0], self.specular[0], self.specular[0])
@@ -378,7 +383,7 @@ class Texture_class(SageObject):
sage: from sage.plot.plot3d.texture import Texture
sage: t = Texture(opacity=0.6)
sage: t.mtl_str()
- 'newmtl texture...\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1\nd 0.600000000000000'
+ 'newmtl texture...\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1.0\nd 0.6'
"""
return "\n".join(["newmtl %s" % self.id,
"Ka %s %s %s" % self.ambient,
diff --git a/src/sage/plot/polygon.py b/src/sage/plot/polygon.py
index 8adfbc4..728d611 100644
--- a/src/sage/plot/polygon.py
+++ b/src/sage/plot/polygon.py
@@ -167,7 +167,7 @@ class Polygon(GraphicPrimitive_xydata):
Polygon defined by 4 points
sage: q=p.plot3d()
sage: q.texture.opacity
- 0.500000000000000
+ 0.5
"""
if options is None:
options = dict(self.options())
diff --git a/src/sage/plot/primitive.py b/src/sage/plot/primitive.py
index ac1c7e7..80b740e 100644
--- a/src/sage/plot/primitive.py
+++ b/src/sage/plot/primitive.py
@@ -95,7 +95,7 @@ class GraphicPrimitive(SageObject):
sage: q.thickness
4
sage: q.texture.opacity
- 0.500000000000000
+ 0.5
"""
if options is None:
options = self.options()
diff --git a/src/sage/repl/attach.py b/src/sage/repl/attach.py
index 1ee74ed..9ebc3ef 100644
--- a/src/sage/repl/attach.py
+++ b/src/sage/repl/attach.py
@@ -566,6 +566,7 @@ def reload_attached_files_if_modified():
sage: shell.run_cell('detach({0})'.format(repr(tmp)))
sage: shell.run_cell('attached_files()')
[]
+ sage: shell.quit()
"""
for filename, mtime in modified_file_iterator():
basename = os.path.basename(filename)
diff --git a/src/sage/repl/display/formatter.py b/src/sage/repl/display/formatter.py
index 90d01c1..4e3e5ec 100644
--- a/src/sage/repl/display/formatter.py
+++ b/src/sage/repl/display/formatter.py
@@ -44,7 +44,8 @@ generally, all sage expression as an ASCII art object::
1 2 2 ]
3 3 ]
, 4 , 4 ]
- sage: shell.run_cell('%display simple')
+ sage: shell.run_cell('%display default')
+ sage: shell.quit()
This other facility uses a simple
:class:`~sage.misc.ascii_art.AsciiArt` object (see and
@@ -60,50 +61,48 @@ This other facility uses a simple
#*****************************************************************************
-from IPython.core.formatters import PlainTextFormatter, warn_format_error
+from IPython.core.formatters import DisplayFormatter, PlainTextFormatter
from IPython.utils.py3compat import str_to_unicode, unicode_to_str
-from sage.repl.display.pretty_print import (
- SagePrettyPrinter, AsciiArtPrettyPrinter, TypesetPrettyPrinter
-)
+from sage.structure.sage_object import SageObject
+from sage.repl.display.pretty_print import SagePrettyPrinter
-class SagePlainTextFormatter(PlainTextFormatter):
+
+class SageDisplayFormatter(DisplayFormatter):
def __init__(self, *args, **kwds):
- r"""
- Improved plain text formatter.
+ """
+ This is where the Sage rich objects are translated to IPython
+
+ INPUT/OUTPUT:
- In particular, it has the following two features:
-
- - correctly print lists of matrices or other objects (see
- :meth:`sage.structure.parent.Parent._repr_option`),
+ See the IPython documentation.
- - print ASCII art objects (like expressions) (see
- :meth:`sage.structure.sage_object.SageObject._ascii_art_`).
-
- EXAMPLES::
-
- sage: from sage.repl.interpreter import get_test_shell
- sage: shell = get_test_shell()
- sage: shell.display_formatter.formatters['text/plain']
- <sage.repl.display.formatter.SagePlainTextFormatter object at 0x...>
- sage: shell.run_cell('a = identity_matrix(ZZ, 2); [a,a]')
- [
- [1 0] [1 0]
- [0 1], [0 1]
- ]
+ EXAMPLES:
+
+ This is part of how Sage works with the IPython output
+ system. It cannot be used in doctests::
+
+ sage: from sage.repl.display.formatter import SageDisplayFormatter
+ sage: fmt = SageDisplayFormatter()
+ Traceback (most recent call last):
+ ...
+ RuntimeError: check failed: current backend is invalid
"""
- super(SagePlainTextFormatter, self).__init__(*args, **kwds)
- self.set_display('simple')
+ super(SageDisplayFormatter, self).__init__(*args, **kwds)
+ from sage.repl.rich_output.display_manager import get_display_manager
+ self.dm = get_display_manager()
+ from sage.repl.rich_output.backend_ipython import BackendIPython
+ self.dm.check_backend_class(BackendIPython)
- def set_display(self, mode):
- r"""
- Select the text formatting method.
+ def format(self, obj, include=None, exclude=None):
+ """
+ Use the Sage rich output instead of IPython
- INPUT:
+ INPUT/OUTPUT:
- - ``mode`` -- string. One of ``simple``, ``ascii_art``, or ``typeset``.
+ See the IPython documentation.
EXAMPLES::
@@ -123,74 +122,44 @@ class SagePlainTextFormatter(PlainTextFormatter):
sage: shell.run_cell('sum(i*x^i, i, 0, 10)')
10 9 8 7 6 5 4 3 2
10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x
- sage: shell.run_cell('%display simple')
- """
- if mode not in ['simple', 'ascii_art', 'typeset']:
- raise ValueError('invalid mode set')
- self._mode = mode
- self._pretty_printer_class = dict(
- simple=SagePrettyPrinter,
- ascii_art=AsciiArtPrettyPrinter,
- typeset=TypesetPrettyPrinter,
- )[mode]
-
- _mode = 'simple'
-
- @property
- def simple(self):
- """
- Whether the mode is the "simple" (default) display.
-
- OUTPUT:
-
- Boolean.
-
- EXAMPLES::
-
- sage: sys.displayhook.formatter.simple
- True
- sage: sys.displayhook.formatter.ascii_art
- False
+ sage: shell.run_cell('%display default')
+ sage: shell.quit()
"""
- return self._mode == 'simple'
+ return self.dm.displayhook(obj)
+
- @property
- def ascii_art(self):
- """
- Whether the mode is the ascii art display.
- OUTPUT:
-
- Boolean.
+class SagePlainTextFormatter(PlainTextFormatter):
- EXAMPLES::
+ def __init__(self, *args, **kwds):
+ r"""
+ Improved plain text IPython formatter.
- sage: sys.displayhook.formatter.simple
- True
- sage: sys.displayhook.formatter.ascii_art
- False
- """
- return self._mode == 'ascii_art'
+ In particular, it correctly print lists of matrices or other
+ objects (see
+ :meth:`sage.structure.parent.Parent._repr_option`).
+
+ .. warning::
- @property
- def typeset(self):
- """
- Whether the mode is the notebook "Typeset" display.
+ This IPython formatter is NOT used. You could use it to
+ enable Sage formatting in IPython, but Sage uses its own
+ rich output system that is more flexible and supports
+ different backends.
- OUTPUT:
+ INPUT/OUTPUT:
- Boolean.
+ See the IPython documentation.
EXAMPLES::
-
- sage: sys.displayhook.formatter.simple
- True
- sage: sys.displayhook.formatter.typeset
- False
+
+ sage: from sage.repl.interpreter import get_test_shell
+ sage: shell = get_test_shell()
+ sage: shell.display_formatter.formatters['text/plain']
+ <sage.repl.display.formatter.SagePlainTextFormatter object at 0x...>
+ sage: shell.quit()
"""
- return self._mode == 'typeset'
+ super(SagePlainTextFormatter, self).__init__(*args, **kwds)
- @warn_format_error
def __call__(self, obj):
"""
Compute the pretty representation of the object.
@@ -207,179 +176,27 @@ class SagePlainTextFormatter(PlainTextFormatter):
EXAMPLES::
- sage: from sage.repl.interpreter import get_test_shell
- sage: shell = get_test_shell()
- sage: fmt = shell.display_formatter.formatters['text/plain']
+ sage: from sage.repl.display.formatter import SagePlainTextFormatter
+ sage: fmt = SagePlainTextFormatter()
sage: fmt
<sage.repl.display.formatter.SagePlainTextFormatter object at 0x...>
- sage: shell.displayhook.compute_format_data(2)
- ({u'text/plain': '2'}, {})
+ sage: fmt(2)
+ ---- calling ipython formatter ----
+ '2'
sage: a = identity_matrix(ZZ, 2)
- sage: shell.displayhook.compute_format_data([a,a])
- ({u'text/plain': '[\n[1 0] [1 0]\n[0 1], [0 1]\n]'}, {})
- sage: fmt.set_display('ascii_art')
- sage: shell.displayhook.compute_format_data([a,a])
- ({u'text/plain': '[ [1 0] [1 0] ]\n[ [0 1], [0 1] ]'}, {})
- sage: i = var('i')
- sage: shell.displayhook.compute_format_data(sum(i*x^i, i, 0, 10))
- ({u'text/plain': ' 10 9 8 7 6 5 4 3
- 2 \n10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x'},
- {})
- sage: fmt.set_display('simple')
- """
+ sage: fmt([a, a])
+ ---- calling ipython formatter ----
+ '[\n[1 0] [1 0]\n[0 1], [0 1]\n]'
+ """
+ from sage.doctest import DOCTEST_MODE
+ if DOCTEST_MODE:
+ # Just to show that this is never executed in any other doctests in the Sage library
+ print('---- calling ipython formatter ----')
import StringIO
stream = StringIO.StringIO()
- printer = self._pretty_printer_class(
+ printer = SagePrettyPrinter(
stream, self.max_width, unicode_to_str(self.newline))
printer.pretty(obj)
printer.flush()
return stream.getvalue()
-
-class SageDoctestTextFormatter(SagePlainTextFormatter):
-
- @warn_format_error
- def __call__(self, obj):
- """
- Display ``obj``.
-
- For doctests, we both
-
- * Print the textual representation. This makes it clear in the
- documentation that the command returns graphics.
-
- * Run ``show()`` on graphics objects, to test that it
- correctly generates graphics. Note that, in
- ``DOCTEST_MODE``, the ``show()`` method will save graphics
- to temporary files but not launch a viewer.
-
- INPUT:
-
- - ``obj`` -- anything.
-
- OUTPUT:
-
- String. The plain text representation.
-
- EXAMPLES::
-
- sage: class FooGraphics(SageObject):
- ....: def _graphics_(self, **kwds):
- ....: print('showing graphics')
- ....: from sage.structure.graphics_file import GraphicsFile
- ....: return GraphicsFile('/nonexistent.png', 'image/png')
- ....: def _repr_(self):
- ....: return 'Textual representation'
- sage: from sage.repl.display.formatter import SageDoctestTextFormatter
- sage: fmt = SageDoctestTextFormatter()
- sage: fmt(FooGraphics())
- showing graphics
- 'Textual representation'
- """
- from sage.structure.sage_object import SageObject
- if isinstance(obj, SageObject) and hasattr(obj, '_graphics_'):
- obj._graphics_() # ignore whether there actually is graphics
- return super(SageDoctestTextFormatter, self).__call__(obj)
-
-
-class SageNBTextFormatter(SagePlainTextFormatter):
-
- @warn_format_error
- def __call__(self, obj):
- """
- Display ``obj``.
-
- .. warning::
-
- This is mostly a workaround for the old Notebook. Do not
- use it as a model for your own code.
-
- This is the default formatter for the old Sage Notebook
- (SageNB).
-
- INPUT:
-
- - ``obj`` -- anything.
-
- OUTPUT:
-
- String. The plain text representation. Graphics output is a
- side effect.
-
- EXAMPLES::
-
- sage: class FooGraphics(SageObject):
- ....: def _graphics_(self, **kwds):
- ....: print('showing graphics')
- ....: from sage.structure.graphics_file import GraphicsFile
- ....: from sage.misc.temporary_file import graphics_filename
- ....: return GraphicsFile(graphics_filename('.png'), 'image/png')
- ....: def _repr_(self):
- ....: return 'Textual representation'
- sage: from sage.repl.display.formatter import SageNBTextFormatter
- sage: fmt = SageNBTextFormatter()
- sage: fmt(FooGraphics())
- showing graphics
- ''
- """
- from sage.plot.plot3d.base import Graphics3d
- if isinstance(obj, Graphics3d):
- # TODO: Clean up Graphics3d and remove all the hardcoded SageNB stuff
- obj.show()
- return ''
- from sage.structure.sage_object import SageObject
- if isinstance(obj, SageObject) and hasattr(obj, '_graphics_'):
- gfx = obj._graphics_()
- if gfx:
- gfx.sagenb_embedding()
- return ''
- return super(SageNBTextFormatter, self).__call__(obj)
-
-
-class SageConsoleTextFormatter(SagePlainTextFormatter):
-
- @warn_format_error
- def __call__(self, obj):
- """
- Display ``obj``.
-
- This is the default formatter for the Sage command line
- interface.
-
- If the object is graphics, the empty string is written to the
- output. An external viewer is started in a separate process as
- a side effect.
-
- Otherwise, the usual textual representation is generated.
-
- INPUT:
-
- - ``obj`` -- anything.
-
- OUTPUT:
-
- String. The plain text representation. Third-party viewers for
- media files can be launched as a side effect.
-
- EXAMPLES::
-
- sage: class FooGraphics(SageObject):
- ....: def _graphics_(self, **kwds):
- ....: print('showing graphics')
- ....: from sage.structure.graphics_file import GraphicsFile
- ....: return GraphicsFile('/nonexistent.png', 'image/png')
- ....: def _repr_(self):
- ....: return 'Textual representation'
- sage: from sage.repl.display.formatter import SageConsoleTextFormatter
- sage: fmt = SageConsoleTextFormatter()
- sage: fmt(FooGraphics())
- showing graphics
- ''
- """
- from sage.structure.sage_object import SageObject
- if isinstance(obj, SageObject) and hasattr(obj, '_graphics_'):
- gfx = obj._graphics_()
- if gfx:
- gfx.launch_viewer()
- return ''
- return super(SageConsoleTextFormatter, self).__call__(obj)
diff --git a/src/sage/repl/display/python_hook.py b/src/sage/repl/display/python_hook.py
deleted file mode 100644
index f9d0ce8..0000000
--- a/src/sage/repl/display/python_hook.py
+++ /dev/null
@@ -1,62 +0,0 @@
-"""
-The display hook for plain Python
-
-This is not used directly in interactive Sage (where we use the
-IPython system for display hooks). This class provides a way to use
-the Sage "plain text" display formatting when not using interactive
-Sage, for example when running doctests.
-"""
-
-#*****************************************************************************
-# Copyright (C) 2014 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 __builtin__
-
-
-from sage.repl.display.formatter import SageDoctestTextFormatter
-
-
-class DoctestDisplayHook(object):
-
- def __init__(self):
- """
- Python constructor
-
- EXAMPLES::
-
- sage: from sage.repl.display.python_hook import DoctestDisplayHook
- sage: d = DoctestDisplayHook()
- sage: d(set([1, 2, 3])) # Sage commandline output
- {1, 2, 3}
- sage: print(set([1, 2, 3])) # Plain Python output
- set([1, 2, 3])
- """
- self.formatter = SageDoctestTextFormatter()
-
- def __call__(self, obj):
- """
- Format the object using Sage's formatting, or format it using the old
- display hook if Sage does not want to handle the object.
-
- EXAMPLES::
-
- sage: from sage.repl.display.python_hook import DoctestDisplayHook
- sage: d = DoctestDisplayHook()
- sage: d((identity_matrix(3), identity_matrix(3)))
- (
- [1 0 0] [1 0 0]
- [0 1 0] [0 1 0]
- [0 0 1], [0 0 1]
- )
- """
- if obj is None:
- return
- print(self.formatter(obj))
- __builtin__._ = obj
diff --git a/src/sage/repl/image.py b/src/sage/repl/image.py
new file mode 100644
index 0000000..187205e
--- /dev/null
+++ b/src/sage/repl/image.py
@@ -0,0 +1,306 @@
+# -*- encoding: utf-8 -*-
+"""
+Sage Wrapper for Bitmap Images
+
+Some computations in Sage return bitmap images, for example matrices
+can be turned into bitmaps directly. Note that this is different from
+all plotting functionality, the latter can equally produce vector
+graphics. This module is about bitmaps only, and a shallow wrapper
+around ``PIL.Image``. The only difference is that :class:`Image`
+is displayed as graphics by the Sage if the UI can.
+
+EXAMPLES::
+
+ sage: from sage.repl.image import Image
+ sage: img = Image('RGB', (256, 256), 'white')
+ sage: pixels = img.pixels()
+ sage: for x, y in CartesianProduct(range(img.width()), range(img.height())):
+ ....: pixels[x, y] = (x, y, 100)
+ sage: img
+ 256x256px 24-bit RGB image
+ sage: type(img)
+ <class 'sage.repl.image.Image'>
+"""
+
+#*****************************************************************************
+# 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 PIL.Image
+from sage.structure.sage_object import SageObject
+
+
+class Image(SageObject):
+
+ def __init__(self, mode, size, color=0):
+ """
+ Creates a new image with the given mode and size.
+
+ INPUT::
+
+ - ``mode`` -- string. The mode to use for the new image. Valid
+ options are:
+
+ * ``'1'`` (1-bit pixels, black and white, stored with
+ one pixel per byte)
+
+ * ``'L'`` (8-bit pixels, black and white)
+
+ * ``'P'`` (8-bit pixels, mapped to any other mode using
+ a color palette)
+
+ * ``'RGB'`` (3x8-bit pixels, true color)
+
+ * ``'RGBA'`` (4x8-bit pixels, true color with
+ transparency mask)
+
+ * ``'CMYK'`` (4x8-bit pixels, color separation)
+
+ * ``'YCbCr'`` (3x8-bit pixels, color video format)
+
+ * ``'LAB'`` (3x8-bit pixels, the L*a*b color space)
+
+ * ``'HSV'`` (3x8-bit pixels, Hue, Saturation, Value
+ color space)
+
+ * ``'I'`` (32-bit signed integer pixels)
+
+ * ``'F'`` (32-bit floating point pixels)
+
+ - ``size`` -- 2-tuple, containing (width, height) in pixels.
+
+ - ``color`` -- string or numeric. What colour to use for the
+ image. Default is black. If given, this should be a single
+ integer or floating point value for single-band modes, and a
+ tuple for multi-band modes (one value per band). When
+ creating RGB images, you can also use colour strings as
+ supported by the ImageColor module. If the colour is None,
+ the image is not initialised.
+
+ OUTPUT:
+
+ A new :class:`Image` object.
+
+ EXAMPLES::
+
+ sage: from sage.repl.image import Image
+ sage: Image('P', (16, 16), 13)
+ 16x16px 8-bit Color image
+ """
+ self._pil = PIL.Image.new(mode, size, color)
+
+ @property
+ def pil(self):
+ """
+ Access the wrapped PIL(low) Image
+
+ OUTPUT:
+
+ The underlying ``PIL.Image.Image object``.
+
+ EXAMPLES::
+
+ sage: from sage.repl.image import Image
+ sage: img = Image('RGB', (16, 16), 'white')
+ sage: img.pil
+ <PIL.Image.Image image mode=RGB size=16x16 at 0x...>
+ """
+ return self._pil
+
+ def pixels(self):
+ """
+ Return the pixel map
+
+ OUTPUT:
+
+ The PIL PixelAccess object that allows you to get/set the
+ pixel data.
+
+ EXAMPLES::
+
+ sage: from sage.repl.image import Image
+ sage: img = Image('RGB', (16, 16), 'white')
+ sage: img.pixels()
+ <PixelAccess object at 0x...>
+ """
+ return self._pil.load()
+
+ def _repr_(self):
+ """
+ Return string representation.
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.image import Image
+ sage: Image('RGB', (16, 16), 'white') # indirect doctest
+ 16x16px 24-bit RGB image
+ """
+ modestr = {
+ '1': '{0}x{1}px BW image',
+ 'L': '{0}x{1}px 8-bit BW image',
+ 'P': '{0}x{1}px 8-bit Color image',
+ 'RGB': '{0}x{1}px 24-bit RGB image',
+ 'RGBA': '{0}x{1}px 32-bit RGBA image',
+ 'CMYK': '{0}x{1}px 24-bit CMYK image',
+ 'YCbCr': '{0}x{1}px 24-bit YCbCr mage',
+ 'LAB': '{0}x{1}px 24-bit LAB image',
+ 'HSV': '{0}x{1}px 24-bit HSV image',
+ 'I': '{0}x{1}px 32-bit signed integer image',
+ 'F': '{0}x{1}px 32-bit float image',
+ }
+ try:
+ mode = modestr[self.pil.mode]
+ except AttributeError:
+ mode = 'Unknown mode'
+ width, height = self.pil.size
+ return mode.format(width, height)
+
+ def mode(self):
+ """
+ Return the color mode
+
+ OUTPUT:
+
+ String. As given when constructing the image.
+
+ EXAMPLES::
+
+ sage: from sage.repl.image import Image
+ sage: img = Image('YCbCr', (16, 16), 'white')
+ sage: img.mode()
+ 'YCbCr'
+ """
+ return self.pil.mode
+
+ def width(self):
+ """
+ Return the horizontal dimension in pixels
+
+ OUTPUT:
+
+ Integer.
+
+ EXAMPLES::
+
+ sage: from sage.repl.image import Image
+ sage: img = Image('1', (12, 34), 'white')
+ sage: img.width()
+ 12
+ sage: img.height()
+ 34
+ """
+ return self.pil.size[0]
+
+ def height(self):
+ """
+ Return the vertical dimension in pixels
+
+ OUTPUT:
+
+ Integer.
+
+ EXAMPLES::
+
+ sage: from sage.repl.image import Image
+ sage: img = Image('1', (12, 34), 'white')
+ sage: img.width()
+ 12
+ sage: img.height()
+ 34
+ """
+ return self.pil.size[1]
+
+ def save(self, filename):
+ """
+ Save the bitmap image
+
+ INPUT:
+
+ - ``filename`` -- string. The filename to save as. The given
+ extension automatically determines the image file type.
+
+ EXAMPLES::
+
+ sage: from sage.repl.image import Image
+ sage: img = Image('P', (12, 34), 13)
+ sage: filename = tmp_filename(ext='.png')
+ sage: img.save(filename)
+ sage: open(filename).read().startswith('\x89PNG')
+ True
+ """
+ self.pil.save(filename)
+
+ def show(self):
+ r"""
+ Show this image immediately.
+
+ This method attempts to display the graphics immediately,
+ without waiting for the currently running code (if any) to
+ return to the command line. Be careful, calling it from within
+ a loop will potentially launch a large number of external
+ viewer programs.
+
+ OUTPUT:
+
+ This method does not return anything. Use :meth:`save` if you
+ want to save the figure as an image.
+
+ EXAMPLES::
+
+ sage: from sage.repl.image import Image
+ sage: img = Image('1', (12, 34), 'white')
+ sage: img.show()
+ """
+ from sage.repl.rich_output import get_display_manager
+ dm = get_display_manager()
+ dm.display_immediately(self)
+
+ def _rich_repr_(self, display_manager, **kwds):
+ """
+ Rich Output Magic Method
+
+ See :mod:`sage.repl.rich_output` for details.
+
+ EXAMPLES::
+
+ sage: from sage.repl.image import Image
+ sage: img = Image('1', (16, 16), 'white')
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: img._rich_repr_(dm)
+ OutputImagePng container
+
+ sage: img = Image('F', (16, 16), 'white') # not supported in PNG
+ sage: img._rich_repr_(dm)
+ OutputImageGif container
+ """
+ if display_manager.preferences.graphics == 'disable':
+ return
+ types = display_manager.types
+ preferred = (
+ ('PNG', types.OutputImagePng),
+ ('JPEG', types.OutputImageJpg),
+ ('GIF', types.OutputImageGif),
+ )
+ import StringIO
+ from sage.repl.rich_output.buffer import OutputBuffer
+ for format, output_container in preferred:
+ if output_container in display_manager.supported_output():
+ stream = StringIO.StringIO()
+ try:
+ self.pil.save(stream, format=format)
+ except IOError:
+ # not all formats support all modes, e.g. no alpha support in gif
+ continue
+ buf = OutputBuffer(stream.getvalue())
+ return output_container(buf)
diff --git a/src/sage/repl/inputhook.pyx b/src/sage/repl/inputhook.pyx
index 379ba4f..4a64c67 100644
--- a/src/sage/repl/inputhook.pyx
+++ b/src/sage/repl/inputhook.pyx
@@ -103,6 +103,7 @@ def sage_inputhook():
sage: shell.run_cell('detach({0})'.format(repr(tmp)))
sage: shell.run_cell('attached_files()')
[]
+ sage: shell.quit()
"""
reload_attached_files_if_modified()
return 0
diff --git a/src/sage/repl/interpreter.py b/src/sage/repl/interpreter.py
index 0e2d692..5c0b980 100644
--- a/src/sage/repl/interpreter.py
+++ b/src/sage/repl/interpreter.py
@@ -73,7 +73,8 @@ TESTS:
Check that Cython source code appears in tracebacks::
sage: from sage.repl.interpreter import get_test_shell
- sage: get_test_shell().run_cell('1/0')
+ sage: shell = get_test_shell()
+ sage: shell.run_cell('1/0')
---------------------------------------------------------------------------
.../sage/rings/integer_ring.pyx in sage.rings.integer_ring.IntegerRing_class._div (build/cythonized/sage/rings/integer_ring.c:...)()
... cdef rational.Rational x = rational.Rational.__new__(rational.Rational)
@@ -83,6 +84,7 @@ Check that Cython source code appears in tracebacks::
... mpz_set(mpq_denref(x.value), right.value)
<BLANKLINE>
ZeroDivisionError: Rational division by zero
+ sage: shell.quit()
"""
#*****************************************************************************
@@ -103,28 +105,12 @@ import sys
from sage.repl.preparse import preparse
from IPython import Config
+from IPython.utils.traitlets import Bool, Type
from sage.env import SAGE_LOCAL
SAGE_EXTENSION = 'sage'
-DEFAULT_SAGE_CONFIG = Config(
- PromptManager = Config(
- in_template = 'sage: ',
- in2_template = '....: ',
- justify = False,
- out_template = ''),
- TerminalIPythonApp = Config(
- display_banner = False,
- verbose_crash = True),
- InteractiveShell = Config(
- ast_node_interactivity = 'all',
- colors = 'LightBG' if sys.stdout.isatty() else 'NoColor',
- confirm_exit = False,
- separate_in = ''),
- InteractiveShellApp = Config(extensions=[SAGE_EXTENSION]),
- )
-
def embedded():
"""
Returns True if Sage is being run from the notebook.
@@ -185,6 +171,7 @@ class SageShellOverride(object):
sage: shell = get_test_shell()
sage: shell.run_cell('?')
Welcome to Sage ...
+ sage: shell.quit()
"""
from sage.misc.sagedoc import help
help()
@@ -216,6 +203,7 @@ class SageShellOverride(object):
R version ...
sage: shell.user_ns['_exit_code']
0
+ sage: shell.quit()
"""
path = os.path.join(SAGE_LOCAL, 'bin',
re.split(r'[\s|;&]', cmd)[0])
@@ -230,11 +218,191 @@ class SageShellOverride(object):
from IPython.core.interactiveshell import InteractiveShell
from IPython.terminal.interactiveshell import TerminalInteractiveShell
-class SageInteractiveShell(SageShellOverride, InteractiveShell):
- pass
+
+class SageNotebookInteractiveShell(SageShellOverride, InteractiveShell):
+ """
+ IPython Shell for the Sage IPython Notebook
+
+ The doctests are not tested since they would change the current
+ rich output backend away from the doctest rich output backend.
+
+ EXAMPLES::
+
+ sage: from sage.repl.interpreter import SageNotebookInteractiveShell
+ sage: SageNotebookInteractiveShell() # not tested
+ <sage.repl.interpreter.SageNotebookInteractiveShell object at 0x...>
+ """
+
+ def init_display_formatter(self):
+ """
+ Switch to the Sage IPython notebook rich output backend
+
+ EXAMPLES::
+
+ sage: from sage.repl.interpreter import SageNotebookInteractiveShell
+ sage: SageNotebookInteractiveShell().init_display_formatter() # not tested
+ """
+ from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook
+ backend = BackendIPythonNotebook()
+ backend.get_display_manager().switch_backend(backend, shell=self)
+
class SageTerminalInteractiveShell(SageShellOverride, TerminalInteractiveShell):
- pass
+ """
+ IPython Shell for the Sage IPython Commandline Interface
+
+ The doctests are not tested since they would change the current
+ rich output backend away from the doctest rich output backend.
+
+ EXAMPLES::
+
+ sage: from sage.repl.interpreter import SageTerminalInteractiveShell
+ sage: SageTerminalInteractiveShell() # not tested
+ <sage.repl.interpreter.SageNotebookInteractiveShell object at 0x...>
+ """
+
+ def init_display_formatter(self):
+ """
+ Switch to the Sage IPython commandline rich output backend
+
+ EXAMPLES::
+
+ sage: from sage.repl.interpreter import SageTerminalInteractiveShell
+ sage: SageTerminalInteractiveShell().init_display_formatter() # not tested
+ """
+ from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
+ backend = BackendIPythonCommandline()
+ backend.get_display_manager().switch_backend(backend, shell=self)
+
+
+class SageTestShell(SageShellOverride, TerminalInteractiveShell):
+ """
+ Test Shell
+
+ Care must be taken in these doctests to quit the test shell in
+ order to switch back the rich output display backend to the
+ doctest backend.
+
+ EXAMPLES::
+
+ sage: from sage.repl.interpreter import get_test_shell
+ sage: shell = get_test_shell(); shell
+ <sage.repl.interpreter.SageTestShell object at 0x...>
+ sage: shell.quit()
+ """
+
+ def init_display_formatter(self):
+ """
+ Switch to the Sage IPython commandline rich output backend
+
+ EXAMPLES::
+
+ sage: from sage.repl.interpreter import get_test_shell
+ sage: shell = get_test_shell(); shell
+ <sage.repl.interpreter.SageTestShell object at 0x...>
+ sage: shell.quit()
+ sage: shell.init_display_formatter()
+ sage: shell.quit()
+ """
+ from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline
+ self._ipython_backend = backend = BackendIPythonCommandline()
+ self._display_manager = backend.get_display_manager()
+ self._doctest_backend = self._display_manager.switch_backend(backend, shell=self)
+
+ def quit(self):
+ """
+ Quit the test shell.
+
+ To make the test shell as realistic as possible, we switch to
+ the
+ :class:`~sage.repl.rich_output.backend_ipython.BackendIPythonCommandline`
+ display backend. This method restores the previous display
+ backend, which is the
+ :class:`~sage.repl.rich_output.backend_doctest.BackendDoctest`
+ during doctests.
+
+ EXAMPLES::
+
+ sage: from sage.repl.interpreter import get_test_shell
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: get_display_manager()
+ The Sage display manager using the doctest backend
+
+ sage: shell = get_test_shell()
+ sage: get_display_manager()
+ The Sage display manager using the IPython command line backend
+
+ sage: shell.quit()
+ sage: get_display_manager()
+ The Sage display manager using the doctest backend
+ """
+ self._display_manager.switch_backend(self._doctest_backend)
+
+ def _restart(self):
+ """
+ Restart the test shell (after :meth:`quit`).
+
+ EXAMPLES::
+
+ sage: from sage.repl.interpreter import get_test_shell
+ sage: shell = get_test_shell()
+ sage: shell.quit()
+ sage: shell._restart()
+ sage: shell.quit()
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: get_display_manager()
+ The Sage display manager using the doctest backend
+ """
+ self._display_manager.switch_backend(self._ipython_backend, shell=self)
+
+ def run_cell(self, *args, **kwds):
+ """
+ Run IPython cell
+
+ Starting with IPython-3.0, this returns an success/failure
+ information. Since it is more convenient for doctests, we
+ ignore it.
+
+
+ EXAMPLES::
+
+ sage: from sage.repl.interpreter import get_test_shell
+ sage: shell = get_test_shell()
+ sage: rc = shell.run_cell('1/0')
+ ---------------------------------------------------------------------------
+ ZeroDivisionError Traceback (most recent call last)
+ ...
+ ZeroDivisionError: Rational division by zero
+ sage: rc is None
+ True
+ sage: shell.quit()
+ """
+ rc = super(SageTestShell, self).run_cell(*args, **kwds)
+
+
+###################################################################
+# Default configuration
+###################################################################
+
+DEFAULT_SAGE_CONFIG = Config(
+ PromptManager = Config(
+ in_template = 'sage: ',
+ in2_template = '....: ',
+ justify = False,
+ out_template = ''),
+ TerminalIPythonApp = Config(
+ display_banner = False,
+ verbose_crash = True,
+ test_shell = False,
+ shell_class = SageTerminalInteractiveShell,
+ ),
+ InteractiveShell = Config(
+ ast_node_interactivity = 'all',
+ colors = 'LightBG' if sys.stdout.isatty() else 'NoColor',
+ confirm_exit = False,
+ separate_in = ''),
+ InteractiveShellApp = Config(extensions=[SAGE_EXTENSION]),
+)
###################################################################
@@ -274,6 +442,7 @@ def SagePreparseTransformer(line):
File "<string>", line unknown
SyntaxError: Mismatched ']'
<BLANKLINE>
+ sage: shell.quit()
"""
if _do_preparse and not line.startswith('%'):
return preparse(line)
@@ -322,6 +491,7 @@ def SagePromptTransformer():
sage: shell.run_cell(' sage: 1+1')
2
+ sage: shell.quit()
"""
_sage_prompt_re = re.compile(r'^(\s*(:?sage: |\.\.\.\.: ))+')
return _strip_prompts(_sage_prompt_re)
@@ -454,6 +624,7 @@ def interface_shell_embed(interface):
sage: shell = interface_shell_embed(gap)
sage: shell.run_cell('List( [1..10], IsPrime )')
[ false, true, true, false, true, false, true, false, false, false ]
+ <IPython.core.interactiveshell.ExecutionResult object at 0x...>
"""
try:
cfg = copy.deepcopy(get_ipython().config)
@@ -483,13 +654,20 @@ def get_test_shell():
Returns a IPython shell that can be used in testing the functions
in this module.
- :returns: an IPython shell
+ OUTPUT:
+
+ An IPython shell
EXAMPLES::
sage: from sage.repl.interpreter import get_test_shell
sage: shell = get_test_shell(); shell
- <sage.repl.interpreter.SageTerminalInteractiveShell object at 0x...>
+ <sage.repl.interpreter.SageTestShell object at 0x...>
+ sage: shell.parent.shell_class
+ <class 'sage.repl.interpreter.SageTestShell'>
+ sage: shell.parent.test_shell
+ True
+ sage: shell.quit()
TESTS:
@@ -501,9 +679,17 @@ def get_test_shell():
sage: out + err
''
"""
- app = SageTerminalApp.instance(config=copy.deepcopy(DEFAULT_SAGE_CONFIG))
+ config = copy.deepcopy(DEFAULT_SAGE_CONFIG)
+ config.TerminalIPythonApp.test_shell = True
+ config.TerminalIPythonApp.shell_class = SageTestShell
+ app = SageTerminalApp.instance(config=config)
if app.shell is None:
app.initialize(argv=[])
+ else:
+ try:
+ app.shell._restart()
+ except AttributeError:
+ pass
# overwrite the default (console + graphics) formatter with the plain text one
import sage.repl.display.formatter as formatter
app.shell.display_formatter.formatters['text/plain'] = (
@@ -548,7 +734,11 @@ class SageCrashHandler(IPAppCrashHandler):
class SageTerminalApp(TerminalIPythonApp):
name = u'Sage'
crash_handler_class = SageCrashHandler
- test_shell = False
+
+ test_shell = Bool(False, config=True,
+ help='Whether the shell is a test shell')
+ shell_class = Type(InteractiveShell, config=True,
+ help='Type of the shell')
def load_config_file(self, *args, **kwds):
r"""
@@ -591,13 +781,12 @@ class SageTerminalApp(TerminalIPythonApp):
EXAMPLES::
sage: from sage.repl.interpreter import SageTerminalApp, DEFAULT_SAGE_CONFIG
- sage: app = SageTerminalApp(config=DEFAULT_SAGE_CONFIG)
- sage: app.initialize(argv=[]) # indirect doctest
+ sage: app = SageTerminalApp.instance()
sage: app.shell
- <sage.repl.interpreter.SageTerminalInteractiveShell object at 0x...>
+ <sage.repl.interpreter.SageTestShell object at 0x...>
"""
# Shell initialization
- self.shell = SageTerminalInteractiveShell.instance(
+ self.shell = self.shell_class.instance(
parent=self,
config=self.config,
display_banner=False,
diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py
index 8cb2d5f..f924006 100644
--- a/src/sage/repl/ipython_extension.py
+++ b/src/sage/repl/ipython_extension.py
@@ -53,6 +53,7 @@ In contrast, input to the ``%time`` magic command is preparsed::
CPU times: user ...
Wall time: ...
2 * 3^3 * 11
+ sage: shell.quit()
"""
from IPython.core.magic import Magics, magics_class, line_magic
@@ -81,6 +82,7 @@ class SageMagics(Magics):
PROFILE: interrupts/evictions/bytes = ...
Using local file ...
Using local file ...
+ sage: shell.quit()
"""
import sage.misc.gperftools
sage.misc.gperftools.crun(s, evaluator=self.shell.ex)
@@ -106,6 +108,7 @@ class SageMagics(Magics):
sage: shell.run_cell('%runfile '+tmp)
sage: shell.run_cell('a')
2
+ sage: shell.quit()
"""
return self.shell.ex(load_wrap(s, attach=False))
@@ -146,6 +149,7 @@ class SageMagics(Magics):
sage: shell.run_cell('attached_files()')
[]
sage: os.remove(tmp)
+ sage: shell.quit()
"""
return self.shell.ex(load_wrap(s, attach=True))
@@ -215,7 +219,8 @@ class SageMagics(Magics):
Then when you want return in 'textual mode'::
- sage: shell.run_cell('%display simple')
+ sage: shell.run_cell('%display text plain')
+ sage: shell.run_cell('%display plain') # shortcut for "text plain"
sage: shell.run_cell('sum(i^2*x^i, i, 0, 10)')
100*x^10 + 81*x^9 + 64*x^8 + 49*x^7 + 36*x^6 + 25*x^5 + 16*x^4 + 9*x^3 + 4*x^2 + x
@@ -248,32 +253,38 @@ class SageMagics(Magics):
As yet another option, typeset mode. This is used in the emacs
interface::
- sage: shell.run_cell('%display typeset')
+ sage: shell.run_cell('%display text latex')
sage: shell.run_cell('1/2')
- <html><script type="math/tex">...\frac{1}{2}</script></html>
+ \newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{2}
Switch back::
- sage: shell.run_cell('%display simple')
+ sage: shell.run_cell('%display default')
+
+ Switch graphics to default to vector or raster graphics file
+ formats::
+
+ sage: shell.run_cell('%display graphics vector')
TESTS::
sage: shell.run_cell('%display invalid_mode')
- ---------------------------------------------------------------------------
- ValueError Traceback (most recent call last)
- ...
- ValueError: invalid mode set
+ value must be unset (None) or one of ('plain', 'ascii_art', 'latex'), got invalid_mode
+ sage: shell.quit()
"""
+ from sage.repl.rich_output import get_display_manager
+ dm = get_display_manager()
args = args.strip().split()
if not args:
- # toggle
- self._magic_display_status = ('ascii_art' if
- self._magic_display_status == 'simple' else 'simple')
- else:
- mode = args[0]
- self._magic_display_status = mode
-
- if self._magic_display_status == 'ascii_art' and len(args) > 1:
+ print(dm.preferences)
+ return
+ arg0 = args[0]
+ # deprecated values
+ if arg0 == 'simple':
+ dm.preferences.text = 'plain'
+ elif arg0 == 'typeset':
+ dm.preferences.text = 'latex'
+ elif arg0 == 'ascii_art' and len(args) > 1:
try:
max_width = int(args[1])
except ValueError:
@@ -283,9 +294,33 @@ class SageMagics(Magics):
"ascii_art max width must be a positive integer")
import sage.misc.ascii_art as ascii_art
ascii_art.MAX_WIDTH = max_width
+ dm.preferences.text = 'ascii_art'
+ # Unset
+ elif arg0 in ['default', 'None']: # un-stringify "%display None"
+ dm.preferences.text = None
+ dm.preferences.graphics = None
+ # Normal argument handling
+ elif arg0 == 'text' and len(args) == 1:
+ print(dm.preferences.text)
+ elif arg0 == 'text' and len(args) == 2:
+ try:
+ dm.preferences.text = args[1]
+ except ValueError as err:
+ print(err) # do not show traceback
+ elif arg0 == 'graphics' and len(args) == 1:
+ print(dm.preferences.graphics)
+ elif arg0 == 'graphics' and len(args) == 2:
+ try:
+ dm.preferences.graphics = args[1]
+ except ValueError as err:
+ print(err) # do not show traceback
+ # If all else fails: assume text
+ else:
+ try:
+ dm.preferences.text = arg0
+ except ValueError as err:
+ print(err) # do not show traceback
- self.shell.display_formatter.formatters['text/plain'].set_display(
- self._magic_display_status)
class SageCustomizations(object):
@@ -300,7 +335,7 @@ class SageCustomizations(object):
import sage.repl.display.formatter as formatter
self.shell.display_formatter.formatters['text/plain'] = (
- formatter.SageConsoleTextFormatter(config=shell.config))
+ formatter.SagePlainTextFormatter(config=shell.config))
import sage.misc.edit_module as edit_module
self.shell.set_hook('editor', edit_module.edit_devel)
diff --git a/src/sage/repl/ipython_kernel/__init__.py b/src/sage/repl/ipython_kernel/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/sage/repl/ipython_kernel/__init__.py
diff --git a/src/sage/repl/ipython_kernel/__main__.py b/src/sage/repl/ipython_kernel/__main__.py
new file mode 100644
index 0000000..ee2d10f
--- /dev/null
+++ b/src/sage/repl/ipython_kernel/__main__.py
@@ -0,0 +1,4 @@
+print('Sage IPython Kernel')
+from IPython.kernel.zmq.kernelapp import IPKernelApp
+from sage.repl.ipython_kernel.kernel import SageKernel
+IPKernelApp.launch_instance(kernel_class=SageKernel)
diff --git a/src/sage/repl/ipython_kernel/install.py b/src/sage/repl/ipython_kernel/install.py
new file mode 100644
index 0000000..6593be6
--- /dev/null
+++ b/src/sage/repl/ipython_kernel/install.py
@@ -0,0 +1,157 @@
+"""
+Sage-Enhanced IPython Notebook
+
+.. note::
+
+ The customized Jinja2 templates for the IPython notebook are in
+ ``SAGE_LOCAL/share/sage/ext/ipython-notebook``. You may have to
+ update them as well when updating to a new IPython version.
+"""
+
+import os
+import errno
+import copy
+
+from IPython.kernel.kernelspec import (
+ get_kernel_spec, install_kernel_spec, NoSuchKernel)
+
+
+from sage.env import (
+ SAGE_ROOT, SAGE_DOC, SAGE_LOCAL, SAGE_EXTCODE,
+ SAGE_VERSION
+)
+from sage.misc.temporary_file import tmp_dir
+
+
+class SageKernelSpec(object):
+
+ def __init__(self):
+ """
+ Utility to manage Sage kernels
+
+ EXAMPLES::
+
+ sage: from sage.repl.ipython_kernel.install import SageKernelSpec
+ sage: spec = SageKernelSpec()
+ sage: spec._display_name # not tested
+ 'Sage 6.6.beta2'
+ sage: spec._identifier # not tested
+ 'sage_6_6_beta2'
+ """
+ self._display_name = 'Sage {0}'.format(SAGE_VERSION)
+
+ @classmethod
+ def identifier(self):
+ return 'Sage {0}'.format(SAGE_VERSION).lower().replace(' ', '_').replace('.', '_')
+
+ def symlink(self, src, dst):
+ """
+ Symlink ``src`` to ``dst``
+
+ This is not an atomic operation.
+
+ Already-existing symlinks will be deleted, already existing
+ non-empty directories will be kept.
+
+ EXAMPLES::
+
+ sage: from sage.repl.ipython_kernel.install import SageKernelSpec
+ sage: spec = SageKernelSpec()
+ sage: path = tmp_dir()
+ sage: spec.symlink(os.path.join(path, 'a'), os.path.join(path, 'b'))
+ sage: os.listdir(path)
+ ['b']
+ """
+ try:
+ os.remove(dst)
+ except OSError as err:
+ if err.errno == errno.EEXIST:
+ return
+ os.symlink(src, dst)
+
+ def use_local_mathjax(self):
+ ipython_dir = os.environ['IPYTHONDIR']
+ src = os.path.join(SAGE_LOCAL, 'share', 'mathjax')
+ dst = os.path.join(ipython_dir, 'nbextensions', 'mathjax')
+ self.symlink(src, dst)
+
+ def _kernel_cmd(self):
+ return [
+ os.path.join(SAGE_ROOT, 'sage'),
+ '-python',
+ '-m', 'sage.repl.ipython_kernel',
+ '-f', '{connection_file}',
+ ]
+
+ def kernel_spec(self):
+ """
+ Return the kernel spec as Python dictionary
+ """
+ return dict(
+ argv=self._kernel_cmd(),
+ display_name=self._display_name,
+ )
+
+ def install_spec(self):
+ """
+ Install the Sage IPython kernel
+
+ It is safe to call this method multiple times, only one Sage
+ kernel spec is ever installed.
+ """
+ import json
+ temp = tmp_dir()
+ kernel_spec = os.path.join(temp, 'kernel.json')
+ with open(kernel_spec, 'w') as f:
+ json.dump(self.kernel_spec(), f)
+ identifier = self.identifier()
+ install_kernel_spec(temp, identifier, user=True, replace=True)
+ self._spec = get_kernel_spec(identifier)
+
+ def symlink_resources(self):
+ spec_dir = self._spec.resource_dir
+ path = os.path.join(SAGE_EXTCODE, 'notebook-ipython')
+ for filename in os.listdir(path):
+ self.symlink(
+ os.path.join(path, filename),
+ os.path.join(spec_dir, filename)
+ )
+ self.symlink(
+ os.path.join(SAGE_DOC, 'output', 'html', 'en'),
+ os.path.join(spec_dir, 'doc')
+ )
+
+ @classmethod
+ def update(cls):
+ instance = cls()
+ instance.use_local_mathjax()
+ instance.install_spec()
+ instance.symlink_resources()
+
+
+def have_prerequisites():
+ """
+ Check that we have all prerequisites to run the IPython notebook.
+
+ In particular, the IPython notebook requires OpenSSL whether or
+ not you are using https. See trac:`17318`.
+
+ OUTPUT:
+
+ Boolean.
+
+ EXAMPLES::
+
+ sage: from sage.repl.notebook_ipython import have_prerequisites
+ sage: have_prerequisites() in [True, False]
+ True
+ """
+ try:
+ from IPython.html.notebookapp import NotebookApp
+ return True
+ except ImportError as err:
+ return False
+
+
+
+
diff --git a/src/sage/repl/ipython_kernel/kernel.py b/src/sage/repl/ipython_kernel/kernel.py
new file mode 100644
index 0000000..4de4ade
--- /dev/null
+++ b/src/sage/repl/ipython_kernel/kernel.py
@@ -0,0 +1,115 @@
+"""
+The Sage ZMQ Kernel
+
+Version of the IPython kernel when running Sage inside the IPython
+notebook or remote IPython sessions.
+"""
+
+#*****************************************************************************
+# 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 sys
+from IPython.kernel.zmq.ipkernel import IPythonKernel
+from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
+from IPython.utils.traitlets import Type
+
+from sage.env import SAGE_VERSION, SAGE_EXTCODE, SAGE_DOC
+from sage.repl.interpreter import SageNotebookInteractiveShell
+from sage.repl.ipython_extension import SageCustomizations
+
+class SageZMQInteractiveShell(SageNotebookInteractiveShell, ZMQInteractiveShell):
+ pass
+
+
+class SageKernel(IPythonKernel):
+ implementation = 'sage'
+ implementation_version = SAGE_VERSION
+
+ shell_class = Type(SageZMQInteractiveShell)
+
+ def __init__(self, **kwds):
+ super(SageKernel, self).__init__(**kwds)
+ SageCustomizations(self.shell)
+
+ @property
+ def banner(self):
+ from sage.misc.banner import banner_text
+ return banner_text()
+
+ @property
+ def help_links(self):
+ from sage.repl.ipython_kernel.install import SageKernelSpec
+ identifier = SageKernelSpec.identifier()
+ kernel_url = lambda x: '/kernelspecs/{0}/{1}'.format(identifier, x)
+ return [
+ {
+ 'text': 'Sage Documentation',
+ 'url': kernel_url('doc/index.html')
+ },
+ {
+ 'text': 'Sage Tutorial',
+ 'url': kernel_url('doc/tutorial/index.html'),
+ },
+ {
+ 'text': 'Thematic Tutorials',
+ 'url': kernel_url('doc/thematic_tutorials/index.html'),
+ },
+ {
+ 'text': 'FAQs',
+ 'url': kernel_url('doc/faq/index.html'),
+ },
+ {
+ 'text': 'PREP Tutorials',
+ 'url': kernel_url('doc/prep/index.html'),
+ },
+ {
+ 'text': 'Sage Reference',
+ 'url': kernel_url('doc/reference/index.html'),
+ },
+ {
+ 'text': 'Developers Guide',
+ 'url': kernel_url('doc/developer/index.html'),
+ },
+ {
+ 'text': "Python",
+ 'url': "http://docs.python.org/%i.%i" % sys.version_info[:2],
+ },
+ {
+ 'text': "IPython",
+ 'url': "http://ipython.org/documentation.html",
+ },
+ {
+ 'text': 'Singular',
+ 'url': 'http://www.singular.uni-kl.de/Manual/latest/index.htm',
+ },
+ {
+ 'text': 'GAP',
+ 'url': 'http://gap-system.org/Manuals/doc/ref/chap0.html',
+ },
+ {
+ 'text': "NumPy",
+ 'url': "http://docs.scipy.org/doc/numpy/reference/",
+ },
+ {
+ 'text': "SciPy",
+ 'url': "http://docs.scipy.org/doc/scipy/reference/",
+ },
+ {
+ 'text': "SymPy",
+ 'url': 'http://docs.sympy.org/latest/index.html',
+ },
+ {
+ 'text': "Matplotlib",
+ 'url': "http://matplotlib.org/contents.html",
+ },
+ {
+ 'text': "Markdown",
+ 'url': "http://help.github.com/articles/github-flavored-markdown",
+ },
+ ]
diff --git a/src/sage/repl/notebook_ipython.py b/src/sage/repl/notebook_ipython.py
deleted file mode 100644
index aa59040..0000000
--- a/src/sage/repl/notebook_ipython.py
+++ /dev/null
@@ -1,128 +0,0 @@
-"""
-Sage-Enhanced IPython Notebook
-
-.. note::
-
- The customized Jinja2 templates for the IPython notebook are in
- ``SAGE_LOCAL/share/sage/ext/ipython-notebook``. You may have to
- update them as well when updating to a new IPython version.
-"""
-
-import os
-import copy
-
-from IPython import Config
-try:
- # Fails if we do not have ssl
- from IPython.html.notebookapp import NotebookApp
-except ImportError:
- NotebookApp = object
-
-
-from sage.env import DOT_SAGE, SAGE_EXTCODE, SAGE_DOC
-from sage.repl.interpreter import (
- SageCrashHandler, DEFAULT_SAGE_CONFIG, SageInteractiveShell,
-)
-
-
-# The notebook Jinja2 templates and static files
-TEMPLATE_PATH = os.path.join(SAGE_EXTCODE, 'notebook-ipython', 'templates')
-STATIC_PATH = os.path.join(SAGE_EXTCODE, 'notebook-ipython', 'static')
-DOCS_PATH = os.path.join(SAGE_DOC, 'output', 'html', 'en')
-
-
-# Note: sage.repl.interpreter.DEFAULT_SAGE_CONFIG will be applied, too
-DEFAULT_SAGE_NOTEBOOK_CONFIG = Config(
- SageNotebookApp = Config(
- # log_level = 'DEBUG', # if you want more logs
- # open_browser = False, # if you want to avoid browser restart
- webapp_settings = Config(
- template_path = TEMPLATE_PATH,
- ),
- extra_static_paths = [STATIC_PATH, DOCS_PATH],
- ),
-)
-
-
-def have_prerequisites():
- """
- Check that we have all prerequisites to run the IPython notebook.
-
- In particular, the IPython notebook requires OpenSSL whether or
- not you are using https. See trac:`17318`.
-
- OUTPUT:
-
- Boolean.
-
- EXAMPLES::
-
- sage: from sage.repl.notebook_ipython import have_prerequisites
- sage: have_prerequisites() in [True, False]
- True
- """
- return NotebookApp != object # that is, import worked
-
-
-class SageNotebookApp(NotebookApp):
- name = u'sage-notebook-ipython'
- crash_handler_class = SageCrashHandler
-
- def load_config_file(self, *args, **kwds):
- r"""
- Merges a config file with the default sage notebook config.
-
- EXAMPLES::
-
- sage: from sage.misc.temporary_file import tmp_dir
- sage: from sage.repl.notebook_ipython import SageNotebookApp
- sage: d = tmp_dir()
- sage: IPYTHONDIR = os.environ['IPYTHONDIR']
- sage: os.environ['IPYTHONDIR'] = d
- sage: app = SageNotebookApp() # optional - ssl
- sage: app.load_config_file() # optional - ssl, random output
- 2014-09-16 23:57:35.6 [SageNotebookApp] Created profile dir:
- u'/home/vbraun/.sage/temp/desktop.localdomain/1490/dir_ZQupP5/profile_default'
- sage: app.notebook_dir # random output, optional - ssl
- u'/home/vbraun/'
- sage: os.environ['IPYTHONDIR'] = IPYTHONDIR
- """
- super(SageNotebookApp, self).load_config_file(*args, **kwds)
- newconfig = copy.deepcopy(DEFAULT_SAGE_CONFIG)
- newconfig.merge(DEFAULT_SAGE_NOTEBOOK_CONFIG)
- newconfig.merge(self.config)
- self.config = newconfig
-
- def init_kernel_argv(self):
- """
- Construct the kernel arguments
-
- The kernel is running in a separate process, so it does not
- see our config dictionary. Any options need to be passed
- through the command line interface.
-
- EXAMPLES::
-
- sage: from sage.repl.notebook_ipython import SageNotebookApp
- sage: d = tmp_dir()
- sage: IPYTHONDIR = os.environ['IPYTHONDIR']
- sage: os.environ['IPYTHONDIR'] = d
- sage: app = SageNotebookApp() # optional - ssl
- sage: app.kernel_argv # optional - ssl
- []
- sage: app.init_kernel_argv() # optional - ssl, random output
- 2014-09-16 23:57:35.6 [SageNotebookApp] Created profile dir:
- u'/home/vbraun/.sage/temp/desktop.localdomain/1490/dir_ZQupP5/profile_default'
- sage: app.kernel_argv # optional - ssl
- [u"--IPKernelApp.parent_appname='sage-notebook-ipython'",
- '--profile-dir',
- u'/.../profile_default',
- u'--IPKernelApp.kernel_class=sage.repl.zmq_kernel.SageKernel',
- u'--IPKernelApp.extra_extension=sage']
- sage: os.environ['IPYTHONDIR'] = IPYTHONDIR
- """
- super(SageNotebookApp, self).init_kernel_argv()
- self.kernel_argv.append(
- u'--IPKernelApp.kernel_class=sage.repl.zmq_kernel.SageKernel')
- self.kernel_argv.append(
- u'--IPKernelApp.extra_extension=sage')
diff --git a/src/sage/repl/rich_output/__init__.py b/src/sage/repl/rich_output/__init__.py
new file mode 100644
index 0000000..5b028cd
--- /dev/null
+++ b/src/sage/repl/rich_output/__init__.py
@@ -0,0 +1,5 @@
+# -*- encoding: utf-8 -*-
+
+from .display_manager import get_display_manager
+
+
diff --git a/src/sage/repl/rich_output/backend_base.py b/src/sage/repl/rich_output/backend_base.py
new file mode 100644
index 0000000..7f4f127
--- /dev/null
+++ b/src/sage/repl/rich_output/backend_base.py
@@ -0,0 +1,542 @@
+# -*- encoding: utf-8 -*-
+r"""
+Base class for Backends
+
+The display backends are the commandline, the SageNB notebook, the
+ipython notebook, the Emacs sage mode, the Sage doctester, .... All of
+these have different capabilities for what they can display.
+
+To implement a new display backend, you need to subclass
+:class:`BackendBase`. All backend-specific handlig of rich output
+should be in :meth:`~BackendBase.displayhook` and
+:meth:`~BackendBase.display_immediately`. See :class:`BackendSimple`
+for an absolutely minimal example of a functioning backend.
+
+You declare the types of rich output that your backend can handle in
+:meth:`~BackendBase.supported_output`. There are two ways to then
+display specific output types in your own backend.
+
+* Directly use one of the existing output containers listed in
+ :mod:`sage.repl.rich_output.output_catalog`. That is, test for the
+ rich output type in :meth:`~BackendBase.display_immediately` and
+ handle it.
+
+* Subclass the rich output container to attach your backend-specific
+ functionality. Then :meth:`~BackendBase.display_immediately` will
+ receive instances of your subclass. See
+ :class:`~sage.repl.rich_output.backend_test.BackendTest` for an
+ example of how this is done.
+
+You can also mix both ways of implementing different rich output types.
+
+EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
+ sage: backend = BackendSimple()
+ sage: plain_text = backend.plain_text_formatter(range(10)); plain_text
+ OutputPlainText container
+ sage: backend.displayhook(plain_text, plain_text)
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+"""
+
+#*****************************************************************************
+# 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/
+#*****************************************************************************
+
+from sage.structure.sage_object import SageObject
+
+
+class BackendBase(SageObject):
+
+ def _repr_(self):
+ """
+ Return string representation of the backend
+
+ Every backend must implement this method.
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: backend._repr_()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: derived classes must implement this method
+ """
+ raise NotImplementedError('derived classes must implement this method')
+
+ def get_display_manager(self):
+ """
+ Return the display manager singleton
+
+ This is a convenience method to access the display manager
+ singleton.
+
+ OUTPUT:
+
+ The unique
+ :class:`~sage.repl.rich_output.display_manager.DisplayManager`
+ instance.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: backend.get_display_manager()
+ The Sage display manager using the doctest backend
+ """
+ from sage.repl.rich_output import get_display_manager
+ return get_display_manager()
+
+ def install(self, **kwds):
+ """
+ Hook that will be called once before the backend is used for the
+ first time.
+
+ The default implementation does nothing.
+
+ INPUT:
+
+ - ``kwds`` -- optional keyword arguments that are passed
+ through by the
+ :meth:`~sage.repl.rich_output.display_manager.DisplayManager.switch_backend`
+ method.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: backend.install()
+ """
+ pass
+
+ def uninstall(self):
+ """
+ Hook that will be called once right before the backend is removed.
+
+ The default implementation does nothing.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: backend.uninstall()
+ """
+ pass
+
+ def default_preferences(self):
+ """
+ Return the backend's display preferences
+
+ Override this method to change the default preferences when
+ using your backend.
+
+ OUTPUT:
+
+ Instance of
+ :class:`~sage.repl.rich_output.preferences.DisplayPreferences`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: backend.default_preferences()
+ Display preferences:
+ * graphics is not specified
+ * text is not specified
+ """
+ from sage.repl.rich_output.preferences import DisplayPreferences
+ return DisplayPreferences()
+
+ def supported_output(self):
+ """
+ Return the outputs that are supported by the backend.
+
+ Subclasses must implement this method.
+
+ OUTPUT:
+
+ Iterable of output container classes, that is, subclass of
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`). May
+ be a list/tuple/set/frozenset. The order is ignored. Only used
+ internally by the display manager.
+
+ You may return backend-specific subclasses of existing output
+ containers. This allows you to attach backend-specifc
+ functionality to the output container.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: backend.supported_output()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: derived classes must implement this method
+ """
+ raise NotImplementedError('derived classes must implement this method')
+
+ def max_width(self):
+ """
+ Return the number of characters that fit into one output line
+
+ OUTPUT:
+
+ Integer.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: backend.max_width()
+ 79
+ """
+ return 79
+
+ def newline(self):
+ r"""
+ Return the newline string.
+
+ OUTPUT:
+
+ String for starting a new line of output.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: backend.newline()
+ '\n'
+ """
+ return '\n'
+
+ def _apply_pretty_printer(self, pretty_printer_class, obj):
+ """
+ Helper method to format ``obj`` as text
+
+ INPUT:
+
+ - ``pretty_printer_class`` -- subclass of
+ :class:`sage.repl.display.pretty_print.SagePrettyPrinter`.
+
+ - ``obj`` -- anything.
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: from sage.repl.display.pretty_print import SagePrettyPrinter
+ sage: backend._apply_pretty_printer(SagePrettyPrinter, 1/2)
+ '1/2'
+ """
+ import StringIO
+ stream = StringIO.StringIO()
+ printer = pretty_printer_class(
+ stream, self.max_width(), self.newline())
+ printer.pretty(obj)
+ printer.flush()
+ return stream.getvalue()
+
+ def plain_text_formatter(self, obj):
+ r"""
+ Hook to override how plain text is being formatted.
+
+ If the object does not have a ``_rich_repr_`` method, or if it
+ does not return a rich output object
+ (:class:`~sage.repl.rich_output.output_basic.OutputBase`),
+ then this method is used to generate plain text output.
+
+ INPUT:
+
+ - ``obj`` -- anything.
+
+ OUTPUT:
+
+ Instance of
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`
+ containing the string representation of the object.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: out = backend.plain_text_formatter(range(30))
+ sage: out
+ OutputPlainText container
+ sage: out.text
+ buffer containing 139 bytes
+ sage: out.text.get()
+ '[0,\n 1,\n 2,\n 3,\n 4,\n 5,\n 6,\n 7,\n 8,\n 9,\n
+ 10,\n 11,\n 12,\n 13,\n 14,\n 15,\n 16,\n 17,\n 18,\n
+ 19,\n 20,\n 21,\n 22,\n 23,\n 24,\n 25,\n 26,\n 27,\n
+ 28,\n 29]'
+ """
+ from sage.repl.display.pretty_print import SagePrettyPrinter
+ plain_text = self._apply_pretty_printer(SagePrettyPrinter, obj)
+ from sage.repl.rich_output.output_basic import OutputPlainText
+ return OutputPlainText(plain_text)
+
+ def ascii_art_formatter(self, obj):
+ """
+ Hook to override how ascii art is being formatted.
+
+ INPUT:
+
+ - ``obj`` -- anything.
+
+ OUTPUT:
+
+ Instance of
+ :class:`~sage.repl.rich_output.output_basic.OutputAsciiArt`
+ containing the ascii art string representation of the object.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: out = backend.ascii_art_formatter(range(30))
+ sage: out
+ OutputAsciiArt container
+ sage: out.ascii_art
+ buffer containing 228 bytes
+ sage: print(out.ascii_art.get())
+ [
+ [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ <BLANKLINE>
+ ]
+ 22, 23, 24, 25, 26, 27, 28, 29 ]
+ """
+ from sage.repl.display.pretty_print import AsciiArtPrettyPrinter
+ ascii_art = self._apply_pretty_printer(AsciiArtPrettyPrinter, obj)
+ from sage.repl.rich_output.output_basic import OutputAsciiArt
+ return OutputAsciiArt(ascii_art)
+
+ def latex_formatter(self, obj):
+ r"""
+ Hook to override how Latex is being formatted.
+
+ INPUT:
+
+ - ``obj`` -- anything.
+
+ OUTPUT:
+
+ Instance of
+ :class:`~sage.repl.rich_output.output_basic.OutputLatex`
+ containing the latex string representation of the object.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: out = backend.latex_formatter(1/2)
+ sage: out
+ OutputLatex container
+ sage: out.latex
+ buffer containing 45 bytes
+ sage: out.latex.get()
+ '\\newcommand{\\Bold}[1]{\\mathbf{#1}}\\frac{1}{2}'
+ sage: out.mathjax()
+ '<html><script type="math/tex; mode=display">\\newcommand{\\Bold}[1]{\\mathbf{#1}}\\frac{1}{2}</script></html>'
+ """
+ from sage.misc.latex import MathJax
+ mathjax = MathJax().eval(obj, mode='plain', combine_all=True)
+ from sage.repl.rich_output.output_basic import OutputLatex
+ return OutputLatex(str(mathjax))
+
+ def set_underscore_variable(self, obj):
+ """
+ Set the ``_`` builtin variable.
+
+ By default, this sets the special ``_`` variable. Backends
+ that organize the history differently (e.g. IPython) can
+ override this method.
+
+ INPUT:
+
+ - ``obj`` -- result of the most recent evaluation.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: backend.set_underscore_variable(123)
+ sage: _
+ 123
+
+ sage: 'foo'
+ 'foo'
+ sage: _ # indirect doctest
+ 'foo'
+ """
+ import __builtin__
+ __builtin__._ = obj
+
+ def displayhook(self, plain_text, rich_output):
+ """
+ Backend implementation of the displayhook
+
+ The value of the last statement on a REPL input line or
+ notebook cell are usually handed to the Python displayhook and
+ shown on screen. By overriding this method you define how
+ your backend handles output. The difference to the usual
+ displayhook is that Sage already converted the value to the
+ most suitable rich output container.
+
+ Derived classes must implement this method.
+
+ 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:
+
+ This method may return something, which is then returned from
+ the display manager's
+ :meth:`~sage.repl.rich_output.display_manager.DisplayManager.displayhook`
+ method.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
+ sage: plain_text = OutputPlainText.example()
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: backend.displayhook(plain_text, plain_text)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: derived classes must implement this method
+ """
+ return self.display_immediately(plain_text, rich_output)
+
+ 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. Typically, it ends
+ up being called by :meth:`sage.plot.graphics.Graphics.show`.
+
+ Derived classes must implement this method.
+
+ INPUT:
+
+ Same as :meth:`displayhook`.
+
+ OUTPUT:
+
+ This method may return something so you can implement
+ :meth:`displayhook` by calling this method. However, when
+ called by the display manager any potential return value is
+ discarded: There is no way to return anything without
+ returning to the command prompt.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
+ sage: plain_text = OutputPlainText.example()
+ sage: from sage.repl.rich_output.backend_base import BackendBase
+ sage: backend = BackendBase()
+ sage: backend.display_immediately(plain_text, plain_text)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: derived classes must implement this method
+ """
+ raise NotImplementedError('derived classes must implement this method')
+
+
+class BackendSimple(BackendBase):
+ """
+ Simple Backend
+
+ This backend only supports plain text.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
+ sage: BackendSimple()
+ simple
+ """
+
+ def _repr_(self):
+ r"""
+ Return string representation of the backend
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
+ sage: backend = BackendSimple()
+ sage: backend._repr_()
+ 'simple'
+ """
+ return 'simple'
+
+ def supported_output(self):
+ """
+ Return the outputs that are supported by the backend.
+
+ OUTPUT:
+
+ Iterable of output container classes, that is, subclass of
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`).
+ This backend only supports the plain text output container.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
+ sage: backend = BackendSimple()
+ sage: backend.supported_output()
+ {<class 'sage.repl.rich_output.output_basic.OutputPlainText'>}
+ """
+ from sage.repl.rich_output.output_basic import OutputPlainText
+ return set([OutputPlainText])
+
+ def display_immediately(self, plain_text, rich_output):
+ """
+ Show output without going back to the command line prompt.
+
+ INPUT:
+
+ Same as :meth:`~BackendBase.displayhook`.
+
+ OUTPUT:
+
+ This backend returns nothing, it just prints to stdout.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
+ sage: plain_text = OutputPlainText.example()
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
+ sage: backend = BackendSimple()
+ sage: backend.display_immediately(plain_text, plain_text)
+ Example plain text output
+ """
+ print(rich_output.text.get())
+
diff --git a/src/sage/repl/rich_output/backend_doctest.py b/src/sage/repl/rich_output/backend_doctest.py
new file mode 100644
index 0000000..3689a7f
--- /dev/null
+++ b/src/sage/repl/rich_output/backend_doctest.py
@@ -0,0 +1,250 @@
+# -*- encoding: utf-8 -*-
+"""
+The backend used for doctests
+
+This backend is active during doctests. It should mimic the behavior
+of the IPython command line as close as possible. Without actually
+launching image viewers, of course.
+
+EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: get_display_manager()
+ The Sage display manager using the doctest backend
+"""
+
+#*****************************************************************************
+# 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 sys
+from sage.repl.rich_output.backend_base import BackendBase
+from sage.repl.rich_output.output_catalog import *
+
+
+
+class BackendDoctest(BackendBase):
+
+ def _repr_(self):
+ """
+ Return a string representation.
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_doctest import BackendDoctest
+ sage: backend = BackendDoctest()
+ sage: backend._repr_()
+ 'doctest'
+ """
+ return 'doctest'
+
+ def install(self, **kwds):
+ """
+ Switch to the the doctest backend
+
+ This method is being called from within
+ :meth:`~sage.repl.rich_output.display_manager.DisplayManager.switch_backend`. You
+ should never call it by hand.
+
+ INPUT:
+
+ None of the optional keyword arguments are used in the doctest
+ backend.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_doctest import BackendDoctest
+ sage: backend = BackendDoctest()
+ sage: backend.install()
+ sage: backend.uninstall()
+ """
+ self._old_displayhook = sys.displayhook
+ sys.displayhook = self.get_display_manager().displayhook
+
+ def uninstall(self):
+ """
+ Switch away from the doctest backend
+
+ This method is being called from within
+ :meth:`~sage.repl.rich_output.display_manager.DisplayManager.switch_backend`. You
+ should never call it by hand.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_doctest import BackendDoctest
+ sage: backend = BackendDoctest()
+ sage: backend.install()
+ sage: backend.uninstall()
+ """
+ sys.displayhook = self._old_displayhook
+
+ def supported_output(self):
+ """
+ Return the supported output types
+
+ OUTPUT:
+
+ Set of subclasses of
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`, the
+ supported output container types.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_doctest import BackendDoctest
+ sage: from sage.repl.rich_output.output_catalog import *
+ sage: backend = BackendDoctest()
+ sage: OutputPlainText in backend.supported_output()
+ True
+ sage: OutputSceneJmol in backend.supported_output()
+ True
+ """
+ return set([
+ OutputPlainText, OutputAsciiArt, OutputLatex,
+ OutputImagePng, OutputImageGif, OutputImageJpg,
+ OutputImageSvg, OutputImagePdf, OutputImageDvi,
+ OutputSceneJmol, OutputSceneCanvas3d, OutputSceneWavefront,
+ ])
+
+ def displayhook(self, plain_text, rich_output):
+ """
+ Display object from 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``.
+
+ EXAMPLES:
+
+ This ends up calling the displayhook::
+
+ sage: plt = plot(sin)
+ sage: plt
+ Graphics object consisting of 1 graphics primitive
+ sage: plt.show()
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm.displayhook(plt) # indirect doctest
+ Graphics object consisting of 1 graphics primitive
+ """
+ self.validate(rich_output)
+ if any(isinstance(rich_output, cls) for cls in
+ [OutputPlainText, OutputAsciiArt, OutputLatex]):
+ rich_output.print_to_stdout()
+ else:
+ plain_text.print_to_stdout()
+
+ def display_immediately(self, plain_text, rich_output):
+ """
+ Display object immediately
+
+ INPUT:
+
+ Same as :meth:`displayhook`.
+
+ EXAMPLES:
+
+ The following example does not call the displayhook. More
+ precisely, the ``show()`` method returns ``None`` which is
+ ignored by the displayhook. When running the example on a Sage
+ display backend capable of displaying graphics outside of the
+ displayhook, the plot is still shown. Nothing is shown during
+ doctests::
+
+ sage: plt = plot(sin)
+ sage: plt
+ Graphics object consisting of 1 graphics primitive
+ sage: plt.show()
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm.display_immediately(plt) # indirect doctest
+ """
+ self.validate(rich_output)
+ if any(isinstance(rich_output, cls) for cls in
+ [OutputPlainText, OutputAsciiArt, OutputLatex]):
+ rich_output.print_to_stdout()
+
+ def validate(self, rich_output):
+ """
+ Perform checks on ``rich_output``
+
+ INPUT:
+
+ - ``rich_output`` -- instance of a subclass of
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`.
+
+ OUTPUT:
+
+ An assertion is triggered if ``rich_output`` is invalid.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: invalid = dm.types.OutputImagePng('invalid')
+ sage: backend = dm._backend; backend
+ doctest
+ sage: backend.validate(invalid)
+ Traceback (most recent call last):
+ ...
+ AssertionError
+ sage: backend.validate(dm.types.OutputPlainText.example())
+ sage: backend.validate(dm.types.OutputAsciiArt.example())
+ sage: backend.validate(dm.types.OutputLatex.example())
+ sage: backend.validate(dm.types.OutputImagePng.example())
+ sage: backend.validate(dm.types.OutputImageGif.example())
+ sage: backend.validate(dm.types.OutputImageJpg.example())
+ sage: backend.validate(dm.types.OutputImageSvg.example())
+ sage: backend.validate(dm.types.OutputImagePdf.example())
+ sage: backend.validate(dm.types.OutputImageDvi.example())
+ sage: backend.validate(dm.types.OutputSceneJmol.example())
+ sage: backend.validate(dm.types.OutputSceneWavefront.example())
+ sage: backend.validate(dm.types.OutputSceneCanvas3d.example())
+ """
+ if isinstance(rich_output, OutputPlainText):
+ pass
+ elif isinstance(rich_output, OutputAsciiArt):
+ pass
+ elif isinstance(rich_output, OutputLatex):
+ assert rich_output.mathjax().startswith('<html>')
+ elif isinstance(rich_output, OutputImagePng):
+ assert rich_output.png.get().startswith('\x89PNG')
+ elif isinstance(rich_output, OutputImageGif):
+ assert rich_output.gif.get().startswith('GIF89a')
+ elif isinstance(rich_output, OutputImageJpg):
+ assert rich_output.jpg.get().startswith('\xff\xd8\xff\xe0\x00\x10JFIF')
+ elif isinstance(rich_output, OutputImageSvg):
+ assert '</svg>' in rich_output.svg.get()
+ elif isinstance(rich_output, OutputImagePdf):
+ assert rich_output.pdf.get().startswith('%PDF-')
+ elif isinstance(rich_output, OutputImageDvi):
+ assert 'TeX output' in rich_output.dvi.get()
+ elif isinstance(rich_output, OutputSceneJmol):
+ assert rich_output.preview_png.get().startswith('\x89PNG')
+ assert rich_output.scene_zip.get().startswith('PK') # zip archive
+ elif isinstance(rich_output, OutputSceneWavefront):
+ assert rich_output.obj.get().startswith('mtllib ')
+ assert rich_output.mtl.get().startswith('newmtl ')
+ elif isinstance(rich_output, OutputSceneCanvas3d):
+ assert rich_output.canvas3d.get().startswith('[{vertices:')
+ else:
+ raise TypeError('rich_output type not supported')
diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py
new file mode 100644
index 0000000..e0889f0
--- /dev/null
+++ b/src/sage/repl/rich_output/backend_ipython.py
@@ -0,0 +1,487 @@
+# -*- encoding: 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
+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
+
+
+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 _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, OutputLatex,
+ OutputImagePng, OutputImageGif,
+ OutputImagePdf, OutputImageDvi,
+ OutputSceneJmol, OutputSceneWavefront,
+ ])
+
+ 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': 'Example plain text output'}, {})
+ """
+ if isinstance(rich_output, OutputPlainText):
+ return ({u'text/plain': rich_output.text.get()}, {})
+ elif isinstance(rich_output, OutputAsciiArt):
+ return ({u'text/plain': rich_output.ascii_art.get()}, {})
+ elif isinstance(rich_output, OutputLatex):
+ return ({u'text/plain': rich_output.latex.get()}, {})
+ elif isinstance(rich_output, OutputImagePng):
+ msg = self.launch_viewer(
+ rich_output.png.filename(ext='png'), plain_text.text.get())
+ return ({u'text/plain': msg}, {})
+ elif isinstance(rich_output, OutputImageGif):
+ msg = self.launch_viewer(
+ rich_output.gif.filename(ext='gif'), plain_text.text.get())
+ return ({u'text/plain': msg}, {})
+ elif isinstance(rich_output, OutputImagePdf):
+ msg = self.launch_viewer(
+ rich_output.pdf.filename(ext='pdf'), plain_text.text.get())
+ return ({u'text/plain': msg}, {})
+ elif isinstance(rich_output, OutputImageDvi):
+ msg = self.launch_viewer(
+ rich_output.dvi.filename(ext='dvi'), plain_text.text.get())
+ return ({u'text/plain': msg}, {})
+ elif isinstance(rich_output, OutputSceneJmol):
+ msg = self.launch_jmol(rich_output, plain_text.text.get())
+ return ({u'text/plain': msg}, {})
+ elif isinstance(rich_output, OutputSceneWavefront):
+ msg = self.launch_sage3d(rich_output, plain_text.text.get())
+ 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()
+ from sage.env import SAGE_LOCAL
+ jmol_cmd = os.path.join(SAGE_LOCAL, 'bin', '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 launch_sage3d(self, output_wavefront, plain_text):
+ """
+ Launch the stand-alone java3d viewer
+
+ INPUT:
+
+ - ``output_wavefront`` --
+ :class:`~sage.repl.rich_output.output_graphics3d.OutputSceneWavefront`. The
+ scene to launch Java3d 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 OutputSceneWavefront
+ sage: backend.launch_sage3d(OutputSceneWavefront.example(), 'Graphics3d object')
+ 'Launched Java 3D viewer for Graphics3d object'
+ """
+ from sage.env import SAGE_LOCAL
+ sage3d = os.path.join(SAGE_LOCAL, 'bin', 'sage3d')
+ obj = output_wavefront.obj_filename()
+ from sage.doctest import DOCTEST_MODE
+ if not DOCTEST_MODE:
+ os.system('{0} {1} 2>/dev/null 1>/dev/null &'
+ .format(sage3d, obj))
+ return 'Launched Java 3D viewer for {0}'.format(plain_text)
+
+
+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
+
+ The IPython notebook cannot display gif images, see
+ https://github.com/ipython/ipython/issues/2115 ::
+
+ sage: from sage.repl.rich_output.output_graphics import OutputImageGif
+ sage: OutputImageGif in supp
+ False
+ """
+ return set([
+ OutputPlainText, OutputAsciiArt, OutputLatex,
+ OutputImagePng, OutputImageJpg,
+ OutputImageSvg, OutputImagePdf,
+ ])
+
+ 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': 'Example plain text output'}, {})
+ """
+ if isinstance(rich_output, OutputPlainText):
+ return ({u'text/plain': rich_output.text.get()}, {})
+ elif isinstance(rich_output, OutputAsciiArt):
+ return ({u'text/plain': rich_output.ascii_art.get()}, {})
+ elif isinstance(rich_output, OutputLatex):
+ return ({u'text/html': rich_output.mathjax(),
+ u'text/plain': plain_text.text.get(),
+ }, {})
+ elif isinstance(rich_output, OutputImagePng):
+ return ({u'image/png': rich_output.png.get(),
+ u'text/plain': plain_text.text.get(),
+ }, {})
+ elif isinstance(rich_output, OutputImageJpg):
+ return ({u'image/jpeg': rich_output.jpg.get(),
+ u'text/plain': plain_text.text.get(),
+ }, {})
+
+ elif isinstance(rich_output, OutputImageSvg):
+ return ({u'image/svg+xml': rich_output.svg.get(),
+ u'text/plain': plain_text.text.get(),
+ }, {})
+ elif isinstance(rich_output, OutputImagePdf):
+ return ({u'image/png': rich_output.png.get(),
+ u'text/plain': plain_text.text.get(),
+ }, {})
+ else:
+ raise TypeError('rich_output type not supported')
+
+
+ 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.
+
+ .. TODO::
+
+ This does not work currently.
+
+ INPUT/OUTPUT:
+
+ Same as :meth:`displayhook`.
+
+ 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)
+ ({u'text/plain': 'Example plain text output'}, {})
+ """
+ return self.displayhook(plain_text, rich_output)
diff --git a/src/sage/repl/rich_output/backend_sagenb.py b/src/sage/repl/rich_output/backend_sagenb.py
new file mode 100644
index 0000000..dc860e3
--- /dev/null
+++ b/src/sage/repl/rich_output/backend_sagenb.py
@@ -0,0 +1,403 @@
+# -*- encoding: utf-8 -*-
+"""
+SageNB Backend for the Sage Rich Output System
+
+This module defines the IPython backends for
+:mod:`sage.repl.rich_output`.
+
+EXAMPLES:
+
+Install the SageNB displayhook while doctesting for the rest of this
+file. This somewhat odd incantation is how SageNB installed its
+displayhook before::
+
+ sage: from sage.misc.displayhook import DisplayHook
+ sage: import sys
+ sage: sys.displayhook = DisplayHook()
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: get_display_manager()
+ The Sage display manager using the SageNB backend
+
+We also enable the SageNB magic global variable::
+
+ sage: import sage.plot.plot
+ sage: sage.plot.plot.EMBEDDED_MODE = True
+
+And switch to a temporary directory so our current directory will not
+get cluttered with temporary files::
+
+ sage: os.chdir(tmp_dir())
+
+The SageNB notebook is based on saving data files with predictable
+filenames::
+
+ sage: os.path.exists('sage0.png')
+ False
+ sage: Graphics()
+ sage: os.path.exists('sage0.png')
+ True
+ sage: os.remove('sage0.png')
+"""
+
+#*****************************************************************************
+# 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 stat
+from sage.misc.cachefunc import cached_method
+from sage.misc.temporary_file import graphics_filename
+from sage.doctest import DOCTEST_MODE
+from sage.repl.rich_output.backend_base import BackendBase
+from sage.repl.rich_output.output_catalog import *
+
+
+def world_readable(filename):
+ """
+ All SageNB temporary files must be world-writeable.
+
+ Discussion of this design choice can be found at :trac:`17743`.
+
+ EXAMPLES::
+
+ sage: import os, stat
+ sage: f = tmp_filename()
+
+ At least on a sane system the temporary files are only readable by
+ the user, but not by others in the group or total strangers::
+
+ sage: mode = os.stat(f).st_mode
+ sage: bool(mode & stat.S_IRUSR), bool(mode & stat.S_IRGRP), bool(mode & stat.S_IROTH) # random output
+ (True, False, False)
+
+ This function disables that protection::
+
+ sage: from sage.repl.rich_output.backend_sagenb import world_readable
+ sage: world_readable(f)
+ sage: mode = os.stat(f).st_mode
+ sage: bool(mode & stat.S_IRUSR), bool(mode & stat.S_IRGRP), bool(mode & stat.S_IROTH)
+ (True, True, True)
+ """
+ os.chmod(filename, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
+
+
+class SageNbOutputSceneJmol(OutputSceneJmol):
+ """
+ Adapt Jmol rich output container for SageNB.
+
+ For legacy reasons, SageNB expects Jmol files saved under strange
+ names. This class takes care of that.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
+ sage: SageNbOutputSceneJmol.example()
+ SageNbOutputSceneJmol container
+ """
+
+ @cached_method
+ def sagenb_launch_script_filename(self):
+ """
+ Return the launch script filename used by SageNB
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
+ sage: j = SageNbOutputSceneJmol.example()
+ sage: j.sagenb_launch_script_filename()
+ 'sage0-size32.jmol'
+ """
+ import PIL.Image
+ from StringIO import StringIO
+ width, height = PIL.Image.open(StringIO(self.preview_png.get())).size
+ ext = '-size{0}.jmol'.format(width)
+ return graphics_filename(ext=ext)
+
+ @cached_method
+ def _base_filename(self):
+ """
+ Return the common part of the file name
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
+ sage: j = SageNbOutputSceneJmol.example()
+ sage: j._base_filename()
+ 'sage0-size32'
+ sage: j.sagenb_launch_script_filename().startswith(j._base_filename())
+ True
+ sage: j.scene_zip_filename().startswith(j._base_filename())
+ True
+ """
+ dot_jmol = '.jmol'
+ filename = self.sagenb_launch_script_filename()
+ assert filename.endswith(dot_jmol)
+ return filename[:-len(dot_jmol)]
+
+ @cached_method
+ def scene_zip_filename(self):
+ """
+ Return the filename for the scene zip archive
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
+ sage: j = SageNbOutputSceneJmol.example()
+ sage: j.scene_zip_filename()
+ 'sage0-size32-....jmol.zip'
+ """
+ from random import randint
+ return '{0}-{1}.jmol.zip'.format(
+ self._base_filename(),
+ randint(0, 1 << 30)
+ )
+
+ @cached_method
+ def preview_filename(self):
+ """
+ Return the filename for the png preview
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
+ sage: j = SageNbOutputSceneJmol.example()
+ sage: j.preview_filename()
+ './.jmol_images/sage0-size32.jmol.png'
+ """
+ directory, filename = os.path.split(self._base_filename())
+ if not directory:
+ directory = '.'
+ return '{0}/.jmol_images/{1}.jmol.png'.format(directory, filename)
+
+ def save_launch_script(self):
+ """
+ Save the Jmol launch script
+
+ See :meth:`sagenb_launch_script_filename`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
+ sage: j = SageNbOutputSceneJmol.example()
+ sage: os.path.exists('sage0-size32.jmol')
+ False
+ sage: j.save_launch_script()
+ sage: os.path.exists('sage0-size32.jmol')
+ True
+ """
+ from sagenb.notebook.interact import SAGE_CELL_ID
+ path = 'cells/{0}/{1}'.format(
+ SAGE_CELL_ID,
+ self.scene_zip_filename())
+ with open(self.sagenb_launch_script_filename(), 'w') as f:
+ f.write('set defaultdirectory "{0}"\n'.format(path))
+ f.write('script SCRIPT\n')
+ world_readable(self.sagenb_launch_script_filename())
+
+ def save_preview(self):
+ """
+ Save the preview PNG image
+
+ See :meth:`preview_filename`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
+ sage: j = SageNbOutputSceneJmol.example()
+ sage: import shutil
+ sage: shutil.rmtree('.jmol_images', ignore_errors=True)
+ sage: j.save_preview()
+ sage: os.listdir('.jmol_images')
+ ['sage1-size32.jmol.png']
+ """
+ from sage.misc.misc import sage_makedirs
+ sage_makedirs('.jmol_images')
+ self.preview_png.save_as(self.preview_filename())
+ world_readable(self.preview_filename())
+
+ def embed(self):
+ """
+ Save all files necessary to embed jmol
+
+ EXAMPLES:
+
+ Switch to a new empty temporary directory::
+
+ sage: os.chdir(tmp_dir())
+
+ sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol
+ sage: j = SageNbOutputSceneJmol.example()
+ sage: j.embed()
+ sage: sorted(os.listdir('.'))
+ ['.jmol_images', 'sage0-size32-....jmol.zip', 'sage0-size32.jmol']
+ sage: sorted(os.listdir('.jmol_images'))
+ ['sage0-size32.jmol.png']
+ """
+ self.save_preview()
+ self.save_launch_script()
+ self.scene_zip.save_as(self.scene_zip_filename())
+ world_readable(self.scene_zip_filename())
+
+
+class BackendSageNB(BackendBase):
+
+ def _repr_(self):
+ """
+ Return the string representation
+
+ OUTPUT:
+
+ String
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_sagenb import BackendSageNB
+ sage: backend = BackendSageNB()
+ sage: backend._repr_()
+ 'SageNB'
+ """
+ return 'SageNB'
+
+ def supported_output(self):
+ """
+ Return the outputs that are supported by the SageNB 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_sagenb import BackendSageNB
+ sage: backend = BackendSageNB()
+ sage: supp = backend.supported_output(); supp # random output
+ set([<class 'sage.repl.rich_output.output_graphics.OutputPlainText'>,
+ ...,
+ <class 'sage.repl.rich_output.output_graphics.OutputCanvas3d'>])
+ sage: from sage.repl.rich_output.output_basic import OutputLatex
+ sage: OutputLatex in supp
+ True
+ """
+ return set([
+ OutputPlainText, OutputAsciiArt, OutputLatex,
+ OutputImagePng, OutputImageGif, OutputImageJpg,
+ OutputImagePdf, OutputImageSvg,
+ SageNbOutputSceneJmol,
+ OutputSceneCanvas3d,
+ ])
+
+ def display_immediately(self, plain_text, rich_output):
+ """
+ Show output without waiting for the prompt.
+
+ 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:
+
+ This method does not return anything.
+
+ EXAMPLES:
+
+ sage: import sage.repl.rich_output.output_catalog as catalog
+ sage: plain_text = catalog.OutputPlainText.example()
+ sage: from sage.repl.rich_output.backend_sagenb import BackendSageNB
+ sage: backend = BackendSageNB()
+ sage: backend.display_immediately(plain_text, plain_text)
+ Example plain text output
+ sage: latex = catalog.OutputLatex.example()
+ sage: backend.display_immediately(plain_text, latex)
+ <html><script type="math/tex; mode=display">\newcommand{\Bold}[1]{\mathbf{#1}}\int \sin\left(x\right)\,{d x}</script></html>
+ """
+ if isinstance(rich_output, (OutputPlainText, OutputAsciiArt)):
+ rich_output.print_to_stdout()
+ elif isinstance(rich_output, OutputLatex):
+ print(rich_output.mathjax())
+ elif isinstance(rich_output, OutputImagePng):
+ self.embed_image(rich_output.png, '.png')
+ elif isinstance(rich_output, OutputImageGif):
+ self.embed_image(rich_output.gif, '.gif')
+ elif isinstance(rich_output, OutputImageJpg):
+ self.embed_image(rich_output.jpg, '.jpg')
+ elif isinstance(rich_output, OutputImagePdf):
+ self.embed_image(rich_output.pdf, '.pdf')
+ elif isinstance(rich_output, OutputImageSvg):
+ self.embed_image(rich_output.svg, '.svg')
+ elif isinstance(rich_output, OutputSceneJmol):
+ rich_output.embed()
+ elif isinstance(rich_output, OutputSceneCanvas3d):
+ self.embed_image(rich_output.canvas3d, '.canvas3d')
+ else:
+ raise TypeError('rich_output type not supported, got {0}'.format(rich_output))
+
+ def embed_image(self, output_buffer, file_ext):
+ """
+ Embed Image in the SageNB worksheet
+
+ SageNB scans per-cell directories for image files, so all we
+ have to do here is to save the image at the right place.
+
+ INPUT:
+
+ - ``output_buffer`` --
+ :class:`~sage.repl.rich_output.buffer.Buffer`. A buffer
+ holding the image data.
+
+ - ``file_ext`` -- string. The file extension to use for saving
+ the image.
+
+ OUTPUT:
+
+ Nothing is returned. The image file is saved in the
+ appropriate place for SageNB.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: rich_output = dm.types.OutputImagePng.example()
+ sage: os.path.exists('sage0.png')
+ False
+ sage: dm._backend.embed_image(rich_output.png, '.png')
+ sage: os.path.exists('sage0.png')
+ True
+ """
+ filename = graphics_filename(ext=file_ext)
+ output_buffer.save_as(filename)
+ world_readable(filename)
+
+
diff --git a/src/sage/repl/rich_output/backend_test.py b/src/sage/repl/rich_output/backend_test.py
new file mode 100644
index 0000000..fcea172
--- /dev/null
+++ b/src/sage/repl/rich_output/backend_test.py
@@ -0,0 +1,204 @@
+# -*- encoding: utf-8 -*-
+r"""
+Test Backend
+
+This backend is only for doctesting purposes.
+
+EXAMPLES:
+
+We switch to the test backend for the remainder of this file::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: from sage.repl.rich_output.backend_test import BackendTest, TestObject
+ sage: doctest_backend = dm.switch_backend(BackendTest())
+ sage: dm
+ The Sage display manager using the test backend
+
+ sage: dm._output_promotions
+ {<class 'sage.repl.rich_output.output_basic.OutputPlainText'>:
+ <class 'sage.repl.rich_output.backend_test.TestOutputPlainText'>}
+ sage: dm.displayhook(1/2)
+ 1/2 [TestOutputPlainText]
+ TestOutputPlainText container
+
+ sage: test = TestObject()
+ sage: test
+ called the _repr_ method
+ sage: dm.displayhook(test)
+ called the _rich_repr_ method [TestOutputPlainText]
+ TestOutputPlainText container
+"""
+
+#*****************************************************************************
+# 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/
+#*****************************************************************************
+
+
+from sage.structure.sage_object import SageObject
+from sage.repl.rich_output.backend_base import BackendBase
+from sage.repl.rich_output.output_catalog import OutputPlainText, OutputImagePng
+
+
+class TestOutputPlainText(OutputPlainText):
+
+ def __init__(self, *args, **kwds):
+ """
+ Backend-specific subclass of the plain text output container.
+
+ Backends must not influence how the display system constucts
+ output containers, they can only control how the output
+ container is displayed. In particular, we cannot override the
+ constructor (only the
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`
+ constructor is used).
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_test import TestOutputPlainText
+ sage: TestOutputPlainText()
+ Traceback (most recent call last):
+ AssertionError: cannot override constructor
+ """
+ raise AssertionError('cannot override constructor')
+
+ def print_to_stdout(self):
+ """
+ Write the data to stdout.
+
+ This is just a convenience method to help with debugging.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: test_output = dm.displayhook(123)
+ 123 [TestOutputPlainText]
+ sage: type(test_output)
+ <class 'sage.repl.rich_output.backend_test.TestOutputPlainText'>
+ sage: test_output.print_to_stdout()
+ 123 [TestOutputPlainText]
+ """
+ print('{0} [{1}]'.format(self.text.get(), self.__class__.__name__))
+
+
+class TestObject(SageObject):
+ """
+ Test object with both :meth:`_repr_` and :meth:`_rich_repr_`
+ """
+
+ def _repr_(self):
+ """
+ Return string representation
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_test import TestObject
+ sage: obj = TestObject()
+ sage: obj._repr_()
+ 'called the _repr_ method'
+ """
+ return 'called the _repr_ method'
+
+ def _rich_repr_(self, display_manager):
+ """
+ Rich Output Magic Method
+
+ See :mod:`sage.repl.rich_output` for details.
+
+ EXAMPLES::
+
+ sage: display_manager = sage.repl.rich_output.get_display_manager()
+ sage: from sage.repl.rich_output.backend_test import TestObject
+ sage: obj = TestObject()
+ sage: rich_output = obj._rich_repr_(display_manager); rich_output
+ OutputPlainText container
+ sage: rich_output.text.get()
+ 'called the _rich_repr_ method'
+ """
+ tp = display_manager.types
+ if tp.OutputPlainText in display_manager.supported_output():
+ return tp.OutputPlainText('called the _rich_repr_ method')
+
+
+class BackendTest(BackendBase):
+
+ def _repr_(self):
+ """
+ Return the string representation
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: display_manager = sage.repl.rich_output.get_display_manager()
+ sage: backend = display_manager._backend
+ sage: backend._repr_()
+ 'test'
+ """
+ return 'test'
+
+ def supported_output(self):
+ """
+ Return the outputs that are supported by the backend.
+
+ OUTPUT:
+
+ Iterable of output container classes. Only the
+ :class:`~sage.repl.rich_repr.backend_test.TestOutputPlainText`
+ output container is supported by the test backend.
+
+ EXAMPLES::
+
+ sage: display_manager = sage.repl.rich_output.get_display_manager()
+ sage: backend = display_manager._backend
+ sage: backend.supported_output()
+ set([<class 'sage.repl.rich_output.backend_test.TestOutputPlainText'>])
+
+ The output of this method is used by the display manager to
+ set up the actual supported outputs. Compare::
+
+ sage: display_manager.supported_output()
+ frozenset([<class 'sage.repl.rich_output.output_basic.OutputPlainText'>])
+ """
+ return set([TestOutputPlainText])
+
+ def display_immediately(self, plain_text, rich_output):
+ """
+ Show output without going back to the command line prompt.
+
+ INPUT:
+
+ Same as :meth:`displayhook`.
+
+ OUTPUT:
+
+ This method returns the rich output for doctesting
+ convenience. The actual display framework ignores the return
+ value.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_basic import OutputPlainText
+ sage: plain_text = OutputPlainText.example()
+ sage: from sage.repl.rich_output.backend_test import BackendTest
+ sage: backend = BackendTest()
+ sage: backend.display_immediately(plain_text, plain_text)
+ Example plain text output
+ OutputPlainText container
+ """
+ plain_text.print_to_stdout()
+ if plain_text is not rich_output:
+ print('rich output type: [{0}]'.format(rich_output.__class__.__name__))
+ return rich_output
diff --git a/src/sage/repl/rich_output/buffer.py b/src/sage/repl/rich_output/buffer.py
new file mode 100644
index 0000000..bc82a7e
--- /dev/null
+++ b/src/sage/repl/rich_output/buffer.py
@@ -0,0 +1,254 @@
+# -*- encoding: utf-8 -*-
+r"""
+Output Buffer
+
+This is the fundamental unit of rich output, a single immutable buffer
+(either in-memory or as a file). Rich output always consists of one or
+more buffers. Ideally, the Sage library always uses the buffer object
+as an in-memory buffer. But you can also ask it for a filename, and it
+will save the data to a file if necessary. Either way, the buffer
+object presents the same interface for getting the content of an
+in-memory buffer or a temporary file. So any rich output backends do
+not need to know where the buffer content is actually stored.
+
+EXAMPLES::
+
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
+ sage: buf = OutputBuffer('this is the buffer content'); buf
+ buffer containing 26 bytes
+ sage: buf.get()
+ 'this is the buffer content'
+"""
+
+#*****************************************************************************
+# 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
+from sage.structure.sage_object import SageObject
+
+
+class OutputBuffer(SageObject):
+
+ def __init__(self, data):
+ """
+ Data stored either in memory or as a file
+
+ This class is an abstraction for "files", in that they can
+ either be defined by a bytes array (Python 3) or string
+ (Python 2) or by a file (see :meth:`from_file`).
+
+ INPUT:
+
+ - ``data`` -- bytes. The data that is stored in the buffer.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
+ sage: buf = OutputBuffer('this is the buffer content'); buf
+ buffer containing 26 bytes
+
+ sage: buf2 = OutputBuffer(buf); buf2
+ buffer containing 26 bytes
+
+ sage: buf.get()
+ 'this is the buffer content'
+ sage: buf.filename(ext='.txt')
+ '/....txt'
+ """
+ if isinstance(data, OutputBuffer):
+ self._filename = data._filename
+ self._data = data._data
+ else:
+ self._filename = None
+ self._data = bytes(data)
+
+ @classmethod
+ def from_file(cls, filename):
+ """
+ Construct buffer from data in file.
+
+ .. warning::
+
+ The buffer assumes that the file content remains the same
+ during the lifetime of the Sage session. To communicate
+ this to the user, the file permissions will be changed to
+ read only.
+
+ INPUT:
+
+ - ``filename`` -- string. The filename under which the data is
+ stored.
+
+ OUTPUT:
+
+ String containing the buffer data.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
+ sage: name = sage.misc.temporary_file.tmp_filename()
+ sage: with open(name, 'w') as f:
+ ....: f.write('file content')
+ sage: buf = OutputBuffer.from_file(name); buf
+ buffer containing 12 bytes
+
+ sage: buf.filename() == name
+ True
+ sage: buf.get()
+ 'file content'
+ """
+ buf = cls.__new__(cls)
+ buf._filename = filename
+ buf._data = None
+ buf._chmod_readonly(buf._filename)
+ return buf
+
+ @classmethod
+ def _chmod_readonly(cls, filename):
+ """
+ Make file readonly
+
+ INPUT:
+
+ - ``filename`` -- string. Name of an already-existing file.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
+ sage: tmp = sage.misc.temporary_file.tmp_filename()
+ sage: with open(tmp, 'w') as f:
+ ....: f.write('file content')
+ sage: OutputBuffer._chmod_readonly(tmp)
+ sage: import os, stat
+ sage: stat.S_IMODE(os.stat(tmp).st_mode) & (stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
+ 0
+ """
+ import os
+ import stat
+ mode = os.stat(filename).st_mode
+ mode = stat.S_IMODE(mode) & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
+ os.chmod(filename, mode)
+
+ def _repr_(self):
+ """
+ Return a string representation
+
+ OUTPUT:
+
+ String
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
+ sage: OutputBuffer('test1234')
+ buffer containing 8 bytes
+ """
+ return 'buffer containing {0} bytes'.format(len(self.get()))
+
+ def get(self):
+ """
+ Return the buffer content
+
+ OUTPUT:
+
+ Bytes. A string in Python 2.x.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
+ sage: OutputBuffer('test1234').get()
+ 'test1234'
+ """
+ if self._data is None:
+ with open(self._filename) as f:
+ self._data = f.read()
+ return self._data
+
+ def filename(self, ext=None):
+ """
+ Return the filename.
+
+ INPUT:
+
+ - ``ext`` -- string. The file extension.
+
+ OUTPUT:
+
+ Name of a file, most likely a temporary file. If ``ext`` is
+ specified, the filename will have that extension.
+
+ You must not modify the returned file. Its permissions are set
+ to readonly to help with that.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
+ sage: buf = OutputBuffer('test')
+ sage: buf.filename() # random output
+ '/home/user/.sage/temp/hostname/26085/tmp_RNSfAc'
+
+ sage: os.path.isfile(buf.filename())
+ True
+ sage: buf.filename(ext='txt') # random output
+ '/home/user/.sage/temp/hostname/26085/tmp_Rjjp4V.txt'
+ sage: buf.filename(ext='txt').endswith('.txt')
+ True
+ """
+ if ext is None:
+ ext = ''
+ elif not ext.startswith('.'):
+ ext = '.' + ext
+
+ if self._filename is None or not self._filename.endswith(ext):
+ from sage.misc.temporary_file import tmp_filename
+ output = tmp_filename(ext=ext)
+ else:
+ output = self._filename
+
+ if self._filename is None:
+ assert self._data is not None
+ with open(output, 'wb') as f:
+ f.write(self._data)
+ self._filename = output
+ elif self._filename != output:
+ try:
+ os.link(self._filename, output)
+ except (OSError, AttributeError):
+ import shutil
+ shutil.copy2(self._filename, output)
+
+ self._chmod_readonly(output)
+ return output
+
+ def save_as(self, filename):
+ """
+ Save a copy of the buffer content.
+
+ You may edit the returned file, unlike the file returned by
+ :meth:`filename`.
+
+ INPUT:
+
+ - ``filename`` -- string. The file name to save under.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.buffer import OutputBuffer
+ sage: buf = OutputBuffer('test')
+ sage: buf.filename(ext='txt') # random output
+ sage: tmp = tmp_dir()
+ sage: filename = os.path.join(tmp, 'foo.txt')
+ sage: buf.save_as(filename)
+ sage: with open(filename, 'r') as f:
+ ....: f.read()
+ 'test'
+ """
+ with open(filename, 'w') as f:
+ f.write(self.get())
diff --git a/src/sage/repl/rich_output/display_manager.py b/src/sage/repl/rich_output/display_manager.py
new file mode 100644
index 0000000..92f6868
--- /dev/null
+++ b/src/sage/repl/rich_output/display_manager.py
@@ -0,0 +1,747 @@
+# -*- encoding: utf-8 -*-
+r"""
+Display Manager
+
+This is the heart of the rich output system, the display manager
+arbitrates between
+
+* Backend capabilities: what can be displayed
+
+* Backend preferences: what gives good quality on the backend
+
+* Sage capabilities: every Sage object can only generate certain
+ representations, and
+
+* User preferences: typeset vs. plain text vs. ascii art, etc.
+
+The display manager is a singleton class, Sage always has exactly one
+instance of it. Use :func:`get_display_manager` to obtain it.
+
+EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager(); dm
+ The Sage display manager using the doctest backend
+"""
+
+#*****************************************************************************
+# 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 warnings
+
+from sage.structure.sage_object import SageObject
+from sage.repl.rich_output.output_basic import (
+ OutputPlainText, OutputAsciiArt, OutputLatex,
+)
+
+
+class DisplayException(Exception):
+ """
+ Base exception for all rich output-related exceptions.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.display_manager import DisplayException
+ sage: raise DisplayException('foo')
+ Traceback (most recent call last):
+ ...
+ DisplayException: foo
+ """
+ pass
+
+class OutputTypeException(DisplayException):
+ """
+ Wrong Output container.
+
+ The output containers are the subclasses of
+ :class:`~sage.repl.rich_output.output_basic.OutputBase` that
+ contain the entire output. The display backends must create output
+ containers of a suitable type depending on the displayed Python
+ object. This exception indicates that there is a mistake in the
+ backend and it returned the wrong type of output container.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.display_manager import OutputTypeException
+ sage: raise OutputTypeException('foo')
+ Traceback (most recent call last):
+ ...
+ OutputTypeException: foo
+ """
+ pass
+
+class RichReprWarning(UserWarning):
+ """
+ Warning that is throws if a call to ``_rich_repr_`` fails.
+
+ If an object implements ``_rich_repr_`` then it must return a
+ value, possibly ``None`` to indicate that no rich output can be
+ generated. But it may not raise an exception as it is very
+ confusing for the user if the displayhook fails.
+
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.display_manager import RichReprWarning
+ sage: raise RichReprWarning('foo')
+ Traceback (most recent call last):
+ ...
+ RichReprWarning: foo
+ """
+ pass
+
+
+class restricted_output(object):
+
+ def __init__(self, display_manager, output_classes):
+ """
+ Context manager to temporarily restrict the accepted output types
+
+ INPUT:
+
+ - ``display_manager`` -- the display manager.
+
+ - ``output_classes`` -- iterable of
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.display_manager import (
+ ....: get_display_manager, restricted_output)
+ sage: dm = get_display_manager()
+ sage: restricted_output(dm, [dm.types.OutputPlainText])
+ <sage.repl.rich_output.display_manager.restricted_output object at 0x...>
+ """
+ self._display_manager = display_manager
+ self._output_classes = frozenset(output_classes)
+
+ def __enter__(self):
+ """
+ Enter the restricted output context
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.display_manager import (
+ ....: get_display_manager, restricted_output)
+ sage: dm = get_display_manager()
+ sage: len(dm.supported_output()) > 1
+ True
+ sage: with restricted_output(dm, [dm.types.OutputPlainText]):
+ ....: dm.supported_output()
+ frozenset({<class 'sage.repl.rich_output.output_basic.OutputPlainText'>})
+ """
+ dm = self._display_manager
+ self._original = dm._supported_output
+ dm._supported_output = self._output_classes
+
+ def __exit__(self, exception_type, value, traceback):
+ """
+ Exit the restricted output context
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.display_manager import (
+ ....: get_display_manager, restricted_output)
+ sage: dm = get_display_manager()
+ sage: with restricted_output(dm, [dm.types.OutputPlainText]):
+ ....: assert len(dm.supported_output()) == 1
+ sage: assert len(dm.supported_output()) > 1
+ """
+ self._display_manager._supported_output = self._original
+
+
+
+
+class DisplayManager(SageObject):
+
+ _instance = None
+
+ def __init__(self):
+ """
+ The Display Manager
+
+ Used to decide what kind of rich output is best.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: get_display_manager()
+ The Sage display manager using the doctest backend
+ """
+ assert DisplayManager._instance is None
+ DisplayManager._instance = self
+ from sage.repl.rich_output.backend_base import BackendSimple
+ self.switch_backend(BackendSimple())
+
+ @classmethod
+ def get_instance(cls):
+ """
+ Get the singleton instance.
+
+ This class method is equivalent to
+ :func:`get_display_manager`.
+
+ OUTPUT:
+
+ The display manager singleton.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.display_manager import DisplayManager
+ sage: DisplayManager.get_instance()
+ The Sage display manager using the doctest backend
+ """
+ if cls._instance is not None:
+ return cls._instance
+ else:
+ return cls()
+
+ def _repr_(self):
+ """
+ Return a string representation.
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: get_display_manager()
+ The Sage display manager using the doctest backend
+ """
+ s = 'The Sage display manager using the {0} backend'.format(self._backend)
+ return s
+
+ @property
+ def types(self):
+ """
+ Catalog of all output container types.
+
+ Note that every output type must be registered in
+ :mod:`sage.repl.rich_output.output_catalog`.
+
+ OUTPUT:
+
+ Returns the :mod:`sage.repl.rich_output.output_catalog`
+ module.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm.types.OutputPlainText
+ <class 'sage.repl.rich_output.output_basic.OutputPlainText'>
+ """
+ import sage.repl.rich_output.output_catalog
+ return sage.repl.rich_output.output_catalog
+
+ def switch_backend(self, backend, **kwds):
+ """
+ Switch to a new backend
+
+ INPUT:
+
+ - ``backend`` -- instance of
+ :class:`~sage.repl.rich_output.backend_base.BackendBase`.
+
+ - ``kwds`` -- optional keyword arguments that are passed on to
+ the
+ :meth:`~sage.repl.rich_output.backend_base.BackendBase.install`
+ method.
+
+ OUTPUT:
+
+ The previous backend.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
+ sage: simple = BackendSimple()
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager(); dm
+ The Sage display manager using the doctest backend
+
+ sage: previous = dm.switch_backend(simple)
+ sage: dm
+ The Sage display manager using the simple backend
+
+ Restore the doctest backend::
+
+ sage: dm.switch_backend(previous) is simple
+ True
+ """
+ from sage.repl.rich_output.backend_base import BackendBase
+ if not isinstance(backend, BackendBase):
+ raise ValueError('backend must be instance of BackendBase class')
+ supported = backend.supported_output()
+ if not any(issubclass(out, OutputPlainText) for out in supported):
+ raise ValueError('every backend must support plain text')
+ try:
+ self._backend.uninstall()
+ except AttributeError:
+ pass # first time we switch
+ # clear caches
+ self._output_promotions = dict()
+ self._supported_output = frozenset(
+ map(self._demote_output_class, backend.supported_output()))
+ # install new backend
+ try:
+ old_backend = self._backend
+ except AttributeError:
+ old_backend = None
+ self._backend = backend
+ self._backend.install(**kwds)
+ from sage.repl.rich_output.preferences import DisplayPreferences
+ self._preferences = DisplayPreferences(self._backend.default_preferences())
+ return old_backend
+
+ @property
+ def preferences(self):
+ """
+ Return the preferences.
+
+ OUTPUT:
+
+ The display preferences as instance of
+ :class:`~sage.repl.rich_output.preferences.DisplayPreferences`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm.preferences
+ Display preferences:
+ * graphics is not specified
+ * text is not specified
+ """
+ return self._preferences
+
+ def check_backend_class(self, backend_class):
+ """
+ Check that the current backend is an instance of
+ ``backend_class``.
+
+ This is, for example, used by the Sage IPython display
+ formatter to ensure that the IPython backend is in use.
+
+ INPUT:
+
+ - ``backend_class`` -- type of a backend class.
+
+ OUTPUT:
+
+ This method returns nothing. A ``RuntimeError`` is raised if
+ ``backend_class`` is not the type of the current backend.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.backend_base import BackendSimple
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm.check_backend_class(BackendSimple)
+ Traceback (most recent call last):
+ ...
+ RuntimeError: check failed: current backend is invalid
+ """
+ if not isinstance(self._backend, backend_class):
+ raise RuntimeError('check failed: current backend is invalid')
+
+ def _demote_output_class(self, output_class):
+ """
+ Helper for :meth:`switch_backend`.
+
+ INPUT:
+
+ - ``output_class`` -- a possibly derived class from one of the
+ output container classes in :meth:`types`.
+
+ OUTPUT:
+
+ The underlying container class that it was derived from.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm._demote_output_class(dm.types.OutputPlainText)
+ <class 'sage.repl.rich_output.output_basic.OutputPlainText'>
+ """
+ from sage.repl.rich_output.output_basic import OutputBase
+ if not issubclass(output_class, OutputBase):
+ raise OutputTypeException(
+ 'invalid output container type: {0} is not subclass of OutputBase'
+ .format(output_class))
+ result = None
+ for type_name in dir(self.types):
+ if type_name.startswith('_'):
+ continue
+ tp = getattr(self.types, type_name)
+ if not issubclass(tp, OutputBase):
+ continue
+ if issubclass(output_class, tp):
+ if result is not None:
+ raise OutputTypeException(
+ '{0} inherits from multiple output classes'
+ .format(output_class))
+ else:
+ self._output_promotions[tp] = output_class
+ result = tp
+ if result is None:
+ raise OutputTypeException(
+ '{0} does not inherit from any known output class'
+ .format(output_class))
+ return result
+
+ def _promote_output(self, output):
+ """
+ Promote output container to a backend-specific subclass
+
+ INPUT:
+
+ - ``output`` -- instance of a subclass of
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`. Backend-agnostic
+ output container.
+
+ OUTPUT:
+
+ If the backend returned a subclass of the type of ``output``:
+ promote the class to the subclass and return it. Otherwise,
+ the unchanged output is returned.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: out = dm._promote_output(dm.types.OutputPlainText('test'))
+ sage: type(out)
+ <class 'sage.repl.rich_output.output_basic.OutputPlainText'>
+ """
+ try:
+ specialized_class = self._output_promotions[type(output)]
+ except KeyError:
+ return output
+ output.__class__ = specialized_class
+ return output
+
+ def _preferred_text_formatter(self, obj, plain_text=None):
+ """
+ Return the preferred textual representation
+
+ INPUT:
+
+ - ``obj`` -- anything.
+
+ - ``plain_text`` -- ``None`` (default) or string. The plain
+ text representation. If specified, this will be used for
+ plain text output.
+
+ OUTPUT:
+
+ One of
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`,
+ :class:`~sage.repl.rich_output.output_basic.OutputAsciiArt`,
+ or
+ :class:`~sage.repl.rich_output.output_basic.OutputLatex`
+ containing the preferred textual representation of ``obj``
+ supported by the backend.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm.preferences.text is None
+ True
+ sage: dm._preferred_text_formatter(1/42)
+ OutputPlainText container
+
+ sage: dm.preferences.text = 'plain'
+ sage: dm._preferred_text_formatter(1/42)
+ OutputPlainText container
+
+ sage: dm.preferences.text = 'ascii_art'
+ sage: dm._preferred_text_formatter(1/42)
+ OutputAsciiArt container
+
+ sage: dm.preferences.text = 'latex'
+ sage: dm._preferred_text_formatter(1/42)
+ \newcommand{\Bold}[1]{\mathbf{#1}}\verb|OutputLatex|\phantom{\verb!x!}\verb|container|
+
+ sage: del dm.preferences.text # reset to default
+ """
+ want = self.preferences.text
+ supported = self._backend.supported_output()
+ if want == 'ascii_art' and OutputAsciiArt in supported:
+ out = self._backend.ascii_art_formatter(obj)
+ if type(out) != OutputAsciiArt:
+ raise OutputTypeException('backend returned wrong output type, require AsciiArt')
+ return out
+ if want == 'latex' and OutputLatex in supported:
+ out = self._backend.latex_formatter(obj)
+ if type(out) != OutputLatex:
+ raise OutputTypeException('backend returned wrong output type, require Latex')
+ return out
+ if plain_text is not None:
+ if type(plain_text) != OutputPlainText:
+ raise OutputTypeException('backend returned wrong output type, require PlainText')
+ return plain_text
+ out = self._backend.plain_text_formatter(obj)
+ if type(out) != OutputPlainText:
+ raise OutputTypeException('backend returned wrong output type, require PlainText')
+ return out
+
+ def _call_rich_repr(self, obj, rich_repr_kwds):
+ """
+ Call the ``_rich_repr_`` method
+
+ This method calls ``obj._rich_repr_``. If this raises an
+ exception, it is caught and a suitable warning is displayed.
+
+ INPUT:
+
+ - ``obj`` -- anything.
+
+ - ``rich_repr_kwds`` -- dictionary. Optional keyword arguments
+ that are passed through to ``obj._rich_repr_``.
+
+ OUTPUT:
+
+ Whatever ``_rich_repr_`` returned. If it raises an exception,
+ then a :class:`DisplayFormatterWarning`` is displayed and
+ ``None`` is returned.
+
+ EXAMPLES::
+
+ sage: class Foo(SageObject):
+ ....: def _rich_repr_(self, display_manager, **kwds):
+ ....: raise ValueError('reason')
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm._call_rich_repr(Foo(), {})
+ doctest:...: RichReprWarning: Exception in _rich_repr_ while displaying object: reason
+ """
+ if rich_repr_kwds:
+ # do not ignore errors from invalid options
+ return obj._rich_repr_(self, **rich_repr_kwds)
+ try:
+ return obj._rich_repr_(self)
+ except NotImplementedError as e:
+ # don't warn on NotImplementedErrors
+ return None
+ except Exception as e:
+ warnings.warn(
+ 'Exception in _rich_repr_ while displaying object: {0}'.format(e),
+ RichReprWarning,
+ )
+
+ def _rich_output_formatter(self, obj, rich_repr_kwds):
+ """
+ Generate appropriate rich output containers.
+
+ INPUT:
+
+ - ``obj`` -- anything.
+
+ - ``rich_repr_kwds`` -- dictionary. Optional keyword arguments
+ that are passed through to ``obj._rich_repr_``.
+
+ OUTPUT:
+
+ A pair of rich output containers. The first is plain text,
+ that is, an instance of
+ :class:`~sage.repl.rich_output.output_basic.OutputPlainText`. The
+ second is the best rich output available, subject to the
+ constraints of the backend.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm._rich_output_formatter(1/2, dict())
+ (OutputPlainText container, OutputPlainText container)
+ """
+ rich_output = None
+ plain_text = None
+ has_rich_repr = isinstance(obj, SageObject) and hasattr(obj, '_rich_repr_')
+ if has_rich_repr:
+ rich_output = self._call_rich_repr(obj, rich_repr_kwds)
+ if isinstance(rich_output, OutputPlainText):
+ plain_text = rich_output
+ elif has_rich_repr:
+ with restricted_output(self, [OutputPlainText]):
+ plain_text = self._call_rich_repr(obj, rich_repr_kwds)
+ if plain_text is None:
+ plain_text = self._backend.plain_text_formatter(obj)
+ if rich_output is None:
+ rich_output = self._preferred_text_formatter(obj, plain_text=plain_text)
+ # promote output container types to backend-specific containers
+ plain_text = self._promote_output(plain_text)
+ rich_output = self._promote_output(rich_output)
+ # check that the output container types are valid for the backend
+ supported = self._backend.supported_output()
+ if not (type(plain_text) in supported):
+ raise OutputTypeException(
+ 'text output container not supported: {0}'.format(type(plain_text)))
+ if not (type(rich_output) in supported):
+ raise OutputTypeException(
+ 'output container not supported: {0}'.format(type(rich_output)))
+ return plain_text, rich_output
+
+ def graphics_from_save(self, save_function, save_kwds,
+ file_extension, output_container,
+ figsize=None, dpi=None):
+ r"""
+ Helper to construct graphics.
+
+ This method can be used to simplify the implementation of a
+ ``_rich_repr_`` method of a graphics object if there is
+ already a function to save graphics to a file.
+
+ INPUT:
+
+ - ``save_function`` -- callable that can save graphics to a file
+ and accepts options like
+ :meth:`sage.plot.graphics.Graphics.save`.
+
+ - ``save_kwds`` -- dictionary. Keyword arguments that are
+ passed to the save function.
+
+ - ``file_extension`` -- string starting with ``'.'``. The file
+ extension of the graphics file.
+
+ - ``output_container`` -- subclass of
+ :class:`sage.repl.rich_output.output_basic.OutputBase`. The
+ output container to use. Must be one of the types in
+ :meth:`supported_output`.
+
+ - ``figsize`` -- pair of integers (optional). The desired graphics
+ size in pixels. Suggested, but need not be respected by the
+ output.
+
+ - ``dpi`` -- integer (optional). The desired resolution in dots
+ per inch. Suggested, but need not be respected by the output.
+
+ OUTPUT:
+
+ Return an instance of ``output_container``.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: plt = plot(sin)
+ sage: out = dm.graphics_from_save(plt.save, dict(), '.png', dm.types.OutputImagePng)
+ sage: out
+ OutputImagePng container
+ sage: out.png.get().startswith('\x89PNG')
+ True
+ sage: out.png.filename() # random
+ '/home/user/.sage/temp/localhost.localdomain/23903/tmp_pu5woK.png'
+ """
+ import os
+ if not file_extension.startswith(os.path.extsep):
+ raise ValueError('file_extension must start with a period')
+ if output_container not in self.supported_output():
+ raise OutputTypeException('output_container is not supported by backend')
+ from sage.misc.temporary_file import tmp_filename
+ filename = tmp_filename(ext=file_extension)
+ # Call the save_function with the right arguments
+ kwds = dict(save_kwds)
+ if figsize is not None:
+ kwds['figsize'] = figsize
+ if dpi is not None:
+ kwds['dpi'] = dpi
+ save_function(filename, **kwds)
+ from sage.repl.rich_output.buffer import OutputBuffer
+ buf = OutputBuffer.from_file(filename)
+ return output_container(buf)
+
+ def supported_output(self):
+ """
+ Return the output container classes that can be used.
+
+ OUTPUT:
+
+ Frozen set of subclasses of
+ :class:`~sage.repl.rich_output.output_basic.OutputBase`. If
+ the backend defines derived container classes, this method
+ will always return their base classes.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm.types.OutputPlainText in dm.supported_output()
+ True
+ sage: type(dm.supported_output())
+ <type 'frozenset'>
+ """
+ return self._supported_output
+
+ def displayhook(self, obj):
+ """
+ Implementation of the displayhook
+
+ Every backend must pass the value of the last statement of a
+ line / cell to this method. See also
+ :meth:`display_immediately` if you want do display rich output
+ while a program is running.
+
+ INPUT:
+
+ - ``obj`` -- anything. The object to be shown.
+
+ OUTPUT:
+
+ Returns whatever the backend's displayhook method returned.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm.displayhook(1/2)
+ 1/2
+ """
+ if obj is None:
+ return
+ self._backend.set_underscore_variable(obj)
+ plain_text, rich_output = self._rich_output_formatter(obj, dict())
+ return self._backend.displayhook(plain_text, rich_output)
+
+ def display_immediately(self, obj, **rich_repr_kwds):
+ """
+ Show output without going back to the command line prompt.
+
+ This method must be called to create rich output from an
+ object when we are not returning to the command line prompt,
+ for example during program execution. Typically, it is being
+ called by :meth:`sage.plot.graphics.Graphics.show`.
+
+ INPUT:
+
+ - ``obj`` -- anything. The object to be shown.
+
+ - ``rich_repr_kwds`` -- optional keyword arguments that are
+ passed through to ``obj._rich_repr_``.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output import get_display_manager
+ sage: dm = get_display_manager()
+ sage: dm.display_immediately(1/2)
+ 1/2
+ """
+ plain_text, rich_output = self._rich_output_formatter(obj, rich_repr_kwds)
+ self._backend.display_immediately(plain_text, rich_output)
+
+
+
+
+get_display_manager = DisplayManager.get_instance
diff --git a/src/sage/repl/rich_output/output_basic.py b/src/sage/repl/rich_output/output_basic.py
new file mode 100644
index 0000000..f445071
--- /dev/null
+++ b/src/sage/repl/rich_output/output_basic.py
@@ -0,0 +1,350 @@
+# -*- encoding: utf-8 -*-
+r"""
+Basic Output Types
+
+The Sage rich representation system requires a special container class
+to hold the data for each type of rich output. They all inherit from
+:class:`OutputBase`, though a more typical example is
+:class:`OutputPlainText`. Some output classes consist of more than one
+data buffer, for example jmol or certain animation formats. The output
+class is independent of user preferences and of the display
+backend.
+
+The display backends can define derived classes to attach
+backend-specific display functionality to, for example how to launch a
+viewer. But they must not change how the output container is
+created. To enforce this, the Sage ``_rich_repr_`` magic method will
+only ever see the output class defined here. The display manager will
+promote it to a backend-specific subclass if necessary prior to
+displaying it.
+
+To create new types of output, you must create your own subclass of
+:class:`OutputBase` and register it in
+:mod:`sage.repl.rich_output.output_catalog`.
+
+.. warning::
+
+ All rich output data in sublasses of :class:`OutputBase` must be
+ contained in :class:`~sage.repl.rich_output.buffer.OutputBuffer`
+ instances. You must never reference any files on the local file
+ system, as there is no guarantee that the notebook server and the
+ worker process are on the same computer. Or even share a common
+ file system.
+"""
+
+#*****************************************************************************
+# 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/
+#*****************************************************************************
+
+
+from sage.structure.sage_object import SageObject
+from sage.repl.rich_output.buffer import OutputBuffer
+
+
+class OutputBase(SageObject):
+ """
+ Base class for all rich output containers.
+ """
+
+ def _repr_(self):
+ """
+ Return a string representation.
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_basic import OutputBase
+ sage: output_base = OutputBase()
+ sage: output_base._repr_()
+ 'OutputBase container'
+ """
+ return '{0} container'.format(self.__class__.__name__)
+
+ @classmethod
+ def example(cls):
+ """
+ Construct a sample instance
+
+ This static method is meant for doctests, so they can easily
+ construt an example.
+
+ OUTPUT:
+
+ An instance of the :class:`OutputBase` subclass.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_basic import OutputBase
+ sage: OutputBase.example()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: derived classes must implement this class method
+ """
+ raise NotImplementedError('derived classes must implement this class method')
+
+
+class OutputPlainText(OutputBase):
+
+ def __init__(self, plain_text):
+ """
+ Plain Text Output
+
+ INPUT:
+
+ - ``plain_text`` --
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
+ a string (bytes) can be passed directly which will then be
+ converted into an
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
+ plain text output.
+
+ This should always be exactly the same as the (non-rich)
+ output from the ``_repr_`` method. Every backend object must
+ support plain text output as fallback.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputPlainText
+ sage: OutputPlainText('foo')
+ OutputPlainText container
+ """
+ self.text = OutputBuffer(plain_text)
+
+ @classmethod
+ def example(cls):
+ """
+ Construct a sample plain text output container
+
+ This static method is meant for doctests, so they can easily
+ construt an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputPlainText`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputPlainText
+ sage: OutputPlainText.example()
+ OutputPlainText container
+ sage: OutputPlainText.example().text.get()
+ 'Example plain text output'
+ """
+ return cls('Example plain text output')
+
+ def print_to_stdout(self):
+ """
+ Write the data to stdout.
+
+ This is just a convenience method to help with debugging.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputPlainText
+ sage: plain_text = OutputPlainText.example()
+ sage: plain_text.print_to_stdout()
+ Example plain text output
+ """
+ print(self.text.get())
+
+
+class OutputAsciiArt(OutputBase):
+
+ def __init__(self, ascii_art):
+ """
+ ASCII Art Output
+
+ INPUT:
+
+ - ``ascii_art`` --
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
+ a string (bytes) can be passed directly which will then be
+ converted into an
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Ascii
+ art rendered into a string.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputAsciiArt
+ sage: OutputAsciiArt(':-}')
+ OutputAsciiArt container
+ """
+ self.ascii_art = OutputBuffer(ascii_art)
+
+ @classmethod
+ def example(cls):
+ r"""
+ Construct a sample ascii art output container
+
+ This static method is meant for doctests, so they can easily
+ construt an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputAsciiArt`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputAsciiArt
+ sage: OutputAsciiArt.example()
+ OutputAsciiArt container
+ sage: OutputAsciiArt.example().ascii_art.get()
+ '[ * * * * ]\n[ ** ** * * * * * * ]\n[ ***, * , * , **, ** , *, * , * , * ]'
+ """
+ return cls('[ * * * * ]\n'
+ '[ ** ** * * * * * * ]\n'
+ '[ ***, * , * , **, ** , *, * , * , * ]')
+
+ def print_to_stdout(self):
+ """
+ Write the data to stdout.
+
+ This is just a convenience method to help with debugging.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputAsciiArt
+ sage: ascii_art = OutputAsciiArt.example()
+ sage: ascii_art.print_to_stdout()
+ [ * * * * ]
+ [ ** ** * * * * * * ]
+ [ ***, * , * , **, ** , *, * , * , * ]
+ """
+ print(self.ascii_art.get())
+
+
+class OutputLatex(OutputBase):
+
+ def __init__(self, latex):
+ """
+ LaTeX Output
+
+ .. note::
+
+ The LaTeX commands will only use a subset of LaTeX that
+ can be displayed by MathJax.
+
+ INPUT:
+
+ - ``latex`` --
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
+ a string (bytes) can be passed directly which will then be
+ converted into an
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. String
+ containing the latex equation code. Excludes the surrounding
+ dollar signs / LaTeX equation environment. Also excludes the
+ surrounding MathJax ``<html>`` tag.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
+ sage: OutputLatex('<html><script type="math/tex; mode=display">1</script></html>')
+ OutputLatex container
+ """
+ self.latex = OutputBuffer(latex)
+
+ def mathjax(self):
+ r"""
+ Return the LaTeX with a surrounding MathJax HTML code.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
+ sage: rich_output = OutputLatex('1')
+ sage: rich_output.latex
+ buffer containing 1 bytes
+ sage: rich_output.latex.get()
+ '1'
+ sage: rich_output.mathjax()
+ '<html><script type="math/tex; mode=display">1</script></html>'
+ """
+ return r'<html><script type="math/tex; mode=display">{0}</script></html>'.format(
+ self.latex.get())
+
+ def display_equation(self):
+ r"""
+ Return the LaTeX code for a display equation
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
+ sage: rich_output = OutputLatex('1')
+ sage: rich_output.latex
+ buffer containing 1 bytes
+ sage: rich_output.latex.get()
+ '1'
+ sage: rich_output.display_equation()
+ '\\begin{equation}\n1\n\\end{equation}'
+ """
+ return '\n'.join([r'\begin{equation}', self.latex.get(), r'\end{equation}'])
+
+ def inline_equation(self):
+ r"""
+ Return the LaTeX code for an inline equation
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
+ sage: rich_output = OutputLatex('1')
+ sage: rich_output.latex
+ buffer containing 1 bytes
+ sage: rich_output.latex.get()
+ '1'
+ sage: rich_output.inline_equation()
+ '\\begin{math}\n1\n\\end{math}'
+ """
+ return '\n'.join([r'\begin{math}', self.latex.get(), r'\end{math}'])
+
+ @classmethod
+ def example(cls):
+ r"""
+ Construct a sample LaTeX output container
+
+ This static method is meant for doctests, so they can easily
+ construt an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputLatex`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
+ sage: OutputLatex.example()
+ OutputLatex container
+ sage: OutputLatex.example().latex.get()
+ '\\newcommand{\\Bold}[1]{\\mathbf{#1}}\\int \\sin\\left(x\\right)\\,{d x}'
+ """
+ return cls(r'\newcommand{\Bold}[1]{\mathbf{#1}}'
+ r'\int \sin\left(x\right)\,{d x}')
+
+ def print_to_stdout(self):
+ r"""
+ Write the data to stdout.
+
+ This is just a convenience method to help with debugging.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputLatex
+ sage: rich_output = OutputLatex.example()
+ sage: rich_output.print_to_stdout()
+ \newcommand{\Bold}[1]{\mathbf{#1}}\int \sin\left(x\right)\,{d x}
+ """
+ print(self.latex.get())
diff --git a/src/sage/repl/rich_output/output_catalog.py b/src/sage/repl/rich_output/output_catalog.py
new file mode 100644
index 0000000..533f4d1
--- /dev/null
+++ b/src/sage/repl/rich_output/output_catalog.py
@@ -0,0 +1,37 @@
+# -*- encoding: utf-8 -*-
+r"""
+Catalog of all available output container types.
+
+If you define another output type then you must add it to the imports here.
+"""
+
+#*****************************************************************************
+# 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/
+#*****************************************************************************
+
+
+from .output_basic import (
+ OutputPlainText,
+ OutputAsciiArt,
+ OutputLatex,
+)
+
+from .output_graphics import (
+ OutputImagePng,
+ OutputImageGif,
+ OutputImageJpg,
+ OutputImageSvg,
+ OutputImagePdf,
+ OutputImageDvi,
+)
+
+from .output_graphics3d import (
+ OutputSceneJmol,
+ OutputSceneWavefront,
+ OutputSceneCanvas3d,
+)
diff --git a/src/sage/repl/rich_output/output_graphics.py b/src/sage/repl/rich_output/output_graphics.py
new file mode 100644
index 0000000..c6cc3f4
--- /dev/null
+++ b/src/sage/repl/rich_output/output_graphics.py
@@ -0,0 +1,334 @@
+# -*- encoding: utf-8 -*-
+r"""
+Graphics Output Types
+
+This module defines the rich output types for 2-d images, both vector
+and raster graphics.
+"""
+
+#*****************************************************************************
+# 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
+
+from sage.repl.rich_output.output_basic import OutputBase
+from sage.repl.rich_output.buffer import OutputBuffer
+
+
+class OutputImagePng(OutputBase):
+
+ def __init__(self, png):
+ """
+ PNG Image
+
+ .. NOTE::
+
+ Every backend that is capable of displaying any kind of
+ graphics is supposed to support the PNG format at least.
+
+ INPUT:
+
+ - ``png`` --
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
+ a string (bytes) can be passed directly which will then be
+ converted into an
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
+ PNG image data.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImagePng
+ sage: OutputImagePng.example() # indirect doctest
+ OutputImagePng container
+ """
+ self.png = OutputBuffer(png)
+
+ @classmethod
+ def example(cls):
+ r"""
+ Construct a sample PNG output container
+
+ This static method is meant for doctests, so they can easily
+ construct an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputImagePng`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImagePng
+ sage: OutputImagePng.example()
+ OutputImagePng container
+ sage: OutputImagePng.example().png
+ buffer containing 608 bytes
+ sage: OutputImagePng.example().png.get().startswith('\x89PNG')
+ True
+ """
+ from sage.env import SAGE_EXTCODE
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.png')
+ with open(filename) as f:
+ return cls(f.read())
+
+
+class OutputImageGif(OutputBase):
+
+ def __init__(self, gif):
+ """
+ GIF Image (possibly animated)
+
+ INPUT:
+
+ - ``gif`` --
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
+ a string (bytes) can be passed directly which will then be
+ converted into an
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
+ GIF image data.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImageGif
+ sage: OutputImageGif.example() # indirect doctest
+ OutputImageGif container
+ """
+ self.gif = OutputBuffer(gif)
+
+ @classmethod
+ def example(cls):
+ r"""
+ Construct a sample GIF output container
+
+ This static method is meant for doctests, so they can easily
+ construct an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputImageGif`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImageGif
+ sage: OutputImageGif.example()
+ OutputImageGif container
+ sage: OutputImageGif.example().gif
+ buffer containing 408 bytes
+ sage: OutputImageGif.example().gif.get().startswith('GIF89a')
+ True
+ """
+ from sage.env import SAGE_EXTCODE
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.gif')
+ with open(filename) as f:
+ return cls(f.read())
+
+
+class OutputImageJpg(OutputBase):
+
+ def __init__(self, jpg):
+ """
+ JPEG Image
+
+ INPUT:
+
+ - ``jpeg`` --
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
+ a string (bytes) can be passed directly which will then be
+ converted into an
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
+ JPEG image data.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImageJpg
+ sage: OutputImageJpg.example() # indirect doctest
+ OutputImageJpg container
+ """
+ self.jpg = OutputBuffer(jpg)
+
+ @classmethod
+ def example(cls):
+ r"""
+ Construct a sample JPEG output container
+
+ This static method is meant for doctests, so they can easily
+ construct an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputImageJpg`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImageJpg
+ sage: OutputImageJpg.example()
+ OutputImageJpg container
+ sage: OutputImageJpg.example().jpg
+ buffer containing 978 bytes
+ sage: OutputImageJpg.example().jpg.get().startswith('\xff\xd8\xff\xe0\x00\x10JFIF')
+ True
+ """
+ from sage.env import SAGE_EXTCODE
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.jpg')
+ with open(filename) as f:
+ return cls(f.read())
+
+
+class OutputImageSvg(OutputBase):
+
+ def __init__(self, svg):
+ """
+ SVG Image
+
+ INPUT:
+
+ - ``SVG`` --
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
+ a string (bytes) can be passed directly which will then be
+ converted into an
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
+ SVG image data.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImageSvg
+ sage: OutputImageSvg.example() # indirect doctest
+ OutputImageSvg container
+ """
+ self.svg = OutputBuffer(svg)
+
+ @classmethod
+ def example(cls):
+ r"""
+ Construct a sample SVG output container
+
+ This static method is meant for doctests, so they can easily
+ construct an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputImageSvg`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImageSvg
+ sage: OutputImageSvg.example()
+ OutputImageSvg container
+ sage: OutputImageSvg.example().svg
+ buffer containing 1422 bytes
+ sage: '</svg>' in OutputImageSvg.example().svg.get()
+ True
+ """
+ from sage.env import SAGE_EXTCODE
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.svg')
+ with open(filename) as f:
+ return cls(f.read())
+
+
+class OutputImagePdf(OutputBase):
+
+ def __init__(self, pdf):
+ """
+ PDF Image
+
+ INPUT:
+
+ - ``pdf`` --
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
+ a string (bytes) can be passed directly which will then be
+ converted into an
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
+ PDF data.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImagePdf
+ sage: OutputImagePdf.example() # indirect doctest
+ OutputImagePdf container
+ """
+ self.pdf = OutputBuffer(pdf)
+
+ @classmethod
+ def example(cls):
+ r"""
+ Construct a sample PDF output container
+
+ This static method is meant for doctests, so they can easily
+ construct an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputImagePdf`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImagePdf
+ sage: OutputImagePdf.example()
+ OutputImagePdf container
+ sage: OutputImagePdf.example().pdf
+ buffer containing 4285 bytes
+ sage: OutputImagePdf.example().pdf.get().startswith('%PDF-1.4')
+ True
+ """
+ from sage.env import SAGE_EXTCODE
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.pdf')
+ with open(filename) as f:
+ return cls(f.read())
+
+
+class OutputImageDvi(OutputBase):
+
+ def __init__(self, dvi):
+ """
+ DVI Image
+
+ INPUT:
+
+ - ``dvi`` --
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively,
+ a string (bytes) can be passed directly which will then be
+ converted into an
+ :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The
+ DVI data.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImageDvi
+ sage: OutputImageDvi.example() # indirect doctest
+ OutputImageDvi container
+ """
+ self.dvi = OutputBuffer(dvi)
+
+ @classmethod
+ def example(cls):
+ r"""
+ Construct a sample DVI output container
+
+ This static method is meant for doctests, so they can easily
+ construct an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputImageDvi`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputImageDvi
+ sage: OutputImageDvi.example()
+ OutputImageDvi container
+ sage: OutputImageDvi.example().dvi
+ buffer containing 212 bytes
+ sage: 'TeX output' in OutputImageDvi.example().dvi.get()
+ True
+ """
+ from sage.env import SAGE_EXTCODE
+ filename = os.path.join(SAGE_EXTCODE, 'doctest', 'rich_output', 'example.dvi')
+ with open(filename) as f:
+ return cls(f.read())
+
diff --git a/src/sage/repl/rich_output/output_graphics3d.py b/src/sage/repl/rich_output/output_graphics3d.py
new file mode 100644
index 0000000..e98f90e
--- /dev/null
+++ b/src/sage/repl/rich_output/output_graphics3d.py
@@ -0,0 +1,343 @@
+# -*- encoding: utf-8 -*-
+r"""
+Three-Dimensional Graphics Output Types
+
+This module defines the rich output types for 3-d scenes.
+"""
+
+#*****************************************************************************
+# 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
+
+from sage.repl.rich_output.output_basic import OutputBase
+from sage.repl.rich_output.buffer import OutputBuffer
+
+
+
+class OutputSceneJmol(OutputBase):
+
+ def __init__(self, scene_zip, preview_png):
+ """
+ JMol Scene
+
+ By our (Sage) convention, the actual scene is called ``SCENE``
+ inside the zip archive.
+
+ INPUT:
+
+ - ``scene_zip`` -- string/bytes. The jmol scene (a zip archive).
+
+ - ``preview_png`` -- string/bytes. Preview as png file.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneJmol
+ sage: OutputSceneJmol.example()
+ OutputSceneJmol container
+ """
+ self.scene_zip = OutputBuffer(scene_zip)
+ self.preview_png = OutputBuffer(preview_png)
+
+ def launch_script_filename(self):
+ """
+ Return a launch script suitable to display the scene.
+
+ This method saves the scene to disk and creates a launch
+ script. The latter contains an absolute path to the scene
+ file. The launch script is often necessary to make jmol
+ render the 3d scene.
+
+ OUTPUT:
+
+ String. The file name of a suitable launch script.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneJmol
+ sage: rich_output = OutputSceneJmol.example(); rich_output
+ OutputSceneJmol container
+ sage: filename = rich_output.launch_script_filename(); filename
+ '/.../scene.spt'
+ sage: print(open(filename).read())
+ set defaultdirectory "/.../scene.spt.zip"
+ script SCRIPT
+ """
+ from sage.misc.temporary_file import tmp_dir
+ basedir = tmp_dir()
+ scene_filename = os.path.join(basedir, 'scene.spt.zip')
+ script_filename = os.path.join(basedir, 'scene.spt')
+ self.scene_zip.save_as(scene_filename)
+ with open(script_filename, 'w') as f:
+ f.write('set defaultdirectory "{0}"\n'.format(scene_filename))
+ f.write('script SCRIPT\n')
+ return script_filename
+
+ @classmethod
+ def example(cls):
+ r"""
+ Construct a sample Jmol output container
+
+ This static method is meant for doctests, so they can easily
+ construct an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputSceneJmol`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneJmol
+ sage: rich_output = OutputSceneJmol.example(); rich_output
+ OutputSceneJmol container
+
+ sage: rich_output.scene_zip
+ buffer containing 654 bytes
+ sage: rich_output.scene_zip.get().startswith('PK')
+ True
+
+ sage: rich_output.preview_png
+ buffer containing 608 bytes
+ sage: rich_output.preview_png.get().startswith('\x89PNG')
+ True
+ """
+ from sage.env import SAGE_EXTCODE
+ example_png_filename = os.path.join(
+ SAGE_EXTCODE, 'doctest', 'rich_output', 'example.png')
+ with open(example_png_filename) as f:
+ example_png = f.read()
+ scene_zip_filename = os.path.join(
+ SAGE_EXTCODE, 'doctest', 'rich_output', 'example_jmol.spt.zip')
+ with open(scene_zip_filename) as f:
+ scene_zip = f.read()
+ return cls(scene_zip, example_png)
+
+
+class OutputSceneCanvas3d(OutputBase):
+
+ def __init__(self, canvas3d):
+ """
+ Canvas3d Scene
+
+ INPUT:
+
+ - ``canvas3d`` -- string/bytes. The canvas3d data.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneCanvas3d
+ sage: OutputSceneCanvas3d.example()
+ OutputSceneCanvas3d container
+ """
+ self.canvas3d = OutputBuffer(canvas3d)
+
+ @classmethod
+ def example(cls):
+ r"""
+ Construct a sample Canvas3D output container
+
+ This static method is meant for doctests, so they can easily
+ construct an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputSceneCanvas3d`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneCanvas3d
+ sage: rich_output = OutputSceneCanvas3d.example(); rich_output
+ OutputSceneCanvas3d container
+
+ sage: rich_output.canvas3d
+ buffer containing 649 bytes
+ sage: rich_output.canvas3d.get()
+ "[{vertices:[{x:1,y:1,z:1},...{x:1,y:-1,z:-1}],faces:[[0,1,2,3]],color:'008000'}]"
+ """
+ from sage.env import SAGE_EXTCODE
+ filename = os.path.join(
+ SAGE_EXTCODE, 'doctest', 'rich_output', 'example.canvas3d')
+ return cls(OutputBuffer.from_file(filename))
+
+
+class OutputSceneWavefront(OutputBase):
+
+ def __init__(self, obj, mtl):
+ """
+ Wavefront `*.obj` Scene
+
+ The Wavefront format consists of two files, an ``.obj`` file
+ defining the geometry data (mesh points, normal vectors, ...)
+ together with a ``.mtl`` file defining texture data.
+
+ INPUT:
+
+ - ``obj`` -- bytes. The Wavefront obj file format describing
+ the mesh shape.
+
+ - ``mtl`` -- bytes. The Wavefront mtl file format describing
+ textures.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneWavefront
+ sage: OutputSceneWavefront.example()
+ OutputSceneWavefront container
+ """
+ self.obj = OutputBuffer(obj)
+ self.mtl = OutputBuffer(mtl)
+ self._check_no_directory(self.mtllib())
+
+ def _check_no_directory(self, filename):
+ """
+ Verify that ``filename`` does not contain a path.
+
+ We disallow anything but plain filenames since it is a
+ potential security issue to point :meth:`mtllib` at random
+ paths.
+
+ INPUT:
+
+ - ``filename`` -- string. A filename.
+
+ OUTPUT:
+
+ This method returns nothing. A ``ValueError`` is raised if
+ ``filename`` is not just a plain filename but contains a
+ directory (relative or absolute).
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneWavefront
+ sage: rich_output = OutputSceneWavefront.example()
+ sage: rich_output._check_no_directory('scene.mtl')
+ sage: rich_output._check_no_directory('/scene.mtl')
+ Traceback (most recent call last):
+ ...
+ ValueError: must be pure filename, got directory component: /scene.mtl
+ sage: rich_output._check_no_directory('relative/scene.mtl')
+ Traceback (most recent call last):
+ ...
+ ValueError: must be pure filename, got directory component: relative/scene.mtl
+ sage: rich_output._check_no_directory('/absolute/scene.mtl')
+ Traceback (most recent call last):
+ ...
+ ValueError: must be pure filename, got directory component: /absolute/scene.mtl
+ """
+ if os.path.split(filename)[0]:
+ raise ValueError('must be pure filename, got directory component: {0}'
+ .format(filename))
+
+ def mtllib(self):
+ """
+ Return the ``mtllib`` filename
+
+ The ``mtllib`` line in the Wavefront file format (``*.obj``)
+ is the name of the separate texture file.
+
+ OUTPUT:
+
+ String. The filename under which ``mtl`` is supposed to be
+ saved.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneWavefront
+ sage: rich_output = OutputSceneWavefront.example()
+ sage: rich_output.mtllib()
+ 'scene.mtl'
+ """
+ marker = 'mtllib '
+ for line in self.obj.get().splitlines():
+ if line.startswith(marker):
+ return line[len(marker):]
+ return 'scene.mtl'
+
+ def obj_filename(self):
+ """
+ Return the file name of the ``.obj`` file
+
+ This method saves the object and texture to separate files in
+ a temporary directory and returns the object file name. This
+ is often used to launch a 3d viewer.
+
+ OUTPUT:
+
+ String. The file name (absolute path) of the saved obj file.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneWavefront
+ sage: rich_output = OutputSceneWavefront.example(); rich_output
+ OutputSceneWavefront container
+ sage: obj = rich_output.obj_filename(); obj
+ '/.../scene.obj'
+ sage: print(open(obj).read())
+ mtllib scene.mtl
+ g obj_1
+ ...
+ f 3 2 6 8
+
+ sage: path = os.path.dirname(obj)
+ sage: mtl = os.path.join(path, 'scene.mtl'); mtl
+ '/.../scene.mtl'
+ sage: os.path.exists(mtl)
+ True
+ sage: os.path.dirname(obj) == os.path.dirname(mtl)
+ True
+ sage: print(open(mtl).read())
+ newmtl texture177
+ Ka 0.2 0.2 0.5
+ ...
+ d 1
+ """
+ from sage.misc.temporary_file import tmp_dir
+ basedir = tmp_dir()
+ obj_filename = os.path.join(basedir, 'scene.obj')
+ mtl_filename = os.path.join(basedir, self.mtllib())
+ self.obj.save_as(obj_filename)
+ self.mtl.save_as(mtl_filename)
+ return os.path.abspath(obj_filename)
+
+ @classmethod
+ def example(cls):
+ r"""
+ Construct a sample Canvas3D output container
+
+ This static method is meant for doctests, so they can easily
+ construct an example.
+
+ OUTPUT:
+
+ An instance of :class:`OutputSceneCanvas3d`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.output_catalog import OutputSceneWavefront
+ sage: rich_output = OutputSceneWavefront.example(); rich_output
+ OutputSceneWavefront container
+
+ sage: rich_output.obj
+ buffer containing 227 bytes
+ sage: rich_output.obj.get()
+ 'mtllib scene.mtl\ng obj_1\n...\nf 1 5 6 2\nf 1 4 7 5\nf 6 5 7 8\nf 7 4 3 8\nf 3 2 6 8\n'
+
+ sage: rich_output.mtl
+ buffer containing 80 bytes
+ sage: rich_output.mtl.get()
+ 'newmtl texture177\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1\nd 1\n'
+ """
+ from sage.env import SAGE_EXTCODE
+ with_path = lambda x: os.path.join(
+ SAGE_EXTCODE, 'doctest', 'rich_output', 'example_wavefront', x)
+ return cls(
+ OutputBuffer.from_file(with_path('scene.obj')),
+ OutputBuffer.from_file(with_path('scene.mtl')),
+ )
diff --git a/src/sage/repl/rich_output/preferences.py b/src/sage/repl/rich_output/preferences.py
new file mode 100644
index 0000000..8b8b442
--- /dev/null
+++ b/src/sage/repl/rich_output/preferences.py
@@ -0,0 +1,413 @@
+# -*- encoding: utf-8 -*-
+r"""
+Display Preferences
+
+This class is used to express display preferences that are not simply
+a choice of a particular output format. For example, whether to prefer
+vector over raster graphics. By convention, the value ``None`` is
+always a valid value for a preference and means no particular
+preference.
+
+EXAMPLES::
+
+ sage: from sage.repl.rich_output.preferences import DisplayPreferences
+ sage: prefs = DisplayPreferences()
+ sage: prefs.available_options()
+ (graphics, text)
+ sage: prefs.text is None
+ True
+ sage: prefs.text = 'ascii_art'
+ sage: prefs.text
+ 'ascii_art'
+ sage: prefs
+ Display preferences:
+ * graphics is not specified
+ * text = ascii_art
+
+Properties can be unset by deleting them or by assinging ``None``::
+
+ sage: prefs.text = 'ascii_art'
+ sage: del prefs.text
+ sage: prefs.text is None
+ True
+
+ sage: prefs.text = 'ascii_art'
+ sage: prefs.text = None
+ sage: prefs.text is None
+ True
+
+Properties have documentation attached::
+
+ sage: import pydoc
+ sage: doc = pydoc.render_doc(prefs)
+ sage: assert ' graphics' in doc
+ sage: assert ' Preferred graphics format' in doc
+ sage: assert ' text' in doc
+ sage: assert ' Which textual representation is preferred' in doc
+
+Values can also be specified as keyword arguments to the constructor::
+
+ sage: DisplayPreferences(text='latex')
+ Display preferences:
+ * graphics is not specified
+ * text = latex
+
+.. TODO::
+
+ A value-checking preference system should be used elsewhere in
+ Sage, too. The class here is just a simple implementation, a
+ proper implementation would use a metaclass to construct the
+ preference items.
+"""
+
+#*****************************************************************************
+# 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/
+#*****************************************************************************
+
+
+from textwrap import dedent
+
+from sage.structure.sage_object import SageObject
+
+
+class Property(property):
+
+ def __init__(self, name, allowed_values, doc=None):
+ r"""
+ Preference item
+
+ INPUT:
+
+ - ``name`` -- string. The name of the property.
+
+ - ``allowed_values`` -- list/tuple/iterable of allowed values.
+
+ - ``doc`` -- string (optional). The docstring of the property.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.preferences import Property
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
+ sage: prop.__doc__
+ 'The Foo Property\n\nAllowed values:\n\n* ``None`` (default): no preference\n\n* 0\n\n* 1\n\n* 2'
+ sage: prop.allowed_values
+ (0, 1, 2)
+ """
+ self.name = name
+ self.underscore_name = '_{0}'.format(name)
+ self.allowed_values = tuple(allowed_values)
+ self.__doc__ = doc = self._make_doc(doc)
+ super(Property, self).__init__(
+ fget=self.getter, fset=self.setter, fdel=self.deleter, doc=doc)
+
+ def _make_doc(self, doc):
+ """
+ Generate the documentation.
+
+ INPUT:
+
+ - ``doc`` -- the title line of the documentation.
+
+ OUTPUT:
+
+ String. The docstring with auto-generated documentation about
+ the allowed values added.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.preferences import Property
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
+ sage: print(prop._make_doc('this is the title'))
+ this is the title
+ <BLANKLINE>
+ Allowed values:
+ <BLANKLINE>
+ * ``None`` (default): no preference
+ <BLANKLINE>
+ * 0
+ <BLANKLINE>
+ * 1
+ <BLANKLINE>
+ * 2
+ """
+ doc = dedent(doc)
+ doc += '\n\n'
+ doc += 'Allowed values:\n\n'
+ values_doc = []
+ values_doc.append('* ``None`` (default): no preference')
+ for value in self.allowed_values:
+ values_doc.append('* {0}'.format(repr(value)))
+ return doc + '\n\n'.join(values_doc)
+
+ def __repr__(self):
+ """
+ Return a string representation
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.preferences import Property
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
+ sage: prop.__repr__()
+ 'foo'
+ """
+ return self.name
+
+ def getter(self, prefs):
+ """
+ Get the current value of the property
+
+ INPUT:
+
+ - ``prefs`` -- the :class:`PreferencesABC` instance that the
+ property is bound to.
+
+ OUTPUT:
+
+ One of the allowed values or ``None`` if not set.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.preferences import Property, PreferencesABC
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
+ sage: prefs = PreferencesABC()
+ sage: prop.getter(prefs) is None
+ True
+ sage: prop.setter(prefs, 1)
+ sage: prop.getter(prefs)
+ 1
+ """
+ try:
+ return getattr(prefs, self.underscore_name)
+ except AttributeError:
+ return None
+
+ def setter(self, prefs, value):
+ """
+ Get the current value of the property
+
+ INPUT:
+
+ - ``prefs`` -- the :class:`PreferencesABC` instance that the
+ property is bound to.
+
+ - ``value`` -- anything. The new value of the
+ property. Setting a property to ``None`` is equivalent to
+ deleting the value.
+
+ OUTPUT:
+
+ This method does not return anything. A ``ValueError`` is
+ raised if the given ``value`` is not one of the allowed
+ values.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.preferences import Property, PreferencesABC
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
+ sage: prefs = PreferencesABC()
+ sage: prop.getter(prefs) is None
+ True
+ sage: prop.setter(prefs, 1)
+ sage: prop.getter(prefs)
+ 1
+
+ sage: prop.setter(prefs, None)
+ sage: prop.getter(prefs) is None
+ True
+ """
+ if value is None:
+ return self.deleter(prefs)
+ allowed = self.allowed_values
+ if value not in allowed:
+ raise ValueError('value must be unset (None) or one of {0}, got {1}'
+ .format(allowed, value))
+ setattr(prefs, self.underscore_name, value)
+
+ def deleter(self, prefs):
+ """
+ Delete the current value of the property
+
+ INPUT:
+
+ - ``prefs`` -- the :class:`PreferencesABC` instance that the
+ property is bound to.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.preferences import Property, PreferencesABC
+ sage: prop = Property('foo', [0, 1, 2], 'The Foo Property')
+ sage: prefs = PreferencesABC()
+ sage: prop.getter(prefs) is None
+ True
+ sage: prop.setter(prefs, 1)
+ sage: prop.deleter(prefs)
+ sage: prop.getter(prefs) is None
+ True
+ """
+ underscore_name = self.underscore_name
+ try:
+ delattr(prefs, underscore_name)
+ except AttributeError:
+ pass
+
+
+class PreferencesABC(SageObject):
+
+ def __init__(self, *args, **kwds):
+ """
+ Preferences for displaying graphics
+
+ These can be preferences expressed by the user or by the
+ display backend. They are specified as keyword arguments.
+
+ INPUT:
+
+ - ``*args*`` -- positional arguments are preferences
+ instances. The property values will be inherited from left
+ to right, that is, later parents override values from
+ earlier parents.
+
+ - ``**kwds`` -- keyword arguments. Will be used to initialize
+ properties, and override inherited values if necessary.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.preferences import DisplayPreferences
+ sage: p1 = DisplayPreferences(graphics='vector')
+ sage: p2 = DisplayPreferences(graphics='raster')
+ sage: DisplayPreferences(p1, p2)
+ Display preferences:
+ * graphics = raster
+ * text is not specified
+
+ If specified in the opposite order, the setting from ``p1`` is
+ inherited::
+
+ sage: DisplayPreferences(p2, p1)
+ Display preferences:
+ * graphics = vector
+ * text is not specified
+
+ Further keywords override::
+
+ sage: DisplayPreferences(p2, p1, graphics='disable')
+ Display preferences:
+ * graphics = disable
+ * text is not specified
+ """
+ for parent in args:
+ for option in self.available_options():
+ value = option.getter(parent)
+ if value is not None:
+ option.setter(self, value)
+ for key, value in kwds.items():
+ setattr(self, key, value)
+
+ @classmethod
+ def _add_option(cls, name, values, doc):
+ """
+ Add an option to the preferences system.
+
+ This method should only be called during the import of
+ :mod:`sage.repl.rich_output.preferences`.
+
+ INPUT:
+
+ - ``name`` -- the name of the option.
+
+ - ``values`` -- the allowed values.
+
+ - ``doc`` -- docstring.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.preferences import PreferencesABC
+ sage: class MyPrefs(PreferencesABC):
+ ....: pass
+ sage: MyPrefs._add_option('foo', [0, 1, 2], 'The Foo Option')
+ sage: prefs = MyPrefs()
+ sage: prefs.foo
+ sage: prefs.foo = 0
+ sage: prefs.foo
+ 0
+ """
+ prop = Property(name, values, doc)
+ setattr(cls, name, prop)
+
+ def available_options(self):
+ """
+ Return the available options
+
+ OUTPUT:
+
+ Tuple of the preference items as instances of
+ :class:`Property`.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.preferences import DisplayPreferences
+ sage: DisplayPreferences().available_options()
+ (graphics, text)
+ """
+ options = []
+ for key, value in self.__class__.__dict__.items():
+ if isinstance(value, Property):
+ options.append(value)
+ return tuple(sorted(options, key=str))
+
+ def _repr_(self):
+ r"""
+ Return a string representation
+
+ OUTPUT:
+
+ String.
+
+ EXAMPLES::
+
+ sage: from sage.repl.rich_output.preferences import DisplayPreferences
+ sage: DisplayPreferences()._repr_()
+ 'Display preferences:\n* graphics is not specified\n* text is not specified'
+ """
+ s = ['Display preferences:']
+ for opt in self.available_options():
+ value = opt.getter(self)
+ if value is None:
+ s += ['* {0} is not specified'.format(opt.name)]
+ else:
+ s += ['* {0} = {1}'.format(opt.name, value)]
+ return '\n'.join(s)
+
+
+class DisplayPreferences(PreferencesABC):
+ pass
+
+
+DisplayPreferences._add_option(
+ 'text',
+ ('plain', 'ascii_art', 'latex'),
+ """
+ Which textual representation is preferred
+ """
+)
+
+
+DisplayPreferences._add_option(
+ 'graphics',
+ ('disable', 'vector', 'raster'),
+ """
+ Preferred graphics format
+ """
+)
+
+
+
diff --git a/src/sage/repl/zmq_kernel.py b/src/sage/repl/zmq_kernel.py
deleted file mode 100644
index e5f77c3..0000000
--- a/src/sage/repl/zmq_kernel.py
+++ /dev/null
@@ -1,122 +0,0 @@
-"""
-The Sage ZMQ Kernel
-
-Version of the IPython kernel when running Sage inside the IPython
-notebook or remote IPython sessions.
-"""
-
-from IPython.kernel.zmq.ipkernel import Kernel
-from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
-from IPython.utils.traitlets import Type
-from IPython.core.formatters import DisplayFormatter
-
-from sage.structure.graphics_file import Mime
-from sage.repl.interpreter import SageInteractiveShell
-from sage.repl.display.formatter import SagePlainTextFormatter
-from sage.misc.temporary_file import tmp_filename
-from sage.structure.sage_object import SageObject
-
-
-class SageZMQDisplayFormatter(DisplayFormatter):
-
- def __init__(self, *args, **kwds):
- """
- Override for IPython's display formatter class
-
- IPython always creates all representations of an object, and
- the frontend then picks which one it displays. This would be
- rather ineffective for Sage, where we have numerous output
- graphics formats. We want to only create the most suitable
- output representation, and not png/jpg/pdf/svg/... versions
- for each image.
- """
- shell = kwds['parent']
- self.plain_text = SagePlainTextFormatter(config=shell.config)
-
- _format_types = frozenset([
- Mime.TEXT,
- Mime.HTML,
- Mime.LATEX,
- Mime.JSON,
- Mime.JAVASCRIPT,
- Mime.PDF,
- Mime.PNG,
- Mime.JPG,
- Mime.SVG,
- ])
-
- @property
- def format_types(self):
- """
- Return the enabled format types (MIME types)
-
- OUTPUT:
-
- Set of mime types (as strings).
-
- EXAMPLES::
-
- sage: from sage.repl.zmq_kernel import SageZMQDisplayFormatter
- sage: from sage.repl.interpreter import get_test_shell
- sage: fmt = SageZMQDisplayFormatter(parent=get_test_shell())
- sage: fmt.format_types
- frozenset({u'application/javascript',
- u'application/json',
- u'application/pdf',
- u'image/jpeg',
- u'image/png',
- u'image/svg+xml',
- u'text/html',
- u'text/latex',
- u'text/plain'})
- """
- return self._format_types
-
- # TODO: setter for format_types?
-
- def format(self, obj, include=None, exclude=None):
- """
- Return a format data dict for an object
-
- INPUT:
-
- - ``obj`` -- anything. The object to represent.
-
- - ``include``, ``exclude`` -- IPython mime types to
- include/exclude. (currently ignored)
-
- OUTPUT:
-
- A pair consisting of the representation dictionary and the metadata dictionary.
-
- EXAMPLES::
-
- sage: from sage.repl.zmq_kernel import SageZMQDisplayFormatter
- sage: from sage.repl.interpreter import get_test_shell
- sage: fmt = SageZMQDisplayFormatter(parent=get_test_shell())
- sage: fmt.format(123)
- ({u'text/plain': '123'}, {})
- """
- output = dict()
- if isinstance(obj, SageObject) and hasattr(obj, '_graphics_'):
- gfx = obj._graphics_(mime_types=self.format_types)
- if gfx is not None:
- output[gfx.mime()] = gfx.data()
- if Mime.TEXT not in output:
- output[Mime.TEXT] = self.plain_text(obj)
- return (output, {})
-
-
-class SageZMQInteractiveShell(SageInteractiveShell, ZMQInteractiveShell):
-
- def init_display_formatter(self):
- """
- Use our display formatter instead of the IPython default.
- """
- self.display_formatter = SageZMQDisplayFormatter(parent=self)
- self.configurables.append(self.display_formatter)
-
-
-class SageKernel(Kernel):
- shell_class = Type(SageZMQInteractiveShell)
-
diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx
index a5506b7..12ba423 100644
--- a/src/sage/structure/sage_object.pyx
+++ b/src/sage/structure/sage_object.pyx
@@ -211,8 +211,9 @@ cdef class SageObject:
+---+---+
| 1 | 2 |
+---+---+
- sage: shell.run_cell('%display simple')
+ sage: shell.run_cell('%display plain')
sage: shell.run_cell('Tableaux.global_options.reset()')
+ sage: shell.quit()
TESTS::