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,824 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from importlib import import_module
|
|
6
|
+
import inspect
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from types import ModuleType, SimpleNamespace
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from pz_core.assembly.app_discovery import (
|
|
12
|
+
discover_leaf_app_packages,
|
|
13
|
+
import_app_entrypoint_module_from_decl,
|
|
14
|
+
read_static_app_declaration,
|
|
15
|
+
)
|
|
16
|
+
from pz_core.assembly.project import active_project_import_root
|
|
17
|
+
from pz_core.assembly.settings import SettingsSnapshot
|
|
18
|
+
from pz_core.core.errors import PzError
|
|
19
|
+
from pz_core.runtime.environment_integrity import validate_installed_app_identities
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True, slots=True)
|
|
23
|
+
class AppDecl:
|
|
24
|
+
app_id: str
|
|
25
|
+
name: str
|
|
26
|
+
version: str
|
|
27
|
+
required_pz: str
|
|
28
|
+
module_path: str
|
|
29
|
+
entrypoint: str
|
|
30
|
+
owner: str
|
|
31
|
+
priority: int = 100
|
|
32
|
+
requires_apps: tuple[str, ...] = ()
|
|
33
|
+
required_protocols: tuple[str, ...] = ()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True, slots=True)
|
|
37
|
+
class AppRegistry:
|
|
38
|
+
apps: tuple[AppDecl, ...]
|
|
39
|
+
initialized_app_ids: tuple[str, ...] = ()
|
|
40
|
+
|
|
41
|
+
def by_id(self) -> dict[str, AppDecl]:
|
|
42
|
+
return {app.app_id: app for app in self.apps}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True, slots=True)
|
|
46
|
+
class AppInitContext:
|
|
47
|
+
app: AppDecl
|
|
48
|
+
settings: SettingsSnapshot
|
|
49
|
+
project_dir: Path | None
|
|
50
|
+
phase: str = "declared"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
_DEFAULT_APP_PRIORITY = 100
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _loaded_pz_version() -> str:
|
|
57
|
+
from pz_core import __version__
|
|
58
|
+
|
|
59
|
+
return __version__
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _read_app_module_path(module_path: object, *, owner: str) -> str:
|
|
63
|
+
if not isinstance(module_path, str) or not module_path.strip():
|
|
64
|
+
raise PzError(
|
|
65
|
+
code="PZ_APP_INVALID_PATH",
|
|
66
|
+
message="installed_apps contains an empty or invalid app path.",
|
|
67
|
+
stage="apps",
|
|
68
|
+
owner=owner,
|
|
69
|
+
action="compile_app_registry",
|
|
70
|
+
)
|
|
71
|
+
return module_path.strip()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _normalize_app_path(module_path: str) -> str:
|
|
75
|
+
value = _read_app_module_path(module_path, owner="apps")
|
|
76
|
+
if ":" in value or value.endswith(".apps"):
|
|
77
|
+
raise PzError(
|
|
78
|
+
code="PZ_APP_INVALID_PATH",
|
|
79
|
+
message=(
|
|
80
|
+
f"App path '{value}' must declare a directory/package name only; "
|
|
81
|
+
"installed_apps does not accept '<pkg>.apps' or '<pkg>.apps:app'."
|
|
82
|
+
),
|
|
83
|
+
stage="apps",
|
|
84
|
+
owner=value,
|
|
85
|
+
action="compile_app_registry",
|
|
86
|
+
)
|
|
87
|
+
return f"{value}.apps:app"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _import_app_module(entrypoint: str) -> tuple[ModuleType, Any]:
|
|
91
|
+
module_name, _, attr_name = entrypoint.partition(":")
|
|
92
|
+
try:
|
|
93
|
+
module = import_module(module_name)
|
|
94
|
+
except Exception as exc:
|
|
95
|
+
raise PzError(
|
|
96
|
+
code="PZ_APP_IMPORT_FAILED",
|
|
97
|
+
message=f"Failed to import app module '{module_name}': {exc}",
|
|
98
|
+
stage="apps",
|
|
99
|
+
owner=module_name,
|
|
100
|
+
action="compile_app_registry",
|
|
101
|
+
) from exc
|
|
102
|
+
try:
|
|
103
|
+
app = getattr(module, attr_name)
|
|
104
|
+
except AttributeError as exc:
|
|
105
|
+
raise PzError(
|
|
106
|
+
code="PZ_APP_ENTRY_MISSING",
|
|
107
|
+
message=f"App module '{module_name}' does not expose '{attr_name}'.",
|
|
108
|
+
stage="apps",
|
|
109
|
+
owner=module_name,
|
|
110
|
+
action="compile_app_registry",
|
|
111
|
+
) from exc
|
|
112
|
+
return module, app
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def load_app_object_from_decl(decl: AppDecl) -> Any:
|
|
116
|
+
_, app = _import_app_module(decl.entrypoint)
|
|
117
|
+
return app
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def load_app_entrypoint_object_from_decl(decl: AppDecl) -> Any:
|
|
121
|
+
try:
|
|
122
|
+
module = import_app_entrypoint_module_from_decl(
|
|
123
|
+
decl.module_path,
|
|
124
|
+
action="load_app_entrypoint_object",
|
|
125
|
+
)
|
|
126
|
+
except PzError:
|
|
127
|
+
raise
|
|
128
|
+
attr_name = decl.entrypoint.partition(":")[2] or "app"
|
|
129
|
+
try:
|
|
130
|
+
return getattr(module, attr_name)
|
|
131
|
+
except AttributeError as exc:
|
|
132
|
+
raise PzError(
|
|
133
|
+
code="PZ_APP_ENTRY_MISSING",
|
|
134
|
+
message=f"App module '{decl.module_path}.apps' does not expose '{attr_name}'.",
|
|
135
|
+
stage="apps",
|
|
136
|
+
owner=decl.module_path,
|
|
137
|
+
action="load_app_entrypoint_object",
|
|
138
|
+
) from exc
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _declaration_fingerprint(app: Any) -> tuple[object, ...]:
|
|
142
|
+
requires_apps = getattr(app, "requires_apps", ())
|
|
143
|
+
if isinstance(requires_apps, (list, tuple)):
|
|
144
|
+
normalized_requires_apps = tuple(requires_apps)
|
|
145
|
+
else:
|
|
146
|
+
normalized_requires_apps = requires_apps
|
|
147
|
+
|
|
148
|
+
required_protocols = getattr(app, "required_protocols", ())
|
|
149
|
+
if isinstance(required_protocols, (list, tuple)):
|
|
150
|
+
normalized_required_protocols = tuple(required_protocols)
|
|
151
|
+
else:
|
|
152
|
+
normalized_required_protocols = required_protocols
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
getattr(app, "id", None),
|
|
156
|
+
getattr(app, "name", None),
|
|
157
|
+
getattr(app, "version", None),
|
|
158
|
+
getattr(app, "required_pz", None),
|
|
159
|
+
getattr(app, "priority", None),
|
|
160
|
+
normalized_requires_apps,
|
|
161
|
+
normalized_required_protocols,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _read_required_str(app: Any, field_name: str, *, owner: str) -> str:
|
|
166
|
+
value = getattr(app, field_name, None)
|
|
167
|
+
if not isinstance(value, str) or not value.strip():
|
|
168
|
+
raise PzError(
|
|
169
|
+
code=f"PZ_APP_{field_name.upper()}_MISSING",
|
|
170
|
+
message=f"App '{owner}' must declare non-empty '{field_name}'.",
|
|
171
|
+
stage="apps",
|
|
172
|
+
owner=owner,
|
|
173
|
+
action="compile_app_registry",
|
|
174
|
+
)
|
|
175
|
+
return value.strip()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _read_priority(app: Any, *, owner: str) -> int:
|
|
179
|
+
value = getattr(app, "priority", None)
|
|
180
|
+
if value is None:
|
|
181
|
+
return _DEFAULT_APP_PRIORITY
|
|
182
|
+
if isinstance(value, bool):
|
|
183
|
+
raise PzError(
|
|
184
|
+
code="PZ_APP_PRIORITY_INVALID",
|
|
185
|
+
message=f"App '{owner}' must declare integer priority when provided.",
|
|
186
|
+
stage="apps",
|
|
187
|
+
owner=owner,
|
|
188
|
+
action="compile_app_registry",
|
|
189
|
+
)
|
|
190
|
+
if isinstance(value, int):
|
|
191
|
+
return int(value)
|
|
192
|
+
if isinstance(value, str) and value.strip():
|
|
193
|
+
try:
|
|
194
|
+
return int(value.strip())
|
|
195
|
+
except ValueError as exc:
|
|
196
|
+
raise PzError(
|
|
197
|
+
code="PZ_APP_PRIORITY_INVALID",
|
|
198
|
+
message=f"App '{owner}' must declare integer priority when provided.",
|
|
199
|
+
stage="apps",
|
|
200
|
+
owner=owner,
|
|
201
|
+
action="compile_app_registry",
|
|
202
|
+
) from exc
|
|
203
|
+
raise PzError(
|
|
204
|
+
code="PZ_APP_PRIORITY_INVALID",
|
|
205
|
+
message=f"App '{owner}' must declare integer priority when provided.",
|
|
206
|
+
stage="apps",
|
|
207
|
+
owner=owner,
|
|
208
|
+
action="compile_app_registry",
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _read_sequence(app: Any, field_name: str) -> tuple[str, ...]:
|
|
213
|
+
value = getattr(app, field_name, ())
|
|
214
|
+
if value is None:
|
|
215
|
+
return ()
|
|
216
|
+
if not isinstance(value, (tuple, list)):
|
|
217
|
+
raise PzError(
|
|
218
|
+
code=f"PZ_APP_{field_name.upper()}_INVALID",
|
|
219
|
+
message=f"App field '{field_name}' must be declared as a list or tuple of strings.",
|
|
220
|
+
stage="apps",
|
|
221
|
+
owner=getattr(app, "id", "<unknown>"),
|
|
222
|
+
action="compile_app_registry",
|
|
223
|
+
)
|
|
224
|
+
out: list[str] = []
|
|
225
|
+
for index, item in enumerate(value):
|
|
226
|
+
if not isinstance(item, str):
|
|
227
|
+
raise PzError(
|
|
228
|
+
code=f"PZ_APP_{field_name.upper()}_ITEM_INVALID",
|
|
229
|
+
message=(
|
|
230
|
+
f"App field '{field_name}' entry at index {index} must be a non-empty string."
|
|
231
|
+
),
|
|
232
|
+
stage="apps",
|
|
233
|
+
owner=getattr(app, "id", "<unknown>"),
|
|
234
|
+
action="compile_app_registry",
|
|
235
|
+
)
|
|
236
|
+
current = item.strip()
|
|
237
|
+
if not current:
|
|
238
|
+
raise PzError(
|
|
239
|
+
code=f"PZ_APP_{field_name.upper()}_ITEM_INVALID",
|
|
240
|
+
message=(
|
|
241
|
+
f"App field '{field_name}' entry at index {index} must be a non-empty string."
|
|
242
|
+
),
|
|
243
|
+
stage="apps",
|
|
244
|
+
owner=getattr(app, "id", "<unknown>"),
|
|
245
|
+
action="compile_app_registry",
|
|
246
|
+
)
|
|
247
|
+
out.append(current)
|
|
248
|
+
return tuple(out)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _parse_version_tuple(value: str, *, owner: str) -> tuple[int, ...]:
|
|
252
|
+
normalized = value.strip()
|
|
253
|
+
if not normalized:
|
|
254
|
+
raise PzError(
|
|
255
|
+
code="PZ_APP_REQUIRED_PZ_INVALID",
|
|
256
|
+
message=f"App '{owner}' declares an empty required_pz version segment.",
|
|
257
|
+
stage="apps",
|
|
258
|
+
owner=owner,
|
|
259
|
+
action="compile_app_registry",
|
|
260
|
+
)
|
|
261
|
+
parts: list[int] = []
|
|
262
|
+
for raw in normalized.split("."):
|
|
263
|
+
segment = raw.strip()
|
|
264
|
+
if not segment or not segment.isdigit():
|
|
265
|
+
raise PzError(
|
|
266
|
+
code="PZ_APP_REQUIRED_PZ_INVALID",
|
|
267
|
+
message=(
|
|
268
|
+
f"App '{owner}' declares invalid required_pz version segment '{raw}'. "
|
|
269
|
+
"Use dot-separated numeric versions."
|
|
270
|
+
),
|
|
271
|
+
stage="apps",
|
|
272
|
+
owner=owner,
|
|
273
|
+
action="compile_app_registry",
|
|
274
|
+
)
|
|
275
|
+
parts.append(int(segment))
|
|
276
|
+
return tuple(parts)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _compare_versions(left: str, right: str, *, owner: str) -> int:
|
|
280
|
+
l_parts = list(_parse_version_tuple(left, owner=owner))
|
|
281
|
+
r_parts = list(_parse_version_tuple(right, owner=owner))
|
|
282
|
+
width = max(len(l_parts), len(r_parts), 1)
|
|
283
|
+
l_parts.extend([0] * (width - len(l_parts)))
|
|
284
|
+
r_parts.extend([0] * (width - len(r_parts)))
|
|
285
|
+
if tuple(l_parts) < tuple(r_parts):
|
|
286
|
+
return -1
|
|
287
|
+
if tuple(l_parts) > tuple(r_parts):
|
|
288
|
+
return 1
|
|
289
|
+
return 0
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _satisfies_required_pz(required_pz: str, *, current_pz: str, owner: str) -> bool:
|
|
293
|
+
for raw_spec in required_pz.split(","):
|
|
294
|
+
spec = raw_spec.strip()
|
|
295
|
+
if not spec:
|
|
296
|
+
continue
|
|
297
|
+
for operator in (">=", "<=", "==", "!=", ">", "<"):
|
|
298
|
+
if spec.startswith(operator):
|
|
299
|
+
target = spec[len(operator):].strip()
|
|
300
|
+
if not target:
|
|
301
|
+
raise PzError(
|
|
302
|
+
code="PZ_APP_REQUIRED_PZ_INVALID",
|
|
303
|
+
message=(
|
|
304
|
+
f"App '{owner}' declares invalid required_pz constraint "
|
|
305
|
+
f"'{required_pz}'."
|
|
306
|
+
),
|
|
307
|
+
stage="apps",
|
|
308
|
+
owner=owner,
|
|
309
|
+
action="compile_app_registry",
|
|
310
|
+
)
|
|
311
|
+
cmp = _compare_versions(current_pz, target, owner=owner)
|
|
312
|
+
if operator == ">=":
|
|
313
|
+
ok = cmp >= 0
|
|
314
|
+
elif operator == "<=":
|
|
315
|
+
ok = cmp <= 0
|
|
316
|
+
elif operator == "==":
|
|
317
|
+
ok = cmp == 0
|
|
318
|
+
elif operator == "!=":
|
|
319
|
+
ok = cmp != 0
|
|
320
|
+
elif operator == ">":
|
|
321
|
+
ok = cmp > 0
|
|
322
|
+
else:
|
|
323
|
+
ok = cmp < 0
|
|
324
|
+
if not ok:
|
|
325
|
+
return False
|
|
326
|
+
break
|
|
327
|
+
else:
|
|
328
|
+
raise PzError(
|
|
329
|
+
code="PZ_APP_REQUIRED_PZ_INVALID",
|
|
330
|
+
message=(
|
|
331
|
+
f"App '{owner}' declares invalid required_pz constraint "
|
|
332
|
+
f"'{required_pz}'."
|
|
333
|
+
),
|
|
334
|
+
stage="apps",
|
|
335
|
+
owner=owner,
|
|
336
|
+
action="compile_app_registry",
|
|
337
|
+
)
|
|
338
|
+
return True
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _validate_protocol_names(app: AppDecl) -> None:
|
|
342
|
+
invalid = tuple(
|
|
343
|
+
item for item in app.required_protocols if ":" in item or "/" in item or "\\" in item
|
|
344
|
+
)
|
|
345
|
+
if invalid:
|
|
346
|
+
raise PzError(
|
|
347
|
+
code="PZ_APP_PROTOCOL_INVALID",
|
|
348
|
+
message=(
|
|
349
|
+
f"App '{app.app_id}' has invalid required_protocols entries: "
|
|
350
|
+
+ ", ".join(invalid)
|
|
351
|
+
),
|
|
352
|
+
stage="apps",
|
|
353
|
+
owner=app.app_id,
|
|
354
|
+
action="compile_app_registry",
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def _run_init_hook(
|
|
359
|
+
decl: AppDecl,
|
|
360
|
+
app_obj: Any,
|
|
361
|
+
*,
|
|
362
|
+
settings: SettingsSnapshot,
|
|
363
|
+
project_dir: Path | None,
|
|
364
|
+
) -> None:
|
|
365
|
+
hook = getattr(app_obj, "init", None)
|
|
366
|
+
if hook is None:
|
|
367
|
+
return
|
|
368
|
+
if not callable(hook):
|
|
369
|
+
raise PzError(
|
|
370
|
+
code="PZ_APP_INIT_INVALID",
|
|
371
|
+
message=f"App '{decl.app_id}' exposes non-callable hook 'init'.",
|
|
372
|
+
stage="apps",
|
|
373
|
+
owner=decl.app_id,
|
|
374
|
+
action="compile_app_registry",
|
|
375
|
+
)
|
|
376
|
+
before = _declaration_fingerprint(app_obj)
|
|
377
|
+
signature = inspect.signature(hook)
|
|
378
|
+
param_count = len(signature.parameters)
|
|
379
|
+
context = AppInitContext(app=decl, settings=settings, project_dir=project_dir)
|
|
380
|
+
if param_count == 0:
|
|
381
|
+
result = hook()
|
|
382
|
+
elif param_count == 1:
|
|
383
|
+
result = hook(context)
|
|
384
|
+
else:
|
|
385
|
+
raise PzError(
|
|
386
|
+
code="PZ_APP_INIT_SIGNATURE_INVALID",
|
|
387
|
+
message=f"App '{decl.app_id}' init() must accept zero or one argument.",
|
|
388
|
+
stage="apps",
|
|
389
|
+
owner=decl.app_id,
|
|
390
|
+
action="compile_app_registry",
|
|
391
|
+
)
|
|
392
|
+
try:
|
|
393
|
+
if inspect.isawaitable(result):
|
|
394
|
+
try:
|
|
395
|
+
asyncio.get_running_loop()
|
|
396
|
+
except RuntimeError:
|
|
397
|
+
asyncio.run(result)
|
|
398
|
+
else:
|
|
399
|
+
raise PzError(
|
|
400
|
+
code="PZ_APP_INIT_ASYNC_IN_RUNNING_LOOP",
|
|
401
|
+
message=(
|
|
402
|
+
f"App '{decl.app_id}' init() returned an awaitable inside a running loop. "
|
|
403
|
+
"Compile-time init must complete synchronously."
|
|
404
|
+
),
|
|
405
|
+
stage="apps",
|
|
406
|
+
owner=decl.app_id,
|
|
407
|
+
action="compile_app_registry",
|
|
408
|
+
)
|
|
409
|
+
except PzError:
|
|
410
|
+
raise
|
|
411
|
+
except Exception as exc:
|
|
412
|
+
raise PzError(
|
|
413
|
+
code="PZ_APP_INIT_FAILED",
|
|
414
|
+
message=f"App '{decl.app_id}' init() failed: {exc}",
|
|
415
|
+
stage="apps",
|
|
416
|
+
owner=decl.app_id,
|
|
417
|
+
action="compile_app_registry",
|
|
418
|
+
) from exc
|
|
419
|
+
after = _declaration_fingerprint(app_obj)
|
|
420
|
+
if after != before:
|
|
421
|
+
raise PzError(
|
|
422
|
+
code="PZ_APP_INIT_MUTATES_DECLARATION",
|
|
423
|
+
message=(
|
|
424
|
+
f"App '{decl.app_id}' init() mutated formal declaration fields. "
|
|
425
|
+
"Declaration metadata must stay stable across declared -> compiled."
|
|
426
|
+
),
|
|
427
|
+
stage="apps",
|
|
428
|
+
owner=decl.app_id,
|
|
429
|
+
action="compile_app_registry",
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def _load_decl(module_path: str, *, current_pz: str) -> AppDecl:
|
|
434
|
+
module_path = _read_app_module_path(module_path, owner="apps")
|
|
435
|
+
entrypoint = _normalize_app_path(module_path)
|
|
436
|
+
try:
|
|
437
|
+
static_decl = read_static_app_declaration(module_path, action="compile_app_registry")
|
|
438
|
+
except PzError as exc:
|
|
439
|
+
if exc.code == "PZ_APP_DECLARATION_NOT_FOUND":
|
|
440
|
+
raise PzError(
|
|
441
|
+
code="PZ_APP_IMPORT_FAILED",
|
|
442
|
+
message=f"Failed to locate app module '{module_path}.apps'.",
|
|
443
|
+
stage="apps",
|
|
444
|
+
owner=module_path,
|
|
445
|
+
action="compile_app_registry",
|
|
446
|
+
detail=exc.detail,
|
|
447
|
+
) from exc
|
|
448
|
+
raise
|
|
449
|
+
app = SimpleNamespace(
|
|
450
|
+
id=static_decl.app_id,
|
|
451
|
+
name=static_decl.name,
|
|
452
|
+
version=static_decl.version,
|
|
453
|
+
required_pz=static_decl.required_pz,
|
|
454
|
+
priority=static_decl.priority,
|
|
455
|
+
requires_apps=static_decl.requires_apps,
|
|
456
|
+
required_protocols=static_decl.required_protocols,
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
app_id = _read_declared_app_id(app, module_path=module_path)
|
|
460
|
+
decl = AppDecl(
|
|
461
|
+
app_id=app_id,
|
|
462
|
+
name=_read_required_str(app, "name", owner=app_id),
|
|
463
|
+
version=_read_required_str(app, "version", owner=app_id),
|
|
464
|
+
required_pz=_read_required_str(app, "required_pz", owner=app_id),
|
|
465
|
+
module_path=module_path,
|
|
466
|
+
entrypoint=entrypoint,
|
|
467
|
+
owner=app_id,
|
|
468
|
+
priority=_read_priority(app, owner=app_id),
|
|
469
|
+
requires_apps=_read_sequence(app, "requires_apps"),
|
|
470
|
+
required_protocols=_read_sequence(app, "required_protocols"),
|
|
471
|
+
)
|
|
472
|
+
_validate_protocol_names(decl)
|
|
473
|
+
if not _satisfies_required_pz(
|
|
474
|
+
decl.required_pz,
|
|
475
|
+
current_pz=current_pz,
|
|
476
|
+
owner=decl.app_id,
|
|
477
|
+
):
|
|
478
|
+
raise PzError(
|
|
479
|
+
code="PZ_APP_REQUIRED_PZ_MISMATCH",
|
|
480
|
+
message=(
|
|
481
|
+
f"App '{decl.app_id}' requires pz '{decl.required_pz}', "
|
|
482
|
+
f"current pz is '{current_pz}'."
|
|
483
|
+
),
|
|
484
|
+
stage="apps",
|
|
485
|
+
owner=decl.app_id,
|
|
486
|
+
action="compile_app_registry",
|
|
487
|
+
)
|
|
488
|
+
return decl
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def _read_declared_app_id(app: Any, *, module_path: str) -> str:
|
|
492
|
+
candidate = getattr(app, "id", None)
|
|
493
|
+
if isinstance(candidate, str) and candidate.strip():
|
|
494
|
+
app_id = candidate.strip()
|
|
495
|
+
else:
|
|
496
|
+
app_id = ""
|
|
497
|
+
if not app_id:
|
|
498
|
+
raise PzError(
|
|
499
|
+
code="PZ_APP_ID_MISSING",
|
|
500
|
+
message=f"App '{module_path}' must explicitly declare app.id.",
|
|
501
|
+
stage="apps",
|
|
502
|
+
owner=module_path,
|
|
503
|
+
action="compile_app_registry",
|
|
504
|
+
)
|
|
505
|
+
if not app_id.isidentifier():
|
|
506
|
+
raise PzError(
|
|
507
|
+
code="PZ_APP_ID_INVALID",
|
|
508
|
+
message=f"App '{module_path}' must declare a valid app.id identifier.",
|
|
509
|
+
stage="apps",
|
|
510
|
+
owner=module_path,
|
|
511
|
+
action="compile_app_registry",
|
|
512
|
+
)
|
|
513
|
+
return app_id
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def _validate_replacements(
|
|
517
|
+
installed_by_path: dict[str, AppDecl],
|
|
518
|
+
app_replacements: dict[str, str],
|
|
519
|
+
) -> tuple[dict[str, str], set[str]]:
|
|
520
|
+
target_to_replacement: dict[str, str] = {}
|
|
521
|
+
replacement_sources: set[str] = set()
|
|
522
|
+
for target_path, replacement_path in app_replacements.items():
|
|
523
|
+
if target_path not in installed_by_path:
|
|
524
|
+
raise PzError(
|
|
525
|
+
code="PZ_APP_REPLACEMENT_TARGET_MISSING",
|
|
526
|
+
message=f"Replacement target '{target_path}' is not in installed_apps.",
|
|
527
|
+
stage="apps",
|
|
528
|
+
owner=target_path,
|
|
529
|
+
action="compile_app_registry",
|
|
530
|
+
)
|
|
531
|
+
if replacement_path not in installed_by_path:
|
|
532
|
+
raise PzError(
|
|
533
|
+
code="PZ_APP_REPLACEMENT_SOURCE_MISSING",
|
|
534
|
+
message=f"Replacement source '{replacement_path}' is not in installed_apps.",
|
|
535
|
+
stage="apps",
|
|
536
|
+
owner=replacement_path,
|
|
537
|
+
action="compile_app_registry",
|
|
538
|
+
)
|
|
539
|
+
if target_path in target_to_replacement:
|
|
540
|
+
raise PzError(
|
|
541
|
+
code="PZ_APP_REPLACEMENT_DUPLICATE_TARGET",
|
|
542
|
+
message=f"Replacement target '{target_path}' is declared more than once.",
|
|
543
|
+
stage="apps",
|
|
544
|
+
owner=target_path,
|
|
545
|
+
action="compile_app_registry",
|
|
546
|
+
)
|
|
547
|
+
if replacement_path in replacement_sources:
|
|
548
|
+
raise PzError(
|
|
549
|
+
code="PZ_APP_REPLACEMENT_DUPLICATE_SOURCE",
|
|
550
|
+
message=f"Replacement source '{replacement_path}' replaces more than one app.",
|
|
551
|
+
stage="apps",
|
|
552
|
+
owner=replacement_path,
|
|
553
|
+
action="compile_app_registry",
|
|
554
|
+
)
|
|
555
|
+
if target_path == replacement_path:
|
|
556
|
+
raise PzError(
|
|
557
|
+
code="PZ_APP_REPLACEMENT_SELF_REFERENCE",
|
|
558
|
+
message=f"App '{target_path}' cannot replace itself.",
|
|
559
|
+
stage="apps",
|
|
560
|
+
owner=target_path,
|
|
561
|
+
action="compile_app_registry",
|
|
562
|
+
)
|
|
563
|
+
target_to_replacement[target_path] = replacement_path
|
|
564
|
+
replacement_sources.add(replacement_path)
|
|
565
|
+
for target_path, replacement_path in target_to_replacement.items():
|
|
566
|
+
current = replacement_path
|
|
567
|
+
seen = {target_path}
|
|
568
|
+
while current in target_to_replacement:
|
|
569
|
+
if current in seen:
|
|
570
|
+
raise PzError(
|
|
571
|
+
code="PZ_APP_REPLACEMENT_CYCLE",
|
|
572
|
+
message="app_replacements must be acyclic.",
|
|
573
|
+
stage="apps",
|
|
574
|
+
owner=target_path,
|
|
575
|
+
action="compile_app_registry",
|
|
576
|
+
)
|
|
577
|
+
seen.add(current)
|
|
578
|
+
current = target_to_replacement[current]
|
|
579
|
+
target_decl = installed_by_path[target_path]
|
|
580
|
+
replacement_decl = installed_by_path[replacement_path]
|
|
581
|
+
if target_decl.app_id != replacement_decl.app_id:
|
|
582
|
+
raise PzError(
|
|
583
|
+
code="PZ_APP_REPLACEMENT_ID_MISMATCH",
|
|
584
|
+
message=(
|
|
585
|
+
f"Replacement '{replacement_path}' must keep app_id '{target_decl.app_id}', "
|
|
586
|
+
f"got '{replacement_decl.app_id}'."
|
|
587
|
+
),
|
|
588
|
+
stage="apps",
|
|
589
|
+
owner=replacement_decl.app_id,
|
|
590
|
+
action="compile_app_registry",
|
|
591
|
+
)
|
|
592
|
+
return target_to_replacement, replacement_sources
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def _toposort_apps(apps: tuple[AppDecl, ...]) -> tuple[AppDecl, ...]:
|
|
596
|
+
seen_ids: set[str] = set()
|
|
597
|
+
for app in apps:
|
|
598
|
+
if app.app_id in seen_ids:
|
|
599
|
+
raise PzError(
|
|
600
|
+
code="PZ_APP_DUPLICATE_ID",
|
|
601
|
+
message=f"Duplicate app_id '{app.app_id}' in installed_apps.",
|
|
602
|
+
stage="apps",
|
|
603
|
+
owner=app.app_id,
|
|
604
|
+
action="compile_app_registry",
|
|
605
|
+
)
|
|
606
|
+
seen_ids.add(app.app_id)
|
|
607
|
+
by_id = {app.app_id: app for app in apps}
|
|
608
|
+
incoming: dict[str, set[str]] = {app.app_id: set() for app in apps}
|
|
609
|
+
outgoing: dict[str, set[str]] = {app.app_id: set() for app in apps}
|
|
610
|
+
order_index = {app.app_id: idx for idx, app in enumerate(apps)}
|
|
611
|
+
|
|
612
|
+
def _sort_key(app_id: str) -> tuple[int, int]:
|
|
613
|
+
current = by_id[app_id]
|
|
614
|
+
return (int(current.priority), order_index[app_id])
|
|
615
|
+
|
|
616
|
+
for app in apps:
|
|
617
|
+
for dep_id in app.requires_apps:
|
|
618
|
+
if dep_id not in by_id:
|
|
619
|
+
raise PzError(
|
|
620
|
+
code="PZ_APP_DEPENDENCY_MISSING",
|
|
621
|
+
message=f"App '{app.app_id}' requires missing app '{dep_id}'.",
|
|
622
|
+
stage="apps",
|
|
623
|
+
owner=app.app_id,
|
|
624
|
+
action="compile_app_registry",
|
|
625
|
+
)
|
|
626
|
+
incoming[app.app_id].add(dep_id)
|
|
627
|
+
outgoing[dep_id].add(app.app_id)
|
|
628
|
+
|
|
629
|
+
ready = [app_id for app_id, deps in incoming.items() if not deps]
|
|
630
|
+
ready.sort(key=_sort_key)
|
|
631
|
+
out: list[AppDecl] = []
|
|
632
|
+
while ready:
|
|
633
|
+
current_id = ready.pop(0)
|
|
634
|
+
out.append(by_id[current_id])
|
|
635
|
+
for dependent_id in sorted(outgoing[current_id], key=_sort_key):
|
|
636
|
+
incoming[dependent_id].discard(current_id)
|
|
637
|
+
if not incoming[dependent_id] and dependent_id not in [item.app_id for item in out] and dependent_id not in ready:
|
|
638
|
+
ready.append(dependent_id)
|
|
639
|
+
ready.sort(key=_sort_key)
|
|
640
|
+
|
|
641
|
+
if len(out) != len(apps):
|
|
642
|
+
raise PzError(
|
|
643
|
+
code="PZ_APP_DEPENDENCY_CYCLE",
|
|
644
|
+
message="App dependency graph contains a cycle.",
|
|
645
|
+
stage="apps",
|
|
646
|
+
owner="apps",
|
|
647
|
+
action="compile_app_registry",
|
|
648
|
+
)
|
|
649
|
+
return tuple(out)
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
def _load_active_apps(
|
|
653
|
+
settings: SettingsSnapshot,
|
|
654
|
+
*,
|
|
655
|
+
project_dir: Path | str | None = None,
|
|
656
|
+
) -> tuple[AppDecl, ...]:
|
|
657
|
+
current_pz = _loaded_pz_version()
|
|
658
|
+
resolved_project_dir = Path(project_dir).resolve() if project_dir is not None else None
|
|
659
|
+
import_context_project_dir = resolved_project_dir or settings.project_dir
|
|
660
|
+
with active_project_import_root(
|
|
661
|
+
import_context_project_dir,
|
|
662
|
+
extra_import_roots=getattr(settings, "import_roots", ()),
|
|
663
|
+
preserve_loaded_modules=True,
|
|
664
|
+
):
|
|
665
|
+
loaded_apps: list[AppDecl] = []
|
|
666
|
+
seen_paths: set[str] = set()
|
|
667
|
+
for module_path in settings.installed_apps:
|
|
668
|
+
normalized_path = _read_app_module_path(module_path, owner="installed_apps")
|
|
669
|
+
discovered_paths = discover_leaf_app_packages(
|
|
670
|
+
normalized_path,
|
|
671
|
+
project_dir=import_context_project_dir,
|
|
672
|
+
) or (normalized_path,)
|
|
673
|
+
for discovered_path in discovered_paths:
|
|
674
|
+
if discovered_path in seen_paths:
|
|
675
|
+
raise PzError(
|
|
676
|
+
code="PZ_APP_DUPLICATE_PATH",
|
|
677
|
+
message=f"Duplicate app path '{discovered_path}' in installed_apps.",
|
|
678
|
+
stage="apps",
|
|
679
|
+
owner=discovered_path,
|
|
680
|
+
action="compile_app_registry",
|
|
681
|
+
)
|
|
682
|
+
seen_paths.add(discovered_path)
|
|
683
|
+
decl = _load_decl(discovered_path, current_pz=current_pz)
|
|
684
|
+
loaded_apps.append(decl)
|
|
685
|
+
|
|
686
|
+
installed_by_path = {app.module_path: app for app in loaded_apps}
|
|
687
|
+
replacements, _ = _validate_replacements(installed_by_path, settings.app_replacements)
|
|
688
|
+
return tuple(app for app in loaded_apps if app.module_path not in replacements)
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
def compile_conventional_app_order(
|
|
692
|
+
settings: SettingsSnapshot,
|
|
693
|
+
*,
|
|
694
|
+
project_dir: Path | str | None = None,
|
|
695
|
+
) -> tuple[str, ...]:
|
|
696
|
+
return tuple(app.module_path for app in _toposort_apps(_load_active_apps(settings, project_dir=project_dir)))
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
def merge_explicit_app_order(
|
|
700
|
+
existing: tuple[str, ...] | list[str],
|
|
701
|
+
conventional: tuple[str, ...] | list[str],
|
|
702
|
+
) -> tuple[str, ...]:
|
|
703
|
+
conventional_items = tuple(str(item).strip() for item in conventional if str(item).strip())
|
|
704
|
+
conventional_set = set(conventional_items)
|
|
705
|
+
existing_items = tuple(
|
|
706
|
+
str(item).strip()
|
|
707
|
+
for item in existing
|
|
708
|
+
if str(item).strip() and str(item).strip() in conventional_set
|
|
709
|
+
)
|
|
710
|
+
if not existing_items:
|
|
711
|
+
return conventional_items
|
|
712
|
+
|
|
713
|
+
existing_set = set(existing_items)
|
|
714
|
+
inserted: set[str] = set()
|
|
715
|
+
out: list[str] = []
|
|
716
|
+
cursor = 0
|
|
717
|
+
|
|
718
|
+
for current in existing_items:
|
|
719
|
+
while cursor < len(conventional_items) and conventional_items[cursor] != current:
|
|
720
|
+
candidate = conventional_items[cursor]
|
|
721
|
+
cursor += 1
|
|
722
|
+
if candidate in existing_set or candidate in inserted:
|
|
723
|
+
continue
|
|
724
|
+
out.append(candidate)
|
|
725
|
+
inserted.add(candidate)
|
|
726
|
+
if cursor < len(conventional_items) and conventional_items[cursor] == current:
|
|
727
|
+
cursor += 1
|
|
728
|
+
out.append(current)
|
|
729
|
+
|
|
730
|
+
while cursor < len(conventional_items):
|
|
731
|
+
candidate = conventional_items[cursor]
|
|
732
|
+
cursor += 1
|
|
733
|
+
if candidate in existing_set or candidate in inserted:
|
|
734
|
+
continue
|
|
735
|
+
out.append(candidate)
|
|
736
|
+
inserted.add(candidate)
|
|
737
|
+
return tuple(out)
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
def _validate_explicit_app_order(apps: tuple[AppDecl, ...]) -> None:
|
|
741
|
+
_toposort_apps(apps)
|
|
742
|
+
explicit_index = {app.app_id: idx for idx, app in enumerate(apps)}
|
|
743
|
+
for app in apps:
|
|
744
|
+
for dep_id in app.requires_apps:
|
|
745
|
+
dep_index = explicit_index.get(dep_id)
|
|
746
|
+
app_index = explicit_index.get(app.app_id)
|
|
747
|
+
if dep_index is None or app_index is None or dep_index < app_index:
|
|
748
|
+
continue
|
|
749
|
+
raise PzError(
|
|
750
|
+
code="PZ_APP_DEPENDENCY_ORDER_INVALID",
|
|
751
|
+
message=(
|
|
752
|
+
f"App '{app.app_id}' must appear after required app '{dep_id}' in installed_apps."
|
|
753
|
+
),
|
|
754
|
+
stage="apps",
|
|
755
|
+
owner=app.app_id,
|
|
756
|
+
action="compile_app_registry",
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
def _declared_installed_apps(
|
|
761
|
+
settings: SettingsSnapshot,
|
|
762
|
+
) -> tuple[str, ...]:
|
|
763
|
+
declared = tuple(str(item).strip() for item in getattr(settings, "declared_installed_apps", ()) if str(item).strip())
|
|
764
|
+
if declared:
|
|
765
|
+
return declared
|
|
766
|
+
return tuple(str(item).strip() for item in settings.installed_apps if str(item).strip())
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
def _uses_explicit_leaf_app_sequence(
|
|
770
|
+
settings: SettingsSnapshot,
|
|
771
|
+
*,
|
|
772
|
+
project_dir: Path | None,
|
|
773
|
+
) -> bool:
|
|
774
|
+
declared_apps = _declared_installed_apps(settings)
|
|
775
|
+
if not declared_apps:
|
|
776
|
+
return True
|
|
777
|
+
discovery_project_dir = project_dir if project_dir is not None else settings.project_dir
|
|
778
|
+
for module_path in declared_apps:
|
|
779
|
+
discovered_paths = discover_leaf_app_packages(module_path, project_dir=discovery_project_dir)
|
|
780
|
+
if not discovered_paths:
|
|
781
|
+
continue
|
|
782
|
+
if len(discovered_paths) != 1 or discovered_paths[0] != module_path:
|
|
783
|
+
return False
|
|
784
|
+
return True
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
def compile_app_registry(
|
|
788
|
+
settings: SettingsSnapshot,
|
|
789
|
+
*,
|
|
790
|
+
project_dir: Path | str | None = None,
|
|
791
|
+
run_init_hooks: bool = True,
|
|
792
|
+
) -> AppRegistry:
|
|
793
|
+
resolved_project_dir = Path(project_dir).resolve() if project_dir is not None else None
|
|
794
|
+
validate_installed_app_identities(
|
|
795
|
+
(*_declared_installed_apps(settings), *tuple(settings.installed_apps))
|
|
796
|
+
)
|
|
797
|
+
active_apps = _load_active_apps(settings, project_dir=resolved_project_dir)
|
|
798
|
+
validate_installed_app_identities(app.app_id for app in active_apps)
|
|
799
|
+
if _uses_explicit_leaf_app_sequence(settings, project_dir=resolved_project_dir):
|
|
800
|
+
_validate_explicit_app_order(active_apps)
|
|
801
|
+
ordered_apps = active_apps
|
|
802
|
+
else:
|
|
803
|
+
ordered_apps = _toposort_apps(active_apps)
|
|
804
|
+
import_context_project_dir = resolved_project_dir or settings.project_dir
|
|
805
|
+
with active_project_import_root(
|
|
806
|
+
import_context_project_dir,
|
|
807
|
+
extra_import_roots=getattr(settings, "import_roots", ()),
|
|
808
|
+
preserve_loaded_modules=True,
|
|
809
|
+
):
|
|
810
|
+
initialized_app_ids: list[str] = []
|
|
811
|
+
if run_init_hooks:
|
|
812
|
+
for decl in ordered_apps:
|
|
813
|
+
app_obj = load_app_object_from_decl(decl)
|
|
814
|
+
_run_init_hook(
|
|
815
|
+
decl,
|
|
816
|
+
app_obj,
|
|
817
|
+
settings=settings,
|
|
818
|
+
project_dir=resolved_project_dir,
|
|
819
|
+
)
|
|
820
|
+
initialized_app_ids.append(decl.app_id)
|
|
821
|
+
return AppRegistry(
|
|
822
|
+
apps=ordered_apps,
|
|
823
|
+
initialized_app_ids=tuple(initialized_app_ids),
|
|
824
|
+
)
|