CoCalc Blog

Creating Custom "Mode Commands" in Sage Worksheets

• smc

What is a Mode Command?

By default, running a cell in a Sage worksheet causes the input to be run as Sage commands, with output from Sage written to the output of the cell. Mode commands in a Sage worksheet cause the input to be run through some other process to create cell output. For example,

There are many built-in modes (e.g. Cython, GAP, Pari, R, Python, Markdown, HTML, etc…)

Note: If it is not the default mode of your *.sagews worksheet, a mode command must be the first line of a cell. In other words, make sure the command %md, %r, or %HTML is the first line of a cell.

Alternatively, you can make any mode the default for all cells in the worksheet using %default_mode <some_mode>. Then all cells will be using that chosen mode. If you choose this approach, you may still explicitly use %sage for cells you want processed by the Sage interpreter (or %foo to explicitly switch to any non-default mode).

There is an entire section of the FAQ page SageMathCloud Worksheet (and User Interface) Help dedicated to questions about the built-in modes. It had 10 questions-and-answers in it as of July 28, 2016.

Is there a list of all currently supported % modes in SageMathCloud?

You can view available built-in modes by selecting Help > Mode commands in the Sage toolbar while cursor is in a sage cell. That will insert the line print('\n'.join(modes())) into the current cell.

What is a Custom Mode Command?

Custom mode commands are modes defined by the user. Like any mode command, a custom mode command processes the input section of a cell and writes the output. As stated in the help for modes,

Create your own mode command by defining a function that takes a string as input and outputs a string. (Yes, it is that simple.)

Examples of Custom Mode Commands

Custom mode commands can be used to

Here are some examples:

Example 1: View CSV data as a table

Define the mode in a sage cell, as follows:

import pandas as pd
from StringIO import StringIO
def csv_table(str):
    print(pd.read_csv(StringIO((str)),index_col = 0))

Input:

%csv_table
Sample,start,middle,end
A,2,5,51
B,6,8,11
C,7,22,41

Output:

        start  middle  end
Sample
A           2       5   51
B           6       8   11
C           7      22   41

NOTE: Sage’s show command is also aware of Pandas tables, so if you instead define

def csv_table(str):
    show(pd.read_csv(StringIO((str)),index_col = 0))

then %csv_table will produce nice HTML output.

Example 2: View JSON converted to YAML

Define the mode:

import json
import yaml
def j2y(str):
    print(yaml.safe_dump(json.loads(str)))

Input:

%j2y
{
  "foo": "bar",
  "baz": [
    "xyzzy",
    "plugh"
  ]
}

Output:

baz: [xyzzy, plugh]
foo: bar

Example 3: Convert Units of Measure

In this example, each input line is a number with units, possibly followed by target units. If target units are not specified, SI units are the target. This example uses the Sage Units of Measurement package.

Define the mode:

def convert_units(str):
    for line in str.split('\n'):
        if 'units' in line:
            lval = eval(line)
            if isinstance(lval, tuple):
                print(lval[0].convert(lval[1]))
            else:
                print(lval.convert())

Input:

%convert_units

# pounds to kilograms
175.0 * units.mass.pound

# miles to kilometers
3.0 * units.length.mile, units.length.kilometer

# an adult doing moderate exercise might burn 200 kcal per hour
# convert to watts
200.0 * units.energy.calorie * units.si_prefixes.kilo/units.time.hour, units.power.watt

Output:

79.37866475*kilogram
4.828032*kilometer
232.6*watt

Example 4: Display Reverse Complement of Nucleotide Sequences

This example uses Biopython, which is already installed on SageMathCloud.

Define the mode:

from Bio.Seq import Seq
def revcomp(str):
    s = Seq(str)
    print(s.reverse_complement())

Input:

%revcomp
ATGC
GCTCCGACACTTT

Output:

AAAGTGTCGGAGC
GCAT

Example 5: Run Multiple Shell Processes

Suppose you want several bash processes with different working directories or environment variables controlled from the same worksheet. You can use the built-in jupyter command to create several custom modes.

More information on “the sage-jupyter bridge” is available at Sage Jupyter. Code creating a mode for anaconda3 is available by selecting Modes > Jupyter bridge. You can view available Jupyter kernels by selecting Help > Jupyter kernels in the Sage toolbar while cursor is in a sage cell. That will insert the line print(jupyter.available_kernels()) into the current cell.

Define the modes:

sh1 = jupyter("bash")
sh2 = jupyter("bash")

Cell 1:

%sh1
# show PID of current sh process
echo $BASHPID
-- output --
23723

Cell 2:

%sh2
echo $BASHPID
-- output --
23727

Cell 3:

%sh1
echo $BASHPID
-- output --
23723

Example 6: Connect to Remote Server and Run Shell Commands

In this example, any cell in the custom mode consists of shell commands to be run on a remote server. The same session is used for all cells in the given mode.

Notes:

Define the mode:

%sage
from pexpect import pxssh

from ansi2html import Ansi2HTMLConverter
conv = Ansi2HTMLConverter(inline=True, linkify=True)

s = pxssh.pxssh(echo = False)
host   = 'myhost.mydomain.org'
user   = 'joe'

if s.login(host, user):
    def sshexec(code):
        for line in code.split('\n'):
            s.sendline(line)
            s.prompt()
            h = s.before
            h = conv.convert(h, full = False)
            h = '<pre style="font-family:monospace;">'+h+'\n</pre>'
            salvus.html(h)
    print 'sshexec defined; logout with s.logout()'
else:
    print 'sshexec setup failed'

Input:

%sshexec
ls
cd /tmp

Output:

... ls listing, showing color-ls output if available ...

Input in second cell, showing that working directory is retained

%sshexec
pwd

Example 7: Nicely typesetting output from the FriCAS computer algebra system

The following function takes whatever the cell input is, executes the code in FriCAS, performs some simple substitutions on the FriCAS output and then displays it using Markdown:

Define the mode:

%sage
def fricas_tex(s):
    import re
    t = fricas.eval(s)
    t=re.compile(r'\r').sub('',t)
    # mathml overbar
    t=re.compile(r'&#x000AF;').sub('&#x203E;',t,count=0)
    # cleanup FriCAS LaTeX
    t=re.compile(r'\\leqno\(.*\)\n').sub('',t)
    t=re.compile(r'\\sb ').sub('_',t,count=0)
    t=re.compile(r'\\sp ').sub('^',t,count=0)
    md(t, hide=False)

With this mode, FriCAS can generate output that is (almost) compatible with Markdown format.

For example you can use this new mode in a cell with the following input:

%fricas_tex
)set output algebra off
)set output mathml on
)set output tex on
sqrt(2)/2+1

Output: This will evaluate ‘sqrt(2)/2+1’ and display the result in both LaTeX format and MathML formats (in a MathML capable browser).

Note: The current version of Sage (6.6 and earlier) requires a patch to correct a bug in the fricas/axiom interface.