From b7e36806895a4ae2044ce9ddca6a5c68969bb595 Mon Sep 17 00:00:00 2001 From: Ben Zinberg Date: Wed, 27 Feb 2019 08:56:54 -0500 Subject: [PATCH 1/4] Add Clojure support --- jupytext/languages.py | 1 + tests/test_mirror.py | 7 +++++++ tests/test_read_simple_clojure.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 tests/test_read_simple_clojure.py diff --git a/jupytext/languages.py b/jupytext/languages.py index 12b0b78dc..3eea0b542 100644 --- a/jupytext/languages.py +++ b/jupytext/languages.py @@ -9,6 +9,7 @@ '.jl': {'language': 'julia', 'comment': '#'}, '.cpp': {'language': 'c++', 'comment': '//'}, '.ss': {'language': 'scheme', 'comment': ';;'}, + '.clj': {'language': 'clojure', 'comment': ';;'}, '.sh': {'language': 'bash', 'comment': '#'}, '.q': {'language': 'q', 'comment': '/'}} diff --git a/tests/test_mirror.py b/tests/test_mirror.py index 100efb99d..ccd9e874c 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -113,6 +113,9 @@ def test_ipynb_to_r(nb_file): def test_ipynb_to_scheme(nb_file): assert_conversion_same_as_mirror(nb_file, 'ss', 'ipynb_to_script') +@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_clojure')) +def test_ipynb_to_clojure(nb_file): + assert_conversion_same_as_mirror(nb_file, 'clj', 'ipynb_to_script') @pytest.mark.parametrize('nb_file', list_notebooks('ipynb_bash')) def test_ipynb_to_bash(nb_file): @@ -173,6 +176,10 @@ def test_ipynb_to_cpp_percent(nb_file): def test_ipynb_to_scheme_percent(nb_file): assert_conversion_same_as_mirror(nb_file, 'ss:percent', 'ipynb_to_percent') +@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_clojure')) +def test_ipynb_to_clojure_percent(nb_file): + assert_conversion_same_as_mirror(nb_file, 'clj:percent', 'ipynb_to_percent') + @pytest.mark.parametrize('nb_file', list_notebooks('ipynb_bash')) def test_ipynb_to_bash_percent(nb_file): diff --git a/tests/test_read_simple_clojure.py b/tests/test_read_simple_clojure.py new file mode 100644 index 000000000..84ee10f6f --- /dev/null +++ b/tests/test_read_simple_clojure.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from testfixtures import compare +import jupytext + + +def test_read_simple_file(script=""";; --- +;; title: Simple file +;; --- + +;; Here we have some text +;; And below we have some code + +((fn + [] + (println "Hello World"))) +"""): + nb = jupytext.reads(script, 'clj') + assert len(nb.cells) == 3 + assert nb.cells[0].cell_type == 'raw' + assert nb.cells[0].source == '---\ntitle: Simple file\n---' + assert nb.cells[1].cell_type == 'markdown' + assert nb.cells[1].source == 'Here we have some text\n' \ + 'And below we have some code' + assert nb.cells[2].cell_type == 'code' + compare(nb.cells[2].source, """((fn + [] + (println "Hello World")))""") + + script2 = jupytext.writes(nb, 'clj') + compare(script, script2) From 7b9a988694eb11a54c36bb5388c0deb692986d4c Mon Sep 17 00:00:00 2001 From: Ben Zinberg Date: Wed, 27 Feb 2019 09:04:06 -0500 Subject: [PATCH 2/4] committed the notebooks --- tests/notebooks/ipynb_clojure/html-demo.ipynb | 261 ++++++++++++++++++ .../mirror/ipynb_to_percent/html-demo.clj | 88 ++++++ .../mirror/ipynb_to_script/html-demo.clj | 74 +++++ 3 files changed, 423 insertions(+) create mode 100644 tests/notebooks/ipynb_clojure/html-demo.ipynb create mode 100644 tests/notebooks/mirror/ipynb_to_percent/html-demo.clj create mode 100644 tests/notebooks/mirror/ipynb_to_script/html-demo.clj diff --git a/tests/notebooks/ipynb_clojure/html-demo.ipynb b/tests/notebooks/ipynb_clojure/html-demo.ipynb new file mode 100644 index 000000000..c559f2d28 --- /dev/null +++ b/tests/notebooks/ipynb_clojure/html-demo.ipynb @@ -0,0 +1,261 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Clojupyter Demo\n", + "\n", + "This example notebook is from the [Clojupyter](https://github.com/clojupyter/clojupyter/blob/1637f6b2557f01db1e35bae5389bc38522eefe9a/examples/html-demo.ipynb) project.", + "\n", + "This notebook demonstrates some of the more advanced features of Clojupyter." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Displaying HTML\n", + "\n", + "To display HTML, you'll need to require a clojupyter helper function to change the cell output" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "(require '[clojupyter.misc.display :as display])" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + ">> should print some text\n" + ] + }, + { + "data": { + "text/html": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(println \">> should print some text\")\n", + ";; displaying html\n", + "(display/hiccup-html \n", + " [:ul \n", + " [:li \"a \" [:i \"emphatic\"] \" idea\"]\n", + " [:li \"a \" [:b \"bold\"] \" idea\"]\n", + " [:li \"an \" [:span {:style \"text-decoration: underline;\"} \"important\"] \" idea\"]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also use this to render SVG:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(display/hiccup-html\n", + " [:svg {:height 100 :width 100 :xmlns \"http://www.w3.org/2000/svg\"}\n", + " [:circle {:cx 50 :cy 40 :r 40 :fill \"red\"}]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Adding External Clojure Dependencies \n", + "\n", + "You can fetch external Clojure dependcies using the `clojupyter.misc.helper` namespace. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "(require '[clojupyter.misc.helper :as helper])" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "(helper/add-dependencies '[org.clojure/data.json \"0.2.6\"])\n", + "(require '[clojure.data.json :as json])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"{\\\"a\\\":1,\\\"b\\\":[2,3],\\\"c\\\":\\\"c\\\"}\"" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(json/write-str {:a 1 :b [2, 3] :c \"c\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Adding External Javascript Dependency\n", + "\n", + "Since you can render arbitrary HTML using `display/hiccup-html`, it's pretty easy to use external Javascript libraries to do things like generate charts. Here's an example using [Highcharts](https://www.highcharts.com/).\n", + "\n", + "First, we use a cell to add javascript to the running notebook:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(helper/add-javascript \"https://code.highcharts.com/highcharts.js\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we define a function which takes Clojure data and returns hiccup HTML to display:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "#'user/plot-highchart" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(defn plot-highchart [highchart-json]\n", + " (let [id (str (java.util.UUID/randomUUID))\n", + " code (format \"Highcharts.chart('%s', %s );\" id, (json/write-str highchart-json))]\n", + " (display/hiccup-html \n", + " [:div [:div {:id id :style {:background-color \"red\"}}]\n", + " [:script code]])))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can make generate interactive plots (try hovering over plot):" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(def raw-data (map #(+ (* 22 (+ % (Math/random)) 78)) (range)))\n", + "(def data-1 (take 500 raw-data))\n", + "(def data-2 (take 500 (drop 500 raw-data)))\n", + "\n", + "(plot-highchart {:chart {:type \"line\"}\n", + " :title {:text \"Plot of random data\"}\n", + " :series [{:data data-1} {:data data-2}]})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Clojure", + "language": "clojure", + "name": "clojure" + }, + "language_info": { + "file_extension": ".clj", + "mimetype": "text/x-clojure", + "name": "clojure", + "version": "1.8.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tests/notebooks/mirror/ipynb_to_percent/html-demo.clj b/tests/notebooks/mirror/ipynb_to_percent/html-demo.clj new file mode 100644 index 000000000..688231622 --- /dev/null +++ b/tests/notebooks/mirror/ipynb_to_percent/html-demo.clj @@ -0,0 +1,88 @@ +;; --- +;; jupyter: +;; kernelspec: +;; display_name: Clojure +;; language: clojure +;; name: clojure +;; --- + +;; %% [markdown] +;; # Clojupyter Demo +;; +;; This example notebook is from the [Clojupyter](https://github.com/clojupyter/clojupyter/blob/1637f6b2557f01db1e35bae5389bc38522eefe9a/examples/html-demo.ipynb) project. +;; This notebook demonstrates some of the more advanced features of Clojupyter. + +;; %% [markdown] +;; ## Displaying HTML +;; +;; To display HTML, you'll need to require a clojupyter helper function to change the cell output + +;; %% +(require '[clojupyter.misc.display :as display]) + +;; %% +(println ">> should print some text") +;; displaying html +(display/hiccup-html + [:ul + [:li "a " [:i "emphatic"] " idea"] + [:li "a " [:b "bold"] " idea"] + [:li "an " [:span {:style "text-decoration: underline;"} "important"] " idea"]]) + +;; %% [markdown] +;; We can also use this to render SVG: + +;; %% +(display/hiccup-html + [:svg {:height 100 :width 100 :xmlns "http://www.w3.org/2000/svg"} + [:circle {:cx 50 :cy 40 :r 40 :fill "red"}]]) + +;; %% [markdown] +;; ## Adding External Clojure Dependencies +;; +;; You can fetch external Clojure dependcies using the `clojupyter.misc.helper` namespace. + +;; %% +(require '[clojupyter.misc.helper :as helper]) + +;; %% +(helper/add-dependencies '[org.clojure/data.json "0.2.6"]) +(require '[clojure.data.json :as json]) + +;; %% +(json/write-str {:a 1 :b [2, 3] :c "c"}) + +;; %% [markdown] +;; ## Adding External Javascript Dependency +;; +;; Since you can render arbitrary HTML using `display/hiccup-html`, it's pretty easy to use external Javascript libraries to do things like generate charts. Here's an example using [Highcharts](https://www.highcharts.com/). +;; +;; First, we use a cell to add javascript to the running notebook: + +;; %% +(helper/add-javascript "https://code.highcharts.com/highcharts.js") + +;; %% [markdown] +;; Now we define a function which takes Clojure data and returns hiccup HTML to display: + +;; %% +(defn plot-highchart [highchart-json] + (let [id (str (java.util.UUID/randomUUID)) + code (format "Highcharts.chart('%s', %s );" id, (json/write-str highchart-json))] + (display/hiccup-html + [:div [:div {:id id :style {:background-color "red"}}] + [:script code]]))) + +;; %% [markdown] +;; Now we can make generate interactive plots (try hovering over plot): + +;; %% +(def raw-data (map #(+ (* 22 (+ % (Math/random)) 78)) (range))) +(def data-1 (take 500 raw-data)) +(def data-2 (take 500 (drop 500 raw-data))) + +(plot-highchart {:chart {:type "line"} + :title {:text "Plot of random data"} + :series [{:data data-1} {:data data-2}]}) + +;; %% diff --git a/tests/notebooks/mirror/ipynb_to_script/html-demo.clj b/tests/notebooks/mirror/ipynb_to_script/html-demo.clj new file mode 100644 index 000000000..54514ef7c --- /dev/null +++ b/tests/notebooks/mirror/ipynb_to_script/html-demo.clj @@ -0,0 +1,74 @@ +;; --- +;; jupyter: +;; kernelspec: +;; display_name: Clojure +;; language: clojure +;; name: clojure +;; --- + +;; # Clojupyter Demo +;; +;; This example notebook is from the [Clojupyter](https://github.com/clojupyter/clojupyter/blob/1637f6b2557f01db1e35bae5389bc38522eefe9a/examples/html-demo.ipynb) project. +;; This notebook demonstrates some of the more advanced features of Clojupyter. + +;; ## Displaying HTML +;; +;; To display HTML, you'll need to require a clojupyter helper function to change the cell output + +(require '[clojupyter.misc.display :as display]) + +(println ">> should print some text") +;; displaying html +(display/hiccup-html + [:ul + [:li "a " [:i "emphatic"] " idea"] + [:li "a " [:b "bold"] " idea"] + [:li "an " [:span {:style "text-decoration: underline;"} "important"] " idea"]]) + +;; We can also use this to render SVG: + +(display/hiccup-html + [:svg {:height 100 :width 100 :xmlns "http://www.w3.org/2000/svg"} + [:circle {:cx 50 :cy 40 :r 40 :fill "red"}]]) + +;; ## Adding External Clojure Dependencies +;; +;; You can fetch external Clojure dependcies using the `clojupyter.misc.helper` namespace. + +(require '[clojupyter.misc.helper :as helper]) + +(helper/add-dependencies '[org.clojure/data.json "0.2.6"]) +(require '[clojure.data.json :as json]) + +(json/write-str {:a 1 :b [2, 3] :c "c"}) + +;; ## Adding External Javascript Dependency +;; +;; Since you can render arbitrary HTML using `display/hiccup-html`, it's pretty easy to use external Javascript libraries to do things like generate charts. Here's an example using [Highcharts](https://www.highcharts.com/). +;; +;; First, we use a cell to add javascript to the running notebook: + +(helper/add-javascript "https://code.highcharts.com/highcharts.js") + +;; Now we define a function which takes Clojure data and returns hiccup HTML to display: + +(defn plot-highchart [highchart-json] + (let [id (str (java.util.UUID/randomUUID)) + code (format "Highcharts.chart('%s', %s );" id, (json/write-str highchart-json))] + (display/hiccup-html + [:div [:div {:id id :style {:background-color "red"}}] + [:script code]]))) + +;; Now we can make generate interactive plots (try hovering over plot): + +;; + +(def raw-data (map #(+ (* 22 (+ % (Math/random)) 78)) (range))) +(def data-1 (take 500 raw-data)) +(def data-2 (take 500 (drop 500 raw-data))) + +(plot-highchart {:chart {:type "line"} + :title {:text "Plot of random data"} + :series [{:data data-1} {:data data-2}]}) +;; - + + From 6f9694210a07353ca4259d2ae511bff23855f243 Mon Sep 17 00:00:00 2001 From: Ben Zinberg Date: Wed, 27 Feb 2019 09:20:39 -0500 Subject: [PATCH 3/4] fix lint error --- tests/test_mirror.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_mirror.py b/tests/test_mirror.py index ccd9e874c..a3a59a097 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -113,10 +113,12 @@ def test_ipynb_to_r(nb_file): def test_ipynb_to_scheme(nb_file): assert_conversion_same_as_mirror(nb_file, 'ss', 'ipynb_to_script') + @pytest.mark.parametrize('nb_file', list_notebooks('ipynb_clojure')) def test_ipynb_to_clojure(nb_file): assert_conversion_same_as_mirror(nb_file, 'clj', 'ipynb_to_script') + @pytest.mark.parametrize('nb_file', list_notebooks('ipynb_bash')) def test_ipynb_to_bash(nb_file): assert_conversion_same_as_mirror(nb_file, 'sh', 'ipynb_to_script') @@ -176,6 +178,7 @@ def test_ipynb_to_cpp_percent(nb_file): def test_ipynb_to_scheme_percent(nb_file): assert_conversion_same_as_mirror(nb_file, 'ss:percent', 'ipynb_to_percent') + @pytest.mark.parametrize('nb_file', list_notebooks('ipynb_clojure')) def test_ipynb_to_clojure_percent(nb_file): assert_conversion_same_as_mirror(nb_file, 'clj:percent', 'ipynb_to_percent') From 09584e7a47706b39a684f5359a0741b9d4dbb635 Mon Sep 17 00:00:00 2001 From: Ben Zinberg Date: Wed, 27 Feb 2019 09:29:55 -0500 Subject: [PATCH 4/4] Mention Clojure support in the readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8af2c0fe2..db5f701be 100755 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Have you always wished Jupyter notebooks were plain text documents? Wished you c Jupytext can save Jupyter notebooks as - Markdown and R Markdown documents, -- Julia, Python, R, Bash, Scheme, C++ and q/kdb+ scripts. +- Julia, Python, R, Bash, Scheme, Clojure, C++ and q/kdb+ scripts. There are multiple ways to use `jupytext`: - Directly from Jupyter Notebook or JupyterLab. Jupytext provides a _contents manager_ that allows Jupyter to save your notebook to your favorite format (`.py`, `.R`, `.jl`, `.md`, `.Rmd`...) in addition to (or in place of) the traditional `.ipynb` file. The text representation can be edited in your favorite editor. When you're done, refresh the notebook in Jupyter: inputs cells are loaded from the text file, while output cells are reloaded from the `.ipynb` file if present. Refreshing preserves kernel variables, so you can resume your work in the notebook and run the modified cells without having to rerun the notebook in full. @@ -347,7 +347,7 @@ The `light` format has: - Code cells are exported verbatim (except for Jupyter magics, which are commented), and separated with blank lines. Code cells are reconstructed from consistent Python paragraphs (no function, class or multiline comment will be broken). - Cells that contain more than one Python paragraphs need an explicit start-of-cell delimiter `# +` (`// +` in C++, etc). Cells that have explicit metadata have a cell header `# + {JSON}` where the metadata is represented, in JSON format. The end of cell delimiter is `# -`, and is omitted when followed by another explicit start of cell marker. -The `light` format is currently available for Python, Julia, R, Bash, Scheme, C++ and q/kdb+. Open our sample notebook in the `light` format [here](https://github.com/mwouts/jupytext/blob/master/demo/World%20population.lgt.py). +The `light` format is currently available for Python, Julia, R, Bash, Scheme, Clojure, C++ and q/kdb+. Open our sample notebook in the `light` format [here](https://github.com/mwouts/jupytext/blob/master/demo/World%20population.lgt.py). A variation of the `light` format is the `bare` format, with no cell marker at all. Please note that this format will split your code cells on code paragraphs. By default, this format still includes a YAML header - if you prefer to also remove the header, set `"notebook_metadata_filter": "-all"` in the jupytext section of your notebook metadata. @@ -368,7 +368,7 @@ where cell type is either omitted (code cells), or `[markdown]` or `[raw]`. The Percent scripts created by Jupytext have a header with an explicit format information. The format of scripts with no header is inferred automatically: scripts with at least one `# %%` cell are identified as `percent` scripts. Scripts with at least one double percent cell, and an uncommented Jupyter magic command, are identified as `hydrogen` scripts. -The `percent` format is currently available for Python, Julia, R, Bash, Scheme, C++ and q/kdb+. Open our sample notebook in the `percent` format [here](https://github.com/mwouts/jupytext/blob/master/demo/World%20population.pct.py). +The `percent` format is currently available for Python, Julia, R, Bash, Scheme, Clojure, C++ and q/kdb+. Open our sample notebook in the `percent` format [here](https://github.com/mwouts/jupytext/blob/master/demo/World%20population.pct.py). If the `percent` format is your favorite one, add the following to your `.jupyter/jupyter_notebook_config.py` file: ```python