This is a fork of google/swift-jupyter. It is made possible to use Jupyterlab (as well as Jupyter Notebook) with most up-to-date Swift toolchain.
This fork made several significant changes to original repository organization. It biased towards using Bazel as code and dependency management tool. This fork is fully separated from Swift for TensorFlow project and is actively maintained to keep everything working with up-to-date Swift toolchain.
Right now, this fork worked well with Swift from 5.4.x to 5.5.x on Linux without any tweaks (if
installed to /opt/swift
). It is tested with Ubuntu 20.04 distribution, but should work with other
flavors.
Because both the notebook integration and PythonKit are used actively with one of my side business, the continuing maintenance of this repository can be assured.
I do want to make this work on macOS. It should be only a few OS / toolchain detection script away.
Operating system:
- Ubuntu 20.04 (64-bit); OR
- Other operating systems may work, but you will have to build Swift from sources.
Dependencies:
- Bazel
- Python 3
Extract the Swift toolchain to /opt/swift
.
Inside the swift-jupyter
repository:
./scripts/bazel/setup_clang.sh /usr/local
Note: /usr/local
should be where your bin/llvm-config
binary resides.
Now you can run Jupyterlab:
SWIFTPATH=/opt/swift bazel run //lab:bootup -- --notebook-dir $PWD
To check if installation is done, run the notebooks/_swift_template.ipynb
notebook.
This fork removed EnableIPythonDisplay.swift
. Using IPython kernel
and its interactive shell as in google/swift-jupyter
cause some silent crashes with the most up-to-date Swift toolchain. It only happens after many tens
of cell executions.
Instead, this fork enhanced EnableJupyterDisplay.swift
to enable rich output from Python (e.g.
Pandas or matplotlib). Because communication with Jupyter notebook requires HMAC computation, this
fork updated EnableJupyterDisplay.swift
to use apple/swift-crypto
for these cryptography primitives.
The EnableJupyterDisplay.swift
now will be automatically included (enabled) once your package
installation is done.
You can call Python libraries using PythonKit. I've set up Bazel with both PythonKit and swift-crypto support.
To see these in action, first install these packages with Bazel inside your notebook:
%bazel "@PythonKit//:PythonKit"
%bazel "@SwiftCrypto//:Crypto"
Now you should be able to display rich output! For example:
let np = Python.import("numpy")
let plt = Python.import("matplotlib.pyplot")
let time = np.arange(0, 10, 0.01)
let amplitude = np.exp(-0.1 * time)
let position = amplitude * np.sin(3 * time)
plt.figure(figsize: [15, 10])
plt.plot(time, position)
plt.plot(time, amplitude)
plt.plot(time, -amplitude)
plt.xlabel("time (s)")
plt.ylabel("position (m)")
plt.title("Oscillations")
plt.show()
let pd = Python.import("pandas")
pd.DataFrame.from_records([["col 1": 3, "col 2": 5], ["col 1": 8, "col 2": 2]]).display()
swift-jupyter
now provides a package called JupyterDisplay
that you can included in your library
to support rich outputs inside Jupyter Notebook. I've been successfully implemented nontrivial
interactive streaming GUI with this capability.
If your package has dependency to JupyterDisplay
, when used inside swift-jupyter
, your query to
JupyterDisplay.isEnabled
will be true and you can use JupyterDisplay.display
functions to send
HTML, PNG images or plain text to Jupyter Notebook. These can be flushed periodically with
JupyterDisplay.flush()
method.
This fork supports simple code completion with sourcekit-lsp
now shipped along Swift toolchain.
The code where uses Swift for TensorFlow's LLDB for code completion is not removed if you choose. I
may remove that support entirely once the sourcekit-lsp
challenge is resolved:
Current sourcekit-lsp
integration relies on appending to a hypothetical file on each successful
cell execution. However, REPL execution is a bit different from code in a file because we can shadow
variables without causing any problems. This can eventually leads to a hypothetical file that AST
is not well-formed. For these cases, sourcekit-lsp
based code completion will be degraded into a
token-based code completion engine.
If you manage your repository with Bazel, integrate with swift-jupyter
provided Jupyterlab is the
best way to run Jupyter Notebook. It keeps Python dependencies clean, can reference to Python packages
either through pip or in-tree Python packages through Bazel. Once it sets up, your repository Jupyterlab
configuration is portable, you don't need to worry about installed extensions not available in a
different computer.
First, add swift-jupyter
to your WORKSPACE
such as:
git_repository(
name = "swift-jupyter",
commit = "daf4eef0ea20be0d6eec5306b5b1cfdb11550d1e",
remote = "https://github.com/liuliu/swift-jupyter.git",
shallow_since = "1658788905 -0400",
)
# You do need to include Python support package for Bazel, if you haven't already:
http_archive(
name = "rules_python",
url = "https://github.com/bazelbuild/rules_python/releases/download/0.4.0/rules_python-0.4.0.tar.gz",
sha256 = "954aa89b491be4a083304a2cb838019c8b8c3720a7abb9c4cb81ac7a24230cea",
)
load("@rules_python//python:pip.bzl", "pip_install")
# Install Python dependencies
pip_install(
requirements = ":requirements.txt",
)
Second, you need to modify / add requirements.txt
file under ./external/
that includes Jupyterlab
reference.
Third, just find a place to use the provided jupyterlab
macro to create a target, for example, this
is what we do inside ./lab/BUILD
:
load("@swift-jupyter//:lab.bzl", "jupyterlab")
jupyterlab(
name = "bootup",
deps = [],
)
You can add more dependencies through deps
parameter. To launch Jupyterlab, simply do:
SWIFTPATH=/opt/swift bazel run lab:bootup -- --notebook-dir $PWD
This fork uses Bazel to setup build and run environment. It helps to maintain a consistent environment between machines. There are two varying factors could impact the environment on Linux:
-
clang
-
Swift toolchain
Current script assumes clang
installed under /usr/local/bin
, which should be a reasonable
assumption but can vary. If you have LLVM toolchain installed somewhere else, ./scripts/bazel/setup_clang.sh
run should take a different prefix other than /usr/local
.
Bazel doesn't inherent environment variables. Thus, your Swift toolchain location needs to be passed
in through command-line every time. Make sure it is the right location for your Swift toolchain. On
Linux, that path should contain a sub-directory called usr
.
Official packages provided through https://www.swift.org/download/ can be a hit-or-miss. There are some CI issues that I actively work with Swift members to make sure Swift LLDB is compiled with Python support. However, they can be missed from time to time. Here is a documented instruction for how to compile Swift LLDB with Python Support from source:
-
Make sure you have enough disk space, and an empty directory for source code from various repositories, I normally use
/opt/swift
; -
To make sure you will build with Python support, you need to install Python's header file. On Ubuntu, it is
apt install python3-dev
; -
git clone
the following repositories to that directory:-
https://github.com/apple/swift-llbuild (Please rename to llbuild)
-
https://github.com/apple/swift-cmark (Please rename to cmark)
-
https://github.com/jpsim/Yams (Please rename to yams)
-
Make sure these repositories are checked out with the matching branch. For example, if you want to compile for Swift 5.6.x, it should be branch
release/5.6
. Forllvm-project
, it isswift/release/5.6
. It is OK to be on the tip forYams
andswift-argument-parser
; -
Create an empty
build
directory at the same level as all other repositories, such as/opt/swift/build
; -
Go to
swift/utils
, and run./build-script --release --lldb
; -
You should be able to find the relevant LLDB files under
/opt/swift/build/Ninja-ReleaseAssert/lldb-linux-x86_64
.