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

Merge jupyterlab-kernel-usage #163

Merged
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
51a9667
initial commit
echarles Jan 7, 2022
47d344e
add Makefile
echarles Jan 13, 2022
7d6d580
refactor react state
echarles Feb 2, 2022
9a7f312
add a timer with react hook to ensure the panel is refreshed - fixes …
echarles Feb 3, 2022
f8831be
panel content for #4 and #7
echarles Feb 14, 2022
0b75795
new icon fixes #3
echarles Feb 14, 2022
4130bb6
ensure minimal ipykernel version - fixes #8
echarles Feb 14, 2022
fb853b5
format cpu to 1 decimal - fixes #2
echarles Feb 14, 2022
c502e13
better format the memory numbers - fixes #1
echarles Feb 14, 2022
fc3c993
bump version to 0.2.0
echarles Feb 14, 2022
e7d1816
better format for the memory
echarles Feb 15, 2022
36d46b5
bump versoin to 0.3.0
echarles Feb 15, 2022
9615ee3
update preview image
echarles Feb 15, 2022
0344355
lint
echarles Feb 16, 2022
659e1b7
tune the panel layout
echarles Apr 5, 2022
9f8d62b
Merge pull request #12 from Quansight/fix/panel-layout
echarles Apr 5, 2022
82a8389
show a message is kernel usage is not available
echarles Apr 5, 2022
cfe4033
Merge pull request #13 from Quansight/fix/kernel-usage-not-available
echarles Apr 5, 2022
14ae0ee
Add PID
echarles Apr 5, 2022
966d6f5
Merge pull request #14 from Quansight/feat/pid
echarles Apr 5, 2022
eb33ff9
readme: add ipykernel requirement - closes #8
echarles Apr 8, 2022
0444216
release: 0.4.0
echarles Apr 11, 2022
35eae6c
remove old kernel poll
echarles Apr 19, 2022
c24ad0c
Merge pull request #17 from Quansight/fix/remove-old-kernel
echarles Apr 26, 2022
80d7dc0
don't throw timeout exception
echarles May 23, 2022
38e4a50
remove logging
echarles May 24, 2022
b98c9f8
Merge pull request #18 from Quansight/fix/deadlock
echarles May 24, 2022
7ab05a4
Pool only the active kernel and if the sidebar is visible
echarles Jun 6, 2022
17f53e0
Merge pull request #19 from Quansight/feat/avoid-constant-pooling
echarles Jun 6, 2022
c2a81ad
lint
echarles Jun 6, 2022
ba84246
polish the ui and add cpu_count
echarles Jun 9, 2022
193c6a3
Merge pull request #21 from Quansight/feat/ui-polish
echarles Jun 10, 2022
b22e7f8
release: 0.5.0
echarles Jun 10, 2022
f9e0a04
Fix issues related to fronted querying too often.
krassowski Dec 12, 2022
4970166
Limit the backend timeout to 1+2+3=6 seconds total
krassowski Dec 12, 2022
77a67e2
Clear kernel ID if disposed
krassowski Dec 12, 2022
683b470
Merge jupyterlab-kernel-usage
davidbrochart Dec 13, 2022
27d392f
Add ipykernel dependency
davidbrochart Dec 13, 2022
74a391c
Update README
davidbrochart Dec 14, 2022
1a28826
Add 'jupyterlab-kernel-usage/' from commit 'b22e7f8b35f1adf7a14f0ff2d…
davidbrochart Dec 14, 2022
22f4c31
Merge pull request #24 from krassowski/fix-request-issues
krassowski Dec 14, 2022
0bc37bf
Pin ipykernel>=6.11.0
davidbrochart Dec 14, 2022
b63f052
Merge commit '22f4c31825d52543a0fd19020aad81d94d4bf842' into jupyterl…
davidbrochart Dec 14, 2022
657b727
Remove jupyterlab-kernel-usage
davidbrochart Dec 14, 2022
6703e02
Include changes from https://github.com/Quansight/jupyterlab-kernel-u…
davidbrochart Dec 14, 2022
640c7c4
Lint
davidbrochart Dec 14, 2022
ec56106
Remove @jupyterlab/launcher dependency
davidbrochart Dec 15, 2022
9bfbcb5
Review
davidbrochart Dec 15, 2022
22d4e68
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 15, 2022
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
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ python -m pip install -e ".[dev]"
jupyter labextension develop . --overwrite

# go to the labextension directory
cd labextension/
cd packages/labextension/

# Rebuild extension Typescript source after making changes
jlpm run build
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ displays an indication of how much resources your current notebook server and
its children (kernels, terminals, etc) are using. This is displayed in the
main toolbar in the notebook itself, refreshing every 5s.

Kernel resource usage can be displayed in a sidebar for IPython kernels with
[ipykernel](https://github.com/ipython/ipykernel) >= 6.11.0.

## Installation

You can currently install this package from PyPI.
Expand Down
7 changes: 3 additions & 4 deletions jupyter_resource_usage/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import json
import os.path as osp
from pathlib import Path

from ._version import __version__
from .server_extension import load_jupyter_server_extension

HERE = osp.abspath(osp.dirname(__file__))
HERE = Path(__file__).parent.resolve()

with open(osp.join(HERE, "labextension", "package.json")) as fid:
data = json.load(fid)
data = json.loads((HERE / "labextension" / "package.json").read_text())


def _jupyter_labextension_paths():
Expand Down
44 changes: 44 additions & 0 deletions jupyter_resource_usage/api.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import json
from concurrent.futures import ThreadPoolExecutor

import ipykernel
import psutil
import zmq
from jupyter_client.jsonutil import date_default
from jupyter_server.base.handlers import APIHandler
from jupyter_server.utils import url_path_join
from packaging import version
from tornado import web
from tornado.concurrent import run_on_executor

Expand All @@ -13,6 +18,11 @@
from .utils import Callable


USAGE_IS_SUPPORTED = version.parse("6.9.0") <= version.parse(ipykernel.__version__)

MAX_RETRIES = 3


class ApiHandler(APIHandler):
executor = ThreadPoolExecutor(max_workers=5)

Expand Down Expand Up @@ -74,3 +84,37 @@ def get_cpu_percent(p):
return 0

return sum([get_cpu_percent(p) for p in all_processes])


class KernelUsageHandler(APIHandler):
@web.authenticated
async def get(self, matched_part=None, *args, **kwargs):

if not USAGE_IS_SUPPORTED:
self.write(json.dumps({}))
return

kernel_id = matched_part
km = self.kernel_manager
lkm = km.pinned_superclass.get_kernel(km, kernel_id)
session = lkm.session
client = lkm.client()

control_channel = client.control_channel
usage_request = session.msg("usage_request", {})

control_channel.send(usage_request)
poller = zmq.Poller()
control_socket = control_channel.socket
poller.register(control_socket, zmq.POLLIN)
for i in range(1, MAX_RETRIES + 1):
timeout_ms = 1000 * i
events = dict(poller.poll(timeout_ms))
if not events:
self.write(json.dumps({}))
break
if control_socket not in events:
continue
res = await client.control_channel.get_msg(timeout=0)
self.write(json.dumps(res, default=date_default))
break
10 changes: 10 additions & 0 deletions jupyter_resource_usage/server_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from tornado import ioloop

from jupyter_resource_usage.api import ApiHandler
from jupyter_resource_usage.api import KernelUsageHandler
from jupyter_resource_usage.config import ResourceUseDisplay
from jupyter_resource_usage.metrics import PSUtilMetricsLoader
from jupyter_resource_usage.prometheus import PrometheusHandler
Expand All @@ -18,6 +19,15 @@ def load_jupyter_server_extension(server_app):
server_app.web_app.add_handlers(
".*", [(url_path_join(base_url, "/api/metrics/v1"), ApiHandler)]
)
server_app.web_app.add_handlers(
".*$",
[
(
url_path_join(base_url, "jupyterlab_kernel_usage", r"get_usage/(.+)$"),
KernelUsageHandler,
)
],
)

if resuseconfig.enable_prometheus_metrics:
callback = ioloop.PeriodicCallback(
Expand Down
55 changes: 45 additions & 10 deletions packages/labextension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
],
"main": "lib/index.js",
"types": "lib/index.d.ts",
"style": "style/index.css",
"repository": {
"type": "git",
"url": "https://github.com/jupyter-server/jupyter-resource-usage.git"
Expand All @@ -41,23 +42,57 @@
"watch:labextension": "jupyter labextension watch ."
},
"dependencies": {
"@jupyterlab/application": "^3.0.0",
"@jupyterlab/apputils": "^3.0.0",
"@jupyterlab/coreutils": "^5.0.0",
"@jupyterlab/services": "^6.0.0",
"@jupyterlab/statusbar": "^3.0.0",
"@jupyterlab/translation": "^3.0.0",
"@lumino/polling": "^1.3.3",
"typestyle": "^2.0.4"
"@jupyterlab/application": "^3.5.1",
"@jupyterlab/apputils": "^3.5.1",
"@jupyterlab/coreutils": "^5.5.1",
"@jupyterlab/notebook": "^3.5.1",
"@jupyterlab/services": "^6.5.1",
"@jupyterlab/statusbar": "^3.5.1",
"@jupyterlab/translation": "^3.5.1",
"@lumino/polling": "^1.11.3",
"typestyle": "^2.4.0"
},
"devDependencies": {
"@jupyterlab/builder": "^3.0.0",
"@jupyterlab/builder": "^3.5.1",
"@typescript-eslint/eslint-plugin": "^4.8.1",
"@typescript-eslint/parser": "^4.8.1",
"eslint": "^7.14.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-prettier": "^3.1.4",
"mkdirp": "^1.0.3",
"npm-run-all": "^4.1.5",
"prettier": "^2.1.1",
"rimraf": "^3.0.2",
"typescript": "~4.0.3"
"typescript": "~4.1.3"
},
"sideEffects": [
"style/*.css",
"style/index.js"
],
"styleModule": "style/index.js",
"publishConfig": {
"access": "public"
},
"jupyterlab": {
"discovery": {
"server": {
"managers": [
"pip"
],
"base": {
"name": "jupyterlab_kernel_usage"
}
}
},
"extension": true,
"outputDir": "../../jupyter_resource_usage/labextension"
},
"jupyter-releaser": {
"hooks": {
"before-build-npm": [
"python -m pip install jupyterlab~=3.1",
"jlpm"
]
}
}
}
57 changes: 57 additions & 0 deletions packages/labextension/src/format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Taken from https://github.com/jupyter-server/jupyter-resource-usage/blob/e6ec53fa69fdb6de8e878974bcff006310658408/packages/labextension/src/memoryUsage.tsx#L272
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these comments needed now that the files live in the same repo?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like memoryUsage.tsx can now reuse things from format.ts?


type MemoryUnit = 'B' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB';

const MEMORY_UNIT_LIMITS: {
readonly [U in MemoryUnit]: number;
} = {
B: 1,
KB: 1024,
MB: 1048576,
GB: 1073741824,
TB: 1099511627776,
PB: 1125899906842624,
};

export function formatForDisplay(numBytes: number | undefined): string {
const lu = convertToLargestUnit(numBytes);
return lu[0].toFixed(2) + ' ' + lu[1];
}

/**
* Given a number of bytes, convert to the most human-readable
* format, (GB, TB, etc).
* Taken from https://github.com/jupyter-server/jupyter-resource-usage/blob/e6ec53fa69fdb6de8e878974bcff006310658408/packages/labextension/src/memoryUsage.tsx#L272
*/
function convertToLargestUnit(
numBytes: number | undefined
): [number, MemoryUnit] {
if (!numBytes) {
return [0, 'B'];
}
if (numBytes < MEMORY_UNIT_LIMITS.KB) {
return [numBytes, 'B'];
} else if (
MEMORY_UNIT_LIMITS.KB === numBytes ||
numBytes < MEMORY_UNIT_LIMITS.MB
) {
return [numBytes / MEMORY_UNIT_LIMITS.KB, 'KB'];
} else if (
MEMORY_UNIT_LIMITS.MB === numBytes ||
numBytes < MEMORY_UNIT_LIMITS.GB
) {
return [numBytes / MEMORY_UNIT_LIMITS.MB, 'MB'];
} else if (
MEMORY_UNIT_LIMITS.GB === numBytes ||
numBytes < MEMORY_UNIT_LIMITS.TB
) {
return [numBytes / MEMORY_UNIT_LIMITS.GB, 'GB'];
} else if (
MEMORY_UNIT_LIMITS.TB === numBytes ||
numBytes < MEMORY_UNIT_LIMITS.PB
) {
return [numBytes / MEMORY_UNIT_LIMITS.TB, 'TB'];
} else {
return [numBytes / MEMORY_UNIT_LIMITS.PB, 'PB'];
}
}
44 changes: 44 additions & 0 deletions packages/labextension/src/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { URLExt } from '@jupyterlab/coreutils';
import { ServerConnection } from '@jupyterlab/services';

/**
* Call the API extension
*
* @param endPoint API REST end point for the extension
* @param init Initial values for the request
* @returns The response body interpreted as JSON
*/
export async function requestAPI<T>(
endPoint = '',
init: RequestInit = {}
): Promise<T> {
const settings = ServerConnection.makeSettings();
const requestUrl = URLExt.join(
settings.baseUrl,
'jupyterlab_kernel_usage', // API Namespace
endPoint
);

let response: Response;
try {
response = await ServerConnection.makeRequest(requestUrl, init, settings);
} catch (error) {
throw new ServerConnection.NetworkError(error as any);
}

let data: any = await response.text();

if (data.length > 0) {
try {
data = JSON.parse(data);
} catch (error) {
console.log('Not a JSON response body.', response);
}
}

if (!response.ok) {
throw new ServerConnection.ResponseError(response, data.message || data);
}

return data;
}
44 changes: 42 additions & 2 deletions packages/labextension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,35 @@ import {
JupyterFrontEnd,
JupyterFrontEndPlugin,
} from '@jupyterlab/application';
import { INotebookTracker } from '@jupyterlab/notebook';
import { LabIcon } from '@jupyterlab/ui-components';
import { ICommandPalette } from '@jupyterlab/apputils';
import { KernelUsagePanel } from './panel';
import tachometer from '../style/tachometer.svg';

import { IStatusBar } from '@jupyterlab/statusbar';

import { ITranslator } from '@jupyterlab/translation';

import { MemoryUsage } from './memoryUsage';

namespace CommandIDs {
export const getKernelUsage = 'kernel-usage:get';
}

/**
* Initialization data for the jupyter-resource-usage extension.
*/
const extension: JupyterFrontEndPlugin<void> = {
id: '@jupyter-server/resource-usage:memory-status-item',
autoStart: true,
requires: [IStatusBar, ITranslator],
requires: [IStatusBar, ITranslator, ICommandPalette, INotebookTracker],
activate: (
app: JupyterFrontEnd,
statusBar: IStatusBar,
translator: ITranslator
translator: ITranslator,
palette: ICommandPalette,
notebookTracker: INotebookTracker
) => {
const item = new MemoryUsage(translator);

Expand All @@ -30,6 +41,35 @@ const extension: JupyterFrontEndPlugin<void> = {
isActive: () => item.model.metricsAvailable,
activeStateChanged: item.model.stateChanged,
});

const { commands, shell } = app;
const category = 'Kernel Resource';

let panel: KernelUsagePanel | null = null;

function createPanel() {
if (!panel || panel.isDisposed) {
panel = new KernelUsagePanel({
widgetAdded: notebookTracker.widgetAdded,
currentNotebookChanged: notebookTracker.currentChanged,
});
shell.add(panel, 'right', { rank: 200 });
}
}

commands.addCommand(CommandIDs.getKernelUsage, {
label: 'Kernel Usage',
caption: 'Kernel Usage',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to pass these strings through the translator.

icon: new LabIcon({
name: 'jupyterlab-kernel-usage:icon',
svgstr: tachometer,
}),
execute: createPanel,
});

palette.addItem({ command: CommandIDs.getKernelUsage, category });

createPanel();
},
};

Expand Down
Loading