memowatch 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. memowatch-0.1.0/PKG-INFO +163 -0
  2. memowatch-0.1.0/README.md +135 -0
  3. memowatch-0.1.0/memowatch/__init__.py +99 -0
  4. memowatch-0.1.0/memowatch/backends/__init__.py +3 -0
  5. memowatch-0.1.0/memowatch/backends/base.py +78 -0
  6. memowatch-0.1.0/memowatch/backends/linux.py +259 -0
  7. memowatch-0.1.0/memowatch/backends/polling.py +156 -0
  8. memowatch-0.1.0/memowatch/backends/win32.py +377 -0
  9. memowatch-0.1.0/memowatch/cli/__init__.py +3 -0
  10. memowatch-0.1.0/memowatch/cli/main.py +218 -0
  11. memowatch-0.1.0/memowatch/core/__init__.py +3 -0
  12. memowatch-0.1.0/memowatch/core/ast_diff.py +108 -0
  13. memowatch-0.1.0/memowatch/core/diff.py +125 -0
  14. memowatch-0.1.0/memowatch/core/events.py +259 -0
  15. memowatch-0.1.0/memowatch/core/observer.py +264 -0
  16. memowatch-0.1.0/memowatch/core/pipeline.py +296 -0
  17. memowatch-0.1.0/memowatch/core/predict.py +74 -0
  18. memowatch-0.1.0/memowatch/filters/__init__.py +3 -0
  19. memowatch-0.1.0/memowatch/filters/gitignore.py +255 -0
  20. memowatch-0.1.0/memowatch/filters/pattern.py +66 -0
  21. memowatch-0.1.0/memowatch/filters/size_mime.py +61 -0
  22. memowatch-0.1.0/memowatch/fluent.py +133 -0
  23. memowatch-0.1.0/memowatch/utils/__init__.py +3 -0
  24. memowatch-0.1.0/memowatch/utils/hasher.py +61 -0
  25. memowatch-0.1.0/memowatch/utils/lock_check.py +52 -0
  26. memowatch-0.1.0/memowatch.egg-info/PKG-INFO +163 -0
  27. memowatch-0.1.0/memowatch.egg-info/SOURCES.txt +34 -0
  28. memowatch-0.1.0/memowatch.egg-info/dependency_links.txt +1 -0
  29. memowatch-0.1.0/memowatch.egg-info/entry_points.txt +2 -0
  30. memowatch-0.1.0/memowatch.egg-info/requires.txt +4 -0
  31. memowatch-0.1.0/memowatch.egg-info/top_level.txt +1 -0
  32. memowatch-0.1.0/pyproject.toml +50 -0
  33. memowatch-0.1.0/setup.cfg +4 -0
  34. memowatch-0.1.0/tests/test_events.py +46 -0
  35. memowatch-0.1.0/tests/test_filters.py +49 -0
  36. memowatch-0.1.0/tests/test_pipeline.py +75 -0
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.4
2
+ Name: memowatch
3
+ Version: 0.1.0
4
+ Summary: A modern, high-performance filesystem monitoring library for Python with async support, event debouncing, file settling detection, and content-aware diffing.
5
+ Author-email: Yogesh V <yogeshgokul372@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/yogesh12s/memowatch
8
+ Project-URL: Documentation, https://github.com/yogesh12s/memowatch#readme
9
+ Project-URL: Issues, https://github.com/yogesh12s/memowatch/issues
10
+ Keywords: filesystem,monitoring,watchdog,inotify,fsevents,async
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: System :: Filesystems
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.8
24
+ Description-Content-Type: text/markdown
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.0; extra == "dev"
27
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
28
+
29
+ <div align="center">
30
+
31
+ # ๐Ÿ”ญ memowatch
32
+ **The file watcher from the year 2042.**
33
+
34
+ [![Python Versions](https://img.shields.io/pypi/pyversions/memowatch.svg?style=for-the-badge&logo=python&logoColor=white)](#)
35
+ [![PyPI version](https://img.shields.io/pypi/v/memowatch.svg?style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/memowatch/)
36
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)
37
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge)](#)
38
+ [![Downloads](https://img.shields.io/pypi/dm/memowatch?style=for-the-badge&color=blue)](#)
39
+ [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/yogeshgokul)
40
+
41
+ *Because knowing a file changed is cool, but knowing your code logic changed before you even hit 'save' is cooler.* ๐Ÿ˜Ž
42
+
43
+ </div>
44
+
45
+ ---
46
+
47
+ ## ๐Ÿ‘ฝ What is this?
48
+
49
+ Look, standard file watchers (we love you, `watchdog` โค๏ธ) are great if you want to be bombarded with 17 rapid-fire `FileModifiedEvent`s every time you hit `Ctrl+S` in your editor.
50
+
51
+ **`memowatch`** is a high-performance, async-first Python file watcher that actually uses its brain. It debounces events, reads your `.gitignore`, natively speaks Windows & Linux C-APIs without clunky dependencies, andโ€”wait for itโ€”**understands your code**.
52
+
53
+ Oh, and it predicts the future. Seriously.
54
+
55
+ ---
56
+
57
+ ## โœจ Features That Will Make You Cry Tears of Joy
58
+
59
+ | ๐Ÿฆธโ€โ™‚๏ธ Superpower | ๐Ÿ“– What it actually does |
60
+ | :--- | :--- |
61
+ | **AST-Aware Code Watcher** ๐Ÿงฌ | Don't just watch files. Watch *logic*. Triggers on `FunctionAdded` or `ClassModified` while totally ignoring that extra space you accidentally typed. |
62
+ | **Predictive Pre-Fetching** ๐Ÿ”ฎ | A built-in Markov Chain engine learns your habits and screams *"Hey, they're probably gonna edit `views.py` next!"* |
63
+ | **Fluent "Reads-like-English" API** ๐Ÿ—ฃ๏ธ | Configure your watcher using a Builder pattern that reads so smoothly your non-coder boss might understand it. |
64
+ | **Event Debouncing** ๐Ÿ›‘ | Collapses the 5 events your IDE generates for a single save into **one** logical change. |
65
+ | **File Settling Detector** ๐Ÿ›Œ | `FileSettledEvent` waits for your giant 10GB video file to *actually finish downloading* and release its file locks before telling you to process it. |
66
+ | **Content Hashing** ๐Ÿ•ต๏ธโ€โ™‚๏ธ | SHA-256 diffing means we ignore files if you just updated the timestamp but didn't change the text. |
67
+ | **Native `.gitignore`** ๐Ÿ™ˆ | It automatically ignores your `__pycache__` and `node_modules` so you don't have to write an exclusion regex. |
68
+ | **Pure Python Win32 & Inotify** ๐Ÿ–ฅ๏ธ | Zero C-extensions to compile. Zero `pywin32` dependencies. Just pure, unadulterated `ctypes` magic. |
69
+
70
+ ---
71
+
72
+ ## ๐Ÿš€ Quick Start (0 to 100 real quick)
73
+
74
+ ### Installation
75
+ *(Requires Python 3.8+ because we have standards)*
76
+ ```bash
77
+ pip install memowatch
78
+ ```
79
+
80
+ ### ๐Ÿ—ฃ๏ธ The "Reads Like English" Fluent API
81
+ Why write boilerplate classes when you can write poetry?
82
+
83
+ ```python
84
+ import memowatch
85
+
86
+ def freak_out(event):
87
+ print(f"๐Ÿšจ Whoa! You changed a function named: {event.node_name}")
88
+
89
+ # Reads like plain English!
90
+ watcher = (
91
+ memowatch.observe("./src")
92
+ .ignoring_directories()
93
+ .when_code_changes()
94
+ .specifically("FunctionAdded", "ClassModified")
95
+ .then_execute(freak_out)
96
+ .with_predictive_fetching()
97
+ .start_in_background()
98
+ )
99
+ ```
100
+
101
+ ### ๐Ÿšฆ The "I just want callbacks" Sync API
102
+ For the traditionalists among us.
103
+
104
+ ```python
105
+ import memowatch
106
+
107
+ class MyChillHandler(memowatch.FileSystemEventHandler):
108
+ def on_settled(self, event):
109
+ print(f"๐Ÿ˜Œ Ahhh, {event.src_path} is finally done writing.")
110
+
111
+ observer = memowatch.Observer(debounce_ms=300, settle_timeout_ms=1000)
112
+ observer.schedule(MyChillHandler(), path="./my_project", recursive=True)
113
+ observer.start()
114
+
115
+ # Keep the main thread alive, grab a coffee โ˜•
116
+ import time
117
+ try:
118
+ while True: time.sleep(1)
119
+ except KeyboardInterrupt:
120
+ observer.stop()
121
+ ```
122
+
123
+ ---
124
+
125
+ ## ๐Ÿ–ฅ๏ธ The Badass CLI
126
+
127
+ Don't want to write code? Use the built-in CLI daemon.
128
+
129
+ ```bash
130
+ # Watch a directory and print events like a normal person
131
+ memowatch watch ./src
132
+
133
+ # Run a daemon watcher in the background and trigger a build script
134
+ memowatch watch ./src --background --run "python build.py"
135
+
136
+ # Stop the invisible background watcher before it becomes self-aware
137
+ memowatch stop ./src
138
+ ```
139
+
140
+ ---
141
+
142
+ ## ๐Ÿ“– The Glossary of Events
143
+
144
+ If it happens on your hard drive, we have an event for it:
145
+
146
+ | Event Class | Why you care |
147
+ | :--- | :--- |
148
+ | `FileCreatedEvent` | A new file appeared out of thin air. |
149
+ | `FileSettledEvent` | **(Special โœจ)** The file is completely finished writing and the locks are gone. |
150
+ | `FileDiffEvent` | **(Special โœจ)** Tells you the *exact* lines added or removed. No `git status` required. |
151
+ | `CodeChangedEvent` | **(Special โœจ)** You added/modified a Python function or class. |
152
+ | `FilePredictedEvent` | **(Special โœจ)** The AI thinks you're about to open this file. |
153
+ | `FileDeletedEvent` | It's gone. Poof. |
154
+ | `FileMovedEvent` | Witness protection program for files. |
155
+
156
+ ---
157
+
158
+ <div align="center">
159
+ Made with โค๏ธ and way too much caffeine by <a href="https://github.com/yogesh12s">Yogesh Gokul</a>.<br>
160
+ <sub>(Drop a โญ if you think file watching should be this fun!)</sub><br><br>
161
+
162
+ <a href="https://www.buymeacoffee.com/yogeshgokul" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 40px !important;width: 145px !important;" ></a>
163
+ </div>
@@ -0,0 +1,135 @@
1
+ <div align="center">
2
+
3
+ # ๐Ÿ”ญ memowatch
4
+ **The file watcher from the year 2042.**
5
+
6
+ [![Python Versions](https://img.shields.io/pypi/pyversions/memowatch.svg?style=for-the-badge&logo=python&logoColor=white)](#)
7
+ [![PyPI version](https://img.shields.io/pypi/v/memowatch.svg?style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/memowatch/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)
9
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge)](#)
10
+ [![Downloads](https://img.shields.io/pypi/dm/memowatch?style=for-the-badge&color=blue)](#)
11
+ [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/yogeshgokul)
12
+
13
+ *Because knowing a file changed is cool, but knowing your code logic changed before you even hit 'save' is cooler.* ๐Ÿ˜Ž
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ ## ๐Ÿ‘ฝ What is this?
20
+
21
+ Look, standard file watchers (we love you, `watchdog` โค๏ธ) are great if you want to be bombarded with 17 rapid-fire `FileModifiedEvent`s every time you hit `Ctrl+S` in your editor.
22
+
23
+ **`memowatch`** is a high-performance, async-first Python file watcher that actually uses its brain. It debounces events, reads your `.gitignore`, natively speaks Windows & Linux C-APIs without clunky dependencies, andโ€”wait for itโ€”**understands your code**.
24
+
25
+ Oh, and it predicts the future. Seriously.
26
+
27
+ ---
28
+
29
+ ## โœจ Features That Will Make You Cry Tears of Joy
30
+
31
+ | ๐Ÿฆธโ€โ™‚๏ธ Superpower | ๐Ÿ“– What it actually does |
32
+ | :--- | :--- |
33
+ | **AST-Aware Code Watcher** ๐Ÿงฌ | Don't just watch files. Watch *logic*. Triggers on `FunctionAdded` or `ClassModified` while totally ignoring that extra space you accidentally typed. |
34
+ | **Predictive Pre-Fetching** ๐Ÿ”ฎ | A built-in Markov Chain engine learns your habits and screams *"Hey, they're probably gonna edit `views.py` next!"* |
35
+ | **Fluent "Reads-like-English" API** ๐Ÿ—ฃ๏ธ | Configure your watcher using a Builder pattern that reads so smoothly your non-coder boss might understand it. |
36
+ | **Event Debouncing** ๐Ÿ›‘ | Collapses the 5 events your IDE generates for a single save into **one** logical change. |
37
+ | **File Settling Detector** ๐Ÿ›Œ | `FileSettledEvent` waits for your giant 10GB video file to *actually finish downloading* and release its file locks before telling you to process it. |
38
+ | **Content Hashing** ๐Ÿ•ต๏ธโ€โ™‚๏ธ | SHA-256 diffing means we ignore files if you just updated the timestamp but didn't change the text. |
39
+ | **Native `.gitignore`** ๐Ÿ™ˆ | It automatically ignores your `__pycache__` and `node_modules` so you don't have to write an exclusion regex. |
40
+ | **Pure Python Win32 & Inotify** ๐Ÿ–ฅ๏ธ | Zero C-extensions to compile. Zero `pywin32` dependencies. Just pure, unadulterated `ctypes` magic. |
41
+
42
+ ---
43
+
44
+ ## ๐Ÿš€ Quick Start (0 to 100 real quick)
45
+
46
+ ### Installation
47
+ *(Requires Python 3.8+ because we have standards)*
48
+ ```bash
49
+ pip install memowatch
50
+ ```
51
+
52
+ ### ๐Ÿ—ฃ๏ธ The "Reads Like English" Fluent API
53
+ Why write boilerplate classes when you can write poetry?
54
+
55
+ ```python
56
+ import memowatch
57
+
58
+ def freak_out(event):
59
+ print(f"๐Ÿšจ Whoa! You changed a function named: {event.node_name}")
60
+
61
+ # Reads like plain English!
62
+ watcher = (
63
+ memowatch.observe("./src")
64
+ .ignoring_directories()
65
+ .when_code_changes()
66
+ .specifically("FunctionAdded", "ClassModified")
67
+ .then_execute(freak_out)
68
+ .with_predictive_fetching()
69
+ .start_in_background()
70
+ )
71
+ ```
72
+
73
+ ### ๐Ÿšฆ The "I just want callbacks" Sync API
74
+ For the traditionalists among us.
75
+
76
+ ```python
77
+ import memowatch
78
+
79
+ class MyChillHandler(memowatch.FileSystemEventHandler):
80
+ def on_settled(self, event):
81
+ print(f"๐Ÿ˜Œ Ahhh, {event.src_path} is finally done writing.")
82
+
83
+ observer = memowatch.Observer(debounce_ms=300, settle_timeout_ms=1000)
84
+ observer.schedule(MyChillHandler(), path="./my_project", recursive=True)
85
+ observer.start()
86
+
87
+ # Keep the main thread alive, grab a coffee โ˜•
88
+ import time
89
+ try:
90
+ while True: time.sleep(1)
91
+ except KeyboardInterrupt:
92
+ observer.stop()
93
+ ```
94
+
95
+ ---
96
+
97
+ ## ๐Ÿ–ฅ๏ธ The Badass CLI
98
+
99
+ Don't want to write code? Use the built-in CLI daemon.
100
+
101
+ ```bash
102
+ # Watch a directory and print events like a normal person
103
+ memowatch watch ./src
104
+
105
+ # Run a daemon watcher in the background and trigger a build script
106
+ memowatch watch ./src --background --run "python build.py"
107
+
108
+ # Stop the invisible background watcher before it becomes self-aware
109
+ memowatch stop ./src
110
+ ```
111
+
112
+ ---
113
+
114
+ ## ๐Ÿ“– The Glossary of Events
115
+
116
+ If it happens on your hard drive, we have an event for it:
117
+
118
+ | Event Class | Why you care |
119
+ | :--- | :--- |
120
+ | `FileCreatedEvent` | A new file appeared out of thin air. |
121
+ | `FileSettledEvent` | **(Special โœจ)** The file is completely finished writing and the locks are gone. |
122
+ | `FileDiffEvent` | **(Special โœจ)** Tells you the *exact* lines added or removed. No `git status` required. |
123
+ | `CodeChangedEvent` | **(Special โœจ)** You added/modified a Python function or class. |
124
+ | `FilePredictedEvent` | **(Special โœจ)** The AI thinks you're about to open this file. |
125
+ | `FileDeletedEvent` | It's gone. Poof. |
126
+ | `FileMovedEvent` | Witness protection program for files. |
127
+
128
+ ---
129
+
130
+ <div align="center">
131
+ Made with โค๏ธ and way too much caffeine by <a href="https://github.com/yogesh12s">Yogesh Gokul</a>.<br>
132
+ <sub>(Drop a โญ if you think file watching should be this fun!)</sub><br><br>
133
+
134
+ <a href="https://www.buymeacoffee.com/yogeshgokul" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 40px !important;width: 145px !important;" ></a>
135
+ </div>
@@ -0,0 +1,99 @@
1
+ """
2
+ memowatch - A modern, high-performance filesystem monitoring library for Python.
3
+
4
+ Features beyond standard watchdog:
5
+ - First-class async/await support (async iterators)
6
+ - Built-in event debouncing & coalescing
7
+ - File settling / lock detection (FileSettledEvent)
8
+ - Content-aware SHA-256 diffing
9
+ - Native .gitignore parser + glob/regex/size filters
10
+ - Pure-Python Windows backend via ctypes (no pywin32)
11
+ - CLI with command runner and JSON streaming
12
+
13
+ Usage:
14
+ # Sync / callback API
15
+ import memowatch
16
+
17
+ class MyHandler(memowatch.FileSystemEventHandler):
18
+ def on_created(self, event):
19
+ print(f"Created: {event.src_path}")
20
+
21
+ observer = memowatch.Observer()
22
+ observer.schedule(MyHandler(), path="./src", recursive=True)
23
+ observer.start()
24
+
25
+ # Async API
26
+ import asyncio
27
+
28
+ async def main():
29
+ async with memowatch.async_watch("./src") as stream:
30
+ async for event in stream:
31
+ print(event)
32
+
33
+ asyncio.run(main())
34
+ """
35
+
36
+ __version__ = "0.1.0"
37
+ __author__ = "Yogesh Gokul <yogeshgokul372@gmail.com>"
38
+
39
+ # Display author information on import as requested
40
+ print(f"Loaded memowatch v{__version__} - Built with โค๏ธ by {__author__}")
41
+
42
+ from memowatch.core.events import (
43
+ FileSystemEvent,
44
+ FileCreatedEvent,
45
+ FileModifiedEvent,
46
+ FileDeletedEvent,
47
+ FileMovedEvent,
48
+ FileSettledEvent,
49
+ DirCreatedEvent,
50
+ DirModifiedEvent,
51
+ DirDeletedEvent,
52
+ DirMovedEvent,
53
+ EVENT_TYPE_CREATED,
54
+ EVENT_TYPE_MODIFIED,
55
+ EVENT_TYPE_DELETED,
56
+ EVENT_TYPE_MOVED,
57
+ EVENT_TYPE_SETTLED,
58
+ )
59
+
60
+ from memowatch.core.observer import (
61
+ FileSystemEventHandler,
62
+ Observer,
63
+ async_watch,
64
+ )
65
+
66
+ from memowatch.fluent import observe
67
+
68
+ __all__ = [
69
+ # Version
70
+ "__version__",
71
+ # Events
72
+ "FileSystemEvent",
73
+ "FileCreatedEvent",
74
+ "FileModifiedEvent",
75
+ "FileDeletedEvent",
76
+ "FileMovedEvent",
77
+ "FileSettledEvent",
78
+ "CodeChangedEvent",
79
+ "FilePredictedEvent",
80
+ "FileDiffEvent",
81
+ "DirCreatedEvent",
82
+ "DirModifiedEvent",
83
+ "DirDeletedEvent",
84
+ "DirMovedEvent",
85
+ "EVENT_TYPE_CREATED",
86
+ "EVENT_TYPE_MODIFIED",
87
+ "EVENT_TYPE_DELETED",
88
+ "EVENT_TYPE_MOVED",
89
+ "EVENT_TYPE_SETTLED",
90
+ "EVENT_TYPE_DIFF",
91
+ "EVENT_TYPE_CODE_CHANGED",
92
+ "EVENT_TYPE_PREDICTED",
93
+ # Observer
94
+ "FileSystemEventHandler",
95
+ "Observer",
96
+ "async_watch",
97
+ # Fluent API
98
+ "observe",
99
+ ]
@@ -0,0 +1,3 @@
1
+ """
2
+ memowatch.backends - Backend sub-package init.
3
+ """
@@ -0,0 +1,78 @@
1
+ """
2
+ memowatch.backends.base
3
+ ~~~~~~~~~~~~~~~~~~~~~~~~
4
+
5
+ Abstract base class for all platform backends.
6
+
7
+ A backend is responsible for:
8
+ 1. Watching a single directory (optionally recursively).
9
+ 2. Emitting raw ``FileSystemEvent`` objects via a callback.
10
+ 3. Running in a background thread managed by the ``Observer``.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import abc
16
+ import threading
17
+ from typing import Callable, Optional
18
+
19
+ from memowatch.core.events import FileSystemEvent
20
+
21
+
22
+ class BaseBackend(abc.ABC):
23
+ """Abstract base class that every platform backend must implement."""
24
+
25
+ def __init__(
26
+ self,
27
+ path: str,
28
+ recursive: bool = False,
29
+ callback: Optional[Callable[[FileSystemEvent], None]] = None,
30
+ ) -> None:
31
+ self.path = path
32
+ self.recursive = recursive
33
+ self.callback = callback
34
+ self._stop_event = threading.Event()
35
+ self._thread: Optional[threading.Thread] = None
36
+
37
+ # โ”€โ”€ Public API โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
38
+
39
+ def start(self) -> None:
40
+ """Start watching in a background thread."""
41
+ self._stop_event.clear()
42
+ self._thread = threading.Thread(
43
+ target=self._run, daemon=True, name=f"memowatch-{self.name}"
44
+ )
45
+ self._thread.start()
46
+
47
+ def stop(self) -> None:
48
+ """Signal the backend to stop and wait for its thread to finish."""
49
+ self._stop_event.set()
50
+ if self._thread and self._thread.is_alive():
51
+ self._thread.join(timeout=5.0)
52
+
53
+ def is_alive(self) -> bool:
54
+ return self._thread is not None and self._thread.is_alive()
55
+
56
+ # โ”€โ”€ Emit helper โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
57
+
58
+ def emit(self, event: FileSystemEvent) -> None:
59
+ """Forward an event to the registered callback."""
60
+ if self.callback is not None:
61
+ self.callback(event)
62
+
63
+ # โ”€โ”€ Abstract interface โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
64
+
65
+ @property
66
+ @abc.abstractmethod
67
+ def name(self) -> str:
68
+ """Short human-readable name for this backend (e.g. 'win32', 'polling')."""
69
+
70
+ @abc.abstractmethod
71
+ def _run(self) -> None:
72
+ """Main loop executed in the background thread.
73
+
74
+ Implementations should watch ``self.path``, detect changes, and call
75
+ ``self.emit(event)`` for each one. The loop must check
76
+ ``self._stop_event.is_set()`` periodically and return when it becomes
77
+ ``True``.
78
+ """