Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/smc_pyutil/smc_pyutil/ipynb2sagews.py
Views: 285
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
4
# This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
5
# License: AGPLv3 s.t. "Commons Clause" – read LICENSE.md for details
6
"""
7
This script converts an .ipynb jupyter notebook file to an SMC sagews file.
8
It relies on the sagews command `jupyter()` to instantiate the communication bridge
9
to the Jupyter kernel.
10
11
Authors:
12
13
* Harald Schilly <[email protected]>, started June 2016
14
"""
15
16
from __future__ import print_function
17
from __future__ import absolute_import
18
import sys
19
import os
20
import codecs
21
import textwrap
22
import json
23
# reading the ipynb via http://nbformat.readthedocs.io/en/latest/api.html
24
import nbformat
25
26
from smc_pyutil.lib import SagewsCell
27
28
29
class Ipynb2SageWS(object):
30
def __init__(self, filename, overwrite=True):
31
"""
32
Convert a Jupyter Notebook .ipynb file to a CoCalc .sagews file.
33
34
INPUT:
35
- ``filename`` -- the name of an ipynb file, say foo.ipynb
36
37
OUTPUT:
38
- creates a file foo.sagews if it doesn't already exist
39
"""
40
self.infile = filename
41
base = os.path.splitext(filename)[0]
42
self.outfile = base + '.sagews'
43
if not overwrite and os.path.exists(self.outfile):
44
raise Exception(
45
"%s: Warning --CoCalc worksheet '%s' already exists. Not overwriting.\n"
46
% (sys.argv[0], self.outfile))
47
48
self.nb = None # holds the notebook data
49
self.output = None # use self.write([line]) to write to output
50
51
def convert(self):
52
"""
53
Main routine
54
"""
55
self.read()
56
self.open()
57
self.kernel()
58
self.body()
59
60
def read(self):
61
"""
62
Reads the ipynb file, regardless of version, and converts to API version 4
63
"""
64
self.nb = nbformat.read(self.infile, 4)
65
66
def write(self, line):
67
if line is not None:
68
self.output.send(line)
69
70
def open(self):
71
sys.stdout.write("%s: Creating CoCalc worksheet '%s'\n" %
72
(sys.argv[0], self.outfile))
73
sys.stdout.flush()
74
75
def output():
76
with codecs.open(self.outfile, 'w', 'utf8') as fout:
77
while True:
78
cell = yield
79
if cell is None:
80
break
81
fout.write(cell)
82
83
self.output = output()
84
next(self.output)
85
86
def kernel(self):
87
"""
88
The first cell contains a small info text and defines the global jupyter mode,
89
based on the kernel name in the ipynb file!
90
"""
91
spec = self.nb['metadata']
92
name = spec['kernelspec']['name']
93
if name.startswith('sage'):
94
cell = '''\
95
# This worksheet was converted from a notebook running Jupyter kernel
96
# version {}.'''.format(name)
97
else:
98
cell = '''\
99
%auto
100
# This cell automatically evaluates on startup -- or run it manually if it didn't evaluate.
101
# Here, it initializes the Jupyter kernel with the specified name and sets it as the default mode for this worksheet.
102
jupyter_kernel = jupyter("{}") # run "jupyter?" for more information.
103
%default_mode jupyter_kernel'''.format(name)
104
self.write(SagewsCell(input=textwrap.dedent(cell)).convert())
105
106
def body(self):
107
"""
108
Converting all cells of the ipynb as the body of the sagews document.
109
110
see http://nbformat.readthedocs.io/en/latest/format_description.html
111
"""
112
113
for cell in self.nb.cells:
114
ct = cell['cell_type']
115
source = cell.get('source', None)
116
outputs = cell.get('outputs', [])
117
118
if ct == 'markdown':
119
self.write(SagewsCell(md=source).convert())
120
121
elif ct == 'code':
122
self.write(SagewsCell(input=source, outputs=outputs).convert())
123
124
elif ct == 'raw':
125
self.write(SagewsCell(input=source).convert())
126
127
else:
128
print("ERROR: cell type '%s' not recognized:\n%s" %
129
(ct, json.dumps(cell, indent=2)))
130
131
132
def main():
133
if len(sys.argv) == 1:
134
sys.stderr.write("""
135
Convert a Jupyter Notebook .ipynb file to a CoCalc .sagews file.
136
137
Usage: %s path/to/filename.ipynb [path/to/filename2.ipynb ...]
138
139
Creates corresponding file path/to/filename.sagews, if it doesn't exist.
140
""" % sys.argv[0])
141
sys.exit(1)
142
143
for path in sys.argv[1:]:
144
Ipynb2SageWS(path).convert()
145
146
147
if __name__ == "__main__":
148
main()
149
150