Skip to content

Commit 0e30011

Browse files
committed
Extension loading from classpath #71
to ensure that kernel is accessible in the notebook unconditonally (whether extensions loading is on or off), provide BaseKernel.notebookKernel() method and remove Extension lifecycle from BaseNotebookStatics and JavaNotebookStatics
1 parent bcbeb1a commit 0e30011

6 files changed

Lines changed: 54 additions & 109 deletions

File tree

jjava-jupyter/src/main/java/org/dflib/jjava/jupyter/kernel/BaseKernel.java

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262

6363
public abstract class BaseKernel {
6464

65+
protected static BaseKernel notebookKernel;
66+
6567
public static final String IS_COMPLETE_YES = "complete";
6668
public static final String IS_COMPLETE_BAD = "invalid";
6769
public static final String IS_COMPLETE_MAYBE = "unknown";
@@ -79,10 +81,21 @@ public abstract class BaseKernel {
7981
protected final MagicParser magicParser;
8082
protected final MagicsRegistry magicsRegistry;
8183
protected final Map<String, Extension> extensions;
84+
// this flag is for custom extensions only. It does not affect the default extensions that are loaded unconditionally
8285
protected final boolean extensionsEnabled;
8386
protected final StringStyler errorStyler;
8487
protected final AtomicInteger executionCount;
8588

89+
/**
90+
* Returns a non-null instance of the kernel associated with the current notebook. Throws an exception if called
91+
* outside the notebook lifecycle.
92+
*/
93+
public static BaseKernel notebookKernel() {
94+
return Objects.requireNonNull(
95+
BaseKernel.notebookKernel,
96+
"No kernel running. Likely called outside of the notebook lifecycle");
97+
}
98+
8699
protected BaseKernel(
87100
String name,
88101
String version,
@@ -283,9 +296,7 @@ public DisplayData eval(String expr) {
283296
* initializes kernel extensions.
284297
*/
285298
public void onStartup() {
286-
if (extensionsEnabled) {
287-
installDefaultExtensions();
288-
}
299+
installNotebookKernel();
289300
}
290301

291302
/**
@@ -297,7 +308,11 @@ public void onStartup() {
297308
* is likely to be started up again.
298309
*/
299310
public void onShutdown(boolean isRestarting) {
311+
uninstallExtension();
312+
uninstallNotebookKernel();
313+
}
300314

315+
protected void uninstallExtension() {
301316
Set<Extension> localExts = new HashSet<>(extensions.values());
302317
extensions.clear();
303318

@@ -311,12 +326,28 @@ public void onShutdown(boolean isRestarting) {
311326
}
312327
}
313328

329+
protected void installNotebookKernel() {
330+
if (BaseKernel.notebookKernel != null) {
331+
throw new IllegalStateException("A different notebook kernel was already started: " + BaseKernel.notebookKernel.getBanner());
332+
}
333+
334+
BaseKernel.notebookKernel = this;
335+
}
336+
337+
protected void uninstallNotebookKernel() {
338+
if (BaseKernel.notebookKernel != null && BaseKernel.notebookKernel != this) {
339+
throw new IllegalStateException("A different notebook kernel is running: " + BaseKernel.notebookKernel.getBanner());
340+
}
341+
342+
BaseKernel.notebookKernel = null;
343+
}
344+
314345
/**
315346
* Invoked when the kernel.json specifies an {@code interrupt_mode} of {@code message}
316347
* and the frontend requests an interrupt of the currently running cell.
317348
*/
318349
public void interrupt() {
319-
//no-op
350+
// no-op
320351
}
321352

322353
/**
@@ -546,14 +577,6 @@ protected ClassLoader getClassLoader() {
546577
return ClassLoader.getSystemClassLoader();
547578
}
548579

549-
/**
550-
* Locates, loads and initializes {@code Extension}s. Extension classes are discovered via {@link ServiceLoader},
551-
* using the kernel's default ClassLoader.
552-
*/
553-
protected void installDefaultExtensions() {
554-
installExtensionsFromClassLoader(getClassLoader());
555-
}
556-
557580
/**
558581
* Locates, loads and initializes {@code Extension}s. Extension classes are discovered via {@link ServiceLoader}.
559582
* It is passed a custom ClassLoader created internally based on a combination of the kernel ClassLoader
@@ -579,8 +602,13 @@ protected void installExtensionsFromClasspath(String classpath) {
579602
protected void installExtensionsFromClassLoader(ClassLoader classLoader) {
580603
ServiceLoader.load(Extension.class, classLoader).stream()
581604
.map(ServiceLoader.Provider::get)
582-
.filter(e -> extensions.putIfAbsent(e.getClass().getName(), e) == null)
583-
.forEach(e -> e.install(this));
605+
.forEach(this::installExtension);
606+
}
607+
608+
protected void installExtension(Extension ext) {
609+
if (extensions.putIfAbsent(ext.getClass().getName(), ext) == null) {
610+
ext.install(this);
611+
}
584612
}
585613

586614
private static URL pathToURL(String path) {
Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,27 @@
11
package org.dflib.jjava.jupyter.kernel;
22

3-
import org.dflib.jjava.jupyter.Extension;
43
import org.dflib.jjava.jupyter.kernel.display.DisplayData;
54
import org.dflib.jjava.jupyter.kernel.magic.UndefinedMagicException;
65

76
import java.util.List;
8-
import java.util.Objects;
97
import java.util.UUID;
108

119
/**
1210
* An automatically-loaded extension that exposes a collection of static methods for notebook code to interact with the
1311
* kernel.
1412
*/
15-
public class BaseNotebookStatics implements Extension {
16-
17-
private static BaseKernel kernel;
18-
19-
@Override
20-
public void install(BaseKernel kernel) {
21-
22-
if (BaseNotebookStatics.kernel != null) {
23-
throw new IllegalStateException("Already initialized with a different kernel: " + BaseNotebookStatics.kernel.getBanner());
24-
}
25-
26-
BaseNotebookStatics.kernel = kernel;
27-
}
28-
29-
@Override
30-
public void uninstall(BaseKernel kernel) {
31-
32-
if (BaseNotebookStatics.kernel != null && BaseNotebookStatics.kernel != kernel) {
33-
throw new IllegalStateException("Was initialized with a different kernel: " + BaseNotebookStatics.kernel.getBanner());
34-
}
35-
36-
BaseNotebookStatics.kernel = null;
37-
}
38-
39-
private static BaseKernel nonNullKernel() {
40-
return Objects.requireNonNull(
41-
BaseNotebookStatics.kernel,
42-
"No kernel running. Likely called outside of the notebook lifecycle");
43-
}
13+
public class BaseNotebookStatics {
4414

4515
public static void printf(String format, Object... args) {
4616
System.out.printf(format, args);
4717
}
4818

4919
public static Object eval(String expr) {
50-
return nonNullKernel().evalRaw(expr);
20+
return BaseKernel.notebookKernel().evalRaw(expr);
5121
}
5222

5323
public static <T> T lineMagic(String name, List<String> args) {
54-
BaseKernel kernel = nonNullKernel();
24+
BaseKernel kernel = BaseKernel.notebookKernel();
5525

5626
try {
5727
return kernel.getMagicsRegistry().evalLineMagic(kernel, name, args);
@@ -63,7 +33,7 @@ public static <T> T lineMagic(String name, List<String> args) {
6333
}
6434

6535
public static <T> T cellMagic(String name, List<String> args, String body) {
66-
BaseKernel kernel = nonNullKernel();
36+
BaseKernel kernel = BaseKernel.notebookKernel();
6737

6838
try {
6939
return kernel.getMagicsRegistry().evalCellMagic(kernel, name, args, body);
@@ -75,11 +45,11 @@ public static <T> T cellMagic(String name, List<String> args, String body) {
7545
}
7646

7747
public static DisplayData render(Object o) {
78-
return nonNullKernel().getRenderer().render(o);
48+
return BaseKernel.notebookKernel().getRenderer().render(o);
7949
}
8050

8151
public static DisplayData render(Object o, String... as) {
82-
return nonNullKernel().getRenderer().renderAs(o, as);
52+
return BaseKernel.notebookKernel().getRenderer().renderAs(o, as);
8353
}
8454

8555
public static String display(Object o) {
@@ -92,7 +62,7 @@ public static String display(Object o) {
9262
data.setDisplayId(id);
9363
}
9464

95-
nonNullKernel().display(data);
65+
BaseKernel.notebookKernel().display(data);
9666
return id;
9767
}
9868

@@ -105,17 +75,17 @@ public static String display(Object o, String... as) {
10575
data.setDisplayId(id);
10676
}
10777

108-
nonNullKernel().display(data);
78+
BaseKernel.notebookKernel().display(data);
10979
return id;
11080
}
11181

11282
public static void updateDisplay(String id, Object o) {
11383
DisplayData data = render(o);
114-
nonNullKernel().getIO().display.updateDisplay(id, data);
84+
BaseKernel.notebookKernel().getIO().display.updateDisplay(id, data);
11585
}
11686

11787
public static void updateDisplay(String id, Object o, String... as) {
11888
DisplayData data = render(o, as);
119-
nonNullKernel().getIO().display.updateDisplay(id, data);
89+
BaseKernel.notebookKernel().getIO().display.updateDisplay(id, data);
12090
}
12191
}

jjava-jupyter/src/main/resources/META-INF/services/org.dflib.jjava.jupyter.Extension

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,12 @@
11
package org.dflib.jjava.kernel;
22

3-
import org.dflib.jjava.jupyter.Extension;
43
import org.dflib.jjava.jupyter.kernel.BaseKernel;
54

6-
import java.util.Objects;
7-
85
/**
96
* An automatically-loaded extension that exposes a collection of static methods for notebook code to interact with the
107
* kernel.
118
*/
12-
public class JavaNotebookStatics implements Extension {
13-
14-
static JavaKernel kernel;
15-
16-
@Override
17-
public void install(BaseKernel kernel) {
18-
19-
if (JavaNotebookStatics.kernel != null) {
20-
throw new IllegalStateException("Already initialized with a different kernel: " + JavaNotebookStatics.kernel.getBanner());
21-
}
22-
23-
if (kernel instanceof JavaKernel) {
24-
JavaNotebookStatics.kernel = (JavaKernel) kernel;
25-
} else {
26-
// TODO: should we have some kind of abstract logger?
27-
System.err.println("Ignoring unexpected kernel '" + kernel.getClass().getName() + "'. Only '" + JavaKernel.class.getName() + "' is supported.");
28-
}
29-
}
30-
31-
@Override
32-
public void uninstall(BaseKernel kernel) {
33-
34-
if (JavaNotebookStatics.kernel != null && JavaNotebookStatics.kernel != kernel) {
35-
throw new IllegalStateException("Was initialized with a different kernel: " + JavaNotebookStatics.kernel.getBanner());
36-
}
37-
38-
JavaNotebookStatics.kernel = null;
39-
}
9+
public class JavaNotebookStatics {
4010

4111
/**
4212
* @deprecated in favor of {@link #kernel()}
@@ -49,8 +19,6 @@ public static JavaKernel getKernelInstance() {
4919
}
5020

5121
public static JavaKernel kernel() {
52-
return Objects.requireNonNull(
53-
JavaNotebookStatics.kernel,
54-
"No Java kernel running. Likely called outside of the notebook lifecycle");
22+
return (JavaKernel) BaseKernel.notebookKernel();
5523
}
5624
}

jjava-kernel/src/main/resources/META-INF/services/org.dflib.jjava.jupyter.Extension

Lines changed: 0 additions & 1 deletion
This file was deleted.

jjava-kernel/src/test/java/org/dflib/jjava/kernel/JavaKernelExtensionsLifecycleTest.java

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,6 @@
1010

1111
public class JavaKernelExtensionsLifecycleTest {
1212

13-
@Test
14-
public void defaultExtension() {
15-
assertNull(JavaNotebookStatics.kernel);
16-
17-
JavaKernel kernel = JavaKernel.builder().name("TestKernel").build();
18-
assertNull(JavaNotebookStatics.kernel, "Kernel should not have loaded any extensions as of creation");
19-
20-
try {
21-
kernel.onStartup();
22-
assertNotNull(JavaNotebookStatics.kernel, "Built-in extension was not installed");
23-
} finally {
24-
kernel.onShutdown(false);
25-
}
26-
27-
assertNull(JavaNotebookStatics.kernel);
28-
}
29-
3013
@Test
3114
public void extraClasspathExtension() throws Exception {
3215
Path extensionJar = TestJarFactory.buildJar(
@@ -46,7 +29,6 @@ public void extraClasspathExtension() throws Exception {
4629
.build();
4730
try {
4831
kernel.onStartup();
49-
assertNotNull(JavaNotebookStatics.kernel, "Built-in extension was not installed");
5032
assertNull(System.getProperty(extInstalledProp), "Custom extension should not be installed yet");
5133

5234
kernel.addToClasspath(extraClasspath);
@@ -58,7 +40,6 @@ public void extraClasspathExtension() throws Exception {
5840
kernel.onShutdown(false);
5941
}
6042

61-
assertNull(JavaNotebookStatics.kernel);
6243
assertNull(System.getProperty(extInstalledProp));
6344
}
6445

0 commit comments

Comments
 (0)