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,282 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import re
5
+ import shlex
6
+ import subprocess
7
+ from collections.abc import Mapping, Sequence
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from pz_core.authoring.project_defaults import load_project_default_jsonc_payload
13
+ from pz_core.project_runtime_cli import resolve_cli_project_dir
14
+ from pz_core.root_cli import CommandPath, RootCommandHandler
15
+ from pz_core.utils.project_jsonc import PROJECT_JSONC_NAME, load_jsonc_file
16
+
17
+
18
+ _OWNER = "pz_core.project_root_commands"
19
+ _COMMAND_NAME_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9:_-]*$")
20
+
21
+
22
+ @dataclass(frozen=True, slots=True)
23
+ class ProjectRootCommandBinding:
24
+ name: str
25
+ source_kind: str
26
+ target: str
27
+ description: str | None
28
+ handler: RootCommandHandler
29
+
30
+
31
+ @dataclass(frozen=True, slots=True)
32
+ class ConfiguredProjectCommand:
33
+ name: str
34
+ description: str | None
35
+ argv: tuple[str, ...] | None
36
+ shell_command: str | None
37
+
38
+
39
+ def discover_project_root_command_bindings(
40
+ *,
41
+ reserved_paths: Sequence[CommandPath] = (),
42
+ project_dir: Path | None = None,
43
+ ) -> tuple[ProjectRootCommandBinding, ...]:
44
+ resolved_project_dir = project_dir if project_dir is not None else resolve_cli_project_dir()
45
+ if resolved_project_dir is None:
46
+ return ()
47
+ project_dir = resolved_project_dir.resolve()
48
+ reserved_tokens = {path[0] for path in reserved_paths if path}
49
+
50
+ bindings: list[ProjectRootCommandBinding] = []
51
+ for command in _discover_configured_project_commands(project_dir):
52
+ if command.name in reserved_tokens:
53
+ continue
54
+ bindings.append(
55
+ ProjectRootCommandBinding(
56
+ name=command.name,
57
+ source_kind="project_command",
58
+ target=command.shell_command or shlex.join(command.argv or ()),
59
+ description=command.description,
60
+ handler=RootCommandHandler(
61
+ command_path=(command.name,),
62
+ handler=lambda argv, command=command, project_dir=project_dir: _run_project_command(
63
+ command,
64
+ project_dir=project_dir,
65
+ argv=_strip_root_project_dir_args(argv),
66
+ ),
67
+ ),
68
+ )
69
+ )
70
+ reserved_tokens.add(command.name)
71
+
72
+ return tuple(bindings)
73
+
74
+
75
+ def discover_project_root_command_handlers(
76
+ *,
77
+ reserved_paths: Sequence[CommandPath] = (),
78
+ project_dir: Path | None = None,
79
+ ) -> dict[CommandPath, RootCommandHandler]:
80
+ return {
81
+ binding.handler.command_path: binding.handler
82
+ for binding in discover_project_root_command_bindings(
83
+ reserved_paths=reserved_paths,
84
+ project_dir=project_dir,
85
+ )
86
+ }
87
+
88
+
89
+ def render_project_root_command_help(
90
+ *,
91
+ reserved_paths: Sequence[CommandPath] = (),
92
+ project_dir: Path | None = None,
93
+ ) -> str:
94
+ bindings = discover_project_root_command_bindings(
95
+ reserved_paths=reserved_paths,
96
+ project_dir=project_dir,
97
+ )
98
+ if not bindings:
99
+ return ""
100
+ lines = ["", "Extensions:"]
101
+ for binding in bindings:
102
+ description = binding.description
103
+ if not description:
104
+ description = f"Project command -> {binding.target}"
105
+ lines.append(f" {binding.name}")
106
+ lines.append(f" {description}")
107
+ return "\n".join(lines) + "\n"
108
+
109
+
110
+ def _discover_configured_project_commands(project_dir: Path) -> tuple[ConfiguredProjectCommand, ...]:
111
+ defaults = load_project_default_jsonc_payload()
112
+ commands_payload = _merge_command_payloads(
113
+ defaults.get("commands"),
114
+ _read_project_config(project_dir).get("commands"),
115
+ )
116
+ commands: list[ConfiguredProjectCommand] = []
117
+ for raw_name, raw_spec in sorted(commands_payload.items()):
118
+ name = _normalize_command_name(raw_name)
119
+ if name is None:
120
+ continue
121
+ commands.append(_coerce_project_command(name=name, payload=raw_spec))
122
+ return tuple(commands)
123
+
124
+
125
+ def _read_project_config(project_dir: Path) -> dict[str, Any]:
126
+ project_path = project_dir / PROJECT_JSONC_NAME
127
+ if not project_path.is_file():
128
+ return {}
129
+ payload = load_jsonc_file(
130
+ project_path,
131
+ code="PZ_PROJECT_CONFIG_INVALID",
132
+ owner=_OWNER,
133
+ action="load_project_root_commands",
134
+ )
135
+ if not isinstance(payload, dict):
136
+ return {}
137
+ return dict(payload)
138
+
139
+
140
+ def _merge_command_payloads(defaults: object, project_payload: object) -> dict[str, Any]:
141
+ merged: dict[str, Any] = {}
142
+ for candidate in (defaults, project_payload):
143
+ if not isinstance(candidate, Mapping):
144
+ continue
145
+ for key, value in candidate.items():
146
+ if isinstance(value, Mapping) and isinstance(merged.get(key), dict):
147
+ merged[key] = _deep_merge_mappings(merged[key], value)
148
+ else:
149
+ merged[str(key)] = value
150
+ return merged
151
+
152
+
153
+ def _deep_merge_mappings(base: Mapping[str, Any], incoming: Mapping[str, Any]) -> dict[str, Any]:
154
+ merged = dict(base)
155
+ for key, value in incoming.items():
156
+ existing = merged.get(key)
157
+ if isinstance(existing, Mapping) and isinstance(value, Mapping):
158
+ merged[str(key)] = _deep_merge_mappings(existing, value)
159
+ continue
160
+ merged[str(key)] = value
161
+ return merged
162
+
163
+
164
+ def _normalize_command_name(raw_name: object) -> str | None:
165
+ if not isinstance(raw_name, str):
166
+ return None
167
+ name = raw_name.strip().lower()
168
+ if not name or not _COMMAND_NAME_RE.match(name):
169
+ return None
170
+ return name
171
+
172
+
173
+ def _coerce_project_command(*, name: str, payload: object) -> ConfiguredProjectCommand:
174
+ if isinstance(payload, str) and payload.strip():
175
+ return ConfiguredProjectCommand(
176
+ name=name,
177
+ description=None,
178
+ argv=None,
179
+ shell_command=payload.strip(),
180
+ )
181
+ if isinstance(payload, Sequence) and not isinstance(payload, (str, bytes)):
182
+ argv = tuple(str(item).strip() for item in payload if str(item).strip())
183
+ if argv:
184
+ return ConfiguredProjectCommand(
185
+ name=name,
186
+ description=None,
187
+ argv=argv,
188
+ shell_command=None,
189
+ )
190
+ if isinstance(payload, Mapping):
191
+ description = str(payload.get("description") or "").strip() or None
192
+ raw_command = payload.get("command")
193
+ if isinstance(raw_command, str) and raw_command.strip():
194
+ return ConfiguredProjectCommand(
195
+ name=name,
196
+ description=description,
197
+ argv=None,
198
+ shell_command=raw_command.strip(),
199
+ )
200
+ raw_argv = payload.get("argv")
201
+ if isinstance(raw_argv, Sequence) and not isinstance(raw_argv, (str, bytes)):
202
+ argv = tuple(str(item).strip() for item in raw_argv if str(item).strip())
203
+ if argv:
204
+ return ConfiguredProjectCommand(
205
+ name=name,
206
+ description=description,
207
+ argv=argv,
208
+ shell_command=None,
209
+ )
210
+ raise ValueError(f"Invalid project root command configuration for `{name}`")
211
+
212
+
213
+ def _run_project_command(command: ConfiguredProjectCommand, *, project_dir: Path, argv: Sequence[str]) -> int:
214
+ env = _build_project_command_env(project_dir, command.name)
215
+ if command.shell_command is not None:
216
+ extra = shlex.join([str(item) for item in argv if str(item).strip()])
217
+ shell_command = command.shell_command if not extra else f"{command.shell_command} {extra}"
218
+ completed = subprocess.run(
219
+ shell_command,
220
+ cwd=str(project_dir),
221
+ env=env,
222
+ shell=True,
223
+ check=False,
224
+ )
225
+ return int(completed.returncode)
226
+ execution_argv = [*(command.argv or ()), *[str(item) for item in argv]]
227
+ completed = subprocess.run(
228
+ execution_argv,
229
+ cwd=str(project_dir),
230
+ env=env,
231
+ shell=False,
232
+ check=False,
233
+ )
234
+ return int(completed.returncode)
235
+
236
+
237
+ def _build_project_command_env(project_dir: Path, command_name: str) -> dict[str, str]:
238
+ env = dict(os.environ)
239
+ project_dir = project_dir.resolve()
240
+ env["PZ_PROJECT_DIR"] = str(project_dir)
241
+ env["PZ_PROJECT_COMMAND"] = command_name
242
+ env["PZ_PROJECT_COMMAND_DEPTH"] = str(int(env.get("PZ_PROJECT_COMMAND_DEPTH", "0")) + 1)
243
+ path_entries = [str(path) for path in _project_bin_dirs(project_dir)]
244
+ existing_path = env.get("PATH", "")
245
+ env["PATH"] = os.pathsep.join([*path_entries, existing_path]) if existing_path else os.pathsep.join(path_entries)
246
+ return env
247
+
248
+
249
+ def _project_bin_dirs(project_dir: Path) -> tuple[Path, ...]:
250
+ candidates = (
251
+ project_dir / ".venv" / "bin",
252
+ project_dir / ".venv" / "Scripts",
253
+ )
254
+ return tuple(path.resolve() for path in candidates if path.is_dir())
255
+
256
+
257
+ def _strip_root_project_dir_args(argv: Sequence[str]) -> list[str]:
258
+ cleaned: list[str] = []
259
+ skip_next = False
260
+ for index, raw in enumerate(argv):
261
+ if skip_next:
262
+ skip_next = False
263
+ continue
264
+ item = str(raw)
265
+ if item in {"--project-dir", "-d"}:
266
+ skip_next = True
267
+ continue
268
+ if item.startswith("--project-dir="):
269
+ continue
270
+ if item.startswith("-d="):
271
+ continue
272
+ cleaned.append(item)
273
+ return cleaned
274
+
275
+
276
+ __all__ = [
277
+ "ConfiguredProjectCommand",
278
+ "ProjectRootCommandBinding",
279
+ "discover_project_root_command_bindings",
280
+ "discover_project_root_command_handlers",
281
+ "render_project_root_command_help",
282
+ ]
@@ -0,0 +1,344 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import shutil
6
+ import subprocess
7
+ import sys
8
+ from collections.abc import Mapping, Sequence
9
+ from pathlib import Path
10
+
11
+ from pz_core.project_sources import (
12
+ resolve_contrib_source_root,
13
+ resolve_runtime_checkout_root,
14
+ resolve_sources_manifest_path,
15
+ )
16
+ from pz_core.runtime.errors import PzError
17
+ from pz_core.utils.project_jsonc import PROJECT_JSONC_NAME
18
+
19
+ PROVIDER_LOCK_ENV_NAME = "PZ_CLI_PROVIDER_LOCK"
20
+
21
+
22
+ def resolve_cli_project_dir(argv: Sequence[str] | None = None) -> Path | None:
23
+ args = list(argv or sys.argv[1:])
24
+ explicit = _parse_project_dir_arg(args)
25
+ if explicit is not None:
26
+ return explicit
27
+ env_project_dir = _read_project_dir_env()
28
+ if env_project_dir is not None:
29
+ return env_project_dir
30
+ return _discover_project_dir_from_cwd()
31
+
32
+
33
+ def resolve_project_runtime_package_root(project_dir: Path) -> Path | None:
34
+ manifest_path = resolve_sources_manifest_path(project_dir)
35
+ if manifest_path.is_file():
36
+ try:
37
+ payload = json.loads(manifest_path.read_text(encoding="utf-8"))
38
+ except Exception:
39
+ payload = {}
40
+ runtime = payload.get("runtime")
41
+ if isinstance(runtime, dict):
42
+ runtime_meta = runtime.get("pz")
43
+ if isinstance(runtime_meta, dict):
44
+ raw_path = str(runtime_meta.get("path") or "").strip()
45
+ if raw_path:
46
+ resolved = _resolve_runtime_package_root_candidate(project_dir, raw_path)
47
+ if resolved is not None:
48
+ return resolved
49
+ return _resolve_runtime_package_root_candidate(
50
+ project_dir,
51
+ "apps/.sources/pz/pz_core/pz_core",
52
+ )
53
+
54
+
55
+ def handoff_to_project_runtime(
56
+ argv: Sequence[str] | None = None,
57
+ *,
58
+ current_package_root: Path,
59
+ ) -> bool:
60
+ project_dir = resolve_cli_project_dir(argv)
61
+ if project_dir is None:
62
+ return False
63
+ runtime_package_root = resolve_project_runtime_package_root(project_dir)
64
+ if runtime_package_root is None:
65
+ return False
66
+ resolved_current = current_package_root.expanduser().resolve()
67
+ project_python = _resolve_project_python(project_dir, runtime_package_root=runtime_package_root)
68
+ if runtime_package_root.resolve() == resolved_current and _same_python_executable(project_python, sys.executable):
69
+ return False
70
+ env = _build_project_runtime_env(project_dir, runtime_package_root)
71
+ os.execvpe(
72
+ project_python,
73
+ [project_python, "-m", "pz_core", *list(argv or sys.argv[1:])],
74
+ env,
75
+ )
76
+ return True
77
+
78
+
79
+ def run_project_runtime_json_command(
80
+ project_dir: Path,
81
+ argv: Sequence[str],
82
+ *,
83
+ action: str,
84
+ error_code: str,
85
+ error_message: str,
86
+ ) -> dict[str, object]:
87
+ runtime_package_root = resolve_project_runtime_package_root(project_dir)
88
+ if runtime_package_root is None:
89
+ raise PzError(
90
+ code=error_code,
91
+ message=error_message,
92
+ stage="cli",
93
+ owner="pz_core.project_runtime",
94
+ action=action,
95
+ detail=f"missing runtime binding for {project_dir}",
96
+ )
97
+ project_python = _resolve_project_python(project_dir, runtime_package_root=runtime_package_root)
98
+ completed = subprocess.run(
99
+ [project_python, "-m", "pz_core", *argv],
100
+ check=False,
101
+ capture_output=True,
102
+ text=True,
103
+ env=_build_project_runtime_env(project_dir, runtime_package_root),
104
+ )
105
+ if completed.returncode != 0:
106
+ detail = (completed.stderr or completed.stdout or "project runtime command failed").strip()
107
+ raise PzError(
108
+ code=error_code,
109
+ message=error_message,
110
+ stage="cli",
111
+ owner="pz_core.project_runtime",
112
+ action=action,
113
+ detail=detail,
114
+ )
115
+ output = (completed.stdout or "").strip()
116
+ try:
117
+ payload = json.loads(output)
118
+ except Exception as exc:
119
+ raise PzError(
120
+ code=error_code,
121
+ message=error_message,
122
+ stage="cli",
123
+ owner="pz_core.project_runtime",
124
+ action=action,
125
+ detail=output or str(exc),
126
+ ) from exc
127
+ if not isinstance(payload, dict):
128
+ raise PzError(
129
+ code=error_code,
130
+ message=error_message,
131
+ stage="cli",
132
+ owner="pz_core.project_runtime",
133
+ action=action,
134
+ detail="project runtime command did not return a JSON object.",
135
+ )
136
+ return payload
137
+
138
+
139
+ def resolve_project_pz_executable(project_dir: Path) -> str:
140
+ venv_root = project_dir / ".venv"
141
+ pz_command = _resolve_venv_pz_command(venv_root)
142
+ if pz_command is not None:
143
+ return str(pz_command)
144
+ resolved = shutil.which("pz")
145
+ if resolved:
146
+ return resolved
147
+ raise PzError(
148
+ code="PZ_PROJECT_RUNTIME_PZ_MISSING",
149
+ message="Formal `pz` launcher is required for project runtime handoff, but no `pz` command was found.",
150
+ stage="cli",
151
+ owner="pz_core.project_runtime",
152
+ action="resolve_project_pz_executable",
153
+ )
154
+
155
+
156
+ def build_project_pz_child_env(
157
+ project_dir: Path,
158
+ *,
159
+ pz_env: str | None = None,
160
+ env_overrides: Mapping[str, str | int | None] | None = None,
161
+ ) -> dict[str, str]:
162
+ env = dict(os.environ)
163
+ env["PZ_PROJECT_DIR"] = str(project_dir.resolve())
164
+ inherited_pz_env = str(env.get("PZ_ENV") or "").strip()
165
+ resolved_pz_env = inherited_pz_env or str(pz_env or "").strip()
166
+ if resolved_pz_env:
167
+ env["PZ_ENV"] = resolved_pz_env
168
+ if env_overrides:
169
+ for raw_key, raw_value in env_overrides.items():
170
+ key = str(raw_key or "").strip()
171
+ if not key:
172
+ continue
173
+ if raw_value is None:
174
+ env.pop(key, None)
175
+ continue
176
+ env[key] = str(raw_value)
177
+ return env
178
+
179
+
180
+ def _parse_project_dir_arg(args: list[str]) -> Path | None:
181
+ for index, raw in enumerate(args):
182
+ item = str(raw).strip()
183
+ if not item:
184
+ continue
185
+ if item in {"--project-dir", "-d"} and index + 1 < len(args):
186
+ value = str(args[index + 1]).strip()
187
+ if value:
188
+ return Path(value).expanduser().resolve()
189
+ return None
190
+ prefix = "--project-dir="
191
+ if item.startswith(prefix):
192
+ value = item[len(prefix) :].strip()
193
+ if value:
194
+ return Path(value).expanduser().resolve()
195
+ return None
196
+ short_prefix = "-d="
197
+ if item.startswith(short_prefix):
198
+ value = item[len(short_prefix) :].strip()
199
+ if value:
200
+ return Path(value).expanduser().resolve()
201
+ return None
202
+ return None
203
+
204
+
205
+ def _read_project_dir_env() -> Path | None:
206
+ raw = os.getenv("PZ_PROJECT_DIR")
207
+ if not isinstance(raw, str) or not raw.strip():
208
+ return None
209
+ return Path(raw.strip()).expanduser().resolve()
210
+
211
+
212
+ def _discover_project_dir_from_cwd() -> Path | None:
213
+ current = Path.cwd().resolve()
214
+ for candidate in (current, *current.parents):
215
+ if (candidate / PROJECT_JSONC_NAME).is_file():
216
+ return candidate
217
+ return None
218
+
219
+
220
+ def _is_runtime_package_root(path: Path) -> bool:
221
+ return (
222
+ path.is_dir()
223
+ and (path / "__init__.py").is_file()
224
+ and (path / "__main__.py").is_file()
225
+ )
226
+
227
+
228
+ def provider_resolution_locked() -> bool:
229
+ raw = os.getenv(PROVIDER_LOCK_ENV_NAME)
230
+ return isinstance(raw, str) and bool(raw.strip())
231
+
232
+
233
+ def _resolve_runtime_package_root_candidate(project_dir: Path, raw_path: str) -> Path | None:
234
+ candidate = Path(raw_path).expanduser()
235
+ if not candidate.is_absolute():
236
+ candidate = project_dir / candidate
237
+ if _is_runtime_package_root(candidate):
238
+ return candidate.absolute()
239
+ nested = candidate / "pz"
240
+ if _is_runtime_package_root(nested):
241
+ return nested.absolute()
242
+ return None
243
+
244
+
245
+ def _build_project_runtime_env(project_dir: Path, runtime_package_root: Path) -> dict[str, str]:
246
+ env = dict(os.environ)
247
+ existing_pythonpath = env.get("PYTHONPATH", "").strip()
248
+ pythonpath_entries = [entry for entry in existing_pythonpath.split(os.pathsep) if entry]
249
+ prepend_entries = [str(project_dir.resolve()), *[str(path) for path in _runtime_import_roots(runtime_package_root)]]
250
+ pythonpath_entries = [entry for entry in pythonpath_entries if entry not in prepend_entries]
251
+ env["PYTHONPATH"] = os.pathsep.join([*prepend_entries, *pythonpath_entries])
252
+ env["PZ_PROJECT_DIR"] = str(project_dir.resolve())
253
+ env["PYTHONNOUSERSITE"] = "1"
254
+ program_name = os.path.basename(str(sys.argv[0] or "")).strip().lower()
255
+ if program_name in {"pz", "pz"}:
256
+ env["PZ_PROGRAM_NAME"] = program_name
257
+ elif not env.get("PZ_PROGRAM_NAME"):
258
+ env["PZ_PROGRAM_NAME"] = "pz"
259
+ return env
260
+
261
+
262
+ def _runtime_import_roots(runtime_package_root: Path) -> tuple[Path, ...]:
263
+ roots: list[Path] = [runtime_package_root.parent.resolve()]
264
+ checkout_root = resolve_runtime_checkout_root(runtime_package_root)
265
+ contrib_root = resolve_contrib_source_root(checkout_root)
266
+ if contrib_root.is_dir():
267
+ for package_root in sorted(contrib_root.iterdir()):
268
+ if not package_root.is_dir():
269
+ continue
270
+ if not (package_root / "pyproject.toml").is_file():
271
+ continue
272
+ roots.append(package_root.resolve())
273
+ return tuple(roots)
274
+
275
+
276
+ def _source_project_root(path: Path) -> Path | None:
277
+ absolute = path.expanduser().absolute()
278
+ for candidate in (absolute, *absolute.parents):
279
+ if candidate.name == ".sources" and candidate.parent.name == "apps":
280
+ return candidate.parent.parent.absolute()
281
+ return None
282
+
283
+
284
+ def _target_virtualenv_path(path: Path) -> Path:
285
+ absolute = path.expanduser().absolute()
286
+ project_root = _source_project_root(absolute)
287
+ if project_root is None:
288
+ return absolute.resolve() / ".venv"
289
+ try:
290
+ relative = absolute.relative_to(project_root)
291
+ except ValueError:
292
+ return absolute.resolve() / ".venv"
293
+ return project_root / ".runtime" / "source-venvs" / relative / ".venv"
294
+
295
+
296
+ def _resolve_project_python(project_dir: Path, *, runtime_package_root: Path | None = None) -> str:
297
+ candidates: list[Path] = [
298
+ project_dir / ".venv" / "bin" / "python",
299
+ project_dir / ".venv" / "Scripts" / "python.exe",
300
+ ]
301
+ if runtime_package_root is not None:
302
+ runtime_roots = [runtime_package_root.parent.absolute(), runtime_package_root.absolute()]
303
+ for root in runtime_roots:
304
+ candidates.extend(
305
+ (
306
+ _target_virtualenv_path(root) / "bin" / "python",
307
+ _target_virtualenv_path(root) / "Scripts" / "python.exe",
308
+ )
309
+ )
310
+ for candidate in candidates:
311
+ if candidate.is_file():
312
+ return str(candidate.expanduser().absolute())
313
+ return sys.executable
314
+
315
+
316
+ def _resolve_venv_pz_command(venv_root: Path) -> Path | None:
317
+ candidates = (
318
+ venv_root / "bin" / "pz",
319
+ venv_root / "Scripts" / "pz.exe",
320
+ venv_root / "Scripts" / "pz",
321
+ )
322
+ for candidate in candidates:
323
+ if candidate.is_file():
324
+ return candidate.expanduser().absolute()
325
+ return None
326
+
327
+
328
+ def _same_python_executable(left: str, right: str) -> bool:
329
+ try:
330
+ return os.path.abspath(os.path.expanduser(left)) == os.path.abspath(os.path.expanduser(right))
331
+ except Exception:
332
+ return left == right
333
+
334
+
335
+ __all__ = [
336
+ "build_project_pz_child_env",
337
+ "PROVIDER_LOCK_ENV_NAME",
338
+ "handoff_to_project_runtime",
339
+ "provider_resolution_locked",
340
+ "resolve_cli_project_dir",
341
+ "resolve_project_pz_executable",
342
+ "resolve_project_runtime_package_root",
343
+ "run_project_runtime_json_command",
344
+ ]