Skip to content

Commit b887b84

Browse files
authored
fastapi: reload app after Gel config update (#854)
Use [the config hook](geldata/gel-cli#1726) to trigger a FastAPI app reload, so that the user could for example: 1. User updates `gel.local.toml` to modify the SMTP server port 2. `gel watch --sync` applies such config 3. Config hook runs to modify a signal file 4. `fastapi dev` is woken to reload the app
1 parent e6ae900 commit b887b84

File tree

2 files changed

+39
-16
lines changed

2 files changed

+39
-16
lines changed

gel/_internal/_integration/_fastapi/_cli/_lifespan.py

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
from __future__ import annotations
2-
32
from typing import TYPE_CHECKING
43

54

65
import contextlib
76
import functools
7+
import pathlib
88
import subprocess
9+
import sys
910
import tempfile
1011
import textwrap
1112
import threading
1213

1314

1415
if TYPE_CHECKING:
1516
import io
16-
import pathlib
1717
import types
1818
from collections.abc import Callable, Iterator
1919
from contextlib import AbstractContextManager
@@ -102,33 +102,47 @@ def __exit__(
102102

103103

104104
@contextlib.contextmanager
105-
def _gel_toml(app_path: pathlib.Path) -> Iterator[str]:
106-
output = app_path / "models"
107-
content = textwrap.dedent(f"""\
108-
[hooks-extend]
109-
schema.update.after="gel-generate-py models --output={output}"
105+
def _gel_toml(app_path: pathlib.Path) -> Iterator[tuple[str, str]]:
106+
with tempfile.TemporaryDirectory() as tmpdir:
107+
output = app_path / "models"
108+
path = pathlib.Path(tmpdir)
109+
reloader = str(path / "reload_fastapi.py")
110+
reload_code = f"""\
111+
with open("{reloader}", "w+t") as f: print(file=f)
112+
""".strip().replace('"', '\\"')
113+
content = textwrap.dedent(f"""\
114+
[hooks-extend]
115+
schema.update.after="gel-generate-py models --output={output}"
116+
config.update.after="{sys.executable} -c '{reload_code}'"
110117
""")
111118

112-
with tempfile.NamedTemporaryFile("w+t", encoding="utf8") as f:
113-
print(content, file=f, flush=True)
114-
yield f.name
119+
with (path / "gel.extend.toml").open("w+t", encoding="utf8") as f:
120+
print(content, file=f, flush=True)
121+
yield f.name, tmpdir
115122

116123

117124
@contextlib.contextmanager
118125
def fastapi_cli_lifespan(
119126
cli: rich_toolkit.RichToolkit,
120127
app_path: pathlib.Path,
121-
) -> Iterator[None]:
122-
with _gel_toml(app_path) as gel_toml:
123-
cmd = ["gel", "watch", "--migrate", "--extend-gel-toml", gel_toml]
128+
) -> Iterator[str]:
129+
with _gel_toml(app_path) as (gel_toml, watch_file):
130+
cmd = [
131+
"gel",
132+
"watch",
133+
"--migrate",
134+
"--sync",
135+
"--extend-gel-toml",
136+
gel_toml,
137+
]
124138
with SubprocessLogger(cmd, cwd=app_path, cli=cli):
125-
yield
139+
yield watch_file
126140

127141

128142
def fastapi_cli_lifespan_hook(
129143
app_name: str,
130144
app_path: pathlib.Path,
131145
cli: rich_toolkit.RichToolkit,
132-
) -> AbstractContextManager[None]:
146+
) -> AbstractContextManager[str]:
133147
cli.print(f"Watching Gel project in [blue]{app_path}[/blue]", tag="gel")
134148
return fastapi_cli_lifespan(cli, app_path)

gel/_internal/_integration/_fastapi/_cli/_patch.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import inspect
66
import importlib.util
7+
import pathlib
78
import types
89

910

@@ -46,7 +47,15 @@ def _patched_uvicorn_run(*args: Any, **kwargs: Any) -> None:
4647
cli = fastapi_cli_import_site.f_locals.get("toolkit")
4748
if import_data is not None and cli is not None:
4849
app_path = import_data.module_data.module_paths[-1].parent
49-
with _lifespan.fastapi_cli_lifespan(cli, app_path):
50+
with _lifespan.fastapi_cli_lifespan(cli, app_path) as reload_watch:
51+
dirs = kwargs.get("reload_dirs")
52+
if dirs is None:
53+
kwargs["reload_dirs"] = [pathlib.Path.cwd(), reload_watch]
54+
elif isinstance(dirs, str):
55+
kwargs["reload_dirs"] = [dirs, reload_watch]
56+
else:
57+
assert isinstance(dirs, list)
58+
kwargs["reload_dirs"].append(reload_watch)
5059
uvicorn.run(*args, **kwargs)
5160
else:
5261
uvicorn.run(*args, **kwargs)

0 commit comments

Comments
 (0)