pz-cli 5.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pz_cli-5.0.0.dist-info/METADATA +51 -0
- pz_cli-5.0.0.dist-info/RECORD +399 -0
- pz_cli-5.0.0.dist-info/WHEEL +4 -0
- pz_cli-5.0.0.dist-info/entry_points.txt +5 -0
- pz_core/__init__.py +64 -0
- pz_core/__main__.py +7 -0
- pz_core/app.py +45 -0
- pz_core/assembly/__init__.py +17 -0
- pz_core/assembly/app_discovery.py +1240 -0
- pz_core/assembly/apps/__init__.py +25 -0
- pz_core/assembly/apps/compiler.py +824 -0
- pz_core/assembly/apps/providers.py +188 -0
- pz_core/assembly/cli/__init__.py +9 -0
- pz_core/assembly/cli/compiler.py +166 -0
- pz_core/assembly/diagnostics/__init__.py +21 -0
- pz_core/assembly/diagnostics/checks.py +257 -0
- pz_core/assembly/diagnostics/dev_sources.py +569 -0
- pz_core/assembly/events/__init__.py +5 -0
- pz_core/assembly/events/compiler.py +219 -0
- pz_core/assembly/extensions/__init__.py +5 -0
- pz_core/assembly/extensions/compiler.py +696 -0
- pz_core/assembly/infra/__init__.py +6 -0
- pz_core/assembly/infra/compiler.py +272 -0
- pz_core/assembly/lifecycle/__init__.py +5 -0
- pz_core/assembly/lifecycle/compiler.py +95 -0
- pz_core/assembly/middlewares/__init__.py +19 -0
- pz_core/assembly/middlewares/compiler.py +508 -0
- pz_core/assembly/project/__init__.py +7 -0
- pz_core/assembly/project/compiler.py +9 -0
- pz_core/assembly/project/context.py +17 -0
- pz_core/assembly/project/discovery.py +227 -0
- pz_core/assembly/resources/__init__.py +19 -0
- pz_core/assembly/resources/compiler.py +310 -0
- pz_core/assembly/resources/settings.py +129 -0
- pz_core/assembly/routes/__init__.py +7 -0
- pz_core/assembly/routes/compiler.py +400 -0
- pz_core/assembly/runtime_manifests/__init__.py +13 -0
- pz_core/assembly/runtime_manifests/compiler.py +159 -0
- pz_core/assembly/settings/__init__.py +17 -0
- pz_core/assembly/settings/compiler.py +786 -0
- pz_core/assembly/settings/host_project_overrides.py +82 -0
- pz_core/assembly/settings/host_runtime_endpoint.py +207 -0
- pz_core/authoring/__init__.py +79 -0
- pz_core/authoring/app_init_service.py +946 -0
- pz_core/authoring/app_versions.py +814 -0
- pz_core/authoring/cli.py +192 -0
- pz_core/authoring/commands.py +1810 -0
- pz_core/authoring/conflicts.py +180 -0
- pz_core/authoring/dev_bindings.py +916 -0
- pz_core/authoring/dev_git.py +1087 -0
- pz_core/authoring/dev_import.py +1061 -0
- pz_core/authoring/dev_manifest.py +522 -0
- pz_core/authoring/dev_sources.py +1638 -0
- pz_core/authoring/env_cli.py +454 -0
- pz_core/authoring/files.py +109 -0
- pz_core/authoring/ide_projection.py +1129 -0
- pz_core/authoring/naming.py +58 -0
- pz_core/authoring/official_dev_sources.py +432 -0
- pz_core/authoring/official_sources.json +156 -0
- pz_core/authoring/orchestration_service.py +558 -0
- pz_core/authoring/project_default.jsonc +53 -0
- pz_core/authoring/project_defaults.py +88 -0
- pz_core/authoring/project_metadata_service.py +199 -0
- pz_core/authoring/project_settings_checks.py +51 -0
- pz_core/authoring/project_settings_cli.py +299 -0
- pz_core/authoring/project_settings_service.py +1700 -0
- pz_core/authoring/project_state.py +399 -0
- pz_core/authoring/publish_git.py +423 -0
- pz_core/authoring/publish_lock.py +188 -0
- pz_core/authoring/publish_source.py +195 -0
- pz_core/authoring/release.py +1382 -0
- pz_core/authoring/repo_keys.py +29 -0
- pz_core/authoring/root_cli.py +28 -0
- pz_core/authoring/scaffold_service.py +325 -0
- pz_core/authoring/sync_service.py +787 -0
- pz_core/authoring/templates/app/__init__.py +1 -0
- pz_core/authoring/templates/app/api.py +25 -0
- pz_core/authoring/templates/app/apps.py +31 -0
- pz_core/authoring/templates/app/auth_package_init.py +14 -0
- pz_core/authoring/templates/app/auth_readme.py +29 -0
- pz_core/authoring/templates/app/configs_package_init.py +14 -0
- pz_core/authoring/templates/app/default_env.py +18 -0
- pz_core/authoring/templates/app/default_jsonc.py +25 -0
- pz_core/authoring/templates/app/locales_readme.py +25 -0
- pz_core/authoring/templates/app/migrations_readme.py +19 -0
- pz_core/authoring/templates/app/models.py +14 -0
- pz_core/authoring/templates/app/package_init.py +8 -0
- pz_core/authoring/templates/app/pages_base.py +161 -0
- pz_core/authoring/templates/app/pages_readme.py +25 -0
- pz_core/authoring/templates/app/public.py +31 -0
- pz_core/authoring/templates/app/pyproject.py +77 -0
- pz_core/authoring/templates/app/readme.py +74 -0
- pz_core/authoring/templates/app/settings.py +46 -0
- pz_core/authoring/templates/app/static_app_js.py +24 -0
- pz_core/authoring/templates/app/static_readme.py +31 -0
- pz_core/authoring/templates/app/template_config_page.py +23 -0
- pz_core/authoring/templates/app/template_index_html.py +28 -0
- pz_core/authoring/templates/app/templates_readme.py +32 -0
- pz_core/authoring/templates/app/urls.py +36 -0
- pz_core/authoring/templates/app/views.py +36 -0
- pz_core/authoring/templates/project/__init__.py +1 -0
- pz_core/authoring/templates/project/agents.py +51 -0
- pz_core/authoring/templates/project/apps_container_readme.py +37 -0
- pz_core/authoring/templates/project/main_py.py +59 -0
- pz_core/authoring/templates/project/project_env.py +19 -0
- pz_core/authoring/templates/project/pyproject.py +26 -0
- pz_core/authoring/templates/project/pz_env_env.py +65 -0
- pz_core/authoring/templates/project/pz_env_jsonc.py +69 -0
- pz_core/authoring/templates/project/pz_jsonc.py +12 -0
- pz_core/authoring/templates/project/readme.py +94 -0
- pz_core/authoring/templates/project/root_gitkeep.py +8 -0
- pz_core/authoring/templates/shared/__init__.py +5 -0
- pz_core/authoring/templates/shared/text.py +10 -0
- pz_core/authoring/venv_service.py +87 -0
- pz_core/authoring/workspace.py +167 -0
- pz_core/cli.py +291 -0
- pz_core/cli_options.py +74 -0
- pz_core/core/__init__.py +3 -0
- pz_core/core/errors/__init__.py +5 -0
- pz_core/core/errors/models.py +117 -0
- pz_core/core/events/__init__.py +5 -0
- pz_core/core/events/registry.py +54 -0
- pz_core/core/extensions/__init__.py +51 -0
- pz_core/core/extensions/registry.py +790 -0
- pz_core/core/health/__init__.py +6 -0
- pz_core/core/health/contracts.py +10 -0
- pz_core/core/middlewares/__init__.py +6 -0
- pz_core/core/middlewares/models.py +24 -0
- pz_core/core/routing/__init__.py +5 -0
- pz_core/core/routing/models.py +21 -0
- pz_core/core/settings_contracts.py +25 -0
- pz_core/docs/README.md +20 -0
- pz_core/docs/distribution-README.md +438 -0
- pz_core/docs/pz/345/221/275/344/273/244/346/200/273/350/247/210.md +1011 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/00-pz/346/236/266/346/236/204/346/200/273/347/272/262.md +240 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/350/247/204/350/214/203.md +216 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203.md +403 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/03-App/346/234/200/345/260/217/345/255/220/345/214/205/350/247/204/350/214/203.md +571 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/04-Settings/344/270/216/351/205/215/347/275/256/350/247/204/350/214/203.md +1177 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/05-/350/265/204/346/272/220/344/270/216/350/277/220/350/241/214/347/233/256/345/275/225/350/247/204/350/214/203.md +575 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/06-/346/225/260/346/215/256/345/272/223/344/270/216/345/217/257/351/200/211/345/237/272/347/241/200/350/256/276/346/226/275/350/247/204/350/214/203.md +499 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/07-App/346/233/277/346/215/242/350/247/204/350/214/203.md +206 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/08-/345/271/263/345/217/260/347/272/247App/344/270/216/350/277/220/350/241/214/346/227/266/345/215/225/344/276/213/350/247/204/350/214/203.md +445 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/09-CLI/344/270/216Worker/345/205/245/345/217/243/350/247/204/350/214/203.md +950 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/10-/346/265/213/350/257/225/344/270/216/345/245/221/347/272/246/350/247/204/350/214/203.md +1001 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/11-/344/273/223/345/272/223/347/234/237/347/233/270/346/272/220/345/234/260/345/233/276.md +102 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/12-/346/236/204/345/273/272/344/270/216/345/217/221/345/270/203/346/243/200/346/237/245/346/270/205/345/215/225.md +171 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/13-/346/255/243/345/274/217Facade/346/200/273/350/241/250.md +402 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/14-/345/210/206/345/261/202/344/270/216/346/211/251/345/261/225/350/247/204/350/214/203.md +550 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/15-/345/255/220/345/214/205/345/274/200/345/217/221/344/275/277/347/224/250/346/211/213/345/206/214.md +1004 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/16-/346/211/251/345/261/225/347/202/271/346/255/243/345/274/217/345/215/217/350/256/256/346/270/205/345/215/225.md +352 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/17-pz.protocols.v1/346/255/243/345/274/217/345/215/217/350/256/256.md +266 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/18-/346/211/251/345/261/225/347/202/271/345/206/262/347/252/201/350/243/201/345/206/263/346/200/273/350/241/250.md +131 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/19-/351/224/231/350/257/257/347/240/201/344/270/216/350/257/212/346/226/255/345/215/217/350/256/256.md +176 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/20-/347/247/201/346/234/211/345/214/205/344/273/223/344/270/216/347/273/237/344/270/200/350/256/244/350/257/201/351/203/250/347/275/262/350/247/204/350/214/203.md +243 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/21-contrib/347/272/246/345/256/232/344/270/216Provider/346/216/245/345/205/245/350/247/204/350/214/203.md +365 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/22-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203.md +912 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/23-/351/246/226/345/261/217/345/261/225/347/244/272/344/270/216/351/246/226/345/270/247/347/250/263/345/256/232/350/247/204/350/214/203.md +269 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/24-pz/345/220/257/345/212/250/344/270/216/350/277/220/350/241/214/346/227/266/347/274/226/350/257/221/346/265/201/347/250/213.md +245 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/25-/351/235/242/345/220/221/345/257/271/350/261/241/345/274/200/345/217/221/350/247/204/350/214/203.md +300 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/README.md +98 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/00-legacy_core/346/240/207/345/207/206pip/345/214/205/345/214/226/351/207/215/346/236/204/346/200/273/347/272/262.md +1073 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/351/207/215/346/236/204/346/211/247/350/241/214/346/226/271/346/241/210.md +224 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203-/345/220/253/345/216/206/345/217/262/345/217/243/345/276/204.md +671 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/13-/344/270/200/346/254/241/346/200/247/345/256/236/346/226/275/346/270/205/345/215/225.md +387 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/14-/345/255/220/345/214/205/351/200/202/351/205/215/346/224/271/351/200/240/346/270/205/345/215/225.md +592 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/15-infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/346/270/205/345/215/225.md +81 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/19-core/345/215/225/344/270/200/346/255/243/345/274/217/346/226/271/346/241/210/346/224/266/346/225/233/346/211/247/350/241/214/346/270/205/345/215/225.md +161 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/25-/345/215/217/350/256/256/345/210/260/346/272/220/347/240/201/345/267/256/350/267/235/346/270/205/345/215/225.md +377 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/26-cutover/345/256/214/345/267/245/346/243/200/346/237/245/346/270/205/345/215/225.md +117 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/29-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203-/345/220/253/350/277/201/347/247/273/347/255/226/347/225/245.md +956 -0
- pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/99-/350/265/204/346/272/220/346/224/266/345/217/243/345/220/216/347/273/255TODO.md +84 -0
- pz_core/docs//345/217/221/347/211/210/345/211/215/346/270/205/345/215/225-4.0.11.md +84 -0
- pz_core/docs//346/226/260/346/211/213/346/214/207/345/215/227.md +422 -0
- pz_core/infra/__init__.py +17 -0
- pz_core/infra/db/__init__.py +23 -0
- pz_core/infra/db/contracts.py +73 -0
- pz_core/infra/health/__init__.py +5 -0
- pz_core/infra/health/contracts.py +8 -0
- pz_core/infra/kv/__init__.py +5 -0
- pz_core/infra/kv/contracts.py +17 -0
- pz_core/infra/observability/__init__.py +23 -0
- pz_core/infra/observability/contracts.py +142 -0
- pz_core/infra/resilience/__init__.py +5 -0
- pz_core/infra/resilience/contracts.py +17 -0
- pz_core/infra/resources/__init__.py +5 -0
- pz_core/infra/resources/contracts.py +16 -0
- pz_core/infra/settings/__init__.py +17 -0
- pz_core/infra/settings/models.py +81 -0
- pz_core/infra/storage/__init__.py +5 -0
- pz_core/infra/storage/ports.py +27 -0
- pz_core/project_root_commands.py +282 -0
- pz_core/project_runtime_cli.py +344 -0
- pz_core/project_sources.py +253 -0
- pz_core/pz_launcher.py +209 -0
- pz_core/root_cli.py +70 -0
- pz_core/runtime/__init__.py +229 -0
- pz_core/runtime/access.py +86 -0
- pz_core/runtime/adapters/__init__.py +5 -0
- pz_core/runtime/adapters/base.py +18 -0
- pz_core/runtime/asyncio_bridge.py +18 -0
- pz_core/runtime/bindings/__init__.py +5 -0
- pz_core/runtime/bindings/container.py +50 -0
- pz_core/runtime/cli/__init__.py +5 -0
- pz_core/runtime/cli/_args.py +67 -0
- pz_core/runtime/cli/app_commands.py +417 -0
- pz_core/runtime/cli/check.py +214 -0
- pz_core/runtime/cli/db.py +297 -0
- pz_core/runtime/cli/main.py +89 -0
- pz_core/runtime/cli/resources.py +280 -0
- pz_core/runtime/discovery.py +23 -0
- pz_core/runtime/environment_integrity.py +157 -0
- pz_core/runtime/errors.py +5 -0
- pz_core/runtime/events.py +87 -0
- pz_core/runtime/facades/__init__.py +38 -0
- pz_core/runtime/facades/_capabilities.py +46 -0
- pz_core/runtime/facades/cache.py +103 -0
- pz_core/runtime/facades/database.py +71 -0
- pz_core/runtime/facades/events.py +380 -0
- pz_core/runtime/facades/health.py +101 -0
- pz_core/runtime/facades/mail.py +121 -0
- pz_core/runtime/facades/observability.py +102 -0
- pz_core/runtime/facades/providers.py +75 -0
- pz_core/runtime/facades/resources.py +52 -0
- pz_core/runtime/facades/storage.py +296 -0
- pz_core/runtime/health/__init__.py +5 -0
- pz_core/runtime/health/service.py +102 -0
- pz_core/runtime/host.py +188 -0
- pz_core/runtime/host_runner.py +282 -0
- pz_core/runtime/http/__init__.py +20 -0
- pz_core/runtime/http/app.py +576 -0
- pz_core/runtime/http_timing.py +173 -0
- pz_core/runtime/invocation.py +29 -0
- pz_core/runtime/json_safe.py +47 -0
- pz_core/runtime/lifecycle/__init__.py +25 -0
- pz_core/runtime/lifecycle/manager.py +533 -0
- pz_core/runtime/plan.py +214 -0
- pz_core/runtime/plan_context.py +33 -0
- pz_core/runtime/plan_snapshot.py +177 -0
- pz_core/runtime/registries/__init__.py +27 -0
- pz_core/runtime/registries/capabilities.py +101 -0
- pz_core/runtime/registries/providers.py +396 -0
- pz_core/runtime/resource_assets.py +344 -0
- pz_core/runtime/run_cli.py +211 -0
- pz_core/runtime/settings.py +888 -0
- pz_core/runtime/worker/__init__.py +21 -0
- pz_core/runtime/worker/engine.py +921 -0
- pz_core/runtime/worker/runtime.py +25 -0
- pz_core/testing/__init__.py +15 -0
- pz_core/testing/doc_route_contracts.py +305 -0
- pz_core/utils/__init__.py +44 -0
- pz_core/utils/env.py +65 -0
- pz_core/utils/project_jsonc/__init__.py +29 -0
- pz_core/utils/project_jsonc/document.py +420 -0
- pz_core/utils/project_settings.py +99 -0
- pz_foundation/__init__.py +50 -0
- pz_foundation/_contracts.py +81 -0
- pz_foundation/_errors.py +34 -0
- pz_foundation/api.py +14 -0
- pz_foundation/apps.py +127 -0
- pz_foundation/auth/__init__.py +1 -0
- pz_foundation/cache_control.py +233 -0
- pz_foundation/cli/__init__.py +2 -0
- pz_foundation/cli/workers.py +565 -0
- pz_foundation/configs/__init__.py +0 -0
- pz_foundation/configs/default.env +15 -0
- pz_foundation/configs/default.jsonc +452 -0
- pz_foundation/db/__init__.py +94 -0
- pz_foundation/db/aerich_executor.py +664 -0
- pz_foundation/db/config.py +358 -0
- pz_foundation/db/connection_manager.py +689 -0
- pz_foundation/db/connection_probe.py +204 -0
- pz_foundation/db/database.py +1291 -0
- pz_foundation/db/deps.py +47 -0
- pz_foundation/db/lifecycle_status.py +85 -0
- pz_foundation/db/migrations.py +477 -0
- pz_foundation/db/mysql.py +57 -0
- pz_foundation/db/postgresql.py +409 -0
- pz_foundation/db/production_pool_fallback.py +143 -0
- pz_foundation/db/public.py +45 -0
- pz_foundation/db/session.py +369 -0
- pz_foundation/db/sqlite.py +15 -0
- pz_foundation/db/tortoise_manager.py +724 -0
- pz_foundation/db/tortoise_psycopg.py +148 -0
- pz_foundation/docs/README.md +18 -0
- pz_foundation/docs/distribution-README.md +319 -0
- pz_foundation/docs/infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/350/257/264/346/230/216.md +39 -0
- pz_foundation/docs//345/205/254/345/274/200/345/205/215/350/264/271/346/216/245/345/217/243/351/230/262/346/273/245/347/224/250/344/270/216/344/272/272/346/234/272/346/240/241/351/252/214.md +680 -0
- pz_foundation/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +288 -0
- pz_foundation/docs//350/267/257/347/224/261/344/270/216URL/345/244/204/347/220/206/350/247/204/350/214/203.md +119 -0
- pz_foundation/health.py +105 -0
- pz_foundation/host/__init__.py +11 -0
- pz_foundation/host/builder.py +34 -0
- pz_foundation/host/cache_runtime.py +44 -0
- pz_foundation/host/db/__init__.py +17 -0
- pz_foundation/host/db/aerich_config.py +36 -0
- pz_foundation/host/db/config.py +106 -0
- pz_foundation/host/db/connection_manager.py +48 -0
- pz_foundation/host/db/deps.py +5 -0
- pz_foundation/host/db/orm_retry_patch.py +566 -0
- pz_foundation/host/db/request_adapter.py +325 -0
- pz_foundation/host/db/router.py +28 -0
- pz_foundation/host/db/session.py +284 -0
- pz_foundation/host/db/tortoise_manager.py +10 -0
- pz_foundation/host/idempotency.py +132 -0
- pz_foundation/host/lifespan.py +247 -0
- pz_foundation/host/redis_runtime.py +100 -0
- pz_foundation/host/web.py +790 -0
- pz_foundation/infra_provider.py +236 -0
- pz_foundation/kv/__init__.py +25 -0
- pz_foundation/kv/cache.py +1166 -0
- pz_foundation/kv/deps.py +84 -0
- pz_foundation/kv/public.py +25 -0
- pz_foundation/kv/redis_manager.py +1531 -0
- pz_foundation/lazy_exports.py +43 -0
- pz_foundation/lazy_imports.py +124 -0
- pz_foundation/lifecycle.py +392 -0
- pz_foundation/managed_workers.py +1117 -0
- pz_foundation/middlewares/__init__.py +26 -0
- pz_foundation/middlewares/_settings.py +250 -0
- pz_foundation/middlewares/analytics.py +124 -0
- pz_foundation/middlewares/cache_control.py +156 -0
- pz_foundation/middlewares/compression.py +202 -0
- pz_foundation/middlewares/cors.py +62 -0
- pz_foundation/middlewares/geo_country.py +44 -0
- pz_foundation/middlewares/inner_router.py +50 -0
- pz_foundation/middlewares/rate_limited/__init__.py +3 -0
- pz_foundation/middlewares/rate_limited/key_limiter.py +115 -0
- pz_foundation/middlewares/request_logging.py +104 -0
- pz_foundation/middlewares/route_owner.py +309 -0
- pz_foundation/middlewares/security_csrf.py +44 -0
- pz_foundation/middlewares/security_headers/__init__.py +4 -0
- pz_foundation/middlewares/security_headers/conflicts.py +44 -0
- pz_foundation/middlewares/security_headers/csp_nonce.py +200 -0
- pz_foundation/middlewares/security_headers/security_headers.py +44 -0
- pz_foundation/migrations/__init__.py +1 -0
- pz_foundation/migrations/pz_foundation/__init__.py +1 -0
- pz_foundation/models.py +22 -0
- pz_foundation/notifications/__init__.py +23 -0
- pz_foundation/notifications/email.py +542 -0
- pz_foundation/notifications/public.py +31 -0
- pz_foundation/observability/__init__.py +71 -0
- pz_foundation/observability/http.py +34 -0
- pz_foundation/observability/logger/__init__.py +49 -0
- pz_foundation/observability/logger/_bot_settings.py +126 -0
- pz_foundation/observability/logger/bot_message.py +592 -0
- pz_foundation/observability/logger/core.py +385 -0
- pz_foundation/observability/logger/delivery_audit.py +84 -0
- pz_foundation/observability/logger/feishu_bot.py +199 -0
- pz_foundation/observability/logger/telegram_bot.py +194 -0
- pz_foundation/observability/metrics.py +365 -0
- pz_foundation/observability/runtime.py +68 -0
- pz_foundation/public.py +405 -0
- pz_foundation/resources/__init__.py +12 -0
- pz_foundation/resources/builder.py +900 -0
- pz_foundation/resources/images.py +363 -0
- pz_foundation/root_cli.py +20 -0
- pz_foundation/routing/__init__.py +203 -0
- pz_foundation/routing/canonical.py +486 -0
- pz_foundation/routing/convertors.py +82 -0
- pz_foundation/routing/declarations.py +843 -0
- pz_foundation/routing/exports.py +148 -0
- pz_foundation/routing/loaders.py +138 -0
- pz_foundation/routing/manifest.py +215 -0
- pz_foundation/routing/manifest_provider.py +47 -0
- pz_foundation/routing/mounts.py +101 -0
- pz_foundation/routing/ordering.py +172 -0
- pz_foundation/routing/registry.py +133 -0
- pz_foundation/routing/utils.py +28 -0
- pz_foundation/runtime.py +234 -0
- pz_foundation/settings.py +253 -0
- pz_foundation/settings_normalization.py +130 -0
- pz_foundation/storage/__init__.py +25 -0
- pz_foundation/storage/local.py +209 -0
- pz_foundation/storage/public.py +106 -0
- pz_foundation/storage/s3.py +116 -0
- pz_foundation/storage/service.py +234 -0
- pz_foundation/urls.py +19 -0
- pz_foundation/utils/__init__.py +6 -0
- pz_foundation/utils/merge.py +160 -0
- pz_foundation/worker_replicas.py +55 -0
- pz_settings_center/__init__.py +5 -0
- pz_settings_center/api/__init__.py +5 -0
- pz_settings_center/api/runtime_overrides.py +360 -0
- pz_settings_center/apps.py +28 -0
- pz_settings_center/configs/__init__.py +0 -0
- pz_settings_center/configs/default.env +2 -0
- pz_settings_center/configs/default.jsonc +1 -0
- pz_settings_center/docs/01-settings_center/350/203/275/345/212/233/350/276/271/347/225/214.md +124 -0
- pz_settings_center/docs/README.md +12 -0
- pz_settings_center/docs/distribution-README.md +150 -0
- pz_settings_center/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +48 -0
- pz_settings_center/integration_payload.py +18 -0
- pz_settings_center/project_helpers.py +405 -0
- pz_settings_center/public.py +139 -0
- pz_settings_center/routing_helpers.py +73 -0
- pz_settings_center/runtime_override_provider.py +61 -0
- pz_settings_center/typed_settings.py +324 -0
- pz_settings_center/urls.py +9 -0
|
@@ -0,0 +1,790 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
from dataclasses import replace
|
|
5
|
+
import importlib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from types import SimpleNamespace
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from fastapi import APIRouter, FastAPI
|
|
11
|
+
from fastapi.encoders import jsonable_encoder
|
|
12
|
+
from fastapi.responses import JSONResponse
|
|
13
|
+
from fastapi.routing import APIRoute
|
|
14
|
+
from fastapi.staticfiles import StaticFiles
|
|
15
|
+
from starlette.routing import compile_path
|
|
16
|
+
|
|
17
|
+
from pz_core.assembly.project import active_project_import_root
|
|
18
|
+
from pz_settings_center.public import active_settings_project, bound_settings_snapshot
|
|
19
|
+
|
|
20
|
+
from ..runtime import bind_runtime_context, reset_runtime_context
|
|
21
|
+
from ..routing import (
|
|
22
|
+
DeclaredUrlSpec,
|
|
23
|
+
build_route_registry_entry,
|
|
24
|
+
clean_and_reorder_routes,
|
|
25
|
+
compile_route_manifest,
|
|
26
|
+
graft_router,
|
|
27
|
+
load_declared_url_specs,
|
|
28
|
+
)
|
|
29
|
+
from ..routing.canonical import is_file_like_path
|
|
30
|
+
from ..routing.exports import _merge_lifespan_context
|
|
31
|
+
from ..routing.manifest import require_foundation_url_manifest, route_path_decls_from_router
|
|
32
|
+
from ..routing.utils import _join_path_prefix
|
|
33
|
+
from .db.request_adapter import wrap_db_request_scope
|
|
34
|
+
from .lifespan import start_infra_background_tasks
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _HostAsgiWrapper:
|
|
38
|
+
def __init__(self, app: FastAPI, wrapped: Any):
|
|
39
|
+
self._app = app
|
|
40
|
+
self._wrapped = wrapped
|
|
41
|
+
self.state = app.state
|
|
42
|
+
self.router = app.router
|
|
43
|
+
self.title = getattr(app, "title", "")
|
|
44
|
+
|
|
45
|
+
def __getattr__(self, name: str) -> Any:
|
|
46
|
+
return getattr(self._app, name)
|
|
47
|
+
|
|
48
|
+
async def __call__(self, scope: dict[str, Any], receive: Any, send: Any) -> None:
|
|
49
|
+
await self._wrapped(scope, receive, send)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _bind_host_state_surface(target: Any, app: FastAPI) -> Any:
|
|
53
|
+
for key, value in (
|
|
54
|
+
("state", app.state),
|
|
55
|
+
("router", app.router),
|
|
56
|
+
("title", getattr(app, "title", "")),
|
|
57
|
+
("_pz_host_app", app),
|
|
58
|
+
):
|
|
59
|
+
try:
|
|
60
|
+
setattr(target, key, value)
|
|
61
|
+
except Exception:
|
|
62
|
+
continue
|
|
63
|
+
return target
|
|
64
|
+
|
|
65
|
+
def _resolve_dotted(target: Any) -> Any:
|
|
66
|
+
if not isinstance(target, str) or not target.strip():
|
|
67
|
+
return target
|
|
68
|
+
raw = target.strip()
|
|
69
|
+
if ":" in raw:
|
|
70
|
+
module_name, attr_name = raw.split(":", 1)
|
|
71
|
+
else:
|
|
72
|
+
module_name, attr_name = raw.rsplit(".", 1)
|
|
73
|
+
module = importlib.import_module(module_name)
|
|
74
|
+
return getattr(module, attr_name)
|
|
75
|
+
|
|
76
|
+
def _iter_payload_items(payload: Any) -> list[Any]:
|
|
77
|
+
if payload is None:
|
|
78
|
+
return []
|
|
79
|
+
if isinstance(payload, (list, tuple)):
|
|
80
|
+
return [item for item in payload if item is not None]
|
|
81
|
+
return [payload]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _load_host_extensions(plan: Any, *, kinds: set[str] | None = None) -> list[Any]:
|
|
85
|
+
loaded: list[Any] = []
|
|
86
|
+
for extension in plan.extension_registry.extensions:
|
|
87
|
+
kind = str(getattr(extension, "kind", "") or "").strip()
|
|
88
|
+
if kinds is not None and kind not in kinds:
|
|
89
|
+
continue
|
|
90
|
+
loaded.append(
|
|
91
|
+
SimpleNamespace(
|
|
92
|
+
id=getattr(extension, "id", ""),
|
|
93
|
+
owner=getattr(extension, "owner_app", ""),
|
|
94
|
+
kind=kind,
|
|
95
|
+
title=getattr(extension, "title", ""),
|
|
96
|
+
priority=int(getattr(extension, "priority", 100) or 100),
|
|
97
|
+
payload=dict(getattr(extension, "payload", None) or {}),
|
|
98
|
+
source=getattr(extension, "source", ""),
|
|
99
|
+
provides=tuple(getattr(extension, "provides", ()) or ()),
|
|
100
|
+
conflicts=tuple(getattr(extension, "conflicts", ()) or ()),
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
loaded.sort(key=lambda ext: (int(getattr(ext, "priority", 100) or 100), str(getattr(ext, "id", "") or "")))
|
|
104
|
+
return loaded
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _router_has_catchall(router: Any) -> bool:
|
|
108
|
+
for route in getattr(router, "routes", []) or []:
|
|
109
|
+
path = str(getattr(route, "path", "") or "")
|
|
110
|
+
if ":path}" in path:
|
|
111
|
+
return True
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _set_route_path(route: APIRoute, path: str) -> None:
|
|
116
|
+
path_regex, path_format, param_convertors = compile_path(path)
|
|
117
|
+
route.path = path
|
|
118
|
+
route.path_regex = path_regex
|
|
119
|
+
route.path_format = path_format
|
|
120
|
+
route.param_convertors = param_convertors
|
|
121
|
+
dependant = getattr(route, "dependant", None)
|
|
122
|
+
if dependant is not None:
|
|
123
|
+
try:
|
|
124
|
+
dependant.path = path_format
|
|
125
|
+
except Exception:
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _is_directory_page_route(route: APIRoute, *, path_prefix: str = "") -> bool:
|
|
130
|
+
path = str(getattr(route, "path", "") or "").strip()
|
|
131
|
+
if not path or path == "/" or path.endswith("/"):
|
|
132
|
+
return False
|
|
133
|
+
effective_path = _join_path_prefix(path_prefix, path)
|
|
134
|
+
if (
|
|
135
|
+
effective_path.startswith("/api/")
|
|
136
|
+
or effective_path.startswith("/static/")
|
|
137
|
+
or is_file_like_path(effective_path)
|
|
138
|
+
):
|
|
139
|
+
return False
|
|
140
|
+
methods = {str(method).upper() for method in (getattr(route, "methods", None) or ())}
|
|
141
|
+
if methods and not methods.issubset({"GET", "HEAD"}):
|
|
142
|
+
return False
|
|
143
|
+
return effective_path == "/admin" or effective_path.startswith("/admin/")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _normalize_directory_page_routes(router: APIRouter, *, path_prefix: str = "") -> None:
|
|
147
|
+
for route in list(getattr(router, "routes", []) or []):
|
|
148
|
+
if not isinstance(route, APIRoute) or not _is_directory_page_route(route, path_prefix=path_prefix):
|
|
149
|
+
continue
|
|
150
|
+
path = str(getattr(route, "path", "") or "").strip()
|
|
151
|
+
_set_route_path(route, f"{path}/")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _ensure_route_registry(app: FastAPI) -> list[dict[str, Any]]:
|
|
155
|
+
registry = getattr(app.state, "pz_route_registry", None)
|
|
156
|
+
if isinstance(registry, list):
|
|
157
|
+
return registry
|
|
158
|
+
registry = []
|
|
159
|
+
app.state.pz_route_registry = registry
|
|
160
|
+
return registry
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _record_route_mount(
|
|
164
|
+
app: FastAPI,
|
|
165
|
+
*,
|
|
166
|
+
namespace: str,
|
|
167
|
+
path_prefix: str,
|
|
168
|
+
router: APIRouter,
|
|
169
|
+
source: str,
|
|
170
|
+
route_hints: tuple[str, ...] = (),
|
|
171
|
+
conflict_policy: str = "fail",
|
|
172
|
+
) -> None:
|
|
173
|
+
registry = _ensure_route_registry(app)
|
|
174
|
+
mounted_paths: list[str] = []
|
|
175
|
+
for route in getattr(router, "routes", ()) or ():
|
|
176
|
+
path = getattr(route, "path", None)
|
|
177
|
+
if isinstance(path, str) and path:
|
|
178
|
+
mounted_paths.append(path)
|
|
179
|
+
try:
|
|
180
|
+
setattr(route, "pz_route_namespace", namespace)
|
|
181
|
+
setattr(route, "pz_route_source", source)
|
|
182
|
+
except Exception:
|
|
183
|
+
continue
|
|
184
|
+
registry.append(
|
|
185
|
+
{
|
|
186
|
+
"namespace": namespace,
|
|
187
|
+
"path_prefix": path_prefix,
|
|
188
|
+
"source": source,
|
|
189
|
+
"paths": tuple(mounted_paths),
|
|
190
|
+
"route_hints": tuple(route_hints or ()),
|
|
191
|
+
"conflict_policy": str(conflict_policy or "fail"),
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _validate_route_registry(app: FastAPI) -> None:
|
|
197
|
+
registry = _ensure_route_registry(app)
|
|
198
|
+
seen: dict[str, dict[str, Any]] = {}
|
|
199
|
+
for item in registry:
|
|
200
|
+
namespace = str(item.get("namespace") or "").strip()
|
|
201
|
+
if not namespace:
|
|
202
|
+
raise RuntimeError("mounted route specs must carry a non-empty namespace.")
|
|
203
|
+
previous = seen.get(namespace)
|
|
204
|
+
if previous is not None:
|
|
205
|
+
raise RuntimeError(
|
|
206
|
+
"duplicate mounted route namespace "
|
|
207
|
+
f"{namespace!r}: {previous.get('source', '<unknown>')} and {item.get('source', '<unknown>')}"
|
|
208
|
+
)
|
|
209
|
+
seen[namespace] = item
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _host_route_routers(
|
|
213
|
+
*,
|
|
214
|
+
app: FastAPI,
|
|
215
|
+
runtime: Any,
|
|
216
|
+
) -> list[DeclaredUrlSpec]:
|
|
217
|
+
routers: list[tuple[int, DeclaredUrlSpec]] = []
|
|
218
|
+
for ext in _load_host_extensions(runtime.plan, kinds={"host.route"}):
|
|
219
|
+
payload = dict(getattr(ext, "payload", {}) or {})
|
|
220
|
+
target = _resolve_dotted(payload.get("router"))
|
|
221
|
+
if target is None:
|
|
222
|
+
raise RuntimeError(f"Host route extension '{getattr(ext, 'id', '<unknown>')}' must declare payload.router.")
|
|
223
|
+
if isinstance(target, APIRouter):
|
|
224
|
+
resolved = target
|
|
225
|
+
else:
|
|
226
|
+
resolved = target(
|
|
227
|
+
app=app,
|
|
228
|
+
host=app,
|
|
229
|
+
runtime=runtime,
|
|
230
|
+
plan=runtime.plan,
|
|
231
|
+
bindings=runtime.bindings,
|
|
232
|
+
)
|
|
233
|
+
path_prefix = str(payload.get("path_prefix") or "").strip()
|
|
234
|
+
for router in _iter_payload_items(resolved):
|
|
235
|
+
if not isinstance(router, APIRouter):
|
|
236
|
+
raise RuntimeError(
|
|
237
|
+
f"Host route extension '{getattr(ext, 'id', '<unknown>')}' must resolve to APIRouter instances."
|
|
238
|
+
)
|
|
239
|
+
_normalize_directory_page_routes(router, path_prefix=path_prefix)
|
|
240
|
+
routers.append(
|
|
241
|
+
(
|
|
242
|
+
int(getattr(ext, "priority", 100) or 100),
|
|
243
|
+
DeclaredUrlSpec(
|
|
244
|
+
router=router,
|
|
245
|
+
path_prefix=path_prefix,
|
|
246
|
+
namespace=str(getattr(ext, "id", "") or getattr(ext, "owner", "") or "host.route"),
|
|
247
|
+
),
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
return [router for _, router in sorted(routers, key=lambda item: item[0])]
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _iter_router_paths(router: APIRouter) -> list[str]:
|
|
254
|
+
out: list[str] = []
|
|
255
|
+
for route in list(getattr(router, "routes", []) or []):
|
|
256
|
+
path = getattr(route, "path", None)
|
|
257
|
+
if isinstance(path, str) and path:
|
|
258
|
+
out.append(path)
|
|
259
|
+
return out
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _admin_panel_routers(
|
|
263
|
+
*,
|
|
264
|
+
app: FastAPI,
|
|
265
|
+
runtime: Any,
|
|
266
|
+
) -> list[DeclaredUrlSpec]:
|
|
267
|
+
routers: list[tuple[int, DeclaredUrlSpec]] = []
|
|
268
|
+
for ext in _load_host_extensions(runtime.plan, kinds={"admin.panel"}):
|
|
269
|
+
payload = dict(getattr(ext, "payload", {}) or {})
|
|
270
|
+
target = _resolve_dotted(payload.get("router"))
|
|
271
|
+
if target is None:
|
|
272
|
+
continue
|
|
273
|
+
if isinstance(target, APIRouter):
|
|
274
|
+
resolved = target
|
|
275
|
+
else:
|
|
276
|
+
resolved = target(
|
|
277
|
+
app=app,
|
|
278
|
+
host=app,
|
|
279
|
+
runtime=runtime,
|
|
280
|
+
plan=runtime.plan,
|
|
281
|
+
bindings=runtime.bindings,
|
|
282
|
+
)
|
|
283
|
+
for router in _iter_payload_items(resolved):
|
|
284
|
+
if not isinstance(router, APIRouter):
|
|
285
|
+
continue
|
|
286
|
+
route_paths = _iter_router_paths(router)
|
|
287
|
+
if not route_paths or not all(path.startswith("/admin") for path in route_paths):
|
|
288
|
+
continue
|
|
289
|
+
_normalize_directory_page_routes(router)
|
|
290
|
+
routers.append(
|
|
291
|
+
(
|
|
292
|
+
int(getattr(ext, "priority", 100) or 100),
|
|
293
|
+
DeclaredUrlSpec(
|
|
294
|
+
router=router,
|
|
295
|
+
path_prefix="",
|
|
296
|
+
namespace=str(getattr(ext, "id", "") or getattr(ext, "owner", "") or "admin.panel"),
|
|
297
|
+
),
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
return [router for _, router in sorted(routers, key=lambda item: item[0])]
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _declared_url_specs_from_manifest(runtime: Any) -> list[tuple[str, DeclaredUrlSpec]]:
|
|
304
|
+
manifest = require_foundation_url_manifest(getattr(runtime, "plan", None))
|
|
305
|
+
specs: list[tuple[str, DeclaredUrlSpec]] = []
|
|
306
|
+
specs_by_namespace: dict[str, DeclaredUrlSpec] = {}
|
|
307
|
+
loaded_by_entrypoint: dict[str, tuple[DeclaredUrlSpec, ...]] = {}
|
|
308
|
+
for entry in manifest.entries:
|
|
309
|
+
entrypoint = str(entry.entrypoint or "").strip()
|
|
310
|
+
if not entrypoint:
|
|
311
|
+
raise RuntimeError(f"compiled URL manifest entry {entry.namespace!r} is missing its app entrypoint.")
|
|
312
|
+
if entrypoint in loaded_by_entrypoint:
|
|
313
|
+
continue
|
|
314
|
+
declared_url_specs = load_declared_url_specs(entrypoint)
|
|
315
|
+
loaded_by_entrypoint[entrypoint] = declared_url_specs
|
|
316
|
+
for spec in declared_url_specs:
|
|
317
|
+
specs_by_namespace[str(spec.namespace or "").strip()] = spec
|
|
318
|
+
for entry in manifest.entries:
|
|
319
|
+
spec = specs_by_namespace.get(str(entry.namespace or "").strip())
|
|
320
|
+
if spec is None:
|
|
321
|
+
raise RuntimeError(
|
|
322
|
+
f"compiled URL manifest entry {entry.namespace!r} does not have a matching materialized router."
|
|
323
|
+
)
|
|
324
|
+
if route_path_decls_from_router(spec.router) != tuple(entry.routes):
|
|
325
|
+
raise RuntimeError(
|
|
326
|
+
f"materialized router for compiled URL manifest entry {entry.namespace!r} "
|
|
327
|
+
"does not match the RuntimePlan URL manifest."
|
|
328
|
+
)
|
|
329
|
+
specs.append((str(entry.owner_app or ""), spec))
|
|
330
|
+
return specs
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _mount_app_routers(
|
|
334
|
+
app: FastAPI,
|
|
335
|
+
*,
|
|
336
|
+
runtime: Any,
|
|
337
|
+
) -> None:
|
|
338
|
+
normal_routers: list[DeclaredUrlSpec] = []
|
|
339
|
+
catchall_routers: list[DeclaredUrlSpec] = []
|
|
340
|
+
registry_entries = []
|
|
341
|
+
manifest_specs = _declared_url_specs_from_manifest(runtime)
|
|
342
|
+
for owner_app, declared_spec in manifest_specs:
|
|
343
|
+
if not isinstance(declared_spec, DeclaredUrlSpec):
|
|
344
|
+
raise RuntimeError(f"router mount for {owner_app} must resolve to DeclaredUrlSpec instances.")
|
|
345
|
+
router = declared_spec.router
|
|
346
|
+
if not isinstance(router, APIRouter):
|
|
347
|
+
raise RuntimeError(f"router mount for {owner_app} must resolve to APIRouter instances.")
|
|
348
|
+
_normalize_directory_page_routes(router, path_prefix=str(declared_spec.path_prefix or ""))
|
|
349
|
+
spec = DeclaredUrlSpec(
|
|
350
|
+
router=router,
|
|
351
|
+
path_prefix=str(declared_spec.path_prefix or ""),
|
|
352
|
+
dependencies=tuple(declared_spec.dependencies or ()),
|
|
353
|
+
namespace=str(declared_spec.namespace or owner_app),
|
|
354
|
+
replace_target=str(declared_spec.replace_target or ""),
|
|
355
|
+
route_hints=tuple(declared_spec.route_hints or ()),
|
|
356
|
+
conflict_policy=str(declared_spec.conflict_policy or "fail"),
|
|
357
|
+
)
|
|
358
|
+
registry_entries.append(
|
|
359
|
+
build_route_registry_entry(
|
|
360
|
+
owner_app=str(owner_app),
|
|
361
|
+
spec=spec,
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
bucket = catchall_routers if _router_has_catchall(router) else normal_routers
|
|
365
|
+
bucket.append(spec)
|
|
366
|
+
for spec in _host_route_routers(app=app, runtime=runtime):
|
|
367
|
+
registry_entries.append(
|
|
368
|
+
build_route_registry_entry(
|
|
369
|
+
owner_app=str(spec.namespace or "host.route"),
|
|
370
|
+
spec=spec,
|
|
371
|
+
route_hints=("host.route",),
|
|
372
|
+
)
|
|
373
|
+
)
|
|
374
|
+
bucket = catchall_routers if _router_has_catchall(spec.router) else normal_routers
|
|
375
|
+
bucket.append(spec)
|
|
376
|
+
for spec in _admin_panel_routers(app=app, runtime=runtime):
|
|
377
|
+
registry_entries.append(
|
|
378
|
+
build_route_registry_entry(
|
|
379
|
+
owner_app=str(spec.namespace or "admin.panel"),
|
|
380
|
+
spec=spec,
|
|
381
|
+
route_hints=("admin.panel",),
|
|
382
|
+
)
|
|
383
|
+
)
|
|
384
|
+
bucket = catchall_routers if _router_has_catchall(spec.router) else normal_routers
|
|
385
|
+
bucket.append(spec)
|
|
386
|
+
app.state.pz_route_manifest = compile_route_manifest(registry_entries)
|
|
387
|
+
app.state.pz_compiled_route_registry = app.state.pz_route_manifest.entries
|
|
388
|
+
for spec in normal_routers + catchall_routers:
|
|
389
|
+
graft_router(
|
|
390
|
+
app,
|
|
391
|
+
spec.router,
|
|
392
|
+
path_prefix=spec.path_prefix,
|
|
393
|
+
dependencies=spec.dependencies,
|
|
394
|
+
)
|
|
395
|
+
_record_route_mount(
|
|
396
|
+
app,
|
|
397
|
+
namespace=spec.namespace,
|
|
398
|
+
path_prefix=spec.path_prefix,
|
|
399
|
+
router=spec.router,
|
|
400
|
+
source=spec.namespace,
|
|
401
|
+
route_hints=tuple(spec.route_hints or ()),
|
|
402
|
+
conflict_policy=str(spec.conflict_policy or "fail"),
|
|
403
|
+
)
|
|
404
|
+
_validate_route_registry(app)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def _attach_runtime_state(
|
|
408
|
+
app: FastAPI,
|
|
409
|
+
*,
|
|
410
|
+
project_dir: Path,
|
|
411
|
+
plan: Any,
|
|
412
|
+
bindings: Any,
|
|
413
|
+
lifecycle_state: Any,
|
|
414
|
+
settings_snapshot: Any,
|
|
415
|
+
bind_context: bool = True,
|
|
416
|
+
) -> None:
|
|
417
|
+
app.state.project_dir = str(project_dir)
|
|
418
|
+
app.state.app_name = str(settings_snapshot.get("project", "name") or "PZ Project")
|
|
419
|
+
app.state.settings_snapshot = settings_snapshot
|
|
420
|
+
app.state.pz_runtime_plan = plan
|
|
421
|
+
app.state.pz_runtime_bindings = bindings
|
|
422
|
+
app.state.pz_runtime_state = lifecycle_state
|
|
423
|
+
if bind_context:
|
|
424
|
+
owner = getattr(app.state, "pz_foundation_owner", None)
|
|
425
|
+
if owner:
|
|
426
|
+
bind_runtime_context(
|
|
427
|
+
owner=str(owner),
|
|
428
|
+
project_dir=project_dir,
|
|
429
|
+
bindings=bindings,
|
|
430
|
+
plan=plan,
|
|
431
|
+
lifecycle_state=lifecycle_state,
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def _to_jsonable(value: Any) -> Any:
|
|
436
|
+
return jsonable_encoder(value)
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def _requires_database_readiness(payload: dict[str, Any]) -> bool:
|
|
440
|
+
statuses = payload.get("capability_statuses")
|
|
441
|
+
if not isinstance(statuses, dict):
|
|
442
|
+
return False
|
|
443
|
+
return statuses.get("database") not in (None, "disabled")
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def _database_lifecycle_payload() -> dict[str, Any]:
|
|
447
|
+
try:
|
|
448
|
+
from ..db.lifecycle_status import connection_lifecycle_status
|
|
449
|
+
from ..db.tortoise_manager import tortoise_manager
|
|
450
|
+
|
|
451
|
+
status = connection_lifecycle_status(tortoise_manager)
|
|
452
|
+
except Exception as exc: # noqa: BLE001
|
|
453
|
+
return {"default_ready": False, "status": "lookup_failed", "error_type": type(exc).__name__}
|
|
454
|
+
payload = _to_jsonable(status)
|
|
455
|
+
if isinstance(payload, dict):
|
|
456
|
+
payload["default_ready"] = status.default_ready
|
|
457
|
+
payload["read_only_ready"] = status.read_only_ready
|
|
458
|
+
return payload
|
|
459
|
+
return {"default_ready": bool(status.default_ready), "read_only_ready": bool(status.read_only_ready)}
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _apply_readiness_lifecycle(payload: dict[str, Any]) -> dict[str, Any]:
|
|
463
|
+
if not _requires_database_readiness(payload):
|
|
464
|
+
return payload
|
|
465
|
+
lifecycle = _database_lifecycle_payload()
|
|
466
|
+
payload["database_lifecycle"] = lifecycle
|
|
467
|
+
if bool(lifecycle.get("default_ready")):
|
|
468
|
+
return payload
|
|
469
|
+
payload["status"] = "unavailable"
|
|
470
|
+
statuses = payload.get("capability_statuses")
|
|
471
|
+
if isinstance(statuses, dict):
|
|
472
|
+
statuses["database"] = "unavailable"
|
|
473
|
+
unavailable = list(payload.get("unavailable_capabilities") or ())
|
|
474
|
+
if "database" not in unavailable:
|
|
475
|
+
unavailable.append("database")
|
|
476
|
+
payload["unavailable_capabilities"] = unavailable
|
|
477
|
+
return payload
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def _has_route_path(app: FastAPI, path: str) -> bool:
|
|
481
|
+
for route in getattr(app.router, "routes", []) or []:
|
|
482
|
+
if getattr(route, "path", None) == path:
|
|
483
|
+
return True
|
|
484
|
+
return False
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _resource_output_dir(manifest: Any, name: str) -> Path:
|
|
488
|
+
for item in getattr(manifest, "items", ()) or ():
|
|
489
|
+
if str(getattr(item, "name", "") or "").strip() == name:
|
|
490
|
+
output_dir = getattr(item, "output_dir", None)
|
|
491
|
+
if output_dir is not None:
|
|
492
|
+
return Path(output_dir)
|
|
493
|
+
raise RuntimeError(f"runtime resource item '{name}' is missing from manifest")
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def _mount_runtime_static(app: FastAPI, *, bindings: Any) -> None:
|
|
497
|
+
if _has_route_path(app, "/static"):
|
|
498
|
+
return
|
|
499
|
+
manifest = bindings.resources.manifest()
|
|
500
|
+
static_dir = _resource_output_dir(manifest, "static")
|
|
501
|
+
if not static_dir.exists():
|
|
502
|
+
raise RuntimeError("runtime static resources must be built before mounting '/static'")
|
|
503
|
+
app.mount("/static", StaticFiles(directory=str(static_dir), check_dir=False), name="static")
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def _build_runtime_resources(*, plan: Any, bindings: Any, host: object | None = None) -> None:
|
|
507
|
+
bindings.providers.activate_declared(
|
|
508
|
+
plan=plan,
|
|
509
|
+
bindings=bindings,
|
|
510
|
+
host=host,
|
|
511
|
+
owner="pz_foundation",
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def _apply_host_state_extensions(
|
|
516
|
+
app: FastAPI,
|
|
517
|
+
*,
|
|
518
|
+
runtime: Any,
|
|
519
|
+
) -> None:
|
|
520
|
+
extensions = _load_host_extensions(runtime.plan, kinds={"host.state"})
|
|
521
|
+
for ext in extensions:
|
|
522
|
+
payload = dict(getattr(ext, "payload", {}) or {})
|
|
523
|
+
if "values" in payload:
|
|
524
|
+
values = dict(payload.get("values") or {})
|
|
525
|
+
else:
|
|
526
|
+
provider = _resolve_dotted(payload.get("provider"))
|
|
527
|
+
values = dict(
|
|
528
|
+
provider(
|
|
529
|
+
app=app,
|
|
530
|
+
host=app,
|
|
531
|
+
runtime=runtime,
|
|
532
|
+
plan=runtime.plan,
|
|
533
|
+
bindings=runtime.bindings,
|
|
534
|
+
)
|
|
535
|
+
or {}
|
|
536
|
+
)
|
|
537
|
+
for key, value in values.items():
|
|
538
|
+
setattr(app.state, str(key), value)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def _apply_host_route_extensions(
|
|
542
|
+
app: FastAPI,
|
|
543
|
+
*,
|
|
544
|
+
runtime: Any,
|
|
545
|
+
) -> None:
|
|
546
|
+
for spec in _host_route_routers(app=app, runtime=runtime):
|
|
547
|
+
graft_router(
|
|
548
|
+
app,
|
|
549
|
+
spec.router,
|
|
550
|
+
path_prefix=spec.path_prefix,
|
|
551
|
+
dependencies=spec.dependencies,
|
|
552
|
+
)
|
|
553
|
+
_record_route_mount(
|
|
554
|
+
app,
|
|
555
|
+
namespace=spec.namespace,
|
|
556
|
+
path_prefix=spec.path_prefix,
|
|
557
|
+
router=spec.router,
|
|
558
|
+
source=spec.namespace,
|
|
559
|
+
route_hints=tuple(spec.route_hints or ()),
|
|
560
|
+
conflict_policy=str(spec.conflict_policy or "fail"),
|
|
561
|
+
)
|
|
562
|
+
_validate_route_registry(app)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def _apply_host_mount_extensions(
|
|
566
|
+
app: FastAPI,
|
|
567
|
+
*,
|
|
568
|
+
runtime: Any,
|
|
569
|
+
) -> None:
|
|
570
|
+
kinds = {"host.mount", "host.static"}
|
|
571
|
+
extensions = _load_host_extensions(runtime.plan, kinds=kinds)
|
|
572
|
+
project_dir = Path(getattr(runtime.plan.project, "project_dir", ".")).resolve()
|
|
573
|
+
for ext in extensions:
|
|
574
|
+
payload = dict(getattr(ext, "payload", {}) or {})
|
|
575
|
+
path = str(payload.get("path") or "").strip()
|
|
576
|
+
if not path:
|
|
577
|
+
raise RuntimeError(f"Host mount extension '{getattr(ext, 'id', '<unknown>')}' must declare payload.path.")
|
|
578
|
+
name = str(payload.get("name") or getattr(ext, "id", "host-mount"))
|
|
579
|
+
if getattr(ext, "kind", "") == "host.static":
|
|
580
|
+
raw_directory = str(payload.get("directory") or "").strip()
|
|
581
|
+
if not raw_directory:
|
|
582
|
+
raise RuntimeError(
|
|
583
|
+
f"Host static extension '{getattr(ext, 'id', '<unknown>')}' must declare payload.directory."
|
|
584
|
+
)
|
|
585
|
+
directory = Path(raw_directory)
|
|
586
|
+
if not directory.is_absolute():
|
|
587
|
+
directory = (project_dir / directory).resolve()
|
|
588
|
+
mounted = StaticFiles(
|
|
589
|
+
directory=str(directory),
|
|
590
|
+
check_dir=bool(payload.get("check_dir", False)),
|
|
591
|
+
)
|
|
592
|
+
else:
|
|
593
|
+
target = _resolve_dotted(payload.get("app"))
|
|
594
|
+
if target is None:
|
|
595
|
+
raise RuntimeError(
|
|
596
|
+
f"Host mount extension '{getattr(ext, 'id', '<unknown>')}' must declare payload.app."
|
|
597
|
+
)
|
|
598
|
+
if isinstance(target, (FastAPI, StaticFiles)):
|
|
599
|
+
mounted = target
|
|
600
|
+
else:
|
|
601
|
+
mounted = target(
|
|
602
|
+
app=app,
|
|
603
|
+
host=app,
|
|
604
|
+
runtime=runtime,
|
|
605
|
+
plan=runtime.plan,
|
|
606
|
+
bindings=runtime.bindings,
|
|
607
|
+
)
|
|
608
|
+
app.mount(path, mounted, name=name)
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def _build_http_app(
|
|
612
|
+
*,
|
|
613
|
+
plan: Any,
|
|
614
|
+
lifecycle_state: Any,
|
|
615
|
+
bindings: Any,
|
|
616
|
+
project_dir: Path,
|
|
617
|
+
health_facade_cls,
|
|
618
|
+
activate_runtime_plan,
|
|
619
|
+
shutdown_runtime_plan,
|
|
620
|
+
apply_runtime_middlewares,
|
|
621
|
+
active_runtime_plan,
|
|
622
|
+
) -> Any:
|
|
623
|
+
settings_snapshot = plan.settings
|
|
624
|
+
runtime_refs: dict[str, Any] = {}
|
|
625
|
+
|
|
626
|
+
_build_runtime_resources(plan=plan, bindings=bindings)
|
|
627
|
+
|
|
628
|
+
@asynccontextmanager
|
|
629
|
+
async def _lifespan(host_app: FastAPI):
|
|
630
|
+
started = False
|
|
631
|
+
try:
|
|
632
|
+
with (
|
|
633
|
+
active_settings_project(str(project_dir)),
|
|
634
|
+
active_runtime_plan(plan),
|
|
635
|
+
active_project_import_root(
|
|
636
|
+
project_dir,
|
|
637
|
+
extra_import_roots=getattr(settings_snapshot, "import_roots", ()),
|
|
638
|
+
preserve_loaded_modules=True,
|
|
639
|
+
),
|
|
640
|
+
):
|
|
641
|
+
started_state = await activate_runtime_plan(
|
|
642
|
+
plan,
|
|
643
|
+
lifecycle_state,
|
|
644
|
+
bindings=bindings,
|
|
645
|
+
host=host_app,
|
|
646
|
+
)
|
|
647
|
+
started_bindings = replace(
|
|
648
|
+
bindings,
|
|
649
|
+
health=health_facade_cls(plan, started_state, providers=bindings.providers),
|
|
650
|
+
)
|
|
651
|
+
started_runtime = SimpleNamespace(
|
|
652
|
+
plan=plan,
|
|
653
|
+
state=started_state,
|
|
654
|
+
bindings=started_bindings,
|
|
655
|
+
)
|
|
656
|
+
runtime_refs["started_state"] = started_state
|
|
657
|
+
runtime_refs["started_bindings"] = started_bindings
|
|
658
|
+
_attach_runtime_state(
|
|
659
|
+
host_app,
|
|
660
|
+
project_dir=project_dir,
|
|
661
|
+
plan=plan,
|
|
662
|
+
bindings=started_bindings,
|
|
663
|
+
lifecycle_state=started_state,
|
|
664
|
+
settings_snapshot=settings_snapshot,
|
|
665
|
+
)
|
|
666
|
+
start_infra_background_tasks(
|
|
667
|
+
getattr(host_app.state, "pz_foundation_lifecycle_infra_context", None)
|
|
668
|
+
)
|
|
669
|
+
_apply_host_state_extensions(host_app, runtime=started_runtime)
|
|
670
|
+
wrapped = apply_runtime_middlewares(
|
|
671
|
+
host_app,
|
|
672
|
+
plan=plan,
|
|
673
|
+
bindings=started_bindings,
|
|
674
|
+
)
|
|
675
|
+
_bind_host_state_surface(wrapped, host_app)
|
|
676
|
+
wrapped = wrap_db_request_scope(wrapped, project_dir=project_dir)
|
|
677
|
+
_bind_host_state_surface(wrapped, host_app)
|
|
678
|
+
runtime_refs["wrapped_app"] = wrapped
|
|
679
|
+
started = True
|
|
680
|
+
yield
|
|
681
|
+
finally:
|
|
682
|
+
if started:
|
|
683
|
+
owner = str(getattr(host_app.state, "pz_foundation_owner", "pz_foundation"))
|
|
684
|
+
current_bindings = getattr(
|
|
685
|
+
host_app.state,
|
|
686
|
+
"pz_runtime_bindings",
|
|
687
|
+
runtime_refs.get("started_bindings"),
|
|
688
|
+
)
|
|
689
|
+
current_state = getattr(
|
|
690
|
+
host_app.state,
|
|
691
|
+
"pz_runtime_state",
|
|
692
|
+
runtime_refs.get("started_state"),
|
|
693
|
+
)
|
|
694
|
+
if current_bindings is None or current_state is None:
|
|
695
|
+
return
|
|
696
|
+
stopped_state = await shutdown_runtime_plan(
|
|
697
|
+
plan,
|
|
698
|
+
current_state,
|
|
699
|
+
bindings=current_bindings,
|
|
700
|
+
host=host_app,
|
|
701
|
+
)
|
|
702
|
+
stopped_bindings = replace(
|
|
703
|
+
current_bindings,
|
|
704
|
+
health=health_facade_cls(plan, stopped_state, providers=current_bindings.providers),
|
|
705
|
+
)
|
|
706
|
+
_attach_runtime_state(
|
|
707
|
+
host_app,
|
|
708
|
+
project_dir=project_dir,
|
|
709
|
+
plan=plan,
|
|
710
|
+
bindings=stopped_bindings,
|
|
711
|
+
lifecycle_state=stopped_state,
|
|
712
|
+
settings_snapshot=settings_snapshot,
|
|
713
|
+
bind_context=False,
|
|
714
|
+
)
|
|
715
|
+
reset_runtime_context(owner=owner)
|
|
716
|
+
|
|
717
|
+
project_http = settings_snapshot.get("project", "http", default={}) or {}
|
|
718
|
+
if hasattr(project_http, "to_dict") and callable(project_http.to_dict):
|
|
719
|
+
project_http = project_http.to_dict()
|
|
720
|
+
if not isinstance(project_http, dict):
|
|
721
|
+
project_http = {}
|
|
722
|
+
app = FastAPI(
|
|
723
|
+
title=str(settings_snapshot.get("project", "name") or "Manga Translate"),
|
|
724
|
+
**dict(project_http),
|
|
725
|
+
)
|
|
726
|
+
app.router.redirect_slashes = False
|
|
727
|
+
app.router.lifespan_context = _merge_lifespan_context(_lifespan, app.router.lifespan_context)
|
|
728
|
+
|
|
729
|
+
app.state.pz_foundation_owner = f"pz_foundation:{id(app)}"
|
|
730
|
+
_attach_runtime_state(
|
|
731
|
+
app,
|
|
732
|
+
project_dir=project_dir,
|
|
733
|
+
plan=plan,
|
|
734
|
+
bindings=bindings,
|
|
735
|
+
lifecycle_state=lifecycle_state,
|
|
736
|
+
settings_snapshot=settings_snapshot,
|
|
737
|
+
)
|
|
738
|
+
_mount_runtime_static(app, bindings=bindings)
|
|
739
|
+
runtime_refs["wrapped_app"] = app
|
|
740
|
+
|
|
741
|
+
@app.get("/health")
|
|
742
|
+
async def _health() -> JSONResponse:
|
|
743
|
+
snapshot = app.state.pz_runtime_bindings.health.liveness()
|
|
744
|
+
return JSONResponse(_to_jsonable(snapshot))
|
|
745
|
+
|
|
746
|
+
@app.get("/live")
|
|
747
|
+
async def _live() -> JSONResponse:
|
|
748
|
+
snapshot = app.state.pz_runtime_bindings.health.liveness()
|
|
749
|
+
return JSONResponse(_to_jsonable(snapshot))
|
|
750
|
+
|
|
751
|
+
@app.get("/ready")
|
|
752
|
+
async def _ready() -> JSONResponse:
|
|
753
|
+
snapshot = app.state.pz_runtime_bindings.health.readiness()
|
|
754
|
+
payload = _apply_readiness_lifecycle(_to_jsonable(snapshot))
|
|
755
|
+
status_code = 200 if payload.get("status") in {"ok", "disabled"} else 503
|
|
756
|
+
return JSONResponse(payload, status_code=status_code)
|
|
757
|
+
|
|
758
|
+
with (
|
|
759
|
+
active_settings_project(str(project_dir)),
|
|
760
|
+
active_runtime_plan(plan),
|
|
761
|
+
active_project_import_root(
|
|
762
|
+
project_dir,
|
|
763
|
+
extra_import_roots=getattr(settings_snapshot, "import_roots", ()),
|
|
764
|
+
preserve_loaded_modules=True,
|
|
765
|
+
),
|
|
766
|
+
):
|
|
767
|
+
pre_start_runtime = SimpleNamespace(
|
|
768
|
+
plan=plan,
|
|
769
|
+
state=lifecycle_state,
|
|
770
|
+
bindings=bindings,
|
|
771
|
+
)
|
|
772
|
+
_apply_host_mount_extensions(app, runtime=pre_start_runtime)
|
|
773
|
+
_mount_app_routers(app, runtime=pre_start_runtime)
|
|
774
|
+
clean_and_reorder_routes(app)
|
|
775
|
+
|
|
776
|
+
async def _scoped(scope, receive, send):
|
|
777
|
+
with (
|
|
778
|
+
bound_settings_snapshot(plan.settings, project_dir=str(project_dir)),
|
|
779
|
+
active_settings_project(str(project_dir)),
|
|
780
|
+
active_runtime_plan(plan),
|
|
781
|
+
):
|
|
782
|
+
wrapped_app = runtime_refs.get("wrapped_app", app)
|
|
783
|
+
await wrapped_app(scope, receive, send)
|
|
784
|
+
|
|
785
|
+
_bind_host_state_surface(_scoped, app)
|
|
786
|
+
|
|
787
|
+
return _HostAsgiWrapper(app, _scoped)
|
|
788
|
+
__all__ = [
|
|
789
|
+
"_build_http_app",
|
|
790
|
+
]
|