Source code for watchdog.utils
""":module: watchdog.utils
:synopsis: Utility classes and functions.
:author: yesudeep@google.com (Yesudeep Mangalapilly)
:author: contact@tiger-222.fr (Mickaël Schoentgen)
Classes
-------
.. autoclass:: BaseThread
   :members:
   :show-inheritance:
   :inherited-members:
"""
from __future__ import annotations
import sys
import threading
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from types import ModuleType
    from watchdog.tricks import Trick
class UnsupportedLibcError(Exception):
    pass
class WatchdogShutdownError(Exception):
    """Semantic exception used to signal an external shutdown event."""
[docs]
class BaseThread(threading.Thread):
    """Convenience class for creating stoppable threads."""
    def __init__(self) -> None:
        threading.Thread.__init__(self)
        if hasattr(self, "daemon"):
            self.daemon = True
        else:
            self.setDaemon(True)
        self._stopped_event = threading.Event()
    @property
    def stopped_event(self) -> threading.Event:
        return self._stopped_event
[docs]
    def should_keep_running(self) -> bool:
        """Determines whether the thread should continue running."""
        return not self._stopped_event.is_set() 
[docs]
    def on_thread_stop(self) -> None:
        """Override this method instead of :meth:`stop()`.
        :meth:`stop()` calls this method.
        This method is called immediately after the thread is signaled to stop.
        """ 
[docs]
    def stop(self) -> None:
        """Signals the thread to stop."""
        self._stopped_event.set()
        self.on_thread_stop() 
[docs]
    def on_thread_start(self) -> None:
        """Override this method instead of :meth:`start()`. :meth:`start()`
        calls this method.
        This method is called right before this thread is started and this
        object's run() method is invoked.
        """ 
[docs]
    def start(self) -> None:
        self.on_thread_start()
        threading.Thread.start(self) 
 
def load_module(module_name: str) -> ModuleType:
    """Imports a module given its name and returns a handle to it."""
    try:
        __import__(module_name)
    except ImportError as e:
        error = f"No module named {module_name}"
        raise ImportError(error) from e
    return sys.modules[module_name]
def load_class(dotted_path: str) -> type[Trick]:
    """Loads and returns a class definition provided a dotted path
    specification the last part of the dotted path is the class name
    and there is at least one module name preceding the class name.
    Notes
    -----
    You will need to ensure that the module you are trying to load
    exists in the Python path.
    Examples
    --------
    - module.name.ClassName    # Provided module.name is in the Python path.
    - module.ClassName         # Provided module is in the Python path.
    What won't work:
    - ClassName
    - modle.name.ClassName     # Typo in module name.
    - module.name.ClasNam      # Typo in classname.
    """
    dotted_path_split = dotted_path.split(".")
    if len(dotted_path_split) <= 1:
        error = f"Dotted module path {dotted_path} must contain a module name and a classname"
        raise ValueError(error)
    klass_name = dotted_path_split[-1]
    module_name = ".".join(dotted_path_split[:-1])
    module = load_module(module_name)
    if hasattr(module, klass_name):
        return getattr(module, klass_name)
    error = f"Module {module_name} does not have class attribute {klass_name}"
    raise AttributeError(error)