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.
Files changed (399) hide show
  1. pz_cli-5.0.0.dist-info/METADATA +51 -0
  2. pz_cli-5.0.0.dist-info/RECORD +399 -0
  3. pz_cli-5.0.0.dist-info/WHEEL +4 -0
  4. pz_cli-5.0.0.dist-info/entry_points.txt +5 -0
  5. pz_core/__init__.py +64 -0
  6. pz_core/__main__.py +7 -0
  7. pz_core/app.py +45 -0
  8. pz_core/assembly/__init__.py +17 -0
  9. pz_core/assembly/app_discovery.py +1240 -0
  10. pz_core/assembly/apps/__init__.py +25 -0
  11. pz_core/assembly/apps/compiler.py +824 -0
  12. pz_core/assembly/apps/providers.py +188 -0
  13. pz_core/assembly/cli/__init__.py +9 -0
  14. pz_core/assembly/cli/compiler.py +166 -0
  15. pz_core/assembly/diagnostics/__init__.py +21 -0
  16. pz_core/assembly/diagnostics/checks.py +257 -0
  17. pz_core/assembly/diagnostics/dev_sources.py +569 -0
  18. pz_core/assembly/events/__init__.py +5 -0
  19. pz_core/assembly/events/compiler.py +219 -0
  20. pz_core/assembly/extensions/__init__.py +5 -0
  21. pz_core/assembly/extensions/compiler.py +696 -0
  22. pz_core/assembly/infra/__init__.py +6 -0
  23. pz_core/assembly/infra/compiler.py +272 -0
  24. pz_core/assembly/lifecycle/__init__.py +5 -0
  25. pz_core/assembly/lifecycle/compiler.py +95 -0
  26. pz_core/assembly/middlewares/__init__.py +19 -0
  27. pz_core/assembly/middlewares/compiler.py +508 -0
  28. pz_core/assembly/project/__init__.py +7 -0
  29. pz_core/assembly/project/compiler.py +9 -0
  30. pz_core/assembly/project/context.py +17 -0
  31. pz_core/assembly/project/discovery.py +227 -0
  32. pz_core/assembly/resources/__init__.py +19 -0
  33. pz_core/assembly/resources/compiler.py +310 -0
  34. pz_core/assembly/resources/settings.py +129 -0
  35. pz_core/assembly/routes/__init__.py +7 -0
  36. pz_core/assembly/routes/compiler.py +400 -0
  37. pz_core/assembly/runtime_manifests/__init__.py +13 -0
  38. pz_core/assembly/runtime_manifests/compiler.py +159 -0
  39. pz_core/assembly/settings/__init__.py +17 -0
  40. pz_core/assembly/settings/compiler.py +786 -0
  41. pz_core/assembly/settings/host_project_overrides.py +82 -0
  42. pz_core/assembly/settings/host_runtime_endpoint.py +207 -0
  43. pz_core/authoring/__init__.py +79 -0
  44. pz_core/authoring/app_init_service.py +946 -0
  45. pz_core/authoring/app_versions.py +814 -0
  46. pz_core/authoring/cli.py +192 -0
  47. pz_core/authoring/commands.py +1810 -0
  48. pz_core/authoring/conflicts.py +180 -0
  49. pz_core/authoring/dev_bindings.py +916 -0
  50. pz_core/authoring/dev_git.py +1087 -0
  51. pz_core/authoring/dev_import.py +1061 -0
  52. pz_core/authoring/dev_manifest.py +522 -0
  53. pz_core/authoring/dev_sources.py +1638 -0
  54. pz_core/authoring/env_cli.py +454 -0
  55. pz_core/authoring/files.py +109 -0
  56. pz_core/authoring/ide_projection.py +1129 -0
  57. pz_core/authoring/naming.py +58 -0
  58. pz_core/authoring/official_dev_sources.py +432 -0
  59. pz_core/authoring/official_sources.json +156 -0
  60. pz_core/authoring/orchestration_service.py +558 -0
  61. pz_core/authoring/project_default.jsonc +53 -0
  62. pz_core/authoring/project_defaults.py +88 -0
  63. pz_core/authoring/project_metadata_service.py +199 -0
  64. pz_core/authoring/project_settings_checks.py +51 -0
  65. pz_core/authoring/project_settings_cli.py +299 -0
  66. pz_core/authoring/project_settings_service.py +1700 -0
  67. pz_core/authoring/project_state.py +399 -0
  68. pz_core/authoring/publish_git.py +423 -0
  69. pz_core/authoring/publish_lock.py +188 -0
  70. pz_core/authoring/publish_source.py +195 -0
  71. pz_core/authoring/release.py +1382 -0
  72. pz_core/authoring/repo_keys.py +29 -0
  73. pz_core/authoring/root_cli.py +28 -0
  74. pz_core/authoring/scaffold_service.py +325 -0
  75. pz_core/authoring/sync_service.py +787 -0
  76. pz_core/authoring/templates/app/__init__.py +1 -0
  77. pz_core/authoring/templates/app/api.py +25 -0
  78. pz_core/authoring/templates/app/apps.py +31 -0
  79. pz_core/authoring/templates/app/auth_package_init.py +14 -0
  80. pz_core/authoring/templates/app/auth_readme.py +29 -0
  81. pz_core/authoring/templates/app/configs_package_init.py +14 -0
  82. pz_core/authoring/templates/app/default_env.py +18 -0
  83. pz_core/authoring/templates/app/default_jsonc.py +25 -0
  84. pz_core/authoring/templates/app/locales_readme.py +25 -0
  85. pz_core/authoring/templates/app/migrations_readme.py +19 -0
  86. pz_core/authoring/templates/app/models.py +14 -0
  87. pz_core/authoring/templates/app/package_init.py +8 -0
  88. pz_core/authoring/templates/app/pages_base.py +161 -0
  89. pz_core/authoring/templates/app/pages_readme.py +25 -0
  90. pz_core/authoring/templates/app/public.py +31 -0
  91. pz_core/authoring/templates/app/pyproject.py +77 -0
  92. pz_core/authoring/templates/app/readme.py +74 -0
  93. pz_core/authoring/templates/app/settings.py +46 -0
  94. pz_core/authoring/templates/app/static_app_js.py +24 -0
  95. pz_core/authoring/templates/app/static_readme.py +31 -0
  96. pz_core/authoring/templates/app/template_config_page.py +23 -0
  97. pz_core/authoring/templates/app/template_index_html.py +28 -0
  98. pz_core/authoring/templates/app/templates_readme.py +32 -0
  99. pz_core/authoring/templates/app/urls.py +36 -0
  100. pz_core/authoring/templates/app/views.py +36 -0
  101. pz_core/authoring/templates/project/__init__.py +1 -0
  102. pz_core/authoring/templates/project/agents.py +51 -0
  103. pz_core/authoring/templates/project/apps_container_readme.py +37 -0
  104. pz_core/authoring/templates/project/main_py.py +59 -0
  105. pz_core/authoring/templates/project/project_env.py +19 -0
  106. pz_core/authoring/templates/project/pyproject.py +26 -0
  107. pz_core/authoring/templates/project/pz_env_env.py +65 -0
  108. pz_core/authoring/templates/project/pz_env_jsonc.py +69 -0
  109. pz_core/authoring/templates/project/pz_jsonc.py +12 -0
  110. pz_core/authoring/templates/project/readme.py +94 -0
  111. pz_core/authoring/templates/project/root_gitkeep.py +8 -0
  112. pz_core/authoring/templates/shared/__init__.py +5 -0
  113. pz_core/authoring/templates/shared/text.py +10 -0
  114. pz_core/authoring/venv_service.py +87 -0
  115. pz_core/authoring/workspace.py +167 -0
  116. pz_core/cli.py +291 -0
  117. pz_core/cli_options.py +74 -0
  118. pz_core/core/__init__.py +3 -0
  119. pz_core/core/errors/__init__.py +5 -0
  120. pz_core/core/errors/models.py +117 -0
  121. pz_core/core/events/__init__.py +5 -0
  122. pz_core/core/events/registry.py +54 -0
  123. pz_core/core/extensions/__init__.py +51 -0
  124. pz_core/core/extensions/registry.py +790 -0
  125. pz_core/core/health/__init__.py +6 -0
  126. pz_core/core/health/contracts.py +10 -0
  127. pz_core/core/middlewares/__init__.py +6 -0
  128. pz_core/core/middlewares/models.py +24 -0
  129. pz_core/core/routing/__init__.py +5 -0
  130. pz_core/core/routing/models.py +21 -0
  131. pz_core/core/settings_contracts.py +25 -0
  132. pz_core/docs/README.md +20 -0
  133. pz_core/docs/distribution-README.md +438 -0
  134. pz_core/docs/pz/345/221/275/344/273/244/346/200/273/350/247/210.md +1011 -0
  135. 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
  136. 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
  137. 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
  138. 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
  139. 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
  140. 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
  141. 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
  142. 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
  143. 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
  144. 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
  145. 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
  146. 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
  147. 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
  148. 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
  149. 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
  150. 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
  151. 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
  152. 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
  153. 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
  154. 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
  155. 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
  156. 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
  157. 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
  158. 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
  159. 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
  160. 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
  161. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/README.md +98 -0
  162. 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
  163. 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
  164. 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
  165. 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
  166. 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
  167. 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
  168. 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
  169. 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
  170. 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
  171. 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
  172. 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
  173. pz_core/docs//345/217/221/347/211/210/345/211/215/346/270/205/345/215/225-4.0.11.md +84 -0
  174. pz_core/docs//346/226/260/346/211/213/346/214/207/345/215/227.md +422 -0
  175. pz_core/infra/__init__.py +17 -0
  176. pz_core/infra/db/__init__.py +23 -0
  177. pz_core/infra/db/contracts.py +73 -0
  178. pz_core/infra/health/__init__.py +5 -0
  179. pz_core/infra/health/contracts.py +8 -0
  180. pz_core/infra/kv/__init__.py +5 -0
  181. pz_core/infra/kv/contracts.py +17 -0
  182. pz_core/infra/observability/__init__.py +23 -0
  183. pz_core/infra/observability/contracts.py +142 -0
  184. pz_core/infra/resilience/__init__.py +5 -0
  185. pz_core/infra/resilience/contracts.py +17 -0
  186. pz_core/infra/resources/__init__.py +5 -0
  187. pz_core/infra/resources/contracts.py +16 -0
  188. pz_core/infra/settings/__init__.py +17 -0
  189. pz_core/infra/settings/models.py +81 -0
  190. pz_core/infra/storage/__init__.py +5 -0
  191. pz_core/infra/storage/ports.py +27 -0
  192. pz_core/project_root_commands.py +282 -0
  193. pz_core/project_runtime_cli.py +344 -0
  194. pz_core/project_sources.py +253 -0
  195. pz_core/pz_launcher.py +209 -0
  196. pz_core/root_cli.py +70 -0
  197. pz_core/runtime/__init__.py +229 -0
  198. pz_core/runtime/access.py +86 -0
  199. pz_core/runtime/adapters/__init__.py +5 -0
  200. pz_core/runtime/adapters/base.py +18 -0
  201. pz_core/runtime/asyncio_bridge.py +18 -0
  202. pz_core/runtime/bindings/__init__.py +5 -0
  203. pz_core/runtime/bindings/container.py +50 -0
  204. pz_core/runtime/cli/__init__.py +5 -0
  205. pz_core/runtime/cli/_args.py +67 -0
  206. pz_core/runtime/cli/app_commands.py +417 -0
  207. pz_core/runtime/cli/check.py +214 -0
  208. pz_core/runtime/cli/db.py +297 -0
  209. pz_core/runtime/cli/main.py +89 -0
  210. pz_core/runtime/cli/resources.py +280 -0
  211. pz_core/runtime/discovery.py +23 -0
  212. pz_core/runtime/environment_integrity.py +157 -0
  213. pz_core/runtime/errors.py +5 -0
  214. pz_core/runtime/events.py +87 -0
  215. pz_core/runtime/facades/__init__.py +38 -0
  216. pz_core/runtime/facades/_capabilities.py +46 -0
  217. pz_core/runtime/facades/cache.py +103 -0
  218. pz_core/runtime/facades/database.py +71 -0
  219. pz_core/runtime/facades/events.py +380 -0
  220. pz_core/runtime/facades/health.py +101 -0
  221. pz_core/runtime/facades/mail.py +121 -0
  222. pz_core/runtime/facades/observability.py +102 -0
  223. pz_core/runtime/facades/providers.py +75 -0
  224. pz_core/runtime/facades/resources.py +52 -0
  225. pz_core/runtime/facades/storage.py +296 -0
  226. pz_core/runtime/health/__init__.py +5 -0
  227. pz_core/runtime/health/service.py +102 -0
  228. pz_core/runtime/host.py +188 -0
  229. pz_core/runtime/host_runner.py +282 -0
  230. pz_core/runtime/http/__init__.py +20 -0
  231. pz_core/runtime/http/app.py +576 -0
  232. pz_core/runtime/http_timing.py +173 -0
  233. pz_core/runtime/invocation.py +29 -0
  234. pz_core/runtime/json_safe.py +47 -0
  235. pz_core/runtime/lifecycle/__init__.py +25 -0
  236. pz_core/runtime/lifecycle/manager.py +533 -0
  237. pz_core/runtime/plan.py +214 -0
  238. pz_core/runtime/plan_context.py +33 -0
  239. pz_core/runtime/plan_snapshot.py +177 -0
  240. pz_core/runtime/registries/__init__.py +27 -0
  241. pz_core/runtime/registries/capabilities.py +101 -0
  242. pz_core/runtime/registries/providers.py +396 -0
  243. pz_core/runtime/resource_assets.py +344 -0
  244. pz_core/runtime/run_cli.py +211 -0
  245. pz_core/runtime/settings.py +888 -0
  246. pz_core/runtime/worker/__init__.py +21 -0
  247. pz_core/runtime/worker/engine.py +921 -0
  248. pz_core/runtime/worker/runtime.py +25 -0
  249. pz_core/testing/__init__.py +15 -0
  250. pz_core/testing/doc_route_contracts.py +305 -0
  251. pz_core/utils/__init__.py +44 -0
  252. pz_core/utils/env.py +65 -0
  253. pz_core/utils/project_jsonc/__init__.py +29 -0
  254. pz_core/utils/project_jsonc/document.py +420 -0
  255. pz_core/utils/project_settings.py +99 -0
  256. pz_foundation/__init__.py +50 -0
  257. pz_foundation/_contracts.py +81 -0
  258. pz_foundation/_errors.py +34 -0
  259. pz_foundation/api.py +14 -0
  260. pz_foundation/apps.py +127 -0
  261. pz_foundation/auth/__init__.py +1 -0
  262. pz_foundation/cache_control.py +233 -0
  263. pz_foundation/cli/__init__.py +2 -0
  264. pz_foundation/cli/workers.py +565 -0
  265. pz_foundation/configs/__init__.py +0 -0
  266. pz_foundation/configs/default.env +15 -0
  267. pz_foundation/configs/default.jsonc +452 -0
  268. pz_foundation/db/__init__.py +94 -0
  269. pz_foundation/db/aerich_executor.py +664 -0
  270. pz_foundation/db/config.py +358 -0
  271. pz_foundation/db/connection_manager.py +689 -0
  272. pz_foundation/db/connection_probe.py +204 -0
  273. pz_foundation/db/database.py +1291 -0
  274. pz_foundation/db/deps.py +47 -0
  275. pz_foundation/db/lifecycle_status.py +85 -0
  276. pz_foundation/db/migrations.py +477 -0
  277. pz_foundation/db/mysql.py +57 -0
  278. pz_foundation/db/postgresql.py +409 -0
  279. pz_foundation/db/production_pool_fallback.py +143 -0
  280. pz_foundation/db/public.py +45 -0
  281. pz_foundation/db/session.py +369 -0
  282. pz_foundation/db/sqlite.py +15 -0
  283. pz_foundation/db/tortoise_manager.py +724 -0
  284. pz_foundation/db/tortoise_psycopg.py +148 -0
  285. pz_foundation/docs/README.md +18 -0
  286. pz_foundation/docs/distribution-README.md +319 -0
  287. 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
  288. 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
  289. pz_foundation/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +288 -0
  290. 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
  291. pz_foundation/health.py +105 -0
  292. pz_foundation/host/__init__.py +11 -0
  293. pz_foundation/host/builder.py +34 -0
  294. pz_foundation/host/cache_runtime.py +44 -0
  295. pz_foundation/host/db/__init__.py +17 -0
  296. pz_foundation/host/db/aerich_config.py +36 -0
  297. pz_foundation/host/db/config.py +106 -0
  298. pz_foundation/host/db/connection_manager.py +48 -0
  299. pz_foundation/host/db/deps.py +5 -0
  300. pz_foundation/host/db/orm_retry_patch.py +566 -0
  301. pz_foundation/host/db/request_adapter.py +325 -0
  302. pz_foundation/host/db/router.py +28 -0
  303. pz_foundation/host/db/session.py +284 -0
  304. pz_foundation/host/db/tortoise_manager.py +10 -0
  305. pz_foundation/host/idempotency.py +132 -0
  306. pz_foundation/host/lifespan.py +247 -0
  307. pz_foundation/host/redis_runtime.py +100 -0
  308. pz_foundation/host/web.py +790 -0
  309. pz_foundation/infra_provider.py +236 -0
  310. pz_foundation/kv/__init__.py +25 -0
  311. pz_foundation/kv/cache.py +1166 -0
  312. pz_foundation/kv/deps.py +84 -0
  313. pz_foundation/kv/public.py +25 -0
  314. pz_foundation/kv/redis_manager.py +1531 -0
  315. pz_foundation/lazy_exports.py +43 -0
  316. pz_foundation/lazy_imports.py +124 -0
  317. pz_foundation/lifecycle.py +392 -0
  318. pz_foundation/managed_workers.py +1117 -0
  319. pz_foundation/middlewares/__init__.py +26 -0
  320. pz_foundation/middlewares/_settings.py +250 -0
  321. pz_foundation/middlewares/analytics.py +124 -0
  322. pz_foundation/middlewares/cache_control.py +156 -0
  323. pz_foundation/middlewares/compression.py +202 -0
  324. pz_foundation/middlewares/cors.py +62 -0
  325. pz_foundation/middlewares/geo_country.py +44 -0
  326. pz_foundation/middlewares/inner_router.py +50 -0
  327. pz_foundation/middlewares/rate_limited/__init__.py +3 -0
  328. pz_foundation/middlewares/rate_limited/key_limiter.py +115 -0
  329. pz_foundation/middlewares/request_logging.py +104 -0
  330. pz_foundation/middlewares/route_owner.py +309 -0
  331. pz_foundation/middlewares/security_csrf.py +44 -0
  332. pz_foundation/middlewares/security_headers/__init__.py +4 -0
  333. pz_foundation/middlewares/security_headers/conflicts.py +44 -0
  334. pz_foundation/middlewares/security_headers/csp_nonce.py +200 -0
  335. pz_foundation/middlewares/security_headers/security_headers.py +44 -0
  336. pz_foundation/migrations/__init__.py +1 -0
  337. pz_foundation/migrations/pz_foundation/__init__.py +1 -0
  338. pz_foundation/models.py +22 -0
  339. pz_foundation/notifications/__init__.py +23 -0
  340. pz_foundation/notifications/email.py +542 -0
  341. pz_foundation/notifications/public.py +31 -0
  342. pz_foundation/observability/__init__.py +71 -0
  343. pz_foundation/observability/http.py +34 -0
  344. pz_foundation/observability/logger/__init__.py +49 -0
  345. pz_foundation/observability/logger/_bot_settings.py +126 -0
  346. pz_foundation/observability/logger/bot_message.py +592 -0
  347. pz_foundation/observability/logger/core.py +385 -0
  348. pz_foundation/observability/logger/delivery_audit.py +84 -0
  349. pz_foundation/observability/logger/feishu_bot.py +199 -0
  350. pz_foundation/observability/logger/telegram_bot.py +194 -0
  351. pz_foundation/observability/metrics.py +365 -0
  352. pz_foundation/observability/runtime.py +68 -0
  353. pz_foundation/public.py +405 -0
  354. pz_foundation/resources/__init__.py +12 -0
  355. pz_foundation/resources/builder.py +900 -0
  356. pz_foundation/resources/images.py +363 -0
  357. pz_foundation/root_cli.py +20 -0
  358. pz_foundation/routing/__init__.py +203 -0
  359. pz_foundation/routing/canonical.py +486 -0
  360. pz_foundation/routing/convertors.py +82 -0
  361. pz_foundation/routing/declarations.py +843 -0
  362. pz_foundation/routing/exports.py +148 -0
  363. pz_foundation/routing/loaders.py +138 -0
  364. pz_foundation/routing/manifest.py +215 -0
  365. pz_foundation/routing/manifest_provider.py +47 -0
  366. pz_foundation/routing/mounts.py +101 -0
  367. pz_foundation/routing/ordering.py +172 -0
  368. pz_foundation/routing/registry.py +133 -0
  369. pz_foundation/routing/utils.py +28 -0
  370. pz_foundation/runtime.py +234 -0
  371. pz_foundation/settings.py +253 -0
  372. pz_foundation/settings_normalization.py +130 -0
  373. pz_foundation/storage/__init__.py +25 -0
  374. pz_foundation/storage/local.py +209 -0
  375. pz_foundation/storage/public.py +106 -0
  376. pz_foundation/storage/s3.py +116 -0
  377. pz_foundation/storage/service.py +234 -0
  378. pz_foundation/urls.py +19 -0
  379. pz_foundation/utils/__init__.py +6 -0
  380. pz_foundation/utils/merge.py +160 -0
  381. pz_foundation/worker_replicas.py +55 -0
  382. pz_settings_center/__init__.py +5 -0
  383. pz_settings_center/api/__init__.py +5 -0
  384. pz_settings_center/api/runtime_overrides.py +360 -0
  385. pz_settings_center/apps.py +28 -0
  386. pz_settings_center/configs/__init__.py +0 -0
  387. pz_settings_center/configs/default.env +2 -0
  388. pz_settings_center/configs/default.jsonc +1 -0
  389. pz_settings_center/docs/01-settings_center/350/203/275/345/212/233/350/276/271/347/225/214.md +124 -0
  390. pz_settings_center/docs/README.md +12 -0
  391. pz_settings_center/docs/distribution-README.md +150 -0
  392. pz_settings_center/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +48 -0
  393. pz_settings_center/integration_payload.py +18 -0
  394. pz_settings_center/project_helpers.py +405 -0
  395. pz_settings_center/public.py +139 -0
  396. pz_settings_center/routing_helpers.py +73 -0
  397. pz_settings_center/runtime_override_provider.py +61 -0
  398. pz_settings_center/typed_settings.py +324 -0
  399. pz_settings_center/urls.py +9 -0
@@ -0,0 +1,420 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ import json
5
+ from pathlib import Path
6
+ import re
7
+ from typing import Any, Iterable
8
+
9
+ from pz_core.core.errors import PzError
10
+
11
+
12
+ PROJECT_JSONC_NAME = "pz.jsonc"
13
+ TOOL_COMMENT_PREFIX = "@pz:"
14
+ MANAGED_START_DIRECTIVE = "managed:start"
15
+ MANAGED_END_DIRECTIVE = "managed:end"
16
+ INLINE_ARRAY_MAX_LINE_LENGTH = 100
17
+ _TOOL_COMMENT_PATTERN = re.compile(r"^\s*//\s*@pz:(?P<directive>[A-Za-z0-9:_-]+)(?:\s+(?P<payload>.+?))?\s*$")
18
+
19
+
20
+ @dataclass(frozen=True, slots=True)
21
+ class ToolComment:
22
+ directive: str
23
+ payload: str | None = None
24
+
25
+
26
+ def parse_tool_comment(line: str) -> ToolComment | None:
27
+ match = _TOOL_COMMENT_PATTERN.match(line)
28
+ if match is None:
29
+ return None
30
+ payload = match.group("payload")
31
+ return ToolComment(
32
+ directive=str(match.group("directive") or "").strip(),
33
+ payload=payload.strip() if isinstance(payload, str) and payload.strip() else None,
34
+ )
35
+
36
+
37
+ def normalize_jsonc_path(path: str | Iterable[str]) -> tuple[str, ...]:
38
+ if isinstance(path, str):
39
+ tokens = tuple(part.strip() for part in path.split(".") if part.strip())
40
+ else:
41
+ tokens = tuple(str(part).strip() for part in path if str(part).strip())
42
+ if not tokens:
43
+ raise PzError(
44
+ code="PZ_PROJECT_JSONC_PATH_INVALID",
45
+ message="JSONC path must contain at least one non-empty segment.",
46
+ stage="settings",
47
+ owner="project_jsonc",
48
+ action="normalize_path",
49
+ )
50
+ return tokens
51
+
52
+
53
+ def set_jsonc_path(mapping: dict[str, Any], path: str | Iterable[str], value: Any) -> None:
54
+ segments = normalize_jsonc_path(path)
55
+ current = mapping
56
+ for segment in segments[:-1]:
57
+ child = current.get(segment)
58
+ if not isinstance(child, dict):
59
+ child = {}
60
+ current[segment] = child
61
+ current = child
62
+ current[segments[-1]] = value
63
+
64
+
65
+ def load_jsonc_file(
66
+ path: str | Path,
67
+ *,
68
+ code: str = "PZ_PROJECT_CONFIG_INVALID",
69
+ owner: str = "project_jsonc",
70
+ action: str = "load_jsonc",
71
+ ) -> dict[str, Any]:
72
+ resolved = Path(path).expanduser().resolve()
73
+ if not resolved.is_file():
74
+ return {}
75
+ raw = resolved.read_text(encoding="utf-8")
76
+ return load_jsonc_text(
77
+ raw,
78
+ code=code,
79
+ owner=owner,
80
+ action=action,
81
+ source_name=resolved.name,
82
+ source_path=resolved,
83
+ )
84
+
85
+
86
+ def load_jsonc_text(
87
+ text: str,
88
+ *,
89
+ code: str = "PZ_PROJECT_CONFIG_INVALID",
90
+ owner: str = "project_jsonc",
91
+ action: str = "load_jsonc",
92
+ source_name: str = "<memory>",
93
+ source_path: str | Path | None = None,
94
+ ) -> dict[str, Any]:
95
+ try:
96
+ payload = json.loads(_prepare_json_text(text))
97
+ except json.JSONDecodeError as exc:
98
+ raise PzError(
99
+ code=code,
100
+ message=f"Invalid JSONC in '{source_name}'.",
101
+ stage="settings",
102
+ owner=owner,
103
+ action=action,
104
+ summary=f"Invalid JSONC in '{source_name}'.",
105
+ detail=f"{source_path or source_name}: {exc}",
106
+ ) from exc
107
+ if not isinstance(payload, dict):
108
+ raise PzError(
109
+ code=code,
110
+ message=f"Project config '{source_name}' must define a JSON object payload.",
111
+ stage="settings",
112
+ owner=owner,
113
+ action=action,
114
+ )
115
+ return payload
116
+
117
+
118
+ def dump_jsonc_document(
119
+ payload: dict[str, Any],
120
+ *,
121
+ managed_paths: Iterable[str | Iterable[str]] = (),
122
+ comments: dict[str | tuple[str, ...], Iterable[str]] | None = None,
123
+ ) -> str:
124
+ normalized_managed_paths = {normalize_jsonc_path(path) for path in managed_paths}
125
+ normalized_comments = _normalize_comments(comments or {})
126
+ lines = _render_object(
127
+ payload,
128
+ indent=0,
129
+ path=(),
130
+ managed_paths=normalized_managed_paths,
131
+ comments=normalized_comments,
132
+ )
133
+ return "\n".join(lines) + "\n"
134
+
135
+
136
+ def _normalize_comments(comments: dict[str | tuple[str, ...], Iterable[str]]) -> dict[tuple[str, ...], tuple[str, ...]]:
137
+ normalized: dict[tuple[str, ...], tuple[str, ...]] = {}
138
+ for raw_path, raw_lines in comments.items():
139
+ path = normalize_jsonc_path(raw_path)
140
+ lines = tuple(str(line).strip() for line in raw_lines if str(line).strip())
141
+ if lines:
142
+ normalized[path] = lines
143
+ return normalized
144
+
145
+
146
+ def _comment_lines(path: tuple[str, ...], *, indent: int, comments: dict[tuple[str, ...], tuple[str, ...]]) -> list[str]:
147
+ lines = comments.get(path, ())
148
+ if not lines:
149
+ return []
150
+ indent_str = " " * indent
151
+ return [indent_str + "// " + line for line in lines]
152
+
153
+
154
+ def _prepare_json_text(text: str) -> str:
155
+ return _strip_trailing_commas(_strip_jsonc_comments(text))
156
+
157
+
158
+ def _strip_jsonc_comments(text: str) -> str:
159
+ chars: list[str] = []
160
+ index = 0
161
+ in_string = False
162
+ string_quote = ""
163
+ in_line_comment = False
164
+ in_block_comment = False
165
+ escaped = False
166
+ length = len(text)
167
+
168
+ while index < length:
169
+ char = text[index]
170
+ next_char = text[index + 1] if index + 1 < length else ""
171
+
172
+ if in_line_comment:
173
+ if char == "\n":
174
+ in_line_comment = False
175
+ chars.append(char)
176
+ index += 1
177
+ continue
178
+
179
+ if in_block_comment:
180
+ if char == "*" and next_char == "/":
181
+ in_block_comment = False
182
+ index += 2
183
+ continue
184
+ if char == "\n":
185
+ chars.append(char)
186
+ index += 1
187
+ continue
188
+
189
+ if in_string:
190
+ chars.append(char)
191
+ if escaped:
192
+ escaped = False
193
+ elif char == "\\":
194
+ escaped = True
195
+ elif char == string_quote:
196
+ in_string = False
197
+ string_quote = ""
198
+ index += 1
199
+ continue
200
+
201
+ if char == "/" and next_char == "/":
202
+ in_line_comment = True
203
+ index += 2
204
+ continue
205
+ if char == "/" and next_char == "*":
206
+ in_block_comment = True
207
+ index += 2
208
+ continue
209
+ if char in {'"', "'"}:
210
+ in_string = True
211
+ string_quote = char
212
+ chars.append(char)
213
+ index += 1
214
+ continue
215
+
216
+ chars.append(char)
217
+ index += 1
218
+
219
+ return "".join(chars)
220
+
221
+
222
+ def _strip_trailing_commas(text: str) -> str:
223
+ chars: list[str] = []
224
+ in_string = False
225
+ string_quote = ""
226
+ escaped = False
227
+ length = len(text)
228
+ index = 0
229
+
230
+ while index < length:
231
+ char = text[index]
232
+ if in_string:
233
+ chars.append(char)
234
+ if escaped:
235
+ escaped = False
236
+ elif char == "\\":
237
+ escaped = True
238
+ elif char == string_quote:
239
+ in_string = False
240
+ string_quote = ""
241
+ index += 1
242
+ continue
243
+
244
+ if char in {'"', "'"}:
245
+ in_string = True
246
+ string_quote = char
247
+ chars.append(char)
248
+ index += 1
249
+ continue
250
+
251
+ if char == ",":
252
+ lookahead = index + 1
253
+ while lookahead < length and text[lookahead] in {" ", "\t", "\r", "\n"}:
254
+ lookahead += 1
255
+ if lookahead < length and text[lookahead] in {"]", "}"}:
256
+ index += 1
257
+ continue
258
+
259
+ chars.append(char)
260
+ index += 1
261
+
262
+ return "".join(chars)
263
+
264
+
265
+ def _render_object(
266
+ payload: dict[str, Any],
267
+ *,
268
+ indent: int,
269
+ path: tuple[str, ...],
270
+ managed_paths: set[tuple[str, ...]],
271
+ comments: dict[tuple[str, ...], tuple[str, ...]],
272
+ ) -> list[str]:
273
+ indent_str = " " * indent
274
+ if not payload:
275
+ return [indent_str + "{}"]
276
+ lines = [indent_str + "{"]
277
+ items = list(payload.items())
278
+ for index, (key, value) in enumerate(items):
279
+ is_last = index == len(items) - 1
280
+ property_path = path + (key,)
281
+ lines.extend(_comment_lines(property_path, indent=indent + 1, comments=comments))
282
+ if property_path in managed_paths:
283
+ lines.append(" " * (indent + 1) + f"// @pz:{MANAGED_START_DIRECTIVE} {'.'.join(property_path)}")
284
+ lines.extend(
285
+ _render_property(
286
+ key,
287
+ value,
288
+ indent=indent + 1,
289
+ is_last=is_last,
290
+ path=property_path,
291
+ managed_paths=managed_paths,
292
+ comments=comments,
293
+ )
294
+ )
295
+ if property_path in managed_paths:
296
+ lines.append(" " * (indent + 1) + f"// @pz:{MANAGED_END_DIRECTIVE} {'.'.join(property_path)}")
297
+ lines.append(indent_str + "}")
298
+ return lines
299
+
300
+
301
+ def _render_property(
302
+ key: str,
303
+ value: Any,
304
+ *,
305
+ indent: int,
306
+ is_last: bool,
307
+ path: tuple[str, ...],
308
+ managed_paths: set[tuple[str, ...]],
309
+ comments: dict[tuple[str, ...], tuple[str, ...]],
310
+ ) -> list[str]:
311
+ indent_str = " " * indent
312
+ key_text = json.dumps(key, ensure_ascii=False)
313
+ suffix = "" if is_last else ","
314
+ if isinstance(value, dict):
315
+ if not value:
316
+ return [indent_str + f"{key_text}: {{}}{suffix}"]
317
+ lines = [indent_str + f"{key_text}: {{"]
318
+ items = list(value.items())
319
+ for index, (child_key, child_value) in enumerate(items):
320
+ child_is_last = index == len(items) - 1
321
+ child_path = path + (child_key,)
322
+ lines.extend(_comment_lines(child_path, indent=indent + 1, comments=comments))
323
+ if child_path in managed_paths:
324
+ lines.append(" " * (indent + 1) + f"// @pz:{MANAGED_START_DIRECTIVE} {'.'.join(child_path)}")
325
+ lines.extend(
326
+ _render_property(
327
+ child_key,
328
+ child_value,
329
+ indent=indent + 1,
330
+ is_last=child_is_last,
331
+ path=child_path,
332
+ managed_paths=managed_paths,
333
+ comments=comments,
334
+ )
335
+ )
336
+ if child_path in managed_paths:
337
+ lines.append(" " * (indent + 1) + f"// @pz:{MANAGED_END_DIRECTIVE} {'.'.join(child_path)}")
338
+ lines.append(indent_str + "}" + suffix)
339
+ return lines
340
+ if isinstance(value, (list, tuple)):
341
+ return _render_array_property(key_text, list(value), indent=indent, is_last=is_last)
342
+ return [indent_str + f"{key_text}: {_format_scalar(value)}{suffix}"]
343
+
344
+
345
+ def _render_array_property(key_text: str, items: list[Any], *, indent: int, is_last: bool) -> list[str]:
346
+ indent_str = " " * indent
347
+ suffix = "" if is_last else ","
348
+ if not items:
349
+ return [indent_str + f"{key_text}: []{suffix}"]
350
+ inline_array = _format_inline_array(items)
351
+ if inline_array is not None:
352
+ inline_line = indent_str + f"{key_text}: {inline_array}{suffix}"
353
+ if len(inline_line) <= INLINE_ARRAY_MAX_LINE_LENGTH:
354
+ return [inline_line]
355
+ lines = [indent_str + f"{key_text}: ["]
356
+ for index, item in enumerate(items):
357
+ lines.extend(_render_array_item(item, indent=indent + 1, is_last=index == len(items) - 1))
358
+ lines.append(indent_str + "]" + suffix)
359
+ return lines
360
+
361
+
362
+ def _render_array_item(value: Any, *, indent: int, is_last: bool) -> list[str]:
363
+ indent_str = " " * indent
364
+ suffix = "" if is_last else ","
365
+ if isinstance(value, dict):
366
+ if not value:
367
+ return [indent_str + "{}" + suffix]
368
+ lines = [indent_str + "{"]
369
+ items = list(value.items())
370
+ for index, (key, item_value) in enumerate(items):
371
+ lines.extend(
372
+ _render_property(
373
+ key,
374
+ item_value,
375
+ indent=indent + 1,
376
+ is_last=index == len(items) - 1,
377
+ path=(str(index), key),
378
+ managed_paths=set(),
379
+ comments={},
380
+ )
381
+ )
382
+ lines.append(indent_str + "}" + suffix)
383
+ return lines
384
+ if isinstance(value, (list, tuple)):
385
+ value = list(value)
386
+ if not value:
387
+ return [indent_str + "[]" + suffix]
388
+ lines = [indent_str + "["]
389
+ for index, item in enumerate(value):
390
+ lines.extend(_render_array_item(item, indent=indent + 1, is_last=index == len(value) - 1))
391
+ lines.append(indent_str + "]" + suffix)
392
+ return lines
393
+ return [indent_str + _format_scalar(value) + suffix]
394
+
395
+
396
+ def _format_scalar(value: Any) -> str:
397
+ if value is None:
398
+ return "null"
399
+ if isinstance(value, bool):
400
+ return "true" if value else "false"
401
+ if isinstance(value, (int, float)):
402
+ return json.dumps(value, ensure_ascii=False)
403
+ if isinstance(value, str):
404
+ return json.dumps(value, ensure_ascii=False)
405
+ raise PzError(
406
+ code="PZ_PROJECT_JSONC_VALUE_INVALID",
407
+ message=f"Unsupported JSONC value type: {type(value).__name__}",
408
+ stage="settings",
409
+ owner="project_jsonc",
410
+ action="dump_jsonc",
411
+ )
412
+
413
+
414
+ def _format_inline_array(items: list[Any]) -> str | None:
415
+ rendered_items: list[str] = []
416
+ for item in items:
417
+ if isinstance(item, (dict, list, tuple)):
418
+ return None
419
+ rendered_items.append(_format_scalar(item))
420
+ return "[" + ", ".join(rendered_items) + "]"
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+
8
+ @dataclass(frozen=True, slots=True)
9
+ class HostProjectShortcuts:
10
+ app_host: str
11
+ app_port: int
12
+ app_reload: bool
13
+ app_workers: int
14
+
15
+
16
+ def resolve_project_dir(anchor: str | Path | None = None) -> Path:
17
+ from pz_core.assembly.project import resolve_project_context
18
+
19
+ project = resolve_project_context()
20
+ if project.project_config_path.is_file():
21
+ return project.project_dir
22
+ if anchor is None:
23
+ return project.project_dir
24
+ return Path(anchor).expanduser().resolve().parent
25
+
26
+
27
+ def _project_settings_snapshot(*, project_dir: str | Path | None = None):
28
+ from pz_core.assembly.project.compiler import compile_project_context
29
+ from pz_core.assembly.settings.compiler import compile_settings_snapshot
30
+
31
+ project = compile_project_context(project_dir=project_dir)
32
+ return compile_settings_snapshot(project)
33
+
34
+
35
+ def _project_value(
36
+ name: str, default: Any, *, project_dir: str | Path | None = None
37
+ ) -> Any:
38
+ try:
39
+ return _project_settings_snapshot(project_dir=project_dir).get(
40
+ "project", str(name).strip(), default=default
41
+ )
42
+ except Exception:
43
+ return default
44
+
45
+
46
+ def _project_int(
47
+ name: str, default: int, *, project_dir: str | Path | None = None
48
+ ) -> int:
49
+ value = _project_value(name, default, project_dir=project_dir)
50
+ try:
51
+ return int(value)
52
+ except Exception:
53
+ return int(default)
54
+
55
+
56
+ def _project_bool(
57
+ name: str, default: bool, *, project_dir: str | Path | None = None
58
+ ) -> bool:
59
+ value = _project_value(name, default, project_dir=project_dir)
60
+ if isinstance(value, bool):
61
+ return value
62
+ if value is None:
63
+ return bool(default)
64
+ return str(value).strip().lower() in {"1", "true", "yes", "on"}
65
+
66
+
67
+ def resolve_host_project_shortcuts(
68
+ *, project_dir: str | Path | None = None
69
+ ) -> HostProjectShortcuts:
70
+ return HostProjectShortcuts(
71
+ app_host=str(
72
+ _project_value("app_host", "0.0.0.0", project_dir=project_dir) or "0.0.0.0"
73
+ ).strip()
74
+ or "0.0.0.0",
75
+ app_port=_project_int("app_port", 8000, project_dir=project_dir),
76
+ app_reload=_project_bool("app_reload", False, project_dir=project_dir),
77
+ app_workers=max(1, _project_int("app_workers", 1, project_dir=project_dir)),
78
+ )
79
+
80
+
81
+ def resolve_host_reload_dirs(*, project_dir: str | Path) -> list[str]:
82
+ from pz_core.assembly.app_discovery import iter_visible_app_projections
83
+
84
+ root = Path(project_dir).expanduser().resolve()
85
+ apps_dir = root / "apps"
86
+ if not apps_dir.is_dir():
87
+ return [str(root)]
88
+ return [
89
+ str(projection.visible_root)
90
+ for projection in iter_visible_app_projections(root)
91
+ ]
92
+
93
+
94
+ __all__ = [
95
+ "HostProjectShortcuts",
96
+ "resolve_host_reload_dirs",
97
+ "resolve_host_project_shortcuts",
98
+ "resolve_project_dir",
99
+ ]
@@ -0,0 +1,50 @@
1
+ """Canonical package interface for foundation capabilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import import_module
6
+
7
+ _EXPORTS = {
8
+ "CacheFacade": ("pz_foundation.public", "CacheFacade"),
9
+ "DatabaseFacade": ("pz_foundation.public", "DatabaseFacade"),
10
+ "FoundationFacade": ("pz_foundation.public", "FoundationFacade"),
11
+ "HealthFacade": ("pz_foundation.public", "HealthFacade"),
12
+ "MailFacade": ("pz_foundation.public", "MailFacade"),
13
+ "ObservabilityFacade": ("pz_foundation.public", "ObservabilityFacade"),
14
+ "StorageFacade": ("pz_foundation.public", "StorageFacade"),
15
+ "build_local_typed_settings": ("pz_foundation.public", "build_local_typed_settings"),
16
+ "build_typed_settings_from_namespace": ("pz_foundation.public", "build_typed_settings_from_namespace"),
17
+ "get_foundation_facade": ("pz_foundation.public", "get_foundation_facade"),
18
+ "install_lazy_callables": ("pz_foundation.lazy_imports", "install_lazy_callables"),
19
+ "LazyCallableProxy": ("pz_foundation.lazy_imports", "LazyCallableProxy"),
20
+ "LazyImportProxy": ("pz_foundation.lazy_imports", "LazyImportProxy"),
21
+ "lazy_import_attr": ("pz_foundation.lazy_imports", "lazy_import_attr"),
22
+ "lazy_import_factory": ("pz_foundation.lazy_imports", "lazy_import_factory"),
23
+ "normalize_settings_payload_keys": ("pz_foundation.public", "normalize_settings_payload_keys"),
24
+ "resolve_typed_app_settings": ("pz_foundation.public", "resolve_typed_app_settings"),
25
+ "FoundationDatabase": ("pz_foundation.db", "FoundationDatabase"),
26
+ "FoundationDatabaseSettings": ("pz_foundation.db", "FoundationDatabaseSettings"),
27
+ "MySQLFoundationDatabase": ("pz_foundation.db", "MySQLFoundationDatabase"),
28
+ "PostgreSQLFoundationDatabase": ("pz_foundation.db", "PostgreSQLFoundationDatabase"),
29
+ "SQLiteFoundationDatabase": ("pz_foundation.db", "SQLiteFoundationDatabase"),
30
+ "build_foundation_connections": ("pz_foundation.db", "build_foundation_connections"),
31
+ "build_foundation_tortoise_config": ("pz_foundation.db", "build_foundation_tortoise_config"),
32
+ "get_foundation_database": ("pz_foundation.db", "get_foundation_database"),
33
+ "get_foundation_database_if_enabled": ("pz_foundation.db", "get_foundation_database_if_enabled"),
34
+ "get_foundation_tortoise_config": ("pz_foundation.db", "get_foundation_tortoise_config"),
35
+ "load_foundation_database_settings": ("pz_foundation.db", "load_foundation_database_settings"),
36
+ "reset_foundation_databases": ("pz_foundation.db", "reset_foundation_databases"),
37
+ }
38
+
39
+ __all__ = sorted(_EXPORTS)
40
+
41
+
42
+ def __getattr__(name: str):
43
+ target = _EXPORTS.get(name)
44
+ if target is None:
45
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
46
+ module_name, attr_name = target
47
+ module = import_module(module_name)
48
+ value = getattr(module, attr_name)
49
+ globals()[name] = value
50
+ return value
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from typing import Any, Protocol, runtime_checkable
6
+
7
+
8
+ @dataclass(frozen=True, slots=True)
9
+ class PresignInfo:
10
+ method: str
11
+ url: str
12
+ expires_in_seconds: int
13
+
14
+
15
+ @dataclass(frozen=True, slots=True)
16
+ class StoredObject:
17
+ object_key: str
18
+ size: int | None = None
19
+ content_type: str | None = None
20
+
21
+
22
+ @runtime_checkable
23
+ class StoragePort(Protocol):
24
+ def store_bytes(self, object_key: str, payload: bytes, *, content_type: str | None = None) -> StoredObject: ...
25
+
26
+ def delete(self, object_key: str) -> None: ...
27
+
28
+ def presign_get(self, object_key: str, *, expires_in_seconds: int) -> PresignInfo: ...
29
+
30
+
31
+ @dataclass(frozen=True, slots=True)
32
+ class ResourceBuildResult:
33
+ manifest_path: Path
34
+ output_dir: Path
35
+
36
+
37
+ @runtime_checkable
38
+ class ResourceBuildPort(Protocol):
39
+ def build(self, *, force: bool = False) -> ResourceBuildResult: ...
40
+
41
+
42
+ @dataclass(frozen=True, slots=True)
43
+ class DbOwner:
44
+ app_id: str
45
+ module_path: str
46
+ owner: str
47
+
48
+
49
+ @dataclass(frozen=True, slots=True)
50
+ class MigrationSource:
51
+ app_id: str
52
+ owner: str
53
+ module_path: str
54
+ version: str
55
+ package_root: str
56
+ migrations_dir: str
57
+ models_module: str | None = None
58
+ files: tuple[str, ...] = ()
59
+
60
+
61
+ @dataclass(frozen=True, slots=True)
62
+ class DatabaseMigrationSnapshot:
63
+ workspace_dir: Path
64
+ manifest_path: Path
65
+ orm_config_path: Path
66
+ lock_path: Path
67
+ database_enabled: bool
68
+ database_driver: str
69
+ migration_executor: str | None
70
+ migration_owners: tuple[DbOwner, ...]
71
+ migration_sources: tuple[MigrationSource, ...]
72
+ history_entries: dict[str, tuple[str, ...]]
73
+
74
+
75
+ @runtime_checkable
76
+ class DatabaseMigrationPort(Protocol):
77
+ def status(self) -> DatabaseMigrationSnapshot: ...
78
+
79
+ def prepare(self, *, action: str) -> DatabaseMigrationSnapshot: ...
80
+
81
+ def execute(self, *, action: str) -> tuple[DatabaseMigrationSnapshot, dict[str, Any] | None]: ...
@@ -0,0 +1,34 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+
7
+ @dataclass(slots=True)
8
+ class FoundationError(Exception):
9
+ code: str
10
+ message: str
11
+ stage: str
12
+ owner: str
13
+ action: str
14
+ summary: str | None = None
15
+ detail: str | None = None
16
+
17
+ def to_dict(self) -> dict[str, Any]:
18
+ payload = {
19
+ "code": self.code,
20
+ "message": self.message,
21
+ "summary": self.summary or self.message,
22
+ "stage": self.stage,
23
+ "owner": self.owner,
24
+ "action": self.action,
25
+ }
26
+ if self.detail:
27
+ payload["detail"] = self.detail
28
+ return payload
29
+
30
+ def __str__(self) -> str:
31
+ summary = self.summary or self.message
32
+ if self.detail:
33
+ return f"[{self.code}] {summary}: {self.detail}"
34
+ return f"[{self.code}] {summary}"
pz_foundation/api.py ADDED
@@ -0,0 +1,14 @@
1
+ """Formal API surface for foundation runtime helpers outside the stable public facade."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .lazy_imports import lazy_import_attr
6
+ from .lazy_exports import export_dir, resolve_lazy_export
7
+ from .resources.builder import build_resource_manifest
8
+
9
+ __all__ = [
10
+ "build_resource_manifest",
11
+ "export_dir",
12
+ "lazy_import_attr",
13
+ "resolve_lazy_export",
14
+ ]