prefect 3.6.12__py3-none-any.whl → 3.6.13__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 +18 -0
- prefect/_internal/urls.py +29 -0
- prefect/_states.py +4 -1
- prefect/client/orchestration/_concurrency_limits/client.py +16 -16
- prefect/client/schemas/filters.py +24 -0
- prefect/client/subscriptions.py +1 -1
- prefect/events/clients.py +52 -13
- prefect/flow_engine.py +192 -10
- prefect/flows.py +61 -2
- prefect/results.py +262 -21
- prefect/runner/runner.py +29 -82
- prefect/runner/storage.py +6 -25
- prefect/server/schemas/filters.py +45 -0
- prefect/server/ui-v2/assets/{artifact-card-Ds2tntMW.js → artifact-card-CUEaRDGw.js} +2 -2
- prefect/server/ui-v2/assets/{artifact-card-Ds2tntMW.js.map → artifact-card-CUEaRDGw.js.map} +1 -1
- prefect/server/ui-v2/assets/{artifact._id-erPNL0a8.js → artifact._id-Ca6VCUS0.js} +2 -2
- prefect/server/ui-v2/assets/{artifact._id-erPNL0a8.js.map → artifact._id-Ca6VCUS0.js.map} +1 -1
- prefect/server/ui-v2/assets/{automation-wizard-Bx9AQxRV.js → automation-wizard-z26pICBl.js} +2 -2
- prefect/server/ui-v2/assets/{automation-wizard-Bx9AQxRV.js.map → automation-wizard-z26pICBl.js.map} +1 -1
- prefect/server/ui-v2/assets/automation._id-DuodjY5t.js +2 -0
- prefect/server/ui-v2/assets/{automation._id-IHh5QJwE.js.map → automation._id-DuodjY5t.js.map} +1 -1
- prefect/server/ui-v2/assets/{automation_._id.edit-Ds8vO0c5.js → automation_._id.edit-CcuJhc-y.js} +2 -2
- prefect/server/ui-v2/assets/{automation_._id.edit-Ds8vO0c5.js.map → automation_._id.edit-CcuJhc-y.js.map} +1 -1
- prefect/server/ui-v2/assets/{automations-header-C6aUe5Tw.js → automations-header-CgOWwuc6.js} +2 -2
- prefect/server/ui-v2/assets/{automations-header-C6aUe5Tw.js.map → automations-header-CgOWwuc6.js.map} +1 -1
- prefect/server/ui-v2/assets/{base-job-template-form-section-a-9TFMcW.js → base-job-template-form-section-DN9HyjM5.js} +2 -2
- prefect/server/ui-v2/assets/{base-job-template-form-section-a-9TFMcW.js.map → base-job-template-form-section-DN9HyjM5.js.map} +1 -1
- prefect/server/ui-v2/assets/{block-type-details-eq3Wg2ht.js → block-type-details-rrToxL5r.js} +2 -2
- prefect/server/ui-v2/assets/{block-type-details-eq3Wg2ht.js.map → block-type-details-rrToxL5r.js.map} +1 -1
- prefect/server/ui-v2/assets/block-type-logo-DURScH8H.js +2 -0
- prefect/server/ui-v2/assets/{block-type-logo-B31LJm5H.js.map → block-type-logo-DURScH8H.js.map} +1 -1
- prefect/server/ui-v2/assets/{block._id-OVdz3htC.js → block._id-Bue6lnrN.js} +2 -2
- prefect/server/ui-v2/assets/{block._id-OVdz3htC.js.map → block._id-Bue6lnrN.js.map} +1 -1
- prefect/server/ui-v2/assets/block_._id.edit-QY0OzN_b.js +2 -0
- prefect/server/ui-v2/assets/{block_._id.edit-CIhIBJrm.js.map → block_._id.edit-QY0OzN_b.js.map} +1 -1
- prefect/server/ui-v2/assets/catalog-CGjRGWv6.js +2 -0
- prefect/server/ui-v2/assets/{catalog-CfniU0UV.js.map → catalog-CGjRGWv6.js.map} +1 -1
- prefect/server/ui-v2/assets/catalog_._slug-BMFNZhEb.js +2 -0
- prefect/server/ui-v2/assets/{catalog_._slug-C9p86T4s.js.map → catalog_._slug-BMFNZhEb.js.map} +1 -1
- prefect/server/ui-v2/assets/catalog_._slug_.create-CuFA3kUC.js +2 -0
- prefect/server/ui-v2/assets/{catalog_._slug_.create-BhSunL__.js.map → catalog_._slug_.create-CuFA3kUC.js.map} +1 -1
- prefect/server/ui-v2/assets/{collapsible-DBD7wjpi.js → collapsible-Ell2FjrX.js} +2 -2
- prefect/server/ui-v2/assets/{collapsible-DBD7wjpi.js.map → collapsible-Ell2FjrX.js.map} +1 -1
- prefect/server/ui-v2/assets/{concurrency-limit._id-C-XWq7Tf.js → concurrency-limit._id-Rr1ysUct.js} +2 -2
- prefect/server/ui-v2/assets/{concurrency-limit._id-C-XWq7Tf.js.map → concurrency-limit._id-Rr1ysUct.js.map} +1 -1
- prefect/server/ui-v2/assets/{create-noFojqGL.js → create-CQK8-uO1.js} +2 -2
- prefect/server/ui-v2/assets/{create-noFojqGL.js.map → create-CQK8-uO1.js.map} +1 -1
- prefect/server/ui-v2/assets/{create-Bj2t5YQq.js → create-ewA3uq_h.js} +2 -2
- prefect/server/ui-v2/assets/{create-Bj2t5YQq.js.map → create-ewA3uq_h.js.map} +1 -1
- prefect/server/ui-v2/assets/{data-table-BjFlVIyC.js → data-table-Bx1uYX_M.js} +2 -2
- prefect/server/ui-v2/assets/{data-table-BjFlVIyC.js.map → data-table-Bx1uYX_M.js.map} +1 -1
- prefect/server/ui-v2/assets/delete-confirmation-dialog-CqKsUEj_.js +2 -0
- prefect/server/ui-v2/assets/{delete-confirmation-dialog-COdZmNfa.js.map → delete-confirmation-dialog-CqKsUEj_.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-action-header-C6v2kZ6V.js → deployment-action-header-Bz5COdER.js} +2 -2
- prefect/server/ui-v2/assets/{deployment-action-header-C6v2kZ6V.js.map → deployment-action-header-Bz5COdER.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-form-D9d5sZM2.js → deployment-form-CrlZlNoj.js} +3 -3
- prefect/server/ui-v2/assets/{deployment-form-D9d5sZM2.js.map → deployment-form-CrlZlNoj.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-links-Dy-M1Que.js → deployment-links-D9ZR_vmp.js} +2 -2
- prefect/server/ui-v2/assets/{deployment-links-Dy-M1Que.js.map → deployment-links-D9ZR_vmp.js.map} +1 -1
- prefect/server/ui-v2/assets/deployment._id-BV0rSqba.js +2 -0
- prefect/server/ui-v2/assets/{deployment._id-DVmqclRz.js.map → deployment._id-BV0rSqba.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment._id-CZlY9261.js → deployment._id-DvHhx-qN.js} +2 -2
- prefect/server/ui-v2/assets/{deployment._id-CZlY9261.js.map → deployment._id-DvHhx-qN.js.map} +1 -1
- prefect/server/ui-v2/assets/deployment_._id.duplicate-BrEOenqP.js +2 -0
- prefect/server/ui-v2/assets/{deployment_._id.duplicate-DVMjpk5m.js.map → deployment_._id.duplicate-BrEOenqP.js.map} +1 -1
- prefect/server/ui-v2/assets/deployment_._id.edit-BbYKPK42.js +2 -0
- prefect/server/ui-v2/assets/{deployment_._id.edit-Ck_P6KDn.js.map → deployment_._id.edit-BbYKPK42.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment_._id.run-Cht7AHDG.js → deployment_._id.run-Dv7S_MJR.js} +2 -2
- prefect/server/ui-v2/assets/{deployment_._id.run-Cht7AHDG.js.map → deployment_._id.run-Dv7S_MJR.js.map} +1 -1
- prefect/server/ui-v2/assets/{dropdown-menu-xZ_3w9OP.js → dropdown-menu-e0Fqb6aw.js} +2 -2
- prefect/server/ui-v2/assets/{dropdown-menu-xZ_3w9OP.js.map → dropdown-menu-e0Fqb6aw.js.map} +1 -1
- prefect/server/ui-v2/assets/{event._eventDate._eventId-CpexuXd6.js → event._eventDate._eventId-Cp4UmGqq.js} +2 -2
- prefect/server/ui-v2/assets/{event._eventDate._eventId-CpexuXd6.js.map → event._eventDate._eventId-Cp4UmGqq.js.map} +1 -1
- prefect/server/ui-v2/assets/flow-run-graph-BrqoR3E2.js +2 -0
- prefect/server/ui-v2/assets/flow-run-graph-BrqoR3E2.js.map +1 -0
- prefect/server/ui-v2/assets/{flow-run._id-ZBlKBwPO.js → flow-run._id-BOp38Pbq.js} +2 -2
- prefect/server/ui-v2/assets/{flow-run._id-ZBlKBwPO.js.map → flow-run._id-BOp38Pbq.js.map} +1 -1
- prefect/server/ui-v2/assets/flow-run._id-DJnTDEN_.js +2 -0
- prefect/server/ui-v2/assets/{flow-run._id-OL0YhyLW.js.map → flow-run._id-DJnTDEN_.js.map} +1 -1
- prefect/server/ui-v2/assets/flow-run._id-D_wY_rBe.js +4 -0
- prefect/server/ui-v2/assets/flow-run._id-D_wY_rBe.js.map +1 -0
- prefect/server/ui-v2/assets/flow-runs-pagination-Bq2ZUzM6.js +2 -0
- prefect/server/ui-v2/assets/flow-runs-pagination-Bq2ZUzM6.js.map +1 -0
- prefect/server/ui-v2/assets/flow._id-eCBL95rg.js +2 -0
- prefect/server/ui-v2/assets/{flow._id-DhrCicwR.js.map → flow._id-eCBL95rg.js.map} +1 -1
- prefect/server/ui-v2/assets/{form-BTub_PhK.js → form-DNerk3LS.js} +2 -2
- prefect/server/ui-v2/assets/{form-BTub_PhK.js.map → form-DNerk3LS.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-6wmrKLU3.js → header-B0ejRncu.js} +2 -2
- prefect/server/ui-v2/assets/{header-6wmrKLU3.js.map → header-B0ejRncu.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-Dp9qi8fq.js → header-DwagHBlF.js} +2 -2
- prefect/server/ui-v2/assets/{header-Dp9qi8fq.js.map → header-DwagHBlF.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-B75eb688.js → header-huSvwxKI.js} +2 -2
- prefect/server/ui-v2/assets/{header-B75eb688.js.map → header-huSvwxKI.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-DSaSov8V.js → index-BO3SOwdV.js} +2 -2
- prefect/server/ui-v2/assets/{index-DSaSov8V.js.map → index-BO3SOwdV.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-DOkFJdYY.js → index-BTPE3vs7.js} +2 -2
- prefect/server/ui-v2/assets/{index-DOkFJdYY.js.map → index-BTPE3vs7.js.map} +1 -1
- prefect/server/ui-v2/assets/index-BU4yZRd3.js +2 -0
- prefect/server/ui-v2/assets/{index-Cs8eFQKw.js.map → index-BU4yZRd3.js.map} +1 -1
- prefect/server/ui-v2/assets/index-BhALpenO.js +2 -0
- prefect/server/ui-v2/assets/{index-qPlIYf3i.js.map → index-BhALpenO.js.map} +1 -1
- prefect/server/ui-v2/assets/index-BzN9bQeM.js +2 -0
- prefect/server/ui-v2/assets/{index-CT_nG86y.js.map → index-BzN9bQeM.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-zpb5iSCL.js → index-CBhi1P9g.js} +2 -2
- prefect/server/ui-v2/assets/{index-zpb5iSCL.js.map → index-CBhi1P9g.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-UN2Tx4jH.js → index-CRDz4nhM.js} +2 -2
- prefect/server/ui-v2/assets/{index-UN2Tx4jH.js.map → index-CRDz4nhM.js.map} +1 -1
- prefect/server/ui-v2/assets/index-CTnoa3Ho.js +2 -0
- prefect/server/ui-v2/assets/index-CTnoa3Ho.js.map +1 -0
- prefect/server/ui-v2/assets/{index-DzMGV8GV.js → index-CWkbSdxY.js} +2 -2
- prefect/server/ui-v2/assets/{index-DzMGV8GV.js.map → index-CWkbSdxY.js.map} +1 -1
- prefect/server/ui-v2/assets/index-CgOsOj5t.js +2 -0
- prefect/server/ui-v2/assets/{index-7ThYp9SY.js.map → index-CgOsOj5t.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CGWoVV2s.js → index-CnIJUujl.js} +2 -2
- prefect/server/ui-v2/assets/{index-CGWoVV2s.js.map → index-CnIJUujl.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-D08xgmV0.js → index-Cutg_A1j.js} +2 -2
- prefect/server/ui-v2/assets/{index-D08xgmV0.js.map → index-Cutg_A1j.js.map} +1 -1
- prefect/server/ui-v2/assets/index-D5RdrxkU.js +17 -0
- prefect/server/ui-v2/assets/index-D5RdrxkU.js.map +1 -0
- prefect/server/ui-v2/assets/{index-DfiNsXba.js → index-DDiyFpIV.js} +2 -2
- prefect/server/ui-v2/assets/{index-DfiNsXba.js.map → index-DDiyFpIV.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-BA7ACCoL.js → index-DI2DC5gd.js} +2 -2
- prefect/server/ui-v2/assets/{index-BA7ACCoL.js.map → index-DI2DC5gd.js.map} +1 -1
- prefect/server/ui-v2/assets/index-Dg_duvDx.js +2 -0
- prefect/server/ui-v2/assets/{index-B_3f8Hcb.js.map → index-Dg_duvDx.js.map} +1 -1
- prefect/server/ui-v2/assets/index-Dspw5HFj.js +2 -0
- prefect/server/ui-v2/assets/{index-7-r4ia_S.js.map → index-Dspw5HFj.js.map} +1 -1
- prefect/server/ui-v2/assets/index-NY089eTx.css +1 -0
- prefect/server/ui-v2/assets/{index-DYOACRXY.js → index-VOOLxiSE.js} +2 -2
- prefect/server/ui-v2/assets/{index-DYOACRXY.js.map → index-VOOLxiSE.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-HGoNWFfP.js → index-Wfs7Cjew.js} +2 -2
- prefect/server/ui-v2/assets/{index-HGoNWFfP.js.map → index-Wfs7Cjew.js.map} +1 -1
- prefect/server/ui-v2/assets/index-dSUEBAqg.js +2 -0
- prefect/server/ui-v2/assets/{index-H6bwm6L6.js.map → index-dSUEBAqg.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-D3ILnEzm.js → index-uvH5a3zO.js} +2 -2
- prefect/server/ui-v2/assets/{index-D3ILnEzm.js.map → index-uvH5a3zO.js.map} +1 -1
- prefect/server/ui-v2/assets/{json-input-9UPGqxTw.js → json-input-gXz7BuJj.js} +2 -2
- prefect/server/ui-v2/assets/{json-input-9UPGqxTw.js.map → json-input-gXz7BuJj.js.map} +1 -1
- prefect/server/ui-v2/assets/{key._key-CTFfXO_k.js → key._key-CJPbLXwU.js} +2 -2
- prefect/server/ui-v2/assets/{key._key-CTFfXO_k.js.map → key._key-CJPbLXwU.js.map} +1 -1
- prefect/server/ui-v2/assets/{lazy-markdown-BHwIrC8E.js → lazy-markdown-wPid80zf.js} +2 -2
- prefect/server/ui-v2/assets/{lazy-markdown-BHwIrC8E.js.map → lazy-markdown-wPid80zf.js.map} +1 -1
- prefect/server/ui-v2/assets/{login-kqmT29n7.js → login-DC63bGXK.js} +2 -2
- prefect/server/ui-v2/assets/{login-kqmT29n7.js.map → login-DC63bGXK.js.map} +1 -1
- prefect/server/ui-v2/assets/{markdown-input-BesmAbLS.js → markdown-input-BhqrU6Eo.js} +2 -2
- prefect/server/ui-v2/assets/{markdown-input-BesmAbLS.js.map → markdown-input-BhqrU6Eo.js.map} +1 -1
- prefect/server/ui-v2/assets/{python-example-snippet-COTWYn1Y.js → python-example-snippet-3jtXWQZN.js} +3 -3
- prefect/server/ui-v2/assets/{python-example-snippet-COTWYn1Y.js.map → python-example-snippet-3jtXWQZN.js.map} +1 -1
- prefect/server/ui-v2/assets/{python-input-Bjccebi0.js → python-input-ZVi-v324.js} +2 -2
- prefect/server/ui-v2/assets/{python-input-Bjccebi0.js.map → python-input-ZVi-v324.js.map} +1 -1
- prefect/server/ui-v2/assets/{radio-group-DkAK0M2h.js → radio-group-BBMLpHGc.js} +2 -2
- prefect/server/ui-v2/assets/{radio-group-DkAK0M2h.js.map → radio-group-BBMLpHGc.js.map} +1 -1
- prefect/server/ui-v2/assets/route-error-state-BJXl8qkX.js +2 -0
- prefect/server/ui-v2/assets/{route-error-state-ALftyvGl.js.map → route-error-state-BJXl8qkX.js.map} +1 -1
- prefect/server/ui-v2/assets/{schema-form-BR4E-WXE.js → schema-form-BEqYjsM-.js} +2 -2
- prefect/server/ui-v2/assets/{schema-form-BR4E-WXE.js.map → schema-form-BEqYjsM-.js.map} +1 -1
- prefect/server/ui-v2/assets/{schema-form-input-string-format-datetime-BhL8C5NS.js → schema-form-input-string-format-datetime-l3xt3PWf.js} +4 -4
- prefect/server/ui-v2/assets/{schema-form-input-string-format-datetime-BhL8C5NS.js.map → schema-form-input-string-format-datetime-l3xt3PWf.js.map} +1 -1
- prefect/server/ui-v2/assets/settings-txD0dR5Q.js +2 -0
- prefect/server/ui-v2/assets/settings-txD0dR5Q.js.map +1 -0
- prefect/server/ui-v2/assets/{sort-filter-BD4vwJXt.js → sort-filter-DHPFdKZ2.js} +2 -2
- prefect/server/ui-v2/assets/{sort-filter-BD4vwJXt.js.map → sort-filter-DHPFdKZ2.js.map} +1 -1
- prefect/server/ui-v2/assets/state-colors-CAAf0Eg3.js +2 -0
- prefect/server/ui-v2/assets/state-colors-CAAf0Eg3.js.map +1 -0
- prefect/server/ui-v2/assets/table-ULfpXJXB.js +2 -0
- prefect/server/ui-v2/assets/table-ULfpXJXB.js.map +1 -0
- prefect/server/ui-v2/assets/tags-input-BLzMOTDb.js +2 -0
- prefect/server/ui-v2/assets/tags-input-BLzMOTDb.js.map +1 -0
- prefect/server/ui-v2/assets/task-run-concurrency-limits-reset-dialog-BpeKHk7g.js +2 -0
- prefect/server/ui-v2/assets/{task-run-concurrency-limits-reset-dialog-CG3den1B.js.map → task-run-concurrency-limits-reset-dialog-BpeKHk7g.js.map} +1 -1
- prefect/server/ui-v2/assets/{task-run._id-DOcIzVi0.js → task-run._id-CkOl9MJs.js} +3 -3
- prefect/server/ui-v2/assets/{task-run._id-DOcIzVi0.js.map → task-run._id-CkOl9MJs.js.map} +1 -1
- prefect/server/ui-v2/assets/task-run._id-udkz1lhh.js +2 -0
- prefect/server/ui-v2/assets/{task-run._id-CnIVqU6v.js.map → task-run._id-udkz1lhh.js.map} +1 -1
- prefect/server/ui-v2/assets/task-runs-pagination-B7D5K_FM.js +2 -0
- prefect/server/ui-v2/assets/{task-runs-pagination-DLSAz-Ur.js.map → task-runs-pagination-B7D5K_FM.js.map} +1 -1
- prefect/server/ui-v2/assets/{textarea-D8LjlIx7.js → textarea-C4bdj7Jk.js} +2 -2
- prefect/server/ui-v2/assets/{textarea-D8LjlIx7.js.map → textarea-C4bdj7Jk.js.map} +1 -1
- prefect/server/ui-v2/assets/{timezone-select-BG3cL3-U.js → timezone-select-AdlSRQxZ.js} +2 -2
- prefect/server/ui-v2/assets/{timezone-select-BG3cL3-U.js.map → timezone-select-AdlSRQxZ.js.map} +1 -1
- prefect/server/ui-v2/assets/{toggle-group-D3zeurIL.js → toggle-group-C-vxYz4l.js} +2 -2
- prefect/server/ui-v2/assets/{toggle-group-D3zeurIL.js.map → toggle-group-C-vxYz4l.js.map} +1 -1
- prefect/server/ui-v2/assets/use-delete-automation-confirmation-dialog-Cqhaqtqe.js +2 -0
- prefect/server/ui-v2/assets/{use-delete-automation-confirmation-dialog-Bzy2ML2T.js.map → use-delete-automation-confirmation-dialog-Cqhaqtqe.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-delete-block-document-confirmation-dialog-DRAP-Tnu.js → use-delete-block-document-confirmation-dialog-GjNhFxZe.js} +2 -2
- prefect/server/ui-v2/assets/{use-delete-block-document-confirmation-dialog-DRAP-Tnu.js.map → use-delete-block-document-confirmation-dialog-GjNhFxZe.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-flow-runs-selected-rows-D4yWonR8.js → use-flow-runs-selected-rows-DfwmswyR.js} +2 -2
- prefect/server/ui-v2/assets/{use-flow-runs-selected-rows-D4yWonR8.js.map → use-flow-runs-selected-rows-DfwmswyR.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-get-artifacts-flow-task-runs-DZeBiVd9.js → use-get-artifacts-flow-task-runs-BEBpG_5J.js} +2 -2
- prefect/server/ui-v2/assets/{use-get-artifacts-flow-task-runs-DZeBiVd9.js.map → use-get-artifacts-flow-task-runs-BEBpG_5J.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-quick-run-BxAMqZDM.js → use-quick-run-BYBRcDwC.js} +2 -2
- prefect/server/ui-v2/assets/{use-quick-run-BxAMqZDM.js.map → use-quick-run-BYBRcDwC.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-stepper-T3wAKNvM.js → use-stepper-Bk97vOTm.js} +2 -2
- prefect/server/ui-v2/assets/{use-stepper-T3wAKNvM.js.map → use-stepper-Bk97vOTm.js.map} +1 -1
- prefect/server/ui-v2/assets/{utilities-B2JMf8iI.js → utilities-BQwGFLk5.js} +2 -2
- prefect/server/ui-v2/assets/{utilities-B2JMf8iI.js.map → utilities-BQwGFLk5.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool-filter-CZz0AJlt.js → work-pool-filter-Ddhp_M-L.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool-filter-CZz0AJlt.js.map → work-pool-filter-Ddhp_M-L.js.map} +1 -1
- prefect/server/ui-v2/assets/work-pool-queue-toggle-DX3eV3R_.js +2 -0
- prefect/server/ui-v2/assets/{work-pool-queue-toggle-D4eeo-hi.js.map → work-pool-queue-toggle-DX3eV3R_.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool._workPoolName-DrWddu9K.js → work-pool._workPoolName-BaRIsXBX.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool._workPoolName-DrWddu9K.js.map → work-pool._workPoolName-BaRIsXBX.js.map} +1 -1
- prefect/server/ui-v2/assets/work-pool_._workPoolName.edit-dhS_Xz32.js +2 -0
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.edit-CA0ePjCk.js.map → work-pool_._workPoolName.edit-dhS_Xz32.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-DpUnE86v.js → work-pool_._workPoolName.queue._workQueueName-BlstL9Se.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-DpUnE86v.js.map → work-pool_._workPoolName.queue._workQueueName-BlstL9Se.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-queue-icon-text-BjiA7vAW.js → work-queue-icon-text-_Ez8e2dw.js} +2 -2
- prefect/server/ui-v2/assets/{work-queue-icon-text-BjiA7vAW.js.map → work-queue-icon-text-_Ez8e2dw.js.map} +1 -1
- prefect/server/ui-v2/index.html +2 -2
- prefect/settings/models/flows.py +14 -1
- prefect/settings/models/runner.py +16 -6
- prefect/task_engine.py +17 -3
- prefect/task_worker.py +99 -17
- prefect/tasks.py +2 -2
- prefect/testing/fixtures.py +41 -6
- {prefect-3.6.12.dist-info → prefect-3.6.13.dist-info}/METADATA +1 -1
- {prefect-3.6.12.dist-info → prefect-3.6.13.dist-info}/RECORD +221 -220
- prefect/server/ui-v2/assets/automation._id-IHh5QJwE.js +0 -2
- prefect/server/ui-v2/assets/block-type-logo-B31LJm5H.js +0 -2
- prefect/server/ui-v2/assets/block_._id.edit-CIhIBJrm.js +0 -2
- prefect/server/ui-v2/assets/catalog-CfniU0UV.js +0 -2
- prefect/server/ui-v2/assets/catalog_._slug-C9p86T4s.js +0 -2
- prefect/server/ui-v2/assets/catalog_._slug_.create-BhSunL__.js +0 -2
- prefect/server/ui-v2/assets/delete-confirmation-dialog-COdZmNfa.js +0 -2
- prefect/server/ui-v2/assets/deployment._id-DVmqclRz.js +0 -2
- prefect/server/ui-v2/assets/deployment_._id.duplicate-DVMjpk5m.js +0 -2
- prefect/server/ui-v2/assets/deployment_._id.edit-Ck_P6KDn.js +0 -2
- prefect/server/ui-v2/assets/flow-run-graph-CfoPEAgQ.js +0 -2
- prefect/server/ui-v2/assets/flow-run-graph-CfoPEAgQ.js.map +0 -1
- prefect/server/ui-v2/assets/flow-run._id-C-qxwEBp.js +0 -4
- prefect/server/ui-v2/assets/flow-run._id-C-qxwEBp.js.map +0 -1
- prefect/server/ui-v2/assets/flow-run._id-OL0YhyLW.js +0 -2
- prefect/server/ui-v2/assets/flow-runs-pagination-DnwkJapB.js +0 -2
- prefect/server/ui-v2/assets/flow-runs-pagination-DnwkJapB.js.map +0 -1
- prefect/server/ui-v2/assets/flow._id-DhrCicwR.js +0 -2
- prefect/server/ui-v2/assets/index-7-r4ia_S.js +0 -2
- prefect/server/ui-v2/assets/index-7ThYp9SY.js +0 -2
- prefect/server/ui-v2/assets/index-B7zHzWQW.css +0 -1
- prefect/server/ui-v2/assets/index-B_3f8Hcb.js +0 -2
- prefect/server/ui-v2/assets/index-BiCd-Iuz.js +0 -2
- prefect/server/ui-v2/assets/index-BiCd-Iuz.js.map +0 -1
- prefect/server/ui-v2/assets/index-CT_nG86y.js +0 -2
- prefect/server/ui-v2/assets/index-Cs8eFQKw.js +0 -2
- prefect/server/ui-v2/assets/index-H6bwm6L6.js +0 -2
- prefect/server/ui-v2/assets/index-WYPZo52S.js +0 -17
- prefect/server/ui-v2/assets/index-WYPZo52S.js.map +0 -1
- prefect/server/ui-v2/assets/index-qPlIYf3i.js +0 -2
- prefect/server/ui-v2/assets/route-error-state-ALftyvGl.js +0 -2
- prefect/server/ui-v2/assets/settings-BL0X8cDU.js +0 -2
- prefect/server/ui-v2/assets/settings-BL0X8cDU.js.map +0 -1
- prefect/server/ui-v2/assets/table-CEAx-qHs.js +0 -2
- prefect/server/ui-v2/assets/table-CEAx-qHs.js.map +0 -1
- prefect/server/ui-v2/assets/tags-input-D1RJZEUA.js +0 -2
- prefect/server/ui-v2/assets/tags-input-D1RJZEUA.js.map +0 -1
- prefect/server/ui-v2/assets/task-run-concurrency-limits-reset-dialog-CG3den1B.js +0 -2
- prefect/server/ui-v2/assets/task-run._id-CnIVqU6v.js +0 -2
- prefect/server/ui-v2/assets/task-runs-pagination-DLSAz-Ur.js +0 -2
- prefect/server/ui-v2/assets/use-delete-automation-confirmation-dialog-Bzy2ML2T.js +0 -2
- prefect/server/ui-v2/assets/use-local-storage-CpxMp5wR.js +0 -2
- prefect/server/ui-v2/assets/use-local-storage-CpxMp5wR.js.map +0 -1
- prefect/server/ui-v2/assets/work-pool-queue-toggle-D4eeo-hi.js +0 -2
- prefect/server/ui-v2/assets/work-pool_._workPoolName.edit-CA0ePjCk.js +0 -2
- {prefect-3.6.12.dist-info → prefect-3.6.13.dist-info}/WHEEL +0 -0
- {prefect-3.6.12.dist-info → prefect-3.6.13.dist-info}/entry_points.txt +0 -0
- {prefect-3.6.12.dist-info → prefect-3.6.13.dist-info}/licenses/LICENSE +0 -0
prefect/results.py
CHANGED
|
@@ -33,7 +33,10 @@ 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
|
|
36
|
+
from prefect._internal.compatibility.blocks import (
|
|
37
|
+
call_explicitly_async_block_method,
|
|
38
|
+
call_explicitly_sync_block_method,
|
|
39
|
+
)
|
|
37
40
|
from prefect._internal.concurrency.event_loop import get_running_loop
|
|
38
41
|
from prefect._result_records import R, ResultRecord, ResultRecordMetadata
|
|
39
42
|
from prefect.blocks.core import Block
|
|
@@ -52,7 +55,6 @@ from prefect.serializers import Serializer
|
|
|
52
55
|
from prefect.settings.context import get_current_settings
|
|
53
56
|
from prefect.types import DateTime
|
|
54
57
|
from prefect.utilities.annotations import NotSet
|
|
55
|
-
from prefect.utilities.asyncutils import sync_compatible
|
|
56
58
|
|
|
57
59
|
if TYPE_CHECKING:
|
|
58
60
|
import logging
|
|
@@ -369,8 +371,7 @@ class ResultStore(BaseModel):
|
|
|
369
371
|
result = await store.aread(metadata.storage_key)
|
|
370
372
|
return result
|
|
371
373
|
|
|
372
|
-
|
|
373
|
-
async def update_for_flow(self, flow: "Flow[..., Any]") -> Self:
|
|
374
|
+
async def aupdate_for_flow(self, flow: "Flow[..., Any]") -> Self:
|
|
374
375
|
"""
|
|
375
376
|
Create a new result store for a flow with updated settings.
|
|
376
377
|
|
|
@@ -393,8 +394,31 @@ class ResultStore(BaseModel):
|
|
|
393
394
|
update["metadata_storage"] = NullFileSystem()
|
|
394
395
|
return self.model_copy(update=update)
|
|
395
396
|
|
|
396
|
-
@
|
|
397
|
-
|
|
397
|
+
@async_dispatch(aupdate_for_flow)
|
|
398
|
+
def update_for_flow(self, flow: "Flow[..., Any]") -> Self:
|
|
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:
|
|
398
422
|
"""
|
|
399
423
|
Create a new result store for a task.
|
|
400
424
|
|
|
@@ -446,6 +470,59 @@ class ResultStore(BaseModel):
|
|
|
446
470
|
update["metadata_storage"] = None
|
|
447
471
|
return self.model_copy(update=update)
|
|
448
472
|
|
|
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
|
+
|
|
449
526
|
@staticmethod
|
|
450
527
|
def generate_default_holder() -> str:
|
|
451
528
|
"""
|
|
@@ -467,8 +544,7 @@ class ResultStore(BaseModel):
|
|
|
467
544
|
return f"{hostname}:{pid}:{thread_id}:{thread_name}:{id(current_task)}"
|
|
468
545
|
return f"{hostname}:{pid}:{thread_id}:{thread_name}"
|
|
469
546
|
|
|
470
|
-
|
|
471
|
-
async def _exists(self, key: str) -> bool:
|
|
547
|
+
async def _aexists(self, key: str) -> bool:
|
|
472
548
|
"""
|
|
473
549
|
Check if a result record exists in storage.
|
|
474
550
|
|
|
@@ -511,6 +587,49 @@ class ResultStore(BaseModel):
|
|
|
511
587
|
exists = True
|
|
512
588
|
return exists
|
|
513
589
|
|
|
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
|
+
|
|
514
633
|
def exists(self, key: str) -> bool:
|
|
515
634
|
"""
|
|
516
635
|
Check if a result record exists in storage.
|
|
@@ -521,7 +640,7 @@ class ResultStore(BaseModel):
|
|
|
521
640
|
Returns:
|
|
522
641
|
bool: True if the result record exists, False otherwise.
|
|
523
642
|
"""
|
|
524
|
-
return self._exists(key=key
|
|
643
|
+
return self._exists(key=key)
|
|
525
644
|
|
|
526
645
|
async def aexists(self, key: str) -> bool:
|
|
527
646
|
"""
|
|
@@ -533,7 +652,7 @@ class ResultStore(BaseModel):
|
|
|
533
652
|
Returns:
|
|
534
653
|
bool: True if the result record exists, False otherwise.
|
|
535
654
|
"""
|
|
536
|
-
return await self.
|
|
655
|
+
return await self._aexists(key=key)
|
|
537
656
|
|
|
538
657
|
def _resolved_key_path(self, key: str) -> str:
|
|
539
658
|
if self.result_storage_block_id is None and (
|
|
@@ -546,8 +665,7 @@ class ResultStore(BaseModel):
|
|
|
546
665
|
return key
|
|
547
666
|
return key
|
|
548
667
|
|
|
549
|
-
|
|
550
|
-
async def _read(self, key: str, holder: str) -> "ResultRecord[Any]":
|
|
668
|
+
async def _aread(self, key: str, holder: str) -> "ResultRecord[Any]":
|
|
551
669
|
"""
|
|
552
670
|
Read a result record from storage.
|
|
553
671
|
|
|
@@ -610,6 +728,69 @@ class ResultStore(BaseModel):
|
|
|
610
728
|
self.cache[resolved_key_path] = result_record
|
|
611
729
|
return result_record
|
|
612
730
|
|
|
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
|
+
|
|
613
794
|
def read(
|
|
614
795
|
self,
|
|
615
796
|
key: str,
|
|
@@ -626,7 +807,7 @@ class ResultStore(BaseModel):
|
|
|
626
807
|
A result record.
|
|
627
808
|
"""
|
|
628
809
|
holder = holder or self.generate_default_holder()
|
|
629
|
-
return self._read(key=key, holder=holder
|
|
810
|
+
return self._read(key=key, holder=holder)
|
|
630
811
|
|
|
631
812
|
async def aread(
|
|
632
813
|
self,
|
|
@@ -644,7 +825,7 @@ class ResultStore(BaseModel):
|
|
|
644
825
|
A result record.
|
|
645
826
|
"""
|
|
646
827
|
holder = holder or self.generate_default_holder()
|
|
647
|
-
return await self.
|
|
828
|
+
return await self._aread(key=key, holder=holder)
|
|
648
829
|
|
|
649
830
|
def create_result_record(
|
|
650
831
|
self,
|
|
@@ -734,8 +915,7 @@ class ResultStore(BaseModel):
|
|
|
734
915
|
holder=holder,
|
|
735
916
|
)
|
|
736
917
|
|
|
737
|
-
|
|
738
|
-
async def _persist_result_record(
|
|
918
|
+
async def _apersist_result_record(
|
|
739
919
|
self, result_record: "ResultRecord[Any]", holder: str
|
|
740
920
|
) -> None:
|
|
741
921
|
"""
|
|
@@ -798,6 +978,69 @@ class ResultStore(BaseModel):
|
|
|
798
978
|
if self.cache_result_in_memory:
|
|
799
979
|
self.cache[key] = result_record
|
|
800
980
|
|
|
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
|
+
|
|
801
1044
|
def persist_result_record(
|
|
802
1045
|
self, result_record: "ResultRecord[Any]", holder: str | None = None
|
|
803
1046
|
) -> None:
|
|
@@ -808,9 +1051,7 @@ class ResultStore(BaseModel):
|
|
|
808
1051
|
result_record: The result record to persist.
|
|
809
1052
|
"""
|
|
810
1053
|
holder = holder or self.generate_default_holder()
|
|
811
|
-
return self._persist_result_record(
|
|
812
|
-
result_record=result_record, holder=holder, _sync=True
|
|
813
|
-
)
|
|
1054
|
+
return self._persist_result_record(result_record=result_record, holder=holder)
|
|
814
1055
|
|
|
815
1056
|
async def apersist_result_record(
|
|
816
1057
|
self, result_record: "ResultRecord[Any]", holder: str | None = None
|
|
@@ -822,8 +1063,8 @@ class ResultStore(BaseModel):
|
|
|
822
1063
|
result_record: The result record to persist.
|
|
823
1064
|
"""
|
|
824
1065
|
holder = holder or self.generate_default_holder()
|
|
825
|
-
return await self.
|
|
826
|
-
result_record=result_record, holder=holder
|
|
1066
|
+
return await self._apersist_result_record(
|
|
1067
|
+
result_record=result_record, holder=holder
|
|
827
1068
|
)
|
|
828
1069
|
|
|
829
1070
|
def supports_isolation_level(self, level: "IsolationLevel") -> bool:
|
prefect/runner/runner.py
CHANGED
|
@@ -56,7 +56,6 @@ from typing import (
|
|
|
56
56
|
TYPE_CHECKING,
|
|
57
57
|
Any,
|
|
58
58
|
Callable,
|
|
59
|
-
Coroutine,
|
|
60
59
|
Dict,
|
|
61
60
|
Iterable,
|
|
62
61
|
List,
|
|
@@ -167,9 +166,10 @@ class Runner:
|
|
|
167
166
|
query_seconds: The number of seconds to wait between querying for
|
|
168
167
|
scheduled flow runs; defaults to `PREFECT_RUNNER_POLL_FREQUENCY`
|
|
169
168
|
prefetch_seconds: The number of seconds to prefetch flow runs for.
|
|
170
|
-
heartbeat_seconds: The number of seconds
|
|
171
|
-
flow
|
|
172
|
-
|
|
169
|
+
heartbeat_seconds: The number of seconds between heartbeat events emitted
|
|
170
|
+
by flow runs managed by this runner. If not provided, the value of
|
|
171
|
+
`PREFECT_FLOWS_HEARTBEAT_FREQUENCY` will be used. Heartbeats are used
|
|
172
|
+
to detect crashed flow runs.
|
|
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,6 +202,8 @@ class Runner:
|
|
|
202
202
|
asyncio.run(runner.start())
|
|
203
203
|
```
|
|
204
204
|
"""
|
|
205
|
+
self._heartbeat_seconds = heartbeat_seconds
|
|
206
|
+
|
|
205
207
|
settings = get_current_settings()
|
|
206
208
|
|
|
207
209
|
if name and ("/" in name or "%" in name):
|
|
@@ -221,12 +223,6 @@ class Runner:
|
|
|
221
223
|
|
|
222
224
|
self.query_seconds: float = query_seconds or settings.runner.poll_frequency
|
|
223
225
|
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
|
|
230
226
|
self._events_client: EventsClient = get_events_client(checkpoint_every=1)
|
|
231
227
|
|
|
232
228
|
self._exit_stack = AsyncExitStack()
|
|
@@ -644,9 +640,6 @@ class Runner:
|
|
|
644
640
|
|
|
645
641
|
task_status.started(process.pid)
|
|
646
642
|
|
|
647
|
-
if self.heartbeat_seconds is not None:
|
|
648
|
-
await self._emit_flow_run_heartbeat(flow_run)
|
|
649
|
-
|
|
650
643
|
# Only add the process to the map if it is still running
|
|
651
644
|
# The process may be a multiprocessing.context.SpawnProcess, in which case it will have an `exitcode`` attribute
|
|
652
645
|
# but no `returncode` attribute
|
|
@@ -682,6 +675,11 @@ class Runner:
|
|
|
682
675
|
|
|
683
676
|
flow_run = FlowRun.model_validate(bundle["flow_run"])
|
|
684
677
|
|
|
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
|
+
|
|
685
683
|
async with context:
|
|
686
684
|
if not self._acquire_limit_slot(flow_run.id):
|
|
687
685
|
return
|
|
@@ -695,9 +693,6 @@ class Runner:
|
|
|
695
693
|
await self._propose_crashed_state(flow_run, msg)
|
|
696
694
|
raise RuntimeError(msg)
|
|
697
695
|
|
|
698
|
-
if self.heartbeat_seconds is not None:
|
|
699
|
-
await self._emit_flow_run_heartbeat(flow_run)
|
|
700
|
-
|
|
701
696
|
await self._add_flow_run_process_map_entry(
|
|
702
697
|
flow_run.id, ProcessMapEntry(pid=process.pid, flow_run=flow_run)
|
|
703
698
|
)
|
|
@@ -796,7 +791,14 @@ class Runner:
|
|
|
796
791
|
if flow_run.deployment_id is not None:
|
|
797
792
|
flow = self._deployment_flow_map.get(flow_run.deployment_id)
|
|
798
793
|
if flow:
|
|
799
|
-
|
|
794
|
+
subprocess_env: dict[str, str] = {}
|
|
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
|
+
)
|
|
800
802
|
task_status.started(process)
|
|
801
803
|
await anyio.to_thread.run_sync(process.join)
|
|
802
804
|
return process.exitcode
|
|
@@ -829,6 +831,15 @@ class Runner:
|
|
|
829
831
|
"PREFECT__ENABLE_CANCELLATION_AND_CRASHED_HOOKS": "false",
|
|
830
832
|
},
|
|
831
833
|
**({"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
|
+
),
|
|
832
843
|
}
|
|
833
844
|
)
|
|
834
845
|
env.update(**os.environ) # is this really necessary??
|
|
@@ -1049,51 +1060,6 @@ class Runner:
|
|
|
1049
1060
|
flow = None
|
|
1050
1061
|
return flow, deployment
|
|
1051
1062
|
|
|
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
|
-
|
|
1097
1063
|
def _event_resource(self):
|
|
1098
1064
|
from prefect import __version__
|
|
1099
1065
|
|
|
@@ -1145,7 +1111,7 @@ class Runner:
|
|
|
1145
1111
|
related=related,
|
|
1146
1112
|
)
|
|
1147
1113
|
)
|
|
1148
|
-
self._logger.debug(f"Emitted flow
|
|
1114
|
+
self._logger.debug(f"Emitted cancelled-flow-run event for {flow_run.id}")
|
|
1149
1115
|
|
|
1150
1116
|
async def _get_scheduled_flow_runs(
|
|
1151
1117
|
self,
|
|
@@ -1289,9 +1255,6 @@ class Runner:
|
|
|
1289
1255
|
flow_run.id,
|
|
1290
1256
|
ProcessMapEntry(pid=readiness_result.pid, flow_run=flow_run),
|
|
1291
1257
|
)
|
|
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)
|
|
1295
1258
|
|
|
1296
1259
|
run_logger.info(f"Completed submission of flow run '{flow_run.id}'")
|
|
1297
1260
|
else:
|
|
@@ -1605,15 +1568,6 @@ class Runner:
|
|
|
1605
1568
|
if not hasattr(self, "_loops_task_group") or not self._loops_task_group:
|
|
1606
1569
|
self._loops_task_group: anyio.abc.TaskGroup = anyio.create_task_group()
|
|
1607
1570
|
|
|
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
|
-
|
|
1617
1571
|
self.started = True
|
|
1618
1572
|
return self
|
|
1619
1573
|
|
|
@@ -1632,13 +1586,6 @@ class Runner:
|
|
|
1632
1586
|
shutil.rmtree(str(self._tmp_dir), ignore_errors=True)
|
|
1633
1587
|
del self._runs_task_group, self._loops_task_group
|
|
1634
1588
|
|
|
1635
|
-
if self._heartbeat_task:
|
|
1636
|
-
self._heartbeat_task.cancel()
|
|
1637
|
-
try:
|
|
1638
|
-
await self._heartbeat_task
|
|
1639
|
-
except asyncio.CancelledError:
|
|
1640
|
-
pass
|
|
1641
|
-
|
|
1642
1589
|
def __repr__(self) -> str:
|
|
1643
1590
|
return f"Runner(name={self.name!r})"
|
|
1644
1591
|
|
prefect/runner/storage.py
CHANGED
|
@@ -20,6 +20,7 @@ from anyio import run_process
|
|
|
20
20
|
from pydantic import SecretStr
|
|
21
21
|
|
|
22
22
|
from prefect._internal.concurrency.api import create_call, from_async
|
|
23
|
+
from prefect._internal.urls import strip_auth_from_url
|
|
23
24
|
from prefect.blocks.core import Block, BlockNotSavedError
|
|
24
25
|
from prefect.blocks.system import Secret
|
|
25
26
|
from prefect.filesystems import ReadableDeploymentStorage, WritableDeploymentStorage
|
|
@@ -344,12 +345,13 @@ class GitRepository:
|
|
|
344
345
|
cwd=str(self.destination),
|
|
345
346
|
)
|
|
346
347
|
existing_repo_url = None
|
|
347
|
-
existing_repo_url =
|
|
348
|
+
existing_repo_url = strip_auth_from_url(result.stdout.decode().strip())
|
|
349
|
+
configured_repo_url = strip_auth_from_url(self._url)
|
|
348
350
|
|
|
349
|
-
if existing_repo_url !=
|
|
351
|
+
if existing_repo_url != configured_repo_url:
|
|
350
352
|
raise ValueError(
|
|
351
353
|
f"The existing repository at {str(self.destination)} "
|
|
352
|
-
f"does not match the configured repository {
|
|
354
|
+
f"does not match the configured repository {configured_repo_url}"
|
|
353
355
|
)
|
|
354
356
|
|
|
355
357
|
# Sparsely checkout the repository if directories are specified and the repo is not in sparse-checkout mode already
|
|
@@ -458,7 +460,7 @@ class GitRepository:
|
|
|
458
460
|
else exc
|
|
459
461
|
)
|
|
460
462
|
raise RuntimeError(
|
|
461
|
-
f"Failed to clone repository {
|
|
463
|
+
f"Failed to clone repository {strip_auth_from_url(self._url)!r} with exit code"
|
|
462
464
|
f" {exc.returncode}."
|
|
463
465
|
) from exc_chain
|
|
464
466
|
|
|
@@ -966,24 +968,3 @@ def _format_token_from_credentials(
|
|
|
966
968
|
|
|
967
969
|
# GitHub and other providers: plain token
|
|
968
970
|
return user_provided_token
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
def _strip_auth_from_url(url: str) -> str:
|
|
972
|
-
parsed = urlparse(url)
|
|
973
|
-
|
|
974
|
-
# Construct a new netloc without the auth info
|
|
975
|
-
netloc = parsed.hostname
|
|
976
|
-
if parsed.port and netloc:
|
|
977
|
-
netloc += f":{parsed.port}"
|
|
978
|
-
|
|
979
|
-
# Build the sanitized URL
|
|
980
|
-
return urlunparse(
|
|
981
|
-
(
|
|
982
|
-
parsed.scheme,
|
|
983
|
-
netloc,
|
|
984
|
-
parsed.path,
|
|
985
|
-
parsed.params,
|
|
986
|
-
parsed.query,
|
|
987
|
-
parsed.fragment,
|
|
988
|
-
)
|
|
989
|
-
)
|