Skip to content
Open
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- First working version of `dockernel install` and `dockernel start`.

[unreleased]: https://github.com/mrmino/dockernel/v1.0.2...HEAD
[1.0.1]: https://github.com/mrmino/dockernel/releases/tag/v1.0.2
[1.0.2]: https://github.com/mrmino/dockernel/releases/tag/v1.0.2
[1.0.1]: https://github.com/mrmino/dockernel/releases/tag/v1.0.1
[1.0.0]: https://github.com/mrmino/dockernel/releases/tag/v1.0.0
39 changes: 37 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,52 @@ different one.

## Usage

*Note for Linux users:

If you run into permission errors with `docker` or `dockernel` - either use
`sudo`, or follow the steps outlined in [Manage Docker as a non-root
user](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user)
guide.*

### Creating a Dockernel image

First, create a docker image that will host your kernel. This will require a
proper dockerfile. An example can be seen
proper dockerfile. A full example for IPython kernel can be seen
[here](https://github.com/MrMino/dockernel/blob/master/example_dockerfile).

Most kernels take a path to a "connection file" (also called "control file" by
some kernels) as a CLI argument. This file contains all of the information
necessary to start up a kernel, including TCP ports to use, IP address, etc.

When running your container, Dockernel will supply this file and put it into a
predefined path in the container. This path will be given via an environment
variable visible in the container as `$DOCKERNEL_CONNECTION_FILE`.

Therefore, in order for the kernel to know the connection settings it should
use, you need to pass the contents of this variable in `CMD` of the container.
For example, for IPython kernel:

```
CMD python -m ipykernel_launcher -f $DOCKERNEL_CONNECTION_FILE
```

Or for the Rust kernel (Evcxr, see the
[example Rust
dockerfile](https://github.com/MrMino/dockernel/blob/master/example_rust_dockerfile)):

```
CMD evcxr_jupyter --control_file $DOCKERNEL_CONNECTION_FILE
```

To build your image, use `docker build`. E.g. to build the example mentioned
above:

```
sudo docker build --tag my_kernel - < example_dockerfile
docker build --tag my_kernel - < example_dockerfile
```

### Installing your image as a Jupyter Kernel

After that, use Dockernel to install the docker image as a Jupyter kernel:

```
Expand Down
2 changes: 1 addition & 1 deletion dockernel/cli/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def python_argv(system_type: str) -> List[str]:

def generate_kernelspec_argv(image_name: str, system_type: str) -> List[str]:
dockernel_argv = ['dockernel', 'start',
image_name, JUPYTER_CONNECTION_FILE_TEMPLATE]
image_name, JUPYTER_CONNECTION_FILE_TEMPLATE]
return python_argv(system_type) + dockernel_argv


Expand Down
18 changes: 18 additions & 0 deletions dockernel/kernelspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
from enum import Enum
from pathlib import Path

from .version import version as dockernel_version


KERNELSPEC_FILENAME = 'kernel.json'
KERNELSPEC_STORE_DIRNAME = 'kernels'
DOCKERNEL_VERSIONFILE_FILENAME = 'DOCKERNEL'


class InterruptMode(str, Enum):
Expand Down Expand Up @@ -165,3 +168,18 @@ def install_kernelspec(kernelspec_dir: Path, kernelspec: Kernelspec) -> None:
kernelspec_dir.mkdir()
kernelspec_file = kernelspec_dir/KERNELSPEC_FILENAME
kernelspec_file.write_text(kernelspec.json())
add_dockernel_versionfile(kernelspec_dir)


def add_dockernel_versionfile(kernelspec_dir: Path) -> None:
"""Generate DOCKERNEL file under the specified path.

Creates a text file with the current version of dockernel package

Parameters
----------
kernelspec_dir
Path object to the store where the kernel should be installed.
"""
versionfile_path = kernelspec_dir/DOCKERNEL_VERSIONFILE_FILENAME
versionfile_path.write_text(dockernel_version + '\n')
4 changes: 4 additions & 0 deletions dockernel/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This file is written to by setuptools_scm.
# The value supplied here is for development purposes only.

__version__ = '0.0.0.dev'
2 changes: 1 addition & 1 deletion example_dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.7-slim-buster

RUN pip install --upgrade pip ipython ipykernel
CMD cat $DOCKERNEL_CONNECTION_FILE; python -m ipykernel_launcher -f $DOCKERNEL_CONNECTION_FILE || true
CMD python -m ipykernel_launcher -f $DOCKERNEL_CONNECTION_FILE
5 changes: 5 additions & 0 deletions example_rust_dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM rust:1.50.0-buster

RUN apt-get update && apt-get install build-essential cmake -y
RUN cargo install evcxr_jupyter
CMD evcxr_jupyter --control_file $DOCKERNEL_CONNECTION_FILE
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"]

[tool.setuptools_scm]
write_to = "dockernel/version.py"
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ classifiers =
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: Implementation :: CPython

[options]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import setuptools

setuptools.setup(use_scm_version=True)
setuptools.setup(use_scm_version={'write_to': 'dockernel/version.py'})
5 changes: 3 additions & 2 deletions tests/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
from dockernel.cli import main_arguments


class TestKernelspecArgvGeneration:
# TODO Add tests for Darwin and Windows
class TestLinuxKernelspecArgvGeneration:
IMAGE_NAME = 'example/image-name'

@pytest.fixture
def argv(self):
return generate_kernelspec_argv(self.IMAGE_NAME)
return generate_kernelspec_argv(self.IMAGE_NAME, 'Linux')

def test_argv_starts_with_usr_bin_env_python_m(self, argv):
assert argv[:3] == ['/usr/bin/env', 'python', '-m']
Expand Down
16 changes: 15 additions & 1 deletion tests/test_kernelspec.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import pytest
import json
from dockernel.kernelspec import (Kernelspec, InterruptMode,
Expand All @@ -7,6 +8,16 @@
from pathlib import Path


@pytest.yield_fixture
def environment_variables():
starting_state = os.environ.copy()

yield os.environ

os.environ.clear()
os.environ.update(starting_state)


@pytest.fixture
def tmpdir(tmpdir):
"""Workaround for tmpdir being py.path.LocalPath instead of Path"""
Expand Down Expand Up @@ -81,7 +92,10 @@ def test_raises_ValueError_when_directory_name_is_not_store(self, tmpdir):

class TestKernelspecStoreDir:
@pytest.mark.parametrize('system_type', ('Linux', 'Windows', 'Darwin'))
def test_works_for_supported_platforms(self, system_type):
def test_works_for_supported_platforms(self, system_type,
environment_variables):
if system_type == 'Windows':
environment_variables['APPDATA'] = ''
user_kernelspec_store(system_type)

def test_raises_TypeError_on_unknown_system(self):
Expand Down