restructure to fit around python event-loop

This commit is contained in:
2025-04-16 00:24:17 +01:00
parent b550d98eaf
commit 0720c5963f
5 changed files with 153 additions and 145 deletions
+37 -12
View File
@@ -9,12 +9,12 @@ import logging
import sys
import traceback
import pathlib
from asyncio import Future
from asyncio import Future, AbstractEventLoop
from typing import Dict, List, Tuple, Callable, Coroutine, Any
from urllib.parse import parse_qs, urlparse
from xml.etree import ElementTree
from aio_pika.abc import AbstractRobustChannel
from aio_pika.abc import AbstractRobustChannel, AbstractRobustConnection, AbstractRobustExchange, AbstractExchange
from aiohttp import ClientSession, ClientResponse
from dataclasses import dataclass
from fastapi import HTTPException
@@ -28,6 +28,8 @@ from amqp.adapter.file_uploader import publish_file_to_rabbitmq
from IPython.core import debugger
from amqp.router.router_base import AMQ_REPLY_TO_EXCHANGE
debug = debugger.Pdb().set_trace
@@ -38,7 +40,7 @@ class AMQMessage(DataMessage):
in case the response should initiate file download.
"""
def __init__(self, data_message: DataMessage, channel: AbstractRobustChannel):
def __init__(self, data_message: DataMessage, connection: AbstractRobustConnection):
# Copy all fields from DataMessage
self.magic = data_message.magic
self.id = data_message.id
@@ -49,7 +51,7 @@ class AMQMessage(DataMessage):
self.trace_info = data_message.trace_info
self.base64_body = data_message.base64_body
# Add the new field
self.channel = channel
self.connection = connection
def parse_request_body(message: DataMessage):
@@ -115,11 +117,35 @@ def parse_request_body(message: DataMessage):
raise ValueError(f"Unsupported Content-Type: {content_type}")
async def convert_file_response(obj: FileResponse, channel: AbstractRobustChannel) -> str:
async def convert_file_response(
obj: FileResponse,
connection: AbstractRobustConnection,
loop: AbstractEventLoop | None
) -> str:
"""
Converts a FileResponse object to a JSON string containing the filename and stream.
"""
(_stream_name, _file_size) = await publish_file_to_rabbitmq(channel, obj.path)
async def wrap_connection_channel(connection: AbstractRobustConnection) -> tuple[AbstractRobustChannel, AbstractExchange, str]:
""" Wrap call to channel() to make it Awaitable """
_channel = await connection.channel()
exchange_name: str = AMQ_REPLY_TO_EXCHANGE
_exchange = await _channel.get_exchange(name=exchange_name, ensure=True)
_queue = await _channel.declare_queue(exclusive=True) # Declare a unique, exclusive queue
_queue_name = _queue.name
await _queue.bind(
exchange=exchange_name,
routing_key=_queue_name, # Use queue name as routing key
)
logging.debug(f" [*] Publishing to exchange: {exchange_name}, q: {_queue_name}")
return _channel, _exchange, _queue_name
_future = asyncio.run_coroutine_threadsafe(wrap_connection_channel(connection), loop)
while not _future.done():
await asyncio.sleep(0.1) # allow coroutine to execute
_channel, _exchange, _routing_key = _future.result()
(_stream_name, _file_size) = await publish_file_to_rabbitmq(_exchange, obj.path, _routing_key, loop)
await _channel.close()
return json.dumps({'files': [
{'filename': obj.filename,
'mediaType': 'application/octet-stream',
@@ -133,7 +159,8 @@ async def call_cleverthis_api(
message: AMQMessage,
args: Any,
api_method: Callable[[Any, Any], Coroutine],
success_code: int
success_code: int,
loop: AbstractEventLoop | None = None
) -> DataResponse:
"""
Handles POST and PUT requests, which typically contain possibly complex payload data, passed as a dictionary.
@@ -162,7 +189,7 @@ async def call_cleverthis_api(
if isinstance(_result, FileResponse):
_json_result, _content_type = await convert_file_response(
_result, message.channel
_result, message.connection, loop
), 'application/octet-stream'
else:
_json_result = _result
@@ -224,6 +251,7 @@ class CleverThisServiceAdapter:
self.session = session
self.service_host = self.amq_configuration.dispatch.service_host
self.service_port = self.amq_configuration.dispatch.service_port
self.loop: AbstractEventLoop | None = None
self.require_authenticated_user = eval(
self.amq_configuration.amq_adapter.require_authenticated_user.capitalize()) if self.amq_configuration.amq_adapter.require_authenticated_user.strip() else True
@@ -247,7 +275,7 @@ class CleverThisServiceAdapter:
"""
return None
async def on_message(self, message: AMQMessage, future: Future[DataResponse] = None) -> DataResponse:
async def on_message(self, message: AMQMessage) -> DataResponse:
"""
Called when a message is received from the AMQP.
:param message:
@@ -292,9 +320,6 @@ class CleverThisServiceAdapter:
raise ValueError(f"Unexpected HTTP method: {message.method}")
else:
amq_response = DataResponseFactory.of(message.id, 401, "text/plain", b"Unauthorized", message.trace_info)
if future:
future.set_result(amq_response)
return amq_response
# ==================================================================================================