5
5
trigger index rebuilds when relevant files are modified, created, or deleted.
6
6
It uses the watchdog library for cross-platform file system event monitoring.
7
7
"""
8
+ # pylint: disable=missing-function-docstring # Fallback stub methods don't need docstrings
8
9
9
10
import logging
11
+ import os
12
+ import traceback
10
13
from threading import Timer
11
14
from typing import Optional , Callable
12
15
from pathlib import Path
18
21
except ImportError :
19
22
# Fallback classes for when watchdog is not available
20
23
class Observer :
21
- def __init__ (self ): pass
22
- def schedule (self , * args , ** kwargs ): pass
23
- def start (self ): pass
24
- def stop (self ): pass
25
- def join (self , * args , ** kwargs ): pass
26
- def is_alive (self ): return False
24
+ """Fallback Observer class when watchdog library is not available."""
25
+ def __init__ (self ):
26
+ pass
27
+ def schedule (self , * args , ** kwargs ):
28
+ pass
29
+ def start (self ):
30
+ pass
31
+ def stop (self ):
32
+ pass
33
+ def join (self , * args , ** kwargs ):
34
+ pass
35
+ def is_alive (self ):
36
+ return False
27
37
28
38
class FileSystemEventHandler :
29
- def __init__ (self ): pass
39
+ """Fallback FileSystemEventHandler class when watchdog library is not available."""
40
+ def __init__ (self ):
41
+ pass
30
42
31
43
class FileSystemEvent :
44
+ """Fallback FileSystemEvent class when watchdog library is not available."""
32
45
def __init__ (self ):
33
46
self .is_directory = False
34
47
self .src_path = ""
@@ -111,8 +124,8 @@ def start_monitoring(self, rebuild_callback: Callable) -> bool:
111
124
112
125
# Log detailed Observer setup
113
126
watch_path = str (self .base_path )
114
- self .logger .debug (f "Scheduling Observer for path: { watch_path } " )
115
-
127
+ self .logger .debug ("Scheduling Observer for path: %s" , watch_path )
128
+
116
129
self .observer .schedule (
117
130
self .event_handler ,
118
131
watch_path ,
@@ -124,11 +137,10 @@ def start_monitoring(self, rebuild_callback: Callable) -> bool:
124
137
self .observer .start ()
125
138
self .is_monitoring = True
126
139
self .restart_attempts = 0
127
-
140
+
128
141
# Log Observer thread info
129
142
if hasattr (self .observer , '_thread' ):
130
- thread_info = f"Observer thread: { self .observer ._thread } "
131
- self .logger .debug (thread_info )
143
+ self .logger .debug ("Observer thread: %s" , self .observer ._thread )
132
144
133
145
# Verify observer is actually running
134
146
if self .observer .is_alive ():
@@ -140,18 +152,17 @@ def start_monitoring(self, rebuild_callback: Callable) -> bool:
140
152
"supported_extensions" : len (SUPPORTED_EXTENSIONS )
141
153
}
142
154
)
143
-
155
+
144
156
# Add diagnostic test - create a test event to verify Observer works
145
- import os
146
- self .logger .debug (f"Observer thread is alive: { self .observer .is_alive ()} " )
147
- self .logger .debug (f"Monitored path exists: { os .path .exists (str (self .base_path ))} " )
148
- self .logger .debug (f"Event handler is set: { self .event_handler is not None } " )
149
-
157
+ self .logger .debug ("Observer thread is alive: %s" , self .observer .is_alive ())
158
+ self .logger .debug ("Monitored path exists: %s" , os .path .exists (str (self .base_path )))
159
+ self .logger .debug ("Event handler is set: %s" , self .event_handler is not None )
160
+
150
161
# Log current directory for comparison
151
162
current_dir = os .getcwd ()
152
- self .logger .debug (f "Current working directory: { current_dir } " )
153
- self .logger .debug (f "Are paths same: { os .path .normpath (current_dir ) == os .path .normpath (str (self .base_path ))} " )
154
-
163
+ self .logger .debug ("Current working directory: %s" , current_dir )
164
+ self .logger .debug ("Are paths same: %s" , os .path .normpath (current_dir ) == os .path .normpath (str (self .base_path )))
165
+
155
166
return True
156
167
else :
157
168
self .logger .error ("File watcher failed to start - Observer not alive" )
@@ -165,7 +176,7 @@ def start_monitoring(self, rebuild_callback: Callable) -> bool:
165
176
def stop_monitoring (self ) -> None :
166
177
"""
167
178
Stop file system monitoring and cleanup all resources.
168
-
179
+
169
180
This method ensures complete cleanup of:
170
181
- Observer thread
171
182
- Event handler
@@ -175,43 +186,43 @@ def stop_monitoring(self) -> None:
175
186
if not self .observer and not self .is_monitoring :
176
187
# Already stopped or never started
177
188
return
178
-
189
+
179
190
self .logger .info ("Stopping file watcher monitoring..." )
180
-
191
+
181
192
try :
182
193
# Step 1: Stop the observer first
183
194
if self .observer :
184
195
self .logger .debug ("Stopping observer..." )
185
196
self .observer .stop ()
186
-
197
+
187
198
# Step 2: Cancel any active debounce timer
188
199
if self .event_handler and self .event_handler .debounce_timer :
189
200
self .logger .debug ("Cancelling debounce timer..." )
190
201
self .event_handler .debounce_timer .cancel ()
191
-
202
+
192
203
# Step 3: Wait for observer thread to finish (with timeout)
193
204
self .logger .debug ("Waiting for observer thread to finish..." )
194
205
self .observer .join (timeout = 5.0 )
195
-
206
+
196
207
# Step 4: Check if thread actually finished
197
208
if self .observer .is_alive ():
198
209
self .logger .warning ("Observer thread did not stop within timeout" )
199
210
else :
200
211
self .logger .debug ("Observer thread stopped successfully" )
201
-
212
+
202
213
# Step 5: Clear all references
203
214
self .observer = None
204
215
self .event_handler = None
205
216
self .rebuild_callback = None
206
217
self .is_monitoring = False
207
-
218
+
208
219
self .logger .info ("File watcher stopped and cleaned up successfully" )
209
220
print ("STOPPED: File watcher stopped" )
210
-
221
+
211
222
except Exception as e :
212
223
self .logger .error ("Error stopping file watcher: %s" , e )
213
224
print (f"WARNING: Error stopping file watcher: { e } " )
214
-
225
+
215
226
# Force cleanup even if there were errors
216
227
self .observer = None
217
228
self .event_handler = None
@@ -340,21 +351,17 @@ def on_any_event(self, event: FileSystemEvent) -> None:
340
351
event: The file system event
341
352
"""
342
353
# Always log events for debugging
343
- event_info = f"Raw event: { event .event_type } - { event .src_path } (is_directory: { event .is_directory } )"
344
- self .logger .debug (event_info )
345
-
354
+ self .logger .debug ("Raw event: %s - %s (is_directory: %s)" , event .event_type , event .src_path , event .is_directory )
355
+
346
356
# Test event processing
347
357
should_process = self .should_process_event (event )
348
- process_info = f"Should process: { should_process } "
349
- self .logger .debug (process_info )
350
-
358
+ self .logger .debug ("Should process: %s" , should_process )
359
+
351
360
if should_process :
352
- process_msg = f"Processing file system event: { event .event_type } - { event .src_path } "
353
- self .logger .debug (process_msg )
361
+ self .logger .debug ("Processing file system event: %s - %s" , event .event_type , event .src_path )
354
362
self .reset_debounce_timer ()
355
363
else :
356
- filter_msg = f"Event filtered out: { event .event_type } - { event .src_path } "
357
- self .logger .debug (filter_msg )
364
+ self .logger .debug ("Event filtered out: %s - %s" , event .event_type , event .src_path )
358
365
359
366
def should_process_event (self , event : FileSystemEvent ) -> bool :
360
367
"""
@@ -368,7 +375,7 @@ def should_process_event(self, event: FileSystemEvent) -> bool:
368
375
"""
369
376
# Skip directory events
370
377
if event .is_directory :
371
- self .logger .debug (f "Skipping directory event: { event .src_path } " )
378
+ self .logger .debug ("Skipping directory event: %s" , event .src_path )
372
379
return False
373
380
374
381
# Select path to check: dest_path for moves, src_path for others
@@ -377,17 +384,17 @@ def should_process_event(self, event: FileSystemEvent) -> bool:
377
384
self .logger .debug ("Move event missing dest_path" )
378
385
return False
379
386
target_path = event .dest_path
380
- self .logger .debug (f "Move event: checking destination path { target_path } " )
387
+ self .logger .debug ("Move event: checking destination path %s" , target_path )
381
388
else :
382
389
target_path = event .src_path
383
- self .logger .debug (f" { event . event_type } event: checking source path { target_path } " )
384
-
390
+ self .logger .debug ("%s event: checking source path %s" , event . event_type , target_path )
391
+
385
392
# Unified path checking
386
393
try :
387
394
path = Path (target_path )
388
395
return self ._should_process_path (path )
389
396
except Exception as e :
390
- self .logger .debug (f "Path conversion failed for { target_path } : { e } " )
397
+ self .logger .debug ("Path conversion failed for %s: %s" , target_path , e )
391
398
return False
392
399
393
400
def _should_process_path (self , path : Path ) -> bool :
@@ -401,27 +408,27 @@ def _should_process_path(self, path: Path) -> bool:
401
408
True if path should trigger rebuild, False otherwise
402
409
"""
403
410
# Log detailed filtering steps
404
- self .logger .debug (f "Checking path: { path } " )
405
-
411
+ self .logger .debug ("Checking path: %s" , path )
412
+
406
413
# Skip excluded paths
407
414
is_excluded = self .is_excluded_path (path )
408
- self .logger .debug (f "Is excluded path: { is_excluded } " )
415
+ self .logger .debug ("Is excluded path: %s" , is_excluded )
409
416
if is_excluded :
410
417
return False
411
418
412
419
# Only process supported file types
413
420
is_supported = self .is_supported_file_type (path )
414
- self .logger .debug (f "Is supported file type: { is_supported } (extension: { path .suffix } )" )
421
+ self .logger .debug ("Is supported file type: %s (extension: %s)" , is_supported , path .suffix )
415
422
if not is_supported :
416
423
return False
417
424
418
425
# Skip temporary files
419
426
is_temp = self .is_temporary_file (path )
420
- self .logger .debug (f "Is temporary file: { is_temp } " )
427
+ self .logger .debug ("Is temporary file: %s" , is_temp )
421
428
if is_temp :
422
429
return False
423
430
424
- self .logger .debug (f "Event will be processed: { path } " )
431
+ self .logger .debug ("Event will be processed: %s" , path )
425
432
return True
426
433
427
434
def is_excluded_path (self , path : Path ) -> bool :
@@ -494,15 +501,14 @@ def reset_debounce_timer(self) -> None:
494
501
self .debounce_timer .cancel ()
495
502
self .logger .debug ("Previous debounce timer cancelled" )
496
503
497
- timer_msg = f"Starting debounce timer for { self .debounce_seconds } seconds"
498
- self .logger .debug (timer_msg )
499
-
504
+ self .logger .debug ("Starting debounce timer for %s seconds" , self .debounce_seconds )
505
+
500
506
self .debounce_timer = Timer (
501
507
self .debounce_seconds ,
502
508
self .trigger_rebuild
503
509
)
504
510
self .debounce_timer .start ()
505
-
511
+
506
512
self .logger .debug ("Debounce timer started successfully" )
507
513
508
514
def trigger_rebuild (self ) -> None :
@@ -512,19 +518,14 @@ def trigger_rebuild(self) -> None:
512
518
513
519
if self .rebuild_callback :
514
520
try :
515
- callback_msg = "Calling rebuild callback..."
516
- self .logger .debug (callback_msg )
517
-
521
+ self .logger .debug ("Calling rebuild callback..." )
522
+
518
523
result = self .rebuild_callback ()
519
-
520
- result_msg = f"Rebuild callback completed with result: { result } "
521
- self .logger .debug (result_msg )
524
+
525
+ self .logger .debug ("Rebuild callback completed with result: %s" , result )
522
526
except Exception as e :
523
- error_msg = f"Rebuild callback failed: { e } "
524
- self .logger .error (error_msg )
525
- import traceback
527
+ self .logger .error ("Rebuild callback failed: %s" , e )
526
528
traceback_msg = traceback .format_exc ()
527
529
self .logger .error ("Traceback: %s" , traceback_msg )
528
530
else :
529
- no_callback_msg = "No rebuild callback configured"
530
- self .logger .warning (no_callback_msg )
531
+ self .logger .warning ("No rebuild callback configured" )
0 commit comments