Skip to content

Add option export from history #39

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

Merged
merged 6 commits into from
May 20, 2020
Merged
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[![npm](https://img.shields.io/npm/v/jupyterlab_conda.svg?style=flat-square)](https://www.npmjs.com/package/jupyterlab_conda)
[![Build Status](https://travis-ci.com/fcollonval/jupyter_conda.svg?branch=master)](https://travis-ci.com/fcollonval/jupyter_conda)
[![Coverage Status](https://coveralls.io/repos/github/fcollonval/jupyter_conda/badge.svg?branch=master)](https://coveralls.io/github/fcollonval/jupyter_conda?branch=master)
[![Swagger Validator](https://img.shields.io/swagger/valid/3.0?specUrl=https%3A%2F%2Fraw.githubusercontent.com%2Ffcollonval%2Fjupyter_conda%2Fmaster%2Fjupyter_conda%2Frest_api.yml)](https://petstore.swagger.io/?url=https://raw.githubusercontent.com/fcollonval/jupyter_conda/master/jupyter_conda/rest_api.yml)

Provides Conda environment and package access extension from within Jupyter Notebook and JupyterLab.

Expand Down
43 changes: 37 additions & 6 deletions jupyter_conda/envmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ def normalize_pkg_info(s: Dict[str, Any]) -> Dict[str, Union[str, List[str]]]:
class EnvManager(LoggingConfigurable):
"""Handles environment and package actions."""

_conda_version: Optional[str] = None

def _clean_conda_json(self, output: str) -> Dict[str, Any]:
"""Clean a command output to fit json format.

Expand Down Expand Up @@ -232,16 +234,29 @@ async def delete_env(self, env: str) -> Dict[str, str]:
return {"error": output}
return output

async def export_env(self, env: str) -> Union[str, Dict[str, str]]:
async def export_env(
self, env: str, from_history: bool = False
) -> Union[str, Dict[str, str]]:
"""Export an environment as YAML file.

Args:
env (str): Environment name
from_history (bool): If True, use `--from-history` option; default False

Returns:
str: YAML file content
"""
ans = await self._execute(CONDA_EXE, "env", "export", "-n", env)
command = [CONDA_EXE, "env", "export", "-n", env]
if from_history:
if EnvManager._conda_version is None:
await self.info() # Set conda version
if EnvManager._conda_version < (4, 7, 12):
self.log.warning(
"[jupyter_conda] conda<4.7.12 does not support `env export --from-history`. It will be ignored."
)
else:
command.append("--from-history")
ans = await self._execute(*command)
rcode, output = ans
if rcode > 0:
return {"error": output}
Expand Down Expand Up @@ -275,6 +290,24 @@ async def import_env(
return {"error": output}
return output

async def info(self) -> Dict[str, Any]:
"""Returns `conda info --json` execution.

Returns:
The dictionary of conda information
"""
ans = await self._execute(CONDA_EXE, "info", "--json")
rcode, output = ans
info = self._clean_conda_json(output)
if rcode == 0:
EnvManager._conda_version = tuple(
map(
lambda part: int(part),
info.get("conda_version", EnvManager._conda_version).split("."),
)
)
return info

async def list_envs(
self, whitelist: bool = False
) -> Dict[str, List[Dict[str, Union[str, bool]]]]:
Expand All @@ -294,10 +327,8 @@ async def list_envs(
Returns:
{"environments": List[env]}: The environments
"""
ans = await self._execute(CONDA_EXE, "info", "--json")
rcode, output = ans
info = self._clean_conda_json(output)
if rcode > 0:
info = await self.info()
if "error" in info:
return info

default_env = info["default_prefix"]
Expand Down
6 changes: 4 additions & 2 deletions jupyter_conda/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,16 +233,18 @@ async def get(self, env: str):
Query arguments:
status: "installed" (default) or "has_update"
download: 0 (default) or 1
history: 0 (default) or 1
"""
status = self.get_query_argument("status", "installed")
download = self.get_query_argument("download", 0)
history = self.get_query_argument("history", 0)

if download:
# export requirements file
self.set_header(
"Content-Disposition", 'attachment; filename="%s"' % (env + ".yml")
)
answer = await self.env_manager.export_env(env)
answer = await self.env_manager.export_env(env, bool(history))
if "error" in answer:
self.set_status(500)
self.finish(tornado.escape.json_encode(answer))
Expand Down Expand Up @@ -485,7 +487,7 @@ def delete(self, index: int):
# PATCH / POST / DELETE
(r"/environments/%s/packages" % _env_regex, PackagesEnvironmentHandler),
(r"/packages", PackagesHandler), # GET
(r"/tasks/%s" % r"(?P<index>\d+)", TaskHandler), # GET
(r"/tasks/%s" % r"(?P<index>\d+)", TaskHandler), # GET / DELETE
]


Expand Down
258 changes: 258 additions & 0 deletions jupyter_conda/rest_api.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
swagger: "2.0"
info:
description: "This is the REST API introduce by the Jupyter server extension `jupyter_conda`; see [GitHub repository](https://github.com/fcollonval/jupyter_conda) for more information."
version: "3.3.0"
title: "jupyter_conda API"
license:
name: "BSD-3-Clause"
url: "https://opensource.org/licenses/BSD-3-Clause"
basePath: "/conda"
tags:
- name: "channel"
description: "Conda channel"
- name: "environment"
description: "Conda environment actions"
- name: "package"
description: "Conda package actions"
- name: "task"
description: "Long running task actions"
schemes:
- "https"
paths:
/channels:
get:
tags:
- "channel"
summary: "List conda channels"
responses:
"200":
description: "Conda channels"
"500":
description: "Fail to list conda channels"
/environments:
get:
tags:
- "environment"
summary: "List conda environments"
produces:
- "application/json"
parameters:
- name: "whitelist"
in: "query"
description: "Whether to respect KernelSpecManager.whitelist"
type: "integer"
default: 0
responses:
"200":
description: "Conda environments"
"500":
description: "Fail to list environments"
post:
tags:
- "environment"
summary: "Add a new conda environment"
consumes:
- "application/json"
parameters:
- in: "body"
name: "body"
description: "Environment option"
required: true
schema:
$ref: "#/definitions/EnvironmentPost"
responses:
"202":
description: "Redirect on tasks"
/environments/{environmentName}:
get:
tags:
- "environment"
summary: "List the environment content"
produces:
- "application/json"
- "attachment"
parameters:
- name: "environmentName"
in: "path"
description: "Environment name to return"
required: true
type: "string"
pattern: /([^/&+$?@<>%*-][^/&+$?@<>%*]*)/
- name: "status"
in: "query"
description: "installed or has_update"
type: "string"
default: "installed"
enum: ["installed", "has_update"]
- name: "download"
in: "query"
description: "Whether to download the packages list"
type: "integer"
default: 0
- name: "history"
in: "query"
description: "Whether to export only from history"
type: "integer"
default: 0
responses:
"200":
description: "Package list"
schema:
type: "object"
properties:
packages:
type: "array"
items:
$ref: "#/definitions/Package"

"202":
description: "Redirect long running task"
"500":
description: "Error listing the packages"
patch:
tags:
- "environment"
summary: "Updates the packages environment"
consumes:
- "application/json"
parameters:
- name: "environmentName"
in: "path"
description: "Environment name to update"
required: true
type: "string"
pattern: /([^/&+$?@<>%*-][^/&+$?@<>%*]*)/
responses:
"202":
description: "Long running task"
delete:
tags:
- "environment"
summary: "Deletes an environment"
description: ""
parameters:
- name: "environmentName"
in: "path"
description: "Environment name to remove"
required: true
type: "string"
pattern: /([^/&+$?@<>%*-][^/&+$?@<>%*]*)/
responses:
"202":
description: "Redirect long running task"
/environments/{environmentName}/packages:
patch:
tags:
- "package"
summary: "Update environment packages"
consumes:
- "application/json"
parameters:
- name: "environmentName"
in: "path"
description: "Environment name to modify"
required: true
type: "string"
pattern: /([^/&+$?@<>%*-][^/&+$?@<>%*]*)/
responses:
"202":
description: "Redirect long running task"
post:
tags:
- "package"
summary: "Install environment packages"
consumes:
- "application/json"
parameters:
- name: "environmentName"
in: "path"
description: "Environment name to modify"
required: true
type: "string"
pattern: /([^/&+$?@<>%*-][^/&+$?@<>%*]*)/
- name: "develop"
in: "query"
description: "Whether to install the package in development mode"
type: "integer"
default: 0
responses:
"202":
description: "Redirect long running task"
delete:
tags:
- "package"
consumes:
- "application/json"
parameters:
- name: "environmentName"
in: "path"
description: "Environment name to modify"
required: true
type: "string"
pattern: /([^/&+$?@<>%*-][^/&+$?@<>%*]*)/
responses:
"202":
description: "Redirect long running task"
/packages:
get:
tags:
- "package"
summary: "Search for packages"
produces:
- "application/json"
parameters:
- name: "query"
in: "query"
description: "Query string to pass to conda search"
type: "string"
default: ""
responses:
"200":
description: "Query result"
"202":
description: "Redirect long running task"
/tasks/{taskId}:
get:
tags:
- "task"
summary: "Get long running task result"
produces:
- "application/json"
parameters:
- name: "taskId"
in: "path"
description: "Task ID"
required: true
type: "integer"
responses:
"200":
description: "Successful execution of the task - returns its result"
"202":
description: "Task still running"
"404":
description: "Task not found"
"500":
description: "An error occurred when executing the task"
delete:
tags:
- "task"
summary: "Stop the long running task"
parameters:
- name: "taskId"
in: "path"
description: "ID of the order that needs to be deleted"
required: true
type: "integer"
responses:
"204":
description: "Task cancelled"
"404":
description: "Task not found"
definitions:
EnvironmentPost:
type: "object"
Package:
type: "object"
externalDocs:
description: "Find out more about jupyter_conda"
url: "https://github.com/fcollonval/jupyter_conda"
Loading