Generally, there are two ways of adding new library:
To support new JVM
library and make it available via %use
magic command you need to create a library descriptor for it.
Check libraries repository to see examples of library descriptors.
Library descriptor is a <libName>.json
file with the following fields:
properties
: a dictionary of properties that are used within library descriptordescription
: a short library description which is used for generating libraries list in READMElink
: a link to library homepage. This link will be displayed in:help
commandminKernelVersion
: a minimal version of Kotlin kernel which may be used with this descriptorrepositories
: a list of maven or ivy repositories to search for dependenciesdependencies
: a list of library dependenciesimports
: a list of default imports for libraryinit
: a list of code snippets to be executed when library is includedinitCell
: a list of code snippets to be executed before execution of any cellshutdown
: a list of code snippets to be executed on kernel shutdown. Any cleanup code goes hererenderers
: a mapping from fully qualified names of types to be rendered to the Kotlin expression returning output value. Source object is referenced as$it
resources
: a list of JS/CSS resources. See this descriptor for example
*All fields are optional
For the most relevant specification see org.jetbrains.kotlinx.jupyter.libraries.LibraryDescriptor
class.
Name of the file should have the <name>.json
format where <name>
is an argument for '%use' command
Library properties can be used in any parts of library descriptor as $property
To register new library descriptor:
- For private usage - create it anywhere on your computer and reference it using file syntax.
- Alternative way for private usage - create descriptor in
.jupyter_kotlin/libraries
folder and reference it using "default" syntax - For sharing with community - commit it to libraries repository and create pull request.
If you are maintaining some library and want to update your library descriptor, create pull request with your update.
After your request is accepted, new version of your library will be available to all Kotlin Jupyter users
immediately on next kernel startup (no kernel update is needed) - but only if they use %useLatestDescriptors
magic.
If not, kernel update is needed.
You may also add a Kotlin kernel integration to your library using a Gradle plugin.
In the following code snippets <jupyterApiVersion>
is one of the published versions from the link above.
It is encouraged to use the latest stable version.
First, add the plugin dependency into your buildscript.
For build.gradle
:
plugins {
id "org.jetbrains.kotlin.jupyter.api" version "<jupyterApiVersion>"
}
For build.gradle.kts
:
plugins {
kotlin("jupyter.api") version "<jupyterApiVersion>"
}
This plugin adds following dependencies to your project:
Artifact | Gradle option to exclude/include | Enabled by default | Dependency scope | Method for adding dependency manually |
---|---|---|---|---|
kotlin-jupyter-api |
kotlin.jupyter.add.api |
yes | compileOnly |
addApiDependency(version: String?) |
kotlin-jupyter-api-annotations |
kotlin.jupyter.add.scanner |
no | compileOnly |
addScannerDependency(version: String?) |
kotlin-jupyter-test-kit |
kotlin.jupyter.add.testkit |
yes | testImplementation |
addTestKitDependency(version: String?) |
You may turn on / turn off the dependency with its default version (version of the plugin)
by setting corresponding Gradle option to true
or false
.
If the corresponding option is set to false
(by default or in your setup), you still
can add it manually using the method from the table inside kotlinJupyter
extension like that:
kotlinJupyter {
addApiDependency() // Use default version
addApiDependency("0.10.0.1") // Use custom artifact version
}
If you are OK with using KSP, you can use annotations to mark integration classes.
First, enable kotlin-jupyter-api-annotations
dependency by adding following line to your gradle.properties
:
kotlin.jupyter.add.scanner = true
Then, implement org.jetbrains.kotlinx.jupyter.api.libraries.LibraryDefinitionProducer
or
org.jetbrains.kotlinx.jupyter.api.libraries.LibraryDefinition
and mark implementation with
JupyterLibrary
annotation:
package org.my.lib
import org.jetbrains.kotlinx.jupyter.api.annotations.JupyterLibrary
import org.jetbrains.kotlinx.jupyter.api.*
import org.jetbrains.kotlinx.jupyter.api.libraries.*
@JupyterLibrary
internal class Integration : JupyterIntegration() {
override fun Builder.onLoaded() {
render<MyClass> { HTML(it.toHTML()) }
import("org.my.lib.*")
import("org.my.lib.io.*")
}
}
For more complicated example see integration of dataframe library.
For a further information see docs for:
org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
org.jetbrains.kotlinx.jupyter.api.libraries.LibraryDefinitionProducer
org.jetbrains.kotlinx.jupyter.api.libraries.LibraryDefinition
You may want not to use KSP plugin for implementations detection. Then you may refer your implementations right in your buildscript. Note that no checking for existence will be performed in this case.
The following example shows how to refer aforementioned Integration
class in your buildscript.
Obviously, in this case you shouldn't mark it with JupyterLibrary
annotation.
For build.gradle
:
processJupyterApiResources {
libraryProducers = ["org.my.lib.Integration"]
}
For build.gradle.kts
:
tasks.processJupyterApiResources {
libraryProducers = listOf("org.my.lib.Integration")
}
You may want to automatically check if your library integrates correctly into kernel. To achieve this, inherit your
test class from org.jetbrains.kotlinx.jupyter.testkit.JupyterReplTestCase
and use its methods to execute cells.
Your library integration descriptors should be already on classpath and will be loaded automatically by the test logic,
you don't need to use %use
magic or DependsOn
annotation to switch on your library. But you may use magics and
annotations for other purposes, as usual.
The artifact containing test templates is included automatically into testImplementation
configuration if you
use the Gradle plugin. You may turn this behavior off by setting kotlin.jupyter.add.testkit
Gradle property
to false
. If you want to include this artifact into your build manually, you'll find the instructions
here.
For the examples of integration testing see org.jetbrains.kotlinx.jupyter.testkit.test.JupyterReplTestingTest
in
this repository or related tests in DataFrame.
If you don't use Gradle as a build system, there is an alternative way.
First, add org.jetbrains.kotlinx:kotlin-jupyter-api:<jupyterApiVersion>
as
a compile dependency. See configuration instructions for different build systems
here
Then add one or more integration classes. They may be derived from
LibraryDefinitionProducer
or from LibraryDefinition
as described above.
Note that you don't need @JupyterLibrary
annotation in this scenario.
Finally, add file META-INF/kotlin-jupyter-libraries/libraries.json
to the JAR
resources. This file should contain FQNs of all integration classes in the JSON form:
{
"definitions":[],
"producers": [
{ "fqn" : "org.jetbrains.kotlinx.jupyter.example.GettingStartedIntegration" }
]
}
Classes derived from LibraryDefinition
should be added to the definitions
array.
Classes derived from LibraryDefinitionProducer
should be added to the producers
array.