repid 2.1.2__tar.gz → 2.1.3__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.
- {repid-2.1.2 → repid-2.1.3}/.github/workflows/codeql.yml +1 -1
- {repid-2.1.2 → repid-2.1.3}/.github/workflows/dependency-review.yml +1 -1
- {repid-2.1.2 → repid-2.1.3}/.github/workflows/docs.yml +1 -1
- {repid-2.1.2 → repid-2.1.3}/.github/workflows/mypy.yml +1 -1
- {repid-2.1.2 → repid-2.1.3}/.github/workflows/release.yaml +1 -1
- {repid-2.1.2 → repid-2.1.3}/.github/workflows/tests.yaml +2 -2
- {repid-2.1.2 → repid-2.1.3}/.pre-commit-config.yaml +2 -2
- {repid-2.1.2 → repid-2.1.3}/PKG-INFO +1 -1
- repid-2.1.3/docs/cookbook/opentelemetry_middleware.md +281 -0
- {repid-2.1.2 → repid-2.1.3}/mkdocs.yml +2 -0
- {repid-2.1.2 → repid-2.1.3}/repid/_runner.py +21 -30
- {repid-2.1.2 → repid-2.1.3}/repid/_worker.py +5 -10
- {repid-2.1.2 → repid-2.1.3}/repid/connections/abc.py +9 -1
- {repid-2.1.2 → repid-2.1.3}/repid/converter.py +11 -20
- {repid-2.1.2 → repid-2.1.3}/repid/data/__init__.py +1 -0
- {repid-2.1.2 → repid-2.1.3}/repid/data/actor.py +9 -1
- {repid-2.1.2 → repid-2.1.3}/repid/dependencies/_utils.py +3 -5
- {repid-2.1.2 → repid-2.1.3}/repid/dependencies/message_dependency.py +20 -15
- {repid-2.1.2 → repid-2.1.3}/repid/main.py +6 -3
- {repid-2.1.2 → repid-2.1.3}/repid/test_client.py +10 -5
- {repid-2.1.2 → repid-2.1.3}/tests/integration/conftest.py +39 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_converter.py +29 -41
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_dependencies.py +14 -5
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_middleware.py +92 -1
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_runner.py +28 -32
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_worker.py +29 -8
- {repid-2.1.2 → repid-2.1.3}/uv.lock +172 -172
- {repid-2.1.2 → repid-2.1.3}/.devcontainer/devcontainer.json +0 -0
- {repid-2.1.2 → repid-2.1.3}/.github/CODE_OF_CONDUCT.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/.github/FUNDING.yml +0 -0
- {repid-2.1.2 → repid-2.1.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/.github/ISSUE_TEMPLATE/other.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/.github/dependabot.yml +0 -0
- {repid-2.1.2 → repid-2.1.3}/.gitignore +0 -0
- {repid-2.1.2 → repid-2.1.3}/LICENSE +0 -0
- {repid-2.1.2 → repid-2.1.3}/Makefile +0 -0
- {repid-2.1.2 → repid-2.1.3}/README.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/SECURITY.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/advanced_user_guide/your_own_brokers.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/advanced_user_guide/your_own_converter.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/advanced_user_guide/your_own_serializer.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/benchmarks.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/benchmarks_img/.gitignore +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/benchmarks_img/chart.svg +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/contributing.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/cookbook/chaining_jobs.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/cookbook/large_payloads.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/cookbook/middlewares.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/cookbook/rabbitmq_retries.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/cookbook/sentry_middleware.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/cookbook/testing.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/features.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/getting_started/glossary.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/getting_started/quickstart.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/getting_started/supported_brokers.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/index.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/integrations/asyncapi.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/integrations/fastapi_and_pydantic.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/playground.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/actors/confirmation_and_errors.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/actors/execution.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/actors/index.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/actors/parsing.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/actors/routers.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/actors/routing.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/dependency_injection.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/message_registry.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/messages.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/raw_message_and_eager_response.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/server_registry.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/server_specific_parameters.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/workers/built_in_servers.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/workers/concurrency.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/workers/index.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/docs/user_guide/workers/lifecycle.md +0 -0
- {repid-2.1.2 → repid-2.1.3}/pyproject.toml +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/_utils/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/_utils/asyncify_.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/_utils/is_installed.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/_utils/json_encoder.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/_utils/not_set.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/generator.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/asyncapi.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/channels.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/common/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/common/bindings.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/common/correlation_id.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/common/external_docs.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/common/message.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/common/reference.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/common/tag.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/components.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/info.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/operations.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi/models/servers.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/asyncapi_server.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/_uamqp/LICENSE.txt +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/_uamqp/_decode.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/_uamqp/_encode.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/_uamqp/amqptypes.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/_uamqp/constants.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/_uamqp/endpoints.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/_uamqp/message.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/_uamqp/outcomes.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/_uamqp/performatives.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/helpers.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/message_broker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/protocol/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/protocol/connection.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/protocol/events.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/protocol/links.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/protocol/managed.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/protocol/reconnect.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/protocol/session.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/protocol/states.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/protocol/transport.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/amqp/subscriber.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/in_memory/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/in_memory/message_broker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/in_memory/utils.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/kafka/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/kafka/message.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/kafka/message_broker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/kafka/protocols.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/kafka/subscriber.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/nats/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/nats/message_broker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/helpers.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/message_broker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/protocol/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/protocol/_helpers.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/protocol/channel.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/protocol/client.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/protocol/control_batcher.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/protocol/credentials.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/protocol/proto.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/protocol/received_message.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/protocol/resilience.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/pubsub/protocol/subscriber.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/redis/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/redis/message_broker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/sqs/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/sqs/constants.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/sqs/message.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/sqs/message_broker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/connections/sqs/subscriber.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/data/channel.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/data/contact.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/data/converter_input_schema.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/data/external_docs.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/data/license.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/data/message.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/data/message_schema.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/data/operation.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/data/runner_info.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/data/tag.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/dependencies/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/dependencies/depends.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/dependencies/full_payload.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/dependencies/header_dependency.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/health_check_server.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/logger.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/message_registry.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/middlewares.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/py.typed +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/router.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/serializer.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/repid/server_registry.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/integration/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/integration/pubsub_proto_helpers.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/integration/rabbitmq_definitions.json +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/integration/test_asyncapi_compatibility.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/integration/test_default_flow.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/integration/test_kafka_specific.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/integration/test_nats_specific.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/integration/test_rabbitmq_specific.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/integration/test_sqs_specific.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/integration/test_worker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/conftest.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_amqp_transport.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_connection.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_encoding_decoding.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_event_emitter.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_frame_buffer.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_links.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_logging.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_managed.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_managed_pools.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_message.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_message_broker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_metrics.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_reconnect.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_session.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_state_machine.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/test_uamqp_types.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/amqp/utils.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/conftest.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/in_memory/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/in_memory/test_message_broker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/in_memory/test_utils.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/protocol/test_channel.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/protocol/test_client.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/protocol/test_control_batcher.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/protocol/test_credentials.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/protocol/test_helpers_internal.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/protocol/test_proto.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/protocol/test_received_message.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/protocol/test_resilience.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/protocol/test_subscriber.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/protocol/test_subscriber_execution.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/test_helpers.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/pubsub/test_message_broker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/redis/__init__.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/redis/test_message_broker.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/redis/test_redis_keep_alive.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_asyncapi_generator.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_channel.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_health_check_server.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_main.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_router.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_runner_manual_explicit.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_serializer.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_server_registry.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_test_client.py +0 -0
- {repid-2.1.2 → repid-2.1.3}/tests/unit/test_utils.py +0 -0
|
@@ -26,7 +26,7 @@ jobs:
|
|
|
26
26
|
|
|
27
27
|
steps:
|
|
28
28
|
- name: Checkout repository
|
|
29
|
-
uses: actions/checkout@
|
|
29
|
+
uses: actions/checkout@v7
|
|
30
30
|
|
|
31
31
|
- name: Setup uv
|
|
32
32
|
uses: astral-sh/setup-uv@v7
|
|
@@ -60,7 +60,7 @@ jobs:
|
|
|
60
60
|
contents: write
|
|
61
61
|
|
|
62
62
|
steps:
|
|
63
|
-
- uses: actions/checkout@
|
|
63
|
+
- uses: actions/checkout@v7
|
|
64
64
|
|
|
65
65
|
- name: Setup uv
|
|
66
66
|
uses: astral-sh/setup-uv@v7
|
|
@@ -19,14 +19,14 @@ repos:
|
|
|
19
19
|
- id: end-of-file-fixer
|
|
20
20
|
|
|
21
21
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
22
|
-
rev: v0.15.
|
|
22
|
+
rev: v0.15.18
|
|
23
23
|
hooks:
|
|
24
24
|
- id: ruff-check
|
|
25
25
|
args: [--fix, --exit-non-zero-on-fix]
|
|
26
26
|
- id: ruff-format
|
|
27
27
|
|
|
28
28
|
- repo: https://github.com/igorshubovych/markdownlint-cli
|
|
29
|
-
rev: v0.
|
|
29
|
+
rev: v0.49.0
|
|
30
30
|
hooks:
|
|
31
31
|
- id: markdownlint-fix
|
|
32
32
|
args: [--config=pyproject.toml, --configPointer=/tool/markdownlint]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: repid
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.3
|
|
4
4
|
Summary: Repid is a simple, fast, and extensible async task queue framework, with built-in AsyncAPI 3.0 schema generation.
|
|
5
5
|
Project-URL: documentation, https://repid.aleksul.space
|
|
6
6
|
Project-URL: funding, https://github.com/sponsors/aleksul
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# OpenTelemetry Middleware
|
|
2
|
+
|
|
3
|
+
[OpenTelemetry](https://opentelemetry.io/) gives you vendor-neutral traces and metrics for your
|
|
4
|
+
Repid producers and actors. This cookbook shows how to configure Repid middlewares that propagate
|
|
5
|
+
trace context through message headers and record message processing and publishing metrics.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
Install the OpenTelemetry API, SDK, and an exporter. This example uses the OTLP gRPC exporter,
|
|
10
|
+
which works with OpenTelemetry Collector and many observability vendors.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install repid opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-grpc
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
If your application already configures OpenTelemetry, keep that setup and only add the Repid
|
|
17
|
+
middlewares below.
|
|
18
|
+
|
|
19
|
+
## OpenTelemetry Setup
|
|
20
|
+
|
|
21
|
+
Configure the OpenTelemetry SDK once when your process starts. A worker process and any process that
|
|
22
|
+
publishes Repid messages should both initialize OpenTelemetry. The exact exporter, endpoint,
|
|
23
|
+
resource attributes, sampling, and authentication settings are up to your application and depend
|
|
24
|
+
heavily on your observability infrastructure.
|
|
25
|
+
|
|
26
|
+
Below is a minimal OTLP gRPC example, not a required Repid configuration.
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from opentelemetry import metrics, trace
|
|
30
|
+
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
|
|
31
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
32
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
|
33
|
+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
|
|
34
|
+
from opentelemetry.sdk.resources import Resource
|
|
35
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
36
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def configure_opentelemetry(service_name: str = "repid-worker") -> None:
|
|
40
|
+
resource = Resource.create({"service.name": service_name})
|
|
41
|
+
|
|
42
|
+
tracer_provider = TracerProvider(resource=resource)
|
|
43
|
+
tracer_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
|
|
44
|
+
trace.set_tracer_provider(tracer_provider)
|
|
45
|
+
|
|
46
|
+
metric_reader = PeriodicExportingMetricReader(OTLPMetricExporter())
|
|
47
|
+
meter_provider = MeterProvider(resource=resource, metric_readers=[metric_reader])
|
|
48
|
+
metrics.set_meter_provider(meter_provider)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## The Middlewares
|
|
52
|
+
|
|
53
|
+
In total, we will create 4 middlewares: 2 metric reporting middlewares, and
|
|
54
|
+
2 tracing middlewares - each for producer and consumer parts.
|
|
55
|
+
|
|
56
|
+
When a producer sends a Repid message, the active `traceparent` and `tracestate` headers
|
|
57
|
+
are injected into the message. When a worker consumes that message,
|
|
58
|
+
the actor span continues the same distributed trace.
|
|
59
|
+
|
|
60
|
+
The span names and attributes follow the
|
|
61
|
+
[OpenTelemetry messaging semantic conventions](https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/#messaging-attributes).
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from collections.abc import Callable, Coroutine
|
|
65
|
+
from dataclasses import replace
|
|
66
|
+
from time import perf_counter
|
|
67
|
+
from typing import Any, TypeVar
|
|
68
|
+
|
|
69
|
+
from opentelemetry import context as context_api
|
|
70
|
+
from opentelemetry import metrics, trace
|
|
71
|
+
from opentelemetry.propagate import extract, inject
|
|
72
|
+
from repid import ActorData, MessageData, ReceivedMessageT
|
|
73
|
+
|
|
74
|
+
T = TypeVar("T")
|
|
75
|
+
|
|
76
|
+
TRACER = trace.get_tracer(__name__)
|
|
77
|
+
METER = metrics.get_meter(__name__)
|
|
78
|
+
|
|
79
|
+
# Pick the semantic-convention value for your broker, for example
|
|
80
|
+
# "rabbitmq", "kafka", "aws_sqs", or "gcp_pubsub". If none applies, use a
|
|
81
|
+
# stable low-cardinality custom value.
|
|
82
|
+
MESSAGING_SYSTEM = "rabbitmq"
|
|
83
|
+
|
|
84
|
+
# Use operation names that match your broker or client terminology.
|
|
85
|
+
PRODUCER_OPERATION_NAME = "send"
|
|
86
|
+
CONSUMER_OPERATION_NAME = "process"
|
|
87
|
+
|
|
88
|
+
REPID_CONSUMER_MESSAGES = METER.create_counter(
|
|
89
|
+
name="repid.consumer.messages",
|
|
90
|
+
unit="{message}",
|
|
91
|
+
description="The number of messages consumed by Repid actors.",
|
|
92
|
+
)
|
|
93
|
+
REPID_CONSUMER_FAILURES = METER.create_counter(
|
|
94
|
+
name="repid.consumer.failures",
|
|
95
|
+
unit="{message}",
|
|
96
|
+
description="The number of Repid actor message handling failures.",
|
|
97
|
+
)
|
|
98
|
+
REPID_CONSUMER_DURATION = METER.create_histogram(
|
|
99
|
+
name="repid.consumer.processing.duration",
|
|
100
|
+
unit="s",
|
|
101
|
+
description="The time spent handling a consumed Repid message.",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
REPID_PRODUCER_MESSAGES = METER.create_counter(
|
|
105
|
+
name="repid.producer.messages",
|
|
106
|
+
unit="{message}",
|
|
107
|
+
description="The number of messages published by Repid producers.",
|
|
108
|
+
)
|
|
109
|
+
REPID_PRODUCER_FAILURES = METER.create_counter(
|
|
110
|
+
name="repid.producer.failures",
|
|
111
|
+
unit="{message}",
|
|
112
|
+
description="The number of Repid producer publish failures.",
|
|
113
|
+
)
|
|
114
|
+
REPID_PRODUCER_DURATION = METER.create_histogram(
|
|
115
|
+
name="repid.producer.publish.duration",
|
|
116
|
+
unit="s",
|
|
117
|
+
description="The time spent publishing a Repid message.",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
async def consumer_tracing_middleware(
|
|
122
|
+
call_next: Callable[[ReceivedMessageT, ActorData], Coroutine[Any, Any, T]],
|
|
123
|
+
message: ReceivedMessageT,
|
|
124
|
+
actor: ActorData,
|
|
125
|
+
) -> T:
|
|
126
|
+
span_attributes = {
|
|
127
|
+
"messaging.system": MESSAGING_SYSTEM,
|
|
128
|
+
"messaging.destination.name": message.channel,
|
|
129
|
+
"messaging.operation.type": "process",
|
|
130
|
+
"messaging.operation.name": CONSUMER_OPERATION_NAME,
|
|
131
|
+
"repid.actor.name": actor.name,
|
|
132
|
+
"repid.actor.channel_address": actor.channel_address,
|
|
133
|
+
}
|
|
134
|
+
if message.message_id:
|
|
135
|
+
span_attributes["messaging.message.id"] = message.message_id
|
|
136
|
+
|
|
137
|
+
ctx = extract(message.headers or {})
|
|
138
|
+
token = context_api.attach(ctx)
|
|
139
|
+
try:
|
|
140
|
+
with TRACER.start_as_current_span(
|
|
141
|
+
f"{CONSUMER_OPERATION_NAME} {message.channel}",
|
|
142
|
+
kind=trace.SpanKind.CONSUMER,
|
|
143
|
+
attributes=span_attributes,
|
|
144
|
+
) as span:
|
|
145
|
+
try:
|
|
146
|
+
return await call_next(message, actor)
|
|
147
|
+
except Exception as exc:
|
|
148
|
+
span.set_attribute("error.type", type(exc).__name__)
|
|
149
|
+
raise
|
|
150
|
+
finally:
|
|
151
|
+
context_api.detach(token)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
async def consumer_metrics_middleware(
|
|
155
|
+
call_next: Callable[[ReceivedMessageT, ActorData], Coroutine[Any, Any, T]],
|
|
156
|
+
message: ReceivedMessageT,
|
|
157
|
+
actor: ActorData,
|
|
158
|
+
) -> T:
|
|
159
|
+
metric_attributes = {
|
|
160
|
+
"messaging.system": MESSAGING_SYSTEM,
|
|
161
|
+
"messaging.destination.name": message.channel,
|
|
162
|
+
"repid.actor.name": actor.name,
|
|
163
|
+
"repid.actor.channel_address": actor.channel_address,
|
|
164
|
+
}
|
|
165
|
+
started_at = perf_counter()
|
|
166
|
+
REPID_CONSUMER_MESSAGES.add(1, attributes=metric_attributes)
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
return await call_next(message, actor)
|
|
170
|
+
except Exception as exc:
|
|
171
|
+
REPID_CONSUMER_FAILURES.add(
|
|
172
|
+
1,
|
|
173
|
+
attributes={**metric_attributes, "error.type": type(exc).__name__},
|
|
174
|
+
)
|
|
175
|
+
raise
|
|
176
|
+
finally:
|
|
177
|
+
REPID_CONSUMER_DURATION.record(
|
|
178
|
+
perf_counter() - started_at,
|
|
179
|
+
attributes=metric_attributes,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
async def producer_tracing_middleware(
|
|
184
|
+
call_next: Callable[
|
|
185
|
+
[str, MessageData, dict[str, Any] | None],
|
|
186
|
+
Coroutine[Any, Any, T],
|
|
187
|
+
],
|
|
188
|
+
channel: str,
|
|
189
|
+
message: MessageData,
|
|
190
|
+
server_specific_parameters: dict[str, Any] | None,
|
|
191
|
+
) -> T:
|
|
192
|
+
span_attributes = {
|
|
193
|
+
"messaging.system": MESSAGING_SYSTEM,
|
|
194
|
+
"messaging.destination.name": channel,
|
|
195
|
+
"messaging.operation.type": "send",
|
|
196
|
+
"messaging.operation.name": PRODUCER_OPERATION_NAME,
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
with TRACER.start_as_current_span(
|
|
200
|
+
f"{PRODUCER_OPERATION_NAME} {channel}",
|
|
201
|
+
kind=trace.SpanKind.PRODUCER,
|
|
202
|
+
attributes=span_attributes,
|
|
203
|
+
) as span:
|
|
204
|
+
headers = dict(message.headers or {})
|
|
205
|
+
inject(headers)
|
|
206
|
+
replaced_message = replace(message, headers=headers)
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
return await call_next(channel, replaced_message, server_specific_parameters)
|
|
210
|
+
except Exception as exc:
|
|
211
|
+
span.set_attribute("error.type", type(exc).__name__)
|
|
212
|
+
raise
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
async def producer_metrics_middleware(
|
|
216
|
+
call_next: Callable[
|
|
217
|
+
[str, MessageData, dict[str, Any] | None],
|
|
218
|
+
Coroutine[Any, Any, T],
|
|
219
|
+
],
|
|
220
|
+
channel: str,
|
|
221
|
+
message: MessageData,
|
|
222
|
+
server_specific_parameters: dict[str, Any] | None,
|
|
223
|
+
) -> T:
|
|
224
|
+
metric_attributes = {
|
|
225
|
+
"messaging.system": MESSAGING_SYSTEM,
|
|
226
|
+
"messaging.destination.name": channel,
|
|
227
|
+
}
|
|
228
|
+
started_at = perf_counter()
|
|
229
|
+
REPID_PRODUCER_MESSAGES.add(1, attributes=metric_attributes)
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
return await call_next(channel, message, server_specific_parameters)
|
|
233
|
+
except Exception as exc:
|
|
234
|
+
REPID_PRODUCER_FAILURES.add(
|
|
235
|
+
1,
|
|
236
|
+
attributes={**metric_attributes, "error.type": type(exc).__name__},
|
|
237
|
+
)
|
|
238
|
+
raise
|
|
239
|
+
finally:
|
|
240
|
+
REPID_PRODUCER_DURATION.record(
|
|
241
|
+
perf_counter() - started_at,
|
|
242
|
+
attributes=metric_attributes,
|
|
243
|
+
)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
!!! note
|
|
247
|
+
Keep metric attributes low-cardinality. Channel names, actor names, and error types are usually
|
|
248
|
+
safe. Message IDs are useful on spans, but should not be added to metrics.
|
|
249
|
+
|
|
250
|
+
## Registering the Middlewares
|
|
251
|
+
|
|
252
|
+
Lastly, you need to register those middlewares:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from repid import Repid, Router
|
|
256
|
+
|
|
257
|
+
from my_project.observability import configure_opentelemetry
|
|
258
|
+
from my_project.repid_otel import (
|
|
259
|
+
consumer_metrics_middleware,
|
|
260
|
+
consumer_tracing_middleware,
|
|
261
|
+
producer_metrics_middleware,
|
|
262
|
+
producer_tracing_middleware,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
configure_opentelemetry(service_name="repid-worker")
|
|
266
|
+
|
|
267
|
+
app = Repid(
|
|
268
|
+
actor_middlewares=[consumer_tracing_middleware, consumer_metrics_middleware],
|
|
269
|
+
producer_middlewares=[producer_tracing_middleware, producer_metrics_middleware],
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
router = Router()
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@router.actor(channel="charge_customer")
|
|
276
|
+
async def charge_customer(customer_id: str) -> None:
|
|
277
|
+
print(f"Charging {customer_id}")
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
app.include_router(router)
|
|
281
|
+
```
|
|
@@ -43,6 +43,7 @@ nav:
|
|
|
43
43
|
- Middlewares: cookbook/middlewares.md
|
|
44
44
|
- Cookbook:
|
|
45
45
|
- Sentry Middleware: cookbook/sentry_middleware.md
|
|
46
|
+
- OpenTelemetry Middleware: cookbook/opentelemetry_middleware.md
|
|
46
47
|
- RabbitMQ Retries: cookbook/rabbitmq_retries.md
|
|
47
48
|
- Large Payloads: cookbook/large_payloads.md
|
|
48
49
|
- Extending Repid:
|
|
@@ -124,6 +125,7 @@ plugins:
|
|
|
124
125
|
- cookbook/middlewares.md
|
|
125
126
|
Cookbook:
|
|
126
127
|
- cookbook/sentry_middleware.md
|
|
128
|
+
- cookbook/opentelemetry_middleware.md
|
|
127
129
|
- cookbook/rabbitmq_retries.md
|
|
128
130
|
- cookbook/large_payloads.md
|
|
129
131
|
Extending Repid:
|
|
@@ -8,7 +8,8 @@ from contextlib import suppress
|
|
|
8
8
|
from functools import partial
|
|
9
9
|
from typing import TYPE_CHECKING, Any
|
|
10
10
|
|
|
11
|
-
from repid.connections.abc import
|
|
11
|
+
from repid.connections.abc import SubscriberT
|
|
12
|
+
from repid.data.actor import ActorExecutionContext
|
|
12
13
|
from repid.health_check_server import HealthCheckStatus
|
|
13
14
|
|
|
14
15
|
logger = logging.getLogger("repid")
|
|
@@ -17,7 +18,6 @@ if TYPE_CHECKING:
|
|
|
17
18
|
from repid.connections.abc import ReceivedMessageT
|
|
18
19
|
from repid.data.actor import ActorData
|
|
19
20
|
from repid.health_check_server import HealthCheckServer
|
|
20
|
-
from repid.serializer import SerializerT
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
ActorResultT = Any
|
|
@@ -26,14 +26,12 @@ ActorResultT = Any
|
|
|
26
26
|
async def _actor_execution(
|
|
27
27
|
message: ReceivedMessageT,
|
|
28
28
|
actor: ActorData,
|
|
29
|
-
|
|
30
|
-
default_serializer: SerializerT,
|
|
29
|
+
actor_context: ActorExecutionContext,
|
|
31
30
|
) -> ActorResultT:
|
|
32
31
|
args, kwargs = await actor.converter.convert_inputs(
|
|
33
32
|
message=message,
|
|
34
33
|
actor=actor,
|
|
35
|
-
|
|
36
|
-
default_serializer=default_serializer,
|
|
34
|
+
actor_context=actor_context,
|
|
37
35
|
)
|
|
38
36
|
return await actor.fn(*args, **kwargs)
|
|
39
37
|
|
|
@@ -52,23 +50,22 @@ async def _keep_alive_loop(message: ReceivedMessageT, interval: float) -> None:
|
|
|
52
50
|
async def _run_with_keepalive(
|
|
53
51
|
message: ReceivedMessageT,
|
|
54
52
|
actor: ActorData,
|
|
55
|
-
|
|
56
|
-
default_serializer: SerializerT,
|
|
53
|
+
actor_context: ActorExecutionContext,
|
|
57
54
|
) -> ActorResultT:
|
|
58
55
|
if actor.keep_alive is False:
|
|
59
|
-
return await _actor_execution(message, actor,
|
|
56
|
+
return await _actor_execution(message, actor, actor_context)
|
|
60
57
|
|
|
61
58
|
interval = (
|
|
62
59
|
actor.keep_alive
|
|
63
60
|
if isinstance(actor.keep_alive, (int, float)) and not isinstance(actor.keep_alive, bool)
|
|
64
61
|
else message.keep_alive_interval
|
|
65
62
|
)
|
|
66
|
-
if not server.capabilities["supports_keep_alive"] or interval is None:
|
|
67
|
-
return await _actor_execution(message, actor,
|
|
63
|
+
if not actor_context.server.capabilities["supports_keep_alive"] or interval is None:
|
|
64
|
+
return await _actor_execution(message, actor, actor_context)
|
|
68
65
|
|
|
69
66
|
keepalive_task = asyncio.create_task(_keep_alive_loop(message, interval))
|
|
70
67
|
try:
|
|
71
|
-
return await _actor_execution(message, actor,
|
|
68
|
+
return await _actor_execution(message, actor, actor_context)
|
|
72
69
|
finally:
|
|
73
70
|
keepalive_task.cancel()
|
|
74
71
|
with suppress(asyncio.CancelledError):
|
|
@@ -78,17 +75,16 @@ async def _run_with_keepalive(
|
|
|
78
75
|
async def _actor_execution_with_confirmation( # noqa: C901, PLR0912
|
|
79
76
|
message: ReceivedMessageT,
|
|
80
77
|
actor: ActorData,
|
|
81
|
-
|
|
82
|
-
default_serializer: SerializerT,
|
|
78
|
+
actor_context: ActorExecutionContext,
|
|
83
79
|
) -> ActorResultT:
|
|
84
80
|
"""Wraps `_actor_execution` to ack/nack the message immediately after the actor fn runs,
|
|
85
81
|
so middlewares unwinding above this leaf can observe `message.action`."""
|
|
86
82
|
try:
|
|
87
83
|
if actor.timeout is None or actor.timeout <= 0 or actor.timeout == float("inf"):
|
|
88
|
-
result = await _run_with_keepalive(message, actor,
|
|
84
|
+
result = await _run_with_keepalive(message, actor, actor_context)
|
|
89
85
|
else:
|
|
90
86
|
result = await asyncio.wait_for(
|
|
91
|
-
_run_with_keepalive(message, actor,
|
|
87
|
+
_run_with_keepalive(message, actor, actor_context),
|
|
92
88
|
timeout=actor.timeout,
|
|
93
89
|
)
|
|
94
90
|
except Exception as exc:
|
|
@@ -131,8 +127,7 @@ async def _actor_execution_with_confirmation( # noqa: C901, PLR0912
|
|
|
131
127
|
async def _actor_run(
|
|
132
128
|
actor: ActorData,
|
|
133
129
|
message: ReceivedMessageT,
|
|
134
|
-
|
|
135
|
-
default_serializer: SerializerT,
|
|
130
|
+
actor_context: ActorExecutionContext,
|
|
136
131
|
) -> ActorResultT | Exception:
|
|
137
132
|
if (
|
|
138
133
|
not message.is_acted_on # theoretically a server can automatically ack the message on receive
|
|
@@ -151,8 +146,7 @@ async def _actor_run(
|
|
|
151
146
|
|
|
152
147
|
leaf = partial(
|
|
153
148
|
_actor_execution_with_confirmation,
|
|
154
|
-
|
|
155
|
-
default_serializer=default_serializer,
|
|
149
|
+
actor_context=actor_context,
|
|
156
150
|
)
|
|
157
151
|
|
|
158
152
|
try:
|
|
@@ -172,13 +166,12 @@ async def _actor_run(
|
|
|
172
166
|
async def _actor_run_with_cancel_event_and_callback(
|
|
173
167
|
actor: ActorData,
|
|
174
168
|
message: ReceivedMessageT,
|
|
175
|
-
|
|
176
|
-
default_serializer: SerializerT,
|
|
169
|
+
actor_context: ActorExecutionContext,
|
|
177
170
|
cancel_event: asyncio.Event,
|
|
178
171
|
cancel_event_task: asyncio.Task,
|
|
179
172
|
callback: Callable[[], Awaitable],
|
|
180
173
|
) -> None:
|
|
181
|
-
process_task = asyncio.create_task(_actor_run(actor, message,
|
|
174
|
+
process_task = asyncio.create_task(_actor_run(actor, message, actor_context))
|
|
182
175
|
await asyncio.wait(
|
|
183
176
|
{cancel_event_task, process_task},
|
|
184
177
|
return_when=asyncio.FIRST_COMPLETED,
|
|
@@ -212,8 +205,8 @@ class _Runner:
|
|
|
212
205
|
"_tasks",
|
|
213
206
|
"_tasks_concurrency_limit",
|
|
214
207
|
"_unrouted_seen_counts",
|
|
208
|
+
"actor_context",
|
|
215
209
|
"cancel_event",
|
|
216
|
-
"default_serializer",
|
|
217
210
|
"max_tasks",
|
|
218
211
|
"max_unrouted_retries",
|
|
219
212
|
"server",
|
|
@@ -223,15 +216,14 @@ class _Runner:
|
|
|
223
216
|
def __init__(
|
|
224
217
|
self,
|
|
225
218
|
*,
|
|
226
|
-
|
|
219
|
+
actor_context: ActorExecutionContext,
|
|
227
220
|
max_tasks: int = float("inf"), # type: ignore[assignment]
|
|
228
221
|
tasks_concurrency_limit: int = 1000,
|
|
229
222
|
concurrency_unpause_percent: float = 0.1, # 10 percent
|
|
230
223
|
health_check_server: HealthCheckServer | None = None,
|
|
231
|
-
default_serializer: SerializerT,
|
|
232
224
|
max_unrouted_retries: int = 10,
|
|
233
225
|
):
|
|
234
|
-
self.server = server
|
|
226
|
+
self.server = actor_context.server
|
|
235
227
|
self._server_subscriber: SubscriberT | None = None
|
|
236
228
|
self._server_subscriber_concurrency_unpause_threshold = max(
|
|
237
229
|
math.ceil(tasks_concurrency_limit * concurrency_unpause_percent),
|
|
@@ -259,7 +251,7 @@ class _Runner:
|
|
|
259
251
|
|
|
260
252
|
self._health_check_server = health_check_server
|
|
261
253
|
|
|
262
|
-
self.
|
|
254
|
+
self.actor_context = actor_context
|
|
263
255
|
|
|
264
256
|
@property
|
|
265
257
|
def processed(self) -> int:
|
|
@@ -343,8 +335,7 @@ class _Runner:
|
|
|
343
335
|
_actor_run_with_cancel_event_and_callback(
|
|
344
336
|
actor,
|
|
345
337
|
message,
|
|
346
|
-
self.
|
|
347
|
-
self.default_serializer,
|
|
338
|
+
self.actor_context,
|
|
348
339
|
self.cancel_event,
|
|
349
340
|
self.cancel_event_task,
|
|
350
341
|
self._actor_run_callback,
|
|
@@ -9,24 +9,22 @@ from typing import TYPE_CHECKING
|
|
|
9
9
|
|
|
10
10
|
from repid._runner import _Runner
|
|
11
11
|
from repid.asyncapi_server import AsyncAPIServer
|
|
12
|
+
from repid.data.actor import ActorExecutionContext
|
|
12
13
|
from repid.health_check_server import HealthCheckServer
|
|
13
14
|
from repid.router import Router
|
|
14
|
-
from repid.serializer import default_serializer as repid_default_serializer
|
|
15
15
|
|
|
16
16
|
logger = logging.getLogger("repid")
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
19
|
from repid.asyncapi import AsyncAPI3Schema
|
|
20
20
|
from repid.asyncapi_server import AsyncAPIServerSettings
|
|
21
|
-
from repid.connections.abc import ServerT
|
|
22
21
|
from repid.health_check_server import HealthCheckServerSettings
|
|
23
|
-
from repid.serializer import SerializerT
|
|
24
22
|
|
|
25
23
|
|
|
26
24
|
class _Worker:
|
|
27
25
|
def __init__(
|
|
28
26
|
self,
|
|
29
|
-
|
|
27
|
+
actor_context: ActorExecutionContext,
|
|
30
28
|
router: Router,
|
|
31
29
|
graceful_shutdown_time: float = 25.0,
|
|
32
30
|
messages_limit: int = float("inf"), # type: ignore[assignment]
|
|
@@ -35,9 +33,9 @@ class _Worker:
|
|
|
35
33
|
health_check_server: HealthCheckServerSettings | None = None,
|
|
36
34
|
asyncapi_server: AsyncAPIServerSettings | None = None,
|
|
37
35
|
asyncapi_schema: AsyncAPI3Schema | None = None,
|
|
38
|
-
default_serializer: SerializerT | None = None,
|
|
39
36
|
):
|
|
40
|
-
self.
|
|
37
|
+
self.actor_context = actor_context
|
|
38
|
+
self.server = actor_context.server
|
|
41
39
|
self.centralized_router = router
|
|
42
40
|
|
|
43
41
|
self.tasks_limit: int = tasks_limit
|
|
@@ -66,8 +64,6 @@ class _Worker:
|
|
|
66
64
|
raise ValueError("AsyncAPI schema is required if AsyncAPI server is enabled.")
|
|
67
65
|
self.asyncapi_server = AsyncAPIServer(asyncapi_schema, asyncapi_server)
|
|
68
66
|
|
|
69
|
-
self.default_serializer = default_serializer or repid_default_serializer
|
|
70
|
-
|
|
71
67
|
async def run(self) -> _Runner:
|
|
72
68
|
logger.info(
|
|
73
69
|
"worker.run.start",
|
|
@@ -85,11 +81,10 @@ class _Worker:
|
|
|
85
81
|
await self.asyncapi_server.start()
|
|
86
82
|
|
|
87
83
|
runner = _Runner(
|
|
88
|
-
|
|
84
|
+
actor_context=self.actor_context,
|
|
89
85
|
max_tasks=self.messages_limit,
|
|
90
86
|
tasks_concurrency_limit=self.tasks_limit,
|
|
91
87
|
health_check_server=self.health_check_server,
|
|
92
|
-
default_serializer=self.default_serializer,
|
|
93
88
|
)
|
|
94
89
|
|
|
95
90
|
if not self.centralized_router.actors:
|
|
@@ -4,7 +4,9 @@ import asyncio
|
|
|
4
4
|
from collections.abc import Callable, Coroutine, Mapping, Sequence
|
|
5
5
|
from contextlib import AbstractAsyncContextManager
|
|
6
6
|
from enum import Enum
|
|
7
|
-
from typing import TYPE_CHECKING, Any, Protocol, TypedDict
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Protocol, TypeAlias, TypedDict
|
|
8
|
+
|
|
9
|
+
from repid.data.message import MessageData
|
|
8
10
|
|
|
9
11
|
if TYPE_CHECKING:
|
|
10
12
|
from repid.asyncapi.models.common import ServerBindingsObject
|
|
@@ -37,6 +39,12 @@ class SentMessageT(BaseMessageT, Protocol):
|
|
|
37
39
|
pass
|
|
38
40
|
|
|
39
41
|
|
|
42
|
+
MessagePublisherT: TypeAlias = Callable[
|
|
43
|
+
[str, MessageData, dict[str, Any] | None],
|
|
44
|
+
Coroutine[Any, Any, Any],
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
40
48
|
class ReceivedMessageT(BaseMessageT, Protocol):
|
|
41
49
|
@property
|
|
42
50
|
def channel(self) -> str: ...
|