├── .github └── workflows │ └── ruff.yml ├── .gitignore ├── .travis.yml ├── HISTORY.rst ├── LICENSE.txt ├── MANIFEST.in ├── Makefile ├── README.rst ├── pyproject.toml ├── scilab_kernel.ipynb ├── scilab_kernel ├── __init__.py ├── __main__.py ├── _make_figures.sci ├── _version.py ├── check.py ├── images │ ├── logo-32x32.png │ └── logo-64x64.png ├── kernel.json ├── kernel.py └── magics │ └── plot_magic.py └── test_scilab_kernel.py /.github/workflows/ruff.yml: -------------------------------------------------------------------------------- 1 | name: Ruff 2 | on: [push, pull_request] 3 | jobs: 4 | ruff: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v4 8 | - uses: chartboost/ruff-action@v1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | build/ 4 | dist/ 5 | MANIFEST 6 | *.egg-info/ 7 | .ipynb_checkpoints/ 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | python: 4 | - "2.7" 5 | - "3.5" 6 | - "3.6" 7 | install: 8 | # download Scilab and add it to the path 9 | - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then export SCILAB=5.4.1; fi 10 | - if [[ $TRAVIS_PYTHON_VERSION == '3.5' ]]; then export SCILAB=5.5.1; fi 11 | - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then export SCILAB=6.0.0; fi 12 | - travis_retry wget http://www.scilab.org/download/$SCILAB/scilab-$SCILAB.bin.linux-x86_64.tar.gz -O scilab.tar.gz; 13 | - tar xfz scilab.tar.gz; 14 | - export SCILAB_EXECUTABLE="$PWD/scilab-$SCILAB/bin/scilab-cli" 15 | - scilab -version; true 16 | - pip install . 17 | script: 18 | - make test 19 | - python -m scilab_kernel install --user 20 | -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | .. :changelog: 2 | 3 | Release History 4 | --------------- 5 | 6 | 0.10.2 (2025-03-07) 7 | +++++++++++++++++++ 8 | - macOS Scilab application bundle detection. 9 | 10 | 0.10.2 (2024-04-05) 11 | +++++++++++++++++++ 12 | - Bug fixes & minor improvements. 13 | 14 | 0.10.0 (2024-02-21) 15 | +++++++++++++++++++ 16 | - Compliance with recent Scilab versions. 17 | - Better detection of Scilab exectable (Windows registry, ...). 18 | - Anti-aliasing management in SVG files. 19 | 20 | 0.8 (2017-01-07) 21 | ++++++++++++++++ 22 | - Clean up plot handling and improve robustness. 23 | 24 | 25 | 0.7 (2016-01-23) 26 | ++++++++++++++++ 27 | - Convert to a MetaKernel. 28 | 29 | 30 | 0.6 (2016-01-16) 31 | ++++++++++++++++ 32 | - Switch to 2-step installation. 33 | 34 | 35 | 0.4 (2014-10-19) 36 | ++++++++++++++++ 37 | - Update for compatibility with Scilab2py 0.5 38 | - Update for compatibility with IPython 3.0 message spec changes. 39 | 40 | 41 | 0.3 (2014-08-23) 42 | ++++++++++++++++ 43 | - Add %inline --format and --size options. 44 | 45 | 46 | 0.2 (2014-08-14) 47 | ++++++++++++++++ 48 | - Added Python 3 and Windows compatibility. 49 | - Added %inline magic. 50 | - Improved completion, help magic and calltips. 51 | 52 | 53 | 0.1 (2014-08-09) 54 | ++++++++++++++++++ 55 | - Initial release: provides all basic features, including inline plots. 56 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2025 Scilab Kernel Developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.txt 2 | include *.rst 3 | include *.py 4 | include Makefile 5 | include scilab_kernel/kernel.json 6 | recursive-include scilab_kernel *.png 7 | recursive-include scilab_kernel *.sci 8 | prune .git 9 | prune docs/build 10 | prune dist 11 | prune build 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Note: This is meant for scilab_kernel developer use only 2 | .PHONY: all clean test release 3 | 4 | export NAME=scilab_kernel 5 | export VERSION=`python -c "import $(NAME); print($(NAME).__version__)"` 6 | export PY=`python -V` 7 | 8 | all: clean 9 | python setup.py install 10 | 11 | clean: 12 | rm -rf build 13 | rm -rf dist 14 | 15 | test: clean 16 | pip install jupyter_kernel_test nbconvert 17 | python -V 2>&1 | grep "Python 3" && python test_scilab_kernel.py || echo "Skipping unit test" 18 | jupyter nbconvert --to notebook --execute --ExecutePreprocessor.kernel_name=scilab --ExecutePreprocessor.timeout=60 --stdout scilab_kernel.ipynb > /dev/null; 19 | python -c "from jupyter_client.kernelspec import find_kernel_specs; assert 'scilab' in find_kernel_specs()" 20 | 21 | release: clean 22 | pip install wheel 23 | git co master 24 | git pull origin master 25 | python setup.py register 26 | rm -rf dist 27 | python setup.py bdist_wheel --universal 28 | python setup.py sdist 29 | git tag v$(VERSION) 30 | git push origin --all 31 | git push origin --tags 32 | twine upload dist/* 33 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | A Jupyter kernel for Scilab 2 | 3 | Prerequisites 4 | ------------- 5 | `Jupyter Notebook `_, and `Scilab `_. 6 | 7 | Installation 8 | ------------ 9 | To install using pip:: 10 | 11 | pip install scilab_kernel 12 | 13 | Add ``--user`` to install in the user-level environment instead of the system environment. 14 | 15 | This kernel needs the Scilab executable to be run, it which will be searched in this order: 16 | - Using environment variable ``SCILAB_EXECUTABLE``, 17 | - Under Windows, based on registry, 18 | - Under macOS, based on Spotlight database, 19 | - Using the ``PATH`` environment variable. 20 | 21 | Use the ``scilab-adv-cli`` executable if using a Posix-like OS, and ``WScilex-cli.exe`` if using Windows. 22 | 23 | Usage 24 | ----- 25 | 26 | To use the kernel, run one of: 27 | 28 | .. code:: shell 29 | 30 | jupyter notebook # or ``jupyter lab``, if available 31 | # In the notebook interface, select Scilab from the 'New' menu 32 | jupyter qtconsole --kernel scilab 33 | jupyter console --kernel scilab 34 | 35 | If ``jupyter`` executable is not found in your ``PATH``, try ``python -m notebook`` instead. 36 | 37 | This kernel is based on `MetaKernel `_, 38 | which means it features a standard set of magics (such as ``%%html``). For a full list of magics, 39 | run ``%lsmagic`` in a cell. 40 | 41 | A sample notebook is available online_. 42 | 43 | Configuration 44 | ------------- 45 | The kernel can be configured by adding an ``scilab_kernel_config.py`` file to the 46 | ``jupyter`` config path (for example ``~/.jupyter/scilab_kernel_config.py``. The ``ScilabKernel`` class offers ``plot_settings`` as a configurable traits. 47 | The available plot settings are: 48 | 49 | - 'format': 'svg' (default), 'png', 'jpg', 50 | - 'backend': 'inline', 51 | - 'size': ',' ('560,420' by default), 52 | - 'antialiasing': for 'svg' backend only, True by default. 53 | 54 | .. code:: python 55 | 56 | c.ScilabKernel.plot_settings = dict(format='svg', backend='inline', size='560,420', antialiasing=False) 57 | 58 | Scilab default behavior is setup using `lines(0, 800)` and `mode(0)`. You can change these behaviors using scilab code on cells. 59 | 60 | Files ending with `.sci` in the current directory are loaded. 61 | 62 | Troubleshooting 63 | --------------- 64 | 65 | Kernel Times Out While Starting 66 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 67 | If the kernel does not start, run the following command from a terminal: 68 | 69 | .. code:: shell 70 | 71 | python -m scilab_kernel.check 72 | 73 | This can help diagnose problems with setting up integration with Scilab. If in doubt, 74 | create an issue with the output of that command. 75 | 76 | Kernel is Not Listed 77 | ~~~~~~~~~~~~~~~~~~~~ 78 | If the kernel is not listed as an available kernel, first try the following command: 79 | 80 | .. code:: shell 81 | 82 | python -m scilab_kernel install --user 83 | 84 | If the kernel is still not listed, verify that the following point to the same 85 | version of python: 86 | 87 | .. code:: shell 88 | 89 | which python # use "where" if using cmd.exe 90 | which jupyter 91 | 92 | Advanced Installation Notes 93 | --------------------------- 94 | We automatically install a Jupyter kernelspec when installing the 95 | python package. This location can be found using ``jupyter kernelspec list``. 96 | If the default location is not desired, you can remove the directory for the 97 | ``scilab`` kernel, and install using `python -m scilab_kernel install`. See 98 | ``python -m scilab_kernel install --help`` for available options. 99 | 100 | .. _online: http://nbviewer.ipython.org/github/calysto/scilab_kernel/blob/master/scilab_kernel.ipynb 101 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling >=1.5"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "scilab-kernel" 7 | description = "'A Jupyter kernel for Scilab.'" 8 | license = {file = "LICENSE.txt"} 9 | authors = [ 10 | {name = "'Steven Silvester'", email = "steven.silvester@ieee.org"}, 11 | {name = "'Douglas Blank'", email = "doug.blank@gmail.com"} 12 | ] 13 | maintainers = [ 14 | {name = "Vincent Couvert", email = "vincent.couvert@3ds.com"} 15 | ] 16 | classifiers = [ 17 | "Intended Audience :: Science/Research", 18 | "License :: OSI Approved :: BSD License", 19 | "Operating System :: OS Independent", 20 | "Programming Language :: Python", 21 | "Programming Language :: Python :: 3", 22 | "Topic :: Scientific/Engineering", 23 | "Topic :: Software Development", 24 | "Topic :: System :: Shells", 25 | ] 26 | urls = {Homepage = "http://github.com/Calysto/scilab_kernel"} 27 | requires-python = ">=3.7" 28 | dependencies = [ 29 | "metakernel >=0.24.0", 30 | "jupyter_client >=4.3.0", 31 | "ipykernel", 32 | ] 33 | dynamic = ["version"] 34 | 35 | [project.readme] 36 | file = "README.rst" 37 | content-type = "text/x-rst" 38 | 39 | [project.optional-dependencies] 40 | test = ["pytest", "nbconvert", "jupyter_kernel_test", "nbconvert"] 41 | 42 | [tool.hatch.build.targets.wheel.shared-data] 43 | "jupyter-data/share" = "share" 44 | 45 | [tool.hatch.build.targets.sdist] 46 | artifacts = ["jupyter-data"] 47 | include = [ 48 | "/jupyter-data", 49 | "/scilab_kernel", 50 | "/*.md", 51 | "/*.ipynb", 52 | "/*.py" 53 | ] 54 | 55 | [tool.hatch.version] 56 | path = "scilab_kernel/_version.py" 57 | source = "code" 58 | 59 | [tool.jupyter-releaser] 60 | skip = ["check-links"] 61 | 62 | [tool.jupyter-releaser.hooks] 63 | after-prep-git = ["make data-files"] 64 | 65 | [tool.tbump.version] 66 | current = "0.10.2" 67 | regex = ''' 68 | (?P\d+)\.(?P\d+)\.(?P\d+) 69 | ((?Pa|b|rc|.dev)(?P\d+))? 70 | ''' 71 | 72 | [tool.tbump.git] 73 | message_template = "Bump to {new_version}" 74 | tag_template = "v{new_version}" 75 | 76 | [[tool.tbump.file]] 77 | src = "scilab_kernel/__init__.py" -------------------------------------------------------------------------------- /scilab_kernel.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Jupyter Scilab Kernel\n", 8 | "============\n", 9 | "\n", 10 | "Interact with Scilab in the Notebook. All commands are interpreted by Scilab. Since this is a [MetaKernel](https://github.com/Calysto/metakernel), a standard set of magics are available. Help on commands is available using the `%help` magic or using `?` with a command." 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 1, 16 | "metadata": {}, 17 | "outputs": [ 18 | { 19 | "data": { 20 | "image/svg+xml": [ 21 | "01002040608010305070900-11-0.50.5" 22 | ], 23 | "text/plain": [ 24 | "" 25 | ] 26 | }, 27 | "metadata": {}, 28 | "output_type": "display_data" 29 | } 30 | ], 31 | "source": [ 32 | "t = linspace(0,6*%pi,100);\n", 33 | "plot(sin(t))\n", 34 | "plot(cos(t), 'r')" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 2, 40 | "metadata": {}, 41 | "outputs": [ 42 | { 43 | "data": { 44 | "image/svg+xml": [ 45 | "01002040608010305070902040302535" 46 | ], 47 | "text/plain": [ 48 | "" 49 | ] 50 | }, 51 | "metadata": {}, 52 | "output_type": "display_data" 53 | } 54 | ], 55 | "source": [ 56 | "b = 10*cos(t)+30; plot(b);" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 3, 62 | "metadata": {}, 63 | "outputs": [ 64 | { 65 | "name": "stdout", 66 | "output_type": "stream", 67 | "text": [ 68 | " a = \n", 69 | " 1. 2. 3.\n" 70 | ] 71 | } 72 | ], 73 | "source": [ 74 | "a = [1,2,3]" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 4, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "b = a + 3;" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": 5, 89 | "metadata": {}, 90 | "outputs": [ 91 | { 92 | "name": "stdout", 93 | "output_type": "stream", 94 | "text": [ 95 | "\n", 96 | " 4. 5. 6.\n" 97 | ] 98 | } 99 | ], 100 | "source": [ 101 | "disp(b)" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 6, 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "name": "stdout", 111 | "output_type": "stream", 112 | "text": [ 113 | "Available line magics:\n", 114 | "%activity %cd %connect_info %conversation %dot %download %edit %get %help %html %include %install %install_magic %javascript %jigsaw %kernel %kx %latex %load %ls %lsmagic %macro %magic %matplotlib %parallel %plot %pmap %px %python %reload_magics %restart %run %scheme %set\n", 115 | "\n", 116 | "Available cell magics:\n", 117 | "%%activity %%brain %%conversation %%debug %%dot %%file %%help %%html %%javascript %%kx %%latex %%macro %%pipe %%processing %%px %%python %%scheme %%show %%time %%tutor\n" 118 | ] 119 | } 120 | ], 121 | "source": [ 122 | "%lsmagic" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "You can configure specific plot settings using the `%plot` magic. For example, to enable or disable the antialiasing." 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 7, 135 | "metadata": {}, 136 | "outputs": [ 137 | { 138 | "name": "stdout", 139 | "output_type": "stream", 140 | "text": [ 141 | "Usage: %plot [options] backend - configure plotting for the session.\n", 142 | "\n", 143 | "This line magic will configure the plot settings for this\n", 144 | "language.\n", 145 | "\n", 146 | "Examples:\n", 147 | " %plot qt --format=png\n", 148 | " %plot inline -w 640\n", 149 | "\n", 150 | "Note: not all languages may support the %plot magic, and not all\n", 151 | "options may be supported.\n", 152 | "\n", 153 | "Options:\n", 154 | "-------\n", 155 | "-a --antialiasing To disable antialiasing set it to False\n", 156 | "-h --height Plot height in pixels\n", 157 | "-w --width Plot width in pixels\n", 158 | "-r --resolution Resolution in pixels per inch\n", 159 | "-b --backend Backend selection [default: inline]\n", 160 | "-f --format Plot format (png, svg or jpg).\n", 161 | "-s --size Pixel size of plots, \"width,height\"\n", 162 | "\n", 163 | "Options:\n", 164 | " --help show this help message and exit\n", 165 | " -a ANTIALIASING, --antialiasing=ANTIALIASING\n", 166 | " To disable antialiasing set it to False\n", 167 | " -h HEIGHT, --height=HEIGHT\n", 168 | " Plot height in pixels\n", 169 | " -w WIDTH, --width=WIDTH\n", 170 | " Plot width in pixels\n", 171 | " -r RESOLUTION, --resolution=RESOLUTION\n", 172 | " Resolution in pixels per inch\n", 173 | " -b BACKEND, --backend=BACKEND\n", 174 | " Backend selection\n", 175 | " -f FORMAT, --format=FORMAT\n", 176 | " Plot format (png, svg or jpg).\n", 177 | " -s SIZE, --size=SIZE Pixel size of plots, \"width,height\"\n" 178 | ] 179 | } 180 | ], 181 | "source": [ 182 | "%plot --help" 183 | ] 184 | } 185 | ], 186 | "metadata": { 187 | "anaconda-cloud": {}, 188 | "kernelspec": { 189 | "display_name": "Scilab", 190 | "language": "scilab", 191 | "name": "scilab" 192 | }, 193 | "language_info": { 194 | "file_extension": ".sci", 195 | "help_links": [ 196 | { 197 | "text": "MetaKernel Magics", 198 | "url": "https://metakernel.readthedocs.io/en/latest/source/README.html" 199 | } 200 | ], 201 | "mimetype": "text/x-scilab", 202 | "name": "scilab", 203 | "version": "0.10.2" 204 | } 205 | }, 206 | "nbformat": 4, 207 | "nbformat_minor": 2 208 | } 209 | -------------------------------------------------------------------------------- /scilab_kernel/__init__.py: -------------------------------------------------------------------------------- 1 | """A Scilab kernel for Jupyter""" 2 | 3 | __version__ = '0.10.2' 4 | -------------------------------------------------------------------------------- /scilab_kernel/__main__.py: -------------------------------------------------------------------------------- 1 | 2 | from .kernel import ScilabKernel 3 | 4 | if __name__ == '__main__': 5 | ScilabKernel.run_as_main() 6 | -------------------------------------------------------------------------------- /scilab_kernel/_make_figures.sci: -------------------------------------------------------------------------------- 1 | function _make_figures(plot_dir, plot_format) 2 | ids_array=winsid(); 3 | for i=1:length(ids_array) 4 | id=ids_array(i); 5 | outfile = sprintf('%s/__ipy_sci_fig_%03d', plot_dir, i); 6 | if plot_format == 'jpg' then 7 | xs2jpg(id, outfile + '.jpg'); 8 | elseif plot_format == 'jpeg' then 9 | xs2jpg(id, outfile + '.jpeg'); 10 | elseif plot_format == 'png' then 11 | xs2png(id, outfile); 12 | else 13 | xs2svg(id, outfile); 14 | end 15 | close(id); 16 | end 17 | endfunction -------------------------------------------------------------------------------- /scilab_kernel/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.10.2' -------------------------------------------------------------------------------- /scilab_kernel/check.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from metakernel import __version__ as mversion 3 | from . import __version__ 4 | from .kernel import ScilabKernel 5 | 6 | 7 | if __name__ == "__main__": 8 | print('Scilab kernel v%s' % __version__) 9 | print('Metakernel v%s' % mversion) 10 | print('Python v%s' % sys.version) 11 | print('Python path: %s' % sys.executable) 12 | print('\nConnecting to Scilab...') 13 | try: 14 | s = ScilabKernel() 15 | print('Scilab connection established') 16 | print(s.banner) 17 | except Exception as e: 18 | print(e) 19 | -------------------------------------------------------------------------------- /scilab_kernel/images/logo-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Calysto/scilab_kernel/0d89f6f930eb847861b3f88041c90d7d6f92d56a/scilab_kernel/images/logo-32x32.png -------------------------------------------------------------------------------- /scilab_kernel/images/logo-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Calysto/scilab_kernel/0d89f6f930eb847861b3f88041c90d7d6f92d56a/scilab_kernel/images/logo-64x64.png -------------------------------------------------------------------------------- /scilab_kernel/kernel.json: -------------------------------------------------------------------------------- 1 | { 2 | "argv": ["python", 3 | "-m", "scilab_kernel", 4 | "-f", "{connection_file}"], 5 | "display_name": "Scilab", 6 | "language": "scilab", 7 | "mimetype": "text/x-scilab", 8 | "name": "scilab" 9 | } 10 | -------------------------------------------------------------------------------- /scilab_kernel/kernel.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | 3 | import codecs 4 | import json 5 | import os 6 | import re 7 | import shutil 8 | import sys 9 | import platform 10 | import tempfile 11 | import importlib 12 | import subprocess 13 | if importlib.util.find_spec('winreg'): 14 | import winreg 15 | from xml.dom import minidom 16 | 17 | from metakernel import MetaKernel, ProcessMetaKernel, REPLWrapper, pexpect 18 | from metakernel.pexpect import which 19 | from IPython.display import Image, SVG 20 | 21 | from . import __version__ 22 | 23 | 24 | def get_kernel_json(): 25 | """Get the kernel json for the kernel. 26 | """ 27 | here = os.path.dirname(__file__) 28 | with open(os.path.join(here, 'kernel.json')) as fid: 29 | data = json.load(fid) 30 | data['argv'][0] = sys.executable 31 | return data 32 | 33 | 34 | class ScilabKernel(ProcessMetaKernel): 35 | app_name = 'scilab_kernel' 36 | implementation = 'Scilab Kernel' 37 | implementation_version = __version__, 38 | language = 'scilab' 39 | language_version = __version__, 40 | language_info = { 41 | 'name': 'scilab', 42 | 'file_extension': '.sci', 43 | "mimetype": "text/x-scilab", 44 | "version": __version__, 45 | 'help_links': MetaKernel.help_links, 46 | } 47 | kernel_json = get_kernel_json() 48 | 49 | _setup = ( 50 | f'lines(0, 800); // TODO: Scilab kernel does not detect output width\n' 51 | f'mode(0);\n' 52 | f'try,getd("."),end\n' 53 | f'try,getd("{os.path.dirname(__file__)}"),end\n' 54 | ) 55 | 56 | _first = True 57 | 58 | _banner = None 59 | 60 | _executable = None 61 | _default_args = None 62 | 63 | @property 64 | def executable(self): 65 | if self._executable: 66 | return self._executable 67 | 68 | executable = next(self._detect_executable()) 69 | if not executable: 70 | msg = ('Scilab Executable not found, please add to path or set ' 71 | '"SCILAB_EXECUTABLE" environment variable') 72 | raise OSError(msg) 73 | 74 | if 'cli' not in executable: 75 | self._default_args = ['-nw'] 76 | else: 77 | self._default_args = [] 78 | self.log.debug(' scilab_kernel._executable: ' + executable) 79 | self.log.debug(' scilab_kernel._default_args: ' + ' '.join(self._default_args)) 80 | self._executable = executable 81 | return executable 82 | 83 | @property 84 | def banner(self): 85 | if self._banner is None: 86 | resp = self.do_execute_direct("getversion()", silent=True) 87 | if resp: 88 | # ans = 89 | # 90 | # "scilab-branch-2024.1" 91 | result = re.search(r'scilab-([a-zA-Z0-9\-\.]+)', resp.output) 92 | if result: 93 | self._banner = result.group(1) 94 | self.log.warning(' scilab_kernel._banner: ' + self._banner) 95 | if self._banner is None: 96 | self._banner = "Unknown version" 97 | return self._banner 98 | 99 | def _detect_executable(self): 100 | # the env. variable can be setup 101 | executable = os.environ.get('SCILAB_EXECUTABLE', None) 102 | if executable: 103 | self.log.warning('SCILAB_EXECUTABLE env. variable: ' + executable) 104 | yield executable 105 | 106 | # read the windows registry 107 | if os.name == 'nt': 108 | try: 109 | with winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, "Scilab5.sce\shell\open\command") as key: 110 | cmd : str = winreg.EnumValue(key, 0)[1] 111 | executable = cmd.split(r'"')[1].replace("wscilex.exe", "wscilex-cli.exe") 112 | self.log.warning('Windows registry binary: ' + executable) 113 | yield executable 114 | except FileNotFoundError: 115 | pass 116 | 117 | # detect macOS bundle 118 | if platform.system() == 'Darwin': 119 | process = subprocess.run(['mdfind', '-onlyin', '/Applications', 'kMDItemCFBundleIdentifier=org.scilab.modules.jvm.Scilab'], 120 | stdout=subprocess.PIPE, 121 | universal_newlines=True) 122 | bundles = process.stdout 123 | if len(bundles) > 0: 124 | executable = bundles.split('\n', 1)[0] + "/Contents/bin/scilab-adv-cli" 125 | self.log.warning('macOS Application binary: ' + executable) 126 | yield executable 127 | 128 | # detect on the path 129 | if os.name == 'nt': 130 | executable = 'WScilex-cli' 131 | else: 132 | executable = 'scilab-adv-cli' 133 | executable = which(executable) 134 | if executable: 135 | self.log.warning('executable in the path: ' + executable) 136 | yield executable 137 | 138 | # default to None, will report an error 139 | yield None 140 | 141 | def makeWrapper(self): 142 | """Start a Scilab process and return a :class:`REPLWrapper` object. 143 | """ 144 | 145 | orig_prompt = r'-[0-9]*->' 146 | prompt_cmd = None 147 | change_prompt = None 148 | continuation_prompt = ' \>' 149 | self._first = True 150 | if os.name == 'nt': 151 | prompt_cmd = 'printf("-->")' 152 | echo = False 153 | else: 154 | echo = True 155 | executable = self.executable 156 | child = pexpect.spawnu(executable, self._default_args, 157 | echo=echo, 158 | codec_errors="ignore", 159 | encoding="utf-8") 160 | wrapper = REPLWrapper(child, orig_prompt, change_prompt, 161 | prompt_emit_cmd=prompt_cmd, echo=echo, 162 | continuation_prompt_regex=continuation_prompt) 163 | 164 | wrapper.child.linesep = '\r\n' if os.name == 'nt' else '\n' 165 | return wrapper 166 | 167 | def Write(self, message): 168 | clean_msg = message.strip("\n\r\t") 169 | super(ScilabKernel, self).Write(clean_msg) 170 | 171 | def Print(self, text): 172 | text = str(text).strip('\x1b[0m').replace('\u0008', '').strip() 173 | text = [line.strip() for line in text.splitlines() 174 | if (not line.startswith(chr(27)))] 175 | text = '\n'.join(text) 176 | if text: 177 | super(ScilabKernel, self).Print(text) 178 | 179 | def do_execute_direct(self, code, silent=False): 180 | if self._first: 181 | self._first = False 182 | self.handle_plot_settings() 183 | setup = self._setup.strip() 184 | self.do_execute_direct(setup, True) 185 | resp = super(ScilabKernel, self).do_execute_direct(code, silent=silent) 186 | if silent: 187 | return resp 188 | if self.plot_settings.get('backend', None) == 'inline': 189 | plot_dir = self.make_figures() 190 | for image in self.extract_figures(plot_dir): 191 | self.Display(image) 192 | shutil.rmtree(plot_dir, True) 193 | 194 | def get_kernel_help_on(self, info, level=0, none_on_fail=False): 195 | obj = info.get('help_obj', '') 196 | if not obj or len(obj.split()) > 1: 197 | if none_on_fail: 198 | return None 199 | else: 200 | return "" 201 | self.do_execute_direct('help %s' % obj, True) 202 | 203 | def do_shutdown(self, restart): 204 | self.wrapper.sendline('quit') 205 | super(ScilabKernel, self).do_shutdown(restart) 206 | 207 | def get_completions(self, info): 208 | """ 209 | Get completions from kernel based on info dict. 210 | """ 211 | cmd = 'completion("%s")' % info['obj'] 212 | output = self.do_execute_direct(cmd, True) 213 | if not output: 214 | return [] 215 | output = output.output.replace('!', '') 216 | return [line.strip() for line in output.splitlines() 217 | if info['obj'] in line] 218 | 219 | def handle_plot_settings(self): 220 | """Handle the current plot settings""" 221 | settings = self.plot_settings 222 | settings.setdefault('backend', 'inline') 223 | settings.setdefault('format', 'svg') 224 | settings.setdefault('size', '560,420') 225 | settings.setdefault('antialiasing', True) 226 | 227 | cmds = [] 228 | 229 | self._plot_fmt = settings['format'] 230 | 231 | cmds.append('h = gdf();') 232 | cmds.append('h.figure_position = [0, 0];') 233 | 234 | width, height = 560, 420 235 | if isinstance(settings['size'], tuple): 236 | width, height = settings['size'] 237 | elif settings['size']: 238 | try: 239 | width, height = settings['size'].split(',') 240 | width, height = int(width), int(height) 241 | except Exception as e: 242 | self.Error('Error setting plot settings: %s' % e) 243 | 244 | cmds.append('h.figure_size = [%s,%s];' % (width, height)) 245 | cmds.append('h.axes_size = [%s * 0.98, %s * 0.8];' % (width, height)) 246 | 247 | if settings['backend'] == 'inline': 248 | cmds.append('h.visible = "off";') 249 | else: 250 | cmds.append('h.visible = "on";') 251 | super(ScilabKernel, self).do_execute_direct('\n'.join(cmds), True) 252 | 253 | def make_figures(self, plot_dir=None): 254 | """Create figures for the current figures. 255 | 256 | Parameters 257 | ---------- 258 | plot_dir: str, optional 259 | The directory in which to create the plots. 260 | 261 | Returns 262 | ------- 263 | out: str 264 | The plot directory containing the files. 265 | """ 266 | plot_dir = plot_dir or tempfile.mkdtemp() 267 | plot_format = self._plot_fmt.lower() 268 | make_figs = '_make_figures("%s", "%s");' 269 | make_figs = make_figs % (plot_dir, plot_format) 270 | super(ScilabKernel, self).do_execute_direct(make_figs, True) 271 | return plot_dir 272 | 273 | def extract_figures(self, plot_dir): 274 | """Get a list of IPython Image objects for the created figures. 275 | 276 | Parameters 277 | ---------- 278 | plot_dir: str 279 | The directory in which to create the plots. 280 | """ 281 | images = [] 282 | for fname in sorted(os.listdir(plot_dir)): 283 | filename = os.path.join(plot_dir, fname) 284 | try: 285 | if fname.lower().endswith('.svg'): 286 | im = self._handle_svg(filename) 287 | else: 288 | im = Image(filename) 289 | images.append(im) 290 | except Exception as e: 291 | if self.error_handler: 292 | self.error_handler(e) 293 | else: 294 | raise e 295 | return images 296 | 297 | def _handle_svg(self, filename): 298 | """ 299 | Handle special considerations for SVG images. 300 | """ 301 | # Gnuplot can create invalid characters in SVG files. 302 | with codecs.open(filename, 'r', encoding='utf-8', 303 | errors='replace') as fid: 304 | data = fid.read() 305 | im = SVG(data=data) 306 | try: 307 | im.data = self._fix_svg_size(im.data) 308 | except Exception: 309 | pass 310 | try: 311 | settings = self.plot_settings 312 | if settings['antialiasing']: 313 | im.data = self._fix_svg_antialiasing(im.data) 314 | except Exception: 315 | pass 316 | return im 317 | 318 | def _fix_svg_size(self, data): 319 | """GnuPlot SVGs do not have height/width attributes. Set 320 | these to be the same as the viewBox, so that the browser 321 | scales the image correctly. 322 | """ 323 | # Minidom does not support parseUnicode, so it must be decoded 324 | # to accept unicode characters 325 | parsed = minidom.parseString(data.encode('utf-8')) 326 | (svg,) = parsed.getElementsByTagName('svg') 327 | 328 | viewbox = svg.getAttribute('viewBox').split(' ') 329 | width, height = viewbox[2:] 330 | width, height = int(width), int(height) 331 | 332 | # Handle overrides in case they were not encoded. 333 | settings = self.plot_settings 334 | if settings['width'] != -1: 335 | if settings['height'] == -1: 336 | height = height * settings['width'] / width 337 | width = settings['width'] 338 | if settings['height'] != -1: 339 | if settings['width'] == -1: 340 | width = width * settings['height'] / height 341 | height = settings['height'] 342 | 343 | svg.setAttribute('width', '%dpx' % width) 344 | svg.setAttribute('height', '%dpx' % height) 345 | return svg.toxml() 346 | 347 | def _fix_svg_antialiasing(self, data): 348 | """Batik API to change line art antialias is broken. 349 | We add shape-rendering:geometricPrecision to content with style containing "clip-path:url(#clipPath1)" 350 | """ 351 | # Minidom does not support parseUnicode, so it must be decoded 352 | # to accept unicode characters 353 | parsed = minidom.parseString(data.encode('utf-8')) 354 | (svg,) = parsed.getElementsByTagName('svg') 355 | g = svg.getElementsByTagName('path') 356 | for i in range(len(g)): 357 | stylestr = g[i].getAttribute('style').replace("clip-path:url(#clipPath", "shape-rendering:geometricPrecision; clip-path:url(#clipPath") 358 | g[i].setAttribute('style', stylestr) 359 | return svg.toxml() 360 | 361 | -------------------------------------------------------------------------------- /scilab_kernel/magics/plot_magic.py: -------------------------------------------------------------------------------- 1 | from metakernel import Magic, option 2 | class ScilabPlotMagic(Magic): 3 | 4 | @option( 5 | '-s', '--size', action='store', 6 | help='Pixel size of plots, "width,height"' 7 | ) 8 | @option( 9 | '-f', '--format', action='store', 10 | help='Plot format (png, svg or jpg).' 11 | ) 12 | @option( 13 | '-b', '--backend', action='store', default='inline', 14 | help='Backend selection' 15 | ) 16 | @option( 17 | '-r', '--resolution', action='store', 18 | help='Resolution in pixels per inch' 19 | ) 20 | @option( 21 | '-w', '--width', action='store', 22 | help='Plot width in pixels' 23 | ) 24 | @option( 25 | '-h', '--height', action='store', 26 | help='Plot height in pixels' 27 | ) 28 | @option( 29 | '-a', '--antialiasing', action='store', 30 | help='To disable antialiasing set it to False' 31 | ) 32 | 33 | def line_plot(self, *args, **kwargs): 34 | """ 35 | %plot [options] backend - configure plotting for the session. 36 | 37 | This line magic will configure the plot settings for this 38 | language. 39 | 40 | Examples: 41 | %plot qt --format=png 42 | %plot inline -w 640 43 | 44 | Note: not all languages may support the %plot magic, and not all 45 | options may be supported. 46 | """ 47 | if args and not args[0].startswith('-'): 48 | kwargs['backend'] = args[0] 49 | if 'size' in kwargs and kwargs['size'] is not None: 50 | width, height = kwargs['size'] 51 | kwargs['width'] = int(width) 52 | kwargs['height'] = int(height) 53 | # Remove empty options so ".setdefault" will work. 54 | for key in ['resolution', 'format', 'size', 'width', 'height', 'antialiasing']: 55 | if key in kwargs and kwargs[key] is None: 56 | del kwargs[key] 57 | self.kernel.plot_settings = kwargs 58 | self.kernel.handle_plot_settings() 59 | 60 | 61 | def register_magics(kernel): 62 | kernel.register_magics(ScilabPlotMagic) 63 | -------------------------------------------------------------------------------- /test_scilab_kernel.py: -------------------------------------------------------------------------------- 1 | """Example use of jupyter_kernel_test, with tests for IPython.""" 2 | 3 | import unittest 4 | import jupyter_kernel_test as jkt 5 | 6 | 7 | class ScilabKernelTests(jkt.KernelTests): 8 | kernel_name = "scilab" 9 | 10 | language_name = "scilab" 11 | 12 | code_hello_world = "disp('hello, world')" 13 | 14 | code_display_data = [ 15 | {'code': '%plot -f png\nplot([1,2,3])', 'mime': 'image/png'}, 16 | {'code': '%plot -f svg\nplot([1,2,3])', 'mime': 'image/svg+xml'} 17 | ] 18 | 19 | completion_samples = [ 20 | { 21 | 'text': 'one', 22 | 'matches': {'ones'}, 23 | }, 24 | ] 25 | 26 | if __name__ == '__main__': 27 | unittest.main() 28 | 29 | --------------------------------------------------------------------------------