prefect-client 2.14.20__tar.gz → 2.14.21__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.14.20 → prefect-client-2.14.21}/PKG-INFO +1 -1
- {prefect-client-2.14.20 → prefect-client-2.14.21}/requirements-dev.txt +4 -3
- {prefect-client-2.14.20 → prefect-client-2.14.21}/requirements.txt +1 -1
- {prefect-client-2.14.20 → prefect-client-2.14.21}/setup.cfg +7 -3
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/blocks/notifications.py +2 -1
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/schemas/objects.py +11 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/subscriptions.py +17 -6
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/context.py +1 -1
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/engine.py +20 -8
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/events/utilities.py +4 -1
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/events/worker.py +10 -6
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/results.py +3 -6
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/runner/server.py +1 -1
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/settings.py +21 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/task_engine.py +10 -11
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/task_server.py +47 -31
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/asyncutils.py +11 -1
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/filesystem.py +10 -5
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect_client.egg-info/PKG-INFO +1 -1
- {prefect-client-2.14.20 → prefect-client-2.14.21}/LICENSE +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/MANIFEST.in +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/README.md +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/requirements-client.txt +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/setup.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/.prefectignore +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/_logging.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/compatibility/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/compatibility/deprecated.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/compatibility/experimental.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/concurrency/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/concurrency/api.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/concurrency/calls.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/concurrency/cancellation.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/concurrency/event_loop.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/concurrency/inspection.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/concurrency/primitives.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/concurrency/services.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/concurrency/threads.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/concurrency/waiters.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/pydantic/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/pydantic/annotations/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/pydantic/annotations/pendulum.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/pydantic/schemas.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/pydantic/v2_schema.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/pydantic/v2_validated_func.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/pytz.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/schemas/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/schemas/bases.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/schemas/fields.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/schemas/serializers.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/schemas/transformations.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_internal/schemas/validators.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/applications.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/background.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/concurrency.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/datastructures.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/dependencies/models.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/dependencies/utils.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/encoders.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/exception_handlers.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/exceptions.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/logger.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/middleware/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/middleware/cors.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/middleware/gzip.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/middleware/trustedhost.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/middleware/wsgi.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/openapi/constants.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/openapi/docs.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/openapi/models.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/openapi/utils.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/param_functions.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/params.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/requests.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/responses.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/routing.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/security/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/security/api_key.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/security/base.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/security/http.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/security/oauth2.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/security/utils.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/staticfiles.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/templating.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/testclient.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/types.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/utils.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_vendor/fastapi/websockets.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/_version.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/agent.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/blocks/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/blocks/abstract.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/blocks/core.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/blocks/fields.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/blocks/kubernetes.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/blocks/system.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/blocks/webhook.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/base.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/cloud.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/collections.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/constants.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/orchestration.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/schemas/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/schemas/actions.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/schemas/filters.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/schemas/responses.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/schemas/schedules.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/schemas/sorting.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/client/utilities.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/concurrency/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/concurrency/asyncio.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/concurrency/common.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/concurrency/events.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/concurrency/services.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/concurrency/sync.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/deployments/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/deployments/base.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/deployments/deployments.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/deployments/runner.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/deployments/steps/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/deployments/steps/core.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/deployments/steps/pull.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/deployments/steps/utility.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/deprecated/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/deprecated/data_documents.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/events/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/events/actions.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/events/clients.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/events/filters.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/events/instrument.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/events/related.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/events/schemas.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/exceptions.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/filesystems.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/flow_runs.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/flows.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/futures.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/infrastructure/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/infrastructure/base.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/infrastructure/container.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/infrastructure/kubernetes.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/infrastructure/process.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/infrastructure/provisioners/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/infrastructure/provisioners/cloud_run.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/infrastructure/provisioners/container_instance.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/infrastructure/provisioners/ecs.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/infrastructure/provisioners/modal.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/input/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/input/actions.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/input/run_input.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/logging/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/logging/configuration.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/logging/formatters.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/logging/handlers.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/logging/highlighters.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/logging/loggers.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/logging/logging.yml +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/manifests.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/packaging/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/packaging/base.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/packaging/docker.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/packaging/file.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/packaging/orion.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/packaging/serializers.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/plugins.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/profiles.toml +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/py.typed +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/runner/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/runner/runner.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/runner/storage.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/runner/submit.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/runner/utils.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/runtime/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/runtime/deployment.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/runtime/flow_run.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/runtime/task_run.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/serializers.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/server/api/collections_data/views/aggregate-worker-metadata.json +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/server/api/static/prefect-logo-mark-gradient.png +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/software/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/software/base.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/software/conda.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/software/pip.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/software/python.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/states.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/task_runners.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/tasks.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/annotations.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/callables.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/collections.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/compat.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/context.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/dispatch.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/dockerutils.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/hashing.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/importtools.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/math.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/names.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/processutils.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/pydantic.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/render_swagger.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/services.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/slugify.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/templating.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/text.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/validation.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/utilities/visualization.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/variables.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/workers/__init__.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/workers/base.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/workers/block.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/workers/process.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/workers/server.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect/workers/utilities.py +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect_client.egg-info/SOURCES.txt +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect_client.egg-info/dependency_links.txt +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect_client.egg-info/requires.txt +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/src/prefect_client.egg-info/top_level.txt +0 -0
- {prefect-client-2.14.20 → prefect-client-2.14.21}/versioneer.py +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
ruff
|
2
2
|
cairosvg
|
3
3
|
codespell
|
4
|
-
|
4
|
+
ddtrace
|
5
5
|
# Pin IPython to 8.12 when building on Python 3.8
|
6
6
|
# https://github.com/ipython/ipython/blob/main/README.rst
|
7
7
|
ipython == 8.12.*; python_version == '3.8'
|
@@ -13,12 +13,13 @@ mkdocs-material
|
|
13
13
|
mkdocstrings-python
|
14
14
|
mike
|
15
15
|
mock; python_version < '3.8'
|
16
|
-
moto
|
16
|
+
moto >= 5
|
17
17
|
mypy
|
18
18
|
numpy
|
19
19
|
pillow
|
20
20
|
pre-commit
|
21
|
-
|
21
|
+
pluggy >= 1.4.0
|
22
|
+
pytest > 7, < 8 # Datadog's ddtrace does not support pytest 8 yet. See https://github.com/DataDog/dd-trace-py/issues/8220
|
22
23
|
pytest-asyncio >= 0.18.2, != 0.22.0, < 0.23.0 # Cannot override event loop in 0.23.0. See https://github.com/pytest-dev/pytest-asyncio/issues/706 for more details.
|
23
24
|
pytest-cov
|
24
25
|
pytest-benchmark
|
@@ -1,5 +1,5 @@
|
|
1
1
|
[tool:pytest]
|
2
|
-
addopts = -
|
2
|
+
addopts = -rfEs
|
3
3
|
norecursedirs = *.egg-info .git .mypy_cache node_modules .pytest_cache .vscode
|
4
4
|
python_files =
|
5
5
|
test_*.py
|
@@ -13,7 +13,7 @@ markers =
|
|
13
13
|
env =
|
14
14
|
PREFECT_TEST_MODE = 1
|
15
15
|
asyncio_mode = auto
|
16
|
-
timeout =
|
16
|
+
timeout = 90
|
17
17
|
filterwarnings =
|
18
18
|
error
|
19
19
|
ignore::DeprecationWarning:tornado.platform.asyncio.*
|
@@ -26,7 +26,7 @@ filterwarnings =
|
|
26
26
|
ignore:The `dict` method is deprecated; use `model_dump` instead
|
27
27
|
ignore:The `schema` method is deprecated; use `model_json_schema` instead
|
28
28
|
ignore:Default value default=
|
29
|
-
ignore:Implicitly cleaning up
|
29
|
+
ignore:Implicitly cleaning up:ResourceWarning
|
30
30
|
ignore::ResourceWarning:distributed.diskutils
|
31
31
|
ignore:unclosed:ResourceWarning:distributed.node
|
32
32
|
ignore::ImportWarning:google.api_core.exceptions
|
@@ -46,6 +46,10 @@ filterwarnings =
|
|
46
46
|
ignore:`PREFECT_API_URL` should have `/api` after the base URL.:UserWarning
|
47
47
|
ignore:datetime\.datetime\.utcnow\(\) is deprecated and scheduled for removal in a future version\..*:DeprecationWarning
|
48
48
|
ignore:datetime\.datetime\.utcfromtimestamp\(\) is deprecated and scheduled for removal in a future version\..*:DeprecationWarning
|
49
|
+
ignore::sqlalchemy.exc.SAWarning
|
50
|
+
ignore::ResourceWarning
|
51
|
+
ignore::pytest.PytestUnraisableExceptionWarning
|
52
|
+
ignore::pluggy.PluggyTeardownRaisedWarning
|
49
53
|
|
50
54
|
[mypy]
|
51
55
|
disallow_untyped_defs = False
|
@@ -714,7 +714,8 @@ class CustomWebhookNotificationBlock(NotificationBlock):
|
|
714
714
|
|
715
715
|
# make request with httpx
|
716
716
|
client = httpx.AsyncClient(headers={"user-agent": "Prefect Notifications"})
|
717
|
-
|
717
|
+
async with client:
|
718
|
+
resp = await client.request(**self._build_request_args(body, subject))
|
718
719
|
resp.raise_for_status()
|
719
720
|
|
720
721
|
|
@@ -97,6 +97,14 @@ class DeploymentStatus(AutoEnum):
|
|
97
97
|
NOT_READY = AutoEnum.auto()
|
98
98
|
|
99
99
|
|
100
|
+
class WorkQueueStatus(AutoEnum):
|
101
|
+
"""Enumeration of work queue statuses."""
|
102
|
+
|
103
|
+
READY = AutoEnum.auto()
|
104
|
+
NOT_READY = AutoEnum.auto()
|
105
|
+
PAUSED = AutoEnum.auto()
|
106
|
+
|
107
|
+
|
100
108
|
class StateDetails(PrefectBaseModel):
|
101
109
|
flow_run_id: UUID = None
|
102
110
|
task_run_id: UUID = None
|
@@ -1222,6 +1230,9 @@ class WorkQueue(ObjectBaseModel):
|
|
1222
1230
|
last_polled: Optional[DateTimeTZ] = Field(
|
1223
1231
|
default=None, description="The last time an agent polled this queue for work."
|
1224
1232
|
)
|
1233
|
+
status: Optional[WorkQueueStatus] = Field(
|
1234
|
+
default=None, description="The queue status."
|
1235
|
+
)
|
1225
1236
|
|
1226
1237
|
@validator("name", check_fields=False)
|
1227
1238
|
def validate_name_characters(cls, v):
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import asyncio
|
2
|
-
from typing import Generic, Type, TypeVar
|
2
|
+
from typing import Generic, List, Type, TypeVar
|
3
3
|
|
4
4
|
import orjson
|
5
5
|
import websockets
|
@@ -8,18 +8,23 @@ from starlette.status import WS_1008_POLICY_VIOLATION
|
|
8
8
|
from typing_extensions import Self
|
9
9
|
|
10
10
|
from prefect._internal.schemas.bases import IDBaseModel
|
11
|
+
from prefect.logging import get_logger
|
11
12
|
from prefect.settings import PREFECT_API_KEY, PREFECT_API_URL
|
12
13
|
|
14
|
+
logger = get_logger(__name__)
|
15
|
+
|
13
16
|
S = TypeVar("S", bound=IDBaseModel)
|
14
17
|
|
15
18
|
|
16
19
|
class Subscription(Generic[S]):
|
17
|
-
def __init__(self, model: Type[S], path: str):
|
20
|
+
def __init__(self, model: Type[S], path: str, keys: List[str]):
|
18
21
|
self.model = model
|
19
22
|
|
20
23
|
base_url = PREFECT_API_URL.value().replace("http", "ws", 1)
|
21
24
|
self.subscription_url = f"{base_url}{path}"
|
22
25
|
|
26
|
+
self.keys = keys
|
27
|
+
|
23
28
|
self._connect = websockets.connect(
|
24
29
|
self.subscription_url,
|
25
30
|
subprotocols=["prefect"],
|
@@ -57,13 +62,19 @@ class Subscription(Generic[S]):
|
|
57
62
|
|
58
63
|
websocket = await self._connect.__aenter__()
|
59
64
|
|
60
|
-
await websocket.send(
|
61
|
-
orjson.dumps({"type": "auth", "token": PREFECT_API_KEY.value()}).decode()
|
62
|
-
)
|
63
|
-
|
64
65
|
try:
|
66
|
+
await websocket.send(
|
67
|
+
orjson.dumps(
|
68
|
+
{"type": "auth", "token": PREFECT_API_KEY.value()}
|
69
|
+
).decode()
|
70
|
+
)
|
71
|
+
|
65
72
|
auth = orjson.loads(await websocket.recv())
|
66
73
|
assert auth["type"] == "auth_success"
|
74
|
+
|
75
|
+
await websocket.send(
|
76
|
+
orjson.dumps({"type": "subscribe", "keys": self.keys}).decode()
|
77
|
+
)
|
67
78
|
except (
|
68
79
|
AssertionError,
|
69
80
|
websockets.exceptions.ConnectionClosedError,
|
@@ -236,7 +236,7 @@ class EngineContext(RunContext):
|
|
236
236
|
autonomous_task_run: Optional[TaskRun] = None
|
237
237
|
task_runner: BaseTaskRunner
|
238
238
|
log_prints: bool = False
|
239
|
-
parameters: Dict[str, Any]
|
239
|
+
parameters: Optional[Dict[str, Any]] = None
|
240
240
|
|
241
241
|
# Result handling
|
242
242
|
result_factory: ResultFactory
|
@@ -1341,12 +1341,13 @@ async def resume_flow_run(flow_run_id, run_input: Optional[Dict] = None):
|
|
1341
1341
|
run_input: a dictionary of inputs to provide to the flow run.
|
1342
1342
|
"""
|
1343
1343
|
client = get_client()
|
1344
|
-
|
1344
|
+
async with client:
|
1345
|
+
flow_run = await client.read_flow_run(flow_run_id)
|
1345
1346
|
|
1346
|
-
|
1347
|
-
|
1347
|
+
if not flow_run.state.is_paused():
|
1348
|
+
raise NotPausedError("Cannot resume a run that isn't paused!")
|
1348
1349
|
|
1349
|
-
|
1350
|
+
response = await client.resume_flow_run(flow_run_id, run_input=run_input)
|
1350
1351
|
|
1351
1352
|
if response.status == SetStateStatus.REJECT:
|
1352
1353
|
if response.state.type == StateType.FAILED:
|
@@ -1369,7 +1370,17 @@ def enter_task_run_engine(
|
|
1369
1370
|
|
1370
1371
|
if not flow_run_context:
|
1371
1372
|
if PREFECT_EXPERIMENTAL_ENABLE_TASK_SCHEDULING.value():
|
1372
|
-
|
1373
|
+
create_autonomous_task_run = create_call(
|
1374
|
+
_create_autonomous_task_run, task=task, parameters=parameters
|
1375
|
+
)
|
1376
|
+
if task.isasync:
|
1377
|
+
return from_async.wait_for_call_in_loop_thread(
|
1378
|
+
create_autonomous_task_run
|
1379
|
+
)
|
1380
|
+
else:
|
1381
|
+
return from_sync.wait_for_call_in_loop_thread(
|
1382
|
+
create_autonomous_task_run
|
1383
|
+
)
|
1373
1384
|
|
1374
1385
|
raise RuntimeError(
|
1375
1386
|
"Tasks cannot be run outside of a flow"
|
@@ -1738,7 +1749,10 @@ async def submit_task_run(
|
|
1738
1749
|
),
|
1739
1750
|
)
|
1740
1751
|
|
1741
|
-
if
|
1752
|
+
if (
|
1753
|
+
task_runner.concurrency_type != TaskConcurrencyType.SEQUENTIAL
|
1754
|
+
and not flow_run_context.autonomous_task_run
|
1755
|
+
):
|
1742
1756
|
logger.info(f"Submitted task run {task_run.name!r} for execution.")
|
1743
1757
|
|
1744
1758
|
return future
|
@@ -2200,7 +2214,6 @@ async def orchestrate_task_run(
|
|
2200
2214
|
level=logging.INFO if state.is_completed() else logging.ERROR,
|
2201
2215
|
msg=f"Finished in state {display_state}",
|
2202
2216
|
)
|
2203
|
-
logger.warning(f"Task run {task_run.name!r} finished in state {display_state}")
|
2204
2217
|
return state
|
2205
2218
|
|
2206
2219
|
|
@@ -2914,7 +2927,6 @@ def _emit_task_run_state_change_event(
|
|
2914
2927
|
)
|
2915
2928
|
|
2916
2929
|
|
2917
|
-
@sync_compatible
|
2918
2930
|
async def _create_autonomous_task_run(
|
2919
2931
|
task: Task, parameters: Dict[str, Any]
|
2920
2932
|
) -> TaskRun:
|
@@ -8,7 +8,7 @@ from prefect._internal.schemas.fields import DateTimeTZ
|
|
8
8
|
|
9
9
|
from .clients import AssertingEventsClient, PrefectCloudEventsClient
|
10
10
|
from .schemas import Event, RelatedResource
|
11
|
-
from .worker import EventsWorker
|
11
|
+
from .worker import EventsWorker, emit_events_to_cloud
|
12
12
|
|
13
13
|
TIGHT_TIMING = timedelta(minutes=5)
|
14
14
|
|
@@ -42,6 +42,9 @@ def emit_event(
|
|
42
42
|
The event that was emitted if worker is using a client that emit
|
43
43
|
events, otherwise None.
|
44
44
|
"""
|
45
|
+
if not emit_events_to_cloud():
|
46
|
+
return None
|
47
|
+
|
45
48
|
operational_clients = [AssertingEventsClient, PrefectCloudEventsClient]
|
46
49
|
worker_instance = EventsWorker.instance()
|
47
50
|
|
@@ -14,6 +14,15 @@ from .related import related_resources_from_run_context
|
|
14
14
|
from .schemas import Event
|
15
15
|
|
16
16
|
|
17
|
+
def emit_events_to_cloud() -> bool:
|
18
|
+
api = PREFECT_API_URL.value()
|
19
|
+
return (
|
20
|
+
experiment_enabled("events_client")
|
21
|
+
and api
|
22
|
+
and api.startswith(PREFECT_CLOUD_API_URL.value())
|
23
|
+
)
|
24
|
+
|
25
|
+
|
17
26
|
class EventsWorker(QueueService[Event]):
|
18
27
|
def __init__(
|
19
28
|
self, client_type: Type[EventsClient], client_options: Tuple[Tuple[str, Any]]
|
@@ -52,12 +61,7 @@ class EventsWorker(QueueService[Event]):
|
|
52
61
|
|
53
62
|
# Select a client type for this worker based on settings
|
54
63
|
if client_type is None:
|
55
|
-
|
56
|
-
if (
|
57
|
-
experiment_enabled("events_client")
|
58
|
-
and api
|
59
|
-
and api.startswith(PREFECT_CLOUD_API_URL.value())
|
60
|
-
):
|
64
|
+
if emit_events_to_cloud():
|
61
65
|
client_type = PrefectCloudEventsClient
|
62
66
|
client_kwargs = {
|
63
67
|
"api_url": PREFECT_API_URL.value(),
|
@@ -185,10 +185,9 @@ class ResultFactory(pydantic.BaseModel):
|
|
185
185
|
persist_result=(
|
186
186
|
flow.persist_result
|
187
187
|
if flow.persist_result is not None
|
188
|
-
else
|
189
188
|
# !! Child flows persist their result by default if the it or the
|
190
189
|
# parent flow uses a feature that requires it
|
191
|
-
(
|
190
|
+
else (
|
192
191
|
flow_features_require_result_persistence(flow)
|
193
192
|
or flow_features_require_child_result_persistence(ctx.flow)
|
194
193
|
or get_default_persist_setting()
|
@@ -209,10 +208,9 @@ class ResultFactory(pydantic.BaseModel):
|
|
209
208
|
persist_result=(
|
210
209
|
flow.persist_result
|
211
210
|
if flow.persist_result is not None
|
212
|
-
else
|
213
211
|
# !! Flows persist their result by default if uses a feature that
|
214
212
|
# requires it
|
215
|
-
(
|
213
|
+
else (
|
216
214
|
flow_features_require_result_persistence(flow)
|
217
215
|
or get_default_persist_setting()
|
218
216
|
)
|
@@ -246,10 +244,9 @@ class ResultFactory(pydantic.BaseModel):
|
|
246
244
|
persist_result = (
|
247
245
|
task.persist_result
|
248
246
|
if task.persist_result is not None
|
249
|
-
else
|
250
247
|
# !! Tasks persist their result by default if their parent flow uses a
|
251
248
|
# feature that requires it or the task uses a feature that requires it
|
252
|
-
(
|
249
|
+
else (
|
253
250
|
(
|
254
251
|
flow_features_require_child_result_persistence(ctx.flow)
|
255
252
|
if ctx
|
@@ -87,7 +87,7 @@ async def _build_endpoint_for_deployment(
|
|
87
87
|
deployment: "DeploymentResponse", runner: "Runner"
|
88
88
|
) -> Callable:
|
89
89
|
async def _create_flow_run_for_deployment(
|
90
|
-
body: Optional[Dict[Any, Any]] = None
|
90
|
+
body: Optional[Dict[Any, Any]] = None,
|
91
91
|
) -> JSONResponse:
|
92
92
|
body = body or {}
|
93
93
|
if deployment.enforce_parameter_schema and deployment.parameter_openapi_schema:
|
@@ -1416,6 +1416,22 @@ PREFECT_TASK_SCHEDULING_DELETE_FAILED_SUBMISSIONS = Setting(
|
|
1416
1416
|
Whether or not to delete failed task submissions from the database.
|
1417
1417
|
"""
|
1418
1418
|
|
1419
|
+
PREFECT_TASK_SCHEDULING_MAX_SCHEDULED_QUEUE_SIZE = Setting(
|
1420
|
+
int,
|
1421
|
+
default=1000,
|
1422
|
+
)
|
1423
|
+
"""
|
1424
|
+
The maximum number of scheduled tasks to queue for submission.
|
1425
|
+
"""
|
1426
|
+
|
1427
|
+
PREFECT_TASK_SCHEDULING_MAX_RETRY_QUEUE_SIZE = Setting(
|
1428
|
+
int,
|
1429
|
+
default=100,
|
1430
|
+
)
|
1431
|
+
"""
|
1432
|
+
The maximum number of retries to queue for submission.
|
1433
|
+
"""
|
1434
|
+
|
1419
1435
|
PREFECT_EXPERIMENTAL_ENABLE_EXTRA_RUNNER_ENDPOINTS = Setting(bool, default=False)
|
1420
1436
|
"""
|
1421
1437
|
Whether or not to enable experimental worker webserver endpoints.
|
@@ -1447,6 +1463,11 @@ PREFECT_EXPERIMENTAL_ENABLE_TASK_SCHEDULING = Setting(bool, default=False)
|
|
1447
1463
|
Whether or not to enable experimental task scheduling.
|
1448
1464
|
"""
|
1449
1465
|
|
1466
|
+
PREFECT_EXPERIMENTAL_ENABLE_WORK_QUEUE_STATUS = Setting(bool, default=False)
|
1467
|
+
"""
|
1468
|
+
Whether or not to enable experimental work queue status in-place of work queue health.
|
1469
|
+
"""
|
1470
|
+
|
1450
1471
|
# Defaults -----------------------------------------------------------------------------
|
1451
1472
|
|
1452
1473
|
PREFECT_DEFAULT_RESULT_STORAGE_BLOCK = Setting(
|
@@ -8,7 +8,6 @@ from typing import (
|
|
8
8
|
)
|
9
9
|
|
10
10
|
import anyio
|
11
|
-
from anyio import start_blocking_portal
|
12
11
|
from typing_extensions import Literal
|
13
12
|
|
14
13
|
from prefect._internal.concurrency.api import create_call, from_async, from_sync
|
@@ -21,7 +20,7 @@ from prefect.engine import (
|
|
21
20
|
)
|
22
21
|
from prefect.futures import PrefectFuture
|
23
22
|
from prefect.results import ResultFactory
|
24
|
-
from prefect.task_runners import BaseTaskRunner
|
23
|
+
from prefect.task_runners import BaseTaskRunner
|
25
24
|
from prefect.tasks import Task
|
26
25
|
from prefect.utilities.asyncutils import sync_compatible
|
27
26
|
|
@@ -32,28 +31,28 @@ EngineReturnType = Literal["future", "state", "result"]
|
|
32
31
|
async def submit_autonomous_task_to_engine(
|
33
32
|
task: Task,
|
34
33
|
task_run: TaskRun,
|
34
|
+
task_runner: Type[BaseTaskRunner],
|
35
35
|
parameters: Optional[Dict] = None,
|
36
36
|
wait_for: Optional[Iterable[PrefectFuture]] = None,
|
37
37
|
mapped: bool = False,
|
38
38
|
return_type: EngineReturnType = "future",
|
39
|
-
|
39
|
+
client=None,
|
40
40
|
) -> Any:
|
41
|
-
parameters = parameters or {}
|
42
41
|
async with AsyncExitStack() as stack:
|
42
|
+
if not task_runner._started:
|
43
|
+
task_runner_ctx = await stack.enter_async_context(task_runner.start())
|
44
|
+
else:
|
45
|
+
task_runner_ctx = task_runner
|
46
|
+
parameters = parameters or {}
|
43
47
|
with EngineContext(
|
44
48
|
flow=None,
|
45
49
|
flow_run=None,
|
46
50
|
autonomous_task_run=task_run,
|
47
|
-
task_runner=
|
48
|
-
|
49
|
-
),
|
50
|
-
client=await stack.enter_async_context(get_client()),
|
51
|
+
task_runner=task_runner_ctx,
|
52
|
+
client=client or await stack.enter_async_context(get_client()),
|
51
53
|
parameters=parameters,
|
52
54
|
result_factory=await ResultFactory.from_task(task),
|
53
55
|
background_tasks=await stack.enter_async_context(anyio.create_task_group()),
|
54
|
-
sync_portal=(
|
55
|
-
stack.enter_context(start_blocking_portal()) if task.isasync else None
|
56
|
-
),
|
57
56
|
) as flow_run_context:
|
58
57
|
begin_run = create_call(
|
59
58
|
begin_task_map if mapped else get_task_call_return_value,
|
@@ -1,8 +1,9 @@
|
|
1
1
|
import asyncio
|
2
2
|
import signal
|
3
3
|
import sys
|
4
|
+
from contextlib import AsyncExitStack
|
4
5
|
from functools import partial
|
5
|
-
from typing import Iterable, Optional
|
6
|
+
from typing import Iterable, Optional, Type
|
6
7
|
|
7
8
|
import anyio
|
8
9
|
import anyio.abc
|
@@ -19,40 +20,44 @@ from prefect.settings import (
|
|
19
20
|
PREFECT_TASK_SCHEDULING_DELETE_FAILED_SUBMISSIONS,
|
20
21
|
)
|
21
22
|
from prefect.task_engine import submit_autonomous_task_to_engine
|
23
|
+
from prefect.task_runners import BaseTaskRunner, ConcurrentTaskRunner
|
22
24
|
from prefect.utilities.asyncutils import asyncnullcontext, sync_compatible
|
23
|
-
from prefect.utilities.collections import distinct
|
24
25
|
from prefect.utilities.processutils import _register_signal
|
25
26
|
|
26
27
|
logger = get_logger("task_server")
|
27
28
|
|
28
29
|
|
29
30
|
class TaskServer:
|
30
|
-
"""This class is responsible for serving tasks that may be executed autonomously
|
31
|
-
|
31
|
+
"""This class is responsible for serving tasks that may be executed autonomously by a
|
32
|
+
task runner in the engine.
|
32
33
|
|
33
|
-
When `start()` is called, the task server will
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
When `start()` is called, the task server will open a websocket connection to a
|
35
|
+
server-side queue of scheduled task runs. When a scheduled task run is found, the
|
36
|
+
scheduled task run is submitted to the engine for execution with a minimal `EngineContext`
|
37
|
+
so that the task run can be governed by orchestration rules.
|
37
38
|
|
38
39
|
Args:
|
39
40
|
- tasks: A list of tasks to serve. These tasks will be submitted to the engine
|
40
41
|
when a scheduled task run is found.
|
41
|
-
-
|
42
|
+
- task_runner: The task runner to use for executing the tasks. Defaults to
|
43
|
+
`ConcurrentTaskRunner`.
|
42
44
|
"""
|
43
45
|
|
44
46
|
def __init__(
|
45
47
|
self,
|
46
48
|
*tasks: Task,
|
47
|
-
|
49
|
+
task_runner: Optional[Type[BaseTaskRunner]] = None,
|
50
|
+
extra_tags: Optional[Iterable[str]] = None,
|
48
51
|
):
|
49
52
|
self.tasks: list[Task] = tasks
|
50
|
-
self.
|
53
|
+
self.task_runner: Type[BaseTaskRunner] = task_runner or ConcurrentTaskRunner()
|
54
|
+
self.extra_tags: Iterable[str] = extra_tags or []
|
51
55
|
self.last_polled: Optional[pendulum.DateTime] = None
|
52
|
-
self.started = False
|
53
|
-
self.stopping = False
|
56
|
+
self.started: bool = False
|
57
|
+
self.stopping: bool = False
|
54
58
|
|
55
59
|
self._client = get_client()
|
60
|
+
self._exit_stack = AsyncExitStack()
|
56
61
|
|
57
62
|
if not asyncio.get_event_loop().is_running():
|
58
63
|
raise RuntimeError(
|
@@ -89,14 +94,15 @@ class TaskServer:
|
|
89
94
|
" calling .start()"
|
90
95
|
)
|
91
96
|
|
92
|
-
logger.info("Stopping task server...")
|
93
97
|
self.started = False
|
94
98
|
self.stopping = True
|
95
99
|
|
96
100
|
async def _subscribe_to_task_scheduling(self):
|
97
|
-
|
98
|
-
|
99
|
-
|
101
|
+
async for task_run in Subscription(
|
102
|
+
TaskRun,
|
103
|
+
"/task_runs/subscriptions/scheduled",
|
104
|
+
[task.task_key for task in self.tasks],
|
105
|
+
):
|
100
106
|
logger.info(f"Received task run: {task_run.id} - {task_run.name}")
|
101
107
|
await self._submit_pending_task_run(task_run)
|
102
108
|
|
@@ -142,22 +148,23 @@ class TaskServer:
|
|
142
148
|
f"Submitting run {task_run.name!r} of task {task.name!r} to engine"
|
143
149
|
)
|
144
150
|
|
145
|
-
task_run.tags = distinct(task_run.tags + list(self.tags))
|
146
|
-
|
147
151
|
self._runs_task_group.start_soon(
|
148
152
|
partial(
|
149
153
|
submit_autonomous_task_to_engine,
|
150
154
|
task=task,
|
151
155
|
task_run=task_run,
|
152
156
|
parameters=parameters,
|
157
|
+
task_runner=self.task_runner,
|
158
|
+
client=self._client,
|
153
159
|
)
|
154
160
|
)
|
155
161
|
|
156
162
|
async def __aenter__(self):
|
157
163
|
logger.debug("Starting task server...")
|
158
|
-
|
159
|
-
await self.
|
160
|
-
await self.
|
164
|
+
|
165
|
+
self._client = await self._exit_stack.enter_async_context(get_client())
|
166
|
+
await self._exit_stack.enter_async_context(self._runs_task_group)
|
167
|
+
await self._exit_stack.enter_async_context(self.task_runner.start())
|
161
168
|
|
162
169
|
self.started = True
|
163
170
|
return self
|
@@ -165,20 +172,25 @@ class TaskServer:
|
|
165
172
|
async def __aexit__(self, *exc_info):
|
166
173
|
logger.debug("Stopping task server...")
|
167
174
|
self.started = False
|
168
|
-
|
169
|
-
|
170
|
-
if self._client:
|
171
|
-
await self._client.__aexit__(*exc_info)
|
175
|
+
|
176
|
+
await self._exit_stack.__aexit__(*exc_info)
|
172
177
|
|
173
178
|
|
174
179
|
@sync_compatible
|
175
|
-
async def serve(
|
176
|
-
|
180
|
+
async def serve(
|
181
|
+
*tasks: Task,
|
182
|
+
task_runner: Optional[Type[BaseTaskRunner]] = None,
|
183
|
+
extra_tags: Optional[Iterable[str]] = None,
|
184
|
+
):
|
185
|
+
"""Serve the provided tasks so that they may be submitted and executed to the engine.
|
186
|
+
Tasks do not need to be within a flow run context to be submitted and executed.
|
187
|
+
Ideally, you should `.submit` the same task object that you pass to `serve`.
|
177
188
|
|
178
189
|
Args:
|
179
190
|
- tasks: A list of tasks to serve. When a scheduled task run is found for a
|
180
191
|
given task, the task run will be submitted to the engine for execution.
|
181
|
-
-
|
192
|
+
- task_runner: The task runner to use for executing the tasks. Defaults to
|
193
|
+
`ConcurrentTaskRunner`.
|
182
194
|
|
183
195
|
Example:
|
184
196
|
```python
|
@@ -204,5 +216,9 @@ async def serve(*tasks: Task, tags: Optional[Iterable[str]] = None):
|
|
204
216
|
" to True."
|
205
217
|
)
|
206
218
|
|
207
|
-
task_server = TaskServer(*tasks,
|
208
|
-
|
219
|
+
task_server = TaskServer(*tasks, task_runner=task_runner)
|
220
|
+
try:
|
221
|
+
await task_server.start()
|
222
|
+
|
223
|
+
except (asyncio.CancelledError, KeyboardInterrupt):
|
224
|
+
logger.info("Task server interrupted, stopping...")
|
@@ -28,6 +28,8 @@ import anyio.abc
|
|
28
28
|
import sniffio
|
29
29
|
from typing_extensions import Literal, ParamSpec, TypeGuard
|
30
30
|
|
31
|
+
from prefect.logging import get_logger
|
32
|
+
|
31
33
|
T = TypeVar("T")
|
32
34
|
P = ParamSpec("P")
|
33
35
|
R = TypeVar("R")
|
@@ -40,6 +42,8 @@ EVENT_LOOP_GC_REFS = {}
|
|
40
42
|
|
41
43
|
PREFECT_THREAD_LIMITER: Optional[anyio.CapacityLimiter] = None
|
42
44
|
|
45
|
+
logger = get_logger()
|
46
|
+
|
43
47
|
|
44
48
|
def get_thread_limiter():
|
45
49
|
global PREFECT_THREAD_LIMITER
|
@@ -334,7 +338,13 @@ async def add_event_loop_shutdown_callback(coroutine_fn: Callable[[], Awaitable]
|
|
334
338
|
EVENT_LOOP_GC_REFS[key] = on_shutdown(key)
|
335
339
|
|
336
340
|
# Begin iterating so it will be cleaned up as an incomplete generator
|
337
|
-
|
341
|
+
try:
|
342
|
+
await EVENT_LOOP_GC_REFS[key].__anext__()
|
343
|
+
# There is a poorly understood edge case we've seen in CI where the key is
|
344
|
+
# removed from the dict before we begin generator iteration.
|
345
|
+
except KeyError:
|
346
|
+
logger.warn("The event loop shutdown callback was not properly registered. ")
|
347
|
+
pass
|
338
348
|
|
339
349
|
|
340
350
|
class GatherIncomplete(RuntimeError):
|
@@ -3,6 +3,7 @@ Utilities for working with file systems
|
|
3
3
|
"""
|
4
4
|
import os
|
5
5
|
import pathlib
|
6
|
+
import threading
|
6
7
|
from contextlib import contextmanager
|
7
8
|
from pathlib import Path, PureWindowsPath
|
8
9
|
from typing import Union
|
@@ -51,6 +52,9 @@ def filter_files(
|
|
51
52
|
return included_files
|
52
53
|
|
53
54
|
|
55
|
+
chdir_lock = threading.Lock()
|
56
|
+
|
57
|
+
|
54
58
|
@contextmanager
|
55
59
|
def tmpchdir(path: str):
|
56
60
|
"""
|
@@ -62,11 +66,12 @@ def tmpchdir(path: str):
|
|
62
66
|
|
63
67
|
owd = os.getcwd()
|
64
68
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
69
|
+
with chdir_lock:
|
70
|
+
try:
|
71
|
+
os.chdir(path)
|
72
|
+
yield path
|
73
|
+
finally:
|
74
|
+
os.chdir(owd)
|
70
75
|
|
71
76
|
|
72
77
|
def filename(path: str) -> str:
|