Skip to content

Commit

Permalink
Add watchfiles attribute to contents manager
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbrochart committed Apr 15, 2022
1 parent 3874a05 commit d009821
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 20 deletions.
1 change: 0 additions & 1 deletion jupyter_server/services/contents/fileio.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from tornado.web import HTTPError
from traitlets import Bool
from traitlets.config import Configurable
from watchfiles import * # noqa

from jupyter_server.utils import to_api_path, to_os_path

Expand Down
5 changes: 5 additions & 0 deletions jupyter_server/services/contents/filemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@

class FileContentsManager(FileManagerMixin, ContentsManager):

import watchfiles

root_dir = Unicode(config=True)

@default("root_dir")
Expand Down Expand Up @@ -546,6 +548,9 @@ def get_kernel_path(self, path, model=None):


class AsyncFileContentsManager(FileContentsManager, AsyncFileManagerMixin, AsyncContentsManager):

import watchfiles

@default("checkpoints_class")
def _checkpoints_class_default(self):
return AsyncFileCheckpoints
Expand Down
21 changes: 21 additions & 0 deletions jupyter_server/services/contents/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@
copy_pat = re.compile(r"\-Copy\d*\.")


class NoWatchfilesAPI:
pass


NOWATCHFILESAPI = NoWatchfilesAPI()


class ContentsManager(LoggingConfigurable):
"""Base class for serving files and directories.
Expand Down Expand Up @@ -409,6 +416,20 @@ def rename_file(self, old_path, new_path):
# ContentsManager API part 2: methods that have useable default
# implementations, but can be overridden in subclasses.

@property
def watchfiles(self):
"""File system change notifyer
Override this method in subclasses if the file system supports change notifications.
Returns
-------
api : class
The supported API for file system change notifications. Loosely follows the API of the
watchfiles Python package (can be a subset of it).
"""
return NOWATCHFILESAPI

def delete(self, path):
"""Delete a file/directory and any associated checkpoints."""
path = path.strip("/")
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ install_requires =
terminado>=0.8.3
tornado>=6.1.0
traitlets>=5.1
watchfiles>=0.12
watchfiles>=0.13
websocket-client

[options.extras_require]
Expand Down
19 changes: 1 addition & 18 deletions tests/services/contents/test_fileio.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import asyncio
import os
import stat
import sys

import pytest

from jupyter_server.services.contents.fileio import Change, atomic_writing, awatch
from jupyter_server.services.contents.fileio import atomic_writing

umask = 0

Expand Down Expand Up @@ -122,19 +121,3 @@ def test_atomic_writing_newlines(tmp_path):
with open(path, newline="") as f:
read = f.read()
assert read == text


async def test_watch_directory(tmp_path):
file_path = tmp_path / "file.txt"

async def change_dir():
# let the watcher start
await asyncio.sleep(0.1)
# add file to directory
file_path.write_text("foo")

asyncio.create_task(change_dir())
stop_event = asyncio.Event()
async for change in awatch(tmp_path, stop_event=stop_event):
assert change == {(Change.added, str(file_path))}
stop_event.set()
19 changes: 19 additions & 0 deletions tests/services/contents/test_largefilemanager.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import asyncio

import pytest
import tornado

Expand Down Expand Up @@ -110,3 +112,20 @@ async def test_save_in_subdirectory(jp_large_contents_manager, tmp_path):
assert "path" in model
assert model["name"] == "Untitled.ipynb"
assert model["path"] == "foo/Untitled.ipynb"


async def test_watch_directory(tmp_path):
cm = AsyncLargeFileManager(root_dir=str(tmp_path))
file_path = tmp_path / "file.txt"

async def change_dir():
# let the watcher start
await asyncio.sleep(0.1)
# add file to directory
file_path.write_text("foo")

asyncio.create_task(change_dir())
stop_event = asyncio.Event()
async for change in cm.watchfiles.awatch(tmp_path, stop_event=stop_event):
assert change == {(cm.watchfiles.Change.added, str(file_path))}
stop_event.set()

0 comments on commit d009821

Please sign in to comment.