Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resolves #25 support alternative Jupyter kernels #29

Merged
merged 1 commit into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ And here's the result:

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

## Document attributes

| Name | Default Value | Mapping |
|----------------------------|---------------|----------------------------------|
| `jupyter-language-name` | `python` | `metadata.language_info.name` |
| `jupyter-language-version` | `3.9.1` | `metadata.language_info.version` |
| `jupyter-kernel-name` | `python3` | `metadata.kernelspec.name` |
| `jupyter-kernel-language` | `python` | `metadata.kernelspec.language` |

**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.
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.

## Notebook file format

This converter generates [Jupyter notebooks] using [Notebook file format](https://nbformat.readthedocs.io/en/latest/format_description.html) version 4.4.
17 changes: 16 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class JupyterConverter {
this.ignoredNodes = []
const languageName = node.getAttribute('jupyter-language-name', 'python')
const languageVersion = node.getAttribute('jupyter-language-version', '3.9.1')
const kernelName = node.getAttribute('jupyter-kernel-name', 'python3')
const kernelLanguage = node.getAttribute('jupyter-kernel-language', 'python')
const blocks = node.getBlocks()
const cells = []
let lastCell = {}
Expand Down Expand Up @@ -49,6 +51,10 @@ class JupyterConverter {
language_info: {
name: languageName,
version: languageVersion
},
kernelspec: {
name: kernelName,
language: kernelLanguage
}
},
nbformat: 4,
Expand Down Expand Up @@ -158,7 +164,16 @@ class JupyterConverter {
const lines = node.lines
const source = lines.map((l) => l + '\n')
const language = node.getAttribute('language')
if (language === 'python' || language === 'py') {
const languageName = node.getDocument().getAttribute('jupyter-language-name', 'python')
let languages
if (languageName === 'python' || languageName === 'py') {
languages = ['python', 'py']
} else if (languageName === 'c++' || languageName === 'cpp') {
languages = ['c++', 'cpp']
} else {
languages = [languageName]
}
if (languages.includes(language)) {
return [{
cell_type: 'code',
execution_count: 0,
Expand Down
122 changes: 122 additions & 0 deletions test/converter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,135 @@ describe('Jupyter converter', () => {
const ipynb = JSON.parse(result)
expect(ipynb.metadata.language_info.name).is.equal('python')
expect(ipynb.metadata.language_info.version).is.equal('2.7.10')
expect(ipynb.metadata.kernelspec.name).is.equal('python3')
expect(ipynb.metadata.kernelspec.language).is.equal('python')
expect(ipynb.cells.length).is.equal(32)
const codeCells = ipynb.cells.filter(cell => cell.cell_type === 'code')
expect(codeCells.length).is.equal(21)
expect(codeCells[0].source.join('')).is.equal(`from py2neo import Graph

graph = Graph()
`)
})
it('should configure language with document attributes', async () => {
const result = asciidoctor.convert(`= Hello World
:jupyter-language-name: c++
:jupyter-language-version: 17

`, { backend: 'jupyter' })
expect(result).is.not.empty()
const ipynb = JSON.parse(result)
expect(ipynb.metadata.language_info.name).is.equal('c++')
expect(ipynb.metadata.language_info.version).is.equal('17')
})
it('should configure kernelspec with document attributes', async () => {
const result = asciidoctor.convert(`= Hello World
:jupyter-language-name: c++
:jupyter-language-version: 17
:jupyter-kernel-name: xcpp17
:jupyter-kernel-language: C++17

`, { backend: 'jupyter' })
expect(result).is.not.empty()
const ipynb = JSON.parse(result)
expect(ipynb.metadata.language_info.name).is.equal('c++')
expect(ipynb.metadata.language_info.version).is.equal('17')
expect(ipynb.metadata.kernelspec.name).is.equal('xcpp17')
expect(ipynb.metadata.kernelspec.language).is.equal('C++17')
})
it('should convert source blocks depending on language name (C++)', async () => {
const result = asciidoctor.convert(`= Hello World
:jupyter-language-name: c++
:jupyter-language-version: 17
:jupyter-kernel-name: xcpp17
:jupyter-kernel-language: C++17

.Python
[source,py]
----
print('hello')
----

.C{pp}
[source,cpp]
----
int i=1;
----
`, { backend: 'jupyter' })
expect(result).is.not.empty()
const ipynb = JSON.parse(result)
expect(ipynb.cells.length).is.equal(2)
expect(ipynb.cells[0].cell_type).is.equal('markdown')
expect(ipynb.cells[0].source.join('')).is.equal(`# Hello World

\`\`\`py
print('hello')
\`\`\``)
expect(ipynb.cells[1].cell_type).is.equal('code')
expect(ipynb.cells[1].source.join('')).is.equal(`int i=1;
`)
})
it('should convert source blocks depending on language name (Python)', async () => {
const result = asciidoctor.convert(`= Hello World
:jupyter-language-name: python
:jupyter-language-version: 3.11.5

.Python
[source,py]
----
print('hello')
----

.C{pp}
[source,cpp]
----
int i=1;
----
`, { backend: 'jupyter' })
expect(result).is.not.empty()
const ipynb = JSON.parse(result)
expect(ipynb.cells.length).is.equal(3)
expect(ipynb.cells[0].cell_type).is.equal('markdown')
expect(ipynb.cells[0].source.join('')).is.equal(`# Hello World

`)
expect(ipynb.cells[1].cell_type).is.equal('code')
expect(ipynb.cells[1].source.join('')).is.equal(`print('hello')
`)
expect(ipynb.cells[2].cell_type).is.equal('markdown')
expect(ipynb.cells[2].source.join('')).is.equal(`\`\`\`cpp
int i=1;
\`\`\``)
})
it('should convert source blocks depending on language name (default -> Python)', async () => {
const result = asciidoctor.convert(`= Hello World

.Python
[source,py]
----
print('hello')
----

.C{pp}
[source,cpp]
----
int i=1;
----
`, { backend: 'jupyter' })
expect(result).is.not.empty()
const ipynb = JSON.parse(result)
expect(ipynb.cells.length).is.equal(3)
expect(ipynb.cells[0].cell_type).is.equal('markdown')
expect(ipynb.cells[0].source.join('')).is.equal(`# Hello World

`)
expect(ipynb.cells[1].cell_type).is.equal('code')
expect(ipynb.cells[1].source.join('')).is.equal(`print('hello')
`)
expect(ipynb.cells[2].cell_type).is.equal('markdown')
expect(ipynb.cells[2].source.join('')).is.equal(`\`\`\`cpp
int i=1;
\`\`\``)
})
it('should convert an exercise guide to ipynb', async () => {
const inputFile = path.join(__dirname, 'fixtures', 'intro-neo4j-guides-01.adoc')
Expand Down
Loading