prefect 3.6.13__py3-none-any.whl → 3.6.13.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/_internal/compatibility/blocks.py +0 -18
- prefect/client/schemas/filters.py +0 -24
- prefect/flow_engine.py +10 -192
- prefect/flows.py +2 -61
- prefect/results.py +21 -262
- prefect/runner/runner.py +82 -29
- prefect/runner/storage.py +2 -3
- prefect/server/schemas/filters.py +0 -45
- prefect/server/ui-v2/assets/{artifact-card-CUEaRDGw.js → artifact-card-C8JEQRHl.js} +2 -2
- prefect/server/ui-v2/assets/{artifact-card-CUEaRDGw.js.map → artifact-card-C8JEQRHl.js.map} +1 -1
- prefect/server/ui-v2/assets/{artifact._id-Ca6VCUS0.js → artifact._id-67V8kTg9.js} +2 -2
- prefect/server/ui-v2/assets/{artifact._id-Ca6VCUS0.js.map → artifact._id-67V8kTg9.js.map} +1 -1
- prefect/server/ui-v2/assets/{automation-wizard-z26pICBl.js → automation-wizard-DuxZ47Nn.js} +2 -2
- prefect/server/ui-v2/assets/{automation-wizard-z26pICBl.js.map → automation-wizard-DuxZ47Nn.js.map} +1 -1
- prefect/server/ui-v2/assets/{automation._id-DuodjY5t.js → automation._id-D8S8r4Ji.js} +2 -2
- prefect/server/ui-v2/assets/{automation._id-DuodjY5t.js.map → automation._id-D8S8r4Ji.js.map} +1 -1
- prefect/server/ui-v2/assets/{automation_._id.edit-CcuJhc-y.js → automation_._id.edit-C1fOM0Hx.js} +2 -2
- prefect/server/ui-v2/assets/{automation_._id.edit-CcuJhc-y.js.map → automation_._id.edit-C1fOM0Hx.js.map} +1 -1
- prefect/server/ui-v2/assets/{automations-header-CgOWwuc6.js → automations-header-DrijTNGi.js} +2 -2
- prefect/server/ui-v2/assets/{automations-header-CgOWwuc6.js.map → automations-header-DrijTNGi.js.map} +1 -1
- prefect/server/ui-v2/assets/{base-job-template-form-section-DN9HyjM5.js → base-job-template-form-section-BDkqOkpe.js} +2 -2
- prefect/server/ui-v2/assets/{base-job-template-form-section-DN9HyjM5.js.map → base-job-template-form-section-BDkqOkpe.js.map} +1 -1
- prefect/server/ui-v2/assets/{block-type-details-rrToxL5r.js → block-type-details-CrFHWH5D.js} +2 -2
- prefect/server/ui-v2/assets/{block-type-details-rrToxL5r.js.map → block-type-details-CrFHWH5D.js.map} +1 -1
- prefect/server/ui-v2/assets/block-type-logo-WQNm0PzP.js +2 -0
- prefect/server/ui-v2/assets/{block-type-logo-DURScH8H.js.map → block-type-logo-WQNm0PzP.js.map} +1 -1
- prefect/server/ui-v2/assets/{block._id-Bue6lnrN.js → block._id-C3gN1Ae8.js} +2 -2
- prefect/server/ui-v2/assets/{block._id-Bue6lnrN.js.map → block._id-C3gN1Ae8.js.map} +1 -1
- prefect/server/ui-v2/assets/{block_._id.edit-QY0OzN_b.js → block_._id.edit-DzPuSvat.js} +2 -2
- prefect/server/ui-v2/assets/{block_._id.edit-QY0OzN_b.js.map → block_._id.edit-DzPuSvat.js.map} +1 -1
- prefect/server/ui-v2/assets/{catalog-CGjRGWv6.js → catalog-T-7MqsyE.js} +2 -2
- prefect/server/ui-v2/assets/{catalog-CGjRGWv6.js.map → catalog-T-7MqsyE.js.map} +1 -1
- prefect/server/ui-v2/assets/{catalog_._slug-BMFNZhEb.js → catalog_._slug-DMOI61fc.js} +2 -2
- prefect/server/ui-v2/assets/{catalog_._slug-BMFNZhEb.js.map → catalog_._slug-DMOI61fc.js.map} +1 -1
- prefect/server/ui-v2/assets/{catalog_._slug_.create-CuFA3kUC.js → catalog_._slug_.create-l9bGUPi-.js} +2 -2
- prefect/server/ui-v2/assets/{catalog_._slug_.create-CuFA3kUC.js.map → catalog_._slug_.create-l9bGUPi-.js.map} +1 -1
- prefect/server/ui-v2/assets/{collapsible-Ell2FjrX.js → collapsible-DM-Ze5-Y.js} +2 -2
- prefect/server/ui-v2/assets/{collapsible-Ell2FjrX.js.map → collapsible-DM-Ze5-Y.js.map} +1 -1
- prefect/server/ui-v2/assets/{concurrency-limit._id-Rr1ysUct.js → concurrency-limit._id-BHEt9viF.js} +2 -2
- prefect/server/ui-v2/assets/{concurrency-limit._id-Rr1ysUct.js.map → concurrency-limit._id-BHEt9viF.js.map} +1 -1
- prefect/server/ui-v2/assets/{create-ewA3uq_h.js → create-BMxUOyN5.js} +2 -2
- prefect/server/ui-v2/assets/{create-ewA3uq_h.js.map → create-BMxUOyN5.js.map} +1 -1
- prefect/server/ui-v2/assets/{create-CQK8-uO1.js → create-Cq-EAHVk.js} +2 -2
- prefect/server/ui-v2/assets/{create-CQK8-uO1.js.map → create-Cq-EAHVk.js.map} +1 -1
- prefect/server/ui-v2/assets/{data-table-Bx1uYX_M.js → data-table-Bw8TZx9D.js} +2 -2
- prefect/server/ui-v2/assets/{data-table-Bx1uYX_M.js.map → data-table-Bw8TZx9D.js.map} +1 -1
- prefect/server/ui-v2/assets/{delete-confirmation-dialog-CqKsUEj_.js → delete-confirmation-dialog-CTdWZlGM.js} +2 -2
- prefect/server/ui-v2/assets/{delete-confirmation-dialog-CqKsUEj_.js.map → delete-confirmation-dialog-CTdWZlGM.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-action-header-Bz5COdER.js → deployment-action-header-BObYqmI_.js} +2 -2
- prefect/server/ui-v2/assets/{deployment-action-header-Bz5COdER.js.map → deployment-action-header-BObYqmI_.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-form-CrlZlNoj.js → deployment-form-CntyhRrK.js} +3 -3
- prefect/server/ui-v2/assets/{deployment-form-CrlZlNoj.js.map → deployment-form-CntyhRrK.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-links-D9ZR_vmp.js → deployment-links-DBUSCaxB.js} +2 -2
- prefect/server/ui-v2/assets/{deployment-links-D9ZR_vmp.js.map → deployment-links-DBUSCaxB.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment._id-DvHhx-qN.js → deployment._id-BXanVFA8.js} +2 -2
- prefect/server/ui-v2/assets/{deployment._id-DvHhx-qN.js.map → deployment._id-BXanVFA8.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment._id-BV0rSqba.js → deployment._id-Q5cvHMX9.js} +2 -2
- prefect/server/ui-v2/assets/{deployment._id-BV0rSqba.js.map → deployment._id-Q5cvHMX9.js.map} +1 -1
- prefect/server/ui-v2/assets/deployment_._id.duplicate-DB-4hHHc.js +2 -0
- prefect/server/ui-v2/assets/{deployment_._id.duplicate-BrEOenqP.js.map → deployment_._id.duplicate-DB-4hHHc.js.map} +1 -1
- prefect/server/ui-v2/assets/deployment_._id.edit-CWf1RIGy.js +2 -0
- prefect/server/ui-v2/assets/{deployment_._id.edit-BbYKPK42.js.map → deployment_._id.edit-CWf1RIGy.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment_._id.run-Dv7S_MJR.js → deployment_._id.run-CoHGXFoM.js} +2 -2
- prefect/server/ui-v2/assets/{deployment_._id.run-Dv7S_MJR.js.map → deployment_._id.run-CoHGXFoM.js.map} +1 -1
- prefect/server/ui-v2/assets/{dropdown-menu-e0Fqb6aw.js → dropdown-menu-CT-s-V7d.js} +2 -2
- prefect/server/ui-v2/assets/{dropdown-menu-e0Fqb6aw.js.map → dropdown-menu-CT-s-V7d.js.map} +1 -1
- prefect/server/ui-v2/assets/{event._eventDate._eventId-Cp4UmGqq.js → event._eventDate._eventId-BITo_GYL.js} +2 -2
- prefect/server/ui-v2/assets/{event._eventDate._eventId-Cp4UmGqq.js.map → event._eventDate._eventId-BITo_GYL.js.map} +1 -1
- prefect/server/ui-v2/assets/flow-run-graph-DxIl6fzW.js +2 -0
- prefect/server/ui-v2/assets/{flow-run-graph-BrqoR3E2.js.map → flow-run-graph-DxIl6fzW.js.map} +1 -1
- prefect/server/ui-v2/assets/{flow-run._id-BOp38Pbq.js → flow-run._id-Bbm9OpDi.js} +2 -2
- prefect/server/ui-v2/assets/{flow-run._id-BOp38Pbq.js.map → flow-run._id-Bbm9OpDi.js.map} +1 -1
- prefect/server/ui-v2/assets/{flow-run._id-DJnTDEN_.js → flow-run._id-BtSgRDtA.js} +2 -2
- prefect/server/ui-v2/assets/{flow-run._id-DJnTDEN_.js.map → flow-run._id-BtSgRDtA.js.map} +1 -1
- prefect/server/ui-v2/assets/flow-run._id-DJuMECRh.js +4 -0
- prefect/server/ui-v2/assets/flow-run._id-DJuMECRh.js.map +1 -0
- prefect/server/ui-v2/assets/{flow-runs-pagination-Bq2ZUzM6.js → flow-runs-pagination-LrU9Aio8.js} +2 -2
- prefect/server/ui-v2/assets/{flow-runs-pagination-Bq2ZUzM6.js.map → flow-runs-pagination-LrU9Aio8.js.map} +1 -1
- prefect/server/ui-v2/assets/{flow._id-eCBL95rg.js → flow._id-BJBRokk4.js} +2 -2
- prefect/server/ui-v2/assets/{flow._id-eCBL95rg.js.map → flow._id-BJBRokk4.js.map} +1 -1
- prefect/server/ui-v2/assets/{form-DNerk3LS.js → form-CVSlEnl8.js} +2 -2
- prefect/server/ui-v2/assets/{form-DNerk3LS.js.map → form-CVSlEnl8.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-DwagHBlF.js → header-4plZZheZ.js} +2 -2
- prefect/server/ui-v2/assets/{header-DwagHBlF.js.map → header-4plZZheZ.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-huSvwxKI.js → header-9rXZ4r39.js} +2 -2
- prefect/server/ui-v2/assets/{header-huSvwxKI.js.map → header-9rXZ4r39.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-B0ejRncu.js → header-D3uM8_xM.js} +2 -2
- prefect/server/ui-v2/assets/{header-B0ejRncu.js.map → header-D3uM8_xM.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-DI2DC5gd.js → index-6OsEYhIi.js} +2 -2
- prefect/server/ui-v2/assets/{index-DI2DC5gd.js.map → index-6OsEYhIi.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-BzN9bQeM.js → index-B7xsJ-QH.js} +2 -2
- prefect/server/ui-v2/assets/{index-BzN9bQeM.js.map → index-B7xsJ-QH.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CnIJUujl.js → index-BeoNC2tJ.js} +2 -2
- prefect/server/ui-v2/assets/{index-CnIJUujl.js.map → index-BeoNC2tJ.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CTnoa3Ho.js → index-BfJLUM7N.js} +2 -2
- prefect/server/ui-v2/assets/{index-CTnoa3Ho.js.map → index-BfJLUM7N.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CWkbSdxY.js → index-Bj0OOguP.js} +2 -2
- prefect/server/ui-v2/assets/{index-CWkbSdxY.js.map → index-Bj0OOguP.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CBhi1P9g.js → index-BlpD74iH.js} +2 -2
- prefect/server/ui-v2/assets/{index-CBhi1P9g.js.map → index-BlpD74iH.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-D5RdrxkU.js → index-BvSl3DKP.js} +8 -8
- prefect/server/ui-v2/assets/index-BvSl3DKP.js.map +1 -0
- prefect/server/ui-v2/assets/{index-BU4yZRd3.js → index-CDwJvjUM.js} +2 -2
- prefect/server/ui-v2/assets/{index-BU4yZRd3.js.map → index-CDwJvjUM.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CRDz4nhM.js → index-CDyLkbVG.js} +2 -2
- prefect/server/ui-v2/assets/{index-CRDz4nhM.js.map → index-CDyLkbVG.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-Cutg_A1j.js → index-ChIrfjIW.js} +2 -2
- prefect/server/ui-v2/assets/{index-Cutg_A1j.js.map → index-ChIrfjIW.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-BhALpenO.js → index-CzCSgCK-.js} +2 -2
- prefect/server/ui-v2/assets/{index-BhALpenO.js.map → index-CzCSgCK-.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-Dg_duvDx.js → index-D5v9S-lB.js} +2 -2
- prefect/server/ui-v2/assets/{index-Dg_duvDx.js.map → index-D5v9S-lB.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-DDiyFpIV.js → index-D6GJ4go1.js} +2 -2
- prefect/server/ui-v2/assets/{index-DDiyFpIV.js.map → index-D6GJ4go1.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-dSUEBAqg.js → index-DJyKqsFO.js} +2 -2
- prefect/server/ui-v2/assets/{index-dSUEBAqg.js.map → index-DJyKqsFO.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-BO3SOwdV.js → index-DODEq1Pi.js} +2 -2
- prefect/server/ui-v2/assets/{index-BO3SOwdV.js.map → index-DODEq1Pi.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-Dspw5HFj.js → index-DicK6p3K.js} +2 -2
- prefect/server/ui-v2/assets/{index-Dspw5HFj.js.map → index-DicK6p3K.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-VOOLxiSE.js → index-DqCPbST9.js} +2 -2
- prefect/server/ui-v2/assets/{index-VOOLxiSE.js.map → index-DqCPbST9.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-BTPE3vs7.js → index-DxPoKag8.js} +2 -2
- prefect/server/ui-v2/assets/{index-BTPE3vs7.js.map → index-DxPoKag8.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-Wfs7Cjew.js → index-h9-QgNjZ.js} +2 -2
- prefect/server/ui-v2/assets/{index-Wfs7Cjew.js.map → index-h9-QgNjZ.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-uvH5a3zO.js → index-tBdv6kBF.js} +2 -2
- prefect/server/ui-v2/assets/{index-uvH5a3zO.js.map → index-tBdv6kBF.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CgOsOj5t.js → index-udb79rgq.js} +2 -2
- prefect/server/ui-v2/assets/{index-CgOsOj5t.js.map → index-udb79rgq.js.map} +1 -1
- prefect/server/ui-v2/assets/{json-input-gXz7BuJj.js → json-input-Dt1icmrn.js} +2 -2
- prefect/server/ui-v2/assets/{json-input-gXz7BuJj.js.map → json-input-Dt1icmrn.js.map} +1 -1
- prefect/server/ui-v2/assets/{key._key-CJPbLXwU.js → key._key-Cyh5MBX_.js} +2 -2
- prefect/server/ui-v2/assets/{key._key-CJPbLXwU.js.map → key._key-Cyh5MBX_.js.map} +1 -1
- prefect/server/ui-v2/assets/{lazy-markdown-wPid80zf.js → lazy-markdown-BwIwKFRF.js} +2 -2
- prefect/server/ui-v2/assets/{lazy-markdown-wPid80zf.js.map → lazy-markdown-BwIwKFRF.js.map} +1 -1
- prefect/server/ui-v2/assets/{login-DC63bGXK.js → login-DKXFVSwk.js} +2 -2
- prefect/server/ui-v2/assets/{login-DC63bGXK.js.map → login-DKXFVSwk.js.map} +1 -1
- prefect/server/ui-v2/assets/{markdown-input-BhqrU6Eo.js → markdown-input-Dp0mBlkV.js} +2 -2
- prefect/server/ui-v2/assets/{markdown-input-BhqrU6Eo.js.map → markdown-input-Dp0mBlkV.js.map} +1 -1
- prefect/server/ui-v2/assets/{python-example-snippet-3jtXWQZN.js → python-example-snippet-BYwPjHI5.js} +3 -3
- prefect/server/ui-v2/assets/{python-example-snippet-3jtXWQZN.js.map → python-example-snippet-BYwPjHI5.js.map} +1 -1
- prefect/server/ui-v2/assets/{python-input-ZVi-v324.js → python-input-y26XMqXw.js} +2 -2
- prefect/server/ui-v2/assets/{python-input-ZVi-v324.js.map → python-input-y26XMqXw.js.map} +1 -1
- prefect/server/ui-v2/assets/{radio-group-BBMLpHGc.js → radio-group-D0van45v.js} +2 -2
- prefect/server/ui-v2/assets/{radio-group-BBMLpHGc.js.map → radio-group-D0van45v.js.map} +1 -1
- prefect/server/ui-v2/assets/{route-error-state-BJXl8qkX.js → route-error-state-CGGpuCGN.js} +2 -2
- prefect/server/ui-v2/assets/{route-error-state-BJXl8qkX.js.map → route-error-state-CGGpuCGN.js.map} +1 -1
- prefect/server/ui-v2/assets/{schema-form-BEqYjsM-.js → schema-form-2tg5SXM4.js} +2 -2
- prefect/server/ui-v2/assets/{schema-form-BEqYjsM-.js.map → schema-form-2tg5SXM4.js.map} +1 -1
- prefect/server/ui-v2/assets/{schema-form-input-string-format-datetime-l3xt3PWf.js → schema-form-input-string-format-datetime-CZt6AJ4z.js} +4 -4
- prefect/server/ui-v2/assets/{schema-form-input-string-format-datetime-l3xt3PWf.js.map → schema-form-input-string-format-datetime-CZt6AJ4z.js.map} +1 -1
- prefect/server/ui-v2/assets/{settings-txD0dR5Q.js → settings-DDUadk_N.js} +2 -2
- prefect/server/ui-v2/assets/{settings-txD0dR5Q.js.map → settings-DDUadk_N.js.map} +1 -1
- prefect/server/ui-v2/assets/{sort-filter-DHPFdKZ2.js → sort-filter-D9p3cPx9.js} +2 -2
- prefect/server/ui-v2/assets/{sort-filter-DHPFdKZ2.js.map → sort-filter-D9p3cPx9.js.map} +1 -1
- prefect/server/ui-v2/assets/{table-ULfpXJXB.js → table-vo9Do8sA.js} +2 -2
- prefect/server/ui-v2/assets/{table-ULfpXJXB.js.map → table-vo9Do8sA.js.map} +1 -1
- prefect/server/ui-v2/assets/{tags-input-BLzMOTDb.js → tags-input-Ci2JQ-k3.js} +2 -2
- prefect/server/ui-v2/assets/{tags-input-BLzMOTDb.js.map → tags-input-Ci2JQ-k3.js.map} +1 -1
- prefect/server/ui-v2/assets/{task-run-concurrency-limits-reset-dialog-BpeKHk7g.js → task-run-concurrency-limits-reset-dialog-CJzPc2gw.js} +2 -2
- prefect/server/ui-v2/assets/{task-run-concurrency-limits-reset-dialog-BpeKHk7g.js.map → task-run-concurrency-limits-reset-dialog-CJzPc2gw.js.map} +1 -1
- prefect/server/ui-v2/assets/{task-run._id-CkOl9MJs.js → task-run._id-CjevSs79.js} +2 -2
- prefect/server/ui-v2/assets/{task-run._id-CkOl9MJs.js.map → task-run._id-CjevSs79.js.map} +1 -1
- prefect/server/ui-v2/assets/{task-run._id-udkz1lhh.js → task-run._id-D8QKG5UZ.js} +2 -2
- prefect/server/ui-v2/assets/{task-run._id-udkz1lhh.js.map → task-run._id-D8QKG5UZ.js.map} +1 -1
- prefect/server/ui-v2/assets/{task-runs-pagination-B7D5K_FM.js → task-runs-pagination-CifoSGct.js} +2 -2
- prefect/server/ui-v2/assets/{task-runs-pagination-B7D5K_FM.js.map → task-runs-pagination-CifoSGct.js.map} +1 -1
- prefect/server/ui-v2/assets/{textarea-C4bdj7Jk.js → textarea-BAtfAxtV.js} +2 -2
- prefect/server/ui-v2/assets/{textarea-C4bdj7Jk.js.map → textarea-BAtfAxtV.js.map} +1 -1
- prefect/server/ui-v2/assets/{timezone-select-AdlSRQxZ.js → timezone-select-QlQTZSsF.js} +2 -2
- prefect/server/ui-v2/assets/{timezone-select-AdlSRQxZ.js.map → timezone-select-QlQTZSsF.js.map} +1 -1
- prefect/server/ui-v2/assets/{toggle-group-C-vxYz4l.js → toggle-group-BJN1vjEh.js} +2 -2
- prefect/server/ui-v2/assets/{toggle-group-C-vxYz4l.js.map → toggle-group-BJN1vjEh.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-delete-automation-confirmation-dialog-Cqhaqtqe.js → use-delete-automation-confirmation-dialog-eOWJYPkD.js} +2 -2
- prefect/server/ui-v2/assets/{use-delete-automation-confirmation-dialog-Cqhaqtqe.js.map → use-delete-automation-confirmation-dialog-eOWJYPkD.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-delete-block-document-confirmation-dialog-GjNhFxZe.js → use-delete-block-document-confirmation-dialog-BTwSeHRM.js} +2 -2
- prefect/server/ui-v2/assets/{use-delete-block-document-confirmation-dialog-GjNhFxZe.js.map → use-delete-block-document-confirmation-dialog-BTwSeHRM.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-flow-runs-selected-rows-DfwmswyR.js → use-flow-runs-selected-rows-BL_Gv9CC.js} +2 -2
- prefect/server/ui-v2/assets/{use-flow-runs-selected-rows-DfwmswyR.js.map → use-flow-runs-selected-rows-BL_Gv9CC.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-get-artifacts-flow-task-runs-BEBpG_5J.js → use-get-artifacts-flow-task-runs-TSCoomjQ.js} +2 -2
- prefect/server/ui-v2/assets/{use-get-artifacts-flow-task-runs-BEBpG_5J.js.map → use-get-artifacts-flow-task-runs-TSCoomjQ.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-quick-run-BYBRcDwC.js → use-quick-run-BMnCkwSv.js} +2 -2
- prefect/server/ui-v2/assets/{use-quick-run-BYBRcDwC.js.map → use-quick-run-BMnCkwSv.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-stepper-Bk97vOTm.js → use-stepper-C1wm66U2.js} +2 -2
- prefect/server/ui-v2/assets/{use-stepper-Bk97vOTm.js.map → use-stepper-C1wm66U2.js.map} +1 -1
- prefect/server/ui-v2/assets/{utilities-BQwGFLk5.js → utilities-DxRXxFOF.js} +2 -2
- prefect/server/ui-v2/assets/{utilities-BQwGFLk5.js.map → utilities-DxRXxFOF.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool-filter-Ddhp_M-L.js → work-pool-filter-PudrkZYj.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool-filter-Ddhp_M-L.js.map → work-pool-filter-PudrkZYj.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool-queue-toggle-DX3eV3R_.js → work-pool-queue-toggle-BAOrV_0R.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool-queue-toggle-DX3eV3R_.js.map → work-pool-queue-toggle-BAOrV_0R.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool._workPoolName-BaRIsXBX.js → work-pool._workPoolName-BOM3849e.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool._workPoolName-BaRIsXBX.js.map → work-pool._workPoolName-BOM3849e.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.edit-dhS_Xz32.js → work-pool_._workPoolName.edit-CIhcG6yr.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.edit-dhS_Xz32.js.map → work-pool_._workPoolName.edit-CIhcG6yr.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-BlstL9Se.js → work-pool_._workPoolName.queue._workQueueName-CjoM77tu.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-BlstL9Se.js.map → work-pool_._workPoolName.queue._workQueueName-CjoM77tu.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-queue-icon-text-_Ez8e2dw.js → work-queue-icon-text-CP4yX3uM.js} +2 -2
- prefect/server/ui-v2/assets/{work-queue-icon-text-_Ez8e2dw.js.map → work-queue-icon-text-CP4yX3uM.js.map} +1 -1
- prefect/server/ui-v2/index.html +1 -1
- prefect/settings/models/flows.py +1 -14
- prefect/settings/models/runner.py +6 -16
- prefect/task_engine.py +3 -1
- prefect/task_worker.py +1 -1
- prefect/tasks.py +2 -2
- prefect/testing/fixtures.py +5 -23
- {prefect-3.6.13.dist-info → prefect-3.6.13.dev2.dist-info}/METADATA +1 -1
- {prefect-3.6.13.dist-info → prefect-3.6.13.dev2.dist-info}/RECORD +213 -213
- prefect/server/ui-v2/assets/block-type-logo-DURScH8H.js +0 -2
- prefect/server/ui-v2/assets/deployment_._id.duplicate-BrEOenqP.js +0 -2
- prefect/server/ui-v2/assets/deployment_._id.edit-BbYKPK42.js +0 -2
- prefect/server/ui-v2/assets/flow-run-graph-BrqoR3E2.js +0 -2
- prefect/server/ui-v2/assets/flow-run._id-D_wY_rBe.js +0 -4
- prefect/server/ui-v2/assets/flow-run._id-D_wY_rBe.js.map +0 -1
- prefect/server/ui-v2/assets/index-D5RdrxkU.js.map +0 -1
- {prefect-3.6.13.dist-info → prefect-3.6.13.dev2.dist-info}/WHEEL +0 -0
- {prefect-3.6.13.dist-info → prefect-3.6.13.dev2.dist-info}/entry_points.txt +0 -0
- {prefect-3.6.13.dist-info → prefect-3.6.13.dev2.dist-info}/licenses/LICENSE +0 -0
prefect/results.py
CHANGED
|
@@ -33,10 +33,7 @@ from typing_extensions import ParamSpec, Self
|
|
|
33
33
|
import prefect
|
|
34
34
|
import prefect.types._datetime
|
|
35
35
|
from prefect._internal.compatibility.async_dispatch import async_dispatch
|
|
36
|
-
from prefect._internal.compatibility.blocks import
|
|
37
|
-
call_explicitly_async_block_method,
|
|
38
|
-
call_explicitly_sync_block_method,
|
|
39
|
-
)
|
|
36
|
+
from prefect._internal.compatibility.blocks import call_explicitly_async_block_method
|
|
40
37
|
from prefect._internal.concurrency.event_loop import get_running_loop
|
|
41
38
|
from prefect._result_records import R, ResultRecord, ResultRecordMetadata
|
|
42
39
|
from prefect.blocks.core import Block
|
|
@@ -55,6 +52,7 @@ from prefect.serializers import Serializer
|
|
|
55
52
|
from prefect.settings.context import get_current_settings
|
|
56
53
|
from prefect.types import DateTime
|
|
57
54
|
from prefect.utilities.annotations import NotSet
|
|
55
|
+
from prefect.utilities.asyncutils import sync_compatible
|
|
58
56
|
|
|
59
57
|
if TYPE_CHECKING:
|
|
60
58
|
import logging
|
|
@@ -371,7 +369,8 @@ class ResultStore(BaseModel):
|
|
|
371
369
|
result = await store.aread(metadata.storage_key)
|
|
372
370
|
return result
|
|
373
371
|
|
|
374
|
-
|
|
372
|
+
@sync_compatible
|
|
373
|
+
async def update_for_flow(self, flow: "Flow[..., Any]") -> Self:
|
|
375
374
|
"""
|
|
376
375
|
Create a new result store for a flow with updated settings.
|
|
377
376
|
|
|
@@ -394,31 +393,8 @@ class ResultStore(BaseModel):
|
|
|
394
393
|
update["metadata_storage"] = NullFileSystem()
|
|
395
394
|
return self.model_copy(update=update)
|
|
396
395
|
|
|
397
|
-
@
|
|
398
|
-
def
|
|
399
|
-
"""
|
|
400
|
-
Create a new result store for a flow with updated settings.
|
|
401
|
-
|
|
402
|
-
Args:
|
|
403
|
-
flow: The flow to update the result store for.
|
|
404
|
-
|
|
405
|
-
Returns:
|
|
406
|
-
An updated result store.
|
|
407
|
-
"""
|
|
408
|
-
update: dict[str, Any] = {}
|
|
409
|
-
update["cache_result_in_memory"] = flow.cache_result_in_memory
|
|
410
|
-
if flow.result_storage is not None:
|
|
411
|
-
update["result_storage"] = resolve_result_storage(
|
|
412
|
-
flow.result_storage, _sync=True
|
|
413
|
-
)
|
|
414
|
-
if flow.result_serializer is not None:
|
|
415
|
-
update["serializer"] = resolve_serializer(flow.result_serializer)
|
|
416
|
-
if self.result_storage is None and update.get("result_storage") is None:
|
|
417
|
-
update["result_storage"] = get_default_result_storage(_sync=True)
|
|
418
|
-
update["metadata_storage"] = NullFileSystem()
|
|
419
|
-
return self.model_copy(update=update)
|
|
420
|
-
|
|
421
|
-
async def aupdate_for_task(self: Self, task: "Task[P, R]") -> Self:
|
|
396
|
+
@sync_compatible
|
|
397
|
+
async def update_for_task(self: Self, task: "Task[P, R]") -> Self:
|
|
422
398
|
"""
|
|
423
399
|
Create a new result store for a task.
|
|
424
400
|
|
|
@@ -470,59 +446,6 @@ class ResultStore(BaseModel):
|
|
|
470
446
|
update["metadata_storage"] = None
|
|
471
447
|
return self.model_copy(update=update)
|
|
472
448
|
|
|
473
|
-
@async_dispatch(aupdate_for_task)
|
|
474
|
-
def update_for_task(self: Self, task: "Task[P, R]") -> Self:
|
|
475
|
-
"""
|
|
476
|
-
Create a new result store for a task.
|
|
477
|
-
|
|
478
|
-
Args:
|
|
479
|
-
task: The task to update the result store for.
|
|
480
|
-
|
|
481
|
-
Returns:
|
|
482
|
-
An updated result store.
|
|
483
|
-
"""
|
|
484
|
-
from prefect.transactions import get_transaction
|
|
485
|
-
|
|
486
|
-
update: dict[str, Any] = {}
|
|
487
|
-
update["cache_result_in_memory"] = task.cache_result_in_memory
|
|
488
|
-
if task.result_storage is not None:
|
|
489
|
-
update["result_storage"] = resolve_result_storage(
|
|
490
|
-
task.result_storage, _sync=True
|
|
491
|
-
)
|
|
492
|
-
if task.result_serializer is not None:
|
|
493
|
-
update["serializer"] = resolve_serializer(task.result_serializer)
|
|
494
|
-
if task.result_storage_key is not None:
|
|
495
|
-
update["storage_key_fn"] = partial(
|
|
496
|
-
_format_user_supplied_storage_key, task.result_storage_key
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
# use the lock manager from a parent transaction if it exists
|
|
500
|
-
if (current_txn := get_transaction()) and isinstance(
|
|
501
|
-
current_txn.store, ResultStore
|
|
502
|
-
):
|
|
503
|
-
update["lock_manager"] = current_txn.store.lock_manager
|
|
504
|
-
|
|
505
|
-
from prefect.cache_policies import CachePolicy
|
|
506
|
-
|
|
507
|
-
if isinstance(task.cache_policy, CachePolicy):
|
|
508
|
-
if task.cache_policy.key_storage is not None:
|
|
509
|
-
storage = task.cache_policy.key_storage
|
|
510
|
-
if isinstance(storage, str) and not len(storage.split("/")) == 2:
|
|
511
|
-
storage = Path(storage)
|
|
512
|
-
update["metadata_storage"] = resolve_result_storage(storage, _sync=True)
|
|
513
|
-
# if the cache policy has a lock manager, it takes precedence over the parent transaction
|
|
514
|
-
if task.cache_policy.lock_manager is not None:
|
|
515
|
-
update["lock_manager"] = task.cache_policy.lock_manager
|
|
516
|
-
|
|
517
|
-
if self.result_storage is None and update.get("result_storage") is None:
|
|
518
|
-
update["result_storage"] = get_default_result_storage(_sync=True)
|
|
519
|
-
if (
|
|
520
|
-
isinstance(self.metadata_storage, NullFileSystem)
|
|
521
|
-
and update.get("metadata_storage", NotSet) is NotSet
|
|
522
|
-
):
|
|
523
|
-
update["metadata_storage"] = None
|
|
524
|
-
return self.model_copy(update=update)
|
|
525
|
-
|
|
526
449
|
@staticmethod
|
|
527
450
|
def generate_default_holder() -> str:
|
|
528
451
|
"""
|
|
@@ -544,7 +467,8 @@ class ResultStore(BaseModel):
|
|
|
544
467
|
return f"{hostname}:{pid}:{thread_id}:{thread_name}:{id(current_task)}"
|
|
545
468
|
return f"{hostname}:{pid}:{thread_id}:{thread_name}"
|
|
546
469
|
|
|
547
|
-
|
|
470
|
+
@sync_compatible
|
|
471
|
+
async def _exists(self, key: str) -> bool:
|
|
548
472
|
"""
|
|
549
473
|
Check if a result record exists in storage.
|
|
550
474
|
|
|
@@ -587,49 +511,6 @@ class ResultStore(BaseModel):
|
|
|
587
511
|
exists = True
|
|
588
512
|
return exists
|
|
589
513
|
|
|
590
|
-
def _exists(self, key: str) -> bool:
|
|
591
|
-
"""
|
|
592
|
-
Check if a result record exists in storage.
|
|
593
|
-
|
|
594
|
-
Args:
|
|
595
|
-
key: The key to check for the existence of a result record.
|
|
596
|
-
|
|
597
|
-
Returns:
|
|
598
|
-
bool: True if the result record exists, False otherwise.
|
|
599
|
-
"""
|
|
600
|
-
if self.metadata_storage is not None:
|
|
601
|
-
# TODO: Add an `exists` method to commonly used storage blocks
|
|
602
|
-
# so the entire payload doesn't need to be read
|
|
603
|
-
try:
|
|
604
|
-
metadata_content = call_explicitly_sync_block_method(
|
|
605
|
-
self.metadata_storage, "read_path", (key,), {}
|
|
606
|
-
)
|
|
607
|
-
if metadata_content is None:
|
|
608
|
-
return False
|
|
609
|
-
metadata = ResultRecordMetadata.load_bytes(metadata_content)
|
|
610
|
-
|
|
611
|
-
except Exception:
|
|
612
|
-
return False
|
|
613
|
-
else:
|
|
614
|
-
try:
|
|
615
|
-
content = call_explicitly_sync_block_method(
|
|
616
|
-
self.result_storage, "read_path", (key,), {}
|
|
617
|
-
)
|
|
618
|
-
if content is None:
|
|
619
|
-
return False
|
|
620
|
-
record: ResultRecord[Any] = ResultRecord.deserialize(content)
|
|
621
|
-
metadata = record.metadata
|
|
622
|
-
except Exception:
|
|
623
|
-
return False
|
|
624
|
-
|
|
625
|
-
if metadata.expiration:
|
|
626
|
-
# if the result has an expiration,
|
|
627
|
-
# check if it is still in the future
|
|
628
|
-
exists = metadata.expiration > prefect.types._datetime.now("UTC")
|
|
629
|
-
else:
|
|
630
|
-
exists = True
|
|
631
|
-
return exists
|
|
632
|
-
|
|
633
514
|
def exists(self, key: str) -> bool:
|
|
634
515
|
"""
|
|
635
516
|
Check if a result record exists in storage.
|
|
@@ -640,7 +521,7 @@ class ResultStore(BaseModel):
|
|
|
640
521
|
Returns:
|
|
641
522
|
bool: True if the result record exists, False otherwise.
|
|
642
523
|
"""
|
|
643
|
-
return self._exists(key=key)
|
|
524
|
+
return self._exists(key=key, _sync=True)
|
|
644
525
|
|
|
645
526
|
async def aexists(self, key: str) -> bool:
|
|
646
527
|
"""
|
|
@@ -652,7 +533,7 @@ class ResultStore(BaseModel):
|
|
|
652
533
|
Returns:
|
|
653
534
|
bool: True if the result record exists, False otherwise.
|
|
654
535
|
"""
|
|
655
|
-
return await self.
|
|
536
|
+
return await self._exists(key=key, _sync=False)
|
|
656
537
|
|
|
657
538
|
def _resolved_key_path(self, key: str) -> str:
|
|
658
539
|
if self.result_storage_block_id is None and (
|
|
@@ -665,7 +546,8 @@ class ResultStore(BaseModel):
|
|
|
665
546
|
return key
|
|
666
547
|
return key
|
|
667
548
|
|
|
668
|
-
|
|
549
|
+
@sync_compatible
|
|
550
|
+
async def _read(self, key: str, holder: str) -> "ResultRecord[Any]":
|
|
669
551
|
"""
|
|
670
552
|
Read a result record from storage.
|
|
671
553
|
|
|
@@ -728,69 +610,6 @@ class ResultStore(BaseModel):
|
|
|
728
610
|
self.cache[resolved_key_path] = result_record
|
|
729
611
|
return result_record
|
|
730
612
|
|
|
731
|
-
def _read(self, key: str, holder: str) -> "ResultRecord[Any]":
|
|
732
|
-
"""
|
|
733
|
-
Read a result record from storage.
|
|
734
|
-
|
|
735
|
-
This is the internal implementation. Use `read` or `aread` for synchronous and
|
|
736
|
-
asynchronous result reading respectively.
|
|
737
|
-
|
|
738
|
-
Args:
|
|
739
|
-
key: The key to read the result record from.
|
|
740
|
-
holder: The holder of the lock if a lock was set on the record.
|
|
741
|
-
|
|
742
|
-
Returns:
|
|
743
|
-
A result record.
|
|
744
|
-
"""
|
|
745
|
-
|
|
746
|
-
if self.lock_manager is not None and not self.is_lock_holder(key, holder):
|
|
747
|
-
self.wait_for_lock(key)
|
|
748
|
-
|
|
749
|
-
resolved_key_path = self._resolved_key_path(key)
|
|
750
|
-
|
|
751
|
-
if resolved_key_path in self.cache:
|
|
752
|
-
return self.cache[resolved_key_path]
|
|
753
|
-
|
|
754
|
-
if self.result_storage is None:
|
|
755
|
-
self.result_storage = get_default_result_storage(_sync=True)
|
|
756
|
-
|
|
757
|
-
if self.metadata_storage is not None:
|
|
758
|
-
metadata_content = call_explicitly_sync_block_method(
|
|
759
|
-
self.metadata_storage,
|
|
760
|
-
"read_path",
|
|
761
|
-
(key,),
|
|
762
|
-
{},
|
|
763
|
-
)
|
|
764
|
-
metadata = ResultRecordMetadata.load_bytes(metadata_content)
|
|
765
|
-
assert metadata.storage_key is not None, (
|
|
766
|
-
"Did not find storage key in metadata"
|
|
767
|
-
)
|
|
768
|
-
result_content = call_explicitly_sync_block_method(
|
|
769
|
-
self.result_storage,
|
|
770
|
-
"read_path",
|
|
771
|
-
(metadata.storage_key,),
|
|
772
|
-
{},
|
|
773
|
-
)
|
|
774
|
-
result_record: ResultRecord[Any] = (
|
|
775
|
-
ResultRecord.deserialize_from_result_and_metadata(
|
|
776
|
-
result=result_content, metadata=metadata_content
|
|
777
|
-
)
|
|
778
|
-
)
|
|
779
|
-
else:
|
|
780
|
-
content = call_explicitly_sync_block_method(
|
|
781
|
-
self.result_storage,
|
|
782
|
-
"read_path",
|
|
783
|
-
(key,),
|
|
784
|
-
{},
|
|
785
|
-
)
|
|
786
|
-
result_record: ResultRecord[Any] = ResultRecord.deserialize(
|
|
787
|
-
content, backup_serializer=self.serializer
|
|
788
|
-
)
|
|
789
|
-
|
|
790
|
-
if self.cache_result_in_memory:
|
|
791
|
-
self.cache[resolved_key_path] = result_record
|
|
792
|
-
return result_record
|
|
793
|
-
|
|
794
613
|
def read(
|
|
795
614
|
self,
|
|
796
615
|
key: str,
|
|
@@ -807,7 +626,7 @@ class ResultStore(BaseModel):
|
|
|
807
626
|
A result record.
|
|
808
627
|
"""
|
|
809
628
|
holder = holder or self.generate_default_holder()
|
|
810
|
-
return self._read(key=key, holder=holder)
|
|
629
|
+
return self._read(key=key, holder=holder, _sync=True)
|
|
811
630
|
|
|
812
631
|
async def aread(
|
|
813
632
|
self,
|
|
@@ -825,7 +644,7 @@ class ResultStore(BaseModel):
|
|
|
825
644
|
A result record.
|
|
826
645
|
"""
|
|
827
646
|
holder = holder or self.generate_default_holder()
|
|
828
|
-
return await self.
|
|
647
|
+
return await self._read(key=key, holder=holder, _sync=False)
|
|
829
648
|
|
|
830
649
|
def create_result_record(
|
|
831
650
|
self,
|
|
@@ -915,7 +734,8 @@ class ResultStore(BaseModel):
|
|
|
915
734
|
holder=holder,
|
|
916
735
|
)
|
|
917
736
|
|
|
918
|
-
|
|
737
|
+
@sync_compatible
|
|
738
|
+
async def _persist_result_record(
|
|
919
739
|
self, result_record: "ResultRecord[Any]", holder: str
|
|
920
740
|
) -> None:
|
|
921
741
|
"""
|
|
@@ -978,69 +798,6 @@ class ResultStore(BaseModel):
|
|
|
978
798
|
if self.cache_result_in_memory:
|
|
979
799
|
self.cache[key] = result_record
|
|
980
800
|
|
|
981
|
-
def _persist_result_record(
|
|
982
|
-
self, result_record: "ResultRecord[Any]", holder: str
|
|
983
|
-
) -> None:
|
|
984
|
-
"""
|
|
985
|
-
Persist a result record to storage.
|
|
986
|
-
|
|
987
|
-
Args:
|
|
988
|
-
result_record: The result record to persist.
|
|
989
|
-
holder: The holder of the lock if a lock was set on the record.
|
|
990
|
-
"""
|
|
991
|
-
assert result_record.metadata.storage_key is not None, (
|
|
992
|
-
"Storage key is required on result record"
|
|
993
|
-
)
|
|
994
|
-
|
|
995
|
-
key = result_record.metadata.storage_key
|
|
996
|
-
if result_record.metadata.storage_block_id is None:
|
|
997
|
-
basepath = (
|
|
998
|
-
_resolve_path("")
|
|
999
|
-
if (
|
|
1000
|
-
_resolve_path := getattr(self.result_storage, "_resolve_path", None)
|
|
1001
|
-
)
|
|
1002
|
-
else Path(".").resolve()
|
|
1003
|
-
)
|
|
1004
|
-
base_key = key if basepath is None else str(Path(key).relative_to(basepath))
|
|
1005
|
-
else:
|
|
1006
|
-
base_key = key
|
|
1007
|
-
if (
|
|
1008
|
-
self.lock_manager is not None
|
|
1009
|
-
and self.is_locked(base_key)
|
|
1010
|
-
and not self.is_lock_holder(base_key, holder)
|
|
1011
|
-
):
|
|
1012
|
-
raise RuntimeError(
|
|
1013
|
-
f"Cannot write to result record with key {base_key} because it is locked by "
|
|
1014
|
-
f"another holder."
|
|
1015
|
-
)
|
|
1016
|
-
if self.result_storage is None:
|
|
1017
|
-
self.result_storage = get_default_result_storage(_sync=True)
|
|
1018
|
-
|
|
1019
|
-
# If metadata storage is configured, write result and metadata separately
|
|
1020
|
-
if self.metadata_storage is not None:
|
|
1021
|
-
call_explicitly_sync_block_method(
|
|
1022
|
-
self.result_storage,
|
|
1023
|
-
"write_path",
|
|
1024
|
-
(result_record.metadata.storage_key,),
|
|
1025
|
-
{"content": result_record.serialize_result()},
|
|
1026
|
-
)
|
|
1027
|
-
call_explicitly_sync_block_method(
|
|
1028
|
-
self.metadata_storage,
|
|
1029
|
-
"write_path",
|
|
1030
|
-
(base_key,),
|
|
1031
|
-
{"content": result_record.serialize_metadata()},
|
|
1032
|
-
)
|
|
1033
|
-
# Otherwise, write the result metadata and result together
|
|
1034
|
-
else:
|
|
1035
|
-
call_explicitly_sync_block_method(
|
|
1036
|
-
self.result_storage,
|
|
1037
|
-
"write_path",
|
|
1038
|
-
(result_record.metadata.storage_key,),
|
|
1039
|
-
{"content": result_record.serialize()},
|
|
1040
|
-
)
|
|
1041
|
-
if self.cache_result_in_memory:
|
|
1042
|
-
self.cache[key] = result_record
|
|
1043
|
-
|
|
1044
801
|
def persist_result_record(
|
|
1045
802
|
self, result_record: "ResultRecord[Any]", holder: str | None = None
|
|
1046
803
|
) -> None:
|
|
@@ -1051,7 +808,9 @@ class ResultStore(BaseModel):
|
|
|
1051
808
|
result_record: The result record to persist.
|
|
1052
809
|
"""
|
|
1053
810
|
holder = holder or self.generate_default_holder()
|
|
1054
|
-
return self._persist_result_record(
|
|
811
|
+
return self._persist_result_record(
|
|
812
|
+
result_record=result_record, holder=holder, _sync=True
|
|
813
|
+
)
|
|
1055
814
|
|
|
1056
815
|
async def apersist_result_record(
|
|
1057
816
|
self, result_record: "ResultRecord[Any]", holder: str | None = None
|
|
@@ -1063,8 +822,8 @@ class ResultStore(BaseModel):
|
|
|
1063
822
|
result_record: The result record to persist.
|
|
1064
823
|
"""
|
|
1065
824
|
holder = holder or self.generate_default_holder()
|
|
1066
|
-
return await self.
|
|
1067
|
-
result_record=result_record, holder=holder
|
|
825
|
+
return await self._persist_result_record(
|
|
826
|
+
result_record=result_record, holder=holder, _sync=False
|
|
1068
827
|
)
|
|
1069
828
|
|
|
1070
829
|
def supports_isolation_level(self, level: "IsolationLevel") -> bool:
|
prefect/runner/runner.py
CHANGED
|
@@ -56,6 +56,7 @@ from typing import (
|
|
|
56
56
|
TYPE_CHECKING,
|
|
57
57
|
Any,
|
|
58
58
|
Callable,
|
|
59
|
+
Coroutine,
|
|
59
60
|
Dict,
|
|
60
61
|
Iterable,
|
|
61
62
|
List,
|
|
@@ -166,10 +167,9 @@ class Runner:
|
|
|
166
167
|
query_seconds: The number of seconds to wait between querying for
|
|
167
168
|
scheduled flow runs; defaults to `PREFECT_RUNNER_POLL_FREQUENCY`
|
|
168
169
|
prefetch_seconds: The number of seconds to prefetch flow runs for.
|
|
169
|
-
heartbeat_seconds: The number of seconds
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
to detect crashed flow runs.
|
|
170
|
+
heartbeat_seconds: The number of seconds to wait between emitting
|
|
171
|
+
flow run heartbeats. The runner will not emit heartbeats if the value is None.
|
|
172
|
+
Defaults to `PREFECT_RUNNER_HEARTBEAT_FREQUENCY`.
|
|
173
173
|
limit: The maximum number of flow runs this runner should be running at. Provide `None` for no limit.
|
|
174
174
|
If not provided, the runner will use the value of `PREFECT_RUNNER_PROCESS_LIMIT`.
|
|
175
175
|
pause_on_shutdown: A boolean for whether or not to automatically pause
|
|
@@ -202,8 +202,6 @@ class Runner:
|
|
|
202
202
|
asyncio.run(runner.start())
|
|
203
203
|
```
|
|
204
204
|
"""
|
|
205
|
-
self._heartbeat_seconds = heartbeat_seconds
|
|
206
|
-
|
|
207
205
|
settings = get_current_settings()
|
|
208
206
|
|
|
209
207
|
if name and ("/" in name or "%" in name):
|
|
@@ -223,6 +221,12 @@ class Runner:
|
|
|
223
221
|
|
|
224
222
|
self.query_seconds: float = query_seconds or settings.runner.poll_frequency
|
|
225
223
|
self._prefetch_seconds: float = prefetch_seconds
|
|
224
|
+
self.heartbeat_seconds: float | None = (
|
|
225
|
+
heartbeat_seconds or settings.runner.heartbeat_frequency
|
|
226
|
+
)
|
|
227
|
+
if self.heartbeat_seconds is not None and self.heartbeat_seconds < 30:
|
|
228
|
+
raise ValueError("Heartbeat must be 30 seconds or greater.")
|
|
229
|
+
self._heartbeat_task: asyncio.Task[None] | None = None
|
|
226
230
|
self._events_client: EventsClient = get_events_client(checkpoint_every=1)
|
|
227
231
|
|
|
228
232
|
self._exit_stack = AsyncExitStack()
|
|
@@ -640,6 +644,9 @@ class Runner:
|
|
|
640
644
|
|
|
641
645
|
task_status.started(process.pid)
|
|
642
646
|
|
|
647
|
+
if self.heartbeat_seconds is not None:
|
|
648
|
+
await self._emit_flow_run_heartbeat(flow_run)
|
|
649
|
+
|
|
643
650
|
# Only add the process to the map if it is still running
|
|
644
651
|
# The process may be a multiprocessing.context.SpawnProcess, in which case it will have an `exitcode`` attribute
|
|
645
652
|
# but no `returncode` attribute
|
|
@@ -675,11 +682,6 @@ class Runner:
|
|
|
675
682
|
|
|
676
683
|
flow_run = FlowRun.model_validate(bundle["flow_run"])
|
|
677
684
|
|
|
678
|
-
# Add heartbeat_seconds to env if configured
|
|
679
|
-
if self._heartbeat_seconds is not None:
|
|
680
|
-
env = env or {}
|
|
681
|
-
env["PREFECT_FLOWS_HEARTBEAT_FREQUENCY"] = str(int(self._heartbeat_seconds))
|
|
682
|
-
|
|
683
685
|
async with context:
|
|
684
686
|
if not self._acquire_limit_slot(flow_run.id):
|
|
685
687
|
return
|
|
@@ -693,6 +695,9 @@ class Runner:
|
|
|
693
695
|
await self._propose_crashed_state(flow_run, msg)
|
|
694
696
|
raise RuntimeError(msg)
|
|
695
697
|
|
|
698
|
+
if self.heartbeat_seconds is not None:
|
|
699
|
+
await self._emit_flow_run_heartbeat(flow_run)
|
|
700
|
+
|
|
696
701
|
await self._add_flow_run_process_map_entry(
|
|
697
702
|
flow_run.id, ProcessMapEntry(pid=process.pid, flow_run=flow_run)
|
|
698
703
|
)
|
|
@@ -791,14 +796,7 @@ class Runner:
|
|
|
791
796
|
if flow_run.deployment_id is not None:
|
|
792
797
|
flow = self._deployment_flow_map.get(flow_run.deployment_id)
|
|
793
798
|
if flow:
|
|
794
|
-
|
|
795
|
-
if self._heartbeat_seconds is not None:
|
|
796
|
-
subprocess_env["PREFECT_FLOWS_HEARTBEAT_FREQUENCY"] = str(
|
|
797
|
-
int(self._heartbeat_seconds)
|
|
798
|
-
)
|
|
799
|
-
process = run_flow_in_subprocess(
|
|
800
|
-
flow, flow_run=flow_run, env=subprocess_env or None
|
|
801
|
-
)
|
|
799
|
+
process = run_flow_in_subprocess(flow, flow_run=flow_run)
|
|
802
800
|
task_status.started(process)
|
|
803
801
|
await anyio.to_thread.run_sync(process.join)
|
|
804
802
|
return process.exitcode
|
|
@@ -831,15 +829,6 @@ class Runner:
|
|
|
831
829
|
"PREFECT__ENABLE_CANCELLATION_AND_CRASHED_HOOKS": "false",
|
|
832
830
|
},
|
|
833
831
|
**({"PREFECT__FLOW_ENTRYPOINT": entrypoint} if entrypoint else {}),
|
|
834
|
-
**(
|
|
835
|
-
{
|
|
836
|
-
"PREFECT_FLOWS_HEARTBEAT_FREQUENCY": str(
|
|
837
|
-
int(self._heartbeat_seconds)
|
|
838
|
-
)
|
|
839
|
-
}
|
|
840
|
-
if self._heartbeat_seconds is not None
|
|
841
|
-
else {}
|
|
842
|
-
),
|
|
843
832
|
}
|
|
844
833
|
)
|
|
845
834
|
env.update(**os.environ) # is this really necessary??
|
|
@@ -1060,6 +1049,51 @@ class Runner:
|
|
|
1060
1049
|
flow = None
|
|
1061
1050
|
return flow, deployment
|
|
1062
1051
|
|
|
1052
|
+
async def _emit_flow_run_heartbeats(self):
|
|
1053
|
+
coros: list[Coroutine[Any, Any, Any]] = []
|
|
1054
|
+
for entry in self._flow_run_process_map.values():
|
|
1055
|
+
coros.append(self._emit_flow_run_heartbeat(entry["flow_run"]))
|
|
1056
|
+
await asyncio.gather(*coros)
|
|
1057
|
+
|
|
1058
|
+
async def _emit_flow_run_heartbeat(self, flow_run: "FlowRun"):
|
|
1059
|
+
from prefect import __version__
|
|
1060
|
+
|
|
1061
|
+
related: list[RelatedResource] = []
|
|
1062
|
+
tags: list[str] = []
|
|
1063
|
+
|
|
1064
|
+
flow, deployment = await self._get_flow_and_deployment(flow_run)
|
|
1065
|
+
if deployment:
|
|
1066
|
+
related.append(deployment.as_related_resource())
|
|
1067
|
+
tags.extend(deployment.tags)
|
|
1068
|
+
if flow:
|
|
1069
|
+
related.append(
|
|
1070
|
+
RelatedResource(
|
|
1071
|
+
{
|
|
1072
|
+
"prefect.resource.id": f"prefect.flow.{flow.id}",
|
|
1073
|
+
"prefect.resource.role": "flow",
|
|
1074
|
+
"prefect.resource.name": flow.name,
|
|
1075
|
+
}
|
|
1076
|
+
)
|
|
1077
|
+
)
|
|
1078
|
+
tags.extend(flow_run.tags)
|
|
1079
|
+
|
|
1080
|
+
related = [RelatedResource.model_validate(r) for r in related]
|
|
1081
|
+
related += tags_as_related_resources(set(tags))
|
|
1082
|
+
|
|
1083
|
+
await self._events_client.emit(
|
|
1084
|
+
Event(
|
|
1085
|
+
event="prefect.flow-run.heartbeat",
|
|
1086
|
+
resource=Resource(
|
|
1087
|
+
{
|
|
1088
|
+
"prefect.resource.id": f"prefect.flow-run.{flow_run.id}",
|
|
1089
|
+
"prefect.resource.name": flow_run.name,
|
|
1090
|
+
"prefect.version": __version__,
|
|
1091
|
+
}
|
|
1092
|
+
),
|
|
1093
|
+
related=related,
|
|
1094
|
+
)
|
|
1095
|
+
)
|
|
1096
|
+
|
|
1063
1097
|
def _event_resource(self):
|
|
1064
1098
|
from prefect import __version__
|
|
1065
1099
|
|
|
@@ -1111,7 +1145,7 @@ class Runner:
|
|
|
1111
1145
|
related=related,
|
|
1112
1146
|
)
|
|
1113
1147
|
)
|
|
1114
|
-
self._logger.debug(f"Emitted
|
|
1148
|
+
self._logger.debug(f"Emitted flow run heartbeat event for {flow_run.id}")
|
|
1115
1149
|
|
|
1116
1150
|
async def _get_scheduled_flow_runs(
|
|
1117
1151
|
self,
|
|
@@ -1255,6 +1289,9 @@ class Runner:
|
|
|
1255
1289
|
flow_run.id,
|
|
1256
1290
|
ProcessMapEntry(pid=readiness_result.pid, flow_run=flow_run),
|
|
1257
1291
|
)
|
|
1292
|
+
# Heartbeats are opt-in and only emitted if a heartbeat frequency is set
|
|
1293
|
+
if self.heartbeat_seconds is not None:
|
|
1294
|
+
await self._emit_flow_run_heartbeat(flow_run)
|
|
1258
1295
|
|
|
1259
1296
|
run_logger.info(f"Completed submission of flow run '{flow_run.id}'")
|
|
1260
1297
|
else:
|
|
@@ -1568,6 +1605,15 @@ class Runner:
|
|
|
1568
1605
|
if not hasattr(self, "_loops_task_group") or not self._loops_task_group:
|
|
1569
1606
|
self._loops_task_group: anyio.abc.TaskGroup = anyio.create_task_group()
|
|
1570
1607
|
|
|
1608
|
+
if self.heartbeat_seconds is not None:
|
|
1609
|
+
self._heartbeat_task = asyncio.create_task(
|
|
1610
|
+
critical_service_loop(
|
|
1611
|
+
workload=self._emit_flow_run_heartbeats,
|
|
1612
|
+
interval=self.heartbeat_seconds,
|
|
1613
|
+
jitter_range=0.3,
|
|
1614
|
+
)
|
|
1615
|
+
)
|
|
1616
|
+
|
|
1571
1617
|
self.started = True
|
|
1572
1618
|
return self
|
|
1573
1619
|
|
|
@@ -1586,6 +1632,13 @@ class Runner:
|
|
|
1586
1632
|
shutil.rmtree(str(self._tmp_dir), ignore_errors=True)
|
|
1587
1633
|
del self._runs_task_group, self._loops_task_group
|
|
1588
1634
|
|
|
1635
|
+
if self._heartbeat_task:
|
|
1636
|
+
self._heartbeat_task.cancel()
|
|
1637
|
+
try:
|
|
1638
|
+
await self._heartbeat_task
|
|
1639
|
+
except asyncio.CancelledError:
|
|
1640
|
+
pass
|
|
1641
|
+
|
|
1589
1642
|
def __repr__(self) -> str:
|
|
1590
1643
|
return f"Runner(name={self.name!r})"
|
|
1591
1644
|
|
prefect/runner/storage.py
CHANGED
|
@@ -346,12 +346,11 @@ class GitRepository:
|
|
|
346
346
|
)
|
|
347
347
|
existing_repo_url = None
|
|
348
348
|
existing_repo_url = strip_auth_from_url(result.stdout.decode().strip())
|
|
349
|
-
configured_repo_url = strip_auth_from_url(self._url)
|
|
350
349
|
|
|
351
|
-
if existing_repo_url !=
|
|
350
|
+
if existing_repo_url != self._url:
|
|
352
351
|
raise ValueError(
|
|
353
352
|
f"The existing repository at {str(self.destination)} "
|
|
354
|
-
f"does not match the configured repository {
|
|
353
|
+
f"does not match the configured repository {self._url}"
|
|
355
354
|
)
|
|
356
355
|
|
|
357
356
|
# Sparsely checkout the repository if directories are specified and the repo is not in sparse-checkout mode already
|
|
@@ -645,45 +645,6 @@ class FlowRunFilterIdempotencyKey(PrefectFilterBaseModel):
|
|
|
645
645
|
return filters
|
|
646
646
|
|
|
647
647
|
|
|
648
|
-
class FlowRunFilterCreatedBy(PrefectOperatorFilterBaseModel):
|
|
649
|
-
"""Filter by `FlowRun.created_by`."""
|
|
650
|
-
|
|
651
|
-
id_: Optional[list[UUID]] = Field(
|
|
652
|
-
default=None,
|
|
653
|
-
description="A list of creator IDs to include",
|
|
654
|
-
)
|
|
655
|
-
type_: Optional[list[str]] = Field(
|
|
656
|
-
default=None,
|
|
657
|
-
description=(
|
|
658
|
-
"A list of creator types to include. For example, 'DEPLOYMENT' for "
|
|
659
|
-
"scheduled runs or 'AUTOMATION' for runs triggered by automations."
|
|
660
|
-
),
|
|
661
|
-
examples=[["DEPLOYMENT", "AUTOMATION"]],
|
|
662
|
-
)
|
|
663
|
-
is_null_: Optional[bool] = Field(
|
|
664
|
-
default=None,
|
|
665
|
-
description="If true, only include flow runs without a creator",
|
|
666
|
-
)
|
|
667
|
-
|
|
668
|
-
def _get_filter_list(
|
|
669
|
-
self, db: "PrefectDBInterface"
|
|
670
|
-
) -> Iterable[sa.ColumnExpressionArgument[bool]]:
|
|
671
|
-
filters: list[sa.ColumnExpressionArgument[bool]] = []
|
|
672
|
-
if self.id_ is not None:
|
|
673
|
-
# JSON stores UUIDs as strings, use astext for text extraction
|
|
674
|
-
id_strings = [str(id_val) for id_val in self.id_]
|
|
675
|
-
filters.append(db.FlowRun.created_by["id"].astext.in_(id_strings))
|
|
676
|
-
if self.type_ is not None:
|
|
677
|
-
filters.append(db.FlowRun.created_by["type"].astext.in_(self.type_))
|
|
678
|
-
if self.is_null_ is not None:
|
|
679
|
-
filters.append(
|
|
680
|
-
db.FlowRun.created_by.is_(None)
|
|
681
|
-
if self.is_null_
|
|
682
|
-
else db.FlowRun.created_by.is_not(None)
|
|
683
|
-
)
|
|
684
|
-
return filters
|
|
685
|
-
|
|
686
|
-
|
|
687
648
|
class FlowRunFilter(PrefectOperatorFilterBaseModel):
|
|
688
649
|
"""Filter flow runs. Only flow runs matching all criteria will be returned"""
|
|
689
650
|
|
|
@@ -730,9 +691,6 @@ class FlowRunFilter(PrefectOperatorFilterBaseModel):
|
|
|
730
691
|
idempotency_key: Optional[FlowRunFilterIdempotencyKey] = Field(
|
|
731
692
|
default=None, description="Filter criteria for `FlowRun.idempotency_key`"
|
|
732
693
|
)
|
|
733
|
-
created_by: Optional[FlowRunFilterCreatedBy] = Field(
|
|
734
|
-
default=None, description="Filter criteria for `FlowRun.created_by`"
|
|
735
|
-
)
|
|
736
694
|
|
|
737
695
|
def only_filters_on_id(self) -> bool:
|
|
738
696
|
return bool(
|
|
@@ -751,7 +709,6 @@ class FlowRunFilter(PrefectOperatorFilterBaseModel):
|
|
|
751
709
|
and self.parent_flow_run_id is None
|
|
752
710
|
and self.parent_task_run_id is None
|
|
753
711
|
and self.idempotency_key is None
|
|
754
|
-
and self.created_by is None
|
|
755
712
|
)
|
|
756
713
|
|
|
757
714
|
def _get_filter_list(
|
|
@@ -787,8 +744,6 @@ class FlowRunFilter(PrefectOperatorFilterBaseModel):
|
|
|
787
744
|
filters.append(self.parent_task_run_id.as_sql_filter())
|
|
788
745
|
if self.idempotency_key is not None:
|
|
789
746
|
filters.append(self.idempotency_key.as_sql_filter())
|
|
790
|
-
if self.created_by is not None:
|
|
791
|
-
filters.append(self.created_by.as_sql_filter())
|
|
792
747
|
|
|
793
748
|
return filters
|
|
794
749
|
|