feat: support focused dev reload scopes
This commit is contained in:
@@ -37,6 +37,7 @@ class DevserverState:
|
||||
config: GovoplanServerConfig
|
||||
registry: PlatformRegistry
|
||||
reload_dirs: list[str]
|
||||
reload_module_ids: tuple[str, ...] | None
|
||||
|
||||
|
||||
def _config_module_runtime_root(config_path: str | None) -> Path | None:
|
||||
@@ -237,20 +238,44 @@ def build_reload_dirs(
|
||||
config_path: str | None = None,
|
||||
registry: PlatformRegistry | None = None,
|
||||
extra_dirs: Sequence[str] = (),
|
||||
module_ids: Sequence[str] | None = None,
|
||||
) -> list[str]:
|
||||
active_registry = registry or build_platform_registry(config.enabled_modules, manifest_factories=config.manifest_factories)
|
||||
manifests = active_registry.manifests()
|
||||
enabled_module_ids = {manifest.id for manifest in manifests}
|
||||
selected_module_ids = (
|
||||
enabled_module_ids
|
||||
if module_ids is None
|
||||
else {module_id.strip() for module_id in module_ids if module_id.strip()}
|
||||
)
|
||||
unknown_module_ids = selected_module_ids - enabled_module_ids
|
||||
if unknown_module_ids:
|
||||
raise SystemExit(
|
||||
"Reload modules are not enabled: "
|
||||
+ ", ".join(sorted(unknown_module_ids))
|
||||
)
|
||||
|
||||
roots: list[Path | str] = []
|
||||
roots.extend(_config_source_roots(config_path))
|
||||
|
||||
for factory in config.manifest_factories:
|
||||
try:
|
||||
manifest = factory()
|
||||
except TypeError:
|
||||
manifest = None
|
||||
if (
|
||||
module_ids is not None
|
||||
and isinstance(manifest, ModuleManifest)
|
||||
and manifest.id not in selected_module_ids
|
||||
):
|
||||
continue
|
||||
roots.extend(_source_roots_for_object(factory))
|
||||
|
||||
roots.extend(_entry_point_source_roots(enabled_module_ids))
|
||||
roots.extend(_entry_point_source_roots(selected_module_ids))
|
||||
|
||||
for manifest in manifests:
|
||||
if manifest.id not in selected_module_ids:
|
||||
continue
|
||||
roots.extend(_manifest_source_roots(manifest))
|
||||
|
||||
roots.extend(extra_dirs)
|
||||
@@ -265,11 +290,32 @@ def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
||||
parser.add_argument("--port", type=int, default=8000, help="Port to bind. Default: 8000.")
|
||||
parser.add_argument("--no-reload", action="store_true", help="Disable uvicorn reload.")
|
||||
parser.add_argument("--reload-dir", action="append", default=[], help="Additional directory to watch. May be passed multiple times.")
|
||||
reload_scope = parser.add_mutually_exclusive_group()
|
||||
reload_scope.add_argument(
|
||||
"--reload-module",
|
||||
action="append",
|
||||
default=None,
|
||||
metavar="MODULE_ID",
|
||||
help=(
|
||||
"Watch only this enabled module in addition to core/config sources. "
|
||||
"May be passed multiple times."
|
||||
),
|
||||
)
|
||||
reload_scope.add_argument(
|
||||
"--reload-core-only",
|
||||
action="store_true",
|
||||
help="Watch core/config sources but no optional module source trees.",
|
||||
)
|
||||
parser.add_argument("--smoke", action="store_true", help="Prepare runtime paths, run app startup, print effective paths, and exit without uvicorn.")
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def prepare_devserver(config_path: str | None, *, extra_reload_dirs: Sequence[str] = ()) -> DevserverState:
|
||||
def prepare_devserver(
|
||||
config_path: str | None,
|
||||
*,
|
||||
extra_reload_dirs: Sequence[str] = (),
|
||||
reload_module_ids: Sequence[str] | None = None,
|
||||
) -> DevserverState:
|
||||
runtime_root = apply_runtime_defaults(config_path)
|
||||
database_url = os.getenv("DATABASE_URL", "")
|
||||
validate_sqlite_database_url(database_url)
|
||||
@@ -291,7 +337,13 @@ def prepare_devserver(config_path: str | None, *, extra_reload_dirs: Sequence[st
|
||||
)
|
||||
enabled_modules = load_startup_enabled_modules(config.enabled_modules, available=available_modules)
|
||||
registry = build_platform_registry(enabled_modules, manifest_factories=config.manifest_factories)
|
||||
reload_dirs = build_reload_dirs(config, config_path=config_path, registry=registry, extra_dirs=extra_reload_dirs)
|
||||
reload_dirs = build_reload_dirs(
|
||||
config,
|
||||
config_path=config_path,
|
||||
registry=registry,
|
||||
extra_dirs=extra_reload_dirs,
|
||||
module_ids=reload_module_ids,
|
||||
)
|
||||
return DevserverState(
|
||||
config_path=config_path,
|
||||
runtime_root=runtime_root,
|
||||
@@ -300,6 +352,11 @@ def prepare_devserver(config_path: str | None, *, extra_reload_dirs: Sequence[st
|
||||
config=config,
|
||||
registry=registry,
|
||||
reload_dirs=reload_dirs,
|
||||
reload_module_ids=(
|
||||
None
|
||||
if reload_module_ids is None
|
||||
else tuple(sorted(set(reload_module_ids)))
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -320,6 +377,15 @@ def print_devserver_summary(state: DevserverState, *, app: str, no_reload: bool)
|
||||
if no_reload:
|
||||
print("Reload: disabled")
|
||||
else:
|
||||
if state.reload_module_ids is None:
|
||||
print("Reload scope: core/config plus all enabled modules")
|
||||
elif state.reload_module_ids:
|
||||
print(
|
||||
"Reload scope: core/config plus "
|
||||
+ ", ".join(state.reload_module_ids)
|
||||
)
|
||||
else:
|
||||
print("Reload scope: core/config only")
|
||||
print("Reload dirs:")
|
||||
for directory in state.reload_dirs:
|
||||
print(f" - {directory}")
|
||||
@@ -353,7 +419,14 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||
os.environ["GOVOPLAN_SERVER_CONFIG"] = args.config
|
||||
|
||||
config_path = args.config or os.getenv("GOVOPLAN_SERVER_CONFIG")
|
||||
state = prepare_devserver(config_path, extra_reload_dirs=args.reload_dir)
|
||||
reload_module_ids: Sequence[str] | None = args.reload_module
|
||||
if args.reload_core_only:
|
||||
reload_module_ids = ()
|
||||
state = prepare_devserver(
|
||||
config_path,
|
||||
extra_reload_dirs=args.reload_dir,
|
||||
reload_module_ids=reload_module_ids,
|
||||
)
|
||||
print_devserver_summary(state, app=args.app, no_reload=args.no_reload)
|
||||
|
||||
if args.smoke:
|
||||
|
||||
Reference in New Issue
Block a user