Path: blob/master/src/smc_pyutil/smc_pyutil/ipynb2sagews.py
Views: 285
#!/usr/bin/env python31# -*- coding: utf-8 -*-23# This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.4# License: AGPLv3 s.t. "Commons Clause" – read LICENSE.md for details5"""6This script converts an .ipynb jupyter notebook file to an SMC sagews file.7It relies on the sagews command `jupyter()` to instantiate the communication bridge8to the Jupyter kernel.910Authors:1112* Harald Schilly <[email protected]>, started June 201613"""1415from __future__ import print_function16from __future__ import absolute_import17import sys18import os19import codecs20import textwrap21import json22# reading the ipynb via http://nbformat.readthedocs.io/en/latest/api.html23import nbformat2425from smc_pyutil.lib import SagewsCell262728class Ipynb2SageWS(object):29def __init__(self, filename, overwrite=True):30"""31Convert a Jupyter Notebook .ipynb file to a CoCalc .sagews file.3233INPUT:34- ``filename`` -- the name of an ipynb file, say foo.ipynb3536OUTPUT:37- creates a file foo.sagews if it doesn't already exist38"""39self.infile = filename40base = os.path.splitext(filename)[0]41self.outfile = base + '.sagews'42if not overwrite and os.path.exists(self.outfile):43raise Exception(44"%s: Warning --CoCalc worksheet '%s' already exists. Not overwriting.\n"45% (sys.argv[0], self.outfile))4647self.nb = None # holds the notebook data48self.output = None # use self.write([line]) to write to output4950def convert(self):51"""52Main routine53"""54self.read()55self.open()56self.kernel()57self.body()5859def read(self):60"""61Reads the ipynb file, regardless of version, and converts to API version 462"""63self.nb = nbformat.read(self.infile, 4)6465def write(self, line):66if line is not None:67self.output.send(line)6869def open(self):70sys.stdout.write("%s: Creating CoCalc worksheet '%s'\n" %71(sys.argv[0], self.outfile))72sys.stdout.flush()7374def output():75with codecs.open(self.outfile, 'w', 'utf8') as fout:76while True:77cell = yield78if cell is None:79break80fout.write(cell)8182self.output = output()83next(self.output)8485def kernel(self):86"""87The first cell contains a small info text and defines the global jupyter mode,88based on the kernel name in the ipynb file!89"""90spec = self.nb['metadata']91name = spec['kernelspec']['name']92if name.startswith('sage'):93cell = '''\94# This worksheet was converted from a notebook running Jupyter kernel95# version {}.'''.format(name)96else:97cell = '''\98%auto99# This cell automatically evaluates on startup -- or run it manually if it didn't evaluate.100# Here, it initializes the Jupyter kernel with the specified name and sets it as the default mode for this worksheet.101jupyter_kernel = jupyter("{}") # run "jupyter?" for more information.102%default_mode jupyter_kernel'''.format(name)103self.write(SagewsCell(input=textwrap.dedent(cell)).convert())104105def body(self):106"""107Converting all cells of the ipynb as the body of the sagews document.108109see http://nbformat.readthedocs.io/en/latest/format_description.html110"""111112for cell in self.nb.cells:113ct = cell['cell_type']114source = cell.get('source', None)115outputs = cell.get('outputs', [])116117if ct == 'markdown':118self.write(SagewsCell(md=source).convert())119120elif ct == 'code':121self.write(SagewsCell(input=source, outputs=outputs).convert())122123elif ct == 'raw':124self.write(SagewsCell(input=source).convert())125126else:127print("ERROR: cell type '%s' not recognized:\n%s" %128(ct, json.dumps(cell, indent=2)))129130131def main():132if len(sys.argv) == 1:133sys.stderr.write("""134Convert a Jupyter Notebook .ipynb file to a CoCalc .sagews file.135136Usage: %s path/to/filename.ipynb [path/to/filename2.ipynb ...]137138Creates corresponding file path/to/filename.sagews, if it doesn't exist.139""" % sys.argv[0])140sys.exit(1)141142for path in sys.argv[1:]:143Ipynb2SageWS(path).convert()144145146if __name__ == "__main__":147main()148149150