Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JupyterLab update to ^3.0.0, and fix "Open With.." regression #1294

Merged
merged 6 commits into from
Jan 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
texodus marked this conversation as resolved.
Show resolved Hide resolved
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});
texodus marked this conversation as resolved.
Show resolved Hide resolved

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