├── .gitignore ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── example-notebook.ipynb ├── fortran_spec └── kernel.json ├── jupyter_fortran_kernel ├── __init__.py ├── __main__.py └── kernel.py ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | MANIFEST 4 | .idea/ 5 | *.egg-info/ 6 | *.egg 7 | *.py[cod] 8 | __pycache__/ 9 | *.so 10 | *~ 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Brendan Rius, Peter Hill 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Minimal Fortran kernel for Jupyter 2 | 3 | Shamelessly hacked together from [jupyter-c-kernel](https://github.com/brendan-rius/jupyter-c-kernel) 4 | 5 | ## Manual installation 6 | 7 | * Make sure you have the following requirements installed: 8 | * gfortran 9 | * jupyter 10 | * python 3 11 | * pip 12 | 13 | ### Step-by-step: 14 | * `git clone git@github.com:ZedThree/jupyter-fortran-kernel.git` 15 | * `pip install -e --user jupyter-fortran-kernel` 16 | * `cd jupyter-fortran-kernel` 17 | * `jupyter-kernelspec install fortran_spec/` 18 | * `jupyter-notebook`. Enjoy! 19 | 20 | ## Example of notebook 21 | 22 | [Example of notebook](example-notebook.ipynb "Example of notebook") 23 | 24 | ## Contributing 25 | 26 | Create branches named `issue-X` where `X` is the no of the issue. 27 | Rebase instead of merge. 28 | 29 | ## License 30 | 31 | [MIT](LICENSE.txt) 32 | -------------------------------------------------------------------------------- /example-notebook.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Use Fortran in a notebook!\n", 8 | "\n", 9 | "Each cell is compiled and run separately currently, so they have to be standalone programs.\n", 10 | "\n", 11 | "The following is fine:" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 7, 17 | "metadata": { 18 | "collapsed": false 19 | }, 20 | "outputs": [ 21 | { 22 | "name": "stdout", 23 | "output_type": "stream", 24 | "text": [ 25 | " Hello, World!\n" 26 | ] 27 | } 28 | ], 29 | "source": [ 30 | "program hello\n", 31 | " implicit none\n", 32 | " print*, \"Hello, World!\"\n", 33 | "end program" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "The following, not so much:" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 8, 46 | "metadata": { 47 | "collapsed": false 48 | }, 49 | "outputs": [ 50 | { 51 | "name": "stderr", 52 | "output_type": "stream", 53 | "text": [ 54 | "/usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../lib64/crt1.o: In function `_start':\n", 55 | "/home/abuild/rpmbuild/BUILD/glibc-2.19/csu/../sysdeps/x86_64/start.S:118: undefined reference to `main'\n", 56 | "collect2: error: ld returned 1 exit status\n", 57 | "[Fortran kernel] gfortran exited with code 1, the executable will not be executed" 58 | ] 59 | } 60 | ], 61 | "source": [ 62 | "subroutine foo\n", 63 | " print*, \"Hello, World\"\n", 64 | "end subroutine" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "You can use functions:" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 25, 77 | "metadata": { 78 | "collapsed": false 79 | }, 80 | "outputs": [ 81 | { 82 | "name": "stdout", 83 | "output_type": "stream", 84 | "text": [ 85 | "x = 7\n", 86 | "x^2 = 49\n" 87 | ] 88 | } 89 | ], 90 | "source": [ 91 | "program maths\n", 92 | " implicit none\n", 93 | " integer :: x\n", 94 | " \n", 95 | " x = 7\n", 96 | " \n", 97 | " write(*, '(a,i4)') \"x =\", x\n", 98 | " write(*, '(a,i4)') \"x^2 =\", square(x)\n", 99 | " \n", 100 | "contains\n", 101 | "\n", 102 | " integer function square(number)\n", 103 | " integer, intent(in) :: number\n", 104 | " square = number * number\n", 105 | " end function\n", 106 | " \n", 107 | "end program" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "You can even use modules! They just have to be in the same cell as the program..." 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 26, 120 | "metadata": { 121 | "collapsed": false 122 | }, 123 | "outputs": [ 124 | { 125 | "name": "stdout", 126 | "output_type": "stream", 127 | "text": [ 128 | "x = 7\n", 129 | "x^2 = 49\n" 130 | ] 131 | } 132 | ], 133 | "source": [ 134 | "module funcs\n", 135 | "\n", 136 | " implicit none\n", 137 | " \n", 138 | "contains\n", 139 | "\n", 140 | " integer function square(number)\n", 141 | " integer, intent(in) :: number\n", 142 | " square = number * number\n", 143 | " end function\n", 144 | " \n", 145 | "end module funcs\n", 146 | "\n", 147 | "program maths\n", 148 | " \n", 149 | " use funcs\n", 150 | "\n", 151 | " implicit none\n", 152 | " integer :: x\n", 153 | " \n", 154 | " x = 7\n", 155 | " \n", 156 | " write(*, '(a,i4)') \"x =\", x\n", 157 | " write(*, '(a,i4)') \"x^2 =\", square(x)\n", 158 | " \n", 159 | "end program" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "metadata": { 166 | "collapsed": true 167 | }, 168 | "outputs": [], 169 | "source": [] 170 | } 171 | ], 172 | "metadata": { 173 | "kernelspec": { 174 | "display_name": "Fortran", 175 | "language": "Fortran", 176 | "name": "fortran_spec" 177 | }, 178 | "language_info": { 179 | "file_extension": "f90", 180 | "mimetype": "text/plain", 181 | "name": "fortran" 182 | } 183 | }, 184 | "nbformat": 4, 185 | "nbformat_minor": 0 186 | } 187 | -------------------------------------------------------------------------------- /fortran_spec/kernel.json: -------------------------------------------------------------------------------- 1 | { 2 | "argv": [ 3 | "python3", 4 | "-m", 5 | "jupyter_fortran_kernel", 6 | "-f", 7 | "{connection_file}" 8 | ], 9 | "display_name": "Fortran", 10 | "language": "Fortran" 11 | } 12 | -------------------------------------------------------------------------------- /jupyter_fortran_kernel/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZedThree/jupyter-fortran-kernel/557603b7be9c30d4f14372bdee7a11be2d1b96cc/jupyter_fortran_kernel/__init__.py -------------------------------------------------------------------------------- /jupyter_fortran_kernel/__main__.py: -------------------------------------------------------------------------------- 1 | from ipykernel.kernelapp import IPKernelApp 2 | from .kernel import FortranKernel 3 | IPKernelApp.launch_instance(kernel_class=FortranKernel) 4 | -------------------------------------------------------------------------------- /jupyter_fortran_kernel/kernel.py: -------------------------------------------------------------------------------- 1 | from queue import Queue 2 | from threading import Thread 3 | 4 | from ipykernel.kernelbase import Kernel 5 | import subprocess 6 | import tempfile 7 | import os 8 | import os.path as path 9 | 10 | 11 | class RealTimeSubprocess(subprocess.Popen): 12 | """ 13 | A subprocess that allows to read its stdout and stderr in real time 14 | """ 15 | 16 | def __init__(self, cmd, write_to_stdout, write_to_stderr): 17 | """ 18 | :param cmd: the command to execute 19 | :param write_to_stdout: a callable that will be called with chunks of data from stdout 20 | :param write_to_stderr: a callable that will be called with chunks of data from stderr 21 | """ 22 | self._write_to_stdout = write_to_stdout 23 | self._write_to_stderr = write_to_stderr 24 | 25 | super().__init__(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0) 26 | 27 | self._stdout_queue = Queue() 28 | self._stdout_thread = Thread(target=RealTimeSubprocess._enqueue_output, args=(self.stdout, self._stdout_queue)) 29 | self._stdout_thread.daemon = True 30 | self._stdout_thread.start() 31 | 32 | self._stderr_queue = Queue() 33 | self._stderr_thread = Thread(target=RealTimeSubprocess._enqueue_output, args=(self.stderr, self._stderr_queue)) 34 | self._stderr_thread.daemon = True 35 | self._stderr_thread.start() 36 | 37 | @staticmethod 38 | def _enqueue_output(stream, queue): 39 | """ 40 | Add chunks of data from a stream to a queue until the stream is empty. 41 | """ 42 | for line in iter(lambda: stream.read(4096), b''): 43 | queue.put(line) 44 | stream.close() 45 | 46 | def write_contents(self): 47 | """ 48 | Write the available content from stdin and stderr where specified when the instance was created 49 | :return: 50 | """ 51 | 52 | def read_all_from_queue(queue): 53 | res = b'' 54 | size = queue.qsize() 55 | while size != 0: 56 | res += queue.get_nowait() 57 | size -= 1 58 | return res 59 | 60 | stdout_contents = read_all_from_queue(self._stdout_queue) 61 | if stdout_contents: 62 | self._write_to_stdout(stdout_contents) 63 | stderr_contents = read_all_from_queue(self._stderr_queue) 64 | if stderr_contents: 65 | self._write_to_stderr(stderr_contents) 66 | 67 | 68 | class FortranKernel(Kernel): 69 | implementation = 'jupyter_fortran_kernel' 70 | implementation_version = '0.1' 71 | language = 'Fortran' 72 | language_version = 'F2008' 73 | language_info = {'name': 'fortran', 74 | 'mimetype': 'text/plain', 75 | 'file_extension': 'f90'} 76 | banner = "Fortran kernel.\n" \ 77 | "Uses gfortran, compiles in F2008, and creates source code files and executables in temporary folder.\n" 78 | 79 | def __init__(self, *args, **kwargs): 80 | super(FortranKernel, self).__init__(*args, **kwargs) 81 | self.files = [] 82 | 83 | def cleanup_files(self): 84 | """Remove all the temporary files created by the kernel""" 85 | for file in self.files: 86 | os.remove(file) 87 | os.remove(self.master_path) 88 | 89 | def new_temp_file(self, **kwargs): 90 | """Create a new temp file to be deleted when the kernel shuts down""" 91 | # We don't want the file to be deleted when closed, but only when the kernel stops 92 | kwargs['delete'] = False 93 | kwargs['mode'] = 'w' 94 | file = tempfile.NamedTemporaryFile(**kwargs) 95 | self.files.append(file.name) 96 | return file 97 | 98 | def _write_to_stdout(self, contents): 99 | self.send_response(self.iopub_socket, 'stream', {'name': 'stdout', 'text': contents}) 100 | 101 | def _write_to_stderr(self, contents): 102 | self.send_response(self.iopub_socket, 'stream', {'name': 'stderr', 'text': contents}) 103 | 104 | def create_jupyter_subprocess(self, cmd): 105 | return RealTimeSubprocess(cmd, 106 | lambda contents: self._write_to_stdout(contents.decode()), 107 | lambda contents: self._write_to_stderr(contents.decode())) 108 | 109 | def compile_with_gfortran(self, source_filename, binary_filename): 110 | args = ['gfortran', source_filename, '-std=f2008', '-o', binary_filename] 111 | return self.create_jupyter_subprocess(args) 112 | 113 | def do_execute(self, code, silent, store_history=True, 114 | user_expressions=None, allow_stdin=False): 115 | with self.new_temp_file(suffix='.f90') as source_file: 116 | source_file.write(code) 117 | source_file.flush() 118 | with self.new_temp_file(suffix='.out') as binary_file: 119 | p = self.compile_with_gfortran(source_file.name, binary_file.name) 120 | while p.poll() is None: 121 | p.write_contents() 122 | p.write_contents() 123 | if p.returncode != 0: # Compilation failed 124 | self._write_to_stderr( 125 | "[Fortran kernel] gfortran exited with code {}, the executable will not be executed".format( 126 | p.returncode)) 127 | return {'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 128 | 'user_expressions': {}} 129 | 130 | p = self.create_jupyter_subprocess(binary_file.name) 131 | while p.poll() is None: 132 | p.write_contents() 133 | p.write_contents() 134 | 135 | if p.returncode != 0: 136 | self._write_to_stderr("[Fortran kernel] Executable exited with code {}".format(p.returncode)) 137 | return {'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 'user_expressions': {}} 138 | 139 | def do_shutdown(self, restart): 140 | """Cleanup the created source code files and executables when shutting down the kernel""" 141 | self.cleanup_files() 142 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | 4 | [bdist_wheel] 5 | universal=1 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup(name='jupyter_fortran_kernel', 4 | version='0.1.0', 5 | description='Minimalistic Fortran kernel for Jupyter', 6 | author='Peter Hill', 7 | author_email='peter@fusionplasma.co.uk', 8 | url='https://github.com/ZedThree/jupyter-fortran-kernel/', 9 | download_url='https://github.com/ZedThree/jupyter-fortran-kernel/tarball/0.1.0', 10 | license='MIT', 11 | classifiers=[ 12 | 'Development Status :: 3 - Alpha', 13 | 'Framework :: IPython', 14 | 'License :: OSI Approved :: MIT License', 15 | 'Programming Language :: Python :: 2', 16 | 'Programming Language :: Python :: 2.6', 17 | 'Programming Language :: Python :: 2.7', 18 | 'Programming Language :: Python :: 3', 19 | 'Programming Language :: Python :: 3.3', 20 | 'Programming Language :: Python :: 3.4', 21 | 'Programming Language :: Python :: 3.5', 22 | 'Programming Language :: Fortran', 23 | ], 24 | packages=['jupyter_fortran_kernel'], 25 | keywords=['jupyter', 'kernel', 'fortran'] 26 | ) 27 | --------------------------------------------------------------------------------