Skip to content

Commit deac00e

Browse files
committed
Add automatic libraries list generation
1 parent 4175906 commit deac00e

32 files changed

+374
-22
lines changed

build.gradle.kts

+43-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class TaskOptions: AllOptions {
2929
println("##teamcity[buildNumber '$version']")
3030
artifactsDir
3131
}()
32+
override val readmePath: Path = rootPath.resolve("docs").resolve("README.md")
3233

3334
private val installPath = rootProject.findProperty("installPath") as String?
3435

@@ -249,7 +250,7 @@ with(ProjectWithOptionsImpl(project, TaskOptions())) {
249250
)
250251
}
251252

252-
tasks.register("buildProperties") {
253+
val buildProperties by tasks.registering {
253254
group = buildGroup
254255
val outputDir = file(getSubDir(buildDir.toPath(), resourcesDir, mainSourceSetDir))
255256

@@ -277,7 +278,47 @@ with(ProjectWithOptionsImpl(project, TaskOptions())) {
277278
}
278279

279280
tasks.processResources {
280-
dependsOn("buildProperties")
281+
dependsOn(buildProperties)
282+
}
283+
284+
val readmeFile = readmePath.toFile()
285+
val readmeStubFile = rootPath.resolve("docs").resolve("README-STUB.md").toFile()
286+
val librariesDir = File(librariesPath)
287+
val readmeGenerator = ReadmeGenerator(librariesDir)
288+
289+
val generateReadme by tasks.registering {
290+
group = buildGroup
291+
292+
readmeFile.parentFile.mkdirs()
293+
294+
inputs.file(readmeStubFile)
295+
inputs.dir(librariesDir)
296+
outputs.file(readmeFile)
297+
298+
doLast {
299+
readmeGenerator.generate(readmeStubFile, readmeFile)
300+
}
301+
}
302+
303+
val checkReadme by tasks.registering {
304+
group = "verification"
305+
306+
inputs.file(readmeStubFile)
307+
inputs.dir(librariesDir)
308+
inputs.file(readmeFile)
309+
310+
doLast {
311+
val tempFile = createTempFile("kotlin-jupyter-readme")
312+
tempFile.deleteOnExit()
313+
readmeGenerator.generate(readmeStubFile, tempFile)
314+
if(tempFile.readText() != readmeFile.readText()) {
315+
throw AssertionError("Readme is not regenerated. Regenerate it using `./gradlew ${generateReadme.name}` command")
316+
}
317+
}
318+
}
319+
320+
tasks.check {
321+
dependsOn(checkReadme)
281322
}
282323

283324
createCleanTasks()

buildSrc/build.gradle.kts

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2-
31
plugins {
42
`kotlin-dsl`
53
}

buildSrc/src/main/kotlin/distTasks.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ fun ProjectWithOptions.prepareDistributionTasks() {
3434
dependsOn("installHintRemoverRequirements")
3535
}
3636
from(distributionPath)
37-
from("README.md")
37+
from(readmePath)
3838
into(distribBuildPath)
3939
exclude(".idea/**")
4040

buildSrc/src/main/kotlin/install.kt

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface BuildOptions {
1414
val rootPath: Path
1515

1616
val artifactsDir: Path
17+
val readmePath: Path
1718
}
1819

1920
interface ProjectWithBuildOptions: Project, BuildOptions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import groovy.json.JsonSlurper
2+
import java.io.File
3+
4+
class ReadmeGenerator(
5+
private val librariesDir: File
6+
) {
7+
fun generate(stub: File, destination: File) {
8+
var result = stub.readText()
9+
for ((stubName, processor) in processors) {
10+
result = result.replace("[[$stubName]]", processor())
11+
}
12+
destination.writeText(result)
13+
}
14+
15+
private val processors: Map<String, () -> String> = mapOf(
16+
"supported_libraries" to ::processSupportedLibraries
17+
)
18+
19+
private fun processSupportedLibraries(): String {
20+
val libraryFiles =
21+
librariesDir.listFiles { file -> file.isFile && file.name.endsWith(".json") } ?: emptyArray()
22+
23+
val sortedMap = sortedMapOf<String, String>()
24+
25+
return libraryFiles.toList().map { file ->
26+
val libraryName = file.nameWithoutExtension
27+
val json = JsonSlurper().parse(file) as Map<*, *>
28+
val link = json["link"] as? String
29+
val description = json["description"] as? String
30+
31+
val namePart = if (link == null) libraryName else "[$libraryName]($link)"
32+
val descriptionPart = if (description == null) "" else " - $description"
33+
libraryName to " - $namePart$descriptionPart"
34+
}.toMap(sortedMap).values.joinToString("\n")
35+
}
36+
}

docs/README-STUB.md

+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
[![JetBrains official project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
2+
[![PyPI](https://img.shields.io/pypi/v/kotlin-jupyter-kernel?label=PyPi)](https://pypi.org/project/kotlin-jupyter-kernel/)
3+
[![Anaconda](https://img.shields.io/conda/v/jetbrains/kotlin-jupyter-kernel?label=Anaconda)](https://anaconda.org/jetbrains/kotlin-jupyter-kernel)
4+
[![GitHub](https://img.shields.io/github/license/Kotlin/kotlin-jupyter)](https://www.apache.org/licenses/LICENSE-2.0)
5+
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/kotlin/kotlin-jupyter/master?filepath=samples)
6+
7+
# Kotlin kernel for IPython/Jupyter
8+
9+
[Kotlin](https://kotlinlang.org/) (1.4.0) kernel for [Jupyter](https://jupyter.org).
10+
11+
Alpha version. Tested with Jupyter 6.0.1 on OS X so far.
12+
13+
![Screenshot in Jupyter](Screenshot.png)
14+
15+
To start using Kotlin kernel for Jupyter take a look at [introductory guide](https://github.com/cheptsov/kotlin-jupyter-demo/blob/master/index.ipynb).
16+
17+
Example notebooks can be found in the [samples](../samples) folder
18+
19+
Try samples online: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/kotlin/kotlin-jupyter/master?filepath=samples)
20+
21+
## Installation
22+
23+
There are three ways to install kernel:
24+
25+
### Conda
26+
27+
If you have `conda` installed, just run the following command to install stable package version:
28+
29+
`conda install -c jetbrains kotlin-jupyter-kernel` ([package home](https://anaconda.org/jetbrains/kotlin-jupyter-kernel))
30+
31+
To install conda package from the dev channel:
32+
33+
`conda install -c jetbrains-dev kotlin-jupyter-kernel` ([package home](https://anaconda.org/jetbrains-dev/kotlin-jupyter-kernel))
34+
35+
Uninstall: `conda remove kotlin-jupyter-kernel`
36+
37+
### Pip
38+
39+
You can also install this package through `pip`:
40+
41+
Stable:
42+
`pip install kotlin-jupyter-kernel` ([package home](https://pypi.org/project/kotlin-jupyter-kernel/))
43+
44+
Dev:
45+
`pip install -i https://test.pypi.org/simple/ kotlin-jupyter-kernel` ([package home](https://test.pypi.org/project/kotlin-jupyter-kernel/))
46+
47+
Uninstall: `pip uninstall kotlin-jupyter-kernel`
48+
49+
### From sources
50+
51+
```bash
52+
git clone https://github.com/Kotlin/kotlin-jupyter.git
53+
cd kotlin-jupyter
54+
./gradlew install
55+
```
56+
57+
Default installation path is `~/.ipython/kernels/kotlin/`. To install to some other location use option `-PinstallPath=`, but note that Jupyter looks for kernel specs files only in predefined places
58+
59+
Uninstall: `./gradlew uninstall`
60+
61+
## Usage
62+
63+
- `jupyter console --kernel=kotlin`
64+
- `jupyter notebook`
65+
- `jupyter lab`
66+
67+
To start using `kotlin` kernel inside Jupyter Notebook or JupyterLab create a new notebook with `kotlin` kernel.
68+
69+
## Supported functionality
70+
71+
### REPL commands
72+
73+
The following REPL commands are supported:
74+
- `:help` - displays REPL commands help
75+
- `:classpath` - displays current classpath
76+
77+
### Dependencies resolving annotations
78+
79+
It is possible to add dynamic dependencies to the notebook using the following annotations:
80+
- `@file:DependsOn(<coordinates>)` - adds artifacts to classpath. Supports absolute and relative paths to class directories or jars, ivy and maven artifacts represented by colon separated string
81+
- `@file:Repository(<absolute-path>)` - adds a directory for relative path resolution or ivy/maven repository.
82+
To specify Maven local, use `@file:Repository("*mavenLocal")`.
83+
84+
Note that dependencies in remote repositories are resolved via Ivy resolver.
85+
Caches are stored in `~/.ivy2/cache` folder by default. Sometimes, due to network
86+
issues or running several artifacts resolutions in parallel, caches may get corrupted.
87+
If you have some troubles with artifacts resolution, please remove caches, restart kernel
88+
and try again.
89+
90+
### Default repositories
91+
92+
The following maven repositories are included by default:
93+
- [Bintray JCenter](https://jcenter.bintray.com)
94+
- [Maven Central](https://repo.maven.apache.org/maven2)
95+
- [JitPack](https://jitpack.io/)
96+
97+
### Line Magics
98+
99+
The following line magics are supported:
100+
- `%use <lib1>, <lib2> ...` - injects code for supported libraries: artifact resolution, default imports, initialization code, type renderers
101+
- `%trackClasspath` - logs any changes of current classpath. Useful for debugging artifact resolution failures
102+
- `%trackExecution` - logs pieces of code that are going to be executed. Useful for debugging of libraries support
103+
- `%useLatestDescriptors` - use latest versions of library descriptors available. By default, bundled descriptors are used
104+
- `%output [options]` - output capturing settings.
105+
106+
See detailed info about line magics [here](magics.md).
107+
108+
### Supported Libraries
109+
110+
When a library is included with `%use` keyword, the following functionality is added to the notebook:
111+
- repositories to search for library artifacts
112+
- artifact dependencies
113+
- default imports
114+
- library initialization code
115+
- renderers for special types, e.g. charts and data frames
116+
117+
This behavior is defined by `json` library descriptor. Descriptors for all supported libraries can be found in [libraries](../libraries) directory.
118+
A library descriptor may provide a set of properties with default values that can be overridden when library is included.
119+
The major use case for library properties is to specify particular version of library. If descriptor has only one property, it can be
120+
defined without naming:
121+
```
122+
%use krangl(0.10)
123+
```
124+
If library descriptor defines more than one property, property names should be used:
125+
```
126+
%use spark(scala=2.11.10, spark=2.4.2)
127+
```
128+
Several libraries can be included in single `%use` statement, separated by `,`:
129+
```
130+
%use lets-plot, krangl, mysql(8.0.15)
131+
```
132+
You can also specify the source of library descriptor. By default, it's downloaded from the latest commit on the
133+
branch which kernel was built from. If you want to try descriptor from another revision, use the following syntax:
134+
```
135+
// Specify tag
136+
%use lets-plot@0.8.2.5
137+
// Specify commit sha, with more verbose syntax
138+
%use lets-plot@ref[24a040fe22335648885b106e2f4ddd63b4d49469]
139+
// Specify git ref along with library arguments
140+
%use krangl@dev(0.10)
141+
```
142+
Other options are resolving library descriptor from a local file or from remote URL:
143+
```
144+
// Load library from file
145+
%use mylib@file[/home/user/lib.json]
146+
// Load library from file: kernel will guess it's a file actually
147+
%use @/home/user/libs/lib.json
148+
// Or use another approach: specify a directory and file name without
149+
// extension (it should be JSON in such case) before it
150+
%use lib@/home/user/libs
151+
// Load library descriptor from a remote URL
152+
%use herlib@url[https://site.com/lib.json]
153+
// If your URL responds with 200(OK), you may skip `url[]` part:
154+
%use @https://site.com/lib.json
155+
// You may omit library name for file and URL resolution:
156+
%use @file[lib.json]
157+
```
158+
159+
List of supported libraries:
160+
[[supported_libraries]]
161+
162+
### Rich output
163+
164+
By default the return values from REPL statements are displayed in the text form. To use richer representations, e.g.
165+
to display graphics or html, it is possible to send MIME-encoded result to the client using the `MIME` helper function:
166+
```kotlin
167+
fun MIME(vararg mimeToData: Pair<String, Any>): MimeTypedResult
168+
```
169+
E.g.:
170+
```kotlin
171+
MIME("text/html" to "<p>Some <em>HTML</em></p>", "text/plain" to "No HTML for text clients")
172+
173+
```
174+
HTML outputs can also be rendered with `HTML` helper function:
175+
```kotlin
176+
fun HTML(text: String): MimeTypedResult
177+
```
178+
179+
### Autocompletion
180+
181+
Press `TAB` to get the list of suggested items for completion. In Jupyter Notebook, you don't need to press `TAB`,
182+
completion is requested automatically. Completion works for all globally defined symbols and for local symbols
183+
which were loaded into notebook during cells evaluation.
184+
185+
### Error analysis
186+
187+
If you use Jupyter Notebook as Jupyter client, you will also see that compilation errors and warnings are underlined in
188+
red and in yellow correspondingly. This is achieved by kernel-level extension of Jupyter notebook which sends
189+
error-analysis requests to kernel and renders their results. If you hover the cursor over underlined text, you will get
190+
an error message which can help you to fix the error.
191+
192+
## Debugging
193+
194+
1. Run `./gradlew installDebug`. Use option `-PdebugPort=` to specify port address for debugger. Default port is 1044.
195+
2. Run `jupyter-notebook`
196+
3. Attach remote debugger to JVM with specified port
197+
198+
## Adding new libraries
199+
200+
To support new `JVM` library and make it available via `%use` magic command you need to create a library descriptor for it.
201+
202+
Check [libraries](../libraries) directory to see examples of library descriptors.
203+
204+
Library descriptor is a `<libName>.json` file with the following fields:
205+
- `properties`: a dictionary of properties that are used within library descriptor
206+
- `description`: a short library description which is used for generating libraries list in README
207+
- `link`: a link to library homepage. This link will be displayed in `:help` command
208+
- `minKernelVersion`: a minimal version of Kotlin kernel which may be used with this descriptor
209+
- `repositories`: a list of maven or ivy repositories to search for dependencies
210+
- `dependencies`: a list of library dependencies
211+
- `imports`: a list of default imports for library
212+
- `init`: a list of code snippets to be executed when library is included
213+
- `initCell`: a list of code snippets to be executed before execution of any cell
214+
- `shutdown`: a list of code snippets to be executed on kernel shutdown. Any cleanup code goes here
215+
- `renderers`: a list of type converters for special rendering of particular types
216+
217+
*All fields are optional
218+
219+
Fields for type renderer:
220+
- `class`: fully-qualified class name for the type to be rendered
221+
- `result`: expression that produces output value. Source object is referenced as `$it`
222+
223+
Name of the file is a library name that is passed to '%use' command
224+
225+
Library properties can be used in any parts of library descriptor as `$property`
226+
227+
To register new library descriptor:
228+
1. For private usage - add it to local settings folder `<UserHome>/.jupyter_kotlin/libraries`
229+
2. For sharing with community - commit it to [libraries](../libraries) directory and create pull request.
230+
231+
If you are maintaining some library and want to update your library descriptor, just create pull request with your update. After your request is accepted,
232+
new version of your library will be available to all Kotlin Jupyter users immediately on next kernel startup (no kernel update is needed).
233+
234+
If a library descriptor with the same name is found in several locations, the following resolution priority is used:
235+
1. Local settings folder (highest priority)
236+
2. [libraries](../libraries) directory at the latest master branch of `https://github.com/Kotlin/kotlin-jupyter` repository
237+
3. Kernel installation directory
238+
239+
If you don't want some library to be updated automatically, put fixed version of its library descriptor into local settings folder.

0 commit comments

Comments
 (0)