1414import linecache
1515import os
1616import sys
17+ import sysconfig
1718import threading
1819import traceback
1920import warnings
3536from watchdog .utils .event_debouncer import EventDebouncer
3637
3738__author__ = "EcmaXp"
38- __version__ = "0.17.1 "
39+ __version__ = "0.18.0 "
3940__license__ = "MIT"
4041__url__ = "https://pypi.org/project/reloader.py/"
4142__all__ = [
@@ -300,7 +301,7 @@ def load_lines(self) -> list[str]:
300301 linecache .updatecache (self .file , self .module .__dict__ )
301302
302303 lines = None
303- if self .module . __loader__ :
304+ if getattr ( self .module , " __loader__" , None ) :
304305 with suppress (OSError ):
305306 lines = inspect .getsourcelines (self .module )[0 ][:]
306307
@@ -444,7 +445,12 @@ def watch_all(self):
444445
445446
446447class Watcher :
447- def __init__ (self , * , debounce_interval : float = DEFAULT_DEBOUNCE_INTERVAL ):
448+ def __init__ (
449+ self ,
450+ * ,
451+ debounce_interval : float = DEFAULT_DEBOUNCE_INTERVAL ,
452+ disallowed_paths : set [Path ] | None = None ,
453+ ):
448454 self .handlers : list [Callable [[list [CodeModule | Path ]], None ]] = []
449455 self ._code_modules : dict [str , CodeModule | ScriptModule ] = {}
450456 self ._resources : set [str ] = set ()
@@ -463,6 +469,7 @@ def __init__(self, *, debounce_interval: float = DEFAULT_DEBOUNCE_INTERVAL):
463469 observer = self ._watchdog_observer ,
464470 )
465471 self ._sys_modules_watcher = SysModulesWatcher (self ._module_callback )
472+ self ._disallowed_paths = disallowed_paths or set ()
466473
467474 def _get_threads (self ):
468475 return (
@@ -504,8 +511,22 @@ def get_code_module(self, module_name: str) -> CodeModule | ScriptModule | None:
504511
505512 raise ValueError (f"Module { module_name !r} not found" )
506513
514+ def _check_module (self , module : ModuleType ):
515+ return self ._is_allowed (module ) and self ._has_source (module )
516+
517+ def _is_allowed (self , module : ModuleType ):
518+ path = getattr (module , "__file__" , None )
519+ if not path :
520+ return True
521+
522+ path = Path (path ).absolute ()
523+ return not any (
524+ path .is_relative_to (disallowed_path )
525+ for disallowed_path in self ._disallowed_paths
526+ )
527+
507528 @staticmethod
508- def _check_module (module : ModuleType ):
529+ def _has_source (module : ModuleType ):
509530 try :
510531 inspect .getsource (module )
511532 return True
@@ -770,6 +791,8 @@ def get_reloader_cls(reloader_cls: Type[T], loop: bool) -> Type[T]:
770791parser .add_argument ("--clear" , "-C" , action = "store_true" )
771792parser .add_argument ("--debounce" , "-d" , type = float , default = DEFAULT_DEBOUNCE_INTERVAL )
772793parser .add_argument ("--no-cwd-python-path" , "-P" , action = "store_true" )
794+ parser .add_argument ("--disallowed-path-types" , type = str , action = "append" , default = [])
795+ parser .add_argument ("--allowed-path-types" , type = str , action = "append" , default = [])
773796parser .add_argument ("--version" , action = "version" , version = f"%(prog)s { __version__ } " )
774797parser .add_argument ("script" , type = Path , nargs = argparse .OPTIONAL )
775798parser .add_argument ("argv" , type = str , nargs = argparse .REMAINDER )
@@ -786,7 +809,21 @@ def main():
786809 if not args .no_cwd_python_path :
787810 sys .path .insert (0 , str (Path .cwd ().resolve ()))
788811
789- watcher = Watcher (debounce_interval = args .debounce )
812+ disallowed_path_types = set (
813+ args .disallowed_path_types or sysconfig .get_path_names ()
814+ )
815+ for allowed_path_type in args .allowed_path_types :
816+ disallowed_path_types .remove (allowed_path_type )
817+
818+ disallowed_paths = {
819+ Path (sysconfig .get_path (path_type )).absolute ()
820+ for path_type in disallowed_path_types
821+ }
822+
823+ watcher = Watcher (
824+ debounce_interval = args .debounce ,
825+ disallowed_paths = disallowed_paths ,
826+ )
790827 for path in args .watch :
791828 watcher .watch_resource (path )
792829
0 commit comments