Skip to content

Commit 1dcc8d1

Browse files
authored
resolves #25 support alternative Jupyter kernels (#29)
1 parent 43e8a4b commit 1dcc8d1

File tree

3 files changed

+150
-1
lines changed

3 files changed

+150
-1
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,18 @@ And here's the result:
9696

9797
![](jupyter-lorenz-notebook.png)
9898

99+
## Document attributes
100+
101+
| Name | Default Value | Mapping |
102+
|----------------------------|---------------|----------------------------------|
103+
| `jupyter-language-name` | `python` | `metadata.language_info.name` |
104+
| `jupyter-language-version` | `3.9.1` | `metadata.language_info.version` |
105+
| `jupyter-kernel-name` | `python3` | `metadata.kernelspec.name` |
106+
| `jupyter-kernel-language` | `python` | `metadata.kernelspec.language` |
107+
108+
**IMPORTANT:** The language name defined in `jupyter-language-name` will be used to decide which AsciiDoc source blocks will be converted to Notebook code cells and which will be converted to Markdown cells.
109+
For instance, if the Jupyter language name is `python` the converter will convert source blocks that have the language `python` to code cells. Source blocks with other languages will be converted as Markdown cells.
110+
99111
## Notebook file format
100112

101113
This converter generates [Jupyter notebooks] using [Notebook file format](https://nbformat.readthedocs.io/en/latest/format_description.html) version 4.4.

src/index.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class JupyterConverter {
1414
this.ignoredNodes = []
1515
const languageName = node.getAttribute('jupyter-language-name', 'python')
1616
const languageVersion = node.getAttribute('jupyter-language-version', '3.9.1')
17+
const kernelName = node.getAttribute('jupyter-kernel-name', 'python3')
18+
const kernelLanguage = node.getAttribute('jupyter-kernel-language', 'python')
1719
const blocks = node.getBlocks()
1820
const cells = []
1921
let lastCell = {}
@@ -49,6 +51,10 @@ class JupyterConverter {
4951
language_info: {
5052
name: languageName,
5153
version: languageVersion
54+
},
55+
kernelspec: {
56+
name: kernelName,
57+
language: kernelLanguage
5258
}
5359
},
5460
nbformat: 4,
@@ -158,7 +164,16 @@ class JupyterConverter {
158164
const lines = node.lines
159165
const source = lines.map((l) => l + '\n')
160166
const language = node.getAttribute('language')
161-
if (language === 'python' || language === 'py') {
167+
const languageName = node.getDocument().getAttribute('jupyter-language-name', 'python')
168+
let languages
169+
if (languageName === 'python' || languageName === 'py') {
170+
languages = ['python', 'py']
171+
} else if (languageName === 'c++' || languageName === 'cpp') {
172+
languages = ['c++', 'cpp']
173+
} else {
174+
languages = [languageName]
175+
}
176+
if (languages.includes(language)) {
162177
return [{
163178
cell_type: 'code',
164179
execution_count: 0,

test/converter.spec.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,135 @@ describe('Jupyter converter', () => {
3030
const ipynb = JSON.parse(result)
3131
expect(ipynb.metadata.language_info.name).is.equal('python')
3232
expect(ipynb.metadata.language_info.version).is.equal('2.7.10')
33+
expect(ipynb.metadata.kernelspec.name).is.equal('python3')
34+
expect(ipynb.metadata.kernelspec.language).is.equal('python')
3335
expect(ipynb.cells.length).is.equal(32)
3436
const codeCells = ipynb.cells.filter(cell => cell.cell_type === 'code')
3537
expect(codeCells.length).is.equal(21)
3638
expect(codeCells[0].source.join('')).is.equal(`from py2neo import Graph
3739
3840
graph = Graph()
3941
`)
42+
})
43+
it('should configure language with document attributes', async () => {
44+
const result = asciidoctor.convert(`= Hello World
45+
:jupyter-language-name: c++
46+
:jupyter-language-version: 17
47+
48+
`, { backend: 'jupyter' })
49+
expect(result).is.not.empty()
50+
const ipynb = JSON.parse(result)
51+
expect(ipynb.metadata.language_info.name).is.equal('c++')
52+
expect(ipynb.metadata.language_info.version).is.equal('17')
53+
})
54+
it('should configure kernelspec with document attributes', async () => {
55+
const result = asciidoctor.convert(`= Hello World
56+
:jupyter-language-name: c++
57+
:jupyter-language-version: 17
58+
:jupyter-kernel-name: xcpp17
59+
:jupyter-kernel-language: C++17
60+
61+
`, { backend: 'jupyter' })
62+
expect(result).is.not.empty()
63+
const ipynb = JSON.parse(result)
64+
expect(ipynb.metadata.language_info.name).is.equal('c++')
65+
expect(ipynb.metadata.language_info.version).is.equal('17')
66+
expect(ipynb.metadata.kernelspec.name).is.equal('xcpp17')
67+
expect(ipynb.metadata.kernelspec.language).is.equal('C++17')
68+
})
69+
it('should convert source blocks depending on language name (C++)', async () => {
70+
const result = asciidoctor.convert(`= Hello World
71+
:jupyter-language-name: c++
72+
:jupyter-language-version: 17
73+
:jupyter-kernel-name: xcpp17
74+
:jupyter-kernel-language: C++17
75+
76+
.Python
77+
[source,py]
78+
----
79+
print('hello')
80+
----
81+
82+
.C{pp}
83+
[source,cpp]
84+
----
85+
int i=1;
86+
----
87+
`, { backend: 'jupyter' })
88+
expect(result).is.not.empty()
89+
const ipynb = JSON.parse(result)
90+
expect(ipynb.cells.length).is.equal(2)
91+
expect(ipynb.cells[0].cell_type).is.equal('markdown')
92+
expect(ipynb.cells[0].source.join('')).is.equal(`# Hello World
93+
94+
\`\`\`py
95+
print('hello')
96+
\`\`\``)
97+
expect(ipynb.cells[1].cell_type).is.equal('code')
98+
expect(ipynb.cells[1].source.join('')).is.equal(`int i=1;
99+
`)
100+
})
101+
it('should convert source blocks depending on language name (Python)', async () => {
102+
const result = asciidoctor.convert(`= Hello World
103+
:jupyter-language-name: python
104+
:jupyter-language-version: 3.11.5
105+
106+
.Python
107+
[source,py]
108+
----
109+
print('hello')
110+
----
111+
112+
.C{pp}
113+
[source,cpp]
114+
----
115+
int i=1;
116+
----
117+
`, { backend: 'jupyter' })
118+
expect(result).is.not.empty()
119+
const ipynb = JSON.parse(result)
120+
expect(ipynb.cells.length).is.equal(3)
121+
expect(ipynb.cells[0].cell_type).is.equal('markdown')
122+
expect(ipynb.cells[0].source.join('')).is.equal(`# Hello World
123+
124+
`)
125+
expect(ipynb.cells[1].cell_type).is.equal('code')
126+
expect(ipynb.cells[1].source.join('')).is.equal(`print('hello')
127+
`)
128+
expect(ipynb.cells[2].cell_type).is.equal('markdown')
129+
expect(ipynb.cells[2].source.join('')).is.equal(`\`\`\`cpp
130+
int i=1;
131+
\`\`\``)
132+
})
133+
it('should convert source blocks depending on language name (default -> Python)', async () => {
134+
const result = asciidoctor.convert(`= Hello World
135+
136+
.Python
137+
[source,py]
138+
----
139+
print('hello')
140+
----
141+
142+
.C{pp}
143+
[source,cpp]
144+
----
145+
int i=1;
146+
----
147+
`, { backend: 'jupyter' })
148+
expect(result).is.not.empty()
149+
const ipynb = JSON.parse(result)
150+
expect(ipynb.cells.length).is.equal(3)
151+
expect(ipynb.cells[0].cell_type).is.equal('markdown')
152+
expect(ipynb.cells[0].source.join('')).is.equal(`# Hello World
153+
154+
`)
155+
expect(ipynb.cells[1].cell_type).is.equal('code')
156+
expect(ipynb.cells[1].source.join('')).is.equal(`print('hello')
157+
`)
158+
expect(ipynb.cells[2].cell_type).is.equal('markdown')
159+
expect(ipynb.cells[2].source.join('')).is.equal(`\`\`\`cpp
160+
int i=1;
161+
\`\`\``)
40162
})
41163
it('should convert an exercise guide to ipynb', async () => {
42164
const inputFile = path.join(__dirname, 'fixtures', 'intro-neo4j-guides-01.adoc')

0 commit comments

Comments
 (0)