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.
- {prefect-client-2.16.4 → prefect-client-2.16.6}/PKG-INFO +1 -1
- {prefect-client-2.16.4 → prefect-client-2.16.6}/requirements-dev.txt +1 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/setup.cfg +10 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/calls.py +3 -2
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/__init__.py +2 -0
- prefect-client-2.16.6/src/prefect/_internal/pydantic/_compat.py +213 -0
- prefect-client-2.16.6/src/prefect/_internal/pydantic/v1_schema.py +48 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/v2_schema.py +6 -2
- prefect-client-2.16.6/src/prefect/artifacts.py +185 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/base.py +80 -10
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/cloud.py +5 -2
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/orchestration.py +17 -1
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/objects.py +13 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/subscriptions.py +3 -3
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/engine.py +10 -6
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/related.py +1 -1
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/schemas.py +45 -1
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/flows.py +12 -4
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/server/api/collections_data/views/aggregate-worker-metadata.json +14 -2
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/settings.py +62 -8
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/task_server.py +9 -3
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/tasks.py +13 -8
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/callables.py +5 -3
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/schema_tools/hydration.py +14 -6
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect_client.egg-info/PKG-INFO +1 -1
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect_client.egg-info/SOURCES.txt +3 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/LICENSE +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/MANIFEST.in +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/README.md +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/requirements-client.txt +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/requirements.txt +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/setup.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/.prefectignore +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/_logging.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/compatibility/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/compatibility/deprecated.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/compatibility/experimental.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/api.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/cancellation.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/event_loop.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/inspection.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/primitives.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/services.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/threads.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/concurrency/waiters.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/annotations/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/annotations/pendulum.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/schemas.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pydantic/v2_validated_func.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/pytz.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/bases.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/fields.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/serializers.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/transformations.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_internal/schemas/validators.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/applications.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/background.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/concurrency.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/datastructures.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/dependencies/models.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/dependencies/utils.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/encoders.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/exception_handlers.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/exceptions.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/logger.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/cors.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/gzip.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/trustedhost.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/middleware/wsgi.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/openapi/constants.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/openapi/docs.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/openapi/models.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/openapi/utils.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/param_functions.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/params.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/requests.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/responses.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/routing.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/api_key.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/base.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/http.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/oauth2.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/security/utils.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/staticfiles.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/templating.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/testclient.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/types.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/utils.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/fastapi/websockets.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/_compat.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/_exception_handler.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/_utils.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/applications.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/authentication.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/background.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/concurrency.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/config.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/convertors.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/datastructures.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/endpoints.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/exceptions.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/formparsers.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/authentication.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/base.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/cors.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/errors.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/exceptions.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/gzip.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/httpsredirect.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/sessions.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/trustedhost.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/middleware/wsgi.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/requests.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/responses.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/routing.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/schemas.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/staticfiles.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/status.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/templating.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/testclient.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/types.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_vendor/starlette/websockets.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/_version.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/agent.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/abstract.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/core.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/fields.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/kubernetes.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/notifications.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/system.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/blocks/webhook.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/collections.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/constants.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/actions.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/filters.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/responses.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/schedules.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/schemas/sorting.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/client/utilities.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/asyncio.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/common.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/events.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/services.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/concurrency/sync.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/context.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/base.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/deployments.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/runner.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/schedules.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/steps/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/steps/core.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/steps/pull.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deployments/steps/utility.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/data_documents.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/base.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/docker.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/file.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/orion.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/deprecated/packaging/serializers.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/actions.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/clients.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/filters.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/instrument.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/utilities.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/events/worker.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/exceptions.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/filesystems.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/flow_runs.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/futures.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/base.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/container.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/kubernetes.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/process.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/provisioners/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/provisioners/cloud_run.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/provisioners/container_instance.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/provisioners/ecs.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/infrastructure/provisioners/modal.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/input/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/input/actions.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/input/run_input.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/configuration.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/filters.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/formatters.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/handlers.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/highlighters.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/loggers.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/logging/logging.yml +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/manifests.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/plugins.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/profiles.toml +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/py.typed +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/results.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/runner.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/server.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/storage.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/submit.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runner/utils.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runtime/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runtime/deployment.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runtime/flow_run.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/runtime/task_run.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/serializers.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/server/api/static/prefect-logo-mark-gradient.png +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/software/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/software/base.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/software/conda.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/software/pip.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/software/python.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/states.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/task_engine.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/task_runners.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/annotations.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/asyncutils.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/collections.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/compat.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/context.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/dispatch.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/dockerutils.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/filesystem.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/hashing.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/importtools.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/math.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/names.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/processutils.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/pydantic.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/render_swagger.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/schema_tools/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/schema_tools/validation.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/services.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/slugify.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/templating.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/text.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/validation.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/utilities/visualization.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/variables.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/__init__.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/base.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/block.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/process.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/server.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect/workers/utilities.py +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect_client.egg-info/dependency_links.txt +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect_client.egg-info/requires.txt +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/src/prefect_client.egg-info/top_level.txt +0 -0
- {prefect-client-2.16.4 → prefect-client-2.16.6}/versioneer.py +0 -0
@@ -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]"] =
|
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
|
@@ -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
|
-
|
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
|
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:
|
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;
|
224
|
-
if
|
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
|
-
|
281
|
-
|
310
|
+
super_send = super().send
|
282
311
|
response = await self._send_with_retry(
|
283
|
-
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)
|