Skip to content

Commit 86fcaf3

Browse files
committed
integrate FileIdManager into ContentsManager implementations
1 parent 9da7b46 commit 86fcaf3

File tree

5 files changed

+47
-8
lines changed

5 files changed

+47
-8
lines changed

jupyter_server/serverapp.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
)
112112
from jupyter_server.log import log_request
113113
from jupyter_server.services.config import ConfigManager
114+
from jupyter_server.services.contents.fileidmanager import FileIdManager
114115
from jupyter_server.services.contents.filemanager import (
115116
AsyncFileContentsManager,
116117
FileContentsManager,
@@ -1886,9 +1887,9 @@ def init_configurables(self):
18861887
connection_dir=self.runtime_dir,
18871888
kernel_spec_manager=self.kernel_spec_manager,
18881889
)
1890+
self.file_id_manager = FileIdManager(parent=self, log=self.log)
18891891
self.contents_manager = self.contents_manager_class(
1890-
parent=self,
1891-
log=self.log,
1892+
parent=self, log=self.log, file_id_manager=self.file_id_manager
18921893
)
18931894
self.session_manager = self.session_manager_class(
18941895
parent=self,
@@ -2508,6 +2509,11 @@ async def cleanup_extensions(self):
25082509
self.log.info(extension_msg % n_extensions)
25092510
await run_sync_in_loop(self.extension_manager.stop_all_extensions())
25102511

2512+
def cleanup_file_id_manager(self):
2513+
if not getattr(self, "file_id_manager", None):
2514+
return
2515+
self.file_id_manager._cleanup()
2516+
25112517
def running_server_info(self, kernel_count=True):
25122518
"Return the current working directory and the server url information"
25132519
info = self.contents_manager.info_string() + "\n"
@@ -2780,6 +2786,7 @@ async def _cleanup(self):
27802786
self.remove_browser_open_files()
27812787
await self.cleanup_extensions()
27822788
await self.cleanup_kernels()
2789+
self.cleanup_file_id_manager()
27832790
if getattr(self, "session_manager", None):
27842791
self.session_manager.close()
27852792
if getattr(self, "event_bus", None):

jupyter_server/services/contents/filemanager.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,10 @@ def get(self, path, content=True, type=None, format=None):
395395
if type == "directory":
396396
raise web.HTTPError(400, "%s is not a directory" % path, reason="bad type")
397397
model = self._file_model(path, content=content, format=format)
398+
399+
# append file ID to model
400+
model["id"] = self.file_id_manager.index(path)
401+
398402
return model
399403

400404
def _save_directory(self, os_path, model, path=""):

jupyter_server/services/contents/handlers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ async def delete(self, path=""):
271271
if await ensure_async(cm.is_hidden(path)) and not cm.allow_hidden:
272272
raise web.HTTPError(400, f"Cannot delete file or directory {path!r}")
273273

274-
self.log.warning("delete %s", path)
274+
self.log.warning("Deleting file at %s", path)
275275
await ensure_async(cm.delete(path))
276276
self.set_status(204)
277277
self.finish()

jupyter_server/services/contents/manager.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
from ...files.handlers import FilesHandler
3232
from .checkpoints import AsyncCheckpoints, Checkpoints
33+
from .fileidmanager import FileIdManager
3334

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

@@ -59,6 +60,10 @@ class ContentsManager(LoggingConfigurable):
5960

6061
notary = Instance(sign.NotebookNotary)
6162

63+
file_id_manager = Instance(
64+
FileIdManager, args=(), help="File ID manager instance to use. Defaults to `FileIdManager`."
65+
)
66+
6267
def _notary_default(self):
6368
return sign.NotebookNotary(parent=self)
6469

@@ -414,12 +419,16 @@ def delete(self, path):
414419
path = path.strip("/")
415420
if not path:
416421
raise HTTPError(400, "Can't delete root")
422+
is_dir = self.dir_exists(path)
417423
self.delete_file(path)
424+
self.file_id_manager.delete(path, recursive=is_dir)
418425
self.checkpoints.delete_all_checkpoints(path)
419426

420427
def rename(self, old_path, new_path):
421428
"""Rename a file and any checkpoints associated with that file."""
429+
is_dir = self.dir_exists(old_path)
422430
self.rename_file(old_path, new_path)
431+
self.file_id_manager.move(old_path, new_path, recursive=is_dir)
423432
self.checkpoints.rename_all_checkpoints(old_path, new_path)
424433

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

627+
is_dir = self.dir_exists(from_path)
618628
model = self.save(model, to_path)
629+
self.file_id_manager.copy(from_path, to_path, recursive=is_dir)
619630
return model
620631

621632
def log_info(self):
@@ -817,12 +828,16 @@ async def delete(self, path):
817828
if not path:
818829
raise HTTPError(400, "Can't delete root")
819830

831+
is_dir = await ensure_async(self.dir_exists(path))
820832
await self.delete_file(path)
833+
self.file_id_manager.delete(path, recursive=is_dir)
821834
await self.checkpoints.delete_all_checkpoints(path)
822835

823836
async def rename(self, old_path, new_path):
824837
"""Rename a file and any checkpoints associated with that file."""
838+
is_dir = await ensure_async(self.dir_exists(old_path))
825839
await self.rename_file(old_path, new_path)
840+
self.file_id_manager.move(old_path, new_path, recursive=is_dir)
826841
await self.checkpoints.rename_all_checkpoints(old_path, new_path)
827842

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

1002+
is_dir = await ensure_async(self.dir_exists(from_path))
9871003
model = await self.save(model, to_path)
1004+
self.file_id_manager.copy(from_path, to_path, recursive=is_dir)
9881005
return model
9891006

9901007
async def trust_notebook(self, path):

tests/services/contents/test_manager.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import sys
33
import time
44
from itertools import combinations
5-
from typing import Dict, Optional, Tuple
5+
from typing import Any, Dict, Optional, Tuple
66
from unittest.mock import patch
77

88
import pytest
@@ -28,14 +28,25 @@
2828
(AsyncFileContentsManager, False),
2929
]
3030
)
31-
def jp_contents_manager(request, tmp_path):
31+
def jp_contents_manager(request, tmp_path, fid_manager):
3232
contents_manager, use_atomic_writing = request.param
33-
return contents_manager(root_dir=str(tmp_path), use_atomic_writing=use_atomic_writing)
33+
return contents_manager(
34+
root_dir=str(tmp_path), use_atomic_writing=use_atomic_writing, file_id_manager=fid_manager
35+
)
3436

3537

3638
@pytest.fixture(params=[FileContentsManager, AsyncFileContentsManager])
37-
def jp_file_contents_manager_class(request, tmp_path):
38-
return request.param
39+
def jp_file_contents_manager_class(request, tmp_path, fid_manager):
40+
# mypy bugs out with dynamic base class
41+
# https://github.com/python/mypy/issues/5865
42+
Klass: Any = request.param
43+
44+
class WrappedKlass(Klass):
45+
file_id_manager = fid_manager
46+
# def __init__(self, *args, **kwargs):
47+
# return Klass(*args, file_id_manager=fid_manager, **kwargs)
48+
49+
return WrappedKlass
3950

4051

4152
# -------------- Functions ----------------------------

0 commit comments

Comments
 (0)