Skip to content

Commit

Permalink
feat: create new environment using a remote mamba solver
Browse files Browse the repository at this point in the history
The code completion in the environment editor can be activated
with CTRL-space.
  • Loading branch information
mariobuikhuizen committed Apr 27, 2021
1 parent b7d87e1 commit 2da6178
Show file tree
Hide file tree
Showing 16 changed files with 666 additions and 3 deletions.
23 changes: 23 additions & 0 deletions mamba_gator/envmanager.py
Expand Up @@ -336,6 +336,29 @@ async def create_env(self, env: str, *args) -> Dict[str, str]:
return {"error": output}
return output

async def create_explicit_env(self, name: str, explicit_list: str) -> Dict[str, str]:
"""Create a environment from an explicit spec.
Args:
name (str): Name of the environment
explicit_list (str): the explicit list of URLs
Returns:
Dict[str, str]: Create command output
"""
with tempfile.NamedTemporaryFile(mode="w") as f:
f.write(explicit_list)
f.flush()

ans = await self._execute(
self.manager, "create", "-y", "-q", "--json", "-n", name, "--file", f.name,
)

rcode, output = ans
if rcode > 0:
return {"error": output}
return output

async def delete_env(self, env: str) -> Dict[str, str]:
"""Delete an environment.
Expand Down
30 changes: 30 additions & 0 deletions mamba_gator/handlers.py
Expand Up @@ -23,6 +23,7 @@
from .log import get_logger
from jupyter_server.base.handlers import APIHandler
from jupyter_server.utils import url_path_join
from conda.base.context import context

NS = r"conda"
# Filename for the available conda packages list cache in temp folder
Expand Down Expand Up @@ -214,6 +215,26 @@ def post(self):
self.redirect_to_task(idx)


class ExplicitListHandler(EnvBaseHandler):
@tornado.web.authenticated
def post(self):
"""`POST /explicit` creates an environment from an explicit spec.
Request json body:
{
name (str): environment name
explicitList (str): the explicit list of URLs
}
"""
data = self.get_json_body()
name = data["name"]
explicit_list = data["explicitList"]

idx = self._stack.put(self.env_manager.create_explicit_env, name, explicit_list)

self.redirect_to_task(idx)


class EnvironmentHandler(EnvBaseHandler):
"""Environment handler."""

Expand Down Expand Up @@ -475,6 +496,13 @@ def delete(self, index: int):
self.finish()


class SubdirHandler(EnvBaseHandler):
@tornado.web.authenticated
async def get(self):
"""`GET /subdir` Get the conda-subdir.
"""
self.finish(tornado.escape.json_encode({'subdir': context.subdir}))

# -----------------------------------------------------------------------------
# URL to handler mappings
# -----------------------------------------------------------------------------
Expand All @@ -488,6 +516,8 @@ def delete(self, index: int):
(r"/channels", ChannelsHandler),
(r"/environments", EnvironmentsHandler), # GET / POST
(r"/environments/%s" % _env_regex, EnvironmentHandler), # GET / PATCH / DELETE
(r"/explicit", ExplicitListHandler), # POST
(r"/subdir", SubdirHandler), # GET
# PATCH / POST / DELETE
(r"/environments/%s/packages" % _env_regex, PackagesEnvironmentHandler),
(r"/packages", PackagesHandler), # GET
Expand Down
3 changes: 3 additions & 0 deletions packages/common/package.json
Expand Up @@ -48,6 +48,8 @@
"@lumino/coreutils": "^1.5.3",
"@lumino/signaling": "^1.4.3",
"@lumino/widgets": "^1.16.1",
"codemirror": "^5.60.0",
"codemirror-show-hint": "^5.58.3",
"jupyterlab_toastify": "^4.1.3",
"d3": "^5.5.0",
"react-d3-graph": "^2.5.0",
Expand All @@ -60,6 +62,7 @@
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@jupyterlab/testutils": "^3.0.0",
"@types/codemirror": "^0.0.108",
"@types/jest": "^26.0.0",
"@types/react": "^17.0.0",
"@types/react-d3-graph": "^2.3.4",
Expand Down
5 changes: 5 additions & 0 deletions packages/common/src/components/CondaEnvList.tsx
Expand Up @@ -55,6 +55,10 @@ export interface IEnvListProps {
* Environment remove handler
*/
onRemove(): void;
/**
* Environment solve handler
*/
onSolve(): void;
}

/**
Expand Down Expand Up @@ -92,6 +96,7 @@ export const CondaEnvList: React.FunctionComponent<IEnvListProps> = (
onExport={props.onExport}
onRefresh={props.onRefresh}
onRemove={props.onRemove}
onSolve={props.onSolve}
/>
<div
id={CONDA_ENVIRONMENT_PANEL_ID}
Expand Down
64 changes: 64 additions & 0 deletions packages/common/src/components/CondaEnvSolve.tsx
@@ -0,0 +1,64 @@
import * as React from 'react';
import CodeMirror from 'codemirror';
import 'codemirror/lib/codemirror.css';
import './yaml';
import * as condaHint from './CondaHint';

/**
* Conda solve properties
*/
export interface ICondaEnvSolveProps {
subdir: string;
create(name: string, explicitList: string): void;
}

export const CondaEnvSolve = (props: ICondaEnvSolveProps): JSX.Element => {
const codemirrorElem = React.useRef();

const [editor, setEditor] = React.useState(null);
const [solveState, setSolveState] = React.useState(null);

async function solve() {
const environment_yml = editor.getValue();
setSolveState('Solving...');
const name = condaHint.getName(environment_yml);
try {
const solveResult = await condaHint.fetchSolve(
props.subdir,
environment_yml
);
setSolveState(`Creating environment ${name}...`);
await props.create(name, solveResult);
setSolveState('Ok');
} catch (e) {
setSolveState(`Error: ${e}`);
}
}

React.useEffect(() => {
if (editor) {
return;
}
setEditor(
CodeMirror(codemirrorElem.current, {
lineNumbers: true,
extraKeys: {
'Ctrl-Space': 'autocomplete',
'Ctrl-Tab': 'autocomplete'
},
tabSize: 2,
mode: 'yaml',
autofocus: true
})
);
});
return (
<div style={{ width: '80vw', maxWidth: '900px' }}>
<div ref={codemirrorElem}></div>
<div style={{ paddingTop: '8px' }}>
<button onClick={solve}>Create</button>
<span style={{ marginLeft: '16px' }}>{solveState}</span>
</div>
</div>
);
};
10 changes: 10 additions & 0 deletions packages/common/src/components/CondaEnvToolBar.tsx
Expand Up @@ -4,6 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ToolbarButtonComponent } from '@jupyterlab/apputils';
import {
addIcon,
buildIcon,
Button,
closeIcon,
downloadIcon,
Expand Down Expand Up @@ -52,6 +53,10 @@ export interface ICondaEnvToolBarProps {
* Remove environment handler
*/
onRemove(): void;
/**
* Solve environment handler
*/
onSolve(): void;
}

export const CondaEnvToolBar = (props: ICondaEnvToolBarProps): JSX.Element => {
Expand Down Expand Up @@ -80,6 +85,11 @@ export const CondaEnvToolBar = (props: ICondaEnvToolBarProps): JSX.Element => {
tooltip="Create"
onClick={props.onCreate}
/>
<ToolbarButtonComponent
icon={buildIcon}
tooltip="Solve new"
onClick={props.onSolve}
/>
<Button
className="jp-ToolbarButtonComponent"
disabled={props.isBase}
Expand Down

0 comments on commit 2da6178

Please sign in to comment.