66import inspect
77import os
88import sys
9+ import time
910from enum import Enum
1011from pathlib import Path
1112from typing import TYPE_CHECKING , Callable , Optional
@@ -32,7 +33,8 @@ class EnvVariables(str, Enum):
3233 PYTEST_DAEMON_START_IF_NEEDED = "PYTEST_DAEMON_START_IF_NEEDED"
3334 PYTEST_DAEMON_DISABLE = "PYTEST_DAEMON_DISABLE"
3435 PYTEST_DAEMON_DO_NOT_AUTOWATCH_FIXTURES = "PYTEST_DAEMON_DO_NOT_AUTOWATCH_FIXTURES"
35- PYTEST_DAEMON_USE_WATCHMAN = "PYTEST_DAEMON_USE_WATCHMAN"
36+ PYTEST_DAEMON_USE_OS_EVENTS = "PYTEST_DAEMON_USE_OS_EVENTS"
37+ PYTEST_DAEMON_POLL_THROTTLE = "PYTEST_DAEMON_POLL_THROTTLE"
3638
3739
3840def pytest_addoption (parser ) -> None :
@@ -110,18 +112,26 @@ def pytest_addoption(parser) -> None:
110112 ),
111113 )
112114 group .addoption (
113- "--daemon-use-watchman " ,
115+ "--daemon-use-os-events " ,
114116 action = "store_true" ,
115117 default = (
116- os .getenv (EnvVariables .PYTEST_DAEMON_USE_WATCHMAN , "False" ).lower () in ("true" , "1" )
118+ os .getenv (EnvVariables .PYTEST_DAEMON_USE_OS_EVENTS , "False" ).lower () in ("true" , "1" )
117119 ),
118120 help = (
119- "Use watchman instead of polling. "
121+ "Use OS events such as inotify instead of polling. "
120122 "This reduces CPU usage, takes up open file handles, and improves responsiveness. "
121123 "Some systems cannot reliably use this."
122124 ),
123125 )
124126
127+ group .addoption (
128+ "--daemon-poll-throttle" ,
129+ default = (os .getenv (EnvVariables .PYTEST_DAEMON_POLL_THROTTLE , "1" )),
130+ help = (
131+ "The throttle for polling, as a float multiplier. Higher numbers are slower but tax the CPU less."
132+ ),
133+ )
134+
125135
126136# list of pytest hooks
127137# https://docs.pytest.org/en/stable/reference.html#_pytest.hookspec.pytest_addhooks
@@ -301,15 +311,37 @@ def setup_jurigged(config: Config):
301311 monkey_patch_jurigged_function_definition ()
302312 monkeypatch_group_definition ()
303313 if not config .option .daemon_do_not_autowatch_fixtures :
304- monkeypatch_fixture_marker (config .option .daemon_use_watchman )
314+ monkeypatch_fixture_marker (config .option .daemon_use_os_events )
305315 else :
306316 print ("Not autowatching fixtures" )
307317
308318 pattern = _get_pattern_filters (config )
309- # TODO: intelligently use poll versus watchman (https://github.com/JamesHutchison/pytest-hot-reloading/issues/16)
319+ # TODO: intelligently use poll (https://github.com/JamesHutchison/pytest-hot-reloading/issues/16)
320+
321+ poll_throttle = float (config .option .daemon_poll_throttle )
322+
323+ from watchdog .observers .polling import PollingObserverVFS
324+
325+ class NewPollingObserverVFS (PollingObserverVFS ):
326+ def __init__ (self , stat , listdir , polling_interval = 2 ) -> None :
327+ def lagged_listdir (* args , ** kwargs ):
328+ time .sleep (0.02 * poll_throttle ) # give CPU a break!
329+ return listdir (* args , ** kwargs )
330+
331+ super ().__init__ (stat , lagged_listdir , polling_interval * poll_throttle )
332+
333+ jurigged .live .PollingObserverVFS = NewPollingObserverVFS
334+
335+ poll : bool | float
336+
337+ if config .option .daemon_use_os_events :
338+ poll = False
339+ else :
340+ poll = 2 # seconds
341+
310342 jurigged .watch (
311343 pattern = pattern ,
312- poll = ( not config . option . daemon_use_watchman ) ,
344+ poll = poll ,
313345 )
314346
315347
@@ -323,7 +355,7 @@ def watch_file(path: Path | str) -> None:
323355seen_files : set [str ] = set ()
324356
325357
326- def monkeypatch_fixture_marker (use_watchman : bool ):
358+ def monkeypatch_fixture_marker (use_os_events : bool ):
327359 import pytest
328360 from _pytest import fixtures
329361
@@ -389,7 +421,8 @@ def _plugin_logic(config: Config) -> int:
389421 pytest_name = pytest_name ,
390422 start_daemon_if_needed = config .option .daemon_start_if_needed , # --daemon-start-if-needed
391423 do_not_autowatch_fixtures = config .option .daemon_do_not_autowatch_fixtures , # --daemon-do-not-autowatch-fixtures
392- use_watchman = config .option .daemon_use_watchman , # --daemon-use-watchman
424+ use_os_events = config .option .daemon_use_os_events , # --daemon-use-os-events
425+ poll_throttle = config .option .daemon_poll_throttle , # --daemon-poll-throttle
393426 additional_args = config .invocation_params .args ,
394427 )
395428
0 commit comments