├── .gitignore
├── LICENSE
├── README.md
├── examples.ipynb
└── nim_magic.py
/.gitignore:
--------------------------------------------------------------------------------
1 | nimmagic/
2 | nimcache/
3 | .ipynb_checkpoints/
4 | .vscode/
5 | __pycache__/
6 | nimsuggest.*
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Axel Pahl
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # nim_magic
2 |
3 | Nim cell magic for JupyterLab or Jupyter Python Notebooks.
4 |
5 | Write Nim modules and use the compiled code directly in the Notebook as extension modules for the Python kernel (similar to e.g. %%cython, but for your favorite language :P ). It builds on @yglukhov 's awesome [nimpy](https://github.com/yglukhov/nimpy) library.
6 |
7 | ## Requirements
8 | * A [Nim](https://nim-lang.org) compiler in your path
9 | * [nimpy](https://github.com/yglukhov/nimpy) package (`nimble install nimpy`)
10 |
11 | ## Installation
12 | Just put the file `nim_magic.py` somewhere in Python's import path, e.g. in one of the dirs that is printed by: `python3 -c "import sys; print(sys.path)"`.
13 |
14 | ## Example
15 | In a JupyterLab or Jupyter Notebook running a Python3 kernel:
16 |
17 | ```Python
18 | # In [1]:
19 | %load_ext nim_magic
20 |
21 |
22 | # In [2]:
23 | %%nim -d:release
24 | proc greet(name: string): string {.exportpy.} =
25 | return "Hello, " & name & "!"
26 |
27 |
28 | # In [3]:
29 | greet("World")
30 |
31 |
32 | # Out [3]:
33 | 'Hello, World!'
34 |
35 |
36 | # In [4] (this will remove temporary dirs created by nim_magic):
37 | %nim_clear
38 | ```
39 |
40 | Further examples can be found [here](examples.ipynb).
41 |
42 | And there are some gists, too:
43 | * [Accelerating Pearson](https://gist.github.com/apahl/d673b0033794cc5f9514de639285592b): Directly accessing Numpy arrays.
44 |
--------------------------------------------------------------------------------
/examples.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "metadata": {},
7 | "outputs": [],
8 | "source": [
9 | "%load_ext nim_magic"
10 | ]
11 | },
12 | {
13 | "cell_type": "markdown",
14 | "metadata": {},
15 | "source": [
16 | "## Simple Example"
17 | ]
18 | },
19 | {
20 | "cell_type": "code",
21 | "execution_count": 2,
22 | "metadata": {},
23 | "outputs": [
24 | {
25 | "name": "stdout",
26 | "output_type": "stream",
27 | "text": [
28 | "\n"
29 | ]
30 | }
31 | ],
32 | "source": [
33 | "%%nim -d:release\n",
34 | "proc greet(name: string): string {.exportpy.} =\n",
35 | " return \"Hello, \" & name & \"!\""
36 | ]
37 | },
38 | {
39 | "cell_type": "code",
40 | "execution_count": 3,
41 | "metadata": {},
42 | "outputs": [
43 | {
44 | "data": {
45 | "text/plain": [
46 | "'Hello, World!'"
47 | ]
48 | },
49 | "execution_count": 3,
50 | "metadata": {},
51 | "output_type": "execute_result"
52 | }
53 | ],
54 | "source": [
55 | "greet(\"World\")"
56 | ]
57 | },
58 | {
59 | "cell_type": "markdown",
60 | "metadata": {},
61 | "source": [
62 | "Show that re-importing the module after changes to the source code works:"
63 | ]
64 | },
65 | {
66 | "cell_type": "code",
67 | "execution_count": 4,
68 | "metadata": {},
69 | "outputs": [
70 | {
71 | "name": "stdout",
72 | "output_type": "stream",
73 | "text": [
74 | "\n"
75 | ]
76 | }
77 | ],
78 | "source": [
79 | "%%nim -d:release\n",
80 | "proc greet(name: string): string {.exportpy.} =\n",
81 | " return \"Greetings, \" & name & \"!\""
82 | ]
83 | },
84 | {
85 | "cell_type": "code",
86 | "execution_count": 5,
87 | "metadata": {},
88 | "outputs": [
89 | {
90 | "data": {
91 | "text/plain": [
92 | "'Greetings, World!'"
93 | ]
94 | },
95 | "execution_count": 5,
96 | "metadata": {},
97 | "output_type": "execute_result"
98 | }
99 | ],
100 | "source": [
101 | "greet(\"World\")"
102 | ]
103 | },
104 | {
105 | "cell_type": "markdown",
106 | "metadata": {},
107 | "source": [
108 | "## Larger Example\n",
109 | "Return a list of primes up to the given number.
\n",
110 | "Show that there actually is a speedup\n",
111 | "\n",
112 | "Python version:"
113 | ]
114 | },
115 | {
116 | "cell_type": "code",
117 | "execution_count": 6,
118 | "metadata": {},
119 | "outputs": [],
120 | "source": [
121 | "def py_get_primes(n):\n",
122 | " result = [2, 3]\n",
123 | " curr = 5\n",
124 | " while curr < n:\n",
125 | " is_prime = True\n",
126 | " for i in range(2, curr):\n",
127 | " if curr % i == 0:\n",
128 | " is_prime = False\n",
129 | " break\n",
130 | " if is_prime:\n",
131 | " result.append(curr)\n",
132 | " curr += 2\n",
133 | " return result"
134 | ]
135 | },
136 | {
137 | "cell_type": "markdown",
138 | "metadata": {},
139 | "source": [
140 | "Nim version (using int32):"
141 | ]
142 | },
143 | {
144 | "cell_type": "code",
145 | "execution_count": 7,
146 | "metadata": {},
147 | "outputs": [
148 | {
149 | "name": "stdout",
150 | "output_type": "stream",
151 | "text": [
152 | "\n"
153 | ]
154 | }
155 | ],
156 | "source": [
157 | "%%nim -d:release\n",
158 | "proc nim_get_primes(n: int32): seq[int32] {.exportpy.} =\n",
159 | " result = @[2.int32, 3]\n",
160 | " var curr: int32 = 5\n",
161 | " while curr < n:\n",
162 | " var is_prime = true\n",
163 | " for i in 2.int32 ..< curr:\n",
164 | " if curr mod i == 0:\n",
165 | " is_prime = false\n",
166 | " break\n",
167 | " if is_prime:\n",
168 | " result.add(curr)\n",
169 | " curr += 2"
170 | ]
171 | },
172 | {
173 | "cell_type": "code",
174 | "execution_count": 8,
175 | "metadata": {},
176 | "outputs": [
177 | {
178 | "name": "stdout",
179 | "output_type": "stream",
180 | "text": [
181 | "1229\n"
182 | ]
183 | }
184 | ],
185 | "source": [
186 | "print(len(py_get_primes(10000)))"
187 | ]
188 | },
189 | {
190 | "cell_type": "code",
191 | "execution_count": 9,
192 | "metadata": {},
193 | "outputs": [
194 | {
195 | "name": "stdout",
196 | "output_type": "stream",
197 | "text": [
198 | "1229\n"
199 | ]
200 | }
201 | ],
202 | "source": [
203 | "print(len(nim_get_primes(10000)))"
204 | ]
205 | },
206 | {
207 | "cell_type": "code",
208 | "execution_count": 10,
209 | "metadata": {},
210 | "outputs": [
211 | {
212 | "name": "stdout",
213 | "output_type": "stream",
214 | "text": [
215 | "383 ms ± 3.17 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
216 | ]
217 | }
218 | ],
219 | "source": [
220 | "%timeit py_get_primes(10000)"
221 | ]
222 | },
223 | {
224 | "cell_type": "code",
225 | "execution_count": 11,
226 | "metadata": {},
227 | "outputs": [
228 | {
229 | "name": "stdout",
230 | "output_type": "stream",
231 | "text": [
232 | "18.1 ms ± 231 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
233 | ]
234 | }
235 | ],
236 | "source": [
237 | "%timeit nim_get_primes(10000)"
238 | ]
239 | },
240 | {
241 | "cell_type": "code",
242 | "execution_count": 12,
243 | "metadata": {},
244 | "outputs": [
245 | {
246 | "name": "stdout",
247 | "output_type": "stream",
248 | "text": [
249 | "Removed temporary files used by nim_magic.\n"
250 | ]
251 | }
252 | ],
253 | "source": [
254 | "%nim_clear"
255 | ]
256 | },
257 | {
258 | "cell_type": "code",
259 | "execution_count": null,
260 | "metadata": {},
261 | "outputs": [],
262 | "source": []
263 | }
264 | ],
265 | "metadata": {
266 | "kernelspec": {
267 | "display_name": "Python 3",
268 | "language": "python",
269 | "name": "python3"
270 | },
271 | "language_info": {
272 | "codemirror_mode": {
273 | "name": "ipython",
274 | "version": 3
275 | },
276 | "file_extension": ".py",
277 | "mimetype": "text/x-python",
278 | "name": "python",
279 | "nbconvert_exporter": "python",
280 | "pygments_lexer": "ipython3",
281 | "version": "3.6.5"
282 | }
283 | },
284 | "nbformat": 4,
285 | "nbformat_minor": 2
286 | }
287 |
--------------------------------------------------------------------------------
/nim_magic.py:
--------------------------------------------------------------------------------
1 | """
2 | nim_magic.py
3 | Nim cell magic for JupyterLab or Juypter Python Notebooks.
4 |
5 | Write Nim modules and use the compiled code directly in the Notebook as extension modules for the Python kernel (similar to e.g. %%cython, but for your favorite language :-P ). It builds on @yglukhov 's awesome [nimpy](https://github.com/yglukhov/nimpy) library.
6 |
7 | ## Requirements
8 | * A [Nim](https://nim-lang.org) compiler in your path
9 | * [nimpy](https://github.com/yglukhov/nimpy) package (`nimble install nimpy`)
10 |
11 | ## Installation
12 | Just put the file `nim_magic.py` somewhere in Python's import path.
13 |
14 | ## Example
15 | In a JupyterLab or Jupyter Notebook:
16 |
17 | ```Python
18 | # In [1]:
19 | %load_ext nim_magic
20 |
21 |
22 | # In [2]:
23 | %%nim -d:release
24 | proc greet(name: string): string {.exportpy.} =
25 | return "Hello, " & name & "!"
26 |
27 |
28 | # In [3]:
29 | greet("World")
30 |
31 |
32 | # Out [3]:
33 | 'Hello, World!'
34 |
35 |
36 | # In [4] (this will remove temporary dirs created by nim_magic):
37 | %nim_clear
38 | ```
39 | """
40 |
41 | import os
42 | import shutil
43 | import sys
44 | import time
45 | import subprocess as sp
46 | from IPython.core.magic import (Magics, magics_class,
47 | line_magic, cell_magic)
48 |
49 |
50 | @magics_class
51 | class NimMagics(Magics):
52 |
53 | @line_magic
54 | def nim_clear(self, cmd):
55 | """%nim_clear
56 | will remove the temporary dirs used by nim_magic."""
57 | shutil.rmtree('nimmagic', ignore_errors=True)
58 | print("Removed temporary files used by nim_magic.")
59 |
60 | @cell_magic
61 | def nim(self, options, code):
62 | """`options` can be left empty or contain further compile options, e.g. "-d:release"
63 | (separated by space).
64 |
65 | Example:
66 |
67 | %%nim -d:release
68 | """
69 | if not os.path.isdir("nimmagic"):
70 | os.mkdir("nimmagic")
71 |
72 | glbls = self.shell.user_ns
73 | name = time.strftime("nim%y%m%d%H%M%S")
74 | code = "import nimpy\n\n" + code
75 | ext = "pyd" if sys.platform.startswith('win') else "so"
76 | open("nimmagic/{}.nim".format(name), "w").write(code)
77 | cmd = "nim {1} --hints:off --app:lib --out:nimmagic/{0}.{2} c nimmagic/{0}.nim".format(name, options, ext)
78 | cp = sp.run(cmd, shell=True, check=False, encoding="utf8", stdout=sp.PIPE, stderr=sp.PIPE)
79 | print(cp.stderr)
80 | if cp.returncode == 0:
81 | import_exec = "from nimmagic.{} import *".format(name)
82 | exec(import_exec, glbls)
83 |
84 |
85 | def load_ipython_extension(ipython):
86 | ipython.register_magics(NimMagics)
87 |
--------------------------------------------------------------------------------