├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependantbot.yml └── workflows │ ├── documentation.yml │ ├── publish_coverage.yml │ ├── publish_pypi.yml │ └── tests.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── docs ├── docs.py ├── docs │ ├── assets │ │ ├── img │ │ │ ├── docs-html-short.png │ │ │ ├── docs-html.png │ │ │ ├── favicon.png │ │ │ ├── logo-no-background.png │ │ │ └── logo-white.png │ │ ├── javascripts │ │ │ ├── custom.js │ │ │ └── termynal.js │ │ └── stylesheets │ │ │ ├── custom.css │ │ │ └── termynal.css │ ├── en │ │ ├── CHANGELOG.md │ │ ├── alternatives.md │ │ ├── contributing │ │ │ ├── 1_todo.md │ │ │ ├── 2_contributing-index.md │ │ │ ├── 3_docs.md │ │ │ └── 4_adapters.md │ │ ├── getting_started │ │ │ ├── 10_settings.md │ │ │ ├── 1_quick-start.md │ │ │ ├── 2_cli.md │ │ │ ├── 3_app.md │ │ │ ├── 4_broker │ │ │ │ ├── 1_index.md │ │ │ │ ├── 2_routing.md │ │ │ │ ├── 3_type-casting.md │ │ │ │ ├── 4_custom_serialization.md │ │ │ │ ├── 5_publishing.md │ │ │ │ └── 6_rpc.md │ │ │ ├── 5_dependency │ │ │ │ ├── 1_di-index.md │ │ │ │ └── 2_context.md │ │ │ ├── 6_lifespans.md │ │ │ ├── 7_testing.md │ │ │ ├── 8_logging.md │ │ │ └── 9_documentation.md │ │ ├── help.md │ │ ├── helpful │ │ │ ├── in-progress.md │ │ │ └── missing-translation.md │ │ ├── index.md │ │ ├── integrations │ │ │ ├── 1_integrations-index.md │ │ │ └── 2_fastapi-plugin.md │ │ ├── kafka │ │ │ └── 1_kafka-index.md │ │ ├── migration.md │ │ ├── nats │ │ │ ├── 1_nats-index.md │ │ │ ├── 2_publishing.md │ │ │ ├── 3_examples │ │ │ │ ├── 1_direct.md │ │ │ │ └── 2_pattern.md │ │ │ └── 4_nats-js.md │ │ ├── rabbit │ │ │ ├── 1_routing.md │ │ │ ├── 2_exchanges.md │ │ │ ├── 3_queues.md │ │ │ ├── 4_publishing.md │ │ │ └── 5_examples │ │ │ │ ├── 1_direct.md │ │ │ │ ├── 2_fanout.md │ │ │ │ ├── 3_topic.md │ │ │ │ └── 4_header.md │ │ ├── redis │ │ │ ├── 1_redis-index.md │ │ │ ├── 2_publishing.md │ │ │ └── 3_examples │ │ │ │ ├── 1_direct.md │ │ │ │ └── 2_pattern.md │ │ └── sqs │ │ │ └── 1_sqs-index.md │ └── ru │ │ ├── CHANGELOG.md │ │ ├── alternatives.md │ │ ├── contributing │ │ ├── 1_todo.md │ │ ├── 2_contributing-index.md │ │ ├── 3_docs.md │ │ └── 4_adapters.md │ │ ├── getting_started │ │ ├── 10_settings.md │ │ ├── 1_quick-start.md │ │ ├── 2_cli.md │ │ ├── 3_app.md │ │ ├── 4_broker │ │ │ ├── 1_index.md │ │ │ ├── 2_routing.md │ │ │ ├── 3_type-casting.md │ │ │ ├── 4_custom_serialization.md │ │ │ ├── 5_publishing.md │ │ │ └── 6_rpc.md │ │ ├── 5_dependency │ │ │ ├── 1_di-index.md │ │ │ └── 2_context.md │ │ ├── 6_lifespans.md │ │ ├── 7_testing.md │ │ ├── 8_logging.md │ │ └── 9_documentation.md │ │ ├── help.md │ │ ├── helpful │ │ ├── in-progress.md │ │ └── missing-translation.md │ │ ├── index.md │ │ ├── integrations │ │ ├── 1_integrations-index.md │ │ └── 2_fastapi-plugin.md │ │ ├── kafka │ │ └── 1_kafka-index.md │ │ ├── nats │ │ ├── 1_nats-index.md │ │ ├── 2_publishing.md │ │ ├── 3_examples │ │ │ ├── 1_direct.md │ │ │ └── 2_pattern.md │ │ └── 4_nats-js.md │ │ ├── rabbit │ │ ├── 1_routing.md │ │ ├── 2_exchanges.md │ │ ├── 3_queues.md │ │ ├── 4_publishing.md │ │ └── 5_examples │ │ │ ├── 1_direct.md │ │ │ ├── 2_fanout.md │ │ │ ├── 3_topic.md │ │ │ └── 4_header.md │ │ ├── redis │ │ ├── 1_redis-index.md │ │ ├── 2_publishing.md │ │ └── 3_examples │ │ │ ├── 1_direct.md │ │ │ └── 2_pattern.md │ │ └── sqs │ │ └── 1_sqs-index.md ├── docs_src │ ├── contributing │ │ └── adapter │ │ │ ├── parent.py │ │ │ ├── rabbit_close.py │ │ │ ├── rabbit_connect.py │ │ │ ├── rabbit_fmt.py │ │ │ ├── rabbit_get_log_context.py │ │ │ ├── rabbit_handle.py │ │ │ ├── rabbit_init.py │ │ │ ├── rabbit_parse.py │ │ │ ├── rabbit_process.py │ │ │ ├── rabbit_start.py │ │ │ ├── redis_process.py │ │ │ ├── redis_publish.py │ │ │ └── redis_start.py │ ├── index │ │ ├── 02_type_casting.py │ │ ├── 03_dependencies.py │ │ ├── kafka │ │ │ ├── 01_base.py │ │ │ ├── 04_http_example.py │ │ │ └── 05_native_fastapi.py │ │ ├── nats │ │ │ ├── 01_base.py │ │ │ ├── 04_http_example.py │ │ │ └── 05_native_fastapi.py │ │ ├── rabbit │ │ │ ├── 01_base.py │ │ │ ├── 04_http_example.py │ │ │ └── 05_native_fastapi.py │ │ ├── redis │ │ │ ├── 01_base.py │ │ │ ├── 04_http_example.py │ │ │ └── 05_native_fastapi.py │ │ └── sqs │ │ │ ├── 01_base.py │ │ │ ├── 04_http_example.py │ │ │ └── 05_native_fastapi.py │ ├── integrations │ │ ├── fastapi │ │ │ └── request.py │ │ ├── http_frameworks_integrations │ │ │ ├── aiohttp.py │ │ │ ├── blacksheep.py │ │ │ ├── falcon.py │ │ │ ├── fastapi.py │ │ │ ├── native_fastapi.py │ │ │ ├── quart.py │ │ │ ├── sanic.py │ │ │ └── tornado.py │ │ ├── kafka │ │ │ ├── fastapi_after_startup.py │ │ │ ├── fastapi_plugin.py │ │ │ ├── fastapi_plugin_depends.py │ │ │ └── fastapi_plugin_send.py │ │ ├── nats │ │ │ ├── fastapi_after_startup.py │ │ │ ├── fastapi_plugin.py │ │ │ ├── fastapi_plugin_depends.py │ │ │ └── fastapi_plugin_send.py │ │ ├── rabbit │ │ │ ├── fastapi_after_startup.py │ │ │ ├── fastapi_plugin.py │ │ │ ├── fastapi_plugin_depends.py │ │ │ └── fastapi_plugin_send.py │ │ ├── redis │ │ │ ├── fastapi_after_startup.py │ │ │ ├── fastapi_plugin.py │ │ │ ├── fastapi_plugin_depends.py │ │ │ └── fastapi_plugin_send.py │ │ └── sqs │ │ │ ├── fastapi_after_startup.py │ │ │ ├── fastapi_plugin.py │ │ │ ├── fastapi_plugin_depends.py │ │ │ └── fastapi_plugin_send.py │ ├── nats │ │ ├── direct.py │ │ ├── js.py │ │ └── pattern.py │ ├── quickstart │ │ ├── app │ │ │ ├── kafka │ │ │ │ ├── 1_broker.py │ │ │ │ └── 2_set_broker.py │ │ │ ├── nats │ │ │ │ ├── 1_broker.py │ │ │ │ └── 2_set_broker.py │ │ │ ├── rabbit │ │ │ │ ├── 1_broker.py │ │ │ │ └── 2_set_broker.py │ │ │ ├── redis │ │ │ │ ├── 1_broker.py │ │ │ │ └── 2_set_broker.py │ │ │ └── sqs │ │ │ │ ├── 1_broker.py │ │ │ │ └── 2_set_broker.py │ │ ├── broker │ │ │ ├── publishing │ │ │ │ ├── kafka │ │ │ │ │ ├── 1_inside_propan.py │ │ │ │ │ └── 2_context.py │ │ │ │ ├── nats │ │ │ │ │ ├── 1_inside_propan.py │ │ │ │ │ └── 2_context.py │ │ │ │ ├── rabbit │ │ │ │ │ ├── 1_inside_propan.py │ │ │ │ │ └── 2_context.py │ │ │ │ ├── redis │ │ │ │ │ ├── 1_inside_propan.py │ │ │ │ │ └── 2_context.py │ │ │ │ └── sqs │ │ │ │ │ ├── 1_inside_propan.py │ │ │ │ │ └── 2_context.py │ │ │ ├── routers │ │ │ │ ├── kafka.py │ │ │ │ ├── nats.py │ │ │ │ ├── publish.py │ │ │ │ ├── rabbit.py │ │ │ │ ├── redis.py │ │ │ │ └── sqs.py │ │ │ ├── rpc │ │ │ │ ├── 3_blocking_client_timeout.py │ │ │ │ ├── 4_blocking_client_timeout_none.py │ │ │ │ ├── 5_blocking_client_timeout_error.py │ │ │ │ ├── nats │ │ │ │ │ ├── 1_handler.py │ │ │ │ │ ├── 2_blocking_client.py │ │ │ │ │ └── 6_noblocking_client.py │ │ │ │ ├── rabbit │ │ │ │ │ ├── 1_handler.py │ │ │ │ │ ├── 2_blocking_client.py │ │ │ │ │ └── 6_noblocking_client.py │ │ │ │ └── redis │ │ │ │ │ ├── 1_handler.py │ │ │ │ │ ├── 2_blocking_client.py │ │ │ │ │ └── 6_noblocking_client.py │ │ │ └── serialization │ │ │ │ ├── 1_kafka_parse.py │ │ │ │ ├── 1_nats_parse.py │ │ │ │ ├── 1_rabbit_parse.py │ │ │ │ ├── 1_redis_parse.py │ │ │ │ ├── 1_sqs_parse.py │ │ │ │ ├── 2_parse_redefine.py │ │ │ │ ├── 3_decode.py │ │ │ │ ├── 4_decode_redefine.py │ │ │ │ └── 5_protobuf.py │ │ ├── cli │ │ │ ├── 01_kafka_context.py │ │ │ ├── 01_nats_context.py │ │ │ ├── 01_rabbit_context.py │ │ │ ├── 01_redis_context.py │ │ │ └── 01_sqs_context.py │ │ ├── dependencies │ │ │ ├── basic │ │ │ │ ├── 3_async.py │ │ │ │ ├── 3_sync.py │ │ │ │ ├── kafka │ │ │ │ │ ├── 1_depends.py │ │ │ │ │ └── 2_nested_depends.py │ │ │ │ ├── nats │ │ │ │ │ ├── 1_depends.py │ │ │ │ │ └── 2_nested_depends.py │ │ │ │ ├── rabbit │ │ │ │ │ ├── 1_depends.py │ │ │ │ │ └── 2_nested_depends.py │ │ │ │ ├── redis │ │ │ │ │ ├── 1_depends.py │ │ │ │ │ └── 2_nested_depends.py │ │ │ │ └── sqs │ │ │ │ │ ├── 1_depends.py │ │ │ │ │ └── 2_nested_depends.py │ │ │ └── context │ │ │ │ ├── 10_context.py │ │ │ │ ├── 11_context.py │ │ │ │ ├── 12_context.py │ │ │ │ ├── 1_context.py │ │ │ │ ├── 2_context.py │ │ │ │ ├── 3_context.py │ │ │ │ ├── 4_context.py │ │ │ │ ├── 5_context.py │ │ │ │ ├── 6_context.py │ │ │ │ ├── 7_context.py │ │ │ │ ├── 8_context.py │ │ │ │ └── 9_context.py │ │ ├── documentation │ │ │ ├── custom_schema.py │ │ │ ├── example.py │ │ │ ├── example.yaml │ │ │ └── fastapi.py │ │ ├── lifespan │ │ │ ├── 3_multiple.py │ │ │ ├── kafka │ │ │ │ ├── 1.py │ │ │ │ └── 2_ml.py │ │ │ ├── nats │ │ │ │ ├── 1.py │ │ │ │ └── 2_ml.py │ │ │ ├── rabbit │ │ │ │ ├── 1.py │ │ │ │ └── 2_ml.py │ │ │ ├── redis │ │ │ │ ├── 1.py │ │ │ │ └── 2_ml.py │ │ │ └── sqs │ │ │ │ ├── 1.py │ │ │ │ └── 2_ml.py │ │ ├── settings │ │ │ ├── settings_base_1.py │ │ │ ├── settings_base_2.py │ │ │ ├── settings_env.py │ │ │ └── usage.py │ │ └── testing │ │ │ ├── 4_suppressed_exc.py │ │ │ ├── kafka │ │ │ ├── 1_main.py │ │ │ ├── 2_test.py │ │ │ ├── 3_conftest.py │ │ │ ├── 5_build_message.py │ │ │ └── 6_reraise.py │ │ │ ├── nats │ │ │ ├── 1_main.py │ │ │ ├── 2_test.py │ │ │ ├── 3_conftest.py │ │ │ ├── 5_build_message.py │ │ │ └── 6_reraise.py │ │ │ ├── rabbit │ │ │ ├── 1_main.py │ │ │ ├── 2_test.py │ │ │ ├── 3_conftest.py │ │ │ ├── 5_build_message.py │ │ │ └── 6_reraise.py │ │ │ ├── redis │ │ │ ├── 1_main.py │ │ │ ├── 2_test.py │ │ │ ├── 3_conftest.py │ │ │ ├── 5_build_message.py │ │ │ └── 6_reraise.py │ │ │ └── sqs │ │ │ ├── 1_main.py │ │ │ ├── 2_test.py │ │ │ ├── 3_conftest.py │ │ │ ├── 5_build_message.py │ │ │ └── 6_reraise.py │ ├── rabbit │ │ ├── direct.py │ │ ├── fanout.py │ │ ├── header.py │ │ └── topic.py │ └── redis │ │ ├── direct.py │ │ └── pattern.py ├── includes │ ├── docker-compose.yml │ ├── getting_started │ │ ├── app │ │ │ ├── 1_broker.md │ │ │ └── 2_set_broker.md │ │ ├── broker │ │ │ ├── index │ │ │ │ ├── imports.md │ │ │ │ ├── initialization.md │ │ │ │ └── install.md │ │ │ ├── publishing │ │ │ │ ├── 1_inside_propan.md │ │ │ │ └── 2_context.md │ │ │ ├── routers.md │ │ │ ├── rpc │ │ │ │ ├── 1_handler.md │ │ │ │ ├── 2_blocking_client.md │ │ │ │ └── 6_noblocking_client.md │ │ │ └── serialization │ │ │ │ └── parse_signature.md │ │ ├── cli │ │ │ └── 01_context.md │ │ ├── dependencies │ │ │ └── depends │ │ │ │ ├── 1.md │ │ │ │ ├── 2.md │ │ │ │ ├── 3.md │ │ │ │ ├── 4.md │ │ │ │ └── 5.md │ │ ├── index │ │ │ ├── 01_base.md │ │ │ ├── 04_http_example.md │ │ │ ├── 05_native_fastapi.md │ │ │ └── install.md │ │ ├── lifespan │ │ │ ├── 1.md │ │ │ ├── 2.md │ │ │ ├── 3.md │ │ │ ├── 4.md │ │ │ ├── 5.md │ │ │ ├── 6.md │ │ │ └── 7.md │ │ └── test │ │ │ ├── 1.md │ │ │ ├── 2.md │ │ │ ├── 3.md │ │ │ ├── 4.md │ │ │ └── 5.md │ └── integrations │ │ └── fastapi │ │ ├── after_startup.md │ │ ├── fastapi_plugin.md │ │ ├── fastapi_plugin_depends.md │ │ └── fastapi_plugin_send.md ├── mkdocs.yml └── requirements.txt ├── examples ├── 1_basic_usage.py ├── 2_specific_exchange.py ├── 3_lifespan_events.py ├── 4_cli_attributes_promotion.py ├── 5_publishing.py ├── 6_arguments_casting.py ├── 7_handler_errors_processing.py ├── 8_rpc_over_mq.py ├── 9_noblocking_callbacks.py ├── dependencies │ ├── 1_dependency_injection.py │ ├── 2_dependency_declaration.py │ ├── 3_dependency_aliases.py │ ├── 4_dependency_deep_aliases.py │ ├── 5_dependency_nesting.py │ ├── 6_dependecy_calling.py │ └── 7_annotated.py ├── http_frameworks_integrations │ ├── aiohttp.py │ ├── blacksheep.py │ ├── falcon.py │ ├── fastapi.py │ ├── native_fastapi.py │ ├── quart.py │ ├── sanic.py │ └── tornado.py ├── kafka │ └── 1_direct.py ├── nats │ └── 1_basic.py ├── rabbit │ ├── direct.py │ ├── fanout.py │ ├── header.py │ ├── streams.py │ └── topic.py ├── redis │ ├── direct.py │ └── pattern.py ├── scheduling │ └── rocketry.py ├── serialization │ ├── gen_py_code.sh │ ├── message.proto │ ├── protobuf.py │ └── requirements.txt └── sqs │ └── 1_basic.py ├── propan ├── __about__.py ├── __init__.py ├── __main__.py ├── _compat.py ├── annotations.py ├── asyncapi │ ├── __init__.py │ ├── bindings │ │ ├── __init__.py │ │ ├── amqp.py │ │ ├── kafka.py │ │ ├── main.py │ │ ├── nats.py │ │ ├── redis.py │ │ └── sqs.py │ ├── channels.py │ ├── info.py │ ├── main.py │ ├── message.py │ ├── security.py │ ├── servers.py │ ├── subscription.py │ └── utils.py ├── brokers │ ├── __init__.py │ ├── _model │ │ ├── __init__.py │ │ ├── broker_usecase.py │ │ ├── routing.py │ │ ├── schemas.py │ │ └── utils.py │ ├── constants.py │ ├── exceptions.py │ ├── kafka │ │ ├── __init__.py │ │ ├── kafka_broker.py │ │ ├── kafka_broker.pyi │ │ ├── routing.py │ │ ├── routing.pyi │ │ └── schemas.py │ ├── middlewares.py │ ├── nats │ │ ├── __init__.py │ │ ├── consts.py │ │ ├── nats_broker.py │ │ ├── nats_broker.pyi │ │ ├── nats_js_broker.py │ │ ├── nats_js_broker.pyi │ │ ├── routing.py │ │ ├── routing.pyi │ │ └── schemas.py │ ├── push_back_watcher.py │ ├── rabbit │ │ ├── __init__.py │ │ ├── logging.py │ │ ├── rabbit_broker.py │ │ ├── rabbit_broker.pyi │ │ ├── routing.py │ │ ├── routing.pyi │ │ ├── schemas.py │ │ └── utils.py │ ├── redis │ │ ├── __init__.py │ │ ├── redis_broker.py │ │ ├── redis_broker.pyi │ │ ├── routing.py │ │ ├── routing.pyi │ │ └── schemas.py │ └── sqs │ │ ├── __init__.py │ │ ├── routing.py │ │ ├── routing.pyi │ │ ├── schema.py │ │ ├── sqs_broker.py │ │ └── sqs_broker.pyi ├── cli │ ├── __init__.py │ ├── app.py │ ├── docs │ │ ├── __init__.py │ │ ├── app.py │ │ ├── gen.py │ │ └── serving.py │ ├── main.py │ ├── startproject │ │ ├── __init__.py │ │ ├── app.py │ │ ├── async_app │ │ │ ├── __init__.py │ │ │ ├── app.py │ │ │ ├── core.py │ │ │ ├── kafka.py │ │ │ ├── nats.py │ │ │ ├── rabbit.py │ │ │ ├── redis.py │ │ │ └── sqs.py │ │ ├── core.py │ │ ├── sync_app │ │ │ ├── __init__.py │ │ │ └── app.py │ │ └── utils.py │ ├── supervisors │ │ ├── __init__.py │ │ ├── basereload.py │ │ ├── multiprocess.py │ │ ├── utils.py │ │ └── watchfiles.py │ └── utils │ │ ├── __init__.py │ │ ├── imports.py │ │ ├── logs.py │ │ └── parser.py ├── fastapi │ ├── __init__.py │ ├── core │ │ ├── __init__.py │ │ ├── route.py │ │ └── router.py │ ├── kafka │ │ ├── __init__.py │ │ ├── router.py │ │ └── router.pyi │ ├── nats │ │ ├── __init__.py │ │ ├── router.py │ │ └── router.pyi │ ├── rabbit │ │ ├── __init__.py │ │ ├── router.py │ │ └── router.pyi │ ├── redis │ │ ├── __init__.py │ │ ├── router.py │ │ └── router.pyi │ └── sqs │ │ ├── __init__.py │ │ ├── router.py │ │ └── router.pyi ├── log │ ├── __init__.py │ ├── formatter.py │ └── logging.py ├── py.typed ├── test │ ├── __init__.py │ ├── kafka.py │ ├── nats.py │ ├── rabbit.py │ ├── redis.py │ ├── sqs.py │ └── utils.py ├── types.py └── utils │ ├── __init__.py │ ├── classes.py │ ├── context │ ├── __init__.py │ ├── main.py │ └── types.py │ ├── functions.py │ └── no_cast.py ├── pyproject.toml ├── requirements.docs.txt ├── requirements.lint.txt ├── requirements.test.txt ├── requirements.txt ├── scripts ├── lint.sh ├── publish.sh ├── test-cov.sh └── test.sh └── tests ├── __init__.py ├── asyncapi ├── __init__.py ├── handler │ ├── __init__.py │ ├── test_base_arguments.py │ ├── test_dependencies_arguments.py │ └── test_naming.py ├── kafka │ ├── __init__.py │ ├── test_handler.py │ └── test_server.py ├── nats │ ├── __init__.py │ ├── test_handler.py │ └── test_server.py ├── rabbit │ ├── __init__.py │ ├── test_handler.py │ └── test_server.py ├── redis │ ├── __init__.py │ ├── test_handler.py │ └── test_server.py ├── sqs │ ├── __init__.py │ ├── test_handler.py │ └── test_server.py └── test_app_info.py ├── brokers ├── __init__.py ├── base │ ├── __init__.py │ ├── connection.py │ ├── consume.py │ ├── middlewares.py │ ├── publish.py │ ├── router.py │ ├── rpc.py │ ├── test_pushback.py │ └── testclient.py ├── conftest.py ├── kafka │ ├── __init__.py │ ├── conftest.py │ ├── test_connect.py │ ├── test_consume.py │ ├── test_middleware.py │ ├── test_publish.py │ ├── test_router.py │ ├── test_rpc.py │ └── test_test_client.py ├── nats │ ├── __init__.py │ ├── conftest.py │ ├── test_connect.py │ ├── test_consume.py │ ├── test_middleware.py │ ├── test_publish.py │ ├── test_router.py │ ├── test_rpc.py │ └── test_test_client.py ├── nats_js │ ├── __init__.py │ ├── conftest.py │ ├── test_connect.py │ ├── test_consume.py │ ├── test_middleware.py │ ├── test_publish.py │ ├── test_router.py │ ├── test_rpc.py │ └── test_test_client.py ├── rabbit │ ├── __init__.py │ ├── conftest.py │ ├── test_connect.py │ ├── test_consume.py │ ├── test_custom_decode.py │ ├── test_declare.py │ ├── test_depends.py │ ├── test_init.py │ ├── test_middleware.py │ ├── test_nested_exchange.py │ ├── test_publish.py │ ├── test_router.py │ ├── test_rpc.py │ └── test_test_client.py ├── redis │ ├── __init__.py │ ├── conftest.py │ ├── test_connect.py │ ├── test_consume.py │ ├── test_middleware.py │ ├── test_publish.py │ ├── test_router.py │ ├── test_rpc.py │ └── test_test_client.py └── sqs │ ├── __init__.py │ ├── conftest.py │ ├── test_connect.py │ ├── test_consume.py │ ├── test_middleware.py │ ├── test_publish.py │ ├── test_router.py │ ├── test_rpc.py │ └── test_test_client.py ├── cli ├── __init__.py ├── conftest.py ├── supervisors │ ├── __init__.py │ ├── test_base_reloader.py │ ├── test_multiprocess.py │ └── test_watchfiles.py ├── test_app.py ├── test_creation.py ├── test_doc.py ├── test_run.py ├── test_version.py └── utils │ ├── __init__.py │ ├── test_imports.py │ ├── test_logs.py │ └── test_parser.py ├── conftest.py ├── fastapi ├── __init__.py ├── case.py ├── test_base.py ├── test_kafka.py ├── test_nats.py ├── test_rabbit.py ├── test_redis.py └── test_sqs.py ├── log ├── __init__.py └── test_formatter.py ├── tools ├── __init__.py └── marks.py └── utils ├── __init__.py ├── context ├── __init__.py ├── test_alias.py ├── test_depends.py └── test_main.py ├── test_classes.py ├── test_functions.py ├── test_no_cast.py └── type_cast ├── __init__.py ├── test_base.py └── test_model.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: 'Bug:' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Add source code 14 | 15 | ```python 16 | from propan import PropanApp 17 | ... 18 | ``` 19 | 20 | And steps to reproduce the behavior: 21 | 22 | 1. ... 23 | 24 | **Expected behavior** 25 | A clear and concise description of what you expected to happen. 26 | 27 | **Screenshots** 28 | If applicable, add screenshots to help explain your problem. 29 | 30 | **Environment** 31 | Add `propan -v` command output to show your current project and system environment 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | 3 | contact_links: 4 | - name: Security Contact 5 | about: Please report security vulnerabilities to diementros@yandex.ru 6 | - name: Question or Problem 7 | about: Ask a question or ask about a problem in GitHub Discussions. 8 | url: https://github.com/Lancetnik/Propan/discussions/categories/questions 9 | -------------------------------------------------------------------------------- /.github/dependantbot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | commit-message: 9 | prefix: ⬆ 10 | # Python 11 | - package-ecosystem: "pip" 12 | directory: "/" 13 | schedule: 14 | interval: "daily" 15 | commit-message: 16 | prefix: ⬆ -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - docs/** 9 | - .github/workflows/documentation.yml 10 | 11 | env: 12 | CI: true 13 | 14 | permissions: 15 | contents: write 16 | 17 | jobs: 18 | deploy: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v3 22 | - uses: actions/setup-python@v4 23 | with: 24 | python-version: 3.x 25 | - uses: actions/cache@v2 26 | with: 27 | key: ${{ github.ref }} 28 | path: .cache 29 | - run: pip install -r requirements.docs.txt 30 | - working-directory: ./docs 31 | run: mkdocs gh-deploy --force 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | __pycache__ 3 | *.lock 4 | .vscode 5 | .pypirc 6 | venv 7 | .venv 8 | serve.py 9 | test.py 10 | poetry.lock 11 | docker-compose.yaml 12 | .pytest_cache 13 | poetry.lock 14 | .coverage* 15 | htmlcov 16 | wtf 17 | .ruff_cache 18 | .mypy_cache 19 | coverage.json 20 | site 21 | Dockerfile 22 | t 23 | asyncapi.yaml 24 | .cache -------------------------------------------------------------------------------- /docs/docs/assets/img/docs-html-short.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/docs/docs/assets/img/docs-html-short.png -------------------------------------------------------------------------------- /docs/docs/assets/img/docs-html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/docs/docs/assets/img/docs-html.png -------------------------------------------------------------------------------- /docs/docs/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/docs/docs/assets/img/favicon.png -------------------------------------------------------------------------------- /docs/docs/assets/img/logo-no-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/docs/docs/assets/img/logo-no-background.png -------------------------------------------------------------------------------- /docs/docs/assets/img/logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/docs/docs/assets/img/logo-white.png -------------------------------------------------------------------------------- /docs/docs/en/helpful/in-progress.md: -------------------------------------------------------------------------------- 1 | ### In progress 2 | 3 | !!! warning 4 | Sorry, the current page still under development, but should be here a little bit later. Hoping see you soon... 5 | -------------------------------------------------------------------------------- /docs/docs/en/helpful/missing-translation.md: -------------------------------------------------------------------------------- 1 | ### Missing translation 2 | 3 | !!! warning 4 | The current page doesn't have a translation yet. 5 | -------------------------------------------------------------------------------- /docs/docs/en/kafka/1_kafka-index.md: -------------------------------------------------------------------------------- 1 | # Kafka 2 | 3 | {! docs/en/helpful/in-progress.md !} 4 | -------------------------------------------------------------------------------- /docs/docs/en/sqs/1_sqs-index.md: -------------------------------------------------------------------------------- 1 | # SQS 2 | 3 | {! docs/en/helpful/in-progress.md !} 4 | -------------------------------------------------------------------------------- /docs/docs/ru/helpful/in-progress.md: -------------------------------------------------------------------------------- 1 | ### Страница разрабатывается 2 | 3 | !!! warning 4 | Простите, сейчас эта страница разрабатывается, но совсем скоро она появится. Загляните позже... 5 | -------------------------------------------------------------------------------- /docs/docs/ru/helpful/missing-translation.md: -------------------------------------------------------------------------------- 1 | ### Перевод отсутствует 2 | 3 | !!! warning 4 | Текущая страница еще не имеет локализации. 5 | -------------------------------------------------------------------------------- /docs/docs/ru/kafka/1_kafka-index.md: -------------------------------------------------------------------------------- 1 | # Kafka 2 | 3 | {! docs/ru/helpful/in-progress.md !} 4 | -------------------------------------------------------------------------------- /docs/docs/ru/sqs/1_sqs-index.md: -------------------------------------------------------------------------------- 1 | # SQS 2 | 3 | {! docs/ru/helpful/in-progress.md !} 4 | -------------------------------------------------------------------------------- /docs/docs_src/contributing/adapter/rabbit_close.py: -------------------------------------------------------------------------------- 1 | class RabbitBroker(BrokerAsyncUsecase): 2 | ... 3 | 4 | async def close(self) -> None: 5 | if self._channel is not None: 6 | await self._channel.close() 7 | self._channel = None 8 | 9 | if self._connection is not None: 10 | await self._connection.close() 11 | self._connection = None 12 | -------------------------------------------------------------------------------- /docs/docs_src/contributing/adapter/rabbit_connect.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from typing import Any, Optional 3 | 4 | 5 | import aio_pika 6 | from propan.brokers._model import BrokerAsyncUsecase 7 | 8 | class RabbitBroker(BrokerAsyncUsecase): 9 | _connection: Optional[aio_pika.RobustConnection] 10 | _channel: Optional[aio_pika.RobustChannel] 11 | 12 | async def _connect( 13 | self, 14 | *args: Any, 15 | **kwargs: Any, 16 | ) -> aio_pika.RobustConnection: 17 | connection = await aio_pika.connect_robust( 18 | *args, **kwargs, loop=asyncio.get_event_loop() 19 | ) 20 | 21 | if self._channel is None: 22 | self._channel = await connection.channel() 23 | 24 | return connection 25 | -------------------------------------------------------------------------------- /docs/docs_src/contributing/adapter/rabbit_fmt.py: -------------------------------------------------------------------------------- 1 | class RabbitBroker(BrokerAsyncUsecase): 2 | __max_exchange_len: int 3 | __max_queue_len: int 4 | 5 | def __init__( 6 | self, 7 | *args: Any, 8 | log_fmt: Optional[str] = None, 9 | **kwargs: Any, 10 | ) -> None: 11 | super().__init__(*args, log_fmt=log_fmt, **kwargs) 12 | 13 | self.__max_queue_len = 4 14 | self.__max_exchange_len = 4 15 | 16 | @property 17 | def fmt(self) -> str: 18 | return super().fmt or ( 19 | "%(asctime)s %(levelname)s - " 20 | f"%(exchange)-{self.__max_exchange_len}s | " 21 | f"%(queue)-{self.__max_queue_len}s | " 22 | f"%(message_id)-10s " 23 | "- %(message)s" 24 | ) 25 | -------------------------------------------------------------------------------- /docs/docs_src/contributing/adapter/rabbit_get_log_context.py: -------------------------------------------------------------------------------- 1 | class RabbitBroker(BrokerAsyncUsecase): 2 | def _get_log_context( 3 | self, 4 | message: Optional[PropanMessage], 5 | queue: RabbitQueue, 6 | exchange: Optional[RabbitExchange] = None, 7 | ) -> Dict[str, Any]: 8 | return { 9 | "queue": queue.name, 10 | "exchange": exchange.name if exchange else "default", 11 | **super()._get_log_context(message), 12 | } 13 | -------------------------------------------------------------------------------- /docs/docs_src/contributing/adapter/rabbit_init.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Optional 2 | 3 | from propan.brokers._model import BrokerAsyncUsecase 4 | 5 | 6 | class RabbitBroker(BrokerAsyncUsecase): 7 | def __init__( 8 | self, 9 | *args: Any, 10 | log_fmt: Optional[str] = None, 11 | **kwargs: Any, 12 | ) -> None: 13 | super().__init__(*args, log_fmt=log_fmt, **kwargs) 14 | -------------------------------------------------------------------------------- /docs/docs_src/contributing/adapter/rabbit_parse.py: -------------------------------------------------------------------------------- 1 | import aio_pika 2 | 3 | from propan.brokers._model import BrokerAsyncUsecase 4 | from propan.brokers._model.schemas import PropanMessage 5 | 6 | 7 | class RabbitBroker(BrokerAsyncUsecase): 8 | ... 9 | @staticmethod 10 | async def _parse_message( 11 | message: aio_pika.message.IncomingMessage, 12 | ) -> PropanMessage: 13 | return PropanMessage( 14 | body=message.body, 15 | headers=message.headers, 16 | reply_to=message.reply_to or "", 17 | message_id=message.message_id, 18 | content_type=message.content_type or "", 19 | raw_message=message, 20 | ) 21 | -------------------------------------------------------------------------------- /docs/docs_src/contributing/adapter/rabbit_start.py: -------------------------------------------------------------------------------- 1 | class RabbitBroker(BrokerAsyncUsecase): 2 | ... 3 | async def start(self) -> None: 4 | await super().start() 5 | 6 | for handler in self.handlers: 7 | queue = await self._channel.declare_queue(**handler.queue.dict()) 8 | func = handler.callback 9 | await queue.consume(func) 10 | -------------------------------------------------------------------------------- /docs/docs_src/contributing/adapter/redis_process.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from typing import Optional, TypeVar, Callable 3 | 4 | from propan.brokers._model import BrokerAsyncUsecase 5 | from propan.brokers._model.schemas import PropanMessage 6 | from propan.brokers.push_back_watcher import BaseWatcher 7 | 8 | T = TypeVar("T") 9 | 10 | class RedisProcess(BrokerAsyncUsecase): 11 | ... 12 | def _process_message( 13 | self, 14 | func: Callable[[PropanMessage], T], 15 | watcher: Optional[BaseWatcher], 16 | ) -> Callable[[PropanMessage], T]: 17 | @wraps(func) 18 | async def wrapper(message: PropanMessage) -> T: 19 | r = await func(message) 20 | if message.reply_to: 21 | await self.publish(r or "", message.reply_to) 22 | return r 23 | 24 | return wrapper 25 | -------------------------------------------------------------------------------- /docs/docs_src/index/02_type_casting.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | 3 | ... 4 | 5 | class SimpleMessage(BaseModel): 6 | key: int 7 | 8 | @broker.handle("test2") 9 | async def second_handler(body: SimpleMessage): 10 | assert isinstance(body.key, int) -------------------------------------------------------------------------------- /docs/docs_src/index/03_dependencies.py: -------------------------------------------------------------------------------- 1 | from logging import Logger 2 | from propan import Context, Depends 3 | 4 | ... 5 | 6 | async def base_dep(user_id: int) -> bool: 7 | return True 8 | 9 | @broker.handle("test") 10 | async def base_handler(user_id: int, 11 | dep: bool = Depends(base_dep), 12 | logger: Logger = Context()): 13 | assert dep is True 14 | logger.info(body) 15 | -------------------------------------------------------------------------------- /docs/docs_src/index/kafka/01_base.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, KafkaBroker 2 | 3 | broker = KafkaBroker("localhost:9092") 4 | 5 | app = PropanApp(broker) 6 | 7 | @broker.handle("test") 8 | async def base_handler(body): 9 | print(body) -------------------------------------------------------------------------------- /docs/docs_src/index/kafka/04_http_example.py: -------------------------------------------------------------------------------- 1 | from propan import KafkaBroker 2 | from sanic import Sanic 3 | 4 | app = Sanic("MyHelloWorldApp") 5 | broker = KafkaBroker("localhost:9092") 6 | 7 | @broker.handle("test") 8 | async def base_handler(body): 9 | print(body) 10 | 11 | @app.after_server_start 12 | async def start_broker(app, loop): 13 | await broker.start() 14 | 15 | @app.after_server_stop 16 | async def stop_broker(app, loop): 17 | await broker.close() -------------------------------------------------------------------------------- /docs/docs_src/index/kafka/05_native_fastapi.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | from propan.fastapi import KafkaRouter 4 | 5 | router = KafkaRouter("localhost:9092") 6 | 7 | app = FastAPI(lifespan=router.lifespan_context) 8 | 9 | class Incoming(BaseModel): 10 | m: dict 11 | 12 | def call(): 13 | return True 14 | 15 | @router.event("test") 16 | async def hello(m: Incoming, d = Depends(call)) -> dict: 17 | return { "response": "Hello, world!"} 18 | 19 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/index/nats/01_base.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker 2 | 3 | broker = NatsBroker("nats://localhost:4222") 4 | 5 | app = PropanApp(broker) 6 | 7 | @broker.handle("test") 8 | async def base_handler(body): 9 | print(body) -------------------------------------------------------------------------------- /docs/docs_src/index/nats/04_http_example.py: -------------------------------------------------------------------------------- 1 | from propan import NatsBroker 2 | from sanic import Sanic 3 | 4 | app = Sanic("MyHelloWorldApp") 5 | broker = NatsBroker("nats://localhost:4222") 6 | 7 | @broker.handle("test") 8 | async def base_handler(body): 9 | print(body) 10 | 11 | @app.after_server_start 12 | async def start_broker(app, loop): 13 | await broker.start() 14 | 15 | @app.after_server_stop 16 | async def stop_broker(app, loop): 17 | await broker.close() -------------------------------------------------------------------------------- /docs/docs_src/index/nats/05_native_fastapi.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | from propan.fastapi import NatsRouter 4 | 5 | router = NatsRouter("nats://localhost:4222") 6 | 7 | app = FastAPI(lifespan=router.lifespan_context) 8 | 9 | class Incoming(BaseModel): 10 | m: dict 11 | 12 | def call(): 13 | return True 14 | 15 | @router.event("test") 16 | async def hello(m: Incoming, d = Depends(call)) -> dict: 17 | return { "response": "Hello, world!"} 18 | 19 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/index/rabbit/01_base.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker 2 | 3 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 4 | 5 | app = PropanApp(broker) 6 | 7 | @broker.handle("test") 8 | async def base_handler(body): 9 | print(body) -------------------------------------------------------------------------------- /docs/docs_src/index/rabbit/04_http_example.py: -------------------------------------------------------------------------------- 1 | from propan import RabbitBroker 2 | from sanic import Sanic 3 | 4 | app = Sanic("MyHelloWorldApp") 5 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 6 | 7 | @broker.handle("test") 8 | async def base_handler(body): 9 | print(body) 10 | 11 | @app.after_server_start 12 | async def start_broker(app, loop): 13 | await broker.start() 14 | 15 | @app.after_server_stop 16 | async def stop_broker(app, loop): 17 | await broker.close() -------------------------------------------------------------------------------- /docs/docs_src/index/rabbit/05_native_fastapi.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | from propan.fastapi import RabbitRouter 4 | 5 | router = RabbitRouter("amqp://guest:guest@localhost:5672") 6 | 7 | app = FastAPI(lifespan=router.lifespan_context) 8 | 9 | class Incoming(BaseModel): 10 | m: dict 11 | 12 | def call(): 13 | return True 14 | 15 | @router.event("test") 16 | async def hello(m: Incoming, d = Depends(call)) -> dict: 17 | return { "response": "Hello, world!"} 18 | 19 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/index/redis/01_base.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | 3 | broker = RedisBroker("redis://localhost:6379") 4 | 5 | app = PropanApp(broker) 6 | 7 | @broker.handle("test") 8 | async def base_handler(body): 9 | print(body) -------------------------------------------------------------------------------- /docs/docs_src/index/redis/04_http_example.py: -------------------------------------------------------------------------------- 1 | from propan import RedisBroker 2 | from sanic import Sanic 3 | 4 | app = Sanic("MyHelloWorldApp") 5 | broker = RedisBroker("redis://localhost:6379") 6 | 7 | @broker.handle("test") 8 | async def base_handler(body): 9 | print(body) 10 | 11 | @app.after_server_start 12 | async def start_broker(app, loop): 13 | await broker.start() 14 | 15 | @app.after_server_stop 16 | async def stop_broker(app, loop): 17 | await broker.close() -------------------------------------------------------------------------------- /docs/docs_src/index/redis/05_native_fastapi.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | from propan.fastapi import RedisRouter 4 | 5 | router = RedisRouter("redis://localhost:6379") 6 | 7 | app = FastAPI(lifespan=router.lifespan_context) 8 | 9 | class Incoming(BaseModel): 10 | m: dict 11 | 12 | def call(): 13 | return True 14 | 15 | @router.event("test") 16 | async def hello(m: Incoming, d = Depends(call)) -> dict: 17 | return { "response": "Hello, world!"} 18 | 19 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/index/sqs/01_base.py: -------------------------------------------------------------------------------- 1 | from botocore import UNSIGNED 2 | from aiobotocore.config import AioConfig 3 | from propan import PropanApp, SQSBroker 4 | 5 | broker = SQSBroker( 6 | url="http://localhost:9324", 7 | region_name="us-west-2", 8 | config = AioConfig(signature_version=UNSIGNED) 9 | ) 10 | 11 | app = PropanApp(broker) 12 | 13 | @broker.handle("test") 14 | async def base_handler(body): 15 | print(body) -------------------------------------------------------------------------------- /docs/docs_src/index/sqs/04_http_example.py: -------------------------------------------------------------------------------- 1 | from propan import SQSBroker 2 | from sanic import Sanic 3 | 4 | app = Sanic("MyHelloWorldApp") 5 | broker = SQSBroker("http://localhost:9324", ...) 6 | 7 | @broker.handle("test") 8 | async def base_handler(body): 9 | print(body) 10 | 11 | @app.after_server_start 12 | async def start_broker(app, loop): 13 | await broker.start() 14 | 15 | @app.after_server_stop 16 | async def stop_broker(app, loop): 17 | await broker.close() -------------------------------------------------------------------------------- /docs/docs_src/index/sqs/05_native_fastapi.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | from propan.fastapi import SQSRouter 4 | 5 | router = SQSRouter("http://localhost:9324") 6 | 7 | app = FastAPI(lifespan=router.lifespan_context) 8 | 9 | class Incoming(BaseModel): 10 | m: dict 11 | 12 | def call(): 13 | return True 14 | 15 | @router.event("test") 16 | async def hello(m: Incoming, d = Depends(call)) -> dict: 17 | return { "response": "Hello, world!"} 18 | 19 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/fastapi/request.py: -------------------------------------------------------------------------------- 1 | @app.get("/") 2 | def main(request: Request): 3 | broker = request.state.broker -------------------------------------------------------------------------------- /docs/docs_src/integrations/http_frameworks_integrations/aiohttp.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can use Propan MQBrokers without PropanApp 3 | Just start and stop them whenever you want 4 | """ 5 | from aiohttp import web 6 | from propan import RabbitBroker 7 | 8 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 9 | 10 | 11 | @broker.handle("test") 12 | async def base_handler(body): 13 | print(body) 14 | 15 | 16 | async def start_broker(app): 17 | await broker.start() 18 | 19 | 20 | async def stop_broker(app): 21 | await broker.close() 22 | 23 | 24 | async def hello(request): 25 | return web.Response(text="Hello, world") 26 | 27 | 28 | app = web.Application() 29 | app.add_routes([web.get("/", hello)]) 30 | app.on_startup.append(start_broker) 31 | app.on_cleanup.append(stop_broker) 32 | 33 | 34 | if __name__ == "__main__": 35 | web.run_app(app) 36 | -------------------------------------------------------------------------------- /docs/docs_src/integrations/http_frameworks_integrations/blacksheep.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can use Propan MQBrokers without PropanApp 3 | Just start and stop them whenever you want 4 | """ 5 | from blacksheep import Application 6 | from propan import RabbitBroker 7 | 8 | app = Application() 9 | 10 | 11 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 12 | 13 | 14 | @broker.handle("test") 15 | async def base_handler(body): 16 | print(body) 17 | 18 | 19 | @app.on_start 20 | async def start_broker(application: Application) -> None: 21 | await broker.start() 22 | 23 | 24 | @app.on_stop 25 | async def stop_broker(application: Application) -> None: 26 | await broker.close() 27 | 28 | 29 | @app.route("/") 30 | async def home(): 31 | return "Hello, World!" 32 | -------------------------------------------------------------------------------- /docs/docs_src/integrations/http_frameworks_integrations/fastapi.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can use Propan MQBrokers without PropanApp 3 | Just start and stop them whenever you want 4 | """ 5 | from contextlib import asynccontextmanager 6 | 7 | from fastapi import FastAPI 8 | from propan import RabbitBroker 9 | 10 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 11 | 12 | app = FastAPI() 13 | 14 | 15 | @asynccontextmanager 16 | async def lifespan(app: FastAPI): 17 | await broker.start() 18 | yield 19 | await broker.close() 20 | 21 | 22 | @broker.handle("test") 23 | async def base_handler(body): 24 | print(body) 25 | 26 | 27 | @app.get("/") 28 | def read_root(): 29 | return {"Hello": "World"} 30 | -------------------------------------------------------------------------------- /docs/docs_src/integrations/http_frameworks_integrations/native_fastapi.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | from propan.fastapi import RabbitRouter 4 | 5 | app = FastAPI() 6 | 7 | router = RabbitRouter("amqp://guest:guest@localhost:5672") 8 | 9 | 10 | class Incoming(BaseModel): 11 | m: dict 12 | 13 | 14 | def call(): 15 | return True 16 | 17 | 18 | @router.event("test") 19 | async def hello(m: Incoming, d=Depends(call)) -> dict: 20 | return {"response": "Hello, world!"} 21 | 22 | 23 | @router.get("/") 24 | async def hello_http(): 25 | return "Hello, http!" 26 | 27 | 28 | app.include_router(router) 29 | -------------------------------------------------------------------------------- /docs/docs_src/integrations/http_frameworks_integrations/quart.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can use Propan MQBrokers without PropanApp 3 | Just start and stop them whenever you want 4 | """ 5 | from quart import Quart 6 | from propan import RabbitBroker 7 | 8 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 9 | 10 | app = Quart(__name__) 11 | 12 | 13 | @broker.handle("test") 14 | async def base_handler(body): 15 | print(body) 16 | 17 | 18 | @app.before_serving 19 | async def start_broker(): 20 | await broker.start() 21 | 22 | 23 | @app.after_serving 24 | async def stop_broker(): 25 | await broker.close() 26 | 27 | 28 | @app.route("/") 29 | async def json(): 30 | return {"hello": "world"} 31 | -------------------------------------------------------------------------------- /docs/docs_src/integrations/http_frameworks_integrations/sanic.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can use Propan MQBrokers without PropanApp 3 | Just start and stop them whenever you want 4 | """ 5 | from sanic import Sanic 6 | from sanic.response import text 7 | from propan import RabbitBroker 8 | 9 | app = Sanic("MyHelloWorldApp") 10 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 11 | 12 | 13 | @broker.handle("test") 14 | async def base_handler(body): 15 | print(body) 16 | 17 | 18 | @app.after_server_start 19 | async def start_broker(app, loop): 20 | await broker.start() 21 | 22 | 23 | @app.after_server_stop 24 | async def stop_broker(app, loop): 25 | await broker.close() 26 | 27 | 28 | @app.get("/") 29 | async def hello_world(request): 30 | return text("Hello, world.") 31 | -------------------------------------------------------------------------------- /docs/docs_src/integrations/kafka/fastapi_after_startup.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from propan.fastapi import KafkaRouter 3 | 4 | router = KafkaRouter("localhost:9092") 5 | 6 | app = FastAPI(lifespan=router.lifespan_context) 7 | 8 | @router.after_startup 9 | def do_smth(app: FastAPI): 10 | ... 11 | 12 | @router.after_startup 13 | async def publish_smth(app: FastAPI): 14 | await router.broker.publish(...) 15 | 16 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/kafka/fastapi_plugin.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | from propan.fastapi import KafkaRouter 4 | 5 | router = KafkaRouter("localhost:9092") 6 | 7 | app = FastAPI(lifespan=router.lifespan_context) 8 | 9 | class Incoming(BaseModel): 10 | m: dict 11 | 12 | def call(): 13 | return True 14 | 15 | @router.event("test") 16 | async def hello(m: Incoming, d = Depends(call)) -> dict: 17 | return { "response": "Hello, Kafka!" } 18 | 19 | @router.get("/") 20 | async def hello_http(): 21 | return "Hello, HTTP!" 22 | 23 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/kafka/fastapi_plugin_depends.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, Depends 2 | from propan import KafkaBroker 3 | from propan.fastapi import KafkaRouter 4 | from typing_extensions import Annotated 5 | 6 | router = KafkaRouter("localhost:9092") 7 | 8 | app = FastAPI(lifespan=router.lifespan_context) 9 | 10 | def broker(): 11 | return router.broker 12 | 13 | @router.get("/") 14 | async def hello_http(broker: Annotated[KafkaBroker, Depends(broker)]): 15 | await broker.publish("Hello, Kafka!", "test") 16 | return "Hello, HTTP!" 17 | 18 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/kafka/fastapi_plugin_send.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from propan.fastapi import KafkaRouter 3 | 4 | router = KafkaRouter("localhost:9092") 5 | 6 | app = FastAPI(lifespan=router.lifespan_context) 7 | 8 | @router.get("/") 9 | async def hello_http(): 10 | await router.broker.publish("Hello, Kafka!", "test") 11 | return "Hello, HTTP!" 12 | 13 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/nats/fastapi_after_startup.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from propan.fastapi import NatsRouter 3 | 4 | router = NatsRouter("nats://localhost:4222") 5 | 6 | app = FastAPI(lifespan=router.lifespan_context) 7 | 8 | @router.after_startup 9 | def do_smth(app: FastAPI): 10 | ... 11 | 12 | @router.after_startup 13 | async def publish_smth(app: FastAPI): 14 | await router.broker.publish(...) 15 | 16 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/nats/fastapi_plugin.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | from propan.fastapi import NatsRouter 4 | 5 | router = NatsRouter("nats://localhost:4222") 6 | 7 | app = FastAPI(lifespan=router.lifespan_context) 8 | 9 | class Incoming(BaseModel): 10 | m: dict 11 | 12 | def call(): 13 | return True 14 | 15 | @router.event("test") 16 | async def hello(m: Incoming, d = Depends(call)) -> dict: 17 | return { "response": "Hello, Nats!" } 18 | 19 | @router.get("/") 20 | async def hello_http(): 21 | return "Hello, HTTP!" 22 | 23 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/nats/fastapi_plugin_depends.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, Depends 2 | from propan import NatsBroker 3 | from propan.fastapi import NatsRouter 4 | from typing_extensions import Annotated 5 | 6 | router = NatsRouter("nats://localhost:4222") 7 | 8 | app = FastAPI(lifespan=router.lifespan_context) 9 | 10 | def broker(): 11 | return router.broker 12 | 13 | @router.get("/") 14 | async def hello_http(broker: Annotated[NatsBroker, Depends(broker)]): 15 | await broker.publish("Hello, Nats!", "test") 16 | return "Hello, HTTP!" 17 | 18 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/nats/fastapi_plugin_send.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from propan.fastapi import NatsRouter 3 | 4 | router = NatsRouter("nats://localhost:4222") 5 | 6 | app = FastAPI(lifespan=router.lifespan_context) 7 | 8 | @router.get("/") 9 | async def hello_http(): 10 | await router.broker.publish("Hello, Nats!", "test") 11 | return "Hello, HTTP!" 12 | 13 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/rabbit/fastapi_after_startup.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from propan.fastapi import RabbitRouter 3 | 4 | router = RabbitRouter("amqp://guest:guest@localhost:5672") 5 | 6 | app = FastAPI(lifespan=router.lifespan_context) 7 | 8 | @router.after_startup 9 | def do_smth(app: FastAPI): 10 | ... 11 | 12 | @router.after_startup 13 | async def publish_smth(app: FastAPI): 14 | await router.broker.publish(...) 15 | 16 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/rabbit/fastapi_plugin.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | from propan.fastapi import RabbitRouter 4 | 5 | router = RabbitRouter("amqp://guest:guest@localhost:5672") 6 | 7 | app = FastAPI(lifespan=router.lifespan_context) 8 | 9 | class Incoming(BaseModel): 10 | m: dict 11 | 12 | def call(): 13 | return True 14 | 15 | @router.event("test") 16 | async def hello(m: Incoming, d = Depends(call)) -> dict: 17 | return { "response": "Hello, Rabbit!" } 18 | 19 | @router.get("/") 20 | async def hello_http(): 21 | return "Hello, HTTP!" 22 | 23 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/rabbit/fastapi_plugin_depends.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, Depends 2 | from propan import RabbitBroker 3 | from propan.fastapi import RabbitRouter 4 | from typing_extensions import Annotated 5 | 6 | router = RabbitRouter("amqp://guest:guest@localhost:5672") 7 | 8 | app = FastAPI(lifespan=router.lifespan_context) 9 | 10 | def broker(): 11 | return router.broker 12 | 13 | @router.get("/") 14 | async def hello_http(broker: Annotated[RabbitBroker, Depends(broker)]): 15 | await broker.publish("Hello, Rabbit!", "test") 16 | return "Hello, HTTP!" 17 | 18 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/rabbit/fastapi_plugin_send.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from propan.fastapi import RabbitRouter 3 | 4 | router = RabbitRouter("amqp://guest:guest@localhost:5672") 5 | 6 | app = FastAPI(lifespan=router.lifespan_context) 7 | 8 | @router.get("/") 9 | async def hello_http(): 10 | await router.broker.publish("Hello, Rabbit!", "test") 11 | return "Hello, HTTP!" 12 | 13 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/redis/fastapi_after_startup.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from propan.fastapi import RedisRouter 3 | 4 | router = RedisRouter("redis://localhost:6379") 5 | 6 | app = FastAPI(lifespan=router.lifespan_context) 7 | 8 | @router.after_startup 9 | def do_smth(app: FastAPI): 10 | ... 11 | 12 | @router.after_startup 13 | async def publish_smth(app: FastAPI): 14 | await router.broker.publish(...) 15 | 16 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/redis/fastapi_plugin.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | from propan.fastapi import RedisRouter 4 | 5 | router = RedisRouter("redis://localhost:6379") 6 | 7 | app = FastAPI(lifespan=router.lifespan_context) 8 | 9 | class Incoming(BaseModel): 10 | m: dict 11 | 12 | def call(): 13 | return True 14 | 15 | @router.event("test") 16 | async def hello(m: Incoming, d = Depends(call)) -> dict: 17 | return { "response": "Hello, Redis!" } 18 | 19 | @router.get("/") 20 | async def hello_http(): 21 | return "Hello, HTTP!" 22 | 23 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/redis/fastapi_plugin_depends.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, Depends 2 | from propan import RedisBroker 3 | from propan.fastapi import RedisRouter 4 | from typing_extensions import Annotated 5 | 6 | router = RedisRouter("redis://localhost:6379") 7 | 8 | app = FastAPI(lifespan=router.lifespan_context) 9 | 10 | def broker(): 11 | return router.broker 12 | 13 | @router.get("/") 14 | async def hello_http(broker: Annotated[RedisBroker, Depends(broker)]): 15 | await broker.publish("Hello, Redis!", "test") 16 | return "Hello, HTTP!" 17 | 18 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/redis/fastapi_plugin_send.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from propan.fastapi import RedisRouter 3 | 4 | router = RedisRouter("redis://localhost:6379") 5 | 6 | app = FastAPI(lifespan=router.lifespan_context) 7 | 8 | @router.get("/") 9 | async def hello_http(): 10 | await router.broker.publish("Hello, Redis!", "test") 11 | return "Hello, HTTP!" 12 | 13 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/sqs/fastapi_after_startup.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from propan.fastapi import SQSRouter 3 | 4 | router = SQSRouter("http://localhost:9324") 5 | 6 | app = FastAPI(lifespan=router.lifespan_context) 7 | 8 | @router.after_startup 9 | def do_smth(app: FastAPI): 10 | ... 11 | 12 | @router.after_startup 13 | async def publish_smth(app: FastAPI): 14 | await router.broker.publish(...) 15 | 16 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/sqs/fastapi_plugin.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | from propan.fastapi import SQSRouter 4 | 5 | router = SQSRouter("http://localhost:9324") 6 | 7 | app = FastAPI(lifespan=router.lifespan_context) 8 | 9 | class Incoming(BaseModel): 10 | m: dict 11 | 12 | def call(): 13 | return True 14 | 15 | @router.event("test") 16 | async def hello(m: Incoming, d = Depends(call)) -> dict: 17 | return { "response": "Hello, SQS!" } 18 | 19 | @router.get("/") 20 | async def hello_http(): 21 | return "Hello, HTTP!" 22 | 23 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/sqs/fastapi_plugin_depends.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, Depends 2 | from propan import SQSBroker 3 | from propan.fastapi import SQSRouter 4 | from typing_extensions import Annotated 5 | 6 | router = SQSRouter("http://localhost:9324") 7 | 8 | app = FastAPI(lifespan=router.lifespan_context) 9 | 10 | def broker(): 11 | return router.broker 12 | 13 | @router.get("/") 14 | async def hello_http(broker: Annotated[SQSBroker, Depends(broker)]): 15 | await broker.publish("Hello, SQS!", "test") 16 | return "Hello, HTTP!" 17 | 18 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/integrations/sqs/fastapi_plugin_send.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from propan.fastapi import SQSRouter 3 | 4 | router = SQSRouter("http://localhost:9324") 5 | 6 | app = FastAPI(lifespan=router.lifespan_context) 7 | 8 | @router.get("/") 9 | async def hello_http(): 10 | await router.broker.publish("Hello, SQS!", "test") 11 | return "Hello, HTTP!" 12 | 13 | app.include_router(router) -------------------------------------------------------------------------------- /docs/docs_src/nats/direct.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker 2 | from propan.annotations import Logger 3 | 4 | broker = NatsBroker() 5 | app = PropanApp(broker) 6 | 7 | @broker.handle("test-subj-1", "workers") 8 | async def base_handler1(logger: Logger): 9 | logger.info("base_handler1") 10 | 11 | @broker.handle("test-subj-1", "workers") 12 | async def base_handler2(logger: Logger): 13 | logger.info("base_handler2") 14 | 15 | @broker.handle("test-subj-2", "workers") 16 | async def base_handler3(logger: Logger): 17 | logger.info("base_handler3") 18 | 19 | @app.after_startup 20 | async def send_messages(): 21 | await broker.publish("", "test-subj-1") # handlers: 1 or 2 22 | await broker.publish("", "test-subj-1") # handlers: 1 or 2 23 | await broker.publish("", "test-subj-2") # handlers: 3 24 | -------------------------------------------------------------------------------- /docs/docs_src/nats/js.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsJSBroker 2 | from propan.annotations import NatsJS 3 | 4 | broker = NatsJSBroker() 5 | app = PropanApp(broker) 6 | 7 | @app.after_startup 8 | async def example(js: NatsJS): 9 | # JS Key-Value Storage 10 | storage = await js.create_key_value(bucket="propan_kv") 11 | 12 | await storage.put("hello", b"propan!") 13 | assert (await storage.get("hello")) == b"propan!" 14 | 15 | # JS Object Storage 16 | storage = await js.create_object_store("propan-obs") 17 | 18 | obj_name = "file.mp4" 19 | with open(obj_name) as f: 20 | await storage.put(obj_name, f) 21 | 22 | with open(f"copy-{obj_name}") as f: 23 | await storage.get(obj_name, f) 24 | -------------------------------------------------------------------------------- /docs/docs_src/nats/pattern.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker 2 | from propan.annotations import Logger 3 | 4 | broker = NatsBroker() 5 | app = PropanApp(broker) 6 | 7 | @broker.handle("*.info", "workers") 8 | async def base_handler1(logger: Logger): 9 | logger.info("base_handler1") 10 | 11 | @broker.handle("*.info", "workers") 12 | async def base_handler2(logger: Logger): 13 | logger.info("base_handler2") 14 | 15 | @broker.handle("*.error", "workers") 16 | async def base_handler3(logger: Logger): 17 | logger.info("base_handler3") 18 | 19 | @app.after_startup 20 | async def send_messages(): 21 | await broker.publish("", "logs.info") # handlers: 1 or 2 22 | await broker.publish("", "logs.info") # handlers: 1 or 2 23 | await broker.publish("", "logs.error") # handlers: 3 24 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/app/kafka/1_broker.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, KafkaBroker 2 | 3 | broker = KafkaBroker("localhost:9092") 4 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/app/kafka/2_set_broker.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, KafkaBroker 2 | 3 | app = PropanApp() 4 | 5 | @app.on_startup 6 | def init_broker(): 7 | broker = KafkaBroker("localhost:9092") 8 | app.set_broker(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/app/nats/1_broker.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker 2 | 3 | broker = NatsBroker("nats://localhost:4222") 4 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/app/nats/2_set_broker.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker 2 | 3 | app = PropanApp() 4 | 5 | @app.on_startup 6 | def init_broker(): 7 | broker = NatsBroker("nats://localhost:4222") 8 | app.set_broker(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/app/rabbit/1_broker.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker 2 | 3 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 4 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/app/rabbit/2_set_broker.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker 2 | 3 | app = PropanApp() 4 | 5 | @app.on_startup 6 | def init_broker(): 7 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 8 | app.set_broker(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/app/redis/1_broker.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | 3 | broker = RedisBroker("redis://localhost:6379") 4 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/app/redis/2_set_broker.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | 3 | app = PropanApp() 4 | 5 | @app.on_startup 6 | def init_broker(): 7 | broker = RedisBroker("redis://localhost:6379") 8 | app.set_broker(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/app/sqs/1_broker.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, SQSBroker 2 | 3 | broker = SQSBroker("http://localhost:9324", ...) 4 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/app/sqs/2_set_broker.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, SQSBroker 2 | 3 | app = PropanApp() 4 | 5 | @app.on_startup 6 | def init_broker(): 7 | broker = SQSBroker("http://localhost:9324", ...) 8 | app.set_broker(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/publishing/kafka/1_inside_propan.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, KafkaBroker 2 | 3 | broker = KafkaBroker("localhost:9092") 4 | app = PropanApp(broker) 5 | 6 | @broker.handle("test") 7 | async def handle(m: str): 8 | await broker.publish(m, "another-topic") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/publishing/kafka/2_context.py: -------------------------------------------------------------------------------- 1 | async with KafkaBroker("localhost:9092") as broker: 2 | await broker.publish(m, "another-topic") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/publishing/nats/1_inside_propan.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker 2 | 3 | broker = NatsBroker("nats://localhost:4222") 4 | app = PropanApp(broker) 5 | 6 | @broker.handle("test") 7 | async def handle(m: str): 8 | await broker.publish(m, "another-subject") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/publishing/nats/2_context.py: -------------------------------------------------------------------------------- 1 | async with NatsBroker("nats://localhost:4222") as broker: 2 | await broker.publish(m, "another-subject") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/publishing/rabbit/1_inside_propan.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker 2 | 3 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 4 | app = PropanApp(broker) 5 | 6 | @broker.handle("test") 7 | async def handle(m: str): 8 | await broker.publish(m, "another-queue") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/publishing/rabbit/2_context.py: -------------------------------------------------------------------------------- 1 | async with RabbitBroker("amqp://guest:guest@localhost:5672/") as broker: 2 | await broker.publish(m, "another-queue") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/publishing/redis/1_inside_propan.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | 3 | broker = RedisBroker("redis://localhost:6379") 4 | app = PropanApp(broker) 5 | 6 | @broker.handle("test") 7 | async def handle(m: str): 8 | await broker.publish(m, "another-channel") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/publishing/redis/2_context.py: -------------------------------------------------------------------------------- 1 | async with RedisBroker("redis://localhost:6379") as broker: 2 | await broker.publish(m, "another-channel") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/publishing/sqs/1_inside_propan.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, SQSBroker 2 | 3 | broker = SQSBroker("http://localhost:9324", ...) 4 | app = PropanApp(broker) 5 | 6 | @broker.handle("test") 7 | async def handle(m: str): 8 | await broker.publish(m, "another-queue") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/publishing/sqs/2_context.py: -------------------------------------------------------------------------------- 1 | async with SQSBroker("http://localhost:9324", ...) as broker: 2 | await broker.publish(m, "another-queue") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/routers/kafka.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, KafkaBroker, KafkaRouter 2 | 3 | router = KafkaRouter(prefix="user/") 4 | 5 | @router.handle("created") 6 | async def handle_user_created_event(user_id: str): 7 | ... 8 | 9 | broker = KafkaBroker() 10 | broker.include_router(router) 11 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/routers/nats.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker, NatsRouter 2 | 3 | router = NatsRouter(prefix="user/") 4 | 5 | @router.handle("created") 6 | async def handle_user_created_event(user_id: str): 7 | ... 8 | 9 | broker = NatsBroker() 10 | broker.include_router(router) 11 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/routers/publish.py: -------------------------------------------------------------------------------- 1 | @app.after_startup 2 | async def publish_test(): 3 | await broker.publish("user-fake-uuid", "user/created") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/routers/rabbit.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker, RabbitRouter 2 | 3 | router = RabbitRouter(prefix="user/") 4 | 5 | @router.handle("created") 6 | async def handle_user_created_event(user_id: str): 7 | ... 8 | 9 | broker = RabbitBroker() 10 | broker.include_router(router) 11 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/routers/redis.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker, RedisRouter 2 | 3 | router = RedisRouter(prefix="user/") 4 | 5 | @router.handle("created") 6 | async def handle_user_created_event(user_id: str): 7 | ... 8 | 9 | broker = RedisBroker() 10 | broker.include_router(router) 11 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/routers/sqs.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, SQSBroker, SQSRouter 2 | 3 | router = SQSRouter(prefix="user/") 4 | 5 | @router.handle("created") 6 | async def handle_user_created_event(user_id: str): 7 | ... 8 | 9 | broker = SQSBroker() 10 | broker.include_router(router) 11 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/3_blocking_client_timeout.py: -------------------------------------------------------------------------------- 1 | await broker.publish( 2 | "hi!", "ping", 3 | callback=True, 4 | callback_timeout=3.0 # (1) 5 | ) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/4_blocking_client_timeout_none.py: -------------------------------------------------------------------------------- 1 | await broker.publish( 2 | "hi!", "ping", 3 | callback=True, 4 | callback_timeout=None 5 | ) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/5_blocking_client_timeout_error.py: -------------------------------------------------------------------------------- 1 | await broker.publish( 2 | "hi!", "ping", 3 | callback=True, 4 | raise_timeout=True 5 | ) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/nats/1_handler.py: -------------------------------------------------------------------------------- 1 | from propan import NatsBroker 2 | 3 | broker = NatsBroker("nats://localhost:4222") 4 | 5 | @broker.handle("ping") 6 | async def ping(m: str): 7 | return "pong!" # <-- send RPC response 8 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/nats/2_blocking_client.py: -------------------------------------------------------------------------------- 1 | from propan import NatsBroker 2 | 3 | async def main(): 4 | async with NatsBroker("nats://localhost:4222") as broker: 5 | r = await broker.publish( 6 | "hi!", "ping", 7 | callback=True 8 | ) 9 | 10 | assert r == "pong" # <-- take the RPC response -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/nats/6_noblocking_client.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from propan import NatsBroker 3 | 4 | broker = NatsBroker("nats://localhost:4222") 5 | 6 | @broker.handle("reply") 7 | async def get_message(m: str): 8 | assert m == "pong!" # <-- take the RPC response 9 | 10 | async def main(): 11 | await broker.start() 12 | 13 | await broker.publish( 14 | "hello", "ping", 15 | reply_to="reply" 16 | ) 17 | 18 | try: 19 | await asyncio.Future() 20 | finally: 21 | await broker.close() 22 | 23 | asyncio.run(main()) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/rabbit/1_handler.py: -------------------------------------------------------------------------------- 1 | from propan import RabbitBroker 2 | 3 | broker = RabbitBroker("amqp://guest:guest@127.0.0.1/") 4 | 5 | @broker.handle("ping") 6 | async def ping(m: str): 7 | return "pong!" # <-- send RPC response 8 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/rabbit/2_blocking_client.py: -------------------------------------------------------------------------------- 1 | from propan import RabbitBroker 2 | 3 | async def main(): 4 | async with RabbitBroker("amqp://guest:guest@127.0.0.1/") as broker: 5 | r = await broker.publish( 6 | "hi!", "ping", 7 | callback=True 8 | ) 9 | 10 | assert r == "pong" # <-- take the RPC response -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/rabbit/6_noblocking_client.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from propan import RabbitBroker 3 | 4 | broker = RabbitBroker("amqp://guest:guest@127.0.0.1/") 5 | 6 | @broker.handle("reply") 7 | async def get_message(m: str): 8 | assert m == "pong!" # <-- take the RPC response 9 | 10 | async def main(): 11 | await broker.start() 12 | 13 | await broker.publish( 14 | "hello", "ping", 15 | reply_to="reply" 16 | ) 17 | 18 | try: 19 | await asyncio.Future() 20 | finally: 21 | await broker.close() 22 | 23 | asyncio.run(main()) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/redis/1_handler.py: -------------------------------------------------------------------------------- 1 | from propan import RedisBroker 2 | 3 | broker = RedisBroker("redis://localhost:6379") 4 | 5 | @broker.handle("ping") 6 | async def ping(m: str): 7 | return "pong!" # <-- send RPC response 8 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/redis/2_blocking_client.py: -------------------------------------------------------------------------------- 1 | from propan import RedisBroker 2 | 3 | async def main(): 4 | async with RedisBroker("redis://localhost:6379") as broker: 5 | r = await broker.publish( 6 | "hi!", "ping", 7 | callback=True 8 | ) 9 | 10 | assert r == "pong" # <-- take the RPC response -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/rpc/redis/6_noblocking_client.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from propan import RedisBroker 3 | 4 | broker = RedisBroker("redis://localhost:6379") 5 | 6 | @broker.handle("reply") 7 | async def get_message(m: str): 8 | assert m == "pong!" # <-- take the RPC response 9 | 10 | async def main(): 11 | await broker.start() 12 | 13 | await broker.publish( 14 | "hello", "ping", 15 | reply_to="reply" 16 | ) 17 | 18 | try: 19 | await asyncio.Future() 20 | finally: 21 | await broker.close() 22 | 23 | asyncio.run(main()) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/serialization/1_kafka_parse.py: -------------------------------------------------------------------------------- 1 | from aiokafka.structs import ConsumerRecord 2 | from propan import PropanMessage 3 | 4 | async def parse_message( 5 | message: ConsumerRecord 6 | ) -> PropanMessage[ConsumerRecord]: 7 | headers = {i: j.decode() for i, j in message.headers} 8 | return PropanMessage( 9 | body=message.value, 10 | raw_message=message, 11 | message_id=f"{message.offset}-{message.timestamp}", 12 | reply_to=headers.get("reply_to", ""), 13 | content_type=headers.get("content-type"), 14 | headers=headers, 15 | ) 16 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/serialization/1_nats_parse.py: -------------------------------------------------------------------------------- 1 | from nats.aio.msg import Msg 2 | from propan import PropanMessage 3 | 4 | async def parse_message( 5 | message: Msg 6 | ) -> PropanMessage[Msg]: 7 | return PropanMessage( 8 | body=message.data, 9 | content_type=message.header.get("content-type", ""), 10 | headers=message.header, 11 | reply_to=message.reply, 12 | raw_message=message, 13 | ) 14 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/serialization/1_rabbit_parse.py: -------------------------------------------------------------------------------- 1 | from aio_pika.message import IncomingMessage 2 | from propan import PropanMessage 3 | 4 | async def parse_message( 5 | message: IncomingMessage 6 | ) -> PropanMessage[IncomingMessage]: 7 | return PropanMessage( 8 | body=message.body, 9 | headers=message.headers, 10 | reply_to=message.reply_to or "", 11 | message_id=message.message_id, 12 | content_type=message.content_type or "", 13 | raw_message=message, 14 | ) 15 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/serialization/1_redis_parse.py: -------------------------------------------------------------------------------- 1 | from propan import PropanMessage 2 | 3 | async def parse_message( 4 | message: bytes 5 | ) -> PropanMessage[bytes]: 6 | return PropanMessage( 7 | body=message, 8 | raw_message=message, 9 | ) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/serialization/1_sqs_parse.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any 2 | from propan import PropanMessage 3 | 4 | async def parse_message( 5 | message: Dict[str, Any], 6 | ) -> PropanMessage[Dict[str, Any]]: 7 | attributes = message.get("MessageAttributes", {}) 8 | headers = {i: j.get("StringValue") for i, j in attributes.items()} 9 | return PropanMessage( 10 | body=message.get("Body", "").encode(), 11 | message_id=message.get("MessageId"), 12 | content_type=headers.pop("content-type", None), 13 | reply_to=headers.pop("reply_to", None) or "", 14 | headers=headers, 15 | raw_message=message, 16 | ) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/serialization/2_parse_redefine.py: -------------------------------------------------------------------------------- 1 | from propan import RabbitBroker 2 | 3 | async def custom_parse(msg, original_parser): 4 | return original_parser(msg) 5 | 6 | broker = RabbitBroker(parse_message=custom_parse) 7 | 8 | @broker.handle("test", parse_message=custom_parse) 9 | async def handler(): ... -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/serialization/3_decode.py: -------------------------------------------------------------------------------- 1 | import json 2 | from propan import PropanMessage 3 | 4 | async def decode_message(message: PropanMessage): 5 | body = message.body 6 | if message.content_type is not None: 7 | return json.loads(body.decode()) 8 | else: 9 | return body -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/serialization/4_decode_redefine.py: -------------------------------------------------------------------------------- 1 | from propan import RabbitBroker 2 | 3 | async def custom_decode(msg, original_decoded): 4 | return original_decoded(msg) 5 | 6 | broker = RabbitBroker(decode_message=custom_decode) 7 | 8 | @broker.handle("test", decode_message=custom_decode) 9 | async def handler(): ... -------------------------------------------------------------------------------- /docs/docs_src/quickstart/broker/serialization/5_protobuf.py: -------------------------------------------------------------------------------- 1 | from message_pb2 import Person 2 | 3 | from propan import PropanApp, RabbitBroker 4 | from propan.annotations import Logger, NoCast 5 | from propan.brokers.rabbit import RabbitMessage 6 | 7 | broker = RabbitBroker() 8 | app = PropanApp(broker) 9 | 10 | async def decode_message(msg: RabbitMessage, original) -> Person: 11 | decoded = Person() 12 | decoded.ParseFromString(msg.body) 13 | return decoded 14 | 15 | @broker.handle("test", decode_message=decode_message) 16 | async def consume(body: NoCast[Person], logger: Logger): 17 | logger.info(body) 18 | 19 | @app.after_startup 20 | async def publish(): 21 | body = Person(name="john", age=25).SerializeToString() 22 | await broker.publish(body, "test") 23 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/cli/01_kafka_context.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, KafkaBroker 2 | from propan.annotations import ContextRepo 3 | from pydantic_settings import BaseSettings 4 | 5 | broker = KafkaBroker() 6 | 7 | app = PropanApp(broker) 8 | 9 | class Settings(BaseSettings): 10 | host: str = "localhost:9092" 11 | 12 | @app.on_startup 13 | async def setup(env: str, context: ContextRepo): 14 | settings = Settings(_env_file=env) 15 | await broker.connect(settings.host) 16 | context.set_global("settings", settings) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/cli/01_nats_context.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker 2 | from propan.annotations import ContextRepo 3 | from pydantic_settings import BaseSettings 4 | 5 | broker = NatsBroker() 6 | 7 | app = PropanApp(broker) 8 | 9 | class Settings(BaseSettings): 10 | host: str = "nats://localhost:4222" 11 | 12 | @app.on_startup 13 | async def setup(env: str, context: ContextRepo): 14 | settings = Settings(_env_file=env) 15 | await broker.connect(settings.host) 16 | context.set_global("settings", settings) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/cli/01_rabbit_context.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker 2 | from propan.annotations import ContextRepo 3 | from pydantic_settings import BaseSettings 4 | 5 | broker = RabbitBroker() 6 | 7 | app = PropanApp(broker) 8 | 9 | class Settings(BaseSettings): 10 | host: str = "amqp://guest:guest@localhost:5672/" 11 | 12 | @app.on_startup 13 | async def setup(env: str, context: ContextRepo): 14 | settings = Settings(_env_file=env) 15 | await broker.connect(settings.host) 16 | context.set_global("settings", settings) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/cli/01_redis_context.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | from propan.annotations import ContextRepo 3 | from pydantic_settings import BaseSettings 4 | 5 | broker = RedisBroker() 6 | 7 | app = PropanApp(broker) 8 | 9 | class Settings(BaseSettings): 10 | host: str = "redis://localhost:6379" 11 | 12 | @app.on_startup 13 | async def setup(env: str, context: ContextRepo): 14 | settings = Settings(_env_file=env) 15 | await broker.connect(settings.host) 16 | context.set_global("settings", settings) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/cli/01_sqs_context.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, SQSBroker 2 | from propan.annotations import ContextRepo 3 | from pydantic_settings import BaseSettings 4 | 5 | broker = SQSBroker() 6 | 7 | app = PropanApp(broker) 8 | 9 | class Settings(BaseSettings): 10 | host: str = "http://localhost:9324" 11 | 12 | @app.on_startup 13 | async def setup(env: str, context: ContextRepo): 14 | settings = Settings(_env_file=env) 15 | await broker.connect(settings.host) 16 | context.set_global("settings", settings) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/3_async.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from propan import Depends, apply_types 3 | 4 | async def simple_dependency(a: int, b: int = 3): 5 | return a + b 6 | 7 | def another_dependency(a: int): 8 | return a 9 | 10 | @apply_types 11 | async def method( 12 | a: int, 13 | b: int = Depends(simple_dependency), 14 | c: int = Depends(another_dependency), 15 | ): 16 | return a + b + c 17 | 18 | assert asyncio.run(method("1")) == 6 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/3_sync.py: -------------------------------------------------------------------------------- 1 | from propan import Depends, apply_types 2 | 3 | def simple_dependency(a: int, b: int = 3): 4 | return a + b 5 | 6 | @apply_types 7 | def method(a: int, d: int = Depends(simple_dependency)): 8 | return a + d 9 | 10 | assert method("1") == 5 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/kafka/1_depends.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, KafkaBroker, Depends 2 | 3 | broker = KafkaBroker("localhost:9092") 4 | app = PropanApp(broker) 5 | 6 | def simple_dependency(): 7 | return 1 8 | 9 | @broker.handle("test") 10 | async def handler(body: dict, d: int = Depends(simple_dependency)): 11 | assert d == 1 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/kafka/2_nested_depends.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, KafkaBroker, Depends 2 | 3 | broker = KafkaBroker("localhost:9092") 4 | app = PropanApp(broker) 5 | 6 | def another_dependency(): 7 | return 1 8 | 9 | def simple_dependency(b: int = Depends(another_dependency)): # (1) 10 | return b * 2 11 | 12 | @broker.handle("test") 13 | async def handler( 14 | body: dict, 15 | a: int = Depends(another_dependency), 16 | b: int = Depends(simple_dependency)): 17 | assert (a + b) == 3 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/nats/1_depends.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker, Depends 2 | 3 | broker = NatsBroker("nats://localhost:4222") 4 | app = PropanApp(broker) 5 | 6 | def simple_dependency(): 7 | return 1 8 | 9 | @broker.handle("test") 10 | async def handler(body: dict, d: int = Depends(simple_dependency)): 11 | assert d == 1 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/nats/2_nested_depends.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker, Depends 2 | 3 | broker = NatsBroker("nats://localhost:4222") 4 | app = PropanApp(broker) 5 | 6 | def another_dependency(): 7 | return 1 8 | 9 | def simple_dependency(b: int = Depends(another_dependency)): # (1) 10 | return b * 2 11 | 12 | @broker.handle("test") 13 | async def handler( 14 | body: dict, 15 | a: int = Depends(another_dependency), 16 | b: int = Depends(simple_dependency)): 17 | assert (a + b) == 3 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/rabbit/1_depends.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker, Depends 2 | 3 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 4 | app = PropanApp(broker) 5 | 6 | def simple_dependency(): 7 | return 1 8 | 9 | @broker.handle("test") 10 | async def handler(body: dict, d: int = Depends(simple_dependency)): 11 | assert d == 1 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/rabbit/2_nested_depends.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker, Depends 2 | 3 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 4 | app = PropanApp(broker) 5 | 6 | def another_dependency(): 7 | return 1 8 | 9 | def simple_dependency(b: int = Depends(another_dependency)): # (1) 10 | return b * 2 11 | 12 | @broker.handle("test") 13 | async def handler( 14 | body: dict, 15 | a: int = Depends(another_dependency), 16 | b: int = Depends(simple_dependency)): 17 | assert (a + b) == 3 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/redis/1_depends.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker, Depends 2 | 3 | broker = RedisBroker("redis://localhost:6379") 4 | app = PropanApp(broker) 5 | 6 | def simple_dependency(): 7 | return 1 8 | 9 | @broker.handle("test") 10 | async def handler(body: dict, d: int = Depends(simple_dependency)): 11 | assert d == 1 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/redis/2_nested_depends.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker, Depends 2 | 3 | broker = RedisBroker("redis://localhost:6379") 4 | app = PropanApp(broker) 5 | 6 | def another_dependency(): 7 | return 1 8 | 9 | def simple_dependency(b: int = Depends(another_dependency)): # (1) 10 | return b * 2 11 | 12 | @broker.handle("test") 13 | async def handler( 14 | body: dict, 15 | a: int = Depends(another_dependency), 16 | b: int = Depends(simple_dependency)): 17 | assert (a + b) == 3 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/sqs/1_depends.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, SQSBroker, Depends 2 | 3 | broker = SQSBroker("http://localhost:9324", ...) 4 | app = PropanApp(broker) 5 | 6 | def simple_dependency(): 7 | return 1 8 | 9 | @broker.handle("test") 10 | async def handler(body: dict, d: int = Depends(simple_dependency)): 11 | assert d == 1 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/basic/sqs/2_nested_depends.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, SQSBroker, Depends 2 | 3 | broker = SQSBroker("http://localhost:9324", ...) 4 | app = PropanApp(broker) 5 | 6 | def another_dependency(): 7 | return 1 8 | 9 | def simple_dependency(b: int = Depends(another_dependency)): # (1) 10 | return b * 2 11 | 12 | @broker.handle("test") 13 | async def handler( 14 | body: dict, 15 | a: int = Depends(another_dependency), 16 | b: int = Depends(simple_dependency)): 17 | assert (a + b) == 3 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/10_context.py: -------------------------------------------------------------------------------- 1 | from propan import apply_types, Context 2 | from propan.annotations import ContextRepo 3 | 4 | @broker.hanlde("test") 5 | async def handler( 6 | body: dict, 7 | context: ContextRepo 8 | ): 9 | token = context.set_local("local", 1): 10 | nested_function() 11 | context.reset_local("local", token) 12 | 13 | @apply_types 14 | def nested_function(local = Context()): 15 | assert local == 1 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/11_context.py: -------------------------------------------------------------------------------- 1 | from propan import Context, Depends 2 | 3 | def nested_func( 4 | body: dict, 5 | logger = Context() 6 | ): 7 | logger.info(body) 8 | return body 9 | 10 | @broker.hanlde("test") 11 | async def handler(body: dict, n = Depends(nested_func)): 12 | pass -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/12_context.py: -------------------------------------------------------------------------------- 1 | from propan import apply_types, Context 2 | 3 | @broker.hanlde("test") 4 | async def handler(body: dict): 5 | nested_func() 6 | 7 | @apply_types 8 | def nested_func( 9 | body: dict, 10 | logger = Context() 11 | ): 12 | logger.info(body) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/1_context.py: -------------------------------------------------------------------------------- 1 | from propan import Context 2 | 3 | @broker.hanlde("test") 4 | async def handler(broker = Context()): 5 | await broker.publish("response", "response-queue") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/2_context.py: -------------------------------------------------------------------------------- 1 | from propan import Context 2 | 3 | @broker.hanlde("test") 4 | async def handler( 5 | body: dict, 6 | app = Context(), 7 | broker = Context(), 8 | context = Context(), 9 | logger = Context(), 10 | message = Context(), 11 | ): 12 | ... -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/3_context.py: -------------------------------------------------------------------------------- 1 | from propan import Context 2 | 3 | @broker.hanlde("test") 4 | async def handler( 5 | body: dict, 6 | propan_app = Context("app"), 7 | publish = Context("broker.publish"), 8 | secret_key = Context("settings.app.secret_key"), 9 | ): 10 | await publish(secret_key, "secret-queue") -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/4_context.py: -------------------------------------------------------------------------------- 1 | from propan import Context, RabbitBroker 2 | from typing_extension import Annotated 3 | 4 | Broker = Annotated[RabbitBroker, Context("broker")] 5 | 6 | @broker.hanlde("test") 7 | async def handler( 8 | body: dict, 9 | broker: Broker, 10 | ): 11 | ... -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/5_context.py: -------------------------------------------------------------------------------- 1 | from propan import annotations 2 | 3 | @rabbit_broker.handle("test") 4 | async def base_handler( 5 | body: dict, 6 | app: annotations.App, 7 | context: annotations.ContextRepo, 8 | logger: annotations.Logger, 9 | broker: annotations.RabbitBroker, 10 | message: annotations.RabbitMessage, 11 | ): 12 | ... 13 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/6_context.py: -------------------------------------------------------------------------------- 1 | from propan import Context 2 | 3 | @broker.hanlde("test") 4 | async def handler( 5 | body: dict, 6 | some_field = Context(default=None) 7 | ): 8 | assert some_field is None -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/7_context.py: -------------------------------------------------------------------------------- 1 | from propan import Context 2 | 3 | @broker.hanlde("test") 4 | async def handler( 5 | body: dict, 6 | some_field: int = Context(default="1", cast=True) 7 | ): 8 | assert some_field == 1 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/8_context.py: -------------------------------------------------------------------------------- 1 | from propan.annotations import ContextRepo 2 | 3 | @broker.hanlde("test") 4 | async def handler( 5 | body: dict, 6 | context: ContextRepo 7 | ): 8 | context.set_global("my_key", 1) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/dependencies/context/9_context.py: -------------------------------------------------------------------------------- 1 | from propan import apply_types, Context 2 | from propan.annotations import ContextRepo 3 | 4 | @broker.hanlde("test") 5 | async def handler( 6 | body: dict, 7 | context: ContextRepo 8 | ): 9 | with context.scope("local", 1): 10 | nested_function() 11 | 12 | @apply_types 13 | def nested_function(local = Context()): 14 | assert local == 1 -------------------------------------------------------------------------------- /docs/docs_src/quickstart/documentation/custom_schema.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker 2 | from propan.asyncapi.main import AsyncAPISchema 3 | from propan.cli.docs.gen import gen_app_schema_json, gen_app_schema_yaml, get_app_schema 4 | from propan.cli.docs.serving import get_asyncapi_html 5 | 6 | broker = RabbitBroker() 7 | app = PropanApp(broker) 8 | 9 | schema: AsyncAPISchema = get_app_schema(app) 10 | json_schema = gen_app_schema_json(app) 11 | yaml_schema = gen_app_schema_yaml(app) 12 | html = get_asyncapi_html(yaml_schema) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/documentation/example.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker 2 | from propan.brokers.rabbit import RabbitQueue, RabbitExchange, ExchangeType 3 | 4 | broker = RabbitBroker() 5 | app = PropanApp( 6 | broker=broker, 7 | title="Smartylighting Streetlights Propan API", 8 | version="1.0.0", 9 | description=""" 10 | The Smartylighting Streetlights API. 11 | ### Check out its awesome features: 12 | * Turn a specific streetlight on/off 🌃 13 | * Receive real-time information about environmental 📈 14 | """ 15 | ) 16 | 17 | @broker.handle( 18 | queue=RabbitQueue("*.info", durable=True), 19 | exchange=RabbitExchange("logs", durable=True, type=ExchangeType.TOPIC) 20 | ) 21 | async def handle_logs(level: int, message: str = ""): 22 | """Handle all environmental events""" 23 | ... 24 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/documentation/fastapi.py: -------------------------------------------------------------------------------- 1 | from propan.fastapi import RabbitRouter 2 | 3 | router = RabbitRouter( 4 | schema_url="/asyncapi", 5 | include_in_schema=True, 6 | ) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/lifespan/3_multiple.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, Context 2 | from propan.annotations import ContextRepo 3 | 4 | app = PropanApp() 5 | 6 | @app.on_startup 7 | async def setup(context: ContextRepo): 8 | context.set_global("field", 1) 9 | 10 | @app.on_startup 11 | async def setup_later(field: int = Context()): 12 | assert field == 1 13 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/lifespan/kafka/1.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, KafkaBroker 2 | from propan.annotations import ContextRepo 3 | from pydantic_settings import BaseSettings 4 | 5 | broker = KafkaBroker() 6 | app = PropanApp(broker) 7 | 8 | class Settings(BaseSettings): 9 | host: str = "localhost:9092" 10 | 11 | @app.on_startup 12 | async def setup(context: ContextRepo, env: str = ".env"): 13 | settings = Settings(_env_file=env) 14 | context.set_global("settings", settings) 15 | await broker.connect(settings.host) 16 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/lifespan/kafka/2_ml.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, Context, KafkaBroker 2 | from propan.annotations import ContextRepo 3 | 4 | broker = KafkaBroker("localhost:9092") 5 | app = PropanApp(broker) 6 | 7 | ml_models = {} # fake ML model 8 | 9 | def fake_answer_to_everything_ml_model(x: float): 10 | return x * 42 11 | 12 | @app.on_startup 13 | async def setup_model(context: ContextRepo): 14 | # Load the ML model 15 | ml_models["answer_to_everything"] = fake_answer_to_everything_ml_model 16 | context.set_global("model", ml_models) 17 | 18 | @app.on_shutdown 19 | async def shutdown_model(model: dict = Context()): 20 | # Clean up the ML models and release the resources 21 | model.clear() 22 | 23 | @broker.handle("test") 24 | async def predict(x: float, model = Context()): 25 | result = model["answer_to_everything"](x) 26 | return {"result": result} -------------------------------------------------------------------------------- /docs/docs_src/quickstart/lifespan/nats/1.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker 2 | from propan.annotations import ContextRepo 3 | from pydantic_settings import BaseSettings 4 | 5 | broker = NatsBroker() 6 | app = PropanApp(broker) 7 | 8 | class Settings(BaseSettings): 9 | host: str = "nats://localhost:4222" 10 | 11 | @app.on_startup 12 | async def setup(context: ContextRepo, env: str = ".env"): 13 | settings = Settings(_env_file=env) 14 | context.set_global("settings", settings) 15 | await broker.connect(settings.host) 16 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/lifespan/nats/2_ml.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, Context, NatsBroker 2 | from propan.annotations import ContextRepo 3 | 4 | broker = NatsBroker("nats://localhost:4222") 5 | app = PropanApp(broker) 6 | 7 | ml_models = {} # fake ML model 8 | 9 | def fake_answer_to_everything_ml_model(x: float): 10 | return x * 42 11 | 12 | @app.on_startup 13 | async def setup_model(context: ContextRepo): 14 | # Load the ML model 15 | ml_models["answer_to_everything"] = fake_answer_to_everything_ml_model 16 | context.set_global("model", ml_models) 17 | 18 | @app.on_shutdown 19 | async def shutdown_model(model: dict = Context()): 20 | # Clean up the ML models and release the resources 21 | model.clear() 22 | 23 | @broker.handle("test") 24 | async def predict(x: float, model = Context()): 25 | result = model["answer_to_everything"](x) 26 | return {"result": result} -------------------------------------------------------------------------------- /docs/docs_src/quickstart/lifespan/rabbit/1.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker 2 | from propan.annotations import ContextRepo 3 | from pydantic_settings import BaseSettings 4 | 5 | broker = RabbitBroker() 6 | app = PropanApp(broker) 7 | 8 | class Settings(BaseSettings): 9 | host: str = "amqp://guest:guest@localhost:5672/" 10 | 11 | @app.on_startup 12 | async def setup(context: ContextRepo, env: str = ".env"): 13 | settings = Settings(_env_file=env) 14 | context.set_global("settings", settings) 15 | await broker.connect(settings.host) 16 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/lifespan/rabbit/2_ml.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, Context, RabbitBroker 2 | from propan.annotations import ContextRepo 3 | 4 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 5 | app = PropanApp(broker) 6 | 7 | ml_models = {} # fake ML model 8 | 9 | def fake_answer_to_everything_ml_model(x: float): 10 | return x * 42 11 | 12 | @app.on_startup 13 | async def setup_model(context: ContextRepo): 14 | # Load the ML model 15 | ml_models["answer_to_everything"] = fake_answer_to_everything_ml_model 16 | context.set_global("model", ml_models) 17 | 18 | @app.on_shutdown 19 | async def shutdown_model(model: dict = Context()): 20 | # Clean up the ML models and release the resources 21 | model.clear() 22 | 23 | @broker.handle("test") 24 | async def predict(x: float, model = Context()): 25 | result = model["answer_to_everything"](x) 26 | return {"result": result} -------------------------------------------------------------------------------- /docs/docs_src/quickstart/lifespan/redis/1.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, SQSBroker 2 | from propan.annotations import ContextRepo 3 | from pydantic_settings import BaseSettings 4 | 5 | broker = SQSBroker() 6 | app = PropanApp(broker) 7 | 8 | class Settings(BaseSettings): 9 | host: str = "http://localhost:9324" 10 | 11 | @app.on_startup 12 | async def setup(context: ContextRepo, env: str = ".env"): 13 | settings = Settings(_env_file=env) 14 | context.set_global("settings", settings) 15 | await broker.connect(settings.host) 16 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/lifespan/redis/2_ml.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, Context, RedisBroker 2 | from propan.annotations import ContextRepo 3 | 4 | broker = RedisBroker("redis://localhost:6379") 5 | app = PropanApp(broker) 6 | 7 | ml_models = {} # fake ML model 8 | 9 | def fake_answer_to_everything_ml_model(x: float): 10 | return x * 42 11 | 12 | @app.on_startup 13 | async def setup_model(context: ContextRepo): 14 | # Load the ML model 15 | ml_models["answer_to_everything"] = fake_answer_to_everything_ml_model 16 | context.set_global("model", ml_models) 17 | 18 | @app.on_shutdown 19 | async def shutdown_model(model: dict = Context()): 20 | # Clean up the ML models and release the resources 21 | model.clear() 22 | 23 | @broker.handle("test") 24 | async def predict(x: float, model = Context()): 25 | result = model["answer_to_everything"](x) 26 | return {"result": result} -------------------------------------------------------------------------------- /docs/docs_src/quickstart/lifespan/sqs/1.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | from propan.annotations import ContextRepo 3 | from pydantic_settings import BaseSettings 4 | 5 | broker = RedisBroker() 6 | app = PropanApp(broker) 7 | 8 | class Settings(BaseSettings): 9 | host: str = "redis://localhost:6379" 10 | 11 | @app.on_startup 12 | async def setup(context: ContextRepo, env: str = ".env"): 13 | settings = Settings(_env_file=env) 14 | context.set_global("settings", settings) 15 | await broker.connect(settings.host) 16 | -------------------------------------------------------------------------------- /docs/docs_src/quickstart/lifespan/sqs/2_ml.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, Context, SQSBroker 2 | from propan.annotations import ContextRepo 3 | 4 | broker = SQSBroker("http://localhost:9324", ...) 5 | app = PropanApp(broker) 6 | 7 | ml_models = {} # fake ML model 8 | 9 | def fake_answer_to_everything_ml_model(x: float): 10 | return x * 42 11 | 12 | @app.on_startup 13 | async def setup_model(context: ContextRepo): 14 | # Load the ML model 15 | ml_models["answer_to_everything"] = fake_answer_to_everything_ml_model 16 | context.set_global("model", ml_models) 17 | 18 | @app.on_shutdown 19 | async def shutdown_model(model: dict = Context()): 20 | # Clean up the ML models and release the resources 21 | model.clear() 22 | 23 | @broker.handle("test") 24 | async def predict(x: float, model = Context()): 25 | result = model["answer_to_everything"](x) 26 | return {"result": result} -------------------------------------------------------------------------------- /docs/docs_src/quickstart/settings/settings_base_1.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseSettings 2 | 3 | class Settings(BaseSettings): 4 | url: str = "" 5 | queue: str = "test-queue" 6 | 7 | settings = Settings() -------------------------------------------------------------------------------- /docs/docs_src/quickstart/settings/settings_base_2.py: -------------------------------------------------------------------------------- 1 | from pydantic_settings import BaseSettings 2 | 3 | class Settings(BaseSettings): 4 | url: str = "" 5 | queue: str = "test-queue" 6 | 7 | settings = Settings() -------------------------------------------------------------------------------- /docs/docs_src/quickstart/settings/settings_env.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | class Settings(BaseSettings): 4 | url: str 5 | queue: str = "test-queue" 6 | 7 | settings = Settings(_env_file=os.getenv("ENV", ".env")) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/settings/usage.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp 2 | from propan.rabbit import RabbitBroker 3 | 4 | from config import setting 5 | 6 | broker = RabbitBroker(settings.url) 7 | app = PropanApp(broker) 8 | 9 | @broker.handle(settings.queue) 10 | async def handler(msg): 11 | ... -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/4_suppressed_exc.py: -------------------------------------------------------------------------------- 1 | async def test_publish(test_broker): 2 | r = await test_broker.publish( 3 | {"msg": "ping"}, "ping", 4 | callback=True, callback_timeout=1 5 | ) 6 | assert r == None -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/kafka/1_main.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, KafkaBroker 2 | 3 | broker = KafkaBroker() 4 | 5 | @broker.handler("ping") 6 | async def healthcheck(msg: str) -> str: 7 | if msg == "ping": 8 | return "pong" 9 | else: 10 | return "wrong" 11 | 12 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/kafka/2_test.py: -------------------------------------------------------------------------------- 1 | from propan.test import TestKafkaBroker 2 | 3 | from main import broker 4 | 5 | async def test_publish(): 6 | async with TestKafkaBroker(broker) as test_broker: 7 | r = await test_broker.publish("ping", "ping", callback=True) 8 | assert r == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/kafka/3_conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from propan.test import TestKafkaBroker 3 | 4 | from main import broker 5 | 6 | @pytest.fixture() 7 | async def test_broker(): 8 | async with TestKafkaBroker(broker) as b: 9 | yield b 10 | 11 | async def test_publish(test_broker): 12 | r = await test_broker.publish("ping", "ping", callback=True) 13 | assert r == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/kafka/5_build_message.py: -------------------------------------------------------------------------------- 1 | from propan.test.kafka import build_message 2 | 3 | from main import healthcheck 4 | 5 | async def test_publish(test_broker): 6 | msg = build_message("ping", "ping") 7 | assert (await healthcheck(msg)) == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/kafka/6_reraise.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import pydantic 3 | from propan.test.kafka import build_message 4 | 5 | from main import healthcheck 6 | 7 | async def test_publish(test_broker): 8 | msg = build_message({ "msg": "ping" }, "ping") 9 | with pytest.raises(pydantic.ValidationError): 10 | await healthcheck(msg, reraise_exc=True) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/nats/1_main.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, NatsBroker 2 | 3 | broker = NatsBroker() 4 | 5 | @broker.handler("ping") 6 | async def healthcheck(msg: str) -> str: 7 | if msg == "ping": 8 | return "pong" 9 | else: 10 | return "wrong" 11 | 12 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/nats/2_test.py: -------------------------------------------------------------------------------- 1 | from propan.test import TestNatsBroker 2 | 3 | from main import broker 4 | 5 | async def test_publish(): 6 | async with TestNatsBroker(broker) as test_broker: 7 | r = await test_broker.publish("ping", "ping", callback=True) 8 | assert r == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/nats/3_conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from propan.test import TestNatsBroker 3 | 4 | from main import broker 5 | 6 | @pytest.fixture() 7 | async def test_broker(): 8 | async with TestNatsBroker(broker) as b: 9 | yield b 10 | 11 | async def test_publish(test_broker): 12 | r = await test_broker.publish("ping", "ping", callback=True) 13 | assert r == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/nats/5_build_message.py: -------------------------------------------------------------------------------- 1 | from propan.test.nats import build_message 2 | 3 | from main import healthcheck 4 | 5 | async def test_publish(test_broker): 6 | msg = build_message("ping", "ping") 7 | assert (await healthcheck(msg)) == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/nats/6_reraise.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import pydantic 3 | from propan.test.nats import build_message 4 | 5 | from main import healthcheck 6 | 7 | async def test_publish(test_broker): 8 | msg = build_message({ "msg": "ping" }, "ping") 9 | with pytest.raises(pydantic.ValidationError): 10 | await healthcheck(msg, reraise_exc=True) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/rabbit/1_main.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker 2 | 3 | broker = RabbitBroker() 4 | 5 | @broker.handler("ping") 6 | async def healthcheck(msg: str) -> str: 7 | if msg == "ping": 8 | return "pong" 9 | else: 10 | return "wrong" 11 | 12 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/rabbit/2_test.py: -------------------------------------------------------------------------------- 1 | from propan.test import TestRabbitBroker 2 | 3 | from main import broker 4 | 5 | async def test_publish(): 6 | async with TestRabbitBroker(broker) as test_broker: 7 | r = await test_broker.publish("ping", "ping", callback=True) 8 | assert r == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/rabbit/3_conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from propan.test import TestRabbitBroker 3 | 4 | from main import broker 5 | 6 | @pytest.fixture() 7 | async def test_broker(): 8 | async with TestRabbitBroker(broker) as b: 9 | yield b 10 | 11 | async def test_publish(test_broker): 12 | r = await test_broker.publish("ping", "ping", callback=True) 13 | assert r == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/rabbit/5_build_message.py: -------------------------------------------------------------------------------- 1 | from propan.test.rabbit import build_message 2 | 3 | from main import healthcheck 4 | 5 | async def test_publish(test_broker): 6 | msg = build_message("ping", "ping") 7 | assert (await healthcheck(msg)) == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/rabbit/6_reraise.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import pydantic 3 | from propan.test.rabbit import build_message 4 | 5 | from main import healthcheck 6 | 7 | async def test_publish(test_broker): 8 | msg = build_message({ "msg": "ping" }, "ping") 9 | with pytest.raises(pydantic.ValidationError): 10 | await healthcheck(msg, reraise_exc=True) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/redis/1_main.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | 3 | broker = RedisBroker() 4 | 5 | @broker.handler("ping") 6 | async def healthcheck(msg: str) -> str: 7 | if msg == "ping": 8 | return "pong" 9 | else: 10 | return "wrong" 11 | 12 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/redis/2_test.py: -------------------------------------------------------------------------------- 1 | from propan.test import TestRedisBroker 2 | 3 | from main import broker 4 | 5 | async def test_publish(): 6 | async with TestRedisBroker(broker) as test_broker: 7 | r = await test_broker.publish("ping", "ping", callback=True) 8 | assert r == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/redis/3_conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from propan.test import TestRedisBroker 3 | 4 | from main import broker 5 | 6 | @pytest.fixture() 7 | async def test_broker(): 8 | async with TestRedisBroker(broker) as b: 9 | yield b 10 | 11 | async def test_publish(test_broker): 12 | r = await test_broker.publish("ping", "ping", callback=True) 13 | assert r == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/redis/5_build_message.py: -------------------------------------------------------------------------------- 1 | from propan.test.redis import build_message 2 | 3 | from main import healthcheck 4 | 5 | async def test_publish(test_broker): 6 | msg = build_message("ping", "ping") 7 | assert (await healthcheck(msg)) == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/redis/6_reraise.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import pydantic 3 | from propan.test.redis import build_message 4 | 5 | from main import healthcheck 6 | 7 | async def test_publish(test_broker): 8 | msg = build_message({ "msg": "ping" }, "ping") 9 | with pytest.raises(pydantic.ValidationError): 10 | await healthcheck(msg, reraise_exc=True) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/sqs/1_main.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, SQSBroker 2 | 3 | broker = SQSBroker() 4 | 5 | @broker.handler("ping") 6 | async def healthcheck(msg: str) -> str: 7 | if msg == "ping": 8 | return "pong" 9 | else: 10 | return "wrong" 11 | 12 | app = PropanApp(broker) -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/sqs/2_test.py: -------------------------------------------------------------------------------- 1 | from propan.test import TestSQSBroker 2 | 3 | from main import broker 4 | 5 | async def test_publish(): 6 | async with TestSQSBroker(broker) as test_broker: 7 | r = await test_broker.publish("ping", "ping", callback=True) 8 | assert r == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/sqs/3_conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from propan.test import TestSQSBroker 3 | 4 | from main import broker 5 | 6 | @pytest.fixture() 7 | def test_broker(): 8 | async with TestSQSBroker(broker) as b: 9 | yield b 10 | 11 | async def test_publish(test_broker): 12 | r = await test_broker.publish("ping", "ping", callback=True) 13 | assert r == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/sqs/5_build_message.py: -------------------------------------------------------------------------------- 1 | from propan.test.sqs import build_message 2 | 3 | from main import healthcheck 4 | 5 | async def test_publish(test_broker): 6 | msg = build_message("ping", "ping") 7 | assert (await healthcheck(msg)) == "pong" -------------------------------------------------------------------------------- /docs/docs_src/quickstart/testing/sqs/6_reraise.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import pydantic 3 | from propan.test.sqs import build_message 4 | 5 | from main import healthcheck 6 | 7 | async def test_publish(test_broker): 8 | msg = build_message({ "msg": "ping" }, "ping") 9 | with pytest.raises(pydantic.ValidationError): 10 | await healthcheck(msg, reraise_exc=True) -------------------------------------------------------------------------------- /docs/docs_src/redis/direct.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | from propan.annotations import Logger 3 | 4 | broker = RedisBroker() 5 | app = PropanApp(broker) 6 | 7 | 8 | @broker.handle("test") 9 | async def handler1(logger: Logger): 10 | logger.info("handler1") 11 | 12 | 13 | @broker.handle("test") 14 | async def handler2(logger: Logger): 15 | logger.info("handler2") 16 | 17 | 18 | @broker.handle("test2") 19 | async def handler3(logger: Logger): 20 | logger.info("handler3") 21 | 22 | 23 | @app.after_startup 24 | async def publish_smth(): 25 | await broker.publish("", "test") # handlers: 1, 2 26 | await broker.publish("", "test2") # handlers: 3 27 | -------------------------------------------------------------------------------- /docs/docs_src/redis/pattern.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | from propan.annotations import Logger 3 | 4 | broker = RedisBroker() 5 | app = PropanApp(broker) 6 | 7 | 8 | @broker.handle("*.info", pattern=True) 9 | async def handler1(b: str, logger: Logger): 10 | logger.info("handler1") 11 | 12 | 13 | @broker.handle("*.info", pattern=True) 14 | async def handler2(b: str, logger: Logger): 15 | logger.info("handler2") 16 | 17 | 18 | @broker.handle("*.error", pattern=True) 19 | async def handler3(logger: Logger): 20 | logger.info("handler3") 21 | 22 | 23 | @app.after_startup 24 | async def publish_smth(): 25 | await broker.publish("", "logs.info") # handlers: 1, 2 26 | await broker.publish("", "logs.error") # handlers: 3 27 | -------------------------------------------------------------------------------- /docs/includes/getting_started/app/1_broker.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python 3 | {!> docs_src/quickstart/app/redis/1_broker.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python 8 | {!> docs_src/quickstart/app/rabbit/1_broker.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python 13 | {!> docs_src/quickstart/app/kafka/1_broker.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python 18 | {!> docs_src/quickstart/app/sqs/1_broker.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python 23 | {!> docs_src/quickstart/app/nats/1_broker.py!} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/includes/getting_started/app/2_set_broker.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python 3 | {!> docs_src/quickstart/app/redis/2_set_broker.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python 8 | {!> docs_src/quickstart/app/rabbit/2_set_broker.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python 13 | {!> docs_src/quickstart/app/kafka/2_set_broker.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python 18 | {!> docs_src/quickstart/app/sqs/2_set_broker.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python 23 | {!> docs_src/quickstart/app/nats/2_set_broker.py!} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/broker/index/imports.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python 3 | from propan import RedisBroker 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python 8 | from propan import RabbitBroker 9 | ``` 10 | 11 | === "Kafka" 12 | ```python 13 | from propan import KafkaBroker 14 | ``` 15 | 16 | === "SQS" 17 | ```python 18 | from propan import SQSBroker 19 | ``` 20 | 21 | === "NATS" 22 | ```python 23 | from propan import NatsBroker 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/broker/index/install.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```bash 3 | pip install "propan[async-redis]" 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```bash 8 | pip install "propan[async-rabbit]" 9 | ``` 10 | 11 | === "Kafka" 12 | ```bash 13 | pip install "propan[async-kafka]" 14 | ``` 15 | 16 | === "SQS" 17 | ```bash 18 | pip install "propan[async-sqs]" 19 | ``` 20 | 21 | === "NATS" 22 | ```bash 23 | pip install "propan[async-nats]" 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/broker/publishing/1_inside_propan.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="8" 3 | {!> docs_src/quickstart/broker/publishing/redis/1_inside_propan.py !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="8" 8 | {!> docs_src/quickstart/broker/publishing/rabbit/1_inside_propan.py !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="8" 13 | {!> docs_src/quickstart/broker/publishing/kafka/1_inside_propan.py !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="8" 18 | {!> docs_src/quickstart/broker/publishing/sqs/1_inside_propan.py !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="8" 23 | {!> docs_src/quickstart/broker/publishing/nats/1_inside_propan.py !} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/broker/publishing/2_context.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python 3 | {!> docs_src/quickstart/broker/publishing/redis/2_context.py !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python 8 | {!> docs_src/quickstart/broker/publishing/rabbit/2_context.py !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python 13 | {!> docs_src/quickstart/broker/publishing/kafka/2_context.py !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python 18 | {!> docs_src/quickstart/broker/publishing/sqs/2_context.py !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python 23 | {!> docs_src/quickstart/broker/publishing/nats/2_context.py !} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/broker/routers.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="3 5 10" 3 | {!> docs_src/quickstart/broker/routers/redis.py !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="3 5 10" 8 | {!> docs_src/quickstart/broker/routers/rabbit.py !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="3 5 10" 13 | {!> docs_src/quickstart/broker/routers/kafka.py !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="3 5 10" 18 | {!> docs_src/quickstart/broker/routers/sqs.py !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="3 5 10" 23 | {!> docs_src/quickstart/broker/routers/nats.py !} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/broker/rpc/1_handler.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="7" 3 | {!> docs_src/quickstart/broker/rpc/redis/1_handler.py !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="7" 8 | {!> docs_src/quickstart/broker/rpc/rabbit/1_handler.py !} 9 | ``` 10 | 11 | === "NATS" 12 | ```python linenums="1" hl_lines="7" 13 | {!> docs_src/quickstart/broker/rpc/nats/1_handler.py !} 14 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/broker/rpc/2_blocking_client.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="7" 3 | {!> docs_src/quickstart/broker/rpc/redis/2_blocking_client.py !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="7" 8 | {!> docs_src/quickstart/broker/rpc/rabbit/2_blocking_client.py !} 9 | ``` 10 | 11 | === "NATS" 12 | ```python linenums="1" hl_lines="7" 13 | {!> docs_src/quickstart/broker/rpc/nats/2_blocking_client.py !} 14 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/broker/rpc/6_noblocking_client.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="6 15" 3 | {!> docs_src/quickstart/broker/rpc/redis/6_noblocking_client.py !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="6 15" 8 | {!> docs_src/quickstart/broker/rpc/rabbit/6_noblocking_client.py !} 9 | ``` 10 | 11 | === "NATS" 12 | ```python linenums="1" hl_lines="6 15" 13 | {!> docs_src/quickstart/broker/rpc/nats/6_noblocking_client.py !} 14 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/broker/serialization/parse_signature.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="3-5" 3 | {!> docs_src/quickstart/broker/serialization/1_redis_parse.py !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="4-6" 8 | {!> docs_src/quickstart/broker/serialization/1_rabbit_parse.py !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="4-6" 13 | {!> docs_src/quickstart/broker/serialization/1_kafka_parse.py !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="4-6" 18 | {!> docs_src/quickstart/broker/serialization/1_sqs_parse.py !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="4-6" 23 | {!> docs_src/quickstart/broker/serialization/1_nats_parse.py !} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/cli/01_context.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="3 14-16" 3 | {!> docs_src/quickstart/cli/01_redis_context.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="3 14-16" 8 | {!> docs_src/quickstart/cli/01_rabbit_context.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="3 14-16" 13 | {!> docs_src/quickstart/cli/01_kafka_context.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="3 14-16" 18 | {!> docs_src/quickstart/cli/01_sqs_context.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="3 14-16" 23 | {!> docs_src/quickstart/cli/01_nats_context.py!} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/dependencies/depends/1.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python 3 | from propan import RedisBroker 4 | broker = RedisBroker(..., apply_types=False) 5 | ``` 6 | 7 | === "RabbitMQ" 8 | ```python 9 | from propan import RabbitBroker 10 | broker = RabbitBroker(..., apply_types=False) 11 | ``` 12 | 13 | === "Kafka" 14 | ```python 15 | from propan import KafkaBroker 16 | broker = KafkaBroker(..., apply_types=False) 17 | ``` 18 | 19 | === "SQS" 20 | ```python 21 | from propan import SQSBroker 22 | broker = SQSBroker(..., apply_types=False) 23 | ``` 24 | 25 | === "NATS" 26 | ```python 27 | from propan import NatsBroker 28 | broker = NatsBroker(..., apply_types=False) 29 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/dependencies/depends/2.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="6-7" 3 | {!> docs_src/quickstart/dependencies/basic/redis/1_depends.py !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="6-7" 8 | {!> docs_src/quickstart/dependencies/basic/rabbit/1_depends.py !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="6-7" 13 | {!> docs_src/quickstart/dependencies/basic/kafka/1_depends.py !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="6-7" 18 | {!> docs_src/quickstart/dependencies/basic/sqs/1_depends.py !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="6-7" 23 | {!> docs_src/quickstart/dependencies/basic/nats/1_depends.py !} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/dependencies/depends/3.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="10" hl_lines="1" 3 | {!> docs_src/quickstart/dependencies/basic/redis/1_depends.py [ln:10-11] !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="10" hl_lines="1" 8 | {!> docs_src/quickstart/dependencies/basic/rabbit/1_depends.py [ln:10-11] !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="10" hl_lines="1" 13 | {!> docs_src/quickstart/dependencies/basic/kafka/1_depends.py [ln:10-11] !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="10" hl_lines="1" 18 | {!> docs_src/quickstart/dependencies/basic/sqs/1_depends.py [ln:10-11] !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="10" hl_lines="1" 23 | {!> docs_src/quickstart/dependencies/basic/nats/1_depends.py [ln:10-11] !} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/dependencies/depends/4.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="10" hl_lines="2" 3 | {!> docs_src/quickstart/dependencies/basic/redis/1_depends.py [ln:10-11] !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="10" hl_lines="2" 8 | {!> docs_src/quickstart/dependencies/basic/rabbit/1_depends.py [ln:10-11] !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="10" hl_lines="2" 13 | {!> docs_src/quickstart/dependencies/basic/kafka/1_depends.py [ln:10-11] !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="10" hl_lines="2" 18 | {!> docs_src/quickstart/dependencies/basic/sqs/1_depends.py [ln:10-11] !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="10" hl_lines="2" 23 | {!> docs_src/quickstart/dependencies/basic/nats/1_depends.py [ln:10-11] !} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/index/01_base.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" title="serve.py" 3 | {!> docs_src/index/redis/01_base.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" title="serve.py" 8 | {!> docs_src/index/rabbit/01_base.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" title="serve.py" 13 | {!> docs_src/index/kafka/01_base.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" title="serve.py" 18 | {!> docs_src/index/sqs/01_base.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" title="serve.py" 23 | {!> docs_src/index/nats/01_base.py!} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/index/04_http_example.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="5 11-13 16-17" 3 | {!> docs_src/index/redis/04_http_example.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="5 11-13 16-17" 8 | {!> docs_src/index/rabbit/04_http_example.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="5 11-13 16-17" 13 | {!> docs_src/index/kafka/04_http_example.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="5 11-13 16-17" 18 | {!> docs_src/index/sqs/04_http_example.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="5 11-13 16-17" 23 | {!> docs_src/index/nats/04_http_example.py!} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/index/05_native_fastapi.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="7 15 19" 3 | {!> docs_src/index/redis/05_native_fastapi.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="7 15 19" 8 | {!> docs_src/index/rabbit/05_native_fastapi.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="7 15 19" 13 | {!> docs_src/index/kafka/05_native_fastapi.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="7 15 19" 18 | {!> docs_src/index/sqs/05_native_fastapi.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="7 15 19" 23 | {!> docs_src/index/nats/05_native_fastapi.py!} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/lifespan/1.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="12-16" 3 | {!> docs_src/quickstart/lifespan/redis/1.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="12-16" 8 | {!> docs_src/quickstart/lifespan/rabbit/1.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="12-16" 13 | {!> docs_src/quickstart/lifespan/kafka/1.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="12-16" 18 | {!> docs_src/quickstart/lifespan/sqs/1.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="12-16" 23 | {!> docs_src/quickstart/lifespan/nats/1.py!} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/includes/getting_started/lifespan/2.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="12" hl_lines="1" 3 | {!> docs_src/quickstart/lifespan/redis/1.py [ln:11-15]!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="12" hl_lines="1" 8 | {!> docs_src/quickstart/lifespan/rabbit/1.py [ln:11-15]!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="12" hl_lines="1" 13 | {!> docs_src/quickstart/lifespan/kafka/1.py [ln:11-15]!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="12" hl_lines="1" 18 | {!> docs_src/quickstart/lifespan/sqs/1.py [ln:11-15] !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="12" hl_lines="1" 23 | {!> docs_src/quickstart/lifespan/nats/1.py [ln:11-15]!} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/includes/getting_started/lifespan/3.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="12" hl_lines="2" 3 | {!> docs_src/quickstart/lifespan/redis/1.py [ln:11-15]!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="12" hl_lines="2" 8 | {!> docs_src/quickstart/lifespan/rabbit/1.py [ln:11-15]!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="12" hl_lines="2" 13 | {!> docs_src/quickstart/lifespan/kafka/1.py [ln:11-15]!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="12" hl_lines="2" 18 | {!> docs_src/quickstart/lifespan/sqs/1.py [ln:11-15] !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="12" hl_lines="2" 23 | {!> docs_src/quickstart/lifespan/nats/1.py [ln:11-15]!} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/includes/getting_started/lifespan/4.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="12" hl_lines="3" 3 | {!> docs_src/quickstart/lifespan/redis/1.py [ln:11-15] !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="12" hl_lines="3" 8 | {!> docs_src/quickstart/lifespan/rabbit/1.py [ln:11-15] !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="12" hl_lines="3" 13 | {!> docs_src/quickstart/lifespan/kafka/1.py [ln:11-15] !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="12" hl_lines="3" 18 | {!> docs_src/quickstart/lifespan/sqs/1.py [ln:11-15] !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="12" hl_lines="3" 23 | {!> docs_src/quickstart/lifespan/nats/1.py [ln:11-15] !} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/includes/getting_started/lifespan/5.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="12" hl_lines="4" 3 | {!> docs_src/quickstart/lifespan/redis/1.py [ln:11-15] !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="12" hl_lines="4" 8 | {!> docs_src/quickstart/lifespan/rabbit/1.py [ln:11-15] !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="12" hl_lines="4" 13 | {!> docs_src/quickstart/lifespan/kafka/1.py [ln:11-15] !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="12" hl_lines="4" 18 | {!> docs_src/quickstart/lifespan/sqs/1.py [ln:11-15] !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="12" hl_lines="4" 23 | {!> docs_src/quickstart/lifespan/nats/1.py [ln:11-15] !} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/includes/getting_started/lifespan/6.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="12" hl_lines="5" 3 | {!> docs_src/quickstart/lifespan/redis/1.py [ln:11-15] !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="12" hl_lines="5" 8 | {!> docs_src/quickstart/lifespan/rabbit/1.py [ln:11-15] !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="12" hl_lines="5" 13 | {!> docs_src/quickstart/lifespan/kafka/1.py [ln:11-15] !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="12" hl_lines="5" 18 | {!> docs_src/quickstart/lifespan/sqs/1.py [ln:11-15] !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="12" hl_lines="5" 23 | {!> docs_src/quickstart/lifespan/nats/1.py [ln:11-15] !} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/includes/getting_started/lifespan/7.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="12 18" 3 | {!> docs_src/quickstart/lifespan/redis/2_ml.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="12 18" 8 | {!> docs_src/quickstart/lifespan/rabbit/2_ml.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="12 18" 13 | {!> docs_src/quickstart/lifespan/kafka/2_ml.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="12 18" 18 | {!> docs_src/quickstart/lifespan/sqs/2_ml.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="12 18" 23 | {!> docs_src/quickstart/lifespan/nats/2_ml.py!} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/includes/getting_started/test/2.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python hl_lines="2" 3 | {!> docs_src/quickstart/testing/redis/2_test.py [ln:6-8]!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python hl_lines="2" 8 | {!> docs_src/quickstart/testing/rabbit/2_test.py [ln:6-8]!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python hl_lines="2" 13 | {!> docs_src/quickstart/testing/kafka/2_test.py [ln:6-8]!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python hl_lines="2" 18 | {!> docs_src/quickstart/testing/sqs/2_test.py [ln:6-8]!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python hl_lines="2" 23 | {!> docs_src/quickstart/testing/nats/2_test.py [ln:6-8]!} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/test/3.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" title="test_broker.py" hl_lines="6-9 11" 3 | {!> docs_src/quickstart/testing/redis/3_conftest.py !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" title="test_broker.py" hl_lines="6-9 11" 8 | {!> docs_src/quickstart/testing/rabbit/3_conftest.py !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" title="test_broker.py" hl_lines="6-9 11" 13 | {!> docs_src/quickstart/testing/kafka/3_conftest.py !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" title="test_broker.py" hl_lines="6-9 11" 18 | {!> docs_src/quickstart/testing/sqs/3_conftest.py !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" title="test_broker.py" hl_lines="6-9 11" 23 | {!> docs_src/quickstart/testing/nats/3_conftest.py !} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/getting_started/test/4.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="6-7" title="test_ping.py" 3 | {!> docs_src/quickstart/testing/redis/5_build_message.py !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="6-7" title="test_ping.py" 8 | {!> docs_src/quickstart/testing/rabbit/5_build_message.py !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="6-7" title="test_ping.py" 13 | {!> docs_src/quickstart/testing/kafka/5_build_message.py !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="6-7" title="test_ping.py" 18 | {!> docs_src/quickstart/testing/sqs/5_build_message.py !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="6-7" title="test_ping.py" 23 | {!> docs_src/quickstart/testing/nats/5_build_message.py !} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/includes/getting_started/test/5.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="9-10" title="test_ping.py" 3 | {!> docs_src/quickstart/testing/redis/6_reraise.py !} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="9-10" title="test_ping.py" 8 | {!> docs_src/quickstart/testing/rabbit/6_reraise.py !} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="9-10" title="test_ping.py" 13 | {!> docs_src/quickstart/testing/kafka/6_reraise.py !} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="9-10" title="test_ping.py" 18 | {!> docs_src/quickstart/testing/sqs/6_reraise.py !} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="9-10" title="test_ping.py" 23 | {!> docs_src/quickstart/testing/nats/6_reraise.py !} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/includes/integrations/fastapi/after_startup.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="8 12" 3 | {!> docs_src/integrations/redis/fastapi_after_startup.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="8 12" 8 | {!> docs_src/integrations/rabbit/fastapi_after_startup.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="8 12" 13 | {!> docs_src/integrations/kafka/fastapi_after_startup.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="8 12" 18 | {!> docs_src/integrations/sqs/fastapi_after_startup.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="8 12" 23 | {!> docs_src/integrations/nats/fastapi_after_startup.py!} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/integrations/fastapi/fastapi_plugin.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="1 3 7 15 19 23" 3 | {!> docs_src/integrations/redis/fastapi_plugin.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="1 3 7 15 19 23" 8 | {!> docs_src/integrations/rabbit/fastapi_plugin.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="1 3 7 15 19 23" 13 | {!> docs_src/integrations/kafka/fastapi_plugin.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="1 3 7 15 19 23" 18 | {!> docs_src/integrations/sqs/fastapi_plugin.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="1 3 7 15 19 23" 23 | {!> docs_src/integrations/nats/fastapi_plugin.py!} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/includes/integrations/fastapi/fastapi_plugin_depends.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="8 14-15" 3 | {!> docs_src/integrations/redis/fastapi_plugin_depends.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="8 14-15" 8 | {!> docs_src/integrations/rabbit/fastapi_plugin_depends.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="8 14-15" 13 | {!> docs_src/integrations/kafka/fastapi_plugin_depends.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="8 14-15" 18 | {!> docs_src/integrations/sqs/fastapi_plugin_depends.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="8 14-15" 23 | {!> docs_src/integrations/nats/fastapi_plugin_depends.py!} 24 | ``` -------------------------------------------------------------------------------- /docs/includes/integrations/fastapi/fastapi_plugin_send.md: -------------------------------------------------------------------------------- 1 | === "Redis" 2 | ```python linenums="1" hl_lines="6 10" 3 | {!> docs_src/integrations/redis/fastapi_plugin_send.py!} 4 | ``` 5 | 6 | === "RabbitMQ" 7 | ```python linenums="1" hl_lines="6 10" 8 | {!> docs_src/integrations/rabbit/fastapi_plugin_send.py!} 9 | ``` 10 | 11 | === "Kafka" 12 | ```python linenums="1" hl_lines="6 10" 13 | {!> docs_src/integrations/kafka/fastapi_plugin_send.py!} 14 | ``` 15 | 16 | === "SQS" 17 | ```python linenums="1" hl_lines="6 10" 18 | {!> docs_src/integrations/sqs/fastapi_plugin_send.py!} 19 | ``` 20 | 21 | === "NATS" 22 | ```python linenums="1" hl_lines="6 10" 23 | {!> docs_src/integrations/nats/fastapi_plugin_send.py!} 24 | ``` -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs-material==9.5.21 2 | mkdocs-static-i18n==0.56 3 | mdx-include==1.4.2 4 | mkdocs-macros-plugin==1.0.5 5 | 6 | # images zoom 7 | mkdocs-glightbox 8 | pillow 9 | cairosvg 10 | 11 | typer[all] -------------------------------------------------------------------------------- /examples/1_basic_usage.py: -------------------------------------------------------------------------------- 1 | """ 2 | Connect to localhost RabbitMQ and listen `default` exchange 3 | with `test` routing key 4 | 5 | use `propan run basic_usage:app` to start example 6 | """ 7 | from propan import PropanApp, RabbitBroker 8 | 9 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 10 | 11 | app = PropanApp(broker) 12 | 13 | 14 | @broker.handle("test") 15 | async def base_handler(body): 16 | print(body) 17 | -------------------------------------------------------------------------------- /examples/2_specific_exchange.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can set specific queue and exchange to listening 3 | with all amqp supported flags 4 | """ 5 | from propan import PropanApp, RabbitBroker 6 | from propan.brokers.rabbit import ExchangeType, RabbitExchange, RabbitQueue 7 | 8 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 9 | 10 | app = PropanApp(broker) 11 | 12 | 13 | @broker.handle( 14 | queue=RabbitQueue("test"), exchange=RabbitExchange("test", type=ExchangeType.FANOUT) 15 | ) 16 | async def base_handler(body: dict): 17 | print(body) 18 | -------------------------------------------------------------------------------- /examples/3_lifespan_events.py: -------------------------------------------------------------------------------- 1 | """ 2 | Also you can use sync/async functions as 3 | application lifecycle hooks 4 | 5 | All `on_startup` hooks runs before broker has been started 6 | All `on_shutdown` hooks runs after broker has been stopped 7 | """ 8 | from propan import PropanApp, RabbitBroker 9 | 10 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 11 | 12 | app = PropanApp(broker) 13 | 14 | 15 | @app.on_startup 16 | def setup(): 17 | print("Application starting up...") 18 | 19 | 20 | @app.on_startup 21 | async def setup_later(): 22 | print("Application still starting up...") 23 | 24 | 25 | @app.on_shutdown 26 | async def shutdown(): 27 | print("Application shutting down...") 28 | 29 | 30 | @broker.handle("test") 31 | async def base_handler(body: dict): 32 | print(body) 33 | -------------------------------------------------------------------------------- /examples/4_cli_attributes_promotion.py: -------------------------------------------------------------------------------- 1 | """ 2 | Using propan cli tool allows to pass command-line 3 | options inside your Context dependencies. 4 | 5 | Ex: 6 | propan run serve:app --env=.env.dev 7 | 8 | You can pass options following ways: 9 | ... --env=.env 10 | ... -env=.env 11 | ... env=.env 12 | "=" is required 13 | 14 | Or you can pass a boolean flags 15 | ... --use-smth # passes as use_smth=True 16 | ... --no-use-smth # passes as use_smth=False 17 | """ 18 | from pydantic import BaseSettings 19 | 20 | from propan import Context, PropanApp, RabbitBroker 21 | 22 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 23 | 24 | app = PropanApp(broker) 25 | 26 | 27 | class Settings(BaseSettings): 28 | ... 29 | 30 | 31 | @app.on_startup 32 | async def setup_later(env: str, context: Context): 33 | settings = Settings(_env_file=env) 34 | context.set_global("settings", settings) 35 | -------------------------------------------------------------------------------- /examples/5_publishing.py: -------------------------------------------------------------------------------- 1 | """ 2 | Propan provides the easiest way to publish messages: 3 | just use Broker as context manager and publish whatever you want! 4 | """ 5 | import asyncio 6 | 7 | from aio_pika import Message 8 | 9 | from propan import RabbitBroker 10 | 11 | 12 | async def main(): 13 | async with RabbitBroker("amqp://guest:guest@localhost:5672/") as broker: 14 | await broker.publish("Hello, Propan!", queue="test") 15 | 16 | await broker.publish( 17 | { 18 | "hello": "again!", 19 | }, 20 | queue="test", 21 | ) 22 | 23 | await broker.publish(Message(b"hi"), queue="test") 24 | 25 | 26 | if __name__ == "__main__": 27 | asyncio.run(main()) 28 | -------------------------------------------------------------------------------- /examples/7_handler_errors_processing.py: -------------------------------------------------------------------------------- 1 | """ 2 | By default handler drops with rejecting messages due 3 | process exceptions. 4 | 5 | If you want to redelivere messages just use 6 | @handler(retry=...) parameter. 7 | 8 | For more complex usecases just use the `tenacity` library. 9 | """ 10 | from propan import PropanApp, RabbitBroker 11 | 12 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 13 | 14 | app = PropanApp(broker) 15 | 16 | 17 | @broker.handle("test", retry=True) 18 | async def base_handler(body): 19 | """Message will be requeue endlessly at exception""" 20 | print(body) 21 | 22 | 23 | @broker.handle("test2", retry=3) 24 | async def another_handler(body): 25 | """Message will be processed at most 3 times and drops after""" 26 | print(body) 27 | -------------------------------------------------------------------------------- /examples/8_rpc_over_mq.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from propan.brokers.rabbit import RabbitBroker 4 | 5 | broker = RabbitBroker("amqp://guest:guest@127.0.0.1/") 6 | 7 | 8 | @broker.handle("test") 9 | async def ping(m: str): 10 | return "hello!" 11 | 12 | 13 | async def main(): 14 | async with broker: 15 | r = await broker.publish( 16 | "hello", 17 | routing_key="test", 18 | callback=True, 19 | ) 20 | assert r == "hello" 21 | 22 | 23 | asyncio.run(main()) 24 | -------------------------------------------------------------------------------- /examples/9_noblocking_callbacks.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from propan.brokers.rabbit import RabbitBroker 4 | 5 | broker = RabbitBroker("amqp://guest:guest@127.0.0.1/") 6 | 7 | 8 | @broker.handle("test") 9 | async def ping(m: str): 10 | return "hello!" 11 | 12 | 13 | @broker.handle("reply") 14 | async def get_message(m: str): 15 | assert m == "hello!" 16 | 17 | 18 | async def main(): 19 | await broker.start() 20 | 21 | await broker.publish("hi!", queue="test", reply_to="reply") 22 | 23 | try: 24 | await asyncio.Future() 25 | finally: 26 | await broker.close() 27 | 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /examples/dependencies/2_dependency_declaration.py: -------------------------------------------------------------------------------- 1 | """ 2 | All Context dependencies also available from on_startup hook 3 | (Some of them are None at application starting). 4 | 5 | You can use it to setup your custom context fields. 6 | """ 7 | from propan import Context, PropanApp, RabbitBroker 8 | from propan.annotations import ContextRepo 9 | 10 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 11 | 12 | app = PropanApp(broker) 13 | 14 | 15 | @app.on_startup 16 | def setup(context: ContextRepo): 17 | context.set_global("my_dependency", True) 18 | 19 | 20 | @broker.handle("test") 21 | async def base_handler(body: dict, my_dependency: bool = Context()): 22 | assert my_dependency is True 23 | -------------------------------------------------------------------------------- /examples/dependencies/3_dependency_aliases.py: -------------------------------------------------------------------------------- 1 | """ 2 | Using Alias allows you rename context dependencies passing 3 | to your function 4 | """ 5 | from propan import Context, PropanApp, RabbitBroker 6 | 7 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 8 | 9 | app = PropanApp(broker) 10 | 11 | 12 | @app.on_startup 13 | def setup(rabbit: RabbitBroker = Context("broker")): 14 | assert rabbit is broker 15 | -------------------------------------------------------------------------------- /examples/dependencies/4_dependency_deep_aliases.py: -------------------------------------------------------------------------------- 1 | """ 2 | Alias is able to provide access to specific attributes of dependency 3 | """ 4 | from pydantic import BaseSettings, Field 5 | 6 | from propan import Context, PropanApp, RabbitBroker 7 | from propan.annotations import ContextRepo 8 | 9 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 10 | 11 | app = PropanApp(broker) 12 | 13 | KEY = "my-secret-key" 14 | 15 | 16 | class Settings(BaseSettings): 17 | key: str = Field("") 18 | 19 | 20 | @app.on_startup 21 | async def setup(context: ContextRepo): 22 | settings = Settings(key=KEY) 23 | context.set_global("settings", settings) 24 | 25 | 26 | @app.on_startup 27 | def setup_later(key: str = Context("settings.key")): 28 | assert key is KEY 29 | -------------------------------------------------------------------------------- /examples/dependencies/5_dependency_nesting.py: -------------------------------------------------------------------------------- 1 | """ 2 | @apply_types decorator allows pass context dependencies 3 | to all functions with the same context through the functions 4 | calling stack. 5 | """ 6 | from propan import PropanApp, RabbitBroker, apply_types 7 | from propan.annotations import Logger, RabbitMessage 8 | 9 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 10 | 11 | app = PropanApp(broker) 12 | 13 | 14 | @broker.handle("test") 15 | async def base_handler(body: dict): 16 | await nested_funcion() 17 | 18 | 19 | @apply_types 20 | async def nested_funcion(logger: Logger, message: RabbitMessage): 21 | logger.info(f"Message `{message}` processing") 22 | -------------------------------------------------------------------------------- /examples/dependencies/7_annotated.py: -------------------------------------------------------------------------------- 1 | """ 2 | With Python 3.10+ you can use 3 | typing.Annotated class as a shortcut to Depends and Alias 4 | """ 5 | import logging 6 | 7 | from typing_extensions import Annotated 8 | 9 | from propan import Context, Depends, PropanApp, RabbitBroker, apply_types 10 | 11 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 12 | 13 | app = PropanApp(broker) 14 | 15 | Logger = Annotated[logging.Logger, Context("broker.logger")] 16 | 17 | 18 | @apply_types 19 | async def async_dependency(body, logger: Logger): 20 | logger.info(f"Async calls with: {body}") 21 | return True 22 | 23 | 24 | Dependency = Annotated[bool, Depends(async_dependency)] 25 | 26 | 27 | @broker.handle("test") 28 | async def base_handler(body: dict, async_called: Dependency): 29 | assert async_called is True 30 | -------------------------------------------------------------------------------- /examples/http_frameworks_integrations/aiohttp.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can use Propan MQBrokers without PropanApp 3 | Just start and stop them whenever you want 4 | """ 5 | from aiohttp import web 6 | 7 | from propan import RabbitBroker 8 | 9 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 10 | 11 | 12 | @broker.handle("test") 13 | async def base_handler(body): 14 | print(body) 15 | 16 | 17 | async def start_broker(app): 18 | await broker.start() 19 | 20 | 21 | async def stop_broker(app): 22 | await broker.close() 23 | 24 | 25 | async def hello(request): 26 | return web.Response(text="Hello, world") 27 | 28 | 29 | app = web.Application() 30 | app.add_routes([web.get("/", hello)]) 31 | app.on_startup.append(start_broker) 32 | app.on_cleanup.append(stop_broker) 33 | 34 | 35 | if __name__ == "__main__": 36 | web.run_app(app) 37 | -------------------------------------------------------------------------------- /examples/http_frameworks_integrations/blacksheep.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can use Propan MQBrokers without PropanApp 3 | Just start and stop them whenever you want 4 | """ 5 | from blacksheep import Application 6 | 7 | from propan import RabbitBroker 8 | 9 | app = Application() 10 | 11 | 12 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 13 | 14 | 15 | @broker.handle("test") 16 | async def base_handler(body): 17 | print(body) 18 | 19 | 20 | @app.on_start 21 | async def start_broker(application: Application) -> None: 22 | await broker.start() 23 | 24 | 25 | @app.on_stop 26 | async def stop_broker(application: Application) -> None: 27 | await broker.close() 28 | 29 | 30 | @app.route("/") 31 | async def home(): 32 | return "Hello, World!" 33 | -------------------------------------------------------------------------------- /examples/http_frameworks_integrations/fastapi.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can use Propan MQBrokers without PropanApp 3 | Just start and stop them whenever you want 4 | """ 5 | from contextlib import asynccontextmanager 6 | 7 | from fastapi import FastAPI 8 | 9 | from propan import RabbitBroker 10 | 11 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 12 | 13 | app = FastAPI() 14 | 15 | 16 | @asynccontextmanager 17 | async def lifespan(app: FastAPI): 18 | await broker.start() 19 | yield 20 | await broker.close() 21 | 22 | 23 | @broker.handle("test") 24 | async def base_handler(body): 25 | print(body) 26 | 27 | 28 | @app.get("/") 29 | def read_root(): 30 | return {"Hello": "World"} 31 | -------------------------------------------------------------------------------- /examples/http_frameworks_integrations/native_fastapi.py: -------------------------------------------------------------------------------- 1 | from fastapi import Depends, FastAPI 2 | from pydantic import BaseModel 3 | 4 | from propan.fastapi import RabbitRouter 5 | 6 | router = RabbitRouter("amqp://guest:guest@localhost:5672") 7 | 8 | app = FastAPI(lifespan=router.lifespan_context) 9 | 10 | 11 | class Incoming(BaseModel): 12 | m: dict 13 | 14 | 15 | def call(): 16 | return True 17 | 18 | 19 | @router.event("test") 20 | async def hello(m: Incoming, d=Depends(call)) -> dict: 21 | return {"response": "Hello, world!"} 22 | 23 | 24 | @router.get("/") 25 | async def hello_http(): 26 | return "Hello, http!" 27 | 28 | 29 | app.include_router(router) 30 | -------------------------------------------------------------------------------- /examples/http_frameworks_integrations/quart.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can use Propan MQBrokers without PropanApp 3 | Just start and stop them whenever you want 4 | """ 5 | from quart import Quart 6 | 7 | from propan import RabbitBroker 8 | 9 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 10 | 11 | app = Quart(__name__) 12 | 13 | 14 | @broker.handle("test") 15 | async def base_handler(body): 16 | print(body) 17 | 18 | 19 | @app.before_serving 20 | async def start_broker(): 21 | await broker.start() 22 | 23 | 24 | @app.after_serving 25 | async def stop_broker(): 26 | await broker.close() 27 | 28 | 29 | @app.route("/") 30 | async def json(): 31 | return {"hello": "world"} 32 | -------------------------------------------------------------------------------- /examples/http_frameworks_integrations/sanic.py: -------------------------------------------------------------------------------- 1 | """ 2 | You can use Propan MQBrokers without PropanApp 3 | Just start and stop them whenever you want 4 | """ 5 | from sanic import Sanic 6 | from sanic.response import text 7 | 8 | from propan import RabbitBroker 9 | 10 | app = Sanic("MyHelloWorldApp") 11 | broker = RabbitBroker("amqp://guest:guest@localhost:5672/") 12 | 13 | 14 | @broker.handle("test") 15 | async def base_handler(body): 16 | print(body) 17 | 18 | 19 | @app.after_server_start 20 | async def start_broker(app, loop): 21 | await broker.start() 22 | 23 | 24 | @app.after_server_stop 25 | async def stop_broker(app, loop): 26 | await broker.close() 27 | 28 | 29 | @app.get("/") 30 | async def hello_world(request): 31 | return text("Hello, world.") 32 | -------------------------------------------------------------------------------- /examples/kafka/1_direct.py: -------------------------------------------------------------------------------- 1 | from propan import KafkaBroker, PropanApp 2 | 3 | broker = KafkaBroker("localhost:9092") 4 | app = PropanApp(broker) 5 | 6 | 7 | @broker.handle("test-topic", auto_offset_reset="earliest") 8 | async def hello(msg: str): 9 | print(msg) 10 | 11 | 12 | @app.after_startup 13 | async def pub(): 14 | await broker.publish("hi", "test-topic") 15 | -------------------------------------------------------------------------------- /examples/nats/1_basic.py: -------------------------------------------------------------------------------- 1 | from propan import NatsBroker, PropanApp 2 | 3 | broker = NatsBroker("nats://localhost:4222") 4 | app = PropanApp(broker) 5 | 6 | 7 | @broker.handle("test") 8 | async def hello(msg: str): 9 | print(msg) 10 | 11 | 12 | @app.after_startup 13 | async def pub(): 14 | await broker.publish("hi", "test") 15 | -------------------------------------------------------------------------------- /examples/rabbit/streams.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker 2 | from propan.annotations import Logger 3 | from propan.brokers.rabbit import RabbitQueue 4 | 5 | broker = RabbitBroker(consumers=10) 6 | app = PropanApp(broker) 7 | 8 | queue = RabbitQueue( 9 | name="test", 10 | durable=True, 11 | arguments={ 12 | "x-queue-type": "stream", 13 | }, 14 | ) 15 | 16 | 17 | @broker.handle(queue, consume_arguments={"x-stream-offset": "first"}) 18 | async def handle(msg, logger: Logger): 19 | logger.info(msg) 20 | 21 | 22 | @app.after_startup 23 | async def test(): 24 | await broker.publish("Hi!", queue) 25 | -------------------------------------------------------------------------------- /examples/redis/direct.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | from propan.annotations import Logger 3 | 4 | broker = RedisBroker() 5 | app = PropanApp(broker) 6 | 7 | 8 | @broker.handle("test") 9 | async def handler1(logger: Logger): 10 | logger.info("handler1") 11 | 12 | 13 | @broker.handle("test") 14 | async def handler2(logger: Logger): 15 | logger.info("handler2") 16 | 17 | 18 | @broker.handle("test2") 19 | async def handler3(logger: Logger): 20 | logger.info("handler3") 21 | 22 | 23 | @app.after_startup 24 | async def publish_smth(): 25 | await broker.publish("", "test") 26 | await broker.publish("", "test2") 27 | -------------------------------------------------------------------------------- /examples/redis/pattern.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | from propan.annotations import Logger 3 | 4 | broker = RedisBroker() 5 | app = PropanApp(broker) 6 | 7 | 8 | @broker.handle("*.info", pattern=True) 9 | async def handler1(b: str, logger: Logger): 10 | logger.info("handler1") 11 | 12 | 13 | @broker.handle("*.info", pattern=True) 14 | async def handler2(b: str, logger: Logger): 15 | logger.info("handler2") 16 | 17 | 18 | @broker.handle("*.error", pattern=True) 19 | async def handler3(logger: Logger): 20 | logger.info("handler3") 21 | 22 | 23 | @app.after_startup 24 | async def publish_smth(): 25 | await broker.publish("", "logs.info") 26 | await broker.publish("", "logs.error") 27 | -------------------------------------------------------------------------------- /examples/scheduling/rocketry.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from rocketry import Rocketry 4 | from rocketry.args import Arg 5 | 6 | from propan import RabbitBroker 7 | 8 | app = Rocketry(execution="async") 9 | 10 | broker = RabbitBroker() 11 | app.params(broker=broker) 12 | 13 | 14 | async def start_app(): 15 | async with broker: 16 | await app.serve() 17 | 18 | 19 | @app.task("every 1 second", execution="async") 20 | async def publish(br: RabbitBroker = Arg("broker")): 21 | await br.publish("Hi, Rocketry!", "test") 22 | 23 | 24 | if __name__ == "__main__": 25 | asyncio.run(start_app()) 26 | -------------------------------------------------------------------------------- /examples/serialization/gen_py_code.sh: -------------------------------------------------------------------------------- 1 | python -m grpc_tools.protoc --python_out=. --pyi_out=. -I . message.proto -------------------------------------------------------------------------------- /examples/serialization/message.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message Person { 4 | string name = 1; 5 | float age = 2; 6 | } -------------------------------------------------------------------------------- /examples/serialization/protobuf.py: -------------------------------------------------------------------------------- 1 | from message_pb2 import Person 2 | 3 | from propan import PropanApp, RabbitBroker 4 | from propan.annotations import Logger, NoCast 5 | from propan.brokers.rabbit import RabbitMessage 6 | 7 | broker = RabbitBroker() 8 | app = PropanApp(broker) 9 | 10 | 11 | async def decode_message(msg: RabbitMessage, *args) -> Person: 12 | decoded = Person() 13 | decoded.ParseFromString(msg.body) 14 | return decoded 15 | 16 | 17 | @broker.handle("test", decode_message=decode_message) 18 | async def consume(body: NoCast[Person], logger: Logger): 19 | logger.info(body) 20 | 21 | 22 | @app.after_startup 23 | async def publish(): 24 | body = Person(name="john", age=25).SerializeToString() 25 | await broker.publish(body, "test") 26 | -------------------------------------------------------------------------------- /examples/serialization/requirements.txt: -------------------------------------------------------------------------------- 1 | grpcio-tools -------------------------------------------------------------------------------- /examples/sqs/1_basic.py: -------------------------------------------------------------------------------- 1 | from aiobotocore.config import AioConfig 2 | from botocore import UNSIGNED 3 | 4 | from propan import PropanApp, SQSBroker 5 | 6 | broker = SQSBroker( 7 | "http://localhost:9324/", 8 | region_name="us-west-2", 9 | config=AioConfig(signature_version=UNSIGNED), 10 | ) 11 | app = PropanApp(broker) 12 | 13 | 14 | @broker.handle("test") 15 | async def hello(msg: str): 16 | print(msg) 17 | 18 | 19 | @app.after_startup 20 | async def pub(): 21 | await broker.publish("hi", "test") 22 | -------------------------------------------------------------------------------- /propan/__main__.py: -------------------------------------------------------------------------------- 1 | from propan.cli import cli 2 | 3 | if __name__ == "__main__": 4 | cli(prog_name="propan") 5 | -------------------------------------------------------------------------------- /propan/asyncapi/bindings/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.asyncapi.bindings.main import ( 2 | AsyncAPIChannelBinding, 3 | AsyncAPIOperationBinding, 4 | ) 5 | 6 | __all__ = ( 7 | "AsyncAPIChannelBinding", 8 | "AsyncAPIOperationBinding", 9 | ) 10 | -------------------------------------------------------------------------------- /propan/asyncapi/bindings/kafka.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | 3 | from pydantic import BaseModel, Field 4 | 5 | from propan.types import AnyDict 6 | 7 | 8 | class AsyncAPIKafkaChannelBinding(BaseModel): 9 | topic: List[str] 10 | partitions: Optional[int] = None 11 | replicas: Optional[int] = None 12 | # TODO: 13 | # topicConfiguration 14 | version: str = Field( 15 | default="0.4.0", 16 | alias="bindingVersion", 17 | ) 18 | 19 | 20 | class AsyncAPIKafkaOperationBinding(BaseModel): 21 | group_id: Optional[AnyDict] = Field( 22 | default=None, 23 | alias="groupId", 24 | ) 25 | client_id: Optional[AnyDict] = Field( 26 | default=None, 27 | alias="clientId", 28 | ) 29 | 30 | reply_to: Optional[AnyDict] = Field(default=None, alias="replyTo") 31 | 32 | version: str = Field( 33 | default="0.4.0", 34 | alias="bindingVersion", 35 | ) 36 | -------------------------------------------------------------------------------- /propan/asyncapi/bindings/nats.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel, Field 4 | 5 | from propan.types import AnyDict 6 | 7 | 8 | class AsyncAPINatsChannelBinding(BaseModel): 9 | subject: str 10 | queue: Optional[str] = None 11 | version: str = Field( 12 | default="custom", 13 | alias="bindingVersion", 14 | ) 15 | 16 | 17 | class AsyncAPINatsOperationBinding(BaseModel): 18 | reply_to: Optional[AnyDict] = Field(default=None, alias="replyTo") 19 | version: str = Field( 20 | default="custom", 21 | alias="bindingVersion", 22 | ) 23 | -------------------------------------------------------------------------------- /propan/asyncapi/bindings/redis.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel, Field 4 | from typing_extensions import Literal 5 | 6 | from propan.types import AnyDict 7 | 8 | 9 | class AsyncAPIRedisChannelBinding(BaseModel): 10 | channel: str 11 | method: Literal["ssubscribe", "psubscribe", "subscribe"] = "subscribe" 12 | version: str = Field( 13 | default="custom", 14 | alias="bindingVersion", 15 | ) 16 | 17 | 18 | class AsyncAPIRedisOperationBinding(BaseModel): 19 | reply_to: Optional[AnyDict] = Field(default=None, alias="replyTo") 20 | version: str = Field( 21 | default="custom", 22 | alias="bindingVersion", 23 | ) 24 | -------------------------------------------------------------------------------- /propan/asyncapi/bindings/sqs.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel, Field 4 | 5 | from propan.types import AnyDict 6 | 7 | 8 | class AsyncAPISQSChannelBinding(BaseModel): 9 | queue: AnyDict 10 | version: str = Field( 11 | default="custom", 12 | alias="bindingVersion", 13 | ) 14 | 15 | 16 | class AsyncAPISQSOperationBinding(BaseModel): 17 | reply_to: Optional[AnyDict] = Field(default=None, alias="replyTo") 18 | 19 | version: str = Field( 20 | default="custom", 21 | alias="bindingVersion", 22 | ) 23 | -------------------------------------------------------------------------------- /propan/asyncapi/channels.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | 3 | from pydantic import BaseModel 4 | 5 | from propan.asyncapi.bindings import AsyncAPIChannelBinding, AsyncAPIOperationBinding 6 | from propan.asyncapi.subscription import AsyncAPISubscription 7 | 8 | 9 | class AsyncAPIPublish(BaseModel): 10 | bindings: Optional[AsyncAPIOperationBinding] = None 11 | 12 | 13 | class AsyncAPIChannelParameters(BaseModel): 14 | # TODO 15 | ... 16 | 17 | 18 | class AsyncAPIChannel(BaseModel): 19 | description: Optional[str] = None 20 | servers: Optional[List[str]] = None 21 | bindings: Optional[AsyncAPIChannelBinding] = None 22 | subscribe: Optional[AsyncAPISubscription] = None 23 | publish: Optional[AsyncAPIPublish] = None 24 | parameters: Optional[AsyncAPIChannelParameters] = None 25 | -------------------------------------------------------------------------------- /propan/asyncapi/info.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel, Field, HttpUrl 4 | 5 | from propan._compat import is_installed 6 | 7 | if is_installed("email_validator"): 8 | from pydantic import EmailStr 9 | else: # pragma: no cover 10 | EmailStr = str # type: ignore 11 | 12 | 13 | class AsyncAPIContact(BaseModel): 14 | name: str 15 | url: HttpUrl 16 | email: Optional[EmailStr] = None 17 | 18 | 19 | class AsyncAPILicense(BaseModel): 20 | name: str 21 | url: HttpUrl 22 | 23 | 24 | class AsyncAPIInfo(BaseModel): 25 | title: str 26 | version: str = "1.0.0" 27 | description: str = "" 28 | terms: Optional[HttpUrl] = Field( 29 | default=None, 30 | alias="termsOfService", 31 | ) 32 | contact: Optional[AsyncAPIContact] = None 33 | license: Optional[AsyncAPILicense] = None 34 | -------------------------------------------------------------------------------- /propan/asyncapi/servers.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Optional 2 | 3 | from pydantic import BaseModel, Field 4 | 5 | from propan.asyncapi.utils import AsyncAPITag 6 | 7 | 8 | class AsyncAPIServer(BaseModel): 9 | url: str 10 | protocol: str 11 | description: Optional[str] = None 12 | protocol_version: Optional[str] = Field( 13 | default=None, 14 | alias="protocolVersion", 15 | ) 16 | tags: Optional[List[AsyncAPITag]] = None 17 | security: Optional[Dict[str, List[str]]] = None 18 | 19 | # TODO: 20 | # variables 21 | # bindings 22 | -------------------------------------------------------------------------------- /propan/asyncapi/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel, Field 4 | 5 | 6 | class AsyncAPIExternalDocs(BaseModel): 7 | url: str = "" 8 | description: str = "" 9 | 10 | 11 | class AsyncAPITag(BaseModel): 12 | name: str 13 | description: str = "" 14 | external_docs: Optional[AsyncAPIExternalDocs] = Field( 15 | default=None, 16 | alias="externalDocs", 17 | ) 18 | -------------------------------------------------------------------------------- /propan/brokers/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.brokers._model.schemas import PropanMessage 2 | 3 | __all__ = ("PropanMessage",) 4 | -------------------------------------------------------------------------------- /propan/brokers/_model/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.brokers._model.broker_usecase import BrokerAsyncUsecase 2 | from propan.brokers._model.routing import BrokerRouter 3 | from propan.brokers._model.schemas import PropanMessage, Queue 4 | 5 | __all__ = ( 6 | "Queue", 7 | "BrokerAsyncUsecase", 8 | "PropanMessage", 9 | "BrokerRouter", 10 | ) 11 | -------------------------------------------------------------------------------- /propan/brokers/constants.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | from typing_extensions import TypeAlias 4 | 5 | ContentType: TypeAlias = str 6 | 7 | 8 | class ContentTypes(str, Enum): 9 | text = "text/plain" 10 | json = "application/json" 11 | -------------------------------------------------------------------------------- /propan/brokers/exceptions.py: -------------------------------------------------------------------------------- 1 | class SkipMessage(Exception): 2 | """PushBackWatcher Instruction to skip message""" 3 | 4 | 5 | WRONG_PUBLISH_ARGS = ValueError( 6 | "You should use `reply_to` to send response to long-living queue " 7 | "and `callback` to get response in sync mode." 8 | ) 9 | -------------------------------------------------------------------------------- /propan/brokers/kafka/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.brokers.kafka.kafka_broker import KafkaBroker, KafkaMessage 2 | from propan.brokers.kafka.routing import KafkaRouter 3 | 4 | __all__ = ("KafkaBroker", "KafkaMessage", "KafkaRouter") 5 | -------------------------------------------------------------------------------- /propan/brokers/kafka/routing.py: -------------------------------------------------------------------------------- 1 | from typing import Awaitable, Callable 2 | 3 | from aiokafka.structs import ConsumerRecord 4 | 5 | from propan.brokers._model.broker_usecase import HandlerCallable, T_HandlerReturn 6 | from propan.brokers._model.routing import BrokerRouter 7 | from propan.types import AnyDict 8 | 9 | 10 | class KafkaRouter(BrokerRouter[ConsumerRecord]): 11 | def handle( # type: ignore[override] 12 | self, 13 | *topics: str, 14 | **kwargs: AnyDict, 15 | ) -> Callable[ 16 | [HandlerCallable[T_HandlerReturn]], 17 | Callable[[ConsumerRecord, bool], Awaitable[T_HandlerReturn]], 18 | ]: 19 | return super().handle(*[self.prefix + t for t in topics], **kwargs) 20 | -------------------------------------------------------------------------------- /propan/brokers/nats/__init__.py: -------------------------------------------------------------------------------- 1 | from nats.js.api import DeliverPolicy 2 | 3 | from propan.brokers.nats.nats_broker import NatsBroker, NatsMessage 4 | from propan.brokers.nats.nats_js_broker import NatsJSBroker 5 | from propan.brokers.nats.routing import NatsRouter 6 | 7 | __all__ = ( 8 | "NatsBroker", 9 | "NatsMessage", 10 | "DeliverPolicy", 11 | "NatsJSBroker", 12 | "NatsRouter", 13 | ) 14 | -------------------------------------------------------------------------------- /propan/brokers/nats/consts.py: -------------------------------------------------------------------------------- 1 | from nats.js.api import ( 2 | ConsumerConfig, 3 | DeliverPolicy, 4 | DiscardPolicy, 5 | Placement, 6 | RePublish, 7 | RetentionPolicy, 8 | StorageType, 9 | StreamConfig, 10 | StreamSource, 11 | ) 12 | 13 | __all__ = ( 14 | "StreamConfig", 15 | "RetentionPolicy", 16 | "DiscardPolicy", 17 | "Placement", 18 | "StorageType", 19 | "StreamSource", 20 | "RePublish", 21 | "ConsumerConfig", 22 | "DeliverPolicy", 23 | ) 24 | -------------------------------------------------------------------------------- /propan/brokers/nats/routing.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Awaitable, Callable 2 | 3 | from nats.aio.msg import Msg 4 | 5 | from propan.brokers._model.broker_usecase import HandlerCallable, T_HandlerReturn 6 | from propan.brokers._model.routing import BrokerRouter 7 | from propan.types import AnyDict 8 | 9 | 10 | class NatsRouter(BrokerRouter[Msg]): 11 | def handle( 12 | self, 13 | subject: str, 14 | *args: Any, 15 | **kwargs: AnyDict, 16 | ) -> Callable[ 17 | [HandlerCallable[T_HandlerReturn]], 18 | Callable[[Msg, bool], Awaitable[T_HandlerReturn]], 19 | ]: 20 | return super().handle(self.prefix + subject, *args, **kwargs) 21 | -------------------------------------------------------------------------------- /propan/brokers/nats/routing.pyi: -------------------------------------------------------------------------------- 1 | from typing import Awaitable, Callable, Sequence, Union 2 | 3 | from fast_depends.dependencies import Depends 4 | from nats.aio.msg import Msg 5 | 6 | from propan.brokers._model.broker_usecase import ( 7 | AsyncDecoder, 8 | AsyncParser, 9 | HandlerCallable, 10 | T_HandlerReturn, 11 | ) 12 | from propan.brokers._model.routing import BrokerRouter 13 | 14 | class NatsRouter(BrokerRouter[Msg]): 15 | def handle( # type: ignore[override] 16 | self, 17 | subject: str, 18 | queue: str = "", 19 | *, 20 | retry: Union[bool, int] = False, 21 | dependencies: Sequence[Depends] = (), 22 | decode_message: AsyncDecoder[Msg] = None, 23 | parse_message: AsyncParser[Msg] = None, 24 | description: str = "", 25 | ) -> Callable[ 26 | [HandlerCallable[T_HandlerReturn]], 27 | Callable[[Msg, bool], Awaitable[T_HandlerReturn]], 28 | ]: ... 29 | -------------------------------------------------------------------------------- /propan/brokers/rabbit/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.brokers.rabbit.rabbit_broker import RabbitBroker, RabbitMessage 2 | from propan.brokers.rabbit.routing import RabbitRouter 3 | from propan.brokers.rabbit.schemas import ExchangeType, RabbitExchange, RabbitQueue 4 | 5 | __all__ = ( 6 | "RabbitBroker", 7 | "RabbitQueue", 8 | "RabbitRouter", 9 | "RabbitExchange", 10 | "ExchangeType", 11 | "RabbitMessage", 12 | ) 13 | -------------------------------------------------------------------------------- /propan/brokers/redis/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.brokers.redis.redis_broker import RedisBroker, RedisMessage 2 | from propan.brokers.redis.routing import RedisRouter 3 | 4 | __all__ = ( 5 | "RedisBroker", 6 | "RedisMessage", 7 | "RedisRouter", 8 | ) 9 | -------------------------------------------------------------------------------- /propan/brokers/redis/routing.py: -------------------------------------------------------------------------------- 1 | from typing import Awaitable, Callable 2 | 3 | from propan.brokers._model.broker_usecase import HandlerCallable, T_HandlerReturn 4 | from propan.brokers._model.routing import BrokerRouter 5 | from propan.types import AnyDict 6 | 7 | 8 | class RedisRouter(BrokerRouter[AnyDict]): 9 | def handle( # type: ignore[override] 10 | self, 11 | channel: str, 12 | **kwargs: AnyDict, 13 | ) -> Callable[ 14 | [HandlerCallable[T_HandlerReturn]], 15 | Callable[[AnyDict, bool], Awaitable[T_HandlerReturn]], 16 | ]: 17 | return super().handle(self.prefix + channel, **kwargs) 18 | -------------------------------------------------------------------------------- /propan/brokers/redis/routing.pyi: -------------------------------------------------------------------------------- 1 | from typing import Awaitable, Callable, Sequence 2 | 3 | from fast_depends.dependencies import Depends 4 | 5 | from propan.brokers._model.broker_usecase import ( 6 | AsyncDecoder, 7 | AsyncParser, 8 | HandlerCallable, 9 | T_HandlerReturn, 10 | ) 11 | from propan.brokers._model.routing import BrokerRouter 12 | from propan.types import AnyDict 13 | 14 | class RedisRouter(BrokerRouter[AnyDict]): 15 | def handle( # type: ignore[override] 16 | self, 17 | channel: str, 18 | *, 19 | pattern: bool = False, 20 | dependencies: Sequence[Depends] = (), 21 | decode_message: AsyncDecoder[AnyDict] = None, 22 | parse_message: AsyncParser[AnyDict] = None, 23 | description: str = "", 24 | ) -> Callable[ 25 | [HandlerCallable[T_HandlerReturn]], 26 | Callable[[AnyDict, bool], Awaitable[T_HandlerReturn]], 27 | ]: ... 28 | -------------------------------------------------------------------------------- /propan/brokers/sqs/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.brokers.sqs.routing import SQSRouter 2 | from propan.brokers.sqs.schema import ( 3 | FifoQueue, 4 | RedriveAllowPolicy, 5 | RedrivePolicy, 6 | SQSQueue, 7 | ) 8 | from propan.brokers.sqs.sqs_broker import SQSBroker, SQSMessage 9 | 10 | __all__ = ( 11 | "SQSBroker", 12 | "SQSQueue", 13 | "SQSRouter", 14 | "RedrivePolicy", 15 | "RedriveAllowPolicy", 16 | "FifoQueue", 17 | "SQSMessage", 18 | ) 19 | -------------------------------------------------------------------------------- /propan/brokers/sqs/routing.py: -------------------------------------------------------------------------------- 1 | from typing import Awaitable, Callable, Union 2 | 3 | from propan._compat import model_copy 4 | from propan.brokers._model.broker_usecase import HandlerCallable, T_HandlerReturn 5 | from propan.brokers._model.routing import BrokerRouter 6 | from propan.brokers.sqs.schema import SQSQueue 7 | from propan.types import AnyDict 8 | 9 | 10 | class SQSRouter(BrokerRouter[AnyDict]): 11 | def handle( # type: ignore[override] 12 | self, 13 | queue: Union[str, SQSQueue], 14 | **kwargs: AnyDict, 15 | ) -> Callable[ 16 | [HandlerCallable[T_HandlerReturn]], 17 | Callable[[AnyDict, bool], Awaitable[T_HandlerReturn]], 18 | ]: 19 | if isinstance(queue, str): 20 | queue = SQSQueue(queue) 21 | 22 | return super().handle( 23 | model_copy(queue, update={"name": self.prefix + queue.name}), **kwargs 24 | ) 25 | -------------------------------------------------------------------------------- /propan/cli/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.cli.main import cli 2 | 3 | __all__ = ("cli",) 4 | -------------------------------------------------------------------------------- /propan/cli/docs/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.cli.docs.app import docs_app 2 | 3 | __all__ = ("docs_app",) 4 | -------------------------------------------------------------------------------- /propan/cli/startproject/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.cli.startproject.app import create_app 2 | 3 | __all__ = ("create_app",) 4 | -------------------------------------------------------------------------------- /propan/cli/startproject/app.py: -------------------------------------------------------------------------------- 1 | import typer 2 | 3 | from propan.cli.startproject.async_app import async_app 4 | from propan.cli.startproject.sync_app import sync_app 5 | 6 | create_app = typer.Typer(pretty_exceptions_short=True) 7 | create_app.add_typer(async_app, name="async", help="Create an asynchronous app") 8 | create_app.add_typer(sync_app, name="sync", help="Create a synchronous app") 9 | -------------------------------------------------------------------------------- /propan/cli/startproject/async_app/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.cli.startproject.async_app.app import async_app 2 | 3 | __all__ = ("async_app",) 4 | -------------------------------------------------------------------------------- /propan/cli/startproject/sync_app/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.cli.startproject.sync_app.app import sync_app 2 | 3 | __all__ = ("sync_app",) 4 | -------------------------------------------------------------------------------- /propan/cli/startproject/sync_app/app.py: -------------------------------------------------------------------------------- 1 | import typer 2 | 3 | sync_app = typer.Typer(pretty_exceptions_short=True) 4 | -------------------------------------------------------------------------------- /propan/cli/startproject/utils.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | 4 | def touch_dir(dir: Path) -> Path: 5 | if not dir.exists(): # pragma: no branch 6 | dir.mkdir() 7 | return dir 8 | 9 | 10 | def write_file(path: Path, *content: str) -> None: 11 | path.touch() 12 | if content: 13 | path.write_text("\n".join(content)) 14 | -------------------------------------------------------------------------------- /propan/cli/supervisors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/propan/cli/supervisors/__init__.py -------------------------------------------------------------------------------- /propan/cli/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/propan/cli/utils/__init__.py -------------------------------------------------------------------------------- /propan/fastapi/core/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.fastapi.core.route import PropanMessage, PropanRoute 2 | from propan.fastapi.core.router import PropanRouter 3 | 4 | __all__ = ( 5 | "PropanMessage", 6 | "PropanRoute", 7 | "PropanRouter", 8 | ) 9 | -------------------------------------------------------------------------------- /propan/fastapi/kafka/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.fastapi.kafka.router import KafkaRouter 2 | 3 | __all__ = ("KafkaRouter",) 4 | -------------------------------------------------------------------------------- /propan/fastapi/kafka/router.py: -------------------------------------------------------------------------------- 1 | from aiokafka.structs import ConsumerRecord 2 | 3 | from propan.brokers.kafka import KafkaBroker 4 | from propan.fastapi.core.router import PropanRouter 5 | 6 | 7 | class KafkaRouter(PropanRouter[KafkaBroker, ConsumerRecord]): 8 | broker_class = KafkaBroker 9 | -------------------------------------------------------------------------------- /propan/fastapi/nats/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.fastapi.nats.router import NatsJSRouter, NatsRouter 2 | 3 | __all__ = ( 4 | "NatsRouter", 5 | "NatsJSRouter", 6 | ) 7 | -------------------------------------------------------------------------------- /propan/fastapi/nats/router.py: -------------------------------------------------------------------------------- 1 | from nats.aio.msg import Msg 2 | 3 | from propan.brokers.nats import NatsBroker, NatsJSBroker 4 | from propan.fastapi.core.router import PropanRouter 5 | 6 | 7 | class NatsRouter(PropanRouter[NatsBroker, Msg]): 8 | broker_class = NatsBroker 9 | 10 | 11 | class NatsJSRouter(PropanRouter[NatsJSBroker, Msg]): 12 | broker_class = NatsJSBroker 13 | -------------------------------------------------------------------------------- /propan/fastapi/rabbit/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.fastapi.rabbit.router import RabbitRouter 2 | 3 | __all__ = ("RabbitRouter",) 4 | -------------------------------------------------------------------------------- /propan/fastapi/rabbit/router.py: -------------------------------------------------------------------------------- 1 | from aio_pika.message import IncomingMessage 2 | 3 | from propan.brokers.rabbit import RabbitBroker 4 | from propan.fastapi.core.router import PropanRouter 5 | 6 | 7 | class RabbitRouter(PropanRouter[RabbitBroker, IncomingMessage]): 8 | broker_class = RabbitBroker 9 | -------------------------------------------------------------------------------- /propan/fastapi/redis/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.fastapi.redis.router import RedisRouter 2 | 3 | __all__ = ("RedisRouter",) 4 | -------------------------------------------------------------------------------- /propan/fastapi/redis/router.py: -------------------------------------------------------------------------------- 1 | from propan.brokers.redis import RedisBroker 2 | from propan.fastapi.core.router import PropanRouter 3 | from propan.types import AnyDict 4 | 5 | 6 | class RedisRouter(PropanRouter[RedisBroker, AnyDict]): 7 | broker_class = RedisBroker 8 | -------------------------------------------------------------------------------- /propan/fastapi/sqs/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.fastapi.sqs.router import SQSRouter 2 | 3 | __all__ = ("SQSRouter",) 4 | -------------------------------------------------------------------------------- /propan/fastapi/sqs/router.py: -------------------------------------------------------------------------------- 1 | from propan.brokers.sqs import SQSBroker 2 | from propan.fastapi.core.router import PropanRouter 3 | from propan.types import AnyDict 4 | 5 | 6 | class SQSRouter(PropanRouter[SQSBroker, AnyDict]): 7 | broker_class = SQSBroker 8 | -------------------------------------------------------------------------------- /propan/log/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.log.logging import access_logger, logger 2 | 3 | __all__ = ( 4 | "logger", 5 | "access_logger", 6 | ) 7 | -------------------------------------------------------------------------------- /propan/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/propan/py.typed -------------------------------------------------------------------------------- /propan/test/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Optional 2 | 3 | import anyio 4 | 5 | from propan.brokers._model.schemas import BaseHandler 6 | 7 | 8 | async def call_handler( 9 | handler: BaseHandler, 10 | message: Any, 11 | callback: bool = False, 12 | callback_timeout: Optional[float] = 30.0, 13 | raise_timeout: bool = False, 14 | ) -> Any: 15 | if raise_timeout: 16 | scope = anyio.fail_after 17 | else: 18 | scope = anyio.move_on_after 19 | 20 | result: Any = None 21 | with scope(callback_timeout): 22 | result = await handler.callback(message) 23 | 24 | if callback is True: # pragma: no branch 25 | return result 26 | -------------------------------------------------------------------------------- /propan/types.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Any, Awaitable, Callable, Dict, Sequence, Union 3 | 4 | from pydantic import BaseModel 5 | from typing_extensions import TypeAlias 6 | 7 | AsyncFunc: TypeAlias = Callable[..., Awaitable[Any]] 8 | 9 | AnyDict: TypeAlias = Dict[str, Any] 10 | AnyCallable: TypeAlias = Callable[..., Any] 11 | NoneCallable: TypeAlias = Callable[..., None] 12 | 13 | DecoratedCallable: TypeAlias = AnyCallable 14 | DecoratedCallableNone: TypeAlias = NoneCallable 15 | 16 | JsonDecodable: TypeAlias = Union[ 17 | float, 18 | int, 19 | bool, 20 | str, 21 | bytes, 22 | ] 23 | DecodedMessage: TypeAlias = Union[ 24 | Dict[str, JsonDecodable], Sequence[JsonDecodable], JsonDecodable 25 | ] 26 | SendableMessage: TypeAlias = Union[ 27 | datetime, 28 | DecodedMessage, 29 | BaseModel, 30 | None, 31 | ] 32 | -------------------------------------------------------------------------------- /propan/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from fast_depends import Depends 2 | from fast_depends import inject as apply_types 3 | 4 | from propan.utils.context import Context, ContextRepo, context 5 | from propan.utils.no_cast import NoCast 6 | 7 | __all__ = ( 8 | "apply_types", 9 | "context", 10 | "Context", 11 | "ContextRepo", 12 | "Depends", 13 | "NoCast", 14 | ) 15 | -------------------------------------------------------------------------------- /propan/utils/classes.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | 4 | class Singleton: 5 | _instance = None 6 | 7 | def __new__(cls, *args: Any, **kwargs: Any) -> "Singleton": 8 | if cls._instance is None: 9 | cls._instance = super().__new__(cls) 10 | return cls._instance 11 | 12 | @classmethod 13 | def _drop(cls) -> None: 14 | cls._instance = None 15 | -------------------------------------------------------------------------------- /propan/utils/context/__init__.py: -------------------------------------------------------------------------------- 1 | from propan.utils.context.main import ContextRepo, context 2 | from propan.utils.context.types import Context 3 | 4 | __all__ = ( 5 | "Context", 6 | "context", 7 | "ContextRepo", 8 | ) 9 | -------------------------------------------------------------------------------- /propan/utils/no_cast.py: -------------------------------------------------------------------------------- 1 | from fast_depends.library import CustomField 2 | 3 | from propan.types import AnyDict 4 | 5 | 6 | class NoCast(CustomField): # type: ignore 7 | def __init__(self) -> None: 8 | super().__init__(cast=False) 9 | 10 | def use(self, **kwargs: AnyDict) -> AnyDict: 11 | return kwargs 12 | -------------------------------------------------------------------------------- /requirements.docs.txt: -------------------------------------------------------------------------------- 1 | mkdocs-material==9.5.21 2 | mkdocs-static-i18n==0.56 3 | mdx-include==1.4.2 4 | mkdocs-macros-plugin==1.0.5 5 | # images zoom 6 | mkdocs-glightbox 7 | pillow 8 | cairosvg -------------------------------------------------------------------------------- /requirements.lint.txt: -------------------------------------------------------------------------------- 1 | types-redis 2 | types-PyYAML 3 | 4 | mypy==1.4.1 5 | black==23.3.0 6 | isort>=5 7 | ruff==0.0.275 8 | pyupgrade-directories 9 | -------------------------------------------------------------------------------- /requirements.test.txt: -------------------------------------------------------------------------------- 1 | coverage[toml]>=7.2 2 | pytest==7.4.0 3 | pytest-asyncio>=0.21 4 | asyncmock 5 | pydantic_settings 6 | 7 | pydantic-settings>=2.0a1 8 | python-dotenv 9 | fastapi>=0.100.0b 10 | httpx 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -e . 2 | -e .[doc] 3 | -e .[async-rabbit] 4 | -e .[async-nats] 5 | -e .[async-sqs] 6 | -e .[async-kafka] 7 | -e .[async-redis] 8 | 9 | -r requirements.test.txt 10 | -r requirements.docs.txt 11 | -r requirements.lint.txt 12 | 13 | typer[all] 14 | -------------------------------------------------------------------------------- /scripts/lint.sh: -------------------------------------------------------------------------------- 1 | pyup_dirs --py37-plus --recursive propan tests 2 | mypy propan 3 | ruff propan examples tests --fix 4 | black propan examples tests 5 | isort propan examples tests -------------------------------------------------------------------------------- /scripts/publish.sh: -------------------------------------------------------------------------------- 1 | hatch clean 2 | hatch build 3 | hatch publish -------------------------------------------------------------------------------- /scripts/test-cov.sh: -------------------------------------------------------------------------------- 1 | bash scripts/test.sh "$@" 2 | coverage run -m pytest -m "run and rabbit" tests/cli/test_run.py 3 | coverage run -m pytest -m "run and kafka" tests/cli/test_run.py 4 | coverage run -m pytest -m "run and sqs" tests/cli/test_run.py 5 | coverage run -m pytest -m "run and redis" tests/cli/test_run.py 6 | coverage run -m pytest -m "run and nats" tests/cli/test_run.py 7 | 8 | coverage combine 9 | coverage report --show-missing 10 | 11 | rm .coverage* -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | coverage run -m pytest -m "all and not run" "$@" || coverage run -m pytest -m "all and not run" "$@" -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/__init__.py -------------------------------------------------------------------------------- /tests/asyncapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/asyncapi/__init__.py -------------------------------------------------------------------------------- /tests/asyncapi/handler/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/asyncapi/handler/__init__.py -------------------------------------------------------------------------------- /tests/asyncapi/kafka/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/asyncapi/kafka/__init__.py -------------------------------------------------------------------------------- /tests/asyncapi/kafka/test_server.py: -------------------------------------------------------------------------------- 1 | from propan import KafkaBroker, PropanApp 2 | from propan.cli.docs.gen import gen_app_schema_json 3 | 4 | 5 | def test_server_info(): 6 | schema = gen_app_schema_json(PropanApp(KafkaBroker())) 7 | assert schema["servers"]["dev"] == { 8 | "protocol": "kafka", 9 | "url": "localhost", 10 | "protocolVersion": "auto", 11 | } 12 | 13 | 14 | def test_server_custom_info(): 15 | schema = gen_app_schema_json( 16 | PropanApp( 17 | KafkaBroker( 18 | "kafka:9092", 19 | protocol="kafka-secury", 20 | api_version="1.0.0", 21 | ) 22 | ) 23 | ) 24 | assert schema["servers"]["dev"] == { 25 | "protocol": "kafka-secury", 26 | "url": "kafka:9092", 27 | "protocolVersion": "1.0.0", 28 | } 29 | -------------------------------------------------------------------------------- /tests/asyncapi/nats/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/asyncapi/nats/__init__.py -------------------------------------------------------------------------------- /tests/asyncapi/nats/test_server.py: -------------------------------------------------------------------------------- 1 | from propan import NatsBroker, PropanApp 2 | from propan.cli.docs.gen import gen_app_schema_json 3 | 4 | 5 | def test_server_info(): 6 | schema = gen_app_schema_json(PropanApp(NatsBroker())) 7 | assert schema["servers"]["dev"] == { 8 | "protocol": "nats", 9 | "url": "nats://localhost:4222", 10 | } 11 | -------------------------------------------------------------------------------- /tests/asyncapi/rabbit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/asyncapi/rabbit/__init__.py -------------------------------------------------------------------------------- /tests/asyncapi/rabbit/test_server.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RabbitBroker 2 | from propan.cli.docs.gen import gen_app_schema_json 3 | 4 | 5 | def test_server_info(): 6 | schema = gen_app_schema_json(PropanApp(RabbitBroker())) 7 | assert schema["servers"]["dev"] == { 8 | "protocol": "amqp", 9 | "url": "amqp://guest:guest@localhost:5672/", 10 | "protocolVersion": "0.9.1", 11 | } 12 | 13 | 14 | def test_server_custom_info(): 15 | schema = gen_app_schema_json( 16 | PropanApp( 17 | RabbitBroker( 18 | "amqps://rabbithost.com", protocol="amqps", protocol_version="0.8.0" 19 | ) 20 | ) 21 | ) 22 | assert schema["servers"]["dev"] == { 23 | "protocol": "amqps", 24 | "url": "amqps://rabbithost.com", 25 | "protocolVersion": "0.8.0", 26 | } 27 | -------------------------------------------------------------------------------- /tests/asyncapi/redis/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/asyncapi/redis/__init__.py -------------------------------------------------------------------------------- /tests/asyncapi/redis/test_server.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, RedisBroker 2 | from propan.cli.docs.gen import gen_app_schema_json 3 | 4 | 5 | def test_server_info(): 6 | schema = gen_app_schema_json(PropanApp(RedisBroker())) 7 | assert schema["servers"]["dev"] == { 8 | "protocol": "redis", 9 | "url": "redis://localhost:6379", 10 | } 11 | -------------------------------------------------------------------------------- /tests/asyncapi/sqs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/asyncapi/sqs/__init__.py -------------------------------------------------------------------------------- /tests/asyncapi/sqs/test_server.py: -------------------------------------------------------------------------------- 1 | from propan import PropanApp, SQSBroker 2 | from propan.cli.docs.gen import gen_app_schema_json 3 | 4 | 5 | def test_server_info(): 6 | schema = gen_app_schema_json(PropanApp(SQSBroker())) 7 | assert schema["servers"]["dev"] == { 8 | "protocol": "sqs", 9 | "url": "http://localhost:9324/", 10 | } 11 | -------------------------------------------------------------------------------- /tests/brokers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/brokers/__init__.py -------------------------------------------------------------------------------- /tests/brokers/base/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/brokers/base/__init__.py -------------------------------------------------------------------------------- /tests/brokers/conftest.py: -------------------------------------------------------------------------------- 1 | from uuid import uuid4 2 | 3 | import pytest 4 | 5 | 6 | @pytest.fixture 7 | def queue(): 8 | return str(uuid4()) 9 | -------------------------------------------------------------------------------- /tests/brokers/kafka/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/brokers/kafka/__init__.py -------------------------------------------------------------------------------- /tests/brokers/kafka/test_connect.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from propan import KafkaBroker 4 | from tests.brokers.base.connection import BrokerConnectionTestcase 5 | 6 | 7 | @pytest.mark.kafka 8 | class TestKafkaConnect(BrokerConnectionTestcase): 9 | broker = KafkaBroker 10 | 11 | @pytest.mark.asyncio 12 | async def test_connect_merge_args_and_kwargs_native(self, settings): 13 | broker = self.broker("fake-url") # will be ignored 14 | assert await broker.connect(bootstrap_servers=settings.url) 15 | await broker.close() 16 | -------------------------------------------------------------------------------- /tests/brokers/kafka/test_consume.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.consume import BrokerConsumeTestcase 4 | 5 | 6 | @pytest.mark.kafka 7 | @pytest.mark.slow 8 | class TestKafkaConsume(BrokerConsumeTestcase): 9 | pass 10 | -------------------------------------------------------------------------------- /tests/brokers/kafka/test_middleware.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from propan.test.kafka import build_message 4 | from tests.brokers.base.middlewares import MiddlewareTestCase 5 | 6 | 7 | @pytest.mark.kafka 8 | class TestKafkaMiddleware(MiddlewareTestCase): 9 | build_message = staticmethod(build_message) 10 | -------------------------------------------------------------------------------- /tests/brokers/kafka/test_router.py: -------------------------------------------------------------------------------- 1 | from propan.test.kafka import build_message 2 | from tests.brokers.base.router import RouterTestcase 3 | 4 | 5 | class TestKafkaRouter(RouterTestcase): 6 | build_message = staticmethod(build_message) 7 | -------------------------------------------------------------------------------- /tests/brokers/kafka/test_rpc.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.rpc import BrokerRPCTestcase 4 | 5 | 6 | @pytest.mark.kafka 7 | @pytest.mark.slow 8 | class TestKafkaRPC(BrokerRPCTestcase): 9 | pass 10 | -------------------------------------------------------------------------------- /tests/brokers/kafka/test_test_client.py: -------------------------------------------------------------------------------- 1 | from propan.test.kafka import build_message 2 | from tests.brokers.base.testclient import BrokerTestclientTestcase 3 | 4 | 5 | class TestKafkaTestclient(BrokerTestclientTestcase): 6 | build_message = staticmethod(build_message) 7 | -------------------------------------------------------------------------------- /tests/brokers/nats/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/brokers/nats/__init__.py -------------------------------------------------------------------------------- /tests/brokers/nats/test_connect.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from propan import NatsBroker 4 | from tests.brokers.base.connection import BrokerConnectionTestcase 5 | 6 | 7 | @pytest.mark.nats 8 | class TestNatsConnect(BrokerConnectionTestcase): 9 | broker = NatsBroker 10 | 11 | @pytest.mark.asyncio 12 | async def test_connect_merge_args_and_kwargs_native(self, settings): 13 | broker = self.broker("fake-url") # will be ignored 14 | assert await broker.connect(servers=settings.url) 15 | await broker.close() 16 | -------------------------------------------------------------------------------- /tests/brokers/nats/test_consume.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.consume import BrokerConsumeTestcase 4 | 5 | 6 | @pytest.mark.nats 7 | class TestNatsConsume(BrokerConsumeTestcase): 8 | pass 9 | -------------------------------------------------------------------------------- /tests/brokers/nats/test_middleware.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from propan.test.nats import build_message 4 | from tests.brokers.base.middlewares import MiddlewareTestCase 5 | 6 | 7 | @pytest.mark.nats 8 | class TestNatsMiddleware(MiddlewareTestCase): 9 | build_message = staticmethod(build_message) 10 | -------------------------------------------------------------------------------- /tests/brokers/nats/test_publish.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.publish import BrokerPublishTestcase 4 | 5 | 6 | @pytest.mark.nats 7 | class TestNatsPublish(BrokerPublishTestcase): 8 | pass 9 | -------------------------------------------------------------------------------- /tests/brokers/nats/test_router.py: -------------------------------------------------------------------------------- 1 | from propan.test.nats import build_message 2 | from tests.brokers.base.router import RouterTestcase 3 | 4 | 5 | class TestNatsRouter(RouterTestcase): 6 | build_message = staticmethod(build_message) 7 | -------------------------------------------------------------------------------- /tests/brokers/nats/test_rpc.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.rpc import BrokerRPCTestcase, ReplyAndConsumeForbidden 4 | 5 | 6 | @pytest.mark.nats 7 | class TestNatsRPC(BrokerRPCTestcase, ReplyAndConsumeForbidden): 8 | pass 9 | -------------------------------------------------------------------------------- /tests/brokers/nats/test_test_client.py: -------------------------------------------------------------------------------- 1 | from propan.test.nats import build_message 2 | from tests.brokers.base.testclient import BrokerTestclientTestcase 3 | 4 | 5 | class TestNatsTestclient(BrokerTestclientTestcase): 6 | build_message = staticmethod(build_message) 7 | -------------------------------------------------------------------------------- /tests/brokers/nats_js/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/brokers/nats_js/__init__.py -------------------------------------------------------------------------------- /tests/brokers/nats_js/test_connect.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from nats.js.client import JetStreamContext 3 | 4 | from propan import NatsJSBroker 5 | from tests.brokers.base.connection import BrokerConnectionTestcase 6 | 7 | 8 | @pytest.mark.nats 9 | class TestNatsJSConnect(BrokerConnectionTestcase): 10 | broker = NatsJSBroker 11 | 12 | @pytest.mark.asyncio 13 | async def test_connect_merge_args_and_kwargs_native(self, settings): 14 | broker = self.broker("fake-url") # will be ignored 15 | assert await broker.connect(servers=settings.url) 16 | await broker.close() 17 | 18 | @pytest.mark.asyncio 19 | async def test_js_exists(self, settings): 20 | broker = self.broker(settings.url) 21 | assert await broker.connect() 22 | assert isinstance(broker.js, JetStreamContext) 23 | await broker.close() 24 | -------------------------------------------------------------------------------- /tests/brokers/nats_js/test_middleware.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from propan.test.nats import build_message 4 | from tests.brokers.base.middlewares import MiddlewareTestCase 5 | 6 | 7 | @pytest.mark.nats 8 | class TestNatsMiddleware(MiddlewareTestCase): 9 | build_message = staticmethod(build_message) 10 | -------------------------------------------------------------------------------- /tests/brokers/nats_js/test_publish.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.publish import BrokerPublishTestcase 4 | 5 | 6 | @pytest.mark.nats 7 | class TestNatsJSPublish(BrokerPublishTestcase): 8 | pass 9 | -------------------------------------------------------------------------------- /tests/brokers/nats_js/test_router.py: -------------------------------------------------------------------------------- 1 | from propan.test.nats import build_message 2 | from tests.brokers.base.router import RouterTestcase 3 | 4 | 5 | class TestNatsJSRouter(RouterTestcase): 6 | build_message = staticmethod(build_message) 7 | -------------------------------------------------------------------------------- /tests/brokers/nats_js/test_rpc.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.rpc import BrokerRPCTestcase, ReplyAndConsumeForbidden 4 | 5 | 6 | @pytest.mark.nats 7 | class TestNatsJSRPC(BrokerRPCTestcase, ReplyAndConsumeForbidden): 8 | pass 9 | -------------------------------------------------------------------------------- /tests/brokers/rabbit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/brokers/rabbit/__init__.py -------------------------------------------------------------------------------- /tests/brokers/rabbit/test_init.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from propan.brokers.rabbit import RabbitBroker 4 | 5 | 6 | @pytest.mark.asyncio 7 | @pytest.mark.rabbit 8 | async def test_set_max(): 9 | broker = RabbitBroker(logger=None, consumers=10) 10 | await broker.start() 11 | assert broker._channel._prefetch_count == 10 12 | await broker.close() 13 | -------------------------------------------------------------------------------- /tests/brokers/rabbit/test_middleware.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from propan.test.rabbit import build_message 4 | from tests.brokers.base.middlewares import MiddlewareTestCase 5 | 6 | 7 | @pytest.mark.rabbit 8 | class TestRabbitMiddleware(MiddlewareTestCase): 9 | build_message = staticmethod(build_message) 10 | -------------------------------------------------------------------------------- /tests/brokers/rabbit/test_publish.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.publish import BrokerPublishTestcase 4 | 5 | 6 | @pytest.mark.rabbit 7 | class TestRabbitPublish(BrokerPublishTestcase): 8 | pass 9 | -------------------------------------------------------------------------------- /tests/brokers/rabbit/test_rpc.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.rpc import BrokerRPCTestcase, ReplyAndConsumeForbidden 4 | 5 | 6 | @pytest.mark.rabbit 7 | class TestRabbitRPC(BrokerRPCTestcase, ReplyAndConsumeForbidden): 8 | pass 9 | -------------------------------------------------------------------------------- /tests/brokers/redis/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/brokers/redis/__init__.py -------------------------------------------------------------------------------- /tests/brokers/redis/test_middleware.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from propan.test.redis import build_message 4 | from tests.brokers.base.middlewares import MiddlewareTestCase 5 | 6 | 7 | @pytest.mark.redis 8 | class TestRedisMiddleware(MiddlewareTestCase): 9 | build_message = staticmethod(build_message) 10 | -------------------------------------------------------------------------------- /tests/brokers/redis/test_publish.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.publish import BrokerPublishTestcase 4 | 5 | 6 | @pytest.mark.redis 7 | class TestRedisPublish(BrokerPublishTestcase): 8 | pass 9 | -------------------------------------------------------------------------------- /tests/brokers/redis/test_router.py: -------------------------------------------------------------------------------- 1 | from propan.test.redis import build_message 2 | from tests.brokers.base.router import RouterTestcase 3 | 4 | 5 | class TestRedisRouter(RouterTestcase): 6 | build_message = staticmethod(build_message) 7 | -------------------------------------------------------------------------------- /tests/brokers/redis/test_rpc.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.rpc import BrokerRPCTestcase, ReplyAndConsumeForbidden 4 | 5 | 6 | @pytest.mark.redis 7 | class TestRedisRPC(BrokerRPCTestcase, ReplyAndConsumeForbidden): 8 | pass 9 | -------------------------------------------------------------------------------- /tests/brokers/redis/test_test_client.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from propan import RedisBroker 4 | from propan.test.redis import build_message 5 | from tests.brokers.base.testclient import BrokerTestclientTestcase 6 | 7 | 8 | class TestKafkaTestclient(BrokerTestclientTestcase): 9 | build_message = staticmethod(build_message) 10 | 11 | @pytest.mark.asyncio 12 | async def test_pattern_consume(self, queue: str, test_broker: RedisBroker): 13 | @test_broker.handle(".*", pattern=True) 14 | async def m(): # pragma: no cover 15 | return 1 16 | 17 | async with test_broker: 18 | await test_broker.start() 19 | 20 | assert ( 21 | await test_broker.publish( 22 | message="hello", 23 | channel=queue, 24 | callback=True, 25 | ) 26 | ) == 1 27 | -------------------------------------------------------------------------------- /tests/brokers/sqs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/brokers/sqs/__init__.py -------------------------------------------------------------------------------- /tests/brokers/sqs/test_connect.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from aiobotocore.config import AioConfig 3 | from botocore import UNSIGNED 4 | 5 | from propan import SQSBroker 6 | from tests.brokers.base.connection import BrokerConnectionTestcase 7 | 8 | 9 | @pytest.mark.sqs 10 | class TestSQSConnection(BrokerConnectionTestcase): 11 | broker = SQSBroker 12 | 13 | def get_broker_args(self, settings): 14 | return (settings.url,), { 15 | "region_name": settings.region_name, 16 | "config": AioConfig(signature_version=UNSIGNED), 17 | } 18 | 19 | @pytest.mark.asyncio 20 | async def test_connect_merge_args_and_kwargs_native(self, settings): 21 | args, kwargs = self.get_broker_args(settings) 22 | broker = self.broker("fake-url") # will be ignored 23 | assert await broker.connect(url=settings.url, **kwargs) 24 | await broker.close() 25 | -------------------------------------------------------------------------------- /tests/brokers/sqs/test_consume.py: -------------------------------------------------------------------------------- 1 | from asyncio import Event, wait_for 2 | from unittest.mock import Mock 3 | 4 | import pytest 5 | 6 | from propan.brokers.sqs import SQSBroker, SQSQueue 7 | from tests.brokers.base.consume import BrokerConsumeTestcase 8 | 9 | 10 | @pytest.mark.sqs 11 | class TestSQSConsume(BrokerConsumeTestcase): 12 | @pytest.mark.asyncio 13 | async def test_consume_from_sqs_queue( 14 | self, 15 | mock: Mock, 16 | queue: str, 17 | broker: SQSBroker, 18 | ): 19 | consume = Event() 20 | mock.side_effect = lambda *_: consume.set() # pragma: no branch 21 | 22 | async with broker: 23 | broker.handle(SQSQueue(queue), retry=1)(mock) 24 | await broker.start() 25 | await broker.publish("hello", queue) 26 | await wait_for(consume.wait(), 3) 27 | 28 | mock.assert_called_once() 29 | -------------------------------------------------------------------------------- /tests/brokers/sqs/test_middleware.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from propan.test.sqs import build_message 4 | from tests.brokers.base.middlewares import MiddlewareTestCase 5 | 6 | 7 | @pytest.mark.sqs 8 | class TestSQSMiddleware(MiddlewareTestCase): 9 | build_message = staticmethod(build_message) 10 | -------------------------------------------------------------------------------- /tests/brokers/sqs/test_publish.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.publish import BrokerPublishTestcase 4 | 5 | 6 | @pytest.mark.sqs 7 | class TestSQSPublish(BrokerPublishTestcase): 8 | pass 9 | -------------------------------------------------------------------------------- /tests/brokers/sqs/test_rpc.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.brokers.base.rpc import BrokerRPCTestcase 4 | 5 | 6 | @pytest.mark.sqs 7 | class TestSQSRPC(BrokerRPCTestcase): 8 | pass 9 | -------------------------------------------------------------------------------- /tests/brokers/sqs/test_test_client.py: -------------------------------------------------------------------------------- 1 | from propan.test.sqs import build_message 2 | from tests.brokers.base.testclient import BrokerTestclientTestcase 3 | 4 | 5 | class TestSQSTestclient(BrokerTestclientTestcase): 6 | build_message = staticmethod(build_message) 7 | -------------------------------------------------------------------------------- /tests/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/cli/__init__.py -------------------------------------------------------------------------------- /tests/cli/supervisors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/cli/supervisors/__init__.py -------------------------------------------------------------------------------- /tests/cli/supervisors/test_base_reloader.py: -------------------------------------------------------------------------------- 1 | import signal 2 | 3 | import pytest 4 | 5 | from propan.cli.supervisors.basereload import BaseReload 6 | 7 | 8 | class PatchedBaseReload(BaseReload): 9 | def restart(self) -> None: 10 | super().restart() 11 | self.should_exit.set() 12 | 13 | def should_restart(self) -> bool: 14 | return True 15 | 16 | 17 | def empty(): 18 | pass 19 | 20 | 21 | @pytest.mark.slow 22 | def test_base(): 23 | processor = PatchedBaseReload(target=empty, args=()) 24 | 25 | processor._args = (processor.pid,) 26 | processor.run() 27 | 28 | code = abs(processor._process.exitcode) 29 | assert code == signal.SIGTERM.value or code == 0 30 | -------------------------------------------------------------------------------- /tests/cli/supervisors/test_multiprocess.py: -------------------------------------------------------------------------------- 1 | import os 2 | import signal 3 | 4 | import pytest 5 | 6 | from propan.cli.supervisors.multiprocess import Multiprocess 7 | 8 | 9 | def exit(parent_id): # pragma: no cover 10 | os.kill(parent_id, signal.SIGINT) 11 | 12 | 13 | @pytest.mark.slow 14 | def test_base(): 15 | processor = Multiprocess(target=exit, args=(), workers=5) 16 | processor._args = (processor.pid,) 17 | processor.run() 18 | 19 | for p in processor.processes: 20 | code = abs(p.exitcode) 21 | assert code == signal.SIGTERM.value or code == 0 22 | -------------------------------------------------------------------------------- /tests/cli/test_creation.py: -------------------------------------------------------------------------------- 1 | def test_create_rabbit_project(rabbit_async_project): 2 | assert rabbit_async_project.exists() 3 | 4 | 5 | def test_create_redis_project(redis_async_project): 6 | assert redis_async_project.exists() 7 | 8 | 9 | def test_create_nats_project(nats_async_project): 10 | assert nats_async_project.exists() 11 | 12 | 13 | def test_create_nats_js_project(nats_js_async_project): 14 | assert nats_js_async_project.exists() 15 | 16 | 17 | def test_create_kafka_project(kafka_async_project): 18 | assert kafka_async_project.exists() 19 | 20 | 21 | def test_create_sqs_project(sqs_async_project): 22 | assert sqs_async_project.exists() 23 | -------------------------------------------------------------------------------- /tests/cli/test_version.py: -------------------------------------------------------------------------------- 1 | import platform 2 | 3 | from propan.cli.main import cli 4 | 5 | 6 | def test_version(runner, version): 7 | result = runner.invoke(cli, ["--version"]) 8 | assert result.exit_code == 0 9 | assert version in result.stdout 10 | assert platform.python_implementation() in result.stdout 11 | assert platform.python_version() in result.stdout 12 | assert platform.system() in result.stdout 13 | -------------------------------------------------------------------------------- /tests/cli/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/cli/utils/__init__.py -------------------------------------------------------------------------------- /tests/cli/utils/test_imports.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import pytest 4 | 5 | from propan.cli.utils.imports import get_app_path, import_object 6 | 7 | 8 | def test_import_wrong(): 9 | dir, app = get_app_path("tests:test_object") 10 | with pytest.raises(FileNotFoundError) as excinfo: 11 | import_object(dir, app) 12 | 13 | assert f"{dir}.py" in str(excinfo.value) 14 | 15 | 16 | @pytest.mark.parametrize( 17 | "test_input,exp_module,exp_app", 18 | ( 19 | ("module:app", "module", "app"), 20 | ("module.module.module:app", "module/module/module", "app"), 21 | ), 22 | ) 23 | def test_get_app_path(test_input, exp_module, exp_app): 24 | dir, app = get_app_path(test_input) 25 | assert app == exp_app 26 | assert dir == Path.cwd() / exp_module 27 | 28 | 29 | def test_get_app_path_wrong(): 30 | with pytest.raises(ValueError): 31 | get_app_path("module.app") 32 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from unittest.mock import Mock 3 | 4 | if sys.version_info < (3, 8): 5 | from asyncmock import AsyncMock 6 | else: 7 | from unittest.mock import AsyncMock 8 | 9 | import pytest 10 | 11 | from propan.__about__ import __version__ 12 | from propan.utils import context as global_context 13 | 14 | 15 | def pytest_collection_modifyitems(items): 16 | for item in items: 17 | item.add_marker("all") 18 | 19 | 20 | @pytest.fixture 21 | def mock(): 22 | m = Mock() 23 | yield m 24 | m.reset_mock() 25 | 26 | 27 | @pytest.fixture 28 | def async_mock(): 29 | m = AsyncMock() 30 | yield m 31 | m.reset_mock() 32 | 33 | 34 | @pytest.fixture(scope="session") 35 | def version(): 36 | return __version__ 37 | 38 | 39 | @pytest.fixture 40 | def context(): 41 | yield global_context 42 | global_context.clear() 43 | -------------------------------------------------------------------------------- /tests/fastapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/fastapi/__init__.py -------------------------------------------------------------------------------- /tests/fastapi/test_kafka.py: -------------------------------------------------------------------------------- 1 | from propan.fastapi import KafkaRouter 2 | from propan.test.kafka import TestKafkaBroker, build_message 3 | from tests.fastapi.case import FastAPITestcase 4 | 5 | 6 | class TestKafkaRouter(FastAPITestcase): 7 | router_class = KafkaRouter 8 | broker_test = staticmethod(TestKafkaBroker) 9 | build_message = staticmethod(build_message) 10 | -------------------------------------------------------------------------------- /tests/fastapi/test_nats.py: -------------------------------------------------------------------------------- 1 | from propan.fastapi import NatsRouter 2 | from propan.test.nats import TestNatsBroker, build_message 3 | from tests.fastapi.case import FastAPITestcase 4 | 5 | 6 | class TestNatsRouter(FastAPITestcase): 7 | router_class = NatsRouter 8 | broker_test = staticmethod(TestNatsBroker) 9 | build_message = staticmethod(build_message) 10 | -------------------------------------------------------------------------------- /tests/fastapi/test_redis.py: -------------------------------------------------------------------------------- 1 | from propan.fastapi import RedisRouter 2 | from propan.test.redis import TestRedisBroker, build_message 3 | from tests.fastapi.case import FastAPITestcase 4 | 5 | 6 | class TestRedisRouter(FastAPITestcase): 7 | router_class = RedisRouter 8 | broker_test = staticmethod(TestRedisBroker) 9 | build_message = staticmethod(build_message) 10 | -------------------------------------------------------------------------------- /tests/fastapi/test_sqs.py: -------------------------------------------------------------------------------- 1 | from propan.fastapi import SQSRouter 2 | from propan.test.sqs import TestSQSBroker, build_message 3 | from tests.fastapi.case import FastAPITestcase 4 | 5 | 6 | class TestSQSRouter(FastAPITestcase): 7 | router_class = SQSRouter 8 | broker_test = staticmethod(TestSQSBroker) 9 | build_message = staticmethod(build_message) 10 | -------------------------------------------------------------------------------- /tests/log/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/log/__init__.py -------------------------------------------------------------------------------- /tests/log/test_formatter.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from propan.log.formatter import ColourizedFormatter 4 | 5 | 6 | def test_formatter(): 7 | logger = logging.getLogger(__file__) 8 | handler = logging.Handler() 9 | formatter = ColourizedFormatter("%(message)s") 10 | handler.setFormatter(formatter) 11 | logger.addHandler(handler) 12 | 13 | logger.info("Hi") 14 | -------------------------------------------------------------------------------- /tests/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/tools/__init__.py -------------------------------------------------------------------------------- /tests/tools/marks.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | 5 | needs_py310 = pytest.mark.skipif( 6 | sys.version_info < (3, 10), reason="requires python3.10+" 7 | ) 8 | 9 | needs_py38 = pytest.mark.skipif(sys.version_info < (3, 8), reason="requires python3.8+") 10 | 11 | needs_ex_py37 = pytest.mark.skipif( 12 | sys.version_info == (3, 8), reason="requires python3.7" 13 | ) 14 | -------------------------------------------------------------------------------- /tests/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/utils/__init__.py -------------------------------------------------------------------------------- /tests/utils/context/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/utils/context/__init__.py -------------------------------------------------------------------------------- /tests/utils/test_classes.py: -------------------------------------------------------------------------------- 1 | from propan.utils.classes import Singleton 2 | 3 | 4 | def test_singleton(): 5 | assert Singleton() is Singleton() 6 | 7 | 8 | def test_drop(): 9 | s1 = Singleton() 10 | s1._drop() 11 | assert Singleton() is not s1 12 | -------------------------------------------------------------------------------- /tests/utils/test_functions.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from propan.utils.functions import call_or_await 4 | 5 | 6 | def sync_func(a): 7 | return a 8 | 9 | 10 | async def async_func(a): 11 | return a 12 | 13 | 14 | @pytest.mark.asyncio 15 | async def test_call(): 16 | assert (await call_or_await(sync_func, a=3)) == 3 17 | 18 | 19 | @pytest.mark.asyncio 20 | async def test_await(): 21 | assert (await call_or_await(async_func, a=3)) == 3 22 | -------------------------------------------------------------------------------- /tests/utils/test_no_cast.py: -------------------------------------------------------------------------------- 1 | from propan import apply_types 2 | from propan.annotations import NoCast 3 | 4 | 5 | def test_no_cast(): 6 | @apply_types 7 | def handler(s: NoCast[str]): 8 | assert isinstance(s, int) 9 | 10 | handler(1) 11 | -------------------------------------------------------------------------------- /tests/utils/type_cast/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lancetnik/Propan/d76b44a222bbc9f612973f96d530c7ca59bcb056/tests/utils/type_cast/__init__.py -------------------------------------------------------------------------------- /tests/utils/type_cast/test_model.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | import pytest 4 | from pydantic import BaseModel 5 | 6 | from propan.utils import apply_types 7 | 8 | 9 | class Base(BaseModel): 10 | field: int 11 | 12 | 13 | @apply_types 14 | def cast_model(t: Base) -> Tuple[bool, Base]: 15 | return isinstance(t, Base), t 16 | 17 | 18 | def test_model(): 19 | is_casted, m = cast_model({"field": 1}) 20 | assert is_casted, m.field == (True, 1) 21 | 22 | is_casted, m = cast_model(Base(field=1)) 23 | assert is_casted, m.field == (True, 1) 24 | 25 | is_casted, m = cast_model({"field": "1"}) 26 | assert is_casted, m.field == (True, 1) 27 | 28 | with pytest.raises(ValueError): 29 | cast_model(("field", 1)) 30 | --------------------------------------------------------------------------------