Notebook / Extensions / APIs
===
> #ipython, jupyter notebook, python notebook, jupyterlab, elyra
###### tags: `Jupyter`
###### tags: `Jupyter`, `JupyterLab`, `Notebook`, `Elyra`, `Extension`
<br>
[TOC]
<br>
<hr>
<br>
## APIs 大全
### jupyterlab
- doc1
https://jupyterlab.readthedocs.io/en/stable/api/modules.html
- doc2 (更詳細,有繼承資訊、實作介面)
https://jupyterlab.github.io/jupyterlab/globals.html
MainAreaWidget: [github](https://jupyterlab.github.io/jupyterlab/classes/_apputils_src_index_.mainareawidget.html) vs [readthedocs](https://jupyterlab.readthedocs.io/en/stable/api/modules/apputils.mainareawidget.html)
- github
https://github.com/jupyterlab/jupyterlab/tree/master/packages
### JupyterLab Services
- [overview](https://github.com/jupyterlab/jupyterlab/blob/master/packages/services/README.md)
- [doc](https://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml)
### lumino
> Lumino is a set of JavaScript packages, written in TypeScript, that provide a rich toolkit of widgets, layouts, events, and data structures. These enable developers to construct extensible high-performance desktop-like web applications, such as JupyterLab. Lumino was formerly known as [PhosphorJS](https://phosphorjs.github.io/).
>
> Lumino 是一套使用 TypeScript 編寫而成的 JavaScript 套件,提供了豐富的 widge t(小工具)、layout (佈局)、event (事件) 和 data structure (資料結構) 工具箱。 這些使開發人員能夠建構可擴展的高效能類似桌面的 Web 應用應試,例如 JupyterLab。 Lumino 以前稱為 [PhosphorJS](https://phosphorjs.github.io/)。
>
- doc
https://jupyterlab.github.io/lumino/
- github
https://github.com/jupyterlab/lumino/tree/main/packages
<br>
<hr>
<br>
## `@jupyterlab/application`
### [`Class JupyterFrontEnd<T, U>`](https://jupyterlab.github.io/jupyterlab/classes/_application_src_index_.jupyterfrontend.html)
> [github](https://github.com/jupyterlab/jupyterlab/blob/master/packages/application/src/frontend.ts)
- [restored](https://jupyterlab.github.io/jupyterlab/classes/_application_src_index_.jupyterfrontend.html#restored)
- 等待 app ready 的兩種作法
- 方法一(建議)
```
Promise.all([app.restored]).then(() => {
console.log('ready1');
});
```
- 方法二(不建議)
```
app.serviceManager.ready.then(() => {
console.log('ready2');
});
```
- ready2 會比 ready1 早執行
- 在此階段,shell 還未 ready,呼叫會失敗
```typescript=
import {
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '@jupyterlab/application';
import {
MainAreaWidget
} from '@jupyterlab/apputils';
import {
Widget
} from '@lumino/widgets';
/**
* Initialization data for the my_test2 extension.
*/
const plugin: JupyterFrontEndPlugin<void> = {
id: 'my_test2:plugin',
autoStart: true,
activate: (app: JupyterFrontEnd) => {
console.log('my_test2 is activated!');
const content = new Widget();
const widget = new MainAreaWidget({content});
widget.title.label = 'MainArea';
app.serviceManager.ready.then(() => {
console.log('[my_test2][1] is ready!');
console.log('[my_test2][1] >> widget.isAttached:', widget.isAttached);
app.shell.add(widget, 'main'); // failed
console.log('[my_test2][1] << widget.isAttached:', widget.isAttached);
});
Promise.all([app.restored])
.then(() => {
console.log('[my_test2][2] is ready!');
console.log('[my_test2][2] >> widget.isAttached:', widget.isAttached);
app.shell.add(widget, 'main');
console.log('[my_test2][2] << widget.isAttached:', widget.isAttached);
});
}
};
export default plugin;
```

- 底下有一些範例,不確定上面是否有誤用?
- [signals](https://github.com/jupyterlab/extension-examples/blob/master/signals/src/index.ts#L64)
- [Kernel Messaging](https://github.com/jupyterlab/extension-examples/blob/master/kernel-messaging/README.md)
- [Kernel Output](https://github.com/jupyterlab/extension-examples/tree/master/kernel-output)
<br>
## `@jupyterlab/apputils`
> [[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/apputils/src/) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/apputils.html)
### showDialog
> [[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/apputils/src/dialog.tsx#L25) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/apputils.html#showdialog)
### showErrorMessage
> [[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/apputils/src/dialog.tsx#L40) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/apputils.html#showerrormessage)
```typescript=
import {
showErrorMessage
} from '@jupyterlab/apputils';
showErrorMessage(
'Title: Error',
'Body: content'
);
```
```typescript=
import {
Dialog,
showErrorMessage
} from '@jupyterlab/apputils';
showErrorMessage(
'Title: Error',
'Body: content',
[Dialog.okButton({label: 'Dismiss'})]
);
```
### MainAreaWidget
> [[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/apputils/src/mainareawidget.ts), [[doc1: readthedocs]](https://jupyterlab.readthedocs.io/en/stable/api/classes/apputils.mainareawidget-1.html), [[doc2: github]](https://jupyterlab.github.io/jupyterlab/classes/_apputils_src_index_.mainareawidget.html)
- layout

- id
```typescript
import { UUID } from '@lumino/coreutils';
```
```typescript
import { DOMUtils } from './domutils';
this.id = DOMUtils.createDomID();
```
### Toolbar
> [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/classes/apputils.toolbar-1.html)
<br>
## `@jupyterlab/coreutils`
> [[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/coreutils/src/) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/coreutils.pageconfig.html)
>
> 資料來源:
> 1. [jupyterlab-latex](https://github.com/jupyterlab/jupyterlab-latex/blob/master/src/index.ts)
> - PathExt, URLExt
### PageConfig
> [[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/coreutils/src/pageconfig.ts) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/coreutils.pageconfig.html)
- ### [getOption](https://jupyterlab.readthedocs.io/en/stable/api/modules/coreutils.pageconfig.html#getoption)
> Get global configuration data for the Jupyter application.
- [How to access default paths from within the JupyterLab client (solved)](https://discourse.jupyter.org/t/how-to-access-default-paths-from-within-the-jupyterlab-client-solved/1509)
- ### [setOption](https://jupyterlab.readthedocs.io/en/stable/api/modules/coreutils.pageconfig.html#setoption)
> Set global configuration data for the Jupyter application.
### [PathExt](https://jupyterlab.readthedocs.io/en/stable/api/modules/coreutils.pathext.html)
- ### [`dirname`](https://jupyterlab.readthedocs.io/en/stable/api/modules/coreutils.pathext.html#dirname), [`basename`](https://jupyterlab.readthedocs.io/en/stable/api/modules/coreutils.pathext.html#basename), [`extname`](https://jupyterlab.readthedocs.io/en/stable/api/modules/coreutils.pathext.html#extname)
```typescript=
import { PathExt } from '@jupyterlab/coreutils';
const filename = '/xxx/yyy/zzz/README.md';
console.log("filename:", filename);
console.log("dirname:", PathExt.dirname(filename));
console.log("basename:", PathExt.basename(filename));
console.log("extname:", PathExt.extname(filename));
```
執行結果:
```=
filename: /xxx/yyy/zzz/README.md
dirname: xxx/yyy/zzz
basename: README.md
extname: .md
```
- ### [`normalize`](https://jupyterlab.readthedocs.io/en/stable/api/modules/coreutils.pathext.html#normalize)
```typescript=
import { PathExt } from '@jupyterlab/coreutils';
console.log("/xxx/README.md:", PathExt.normalize('./xxx/README.md'));
console.log("/xxx///README.md:", PathExt.normalize('./xxx///README.md'));
console.log("/xxx/yyy/../../zzz/README.md:", PathExt.normalize('/xxx/yyy/../../zzz/README.md'));
```
執行結果:
```=
/xxx/README.md: xxx/README.md
/xxx///README.md: xxx/README.md
/xxx/yyy/../zzz/README.md: zzz/README.md
```
<br>
<hr>
<br>
## `@jupyterlab/codeeditor`
> Sources:
> 1. [jupyterlab-latex](https://github.com/jupyterlab/jupyterlab-latex/blob/master/src/index.ts)
- CodeEditor
<br>
<hr>
<br>
## `@jupyterlab/docmanager-extension`
[[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/docmanager-extension) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/docmanager_extension.html)
<br>
### namespace CommandIDs
> 並沒有 expose 出來
[[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/docmanager-extension/src/index.tsx#L53)
- deleteFile
- newUntitled
- open
- rename
- del
- save
- saveAll
- saveAs
<br>
### addCommands
[[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/docmanager-extension/src/index.tsx#L517)
- ### [`commands.addCommand(CommandIDs.open, {...}`](https://github.com/jupyterlab/jupyterlab/blob/master/packages/docmanager-extension/src/index.tsx#L588)
- ### [`commands.addCommand(CommandIDs.save, {...}`](https://github.com/jupyterlab/jupyterlab/blob/master/packages/docmanager-extension/src/index.tsx#L713)
<br>
<hr>
<br>
## `@jupyterlab/mainmenu-extension`
[[src]](https://github.com/jupyterlab/jupyterlab/tree/master/packages/mainmenu-extension/src) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/mainmenu_extension.html)
info:
- [jtpio / jupyterlab-python-file](https://github.com/jtpio/jupyterlab-python-file) :+1: :+1: :+1: :100:
<br>
<hr>
<br>
## `@jupyterlab/mainmenu`
[[src]](https://github.com/jupyterlab/jupyterlab/tree/master/packages/mainmenu/src) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/mainmenu.html)
info:
- [jtpio / jupyterlab-python-file](https://github.com/jtpio/jupyterlab-python-file) :+1: :+1: :+1: :100:
### debug menu
```typescript=
activate: (app: JupyterFrontEnd, menu: IMainMenu | null) => {
console.log('menu.fileMenu.menu:', menu);
}
```

| menu | `_rank` |
| -------------- | ---- |
| `fileMenu` | 1 |
| `editMenu` | 2 |
| `viewMenu` | 3 |
| `runMenu` | 4 |
| `kernelMenu` | 5 |
| `tabsMenu` | 500 |
| `settingsMenu` | 999 |
| `helpMenu` | 1000 |
### debug fileMenu
```typescript=
activate: (app: JupyterFrontEnd, menu: IMainMenu | null) => {
console.log('menu.fileMenu:', menu.fileMenu);
}
```
### 目標:停用 Log Out (command: `filemenu:logout`)

> **Q & A:**
> - [Requesting help with how to remove menu items from the Jupyterlab Context menu](https://discourse.jupyter.org/t/requesting-help-with-how-to-remove-menu-items-from-the-jupyterlab-context-menu/3427)
> 
>
> - [Add hook to add/remove items from a filebrowser's contextMenu #3994](https://github.com/jupyterlab/jupyterlab/issues/3994)
> 
> #暴力法
0. 該選項,定義在 [jupyterlab / packages / mainmenu-extension / src / index.ts#L438](https://github.com/jupyterlab/jupyterlab/blob/master/packages/mainmenu-extension/src/index.ts#L438)

1. 無法替換 `filemenu:logout` command,會有 error:
```typescript=
commands.addCommand('filemenu:logout', {
label: '[TJ] Log Out',
caption: 'Log out of the Hub',
isEnabled: () => false,
execute: () => {
console.log('logout is disabled');
}
});
```
> index.es6.js:289 Error: Command 'filemenu:logout' already registered.

2. 從 commands 物件著手,嘗試存取:
> Property '_commands' is ==**private**== and only accessible within class 'CommandRegistry'.

3. 從 [source code](https://github.com/jupyterlab/jupyterlab/blob/master/packages/mainmenu-extension/src/index.ts#L166) 著手 ==(有效 :+1:)==
```typescript==166
const quitButton = PageConfig.getOption('quitButton').toLowerCase();
menu.fileMenu.quitEntry = quitButton === 'true';
```
- 添加程式碼
```
menu.fileMenu.quitEntry = false;
```
- menu 選項變化
 
- command pallete 選項
修改前:

修改後:

4. 透過 PageConfig.setOption() 變更選項值 ==(無效!!)==
- 添加程式碼
```typescript=
import { PageConfig } from '@jupyterlab/coreutils';
...
Promise.all([app.restored]).then(() => {
console.log(">> getOption('quitButton'):", PageConfig.getOption('quitButton'));
PageConfig.setOption('quitButton', 'false');
console.log("<< getOption('quitButton'):", PageConfig.getOption('quitButton'));
});
```
- **無效原因:**
此時 menu.FileMenu 的屬性值已經被初始化
5. 直接從 `jupyter_lab_config.py` 修改 ==(有效 :+1:)==
1. 產生 config
```
$ jupyter-lab --generate-config
Writing default config to: /home/tj/.jupyter/jupyter_lab_config.py
```
2. 修改屬性

結果如下:
```
## If True, display controls to shut down the Jupyter server, such as menu items
# or buttons.
# Default: True
c.ServerApp.quit_button = False
```
- 參考資料
- [Lacking quit buttton after setting command line flag #8188](https://github.com/jupyterlab/jupyterlab/issues/8188)

6. 使用 js 模板,不使用 ts 模板,避開 ts 編譯器的檢查 ==(有效 :+1:)==
```javascript=
Promise.all([app.researved]).then((_) => {
// app 完成讀取後,然要等 mainmenu 載入完成
setTimeout(() => {
cmd = app.commands._commands['filemenu:logout'];
cmd.isEnabled = () => false;
}, 2000);
});
```

<br>
### 目標:移除 File menu
```
menu.fileMenu.dispose();
```
<br>
### 目標:移除整個 menu

```
$ jupyter labextension enable @jupyterlab/mainmenu-extension
```
<br>
### 目標:在 File > New 中加入選項

```
menu.fileMenu.newMenu.addGroup([{ command }], 0);
menu.fileMenu.newMenu.addItem({ command });
```
- 說明
- addGroup 後在 menu 前後加上分隔線 (separator)
- 參考資料
- [jtpio / jupyterlab-python-file / src / index.ts](https://github.com/jtpio/jupyterlab-python-file/blob/main/src/index.ts#L88)
<br>
### Class MainMenu
[[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/mainmenu/src/mainmenu.ts) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/classes/mainmenu.mainmenu-1.html)
<br>
<hr>
<br>
## `@jupyterlab/services`
[[src]](https://github.com/jupyterlab/jupyterlab/tree/master/packages/services) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/services.html)
info:
- 如何讀取 local 端檔案
### Namespace Contents
[[src]](https://github.com/jupyterlab/jupyterlab/tree/master/packages/services/src/contents) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/services.contents.html#contenttype)
- ### SERVICE_DRIVE_URL
#url, endpoint
```
/**
* The url for the default drive service.
*/
const SERVICE_DRIVE_URL = 'api/contents';
```
- ### Type ContentType
[[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/services/src/contents/index.ts#L118) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/services.contents.html#contenttype)
- ### Type FileFormat
[[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/services/src/contents/index.ts#L123) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/services.contents.html#fileformat)
- ### IManager.get
> `get(path: string, options?: IFetchOptions): Promise<IModel>;`
> Get a file or directory.
[[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/services/src/contents/index.ts#L696) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/interfaces/services.contents.imanager.html#get)
#read, get, input, file
- [用法1](https://github.com/jupyterlab/jupyterlab/blob/master/packages/docmanager-extension/src/index.tsx#L588)
#docmanager-extension
```typescript=
return docManager.services.contents
.get(path, { content: false })
.then(() => docManager.openOrReveal(path, factory, kernel, options));
```
- ### IManager.newuntitled
> `IManager.newUntitled(options?: ICreateOptions): Promise<IModel>;`
> Create a new untitled file or directory in the specified directory path.
[[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/services/src/contents/index.ts#L748) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/interfaces/services.contents.imanager.html#newuntitled)
- [用法1](https://github.com/jupyterlab/jupyterlab/blob/master/packages/docmanager-extension/src/index.tsx#L588)
#docmanager-extension
```typescript=
return docManager.services.contents
.newUntitled(options)
.catch(error => showErrorMessage(errorTitle, error));
```
- ### IManager.save
> `IManager.save(path: string, options?: Partial<IModel>): Promise<IModel>;`
> Save a file.
[[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/services/src/contents/index.ts#L810) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/interfaces/services.contents.imanager.html#save)
#write, set, output, file
- 範例
- [filebrowser/src/model.ts](https://github.com/jupyterlab/jupyterlab/blob/master/packages/filebrowser/src/model.ts#L459)

- also
- [docregistry/src/registry.ts#IContext.save()](https://github.com/jupyterlab/jupyterlab/blob/602b05399b0ca762613c8f560a49b15abdefee39/packages/docregistry/src/registry.ts#L966)
- [docregistry/src/context.ts#save()](https://github.com/jupyterlab/jupyterlab/blob/602b05399b0ca762613c8f560a49b15abdefee39/packages/docregistry/src/context.ts#L298)
- [docregistry/src/context.ts#_save()](https://github.com/jupyterlab/jupyterlab/blob/602b05399b0ca762613c8f560a49b15abdefee39/packages/docregistry/src/context.ts#L594)
<br>
### Namespace ServerConnection
[[src]](https://github.com/jupyterlab/jupyterlab/blob/master/packages/services/src/serverconnection.ts) [[doc]](https://jupyterlab.readthedocs.io/en/stable/api/modules/services.serverconnection.html)
- ### [makeSettings](https://jupyterlab.readthedocs.io/en/stable/api/modules/services.serverconnection.html#makesettings)
- [範例](https://github.com/jupyterlab/extension-examples/blob/master/server-extension/src/handler.ts#L17)
- ### [makeRequest](https://jupyterlab.readthedocs.io/en/stable/api/modules/services.serverconnection.html#makerequest)
<br>
<hr>
<br>
## [`@lumino/commands`](https://jupyterlab.github.io/lumino/commands/index.html)
- [github](https://github.com/jupyterlab/lumino/blob/main/packages/commands/src/index.ts)
- [JupyterLab doc](https://jupyterlab.readthedocs.io/en/stable/extension/extension_points.html#commands)
```typescript=
const commandID = 'my-command';
let toggled = false;
app.commands.addCommand(commandID, {
label: 'My Cool Command',
isEnabled: () => true,
isVisible: () => true,
isToggled: () => toggled,
iconClass: 'some-css-icon-class',
execute: () => {
console.log(`Executed ${commandID}`);
toggled = !toggled;
});
```
### [`Class CommandRegistry`]()
- [isEnabled](https://jupyterlab.github.io/lumino/commands/classes/commandregistry.html#isenabled)
- [isToggled](https://jupyterlab.github.io/lumino/commands/classes/commandregistry.html#istoggled)
- [isVisible](https://jupyterlab.github.io/lumino/commands/classes/commandregistry.html#isvisible)
- [label](https://jupyterlab.github.io/lumino/commands/classes/commandregistry.html#label)
<br>
<hr>
<br>
## JupyterLab Services
> https://github.com/jupyterlab/jupyterlab/blob/master/packages/services/README.md
### REST API Docs
> https://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml
- ### 讓 yaml 跑在 local 端
```yaml
schemes:
- "http"
host: "localhost:8888"
```
- 但須額外添加 token (參數:`&token=xxx`)
- token 來源:在執行 jupyter lab 時會顯示在 log 裡
- ### contents

- `/api/contents`
<br>
### contents: get
```bash
TOKEN=322a7f056033fb322ecdfc30b551bd306029eb37b669a1e0
curl "http://localhost:8888/api/contents/hello.py?type=file&format=text&content=1&token=$TOKEN"
```
- 要加雙引號才能正常執行
執行結果:
```json=
{
"name": "hello.py",
"path": "hello.py",
"last_modified": "2022-01-03T06:33:30.625767Z",
"created": "2022-01-03T06:33:30.625767Z",
"content": "print('hello!')",
"format": "text",
"mimetype": "text/x-python",
"size": 15,
"writable": true,
"type": "file"
}
```
<br>
### contents: save
```bash
TOKEN=322a7f056033fb322ecdfc30b551bd306029eb37b669a1e0
curl -X 'PUT' \
"http://localhost:8888/api/contents/hello.py?token=${TOKEN}" \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name": "hello.py",
"path": "hello.py",
"type": "file",
"format": "text",
"content": "print('\''hello!'\'')"
}'
```
- `"http://localhost:8888/api/contents/hello.py?token=${TOKEN}"`
- 要使用雙引號,才能引入 ${TOKEN}
- 使用單引號會失敗
- path
- api/contents/hello.py
workspace:/hello.py
- api/contents/sample/hello.py
workspace:/sample/hello.py
- 不清楚 body 中的 path 含意
執行結果:
```json=
{
"name": "hello.py",
"path": "hello.py",
"last_modified": "2022-01-03T06:40:33.632436Z",
"created": "2022-01-03T06:40:33.632436Z",
"content": null,
"format": null,
"mimetype": "text/x-python",
"size": 15,
"writable": true,
"type": "file"
}
```
<br>
<hr>
<br>
## Tornado
### Application.add_handlers(handlers: List[Union[Rule, Tuple]])
> [[src]](https://www.tornadoweb.org/en/stable/_modules/tornado/web.html#Application.add_handlers) [[doc]](https://www.tornadoweb.org/en/stable/web.html#tornado.web.Application.add_handlers)
- [tornado.web.Application類配置及使用](https://www.codeprj.com/zh/blog/58e01a1.html)
<br>
<hr>
<br>
## 參考資料
### @jupyterlab/docmanager
- [How to use @jupyterlab/docmanager - 10 common examples](https://snyk.io/advisor/npm-package/@jupyterlab/docmanager/example)