prefect-client 3.1.0__tar.gz → 3.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. {prefect-client-3.1.0/src/prefect_client.egg-info → prefect-client-3.1.1}/PKG-INFO +1 -1
  2. {prefect-client-3.1.0 → prefect-client-3.1.1}/requirements-dev.txt +9 -0
  3. {prefect-client-3.1.0 → prefect-client-3.1.1}/requirements.txt +1 -1
  4. prefect-client-3.1.1/src/prefect/_internal/compatibility/async_dispatch.py +73 -0
  5. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_version.py +3 -3
  6. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/orchestration.py +11 -9
  7. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/schemas/objects.py +21 -0
  8. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/logging/handlers.py +41 -0
  9. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/logging/loggers.py +33 -0
  10. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/logging/logging.yml +8 -0
  11. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/main.py +5 -0
  12. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/api.py +1 -1
  13. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/experiments.py +5 -0
  14. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/logging.py +10 -3
  15. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/root.py +1 -1
  16. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/server/events.py +1 -1
  17. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/sources.py +21 -0
  18. prefect-client-3.1.1/src/prefect/telemetry/bootstrap.py +32 -0
  19. prefect-client-3.1.1/src/prefect/telemetry/instrumentation.py +125 -0
  20. prefect-client-3.1.1/src/prefect/telemetry/logging.py +26 -0
  21. prefect-client-3.1.1/src/prefect/telemetry/processors.py +57 -0
  22. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/types/__init__.py +1 -2
  23. prefect-client-3.1.1/src/prefect/utilities/__init__.py +0 -0
  24. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/workers/base.py +112 -41
  25. {prefect-client-3.1.0 → prefect-client-3.1.1/src/prefect_client.egg-info}/PKG-INFO +1 -1
  26. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect_client.egg-info/SOURCES.txt +6 -0
  27. {prefect-client-3.1.0 → prefect-client-3.1.1}/LICENSE +0 -0
  28. {prefect-client-3.1.0 → prefect-client-3.1.1}/MANIFEST.in +0 -0
  29. {prefect-client-3.1.0 → prefect-client-3.1.1}/README.md +0 -0
  30. {prefect-client-3.1.0 → prefect-client-3.1.1}/requirements-client.txt +0 -0
  31. {prefect-client-3.1.0 → prefect-client-3.1.1}/setup.cfg +0 -0
  32. {prefect-client-3.1.0 → prefect-client-3.1.1}/setup.py +0 -0
  33. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/.prefectignore +0 -0
  34. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/__init__.py +0 -0
  35. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/__init__.py +0 -0
  36. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/_logging.py +0 -0
  37. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/compatibility/__init__.py +0 -0
  38. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/compatibility/deprecated.py +0 -0
  39. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/compatibility/migration.py +0 -0
  40. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/concurrency/__init__.py +0 -0
  41. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/concurrency/api.py +0 -0
  42. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/concurrency/calls.py +0 -0
  43. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/concurrency/cancellation.py +0 -0
  44. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/concurrency/event_loop.py +0 -0
  45. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/concurrency/inspection.py +0 -0
  46. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/concurrency/primitives.py +0 -0
  47. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/concurrency/services.py +0 -0
  48. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/concurrency/threads.py +0 -0
  49. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/concurrency/waiters.py +0 -0
  50. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/integrations.py +0 -0
  51. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/pydantic/__init__.py +0 -0
  52. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/pydantic/annotations/__init__.py +0 -0
  53. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/pydantic/annotations/pendulum.py +0 -0
  54. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/pydantic/schemas.py +0 -0
  55. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/pydantic/v1_schema.py +0 -0
  56. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/pydantic/v2_schema.py +0 -0
  57. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/pydantic/v2_validated_func.py +0 -0
  58. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/pytz.py +0 -0
  59. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/retries.py +0 -0
  60. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/schemas/__init__.py +0 -0
  61. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/schemas/bases.py +0 -0
  62. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/schemas/fields.py +0 -0
  63. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/schemas/serializers.py +0 -0
  64. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/_internal/schemas/validators.py +0 -0
  65. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/agent.py +0 -0
  66. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/artifacts.py +0 -0
  67. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/automations.py +0 -0
  68. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/blocks/__init__.py +0 -0
  69. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/blocks/abstract.py +0 -0
  70. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/blocks/core.py +0 -0
  71. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/blocks/fields.py +0 -0
  72. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/blocks/notifications.py +0 -0
  73. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/blocks/redis.py +0 -0
  74. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/blocks/system.py +0 -0
  75. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/blocks/webhook.py +0 -0
  76. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/cache_policies.py +0 -0
  77. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/__init__.py +0 -0
  78. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/base.py +0 -0
  79. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/cloud.py +0 -0
  80. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/collections.py +0 -0
  81. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/constants.py +0 -0
  82. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/schemas/__init__.py +0 -0
  83. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/schemas/actions.py +0 -0
  84. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/schemas/filters.py +0 -0
  85. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/schemas/responses.py +0 -0
  86. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/schemas/schedules.py +0 -0
  87. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/schemas/sorting.py +0 -0
  88. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/subscriptions.py +0 -0
  89. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/types/__init__.py +0 -0
  90. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/types/flexible_schedule_list.py +0 -0
  91. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/client/utilities.py +0 -0
  92. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/__init__.py +0 -0
  93. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/asyncio.py +0 -0
  94. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/context.py +0 -0
  95. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/events.py +0 -0
  96. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/services.py +0 -0
  97. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/sync.py +0 -0
  98. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/v1/__init__.py +0 -0
  99. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/v1/asyncio.py +0 -0
  100. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/v1/context.py +0 -0
  101. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/v1/events.py +0 -0
  102. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/v1/services.py +0 -0
  103. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/concurrency/v1/sync.py +0 -0
  104. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/context.py +0 -0
  105. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/deployments/__init__.py +0 -0
  106. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/deployments/base.py +0 -0
  107. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/deployments/deployments.py +0 -0
  108. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/deployments/flow_runs.py +0 -0
  109. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/deployments/runner.py +0 -0
  110. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/deployments/schedules.py +0 -0
  111. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/deployments/steps/__init__.py +0 -0
  112. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/deployments/steps/core.py +0 -0
  113. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/deployments/steps/pull.py +0 -0
  114. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/deployments/steps/utility.py +0 -0
  115. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/docker/__init__.py +0 -0
  116. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/docker/docker_image.py +0 -0
  117. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/engine.py +0 -0
  118. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/__init__.py +0 -0
  119. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/actions.py +0 -0
  120. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/cli/__init__.py +0 -0
  121. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/cli/automations.py +0 -0
  122. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/clients.py +0 -0
  123. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/filters.py +0 -0
  124. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/related.py +0 -0
  125. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/schemas/__init__.py +0 -0
  126. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/schemas/automations.py +0 -0
  127. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/schemas/deployment_triggers.py +0 -0
  128. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/schemas/events.py +0 -0
  129. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/schemas/labelling.py +0 -0
  130. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/utilities.py +0 -0
  131. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/events/worker.py +0 -0
  132. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/exceptions.py +0 -0
  133. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/filesystems.py +0 -0
  134. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/flow_engine.py +0 -0
  135. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/flow_runs.py +0 -0
  136. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/flows.py +0 -0
  137. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/futures.py +0 -0
  138. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/infrastructure/__init__.py +0 -0
  139. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/infrastructure/base.py +0 -0
  140. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/infrastructure/provisioners/__init__.py +0 -0
  141. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/infrastructure/provisioners/cloud_run.py +0 -0
  142. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/infrastructure/provisioners/container_instance.py +0 -0
  143. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/infrastructure/provisioners/ecs.py +0 -0
  144. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/infrastructure/provisioners/modal.py +0 -0
  145. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/input/__init__.py +0 -0
  146. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/input/actions.py +0 -0
  147. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/input/run_input.py +0 -0
  148. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/locking/__init__.py +0 -0
  149. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/locking/filesystem.py +0 -0
  150. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/locking/memory.py +0 -0
  151. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/locking/protocol.py +0 -0
  152. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/logging/__init__.py +0 -0
  153. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/logging/configuration.py +0 -0
  154. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/logging/filters.py +0 -0
  155. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/logging/formatters.py +0 -0
  156. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/logging/highlighters.py +0 -0
  157. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/plugins.py +0 -0
  158. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/py.typed +0 -0
  159. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/records/__init__.py +0 -0
  160. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/records/base.py +0 -0
  161. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/records/filesystem.py +0 -0
  162. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/records/memory.py +0 -0
  163. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/records/result_store.py +0 -0
  164. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/results.py +0 -0
  165. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/runner/__init__.py +0 -0
  166. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/runner/runner.py +0 -0
  167. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/runner/server.py +0 -0
  168. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/runner/storage.py +0 -0
  169. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/runner/submit.py +0 -0
  170. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/runner/utils.py +0 -0
  171. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/runtime/__init__.py +0 -0
  172. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/runtime/deployment.py +0 -0
  173. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/runtime/flow_run.py +0 -0
  174. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/runtime/task_run.py +0 -0
  175. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/serializers.py +0 -0
  176. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/server/api/collections_data/views/aggregate-worker-metadata.json +0 -0
  177. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/server/api/static/prefect-logo-mark-gradient.png +0 -0
  178. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/__init__.py +0 -0
  179. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/base.py +0 -0
  180. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/constants.py +0 -0
  181. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/context.py +0 -0
  182. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/legacy.py +0 -0
  183. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/__init__.py +0 -0
  184. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/cli.py +0 -0
  185. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/client.py +0 -0
  186. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/cloud.py +0 -0
  187. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/deployments.py +0 -0
  188. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/flows.py +0 -0
  189. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/internal.py +0 -0
  190. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/results.py +0 -0
  191. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/runner.py +0 -0
  192. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/server/__init__.py +0 -0
  193. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/server/api.py +0 -0
  194. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/server/database.py +0 -0
  195. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/server/deployments.py +0 -0
  196. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/server/ephemeral.py +0 -0
  197. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/server/flow_run_graph.py +0 -0
  198. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/server/root.py +0 -0
  199. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/server/services.py +0 -0
  200. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/server/tasks.py +0 -0
  201. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/server/ui.py +0 -0
  202. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/tasks.py +0 -0
  203. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/testing.py +0 -0
  204. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/models/worker.py +0 -0
  205. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/profiles.py +0 -0
  206. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/settings/profiles.toml +0 -0
  207. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/states.py +0 -0
  208. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/task_engine.py +0 -0
  209. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/task_runners.py +0 -0
  210. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/task_runs.py +0 -0
  211. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/task_worker.py +0 -0
  212. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/tasks.py +0 -0
  213. {prefect-client-3.1.0/src/prefect/utilities → prefect-client-3.1.1/src/prefect/telemetry}/__init__.py +0 -0
  214. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/transactions.py +0 -0
  215. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/types/entrypoint.py +0 -0
  216. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/annotations.py +0 -0
  217. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/asyncutils.py +0 -0
  218. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/callables.py +0 -0
  219. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/collections.py +0 -0
  220. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/compat.py +0 -0
  221. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/context.py +0 -0
  222. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/dispatch.py +0 -0
  223. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/dockerutils.py +0 -0
  224. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/engine.py +0 -0
  225. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/filesystem.py +0 -0
  226. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/hashing.py +0 -0
  227. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/importtools.py +0 -0
  228. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/math.py +0 -0
  229. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/names.py +0 -0
  230. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/processutils.py +0 -0
  231. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/pydantic.py +0 -0
  232. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/render_swagger.py +0 -0
  233. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/schema_tools/__init__.py +0 -0
  234. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/schema_tools/hydration.py +0 -0
  235. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/schema_tools/validation.py +0 -0
  236. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/services.py +0 -0
  237. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/slugify.py +0 -0
  238. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/templating.py +0 -0
  239. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/text.py +0 -0
  240. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/timeout.py +0 -0
  241. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/urls.py +0 -0
  242. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/utilities/visualization.py +0 -0
  243. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/variables.py +0 -0
  244. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/workers/__init__.py +0 -0
  245. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/workers/block.py +0 -0
  246. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/workers/cloud.py +0 -0
  247. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/workers/process.py +0 -0
  248. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/workers/server.py +0 -0
  249. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect/workers/utilities.py +0 -0
  250. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect_client.egg-info/dependency_links.txt +0 -0
  251. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect_client.egg-info/requires.txt +0 -0
  252. {prefect-client-3.1.0 → prefect-client-3.1.1}/src/prefect_client.egg-info/top_level.txt +0 -0
  253. {prefect-client-3.1.0 → prefect-client-3.1.1}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prefect-client
3
- Version: 3.1.0
3
+ Version: 3.1.1
4
4
  Summary: Workflow orchestration and management.
5
5
  Home-page: https://www.prefect.io
6
6
  Author: Prefect Technologies, Inc.
@@ -36,3 +36,12 @@ mkdocs
36
36
  mkdocs-material
37
37
  mkdocstrings[python]
38
38
  mkdocs-gen-files
39
+
40
+ # OpenTelemetry
41
+ # Other than the `test-utils` package these versions should match the versions
42
+ # in `requirements-otel.txt`
43
+ opentelemetry-distro >= 0.48b0, < 1.0.0
44
+ opentelemetry-exporter-otlp >= 1.27.0, < 2.0.0
45
+ opentelemetry-instrumentation >= 0.48b0, < 1.0.0
46
+ opentelemetry-instrumentation-logging >= 0.48b0, < 1.0.0
47
+ opentelemetry-test-utils >= 0.48b0, < 1.0.0
@@ -16,4 +16,4 @@ opentelemetry-api >= 1.27.0, < 2.0.0
16
16
  pytz >= 2021.1, < 2025
17
17
  readchar >= 4.0.0, < 5.0.0
18
18
  sqlalchemy[asyncio] >= 2.0, < 3.0.0
19
- typer >= 0.12.0, != 0.12.2, < 0.13.0
19
+ typer >= 0.12.0, != 0.12.2, < 0.14.0
@@ -0,0 +1,73 @@
1
+ import asyncio
2
+ import inspect
3
+ from functools import wraps
4
+ from typing import Any, Callable, Coroutine, Protocol, TypeVar, Union
5
+
6
+ from typing_extensions import ParamSpec
7
+
8
+ R = TypeVar("R")
9
+ P = ParamSpec("P")
10
+
11
+
12
+ class AsyncDispatchable(Protocol[P, R]):
13
+ """Protocol for functions decorated with async_dispatch."""
14
+
15
+ def __call__(
16
+ self, *args: P.args, **kwargs: P.kwargs
17
+ ) -> Union[R, Coroutine[Any, Any, R]]:
18
+ ...
19
+
20
+ aio: Callable[P, Coroutine[Any, Any, R]]
21
+ sync: Callable[P, R]
22
+
23
+
24
+ def is_in_async_context() -> bool:
25
+ """Check if we're in an async context."""
26
+ try:
27
+ # First check if we're in a coroutine
28
+ if asyncio.current_task() is not None:
29
+ return True
30
+
31
+ # Check if we have a loop and it's running
32
+ loop = asyncio.get_event_loop()
33
+ return loop.is_running()
34
+ except RuntimeError:
35
+ return False
36
+
37
+
38
+ def async_dispatch(
39
+ async_impl: Callable[P, Coroutine[Any, Any, R]],
40
+ ) -> Callable[[Callable[P, R]], AsyncDispatchable[P, R]]:
41
+ """
42
+ Decorator that adds async compatibility to a sync function.
43
+
44
+ The decorated function will:
45
+ - Return a coroutine when in an async context (detected via running event loop)
46
+ - Run synchronously when in a sync context
47
+ - Provide .aio for explicit async access
48
+ - Provide .sync for explicit sync access
49
+
50
+ Args:
51
+ async_impl: The async implementation to dispatch to when async execution
52
+ is needed
53
+ """
54
+ if not inspect.iscoroutinefunction(async_impl):
55
+ raise TypeError(
56
+ "async_impl must be an async function to dispatch in async contexts"
57
+ )
58
+
59
+ def decorator(sync_fn: Callable[P, R]) -> AsyncDispatchable[P, R]:
60
+ @wraps(sync_fn)
61
+ def wrapper(
62
+ *args: P.args, **kwargs: P.kwargs
63
+ ) -> Union[R, Coroutine[Any, Any, R]]:
64
+ if is_in_async_context():
65
+ return async_impl(*args, **kwargs)
66
+ return sync_fn(*args, **kwargs)
67
+
68
+ # Attach both async and sync implementations directly
69
+ wrapper.aio = async_impl
70
+ wrapper.sync = sync_fn
71
+ return wrapper # type: ignore
72
+
73
+ return decorator
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-10-31T12:43:46-0700",
11
+ "date": "2024-11-08T12:38:16-0800",
12
12
  "dirty": true,
13
13
  "error": null,
14
- "full-revisionid": "a83ba39b095e5945140ab6313e39dbc56056afe1",
15
- "version": "3.1.0"
14
+ "full-revisionid": "6b50a2b9f9d4ebf59703c55e1156c6f79151f1c3",
15
+ "version": "3.1.1"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -99,6 +99,7 @@ from prefect.client.schemas.objects import (
99
99
  TaskRunResult,
100
100
  Variable,
101
101
  Worker,
102
+ WorkerMetadata,
102
103
  WorkPool,
103
104
  WorkQueue,
104
105
  WorkQueueStatusDetail,
@@ -2596,6 +2597,7 @@ class PrefectClient:
2596
2597
  worker_name: str,
2597
2598
  heartbeat_interval_seconds: Optional[float] = None,
2598
2599
  get_worker_id: bool = False,
2600
+ worker_metadata: Optional[WorkerMetadata] = None,
2599
2601
  ) -> Optional[UUID]:
2600
2602
  """
2601
2603
  Sends a worker heartbeat for a given work pool.
@@ -2604,20 +2606,20 @@ class PrefectClient:
2604
2606
  work_pool_name: The name of the work pool to heartbeat against.
2605
2607
  worker_name: The name of the worker sending the heartbeat.
2606
2608
  return_id: Whether to return the worker ID. Note: will return `None` if the connected server does not support returning worker IDs, even if `return_id` is `True`.
2609
+ worker_metadata: Metadata about the worker to send to the server.
2607
2610
  """
2608
-
2611
+ params = {
2612
+ "name": worker_name,
2613
+ "heartbeat_interval_seconds": heartbeat_interval_seconds,
2614
+ }
2615
+ if worker_metadata:
2616
+ params["worker_metadata"] = worker_metadata.model_dump(mode="json")
2609
2617
  if get_worker_id:
2610
- return_dict = {"return_id": get_worker_id}
2611
- else:
2612
- return_dict = {}
2618
+ params["return_id"] = get_worker_id
2613
2619
 
2614
2620
  resp = await self._client.post(
2615
2621
  f"/work_pools/{work_pool_name}/workers/heartbeat",
2616
- json={
2617
- "name": worker_name,
2618
- "heartbeat_interval_seconds": heartbeat_interval_seconds,
2619
- }
2620
- | return_dict,
2622
+ json=params,
2621
2623
  )
2622
2624
 
2623
2625
  if (
@@ -1689,3 +1689,24 @@ class CsrfToken(ObjectBaseModel):
1689
1689
 
1690
1690
 
1691
1691
  __getattr__ = getattr_migration(__name__)
1692
+
1693
+
1694
+ class Integration(PrefectBaseModel):
1695
+ """A representation of an installed Prefect integration."""
1696
+
1697
+ name: str = Field(description="The name of the Prefect integration.")
1698
+ version: str = Field(description="The version of the Prefect integration.")
1699
+
1700
+
1701
+ class WorkerMetadata(PrefectBaseModel):
1702
+ """
1703
+ Worker metadata.
1704
+
1705
+ We depend on the structure of `integrations`, but otherwise, worker classes
1706
+ should support flexible metadata.
1707
+ """
1708
+
1709
+ integrations: List[Integration] = Field(
1710
+ default=..., description="Prefect integrations installed in the worker."
1711
+ )
1712
+ model_config = ConfigDict(extra="allow")
@@ -32,6 +32,7 @@ from prefect.settings import (
32
32
  PREFECT_LOGGING_TO_API_BATCH_SIZE,
33
33
  PREFECT_LOGGING_TO_API_MAX_LOG_SIZE,
34
34
  PREFECT_LOGGING_TO_API_WHEN_MISSING_FLOW,
35
+ get_current_settings,
35
36
  )
36
37
 
37
38
 
@@ -180,6 +181,7 @@ class APILogHandler(logging.Handler):
180
181
  """
181
182
  flow_run_id = getattr(record, "flow_run_id", None)
182
183
  task_run_id = getattr(record, "task_run_id", None)
184
+ worker_id = getattr(record, "worker_id", None)
183
185
 
184
186
  if not flow_run_id:
185
187
  try:
@@ -215,6 +217,7 @@ class APILogHandler(logging.Handler):
215
217
  log = LogCreate(
216
218
  flow_run_id=flow_run_id if is_uuid_like else None,
217
219
  task_run_id=task_run_id,
220
+ worker_id=worker_id,
218
221
  name=record.name,
219
222
  level=record.levelno,
220
223
  timestamp=pendulum.from_timestamp(
@@ -236,6 +239,44 @@ class APILogHandler(logging.Handler):
236
239
  return len(json.dumps(log).encode())
237
240
 
238
241
 
242
+ class WorkerAPILogHandler(APILogHandler):
243
+ def emit(self, record: logging.LogRecord):
244
+ if get_current_settings().experiments.worker_logging_to_api_enabled:
245
+ super().emit(record)
246
+ else:
247
+ return
248
+
249
+ def prepare(self, record: logging.LogRecord) -> Dict[str, Any]:
250
+ """
251
+ Convert a `logging.LogRecord` to the API `LogCreate` schema and serialize.
252
+
253
+ This will add in the worker id to the log.
254
+
255
+ Logs exceeding the maximum size will be dropped.
256
+ """
257
+
258
+ worker_id = getattr(record, "worker_id", None)
259
+
260
+ log = LogCreate(
261
+ worker_id=worker_id,
262
+ name=record.name,
263
+ level=record.levelno,
264
+ timestamp=pendulum.from_timestamp(
265
+ getattr(record, "created", None) or time.time()
266
+ ),
267
+ message=self.format(record),
268
+ ).model_dump(mode="json")
269
+
270
+ log_size = log["__payload_size__"] = self._get_payload_size(log)
271
+ if log_size > PREFECT_LOGGING_TO_API_MAX_LOG_SIZE.value():
272
+ raise ValueError(
273
+ f"Log of size {log_size} is greater than the max size of "
274
+ f"{PREFECT_LOGGING_TO_API_MAX_LOG_SIZE.value()}"
275
+ )
276
+
277
+ return log
278
+
279
+
239
280
  class PrefectConsoleHandler(logging.StreamHandler):
240
281
  def __init__(
241
282
  self,
@@ -12,6 +12,7 @@ from typing_extensions import Self
12
12
  import prefect
13
13
  from prefect.exceptions import MissingContextError
14
14
  from prefect.logging.filters import ObfuscateApiKeyFilter
15
+ from prefect.telemetry.logging import add_telemetry_log_handler
15
16
 
16
17
  if TYPE_CHECKING:
17
18
  from prefect.client.schemas import FlowRun as ClientFlowRun
@@ -19,6 +20,7 @@ if TYPE_CHECKING:
19
20
  from prefect.context import RunContext
20
21
  from prefect.flows import Flow
21
22
  from prefect.tasks import Task
23
+ from prefect.workers.base import BaseWorker
22
24
 
23
25
 
24
26
  class PrefectLogAdapter(logging.LoggerAdapter):
@@ -75,6 +77,8 @@ def get_logger(name: Optional[str] = None) -> logging.Logger:
75
77
  obfuscate_api_key_filter = ObfuscateApiKeyFilter()
76
78
  logger.addFilter(obfuscate_api_key_filter)
77
79
 
80
+ add_telemetry_log_handler(logger=logger)
81
+
78
82
  return logger
79
83
 
80
84
 
@@ -136,6 +140,12 @@ def get_run_logger(
136
140
  else:
137
141
  raise MissingContextError("There is no active flow or task run context.")
138
142
 
143
+ if isinstance(logger, logging.LoggerAdapter):
144
+ assert isinstance(logger.logger, logging.Logger)
145
+ add_telemetry_log_handler(logger.logger)
146
+ else:
147
+ add_telemetry_log_handler(logger)
148
+
139
149
  return logger
140
150
 
141
151
 
@@ -205,6 +215,29 @@ def task_run_logger(
205
215
  )
206
216
 
207
217
 
218
+ def get_worker_logger(worker: "BaseWorker", name: Optional[str] = None):
219
+ """
220
+ Create a worker logger with the worker's metadata attached.
221
+
222
+ If the worker has a backend_id, it will be attached to the log records.
223
+ If the worker does not have a backend_id a basic logger will be returned.
224
+ If the worker does not have a backend_id attribute, a basic logger will be returned.
225
+ """
226
+
227
+ worker_log_name = name or f"workers.{worker.__class__.type}.{worker.name.lower()}"
228
+
229
+ worker_id = getattr(worker, "backend_id", None)
230
+ if worker_id:
231
+ return PrefectLogAdapter(
232
+ get_logger(worker_log_name),
233
+ extra={
234
+ "worker_id": str(worker.backend_id),
235
+ },
236
+ )
237
+ else:
238
+ return get_logger(worker_log_name)
239
+
240
+
208
241
  @contextmanager
209
242
  def disable_logger(name: str):
210
243
  """
@@ -69,6 +69,10 @@ handlers:
69
69
  class: logging.StreamHandler
70
70
  formatter: debug
71
71
 
72
+ worker_api:
73
+ level: 0
74
+ class: prefect.logging.handlers.WorkerAPILogHandler
75
+
72
76
  loggers:
73
77
  prefect:
74
78
  level: "${PREFECT_LOGGING_LEVEL}"
@@ -86,6 +90,10 @@ loggers:
86
90
  level: NOTSET
87
91
  handlers: [api]
88
92
 
93
+ prefect.workers:
94
+ level: NOTSET
95
+ handlers: [worker_api]
96
+
89
97
  prefect.server:
90
98
  level: "${PREFECT_SERVER_LOGGING_LEVEL}"
91
99
 
@@ -57,6 +57,11 @@ prefect.logging.get_logger("profiles").debug(
57
57
  f"Using profile {prefect.context.get_settings_context().profile.name!r}"
58
58
  )
59
59
 
60
+ # Configure telemetry
61
+ import prefect.telemetry.bootstrap
62
+
63
+ prefect.telemetry.bootstrap.setup_telemetry()
64
+
60
65
 
61
66
  from prefect._internal.compatibility.deprecated import (
62
67
  inject_renamed_module_alias_finder,
@@ -25,7 +25,7 @@ class APISettings(PrefectBaseSettings):
25
25
  )
26
26
  tls_insecure_skip_verify: bool = Field(
27
27
  default=False,
28
- description="If `True`, disables SSL checking to allow insecure requests. This is recommended only during development, e.g. when using self-signed certificates.",
28
+ description="If `True`, disables SSL checking to allow insecure requests. Setting to False is recommended only during development. For example, when using self-signed certificates.",
29
29
  )
30
30
  ssl_cert_file: Optional[str] = Field(
31
31
  default=os.environ.get("SSL_CERT_FILE"),
@@ -22,3 +22,8 @@ class ExperimentsSettings(PrefectBaseSettings):
22
22
  default=False,
23
23
  description="Enables the logging of worker logs to Prefect Cloud.",
24
24
  )
25
+
26
+ telemetry_enabled: bool = Field(
27
+ default=False,
28
+ description="Enables sending telemetry to Prefect Cloud.",
29
+ )
@@ -1,11 +1,18 @@
1
+ from functools import partial
1
2
  from pathlib import Path
2
3
  from typing import Annotated, Literal, Optional, Union
3
4
 
4
- from pydantic import AfterValidator, AliasChoices, AliasPath, Field, model_validator
5
+ from pydantic import (
6
+ AliasChoices,
7
+ AliasPath,
8
+ BeforeValidator,
9
+ Field,
10
+ model_validator,
11
+ )
5
12
  from typing_extensions import Self
6
13
 
7
14
  from prefect.settings.base import PrefectBaseSettings, _build_settings_config
8
- from prefect.types import LogLevel
15
+ from prefect.types import LogLevel, validate_set_T_from_delim_string
9
16
 
10
17
 
11
18
  def max_log_size_smaller_than_batch_size(values):
@@ -97,7 +104,7 @@ class LoggingSettings(PrefectBaseSettings):
97
104
 
98
105
  extra_loggers: Annotated[
99
106
  Union[str, list[str], None],
100
- AfterValidator(lambda v: [n.strip() for n in v.split(",")] if v else []),
107
+ BeforeValidator(partial(validate_set_T_from_delim_string, type_=str)),
101
108
  ] = Field(
102
109
  default=None,
103
110
  description="Additional loggers to attach to Prefect logging at runtime.",
@@ -72,7 +72,7 @@ class Settings(PrefectBaseSettings):
72
72
 
73
73
  client: ClientSettings = Field(
74
74
  default_factory=ClientSettings,
75
- description="Settings for for controlling API client behavior",
75
+ description="Settings for controlling API client behavior",
76
76
  )
77
77
 
78
78
  cloud: CloudSettings = Field(
@@ -128,7 +128,7 @@ class ServerEventsSettings(PrefectBaseSettings):
128
128
 
129
129
  messaging_cache: str = Field(
130
130
  default="prefect.server.utilities.messaging.memory",
131
- description="Which cache implementation to use for the events system. Should point to a module that exports a Cache class.",
131
+ description="Which cache implementation to use for the events system. Should point to a module that exports a Cache class.",
132
132
  validation_alias=AliasChoices(
133
133
  AliasPath("messaging_cache"),
134
134
  "prefect_server_events_messaging_cache",
@@ -4,6 +4,7 @@ import warnings
4
4
  from pathlib import Path
5
5
  from typing import Any, Dict, List, Optional, Tuple, Type
6
6
 
7
+ import dotenv
7
8
  import toml
8
9
  from pydantic import AliasChoices
9
10
  from pydantic.fields import FieldInfo
@@ -15,6 +16,7 @@ from pydantic_settings import (
15
16
  from pydantic_settings.sources import ConfigFileSourceMixin
16
17
 
17
18
  from prefect.settings.constants import DEFAULT_PREFECT_HOME, DEFAULT_PROFILES_PATH
19
+ from prefect.utilities.collections import get_from_dict
18
20
 
19
21
 
20
22
  class EnvFilterSettingsSource(EnvSettingsSource):
@@ -230,6 +232,25 @@ def _get_profiles_path() -> Path:
230
232
  return DEFAULT_PROFILES_PATH
231
233
  if env_path := os.getenv("PREFECT_PROFILES_PATH"):
232
234
  return Path(env_path)
235
+ if dotenv_path := dotenv.dotenv_values(".env").get("PREFECT_PROFILES_PATH"):
236
+ return Path(dotenv_path)
237
+ if toml_path := _get_profiles_path_from_toml("prefect.toml", ["profiles_path"]):
238
+ return Path(toml_path)
239
+ if pyproject_path := _get_profiles_path_from_toml(
240
+ "pyproject.toml", ["tool", "prefect", "profiles_path"]
241
+ ):
242
+ return Path(pyproject_path)
233
243
  if not (DEFAULT_PREFECT_HOME / "profiles.toml").exists():
234
244
  return DEFAULT_PROFILES_PATH
235
245
  return DEFAULT_PREFECT_HOME / "profiles.toml"
246
+
247
+
248
+ def _get_profiles_path_from_toml(path: str, keys: List[str]) -> Optional[str]:
249
+ """Helper to get the profiles path from a toml file."""
250
+
251
+ try:
252
+ toml_data = toml.load(path)
253
+ except FileNotFoundError:
254
+ return None
255
+
256
+ return get_from_dict(toml_data, keys)
@@ -0,0 +1,32 @@
1
+ from typing import TYPE_CHECKING, Union
2
+
3
+ import prefect.settings
4
+ from prefect.client.base import ServerType, determine_server_type
5
+
6
+ if TYPE_CHECKING:
7
+ from opentelemetry.sdk._logs import LoggerProvider
8
+ from opentelemetry.sdk.metrics import MeterProvider
9
+ from opentelemetry.sdk.trace import TracerProvider
10
+
11
+
12
+ def setup_telemetry() -> (
13
+ Union[
14
+ tuple["TracerProvider", "MeterProvider", "LoggerProvider"],
15
+ tuple[None, None, None],
16
+ ]
17
+ ):
18
+ settings = prefect.settings.get_current_settings()
19
+ if not settings.experiments.telemetry_enabled:
20
+ return None, None, None
21
+
22
+ server_type = determine_server_type()
23
+ if server_type != ServerType.CLOUD:
24
+ return None, None, None
25
+
26
+ assert settings.api.key
27
+ assert settings.api.url
28
+
29
+ # This import is here to defer importing of the `opentelemetry` packages.
30
+ from .instrumentation import setup_exporters
31
+
32
+ return setup_exporters(settings.api.url, settings.api.key.get_secret_value())
@@ -0,0 +1,125 @@
1
+ import logging
2
+ import os
3
+ import re
4
+ from typing import TYPE_CHECKING
5
+ from urllib.parse import urljoin
6
+ from uuid import UUID
7
+
8
+ from opentelemetry import metrics, trace
9
+ from opentelemetry._logs import set_logger_provider
10
+ from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
11
+ from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
12
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
13
+ from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
14
+ from opentelemetry.sdk._logs.export import SimpleLogRecordProcessor
15
+ from opentelemetry.sdk.metrics import MeterProvider
16
+ from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
17
+ from opentelemetry.sdk.resources import Resource
18
+ from opentelemetry.sdk.trace import TracerProvider
19
+
20
+ from .logging import set_log_handler
21
+ from .processors import InFlightSpanProcessor
22
+
23
+ if TYPE_CHECKING:
24
+ from opentelemetry.sdk._logs import LoggerProvider
25
+
26
+ UUID_REGEX = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
27
+
28
+ ACCOUNTS_PREFIX = "accounts/"
29
+ ACCOUNT_ID_REGEX = f"{ACCOUNTS_PREFIX}{UUID_REGEX}"
30
+
31
+ WORKSPACES_PREFIX = "workspaces/"
32
+ WORKSPACE_ID_REGEX = f"{WORKSPACES_PREFIX}{UUID_REGEX}"
33
+
34
+
35
+ def extract_account_and_workspace_id(url: str) -> tuple[UUID, UUID]:
36
+ account_id, workspace_id = None, None
37
+
38
+ if res := re.search(ACCOUNT_ID_REGEX, url):
39
+ account_id = UUID(res.group().removeprefix(ACCOUNTS_PREFIX))
40
+
41
+ if res := re.search(WORKSPACE_ID_REGEX, url):
42
+ workspace_id = UUID(res.group().removeprefix(WORKSPACES_PREFIX))
43
+
44
+ if account_id and workspace_id:
45
+ return account_id, workspace_id
46
+
47
+ raise ValueError(
48
+ f"Could not extract account and workspace id from API url: {url!r}"
49
+ )
50
+
51
+
52
+ def _url_join(base_url: str, path: str) -> str:
53
+ return urljoin(base_url.rstrip("/") + "/", path.lstrip("/"))
54
+
55
+
56
+ def setup_exporters(
57
+ api_url: str, api_key: str
58
+ ) -> tuple[TracerProvider, MeterProvider, "LoggerProvider"]:
59
+ account_id, workspace_id = extract_account_and_workspace_id(api_url)
60
+ telemetry_url = _url_join(api_url, "telemetry/")
61
+
62
+ headers = {
63
+ "Authorization": f"Bearer {api_key}",
64
+ }
65
+
66
+ resource = Resource.create(
67
+ {
68
+ "service.name": "prefect",
69
+ "service.instance.id": os.uname().nodename,
70
+ "prefect.account": str(account_id),
71
+ "prefect.workspace": str(workspace_id),
72
+ }
73
+ )
74
+
75
+ trace_provider = _setup_trace_provider(resource, headers, telemetry_url)
76
+ meter_provider = _setup_meter_provider(resource, headers, telemetry_url)
77
+ logger_provider = _setup_logger_provider(resource, headers, telemetry_url)
78
+
79
+ return trace_provider, meter_provider, logger_provider
80
+
81
+
82
+ def _setup_trace_provider(
83
+ resource: Resource, headers: dict[str, str], telemetry_url: str
84
+ ) -> TracerProvider:
85
+ trace_provider = TracerProvider(resource=resource)
86
+ otlp_span_exporter = OTLPSpanExporter(
87
+ endpoint=_url_join(telemetry_url, "v1/traces"),
88
+ headers=headers,
89
+ )
90
+ trace_provider.add_span_processor(InFlightSpanProcessor(otlp_span_exporter))
91
+ trace.set_tracer_provider(trace_provider)
92
+
93
+ return trace_provider
94
+
95
+
96
+ def _setup_meter_provider(
97
+ resource: Resource, headers: dict[str, str], telemetry_url: str
98
+ ) -> MeterProvider:
99
+ metric_reader = PeriodicExportingMetricReader(
100
+ OTLPMetricExporter(
101
+ endpoint=_url_join(telemetry_url, "v1/metrics"),
102
+ headers=headers,
103
+ )
104
+ )
105
+ meter_provider = MeterProvider(resource=resource, metric_readers=[metric_reader])
106
+ metrics.set_meter_provider(meter_provider)
107
+
108
+ return meter_provider
109
+
110
+
111
+ def _setup_logger_provider(
112
+ resource: Resource, headers: dict[str, str], telemetry_url: str
113
+ ) -> LoggerProvider:
114
+ logger_provider = LoggerProvider(resource=resource)
115
+ otlp_exporter = OTLPLogExporter(
116
+ endpoint=_url_join(telemetry_url, "v1/logs"),
117
+ headers=headers,
118
+ )
119
+ logger_provider.add_log_record_processor(SimpleLogRecordProcessor(otlp_exporter))
120
+ set_logger_provider(logger_provider)
121
+ log_handler = LoggingHandler(level=logging.NOTSET, logger_provider=logger_provider)
122
+
123
+ set_log_handler(log_handler)
124
+
125
+ return logger_provider
@@ -0,0 +1,26 @@
1
+ import logging
2
+ from typing import TYPE_CHECKING, Optional
3
+
4
+ if TYPE_CHECKING:
5
+ from opentelemetry.sdk._logs import LoggingHandler
6
+
7
+ _log_handler: Optional["LoggingHandler"] = None
8
+
9
+
10
+ def set_log_handler(log_handler: Optional["LoggingHandler"]) -> None:
11
+ """Set the OTLP log handler."""
12
+ global _log_handler
13
+ _log_handler = log_handler
14
+
15
+
16
+ def get_log_handler() -> Optional["LoggingHandler"]:
17
+ """Get the OTLP log handler."""
18
+ global _log_handler
19
+ return _log_handler
20
+
21
+
22
+ def add_telemetry_log_handler(logger: logging.Logger) -> None:
23
+ """Add the OTLP log handler to the given logger if the log handler has
24
+ been configured."""
25
+ if log_handler := get_log_handler():
26
+ logger.addHandler(log_handler)