prefect-client 2.16.4__tar.gz → 2.16.6__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 (273) hide show
  1. {prefect-client-2.16.4 → prefect-client-2.16.6}/PKG-INFO +1 -1
  2. {prefect-client-2.16.4 → prefect-client-2.16.6}/requirements-dev.txt +1 -0
  3. {prefect-client-2.16.4 → prefect-client-2.16.6}/setup.cfg +10 -0
  4. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/calls.py +3 -2
  5. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/__init__.py +2 -0
  6. prefect-client-2.16.6/src/prefect/_internal/pydantic/_compat.py +213 -0
  7. prefect-client-2.16.6/src/prefect/_internal/pydantic/v1_schema.py +48 -0
  8. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/v2_schema.py +6 -2
  9. prefect-client-2.16.6/src/prefect/artifacts.py +185 -0
  10. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/base.py +80 -10
  11. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/cloud.py +5 -2
  12. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/orchestration.py +17 -1
  13. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/objects.py +13 -0
  14. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/subscriptions.py +3 -3
  15. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/engine.py +10 -6
  16. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/related.py +1 -1
  17. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/schemas.py +45 -1
  18. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/flows.py +12 -4
  19. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/server/api/collections_data/views/aggregate-worker-metadata.json +14 -2
  20. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/settings.py +62 -8
  21. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/task_server.py +9 -3
  22. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/tasks.py +13 -8
  23. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/callables.py +5 -3
  24. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/schema_tools/hydration.py +14 -6
  25. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect_client.egg-info/PKG-INFO +1 -1
  26. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect_client.egg-info/SOURCES.txt +3 -0
  27. {prefect-client-2.16.4 → prefect-client-2.16.6}/LICENSE +0 -0
  28. {prefect-client-2.16.4 → prefect-client-2.16.6}/MANIFEST.in +0 -0
  29. {prefect-client-2.16.4 → prefect-client-2.16.6}/README.md +0 -0
  30. {prefect-client-2.16.4 → prefect-client-2.16.6}/requirements-client.txt +0 -0
  31. {prefect-client-2.16.4 → prefect-client-2.16.6}/requirements.txt +0 -0
  32. {prefect-client-2.16.4 → prefect-client-2.16.6}/setup.py +0 -0
  33. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/.prefectignore +0 -0
  34. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/__init__.py +0 -0
  35. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/__init__.py +0 -0
  36. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/_logging.py +0 -0
  37. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/compatibility/__init__.py +0 -0
  38. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/compatibility/deprecated.py +0 -0
  39. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/compatibility/experimental.py +0 -0
  40. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/__init__.py +0 -0
  41. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/api.py +0 -0
  42. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/cancellation.py +0 -0
  43. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/event_loop.py +0 -0
  44. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/inspection.py +0 -0
  45. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/primitives.py +0 -0
  46. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/services.py +0 -0
  47. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/threads.py +0 -0
  48. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/waiters.py +0 -0
  49. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/annotations/__init__.py +0 -0
  50. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/annotations/pendulum.py +0 -0
  51. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/schemas.py +0 -0
  52. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/v2_validated_func.py +0 -0
  53. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pytz.py +0 -0
  54. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/__init__.py +0 -0
  55. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/bases.py +0 -0
  56. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/fields.py +0 -0
  57. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/serializers.py +0 -0
  58. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/transformations.py +0 -0
  59. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/validators.py +0 -0
  60. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/__init__.py +0 -0
  61. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/__init__.py +0 -0
  62. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/applications.py +0 -0
  63. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/background.py +0 -0
  64. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/concurrency.py +0 -0
  65. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/datastructures.py +0 -0
  66. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  67. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/dependencies/models.py +0 -0
  68. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/dependencies/utils.py +0 -0
  69. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/encoders.py +0 -0
  70. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/exception_handlers.py +0 -0
  71. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/exceptions.py +0 -0
  72. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/logger.py +0 -0
  73. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/__init__.py +0 -0
  74. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -0
  75. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/cors.py +0 -0
  76. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/gzip.py +0 -0
  77. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -0
  78. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/trustedhost.py +0 -0
  79. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/wsgi.py +0 -0
  80. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  81. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/openapi/constants.py +0 -0
  82. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/openapi/docs.py +0 -0
  83. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/openapi/models.py +0 -0
  84. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/openapi/utils.py +0 -0
  85. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/param_functions.py +0 -0
  86. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/params.py +0 -0
  87. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/requests.py +0 -0
  88. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/responses.py +0 -0
  89. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/routing.py +0 -0
  90. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/__init__.py +0 -0
  91. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/api_key.py +0 -0
  92. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/base.py +0 -0
  93. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/http.py +0 -0
  94. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/oauth2.py +0 -0
  95. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -0
  96. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/utils.py +0 -0
  97. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/staticfiles.py +0 -0
  98. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/templating.py +0 -0
  99. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/testclient.py +0 -0
  100. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/types.py +0 -0
  101. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/utils.py +0 -0
  102. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/websockets.py +0 -0
  103. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/__init__.py +0 -0
  104. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/_compat.py +0 -0
  105. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/_exception_handler.py +0 -0
  106. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/_utils.py +0 -0
  107. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/applications.py +0 -0
  108. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/authentication.py +0 -0
  109. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/background.py +0 -0
  110. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/concurrency.py +0 -0
  111. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/config.py +0 -0
  112. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/convertors.py +0 -0
  113. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/datastructures.py +0 -0
  114. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/endpoints.py +0 -0
  115. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/exceptions.py +0 -0
  116. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/formparsers.py +0 -0
  117. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/__init__.py +0 -0
  118. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/authentication.py +0 -0
  119. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/base.py +0 -0
  120. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/cors.py +0 -0
  121. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/errors.py +0 -0
  122. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/exceptions.py +0 -0
  123. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/gzip.py +0 -0
  124. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/httpsredirect.py +0 -0
  125. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/sessions.py +0 -0
  126. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/trustedhost.py +0 -0
  127. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/wsgi.py +0 -0
  128. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/requests.py +0 -0
  129. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/responses.py +0 -0
  130. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/routing.py +0 -0
  131. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/schemas.py +0 -0
  132. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/staticfiles.py +0 -0
  133. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/status.py +0 -0
  134. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/templating.py +0 -0
  135. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/testclient.py +0 -0
  136. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/types.py +0 -0
  137. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/websockets.py +0 -0
  138. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_version.py +0 -0
  139. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/agent.py +0 -0
  140. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/__init__.py +0 -0
  141. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/abstract.py +0 -0
  142. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/core.py +0 -0
  143. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/fields.py +0 -0
  144. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/kubernetes.py +0 -0
  145. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/notifications.py +0 -0
  146. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/system.py +0 -0
  147. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/webhook.py +0 -0
  148. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/__init__.py +0 -0
  149. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/collections.py +0 -0
  150. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/constants.py +0 -0
  151. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/__init__.py +0 -0
  152. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/actions.py +0 -0
  153. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/filters.py +0 -0
  154. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/responses.py +0 -0
  155. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/schedules.py +0 -0
  156. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/sorting.py +0 -0
  157. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/utilities.py +0 -0
  158. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/__init__.py +0 -0
  159. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/asyncio.py +0 -0
  160. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/common.py +0 -0
  161. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/events.py +0 -0
  162. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/services.py +0 -0
  163. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/sync.py +0 -0
  164. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/context.py +0 -0
  165. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/__init__.py +0 -0
  166. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/base.py +0 -0
  167. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/deployments.py +0 -0
  168. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/runner.py +0 -0
  169. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/schedules.py +0 -0
  170. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/steps/__init__.py +0 -0
  171. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/steps/core.py +0 -0
  172. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/steps/pull.py +0 -0
  173. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/steps/utility.py +0 -0
  174. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/__init__.py +0 -0
  175. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/data_documents.py +0 -0
  176. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/__init__.py +0 -0
  177. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/base.py +0 -0
  178. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/docker.py +0 -0
  179. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/file.py +0 -0
  180. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/orion.py +0 -0
  181. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/serializers.py +0 -0
  182. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/__init__.py +0 -0
  183. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/actions.py +0 -0
  184. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/clients.py +0 -0
  185. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/filters.py +0 -0
  186. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/instrument.py +0 -0
  187. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/utilities.py +0 -0
  188. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/worker.py +0 -0
  189. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/exceptions.py +0 -0
  190. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/filesystems.py +0 -0
  191. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/flow_runs.py +0 -0
  192. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/futures.py +0 -0
  193. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/__init__.py +0 -0
  194. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/base.py +0 -0
  195. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/container.py +0 -0
  196. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/kubernetes.py +0 -0
  197. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/process.py +0 -0
  198. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/provisioners/__init__.py +0 -0
  199. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/provisioners/cloud_run.py +0 -0
  200. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/provisioners/container_instance.py +0 -0
  201. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/provisioners/ecs.py +0 -0
  202. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/provisioners/modal.py +0 -0
  203. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/input/__init__.py +0 -0
  204. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/input/actions.py +0 -0
  205. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/input/run_input.py +0 -0
  206. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/__init__.py +0 -0
  207. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/configuration.py +0 -0
  208. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/filters.py +0 -0
  209. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/formatters.py +0 -0
  210. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/handlers.py +0 -0
  211. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/highlighters.py +0 -0
  212. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/loggers.py +0 -0
  213. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/logging.yml +0 -0
  214. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/manifests.py +0 -0
  215. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/plugins.py +0 -0
  216. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/profiles.toml +0 -0
  217. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/py.typed +0 -0
  218. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/results.py +0 -0
  219. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/__init__.py +0 -0
  220. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/runner.py +0 -0
  221. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/server.py +0 -0
  222. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/storage.py +0 -0
  223. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/submit.py +0 -0
  224. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/utils.py +0 -0
  225. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runtime/__init__.py +0 -0
  226. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runtime/deployment.py +0 -0
  227. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runtime/flow_run.py +0 -0
  228. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runtime/task_run.py +0 -0
  229. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/serializers.py +0 -0
  230. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/server/api/static/prefect-logo-mark-gradient.png +0 -0
  231. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/software/__init__.py +0 -0
  232. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/software/base.py +0 -0
  233. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/software/conda.py +0 -0
  234. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/software/pip.py +0 -0
  235. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/software/python.py +0 -0
  236. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/states.py +0 -0
  237. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/task_engine.py +0 -0
  238. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/task_runners.py +0 -0
  239. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/__init__.py +0 -0
  240. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/annotations.py +0 -0
  241. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/asyncutils.py +0 -0
  242. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/collections.py +0 -0
  243. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/compat.py +0 -0
  244. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/context.py +0 -0
  245. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/dispatch.py +0 -0
  246. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/dockerutils.py +0 -0
  247. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/filesystem.py +0 -0
  248. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/hashing.py +0 -0
  249. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/importtools.py +0 -0
  250. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/math.py +0 -0
  251. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/names.py +0 -0
  252. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/processutils.py +0 -0
  253. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/pydantic.py +0 -0
  254. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/render_swagger.py +0 -0
  255. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/schema_tools/__init__.py +0 -0
  256. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/schema_tools/validation.py +0 -0
  257. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/services.py +0 -0
  258. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/slugify.py +0 -0
  259. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/templating.py +0 -0
  260. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/text.py +0 -0
  261. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/validation.py +0 -0
  262. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/visualization.py +0 -0
  263. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/variables.py +0 -0
  264. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/__init__.py +0 -0
  265. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/base.py +0 -0
  266. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/block.py +0 -0
  267. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/process.py +0 -0
  268. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/server.py +0 -0
  269. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/utilities.py +0 -0
  270. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect_client.egg-info/dependency_links.txt +0 -0
  271. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect_client.egg-info/requires.txt +0 -0
  272. {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect_client.egg-info/top_level.txt +0 -0
  273. {prefect-client-2.16.4 → prefect-client-2.16.6}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prefect-client
3
- Version: 2.16.4
3
+ Version: 2.16.6
4
4
  Summary: Workflow orchestration and management.
5
5
  Home-page: https://www.prefect.io
6
6
  Author: Prefect Technologies, Inc.
@@ -31,6 +31,7 @@ pytkdocs >= 0.14.2
31
31
  pyyaml
32
32
  requests
33
33
  setuptools != 60.9.0; python_version < '3.8'
34
+ vermin
34
35
  virtualenv
35
36
  watchfiles
36
37
  respx
@@ -81,6 +81,16 @@ omit =
81
81
  src/prefect/server/database/migrations/versions/*
82
82
  ignore_errors = True
83
83
 
84
+ [vermin]
85
+ make_paths_absolute = no
86
+ backports =
87
+ typing_extensions
88
+ format = parsable
89
+ eval_annotations = yes
90
+ only_show_violations = yes
91
+ targets = 3.8-
92
+ exclusion_regex = ^src/prefect/_vendor/.*$|^src/prefect/utilities/compat\.py$
93
+
84
94
  [egg_info]
85
95
  tag_build =
86
96
  tag_date = 0
@@ -2,6 +2,7 @@
2
2
  Implementation of the `Call` data structure for transport of deferred function calls
3
3
  and low-level management of call execution.
4
4
  """
5
+
5
6
  import abc
6
7
  import asyncio
7
8
  import concurrent.futures
@@ -40,8 +41,8 @@ P = ParamSpec("P")
40
41
  # we already have strong references to the `Call` objects in other places
41
42
  # and b) this is used for performance optimizations where we have fallback
42
43
  # behavior if this weakref is garbage collected. A fix for issue #10952.
43
- current_call: contextvars.ContextVar["weakref.ref[Call]"] = contextvars.ContextVar(
44
- "current_call"
44
+ current_call: contextvars.ContextVar["weakref.ref[Call]"] = ( # novm
45
+ contextvars.ContextVar("current_call")
45
46
  )
46
47
 
47
48
  # Create a strong reference to tasks to prevent destruction during execution errors
@@ -9,3 +9,5 @@
9
9
  from pydantic.version import VERSION as PYDANTIC_VERSION
10
10
 
11
11
  HAS_PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.")
12
+
13
+ from ._compat import model_dump, model_json_schema, model_validate, IncEx
@@ -0,0 +1,213 @@
1
+ from typing import Any, Dict, Literal, Optional, Set, Type, Union
2
+
3
+ import typing_extensions
4
+ from pydantic import BaseModel
5
+
6
+ from prefect._internal.pydantic import HAS_PYDANTIC_V2
7
+ from prefect.logging.loggers import get_logger
8
+ from prefect.settings import PREFECT_EXPERIMENTAL_ENABLE_PYDANTIC_V2_INTERNALS
9
+
10
+ IncEx: typing_extensions.TypeAlias = (
11
+ "Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any], None]"
12
+ )
13
+
14
+ logger = get_logger("prefect._internal.pydantic")
15
+
16
+ if HAS_PYDANTIC_V2:
17
+ from pydantic.json_schema import GenerateJsonSchema
18
+
19
+
20
+ def is_pydantic_v2_compatible(
21
+ model_instance: Optional[BaseModel] = None, fn_name: Optional[str] = None
22
+ ) -> bool:
23
+ """
24
+ Determines if the current environment is compatible with Pydantic V2 features,
25
+ based on the presence of Pydantic V2 and a global setting that enables V2 functionalities.
26
+
27
+ This function primarily serves to facilitate conditional logic in code that needs to
28
+ operate differently depending on the availability of Pydantic V2 features. It checks
29
+ two conditions: whether Pydantic V2 is installed, and whether the use of V2 features
30
+ is explicitly enabled through a global setting (`PREFECT_EXPERIMENTAL_ENABLE_PYDANTIC_V2_INTERNALS`).
31
+
32
+ Parameters:
33
+ -----------
34
+ model_instance : Optional[BaseModel], optional
35
+ An instance of a Pydantic model. This parameter is used to perform a type check
36
+ to ensure the passed object is a Pydantic model instance. If not provided or if
37
+ the object is not a Pydantic model, a TypeError is raised. Defaults to None.
38
+
39
+ fn_name : Optional[str], optional
40
+ The name of the function or feature for which V2 compatibility is being checked.
41
+ This is used for logging purposes to provide more context in debug messages.
42
+ Defaults to None.
43
+
44
+ Returns:
45
+ --------
46
+ bool
47
+ True if the current environment supports Pydantic V2 features and if the global
48
+ setting for enabling V2 features is set to True. False otherwise.
49
+
50
+ Raises:
51
+ -------
52
+ TypeError
53
+ If `model_instance` is provided but is not an instance of a Pydantic BaseModel.
54
+ """
55
+ if model_instance and not isinstance(model_instance, BaseModel):
56
+ raise TypeError(
57
+ f"Expected a Pydantic model, but got {type(model_instance).__name__}"
58
+ )
59
+
60
+ should_dump_as_v2_model = (
61
+ HAS_PYDANTIC_V2 and PREFECT_EXPERIMENTAL_ENABLE_PYDANTIC_V2_INTERNALS
62
+ )
63
+
64
+ if should_dump_as_v2_model:
65
+ logger.debug(
66
+ f"Using Pydantic v2 compatibility layer for `{fn_name}`. This will be removed in a future release."
67
+ )
68
+
69
+ return True
70
+
71
+ elif HAS_PYDANTIC_V2:
72
+ logger.debug(
73
+ "Pydantic v2 compatibility layer is disabled. To enable, set `PREFECT_EXPERIMENTAL_ENABLE_PYDANTIC_V2_INTERNALS` to `True`."
74
+ )
75
+
76
+ else:
77
+ logger.debug("Pydantic v2 is not installed.")
78
+
79
+ return False
80
+
81
+
82
+ def model_dump(
83
+ model_instance: BaseModel,
84
+ *,
85
+ mode: Union[Literal["json", "python"], str] = "python",
86
+ include: IncEx = None,
87
+ exclude: IncEx = None,
88
+ by_alias: bool = False,
89
+ exclude_unset: bool = False,
90
+ exclude_defaults: bool = False,
91
+ exclude_none: bool = False,
92
+ round_trip: bool = False,
93
+ warnings: bool = True,
94
+ ) -> Dict[str, Any]:
95
+ """
96
+ Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.
97
+
98
+ Args:
99
+ mode: The mode in which `to_python` should run.
100
+ If mode is 'json', the output will only contain JSON serializable types.
101
+ If mode is 'python', the output may contain non-JSON-serializable Python objects.
102
+ include: A list of fields to include in the output.
103
+ exclude: A list of fields to exclude from the output.
104
+ by_alias: Whether to use the field's alias in the dictionary key if defined.
105
+ exclude_unset: Whether to exclude fields that have not been explicitly set.
106
+ exclude_defaults: Whether to exclude fields that are set to their default value.
107
+ exclude_none: Whether to exclude fields that have a value of `None`.
108
+ round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
109
+ warnings: Whether to log warnings when invalid fields are encountered.
110
+
111
+ Returns:
112
+ A dictionary representation of the model.
113
+ """
114
+ if is_pydantic_v2_compatible(model_instance=model_instance, fn_name="model_dump"):
115
+ return model_instance.model_dump(
116
+ mode=mode,
117
+ include=include,
118
+ exclude=exclude,
119
+ by_alias=by_alias,
120
+ exclude_unset=exclude_unset,
121
+ exclude_defaults=exclude_defaults,
122
+ exclude_none=exclude_none,
123
+ round_trip=round_trip,
124
+ warnings=warnings,
125
+ )
126
+
127
+ return model_instance.dict(
128
+ include=include,
129
+ exclude=exclude,
130
+ by_alias=by_alias,
131
+ exclude_unset=exclude_unset,
132
+ exclude_defaults=exclude_defaults,
133
+ exclude_none=exclude_none,
134
+ )
135
+
136
+
137
+ DEFAULT_REF_TEMPLATE = "#/$defs/{model}"
138
+ JsonSchemaMode = Literal["validation", "serialization"]
139
+
140
+
141
+ def model_json_schema(
142
+ model: Type[BaseModel],
143
+ *,
144
+ by_alias: bool = True,
145
+ ref_template: str = DEFAULT_REF_TEMPLATE,
146
+ schema_generator=None,
147
+ mode: JsonSchemaMode = "validation",
148
+ ) -> Dict[str, Any]:
149
+ """
150
+ Generates a JSON schema for a model class.
151
+
152
+ Parameters
153
+ ----------
154
+ by_alias : bool, optional
155
+ Whether to use attribute aliases or not, by default True
156
+ ref_template : str, optional
157
+ The reference template, by default DEFAULT_REF_TEMPLATE
158
+ schema_generator : type[GenerateEmptySchemaForUserClasses], optional
159
+ To override the logic used to generate the JSON schema, as a subclass of GenerateEmptySchemaForUserClasses with your desired modifications, by default GenerateEmptySchemaForUserClasses
160
+ mode : JsonSchemaMode, optional
161
+ The mode in which to generate the schema, by default 'validation'
162
+
163
+ Returns
164
+ -------
165
+ dict[str, Any]
166
+ The JSON schema for the given model class.
167
+ """
168
+ if is_pydantic_v2_compatible(fn_name="model_json_schema"):
169
+ schema_generator = GenerateJsonSchema
170
+ return model.model_json_schema(
171
+ by_alias=by_alias,
172
+ ref_template=ref_template,
173
+ schema_generator=schema_generator,
174
+ mode=mode,
175
+ )
176
+
177
+ return model.schema(
178
+ by_alias=by_alias,
179
+ ref_template=ref_template,
180
+ )
181
+
182
+
183
+ def model_validate(
184
+ model: Type[BaseModel],
185
+ obj: Any,
186
+ *,
187
+ strict: bool = False,
188
+ from_attributes: bool = False,
189
+ context: Optional[Dict[str, Any]] = None,
190
+ ) -> Union[BaseModel, Dict[str, Any]]:
191
+ """Validate a pydantic model instance.
192
+
193
+ Args:
194
+ obj: The object to validate.
195
+ strict: Whether to enforce types strictly.
196
+ from_attributes: Whether to extract data from object attributes.
197
+ context: Additional context to pass to the validator.
198
+
199
+ Raises:
200
+ ValidationError: If the object could not be validated.
201
+
202
+ Returns:
203
+ The validated model instance.
204
+ """
205
+ if is_pydantic_v2_compatible(fn_name="model_validate"):
206
+ return model.model_validate(
207
+ obj=obj,
208
+ strict=strict,
209
+ from_attributes=from_attributes,
210
+ context=context,
211
+ )
212
+
213
+ return model.parse_obj(obj)
@@ -0,0 +1,48 @@
1
+ import inspect
2
+ import typing
3
+
4
+ from prefect._internal.pydantic import HAS_PYDANTIC_V2
5
+
6
+ if HAS_PYDANTIC_V2:
7
+ from pydantic.v1 import BaseModel as V1BaseModel
8
+ else:
9
+ from pydantic import BaseModel as V1BaseModel
10
+
11
+
12
+ def is_v1_model(v) -> bool:
13
+ if isinstance(v, V1BaseModel):
14
+ return True
15
+ try:
16
+ if inspect.isclass(v) and issubclass(v, V1BaseModel):
17
+ return True
18
+ except TypeError:
19
+ pass
20
+
21
+ return False
22
+
23
+
24
+ def is_v1_type(v) -> bool:
25
+ if HAS_PYDANTIC_V2:
26
+ if is_v1_model(v):
27
+ return True
28
+
29
+ try:
30
+ return v.__module__.startswith("pydantic.v1.types")
31
+ except AttributeError:
32
+ return False
33
+
34
+ return True
35
+
36
+
37
+ def has_v1_type_as_param(signature: inspect.Signature) -> bool:
38
+ parameters = signature.parameters.values()
39
+ for p in parameters:
40
+ # check if this parameter is a v1 model
41
+ if is_v1_type(p.annotation):
42
+ return True
43
+
44
+ # check if this parameter is a collection of types
45
+ for v in typing.get_args(p.annotation):
46
+ if is_v1_type(v):
47
+ return True
48
+ return False
@@ -7,7 +7,7 @@ import typing as t
7
7
  import pendulum
8
8
  import pydantic
9
9
  from pydantic import BaseModel as V2BaseModel
10
- from pydantic import ConfigDict, create_model
10
+ from pydantic import ConfigDict, PydanticUndefinedAnnotation, create_model
11
11
  from pydantic.type_adapter import TypeAdapter
12
12
 
13
13
  from prefect._internal.pydantic.annotations.pendulum import (
@@ -106,7 +106,11 @@ def create_v2_schema(
106
106
  model = create_model(
107
107
  name_, __config__=model_cfg, __base__=model_base, **model_fields
108
108
  )
109
- adapter = TypeAdapter(model)
109
+ try:
110
+ adapter = TypeAdapter(model)
111
+ except PydanticUndefinedAnnotation as exc:
112
+ # in v1 this raises a TypeError, which is handled by parameter_schema
113
+ raise TypeError(exc.message)
110
114
 
111
115
  # root model references under #definitions
112
116
  schema = adapter.json_schema(
@@ -0,0 +1,185 @@
1
+ """
2
+ Interface for creating and reading artifacts.
3
+ """
4
+
5
+ import json
6
+ import math
7
+ from typing import Any, Dict, List, Optional, Union
8
+ from uuid import UUID
9
+
10
+ from prefect.client.orchestration import PrefectClient
11
+ from prefect.client.schemas.actions import ArtifactCreate
12
+ from prefect.client.utilities import inject_client
13
+ from prefect.context import FlowRunContext, TaskRunContext
14
+ from prefect.utilities.asyncutils import sync_compatible
15
+
16
+ INVALID_TABLE_TYPE_ERROR = (
17
+ "`create_table_artifact` requires a `table` argument of type `dict[list]` or"
18
+ " `list[dict]`."
19
+ )
20
+
21
+
22
+ @inject_client
23
+ async def _create_artifact(
24
+ type: str,
25
+ key: Optional[str] = None,
26
+ description: Optional[str] = None,
27
+ data: Optional[Union[Dict[str, Any], Any]] = None,
28
+ client: Optional[PrefectClient] = None,
29
+ ) -> UUID:
30
+ """
31
+ Helper function to create an artifact.
32
+
33
+ Arguments:
34
+ type: A string identifying the type of artifact.
35
+ key: A user-provided string identifier.
36
+ The key must only contain lowercase letters, numbers, and dashes.
37
+ description: A user-specified description of the artifact.
38
+ data: A JSON payload that allows for a result to be retrieved.
39
+ client: The PrefectClient
40
+
41
+ Returns:
42
+ - The table artifact ID.
43
+ """
44
+ artifact_args = {}
45
+ task_run_ctx = TaskRunContext.get()
46
+ flow_run_ctx = FlowRunContext.get()
47
+
48
+ if task_run_ctx:
49
+ artifact_args["task_run_id"] = task_run_ctx.task_run.id
50
+ artifact_args["flow_run_id"] = task_run_ctx.task_run.flow_run_id
51
+ elif flow_run_ctx:
52
+ artifact_args["flow_run_id"] = flow_run_ctx.flow_run.id
53
+
54
+ if key is not None:
55
+ artifact_args["key"] = key
56
+ if type is not None:
57
+ artifact_args["type"] = type
58
+ if description is not None:
59
+ artifact_args["description"] = description
60
+ if data is not None:
61
+ artifact_args["data"] = data
62
+
63
+ artifact = ArtifactCreate(**artifact_args)
64
+
65
+ return await client.create_artifact(artifact=artifact)
66
+
67
+
68
+ @sync_compatible
69
+ async def create_link_artifact(
70
+ link: str,
71
+ link_text: Optional[str] = None,
72
+ key: Optional[str] = None,
73
+ description: Optional[str] = None,
74
+ ) -> UUID:
75
+ """
76
+ Create a link artifact.
77
+
78
+ Arguments:
79
+ link: The link to create.
80
+ link_text: The link text.
81
+ key: A user-provided string identifier.
82
+ Required for the artifact to show in the Artifacts page in the UI.
83
+ The key must only contain lowercase letters, numbers, and dashes.
84
+ description: A user-specified description of the artifact.
85
+
86
+
87
+ Returns:
88
+ The table artifact ID.
89
+ """
90
+ formatted_link = f"[{link_text}]({link})" if link_text else f"[{link}]({link})"
91
+ artifact = await _create_artifact(
92
+ key=key,
93
+ type="markdown",
94
+ description=description,
95
+ data=formatted_link,
96
+ )
97
+
98
+ return artifact.id
99
+
100
+
101
+ @sync_compatible
102
+ async def create_markdown_artifact(
103
+ markdown: str,
104
+ key: Optional[str] = None,
105
+ description: Optional[str] = None,
106
+ ) -> UUID:
107
+ """
108
+ Create a markdown artifact.
109
+
110
+ Arguments:
111
+ markdown: The markdown to create.
112
+ key: A user-provided string identifier.
113
+ Required for the artifact to show in the Artifacts page in the UI.
114
+ The key must only contain lowercase letters, numbers, and dashes.
115
+ description: A user-specified description of the artifact.
116
+
117
+ Returns:
118
+ The table artifact ID.
119
+ """
120
+ artifact = await _create_artifact(
121
+ key=key,
122
+ type="markdown",
123
+ description=description,
124
+ data=markdown,
125
+ )
126
+
127
+ return artifact.id
128
+
129
+
130
+ @sync_compatible
131
+ async def create_table_artifact(
132
+ table: Union[Dict[str, List[Any]], List[Dict[str, Any]], List[List[Any]]],
133
+ key: Optional[str] = None,
134
+ description: Optional[str] = None,
135
+ ) -> UUID:
136
+ """
137
+ Create a table artifact.
138
+
139
+ Arguments:
140
+ table: The table to create.
141
+ key: A user-provided string identifier.
142
+ Required for the artifact to show in the Artifacts page in the UI.
143
+ The key must only contain lowercase letters, numbers, and dashes.
144
+ description: A user-specified description of the artifact.
145
+
146
+ Returns:
147
+ The table artifact ID.
148
+ """
149
+
150
+ def _sanitize_nan_values(item):
151
+ """
152
+ Sanitize NaN values in a given item. The item can be a dict, list or float.
153
+ """
154
+
155
+ if isinstance(item, list):
156
+ return [_sanitize_nan_values(sub_item) for sub_item in item]
157
+
158
+ elif isinstance(item, dict):
159
+ return {k: _sanitize_nan_values(v) for k, v in item.items()}
160
+
161
+ elif isinstance(item, float) and math.isnan(item):
162
+ return None
163
+
164
+ else:
165
+ return item
166
+
167
+ sanitized_table = _sanitize_nan_values(table)
168
+
169
+ if isinstance(table, dict) and all(isinstance(v, list) for v in table.values()):
170
+ pass
171
+ elif isinstance(table, list) and all(isinstance(v, (list, dict)) for v in table):
172
+ pass
173
+ else:
174
+ raise TypeError(INVALID_TABLE_TYPE_ERROR)
175
+
176
+ formatted_table = json.dumps(sanitized_table)
177
+
178
+ artifact = await _create_artifact(
179
+ key=key,
180
+ type="table",
181
+ description=description,
182
+ data=formatted_table,
183
+ )
184
+
185
+ return artifact.id
@@ -1,9 +1,10 @@
1
1
  import copy
2
2
  import sys
3
3
  import threading
4
+ import uuid
4
5
  from collections import defaultdict
5
6
  from contextlib import asynccontextmanager
6
- from functools import partial
7
+ from datetime import datetime, timezone
7
8
  from typing import (
8
9
  Any,
9
10
  AsyncGenerator,
@@ -11,6 +12,7 @@ from typing import (
11
12
  Callable,
12
13
  Dict,
13
14
  MutableMapping,
15
+ Optional,
14
16
  Protocol,
15
17
  Set,
16
18
  Tuple,
@@ -21,10 +23,11 @@ from typing import (
21
23
  import anyio
22
24
  import httpx
23
25
  from asgi_lifespan import LifespanManager
24
- from httpx import HTTPStatusError, Response
26
+ from httpx import HTTPStatusError, Request, Response
25
27
  from prefect._vendor.starlette import status
26
28
  from typing_extensions import Self
27
29
 
30
+ from prefect.client.schemas.objects import CsrfToken
28
31
  from prefect.exceptions import PrefectHTTPStatusError
29
32
  from prefect.logging import get_logger
30
33
  from prefect.settings import (
@@ -188,9 +191,20 @@ class PrefectHttpxClient(httpx.AsyncClient):
188
191
  [Configuring Cloudflare Rate Limiting](https://support.cloudflare.com/hc/en-us/articles/115001635128-Configuring-Rate-Limiting-from-UI)
189
192
  """
190
193
 
194
+ def __init__(self, *args, enable_csrf_support: bool = False, **kwargs):
195
+ self.enable_csrf_support: bool = enable_csrf_support
196
+ self.csrf_token: Optional[str] = None
197
+ self.csrf_token_expiration: Optional[datetime] = None
198
+ self.csrf_client_id: uuid.UUID = uuid.uuid4()
199
+
200
+ super().__init__(*args, **kwargs)
201
+
191
202
  async def _send_with_retry(
192
203
  self,
193
- request: Callable,
204
+ request: Request,
205
+ send: Callable[[Request], Awaitable[Response]],
206
+ send_args: Tuple,
207
+ send_kwargs: Dict,
194
208
  retry_codes: Set[int] = set(),
195
209
  retry_exceptions: Tuple[Exception, ...] = tuple(),
196
210
  ):
@@ -207,21 +221,34 @@ class PrefectHttpxClient(httpx.AsyncClient):
207
221
  try_count = 0
208
222
  response = None
209
223
 
224
+ is_change_request = request.method.lower() in {"post", "put", "patch", "delete"}
225
+
226
+ if self.enable_csrf_support and is_change_request:
227
+ await self._add_csrf_headers(request=request)
228
+
210
229
  while try_count <= PREFECT_CLIENT_MAX_RETRIES.value():
211
230
  try_count += 1
212
231
  retry_seconds = None
213
232
  exc_info = None
214
233
 
215
234
  try:
216
- response = await request()
235
+ response = await send(request, *send_args, **send_kwargs)
217
236
  except retry_exceptions: # type: ignore
218
237
  if try_count > PREFECT_CLIENT_MAX_RETRIES.value():
219
238
  raise
220
239
  # Otherwise, we will ignore this error but capture the info for logging
221
240
  exc_info = sys.exc_info()
222
241
  else:
223
- # We got a response; return immediately if it is not retryable
224
- if response.status_code not in retry_codes:
242
+ # We got a response; check if it's a CSRF error, otherwise
243
+ # return immediately if it is not retryable
244
+ if (
245
+ response.status_code == status.HTTP_403_FORBIDDEN
246
+ and "Invalid CSRF token" in response.text
247
+ ):
248
+ # We got a CSRF error, clear the token and try again
249
+ self.csrf_token = None
250
+ await self._add_csrf_headers(request)
251
+ elif response.status_code not in retry_codes:
225
252
  return response
226
253
 
227
254
  if "Retry-After" in response.headers:
@@ -268,19 +295,24 @@ class PrefectHttpxClient(httpx.AsyncClient):
268
295
  # We ran out of retries, return the failed response
269
296
  return response
270
297
 
271
- async def send(self, *args, **kwargs) -> Response:
298
+ async def send(self, request: Request, *args, **kwargs) -> Response:
272
299
  """
273
300
  Send a request with automatic retry behavior for the following status codes:
274
301
 
302
+ - 403 Forbidden, if the request failed due to CSRF protection
303
+ - 408 Request Timeout
275
304
  - 429 CloudFlare-style rate limiting
276
305
  - 502 Bad Gateway
277
306
  - 503 Service unavailable
307
+ - Any additional status codes provided in `PREFECT_CLIENT_RETRY_EXTRA_CODES`
278
308
  """
279
309
 
280
- api_request = partial(super().send, *args, **kwargs)
281
-
310
+ super_send = super().send
282
311
  response = await self._send_with_retry(
283
- request=api_request,
312
+ request=request,
313
+ send=super_send,
314
+ send_args=args,
315
+ send_kwargs=kwargs,
284
316
  retry_codes={
285
317
  status.HTTP_429_TOO_MANY_REQUESTS,
286
318
  status.HTTP_503_SERVICE_UNAVAILABLE,
@@ -312,3 +344,41 @@ class PrefectHttpxClient(httpx.AsyncClient):
312
344
  response.raise_for_status()
313
345
 
314
346
  return response
347
+
348
+ async def _add_csrf_headers(self, request: Request):
349
+ now = datetime.now(timezone.utc)
350
+
351
+ if not self.enable_csrf_support:
352
+ return
353
+
354
+ if not self.csrf_token or (
355
+ self.csrf_token_expiration and now > self.csrf_token_expiration
356
+ ):
357
+ token_request = self.build_request(
358
+ "GET", f"/csrf-token?client={self.csrf_client_id}"
359
+ )
360
+
361
+ try:
362
+ token_response = await self.send(token_request)
363
+ except PrefectHTTPStatusError as exc:
364
+ old_server = exc.response.status_code == status.HTTP_404_NOT_FOUND
365
+ unconfigured_server = (
366
+ exc.response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
367
+ and "CSRF protection is disabled." in exc.response.text
368
+ )
369
+
370
+ if old_server or unconfigured_server:
371
+ # The token endpoint is either unavailable, suggesting an
372
+ # older server, or CSRF protection is disabled. In either
373
+ # case we should disable CSRF support.
374
+ self.enable_csrf_support = False
375
+ return
376
+
377
+ raise
378
+
379
+ token: CsrfToken = CsrfToken.parse_obj(token_response.json())
380
+ self.csrf_token = token.token
381
+ self.csrf_token_expiration = token.expiration
382
+
383
+ request.headers["Prefect-Csrf-Token"] = self.csrf_token
384
+ request.headers["Prefect-Csrf-Client"] = str(self.csrf_client_id)