Skip to content

Commit

Permalink
integrate FileIdManager into ContentsManager implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
dlqqq committed Jul 13, 2022
1 parent 9da7b46 commit 86fcaf3
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 8 deletions.
11 changes: 9 additions & 2 deletions jupyter_server/serverapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
)
from jupyter_server.log import log_request
from jupyter_server.services.config import ConfigManager
from jupyter_server.services.contents.fileidmanager import FileIdManager
from jupyter_server.services.contents.filemanager import (
AsyncFileContentsManager,
FileContentsManager,
Expand Down Expand Up @@ -1886,9 +1887,9 @@ def init_configurables(self):
connection_dir=self.runtime_dir,
kernel_spec_manager=self.kernel_spec_manager,
)
self.file_id_manager = FileIdManager(parent=self, log=self.log)
self.contents_manager = self.contents_manager_class(
parent=self,
log=self.log,
parent=self, log=self.log, file_id_manager=self.file_id_manager
)
self.session_manager = self.session_manager_class(
parent=self,
Expand Down Expand Up @@ -2508,6 +2509,11 @@ async def cleanup_extensions(self):
self.log.info(extension_msg % n_extensions)
await run_sync_in_loop(self.extension_manager.stop_all_extensions())

def cleanup_file_id_manager(self):
if not getattr(self, "file_id_manager", None):
return
self.file_id_manager._cleanup()

def running_server_info(self, kernel_count=True):
"Return the current working directory and the server url information"
info = self.contents_manager.info_string() + "\n"
Expand Down Expand Up @@ -2780,6 +2786,7 @@ async def _cleanup(self):
self.remove_browser_open_files()
await self.cleanup_extensions()
await self.cleanup_kernels()
self.cleanup_file_id_manager()
if getattr(self, "session_manager", None):
self.session_manager.close()
if getattr(self, "event_bus", None):
Expand Down
4 changes: 4 additions & 0 deletions jupyter_server/services/contents/filemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,10 @@ def get(self, path, content=True, type=None, format=None):
if type == "directory":
raise web.HTTPError(400, "%s is not a directory" % path, reason="bad type")
model = self._file_model(path, content=content, format=format)

# append file ID to model
model["id"] = self.file_id_manager.index(path)

return model

def _save_directory(self, os_path, model, path=""):
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/services/contents/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ async def delete(self, path=""):
if await ensure_async(cm.is_hidden(path)) and not cm.allow_hidden:
raise web.HTTPError(400, f"Cannot delete file or directory {path!r}")

self.log.warning("delete %s", path)
self.log.warning("Deleting file at %s", path)
await ensure_async(cm.delete(path))
self.set_status(204)
self.finish()
Expand Down
17 changes: 17 additions & 0 deletions jupyter_server/services/contents/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

from ...files.handlers import FilesHandler
from .checkpoints import AsyncCheckpoints, Checkpoints
from .fileidmanager import FileIdManager

copy_pat = re.compile(r"\-Copy\d*\.")

Expand Down Expand Up @@ -59,6 +60,10 @@ class ContentsManager(LoggingConfigurable):

notary = Instance(sign.NotebookNotary)

file_id_manager = Instance(
FileIdManager, args=(), help="File ID manager instance to use. Defaults to `FileIdManager`."
)

def _notary_default(self):
return sign.NotebookNotary(parent=self)

Expand Down Expand Up @@ -414,12 +419,16 @@ def delete(self, path):
path = path.strip("/")
if not path:
raise HTTPError(400, "Can't delete root")
is_dir = self.dir_exists(path)
self.delete_file(path)
self.file_id_manager.delete(path, recursive=is_dir)
self.checkpoints.delete_all_checkpoints(path)

def rename(self, old_path, new_path):
"""Rename a file and any checkpoints associated with that file."""
is_dir = self.dir_exists(old_path)
self.rename_file(old_path, new_path)
self.file_id_manager.move(old_path, new_path, recursive=is_dir)
self.checkpoints.rename_all_checkpoints(old_path, new_path)

def update(self, model, path):
Expand Down Expand Up @@ -615,7 +624,9 @@ def copy(self, from_path, to_path=None):
else:
raise HTTPError(404, "No such directory: %s" % to_path)

is_dir = self.dir_exists(from_path)
model = self.save(model, to_path)
self.file_id_manager.copy(from_path, to_path, recursive=is_dir)
return model

def log_info(self):
Expand Down Expand Up @@ -817,12 +828,16 @@ async def delete(self, path):
if not path:
raise HTTPError(400, "Can't delete root")

is_dir = await ensure_async(self.dir_exists(path))
await self.delete_file(path)
self.file_id_manager.delete(path, recursive=is_dir)
await self.checkpoints.delete_all_checkpoints(path)

async def rename(self, old_path, new_path):
"""Rename a file and any checkpoints associated with that file."""
is_dir = await ensure_async(self.dir_exists(old_path))
await self.rename_file(old_path, new_path)
self.file_id_manager.move(old_path, new_path, recursive=is_dir)
await self.checkpoints.rename_all_checkpoints(old_path, new_path)

async def update(self, model, path):
Expand Down Expand Up @@ -984,7 +999,9 @@ async def copy(self, from_path, to_path=None):
else:
raise HTTPError(404, "No such directory: %s" % to_path)

is_dir = await ensure_async(self.dir_exists(from_path))
model = await self.save(model, to_path)
self.file_id_manager.copy(from_path, to_path, recursive=is_dir)
return model

async def trust_notebook(self, path):
Expand Down
21 changes: 16 additions & 5 deletions tests/services/contents/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import sys
import time
from itertools import combinations
from typing import Dict, Optional, Tuple
from typing import Any, Dict, Optional, Tuple
from unittest.mock import patch

import pytest
Expand All @@ -28,14 +28,25 @@
(AsyncFileContentsManager, False),
]
)
def jp_contents_manager(request, tmp_path):
def jp_contents_manager(request, tmp_path, fid_manager):
contents_manager, use_atomic_writing = request.param
return contents_manager(root_dir=str(tmp_path), use_atomic_writing=use_atomic_writing)
return contents_manager(
root_dir=str(tmp_path), use_atomic_writing=use_atomic_writing, file_id_manager=fid_manager
)


@pytest.fixture(params=[FileContentsManager, AsyncFileContentsManager])
def jp_file_contents_manager_class(request, tmp_path):
return request.param
def jp_file_contents_manager_class(request, tmp_path, fid_manager):
# mypy bugs out with dynamic base class
# https://github.com/python/mypy/issues/5865
Klass: Any = request.param

class WrappedKlass(Klass):
file_id_manager = fid_manager
# def __init__(self, *args, **kwargs):
# return Klass(*args, file_id_manager=fid_manager, **kwargs)

return WrappedKlass


# -------------- Functions ----------------------------
Expand Down

0 comments on commit 86fcaf3

Please sign in to comment.