prefect 3.6.14.dev1__py3-none-any.whl → 3.6.14.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 (228) hide show
  1. prefect/_build_info.py +3 -3
  2. prefect/_observers.py +20 -8
  3. prefect/cli/server.py +18 -3
  4. prefect/deployments/runner.py +448 -8
  5. prefect/flows.py +162 -34
  6. prefect/runner/runner.py +48 -0
  7. prefect/server/orchestration/global_policy.py +4 -2
  8. prefect/server/ui-v2/assets/{artifact-card-DkgEVahF.js → artifact-card-D41jcvLX.js} +2 -2
  9. prefect/server/ui-v2/assets/{artifact-card-DkgEVahF.js.map → artifact-card-D41jcvLX.js.map} +1 -1
  10. prefect/server/ui-v2/assets/{artifact._id-C7yJhkKY.js → artifact._id-CYMvBFBb.js} +2 -2
  11. prefect/server/ui-v2/assets/{artifact._id-C7yJhkKY.js.map → artifact._id-CYMvBFBb.js.map} +1 -1
  12. prefect/server/ui-v2/assets/{automation-wizard-BLI4sqs9.js → automation-wizard-DHsWJ8JS.js} +2 -2
  13. prefect/server/ui-v2/assets/{automation-wizard-BLI4sqs9.js.map → automation-wizard-DHsWJ8JS.js.map} +1 -1
  14. prefect/server/ui-v2/assets/{automation._id-OLes3du9.js → automation._id-CCroE1-1.js} +2 -2
  15. prefect/server/ui-v2/assets/{automation._id-OLes3du9.js.map → automation._id-CCroE1-1.js.map} +1 -1
  16. prefect/server/ui-v2/assets/{automation_._id.edit-QrSzkDDB.js → automation_._id.edit-BgpDmMq7.js} +2 -2
  17. prefect/server/ui-v2/assets/{automation_._id.edit-QrSzkDDB.js.map → automation_._id.edit-BgpDmMq7.js.map} +1 -1
  18. prefect/server/ui-v2/assets/{automations-header-BoJc9HK8.js → automations-header-BEZXZmli.js} +2 -2
  19. prefect/server/ui-v2/assets/{automations-header-BoJc9HK8.js.map → automations-header-BEZXZmli.js.map} +1 -1
  20. prefect/server/ui-v2/assets/{base-job-template-form-section-Br6s0jNS.js → base-job-template-form-section-BubT4OHj.js} +2 -2
  21. prefect/server/ui-v2/assets/{base-job-template-form-section-Br6s0jNS.js.map → base-job-template-form-section-BubT4OHj.js.map} +1 -1
  22. prefect/server/ui-v2/assets/{block-type-details-BSbBP-Nm.js → block-type-details-DCWuBOwb.js} +2 -2
  23. prefect/server/ui-v2/assets/{block-type-details-BSbBP-Nm.js.map → block-type-details-DCWuBOwb.js.map} +1 -1
  24. prefect/server/ui-v2/assets/{block-type-logo-DNFERTyH.js → block-type-logo-SZMe2NdY.js} +2 -2
  25. prefect/server/ui-v2/assets/{block-type-logo-DNFERTyH.js.map → block-type-logo-SZMe2NdY.js.map} +1 -1
  26. prefect/server/ui-v2/assets/{block._id-DO6Hulrv.js → block._id-B6_kwTZ-.js} +2 -2
  27. prefect/server/ui-v2/assets/{block._id-DO6Hulrv.js.map → block._id-B6_kwTZ-.js.map} +1 -1
  28. prefect/server/ui-v2/assets/{block_._id.edit-BWF0TQvv.js → block_._id.edit-Bjg9WgY6.js} +2 -2
  29. prefect/server/ui-v2/assets/{block_._id.edit-BWF0TQvv.js.map → block_._id.edit-Bjg9WgY6.js.map} +1 -1
  30. prefect/server/ui-v2/assets/{catalog-BlaqZP0r.js → catalog-Bv8x-rIx.js} +2 -2
  31. prefect/server/ui-v2/assets/{catalog-BlaqZP0r.js.map → catalog-Bv8x-rIx.js.map} +1 -1
  32. prefect/server/ui-v2/assets/{catalog_._slug-BZWiMtjq.js → catalog_._slug-2GldfHc0.js} +2 -2
  33. prefect/server/ui-v2/assets/{catalog_._slug-BZWiMtjq.js.map → catalog_._slug-2GldfHc0.js.map} +1 -1
  34. prefect/server/ui-v2/assets/{catalog_._slug_.create-oHffbeMs.js → catalog_._slug_.create-Bjt7az95.js} +2 -2
  35. prefect/server/ui-v2/assets/{catalog_._slug_.create-oHffbeMs.js.map → catalog_._slug_.create-Bjt7az95.js.map} +1 -1
  36. prefect/server/ui-v2/assets/{collapsible-D45P93xU.js → collapsible-BT0EnMmT.js} +2 -2
  37. prefect/server/ui-v2/assets/{collapsible-D45P93xU.js.map → collapsible-BT0EnMmT.js.map} +1 -1
  38. prefect/server/ui-v2/assets/{concurrency-limit._id-B6oyPVya.js → concurrency-limit._id-Ba5Uof4l.js} +2 -2
  39. prefect/server/ui-v2/assets/{concurrency-limit._id-B6oyPVya.js.map → concurrency-limit._id-Ba5Uof4l.js.map} +1 -1
  40. prefect/server/ui-v2/assets/{create-CcYMd6w6.js → create-DK8Cy1hG.js} +2 -2
  41. prefect/server/ui-v2/assets/{create-CcYMd6w6.js.map → create-DK8Cy1hG.js.map} +1 -1
  42. prefect/server/ui-v2/assets/{create-DK1WvDhf.js → create-Ds8Lheyk.js} +2 -2
  43. prefect/server/ui-v2/assets/{create-DK1WvDhf.js.map → create-Ds8Lheyk.js.map} +1 -1
  44. prefect/server/ui-v2/assets/cronstrue-C72j5NDa.js +2 -0
  45. prefect/server/ui-v2/assets/cronstrue-C72j5NDa.js.map +1 -0
  46. prefect/server/ui-v2/assets/{data-table-B_iyrrTm.js → data-table-B6bdz5Zj.js} +2 -2
  47. prefect/server/ui-v2/assets/{data-table-B_iyrrTm.js.map → data-table-B6bdz5Zj.js.map} +1 -1
  48. prefect/server/ui-v2/assets/{delete-confirmation-dialog-7GKqeKy1.js → delete-confirmation-dialog-CV8ITEEW.js} +2 -2
  49. prefect/server/ui-v2/assets/{delete-confirmation-dialog-7GKqeKy1.js.map → delete-confirmation-dialog-CV8ITEEW.js.map} +1 -1
  50. prefect/server/ui-v2/assets/{deployment-action-header-DrvnfE9Y.js → deployment-action-header-DpjOcab0.js} +2 -2
  51. prefect/server/ui-v2/assets/{deployment-action-header-DrvnfE9Y.js.map → deployment-action-header-DpjOcab0.js.map} +1 -1
  52. prefect/server/ui-v2/assets/{deployment-form-DsAij22s.js → deployment-form-CjIPPnwV.js} +3 -3
  53. prefect/server/ui-v2/assets/{deployment-form-DsAij22s.js.map → deployment-form-CjIPPnwV.js.map} +1 -1
  54. prefect/server/ui-v2/assets/{deployment-links-BwqfKWQX.js → deployment-links--VkBo9wV.js} +2 -2
  55. prefect/server/ui-v2/assets/{deployment-links-BwqfKWQX.js.map → deployment-links--VkBo9wV.js.map} +1 -1
  56. prefect/server/ui-v2/assets/{deployment._id-E5e5s6bO.js → deployment._id-CW00MZGG.js} +2 -2
  57. prefect/server/ui-v2/assets/{deployment._id-E5e5s6bO.js.map → deployment._id-CW00MZGG.js.map} +1 -1
  58. prefect/server/ui-v2/assets/{deployment._id-U6KrtWw0.js → deployment._id-DjYh9IiR.js} +2 -2
  59. prefect/server/ui-v2/assets/{deployment._id-U6KrtWw0.js.map → deployment._id-DjYh9IiR.js.map} +1 -1
  60. prefect/server/ui-v2/assets/deployment_._id.duplicate-18ZQHHgI.js +2 -0
  61. prefect/server/ui-v2/assets/{deployment_._id.duplicate-C_kNyCA9.js.map → deployment_._id.duplicate-18ZQHHgI.js.map} +1 -1
  62. prefect/server/ui-v2/assets/deployment_._id.edit-C9H0Teuv.js +2 -0
  63. prefect/server/ui-v2/assets/{deployment_._id.edit-BP8pfTOR.js.map → deployment_._id.edit-C9H0Teuv.js.map} +1 -1
  64. prefect/server/ui-v2/assets/{deployment_._id.run-Di3-4Mtk.js → deployment_._id.run-CwHYw8j1.js} +2 -2
  65. prefect/server/ui-v2/assets/{deployment_._id.run-Di3-4Mtk.js.map → deployment_._id.run-CwHYw8j1.js.map} +1 -1
  66. prefect/server/ui-v2/assets/{dropdown-menu-iS8iJMpx.js → dropdown-menu-BYgkpoqb.js} +2 -2
  67. prefect/server/ui-v2/assets/{dropdown-menu-iS8iJMpx.js.map → dropdown-menu-BYgkpoqb.js.map} +1 -1
  68. prefect/server/ui-v2/assets/{event._eventDate._eventId-CG15qDSZ.js → event._eventDate._eventId-mPwFHce2.js} +2 -2
  69. prefect/server/ui-v2/assets/{event._eventDate._eventId-CG15qDSZ.js.map → event._eventDate._eventId-mPwFHce2.js.map} +1 -1
  70. prefect/server/ui-v2/assets/flow-run-graph-BMp5vJJa.js +2 -0
  71. prefect/server/ui-v2/assets/flow-run-graph-BMp5vJJa.js.map +1 -0
  72. prefect/server/ui-v2/assets/{flow-run._id-D_eAHZ5z.js → flow-run._id-CafHaMND.js} +2 -2
  73. prefect/server/ui-v2/assets/{flow-run._id-D_eAHZ5z.js.map → flow-run._id-CafHaMND.js.map} +1 -1
  74. prefect/server/ui-v2/assets/flow-run._id-CelqqA9P.js +4 -0
  75. prefect/server/ui-v2/assets/flow-run._id-CelqqA9P.js.map +1 -0
  76. prefect/server/ui-v2/assets/{flow-run._id-wco0Q19f.js → flow-run._id-hVuHWXbi.js} +2 -2
  77. prefect/server/ui-v2/assets/{flow-run._id-wco0Q19f.js.map → flow-run._id-hVuHWXbi.js.map} +1 -1
  78. prefect/server/ui-v2/assets/{flow-runs-pagination-DzGHOht3.js → flow-runs-pagination-CqjL9gOj.js} +2 -2
  79. prefect/server/ui-v2/assets/{flow-runs-pagination-DzGHOht3.js.map → flow-runs-pagination-CqjL9gOj.js.map} +1 -1
  80. prefect/server/ui-v2/assets/{flow._id-Dn2so3rK.js → flow._id-C1Djus0m.js} +2 -2
  81. prefect/server/ui-v2/assets/{flow._id-Dn2so3rK.js.map → flow._id-C1Djus0m.js.map} +1 -1
  82. prefect/server/ui-v2/assets/{form-rC8pxeRl.js → form-z_RaFdEo.js} +2 -2
  83. prefect/server/ui-v2/assets/{form-rC8pxeRl.js.map → form-z_RaFdEo.js.map} +1 -1
  84. prefect/server/ui-v2/assets/{header-CuAwmJ64.js → header-B-PvVnN7.js} +2 -2
  85. prefect/server/ui-v2/assets/{header-CuAwmJ64.js.map → header-B-PvVnN7.js.map} +1 -1
  86. prefect/server/ui-v2/assets/{header-C0zV3WDn.js → header-BSXYXvPS.js} +2 -2
  87. prefect/server/ui-v2/assets/{header-C0zV3WDn.js.map → header-BSXYXvPS.js.map} +1 -1
  88. prefect/server/ui-v2/assets/{header-C6ko3O7G.js → header-CKVO4oLE.js} +2 -2
  89. prefect/server/ui-v2/assets/{header-C6ko3O7G.js.map → header-CKVO4oLE.js.map} +1 -1
  90. prefect/server/ui-v2/assets/{index-CotXErTe.js → index-B5zctA6A.js} +2 -2
  91. prefect/server/ui-v2/assets/{index-CotXErTe.js.map → index-B5zctA6A.js.map} +1 -1
  92. prefect/server/ui-v2/assets/{index-k2161reF.js → index-BCzGofA2.js} +2 -2
  93. prefect/server/ui-v2/assets/{index-k2161reF.js.map → index-BCzGofA2.js.map} +1 -1
  94. prefect/server/ui-v2/assets/{index-BdgomKuA.js → index-BOuQzuoR.js} +2 -2
  95. prefect/server/ui-v2/assets/{index-BdgomKuA.js.map → index-BOuQzuoR.js.map} +1 -1
  96. prefect/server/ui-v2/assets/{index-pE_31vXF.js → index-BQOxk1xu.js} +2 -2
  97. prefect/server/ui-v2/assets/{index-pE_31vXF.js.map → index-BQOxk1xu.js.map} +1 -1
  98. prefect/server/ui-v2/assets/{index-Benf9xn3.js → index-BU1Y-wgo.js} +2 -2
  99. prefect/server/ui-v2/assets/{index-Benf9xn3.js.map → index-BU1Y-wgo.js.map} +1 -1
  100. prefect/server/ui-v2/assets/{index-CYpFLd7y.js → index-BxGluvOD.js} +2 -2
  101. prefect/server/ui-v2/assets/{index-CYpFLd7y.js.map → index-BxGluvOD.js.map} +1 -1
  102. prefect/server/ui-v2/assets/{index-CcVSC2pV.js → index-CAbBPipD.js} +2 -2
  103. prefect/server/ui-v2/assets/{index-CcVSC2pV.js.map → index-CAbBPipD.js.map} +1 -1
  104. prefect/server/ui-v2/assets/index-CHwhYh-B.js +2 -0
  105. prefect/server/ui-v2/assets/{index-BYQ1P2TC.js.map → index-CHwhYh-B.js.map} +1 -1
  106. prefect/server/ui-v2/assets/{index-0g8aAM30.js → index-CIKCrLeZ.js} +2 -2
  107. prefect/server/ui-v2/assets/{index-0g8aAM30.js.map → index-CIKCrLeZ.js.map} +1 -1
  108. prefect/server/ui-v2/assets/{index-fhrsIceD.js → index-CLUjhc4A.js} +2 -2
  109. prefect/server/ui-v2/assets/{index-fhrsIceD.js.map → index-CLUjhc4A.js.map} +1 -1
  110. prefect/server/ui-v2/assets/index-CX5uU1zd.js +2 -0
  111. prefect/server/ui-v2/assets/index-CX5uU1zd.js.map +1 -0
  112. prefect/server/ui-v2/assets/{index-DwkedbVy.js → index-CXVePXfG.js} +2 -2
  113. prefect/server/ui-v2/assets/{index-DwkedbVy.js.map → index-CXVePXfG.js.map} +1 -1
  114. prefect/server/ui-v2/assets/{index-DB2qeheX.js → index-D-ZIgaBw.js} +2 -2
  115. prefect/server/ui-v2/assets/{index-DB2qeheX.js.map → index-D-ZIgaBw.js.map} +1 -1
  116. prefect/server/ui-v2/assets/{index-D8fZaZa7.js → index-D10Svo9k.js} +2 -2
  117. prefect/server/ui-v2/assets/{index-D8fZaZa7.js.map → index-D10Svo9k.js.map} +1 -1
  118. prefect/server/ui-v2/assets/{index-CEStyUb-.js → index-D2EEipYt.js} +2 -2
  119. prefect/server/ui-v2/assets/{index-CEStyUb-.js.map → index-D2EEipYt.js.map} +1 -1
  120. prefect/server/ui-v2/assets/{index-BIqzfifB.js → index-DV5fpxXB.js} +2 -2
  121. prefect/server/ui-v2/assets/{index-BIqzfifB.js.map → index-DV5fpxXB.js.map} +1 -1
  122. prefect/server/ui-v2/assets/{index-C3D0Re7U.js → index-FHRYGk_R.js} +2 -2
  123. prefect/server/ui-v2/assets/{index-C3D0Re7U.js.map → index-FHRYGk_R.js.map} +1 -1
  124. prefect/server/ui-v2/assets/{index-CCs20W5W.js → index-IGxqySze.js} +2 -2
  125. prefect/server/ui-v2/assets/{index-CCs20W5W.js.map → index-IGxqySze.js.map} +1 -1
  126. prefect/server/ui-v2/assets/{index-BKbBu9gL.js → index-P2xc38Yh.js} +2 -2
  127. prefect/server/ui-v2/assets/{index-BKbBu9gL.js.map → index-P2xc38Yh.js.map} +1 -1
  128. prefect/server/ui-v2/assets/index-Yzhlw6-Q.js +17 -0
  129. prefect/server/ui-v2/assets/index-Yzhlw6-Q.js.map +1 -0
  130. prefect/server/ui-v2/assets/{index-BjsWjEUO.js → index-n7YFiQkB.js} +2 -2
  131. prefect/server/ui-v2/assets/{index-BjsWjEUO.js.map → index-n7YFiQkB.js.map} +1 -1
  132. prefect/server/ui-v2/assets/{json-input-C9dkBSdc.js → json-input-Dlzy_XLK.js} +2 -2
  133. prefect/server/ui-v2/assets/{json-input-C9dkBSdc.js.map → json-input-Dlzy_XLK.js.map} +1 -1
  134. prefect/server/ui-v2/assets/{key._key-BBVH44_R.js → key._key-DN_txnIw.js} +2 -2
  135. prefect/server/ui-v2/assets/{key._key-BBVH44_R.js.map → key._key-DN_txnIw.js.map} +1 -1
  136. prefect/server/ui-v2/assets/{lazy-markdown-BoMKGRXo.js → lazy-markdown-CPQjrQrX.js} +2 -2
  137. prefect/server/ui-v2/assets/{lazy-markdown-BoMKGRXo.js.map → lazy-markdown-CPQjrQrX.js.map} +1 -1
  138. prefect/server/ui-v2/assets/{login-BmmIDesJ.js → login-BsnFlv-8.js} +2 -2
  139. prefect/server/ui-v2/assets/{login-BmmIDesJ.js.map → login-BsnFlv-8.js.map} +1 -1
  140. prefect/server/ui-v2/assets/{markdown-input-C6P8cYT1.js → markdown-input-b0zl1NLl.js} +2 -2
  141. prefect/server/ui-v2/assets/{markdown-input-C6P8cYT1.js.map → markdown-input-b0zl1NLl.js.map} +1 -1
  142. prefect/server/ui-v2/assets/{python-example-snippet-DPumgTpL.js → python-example-snippet-W9Z_3hjQ.js} +3 -3
  143. prefect/server/ui-v2/assets/{python-example-snippet-DPumgTpL.js.map → python-example-snippet-W9Z_3hjQ.js.map} +1 -1
  144. prefect/server/ui-v2/assets/{python-input-DX4uV8MF.js → python-input-EKDBgTDE.js} +2 -2
  145. prefect/server/ui-v2/assets/{python-input-DX4uV8MF.js.map → python-input-EKDBgTDE.js.map} +1 -1
  146. prefect/server/ui-v2/assets/{radio-group-DeNK-Ob0.js → radio-group-BMeznoY8.js} +2 -2
  147. prefect/server/ui-v2/assets/{radio-group-DeNK-Ob0.js.map → radio-group-BMeznoY8.js.map} +1 -1
  148. prefect/server/ui-v2/assets/{route-error-state-C9CbByMx.js → route-error-state-BGOi4dn-.js} +2 -2
  149. prefect/server/ui-v2/assets/{route-error-state-C9CbByMx.js.map → route-error-state-BGOi4dn-.js.map} +1 -1
  150. prefect/server/ui-v2/assets/{schema-form-B45xnsnK.js → schema-form-BHWTZ8Sy.js} +2 -2
  151. prefect/server/ui-v2/assets/{schema-form-B45xnsnK.js.map → schema-form-BHWTZ8Sy.js.map} +1 -1
  152. prefect/server/ui-v2/assets/{schema-form-input-string-format-datetime-BJj81bu-.js → schema-form-input-string-format-datetime-hWIr4wQr.js} +4 -4
  153. prefect/server/ui-v2/assets/{schema-form-input-string-format-datetime-BJj81bu-.js.map → schema-form-input-string-format-datetime-hWIr4wQr.js.map} +1 -1
  154. prefect/server/ui-v2/assets/{settings-CbkeWqQ9.js → settings-DVSPGBen.js} +2 -2
  155. prefect/server/ui-v2/assets/{settings-CbkeWqQ9.js.map → settings-DVSPGBen.js.map} +1 -1
  156. prefect/server/ui-v2/assets/{sort-filter-DM7SvBB0.js → sort-filter-MiwMjIYc.js} +2 -2
  157. prefect/server/ui-v2/assets/{sort-filter-DM7SvBB0.js.map → sort-filter-MiwMjIYc.js.map} +1 -1
  158. prefect/server/ui-v2/assets/{table-CEPo6xb_.js → table-BJkw82b1.js} +2 -2
  159. prefect/server/ui-v2/assets/{table-CEPo6xb_.js.map → table-BJkw82b1.js.map} +1 -1
  160. prefect/server/ui-v2/assets/{tags-input-DJiZSVHp.js → tags-input-1KkWhGJZ.js} +2 -2
  161. prefect/server/ui-v2/assets/{tags-input-DJiZSVHp.js.map → tags-input-1KkWhGJZ.js.map} +1 -1
  162. prefect/server/ui-v2/assets/{task-run-concurrency-limits-reset-dialog-vJEkvqWp.js → task-run-concurrency-limits-reset-dialog-ClBlDic_.js} +2 -2
  163. prefect/server/ui-v2/assets/{task-run-concurrency-limits-reset-dialog-vJEkvqWp.js.map → task-run-concurrency-limits-reset-dialog-ClBlDic_.js.map} +1 -1
  164. prefect/server/ui-v2/assets/{task-run._id-BCnDnXrL.js → task-run._id-48TwMBP1.js} +2 -2
  165. prefect/server/ui-v2/assets/{task-run._id-BCnDnXrL.js.map → task-run._id-48TwMBP1.js.map} +1 -1
  166. prefect/server/ui-v2/assets/{task-run._id-ByJw1K82.js → task-run._id-BmK7HGNl.js} +2 -2
  167. prefect/server/ui-v2/assets/{task-run._id-ByJw1K82.js.map → task-run._id-BmK7HGNl.js.map} +1 -1
  168. prefect/server/ui-v2/assets/{task-runs-pagination-DbjB6XnY.js → task-runs-pagination-DA4izCRv.js} +2 -2
  169. prefect/server/ui-v2/assets/{task-runs-pagination-DbjB6XnY.js.map → task-runs-pagination-DA4izCRv.js.map} +1 -1
  170. prefect/server/ui-v2/assets/{textarea-BX8KbLGx.js → textarea-BqtBNhJB.js} +2 -2
  171. prefect/server/ui-v2/assets/{textarea-BX8KbLGx.js.map → textarea-BqtBNhJB.js.map} +1 -1
  172. prefect/server/ui-v2/assets/{timezone-select-TBj2n6Ox.js → timezone-select-BgD_PENN.js} +2 -2
  173. prefect/server/ui-v2/assets/{timezone-select-TBj2n6Ox.js.map → timezone-select-BgD_PENN.js.map} +1 -1
  174. prefect/server/ui-v2/assets/{toggle-group-D7mfSUim.js → toggle-group-ChA-JYQB.js} +2 -2
  175. prefect/server/ui-v2/assets/{toggle-group-D7mfSUim.js.map → toggle-group-ChA-JYQB.js.map} +1 -1
  176. prefect/server/ui-v2/assets/{use-delete-automation-confirmation-dialog-BpBSjz60.js → use-delete-automation-confirmation-dialog-Da1iWNZ_.js} +2 -2
  177. prefect/server/ui-v2/assets/{use-delete-automation-confirmation-dialog-BpBSjz60.js.map → use-delete-automation-confirmation-dialog-Da1iWNZ_.js.map} +1 -1
  178. prefect/server/ui-v2/assets/{use-delete-block-document-confirmation-dialog-BjKsNw6b.js → use-delete-block-document-confirmation-dialog-DfrxGSyY.js} +2 -2
  179. prefect/server/ui-v2/assets/{use-delete-block-document-confirmation-dialog-BjKsNw6b.js.map → use-delete-block-document-confirmation-dialog-DfrxGSyY.js.map} +1 -1
  180. prefect/server/ui-v2/assets/{use-flow-runs-selected-rows-B_BlBMic.js → use-flow-runs-selected-rows-CYOsQ-Df.js} +2 -2
  181. prefect/server/ui-v2/assets/{use-flow-runs-selected-rows-B_BlBMic.js.map → use-flow-runs-selected-rows-CYOsQ-Df.js.map} +1 -1
  182. prefect/server/ui-v2/assets/{use-get-artifacts-flow-task-runs-BfE433bC.js → use-get-artifacts-flow-task-runs-C0V3_gYz.js} +2 -2
  183. prefect/server/ui-v2/assets/{use-get-artifacts-flow-task-runs-BfE433bC.js.map → use-get-artifacts-flow-task-runs-C0V3_gYz.js.map} +1 -1
  184. prefect/server/ui-v2/assets/{use-quick-run-qrtcF-DD.js → use-quick-run-CgM0EheE.js} +2 -2
  185. prefect/server/ui-v2/assets/{use-quick-run-qrtcF-DD.js.map → use-quick-run-CgM0EheE.js.map} +1 -1
  186. prefect/server/ui-v2/assets/{use-stepper-CVy1_GBw.js → use-stepper-CSAs1T4U.js} +2 -2
  187. prefect/server/ui-v2/assets/{use-stepper-CVy1_GBw.js.map → use-stepper-CSAs1T4U.js.map} +1 -1
  188. prefect/server/ui-v2/assets/{utilities-D5-00wjP.js → utilities-DqaYuzh1.js} +2 -2
  189. prefect/server/ui-v2/assets/{utilities-D5-00wjP.js.map → utilities-DqaYuzh1.js.map} +1 -1
  190. prefect/server/ui-v2/assets/{vendor-date-7GxEwcQM.js → vendor-date-JS-ReHrg.js} +2 -2
  191. prefect/server/ui-v2/assets/{vendor-date-7GxEwcQM.js.map → vendor-date-JS-ReHrg.js.map} +1 -1
  192. prefect/server/ui-v2/assets/{vendor-graphs-R8z6S3Jg.js → vendor-graphs-BRVnayIy.js} +2 -2
  193. prefect/server/ui-v2/assets/{vendor-graphs-R8z6S3Jg.js.map → vendor-graphs-BRVnayIy.js.map} +1 -1
  194. prefect/server/ui-v2/assets/{vendor-radix-CznP1SOq.js → vendor-radix-BZJHvSaq.js} +9 -9
  195. prefect/server/ui-v2/assets/{vendor-radix-CznP1SOq.js.map → vendor-radix-BZJHvSaq.js.map} +1 -1
  196. prefect/server/ui-v2/assets/{work-pool-filter-D8MkTbSW.js → work-pool-filter-T6dM4jKE.js} +2 -2
  197. prefect/server/ui-v2/assets/{work-pool-filter-D8MkTbSW.js.map → work-pool-filter-T6dM4jKE.js.map} +1 -1
  198. prefect/server/ui-v2/assets/{work-pool-queue-toggle-CbI7Gfu5.js → work-pool-queue-toggle-CdUw2mAH.js} +2 -2
  199. prefect/server/ui-v2/assets/{work-pool-queue-toggle-CbI7Gfu5.js.map → work-pool-queue-toggle-CdUw2mAH.js.map} +1 -1
  200. prefect/server/ui-v2/assets/{work-pool._workPoolName-CCzzgVV2.js → work-pool._workPoolName-DD8gh9X-.js} +2 -2
  201. prefect/server/ui-v2/assets/{work-pool._workPoolName-CCzzgVV2.js.map → work-pool._workPoolName-DD8gh9X-.js.map} +1 -1
  202. prefect/server/ui-v2/assets/{work-pool_._workPoolName.edit-BAgiuU_j.js → work-pool_._workPoolName.edit-mmnZTSTy.js} +2 -2
  203. prefect/server/ui-v2/assets/{work-pool_._workPoolName.edit-BAgiuU_j.js.map → work-pool_._workPoolName.edit-mmnZTSTy.js.map} +1 -1
  204. prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-BUawWJEy.js → work-pool_._workPoolName.queue._workQueueName-fbwQqkVN.js} +2 -2
  205. prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-BUawWJEy.js.map → work-pool_._workPoolName.queue._workQueueName-fbwQqkVN.js.map} +1 -1
  206. prefect/server/ui-v2/assets/{work-queue-icon-text-D_G_UCJX.js → work-queue-icon-text-YyKOH96e.js} +2 -2
  207. prefect/server/ui-v2/assets/{work-queue-icon-text-D_G_UCJX.js.map → work-queue-icon-text-YyKOH96e.js.map} +1 -1
  208. prefect/server/ui-v2/index.html +3 -3
  209. prefect/settings/models/runner.py +11 -0
  210. prefect/tasks.py +30 -4
  211. {prefect-3.6.14.dev1.dist-info → prefect-3.6.14.dev2.dist-info}/METADATA +1 -1
  212. {prefect-3.6.14.dev1.dist-info → prefect-3.6.14.dev2.dist-info}/RECORD +215 -215
  213. prefect/server/ui-v2/assets/cronstrue-WDy4rv2V.js +0 -2
  214. prefect/server/ui-v2/assets/cronstrue-WDy4rv2V.js.map +0 -1
  215. prefect/server/ui-v2/assets/deployment_._id.duplicate-C_kNyCA9.js +0 -2
  216. prefect/server/ui-v2/assets/deployment_._id.edit-BP8pfTOR.js +0 -2
  217. prefect/server/ui-v2/assets/flow-run-graph-B1vF1w0Z.js +0 -2
  218. prefect/server/ui-v2/assets/flow-run-graph-B1vF1w0Z.js.map +0 -1
  219. prefect/server/ui-v2/assets/flow-run._id-BHY61H6u.js +0 -4
  220. prefect/server/ui-v2/assets/flow-run._id-BHY61H6u.js.map +0 -1
  221. prefect/server/ui-v2/assets/index-BYQ1P2TC.js +0 -2
  222. prefect/server/ui-v2/assets/index-Cd7pM9pk.js +0 -17
  223. prefect/server/ui-v2/assets/index-Cd7pM9pk.js.map +0 -1
  224. prefect/server/ui-v2/assets/index-DvKYj8ko.js +0 -2
  225. prefect/server/ui-v2/assets/index-DvKYj8ko.js.map +0 -1
  226. {prefect-3.6.14.dev1.dist-info → prefect-3.6.14.dev2.dist-info}/WHEEL +0 -0
  227. {prefect-3.6.14.dev1.dist-info → prefect-3.6.14.dev2.dist-info}/entry_points.txt +0 -0
  228. {prefect-3.6.14.dev1.dist-info → prefect-3.6.14.dev2.dist-info}/licenses/LICENSE +0 -0
prefect/_build_info.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # Generated by versioningit
2
- __version__ = "3.6.14.dev1"
3
- __build_date__ = "2026-01-23 08:12:06.479550+00:00"
4
- __git_commit__ = "61276aac1e484a94f570b66a73d5dd6b3eeee5ea"
2
+ __version__ = "3.6.14.dev2"
3
+ __build_date__ = "2026-01-24 08:10:38.113674+00:00"
4
+ __git_commit__ = "1e406408c582f2870a29b6abf35a190fb900f6b3"
5
5
  __dirty__ = False
prefect/_observers.py CHANGED
@@ -24,12 +24,17 @@ class OnCancellingCallback(Protocol):
24
24
  def __call__(self, flow_run_id: uuid.UUID) -> None: ...
25
25
 
26
26
 
27
+ class OnFailureCallback(Protocol):
28
+ def __call__(self, flow_run_ids: set[uuid.UUID]) -> None: ...
29
+
30
+
27
31
  class FlowRunCancellingObserver:
28
32
  def __init__(
29
33
  self,
30
34
  on_cancelling: OnCancellingCallback,
31
35
  polling_interval: float = 10,
32
36
  event_filter: EventFilter | None = None,
37
+ on_failure: OnFailureCallback | None = None,
33
38
  ):
34
39
  """
35
40
  Observer that cancels flow runs when they are marked as cancelling.
@@ -42,9 +47,12 @@ class FlowRunCancellingObserver:
42
47
  polling_interval: Interval in seconds to poll for cancelling flow runs when websocket connection is lost.
43
48
  event_filter: Optional event filter to use for the websocket subscription.
44
49
  If not provided, defaults to filtering for "prefect.flow-run.Cancelling" events.
50
+ on_failure: Optional callback to call when both websocket and polling mechanisms fail.
51
+ Called with the set of in-flight flow run IDs that can no longer be monitored for cancellation.
45
52
  """
46
53
  self.logger = get_logger("FlowRunCancellingObserver")
47
54
  self.on_cancelling = on_cancelling
55
+ self.on_failure = on_failure
48
56
  self.polling_interval = polling_interval
49
57
 
50
58
  if event_filter is not None:
@@ -104,7 +112,7 @@ class FlowRunCancellingObserver:
104
112
  # and we don't need to start the polling task
105
113
  return
106
114
  if exc := task.exception():
107
- self.logger.debug(
115
+ self.logger.warning(
108
116
  "The FlowRunCancellingObserver websocket failed with an exception. Switching to polling mode.",
109
117
  exc_info=exc,
110
118
  )
@@ -115,14 +123,18 @@ class FlowRunCancellingObserver:
115
123
  jitter_range=0.3,
116
124
  )
117
125
  )
118
- self._polling_task.add_done_callback(
119
- lambda task: self.logger.error(
120
- "Cancellation polling task failed. Execution will continue, but flow run cancellation will fail.",
121
- exc_info=task.exception(),
122
- )
123
- if task.exception()
124
- else self.logger.debug("Polling task completed")
126
+ self._polling_task.add_done_callback(self._handle_polling_task_done)
127
+
128
+ def _handle_polling_task_done(self, task: asyncio.Task[None]):
129
+ if task.exception():
130
+ self.logger.error(
131
+ "Cancellation polling task failed. Execution will continue, but flow run cancellation will fail.",
132
+ exc_info=task.exception(),
125
133
  )
134
+ if self.on_failure is not None:
135
+ self.on_failure(self._in_flight_flow_run_ids.copy())
136
+ else:
137
+ self.logger.debug("Polling task completed")
126
138
 
127
139
  async def _check_for_cancelled_flow_runs(self):
128
140
  if self._is_shutting_down:
prefect/cli/server.py CHANGED
@@ -6,6 +6,7 @@ from __future__ import annotations
6
6
 
7
7
  import asyncio
8
8
  import inspect
9
+ import ipaddress
9
10
  import os
10
11
  import shlex
11
12
  import signal
@@ -75,6 +76,17 @@ SERVER_PID_FILE_NAME = "server.pid"
75
76
  SERVICES_PID_FILE = Path(PREFECT_HOME.value()) / "services.pid"
76
77
 
77
78
 
79
+ def _format_host_for_url(host: str) -> str:
80
+ """Format a host for use in a URL, adding brackets for IPv6 addresses."""
81
+ try:
82
+ ip = ipaddress.ip_address(host)
83
+ if ip.version == 6:
84
+ return f"[{host}]"
85
+ except ValueError:
86
+ pass # not an IP address (e.g., hostname)
87
+ return host
88
+
89
+
78
90
  def generate_welcome_blurb(base_url: str, ui_enabled: bool) -> str:
79
91
  if PREFECT_SERVER_API_BASE_PATH:
80
92
  suffix = PREFECT_SERVER_API_BASE_PATH.value()
@@ -328,7 +340,7 @@ def start(
328
340
  """
329
341
  Start a Prefect server instance
330
342
  """
331
- base_url = f"http://{host}:{port}"
343
+ base_url = f"http://{_format_host_for_url(host)}:{port}"
332
344
  if is_interactive():
333
345
  try:
334
346
  prestart_check(base_url)
@@ -353,9 +365,12 @@ def start(
353
365
  pid_file = Path(PREFECT_HOME.value()) / SERVER_PID_FILE_NAME
354
366
  # check if port is already in use
355
367
  try:
356
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
368
+ # use getaddrinfo to support both IPv4 and IPv6 addresses
369
+ info = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
370
+ family, socktype, proto, canonname, sockaddr = info[0]
371
+ with socket.socket(family, socktype, proto) as s:
357
372
  s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
358
- s.bind((host, port))
373
+ s.bind(sockaddr)
359
374
  except socket.gaierror:
360
375
  exit_with_error(
361
376
  f"Invalid host '{host}'. Please specify a valid hostname or IP address."
@@ -68,7 +68,7 @@ from prefect._internal.schemas.validators import (
68
68
  )
69
69
  from prefect._versioning import VersionType, get_inferred_version_info
70
70
  from prefect.client.base import ServerType
71
- from prefect.client.orchestration import PrefectClient, get_client
71
+ from prefect.client.orchestration import PrefectClient, SyncPrefectClient, get_client
72
72
  from prefect.client.schemas.actions import (
73
73
  DeploymentScheduleCreate,
74
74
  DeploymentScheduleUpdate,
@@ -102,7 +102,7 @@ from prefect.settings import (
102
102
  from prefect.types import ListOfNonEmptyStrings
103
103
  from prefect.types.entrypoint import EntrypointType
104
104
  from prefect.utilities.annotations import freeze
105
- from prefect.utilities.asyncutils import run_coro_as_sync, sync_compatible
105
+ from prefect.utilities.asyncutils import run_coro_as_sync
106
106
  from prefect.utilities.callables import ParameterSchema, parameter_schema
107
107
  from prefect.utilities.collections import get_from_dict, isiterable
108
108
  from prefect.utilities.dockerutils import (
@@ -443,6 +443,93 @@ class RunnerDeployment(BaseModel):
443
443
 
444
444
  return deployment_id
445
445
 
446
+ def _create_sync(
447
+ self,
448
+ work_pool_name: Optional[str] = None,
449
+ image: Optional[str] = None,
450
+ version_info: VersionInfo | None = None,
451
+ ) -> UUID:
452
+ work_pool_name = work_pool_name or self.work_pool_name
453
+
454
+ if image and not work_pool_name:
455
+ raise ValueError(
456
+ "An image can only be provided when registering a deployment with a"
457
+ " work pool."
458
+ )
459
+
460
+ if self.work_queue_name and not work_pool_name:
461
+ raise ValueError(
462
+ "A work queue can only be provided when registering a deployment with"
463
+ " a work pool."
464
+ )
465
+
466
+ if self.job_variables and not work_pool_name:
467
+ raise ValueError(
468
+ "Job variables can only be provided when registering a deployment"
469
+ " with a work pool."
470
+ )
471
+
472
+ with get_client(sync_client=True) as client:
473
+ flow_id = client.create_flow_from_name(self.flow_name)
474
+
475
+ create_payload: dict[str, Any] = dict(
476
+ flow_id=flow_id,
477
+ name=self.name,
478
+ work_queue_name=self.work_queue_name,
479
+ work_pool_name=work_pool_name,
480
+ version=self.version,
481
+ version_info=version_info,
482
+ paused=self.paused,
483
+ schedules=self.schedules,
484
+ concurrency_limit=self.concurrency_limit,
485
+ concurrency_options=self.concurrency_options,
486
+ parameters=self.parameters,
487
+ description=self.description,
488
+ tags=self.tags,
489
+ path=self._path,
490
+ entrypoint=self.entrypoint,
491
+ storage_document_id=None,
492
+ infrastructure_document_id=None,
493
+ parameter_openapi_schema=self._parameter_openapi_schema.model_dump(
494
+ exclude_unset=True
495
+ ),
496
+ enforce_parameter_schema=self.enforce_parameter_schema,
497
+ )
498
+
499
+ if work_pool_name:
500
+ create_payload["job_variables"] = self.job_variables
501
+ if image:
502
+ create_payload["job_variables"]["image"] = image
503
+ create_payload["path"] = None if self.storage else self._path
504
+ if self.storage:
505
+ pull_steps = self.storage.to_pull_step()
506
+ if isinstance(pull_steps, list):
507
+ create_payload["pull_steps"] = pull_steps
508
+ elif pull_steps:
509
+ create_payload["pull_steps"] = [pull_steps]
510
+ else:
511
+ create_payload["pull_steps"] = []
512
+ else:
513
+ create_payload["pull_steps"] = []
514
+
515
+ try:
516
+ deployment_id = client.create_deployment(**create_payload)
517
+ except Exception as exc:
518
+ if isinstance(exc, PrefectHTTPStatusError):
519
+ detail = exc.response.json().get("detail")
520
+ if detail:
521
+ raise DeploymentApplyError(detail) from exc
522
+ raise DeploymentApplyError(
523
+ f"Error while applying deployment: {str(exc)}"
524
+ ) from exc
525
+
526
+ self._create_triggers_sync(deployment_id, client)
527
+
528
+ if self._sla or self._sla == []:
529
+ self._create_slas_sync(deployment_id, client)
530
+
531
+ return deployment_id
532
+
446
533
  async def _update(
447
534
  self,
448
535
  deployment_id: UUID,
@@ -495,6 +582,52 @@ class RunnerDeployment(BaseModel):
495
582
 
496
583
  return deployment_id
497
584
 
585
+ def _update_sync(
586
+ self,
587
+ deployment_id: UUID,
588
+ client: SyncPrefectClient,
589
+ version_info: VersionInfo | None,
590
+ ):
591
+ parameter_openapi_schema = self._parameter_openapi_schema.model_dump(
592
+ exclude_unset=True
593
+ )
594
+
595
+ update_payload = self.model_dump(
596
+ mode="json",
597
+ exclude_unset=True,
598
+ exclude={"storage", "name", "flow_name", "triggers", "version_type"},
599
+ )
600
+
601
+ if self.storage:
602
+ pull_steps = self.storage.to_pull_step()
603
+ if pull_steps and not isinstance(pull_steps, list):
604
+ pull_steps = [pull_steps]
605
+ update_payload["pull_steps"] = pull_steps
606
+ else:
607
+ update_payload["pull_steps"] = None
608
+
609
+ if self.schedules:
610
+ update_payload["schedules"] = [
611
+ schedule.model_dump(mode="json", exclude_unset=True)
612
+ for schedule in self.schedules
613
+ ]
614
+
615
+ client.update_deployment(
616
+ deployment_id,
617
+ deployment=DeploymentUpdate(
618
+ **update_payload,
619
+ version_info=version_info,
620
+ parameter_openapi_schema=parameter_openapi_schema,
621
+ ),
622
+ )
623
+
624
+ self._create_triggers_sync(deployment_id, client)
625
+
626
+ if self._sla or self._sla == []:
627
+ self._create_slas_sync(deployment_id, client)
628
+
629
+ return deployment_id
630
+
498
631
  async def _create_triggers(self, deployment_id: UUID, client: PrefectClient):
499
632
  try:
500
633
  # The triggers defined in the deployment spec are, essentially,
@@ -516,8 +649,21 @@ class RunnerDeployment(BaseModel):
516
649
  trigger.set_deployment_id(deployment_id)
517
650
  await client.create_automation(trigger.as_automation())
518
651
 
519
- @sync_compatible
520
- async def apply(
652
+ def _create_triggers_sync(self, deployment_id: UUID, client: SyncPrefectClient):
653
+ try:
654
+ client.delete_resource_owned_automations(
655
+ f"prefect.deployment.{deployment_id}"
656
+ )
657
+ except PrefectHTTPStatusError as e:
658
+ if e.response.status_code == 404:
659
+ return deployment_id
660
+ raise e
661
+
662
+ for trigger in self.triggers:
663
+ trigger.set_deployment_id(deployment_id)
664
+ client.create_automation(trigger.as_automation())
665
+
666
+ async def aapply(
521
667
  self,
522
668
  schedules: Optional[List[dict[str, Any]]] = None,
523
669
  work_pool_name: Optional[str] = None,
@@ -562,6 +708,51 @@ class RunnerDeployment(BaseModel):
562
708
  ]
563
709
  return await self._update(deployment.id, client, version_info)
564
710
 
711
+ @async_dispatch(aapply)
712
+ def apply(
713
+ self,
714
+ schedules: Optional[List[dict[str, Any]]] = None,
715
+ work_pool_name: Optional[str] = None,
716
+ image: Optional[str] = None,
717
+ version_info: Optional[VersionInfo] = None,
718
+ ) -> UUID:
719
+ """
720
+ Registers this deployment with the API and returns the deployment's ID.
721
+
722
+ Args:
723
+ work_pool_name: The name of the work pool to use for this
724
+ deployment.
725
+ image: The registry, name, and tag of the Docker image to
726
+ use for this deployment. Only used when the deployment is
727
+ deployed to a work pool.
728
+ version_info: The version information to use for the deployment.
729
+ Returns:
730
+ The ID of the created deployment.
731
+ """
732
+ version_info = version_info or self._get_deployment_version_info(
733
+ self.version_type
734
+ )
735
+
736
+ with get_client(sync_client=True) as client:
737
+ try:
738
+ deployment = client.read_deployment_by_name(self.full_name)
739
+ except ObjectNotFound:
740
+ if schedules:
741
+ self.schedules = [
742
+ DeploymentScheduleCreate(**schedule) for schedule in schedules
743
+ ]
744
+ return self._create_sync(work_pool_name, image, version_info)
745
+ else:
746
+ if image:
747
+ self.job_variables["image"] = image
748
+ if work_pool_name:
749
+ self.work_pool_name = work_pool_name
750
+ if schedules:
751
+ self.schedules = [
752
+ DeploymentScheduleUpdate(**schedule) for schedule in schedules
753
+ ]
754
+ return self._update_sync(deployment.id, client, version_info)
755
+
565
756
  async def _create_slas(self, deployment_id: UUID, client: PrefectClient):
566
757
  if not isinstance(self._sla, list):
567
758
  self._sla = [self._sla]
@@ -573,6 +764,17 @@ class RunnerDeployment(BaseModel):
573
764
  "SLA configuration is currently only supported on Prefect Cloud."
574
765
  )
575
766
 
767
+ def _create_slas_sync(self, deployment_id: UUID, client: SyncPrefectClient):
768
+ if not isinstance(self._sla, list):
769
+ self._sla = [self._sla]
770
+
771
+ if client.server_type == ServerType.CLOUD:
772
+ client.apply_slas_for_deployment(deployment_id, self._sla)
773
+ else:
774
+ raise ValueError(
775
+ "SLA configuration is currently only supported on Prefect Cloud."
776
+ )
777
+
576
778
  @staticmethod
577
779
  def _construct_deployment_schedules(
578
780
  interval: Optional[
@@ -1146,8 +1348,7 @@ class RunnerDeployment(BaseModel):
1146
1348
  return deployment
1147
1349
 
1148
1350
 
1149
- @sync_compatible
1150
- async def deploy(
1351
+ async def adeploy(
1151
1352
  *deployments: RunnerDeployment,
1152
1353
  work_pool_name: Optional[str] = None,
1153
1354
  image: Optional[Union[str, DockerImage]] = None,
@@ -1194,7 +1395,7 @@ async def deploy(
1194
1395
  print("I'm a locally defined flow!")
1195
1396
 
1196
1397
  if __name__ == "__main__":
1197
- deploy(
1398
+ await adeploy(
1198
1399
  local_flow.to_deployment(name="example-deploy-local-flow"),
1199
1400
  flow.from_source(
1200
1401
  source="https://github.com/org/repo.git",
@@ -1316,7 +1517,246 @@ async def deploy(
1316
1517
  ):
1317
1518
  try:
1318
1519
  deployment_ids.append(
1319
- await deployment.apply(image=image_ref, work_pool_name=work_pool_name)
1520
+ await deployment.aapply(image=image_ref, work_pool_name=work_pool_name)
1521
+ )
1522
+ except Exception as exc:
1523
+ if len(deployments) == 1:
1524
+ raise
1525
+ deployment_exceptions.append({"deployment": deployment, "exc": exc})
1526
+
1527
+ if deployment_exceptions:
1528
+ console.print(
1529
+ "Encountered errors while creating/updating deployments:\n",
1530
+ style="orange_red1",
1531
+ )
1532
+ else:
1533
+ console.print("Successfully created/updated all deployments!\n", style="green")
1534
+
1535
+ complete_failure = len(deployment_exceptions) == len(deployments)
1536
+
1537
+ table = Table(
1538
+ title="Deployments",
1539
+ show_lines=True,
1540
+ )
1541
+
1542
+ table.add_column(header="Name", style="blue", no_wrap=True)
1543
+ table.add_column(header="Status", style="blue", no_wrap=True)
1544
+ table.add_column(header="Details", style="blue")
1545
+
1546
+ for deployment in deployments:
1547
+ errored_deployment = next(
1548
+ (d for d in deployment_exceptions if d["deployment"] == deployment),
1549
+ None,
1550
+ )
1551
+ if errored_deployment:
1552
+ table.add_row(
1553
+ f"{deployment.flow_name}/{deployment.name}",
1554
+ "failed",
1555
+ str(errored_deployment["exc"]),
1556
+ style="red",
1557
+ )
1558
+ else:
1559
+ table.add_row(f"{deployment.flow_name}/{deployment.name}", "applied")
1560
+ console.print(table)
1561
+
1562
+ if print_next_steps_message and not complete_failure:
1563
+ if (
1564
+ not work_pool.is_push_pool
1565
+ and not work_pool.is_managed_pool
1566
+ and not active_workers
1567
+ ):
1568
+ console.print(
1569
+ "\nTo execute flow runs from these deployments, start a worker in a"
1570
+ " separate terminal that pulls work from the"
1571
+ f" {work_pool_name!r} work pool:"
1572
+ f"\n\t[blue]$ prefect worker start --pool {work_pool_name!r}[/]",
1573
+ )
1574
+ console.print(
1575
+ "\nTo trigger any of these deployments, use the"
1576
+ " following command:\n[blue]\n\t$ prefect deployment run"
1577
+ " [DEPLOYMENT_NAME]\n[/]"
1578
+ )
1579
+
1580
+ if PREFECT_UI_URL:
1581
+ console.print(
1582
+ "\nYou can also trigger your deployments via the Prefect UI:"
1583
+ f" [blue]{PREFECT_UI_URL.value()}/deployments[/]\n"
1584
+ )
1585
+
1586
+ return deployment_ids
1587
+
1588
+
1589
+ @async_dispatch(adeploy)
1590
+ def deploy(
1591
+ *deployments: RunnerDeployment,
1592
+ work_pool_name: Optional[str] = None,
1593
+ image: Optional[Union[str, DockerImage]] = None,
1594
+ build: bool = True,
1595
+ push: bool = True,
1596
+ print_next_steps_message: bool = True,
1597
+ ignore_warnings: bool = False,
1598
+ ) -> List[UUID]:
1599
+ """
1600
+ Deploy the provided list of deployments to dynamic infrastructure via a
1601
+ work pool.
1602
+
1603
+ By default, calling this function will build a Docker image for the deployments, push it to a
1604
+ registry, and create each deployment via the Prefect API that will run the corresponding
1605
+ flow on the given schedule.
1606
+
1607
+ If you want to use an existing image, you can pass `build=False` to skip building and pushing
1608
+ an image.
1609
+
1610
+ Args:
1611
+ *deployments: A list of deployments to deploy.
1612
+ work_pool_name: The name of the work pool to use for these deployments. Defaults to
1613
+ the value of `PREFECT_DEFAULT_WORK_POOL_NAME`.
1614
+ image: The name of the Docker image to build, including the registry and
1615
+ repository. Pass a DockerImage instance to customize the Dockerfile used
1616
+ and build arguments.
1617
+ build: Whether or not to build a new image for the flow. If False, the provided
1618
+ image will be used as-is and pulled at runtime.
1619
+ push: Whether or not to skip pushing the built image to a registry.
1620
+ print_next_steps_message: Whether or not to print a message with next steps
1621
+ after deploying the deployments.
1622
+
1623
+ Returns:
1624
+ A list of deployment IDs for the created/updated deployments.
1625
+
1626
+ Examples:
1627
+ Deploy a group of flows to a work pool:
1628
+
1629
+ ```python
1630
+ from prefect import deploy, flow
1631
+
1632
+ @flow(log_prints=True)
1633
+ def local_flow():
1634
+ print("I'm a locally defined flow!")
1635
+
1636
+ if __name__ == "__main__":
1637
+ deploy(
1638
+ local_flow.to_deployment(name="example-deploy-local-flow"),
1639
+ flow.from_source(
1640
+ source="https://github.com/org/repo.git",
1641
+ entrypoint="flows.py:my_flow",
1642
+ ).to_deployment(
1643
+ name="example-deploy-remote-flow",
1644
+ ),
1645
+ work_pool_name="my-work-pool",
1646
+ image="my-registry/my-image:dev",
1647
+ )
1648
+ ```
1649
+ """
1650
+ work_pool_name = work_pool_name or PREFECT_DEFAULT_WORK_POOL_NAME.value()
1651
+
1652
+ if not image and not all(
1653
+ d.storage or d.entrypoint_type == EntrypointType.MODULE_PATH
1654
+ for d in deployments
1655
+ ):
1656
+ raise ValueError(
1657
+ "Either an image or remote storage location must be provided when deploying"
1658
+ " a deployment."
1659
+ )
1660
+
1661
+ if not work_pool_name:
1662
+ raise ValueError(
1663
+ "A work pool name must be provided when deploying a deployment. Either"
1664
+ " provide a work pool name when calling `deploy` or set"
1665
+ " `PREFECT_DEFAULT_WORK_POOL_NAME` in your profile."
1666
+ )
1667
+
1668
+ if image and isinstance(image, str):
1669
+ image_name, image_tag = parse_image_tag(image)
1670
+ image = DockerImage(name=image_name, tag=image_tag)
1671
+
1672
+ try:
1673
+ with get_client(sync_client=True) as client:
1674
+ work_pool = client.read_work_pool(work_pool_name)
1675
+ active_workers = client.read_workers_for_work_pool(
1676
+ work_pool_name,
1677
+ worker_filter=WorkerFilter(status=WorkerFilterStatus(any_=["ONLINE"])),
1678
+ )
1679
+ except ObjectNotFound as exc:
1680
+ raise ValueError(
1681
+ f"Could not find work pool {work_pool_name!r}. Please create it before"
1682
+ " deploying this flow."
1683
+ ) from exc
1684
+
1685
+ is_docker_based_work_pool = get_from_dict(
1686
+ work_pool.base_job_template, "variables.properties.image", False
1687
+ )
1688
+ is_block_based_work_pool = get_from_dict(
1689
+ work_pool.base_job_template, "variables.properties.block", False
1690
+ )
1691
+ # carve out an exception for block based work pools that only have a block in their base job template
1692
+ console = Console()
1693
+ if not is_docker_based_work_pool and not is_block_based_work_pool:
1694
+ if image:
1695
+ raise ValueError(
1696
+ f"Work pool {work_pool_name!r} does not support custom Docker images."
1697
+ " Please use a work pool with an `image` variable in its base job template"
1698
+ " or specify a remote storage location for the flow with `.from_source`."
1699
+ " If you are attempting to deploy a flow to a local process work pool,"
1700
+ " consider using `flow.serve` instead. See the documentation for more"
1701
+ " information: https://docs.prefect.io/latest/how-to-guides/deployments/run-flows-in-local-processes"
1702
+ )
1703
+ elif work_pool.type == "process" and not ignore_warnings:
1704
+ console.print(
1705
+ "Looks like you're deploying to a process work pool. If you're creating a"
1706
+ " deployment for local development, calling `.serve` on your flow is a great"
1707
+ " way to get started. See the documentation for more information:"
1708
+ " https://docs.prefect.io/latest/how-to-guides/deployments/run-flows-in-local-processes "
1709
+ " Set `ignore_warnings=True` to suppress this message.",
1710
+ style="yellow",
1711
+ )
1712
+
1713
+ is_managed_pool = work_pool.is_managed_pool
1714
+ if is_managed_pool:
1715
+ build = False
1716
+ push = False
1717
+
1718
+ if image and build:
1719
+ with Progress(
1720
+ SpinnerColumn(),
1721
+ TextColumn(f"Building image {image.reference}..."),
1722
+ transient=True,
1723
+ console=console,
1724
+ ) as progress:
1725
+ docker_build_task = progress.add_task("docker_build", total=1)
1726
+ image.build()
1727
+
1728
+ progress.update(docker_build_task, completed=1)
1729
+ console.print(
1730
+ f"Successfully built image {image.reference!r}", style="green"
1731
+ )
1732
+
1733
+ if image and build and push:
1734
+ with Progress(
1735
+ SpinnerColumn(),
1736
+ TextColumn("Pushing image..."),
1737
+ transient=True,
1738
+ console=console,
1739
+ ) as progress:
1740
+ docker_push_task = progress.add_task("docker_push", total=1)
1741
+
1742
+ image.push()
1743
+
1744
+ progress.update(docker_push_task, completed=1)
1745
+
1746
+ console.print(f"Successfully pushed image {image.reference!r}", style="green")
1747
+
1748
+ deployment_exceptions: list[dict[str, Any]] = []
1749
+ deployment_ids: list[UUID] = []
1750
+ image_ref = image.reference if image else None
1751
+ for deployment in track(
1752
+ deployments,
1753
+ description="Creating/updating deployments...",
1754
+ console=console,
1755
+ transient=True,
1756
+ ):
1757
+ try:
1758
+ deployment_ids.append(
1759
+ deployment.apply(image=image_ref, work_pool_name=work_pool_name)
1320
1760
  )
1321
1761
  except Exception as exc:
1322
1762
  if len(deployments) == 1: