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.
- prefect/_build_info.py +3 -3
- prefect/_observers.py +20 -8
- prefect/cli/server.py +18 -3
- prefect/deployments/runner.py +448 -8
- prefect/flows.py +162 -34
- prefect/runner/runner.py +48 -0
- prefect/server/orchestration/global_policy.py +4 -2
- prefect/server/ui-v2/assets/{artifact-card-DkgEVahF.js → artifact-card-D41jcvLX.js} +2 -2
- prefect/server/ui-v2/assets/{artifact-card-DkgEVahF.js.map → artifact-card-D41jcvLX.js.map} +1 -1
- prefect/server/ui-v2/assets/{artifact._id-C7yJhkKY.js → artifact._id-CYMvBFBb.js} +2 -2
- prefect/server/ui-v2/assets/{artifact._id-C7yJhkKY.js.map → artifact._id-CYMvBFBb.js.map} +1 -1
- prefect/server/ui-v2/assets/{automation-wizard-BLI4sqs9.js → automation-wizard-DHsWJ8JS.js} +2 -2
- prefect/server/ui-v2/assets/{automation-wizard-BLI4sqs9.js.map → automation-wizard-DHsWJ8JS.js.map} +1 -1
- prefect/server/ui-v2/assets/{automation._id-OLes3du9.js → automation._id-CCroE1-1.js} +2 -2
- prefect/server/ui-v2/assets/{automation._id-OLes3du9.js.map → automation._id-CCroE1-1.js.map} +1 -1
- prefect/server/ui-v2/assets/{automation_._id.edit-QrSzkDDB.js → automation_._id.edit-BgpDmMq7.js} +2 -2
- prefect/server/ui-v2/assets/{automation_._id.edit-QrSzkDDB.js.map → automation_._id.edit-BgpDmMq7.js.map} +1 -1
- prefect/server/ui-v2/assets/{automations-header-BoJc9HK8.js → automations-header-BEZXZmli.js} +2 -2
- prefect/server/ui-v2/assets/{automations-header-BoJc9HK8.js.map → automations-header-BEZXZmli.js.map} +1 -1
- prefect/server/ui-v2/assets/{base-job-template-form-section-Br6s0jNS.js → base-job-template-form-section-BubT4OHj.js} +2 -2
- prefect/server/ui-v2/assets/{base-job-template-form-section-Br6s0jNS.js.map → base-job-template-form-section-BubT4OHj.js.map} +1 -1
- prefect/server/ui-v2/assets/{block-type-details-BSbBP-Nm.js → block-type-details-DCWuBOwb.js} +2 -2
- prefect/server/ui-v2/assets/{block-type-details-BSbBP-Nm.js.map → block-type-details-DCWuBOwb.js.map} +1 -1
- prefect/server/ui-v2/assets/{block-type-logo-DNFERTyH.js → block-type-logo-SZMe2NdY.js} +2 -2
- prefect/server/ui-v2/assets/{block-type-logo-DNFERTyH.js.map → block-type-logo-SZMe2NdY.js.map} +1 -1
- prefect/server/ui-v2/assets/{block._id-DO6Hulrv.js → block._id-B6_kwTZ-.js} +2 -2
- prefect/server/ui-v2/assets/{block._id-DO6Hulrv.js.map → block._id-B6_kwTZ-.js.map} +1 -1
- prefect/server/ui-v2/assets/{block_._id.edit-BWF0TQvv.js → block_._id.edit-Bjg9WgY6.js} +2 -2
- prefect/server/ui-v2/assets/{block_._id.edit-BWF0TQvv.js.map → block_._id.edit-Bjg9WgY6.js.map} +1 -1
- prefect/server/ui-v2/assets/{catalog-BlaqZP0r.js → catalog-Bv8x-rIx.js} +2 -2
- prefect/server/ui-v2/assets/{catalog-BlaqZP0r.js.map → catalog-Bv8x-rIx.js.map} +1 -1
- prefect/server/ui-v2/assets/{catalog_._slug-BZWiMtjq.js → catalog_._slug-2GldfHc0.js} +2 -2
- prefect/server/ui-v2/assets/{catalog_._slug-BZWiMtjq.js.map → catalog_._slug-2GldfHc0.js.map} +1 -1
- prefect/server/ui-v2/assets/{catalog_._slug_.create-oHffbeMs.js → catalog_._slug_.create-Bjt7az95.js} +2 -2
- prefect/server/ui-v2/assets/{catalog_._slug_.create-oHffbeMs.js.map → catalog_._slug_.create-Bjt7az95.js.map} +1 -1
- prefect/server/ui-v2/assets/{collapsible-D45P93xU.js → collapsible-BT0EnMmT.js} +2 -2
- prefect/server/ui-v2/assets/{collapsible-D45P93xU.js.map → collapsible-BT0EnMmT.js.map} +1 -1
- prefect/server/ui-v2/assets/{concurrency-limit._id-B6oyPVya.js → concurrency-limit._id-Ba5Uof4l.js} +2 -2
- prefect/server/ui-v2/assets/{concurrency-limit._id-B6oyPVya.js.map → concurrency-limit._id-Ba5Uof4l.js.map} +1 -1
- prefect/server/ui-v2/assets/{create-CcYMd6w6.js → create-DK8Cy1hG.js} +2 -2
- prefect/server/ui-v2/assets/{create-CcYMd6w6.js.map → create-DK8Cy1hG.js.map} +1 -1
- prefect/server/ui-v2/assets/{create-DK1WvDhf.js → create-Ds8Lheyk.js} +2 -2
- prefect/server/ui-v2/assets/{create-DK1WvDhf.js.map → create-Ds8Lheyk.js.map} +1 -1
- prefect/server/ui-v2/assets/cronstrue-C72j5NDa.js +2 -0
- prefect/server/ui-v2/assets/cronstrue-C72j5NDa.js.map +1 -0
- prefect/server/ui-v2/assets/{data-table-B_iyrrTm.js → data-table-B6bdz5Zj.js} +2 -2
- prefect/server/ui-v2/assets/{data-table-B_iyrrTm.js.map → data-table-B6bdz5Zj.js.map} +1 -1
- prefect/server/ui-v2/assets/{delete-confirmation-dialog-7GKqeKy1.js → delete-confirmation-dialog-CV8ITEEW.js} +2 -2
- prefect/server/ui-v2/assets/{delete-confirmation-dialog-7GKqeKy1.js.map → delete-confirmation-dialog-CV8ITEEW.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-action-header-DrvnfE9Y.js → deployment-action-header-DpjOcab0.js} +2 -2
- prefect/server/ui-v2/assets/{deployment-action-header-DrvnfE9Y.js.map → deployment-action-header-DpjOcab0.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-form-DsAij22s.js → deployment-form-CjIPPnwV.js} +3 -3
- prefect/server/ui-v2/assets/{deployment-form-DsAij22s.js.map → deployment-form-CjIPPnwV.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-links-BwqfKWQX.js → deployment-links--VkBo9wV.js} +2 -2
- prefect/server/ui-v2/assets/{deployment-links-BwqfKWQX.js.map → deployment-links--VkBo9wV.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment._id-E5e5s6bO.js → deployment._id-CW00MZGG.js} +2 -2
- prefect/server/ui-v2/assets/{deployment._id-E5e5s6bO.js.map → deployment._id-CW00MZGG.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment._id-U6KrtWw0.js → deployment._id-DjYh9IiR.js} +2 -2
- prefect/server/ui-v2/assets/{deployment._id-U6KrtWw0.js.map → deployment._id-DjYh9IiR.js.map} +1 -1
- prefect/server/ui-v2/assets/deployment_._id.duplicate-18ZQHHgI.js +2 -0
- prefect/server/ui-v2/assets/{deployment_._id.duplicate-C_kNyCA9.js.map → deployment_._id.duplicate-18ZQHHgI.js.map} +1 -1
- prefect/server/ui-v2/assets/deployment_._id.edit-C9H0Teuv.js +2 -0
- prefect/server/ui-v2/assets/{deployment_._id.edit-BP8pfTOR.js.map → deployment_._id.edit-C9H0Teuv.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment_._id.run-Di3-4Mtk.js → deployment_._id.run-CwHYw8j1.js} +2 -2
- prefect/server/ui-v2/assets/{deployment_._id.run-Di3-4Mtk.js.map → deployment_._id.run-CwHYw8j1.js.map} +1 -1
- prefect/server/ui-v2/assets/{dropdown-menu-iS8iJMpx.js → dropdown-menu-BYgkpoqb.js} +2 -2
- prefect/server/ui-v2/assets/{dropdown-menu-iS8iJMpx.js.map → dropdown-menu-BYgkpoqb.js.map} +1 -1
- prefect/server/ui-v2/assets/{event._eventDate._eventId-CG15qDSZ.js → event._eventDate._eventId-mPwFHce2.js} +2 -2
- prefect/server/ui-v2/assets/{event._eventDate._eventId-CG15qDSZ.js.map → event._eventDate._eventId-mPwFHce2.js.map} +1 -1
- prefect/server/ui-v2/assets/flow-run-graph-BMp5vJJa.js +2 -0
- prefect/server/ui-v2/assets/flow-run-graph-BMp5vJJa.js.map +1 -0
- prefect/server/ui-v2/assets/{flow-run._id-D_eAHZ5z.js → flow-run._id-CafHaMND.js} +2 -2
- prefect/server/ui-v2/assets/{flow-run._id-D_eAHZ5z.js.map → flow-run._id-CafHaMND.js.map} +1 -1
- prefect/server/ui-v2/assets/flow-run._id-CelqqA9P.js +4 -0
- prefect/server/ui-v2/assets/flow-run._id-CelqqA9P.js.map +1 -0
- prefect/server/ui-v2/assets/{flow-run._id-wco0Q19f.js → flow-run._id-hVuHWXbi.js} +2 -2
- prefect/server/ui-v2/assets/{flow-run._id-wco0Q19f.js.map → flow-run._id-hVuHWXbi.js.map} +1 -1
- prefect/server/ui-v2/assets/{flow-runs-pagination-DzGHOht3.js → flow-runs-pagination-CqjL9gOj.js} +2 -2
- prefect/server/ui-v2/assets/{flow-runs-pagination-DzGHOht3.js.map → flow-runs-pagination-CqjL9gOj.js.map} +1 -1
- prefect/server/ui-v2/assets/{flow._id-Dn2so3rK.js → flow._id-C1Djus0m.js} +2 -2
- prefect/server/ui-v2/assets/{flow._id-Dn2so3rK.js.map → flow._id-C1Djus0m.js.map} +1 -1
- prefect/server/ui-v2/assets/{form-rC8pxeRl.js → form-z_RaFdEo.js} +2 -2
- prefect/server/ui-v2/assets/{form-rC8pxeRl.js.map → form-z_RaFdEo.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-CuAwmJ64.js → header-B-PvVnN7.js} +2 -2
- prefect/server/ui-v2/assets/{header-CuAwmJ64.js.map → header-B-PvVnN7.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-C0zV3WDn.js → header-BSXYXvPS.js} +2 -2
- prefect/server/ui-v2/assets/{header-C0zV3WDn.js.map → header-BSXYXvPS.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-C6ko3O7G.js → header-CKVO4oLE.js} +2 -2
- prefect/server/ui-v2/assets/{header-C6ko3O7G.js.map → header-CKVO4oLE.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CotXErTe.js → index-B5zctA6A.js} +2 -2
- prefect/server/ui-v2/assets/{index-CotXErTe.js.map → index-B5zctA6A.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-k2161reF.js → index-BCzGofA2.js} +2 -2
- prefect/server/ui-v2/assets/{index-k2161reF.js.map → index-BCzGofA2.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-BdgomKuA.js → index-BOuQzuoR.js} +2 -2
- prefect/server/ui-v2/assets/{index-BdgomKuA.js.map → index-BOuQzuoR.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-pE_31vXF.js → index-BQOxk1xu.js} +2 -2
- prefect/server/ui-v2/assets/{index-pE_31vXF.js.map → index-BQOxk1xu.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-Benf9xn3.js → index-BU1Y-wgo.js} +2 -2
- prefect/server/ui-v2/assets/{index-Benf9xn3.js.map → index-BU1Y-wgo.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CYpFLd7y.js → index-BxGluvOD.js} +2 -2
- prefect/server/ui-v2/assets/{index-CYpFLd7y.js.map → index-BxGluvOD.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CcVSC2pV.js → index-CAbBPipD.js} +2 -2
- prefect/server/ui-v2/assets/{index-CcVSC2pV.js.map → index-CAbBPipD.js.map} +1 -1
- prefect/server/ui-v2/assets/index-CHwhYh-B.js +2 -0
- prefect/server/ui-v2/assets/{index-BYQ1P2TC.js.map → index-CHwhYh-B.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-0g8aAM30.js → index-CIKCrLeZ.js} +2 -2
- prefect/server/ui-v2/assets/{index-0g8aAM30.js.map → index-CIKCrLeZ.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-fhrsIceD.js → index-CLUjhc4A.js} +2 -2
- prefect/server/ui-v2/assets/{index-fhrsIceD.js.map → index-CLUjhc4A.js.map} +1 -1
- prefect/server/ui-v2/assets/index-CX5uU1zd.js +2 -0
- prefect/server/ui-v2/assets/index-CX5uU1zd.js.map +1 -0
- prefect/server/ui-v2/assets/{index-DwkedbVy.js → index-CXVePXfG.js} +2 -2
- prefect/server/ui-v2/assets/{index-DwkedbVy.js.map → index-CXVePXfG.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-DB2qeheX.js → index-D-ZIgaBw.js} +2 -2
- prefect/server/ui-v2/assets/{index-DB2qeheX.js.map → index-D-ZIgaBw.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-D8fZaZa7.js → index-D10Svo9k.js} +2 -2
- prefect/server/ui-v2/assets/{index-D8fZaZa7.js.map → index-D10Svo9k.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CEStyUb-.js → index-D2EEipYt.js} +2 -2
- prefect/server/ui-v2/assets/{index-CEStyUb-.js.map → index-D2EEipYt.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-BIqzfifB.js → index-DV5fpxXB.js} +2 -2
- prefect/server/ui-v2/assets/{index-BIqzfifB.js.map → index-DV5fpxXB.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-C3D0Re7U.js → index-FHRYGk_R.js} +2 -2
- prefect/server/ui-v2/assets/{index-C3D0Re7U.js.map → index-FHRYGk_R.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CCs20W5W.js → index-IGxqySze.js} +2 -2
- prefect/server/ui-v2/assets/{index-CCs20W5W.js.map → index-IGxqySze.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-BKbBu9gL.js → index-P2xc38Yh.js} +2 -2
- prefect/server/ui-v2/assets/{index-BKbBu9gL.js.map → index-P2xc38Yh.js.map} +1 -1
- prefect/server/ui-v2/assets/index-Yzhlw6-Q.js +17 -0
- prefect/server/ui-v2/assets/index-Yzhlw6-Q.js.map +1 -0
- prefect/server/ui-v2/assets/{index-BjsWjEUO.js → index-n7YFiQkB.js} +2 -2
- prefect/server/ui-v2/assets/{index-BjsWjEUO.js.map → index-n7YFiQkB.js.map} +1 -1
- prefect/server/ui-v2/assets/{json-input-C9dkBSdc.js → json-input-Dlzy_XLK.js} +2 -2
- prefect/server/ui-v2/assets/{json-input-C9dkBSdc.js.map → json-input-Dlzy_XLK.js.map} +1 -1
- prefect/server/ui-v2/assets/{key._key-BBVH44_R.js → key._key-DN_txnIw.js} +2 -2
- prefect/server/ui-v2/assets/{key._key-BBVH44_R.js.map → key._key-DN_txnIw.js.map} +1 -1
- prefect/server/ui-v2/assets/{lazy-markdown-BoMKGRXo.js → lazy-markdown-CPQjrQrX.js} +2 -2
- prefect/server/ui-v2/assets/{lazy-markdown-BoMKGRXo.js.map → lazy-markdown-CPQjrQrX.js.map} +1 -1
- prefect/server/ui-v2/assets/{login-BmmIDesJ.js → login-BsnFlv-8.js} +2 -2
- prefect/server/ui-v2/assets/{login-BmmIDesJ.js.map → login-BsnFlv-8.js.map} +1 -1
- prefect/server/ui-v2/assets/{markdown-input-C6P8cYT1.js → markdown-input-b0zl1NLl.js} +2 -2
- prefect/server/ui-v2/assets/{markdown-input-C6P8cYT1.js.map → markdown-input-b0zl1NLl.js.map} +1 -1
- prefect/server/ui-v2/assets/{python-example-snippet-DPumgTpL.js → python-example-snippet-W9Z_3hjQ.js} +3 -3
- prefect/server/ui-v2/assets/{python-example-snippet-DPumgTpL.js.map → python-example-snippet-W9Z_3hjQ.js.map} +1 -1
- prefect/server/ui-v2/assets/{python-input-DX4uV8MF.js → python-input-EKDBgTDE.js} +2 -2
- prefect/server/ui-v2/assets/{python-input-DX4uV8MF.js.map → python-input-EKDBgTDE.js.map} +1 -1
- prefect/server/ui-v2/assets/{radio-group-DeNK-Ob0.js → radio-group-BMeznoY8.js} +2 -2
- prefect/server/ui-v2/assets/{radio-group-DeNK-Ob0.js.map → radio-group-BMeznoY8.js.map} +1 -1
- prefect/server/ui-v2/assets/{route-error-state-C9CbByMx.js → route-error-state-BGOi4dn-.js} +2 -2
- prefect/server/ui-v2/assets/{route-error-state-C9CbByMx.js.map → route-error-state-BGOi4dn-.js.map} +1 -1
- prefect/server/ui-v2/assets/{schema-form-B45xnsnK.js → schema-form-BHWTZ8Sy.js} +2 -2
- prefect/server/ui-v2/assets/{schema-form-B45xnsnK.js.map → schema-form-BHWTZ8Sy.js.map} +1 -1
- prefect/server/ui-v2/assets/{schema-form-input-string-format-datetime-BJj81bu-.js → schema-form-input-string-format-datetime-hWIr4wQr.js} +4 -4
- 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
- prefect/server/ui-v2/assets/{settings-CbkeWqQ9.js → settings-DVSPGBen.js} +2 -2
- prefect/server/ui-v2/assets/{settings-CbkeWqQ9.js.map → settings-DVSPGBen.js.map} +1 -1
- prefect/server/ui-v2/assets/{sort-filter-DM7SvBB0.js → sort-filter-MiwMjIYc.js} +2 -2
- prefect/server/ui-v2/assets/{sort-filter-DM7SvBB0.js.map → sort-filter-MiwMjIYc.js.map} +1 -1
- prefect/server/ui-v2/assets/{table-CEPo6xb_.js → table-BJkw82b1.js} +2 -2
- prefect/server/ui-v2/assets/{table-CEPo6xb_.js.map → table-BJkw82b1.js.map} +1 -1
- prefect/server/ui-v2/assets/{tags-input-DJiZSVHp.js → tags-input-1KkWhGJZ.js} +2 -2
- prefect/server/ui-v2/assets/{tags-input-DJiZSVHp.js.map → tags-input-1KkWhGJZ.js.map} +1 -1
- prefect/server/ui-v2/assets/{task-run-concurrency-limits-reset-dialog-vJEkvqWp.js → task-run-concurrency-limits-reset-dialog-ClBlDic_.js} +2 -2
- 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
- prefect/server/ui-v2/assets/{task-run._id-BCnDnXrL.js → task-run._id-48TwMBP1.js} +2 -2
- prefect/server/ui-v2/assets/{task-run._id-BCnDnXrL.js.map → task-run._id-48TwMBP1.js.map} +1 -1
- prefect/server/ui-v2/assets/{task-run._id-ByJw1K82.js → task-run._id-BmK7HGNl.js} +2 -2
- prefect/server/ui-v2/assets/{task-run._id-ByJw1K82.js.map → task-run._id-BmK7HGNl.js.map} +1 -1
- prefect/server/ui-v2/assets/{task-runs-pagination-DbjB6XnY.js → task-runs-pagination-DA4izCRv.js} +2 -2
- prefect/server/ui-v2/assets/{task-runs-pagination-DbjB6XnY.js.map → task-runs-pagination-DA4izCRv.js.map} +1 -1
- prefect/server/ui-v2/assets/{textarea-BX8KbLGx.js → textarea-BqtBNhJB.js} +2 -2
- prefect/server/ui-v2/assets/{textarea-BX8KbLGx.js.map → textarea-BqtBNhJB.js.map} +1 -1
- prefect/server/ui-v2/assets/{timezone-select-TBj2n6Ox.js → timezone-select-BgD_PENN.js} +2 -2
- prefect/server/ui-v2/assets/{timezone-select-TBj2n6Ox.js.map → timezone-select-BgD_PENN.js.map} +1 -1
- prefect/server/ui-v2/assets/{toggle-group-D7mfSUim.js → toggle-group-ChA-JYQB.js} +2 -2
- prefect/server/ui-v2/assets/{toggle-group-D7mfSUim.js.map → toggle-group-ChA-JYQB.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-delete-automation-confirmation-dialog-BpBSjz60.js → use-delete-automation-confirmation-dialog-Da1iWNZ_.js} +2 -2
- prefect/server/ui-v2/assets/{use-delete-automation-confirmation-dialog-BpBSjz60.js.map → use-delete-automation-confirmation-dialog-Da1iWNZ_.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-delete-block-document-confirmation-dialog-BjKsNw6b.js → use-delete-block-document-confirmation-dialog-DfrxGSyY.js} +2 -2
- 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
- prefect/server/ui-v2/assets/{use-flow-runs-selected-rows-B_BlBMic.js → use-flow-runs-selected-rows-CYOsQ-Df.js} +2 -2
- 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
- prefect/server/ui-v2/assets/{use-get-artifacts-flow-task-runs-BfE433bC.js → use-get-artifacts-flow-task-runs-C0V3_gYz.js} +2 -2
- 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
- prefect/server/ui-v2/assets/{use-quick-run-qrtcF-DD.js → use-quick-run-CgM0EheE.js} +2 -2
- prefect/server/ui-v2/assets/{use-quick-run-qrtcF-DD.js.map → use-quick-run-CgM0EheE.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-stepper-CVy1_GBw.js → use-stepper-CSAs1T4U.js} +2 -2
- prefect/server/ui-v2/assets/{use-stepper-CVy1_GBw.js.map → use-stepper-CSAs1T4U.js.map} +1 -1
- prefect/server/ui-v2/assets/{utilities-D5-00wjP.js → utilities-DqaYuzh1.js} +2 -2
- prefect/server/ui-v2/assets/{utilities-D5-00wjP.js.map → utilities-DqaYuzh1.js.map} +1 -1
- prefect/server/ui-v2/assets/{vendor-date-7GxEwcQM.js → vendor-date-JS-ReHrg.js} +2 -2
- prefect/server/ui-v2/assets/{vendor-date-7GxEwcQM.js.map → vendor-date-JS-ReHrg.js.map} +1 -1
- prefect/server/ui-v2/assets/{vendor-graphs-R8z6S3Jg.js → vendor-graphs-BRVnayIy.js} +2 -2
- prefect/server/ui-v2/assets/{vendor-graphs-R8z6S3Jg.js.map → vendor-graphs-BRVnayIy.js.map} +1 -1
- prefect/server/ui-v2/assets/{vendor-radix-CznP1SOq.js → vendor-radix-BZJHvSaq.js} +9 -9
- prefect/server/ui-v2/assets/{vendor-radix-CznP1SOq.js.map → vendor-radix-BZJHvSaq.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool-filter-D8MkTbSW.js → work-pool-filter-T6dM4jKE.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool-filter-D8MkTbSW.js.map → work-pool-filter-T6dM4jKE.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool-queue-toggle-CbI7Gfu5.js → work-pool-queue-toggle-CdUw2mAH.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool-queue-toggle-CbI7Gfu5.js.map → work-pool-queue-toggle-CdUw2mAH.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool._workPoolName-CCzzgVV2.js → work-pool._workPoolName-DD8gh9X-.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool._workPoolName-CCzzgVV2.js.map → work-pool._workPoolName-DD8gh9X-.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.edit-BAgiuU_j.js → work-pool_._workPoolName.edit-mmnZTSTy.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.edit-BAgiuU_j.js.map → work-pool_._workPoolName.edit-mmnZTSTy.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-BUawWJEy.js → work-pool_._workPoolName.queue._workQueueName-fbwQqkVN.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-BUawWJEy.js.map → work-pool_._workPoolName.queue._workQueueName-fbwQqkVN.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-queue-icon-text-D_G_UCJX.js → work-queue-icon-text-YyKOH96e.js} +2 -2
- prefect/server/ui-v2/assets/{work-queue-icon-text-D_G_UCJX.js.map → work-queue-icon-text-YyKOH96e.js.map} +1 -1
- prefect/server/ui-v2/index.html +3 -3
- prefect/settings/models/runner.py +11 -0
- prefect/tasks.py +30 -4
- {prefect-3.6.14.dev1.dist-info → prefect-3.6.14.dev2.dist-info}/METADATA +1 -1
- {prefect-3.6.14.dev1.dist-info → prefect-3.6.14.dev2.dist-info}/RECORD +215 -215
- prefect/server/ui-v2/assets/cronstrue-WDy4rv2V.js +0 -2
- prefect/server/ui-v2/assets/cronstrue-WDy4rv2V.js.map +0 -1
- prefect/server/ui-v2/assets/deployment_._id.duplicate-C_kNyCA9.js +0 -2
- prefect/server/ui-v2/assets/deployment_._id.edit-BP8pfTOR.js +0 -2
- prefect/server/ui-v2/assets/flow-run-graph-B1vF1w0Z.js +0 -2
- prefect/server/ui-v2/assets/flow-run-graph-B1vF1w0Z.js.map +0 -1
- prefect/server/ui-v2/assets/flow-run._id-BHY61H6u.js +0 -4
- prefect/server/ui-v2/assets/flow-run._id-BHY61H6u.js.map +0 -1
- prefect/server/ui-v2/assets/index-BYQ1P2TC.js +0 -2
- prefect/server/ui-v2/assets/index-Cd7pM9pk.js +0 -17
- prefect/server/ui-v2/assets/index-Cd7pM9pk.js.map +0 -1
- prefect/server/ui-v2/assets/index-DvKYj8ko.js +0 -2
- prefect/server/ui-v2/assets/index-DvKYj8ko.js.map +0 -1
- {prefect-3.6.14.dev1.dist-info → prefect-3.6.14.dev2.dist-info}/WHEEL +0 -0
- {prefect-3.6.14.dev1.dist-info → prefect-3.6.14.dev2.dist-info}/entry_points.txt +0 -0
- {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.
|
|
3
|
-
__build_date__ = "2026-01-
|
|
4
|
-
__git_commit__ = "
|
|
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.
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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(
|
|
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."
|
prefect/deployments/runner.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
520
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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:
|