prefect 3.6.15__py3-none-any.whl → 3.6.16.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/__init__.py +20 -0
- prefect/_build_info.py +3 -3
- prefect/_internal/analytics/__init__.py +80 -0
- prefect/_internal/analytics/_config.py +2 -0
- prefect/_internal/analytics/ci_detection.py +39 -0
- prefect/_internal/analytics/client.py +132 -0
- prefect/_internal/analytics/device_id.py +55 -0
- prefect/_internal/analytics/emit.py +90 -0
- prefect/_internal/analytics/enabled.py +33 -0
- prefect/_internal/analytics/events.py +14 -0
- prefect/_internal/analytics/milestones.py +177 -0
- prefect/_internal/analytics/notice.py +60 -0
- prefect/_internal/analytics/service.py +238 -0
- prefect/analytics/__init__.py +67 -0
- prefect/deployments/runner.py +20 -0
- prefect/flow_engine.py +16 -0
- prefect/flows.py +8 -0
- prefect/server/ui-v2/assets/{artifact-card-BBzQR-Cj.js → artifact-card-BclosG8t.js} +2 -2
- prefect/server/ui-v2/assets/{artifact-card-BBzQR-Cj.js.map → artifact-card-BclosG8t.js.map} +1 -1
- prefect/server/ui-v2/assets/artifact._id-C0r4BJr2.js +2 -0
- prefect/server/ui-v2/assets/artifact._id-C0r4BJr2.js.map +1 -0
- prefect/server/ui-v2/assets/{automation-wizard-EYSfXlQ7.js → automation-wizard-Bmgt6nn8.js} +2 -2
- prefect/server/ui-v2/assets/{automation-wizard-EYSfXlQ7.js.map → automation-wizard-Bmgt6nn8.js.map} +1 -1
- prefect/server/ui-v2/assets/automation._id-Ccke3KJz.js +2 -0
- prefect/server/ui-v2/assets/{automation._id-Dn7jABys.js.map → automation._id-Ccke3KJz.js.map} +1 -1
- prefect/server/ui-v2/assets/{automation_._id.edit-COrqswD3.js → automation_._id.edit-BlKPUTVf.js} +2 -2
- prefect/server/ui-v2/assets/{automation_._id.edit-COrqswD3.js.map → automation_._id.edit-BlKPUTVf.js.map} +1 -1
- prefect/server/ui-v2/assets/{automations-header-DN_dmMXW.js → automations-header-Df2zw3SC.js} +2 -2
- prefect/server/ui-v2/assets/{automations-header-DN_dmMXW.js.map → automations-header-Df2zw3SC.js.map} +1 -1
- prefect/server/ui-v2/assets/{base-job-template-form-section-De2ACYbw.js → base-job-template-form-section-oZD6U-RW.js} +2 -2
- prefect/server/ui-v2/assets/{base-job-template-form-section-De2ACYbw.js.map → base-job-template-form-section-oZD6U-RW.js.map} +1 -1
- prefect/server/ui-v2/assets/{block-type-details-Cklc7TkK.js → block-type-details-CYrdaLot.js} +2 -2
- prefect/server/ui-v2/assets/{block-type-details-Cklc7TkK.js.map → block-type-details-CYrdaLot.js.map} +1 -1
- prefect/server/ui-v2/assets/block-type-logo-CyAWq7mC.js +2 -0
- prefect/server/ui-v2/assets/block-type-logo-CyAWq7mC.js.map +1 -0
- prefect/server/ui-v2/assets/{block._id-tuz1Bcj2.js → block._id-CBgHrpTP.js} +2 -2
- prefect/server/ui-v2/assets/{block._id-tuz1Bcj2.js.map → block._id-CBgHrpTP.js.map} +1 -1
- prefect/server/ui-v2/assets/{block_._id.edit-CFEYhF5R.js → block_._id.edit-CW-kdi4O.js} +2 -2
- prefect/server/ui-v2/assets/{block_._id.edit-CFEYhF5R.js.map → block_._id.edit-CW-kdi4O.js.map} +1 -1
- prefect/server/ui-v2/assets/{catalog-CCPRo7nY.js → catalog-Brh0kn1I.js} +2 -2
- prefect/server/ui-v2/assets/{catalog-CCPRo7nY.js.map → catalog-Brh0kn1I.js.map} +1 -1
- prefect/server/ui-v2/assets/catalog_._slug-MCgzguIZ.js +2 -0
- prefect/server/ui-v2/assets/{catalog_._slug-Bl2J2oI3.js.map → catalog_._slug-MCgzguIZ.js.map} +1 -1
- prefect/server/ui-v2/assets/{catalog_._slug_.create-9UD_SxDN.js → catalog_._slug_.create-DBaeHi9Q.js} +2 -2
- prefect/server/ui-v2/assets/{catalog_._slug_.create-9UD_SxDN.js.map → catalog_._slug_.create-DBaeHi9Q.js.map} +1 -1
- prefect/server/ui-v2/assets/{collapsible-BVQTBG_U.js → collapsible-B_NJS0uJ.js} +2 -2
- prefect/server/ui-v2/assets/{collapsible-BVQTBG_U.js.map → collapsible-B_NJS0uJ.js.map} +1 -1
- prefect/server/ui-v2/assets/{concurrency-limit._id-CsgRxA0a.js → concurrency-limit._id-qzNUQzQa.js} +2 -2
- prefect/server/ui-v2/assets/{concurrency-limit._id-CsgRxA0a.js.map → concurrency-limit._id-qzNUQzQa.js.map} +1 -1
- prefect/server/ui-v2/assets/create-BwXG6EIb.js +2 -0
- prefect/server/ui-v2/assets/{create-D--LHaji.js.map → create-BwXG6EIb.js.map} +1 -1
- prefect/server/ui-v2/assets/{create-BycfZHGQ.js → create-MoiIE9Ms.js} +2 -2
- prefect/server/ui-v2/assets/{create-BycfZHGQ.js.map → create-MoiIE9Ms.js.map} +1 -1
- prefect/server/ui-v2/assets/data-table-CPkppyg6.js +2 -0
- prefect/server/ui-v2/assets/data-table-CPkppyg6.js.map +1 -0
- prefect/server/ui-v2/assets/delete-confirmation-dialog-CwDK_2QS.js +2 -0
- prefect/server/ui-v2/assets/{delete-confirmation-dialog-DHkm2p8m.js.map → delete-confirmation-dialog-CwDK_2QS.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-action-header-D5GOooPm.js → deployment-action-header-Vp9YsCJO.js} +2 -2
- prefect/server/ui-v2/assets/{deployment-action-header-D5GOooPm.js.map → deployment-action-header-Vp9YsCJO.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-form-wfg6HeAJ.js → deployment-form-CJcg_6DJ.js} +3 -3
- prefect/server/ui-v2/assets/{deployment-form-wfg6HeAJ.js.map → deployment-form-CJcg_6DJ.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment-links-6iugOgDh.js → deployment-links-CIkJIuHh.js} +2 -2
- prefect/server/ui-v2/assets/{deployment-links-6iugOgDh.js.map → deployment-links-CIkJIuHh.js.map} +1 -1
- prefect/server/ui-v2/assets/deployment._id-C6XrGMp_.js +2 -0
- prefect/server/ui-v2/assets/{deployment._id-Ci9zge5n.js.map → deployment._id-C6XrGMp_.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment._id-whsGcHdu.js → deployment._id-CWgTYmTG.js} +2 -2
- prefect/server/ui-v2/assets/{deployment._id-whsGcHdu.js.map → deployment._id-CWgTYmTG.js.map} +1 -1
- prefect/server/ui-v2/assets/deployment_._id.duplicate-Bg0s0ZJn.js +2 -0
- prefect/server/ui-v2/assets/{deployment_._id.duplicate-D9ZWkh8H.js.map → deployment_._id.duplicate-Bg0s0ZJn.js.map} +1 -1
- prefect/server/ui-v2/assets/deployment_._id.edit-CnoB0UeG.js +2 -0
- prefect/server/ui-v2/assets/{deployment_._id.edit-CS7sObzC.js.map → deployment_._id.edit-CnoB0UeG.js.map} +1 -1
- prefect/server/ui-v2/assets/{deployment_._id.run-g7SZuDFi.js → deployment_._id.run-BqPK6rTB.js} +2 -2
- prefect/server/ui-v2/assets/{deployment_._id.run-g7SZuDFi.js.map → deployment_._id.run-BqPK6rTB.js.map} +1 -1
- prefect/server/ui-v2/assets/{dropdown-menu-0atiOSFo.js → dropdown-menu-D7Gwbd15.js} +2 -2
- prefect/server/ui-v2/assets/{dropdown-menu-0atiOSFo.js.map → dropdown-menu-D7Gwbd15.js.map} +1 -1
- prefect/server/ui-v2/assets/{event-resource-display-DHiAb1Up.js → event-resource-display-CDRgVKd4.js} +2 -2
- prefect/server/ui-v2/assets/{event-resource-display-DHiAb1Up.js.map → event-resource-display-CDRgVKd4.js.map} +1 -1
- prefect/server/ui-v2/assets/event._eventDate._eventId-C73GMDkK.js +2 -0
- prefect/server/ui-v2/assets/{event._eventDate._eventId-D7Twzb22.js.map → event._eventDate._eventId-C73GMDkK.js.map} +1 -1
- prefect/server/ui-v2/assets/{flow-run-graph-BhzuSJLm.js → flow-run-graph-ckiE3mA9.js} +2 -2
- prefect/server/ui-v2/assets/{flow-run-graph-BhzuSJLm.js.map → flow-run-graph-ckiE3mA9.js.map} +1 -1
- prefect/server/ui-v2/assets/{flow-run._id-CqpH6bvx.js → flow-run._id-BukrNCAq.js} +2 -2
- prefect/server/ui-v2/assets/{flow-run._id-CqpH6bvx.js.map → flow-run._id-BukrNCAq.js.map} +1 -1
- prefect/server/ui-v2/assets/{flow-run._id-lIIQBD7H.js → flow-run._id-CtI5VZ2M.js} +2 -2
- prefect/server/ui-v2/assets/{flow-run._id-lIIQBD7H.js.map → flow-run._id-CtI5VZ2M.js.map} +1 -1
- prefect/server/ui-v2/assets/flow-run._id-s1UJuakA.js +4 -0
- prefect/server/ui-v2/assets/{flow-run._id-RLy2kBh0.js.map → flow-run._id-s1UJuakA.js.map} +1 -1
- prefect/server/ui-v2/assets/{flow-runs-pagination-BATUweRV.js → flow-runs-pagination-SLbYtvaR.js} +2 -2
- prefect/server/ui-v2/assets/{flow-runs-pagination-BATUweRV.js.map → flow-runs-pagination-SLbYtvaR.js.map} +1 -1
- prefect/server/ui-v2/assets/flow._id-CZ5P0v4N.js +2 -0
- prefect/server/ui-v2/assets/{flow._id-BdQGWPKP.js.map → flow._id-CZ5P0v4N.js.map} +1 -1
- prefect/server/ui-v2/assets/{form-DLrbC7rV.js → form-mws4law8.js} +2 -2
- prefect/server/ui-v2/assets/{form-DLrbC7rV.js.map → form-mws4law8.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-BTAWyeXv.js → header-Bes1LmY7.js} +2 -2
- prefect/server/ui-v2/assets/{header-BTAWyeXv.js.map → header-Bes1LmY7.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-ChFAx5uR.js → header-Bj_zBR-a.js} +2 -2
- prefect/server/ui-v2/assets/{header-ChFAx5uR.js.map → header-Bj_zBR-a.js.map} +1 -1
- prefect/server/ui-v2/assets/{header-CyQqOj1w.js → header-CPxTHSvz.js} +2 -2
- prefect/server/ui-v2/assets/{header-CyQqOj1w.js.map → header-CPxTHSvz.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-m48E4KcG.js → index-B8vo2Lrg.js} +5 -5
- prefect/server/ui-v2/assets/{index-m48E4KcG.js.map → index-B8vo2Lrg.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-D5RT-SAp.js → index-BBeh-3Nh.js} +2 -2
- prefect/server/ui-v2/assets/{index-D5RT-SAp.js.map → index-BBeh-3Nh.js.map} +1 -1
- prefect/server/ui-v2/assets/index-BLnEvHia.js +2 -0
- prefect/server/ui-v2/assets/{index-7i4X9TeK.js.map → index-BLnEvHia.js.map} +1 -1
- prefect/server/ui-v2/assets/index-BpKoubXu.js +2 -0
- prefect/server/ui-v2/assets/index-BpKoubXu.js.map +1 -0
- prefect/server/ui-v2/assets/index-By6YGqR3.js +2 -0
- prefect/server/ui-v2/assets/index-By6YGqR3.js.map +1 -0
- prefect/server/ui-v2/assets/index-CEi3BlKI.js +2 -0
- prefect/server/ui-v2/assets/index-CEi3BlKI.js.map +1 -0
- prefect/server/ui-v2/assets/{index-BaWDC4da.js → index-CS-zxv7Z.js} +2 -2
- prefect/server/ui-v2/assets/{index-BaWDC4da.js.map → index-CS-zxv7Z.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-kP_I5Qfd.js → index-CToHKh4q.js} +2 -2
- prefect/server/ui-v2/assets/{index-kP_I5Qfd.js.map → index-CToHKh4q.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CKGj5lSu.js → index-CUVvZndW.js} +2 -2
- prefect/server/ui-v2/assets/{index-CKGj5lSu.js.map → index-CUVvZndW.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CxUfZRkN.js → index-CYVn-I3i.js} +2 -2
- prefect/server/ui-v2/assets/{index-CxUfZRkN.js.map → index-CYVn-I3i.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CnlN-VC_.js → index-Cun0JN4t.js} +2 -2
- prefect/server/ui-v2/assets/{index-CnlN-VC_.js.map → index-Cun0JN4t.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-BwCLKpNb.js → index-D6ynV6U7.js} +2 -2
- prefect/server/ui-v2/assets/{index-BwCLKpNb.js.map → index-D6ynV6U7.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CHf6xi9j.js → index-DdNUJLRW.js} +2 -2
- prefect/server/ui-v2/assets/{index-CHf6xi9j.js.map → index-DdNUJLRW.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-CQnWiUE5.js → index-DlHOXQhu.js} +2 -2
- prefect/server/ui-v2/assets/{index-CQnWiUE5.js.map → index-DlHOXQhu.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-Eqdr6Ff7.js → index-DyVw8YE8.js} +2 -2
- prefect/server/ui-v2/assets/{index-Eqdr6Ff7.js.map → index-DyVw8YE8.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-Btb_-PDp.js → index-DzRz7D2P.js} +2 -2
- prefect/server/ui-v2/assets/{index-Btb_-PDp.js.map → index-DzRz7D2P.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-DQGif3jm.js → index-DzUcVNZg.js} +2 -2
- prefect/server/ui-v2/assets/{index-DQGif3jm.js.map → index-DzUcVNZg.js.map} +1 -1
- prefect/server/ui-v2/assets/{index-DNh9ZBy4.js → index-IBvMMs6S.js} +2 -2
- prefect/server/ui-v2/assets/{index-DNh9ZBy4.js.map → index-IBvMMs6S.js.map} +1 -1
- prefect/server/ui-v2/assets/index-_kpA__te.js +2 -0
- prefect/server/ui-v2/assets/{index-CiSFHkhI.js.map → index-_kpA__te.js.map} +1 -1
- prefect/server/ui-v2/assets/index-m9O-nIOl.css +1 -0
- prefect/server/ui-v2/assets/{index-lyQav_XM.js → index-wOvyf10b.js} +2 -2
- prefect/server/ui-v2/assets/{index-lyQav_XM.js.map → index-wOvyf10b.js.map} +1 -1
- prefect/server/ui-v2/assets/{json-input-Ba-BeBcw.js → json-input-Ce-HlRqa.js} +2 -2
- prefect/server/ui-v2/assets/{json-input-Ba-BeBcw.js.map → json-input-Ce-HlRqa.js.map} +1 -1
- prefect/server/ui-v2/assets/{key-value-DkSKn6jE.js → key-value-XHEZOtZX.js} +2 -2
- prefect/server/ui-v2/assets/{key-value-DkSKn6jE.js.map → key-value-XHEZOtZX.js.map} +1 -1
- prefect/server/ui-v2/assets/{key._key-Cjx57ymg.js → key._key-LpsMf3zD.js} +2 -2
- prefect/server/ui-v2/assets/{key._key-Cjx57ymg.js.map → key._key-LpsMf3zD.js.map} +1 -1
- prefect/server/ui-v2/assets/{lazy-markdown-Du2Xu3Yp.js → lazy-markdown-1Hz0xzca.js} +2 -2
- prefect/server/ui-v2/assets/{lazy-markdown-Du2Xu3Yp.js.map → lazy-markdown-1Hz0xzca.js.map} +1 -1
- prefect/server/ui-v2/assets/{login-DxMF9Jvj.js → login-D5uepl9L.js} +2 -2
- prefect/server/ui-v2/assets/{login-DxMF9Jvj.js.map → login-D5uepl9L.js.map} +1 -1
- prefect/server/ui-v2/assets/{markdown-input-DPi8zyRr.js → markdown-input-CG6M9zD0.js} +2 -2
- prefect/server/ui-v2/assets/{markdown-input-DPi8zyRr.js.map → markdown-input-CG6M9zD0.js.map} +1 -1
- prefect/server/ui-v2/assets/python-example-snippet-DRHcUlCX.js +3 -0
- prefect/server/ui-v2/assets/{python-example-snippet-DfCFYPN_.js.map → python-example-snippet-DRHcUlCX.js.map} +1 -1
- prefect/server/ui-v2/assets/{python-input-mr6GD840.js → python-input-BsUS8fw1.js} +2 -2
- prefect/server/ui-v2/assets/{python-input-mr6GD840.js.map → python-input-BsUS8fw1.js.map} +1 -1
- prefect/server/ui-v2/assets/{radio-group-Co3cN0Kv.js → radio-group-BUsmwdrt.js} +2 -2
- prefect/server/ui-v2/assets/{radio-group-Co3cN0Kv.js.map → radio-group-BUsmwdrt.js.map} +1 -1
- prefect/server/ui-v2/assets/{route-error-state-DnBaNT2T.js → route-error-state-BFBpiIhD.js} +2 -2
- prefect/server/ui-v2/assets/{route-error-state-DnBaNT2T.js.map → route-error-state-BFBpiIhD.js.map} +1 -1
- prefect/server/ui-v2/assets/{schema-form-CWDHSME9.js → schema-form-BwTmkvJk.js} +2 -2
- prefect/server/ui-v2/assets/{schema-form-CWDHSME9.js.map → schema-form-BwTmkvJk.js.map} +1 -1
- prefect/server/ui-v2/assets/{schema-form-input-string-format-datetime-BSWlW_aQ.js → schema-form-input-string-format-datetime-nrb3g-JJ.js} +4 -4
- prefect/server/ui-v2/assets/{schema-form-input-string-format-datetime-BSWlW_aQ.js.map → schema-form-input-string-format-datetime-nrb3g-JJ.js.map} +1 -1
- prefect/server/ui-v2/assets/{settings-DU5y6tJE.js → settings-CUBtK5aW.js} +2 -2
- prefect/server/ui-v2/assets/{settings-DU5y6tJE.js.map → settings-CUBtK5aW.js.map} +1 -1
- prefect/server/ui-v2/assets/{sort-filter-DpuWNkQo.js → sort-filter-Cv8zAXMp.js} +2 -2
- prefect/server/ui-v2/assets/{sort-filter-DpuWNkQo.js.map → sort-filter-Cv8zAXMp.js.map} +1 -1
- prefect/server/ui-v2/assets/{table-bBL6C0ZK.js → table-BBNGfPra.js} +2 -2
- prefect/server/ui-v2/assets/{table-bBL6C0ZK.js.map → table-BBNGfPra.js.map} +1 -1
- prefect/server/ui-v2/assets/{tags-input-BLPMVWpZ.js → tags-input-DHrnkwjQ.js} +2 -2
- prefect/server/ui-v2/assets/{tags-input-BLPMVWpZ.js.map → tags-input-DHrnkwjQ.js.map} +1 -1
- prefect/server/ui-v2/assets/task-run-concurrency-limits-reset-dialog-Z8-yFZ2_.js +2 -0
- prefect/server/ui-v2/assets/{task-run-concurrency-limits-reset-dialog-S69sih55.js.map → task-run-concurrency-limits-reset-dialog-Z8-yFZ2_.js.map} +1 -1
- prefect/server/ui-v2/assets/{task-run._id-Dl3Azs8t.js → task-run._id-BY58gqs2.js} +2 -2
- prefect/server/ui-v2/assets/{task-run._id-Dl3Azs8t.js.map → task-run._id-BY58gqs2.js.map} +1 -1
- prefect/server/ui-v2/assets/{task-run._id-B_tIDm4K.js → task-run._id-u73Jxfqw.js} +3 -3
- prefect/server/ui-v2/assets/{task-run._id-B_tIDm4K.js.map → task-run._id-u73Jxfqw.js.map} +1 -1
- prefect/server/ui-v2/assets/task-runs-pagination-DUy3147A.js +2 -0
- prefect/server/ui-v2/assets/{task-runs-pagination-VdR-LO4r.js.map → task-runs-pagination-DUy3147A.js.map} +1 -1
- prefect/server/ui-v2/assets/{textarea-B3_TtPRi.js → textarea-DUhh6-kq.js} +2 -2
- prefect/server/ui-v2/assets/{textarea-B3_TtPRi.js.map → textarea-DUhh6-kq.js.map} +1 -1
- prefect/server/ui-v2/assets/timezone-select-D4Q6VBtD.js +2 -0
- prefect/server/ui-v2/assets/{timezone-select-BuPYznge.js.map → timezone-select-D4Q6VBtD.js.map} +1 -1
- prefect/server/ui-v2/assets/{toggle-group-Cn07UIim.js → toggle-group-7WUJn2Tx.js} +2 -2
- prefect/server/ui-v2/assets/{toggle-group-Cn07UIim.js.map → toggle-group-7WUJn2Tx.js.map} +1 -1
- prefect/server/ui-v2/assets/use-delete-automation-confirmation-dialog-D4AD5Ndf.js +2 -0
- prefect/server/ui-v2/assets/{use-delete-automation-confirmation-dialog-DpdsLH4t.js.map → use-delete-automation-confirmation-dialog-D4AD5Ndf.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-delete-block-document-confirmation-dialog-Cq5nGhzr.js → use-delete-block-document-confirmation-dialog-DnwWEGUY.js} +2 -2
- prefect/server/ui-v2/assets/{use-delete-block-document-confirmation-dialog-Cq5nGhzr.js.map → use-delete-block-document-confirmation-dialog-DnwWEGUY.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-flow-runs-selected-rows-DmQSWBTu.js → use-flow-runs-selected-rows-C-tehPRB.js} +2 -2
- prefect/server/ui-v2/assets/{use-flow-runs-selected-rows-DmQSWBTu.js.map → use-flow-runs-selected-rows-C-tehPRB.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-get-artifacts-flow-task-runs-DTwcWiqW.js → use-get-artifacts-flow-task-runs-DRWRrmrb.js} +2 -2
- prefect/server/ui-v2/assets/{use-get-artifacts-flow-task-runs-DTwcWiqW.js.map → use-get-artifacts-flow-task-runs-DRWRrmrb.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-quick-run-CPempwLh.js → use-quick-run-B4Xj-YQQ.js} +2 -2
- prefect/server/ui-v2/assets/{use-quick-run-CPempwLh.js.map → use-quick-run-B4Xj-YQQ.js.map} +1 -1
- prefect/server/ui-v2/assets/use-state-favicon-au2TZdMV.js +2 -0
- prefect/server/ui-v2/assets/{use-state-favicon-M09DmrNy.js.map → use-state-favicon-au2TZdMV.js.map} +1 -1
- prefect/server/ui-v2/assets/{use-stepper-CDucnvzp.js → use-stepper-DyIb3vlq.js} +2 -2
- prefect/server/ui-v2/assets/{use-stepper-CDucnvzp.js.map → use-stepper-DyIb3vlq.js.map} +1 -1
- prefect/server/ui-v2/assets/{utilities-BRCNRcQl.js → utilities-D9Y2wo66.js} +2 -2
- prefect/server/ui-v2/assets/{utilities-BRCNRcQl.js.map → utilities-D9Y2wo66.js.map} +1 -1
- prefect/server/ui-v2/assets/vendor-recharts-BvvJP9Po.js +34 -0
- prefect/server/ui-v2/assets/vendor-recharts-BvvJP9Po.js.map +1 -0
- prefect/server/ui-v2/assets/{work-pool-filter-C2tpSS-w.js → work-pool-filter-CSOATj2D.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool-filter-C2tpSS-w.js.map → work-pool-filter-CSOATj2D.js.map} +1 -1
- prefect/server/ui-v2/assets/work-pool-queue-toggle-Dh7B1zMo.js +2 -0
- prefect/server/ui-v2/assets/{work-pool-queue-toggle-BQ9b-Wxy.js.map → work-pool-queue-toggle-Dh7B1zMo.js.map} +1 -1
- prefect/server/ui-v2/assets/work-pool._workPoolName-B_NFWXKS.js +2 -0
- prefect/server/ui-v2/assets/{work-pool._workPoolName-C_LiL3SE.js.map → work-pool._workPoolName-B_NFWXKS.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.edit-DBHlrVVx.js → work-pool_._workPoolName.edit-Cyzn8g-S.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.edit-DBHlrVVx.js.map → work-pool_._workPoolName.edit-Cyzn8g-S.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-DkITxJOP.js → work-pool_._workPoolName.queue._workQueueName-DQzqQWPT.js} +2 -2
- prefect/server/ui-v2/assets/{work-pool_._workPoolName.queue._workQueueName-DkITxJOP.js.map → work-pool_._workPoolName.queue._workQueueName-DQzqQWPT.js.map} +1 -1
- prefect/server/ui-v2/assets/{work-queue-icon-text-D4BWBVDN.js → work-queue-icon-text-BOpcJ8YU.js} +2 -2
- prefect/server/ui-v2/assets/{work-queue-icon-text-D4BWBVDN.js.map → work-queue-icon-text-BOpcJ8YU.js.map} +1 -1
- prefect/server/ui-v2/index.html +3 -3
- {prefect-3.6.15.dist-info → prefect-3.6.16.dev2.dist-info}/METADATA +2 -1
- {prefect-3.6.15.dist-info → prefect-3.6.16.dev2.dist-info}/RECORD +222 -210
- prefect/server/ui-v2/assets/artifact._id-BFdf1WUA.js +0 -2
- prefect/server/ui-v2/assets/artifact._id-BFdf1WUA.js.map +0 -1
- prefect/server/ui-v2/assets/automation._id-Dn7jABys.js +0 -2
- prefect/server/ui-v2/assets/block-type-logo-Cbpfxded.js +0 -2
- prefect/server/ui-v2/assets/block-type-logo-Cbpfxded.js.map +0 -1
- prefect/server/ui-v2/assets/catalog_._slug-Bl2J2oI3.js +0 -2
- prefect/server/ui-v2/assets/create-D--LHaji.js +0 -2
- prefect/server/ui-v2/assets/data-table-rlbkLcrY.js +0 -2
- prefect/server/ui-v2/assets/data-table-rlbkLcrY.js.map +0 -1
- prefect/server/ui-v2/assets/delete-confirmation-dialog-DHkm2p8m.js +0 -2
- prefect/server/ui-v2/assets/deployment._id-Ci9zge5n.js +0 -2
- prefect/server/ui-v2/assets/deployment_._id.duplicate-D9ZWkh8H.js +0 -2
- prefect/server/ui-v2/assets/deployment_._id.edit-CS7sObzC.js +0 -2
- prefect/server/ui-v2/assets/event._eventDate._eventId-D7Twzb22.js +0 -2
- prefect/server/ui-v2/assets/flow-run._id-RLy2kBh0.js +0 -4
- prefect/server/ui-v2/assets/flow._id-BdQGWPKP.js +0 -2
- prefect/server/ui-v2/assets/index-7i4X9TeK.js +0 -2
- prefect/server/ui-v2/assets/index-BkiGTHwU.js +0 -2
- prefect/server/ui-v2/assets/index-BkiGTHwU.js.map +0 -1
- prefect/server/ui-v2/assets/index-COouj_0-.css +0 -1
- prefect/server/ui-v2/assets/index-CiSFHkhI.js +0 -2
- prefect/server/ui-v2/assets/index-DATPYZfT.js +0 -2
- prefect/server/ui-v2/assets/index-DATPYZfT.js.map +0 -1
- prefect/server/ui-v2/assets/index-VH4TnZ4e.js +0 -2
- prefect/server/ui-v2/assets/index-VH4TnZ4e.js.map +0 -1
- prefect/server/ui-v2/assets/python-example-snippet-DfCFYPN_.js +0 -3
- prefect/server/ui-v2/assets/task-run-concurrency-limits-reset-dialog-S69sih55.js +0 -2
- prefect/server/ui-v2/assets/task-runs-pagination-VdR-LO4r.js +0 -2
- prefect/server/ui-v2/assets/timezone-select-BuPYznge.js +0 -2
- prefect/server/ui-v2/assets/use-delete-automation-confirmation-dialog-DpdsLH4t.js +0 -2
- prefect/server/ui-v2/assets/use-state-favicon-M09DmrNy.js +0 -2
- prefect/server/ui-v2/assets/vendor-recharts-BAN776s_.js +0 -34
- prefect/server/ui-v2/assets/vendor-recharts-BAN776s_.js.map +0 -1
- prefect/server/ui-v2/assets/work-pool-queue-toggle-BQ9b-Wxy.js +0 -2
- prefect/server/ui-v2/assets/work-pool._workPoolName-C_LiL3SE.js +0 -2
- {prefect-3.6.15.dist-info → prefect-3.6.16.dev2.dist-info}/WHEEL +0 -0
- {prefect-3.6.15.dist-info → prefect-3.6.16.dev2.dist-info}/entry_points.txt +0 -0
- {prefect-3.6.15.dist-info → prefect-3.6.16.dev2.dist-info}/licenses/LICENSE +0 -0
prefect/__init__.py
CHANGED
|
@@ -130,6 +130,26 @@ def _initialize_plugins() -> None:
|
|
|
130
130
|
# Initialize plugins on import if enabled
|
|
131
131
|
_initialize_plugins()
|
|
132
132
|
|
|
133
|
+
|
|
134
|
+
def _initialize_sdk_analytics() -> None:
|
|
135
|
+
"""
|
|
136
|
+
Initialize SDK analytics for telemetry.
|
|
137
|
+
|
|
138
|
+
This runs automatically when Prefect is imported. Errors are silently
|
|
139
|
+
ignored to ensure analytics never impacts normal Prefect operation.
|
|
140
|
+
"""
|
|
141
|
+
try:
|
|
142
|
+
from prefect._internal.analytics import initialize_analytics
|
|
143
|
+
|
|
144
|
+
initialize_analytics()
|
|
145
|
+
except Exception:
|
|
146
|
+
# Never let analytics initialization impact Prefect
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# Initialize SDK analytics on import
|
|
151
|
+
_initialize_sdk_analytics()
|
|
152
|
+
|
|
133
153
|
_public_api: dict[str, tuple[Optional[str], str]] = {
|
|
134
154
|
"allow_failure": (__spec__.parent, ".main"),
|
|
135
155
|
"apause_flow_run": (__spec__.parent, ".main"),
|
prefect/_build_info.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Generated by versioningit
|
|
2
|
-
__version__ = "3.6.
|
|
3
|
-
__build_date__ = "2026-01-30
|
|
4
|
-
__git_commit__ = "
|
|
2
|
+
__version__ = "3.6.16.dev2"
|
|
3
|
+
__build_date__ = "2026-01-30 21:51:06.104489+00:00"
|
|
4
|
+
__git_commit__ = "17574a7bfd5d441988acf1b86b1aa618e06af271"
|
|
5
5
|
__dirty__ = False
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Internal implementation for SDK Analytics.
|
|
3
|
+
|
|
4
|
+
This module contains internal functions that should not be used directly.
|
|
5
|
+
Use the public API from prefect.analytics instead.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from prefect._internal.analytics.emit import (
|
|
9
|
+
_is_interactive_terminal,
|
|
10
|
+
emit_integration_event,
|
|
11
|
+
emit_sdk_event,
|
|
12
|
+
)
|
|
13
|
+
from prefect._internal.analytics.enabled import is_telemetry_enabled
|
|
14
|
+
from prefect._internal.analytics.milestones import (
|
|
15
|
+
_mark_existing_user_milestones,
|
|
16
|
+
try_mark_milestone,
|
|
17
|
+
)
|
|
18
|
+
from prefect._internal.analytics.notice import maybe_show_telemetry_notice
|
|
19
|
+
|
|
20
|
+
# Track initialization state
|
|
21
|
+
_telemetry_initialized = False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def initialize_analytics() -> None:
|
|
25
|
+
"""
|
|
26
|
+
Initialize SDK analytics on Prefect import.
|
|
27
|
+
|
|
28
|
+
This function:
|
|
29
|
+
1. Checks if telemetry is enabled (quick local checks only - non-blocking)
|
|
30
|
+
2. Detects existing users and pre-marks their milestones (no events emitted)
|
|
31
|
+
3. For new users: shows the first-run notice and emits first_sdk_import
|
|
32
|
+
|
|
33
|
+
Onboarding events are only emitted in interactive terminals to avoid
|
|
34
|
+
tracking deployed flow runs (e.g., Kubernetes jobs) as new users.
|
|
35
|
+
|
|
36
|
+
Called automatically when Prefect is imported.
|
|
37
|
+
"""
|
|
38
|
+
global _telemetry_initialized
|
|
39
|
+
|
|
40
|
+
if _telemetry_initialized:
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
_telemetry_initialized = True
|
|
44
|
+
|
|
45
|
+
# Quick local checks only - no network calls
|
|
46
|
+
if not is_telemetry_enabled():
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
# Only emit onboarding events in interactive terminals
|
|
50
|
+
# This prevents deployed flow runs from being counted as new users
|
|
51
|
+
if not _is_interactive_terminal():
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
# Check for existing users and pre-mark their milestones
|
|
56
|
+
# This must happen BEFORE any events are emitted
|
|
57
|
+
is_existing_user = _mark_existing_user_milestones()
|
|
58
|
+
|
|
59
|
+
# Don't emit onboarding events for existing users
|
|
60
|
+
if is_existing_user:
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
# Show first-run notice (only in interactive terminals)
|
|
64
|
+
maybe_show_telemetry_notice()
|
|
65
|
+
|
|
66
|
+
# Emit first_sdk_import event for new users only (tracked as a milestone
|
|
67
|
+
# so it is only emitted once per installation)
|
|
68
|
+
try_mark_milestone("first_sdk_import")
|
|
69
|
+
except Exception:
|
|
70
|
+
pass # Silently ignore initialization errors
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
__all__ = [
|
|
74
|
+
"initialize_analytics",
|
|
75
|
+
"is_telemetry_enabled",
|
|
76
|
+
"emit_sdk_event",
|
|
77
|
+
"emit_integration_event",
|
|
78
|
+
"try_mark_milestone",
|
|
79
|
+
"_is_interactive_terminal",
|
|
80
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CI environment detection for SDK telemetry.
|
|
3
|
+
|
|
4
|
+
Telemetry is automatically disabled in CI environments.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
# Common CI environment variables
|
|
10
|
+
CI_ENV_VARS = frozenset(
|
|
11
|
+
{
|
|
12
|
+
"CI",
|
|
13
|
+
"GITHUB_ACTIONS",
|
|
14
|
+
"GITLAB_CI",
|
|
15
|
+
"JENKINS_URL",
|
|
16
|
+
"TRAVIS",
|
|
17
|
+
"CIRCLECI",
|
|
18
|
+
"BUILDKITE",
|
|
19
|
+
"TF_BUILD", # Azure DevOps
|
|
20
|
+
"CODEBUILD_BUILD_ID", # AWS CodeBuild
|
|
21
|
+
"BITBUCKET_COMMIT",
|
|
22
|
+
"TEAMCITY_VERSION",
|
|
23
|
+
"DRONE",
|
|
24
|
+
"SEMAPHORE",
|
|
25
|
+
"APPVEYOR",
|
|
26
|
+
"BUDDY",
|
|
27
|
+
"CI_NAME", # Generic CI indicator
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def is_ci_environment() -> bool:
|
|
33
|
+
"""
|
|
34
|
+
Check if the current environment is a CI environment.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
True if any known CI environment variable is set
|
|
38
|
+
"""
|
|
39
|
+
return any(os.environ.get(var) for var in CI_ENV_VARS)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Amplitude client wrapper for SDK telemetry.
|
|
3
|
+
|
|
4
|
+
Provides fire-and-forget event tracking with silent failure handling.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import atexit
|
|
8
|
+
import logging
|
|
9
|
+
import platform
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from amplitude import Amplitude, BaseEvent, Config
|
|
13
|
+
|
|
14
|
+
import prefect
|
|
15
|
+
|
|
16
|
+
# Amplitude API key for SDK telemetry
|
|
17
|
+
# This is a write-only key that can only send events, not read data
|
|
18
|
+
try:
|
|
19
|
+
from prefect._internal.analytics._config import AMPLITUDE_API_KEY
|
|
20
|
+
except ImportError:
|
|
21
|
+
AMPLITUDE_API_KEY = "YOUR_AMPLITUDE_API_KEY_HERE"
|
|
22
|
+
|
|
23
|
+
# Module-level client instance
|
|
24
|
+
_amplitude_client: Amplitude | None = None
|
|
25
|
+
_initialized = False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _get_event_properties() -> dict[str, str]:
|
|
29
|
+
"""Get common event properties included with all events."""
|
|
30
|
+
return {
|
|
31
|
+
"prefect_version": prefect.__version__,
|
|
32
|
+
"python_version": platform.python_version(),
|
|
33
|
+
"platform": platform.system(),
|
|
34
|
+
"architecture": platform.machine(),
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _initialize_client() -> bool:
|
|
39
|
+
"""
|
|
40
|
+
Initialize the Amplitude client.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
True if initialization succeeded, False otherwise
|
|
44
|
+
"""
|
|
45
|
+
global _amplitude_client, _initialized
|
|
46
|
+
|
|
47
|
+
if _initialized:
|
|
48
|
+
return _amplitude_client is not None
|
|
49
|
+
|
|
50
|
+
_initialized = True
|
|
51
|
+
|
|
52
|
+
if AMPLITUDE_API_KEY == "YOUR_AMPLITUDE_API_KEY_HERE":
|
|
53
|
+
# API key not configured - telemetry disabled
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
# Create a silent logger for Amplitude to prevent SDK errors from reaching users
|
|
58
|
+
amplitude_logger = logging.getLogger("amplitude")
|
|
59
|
+
amplitude_logger.setLevel(logging.CRITICAL)
|
|
60
|
+
|
|
61
|
+
config = Config(
|
|
62
|
+
# Flush events after a short delay to avoid blocking
|
|
63
|
+
flush_interval_millis=10000, # 10 seconds
|
|
64
|
+
flush_queue_size=10,
|
|
65
|
+
# Minimize network overhead
|
|
66
|
+
min_id_length=1,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
_amplitude_client = Amplitude(AMPLITUDE_API_KEY, config)
|
|
70
|
+
|
|
71
|
+
# Register shutdown handler to flush remaining events
|
|
72
|
+
atexit.register(_shutdown_client)
|
|
73
|
+
|
|
74
|
+
return True
|
|
75
|
+
except Exception:
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _shutdown_client() -> None:
|
|
80
|
+
"""Shutdown the Amplitude client, flushing any remaining events."""
|
|
81
|
+
global _amplitude_client
|
|
82
|
+
|
|
83
|
+
if _amplitude_client is not None:
|
|
84
|
+
try:
|
|
85
|
+
_amplitude_client.shutdown()
|
|
86
|
+
except Exception:
|
|
87
|
+
pass
|
|
88
|
+
_amplitude_client = None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def track_event(
|
|
92
|
+
event_name: str,
|
|
93
|
+
device_id: str,
|
|
94
|
+
extra_properties: dict[str, Any] | None = None,
|
|
95
|
+
) -> bool:
|
|
96
|
+
"""
|
|
97
|
+
Track an event with Amplitude.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
event_name: The name of the event to track
|
|
101
|
+
device_id: The anonymous device identifier
|
|
102
|
+
extra_properties: Additional event properties
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
True if the event was tracked, False otherwise
|
|
106
|
+
"""
|
|
107
|
+
properties = _get_event_properties()
|
|
108
|
+
if extra_properties:
|
|
109
|
+
properties.update(extra_properties)
|
|
110
|
+
|
|
111
|
+
if not _initialize_client():
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
event = BaseEvent(
|
|
116
|
+
event_type=event_name,
|
|
117
|
+
device_id=device_id,
|
|
118
|
+
event_properties=properties,
|
|
119
|
+
)
|
|
120
|
+
_amplitude_client.track(event)
|
|
121
|
+
return True
|
|
122
|
+
except Exception:
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def flush() -> None:
|
|
127
|
+
"""Flush any pending events immediately."""
|
|
128
|
+
if _amplitude_client is not None:
|
|
129
|
+
try:
|
|
130
|
+
_amplitude_client.flush()
|
|
131
|
+
except Exception:
|
|
132
|
+
pass
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Device ID generation and persistence for SDK telemetry.
|
|
3
|
+
|
|
4
|
+
The device ID is an anonymous identifier used to correlate events
|
|
5
|
+
from the same installation without identifying the user.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from uuid import uuid4
|
|
11
|
+
|
|
12
|
+
from prefect.settings import get_current_settings
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _get_device_id_path() -> Path:
|
|
16
|
+
"""Get the path to the device ID file."""
|
|
17
|
+
settings = get_current_settings()
|
|
18
|
+
return settings.home / ".sdk_telemetry" / "device_id"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_or_create_device_id() -> str:
|
|
22
|
+
"""
|
|
23
|
+
Get the existing device ID or create a new one.
|
|
24
|
+
|
|
25
|
+
The device ID is stored in $PREFECT_HOME/.sdk_telemetry/device_id
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
A UUID string identifying this installation
|
|
29
|
+
"""
|
|
30
|
+
device_id_path = _get_device_id_path()
|
|
31
|
+
|
|
32
|
+
# Try to read existing device ID
|
|
33
|
+
if device_id_path.exists():
|
|
34
|
+
try:
|
|
35
|
+
device_id = device_id_path.read_text().strip()
|
|
36
|
+
if device_id:
|
|
37
|
+
return device_id
|
|
38
|
+
except Exception:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
# Generate new device ID
|
|
42
|
+
device_id = str(uuid4())
|
|
43
|
+
|
|
44
|
+
# Persist device ID
|
|
45
|
+
try:
|
|
46
|
+
device_id_path.parent.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
device_id_path.write_text(device_id)
|
|
48
|
+
# Set restrictive permissions (owner read/write only)
|
|
49
|
+
os.chmod(device_id_path, 0o600)
|
|
50
|
+
except Exception:
|
|
51
|
+
# If we can't persist, still return the generated ID
|
|
52
|
+
# (it will be regenerated next time)
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
return device_id
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Event emission for SDK analytics.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from prefect._internal.analytics.device_id import get_or_create_device_id
|
|
9
|
+
from prefect._internal.analytics.events import SDKEvent
|
|
10
|
+
from prefect._internal.analytics.service import AnalyticsEvent, AnalyticsService
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _is_interactive_terminal() -> bool:
|
|
14
|
+
"""Check if we're running in an interactive terminal."""
|
|
15
|
+
try:
|
|
16
|
+
return sys.stdout.isatty() or sys.stderr.isatty()
|
|
17
|
+
except Exception:
|
|
18
|
+
return False
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def emit_sdk_event(
|
|
22
|
+
event_name: SDKEvent,
|
|
23
|
+
extra_properties: dict[str, Any] | None = None,
|
|
24
|
+
) -> bool:
|
|
25
|
+
"""
|
|
26
|
+
Emit an SDK telemetry event.
|
|
27
|
+
|
|
28
|
+
This is an internal function for tracking SDK events.
|
|
29
|
+
Events are queued for processing in a background thread (non-blocking).
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
event_name: The name of the event to track
|
|
33
|
+
extra_properties: Additional event properties
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
True if the event was queued, False otherwise
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
device_id = get_or_create_device_id()
|
|
40
|
+
event = AnalyticsEvent(
|
|
41
|
+
event_name=event_name,
|
|
42
|
+
device_id=device_id,
|
|
43
|
+
extra_properties=extra_properties,
|
|
44
|
+
)
|
|
45
|
+
AnalyticsService.instance().enqueue(event)
|
|
46
|
+
return True
|
|
47
|
+
except Exception:
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def emit_integration_event(
|
|
52
|
+
integration: str,
|
|
53
|
+
event_name: str,
|
|
54
|
+
extra_properties: dict[str, Any] | None = None,
|
|
55
|
+
) -> bool:
|
|
56
|
+
"""
|
|
57
|
+
Emit a telemetry event from an integration library.
|
|
58
|
+
|
|
59
|
+
This is exposed via the public API in prefect.analytics for integration
|
|
60
|
+
libraries (e.g., prefect-aws, prefect-gcp) to emit telemetry events.
|
|
61
|
+
Events are automatically namespaced with the integration name.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
integration: The integration name (e.g., "prefect-aws", "prefect-gcp")
|
|
65
|
+
event_name: The event name (e.g., "s3_block_created")
|
|
66
|
+
extra_properties: Additional event properties
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
True if the event was queued, False otherwise
|
|
70
|
+
"""
|
|
71
|
+
try:
|
|
72
|
+
# Namespace the event with the integration name
|
|
73
|
+
namespaced_event = f"{integration}:{event_name}"
|
|
74
|
+
|
|
75
|
+
device_id = get_or_create_device_id()
|
|
76
|
+
|
|
77
|
+
# Add integration name to properties
|
|
78
|
+
properties = {"integration": integration}
|
|
79
|
+
if extra_properties:
|
|
80
|
+
properties.update(extra_properties)
|
|
81
|
+
|
|
82
|
+
event = AnalyticsEvent(
|
|
83
|
+
event_name=namespaced_event,
|
|
84
|
+
device_id=device_id,
|
|
85
|
+
extra_properties=properties,
|
|
86
|
+
)
|
|
87
|
+
AnalyticsService.instance().enqueue(event)
|
|
88
|
+
return True
|
|
89
|
+
except Exception:
|
|
90
|
+
return False
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Telemetry enabled check for SDK analytics.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from prefect._internal.analytics.ci_detection import is_ci_environment
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_telemetry_enabled() -> bool:
|
|
11
|
+
"""
|
|
12
|
+
Quick non-blocking check of local telemetry settings.
|
|
13
|
+
|
|
14
|
+
Telemetry is disabled if:
|
|
15
|
+
- DO_NOT_TRACK environment variable is set (client-side)
|
|
16
|
+
- Running in a CI environment
|
|
17
|
+
|
|
18
|
+
Note: Server-side analytics check is performed in the background service
|
|
19
|
+
to avoid blocking the main thread.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
True if local telemetry checks pass, False otherwise
|
|
23
|
+
"""
|
|
24
|
+
# Check DO_NOT_TRACK standard (client-side setting)
|
|
25
|
+
do_not_track = os.environ.get("DO_NOT_TRACK", "").lower()
|
|
26
|
+
if do_not_track in ("1", "true", "yes"):
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
# Check CI environment
|
|
30
|
+
if is_ci_environment():
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
return True
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Event type definitions for SDK telemetry.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
# Quick Start Funnel events
|
|
8
|
+
SDKEvent = Literal[
|
|
9
|
+
"first_sdk_import",
|
|
10
|
+
"first_flow_defined",
|
|
11
|
+
"first_flow_run",
|
|
12
|
+
"first_deployment_created",
|
|
13
|
+
"first_schedule_created",
|
|
14
|
+
]
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Milestone tracking for SDK telemetry.
|
|
3
|
+
|
|
4
|
+
Tracks first-* events to avoid duplicate telemetry.
|
|
5
|
+
Milestones are stored in $PREFECT_HOME/.sdk_telemetry/milestones.json
|
|
6
|
+
|
|
7
|
+
For existing users (detected by presence of Prefect artifacts), all milestones
|
|
8
|
+
are pre-marked as reached to avoid emitting onboarding events on upgrade.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Literal
|
|
14
|
+
|
|
15
|
+
from prefect._internal.analytics.emit import _is_interactive_terminal, emit_sdk_event
|
|
16
|
+
from prefect.settings import get_current_settings
|
|
17
|
+
|
|
18
|
+
MilestoneName = Literal[
|
|
19
|
+
"first_sdk_import",
|
|
20
|
+
"first_flow_defined",
|
|
21
|
+
"first_flow_run",
|
|
22
|
+
"first_deployment_created",
|
|
23
|
+
"first_schedule_created",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
# All milestone names for pre-marking existing users
|
|
27
|
+
ALL_MILESTONES: list[MilestoneName] = [
|
|
28
|
+
"first_sdk_import",
|
|
29
|
+
"first_flow_defined",
|
|
30
|
+
"first_flow_run",
|
|
31
|
+
"first_deployment_created",
|
|
32
|
+
"first_schedule_created",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
# Files/directories that indicate an existing Prefect user
|
|
36
|
+
_EXISTING_USER_INDICATORS = [
|
|
37
|
+
"profiles.toml", # User has configured profiles
|
|
38
|
+
".prefect.db", # Local server database
|
|
39
|
+
"prefect.db", # Alternative database location
|
|
40
|
+
"storage", # Result storage directory
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _get_telemetry_dir() -> Path:
|
|
45
|
+
"""Get the path to the telemetry directory."""
|
|
46
|
+
settings = get_current_settings()
|
|
47
|
+
return settings.home / ".sdk_telemetry"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _get_milestones_path() -> Path:
|
|
51
|
+
"""Get the path to the milestones file."""
|
|
52
|
+
return _get_telemetry_dir() / "milestones.json"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _read_milestones() -> dict[str, bool]:
|
|
56
|
+
"""Read milestones from disk."""
|
|
57
|
+
milestones_path = _get_milestones_path()
|
|
58
|
+
if milestones_path.exists():
|
|
59
|
+
try:
|
|
60
|
+
return json.loads(milestones_path.read_text())
|
|
61
|
+
except Exception:
|
|
62
|
+
pass
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _write_milestones(milestones: dict[str, bool]) -> None:
|
|
67
|
+
"""Write milestones to disk."""
|
|
68
|
+
milestones_path = _get_milestones_path()
|
|
69
|
+
try:
|
|
70
|
+
milestones_path.parent.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
milestones_path.write_text(json.dumps(milestones, indent=2))
|
|
72
|
+
except Exception:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _is_existing_user() -> bool:
|
|
77
|
+
"""
|
|
78
|
+
Check if this is an existing Prefect user.
|
|
79
|
+
|
|
80
|
+
An existing user is detected by the presence of common Prefect artifacts
|
|
81
|
+
in PREFECT_HOME that would have been created before telemetry was added.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
True if existing user indicators are found
|
|
85
|
+
"""
|
|
86
|
+
settings = get_current_settings()
|
|
87
|
+
prefect_home = settings.home
|
|
88
|
+
|
|
89
|
+
if not prefect_home.exists():
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
for indicator in _EXISTING_USER_INDICATORS:
|
|
93
|
+
if (prefect_home / indicator).exists():
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _mark_existing_user_milestones() -> bool:
|
|
100
|
+
"""
|
|
101
|
+
Pre-mark all milestones for existing users.
|
|
102
|
+
|
|
103
|
+
This prevents existing users from emitting onboarding events when they
|
|
104
|
+
upgrade to a version with telemetry.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
True if milestones were pre-marked (existing user detected)
|
|
108
|
+
"""
|
|
109
|
+
telemetry_dir = _get_telemetry_dir()
|
|
110
|
+
|
|
111
|
+
# If telemetry directory already exists, we've already handled this
|
|
112
|
+
if telemetry_dir.exists():
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
# Check if this is an existing user
|
|
116
|
+
if not _is_existing_user():
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
# Pre-mark all milestones for existing users
|
|
120
|
+
milestones = {milestone: True for milestone in ALL_MILESTONES}
|
|
121
|
+
_write_milestones(milestones)
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def has_reached_milestone(milestone: MilestoneName) -> bool:
|
|
126
|
+
"""
|
|
127
|
+
Check if a milestone has been reached.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
milestone: The milestone name to check
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
True if the milestone has been recorded
|
|
134
|
+
"""
|
|
135
|
+
milestones = _read_milestones()
|
|
136
|
+
return milestones.get(milestone, False)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def mark_milestone(milestone: MilestoneName) -> None:
|
|
140
|
+
"""
|
|
141
|
+
Mark a milestone as reached.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
milestone: The milestone name to mark
|
|
145
|
+
"""
|
|
146
|
+
milestones = _read_milestones()
|
|
147
|
+
milestones[milestone] = True
|
|
148
|
+
_write_milestones(milestones)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def try_mark_milestone(milestone: MilestoneName) -> bool:
|
|
152
|
+
"""
|
|
153
|
+
Try to mark a milestone and emit an event if it's new.
|
|
154
|
+
|
|
155
|
+
This is the primary entry point for milestone tracking.
|
|
156
|
+
It checks if the milestone is new, marks it, and emits the event.
|
|
157
|
+
|
|
158
|
+
Events are only emitted in interactive terminals to avoid tracking
|
|
159
|
+
deployed flow runs (e.g., Kubernetes jobs) as new users.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
milestone: The milestone name to mark
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
True if this was the first time reaching the milestone
|
|
166
|
+
"""
|
|
167
|
+
# Only emit events in interactive terminals
|
|
168
|
+
# This prevents deployed flow runs from being counted as new users
|
|
169
|
+
if not _is_interactive_terminal():
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
if has_reached_milestone(milestone):
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
mark_milestone(milestone)
|
|
176
|
+
emit_sdk_event(milestone)
|
|
177
|
+
return True
|