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

Use proper bokehjs' types in src/widgets.ts #188

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
26 changes: 1 addition & 25 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,5 @@ module.exports = {
},
plugins: ['@typescript-eslint'],
rules: {
'@typescript-eslint/naming-convention': [
'warn',
{
'selector': 'interface',
'format': ['PascalCase'],
'custom': {
'regex': '^I[A-Z]',
'match': true
}
}
],
'@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/quotes': [
'error',
'single',
{ avoidEscape: true, allowTemplateLiterals: false }
],
curly: ['error', 'all'],
semi: ['warn', 'never'],
eqeqeq: 'warn',
'prefer-arrow-callback': 'error'
}
};
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"@jupyter-widgets/jupyterlab-manager": "^5.0.4"
},
"devDependencies": {
"@bokeh/bokehjs": "~3.4.0",
"@jupyterlab/builder": "^4",
"@typescript-eslint/eslint-plugin": "^7.0.1",
"@typescript-eslint/parser": "^7.0.1",
Expand Down
152 changes: 63 additions & 89 deletions src/widgets.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,46 @@
import { DOMWidgetModel, DOMWidgetView } from '@jupyter-widgets/base'
import {DOMWidgetModel, DOMWidgetView} from "@jupyter-widgets/base"

//import {Document, DocumentChangedEvent, ModelChangedEvent} from "document"
//import {Receiver, Fragment} from "protocol/receiver"
//import {keys, values} from "core/util/object"
// Use only `import type`, so that all imports are erased at run time
import type {Document, DocJson, Patch, DocumentChangedEvent} from "@bokeh/bokehjs/document"
import type {DocumentChanged} from "@bokeh/bokehjs/document/events"
import type {Receiver, Fragment} from "@bokeh/bokehjs/protocol/receiver"
import type {RenderItem} from "@bokeh/bokehjs/embed/json"
import type {HasProps} from "@bokeh/bokehjs/core/has_props"
import type {Ref} from "@bokeh/bokehjs/core/util/refs"
import type {Serializer} from "@bokeh/bokehjs/core/serialization"
import type {BokehEvent} from "@bokeh/bokehjs/core/bokeh_events"
import type {add_document_standalone} from "@bokeh/bokehjs/embed/standalone"

import { name, version } from './metadata'
import {name, version} from './metadata'

function bk_require(name: string): any {
return (window as any).Bokeh.require(name)
declare const Bokeh: {require(name: string): unknown}

function bk_require<T>(name: string): T {
return Bokeh.require(name) as T
}

type DocsJson = any
type RenderItem = any
type Document = any
type DocumentChangedEvent = any
type Receiver = any
type Fragment = any
type HasProps = any
type Ref = any
declare const Jupyter: {notebook?: unknown}

declare function require(name: string): unknown

const { keys, values } = Object
const {keys} = Object

const version_range = `^${version}`

export type RenderBundle = {
docs_json: DocsJson
docs_json: DocJson[]
render_items: RenderItem[]
div: string
}

export interface DocumentChanged {
event: 'jsevent'
kind: string
}

export interface ModelChanged extends DocumentChanged {
event: 'jsevent'
kind: 'ModelChanged'
id: string
new: unknown
attr: string
}

export interface MessageSent extends DocumentChanged {
event: 'jsevent'
kind: 'MessageSent'
msg_data: {
event_name: string
event_values: {
model: { id: string }
[other: string]: any
}
}
msg_type: string
/*
declare interface DocumentChanged {
event: "jsevent"
}
*/

export class BokehModel extends DOMWidgetModel {
defaults(): any {
defaults(): {[key: string]: unknown} {
return {
...super.defaults(),

Expand All @@ -69,7 +53,7 @@
_view_module_version: version_range,

combine_events: false,
render_bundle: {}
render_bundle: {},
}
}

Expand All @@ -82,50 +66,43 @@
private _document: Document | null
private _receiver: Receiver
private _blocked: boolean
private _msgs: any[]
private _msgs: DocumentChanged[]
private _idle: boolean
private _combine: boolean

constructor(options?: any) {
constructor(options?: unknown) {
super(options)
this._document = null
this._blocked = false
this._idle = true
this._combine = true
this._msgs = []
const { Receiver } = bk_require('protocol/receiver')
this._receiver = new Receiver()
const receiver = bk_require<{Receiver: typeof Receiver}>('protocol/receiver')
this._receiver = new receiver.Receiver()
this.model.on('change:render_bundle', () => this.render())
if (
(window as any).Jupyter != null &&
(window as any).Jupyter.notebook != null
) {
if (Jupyter?.notebook != null) {
// Handle classic Jupyter notebook
const events = (window as any).require('base/js/events')
const events = require('base/js/events')
events.on('kernel_idle.Kernel', () => this._process_msg())
} else if ((this.model.widget_manager as any).context != null) {

Check failure on line 87 in src/widgets.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
// Handle JupyterLab and Voila
const context = (this.model.widget_manager as any).context

Check failure on line 89 in src/widgets.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
const statusChanged =
context.session != null
? context.session.kernel.statusChanged
: context.sessionContext.statusChanged
if (statusChanged != null) {
statusChanged.connect((_: any, status: any) => {

Check failure on line 95 in src/widgets.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type

Check failure on line 95 in src/widgets.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
if (status === 'idle') {
this._process_msg()
}
})
} else if (this.model.get('combine_events')) {
console.warn(
'BokehView cannot combine events because Kernel idle status cannot be determined.'
)
console.warn('BokehView cannot combine events because Kernel idle status cannot be determined.')
this._combine = false
}
} else if (this.model.get('combine_events')) {
console.warn(
'BokehView cannot combine events because Kernel idle status cannot be determined.'
)
console.warn('BokehView cannot combine events because Kernel idle status cannot be determined.')
this._combine = false
}
this.listenTo(this.model, 'msg:custom', (content, buffers) =>
Expand All @@ -143,27 +120,25 @@

render(): void {
const bundle = JSON.parse(this.model.get('render_bundle'))
const { docs_json, render_items, div } = bundle as RenderBundle
const {docs_json, render_items, div} = bundle as RenderBundle
this.el.innerHTML = div
const element = this.el.children[0]
const json = values(docs_json)[0]
const { Document } = bk_require('document')
const { add_document_standalone } = bk_require('embed/standalone')
this._document = Document.from_json(json)
for (const item of render_items) {
const roots: { [key: string]: Element } = {}
for (const root_id in item.roots) {
roots[root_id] = element
}
add_document_standalone(this._document, element, roots)
const element = this.el.children[0] as HTMLElement
// assumes docs_json.length == 1 && render_items.length == 1
const doc_json = docs_json[0]
const render_item = render_items[0]
const document = bk_require<{Document: typeof Document}>('document')
const standalone = bk_require<{add_document_standalone: typeof add_document_standalone}>('embed/standalone')
this._document = document.Document.from_json(doc_json)
const roots: {[key: string]: HTMLElement} = {}
for (const root_id in render_item.roots) {
roots[root_id] = element
}
this._document.on_change((event: any) => this._change_event(event))
standalone.add_document_standalone(this._document, element, roots)
this._document.on_change((event) => this._change_event(event))
}

_combine_events(
new_msg: ModelChanged | MessageSent
): (ModelChanged | MessageSent)[] {
const new_msgs = []
_combine_events(new_msg: DocumentChanged): DocumentChanged[] {
const new_msgs: DocumentChanged[] = []
for (const msg of this._msgs) {
if (new_msg.kind != msg.kind) {
new_msgs.push(msg)
Expand All @@ -172,11 +147,13 @@
new_msgs.push(msg)
}
} else if (msg.kind == 'MessageSent' && new_msg.kind == 'MessageSent') {
// assert msg.msg_type == "bokeh_event"
const data = msg.msg_data as BokehEvent
const new_data = new_msg.msg_data as BokehEvent
if (
msg.msg_data.event_values.model == null ||
msg.msg_data.event_values.model.id !=
new_msg.msg_data.event_values.model.id ||
msg.msg_data.event_name != new_msg.msg_data.event_name
data.event_values.model == null ||
data.event_values.model.id != new_data.event_values.model.id ||
data.event_name != new_data.event_name
) {
new_msgs.push(msg)
}
Expand All @@ -186,7 +163,7 @@
return new_msgs
}

_send(msg: ModelChanged | MessageSent): void {
_send(msg: DocumentChanged): void {
if (!this._idle && this._combine && this.model.get('combine_events')) {
// Queue event and drop previous events on same model attribute
this._msgs = this._combine_events(msg)
Expand All @@ -200,21 +177,18 @@
if (this._blocked) {
return
}
const { Serializer } = bk_require('core/serialization')
const serialization = bk_require<{Serializer: typeof Serializer}>("core/serialization")
const references: Map<HasProps, Ref> = new Map()
for (const model of event.document._all_models.values()) {
references.set(model, model.ref())
}
const serializer = new Serializer({references})
const event_rep = serializer.encode(event)
event_rep.event = 'jsevent'
const serializer = new serialization.Serializer({references})
const event_rep = serializer.encode(event) as DocumentChanged & {event: "jsevent"}
event_rep.event = "jsevent"
this._send(event_rep)
}

protected _consume_patch(
content: { msg: 'patch'; payload?: Fragment },
buffers: DataView[]
): void {
protected _consume_patch(content: {msg: 'patch'; payload?: Fragment}, buffers: DataView[]): void {
if (this._document == null) {
return
}
Expand All @@ -225,7 +199,7 @@
if (comm_msg != null && keys(comm_msg.content).length > 0) {
this._blocked = true
try {
this._document.apply_json_patch(comm_msg.content, comm_msg.buffers)
this._document.apply_json_patch(comm_msg.content as Patch, comm_msg.buffers)
} finally {
this._blocked = false
}
Expand Down
7 changes: 6 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@
"strict": true,
"strictNullChecks": false,
"target": "es2017",
"types": []
"types": [],
"paths": {
"@bokeh/bokehjs/*": [
"./node_modules/@bokeh/bokehjs/build/js/lib/*"
]
}
},
"include": ["src/*"]
}
Loading
Loading