prefect 3.7.4.dev4__py3-none-any.whl → 3.7.5.dev2__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 (184) hide show
  1. prefect/_build_info.py +3 -3
  2. prefect/cli/deploy/_core.py +7 -2
  3. prefect/client/schemas/schedules.py +28 -7
  4. prefect/deployments/base.py +27 -0
  5. prefect/flows.py +5 -1
  6. prefect/server/AGENTS.md +7 -0
  7. prefect/server/api/workers.py +59 -3
  8. prefect/server/events/models/automations.py +34 -1
  9. prefect/server/events/schemas/lifecycle.py +474 -0
  10. prefect/server/models/artifacts.py +51 -0
  11. prefect/server/models/block_documents.py +55 -3
  12. prefect/server/models/block_types.py +50 -6
  13. prefect/server/models/concurrency_limits.py +62 -8
  14. prefect/server/models/concurrency_limits_v2.py +49 -6
  15. prefect/server/models/events.py +159 -0
  16. prefect/server/models/flows.py +52 -9
  17. prefect/server/models/variables.py +54 -11
  18. prefect/server/models/work_queues.py +42 -4
  19. prefect/server/models/workers.py +55 -4
  20. prefect/server/ui-v2/assets/{artifact._id-D_sj2Bai.js → artifact._id-CGn6mN4P.js} +2 -2
  21. prefect/server/ui-v2/assets/{artifact._id-D_sj2Bai.js.map → artifact._id-CGn6mN4P.js.map} +1 -1
  22. prefect/server/ui-v2/assets/{artifacts-Ckq-7rKM.js → artifacts-Do1ciXaG.js} +2 -2
  23. prefect/server/ui-v2/assets/{artifacts-Ckq-7rKM.js.map → artifacts-Do1ciXaG.js.map} +1 -1
  24. prefect/server/ui-v2/assets/{automation-wizard-DK_oIkuy.js → automation-wizard-CEKw1mSo.js} +2 -2
  25. prefect/server/ui-v2/assets/{automation-wizard-DK_oIkuy.js.map → automation-wizard-CEKw1mSo.js.map} +1 -1
  26. prefect/server/ui-v2/assets/{automation._id-DHPfuZoy.js → automation._id-DWGZbeYH.js} +2 -2
  27. prefect/server/ui-v2/assets/{automation._id-DHPfuZoy.js.map → automation._id-DWGZbeYH.js.map} +1 -1
  28. prefect/server/ui-v2/assets/{automation_._id.edit-DhzmIbRy.js → automation_._id.edit-HRV19VwZ.js} +2 -2
  29. prefect/server/ui-v2/assets/{automation_._id.edit-DhzmIbRy.js.map → automation_._id.edit-HRV19VwZ.js.map} +1 -1
  30. prefect/server/ui-v2/assets/{automations-18EVeC4g.js → automations-9tGVzn7H.js} +2 -2
  31. prefect/server/ui-v2/assets/{automations-18EVeC4g.js.map → automations-9tGVzn7H.js.map} +1 -1
  32. prefect/server/ui-v2/assets/{automations-OjniTeHI.js → automations-BeIPMhoH.js} +2 -2
  33. prefect/server/ui-v2/assets/{automations-OjniTeHI.js.map → automations-BeIPMhoH.js.map} +1 -1
  34. prefect/server/ui-v2/assets/{automations-header-WqOJ9BHA.js → automations-header-CV6NSb4r.js} +2 -2
  35. prefect/server/ui-v2/assets/{automations-header-WqOJ9BHA.js.map → automations-header-CV6NSb4r.js.map} +1 -1
  36. prefect/server/ui-v2/assets/{automations-wizard-DLsg6Q9Q.js → automations-wizard-XwyXNcrj.js} +2 -2
  37. prefect/server/ui-v2/assets/{automations-wizard-DLsg6Q9Q.js.map → automations-wizard-XwyXNcrj.js.map} +1 -1
  38. prefect/server/ui-v2/assets/{base-job-template-form-section-B_vYchzo.js → base-job-template-form-section-DMs875TU.js} +2 -2
  39. prefect/server/ui-v2/assets/{base-job-template-form-section-B_vYchzo.js.map → base-job-template-form-section-DMs875TU.js.map} +1 -1
  40. prefect/server/ui-v2/assets/{block-type-details-CQJNLWUB.js → block-type-details-BcDSzzxg.js} +2 -2
  41. prefect/server/ui-v2/assets/{block-type-details-CQJNLWUB.js.map → block-type-details-BcDSzzxg.js.map} +1 -1
  42. prefect/server/ui-v2/assets/{block._id-DLIoJk29.js → block._id-71pTiwRc.js} +2 -2
  43. prefect/server/ui-v2/assets/{block._id-DLIoJk29.js.map → block._id-71pTiwRc.js.map} +1 -1
  44. prefect/server/ui-v2/assets/{block_._id.edit-DP7dd7Wq.js → block_._id.edit-CQCrJtwK.js} +2 -2
  45. prefect/server/ui-v2/assets/{block_._id.edit-DP7dd7Wq.js.map → block_._id.edit-CQCrJtwK.js.map} +1 -1
  46. prefect/server/ui-v2/assets/{blocks-DA5cj_oB.js → blocks-BO6kd_nW.js} +2 -2
  47. prefect/server/ui-v2/assets/{blocks-DA5cj_oB.js.map → blocks-BO6kd_nW.js.map} +1 -1
  48. prefect/server/ui-v2/assets/{catalog-0qes6pkx.js → catalog-CAWqoAr-.js} +2 -2
  49. prefect/server/ui-v2/assets/{catalog-0qes6pkx.js.map → catalog-CAWqoAr-.js.map} +1 -1
  50. prefect/server/ui-v2/assets/{catalog_._slug-DrGBCMOe.js → catalog_._slug-BFPfOSRG.js} +2 -2
  51. prefect/server/ui-v2/assets/{catalog_._slug-DrGBCMOe.js.map → catalog_._slug-BFPfOSRG.js.map} +1 -1
  52. prefect/server/ui-v2/assets/{catalog_._slug_.create-QphOkjVD.js → catalog_._slug_.create-Zmb9Qc_k.js} +2 -2
  53. prefect/server/ui-v2/assets/{catalog_._slug_.create-QphOkjVD.js.map → catalog_._slug_.create-Zmb9Qc_k.js.map} +1 -1
  54. prefect/server/ui-v2/assets/{concurrency-limit._id-CASCpu3g.js → concurrency-limit._id-BkcUCp2C.js} +2 -2
  55. prefect/server/ui-v2/assets/{concurrency-limit._id-CASCpu3g.js.map → concurrency-limit._id-BkcUCp2C.js.map} +1 -1
  56. prefect/server/ui-v2/assets/{concurrency-limits-D5CSgwVA.js → concurrency-limits-CenLAieY.js} +2 -2
  57. prefect/server/ui-v2/assets/{concurrency-limits-D5CSgwVA.js.map → concurrency-limits-CenLAieY.js.map} +1 -1
  58. prefect/server/ui-v2/assets/{create--i1MDKBO.js → create-B9a1kbgD.js} +2 -2
  59. prefect/server/ui-v2/assets/{create--i1MDKBO.js.map → create-B9a1kbgD.js.map} +1 -1
  60. prefect/server/ui-v2/assets/{create-D4Mgq9Tc.js → create-CW_W7MJG.js} +2 -2
  61. prefect/server/ui-v2/assets/{create-D4Mgq9Tc.js.map → create-CW_W7MJG.js.map} +1 -1
  62. prefect/server/ui-v2/assets/{data-table-D_g1sVhO.js → data-table-CwWzYblB.js} +2 -2
  63. prefect/server/ui-v2/assets/{data-table-D_g1sVhO.js.map → data-table-CwWzYblB.js.map} +1 -1
  64. prefect/server/ui-v2/assets/{data-table-CPTOcoHk.js → data-table-UtREbgzl.js} +2 -2
  65. prefect/server/ui-v2/assets/{data-table-CPTOcoHk.js.map → data-table-UtREbgzl.js.map} +1 -1
  66. prefect/server/ui-v2/assets/{deployment-form-B3bk-cgB.js → deployment-form-b8Uy0wZi.js} +2 -2
  67. prefect/server/ui-v2/assets/{deployment-form-B3bk-cgB.js.map → deployment-form-b8Uy0wZi.js.map} +1 -1
  68. prefect/server/ui-v2/assets/{deployment-links-BPfEnrnX.js → deployment-links-D_m7WPrI.js} +2 -2
  69. prefect/server/ui-v2/assets/{deployment-links-BPfEnrnX.js.map → deployment-links-D_m7WPrI.js.map} +1 -1
  70. prefect/server/ui-v2/assets/{deployment._id-1LR1kAs9.js → deployment._id-VeI6NfbO.js} +2 -2
  71. prefect/server/ui-v2/assets/{deployment._id-1LR1kAs9.js.map → deployment._id-VeI6NfbO.js.map} +1 -1
  72. prefect/server/ui-v2/assets/{deployment_._id.duplicate-BqeoxpSJ.js → deployment_._id.duplicate-BdBlS5Hk.js} +2 -2
  73. prefect/server/ui-v2/assets/{deployment_._id.duplicate-BqeoxpSJ.js.map → deployment_._id.duplicate-BdBlS5Hk.js.map} +1 -1
  74. prefect/server/ui-v2/assets/{deployment_._id.edit-CphaQ1Q-.js → deployment_._id.edit-C1G5Wlhz.js} +2 -2
  75. prefect/server/ui-v2/assets/{deployment_._id.edit-CphaQ1Q-.js.map → deployment_._id.edit-C1G5Wlhz.js.map} +1 -1
  76. prefect/server/ui-v2/assets/{deployment_._id.run-DJgb6uhH.js → deployment_._id.run-CMco3t02.js} +2 -2
  77. prefect/server/ui-v2/assets/{deployment_._id.run-DJgb6uhH.js.map → deployment_._id.run-CMco3t02.js.map} +1 -1
  78. prefect/server/ui-v2/assets/{deployments-KARnGMSo.js → deployments-Cw4bUdZz.js} +2 -2
  79. prefect/server/ui-v2/assets/{deployments-KARnGMSo.js.map → deployments-Cw4bUdZz.js.map} +1 -1
  80. prefect/server/ui-v2/assets/{deployments-xDRq6pwu.js → deployments-TsvJSdjL.js} +2 -2
  81. prefect/server/ui-v2/assets/{deployments-xDRq6pwu.js.map → deployments-TsvJSdjL.js.map} +1 -1
  82. prefect/server/ui-v2/assets/{event-resource-display-Cpa7n5Gi.js → event-resource-display-VOk6tXTt.js} +2 -2
  83. prefect/server/ui-v2/assets/{event-resource-display-Cpa7n5Gi.js.map → event-resource-display-VOk6tXTt.js.map} +1 -1
  84. prefect/server/ui-v2/assets/{event._eventDate._eventId-DEOpiwP4.js → event._eventDate._eventId-BZTy7e1G.js} +2 -2
  85. prefect/server/ui-v2/assets/{event._eventDate._eventId-DEOpiwP4.js.map → event._eventDate._eventId-BZTy7e1G.js.map} +1 -1
  86. prefect/server/ui-v2/assets/{events-B419Nv8p.js → events-C3fd4JK8.js} +2 -2
  87. prefect/server/ui-v2/assets/{events-B419Nv8p.js.map → events-C3fd4JK8.js.map} +1 -1
  88. prefect/server/ui-v2/assets/{events-timeline-BqHjvYQo.js → events-timeline-DMEKqiaJ.js} +2 -2
  89. prefect/server/ui-v2/assets/{events-timeline-BqHjvYQo.js.map → events-timeline-DMEKqiaJ.js.map} +1 -1
  90. prefect/server/ui-v2/assets/{flow-run-graph-B9WKwHgt.js → flow-run-graph-CzgTV1Fm.js} +2 -2
  91. prefect/server/ui-v2/assets/{flow-run-graph-B9WKwHgt.js.map → flow-run-graph-CzgTV1Fm.js.map} +1 -1
  92. prefect/server/ui-v2/assets/{flow-run._id-C-H9_3kK.js → flow-run._id-BO7lgrUy.js} +2 -2
  93. prefect/server/ui-v2/assets/{flow-run._id-C-H9_3kK.js.map → flow-run._id-BO7lgrUy.js.map} +1 -1
  94. prefect/server/ui-v2/assets/{flow-run._id-B-Nq67aN.js → flow-run._id-Bw2S7_Ra.js} +3 -3
  95. prefect/server/ui-v2/assets/{flow-run._id-B-Nq67aN.js.map → flow-run._id-Bw2S7_Ra.js.map} +1 -1
  96. prefect/server/ui-v2/assets/{flow._id-BcIuffAZ.js → flow._id-CYUIt-ri.js} +2 -2
  97. prefect/server/ui-v2/assets/{flow._id-BcIuffAZ.js.map → flow._id-CYUIt-ri.js.map} +1 -1
  98. prefect/server/ui-v2/assets/{flow._id-BQ9hMaOc.js → flow._id-D0AM7DlY.js} +2 -2
  99. prefect/server/ui-v2/assets/{flow._id-BQ9hMaOc.js.map → flow._id-D0AM7DlY.js.map} +1 -1
  100. prefect/server/ui-v2/assets/{flows-CdREF3X8.js → flows-DQ2n-Pk-.js} +2 -2
  101. prefect/server/ui-v2/assets/{flows-CdREF3X8.js.map → flows-DQ2n-Pk-.js.map} +1 -1
  102. prefect/server/ui-v2/assets/{header-DcHk9RJB.js → header-B4ptMDx7.js} +2 -2
  103. prefect/server/ui-v2/assets/{header-DcHk9RJB.js.map → header-B4ptMDx7.js.map} +1 -1
  104. prefect/server/ui-v2/assets/{header-DcteQO4w.js → header-DJLajBCT.js} +2 -2
  105. prefect/server/ui-v2/assets/{header-DcteQO4w.js.map → header-DJLajBCT.js.map} +1 -1
  106. prefect/server/ui-v2/assets/{header-rP3ESS84.js → header-D_rfPmc4.js} +2 -2
  107. prefect/server/ui-v2/assets/{header-rP3ESS84.js.map → header-D_rfPmc4.js.map} +1 -1
  108. prefect/server/ui-v2/assets/{index-Bx8t2aUw.js → index-1m3WFD_G.js} +4 -4
  109. prefect/server/ui-v2/assets/index-1m3WFD_G.js.map +1 -0
  110. prefect/server/ui-v2/assets/{json-input-DBfGBkOn.js → json-input-BvUPoDaQ.js} +2 -2
  111. prefect/server/ui-v2/assets/{json-input-DBfGBkOn.js.map → json-input-BvUPoDaQ.js.map} +1 -1
  112. prefect/server/ui-v2/assets/{key._key-BxFZCzla.js → key._key-tyBiTQht.js} +2 -2
  113. prefect/server/ui-v2/assets/{key._key-BxFZCzla.js.map → key._key-tyBiTQht.js.map} +1 -1
  114. prefect/server/ui-v2/assets/{lazy-markdown-Du0WEaoD.js → lazy-markdown-DFiPnlm6.js} +2 -2
  115. prefect/server/ui-v2/assets/{lazy-markdown-Du0WEaoD.js.map → lazy-markdown-DFiPnlm6.js.map} +1 -1
  116. prefect/server/ui-v2/assets/login-azcClxzD.js +2 -0
  117. prefect/server/ui-v2/assets/login-azcClxzD.js.map +1 -0
  118. prefect/server/ui-v2/assets/{runs-2BGJtY7A.js → runs-BXhNHojM.js} +2 -2
  119. prefect/server/ui-v2/assets/{runs-2BGJtY7A.js.map → runs-BXhNHojM.js.map} +1 -1
  120. prefect/server/ui-v2/assets/{runs-DtVmvXjn.js → runs-Bi8xBTnP.js} +2 -2
  121. prefect/server/ui-v2/assets/{runs-DtVmvXjn.js.map → runs-Bi8xBTnP.js.map} +1 -1
  122. prefect/server/ui-v2/assets/{schedule-badge-vp3vRuiQ.js → schedule-badge-DyA6_9bW.js} +2 -2
  123. prefect/server/ui-v2/assets/{schedule-badge-vp3vRuiQ.js.map → schedule-badge-DyA6_9bW.js.map} +1 -1
  124. prefect/server/ui-v2/assets/{schema-form-B63OZdZA.js → schema-form-BbUYdQ1l.js} +2 -2
  125. prefect/server/ui-v2/assets/{schema-form-B63OZdZA.js.map → schema-form-BbUYdQ1l.js.map} +1 -1
  126. prefect/server/ui-v2/assets/{settings-CAfOkKsQ.js → settings-D0cOV1Wh.js} +2 -2
  127. prefect/server/ui-v2/assets/{settings-CAfOkKsQ.js.map → settings-D0cOV1Wh.js.map} +1 -1
  128. prefect/server/ui-v2/assets/{task-run-concurrency-limits-reset-dialog-DoT8gKqg.js → task-run-concurrency-limits-reset-dialog-BGyNodD6.js} +2 -2
  129. prefect/server/ui-v2/assets/{task-run-concurrency-limits-reset-dialog-DoT8gKqg.js.map → task-run-concurrency-limits-reset-dialog-BGyNodD6.js.map} +1 -1
  130. prefect/server/ui-v2/assets/{task-run._id-ByHXEkPr.js → task-run._id-BzgIQuAB.js} +2 -2
  131. prefect/server/ui-v2/assets/{task-run._id-ByHXEkPr.js.map → task-run._id-BzgIQuAB.js.map} +1 -1
  132. prefect/server/ui-v2/assets/{timezone-select-BbC7_ofj.js → timezone-select-YUKrhekj.js} +2 -2
  133. prefect/server/ui-v2/assets/{timezone-select-BbC7_ofj.js.map → timezone-select-YUKrhekj.js.map} +1 -1
  134. prefect/server/ui-v2/assets/{toggle-group-3mnMEnd4.js → toggle-group-DtF-kiye.js} +2 -2
  135. prefect/server/ui-v2/assets/{toggle-group-3mnMEnd4.js.map → toggle-group-DtF-kiye.js.map} +1 -1
  136. prefect/server/ui-v2/assets/{trigger-details-CXPHSuD6.js → trigger-details-DKXLeut_.js} +2 -2
  137. prefect/server/ui-v2/assets/{trigger-details-CXPHSuD6.js.map → trigger-details-DKXLeut_.js.map} +1 -1
  138. prefect/server/ui-v2/assets/{use-delete-automation-confirmation-dialog-DoqZBI87.js → use-delete-automation-confirmation-dialog-rWsqU6dD.js} +2 -2
  139. prefect/server/ui-v2/assets/{use-delete-automation-confirmation-dialog-DoqZBI87.js.map → use-delete-automation-confirmation-dialog-rWsqU6dD.js.map} +1 -1
  140. prefect/server/ui-v2/assets/{use-delete-block-document-confirmation-dialog-OksMPjKJ.js → use-delete-block-document-confirmation-dialog-CxykKCvn.js} +2 -2
  141. prefect/server/ui-v2/assets/{use-delete-block-document-confirmation-dialog-OksMPjKJ.js.map → use-delete-block-document-confirmation-dialog-CxykKCvn.js.map} +1 -1
  142. prefect/server/ui-v2/assets/{use-delete-deployment-confirmation-dialog-QUJxKWuu.js → use-delete-deployment-confirmation-dialog-BVLOKC3S.js} +2 -2
  143. prefect/server/ui-v2/assets/{use-delete-deployment-confirmation-dialog-QUJxKWuu.js.map → use-delete-deployment-confirmation-dialog-BVLOKC3S.js.map} +1 -1
  144. prefect/server/ui-v2/assets/{use-get-artifacts-flow-task-runs-BrI5eJXx.js → use-get-artifacts-flow-task-runs-DYXKmjMZ.js} +2 -2
  145. prefect/server/ui-v2/assets/{use-get-artifacts-flow-task-runs-BrI5eJXx.js.map → use-get-artifacts-flow-task-runs-DYXKmjMZ.js.map} +1 -1
  146. prefect/server/ui-v2/assets/{use-resource-options-C8BxAJ3_.js → use-resource-options-CI-_-q8G.js} +2 -2
  147. prefect/server/ui-v2/assets/{use-resource-options-C8BxAJ3_.js.map → use-resource-options-CI-_-q8G.js.map} +1 -1
  148. prefect/server/ui-v2/assets/{variables-J400NeB8.js → variables-DzF6dWfi.js} +2 -2
  149. prefect/server/ui-v2/assets/{variables-J400NeB8.js.map → variables-DzF6dWfi.js.map} +1 -1
  150. prefect/server/ui-v2/assets/{variables-B_xPJctQ.js → variables-YgoHBKH3.js} +2 -2
  151. prefect/server/ui-v2/assets/{variables-B_xPJctQ.js.map → variables-YgoHBKH3.js.map} +1 -1
  152. prefect/server/ui-v2/assets/{work-pool-queue-form-zjSDZgJw.js → work-pool-queue-form-DwUMMWWg.js} +2 -2
  153. prefect/server/ui-v2/assets/{work-pool-queue-form-zjSDZgJw.js.map → work-pool-queue-form-DwUMMWWg.js.map} +1 -1
  154. prefect/server/ui-v2/assets/{work-pool-queue-toggle-D9yejO8R.js → work-pool-queue-toggle-DxntiOMF.js} +2 -2
  155. prefect/server/ui-v2/assets/{work-pool-queue-toggle-D9yejO8R.js.map → work-pool-queue-toggle-DxntiOMF.js.map} +1 -1
  156. prefect/server/ui-v2/assets/{work-pool._workPoolName-B7GY71Om.js → work-pool._workPoolName-DFsnzX7t.js} +2 -2
  157. prefect/server/ui-v2/assets/{work-pool._workPoolName-B7GY71Om.js.map → work-pool._workPoolName-DFsnzX7t.js.map} +1 -1
  158. prefect/server/ui-v2/assets/{work-pool_._workPoolName.edit-BI2_05Fv.js → work-pool_._workPoolName.edit-BXgOVNvH.js} +2 -2
  159. prefect/server/ui-v2/assets/{work-pool_._workPoolName.edit-BI2_05Fv.js.map → work-pool_._workPoolName.edit-BXgOVNvH.js.map} +1 -1
  160. prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-DLPPStC4.js → work-pool_._workPoolName.queue._workQueueName-9m0TaeoJ.js} +2 -2
  161. prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-DLPPStC4.js.map → work-pool_._workPoolName.queue._workQueueName-9m0TaeoJ.js.map} +1 -1
  162. prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue.create-BDfrNViS.js → work-pool_._workPoolName.queue.create-Bue87lwm.js} +2 -2
  163. prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue.create-BDfrNViS.js.map → work-pool_._workPoolName.queue.create-Bue87lwm.js.map} +1 -1
  164. prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue_._workQueueName.edit-IkPJodEj.js → work-pool_._workPoolName.queue_._workQueueName.edit-BRRcpUcG.js} +2 -2
  165. prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue_._workQueueName.edit-IkPJodEj.js.map → work-pool_._workPoolName.queue_._workQueueName.edit-BRRcpUcG.js.map} +1 -1
  166. prefect/server/ui-v2/assets/{work-pools-h0aBSwR4.js → work-pools-BTYKiBdv.js} +2 -2
  167. prefect/server/ui-v2/assets/{work-pools-h0aBSwR4.js.map → work-pools-BTYKiBdv.js.map} +1 -1
  168. prefect/server/ui-v2/assets/{work-pools-C8fZNspQ.js → work-pools-DmqxpFl6.js} +2 -2
  169. prefect/server/ui-v2/assets/{work-pools-C8fZNspQ.js.map → work-pools-DmqxpFl6.js.map} +1 -1
  170. prefect/server/ui-v2/assets/{work-queue-icon-text-DkodkzYl.js → work-queue-icon-text-BVl4_3HI.js} +2 -2
  171. prefect/server/ui-v2/assets/{work-queue-icon-text-DkodkzYl.js.map → work-queue-icon-text-BVl4_3HI.js.map} +1 -1
  172. prefect/server/ui-v2/assets/{work-queue-select-DfK_LqtB.js → work-queue-select-qFUs-bnS.js} +2 -2
  173. prefect/server/ui-v2/assets/{work-queue-select-DfK_LqtB.js.map → work-queue-select-qFUs-bnS.js.map} +1 -1
  174. prefect/server/ui-v2/index.html +1 -1
  175. prefect/server/utilities/worker_channel.py +369 -20
  176. prefect/server/utilities/worker_channel_cleanup.py +451 -0
  177. {prefect-3.7.4.dev4.dist-info → prefect-3.7.5.dev2.dist-info}/METADATA +1 -1
  178. {prefect-3.7.4.dev4.dist-info → prefect-3.7.5.dev2.dist-info}/RECORD +181 -179
  179. prefect/server/ui-v2/assets/index-Bx8t2aUw.js.map +0 -1
  180. prefect/server/ui-v2/assets/login-D2Aziy0o.js +0 -2
  181. prefect/server/ui-v2/assets/login-D2Aziy0o.js.map +0 -1
  182. {prefect-3.7.4.dev4.dist-info → prefect-3.7.5.dev2.dist-info}/WHEEL +0 -0
  183. {prefect-3.7.4.dev4.dist-info → prefect-3.7.5.dev2.dist-info}/entry_points.txt +0 -0
  184. {prefect-3.7.4.dev4.dist-info → prefect-3.7.5.dev2.dist-info}/licenses/LICENSE +0 -0
prefect/_build_info.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # Generated by versioningit
2
- __version__ = "3.7.4.dev4"
3
- __build_date__ = "2026-06-05 09:17:18.703805+00:00"
4
- __git_commit__ = "0bad21f4ce99db95716f64cc2eab836eb274b52c"
2
+ __version__ = "3.7.5.dev2"
3
+ __build_date__ = "2026-06-09 09:17:59.185976+00:00"
4
+ __git_commit__ = "d858cb6725864c9a993be6a6633009b03b5e6f5e"
5
5
  __dirty__ = False
@@ -19,7 +19,10 @@ from prefect.cli._prompts import (
19
19
  )
20
20
  from prefect.client.orchestration import get_client
21
21
  from prefect.client.schemas.filters import WorkerFilter
22
- from prefect.deployments.base import _save_deployment_to_prefect_file
22
+ from prefect.deployments.base import (
23
+ _deployment_already_saved_to_prefect_file,
24
+ _save_deployment_to_prefect_file,
25
+ )
23
26
  from prefect.deployments.runner import RunnerDeployment
24
27
  from prefect.deployments.steps.core import run_steps
25
28
  from prefect.exceptions import ObjectNotFound
@@ -420,7 +423,9 @@ async def _run_single_deploy(
420
423
  )
421
424
  console.print(message, soft_wrap=True)
422
425
 
423
- if is_interactive() and not prefect_file.exists():
426
+ if is_interactive() and not _deployment_already_saved_to_prefect_file(
427
+ deploy_config_before_templating, prefect_file
428
+ ):
424
429
  if confirm(
425
430
  (
426
431
  "Would you like to save configuration for this deployment for faster"
@@ -26,6 +26,26 @@ MAX_ITERATIONS = 1000
26
26
  MAX_RRULE_LENGTH = 6500
27
27
 
28
28
 
29
+ def _iana_timezone_name(tzinfo: datetime.tzinfo) -> str:
30
+ """Return a valid IANA timezone name for *tzinfo*, or `"UTC"`."""
31
+ for attr in ("key", "name"):
32
+ value: Optional[str] = getattr(tzinfo, attr, None)
33
+ if value and is_valid_timezone(value):
34
+ return value
35
+
36
+ filename: Optional[str] = getattr(tzinfo, "_filename", None)
37
+ if filename:
38
+ normalized = filename.replace("\\", "/")
39
+ marker = "/zoneinfo/"
40
+ idx = normalized.find(marker)
41
+ if idx != -1:
42
+ candidate = normalized[idx + len(marker) :]
43
+ if candidate and is_valid_timezone(candidate):
44
+ return candidate
45
+
46
+ return "UTC"
47
+
48
+
29
49
  def is_valid_timezone(v: str) -> bool:
30
50
  """
31
51
  Validate that the provided timezone is a valid IANA timezone.
@@ -204,26 +224,27 @@ class RRuleSchedule(PrefectBaseModel):
204
224
  if isinstance(rrule, dateutil.rrule.rrule):
205
225
  dtstart = _rrule_dt(rrule)
206
226
  if dtstart and dtstart.tzinfo is not None:
207
- timezone = dtstart.tzinfo.tzname(dtstart)
227
+ timezone = _iana_timezone_name(dtstart.tzinfo)
208
228
  else:
209
229
  timezone = "UTC"
210
230
  return RRuleSchedule(rrule=str(rrule), timezone=timezone)
211
231
  rrules = _rrule(rrule)
212
232
  dtstarts = [dts for rr in rrules if (dts := _rrule_dt(rr)) is not None]
213
233
  unique_dstarts = set(d.astimezone(ZoneInfo("UTC")) for d in dtstarts)
214
- unique_timezones = set(d.tzinfo for d in dtstarts if d.tzinfo is not None)
234
+ unique_tz_names = set(
235
+ _iana_timezone_name(d.tzinfo) for d in dtstarts if d.tzinfo is not None
236
+ )
215
237
 
216
- if len(unique_timezones) > 1:
238
+ if len(unique_tz_names) > 1:
217
239
  raise ValueError(
218
- f"rruleset has too many dtstart timezones: {unique_timezones}"
240
+ f"rruleset has too many dtstart timezones: {unique_tz_names}"
219
241
  )
220
242
 
221
243
  if len(unique_dstarts) > 1:
222
244
  raise ValueError(f"rruleset has too many dtstarts: {unique_dstarts}")
223
245
 
224
- if unique_dstarts and unique_timezones:
225
- [unique_tz] = unique_timezones
226
- timezone = unique_tz.tzname(dtstarts[0])
246
+ if unique_dstarts and unique_tz_names:
247
+ [timezone] = unique_tz_names
227
248
  else:
228
249
  timezone = "UTC"
229
250
 
@@ -278,6 +278,33 @@ def _interval_schedule_to_dict(schedule: IntervalSchedule) -> dict[str, Any]:
278
278
  return schedule_config
279
279
 
280
280
 
281
+ def _deployment_already_saved_to_prefect_file(
282
+ deployment: dict[str, Any],
283
+ prefect_file: Path = Path("prefect.yaml"),
284
+ ) -> bool:
285
+ """
286
+ Return True if `prefect.yaml` already contains a deployment entry matching the
287
+ given deployment's name and entrypoint.
288
+
289
+ Used to decide whether the interactive `prefect deploy` flow should offer to
290
+ persist a newly-created deployment: a deployment that is already declared in
291
+ the file does not need to be saved again, but a brand-new one (even when the
292
+ file otherwise exists) should still be offered up for saving.
293
+ """
294
+ if not prefect_file.exists():
295
+ return False
296
+
297
+ with prefect_file.open(mode="r") as f:
298
+ contents = yaml.safe_load(f) or {}
299
+
300
+ for existing in contents.get("deployments") or []:
301
+ if existing.get("name") == deployment.get("name") and existing.get(
302
+ "entrypoint"
303
+ ) == deployment.get("entrypoint"):
304
+ return True
305
+ return False
306
+
307
+
281
308
  def _save_deployment_to_prefect_file(
282
309
  deployment: dict[str, Any],
283
310
  build_steps: list[dict[str, Any]] | None = None,
prefect/flows.py CHANGED
@@ -706,7 +706,11 @@ class Flow(Generic[P, R]):
706
706
  from fastapi.encoders import jsonable_encoder
707
707
 
708
708
  serialized_parameters[key] = jsonable_encoder(value)
709
- except (TypeError, ValueError):
709
+ except (TypeError, ValueError, RecursionError):
710
+ # `jsonable_encoder` recurses into unknown objects with no cycle
711
+ # or depth limit, so a deeply-nested or self-referential value
712
+ # raises `RecursionError`. Treat it like any other unserializable
713
+ # value and fall back to the placeholder below.
710
714
  logger.debug(
711
715
  f"Parameter {key!r} for flow {self.name!r} is unserializable. "
712
716
  f"Type {type(value).__name__!r} and will not be stored "
prefect/server/AGENTS.md CHANGED
@@ -24,6 +24,13 @@ The `variables` endpoints are a good canonical example of this pattern for simpl
24
24
 
25
25
  **Singleton server settings** (not tied to a specific run or resource) skip a dedicated table — store them as JSON in the `Configuration` key-value store via `models/configuration.py`. Define a string key constant in the wrapping module (see `models/storage_defaults.py` for the pattern).
26
26
 
27
+ ## Object Lifecycle Events
28
+
29
+ Domain objects emit `prefect.<object>.{created,updated,deleted}` events from their `models/` CRUD functions (`models/variables.py` is the simplest example). Two non-obvious rules:
30
+
31
+ - **Builders live in `events/schemas/lifecycle.py`, not `models/events.py`.** `models/events.py` imports `models.deployments`, so a model imported early in `models/__init__` (e.g. `block_types`, `block_documents`) that imports a builder from there triggers a circular import. The builders in `lifecycle.py` are pure functions of an ORM object + timestamp with no model-layer imports, so any model can use them safely.
32
+ - **Emission is inline and pre-commit.** `emit_*` helpers reference `clients.PrefectServerEventsClient` as a module attribute (so the autouse test fixture's single patch on `events.clients.PrefectServerEventsClient` captures them) and publish within the request transaction — there is no request-scoped buffer, so a rolled-back create still emits.
33
+
27
34
  ## Database Migrations
28
35
 
29
36
  Migrations use Alembic via wrapper functions in `database/alembic_commands.py`:
@@ -25,8 +25,10 @@ import prefect.server.models as models
25
25
  import prefect.server.schemas as schemas
26
26
  from prefect._internal.uuid7 import uuid7
27
27
  from prefect.client.schemas.worker_channel import (
28
+ CLEANUP_DELIVERY_CAPABILITY,
28
29
  WORK_POOL_SNAPSHOT_CAPABILITY,
29
30
  WORKER_HEARTBEAT_CAPABILITY,
31
+ WorkerChannelCapability,
30
32
  WorkerChannelCloseReason,
31
33
  WorkerChannelProtocolError,
32
34
  WorkerHelloFrame,
@@ -48,6 +50,10 @@ from prefect.server.schemas.statuses import WorkQueueStatus
48
50
  from prefect.server.utilities import subscriptions
49
51
  from prefect.server.utilities import worker_channel as worker_channel_utils
50
52
  from prefect.server.utilities.server import PrefectRouter
53
+ from prefect.server.worker_communication.cleanup_queue import (
54
+ WorkerCleanupQueue,
55
+ get_worker_cleanup_queue,
56
+ )
51
57
  from prefect.types import DateTime
52
58
  from prefect.types._datetime import now
53
59
 
@@ -61,7 +67,7 @@ router: PrefectRouter = PrefectRouter(
61
67
  )
62
68
  logger: Logger = get_logger("prefect.server.api.workers")
63
69
 
64
- _OSS_WORKER_CHANNEL_ACCEPTED_CAPABILITIES = [
70
+ _OSS_WORKER_CHANNEL_REQUIRED_CAPABILITIES: list[WorkerChannelCapability] = [
65
71
  WORKER_HEARTBEAT_CAPABILITY,
66
72
  WORK_POOL_SNAPSHOT_CAPABILITY,
67
73
  ]
@@ -183,6 +189,26 @@ class WorkerChannelWorkPoolUpdateEvent:
183
189
  changed_fields: dict[str, dict[str, Any]]
184
190
 
185
191
 
192
+ def _worker_requested_cleanup_delivery(hello: WorkerHelloFrame) -> bool:
193
+ return (
194
+ CLEANUP_DELIVERY_CAPABILITY in hello.payload.requested_capabilities
195
+ and bool(hello.payload.handled_cleanup_kinds)
196
+ and hello.payload.max_cleanup_concurrency > 0
197
+ )
198
+
199
+
200
+ def _accepted_worker_channel_capabilities(
201
+ hello: WorkerHelloFrame,
202
+ *,
203
+ cleanup_queue_available: bool,
204
+ ) -> list[WorkerChannelCapability]:
205
+ accepted = list(_OSS_WORKER_CHANNEL_REQUIRED_CAPABILITIES)
206
+ if cleanup_queue_available and _worker_requested_cleanup_delivery(hello):
207
+ accepted.append(CLEANUP_DELIVERY_CAPABILITY)
208
+
209
+ return accepted
210
+
211
+
186
212
  async def _receive_worker_hello(websocket: WebSocket) -> WorkerHelloFrame:
187
213
  try:
188
214
  message = await websocket.receive_json()
@@ -290,6 +316,7 @@ async def _build_worker_ready_frame(
290
316
  session: AsyncSession,
291
317
  work_pool_name: str,
292
318
  hello: WorkerHelloFrame,
319
+ cleanup_queue_available: bool,
293
320
  ) -> tuple[WorkerReadyFrame, WorkerChannelWorkPoolUpdateEvent | None]:
294
321
  try:
295
322
  selected_channel_version = select_worker_channel_version(
@@ -371,7 +398,10 @@ async def _build_worker_ready_frame(
371
398
  )
372
399
 
373
400
  requested_capabilities = list(dict.fromkeys(hello.payload.requested_capabilities))
374
- accepted = _OSS_WORKER_CHANNEL_ACCEPTED_CAPABILITIES
401
+ accepted = _accepted_worker_channel_capabilities(
402
+ hello,
403
+ cleanup_queue_available=cleanup_queue_available,
404
+ )
375
405
  accepted_set = set(accepted)
376
406
  rejected = [
377
407
  capability
@@ -393,7 +423,11 @@ async def _build_worker_ready_frame(
393
423
  ),
394
424
  "accepted_capabilities": accepted,
395
425
  "rejected_capabilities": rejected,
396
- "effective_max_cleanup_concurrency": 0,
426
+ "effective_max_cleanup_concurrency": (
427
+ hello.payload.max_cleanup_concurrency
428
+ if CLEANUP_DELIVERY_CAPABILITY in accepted_set
429
+ else 0
430
+ ),
397
431
  "resolved_work_queues": [
398
432
  {"id": work_queue.id, "name": work_queue.name}
399
433
  for work_queue in work_queues
@@ -1058,6 +1092,16 @@ async def worker_channel_connect(
1058
1092
 
1059
1093
  try:
1060
1094
  hello = await _receive_worker_hello(websocket)
1095
+ cleanup_queue: WorkerCleanupQueue | None = None
1096
+ if _worker_requested_cleanup_delivery(hello):
1097
+ try:
1098
+ cleanup_queue = get_worker_cleanup_queue()
1099
+ except Exception:
1100
+ logger.exception(
1101
+ "Worker cleanup delivery queue initialization failed; "
1102
+ "rejecting cleanup delivery capability"
1103
+ )
1104
+
1061
1105
  async with worker_channel_utils.messaging.ephemeral_subscription(
1062
1106
  worker_channel_utils.WORKER_CHANNEL_SNAPSHOT_TOPIC,
1063
1107
  ) as consumer_kwargs:
@@ -1066,6 +1110,7 @@ async def worker_channel_connect(
1066
1110
  session=session,
1067
1111
  work_pool_name=work_pool_name,
1068
1112
  hello=hello,
1113
+ cleanup_queue_available=cleanup_queue is not None,
1069
1114
  )
1070
1115
 
1071
1116
  if work_pool_update_event is not None:
@@ -1094,6 +1139,17 @@ async def worker_channel_connect(
1094
1139
  work_pool_id=ready.payload.initial_snapshot.work_pool.id,
1095
1140
  consumer_id=hello.payload.consumer_id,
1096
1141
  worker_name=hello.payload.worker_name,
1142
+ cleanup_queue=(
1143
+ cleanup_queue
1144
+ if CLEANUP_DELIVERY_CAPABILITY
1145
+ in ready.payload.accepted_capabilities
1146
+ else None
1147
+ ),
1148
+ cleanup_kinds=tuple(hello.payload.handled_cleanup_kinds),
1149
+ cleanup_work_queue_ids=tuple(
1150
+ work_queue.id for work_queue in ready.payload.resolved_work_queues
1151
+ ),
1152
+ max_cleanup_concurrency=ready.payload.effective_max_cleanup_concurrency,
1097
1153
  )
1098
1154
  await connection.run(ready, consumer_kwargs)
1099
1155
 
@@ -10,7 +10,8 @@ from typing_extensions import Literal, TypeAlias
10
10
 
11
11
  from prefect.logging import get_logger
12
12
  from prefect.server.database import PrefectDBInterface, db_injector
13
- from prefect.server.events import filters
13
+ from prefect.server.events import clients, filters
14
+ from prefect.server.events.schemas import lifecycle
14
15
  from prefect.server.events.schemas.automations import (
15
16
  Automation,
16
17
  AutomationPartialUpdate,
@@ -23,6 +24,31 @@ from prefect.utilities.asyncutils import run_coro_as_sync
23
24
 
24
25
  logger: logging.Logger = get_logger(__name__)
25
26
 
27
+
28
+ async def emit_automation_created_event(automation: Automation) -> None:
29
+ """Emit an event when an automation is created."""
30
+ async with clients.PrefectServerEventsClient() as events_client:
31
+ await events_client.emit(
32
+ lifecycle.automation_created_event(automation, now("UTC"))
33
+ )
34
+
35
+
36
+ async def emit_automation_updated_event(automation: Automation) -> None:
37
+ """Emit an event when an automation is updated."""
38
+ async with clients.PrefectServerEventsClient() as events_client:
39
+ await events_client.emit(
40
+ lifecycle.automation_updated_event(automation, now("UTC"))
41
+ )
42
+
43
+
44
+ async def emit_automation_deleted_event(automation: Automation) -> None:
45
+ """Emit an event when an automation is deleted."""
46
+ async with clients.PrefectServerEventsClient() as events_client:
47
+ await events_client.emit(
48
+ lifecycle.automation_deleted_event(automation, now("UTC"))
49
+ )
50
+
51
+
26
52
  AutomationChangeEvent: TypeAlias = Literal[
27
53
  "automation__created", "automation__updated", "automation__deleted"
28
54
  ]
@@ -176,6 +202,7 @@ async def create_automation(
176
202
  await _sync_automation_related_resources(session, new_automation.id, automation)
177
203
 
178
204
  await _notify(session, automation, "created")
205
+ await emit_automation_created_event(automation)
179
206
  return automation
180
207
 
181
208
 
@@ -220,6 +247,11 @@ async def update_automation(
220
247
  )
221
248
 
222
249
  await _notify(session, automation, "updated")
250
+
251
+ updated_automation = await read_automation(session, automation_id)
252
+ if updated_automation is not None:
253
+ await emit_automation_updated_event(updated_automation)
254
+
223
255
  return result.rowcount > 0 # type: ignore
224
256
 
225
257
 
@@ -260,6 +292,7 @@ async def delete_automation(
260
292
  await _sync_automation_related_resources(session, automation_id, None)
261
293
 
262
294
  await _notify(session, automation, "deleted")
295
+ await emit_automation_deleted_event(automation)
263
296
  return True
264
297
 
265
298