Skip to content

Commit 36d44b7

Browse files
committed
Update the unit test's comments with pointers to the relevant issues
1 parent 5ac6354 commit 36d44b7

File tree

2 files changed

+130
-139
lines changed

2 files changed

+130
-139
lines changed

jupyter_server_fileid/manager.py

+114-134
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,7 @@ def _move_recursive(self, old_path: str, new_path: str, path_mgr: Any = os.path)
121121
for record in records:
122122
id, old_recpath = record
123123
new_recpath = path_mgr.join(new_path, path_mgr.relpath(old_recpath, start=old_path))
124-
try:
125-
self.con.execute("UPDATE Files SET path = ? WHERE id = ?", (new_recpath, id))
126-
except Exception as err:
127-
self.log.error(err)
124+
self.con.execute("UPDATE Files SET path = ? WHERE id = ?", (new_recpath, id))
128125

129126
def _copy_recursive(self, from_path: str, to_path: str, path_mgr: Any = os.path) -> None:
130127
"""Copy all children of a given directory at `from_path` to a new
@@ -137,12 +134,9 @@ def _copy_recursive(self, from_path: str, to_path: str, path_mgr: Any = os.path)
137134
for record in records:
138135
(from_recpath,) = record
139136
to_recpath = path_mgr.join(to_path, path_mgr.relpath(from_recpath, start=from_path))
140-
try:
141-
self.con.execute(
142-
"INSERT INTO Files (id, path) VALUES (?, ?)", (self._uuid(), to_recpath)
143-
)
144-
except Exception as err:
145-
self.log.error(err)
137+
self.con.execute(
138+
"INSERT INTO Files (id, path) VALUES (?, ?)", (self._uuid(), to_recpath)
139+
)
146140

147141
def _delete_recursive(self, path: str, path_mgr: Any = os.path) -> None:
148142
"""Delete all children of a given directory, delimited by `sep`."""
@@ -319,24 +313,21 @@ def _from_normalized_path(self, path: Optional[str]) -> Optional[str]:
319313
def _create(self, path: str) -> str:
320314
path = self._normalize_path(path)
321315
id = self._uuid()
322-
try:
323-
self.con.execute("INSERT INTO Files (id, path) VALUES (?, ?)", (id, path))
324-
except Exception as err:
325-
self.log.error(err)
316+
self.con.execute("INSERT INTO Files (id, path) VALUES (?, ?)", (id, path))
326317
return id
327318

328319
def index(self, path: str) -> str:
329-
path = self._normalize_path(path)
330-
row = self.con.execute("SELECT id FROM Files WHERE path = ?", (path,)).fetchone()
331-
existing_id = row and row[0]
320+
with self.con:
321+
path = self._normalize_path(path)
322+
row = self.con.execute("SELECT id FROM Files WHERE path = ?", (path,)).fetchone()
323+
existing_id = row and row[0]
332324

333-
if existing_id:
334-
return existing_id
325+
if existing_id:
326+
return existing_id
335327

336-
# create new record
337-
id = self._create(path)
338-
self.con.commit()
339-
return id
328+
# create new record
329+
id = self._create(path)
330+
return id
340331

341332
def get_id(self, path: str) -> Optional[str]:
342333
path = self._normalize_path(path)
@@ -349,40 +340,36 @@ def get_path(self, id: str) -> Optional[str]:
349340
return self._from_normalized_path(path)
350341

351342
def move(self, old_path: str, new_path: str) -> None:
352-
old_path = self._normalize_path(old_path)
353-
new_path = self._normalize_path(new_path)
354-
row = self.con.execute("SELECT id FROM Files WHERE path = ?", (old_path,)).fetchone()
355-
id = row and row[0]
343+
with self.con:
344+
old_path = self._normalize_path(old_path)
345+
new_path = self._normalize_path(new_path)
346+
row = self.con.execute("SELECT id FROM Files WHERE path = ?", (old_path,)).fetchone()
347+
id = row and row[0]
356348

357-
if id:
358-
try:
349+
if id:
359350
self.con.execute("UPDATE Files SET path = ? WHERE path = ?", (new_path, old_path))
360-
except Exception as err:
361-
self.log.error(err)
362-
self._move_recursive(old_path, new_path, posixpath)
363-
else:
364-
id = self._create(new_path)
351+
self._move_recursive(old_path, new_path, posixpath)
352+
else:
353+
id = self._create(new_path)
365354

366-
self.con.commit()
367-
return id
355+
return id
368356

369357
def copy(self, from_path: str, to_path: str) -> Optional[str]:
370-
from_path = self._normalize_path(from_path)
371-
to_path = self._normalize_path(to_path)
358+
with self.con:
359+
from_path = self._normalize_path(from_path)
360+
to_path = self._normalize_path(to_path)
372361

373-
id = self._create(to_path)
374-
self._copy_recursive(from_path, to_path, posixpath)
362+
id = self._create(to_path)
363+
self._copy_recursive(from_path, to_path, posixpath)
375364

376-
self.con.commit()
377-
return id
365+
return id
378366

379367
def delete(self, path: str) -> None:
380-
path = self._normalize_path(path)
368+
with self.con:
369+
path = self._normalize_path(path)
381370

382-
self.con.execute("DELETE FROM Files WHERE path = ?", (path,))
383-
self._delete_recursive(path, posixpath)
384-
385-
self.con.commit()
371+
self.con.execute("DELETE FROM Files WHERE path = ?", (path,))
372+
self._delete_recursive(path, posixpath)
386373

387374
def save(self, path: str) -> None:
388375
return
@@ -685,13 +672,10 @@ def _create(self, path, stat_info):
685672
have a unique `ino`.
686673
"""
687674
id = self._uuid()
688-
try:
689-
self.con.execute(
690-
"INSERT INTO Files (id, path, ino, crtime, mtime, is_dir) VALUES (?, ?, ?, ?, ?, ?)",
691-
(id, path, stat_info.ino, stat_info.crtime, stat_info.mtime, stat_info.is_dir),
692-
)
693-
except Exception as err:
694-
self.log.error(err)
675+
self.con.execute(
676+
"INSERT INTO Files (id, path, ino, crtime, mtime, is_dir) VALUES (?, ?, ?, ?, ?, ?)",
677+
(id, path, stat_info.ino, stat_info.crtime, stat_info.mtime, stat_info.is_dir),
678+
)
695679
return id
696680

697681
def _update(self, id, stat_info=None, path=None):
@@ -709,66 +693,62 @@ def _update(self, id, stat_info=None, path=None):
709693
dangerous and may throw a runtime error if the file is not guaranteed to
710694
have a unique `ino`.
711695
"""
712-
try:
713-
if stat_info and path:
714-
self.con.execute(
715-
"UPDATE Files SET ino = ?, crtime = ?, mtime = ?, path = ? WHERE id = ?",
716-
(stat_info.ino, stat_info.crtime, stat_info.mtime, path, id),
717-
)
718-
return
696+
if stat_info and path:
697+
self.con.execute(
698+
"UPDATE Files SET ino = ?, crtime = ?, mtime = ?, path = ? WHERE id = ?",
699+
(stat_info.ino, stat_info.crtime, stat_info.mtime, path, id),
700+
)
701+
return
719702

720-
if stat_info:
721-
self.con.execute(
722-
"UPDATE Files SET ino = ?, crtime = ?, mtime = ? WHERE id = ?",
723-
(stat_info.ino, stat_info.crtime, stat_info.mtime, id),
724-
)
725-
return
703+
if stat_info:
704+
self.con.execute(
705+
"UPDATE Files SET ino = ?, crtime = ?, mtime = ? WHERE id = ?",
706+
(stat_info.ino, stat_info.crtime, stat_info.mtime, id),
707+
)
708+
return
726709

727-
if path:
728-
self.con.execute(
729-
"UPDATE Files SET path = ? WHERE id = ?",
730-
(path, id),
731-
)
732-
return
733-
except Exception as err:
734-
self.log.error(err)
710+
if path:
711+
self.con.execute(
712+
"UPDATE Files SET path = ? WHERE id = ?",
713+
(path, id),
714+
)
715+
return
735716

736717
def index(self, path, stat_info=None, commit=True):
737718
"""Returns the file ID for the file at `path`, creating a new file ID if
738719
one does not exist. Returns None only if file does not exist at path."""
739-
path = self._normalize_path(path)
740-
stat_info = stat_info or self._stat(path)
741-
if not stat_info:
742-
return None
720+
with self.con:
721+
path = self._normalize_path(path)
722+
stat_info = stat_info or self._stat(path)
723+
if not stat_info:
724+
return None
743725

744-
# if file is symlink, then index the path it refers to instead
745-
if stat_info.is_symlink:
746-
return self.index(os.path.realpath(path))
726+
# if file is symlink, then index the path it refers to instead
727+
if stat_info.is_symlink:
728+
return self.index(os.path.realpath(path))
747729

748-
# sync file at path and return file ID if it exists
749-
id = self._sync_file(path, stat_info)
750-
if id is not None:
751-
return id
730+
# sync file at path and return file ID if it exists
731+
id = self._sync_file(path, stat_info)
732+
if id is not None:
733+
return id
752734

753-
# otherwise, create a new record and return the file ID
754-
id = self._create(path, stat_info)
755-
if commit:
756-
self.con.commit()
757-
return id
735+
# otherwise, create a new record and return the file ID
736+
id = self._create(path, stat_info)
737+
return id
758738

759739
def get_id(self, path):
760740
"""Retrieves the file ID associated with a file path. Returns None if
761741
the file has not yet been indexed or does not exist at the given
762742
path."""
763-
path = self._normalize_path(path)
764-
stat_info = self._stat(path)
765-
if not stat_info:
766-
return None
743+
with self.con:
744+
path = self._normalize_path(path)
745+
stat_info = self._stat(path)
746+
if not stat_info:
747+
return None
767748

768-
# then sync file at path and retrieve id, if any
769-
id = self._sync_file(path, stat_info)
770-
self.con.commit()
771-
return id
749+
# then sync file at path and retrieve id, if any
750+
id = self._sync_file(path, stat_info)
751+
return id
772752

773753
def get_path(self, id):
774754
"""Retrieves the file path associated with a file ID. The file path is
@@ -813,26 +793,26 @@ def get_path(self, id):
813793
def move(self, old_path, new_path):
814794
"""Handles file moves by updating the file path of the associated file
815795
ID. Returns the file ID. Returns None if file does not exist at new_path."""
816-
old_path = self._normalize_path(old_path)
817-
new_path = self._normalize_path(new_path)
796+
with self.con:
797+
old_path = self._normalize_path(old_path)
798+
new_path = self._normalize_path(new_path)
818799

819-
# verify file exists at new_path
820-
stat_info = self._stat(new_path)
821-
if stat_info is None:
822-
return None
800+
# verify file exists at new_path
801+
stat_info = self._stat(new_path)
802+
if stat_info is None:
803+
return None
823804

824-
# sync the file and see if it was already indexed
825-
#
826-
# originally this method did not call _sync_file() for performance
827-
# reasons, but this is needed to handle an edge case:
828-
# https://github.com/jupyter-server/jupyter_server_fileid/issues/62
829-
id = self._sync_file(new_path, stat_info)
830-
if id is None:
831-
# if no existing record, create a new one
832-
id = self._create(new_path, stat_info)
805+
# sync the file and see if it was already indexed
806+
#
807+
# originally this method did not call _sync_file() for performance
808+
# reasons, but this is needed to handle an edge case:
809+
# https://github.com/jupyter-server/jupyter_server_fileid/issues/62
810+
id = self._sync_file(new_path, stat_info)
811+
if id is None:
812+
# if no existing record, create a new one
813+
id = self._create(new_path, stat_info)
833814

834-
self.con.commit()
835-
return id
815+
return id
836816

837817
def _copy_recursive(self, from_path: str, to_path: str, _: str = "") -> None:
838818
"""Copy all children of a given directory at `from_path` to a new
@@ -877,13 +857,13 @@ def copy(self, from_path, to_path):
877857
def delete(self, path):
878858
"""Handles file deletions by deleting the associated record in the File
879859
table. Returns None."""
880-
path = self._normalize_path(path)
860+
with self.con:
861+
path = self._normalize_path(path)
881862

882-
if os.path.isdir(path):
883-
self._delete_recursive(path)
863+
if os.path.isdir(path):
864+
self._delete_recursive(path)
884865

885-
self.con.execute("DELETE FROM Files WHERE path = ?", (path,))
886-
self.con.commit()
866+
self.con.execute("DELETE FROM Files WHERE path = ?", (path,))
887867

888868
def save(self, path):
889869
"""Handles file saves (edits) by updating recorded stat info.
@@ -896,21 +876,21 @@ def save(self, path):
896876
JupyterLab. This would (wrongly) preserve the association b/w the old
897877
file ID and the current path rather than create a new file ID.
898878
"""
899-
path = self._normalize_path(path)
879+
with self.con:
880+
path = self._normalize_path(path)
900881

901-
# look up record by ino and path
902-
stat_info = self._stat(path)
903-
row = self.con.execute(
904-
"SELECT id FROM Files WHERE ino = ? AND path = ?", (stat_info.ino, path)
905-
).fetchone()
906-
# if no record exists, return early
907-
if row is None:
908-
return
882+
# look up record by ino and path
883+
stat_info = self._stat(path)
884+
row = self.con.execute(
885+
"SELECT id FROM Files WHERE ino = ? AND path = ?", (stat_info.ino, path)
886+
).fetchone()
887+
# if no record exists, return early
888+
if row is None:
889+
return
909890

910-
# otherwise, update the stat info
911-
(id,) = row
912-
self._update(id, stat_info)
913-
self.con.commit()
891+
# otherwise, update the stat info
892+
(id,) = row
893+
self._update(id, stat_info)
914894

915895
def get_handlers_by_action(self) -> Dict[str, Optional[Callable[[Dict[str, Any]], Any]]]:
916896
return {

tests/test_manager.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
LocalFileIdManager,
1616
)
1717

18-
1918
@pytest.fixture
2019
def test_path(fs_helpers):
2120
path = "test_path"
@@ -621,7 +620,13 @@ def test_db_journal_mode(any_fid_manager_class, fid_db_path, jp_root_dir, db_jou
621620
assert actual_journal_mode[0].upper() == expected_journal_mode
622621

623622

624-
623+
# This test demonstrates an issue raised in
624+
# https://github.com/jupyter-server/jupyter_server_fileid/pull/76
625+
# which was later fixed in
626+
# https://github.com/jupyter-server/jupyter_server_fileid/pull/77
627+
#
628+
# We use this unit test to catch this edge case and ensure
629+
# its covered going forward.
625630
def test_multiple_fileIdManager_connections_after_exception(fid_db_path):
626631
original_file_path = "/path/to/file"
627632
copy_location = "/path/to/copy"
@@ -635,15 +640,21 @@ def test_multiple_fileIdManager_connections_after_exception(fid_db_path):
635640
# Copy the file
636641
manager_1.copy(original_file_path, copy_location)
637642
# Try copying the file again.
643+
excepted = False
638644
try:
639645
manager_1.copy(original_file_path, copy_location)
640646
# We expect this to fail because the file is already in the database.
641647
except sqlite3.IntegrityError:
648+
excepted = True
642649
pass
643650

644-
# Now the database is locked and no other connections can be made.
645-
# Start a second connection to the database and demonstrate
646-
# that the database is now stuck in a locked state.
651+
assert excepted, "Copying to the same location should raise an exception, but it did not here."
652+
653+
# Previously, these actions locked the database for future connections.
654+
# This was fixed in: https://github.com/jupyter-server/jupyter_server_fileid/pull/77
655+
656+
# Try making a second connection that writes to the DB and
657+
# make sure no exceptions were raised.
647658
manager_2 = ArbitraryFileIdManager(db_path=fid_db_path)
648659
manager_2.copy(original_file_path, another_copy_location)
649660

0 commit comments

Comments
 (0)