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,253 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from dataclasses import dataclass
5
+ from functools import lru_cache
6
+ from importlib.resources import files
7
+ import json
8
+ from pathlib import Path, PurePosixPath
9
+ from types import MappingProxyType
10
+ from typing import Literal, cast
11
+
12
+ from pz_core.runtime.errors import PzError
13
+
14
+
15
+ _SOURCES_DIR = ".sources"
16
+ _SOURCES_ROOT = Path("apps") / _SOURCES_DIR
17
+ _MANIFEST_PATH = _SOURCES_ROOT / "manifest.json"
18
+ OFFICIAL_CONTRIB_SOURCE_ROOT = "pz_contrib"
19
+
20
+
21
+ @dataclass(frozen=True, slots=True)
22
+ class OfficialDevSourceSpec:
23
+ name: str
24
+ remote_url: str
25
+ workspace_identifier: str
26
+ default_branch: str
27
+ runtime_id: str | None = None
28
+ declared_app: str | None = None
29
+
30
+
31
+ OFFICIAL_RUNTIME_SOURCE = OfficialDevSourceSpec(
32
+ name="pz",
33
+ remote_url="git@github.com:JiXiangTools/pyzza.git",
34
+ workspace_identifier="pz",
35
+ default_branch="develop",
36
+ runtime_id="pz",
37
+ )
38
+ OFFICIAL_SOURCE_ROOTS = (
39
+ OfficialDevSourceSpec(
40
+ name="pz_apps",
41
+ remote_url="git@github.com:JiXiangTools/pyzza_platform.git",
42
+ workspace_identifier="pz-apps-source-root",
43
+ default_branch="develop",
44
+ declared_app="pz_apps",
45
+ ),
46
+ )
47
+ OFFICIAL_TRUTH_SOURCE_REPO_NAMES = (
48
+ OFFICIAL_RUNTIME_SOURCE.name,
49
+ *(item.name for item in OFFICIAL_SOURCE_ROOTS),
50
+ )
51
+
52
+
53
+ @dataclass(frozen=True, slots=True)
54
+ class OfficialSourceChild:
55
+ kind: Literal["project", "app"]
56
+ subpath: PurePosixPath
57
+ expected_app_id: str | None
58
+
59
+
60
+ @dataclass(frozen=True, slots=True)
61
+ class OfficialSourceRoot:
62
+ repo_key: str
63
+ source: str
64
+ kind: Literal["directory"]
65
+ bootstrap: bool
66
+ children: Mapping[str, OfficialSourceChild]
67
+
68
+ def __post_init__(self) -> None:
69
+ object.__setattr__(self, "children", MappingProxyType(dict(self.children)))
70
+
71
+
72
+ def _catalog_invalid(path: str, reason: str) -> PzError:
73
+ return PzError(
74
+ code="PZ_DEV_OFFICIAL_SOURCE_CATALOG_INVALID",
75
+ message=f"{path}: {reason}",
76
+ stage="dev",
77
+ owner="pz_core.project_sources",
78
+ action="load_official_source_catalog",
79
+ )
80
+
81
+
82
+ def _require_catalog_object(value: object, path: str) -> dict[str, object]:
83
+ if not isinstance(value, dict) or not all(isinstance(key, str) for key in value):
84
+ raise _catalog_invalid(path, "expected an object")
85
+ return cast(dict[str, object], value)
86
+
87
+
88
+ def _require_exact_fields(
89
+ value: object,
90
+ path: str,
91
+ expected: frozenset[str],
92
+ ) -> dict[str, object]:
93
+ payload = _require_catalog_object(value, path)
94
+ fields = set(payload)
95
+ if fields != expected:
96
+ missing = sorted(expected - fields)
97
+ unknown = sorted(fields - expected)
98
+ reason = (
99
+ f"missing field {missing[0]!r}"
100
+ if missing
101
+ else f"unknown field {unknown[0]!r}"
102
+ )
103
+ raise _catalog_invalid(path, reason)
104
+ return payload
105
+
106
+
107
+ def _parse_catalog_subpath(value: object, path: str) -> PurePosixPath:
108
+ if not isinstance(value, str) or not value or value != value.strip():
109
+ raise _catalog_invalid(path, "expected a normalized relative POSIX path")
110
+ subpath = PurePosixPath(value)
111
+ if (
112
+ subpath.is_absolute()
113
+ or ".." in subpath.parts
114
+ or subpath.as_posix() != value
115
+ ):
116
+ raise _catalog_invalid(path, "expected a normalized relative POSIX path")
117
+ return subpath
118
+
119
+
120
+ def _parse_official_child(key: str, value: object, path: str) -> OfficialSourceChild:
121
+ payload = _require_exact_fields(
122
+ value,
123
+ path,
124
+ frozenset({"kind", "subpath", "expected_app_id"}),
125
+ )
126
+ kind = payload["kind"]
127
+ if kind not in {"project", "app"}:
128
+ raise _catalog_invalid(f"{path}.kind", "expected 'project' or 'app'")
129
+ expected_app_id = payload["expected_app_id"]
130
+ if kind == "project":
131
+ if expected_app_id is not None:
132
+ raise _catalog_invalid(
133
+ f"{path}.expected_app_id",
134
+ "project metadata cannot expose an app id",
135
+ )
136
+ elif expected_app_id != key.replace("-", "_"):
137
+ raise _catalog_invalid(
138
+ f"{path}.expected_app_id",
139
+ "app metadata must match the normalized child key",
140
+ )
141
+ subpath = _parse_catalog_subpath(payload["subpath"], f"{path}.subpath")
142
+ if kind == "app" and subpath == PurePosixPath("."):
143
+ raise _catalog_invalid(
144
+ f"{path}.subpath",
145
+ "app metadata must resolve below its source root",
146
+ )
147
+ return OfficialSourceChild(
148
+ kind=cast(Literal["project", "app"], kind),
149
+ subpath=subpath,
150
+ expected_app_id=cast(str | None, expected_app_id),
151
+ )
152
+
153
+
154
+ def _parse_official_root(
155
+ repo_key: str,
156
+ value: object,
157
+ path: str,
158
+ ) -> OfficialSourceRoot:
159
+ payload = _require_exact_fields(
160
+ value,
161
+ path,
162
+ frozenset({"source", "kind", "bootstrap", "children"}),
163
+ )
164
+ source = payload["source"]
165
+ if not isinstance(source, str) or not source.strip() or source != source.strip():
166
+ raise _catalog_invalid(f"{path}.source", "expected a normalized source URL")
167
+ if payload["kind"] != "directory":
168
+ raise _catalog_invalid(f"{path}.kind", "expected 'directory'")
169
+ bootstrap = payload["bootstrap"]
170
+ if type(bootstrap) is not bool:
171
+ raise _catalog_invalid(f"{path}.bootstrap", "expected a boolean")
172
+ raw_children = _require_catalog_object(payload["children"], f"{path}.children")
173
+ children = {
174
+ key: _parse_official_child(key, child, f"{path}.children.{key}")
175
+ for key, child in raw_children.items()
176
+ }
177
+ return OfficialSourceRoot(
178
+ repo_key=repo_key,
179
+ source=source,
180
+ kind="directory",
181
+ bootstrap=bootstrap,
182
+ children=children,
183
+ )
184
+
185
+
186
+ @lru_cache(maxsize=1)
187
+ def load_official_source_catalog() -> Mapping[str, OfficialSourceRoot]:
188
+ resource = files("pz_core.authoring").joinpath("official_sources.json")
189
+ try:
190
+ raw = json.loads(resource.read_text(encoding="utf-8"))
191
+ except (OSError, UnicodeError, json.JSONDecodeError) as exc:
192
+ raise _catalog_invalid("$", f"cannot read catalog JSON: {exc}") from exc
193
+ payload = _require_catalog_object(raw, "$")
194
+ if set(payload) != {"pz", "pz_apps"}:
195
+ raise _catalog_invalid("$", "expected exactly the pz and pz_apps roots")
196
+ catalog = {
197
+ key: _parse_official_root(key, value, f"$.{key}")
198
+ for key, value in payload.items()
199
+ }
200
+ if catalog["pz"].bootstrap is not True:
201
+ raise _catalog_invalid("$.pz.bootstrap", "pz must be the bootstrap root")
202
+ if catalog["pz_apps"].bootstrap is not False:
203
+ raise _catalog_invalid(
204
+ "$.pz_apps.bootstrap",
205
+ "pz_apps cannot be a bootstrap root",
206
+ )
207
+ return MappingProxyType(catalog)
208
+
209
+
210
+ def resolve_sources_root(project_dir: Path) -> Path:
211
+ return project_dir / _SOURCES_ROOT
212
+
213
+
214
+ def resolve_sources_manifest_path(project_dir: Path) -> Path:
215
+ return project_dir / _MANIFEST_PATH
216
+
217
+
218
+ def resolve_sources_repo_checkout_root(project_dir: Path, repo_key: str) -> Path:
219
+ return resolve_sources_root(project_dir) / repo_key
220
+
221
+
222
+ def resolve_known_sources_roots(project_dir: Path) -> tuple[Path, ...]:
223
+ return (resolve_sources_root(project_dir),)
224
+
225
+
226
+ def resolve_runtime_checkout_root(runtime_package_root: Path) -> Path:
227
+ distribution_root = runtime_package_root.expanduser().resolve().parent
228
+ for candidate in (distribution_root, distribution_root.parent.resolve()):
229
+ if resolve_contrib_source_root(candidate).is_dir():
230
+ return candidate
231
+ return distribution_root
232
+
233
+
234
+ def resolve_contrib_source_root(checkout_root: Path) -> Path:
235
+ return checkout_root.expanduser().resolve() / OFFICIAL_CONTRIB_SOURCE_ROOT
236
+
237
+
238
+ __all__ = [
239
+ "OFFICIAL_CONTRIB_SOURCE_ROOT",
240
+ "OFFICIAL_RUNTIME_SOURCE",
241
+ "OFFICIAL_SOURCE_ROOTS",
242
+ "OFFICIAL_TRUTH_SOURCE_REPO_NAMES",
243
+ "OfficialDevSourceSpec",
244
+ "OfficialSourceChild",
245
+ "OfficialSourceRoot",
246
+ "load_official_source_catalog",
247
+ "resolve_contrib_source_root",
248
+ "resolve_known_sources_roots",
249
+ "resolve_runtime_checkout_root",
250
+ "resolve_sources_manifest_path",
251
+ "resolve_sources_repo_checkout_root",
252
+ "resolve_sources_root",
253
+ ]
pz_core/pz_launcher.py ADDED
@@ -0,0 +1,209 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import shutil
5
+ import sys
6
+ from collections.abc import Sequence
7
+ from pathlib import Path
8
+
9
+ from pz_core.project_runtime_cli import (
10
+ PROVIDER_LOCK_ENV_NAME,
11
+ _build_project_runtime_env,
12
+ _target_virtualenv_path,
13
+ provider_resolution_locked,
14
+ resolve_cli_project_dir,
15
+ resolve_project_runtime_package_root,
16
+ )
17
+ from pz_core.runtime.environment_integrity import validate_current_environment
18
+ from pz_core.runtime.errors import PzError
19
+
20
+
21
+ def main(argv: Sequence[str] | None = None) -> int:
22
+ try:
23
+ validate_current_environment(os.environ)
24
+ except PzError as exc:
25
+ sys.stderr.write(f"{exc}\n")
26
+ return 2
27
+ args = list(argv or sys.argv[1:])
28
+ if not provider_resolution_locked():
29
+ if handoff_to_preferred_pz_provider(args, current_package_root=Path(__file__).resolve().parent):
30
+ return 0
31
+ return _run_current_cli(args)
32
+
33
+
34
+ def handoff_to_preferred_pz_provider(
35
+ argv: Sequence[str] | None = None,
36
+ *,
37
+ current_package_root: Path,
38
+ ) -> bool:
39
+ project_dir = resolve_cli_project_dir(argv)
40
+ if project_dir is None:
41
+ return False
42
+ if _handoff_to_project_venv(argv, project_dir=project_dir, current_package_root=current_package_root):
43
+ return True
44
+ if _handoff_to_project_source(argv, project_dir=project_dir, current_package_root=current_package_root):
45
+ return True
46
+ return False
47
+
48
+
49
+ def _run_current_cli(argv: Sequence[str]) -> int:
50
+ try:
51
+ from pz_core.cli import main as cli_main
52
+ except Exception:
53
+ if not provider_resolution_locked() and _handoff_to_uv_tool(argv):
54
+ return 0
55
+ raise
56
+ return cli_main(list(argv))
57
+
58
+
59
+ def _handoff_to_project_venv(
60
+ argv: Sequence[str] | None,
61
+ *,
62
+ project_dir: Path,
63
+ current_package_root: Path,
64
+ ) -> bool:
65
+ venv_root = project_dir / ".venv"
66
+ if not venv_root.is_dir():
67
+ return False
68
+ venv_python = _resolve_venv_python(venv_root)
69
+ if venv_python is None:
70
+ return False
71
+ current_in_project_venv = _same_python_executable(str(venv_python), sys.executable) and any(
72
+ _path_is_relative_to(current_package_root, site_packages_root)
73
+ for site_packages_root in _site_packages_roots(venv_root)
74
+ )
75
+ if current_in_project_venv:
76
+ return False
77
+ env = dict(os.environ)
78
+ env[PROVIDER_LOCK_ENV_NAME] = "project-venv"
79
+ env["PZ_PROJECT_DIR"] = str(project_dir.resolve())
80
+ pz_command = _resolve_venv_pz_command(venv_root)
81
+ if pz_command is not None:
82
+ os.execvpe(str(pz_command), [str(pz_command), *list(argv or sys.argv[1:])], env)
83
+ return True
84
+ if not _venv_contains_pz(venv_root):
85
+ return False
86
+ os.execvpe(
87
+ str(venv_python),
88
+ [str(venv_python), "-m", "pz_core", *list(argv or sys.argv[1:])],
89
+ env,
90
+ )
91
+ return True
92
+
93
+
94
+ def _handoff_to_project_source(
95
+ argv: Sequence[str] | None,
96
+ *,
97
+ project_dir: Path,
98
+ current_package_root: Path,
99
+ ) -> bool:
100
+ runtime_package_root = resolve_project_runtime_package_root(project_dir)
101
+ if runtime_package_root is None:
102
+ return False
103
+ project_python = _resolve_source_python(runtime_package_root)
104
+ if runtime_package_root.resolve() == current_package_root.expanduser().resolve() and _same_python_executable(
105
+ project_python,
106
+ sys.executable,
107
+ ):
108
+ return False
109
+ env = _build_project_runtime_env(project_dir, runtime_package_root)
110
+ env[PROVIDER_LOCK_ENV_NAME] = "project-source"
111
+ os.execvpe(
112
+ project_python,
113
+ [project_python, "-m", "pz_core", *list(argv or sys.argv[1:])],
114
+ env,
115
+ )
116
+ return True
117
+
118
+
119
+ def _handoff_to_uv_tool(argv: Sequence[str]) -> bool:
120
+ uv_binary = shutil.which("uv")
121
+ if not uv_binary:
122
+ return False
123
+ env = dict(os.environ)
124
+ env[PROVIDER_LOCK_ENV_NAME] = "uv-tool"
125
+ os.execvpe(
126
+ uv_binary,
127
+ [uv_binary, "tool", "run", "--from", "pz-cli", "pz", *list(argv)],
128
+ env,
129
+ )
130
+ return True
131
+
132
+
133
+ def _resolve_venv_pz_command(venv_root: Path) -> Path | None:
134
+ candidates = (
135
+ venv_root / "bin" / "pz",
136
+ venv_root / "Scripts" / "pz.exe",
137
+ venv_root / "Scripts" / "pz",
138
+ )
139
+ for candidate in candidates:
140
+ if candidate.is_file():
141
+ return candidate.absolute()
142
+ return None
143
+
144
+
145
+ def _resolve_venv_python(venv_root: Path) -> Path | None:
146
+ candidates = (
147
+ venv_root / "bin" / "python",
148
+ venv_root / "Scripts" / "python.exe",
149
+ )
150
+ for candidate in candidates:
151
+ if candidate.is_file():
152
+ return candidate.absolute()
153
+ return None
154
+
155
+
156
+ def _site_packages_roots(venv_root: Path) -> tuple[Path, ...]:
157
+ roots: list[Path] = []
158
+ lib_root = venv_root / "lib"
159
+ if lib_root.is_dir():
160
+ for candidate in sorted(lib_root.glob("python*/site-packages")):
161
+ if candidate.is_dir():
162
+ roots.append(candidate.resolve())
163
+ windows_root = venv_root / "Lib" / "site-packages"
164
+ if windows_root.is_dir():
165
+ roots.append(windows_root.resolve())
166
+ return tuple(roots)
167
+
168
+
169
+ def _venv_contains_pz(venv_root: Path) -> bool:
170
+ for site_packages_root in _site_packages_roots(venv_root):
171
+ if (site_packages_root / "pz" / "__init__.py").is_file():
172
+ return True
173
+ if any(site_packages_root.glob("pz-*.dist-info")):
174
+ return True
175
+ if any(site_packages_root.glob("__editable__.pz*.pth")):
176
+ return True
177
+ if any(site_packages_root.glob("__editable___pz*.py")):
178
+ return True
179
+ return False
180
+
181
+
182
+ def _path_is_relative_to(path: Path, parent: Path) -> bool:
183
+ try:
184
+ path.expanduser().resolve().relative_to(parent.expanduser().resolve())
185
+ return True
186
+ except ValueError:
187
+ return False
188
+
189
+
190
+ def _resolve_source_python(runtime_package_root: Path) -> str:
191
+ for source_root in (runtime_package_root.parent.absolute(), runtime_package_root.absolute()):
192
+ source_venv = _target_virtualenv_path(source_root)
193
+ source_python = source_venv / "bin" / "python"
194
+ if source_python.is_file():
195
+ return str(source_python.absolute())
196
+ source_python_windows = source_venv / "Scripts" / "python.exe"
197
+ if source_python_windows.is_file():
198
+ return str(source_python_windows.absolute())
199
+ return sys.executable
200
+
201
+
202
+ def _same_python_executable(left: str, right: str) -> bool:
203
+ try:
204
+ return os.path.abspath(os.path.expanduser(left)) == os.path.abspath(os.path.expanduser(right))
205
+ except Exception:
206
+ return left == right
207
+
208
+
209
+ __all__ = ["handoff_to_preferred_pz_provider", "main"]
pz_core/root_cli.py ADDED
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Iterable, Sequence
4
+ from dataclasses import dataclass
5
+ from importlib.metadata import entry_points
6
+
7
+
8
+ ROOT_CLI_PROVIDER_GROUP = "pz.root_cli"
9
+ CommandPath = tuple[str, ...]
10
+ RootCommandCallable = Callable[[Sequence[str]], int]
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class RootCommandHandler:
15
+ command_path: CommandPath
16
+ handler: RootCommandCallable
17
+
18
+
19
+ def normalize_command_path(parts: Sequence[str]) -> CommandPath:
20
+ normalized = tuple(str(part).strip().lower() for part in parts if str(part).strip())
21
+ if not normalized:
22
+ raise ValueError("root CLI command path must not be empty")
23
+ return normalized
24
+
25
+
26
+ def coerce_root_command_handlers(payload: object) -> tuple[RootCommandHandler, ...]:
27
+ if isinstance(payload, RootCommandHandler):
28
+ handlers = (payload,)
29
+ elif isinstance(payload, Iterable) and not isinstance(payload, (str, bytes)):
30
+ handlers = tuple(payload)
31
+ else:
32
+ raise TypeError("root CLI provider must return RootCommandHandler or iterable[RootCommandHandler]")
33
+ if not handlers:
34
+ raise ValueError("root CLI provider returned no handlers")
35
+ normalized_handlers: list[RootCommandHandler] = []
36
+ for item in handlers:
37
+ if not isinstance(item, RootCommandHandler):
38
+ raise TypeError("root CLI provider returned a non-RootCommandHandler item")
39
+ normalized_handlers.append(
40
+ RootCommandHandler(command_path=normalize_command_path(item.command_path), handler=item.handler)
41
+ )
42
+ return tuple(normalized_handlers)
43
+
44
+
45
+ def discover_root_command_handlers() -> dict[CommandPath, RootCommandHandler]:
46
+ discovered: dict[CommandPath, RootCommandHandler] = {}
47
+ for registered in entry_points(group=ROOT_CLI_PROVIDER_GROUP):
48
+ provider = registered.load()
49
+ handlers = coerce_root_command_handlers(provider())
50
+ for handler in handlers:
51
+ existing = discovered.get(handler.command_path)
52
+ if existing is not None:
53
+ command_text = " ".join(handler.command_path)
54
+ raise RuntimeError(
55
+ f"Duplicate root CLI provider for `{command_text}`: "
56
+ f"`{registered.value}` conflicts with an already-registered provider."
57
+ )
58
+ discovered[handler.command_path] = handler
59
+ return discovered
60
+
61
+
62
+ __all__ = [
63
+ "CommandPath",
64
+ "ROOT_CLI_PROVIDER_GROUP",
65
+ "RootCommandCallable",
66
+ "RootCommandHandler",
67
+ "coerce_root_command_handlers",
68
+ "discover_root_command_handlers",
69
+ "normalize_command_path",
70
+ ]