Skip to content

Commit 52d8692

Browse files
authored
feat: Add DirectorySnapshotDiff.ContextManager (gorakhargosh#1011)
* Add DirectorySnapshotDiff.ContextManager A context manager that creates two directory snapshots and a diff object that represents the difference between the two snapshots. ```python dir_snapshot_diff_context_manager = DirectorySnapshotDiff.ContextManager("some_path") with dir_snapshot_diff_context_manager: # Do some things that change files... ... print(dir_snapshot_diff_context_manager.diff.files_created) print(dir_snapshot_diff_context_manager.diff.files_deleted) ``` * Add entry to changelog.rst * Add typing to ContextManager.__init__
1 parent 2338837 commit 52d8692

File tree

3 files changed

+82
-1
lines changed

3 files changed

+82
-1
lines changed

changelog.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ Changelog
88

99
2023-xx-xx • `full history <https://github.com/gorakhargosh/watchdog/compare/v3.0.0...HEAD>`__
1010

11-
1211
- [snapshot] Add typing to ``dirsnapshot`` (`#1012 <https://github.com/gorakhargosh/watchdog/pull/1012>`__)
12+
- [snapshot] Added ``DirectorySnapshotDiff.ContextManager`` (`#1011 <https://github.com/gorakhargosh/watchdog/pull/1011>`__)
1313
- [events] ``FileSystemEvent``, and subclasses, are now ``dataclass``es, and their ``repr()`` has changed
1414
- [windows] ``WinAPINativeEvent`` is now a ``dataclass``, and its ``repr()`` has changed
1515
- [events] Log ``FileOpenedEvent``, and ``FileClosedEvent``, events in ``LoggingEventHandler``

src/watchdog/utils/dirsnapshot.py

+67
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,73 @@ def dirs_created(self) -> List[str]:
220220
"""
221221
return self._dirs_created
222222

223+
class ContextManager:
224+
"""
225+
Context manager that creates two directory snapshots and a
226+
diff object that represents the difference between the two snapshots.
227+
228+
:param path:
229+
The directory path for which a snapshot should be taken.
230+
:type path:
231+
``str``
232+
:param recursive:
233+
``True`` if the entire directory tree should be included in the
234+
snapshot; ``False`` otherwise.
235+
:type recursive:
236+
``bool``
237+
:param stat:
238+
Use custom stat function that returns a stat structure for path.
239+
Currently only st_dev, st_ino, st_mode and st_mtime are needed.
240+
241+
A function taking a ``path`` as argument which will be called
242+
for every entry in the directory tree.
243+
:param listdir:
244+
Use custom listdir function. For details see ``os.scandir``.
245+
:param ignore_device:
246+
A boolean indicating whether to ignore the device id or not.
247+
By default, a file may be uniquely identified by a combination of its first
248+
inode and its device id. The problem is that the device id may (or may not)
249+
change between system boots. This problem would cause the DirectorySnapshotDiff
250+
to think a file has been deleted and created again but it would be the
251+
exact same file.
252+
Set to True only if you are sure you will always use the same device.
253+
:type ignore_device:
254+
:class:`bool`
255+
"""
256+
257+
def __init__(
258+
self,
259+
path: str,
260+
recursive: bool = True,
261+
stat: Callable[[str], os.stat_result] = os.stat,
262+
listdir: Callable[[Optional[str]], Iterator[os.DirEntry]] = os.scandir,
263+
ignore_device: bool = False,
264+
):
265+
self.path = path
266+
self.recursive = recursive
267+
self.stat = stat
268+
self.listdir = listdir
269+
self.ignore_device = ignore_device
270+
271+
def __enter__(self):
272+
self.pre_snapshot = self.get_snapshot()
273+
274+
def __exit__(self, *args):
275+
self.post_snapshot = self.get_snapshot()
276+
self.diff = DirectorySnapshotDiff(
277+
self.pre_snapshot,
278+
self.post_snapshot,
279+
ignore_device=self.ignore_device,
280+
)
281+
282+
def get_snapshot(self):
283+
return DirectorySnapshot(
284+
path=self.path,
285+
recursive=self.recursive,
286+
stat=self.stat,
287+
listdir=self.listdir,
288+
)
289+
223290

224291
class DirectorySnapshot:
225292
"""

tests/test_snapshot_diff.py

+14
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ def test_move_to(p):
5555
assert diff.files_created == [p("dir2", "b")]
5656

5757

58+
def test_move_to_with_context_manager(p):
59+
mkdir(p("dir1"))
60+
touch(p("dir1", "a"))
61+
mkdir(p("dir2"))
62+
63+
dir1_cm = DirectorySnapshotDiff.ContextManager(p("dir1"))
64+
dir2_cm = DirectorySnapshotDiff.ContextManager(p("dir2"))
65+
with dir1_cm, dir2_cm:
66+
mv(p("dir1", "a"), p("dir2", "b"))
67+
68+
assert dir1_cm.diff.files_deleted == [p("dir1", "a")]
69+
assert dir2_cm.diff.files_created == [p("dir2", "b")]
70+
71+
5872
def test_move_from(p):
5973
mkdir(p("dir1"))
6074
mkdir(p("dir2"))

0 commit comments

Comments
 (0)