forked from nthiery/rst-to-ipynb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rst2ipynb
134 lines (108 loc) · 4.38 KB
/
rst2ipynb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env python
"""
WHAT THIS FILE DOES:
converts an RST file to a .ipynb file
HOW THIS FILE WORKS:
1. calls pandoc to convert between .rst and .md
2. calls notedown to convert between .md and .ipynb
TODO:
* properly parse command line args (escape chars etc)
* Features notedown needs:
* figure emedding
* unicode
DEPS:
* [notedown], [pandoc]
[notedown]:https://github.com/aaren/notedown
[pandoc]:http://pandoc.org
"""
import io
import os
pjoin = os.path.join
import re
import sys
from subprocess import Popen, PIPE
import argparse
import json
parser = argparse.ArgumentParser(description="Convert a reStructuredText (.rst) file to a Jupyter notebook (.ipynb)")
parser.add_argument("input", nargs='?', help="input file (default: read from standard input)")
parser.add_argument("output", nargs='?', help="output file (default: write to standard output)")
parser.add_argument("-o", "--output", help="output file")
parser.add_argument("-k", "--kernel", help="sets the Jupyter kernel in the notebook")
parser.add_argument("-v", "--verbose", action="store_true", help="be verbose")
parser.add_argument("-d", "--debug", action="store_true", help="write debug information and keep temporary .md file")
args = parser.parse_args()
if args.verbose:
print(args.input, args.output)
here = os.path.dirname(__file__)
if args.input:
input_text = io.open(args.input).read()
else:
input_text = sys.stdin.read()
input_text = '\n'.join([
# add default-role: math for sage rst
'.. default-role:: math',
input_text])
# pandoc doesn't properly handle : immediately-following close-`
input_text = input_text.replace('`:', '` :')
# Interpret simple :ref:'s as crosslinks to notebooks in the same directory with the same name
input_text = re.sub(r":ref:`([\w-]*)`",r"`\1 <\1.ipynb>`_", input_text)
input_text = re.sub(r":ref:`([\w -]*) *<([\w-]*)>`",r"`\1 <\2.ipynb>`_", input_text)
# convert rst->markdown with pandoc
if args.verbose:
sys.stderr.write("Calling pandoc to convert from rst to markdown\n")
p = Popen([
'pandoc',
'--filter', pjoin(here, 'rst2ipynb-sageblock-filter'),
'--atx-headers',
'--from', 'rst',
'--to', 'markdown_github+tex_math_dollars+fenced_code_attributes',
], stdout=PIPE, stdin=PIPE)
# pipe_tables are supported by the notebook; don't know why pandoc seem to ignore the option ...
# grid_tables does not seem supported by the notebook
# Using markdown_github requires:
# +tex_math_dollars to handle maths (see tests/math.rst)
# +fenced_code_attributes to handle outputs (see tests/sage_code_blocks.rst)
intermediate_md, _ = p.communicate(input_text.encode('utf8'))
if p.returncode:
sys.exit("pandoc failed: %s" % p.returncode)
intermediate_md = intermediate_md.decode('utf8', 'replace')
# define some math macros for mathjax
intermediate_md = '\n'.join([
# add some sage-defined macros:
'$$',
r'\def\CC{\bf C}',
r'\def\QQ{\bf Q}',
r'\def\RR{\bf R}',
r'\def\ZZ{\bf Z}',
r'\def\NN{\bf N}',
'$$',
intermediate_md])
# Workaround:
# notedown does not handle nicely indented fenced code blocks
# This at least deindents the starting and trailing ```.
regexp = re.compile("^\s*```", flags=re.M)
intermediate_md = regexp.sub("```", intermediate_md)
intermediate_md = intermediate_md.replace("``` {", "```{") # Temporary workaround for older versions of notedown; see: https://github.com/aaren/notedown/issues/29.
# write intermediate markdown for debugging:
if args.debug:
md = args.input.replace(".rst", ".md")
sys.stderr.write("Writing intermediate markdown in %s\n"%md)
with open(md, 'w') as f:
f.write(intermediate_md)
# convert md->ipynb via notedown + postprocess custom kernel
if args.verbose:
sys.stderr.write("Calling notedown to convert from markdown to ipynb\n")
p = Popen(['notedown', '--match=fenced'], stdin=PIPE, stdout=PIPE)
intermediate_ipynb, _ = p.communicate(intermediate_md.encode('utf8'))
if p.returncode:
sys.exit("notedown failed: %s" % p.returncode)
intermediate_ipynb = intermediate_ipynb.decode('utf8', 'replace')
if args.kernel:
worksheet = json.loads(intermediate_ipynb)
worksheet['metadata']['kernelspec'] = {"display_name": args.kernel, 'name': args.kernel}
intermediate_ipynb = json.dumps(worksheet,indent=1)
if args.output:
with open(args.output, 'w') as f:
f.write(intermediate_ipynb)
else:
print(intermediate_ipynb)