Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 19 additions & 14 deletions sphinx_autobuild/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import argparse
import shlex
import sys
import webbrowser
from contextlib import asynccontextmanager
from pathlib import Path

import colorama
Expand All @@ -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=()):
Expand Down Expand Up @@ -80,32 +82,41 @@ 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, args.delay)

show_message("Waiting to detect changes...")
try:
uvicorn.run(app, host=host_name, port=port_num, log_level="warning")
except KeyboardInterrupt:
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,
)


Expand Down Expand Up @@ -216,13 +227,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",
Expand Down
16 changes: 10 additions & 6 deletions sphinx_autobuild/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,32 @@

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

import watchfiles
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
Expand All @@ -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()
Expand Down
13 changes: 0 additions & 13 deletions sphinx_autobuild/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

import shlex
import socket
import threading
import time
import webbrowser

from colorama import Fore, Style

Expand All @@ -22,16 +19,6 @@ def find_free_port():
return s.getsockname()[1]


def open_browser(url_host: str, delay: float) -> None:
def _opener():
time.sleep(delay)
webbrowser.open(f"http://{url_host}")

t = threading.Thread(target=_opener)
t.start()
t.join()


def _log(text, *, colour):
print(f"{Fore.GREEN}[sphinx-autobuild] {colour}{text}{Style.RESET_ALL}")

Expand Down