Skip to content

Commit

Permalink
Add reload option (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
gi0baro committed Mar 13, 2023
1 parent 46bf15e commit 195e945
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 19 deletions.
8 changes: 7 additions & 1 deletion granian/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ def main(
None,
help="URL path prefix the app is mounted on"
),
reload: bool = typer.Option(
False,
"--reload/--no-reload",
help="Enable auto reload on application's files changes"
),
_: Optional[bool] = typer.Option(
None,
"--version",
Expand All @@ -98,5 +103,6 @@ def main(
log_level=log_level,
ssl_cert=ssl_certificate,
ssl_key=ssl_keyfile,
url_path_prefix=url_path_prefix
url_path_prefix=url_path_prefix,
reload=reload
).serve()
67 changes: 49 additions & 18 deletions granian/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from pathlib import Path
from typing import List, Optional

import watchfiles

from ._granian import ASGIWorker, RSGIWorker, WSGIWorker
from ._internal import load_target
from .asgi import LifespanProtocol, _callback_wrapper as _asgi_call_wrap
Expand Down Expand Up @@ -42,7 +44,8 @@ def __init__(
log_level: LogLevels = LogLevels.info,
ssl_cert: Optional[Path] = None,
ssl_key: Optional[Path] = None,
url_path_prefix: Optional[str] = None
url_path_prefix: Optional[str] = None,
reload: bool = False
):
self.target = target
self.bind_addr = address
Expand All @@ -59,6 +62,7 @@ def __init__(
self.http1_buffer_size = http1_buffer_size
self.log_level = log_level
self.url_path_prefix = url_path_prefix
self.reload_on_changes = reload
configure_logging(self.log_level)
self.build_ssl_context(ssl_cert, ssl_key)
self._shd = None
Expand Down Expand Up @@ -276,17 +280,7 @@ def _spawn_proc(
)
)

def startup(self, spawn_target, target_loader):
logger.info("Starting granian")

for sig in self.SIGNALS:
signal.signal(sig, self.signal_handler)

self._init_shared_socket()
sock = socket.socket(fileno=self._sfd)
sock.set_inheritable(True)
logger.info(f"Listening at: {self.bind_addr}:{self.bind_port}")

def _spawn_workers(self, sock, spawn_target, target_loader):
def socket_loader():
return sock

Expand All @@ -299,15 +293,50 @@ def socket_loader():
)
proc.start()
self.procs.append(proc)
logger.info(f"Booting worker-{idx + 1} with pid: {proc.pid}")
logger.info(f"Spawning worker-{idx + 1} with pid: {proc.pid}")

def shutdown(self):
logger.info("Shutting down granian")
def _stop_workers(self):
for proc in self.procs:
proc.terminate()
for proc in self.procs:
proc.join()

def startup(self, spawn_target, target_loader):
logger.info("Starting granian")

for sig in self.SIGNALS:
signal.signal(sig, self.signal_handler)

self._init_shared_socket()
sock = socket.socket(fileno=self._sfd)
sock.set_inheritable(True)
logger.info(f"Listening at: {self.bind_addr}:{self.bind_port}")

self._spawn_workers(sock, spawn_target, target_loader)
return sock

def shutdown(self):
logger.info("Shutting down granian")
self._stop_workers()

def _serve(self, spawn_target, target_loader):
self.startup(spawn_target, target_loader)
self.exit_event.wait()
self.shutdown()

def _serve_with_reloader(self, spawn_target, target_loader):
reload_path = Path.cwd()
sock = self.startup(spawn_target, target_loader)

try:
for _ in watchfiles.watch(reload_path, stop_event=self.exit_event):
self._stop_workers()
self._spawn_workers(sock, spawn_target, target_loader)
except StopIteration:
pass

self.shutdown()

def serve(self, spawn_target = None, target_loader = None):
default_spawners = {
Interfaces.ASGI: self._spawn_asgi_worker,
Expand All @@ -317,6 +346,8 @@ def serve(self, spawn_target = None, target_loader = None):
target_loader = target_loader or load_target
spawn_target = spawn_target or default_spawners[self.interface]

self.startup(spawn_target, partial(target_loader, self.target))
self.exit_event.wait()
self.shutdown()
serve_method = (
self._serve_with_reloader if self.reload_on_changes else
self._serve
)
serve_method(spawn_target, partial(target_loader, self.target))
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dynamic = [

requires-python = ">=3.7"
dependencies = [
"watchfiles~=0.18",
"typer~=0.4",
"uvloop~=0.17.0; sys_platform != 'win32' and platform_python_implementation == 'CPython'"
]
Expand Down

0 comments on commit 195e945

Please sign in to comment.