Skip to content

Commit

Permalink
implement autosync_interval trait (#25)
Browse files Browse the repository at this point in the history
* implement autosync_interval trait

* remove autosync arg
  • Loading branch information
dlqqq authored Oct 25, 2022
1 parent 105415d commit 867be79
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 14 deletions.
40 changes: 26 additions & 14 deletions jupyter_server_fileid/manager.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import os
import sqlite3
import stat
import time
from typing import Optional

from jupyter_core.paths import jupyter_data_dir
from traitlets import TraitError, Unicode, validate
from traitlets import Int, TraitError, Unicode, validate
from traitlets.config.configurable import LoggingConfigurable


Expand Down Expand Up @@ -65,11 +66,23 @@ class FileIdManager(LoggingConfigurable):
config=True,
)

autosync_interval = Int(
default_value=5,
help=(
"How often to automatically invoke `sync_all()` when calling `get_path()`, in seconds. "
"Set to 0 to force `sync_all()` to always be called when calling `get_path()`. "
"Set to a negative value to disable autosync for `get_path()`."
"Defaults to 5."
),
config=True,
)

def __init__(self, *args, **kwargs):
# pass args and kwargs to parent Configurable
super().__init__(*args, **kwargs)
# initialize instance attrs
self._update_cursor = False
self._last_sync = 0.0
# initialize connection with db
self.log.info(f"FileIdManager : Configured root dir: {self.root_dir}")
self.log.info(f"FileIdManager : Configured database path: {self.db_path}")
Expand Down Expand Up @@ -140,6 +153,7 @@ def _sync_all(self):
_sync_file(). Hence the cursor needs to be redefined if
self._update_cursor is set to True by _sync_file().
"""
now = time.time()
cursor = self.con.execute("SELECT path, mtime FROM Files WHERE is_dir = 1")
self._update_cursor = False
dir = cursor.fetchone()
Expand Down Expand Up @@ -170,6 +184,8 @@ def _sync_all(self):

dir = cursor.fetchone()

self._last_sync = now

def _sync_dir(self, dir_path):
"""
Syncs the contents of a directory. If a child directory is dirty because
Expand Down Expand Up @@ -361,8 +377,7 @@ def _update(self, id, stat_info=None, path=None):
def sync_all(self):
"""
Syncs Files table with the filesystem and ensures that the correct path
is associated with each file ID. Required to be called manually
beforehand if `sync=False` is passed to `get_path()`.
is associated with each file ID.
Notes
-----
Expand Down Expand Up @@ -408,25 +423,22 @@ def get_id(self, path):
self.con.commit()
return id

def get_path(self, id, sync=True):
def get_path(self, id):
"""Retrieves the file path associated with a file ID. The file path is
relative to `self.root_dir`. Returns None if the ID does not
exist in the Files table, if the path no longer has a
file, or if the path is not a child of `self.root_dir`.
Parameters
----------
sync : bool
Whether to invoke `sync_all()` automatically prior to ID lookup.
Notes
-----
- For performance reasons, if this method must be called multiple times
in serial, then it is best to first invoke `sync_all()` and then call
this method with the argument `sync=False`.
- To force syncing when calling `get_path()`, call `sync_all()` manually
prior to calling `get_path()`.
"""
if sync:
self.sync_all()
if (
self.autosync_interval >= 0
and (time.time() - self._last_sync) >= self.autosync_interval
):
self._sync_all()

row = self.con.execute("SELECT path, ino FROM Files WHERE id = ?", (id,)).fetchone()

Expand Down
38 changes: 38 additions & 0 deletions tests/test_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from unittest.mock import patch

import pytest
from traitlets import TraitError
Expand Down Expand Up @@ -275,6 +276,7 @@ def test_get_path_oob_move_back_to_original_path(fid_manager, old_path, new_path

assert fid_manager.get_path(id) == new_path
fs_helpers.move(new_path, old_path)
fid_manager.sync_all()
assert fid_manager.get_path(id) == old_path


Expand Down Expand Up @@ -430,3 +432,39 @@ def test_save(fid_manager, test_path, fs_helpers):
fid_manager.save(test_path)

assert fid_manager.get_id(test_path) == id


def test_autosync_gt_0(fid_manager, old_path, new_path, fs_helpers):
fid_manager.autosync_interval = 10
id = fid_manager.index(old_path)
fid_manager.sync_all()
fs_helpers.move(old_path, new_path)

assert fid_manager.get_path(id) != new_path
with patch("time.time") as mock_time:
mock_time.return_value = fid_manager._last_sync + 999
assert fid_manager.get_path(id) == new_path


def test_autosync_eq_0(fid_manager, old_path, new_path, fs_helpers):
fid_manager.autosync_interval = 0
id = fid_manager.index(old_path)
fid_manager.sync_all()
fs_helpers.move(old_path, new_path)

assert fid_manager.get_path(id) == new_path


def test_autosync_lt_0(fid_manager, old_path, new_path, fs_helpers):
fid_manager.autosync_interval = -10
id = fid_manager.index(old_path)
fid_manager.sync_all()
fs_helpers.move(old_path, new_path)

assert fid_manager.get_path(id) != new_path
with patch("time.time") as mock_time:
mock_time.return_value = fid_manager._last_sync + 999
assert fid_manager.get_path(id) != new_path

fid_manager.sync_all()
assert fid_manager.get_path(id) == new_path

0 comments on commit 867be79

Please sign in to comment.