Skip to content

Commit

Permalink
馃external rendermime management (#632)
Browse files Browse the repository at this point in the history
* 馃external rendermime means users explititly set their rendermime boundary 馃憦
* 馃搫changeset
  • Loading branch information
stevejpurves committed May 31, 2023
1 parent 830018c commit 6eea92e
Show file tree
Hide file tree
Showing 21 changed files with 143 additions and 64 deletions.
9 changes: 9 additions & 0 deletions .changeset/tasty-ducks-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'demo-react': minor
'demo-core': minor
'thebe-react': minor
'thebe-core': minor
'thebe': patch
---

Made changes to the `thebe-core` APIs to make rendermime registries external, the caller now has to manage how registries are used across the other session nd notebook object. Updates the demos, `thebe` and `thebe-react` to reflect this base change. `thebe-react` now has a new provider making it easy to add a rendermine registry in the component tree.
1 change: 1 addition & 0 deletions _build/templates/site/myst/book-theme
Submodule book-theme added at df9cbc
6 changes: 5 additions & 1 deletion apps/demo-core/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ThebeSession } from 'thebe-core';
import {
makeRenderMimeRegistry,
ThebeEvents,
ThebeEventType,
shortId,
Expand Down Expand Up @@ -216,14 +217,17 @@ class App {
await this.server?.connectToJupyterServer();
}

this.session = await this.server.startNewSession();
const rendermime = makeRenderMimeRegistry(this.server.config.mathjax);

this.session = await this.server.startNewSession(rendermime);

this.notebook = ThebeNotebook.fromCodeBlocks(
code[this.exampleType].map((source) => ({
id: shortId(),
source,
})),
this.server.config,
rendermime,
);

if (this.session == null) console.error('could not start session');
Expand Down
2 changes: 1 addition & 1 deletion apps/demo-core/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-MML-AM_CHTML"
></script>
<script src="thebe-lite.min.js" type="text/javascript"></script>
<script src="demo.js" type="text/javascript"></script>
<script src="thebe-demo.js" type="text/javascript"></script>
<link rel="stylesheet" href="demo.css" />
<link rel="stylesheet" href="thebe-core.css" />
<link
Expand Down
10 changes: 6 additions & 4 deletions apps/demo-react/src/NotebookPage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { ThebeSessionProvider, useThebeServer } from 'thebe-react';
import { ThebeSessionProvider, ThebeRenderMimeRegistryProvider, useThebeServer } from 'thebe-react';

export function NotebookPage({ name, children }: React.PropsWithChildren<{ name: string }>) {
const { ready } = useThebeServer();

if (!ready) return null;
return (
<ThebeSessionProvider start name={name}>
{children}
</ThebeSessionProvider>
<ThebeRenderMimeRegistryProvider>
<ThebeSessionProvider start name={name}>
{children}
</ThebeSessionProvider>
</ThebeRenderMimeRegistryProvider>
);
}
2 changes: 0 additions & 2 deletions packages/core/src/cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ class ThebeCell extends PassiveCellRenderer implements IThebeCell {
* @param session
*/
attachSession(session: ThebeSession) {
session.manager.addWidgetFactories(this.rendermime);
this.session = session;
this.events.triggerStatus({
status: CellStatusEvent.attached,
Expand All @@ -85,7 +84,6 @@ class ThebeCell extends PassiveCellRenderer implements IThebeCell {
*
*/
detachSession() {
this.session?.manager.removeWidgetFactories(this.rendermime);
this.session = undefined;
this.events.triggerStatus({
status: CellStatusEvent.detached,
Expand Down
30 changes: 8 additions & 22 deletions packages/core/src/manager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import { RenderMimeRegistry, standardRendererFactories } from '@jupyterlab/rendermime';
import type { IKernelConnection } from '@jupyterlab/services/lib/kernel/kernel';
import type { Widget } from '@lumino/widgets';

Expand All @@ -26,34 +25,21 @@ export class ThebeManager extends KernelWidgetManager {
id: string;
_loader: RequireJsLoader;

constructor(kernel: IKernelConnection, rendermime?: IRenderMimeRegistry) {
const rm =
rendermime ??
new RenderMimeRegistry({
initialFactories: standardRendererFactories,
});
constructor(kernel: IKernelConnection, rendermime: IRenderMimeRegistry) {
super(kernel, rendermime);

this.id = shortId();
/** ensure this registry always gets the widget renderer.
* This is essential for cases where widgets are rendered heirarchically
*/
rm.addFactory(
{
safe: false,
mimeTypes: [WIDGET_MIMETYPE],
createRenderer: (options) => new WidgetRenderer(options, this as any),
},
1,
);
this.addWidgetFactories();

super(kernel, rm);

this.id = shortId();
this._registerWidgets();
this._loader = new RequireJsLoader();
}

addWidgetFactories(rendermime: IRenderMimeRegistry) {
rendermime.addFactory(
addWidgetFactories() {
this.rendermime.addFactory(
{
safe: false,
mimeTypes: [WIDGET_MIMETYPE],
Expand All @@ -63,8 +49,8 @@ export class ThebeManager extends KernelWidgetManager {
);
}

removeWidgetFactories(rendermime: IRenderMimeRegistry) {
rendermime.removeMimeType(WIDGET_MIMETYPE);
removeWidgetFactories() {
this.rendermime.removeMimeType(WIDGET_MIMETYPE);
}

/**
Expand Down
11 changes: 4 additions & 7 deletions packages/core/src/notebook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type ThebeSession from './session';
import type { IThebeCell, IThebeCellExecuteReturn } from './types';
import { shortId } from './utils';
import type { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import { getRenderMimeRegistry } from './rendermime';
import type { Config } from './config';
import { EventSubject, NotebookStatusEvent } from './events';
import { EventEmitter } from './emitter';
Expand All @@ -24,16 +23,16 @@ class ThebeNotebook {
session?: ThebeSession;
protected events: EventEmitter;

constructor(id: string, config: Config, rendermime?: IRenderMimeRegistry) {
constructor(id: string, config: Config, rendermime: IRenderMimeRegistry) {
this.id = id;
this.events = new EventEmitter(id, config, EventSubject.notebook, this);
this.cells = [];
this.metadata = {};
this.rendermime = rendermime ?? getRenderMimeRegistry(config.mathjax);
this.rendermime = rendermime;
console.debug('thebe:notebook constructor', this);
}

static fromCodeBlocks(blocks: CodeBlock[], config: Config, rendermime?: IRenderMimeRegistry) {
static fromCodeBlocks(blocks: CodeBlock[], config: Config, rendermime: IRenderMimeRegistry) {
const id = shortId();
const notebook = new ThebeNotebook(id, config, rendermime);
notebook.cells = blocks.map((c) => {
Expand All @@ -46,7 +45,7 @@ class ThebeNotebook {
return notebook;
}

static fromIpynb(ipynb: INotebookContent, config: Config, rendermime?: IRenderMimeRegistry) {
static fromIpynb(ipynb: INotebookContent, config: Config, rendermime: IRenderMimeRegistry) {
const notebook = new ThebeNotebook(shortId(), config, rendermime);

Object.assign(notebook.metadata, ipynb.metadata);
Expand Down Expand Up @@ -119,7 +118,6 @@ class ThebeNotebook {
// note all cells in a notebook share the rendermime registry
// we only need to add the widgets factory once
this.session = session;
session.manager.addWidgetFactories(this.rendermime);
this.cells?.forEach((cell) => (cell.session = session));
this.events.triggerStatus({
status: NotebookStatusEvent.attached,
Expand All @@ -128,7 +126,6 @@ class ThebeNotebook {
}

detachSession() {
this.session?.manager.removeWidgetFactories(this.rendermime);
this.cells?.map((cell) => (cell.session = undefined));
this.session = undefined;
this.events.triggerStatus({
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/passive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type * as nbformat from '@jupyterlab/nbformat';
import { getRenderMimeRegistry } from './rendermime';
import { makeRenderMimeRegistry } from './rendermime';
import { OutputArea, OutputAreaModel } from '@jupyterlab/outputarea';
import type { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import type { IPassiveCell, MathjaxOptions } from './types';
Expand Down Expand Up @@ -49,7 +49,7 @@ class PassiveCellRenderer implements IPassiveCell {

constructor(id: string, rendermime?: IRenderMimeRegistry, mathjax?: MathjaxOptions) {
this.id = id;
this.rendermime = rendermime ?? getRenderMimeRegistry(mathjax ?? makeMathjaxOptions());
this.rendermime = rendermime ?? makeRenderMimeRegistry(mathjax ?? makeMathjaxOptions());
this.model = new OutputAreaModel({ trusted: true });
this.area = new OutputArea({
model: this.model,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/rendermime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function getRenderers(mathjax: MathjaxOptions) {
};
}

export function getRenderMimeRegistry(mathjax?: MathjaxOptions) {
export function makeRenderMimeRegistry(mathjax?: MathjaxOptions) {
const rendermime = new RenderMimeRegistry(getRenderers(mathjax ?? makeMathjaxOptions()));
rendermime.addFactory(jsonRendererFactory, 10);
return rendermime;
Expand Down
20 changes: 12 additions & 8 deletions packages/core/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,24 @@ import type {
ServerSettings,
SessionIModel,
} from './types';
import type { Config } from './config';
import type { ServiceManager } from '@jupyterlab/services';
import type { LiteServerConfig } from 'thebe-lite';
import type { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import type { StatusEvent } from './events';
import { RepoProvider } from './types';
import { makeGitHubUrl, makeGitLabUrl, makeGitUrl } from './url';
import { getExistingServer, makeStorageKey, saveServerInfo } from './sessions';
import type { ServiceManager } from '@jupyterlab/services';
import {
KernelManager,
KernelSpecAPI,
ServerConnection,
SessionManager,
} from '@jupyterlab/services';
import ThebeSession from './session';
import type { Config } from './config';
import { shortId } from './utils';
import type { StatusEvent } from './events';
import { ServerStatusEvent, EventSubject, ErrorStatusEvent } from './events';
import { EventEmitter } from './emitter';
import { LiteServerConfig } from 'thebe-lite';

async function responseToJson(res: Response) {
if (!res.ok) throw Error(`${res.status} - ${res.statusText}`);
Expand Down Expand Up @@ -79,7 +80,10 @@ class ThebeServer implements ServerRuntime, ServerRestAPI {
this._isDisposed = true;
}

async startNewSession(kernelOptions?: KernelOptions): Promise<ThebeSession | null> {
async startNewSession(
rendermime: IRenderMimeRegistry,
kernelOptions?: KernelOptions,
): Promise<ThebeSession | null> {
await this.ready;

if (!this.sessionManager?.isReady) {
Expand All @@ -96,7 +100,7 @@ class ThebeServer implements ServerRuntime, ServerRestAPI {
},
});

return new ThebeSession(this, connection);
return new ThebeSession(this, connection, rendermime);
}

async listRunningSessions(): Promise<SessionIModel[]> {
Expand All @@ -116,15 +120,15 @@ class ThebeServer implements ServerRuntime, ServerRestAPI {
return this.listRunningSessions();
}

async connectToExistingSession(model: SessionIModel) {
async connectToExistingSession(model: SessionIModel, rendermime: IRenderMimeRegistry) {
await this.ready;
if (!this.sessionManager?.isReady) {
throw Error('Requesting session from a server, with no SessionManager available');
}

const connection = this.sessionManager?.connectTo({ model });

return new ThebeSession(this, connection);
return new ThebeSession(this, connection, rendermime);
}

async clearSavedBinderSessions() {
Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { EventSubject, SessionStatusEvent } from './events';
import { ThebeManager } from './manager';
import type ThebeServer from './server';
import { EventEmitter } from './emitter';
import type { IRenderMimeRegistry } from '@jupyterlab/rendermime';

class ThebeSession {
readonly server: ThebeServer;
Expand All @@ -11,13 +12,17 @@ class ThebeSession {
private connection: ISessionConnection;
private events: EventEmitter;

constructor(server: ThebeServer, connection: ISessionConnection) {
constructor(
server: ThebeServer,
connection: ISessionConnection,
rendermime: IRenderMimeRegistry,
) {
this.server = server;
this.connection = connection;
this.events = new EventEmitter(this.connection.id, server.config, EventSubject.session, this);

if (this.connection.kernel == null) throw Error('ThebeSession - kernel is null');
this.manager = new ThebeManager(this.connection.kernel);
this.manager = new ThebeManager(this.connection.kernel, rendermime);

this.events.triggerStatus({
status: SessionStatusEvent.ready,
Expand Down
23 changes: 18 additions & 5 deletions packages/core/src/thebe/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import type { CodeBlock } from '../notebook';
import ThebeNotebook from '../notebook';
import type { INotebookContent } from '@jupyterlab/nbformat';
import type { Config } from '..';
import { makeConfiguration, ThebeEvents } from '..';
import { ThebeEvents } from '../events';
import { makeConfiguration } from '../options';
import { makeRenderMimeRegistry } from '../rendermime';
import * as coreModule from '../index';
import type { IRenderMimeRegistry } from '@jupyterlab/rendermime';

export function connectToBinder(config: Config): ThebeServer {
const server: ThebeServer = new ThebeServer(config);
Expand Down Expand Up @@ -35,21 +38,31 @@ export function makeServer(config: Config) {
return new ThebeServer(config);
}

export function setupNotebookFromBlocks(blocks: CodeBlock[], config: Config) {
return ThebeNotebook.fromCodeBlocks(blocks, config);
export function setupNotebookFromBlocks(
blocks: CodeBlock[],
config: Config,
rendermime: IRenderMimeRegistry,
) {
return ThebeNotebook.fromCodeBlocks(blocks, config, rendermime);
}

export function setupNotebookFromIpynb(ipynb: INotebookContent, config: Config) {
return ThebeNotebook.fromIpynb(ipynb, config);
export function setupNotebookFromIpynb(
ipynb: INotebookContent,
config: Config,
rendermime: IRenderMimeRegistry,
) {
return ThebeNotebook.fromIpynb(ipynb, config, rendermime);
}

export function setupThebeCore() {
console.log(`thebe:api:setupThebeCore`, { coreModule });
window.thebeCore = Object.assign(window.thebeCore ?? {}, {
module: coreModule,
api: {
makeConfiguration,
makeEvents,
makeServer,
makeRenderMimeRegistry,
connectToBinder,
connectToJupyter,
connectToJupyterLite,
Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/thebe/entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { ThebeEvents } from '../events';
import type { ThebeLiteGlobal } from 'thebe-lite';
import type * as coreModule from '../index';
import type { INotebookContent } from '@jupyterlab/nbformat';
import type { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import { setupThebeCore } from './api';

/**
Expand All @@ -24,11 +25,20 @@ export interface JsApi {
makeEvents: () => ThebeEvents;
makeConfiguration: (options: Partial<CoreOptions>, events?: ThebeEvents) => Config;
makeServer: (config: Config) => ThebeServer;
makeRenderMimeRegistry: (mathjax?: coreModule.MathjaxOptions | undefined) => IRenderMimeRegistry;
connectToBinder: (config: Config) => ThebeServer;
connectToJupyter: (config: Config) => ThebeServer;
connectToJupyterLite: (config: Config) => ThebeServer;
setupNotebookFromBlocks: (blocks: CodeBlock[], config: Config) => ThebeNotebook;
setupNotebookFromIpynb: (ipynb: INotebookContent, config: Config) => ThebeNotebook;
setupNotebookFromBlocks: (
blocks: CodeBlock[],
config: Config,
rendermime: IRenderMimeRegistry,
) => ThebeNotebook;
setupNotebookFromIpynb: (
ipynb: INotebookContent,
config: Config,
rendermime: IRenderMimeRegistry,
) => ThebeNotebook;
}

export type ThebeCore = typeof coreModule;
Expand Down

0 comments on commit 6eea92e

Please sign in to comment.