Skip to content

Commit

Permalink
Merge pull request #317 from mbektas/custom_env_support
Browse files Browse the repository at this point in the history
Custom python environment support
  • Loading branch information
mbektas committed Nov 14, 2021
2 parents 8face9d + 5f6dfbc commit 9e3747c
Show file tree
Hide file tree
Showing 13 changed files with 904 additions and 450 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
"license": "BSD-3-Clause",
"devDependencies": {
"@yarnpkg/lockfile": "~1.1.0",
"@types/ejs": "^3.1.0",
"css-loader": "~5.1.1",
"electron": "^12.0.0",
"electron-builder": "^22.11.11",
Expand Down Expand Up @@ -258,7 +259,7 @@
"@types/semver": "^7.3.4",
"@types/yargs": "^16.0.0",
"bottlejs": "^2.0.0",
"electron-debug": "^3.2.0",
"ejs": "^3.1.6",
"electron-log": "^4.3.2",
"fix-path": "^3.0.0",
"node-fetch": "~2.6.1",
Expand Down
3 changes: 3 additions & 0 deletions scripts/copyassets.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ function copyAssests() {
const htmlPath = path.join('browser', 'index.html');
fs.copySync(path.join(srcDir, htmlPath), path.join(dest, htmlPath));

const envInfoPath = path.join('main', 'env_info.py');
fs.copySync(path.join(srcDir, envInfoPath), path.join(dest, envInfoPath));

// Copy install scripts
if (platform === 'darwin') {
fs.copySync(path.join(path.resolve('./'), 'electron-builder-scripts', 'postinstall'), path.join(buildDir, 'pkg-scripts', 'postinstall'));
Expand Down
6 changes: 4 additions & 2 deletions src/browser/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class Application extends React.Component<Application.IProps, Application.IState
this._launchFromPath = this._launchFromPath.bind(this);

if (this.props.options.serverState === 'local') {
this.state = {renderSplash: this._renderSplash, renderState: this._renderEmpty, remotes: []};
this.state = {renderSplash: this._renderEmpty, renderState: this._renderEmpty, remotes: []};
asyncRemoteRenderer.runRemoteMethod(IServerFactory.requestServerStart, undefined)
.then((data) => {
this._serverReady(data);
Expand Down Expand Up @@ -191,7 +191,9 @@ class Application extends React.Component<Application.IProps, Application.IState
}
this._lab.restored.then( () => {
ipcRenderer.send('lab-ready');
(this.refs.splash as SplashScreen).fadeSplashScreen();
if (this.refs.splash) {
(this.refs.splash as SplashScreen).fadeSplashScreen();
}
});
});
})
Expand Down
145 changes: 145 additions & 0 deletions src/browser/extensions/desktop-extension/envStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

import { VDomModel, VDomRenderer } from '@jupyterlab/apputils';
import React from 'react';
import { GroupItem, interactiveItem, TextItem } from '@jupyterlab/statusbar';
import {
pythonIcon
} from '@jupyterlab/ui-components';

/**
* A pure functional component for rendering environment status.
*/
function EnvironmentStatusComponent(
props: EnvironmentStatusComponent.IProps
): React.ReactElement<EnvironmentStatusComponent.IProps> {
return (
<GroupItem onClick={props.handleClick} spacing={2} title={props.description}>
<pythonIcon.react title={''} top={'2px'} stylesheet={'statusBar'} />
<TextItem source={props.name} />
</GroupItem>
);
}

/**
* A namespace for EnvironmentStatusComponent statics.
*/
namespace EnvironmentStatusComponent {
/**
* Props for the environment status component.
*/
export interface IProps {
/**
* A click handler for the environment status component. By default
* we have it bring up the environment change dialog.
*/
handleClick: () => void;

/**
* The name the environment.
*/
name: string;

/**
* The description of the environment.
*/
description: string;
}
}

/**
* A VDomRenderer widget for displaying the environment.
*/
export class EnvironmentStatus extends VDomRenderer<EnvironmentStatus.Model> {
/**
* Construct the environment status widget.
*/
constructor(opts: EnvironmentStatus.IOptions) {
super(new EnvironmentStatus.Model());
this.model.name = opts.name;
this.model.description = opts.description;
this._handleClick = opts.onClick;
this.addClass(interactiveItem);
}

/**
* Render the environment status item.
*/
render() {
if (this.model === null) {
return null;
} else {
return (
<EnvironmentStatusComponent
name={this.model.name}
description={this.model.description}
handleClick={this._handleClick}
/>
);
}
}

private _handleClick: () => void;
}

/**
* A namespace for EnvironmentStatus statics.
*/
export namespace EnvironmentStatus {
export class Model extends VDomModel {
constructor() {
super();

this._name = 'env';
this._description = '';
}

get name() {
return this._name;
}

set name(val: string) {
const oldVal = this._name;
if (oldVal === val) {
return;
}
this._name = val;
this.stateChanged.emit(void 0);
}

get description(): string {
return this._description;
}
set description(val: string) {
const oldVal = this._description;
if (oldVal === val) {
return;
}
this._description = val;
this.stateChanged.emit(void 0);
}

private _name: string;
private _description: string;
}

/**
* Options for creating a EnvironmentStatus object.
*/
export interface IOptions {
/**
* Environment name
*/
name: string;
/**
* Environment description
*/
description: string;
/**
* A click handler for the item. By default
* we launch an environment selection dialog.
*/
onClick: () => void;
}
}
32 changes: 30 additions & 2 deletions src/browser/extensions/desktop-extension/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
IMainMenu
} from '@jupyterlab/mainmenu';

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

import {
JupyterFrontEndPlugin
} from '@jupyterlab/application';
Expand All @@ -24,12 +26,14 @@ import {
} from '../../../asyncremote';

import { IAppRemoteInterface } from '../../../main/app';
import { IPythonEnvironment } from 'src/main/tokens';
import { EnvironmentStatus } from './envStatus';


const desktopExtension: JupyterFrontEndPlugin<void> = {
id: 'jupyterlab-desktop.extensions.desktop',
requires: [ICommandPalette, IMainMenu],
activate: (app: ElectronJupyterLab, palette: ICommandPalette, menu: IMainMenu) => {
requires: [ICommandPalette, IMainMenu, IStatusBar],
activate: (app: ElectronJupyterLab, palette: ICommandPalette, menu: IMainMenu, statusBar: IStatusBar) => {
app.commands.addCommand('check-for-updates', {
label: 'Check for Updates…',
execute: () => {
Expand All @@ -48,6 +52,30 @@ const desktopExtension: JupyterFrontEndPlugin<void> = {
{ command: 'open-dev-tools' },
{ command: 'check-for-updates' }
], 20);

const changeEnvironment = async () => {
asyncRemoteRenderer.runRemoteMethod(IAppRemoteInterface.showPythonPathSelector, void(0));
};

const statusItem = new EnvironmentStatus({ name: 'env', description: '', onClick: changeEnvironment });

statusBar.registerStatusItem('jupyterlab-desktop-py-env-status', {
item: statusItem,
align: 'left'
});

const updateStatusItem = (env: IPythonEnvironment) => {
statusItem.model.name = env.name;
let packages = [];
for (const name in env.versions) {
packages.push(`${name}: ${env.versions[name]}`);
}
statusItem.model.description = `${env.name}\n${env.path}\n${packages.join(', ')}`;
};

asyncRemoteRenderer.runRemoteMethod(IAppRemoteInterface.getCurrentPythonEnvironment, void(0)).then((env) => {
updateStatusItem(env);
});
},
autoStart: true
};
Expand Down

0 comments on commit 9e3747c

Please sign in to comment.