Skip to content

Commit

Permalink
Merge pull request #1294 from finos/jlab3
Browse files Browse the repository at this point in the history
JupyterLab update to ^3.0.0, and fix "Open With.." regression
  • Loading branch information
texodus authored Jan 19, 2021
2 parents 4d75f30 + 4ec355b commit a2c1e12
Show file tree
Hide file tree
Showing 17 changed files with 717 additions and 409 deletions.
4 changes: 4 additions & 0 deletions cpp/perspective/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,10 @@ elseif(PSP_CPP_BUILD OR PSP_PYTHON_BUILD)

# .dll not importable
set_property(TARGET binding PROPERTY SUFFIX .pyd)
elseif (MACOS OR NOT MANYLINUX)
target_compile_options(binding PRIVATE -Wdeprecated-declarations)
set_property(TARGET psp PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_RPATH} ${module_origin_path})
set_property(TARGET binding PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_RPATH} ${module_origin_path})
else()
target_compile_options(binding PRIVATE -Wdeprecated-declarations)
endif()
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
"precommit": "npm run lint",
"lint": "npm-run-all lint:*",
"lint:eslint": "eslint \"packages/*/src/**/*.js\" \"packages/*/test/**/*.js\" \"examples/*/*.js\"",
"lint_:tslint": "eslint \"packages/*/src/**/*.ts\" \"packages/*/test/**/*.ts\" \"examples/*/*.ts\"",
"lint:typescript": "eslint \"packages/perspective-jupyterlab/src/ts/*.ts\"",
"lint:python": "node scripts/lint_python.js",
"fix:es": "npm run lint:eslint -- --fix",
"fix:md": "prettier docs/md/*.md --prose-wrap=always --write",
Expand Down
4 changes: 2 additions & 2 deletions packages/perspective-jupyterlab/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
"@finos/perspective-viewer": "^0.6.0",
"@finos/perspective-viewer-d3fc": "^0.6.0",
"@finos/perspective-viewer-datagrid": "^0.6.0",
"@jupyter-widgets/base": "^3.0.0",
"@jupyterlab/application": "^2.0.0",
"@jupyter-widgets/base": "^3.0.0 || ^4.0.0",
"@jupyterlab/application": "^3.0.0",
"@lumino/application": "^1.7.3",
"@lumino/widgets": "^1.9.3"
},
Expand Down
7 changes: 6 additions & 1 deletion packages/perspective-jupyterlab/src/ts/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@
"tabWidth": 4,
"bracketSpacing": false
}],

"max-len": ["error", 200],
"no-const-assign": "error",
"no-this-before-super": "error",
"no-undef": "error",
"no-unreachable": "error",
"no-unused-vars": "error",
"constructor-super": "error",
"valid-typeof": "error"
"valid-typeof": "error",

"@typescript-eslint/camelcase": "off",
"@typescript-eslint/ban-ts-ignore": "off"
}
}
3 changes: 2 additions & 1 deletion packages/perspective-jupyterlab/src/ts/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* the Apache License 2.0. The full license can be found in the LICENSE file.
*
*/
/* eslint-disable @typescript-eslint/camelcase */

/* eslint-disable @typescript-eslint/no-explicit-any */

import {DOMWidgetView} from "@jupyter-widgets/base";
import {Client} from "@finos/perspective/dist/esm/api/client";
Expand Down
3 changes: 1 addition & 2 deletions packages/perspective-jupyterlab/src/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@ import "!!style-loader!css-loader!less-loader!../less/index.less";
import "@finos/perspective-viewer-datagrid";
import "@finos/perspective-viewer-d3fc";

import {JupyterFrontEndPlugin} from "@jupyterlab/application";
import {perspectiveRenderers} from "./renderer";
import {PerspectiveJupyterPlugin} from "./plugin";

/**
* Export the renderer as default.
*/
const plugins: JupyterFrontEndPlugin<any>[] = [PerspectiveJupyterPlugin, perspectiveRenderers];
const plugins = [PerspectiveJupyterPlugin, perspectiveRenderers];
export default plugins;
7 changes: 5 additions & 2 deletions packages/perspective-jupyterlab/src/ts/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@ import {PerspectiveView} from "./view";

import {PERSPECTIVE_VERSION} from "./version";

const EXTENSION_ID = "@finos/perspective-jupyterlab";

/**
* PerspectiveJupyterPlugin Defines the Jupyterlab plugin, and registers `PerspectiveModel` and `PerspectiveView`
* to be called on initialization.
*/
export const PerspectiveJupyterPlugin: IPlugin<Application<Widget>, void> = {
id: "@finos/perspective-jupyterlab",
id: EXTENSION_ID,
// @ts-ignore
requires: [IJupyterWidgetRegistry],
activate: (app: Application<Widget>, registry: IJupyterWidgetRegistry): void => {
registry.registerWidget({
name: "@finos/perspective-jupyterlab",
name: EXTENSION_ID,
version: PERSPECTIVE_VERSION,
exports: {
PerspectiveModel: PerspectiveModel,
Expand Down
2 changes: 0 additions & 2 deletions packages/perspective-jupyterlab/src/ts/psp_widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
*
*/

/* eslint-disable @typescript-eslint/camelcase */

import "@finos/perspective-viewer";

import {Table, TableData} from "@finos/perspective";
Expand Down
133 changes: 111 additions & 22 deletions packages/perspective-jupyterlab/src/ts/renderer.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
/******************************************************************************
*
* Copyright (c) 2018, the Perspective Authors.
*
* This file is part of the Perspective library, distributed under the terms of
* the Apache License 2.0. The full license can be found in the LICENSE file.
*
*/

import {ActivityMonitor} from "@jupyterlab/coreutils";
import {ILayoutRestorer, JupyterFrontEnd, JupyterFrontEndPlugin} from "@jupyterlab/application";
import {IThemeManager, WidgetTracker, Dialog, showDialog} from "@jupyterlab/apputils";
import {ABCWidgetFactory, DocumentRegistry, IDocumentWidget, DocumentWidget} from "@jupyterlab/docregistry";
import {PerspectiveWidget} from "./psp_widget";

// eslint-disable-next-line @typescript-eslint/no-var-requires
const perspective = require("@finos/perspective");

/**
* The name of the factories that creates widgets.
*/
const FACTORY_CSV = "CSVPerspective";
const FACTORY_JSON = "JSONPerspective";
const FACTORY_ARROW = "ArrowPerspective";

const RENDER_TIMEOUT = 1000;

type IPerspectiveDocumentType = "csv" | "json";
type IPerspectiveDocumentType = "csv" | "json" | "arrow";

// create here to reuse for exception handling
const baddialog = () => {
const baddialog = (): void => {
showDialog({
body: "Perspective could not render the data",
buttons: [Dialog.okButton({label: "Dismiss"})],
Expand All @@ -26,9 +39,11 @@ const baddialog = () => {

export class PerspectiveDocumentWidget extends DocumentWidget<PerspectiveWidget> {
constructor(options: DocumentWidget.IOptionsOptionalContent<PerspectiveWidget>, type: IPerspectiveDocumentType = "csv") {
super({content: new PerspectiveWidget("test"), context: options.context, reveal: options.reveal});
super({content: new PerspectiveWidget("Perspective"), context: options.context, reveal: options.reveal});

this._psp = this.content;
this._table = undefined;

this._type = type;
this._context = options.context;

Expand All @@ -42,18 +57,20 @@ export class PerspectiveDocumentWidget extends DocumentWidget<PerspectiveWidget>
});
}

private _update() {
private _update(): void {
try {
let data;
if (this._type === "csv") {
// load csv directly
const data: string = this._context.model.toString();
this._psp._update(data);
data = this._context.model.toString();
} else if (this._type === "arrow") {
// load arrow directly
data = Uint8Array.from(atob(this._context.model.toString()), c => c.charCodeAt(0)).buffer;
} else if (this._type === "json") {
const data = this._context.model.toJSON() as any;

data = this._context.model.toJSON();
if (Array.isArray(data) && data.length > 0) {
// already is records form, load directly
this._psp._update(data);
data = data as Array<object>;
} else {
// Column-oriented or single records JSON
// don't handle for now, just need to implement
Expand All @@ -65,6 +82,14 @@ export class PerspectiveDocumentWidget extends DocumentWidget<PerspectiveWidget>
// don't handle other mimetypes for now
throw "Not handled";
}
this._table = perspective.worker().table(data);
if (this._psp.viewer.table === undefined) {
// construct new table
this._psp.viewer.load(this._table);
} else {
// replace existing table for whatever reason
this._psp.replace(this._table);
}
} catch {
baddialog();
}
Expand All @@ -77,6 +102,7 @@ export class PerspectiveDocumentWidget extends DocumentWidget<PerspectiveWidget>
if (this._monitor) {
this._monitor.dispose();
}
this._psp.delete(true);
super.dispose();
}

Expand All @@ -87,6 +113,7 @@ export class PerspectiveDocumentWidget extends DocumentWidget<PerspectiveWidget>
private _type: IPerspectiveDocumentType;
private _context: DocumentRegistry.Context;
private _psp: PerspectiveWidget;
private _table: any;
private _monitor: ActivityMonitor<DocumentRegistry.IModel, void> | null = null;
}

Expand All @@ -109,15 +136,13 @@ export class PerspectiveJSONFactory extends ABCWidgetFactory<IDocumentWidget<Per
}

/**
* The perspective extension for files
* A widget factory for arrow widgets.
*/
export const perspectiveRenderers: JupyterFrontEndPlugin<void> = {
activate: activate,
id: "@finos/perspective-jupyterlab:renderers",
requires: [],
optional: [ILayoutRestorer, IThemeManager],
autoStart: true
};
export class PerspectiveArrowFactory extends ABCWidgetFactory<IDocumentWidget<PerspectiveWidget>> {
protected createNewWidget(context: DocumentRegistry.Context): IDocumentWidget<PerspectiveWidget> {
return new PerspectiveDocumentWidget({context}, "arrow");
}
}

/**
* Activate cssviewer extension for CSV files
Expand All @@ -137,6 +162,27 @@ function activate(app: JupyterFrontEnd, restorer: ILayoutRestorer | null, themeM
readOnly: true
});

try {
app.docRegistry.addFileType({
name: "arrow",
displayName: "arrow",
extensions: [".arrow"],
mimeTypes: ["application/octet-stream"],
contentType: "file",
fileFormat: "base64"
});
} catch {
// do nothing
}

const factoryarrow = new PerspectiveArrowFactory({
name: FACTORY_ARROW,
fileTypes: ["arrow"],
defaultFor: ["arrow"],
readOnly: true,
modelName: "base64"
});

const trackercsv = new WidgetTracker<IDocumentWidget<PerspectiveWidget>>({
namespace: "csvperspective"
});
Expand All @@ -145,6 +191,10 @@ function activate(app: JupyterFrontEnd, restorer: ILayoutRestorer | null, themeM
namespace: "jsonperspective"
});

const trackerarrow = new WidgetTracker<IDocumentWidget<PerspectiveWidget>>({
namespace: "arrowperspective"
});

if (restorer) {
// Handle state restoration.
void restorer.restore(trackercsv, {
Expand All @@ -158,54 +208,93 @@ function activate(app: JupyterFrontEnd, restorer: ILayoutRestorer | null, themeM
args: widget => ({path: widget.context.path, factory: FACTORY_JSON}),
name: widget => widget.context.path
});

void restorer.restore(trackerarrow, {
command: "docmanager:open",
args: widget => ({path: widget.context.path, factory: FACTORY_ARROW}),
name: widget => widget.context.path
});
}

app.docRegistry.addWidgetFactory(factorycsv);
app.docRegistry.addWidgetFactory(factoryjson);
app.docRegistry.addWidgetFactory(factoryarrow);

const ftcsv = app.docRegistry.getFileType("csv");
const ftjson = app.docRegistry.getFileType("json");
const ftarrow = app.docRegistry.getFileType("arrow");

factorycsv.widgetCreated.connect((sender, widget) => {
// Track the widget.
void trackercsv.add(widget);

// Notify the widget tracker if restore data needs to update.
widget.context.pathChanged.connect(() => {
void trackercsv.save(widget);
});

if (ftcsv) {
widget.title.iconClass = ftcsv.iconClass!;
widget.title.iconLabel = ftcsv.iconLabel!;
widget.title.iconClass = ftcsv.iconClass || "";
widget.title.iconLabel = ftcsv.iconLabel || "";
}
});

factoryjson.widgetCreated.connect((sender, widget) => {
// Track the widget.
void trackerjson.add(widget);

// Notify the widget tracker if restore data needs to update.
widget.context.pathChanged.connect(() => {
void trackerjson.save(widget);
});

if (ftjson) {
widget.title.iconClass = ftjson.iconClass!;
widget.title.iconLabel = ftjson.iconLabel!;
widget.title.iconClass = ftjson.iconClass || "";
widget.title.iconLabel = ftjson.iconLabel || "";
}
});

factoryarrow.widgetCreated.connect((sender, widget) => {
// Track the widget.
void trackerarrow.add(widget);

// Notify the widget tracker if restore data needs to update.
widget.context.pathChanged.connect(() => {
void trackerarrow.save(widget);
});

if (ftarrow) {
widget.title.iconClass = ftarrow.iconClass || "";
widget.title.iconLabel = ftarrow.iconLabel || "";
}
});

// Keep the themes up-to-date.
const updateThemes = () => {
const updateThemes = (): void => {
const isLight = themeManager && themeManager.theme ? themeManager.isLight(themeManager.theme) : true;
trackercsv.forEach((pspDocWidget: PerspectiveDocumentWidget) => {
pspDocWidget.psp.dark = !isLight;
});
trackerjson.forEach((pspDocWidget: PerspectiveDocumentWidget) => {
pspDocWidget.psp.dark = !isLight;
});
trackerarrow.forEach((pspDocWidget: PerspectiveDocumentWidget) => {
pspDocWidget.psp.dark = !isLight;
});
};

if (themeManager) {
themeManager.themeChanged.connect(updateThemes);
}
}

/**
* The perspective extension for files
*/
export const perspectiveRenderers: JupyterFrontEndPlugin<void> = {
activate: activate,
id: "@finos/perspective-jupyterlab:renderers",
requires: [],
optional: [ILayoutRestorer, IThemeManager],
autoStart: true
};
2 changes: 2 additions & 0 deletions packages/perspective-jupyterlab/src/ts/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
* the Apache License 2.0. The full license can be found in the LICENSE file.
*
*/

// eslint-disable-next-line @typescript-eslint/no-var-requires
const pkg_json = require("../../package.json");

export const PERSPECTIVE_VERSION = pkg_json.version;
Loading

0 comments on commit a2c1e12

Please sign in to comment.