If you're reading this section, you're probably interested in contributing to JupyterLab. Welcome and thanks for your interest in contributing!
Please take a look at the Contributor documentation, familiarize yourself with using JupyterLab, and introduce yourself to the community (on the mailing list or discourse) and share what area of the project you are interested in working on. Please also see the Jupyter Community Guides.
We have labeled some issues as good first issue or help wanted that we believe are good examples of small, self-contained changes. We encourage those that are new to the code base to implement and/or ask questions about these issues.
If you believe you’ve found a security vulnerability in JupyterLab or any Jupyter project, please report it to security@ipython.org. If you prefer to encrypt your security reports, you can use this PGP public key.
Table of contents
- General Guidelines for Contributing
- Submitting a Pull Request Contribution
- Setting Up a Development Environment
- Installing JupyterLab
- Performance Testing
- Build and run the stand-alone examples
- Debugging in the Browser
- High level Architecture
- The NPM Packages
- Writing Documentation
- The Jupyter Server Extension
- Build Utilities
- Testing Changes to External Packages
- Keyboard Shortcuts
- Screenshots and Animations
- Notes
For general documentation about contributing to Jupyter projects, see the Project Jupyter Contributor Documentation and Code of Conduct.
All source code is written in TypeScript. See the Style Guide.
All source code is formatted using prettier. When code is modified and committed, all staged files will be automatically formatted using pre-commit git hooks (with help from the lint-staged and husky libraries). The benefit of using a code formatter like prettier is that it removes the topic of code style from the conversation when reviewing pull requests, thereby speeding up the review process.
You may also use the prettier npm script (e.g. npm run prettier
or
yarn prettier
or jlpm prettier
) to format the entire code base.
We recommend installing a prettier extension for your code editor and
configuring it to format your code with a keyboard shortcut or
automatically on save.
Generally, an issue should be opened describing a piece of proposed work and the issues it solves before a pull request is opened.
Opening an issue lets community members participate in the design discussion, makes others aware of work being done, and sets the stage for a fruitful community interaction. A pull request should reference the issue it is addressing. Once the pull request is merged, the issue related to it will also be closed. If there is additional discussion around implemementation the issue may be re-opened. Once 30 days have passed with no additional discussion, the lock bot will lock the issue. If additional discussion is desired, or if the pull request doesn't fully address the locked issue, please open a new issue referencing the locked issue.
Users without the commit rights to the JupyterLab repository can tag
issues with labels using the @meeseeksdev
bot. For example: To apply
the label foo
and bar baz
to an issue, comment
@meeseeksdev tag foo "bar baz"
on the issue.
You can launch a binder with the latest JupyterLab master to test something (this may take a few minutes to load):
Building JupyterLab from its GitHub source code requires Node.js. The
development version requires Node.js version 10+, as defined in the
engines
specification in
dev_mode/package.json.
If you use conda
, you can get it with:
conda install -c conda-forge 'nodejs'
If you use Homebrew on Mac OS X:
brew install node
You can also use the installer from the Node.js website.
To check which version of Node.js is installed:
node -v
JupyterLab requires Jupyter Notebook version 4.3 or later.
If you use conda
, you can install notebook using:
conda install -c conda-forge notebook
You may also want to install nb_conda_kernels
to have a kernel
option for different conda
environments
conda install -c conda-forge nb_conda_kernels
If you use pip
, you can install notebook using:
pip install notebook
Fork the JupyterLab repository.
Once you have installed the dependencies mentioned above, use the following steps:
git clone https://github.com/<your-github-username>/jupyterlab.git
cd jupyterlab
pip install -e .
jlpm install
jlpm run build # Build the dev mode assets (optional)
jlpm run build:core # Build the core mode assets (optional)
jupyter lab build # Build the app dir assets (optional)
Notes:
- A few of the scripts will run "python". If your target python is called something else (such as "python3") then parts of the build will fail. You may wish to build in a conda environment, or make an alias.
- Some of the packages used in the development environment require
Python 3.0 or higher. If you encounter an ImportError during the
installation, make sure Python 3.0+ is installed. Also, try using the
Python 3.0+ version of
pip
orpip3 install -e .
command to install JupyterLab from the forked repository. - The
jlpm
command is a JupyterLab-provided, locked version of the yarn package manager. If you haveyarn
installed already, you can use theyarn
command when developing, and it will use the local version ofyarn
injupyterlab/yarn.js
when run in the repository or a built application directory. - If you decide to use the
jlpm
command and encounter thejlpm: command not found
error, try adding the user-level bin directory to yourPATH
environment variable. You already installedjlpm
along with JupyterLab in the previous command, butjlpm
might not be accessible due toPATH
environment variable related issues. If you are using a Unix derivative (FreeBSD, GNU / Linux, OS X), you can achieve this by usingexport PATH="$HOME/.local/bin:$PATH"
command. - At times, it may be necessary to clean your local repo with the
command
npm run clean:slate
. This will clean the repository, and re-install and rebuild. - If
pip
gives aVersionConflict
error, it usually means that the installed version ofjupyterlab_server
is out of date. Runpip install --upgrade jupyterlab_server
to get the latest version. - To install JupyterLab in isolation for a single conda/virtual
environment, you can add the
--sys-prefix
flag to the extension activation above; this will tie the installation to thesys.prefix
location of your environment, without writing anything in your user-wide settings area (which are visible to all your envs): - You can run
jlpm run build:dev:prod
to build more accurate sourcemaps that show the original Typescript code when debugging. However, it takes a bit longer to build the sources, so is used only to build for production by default.
If you are using a version of Jupyter Notebook earlier than 5.3, then you must also run the following command to enable the JupyterLab server extension:
jupyter serverextension enable --py --sys-prefix jupyterlab
For installation instructions to write documentation, please see Writing Documentation
Start JupyterLab in development mode:
jupyter lab --dev-mode
Development mode ensures that you are running the JavaScript assets that are built in the dev-installed Python package. Note that when running in dev mode, extensions will not be activated by default.
When running in dev mode, a red stripe will appear at the top of the page; this is to indicate running an unreleased version.
jlpm run build:testutils
jlpm test
You can run tests for an individual package by changing to the appropriate package folder:
cd packages/notebook
jlpm run build:test
jlpm test
We use jest
for all tests, so standard jest
workflows apply.
Tests can be debugged in either VSCode or Chrome. It can help to add an
it.only
to a specific test when debugging. All of the test*
scripts in each package accept jest
cli
options.
To debug in VSCode, open a package folder in VSCode. We provide a launch
configuration in each package folder. In a terminal, run
jlpm test:debug:watch
. In VSCode, select "Attach to Jest" from the
"Run" sidebar to begin debugging. See VSCode docs on
debugging for
more details.
To debug in Chrome, run jlpm test:debug:watch
in the terminal. Open
Chrome and go to chrome://inspect/
. Select the remote device and
begin debugging.
There are some helper functions in testutils
(which is a public npm
package called @jupyterlab/testutils
) that are used by many of the
tests.
For tests that rely on @jupyterlab/services
(starting kernels,
interacting with files, etc.), there are two options. If a simple
interaction is needed, the Mock
namespace exposed by testutils
has a number of mock implmentations (see testutils/src/mock.ts
). If
a full server interaction is required, use the JupyterServer
class.
We have a helper function called testEmission
to help with writing
tests that use Lumino
signals, as well as a framePromise
function to get a Promise
for a requestAnimationFrame
. We
sometimes have to set a sentinel value inside a Promise
and then
check that the sentinel was set if we need a promise to run without
blocking.
If you are making a change that might affect how long it takes to load JupyterLab in the browser, we recommend doing some performance testing using Lighthouse. It let's you easily compute a number of metrics, like page load time, for the site.
To use it, first build JupyterLab in dev mode:
jlpm run build:dev
Then, start JupyterLab using the dev build:
jupyter lab --dev --NotebookApp.token='' --no-browser
Now run Lighthouse against this local server and show the results:
jlpm run lighthouse --view
Lighthouse recommends using the system level
comcast tool to throttle
your network connection and emulate different scenarios. To use it,
first install that tool using go
:
go get github.com/tylertreat/comcast
Then, before you run Lighthouse, enable the throttling (this requires sudo):
run lighthouse:throttling:start
This enables the "WIFI (good)" preset of comcast, which should emulate loading JupyterLab over a local network.
Then run the lighthouse tests:
jlpm run lighthouse [...]
Then disable the throttling after you are done:
jlpm run lighthouse:throttling:stop
Performance results are usually only useful in comparison to other results. For that reason, we have included a comparison script that can take two lighthouse results and show the changes between them.
Let's say we want to compare the results of the production build of JupyterLab with the normal build. The production build minifies all the JavaScript, so should load a bit faster.
First, we build JupyterLab normally, start it up, profile it and save the results:
jlpm build:dev
jupyter lab --dev --NotebookApp.token='' --no-browser
# in new window
jlpm run lighthouse --output json --output-path normal.json
Then rebuild with the production build and retest:
jlpm run build:dev:prod
jupyter lab --dev --NotebookApp.token='' --no-browser
# in new window
jlpm run lighthouse --output json --output-path prod.json
Now we can use compare the two outputs:
jlpm run lighthouse:compare normal.json prod.json
This gives us a report of the relative differences between the audits in the two reports:
Resulting Output
normal.json
-> prod.json
To install and build the examples in the examples
directory:
jlpm run build:examples
To run a specific example, change to the examples directory (i.e.
examples/filebrowser
) and enter:
python main.py
All methods of building JupyterLab produce source maps. The source maps
should be available in the source files view of your browser's
development tools under the webpack://
header.
When running JupyterLab normally, expand the ~
header to see the
source maps for individual packages.
When running in --dev-mode
, the core packages are available under
packages/
, while the third party libraries are available under
~
. Note: it is recommended to use jupyter lab --watch --dev-mode
while debugging.
When running a test, the packages will be available at the top level
(e.g. application/src
), and the current set of test files available
under /src
. Note: it is recommended to use jlpm run watch
in the
test folder while debugging test options. See
above for more info.
The JupyterLab application is made up of two major parts:
- an npm package
- a Jupyter server extension (Python package)
Each part is named jupyterlab
. The :ref:`developer tutorial
documentation <developer-guide>`
provides additional architecture information.
The repository consists of many npm packages that are managed using the
lerna build tool. The npm package source files are in the packages/
subdirectory.
git clone https://github.com/jupyterlab/jupyterlab.git
cd jupyterlab
pip install -e .
jlpm
jlpm run build:packages
Rebuild
jlpm run clean
jlpm run build:packages
Documentation is written in Markdown and reStructuredText. In
particular, the documentation on our Read the Docs page is written in
reStructuredText. To ensure that the Read the Docs page builds, you'll
need to install the documentation dependencies with pip
:
pip install -r docs/requirements.txt
To test the docs run:
py.test --check-links -k .md . || py.test --check-links -k .md --lf .
The Read the Docs pages can be built using make
:
cd docs
make html
Or with jlpm
:
jlpm run docs
- The documentation should be written in the second person, referring to the reader as "you" and not using the first person plural "we." The author of the documentation is not sitting next to the user, so using "we" can lead to frustration when things don't work as expected.
- Avoid words that trivialize using JupyterLab such as "simply" or "just." Tasks that developers find simple or easy may not be for users.
- Write in the active tense, so "drag the notebook cells..." rather than "notebook cells can be dragged..."
- The beginning of each section should begin with a short (1-2 sentence) high-level description of the topic, feature or component.
- Use "enable" rather than "allow" to indicate what JupyterLab makes possible for users. Using "allow" connotes that we are giving them permission, whereas "enable" connotes empowerment.
Files are referrred to as either files or documents, depending on the context.
Documents are more human centered. If human viewing, interpretation, interaction is an important part of the experience, it is a document in that context. For example, notebooks and markdown files will often be referring to as documents unless referring to the file-ness aspect of it (e.g., the notebook filename).
Files are used in a less human-focused context. For example, we refer to files in relation to a file system or file name.
Activities can be either a document or another UI panel that is not file backed, such as terminals, consoles or the inspector. An open document or file is an activity in that it is represented by a panel that you can interact with.
- The generic content area of a tabbed UI is a panel, but prefer to refer to the more specific name, such as “File browser.” Tab bars have tabs which toggle panels.
- The menu bar contains menu items, which have their own submenus.
- The main work area can be referred to as the work area when the name is unambiguous.
- When describing elements in the UI, colloquial names are preferred (e.g., “File browser” instead of “Files panel”).
The majority of names are written in lower case. These names include:
- tab
- panel
- menu bar
- sidebar
- file
- document
- activity
- tab bar
- main work area
- file browser
- command palette
- cell inspector
- code console
The following sections of the user interface should be in title case, directly quoting a word in the UI:
- File menu
- Files tab
- Running panel
- Tabs panel
- Single-Document Mode
The capitalized words match the label of the UI element the user is clicking on because there does not exist a good colloquial name for the tool, such as “file browser” or “command palette”.
See :ref:`interface` for descriptions of elements in the UI.
The Jupyter server extension source files are in the jupyterlab/ subdirectory. To use this extension, make sure the Jupyter Notebook server version 4.3 or later is installed.
When you make a change to JupyterLab npm package source files, run:
jlpm run build
to build the changes, and then refresh your browser to see the changes.
To have the system build after each source file change, run:
jupyter lab --dev-mode --watch
There is a range of build utilities for maintaining the repository. To
get a suggested version for a library use
jlpm run get:dependency foo
. To update the version of a library
across the repo use jlpm run update:dependency foo ^latest
. To
remove an unwanted dependency use jlpm run remove:dependency foo
.
The key utility is jlpm run integrity
, which ensures the integrity
of the packages in the repo. It will:
- Ensure the core package version dependencies match everywhere.
- Ensure imported packages match dependencies.
- Ensure a consistent version of all packages.
- Manage the meta package.
The packages/metapackage
package is used to build all of the
TypeScript in the repository at once, instead of 50+ individual builds.
The integrity script also allows you to automatically add a dependency
for a package by importing from it in the TypeScript file, and then
running: jlpm run integrity
from the repo root.
We also have scripts for creating and removing packages in
packages/
, jlpm run create:package
and
jlpm run remove:package
. When creating a package, if it is meant to
be included in the core bundle, add the
jupyterlab: { coreDependency: true }
metadata to the
package.json
. Packages with extension
or mimeExtension
metadata are considered to be a core dependency unless they are
explicitly marked otherwise.
If you want to make changes to one of JupyterLab's external packages
(for example, Lumino and test
them out against your copy of JupyterLab, you can easily do so using the
link
command:
- Make your changes and then build the external package
- Register a link to the modified external package
- navigate to the external package dir and run
jlpm link
- navigate to the external package dir and run
- Link JupyterLab to modded package
- navigate to top level of your JupyterLab repo, then run
jlpm link "<package-of-interest>"
- navigate to top level of your JupyterLab repo, then run
You can then (re)build JupyterLab (eg jlpm run build
) and your
changes should be picked up by the build.
To restore JupyterLab to its original state, you use the unlink
command:
- Unlink JupyterLab and modded package
- navigate to top level of your JupyterLab repo, then run
jlpm unlink "<package-of-interest>"
- navigate to top level of your JupyterLab repo, then run
- Reinstall original version of the external package in JupyterLab
- run
jlpm install --check-files
- run
You can then (re)build JupyterLab and everything should be back to default.
If you're working on an external project with more than one package, you'll probably have to link in your copies of every package in the project, including those you made no changes to. Failing to do so may cause issues relating to duplication of shared state.
Specifically, when working with Lumino, you'll probably have to link
your copy of the "@lumino/messaging"
package (in addition to
whatever packages you actually made changes to). This is due to
potential duplication of objects contained in the MessageLoop
namespace provided by the messaging
package.
Typeset keyboard shortcuts as follows:
- Monospace typeface, with spaces between individual keys:
Shift Enter
. - For modifiers, use the platform independent word describing key:
Shift
. - For the
Accel
key use the phrase:Command/Ctrl
. - Don’t use platform specific icons for modifier keys, as they are difficult to display in a platform specific way on Sphinx/RTD.
Our documentation should contain screenshots and animations that illustrate and demonstrate the software. Here are some guidelines for preparing them:
- Make sure the screenshot does not contain copyrighted material (preferable), or the license is allowed in our documentation and clearly stated.
- If taking a png screenshot, use the Firefox or Chrome developer tools
to do the following:
- set the browser viewport to 1280x720 pixels
- set the device pixel ratio to 1:1 (i.e., non-hidpi, non-retina)
- screenshot the entire viewport using the browser developer tools. Screenshots should not include any browser elements such as the browser address bar, browser title bar, etc., and should not contain any desktop background.
- If creating a movie, adjust the settings as above (1280x720 viewport resolution, non-hidpi) and use a screen capture utility of your choice to capture just the browser viewport.
- For PNGs, reduce their size using
pngquant --speed 1 <filename>
. The resulting filename will have-fs8
appended, so make sure to rename it and use the resulting file. Commit the optimized png file to the main repository. Each png file should be no more than a few hundred kilobytes. - For movies, upload them to the IPython/Jupyter YouTube channel and
add them to the
jupyterlab-media
repository. To embed a movie in the documentation, use the
www.youtube-nocookie.com
website, which can be found by clicking on the 'privacy-enhanced' embedding option in the Share dialog on YouTube. Add the following parameters the end of the URL?rel=0&showinfo=0
. This disables the video title and related video suggestions. - Screenshots or animations should be preceded by a sentence describing the content, such as "To open a file, double-click on its name in the File Browser:".
- We have custom CSS that will add box shadows, and proper sizing of screenshots and embedded YouTube videos. See examples in the documentation for how to embed these assets.
To help us organize screenshots and animations, please name the files with a prefix that matches the names of the source file in which they are used:
sourcefile.rst sourcefile_filebrowser.png sourcefile_editmenu.png
This will help us to keep track of the images as documentation content evolves.
- By default, the application will load from the JupyterLab staging
directory (default is
<sys-prefix>/share/jupyter/lab/build
. If you wish to run the core application in<git root>/jupyterlab/build
, runjupyter lab --core-mode
. This is the core application that will be shipped. - If working with extensions, see the :ref:`extension documentation <developer_extensions>`.
- The npm modules are fully compatible with Node/Babel/ES6/ES5. Simply omit the type declarations when using a language other than TypeScript.