Skip to content

Commit c82834c

Browse files
authored
Merge pull request #9 from DenisaCG/contentManagerS3
Add S3ContentsManager
2 parents 9d40101 + a6f2006 commit c82834c

File tree

9 files changed

+702
-86
lines changed

9 files changed

+702
-86
lines changed

jupyter_drives/handlers.py

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import json
55
import logging
66
import traceback
7-
from typing import Optional
7+
from typing import Optional, Tuple, Union
88

9-
from jupyter_server.base.handlers import APIHandler
9+
from jupyter_server.base.handlers import APIHandler, path_regex
1010
from jupyter_server.utils import url_path_join
1111
import tornado
1212
import traitlets
@@ -42,27 +42,56 @@ def write_error(self, status_code, **kwargs):
4242
reply["error"] = "".join(traceback.format_exception(*exc_info))
4343
self.finish(json.dumps(reply))
4444

45-
class ListJupyterDrives(JupyterDrivesAPIHandler):
45+
class ListJupyterDrivesHandler(JupyterDrivesAPIHandler):
4646
"""
47-
Returns list of available drives.
47+
List available drives. Mounts drives.
4848
"""
4949
def initialize(self, logger: logging.Logger, manager: JupyterDrivesManager):
5050
return super().initialize(logger, manager)
5151

5252
# Later on, filters can be added for the listing
5353
@tornado.web.authenticated
5454
async def get(self):
55-
drives, error = await self._manager.list_drives()
56-
self.finish(json.dumps(drives))
57-
self._jp_log.debug(error)
55+
result = await self._manager.list_drives()
56+
self.finish(json.dumps(result))
57+
58+
@tornado.web.authenticated
59+
async def post(self):
60+
body = self.get_json_body()
61+
result = await self._manager.mount_drive(**body)
62+
self.finish(json.dump(result.message))
63+
64+
class ContentsJupyterDrivesHandler(JupyterDrivesAPIHandler):
65+
"""
66+
Deals with contents of a drive.
67+
"""
68+
@tornado.web.authenticated
69+
async def get(self, path: str = "", drive: str = ""):
70+
result = await self._manager.get_contents(drive, path)
71+
self.finish(json.dump(result))
72+
73+
@tornado.web.authenticated
74+
async def post(self, path: str = "", drive: str = ""):
75+
result = await self._manager.new_file(drive, path)
76+
self.finish(json.dump(result))
77+
78+
@tornado.web.authenticated
79+
async def patch(self, path: str = "", drive: str = ""):
80+
body = self.get_json_body()
81+
result = await self._manager.rename_file(drive, path, **body)
82+
self.finish(json.dump(result))
83+
84+
handlers = [
85+
("drives", ListJupyterDrivesHandler)
86+
]
5887

59-
default_handlers = [
60-
("drives", ListJupyterDrives)
88+
handlers_with_path = [
89+
("drives", ContentsJupyterDrivesHandler)
6190
]
6291

6392
def setup_handlers(web_app: tornado.web.Application, config: traitlets.config.Config, log: Optional[logging.Logger] = None):
6493
host_pattern = ".*$"
65-
base_url = url_path_join(web_app.settings["base_url"], NAMESPACE)
94+
base_url = web_app.settings["base_url"]
6695

6796
log = log or logging.getLogger(__name__)
6897

@@ -80,15 +109,26 @@ def setup_handlers(web_app: tornado.web.Application, config: traitlets.config.Co
80109
logging.error("JupyterDrives Manager Exception", exc_info=1)
81110
raise err
82111

83-
handlers = [
84-
(
85-
url_path_join(base_url, pattern),
86-
handler,
87-
{"logger": log, "manager": manager}
88-
)
89-
for pattern, handler in default_handlers
90-
]
112+
drives_handlers = (
113+
[
114+
(
115+
url_path_join(base_url, NAMESPACE, pattern),
116+
handler,
117+
{"logger": log, "manager": manager}
118+
)
119+
for pattern, handler in handlers
120+
]
121+
+ [
122+
(
123+
url_path_join(
124+
base_url, NAMESPACE, pattern, r"(?P<drive>\w+)", path_regex
125+
),
126+
handler,
127+
)
128+
for pattern, handler in handlers_with_path
129+
]
130+
)
91131

92-
log.debug(f"Jupyter-Drives Handlers: {handlers}")
132+
log.debug(f"Jupyter-Drives Handlers: {drives_handlers}")
93133

94-
web_app.add_handlers(host_pattern, handlers)
134+
web_app.add_handlers(host_pattern, drives_handlers)

jupyter_drives/managers/manager.py

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import http
33
import json
44
import logging
5-
from typing import Dict, List, Optional, Tuple, Union
5+
from typing import Dict, List, Optional, Tuple, Union, Any
66

77
import nbformat
88
import tornado
@@ -53,31 +53,64 @@ def per_page_argument(self) -> Optional[Tuple[str, int]]:
5353
return None
5454

5555
@abc.abstractclassmethod
56-
async def list_drives(self) -> list:
56+
async def list_drives(self):
5757
"""Get list of available drives.
5858
5959
Returns:
6060
List of available drives and their properties.
6161
"""
6262
raise NotImplementedError()
6363

64-
# @abc.abstractclassmethod
65-
# async def mount_drive(self) -> str:
66-
# """Mount a drive.
67-
68-
# Returns:
69-
# The URL for the new drive content.
70-
# """
71-
# raise NotImplementedError()
64+
@abc.abstractclassmethod
65+
async def mount_drive(self, drive_name, **kwargs):
66+
"""Mount a drive.
67+
68+
Args:
69+
drive_name: name of drive to mount
70+
71+
Returns:
72+
The content manager for the drive.
73+
"""
74+
raise NotImplementedError()
7275

73-
# @abc.abstractclassmethod
74-
# async def unmount_drive(self, drive_name: str):
75-
# """Unmount a drive.
76-
77-
# Args:
78-
# drive_name: name of drive to unmount
79-
# """
80-
# raise NotImplementedError()
76+
@abc.abstractclassmethod
77+
async def unmount_drive(self, drive_name: str, **kwargs):
78+
"""Unmount a drive.
79+
80+
Args:
81+
drive_name: name of drive to unmount
82+
"""
83+
raise NotImplementedError()
84+
85+
@abc.abstractclassmethod
86+
async def get_contents(self, drive_name, path, **kwargs):
87+
"""Get contents of a file or directory.
88+
89+
Args:
90+
drive_name: name of drive to get the contents of
91+
path: path to file or directory
92+
"""
93+
raise NotImplementedError()
94+
95+
@abc.abstractclassmethod
96+
async def new_file(self, drive_name, path, **kwargs):
97+
"""Create a new file or directory at the given path.
98+
99+
Args:
100+
drive_name: name of drive where the new content is created
101+
path: path where new content should be created
102+
"""
103+
raise NotImplementedError()
104+
105+
@abc.abstractclassmethod
106+
async def rename_file(self, drive_name, path, **kwargs):
107+
"""Rename a file.
108+
109+
Args:
110+
drive_name: name of drive where file is located
111+
path: path of file
112+
"""
113+
raise NotImplementedError()
81114

82115
async def _call_provider(
83116
self,
@@ -110,19 +143,19 @@ async def _call_provider(
110143
"""
111144
if not self._config.session_token:
112145
raise tornado.web.HTTPError(
113-
status_code=http.HTTPStatus.BAD_REQUEST,
146+
status_code= httpx.codes.BAD_REQUEST,
114147
reason="No session token specified. Please set DriversConfig.session_token in your user jupyter_server_config file.",
115148
)
116149

117150
if not self._config.access_key_id:
118151
raise tornado.web.HTTPError(
119-
status_code=http.HTTPStatus.BAD_REQUEST,
152+
status_code= httpx.codes.BAD_REQUEST,
120153
reason="No access key id specified. Please set DriversConfig.access_key_id in your user jupyter_server_config file.",
121154
)
122155

123156
if not self._config.secret_access_key:
124157
raise tornado.web.HTTPError(
125-
status_code=http.HTTPStatus.BAD_REQUEST,
158+
status_code= httpx.codes.BAD_REQUEST,
126159
reason="No secret access key specified. Please set DriversConfig.secret_access_key in your user jupyter_server_config file.",
127160
)
128161

0 commit comments

Comments
 (0)