From 6ae29729585ce4e6cb99f2fe708c2fd66dfdf74d Mon Sep 17 00:00:00 2001 From: Abdelhakim Qbaich Date: Sat, 6 Dec 2025 16:58:35 -0500 Subject: [PATCH 1/4] Removing the delay argument --- README.rst | 1 - sphinx_autobuild/__main__.py | 11 +++-------- sphinx_autobuild/utils.py | 4 +--- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 190b9a1..1c982c2 100644 --- a/README.rst +++ b/README.rst @@ -64,7 +64,6 @@ which can seen by running ``sphinx-autobuild --help``: --ignore IGNORE glob expression for files to ignore, when watching for changes --no-initial skip the initial build --open-browser open the browser after building documentation - --delay DELAY how long to wait before opening the browser --watch DIR additional directories to watch --pre-build COMMAND additional command(s) to run prior to building the documentation --post-build COMMAND additional command(s) to run after building the documentation diff --git a/sphinx_autobuild/__main__.py b/sphinx_autobuild/__main__.py index b1f058f..a2e92ad 100644 --- a/sphinx_autobuild/__main__.py +++ b/sphinx_autobuild/__main__.py @@ -80,6 +80,7 @@ def main(argv=()): ] ignore_dirs = list(filter(None, ignore_dirs)) ignore_handler = IgnoreFilter(ignore_dirs, args.re_ignore) + app = _create_app(watch_dirs, ignore_handler, builder, serve_dir, url_host) if not args.no_initial_build: @@ -87,7 +88,7 @@ def main(argv=()): builder(changed_paths=()) if args.open_browser: - open_browser(url_host, args.delay) + open_browser(url_host) show_message("Waiting to detect changes...") try: @@ -216,13 +217,7 @@ def _add_autobuild_arguments(parser): default=False, help="open the browser after building documentation", ) - group.add_argument( - "--delay", - dest="delay", - type=float, - default=5, - help="how long to wait before opening the browser", - ) + group.add_argument( "--watch", action="append", diff --git a/sphinx_autobuild/utils.py b/sphinx_autobuild/utils.py index 16e19fb..1598880 100644 --- a/sphinx_autobuild/utils.py +++ b/sphinx_autobuild/utils.py @@ -5,7 +5,6 @@ import shlex import socket import threading -import time import webbrowser from colorama import Fore, Style @@ -22,9 +21,8 @@ def find_free_port(): return s.getsockname()[1] -def open_browser(url_host: str, delay: float) -> None: +def open_browser(url_host: str) -> None: def _opener(): - time.sleep(delay) webbrowser.open(f"http://{url_host}") t = threading.Thread(target=_opener) From 489beb8523bce688c51549c41ee5144883ae1f0e Mon Sep 17 00:00:00 2001 From: Abdelhakim Qbaich Date: Sat, 6 Dec 2025 17:03:41 -0500 Subject: [PATCH 2/4] Drop the threading usage for subprocess call --- sphinx_autobuild/utils.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sphinx_autobuild/utils.py b/sphinx_autobuild/utils.py index 1598880..5d97a0e 100644 --- a/sphinx_autobuild/utils.py +++ b/sphinx_autobuild/utils.py @@ -4,7 +4,6 @@ import shlex import socket -import threading import webbrowser from colorama import Fore, Style @@ -22,12 +21,7 @@ def find_free_port(): def open_browser(url_host: str) -> None: - def _opener(): - webbrowser.open(f"http://{url_host}") - - t = threading.Thread(target=_opener) - t.start() - t.join() + webbrowser.open(f"http://{url_host}") def _log(text, *, colour): From 18a85f5d608d66f05365eda6124d7afb5e583179 Mon Sep 17 00:00:00 2001 From: Abdelhakim Qbaich Date: Sat, 6 Dec 2025 16:58:05 -0500 Subject: [PATCH 3/4] Open browser after uvicorn is ready --- sphinx_autobuild/__main__.py | 24 +++++++++++++++++------- sphinx_autobuild/utils.py | 5 ----- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/sphinx_autobuild/__main__.py b/sphinx_autobuild/__main__.py index a2e92ad..88ce9a3 100644 --- a/sphinx_autobuild/__main__.py +++ b/sphinx_autobuild/__main__.py @@ -5,6 +5,8 @@ import argparse import shlex import sys +import webbrowser +from contextlib import asynccontextmanager from pathlib import Path import colorama @@ -22,7 +24,7 @@ from sphinx_autobuild.filter import IgnoreFilter from sphinx_autobuild.middleware import JavascriptInjectorMiddleware from sphinx_autobuild.server import RebuildServer -from sphinx_autobuild.utils import find_free_port, open_browser, show_message +from sphinx_autobuild.utils import find_free_port, show_message def main(argv=()): @@ -81,15 +83,14 @@ def main(argv=()): ignore_dirs = list(filter(None, ignore_dirs)) ignore_handler = IgnoreFilter(ignore_dirs, args.re_ignore) - app = _create_app(watch_dirs, ignore_handler, builder, serve_dir, url_host) + app = _create_app( + watch_dirs, ignore_handler, builder, serve_dir, url_host, args.open_browser + ) if not args.no_initial_build: show_message("Starting initial build") builder(changed_paths=()) - if args.open_browser: - open_browser(url_host) - show_message("Waiting to detect changes...") try: uvicorn.run(app, host=host_name, port=port_num, log_level="warning") @@ -97,16 +98,25 @@ def main(argv=()): show_message("Server ceasing operations. Cheerio!") -def _create_app(watch_dirs, ignore_handler, builder, out_dir, url_host): +def _create_app( + watch_dirs, ignore_handler, builder, out_dir, url_host, open_browser=False +): watcher = RebuildServer(watch_dirs, ignore_handler, change_callback=builder) + @asynccontextmanager + async def lifespan(app): + async with watcher.lifespan(app): + if open_browser: + webbrowser.open(f"http://{url_host}") + yield + return Starlette( routes=[ WebSocketRoute("/websocket-reload", watcher, name="reload"), Mount("/", app=StaticFiles(directory=out_dir, html=True), name="static"), ], middleware=[Middleware(JavascriptInjectorMiddleware, ws_url=url_host)], - lifespan=watcher.lifespan, + lifespan=lifespan, ) diff --git a/sphinx_autobuild/utils.py b/sphinx_autobuild/utils.py index 5d97a0e..e3fc9ed 100644 --- a/sphinx_autobuild/utils.py +++ b/sphinx_autobuild/utils.py @@ -4,7 +4,6 @@ import shlex import socket -import webbrowser from colorama import Fore, Style @@ -20,10 +19,6 @@ def find_free_port(): return s.getsockname()[1] -def open_browser(url_host: str) -> None: - webbrowser.open(f"http://{url_host}") - - def _log(text, *, colour): print(f"{Fore.GREEN}[sphinx-autobuild] {colour}{text}{Style.RESET_ALL}") From 6ce336e8538387a95937c67ca42b4a0de3ee95c3 Mon Sep 17 00:00:00 2001 From: Abdelhakim Qbaich Date: Sat, 6 Dec 2025 18:08:36 -0500 Subject: [PATCH 4/4] Fixing other type hint issues --- sphinx_autobuild/server.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sphinx_autobuild/server.py b/sphinx_autobuild/server.py index c32b3f2..55ad079 100644 --- a/sphinx_autobuild/server.py +++ b/sphinx_autobuild/server.py @@ -2,7 +2,7 @@ import asyncio from concurrent.futures import ProcessPoolExecutor -from contextlib import AbstractAsyncContextManager, asynccontextmanager +from contextlib import asynccontextmanager from pathlib import Path from typing import TYPE_CHECKING @@ -10,20 +10,24 @@ from starlette.websockets import WebSocket if TYPE_CHECKING: - import os - from collections.abc import Callable, Sequence + from collections.abc import AsyncGenerator, Sequence + from os import PathLike + from typing import Protocol from starlette.types import Receive, Scope, Send from sphinx_autobuild.filter import IgnoreFilter + class ChangeCallback(Protocol): + def __call__(self, *, changed_paths: Sequence[Path]) -> None: ... + class RebuildServer: def __init__( self, - paths: list[os.PathLike[str]], + paths: list[PathLike[str]], ignore_filter: IgnoreFilter, - change_callback: Callable[[Sequence[Path]], None], + change_callback: ChangeCallback, ) -> None: self.paths = [Path(path).resolve(strict=True) for path in paths] self.ignore = ignore_filter @@ -32,7 +36,7 @@ def __init__( self.should_exit = asyncio.Event() @asynccontextmanager - async def lifespan(self, _app) -> AbstractAsyncContextManager[None]: + async def lifespan(self, _app) -> AsyncGenerator[None]: task = asyncio.create_task(self.main()) yield self.should_exit.set()