Skip to content

Commit 9704a79

Browse files
committed
"kernel.evalBuilder(..)" #111
1 parent 77ae079 commit 9704a79

13 files changed

Lines changed: 121 additions & 71 deletions

File tree

RELEASE-NOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* #105 Exploding classpath
77
* #107 "java.class.path" uses an incorrect separator
88
* #110 Magic return values are suppressed
9+
* #111 "kernel.evalBuilder(..)"
910

1011
## 1.0-a6
1112

jjava-distro/src/main/java/org/dflib/jjava/distro/JJava.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public static void main(String[] args) throws Exception {
7979
kernel.addToClasspath(Env.extraClasspath());
8080

8181
// run user defined startup snippets explicitly after the default startup
82-
Env.startupSnippets().forEach(kernel::evalRaw);
82+
Env.startupSnippets().forEach(s -> kernel.evalBuilder(s).resolveMagics().eval());
8383

8484
// connect to Jupyter
8585
kernel.becomeHandlerForConnection(connection);

jjava-distro/src/main/java/org/dflib/jjava/distro/NotebookInitializer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ public class NotebookInitializer implements Extension {
2929

3030
@Override
3131
public void install(BaseKernel kernel) {
32-
kernel.eval(STARTUP_SCRIPT);
32+
kernel.evalBuilder(STARTUP_SCRIPT).resolveMagics().eval();
3333
}
3434
}

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

Lines changed: 22 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -198,41 +198,33 @@ public void display(DisplayData data) {
198198
}
199199

200200
/**
201-
* Same as {@link #eval(String)}, but not applying the renderer to evaluation result.
201+
* @deprecated unused, replaced with {@link #evalBuilder(String)} pipeline.
202202
*/
203-
public abstract Object evalRaw(String source);
203+
@Deprecated(forRemoval = true)
204+
public DisplayData eval(String source) {
205+
return evalBuilder(source).resolveMagics().renderResults().eval();
206+
}
204207

205208
/**
206-
* Evaluates a code expression in the kernel's language environment and returns the result
207-
* as display data. This is the core evaluation method called when executing code cells
208-
* in a Jupyter notebook.
209-
*
210-
* <p>The implementation should:
211-
* <ul>
212-
* <li>Parse and evaluate the provided expression string</li>
213-
* <li>Convert the evaluation result into appropriate display data formats</li>
214-
* <li>Handle any language-specific evaluation context/scope</li>
215-
* </ul>
216-
*
217-
* <p>The returned {@link DisplayData} can contain multiple representations of the result
218-
* (e.g. text/plain, text/html, image/png) to allow rich display in the notebook.
219-
* Return null if the expression produces no displayable result.
220-
*
221-
* @param expr The code expression to evaluate as received from the Jupyter frontend
222-
* @return A {@link DisplayData} object containing the evaluation result in one or more
223-
* MIME formats, or null if there is no displayable result
209+
* @deprecated unused, replaced with {@link #evalBuilder(String)} pipeline.
224210
*/
225-
public DisplayData eval(String expr) {
226-
Object result = evalRaw(expr);
227-
if (result == null) {
228-
return null;
229-
}
211+
@Deprecated(forRemoval = true)
212+
public Object evalRaw(String source) {
213+
return evalBuilder(source).resolveMagics().eval();
214+
}
230215

231-
return result instanceof DisplayData
232-
? (DisplayData) result
233-
: getRenderer().render(result);
216+
/**
217+
* Creates and returns a builder for an evaluation pipeline.
218+
*/
219+
public <T> SimpleEvalBuilder<T> evalBuilder(String source) {
220+
return new SimpleEvalBuilder<>(this, source);
234221
}
235222

223+
/**
224+
* Evaluates the source code in a way appropriate for a given kernel subclass.
225+
*/
226+
protected abstract Object doEval(String source);
227+
236228
/**
237229
* Inspect the code to get things such as documentation for a function. This is
238230
* triggered by {@code shift-tab} in the Jupyter notebook which opens a tooltip displaying
@@ -360,21 +352,7 @@ public void interrupt() {
360352
// no-op
361353
}
362354

363-
/**
364-
* Formats an error into a human friendly format. The default implementation prints
365-
* the stack trace as written by {@link Throwable#printStackTrace()} with a dividing
366-
* separator as a prefix.
367-
* <p>
368-
* Subclasses may override this method write better messages for specific errors but
369-
* may choose to still use this to display the stack trace. In this case it is recommended
370-
* to add the output of this call to the end of the output list.
371-
*
372-
* @param e the error to format
373-
* @return a list of lines that make up the formatted error. This format should
374-
* not include strings with newlines but rather separate strings each to go on a
375-
* new line.
376-
*/
377-
public List<String> formatError(Throwable e) {
355+
protected List<String> formatError(Throwable e) {
378356
List<String> lines = new ArrayList<>();
379357
lines.add(this.errorStyler.secondary("---------------------------------------------------------------------------"));
380358

@@ -389,12 +367,6 @@ public List<String> formatError(Throwable e) {
389367
return lines;
390368
}
391369

392-
/*
393-
* ===================================
394-
* | Default handler implementations |
395-
* ===================================
396-
*/
397-
398370
public void becomeHandlerForConnection(JupyterConnection connection) {
399371
connection.setHandler(MessageType.EXECUTE_REQUEST, this::handleExecuteRequest);
400372
connection.setHandler(MessageType.INSPECT_REQUEST, this::handleInspectRequest);
@@ -446,7 +418,7 @@ protected synchronized void handleExecuteRequest(ShellReplyEnvironment env, Mess
446418
this.io.setJupyterInEnabled(request.isStdinEnabled());
447419

448420
try {
449-
DisplayData out = eval(request.getCode());
421+
DisplayData out = evalBuilder(request.getCode()).resolveMagics().renderResults().eval();
450422

451423
if (out != null) {
452424
PublishExecuteResult result = new PublishExecuteResult(count, out);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ public static void printf(String format, Object... args) {
1616
System.out.printf(format, args);
1717
}
1818

19-
public static Object eval(String expr) {
20-
return BaseKernel.notebookKernel().evalRaw(expr);
19+
public static Object eval(String source) {
20+
return BaseKernel.notebookKernel().evalBuilder(source).resolveMagics().eval();
2121
}
2222

2323
public static <T> T lineMagic(String name, List<String> args) {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.dflib.jjava.jupyter.kernel;
2+
3+
import org.dflib.jjava.jupyter.kernel.display.DisplayData;
4+
5+
public interface EvalBuilder<T> {
6+
7+
EvalBuilder<T> resolveMagics();
8+
9+
EvalBuilder<DisplayData> renderResults();
10+
11+
T eval();
12+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.dflib.jjava.jupyter.kernel;
2+
3+
import org.dflib.jjava.jupyter.kernel.display.DisplayData;
4+
5+
public class RenderedEvalBuilder implements EvalBuilder<DisplayData> {
6+
7+
private final BaseKernel kernel;
8+
private final EvalBuilder<?> delegate;
9+
10+
protected RenderedEvalBuilder(BaseKernel kernel, EvalBuilder<?> delegate) {
11+
this.kernel = kernel;
12+
this.delegate = delegate;
13+
}
14+
15+
@Override
16+
public EvalBuilder<DisplayData> renderResults() {
17+
return this;
18+
}
19+
20+
@Override
21+
public RenderedEvalBuilder resolveMagics() {
22+
return new RenderedEvalBuilder(kernel, delegate.resolveMagics());
23+
}
24+
25+
@Override
26+
public DisplayData eval() {
27+
Object o = delegate.eval();
28+
29+
if (o == null) {
30+
return null;
31+
}
32+
33+
return o instanceof DisplayData
34+
? (DisplayData) o
35+
: kernel.getRenderer().render(o);
36+
}
37+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.dflib.jjava.jupyter.kernel;
2+
3+
import org.dflib.jjava.jupyter.kernel.display.DisplayData;
4+
5+
public class SimpleEvalBuilder<T> implements EvalBuilder<T> {
6+
7+
private final BaseKernel kernel;
8+
private final String source;
9+
10+
protected SimpleEvalBuilder(BaseKernel kernel, String source) {
11+
this.kernel = kernel;
12+
this.source = source;
13+
}
14+
15+
@Override
16+
public EvalBuilder<T> resolveMagics() {
17+
return new SimpleEvalBuilder<>(kernel, kernel.getMagicsResolver().resolve(source));
18+
}
19+
20+
@Override
21+
public EvalBuilder<DisplayData> renderResults() {
22+
return new RenderedEvalBuilder(kernel, this);
23+
}
24+
25+
@Override
26+
public T eval() {
27+
return (T) kernel.doEval(source);
28+
}
29+
}

jjava-kernel/src/main/java/org/dflib/jjava/kernel/JavaKernel.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public void addToClasspath(String classpath) {
115115
}
116116

117117
@Override
118-
public List<String> formatError(Throwable e) {
118+
protected List<String> formatError(Throwable e) {
119119
if (e instanceof CompilationException) {
120120
return formatCompilationException((CompilationException) e);
121121
} else if (e instanceof IncompleteSourceException) {
@@ -226,12 +226,9 @@ private List<String> formatEvaluationInterruptedException(EvaluationInterruptedE
226226
return fmt;
227227
}
228228

229-
/**
230-
* Same as {@link #eval(String)}, but not applying the renderer to evaluation result.
231-
*/
232229
@Override
233-
public Object evalRaw(String source) {
234-
return evaluator.eval(magicsResolver.resolve(source));
230+
protected Object doEval(String source) {
231+
return evaluator.eval(source);
235232
}
236233

237234
@Override

jjava-kernel/src/main/java/org/dflib/jjava/kernel/magics/LoadCodeMagic.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ public Void eval(JavaKernel kernel, List<String> args) throws Exception {
4949
Path scriptPath = sourcePath.resolveSibling(file + extension);
5050
if (Files.isRegularFile(scriptPath)) {
5151

52+
// TODO: return rendered "eval" results to the caller would make sense here
53+
5254
if (scriptPath.getFileName().endsWith(NOTEBOOK_EXTENSION)) {
5355
execNotebook(kernel, scriptPath);
5456
} else {
55-
String sourceContents = Files.readString(scriptPath);
56-
kernel.eval(sourceContents);
57+
String source = Files.readString(scriptPath);
58+
kernel.evalBuilder(source).resolveMagics().eval();
5759
}
5860
return null;
5961
}
@@ -118,7 +120,7 @@ private void execNotebook(JavaKernel kernel, Path notebookPath) throws Exception
118120

119121
// Found a code cell!
120122
if (isCode != null && isCode) {
121-
kernel.eval(source);
123+
kernel.evalBuilder(source).resolveMagics().eval();
122124
}
123125
}
124126
reader.endArray();

0 commit comments

Comments
 (0)