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,227 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
import importlib
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Iterable
|
|
9
|
+
|
|
10
|
+
from pz_core.assembly.app_discovery import iter_visible_app_projections
|
|
11
|
+
from pz_core.assembly.project.context import ProjectContext
|
|
12
|
+
from pz_core.core.errors import PzError
|
|
13
|
+
from pz_core.runtime.environment_integrity import (
|
|
14
|
+
validate_current_environment,
|
|
15
|
+
validate_project_config_identity,
|
|
16
|
+
)
|
|
17
|
+
from pz_core.utils.project_jsonc import PROJECT_JSONC_NAME
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _is_project_root(candidate: Path) -> bool:
|
|
21
|
+
return (candidate / PROJECT_JSONC_NAME).is_file()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _is_under_source_root(candidate: Path) -> bool:
|
|
25
|
+
return ".sources" in candidate.parts
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _resolve_project_root(candidate: Path) -> Path:
|
|
29
|
+
expanded = candidate.expanduser()
|
|
30
|
+
search_roots: list[Path] = []
|
|
31
|
+
if expanded.is_absolute():
|
|
32
|
+
search_roots.append(expanded)
|
|
33
|
+
else:
|
|
34
|
+
search_roots.append((Path.cwd() / expanded).absolute())
|
|
35
|
+
resolved = expanded.resolve()
|
|
36
|
+
if resolved not in search_roots:
|
|
37
|
+
search_roots.append(resolved)
|
|
38
|
+
for search_root in search_roots:
|
|
39
|
+
for current in (search_root, *search_root.parents):
|
|
40
|
+
validate_project_config_identity(current)
|
|
41
|
+
if _is_project_root(current):
|
|
42
|
+
return current.resolve()
|
|
43
|
+
if any(_is_under_source_root(root) for root in search_roots):
|
|
44
|
+
raise PzError(
|
|
45
|
+
code="PZ_PROJECT_DIR_REQUIRED_FOR_SOURCE_ROOT",
|
|
46
|
+
message=(
|
|
47
|
+
"Cannot infer project_dir from a package source root. "
|
|
48
|
+
f"Set PZ_PROJECT_DIR or pass an explicit project_dir that resolves to a project with {PROJECT_JSONC_NAME}."
|
|
49
|
+
),
|
|
50
|
+
stage="project",
|
|
51
|
+
owner="assembly.project",
|
|
52
|
+
action="resolve_project_context",
|
|
53
|
+
)
|
|
54
|
+
return resolved
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _discover_project_dir_from_cwd() -> Path:
|
|
58
|
+
return _resolve_project_root(Path.cwd())
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _read_project_dir_source(project_dir: str | os.PathLike[str]) -> str:
|
|
62
|
+
if not isinstance(project_dir, (str, os.PathLike)):
|
|
63
|
+
raise PzError(
|
|
64
|
+
code="PZ_PROJECT_DIR_INVALID",
|
|
65
|
+
message="project_dir must be a non-empty path when provided.",
|
|
66
|
+
stage="project",
|
|
67
|
+
owner="assembly.project",
|
|
68
|
+
action="resolve_project_context",
|
|
69
|
+
)
|
|
70
|
+
value = os.fspath(project_dir)
|
|
71
|
+
if not isinstance(value, str) or not value.strip():
|
|
72
|
+
raise PzError(
|
|
73
|
+
code="PZ_PROJECT_DIR_INVALID",
|
|
74
|
+
message="project_dir must be a non-empty path when provided.",
|
|
75
|
+
stage="project",
|
|
76
|
+
owner="assembly.project",
|
|
77
|
+
action="resolve_project_context",
|
|
78
|
+
)
|
|
79
|
+
return value
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _read_project_dir_env(name: str) -> str | None:
|
|
83
|
+
raw = os.getenv(name)
|
|
84
|
+
if raw is None:
|
|
85
|
+
return None
|
|
86
|
+
if not raw.strip():
|
|
87
|
+
raise PzError(
|
|
88
|
+
code="PZ_PROJECT_DIR_INVALID",
|
|
89
|
+
message=f"{name} must be a non-empty path when configured.",
|
|
90
|
+
stage="project",
|
|
91
|
+
owner="assembly.project",
|
|
92
|
+
action="resolve_project_context",
|
|
93
|
+
)
|
|
94
|
+
return raw.strip()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def resolve_project_context(
|
|
98
|
+
project_dir: str | os.PathLike[str] | None = None,
|
|
99
|
+
) -> ProjectContext:
|
|
100
|
+
validate_current_environment(os.environ)
|
|
101
|
+
env_project_dir = _read_project_dir_env("PZ_PROJECT_DIR")
|
|
102
|
+
source = (
|
|
103
|
+
_read_project_dir_source(project_dir)
|
|
104
|
+
if project_dir is not None
|
|
105
|
+
else env_project_dir or _discover_project_dir_from_cwd()
|
|
106
|
+
)
|
|
107
|
+
try:
|
|
108
|
+
root = _resolve_project_root(Path(source))
|
|
109
|
+
except PzError as exc:
|
|
110
|
+
if (
|
|
111
|
+
project_dir is None
|
|
112
|
+
or env_project_dir is None
|
|
113
|
+
or exc.code != "PZ_PROJECT_DIR_REQUIRED_FOR_SOURCE_ROOT"
|
|
114
|
+
):
|
|
115
|
+
raise
|
|
116
|
+
root = _resolve_project_root(Path(env_project_dir))
|
|
117
|
+
runtime_root = root / ".runtime"
|
|
118
|
+
project_config_path = root / PROJECT_JSONC_NAME
|
|
119
|
+
return ProjectContext(
|
|
120
|
+
project_dir=root,
|
|
121
|
+
project_config_path=project_config_path,
|
|
122
|
+
env_file_path=root / ".env",
|
|
123
|
+
cache_dir=runtime_root / "cache",
|
|
124
|
+
runtime_dir=runtime_root / "run",
|
|
125
|
+
asset_dir=root / ".assets",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _module_belongs_to_roots(module: object, roots: set[str]) -> bool:
|
|
130
|
+
module_file = getattr(module, "__file__", None)
|
|
131
|
+
if isinstance(module_file, str) and module_file:
|
|
132
|
+
resolved_file = str(Path(module_file).resolve())
|
|
133
|
+
for root in roots:
|
|
134
|
+
if resolved_file == root or resolved_file.startswith(root + os.sep):
|
|
135
|
+
return True
|
|
136
|
+
|
|
137
|
+
search_locations = getattr(module, "__path__", None)
|
|
138
|
+
if search_locations is None:
|
|
139
|
+
return False
|
|
140
|
+
for raw_location in search_locations:
|
|
141
|
+
resolved_location = str(Path(raw_location).resolve())
|
|
142
|
+
for root in roots:
|
|
143
|
+
if resolved_location == root or resolved_location.startswith(root + os.sep):
|
|
144
|
+
return True
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _managed_top_level_modules(
|
|
149
|
+
project_root: Path, import_roots: Iterable[Path]
|
|
150
|
+
) -> set[str]:
|
|
151
|
+
module_names: set[str] = set()
|
|
152
|
+
if (project_root / "apps" / "__init__.py").is_file():
|
|
153
|
+
module_names.add("apps")
|
|
154
|
+
module_names.update(
|
|
155
|
+
projection.module_path.split(".", 1)[0]
|
|
156
|
+
for projection in iter_visible_app_projections(project_root)
|
|
157
|
+
)
|
|
158
|
+
for import_root in import_roots:
|
|
159
|
+
resolved_import_root = import_root.resolve()
|
|
160
|
+
if (
|
|
161
|
+
not resolved_import_root.is_dir()
|
|
162
|
+
or resolved_import_root == project_root
|
|
163
|
+
or resolved_import_root.is_relative_to(project_root)
|
|
164
|
+
):
|
|
165
|
+
continue
|
|
166
|
+
for child in resolved_import_root.iterdir():
|
|
167
|
+
if child.is_dir() and (child / "__init__.py").is_file():
|
|
168
|
+
module_names.add(child.name)
|
|
169
|
+
return module_names
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@contextmanager
|
|
173
|
+
def active_project_import_root(
|
|
174
|
+
project_dir: str | os.PathLike[str] | None,
|
|
175
|
+
*,
|
|
176
|
+
extra_import_roots: Iterable[str | os.PathLike[str]] = (),
|
|
177
|
+
preserve_loaded_modules: bool = False,
|
|
178
|
+
):
|
|
179
|
+
if project_dir is None:
|
|
180
|
+
yield
|
|
181
|
+
return
|
|
182
|
+
root = Path(project_dir).expanduser().resolve()
|
|
183
|
+
normalized_roots = [root]
|
|
184
|
+
seen_roots = {str(root)}
|
|
185
|
+
for raw_root in extra_import_roots:
|
|
186
|
+
candidate = Path(raw_root).expanduser().resolve()
|
|
187
|
+
candidate_key = str(candidate)
|
|
188
|
+
if candidate_key in seen_roots:
|
|
189
|
+
continue
|
|
190
|
+
seen_roots.add(candidate_key)
|
|
191
|
+
normalized_roots.append(candidate)
|
|
192
|
+
original_path = list(sys.path)
|
|
193
|
+
managed_modules = _managed_top_level_modules(root, normalized_roots[1:])
|
|
194
|
+
saved_modules = {
|
|
195
|
+
name: module
|
|
196
|
+
for name, module in sys.modules.items()
|
|
197
|
+
if any(
|
|
198
|
+
name == target or name.startswith(target + ".")
|
|
199
|
+
for target in managed_modules
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
try:
|
|
203
|
+
root_strings = {str(item) for item in normalized_roots}
|
|
204
|
+
sys.path[:] = [entry for entry in original_path if entry not in root_strings]
|
|
205
|
+
for candidate in reversed(normalized_roots):
|
|
206
|
+
sys.path.insert(0, str(candidate))
|
|
207
|
+
for name, module in saved_modules.items():
|
|
208
|
+
if not _module_belongs_to_roots(module, root_strings):
|
|
209
|
+
sys.modules.pop(name, None)
|
|
210
|
+
importlib.invalidate_caches()
|
|
211
|
+
yield
|
|
212
|
+
finally:
|
|
213
|
+
sys.path[:] = original_path
|
|
214
|
+
if preserve_loaded_modules:
|
|
215
|
+
importlib.invalidate_caches()
|
|
216
|
+
else:
|
|
217
|
+
for name in list(sys.modules):
|
|
218
|
+
if any(
|
|
219
|
+
name == target or name.startswith(target + ".")
|
|
220
|
+
for target in managed_modules
|
|
221
|
+
):
|
|
222
|
+
sys.modules.pop(name, None)
|
|
223
|
+
sys.modules.update(saved_modules)
|
|
224
|
+
importlib.invalidate_caches()
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
__all__ = ["ProjectContext", "active_project_import_root", "resolve_project_context"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .compiler import (
|
|
4
|
+
ResourceManifest,
|
|
5
|
+
ResourceItem,
|
|
6
|
+
ResourceSource,
|
|
7
|
+
build_resource_source,
|
|
8
|
+
compile_resource_manifest,
|
|
9
|
+
discover_resource_sources,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"ResourceManifest",
|
|
14
|
+
"ResourceItem",
|
|
15
|
+
"ResourceSource",
|
|
16
|
+
"build_resource_source",
|
|
17
|
+
"compile_resource_manifest",
|
|
18
|
+
"discover_resource_sources",
|
|
19
|
+
]
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from importlib.util import find_spec
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Iterable
|
|
8
|
+
|
|
9
|
+
from pz_core.assembly.apps import AppRegistry
|
|
10
|
+
from pz_core.assembly.resources.settings import DEFAULT_RESOURCE_ITEMS, read_resource_settings
|
|
11
|
+
from pz_core.assembly.settings import SettingsSnapshot
|
|
12
|
+
from pz_core.core.errors import PzError
|
|
13
|
+
from pz_core.core.extensions import ExtensionRegistry, ResourceContributionSpec
|
|
14
|
+
from pz_core.assembly.project import ProjectContext
|
|
15
|
+
from pz_core.infra.settings import ResourceItemContract
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True, slots=True)
|
|
19
|
+
class ResourceItem:
|
|
20
|
+
name: str
|
|
21
|
+
source_dirs: tuple[Path, ...]
|
|
22
|
+
output_dir: Path
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True, slots=True)
|
|
26
|
+
class ResourceSource:
|
|
27
|
+
app_id: str
|
|
28
|
+
module_path: str
|
|
29
|
+
package_root: Path
|
|
30
|
+
items: tuple[ResourceItem, ...]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True, slots=True)
|
|
34
|
+
class ResourceManifest:
|
|
35
|
+
app_order: tuple[str, ...]
|
|
36
|
+
manifest_path: Path
|
|
37
|
+
output_root: Path
|
|
38
|
+
items: tuple[ResourceItem, ...]
|
|
39
|
+
staging_root: Path
|
|
40
|
+
lock_path: Path
|
|
41
|
+
sources: tuple[ResourceSource, ...]
|
|
42
|
+
resource_contributions: tuple[ResourceContributionSpec, ...] = ()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _resource_import_root(module_path: str, *, entrypoint: str, owner: str) -> str:
|
|
46
|
+
module_name, _, attr_name = entrypoint.partition(":")
|
|
47
|
+
if attr_name == "app" and module_name.endswith(".apps"):
|
|
48
|
+
return module_name[:-5]
|
|
49
|
+
raise PzError(
|
|
50
|
+
code="PZ_RESOURCE_APP_ENTRYPOINT_INVALID",
|
|
51
|
+
message=(
|
|
52
|
+
f"App '{owner}' entrypoint '{entrypoint}' must use '<pkg>.apps:app' "
|
|
53
|
+
"before compiling resource manifests."
|
|
54
|
+
),
|
|
55
|
+
stage="resources",
|
|
56
|
+
owner=owner,
|
|
57
|
+
action="compile_resource_manifest",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _package_root(module_path: str, *, entrypoint: str, owner: str) -> Path:
|
|
62
|
+
module_name, _, attr_name = entrypoint.partition(":")
|
|
63
|
+
if attr_name != "app" or not module_name.endswith(".apps"):
|
|
64
|
+
_resource_import_root(module_path, entrypoint=entrypoint, owner=owner)
|
|
65
|
+
raise PzError(
|
|
66
|
+
code="PZ_RESOURCE_APP_ENTRYPOINT_INVALID",
|
|
67
|
+
message=(
|
|
68
|
+
f"App '{owner}' entrypoint '{entrypoint}' must use '<pkg>.apps:app' "
|
|
69
|
+
"before compiling resource manifests."
|
|
70
|
+
),
|
|
71
|
+
stage="resources",
|
|
72
|
+
owner=owner,
|
|
73
|
+
action="compile_resource_manifest",
|
|
74
|
+
)
|
|
75
|
+
spec = find_spec(module_name)
|
|
76
|
+
if spec is None:
|
|
77
|
+
raise PzError(
|
|
78
|
+
code="PZ_RESOURCE_APP_NOT_FOUND",
|
|
79
|
+
message=f"Cannot resolve resource package for app '{module_path}'.",
|
|
80
|
+
stage="resources",
|
|
81
|
+
owner=module_path,
|
|
82
|
+
action="compile_resource_manifest",
|
|
83
|
+
)
|
|
84
|
+
if spec.origin:
|
|
85
|
+
return Path(spec.origin).resolve().parent
|
|
86
|
+
if spec.submodule_search_locations:
|
|
87
|
+
return Path(next(iter(spec.submodule_search_locations))).resolve()
|
|
88
|
+
import_root = _resource_import_root(module_path, entrypoint=entrypoint, owner=owner)
|
|
89
|
+
spec = find_spec(import_root)
|
|
90
|
+
if spec is None:
|
|
91
|
+
raise PzError(
|
|
92
|
+
code="PZ_RESOURCE_APP_NOT_FOUND",
|
|
93
|
+
message=f"Cannot resolve package root for app '{module_path}'.",
|
|
94
|
+
stage="resources",
|
|
95
|
+
owner=module_path,
|
|
96
|
+
action="compile_resource_manifest",
|
|
97
|
+
)
|
|
98
|
+
if spec.origin:
|
|
99
|
+
return Path(spec.origin).resolve().parent
|
|
100
|
+
if spec.submodule_search_locations:
|
|
101
|
+
return Path(next(iter(spec.submodule_search_locations))).resolve()
|
|
102
|
+
raise PzError(
|
|
103
|
+
code="PZ_RESOURCE_APP_NOT_FOUND",
|
|
104
|
+
message=f"Cannot resolve package root for app '{module_path}'.",
|
|
105
|
+
stage="resources",
|
|
106
|
+
owner=module_path,
|
|
107
|
+
action="compile_resource_manifest",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _optional_dir(package_root: Path, name: str) -> Path | None:
|
|
112
|
+
path = package_root / name
|
|
113
|
+
if path.is_dir():
|
|
114
|
+
return path
|
|
115
|
+
if path.exists():
|
|
116
|
+
raise PzError(
|
|
117
|
+
code="PZ_RESOURCE_DIR_INVALID",
|
|
118
|
+
message=f"Resource path '{path.name}' for package '{package_root.name}' must be a directory.",
|
|
119
|
+
stage="resources",
|
|
120
|
+
owner=str(package_root),
|
|
121
|
+
action="compile_resource_manifest",
|
|
122
|
+
)
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
_IGNORED_RESOURCE_PARTS = {"tests", "test", "docs", "__pycache__"}
|
|
127
|
+
|
|
128
|
+
def _default_resource_items() -> tuple[ResourceItemContract, ...]:
|
|
129
|
+
return tuple(
|
|
130
|
+
ResourceItemContract(
|
|
131
|
+
name=item["name"],
|
|
132
|
+
output_dir=Path("."),
|
|
133
|
+
)
|
|
134
|
+
for item in DEFAULT_RESOURCE_ITEMS
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _discover_resource_dirs(
|
|
139
|
+
package_root: Path,
|
|
140
|
+
*,
|
|
141
|
+
resource_items: tuple[ResourceItemContract, ...],
|
|
142
|
+
) -> dict[str, tuple[Path, ...]]:
|
|
143
|
+
resource_item_names = tuple(item.name for item in resource_items)
|
|
144
|
+
collected: dict[str, list[Path]] = {name: [] for name in resource_item_names}
|
|
145
|
+
root_resolved: dict[str, Path | None] = {}
|
|
146
|
+
|
|
147
|
+
for name in resource_item_names:
|
|
148
|
+
root_dir = _optional_dir(package_root, name)
|
|
149
|
+
if root_dir is not None:
|
|
150
|
+
resolved = root_dir.resolve()
|
|
151
|
+
collected[name].append(resolved)
|
|
152
|
+
root_resolved[name] = resolved
|
|
153
|
+
else:
|
|
154
|
+
root_resolved[name] = None
|
|
155
|
+
|
|
156
|
+
for current_root, dirnames, filenames in os.walk(package_root):
|
|
157
|
+
current_path = Path(current_root)
|
|
158
|
+
if current_path.name in resource_item_names:
|
|
159
|
+
dirnames[:] = []
|
|
160
|
+
continue
|
|
161
|
+
dirnames[:] = sorted(
|
|
162
|
+
name
|
|
163
|
+
for name in dirnames
|
|
164
|
+
if name not in _IGNORED_RESOURCE_PARTS
|
|
165
|
+
)
|
|
166
|
+
for file_name in filenames:
|
|
167
|
+
if file_name in resource_item_names:
|
|
168
|
+
raise PzError(
|
|
169
|
+
code="PZ_RESOURCE_DIR_INVALID",
|
|
170
|
+
message=f"Resource path '{file_name}' for package '{package_root.name}' must be a directory.",
|
|
171
|
+
stage="resources",
|
|
172
|
+
owner=str(package_root),
|
|
173
|
+
action="compile_resource_manifest",
|
|
174
|
+
)
|
|
175
|
+
for dir_name in dirnames:
|
|
176
|
+
if dir_name not in resource_item_names:
|
|
177
|
+
continue
|
|
178
|
+
candidate = (current_path / dir_name).resolve()
|
|
179
|
+
if root_resolved[dir_name] is not None and candidate == root_resolved[dir_name]:
|
|
180
|
+
continue
|
|
181
|
+
collected[dir_name].append(candidate)
|
|
182
|
+
|
|
183
|
+
return {name: tuple(paths) for name, paths in collected.items()}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def build_resource_source(
|
|
187
|
+
*,
|
|
188
|
+
app_id: str,
|
|
189
|
+
module_path: str,
|
|
190
|
+
package_root: Path,
|
|
191
|
+
resource_items: tuple[ResourceItemContract, ...],
|
|
192
|
+
output_root: Path | None = None,
|
|
193
|
+
) -> ResourceSource:
|
|
194
|
+
discovered = _discover_resource_dirs(package_root, resource_items=resource_items)
|
|
195
|
+
resolved_output_root = output_root if output_root is not None else Path(".")
|
|
196
|
+
return ResourceSource(
|
|
197
|
+
app_id=app_id,
|
|
198
|
+
module_path=module_path,
|
|
199
|
+
package_root=package_root,
|
|
200
|
+
items=tuple(
|
|
201
|
+
ResourceItem(
|
|
202
|
+
name=item.name,
|
|
203
|
+
source_dirs=discovered.get(item.name, ()),
|
|
204
|
+
output_dir=resolved_output_root / item.name,
|
|
205
|
+
)
|
|
206
|
+
for item in resource_items
|
|
207
|
+
),
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def discover_resource_sources(
|
|
212
|
+
module_paths: Iterable[str],
|
|
213
|
+
*,
|
|
214
|
+
resource_items: tuple[ResourceItemContract, ...] | None = None,
|
|
215
|
+
) -> tuple[ResourceSource, ...]:
|
|
216
|
+
resolved_resource_items = resource_items or _default_resource_items()
|
|
217
|
+
sources: list[ResourceSource] = []
|
|
218
|
+
for raw_module_path in module_paths:
|
|
219
|
+
module_path = str(raw_module_path).split(":", 1)[0].strip()
|
|
220
|
+
if not module_path:
|
|
221
|
+
continue
|
|
222
|
+
try:
|
|
223
|
+
spec = find_spec(module_path)
|
|
224
|
+
except ModuleNotFoundError:
|
|
225
|
+
continue
|
|
226
|
+
if spec is None:
|
|
227
|
+
continue
|
|
228
|
+
if spec.submodule_search_locations:
|
|
229
|
+
package_root = Path(next(iter(spec.submodule_search_locations))).resolve()
|
|
230
|
+
elif spec.origin:
|
|
231
|
+
package_root = Path(spec.origin).resolve().parent
|
|
232
|
+
else:
|
|
233
|
+
continue
|
|
234
|
+
sources.append(
|
|
235
|
+
build_resource_source(
|
|
236
|
+
app_id=module_path.split(".")[-1],
|
|
237
|
+
module_path=module_path,
|
|
238
|
+
package_root=package_root,
|
|
239
|
+
resource_items=resolved_resource_items,
|
|
240
|
+
output_root=Path("."),
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
return tuple(sources)
|
|
244
|
+
|
|
245
|
+
def _compile_resource_contribution_specs(
|
|
246
|
+
extension_registry: ExtensionRegistry | None,
|
|
247
|
+
) -> tuple[ResourceContributionSpec, ...]:
|
|
248
|
+
if extension_registry is None:
|
|
249
|
+
return ()
|
|
250
|
+
return tuple(
|
|
251
|
+
item
|
|
252
|
+
for item in extension_registry.by_kind("resource.contribution")
|
|
253
|
+
if isinstance(item, ResourceContributionSpec)
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def compile_resource_manifest(
|
|
258
|
+
project: ProjectContext,
|
|
259
|
+
settings: SettingsSnapshot,
|
|
260
|
+
app_registry: AppRegistry,
|
|
261
|
+
extension_registry: ExtensionRegistry | None = None,
|
|
262
|
+
) -> ResourceManifest:
|
|
263
|
+
resource_settings = read_resource_settings(
|
|
264
|
+
settings,
|
|
265
|
+
project,
|
|
266
|
+
stage="resources",
|
|
267
|
+
owner="resources",
|
|
268
|
+
action="compile_resource_manifest",
|
|
269
|
+
)
|
|
270
|
+
output_root = resource_settings.output_dir
|
|
271
|
+
manifest_path = resource_settings.manifest_path
|
|
272
|
+
staging_root = resource_settings.staging_dir
|
|
273
|
+
lock_path = resource_settings.lock_path
|
|
274
|
+
items = tuple(
|
|
275
|
+
ResourceItem(
|
|
276
|
+
name=item.name,
|
|
277
|
+
source_dirs=(),
|
|
278
|
+
output_dir=item.output_dir,
|
|
279
|
+
)
|
|
280
|
+
for item in resource_settings.items
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
sources: list[ResourceSource] = []
|
|
284
|
+
for app in app_registry.apps:
|
|
285
|
+
package_root = _package_root(
|
|
286
|
+
app.module_path,
|
|
287
|
+
entrypoint=app.entrypoint,
|
|
288
|
+
owner=app.app_id,
|
|
289
|
+
)
|
|
290
|
+
sources.append(
|
|
291
|
+
build_resource_source(
|
|
292
|
+
app_id=app.app_id,
|
|
293
|
+
module_path=app.module_path,
|
|
294
|
+
package_root=package_root,
|
|
295
|
+
resource_items=resource_settings.items,
|
|
296
|
+
output_root=output_root,
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
resource_contributions = _compile_resource_contribution_specs(extension_registry)
|
|
301
|
+
return ResourceManifest(
|
|
302
|
+
app_order=tuple(app.app_id for app in app_registry.apps),
|
|
303
|
+
manifest_path=manifest_path,
|
|
304
|
+
output_root=output_root,
|
|
305
|
+
items=items,
|
|
306
|
+
staging_root=staging_root,
|
|
307
|
+
lock_path=lock_path,
|
|
308
|
+
sources=tuple(sources),
|
|
309
|
+
resource_contributions=resource_contributions,
|
|
310
|
+
)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from pz_core.assembly.project import ProjectContext
|
|
6
|
+
from pz_core.assembly.settings import SettingsSnapshot
|
|
7
|
+
from pz_core.core.errors import PzError
|
|
8
|
+
from pz_core.infra.settings import ResourceItemContract, ResourceSettings
|
|
9
|
+
|
|
10
|
+
DEFAULT_RESOURCE_ITEMS = (
|
|
11
|
+
{"name": "templates"},
|
|
12
|
+
{"name": "static"},
|
|
13
|
+
{"name": "locales"},
|
|
14
|
+
{"name": "pages"},
|
|
15
|
+
{"name": "template_config"},
|
|
16
|
+
{"name": "migrations"},
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _resource_environment(settings: SettingsSnapshot) -> str:
|
|
21
|
+
raw = settings.get("project", "pz_env", default="develop")
|
|
22
|
+
value = str(raw or "").strip().lower()
|
|
23
|
+
return value or "develop"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def read_resource_settings(
|
|
27
|
+
settings: SettingsSnapshot,
|
|
28
|
+
project: ProjectContext,
|
|
29
|
+
*,
|
|
30
|
+
stage: str,
|
|
31
|
+
owner: str,
|
|
32
|
+
action: str,
|
|
33
|
+
) -> ResourceSettings:
|
|
34
|
+
raw_resources = settings.get("resources", default={})
|
|
35
|
+
if not isinstance(raw_resources, dict):
|
|
36
|
+
raise PzError(
|
|
37
|
+
code="PZ_RESOURCE_SETTINGS_INVALID",
|
|
38
|
+
message="resources must be declared as a table.",
|
|
39
|
+
stage=stage,
|
|
40
|
+
owner=owner,
|
|
41
|
+
action=action,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
raw_items = raw_resources.get("items", DEFAULT_RESOURCE_ITEMS)
|
|
45
|
+
if not isinstance(raw_items, (list, tuple)):
|
|
46
|
+
raise PzError(
|
|
47
|
+
code="PZ_RESOURCE_SETTINGS_INVALID",
|
|
48
|
+
message="resources.items must be declared as an array of objects with non-empty name fields.",
|
|
49
|
+
stage=stage,
|
|
50
|
+
owner=owner,
|
|
51
|
+
action=action,
|
|
52
|
+
)
|
|
53
|
+
item_names: list[str] = []
|
|
54
|
+
parsed_items: list[ResourceItemContract] = []
|
|
55
|
+
for item in raw_items:
|
|
56
|
+
if not isinstance(item, dict):
|
|
57
|
+
raise PzError(
|
|
58
|
+
code="PZ_RESOURCE_SETTINGS_INVALID",
|
|
59
|
+
message="resources.items must be declared as an array of objects with non-empty name fields.",
|
|
60
|
+
stage=stage,
|
|
61
|
+
owner=owner,
|
|
62
|
+
action=action,
|
|
63
|
+
)
|
|
64
|
+
raw_name = item.get("name")
|
|
65
|
+
if not isinstance(raw_name, str) or not raw_name.strip():
|
|
66
|
+
raise PzError(
|
|
67
|
+
code="PZ_RESOURCE_SETTINGS_INVALID",
|
|
68
|
+
message="resources.items must be declared as an array of objects with non-empty name fields.",
|
|
69
|
+
stage=stage,
|
|
70
|
+
owner=owner,
|
|
71
|
+
action=action,
|
|
72
|
+
)
|
|
73
|
+
normalized = raw_name.strip()
|
|
74
|
+
if normalized in item_names:
|
|
75
|
+
continue
|
|
76
|
+
item_names.append(normalized)
|
|
77
|
+
parsed_items.append(
|
|
78
|
+
ResourceItemContract(
|
|
79
|
+
name=normalized,
|
|
80
|
+
output_dir=Path("."),
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
if not parsed_items:
|
|
84
|
+
raise PzError(
|
|
85
|
+
code="PZ_RESOURCE_SETTINGS_INVALID",
|
|
86
|
+
message="resources.items must declare at least one resource item.",
|
|
87
|
+
stage=stage,
|
|
88
|
+
owner=owner,
|
|
89
|
+
action=action,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
output_root_name = str(raw_resources.get("output_root", "resources") or "resources").strip()
|
|
93
|
+
manifest_name = str(raw_resources.get("manifest_name", "manifest.json") or "manifest.json").strip()
|
|
94
|
+
staging_name = str(raw_resources.get("staging_dir", "staging") or "staging").strip()
|
|
95
|
+
lock_name = str(raw_resources.get("lock_name", "resources.lock") or "resources.lock").strip()
|
|
96
|
+
if not output_root_name or not manifest_name or not staging_name or not lock_name:
|
|
97
|
+
raise PzError(
|
|
98
|
+
code="PZ_RESOURCE_SETTINGS_INVALID",
|
|
99
|
+
message=(
|
|
100
|
+
"resources.output_root, resources.manifest_name, "
|
|
101
|
+
"resources.staging_dir, and resources.lock_name must be non-empty strings."
|
|
102
|
+
),
|
|
103
|
+
stage=stage,
|
|
104
|
+
owner=owner,
|
|
105
|
+
action=action,
|
|
106
|
+
)
|
|
107
|
+
env_name = _resource_environment(settings)
|
|
108
|
+
output_dir = project.cache_dir / env_name / output_root_name
|
|
109
|
+
return ResourceSettings(
|
|
110
|
+
items=tuple(
|
|
111
|
+
ResourceItemContract(
|
|
112
|
+
name=item.name,
|
|
113
|
+
output_dir=output_dir / item.name,
|
|
114
|
+
)
|
|
115
|
+
for item in parsed_items
|
|
116
|
+
),
|
|
117
|
+
output_dir=output_dir,
|
|
118
|
+
lock_path=project.runtime_dir / env_name / "locks" / lock_name,
|
|
119
|
+
manifest_path=output_dir / manifest_name,
|
|
120
|
+
staging_dir=output_dir / staging_name,
|
|
121
|
+
manifest_name=manifest_name,
|
|
122
|
+
lock_name=lock_name,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
__all__ = [
|
|
127
|
+
"DEFAULT_RESOURCE_ITEMS",
|
|
128
|
+
"read_resource_settings",
|
|
129
|
+
]
|