I hope somebody can help me to understand what I am doing wrong with this azure function.
My idea is structure as follow.
I have 2 topics. TOPIC_A is designed to receive a message and process it and send it to TOPIC_B which I want just to have that message until when I will decide what to do with it.
and this is the actual azure function app code.
import logging
import json
import boto3
from azure.servicebus import ServiceBusClient, ServiceBusMessage
from azure.keyvault.secrets import SecretClient
from azure.identity import DefaultAzureCredential
import azure.functions as func
def main(message: func.ServiceBusMessage):
# Log the Service Bus Message as plaintext
message_content_type = message.content_type
message_body = message.get_body().decode("utf-8")
logging.info("Python ServiceBus topic trigger processed message.")
logging.info("Message Content Type: " + message_content_type)
logging.info("Message Body: " + message_body)
#KeyVault Configuration
KeyVault_Url = f'keyvailt_url'
Keyvault_Name = 'keyvalt_name'
credential = DefaultAzureCredential()
client_keyvault = SecretClient(vault_url=KeyVault_Url, credential=credential)
# Service Bus Connection string
CONNECTION_STR = client_keyvault.get_secret("Service-CONN").value
# For receiving the feedback from campaigns
TOPIC_NAME_A = "topicAname"
SUBSCRIPTION_NAME = "subscriptionName"
# For sending feedback and results of sentiment analysis and language detection
TOPIC_NAME_B = "topicBname"
comprehend = boto3.client(service_name='comprehend', region_name='eu-west-1', aws_access_key_id=client_keyvault.get_secret("ID").value, aws_secret_access_key=client_keyvault.get_secret("SECRET").value)
servicebus_client = ServiceBusClient.from_connection_string(conn_str=CONNECTION_STR)
with servicebus_client:
receiver = servicebus_client.get_subscription_receiver(
topic_name=TOPIC_NAME_A,
subscription_name=SUBSCRIPTION_NAME
)
with receiver:
received_msgs = receiver.receive_messages(max_message_count=10, max_wait_time=5)
for msg in received_msgs:
message= str(msg)
res = json.loads(message)
text = res['Text']
result_json= json.dumps(comprehend.detect_sentiment(Text=text, LanguageCode='en'), sort_keys=True, indent=4)
result = json.loads(result_json) # converting json to python dictionary
#extracting the sentiment value
sentiment = result["Sentiment"]
#extracting the sentiment score
if sentiment == "POSITIVE":
value = round(result["SentimentScore"]["Positive"] * 100,2)
elif sentiment == "NEGATIVE":
value = round(result["SentimentScore"]["Negative"] * 100,2)
elif sentiment == "NEUTRAL":
value = round(result["SentimentScore"]["Neutral"] * 100,2)
elif sentiment == "MIXED":
value = round(result["SentimentScore"]["Mixed"] * 100,2)
lang_result=json.dumps(comprehend.detect_dominant_language(Text = text), sort_keys=True, indent=4)
#converting languages detection results into a dictionary
lang_result_json=json.loads(lang_result)
#Formatting the score from the results
for line in lang_result_json["Languages"]:
line['Score'] = round(line['Score']* 100, 2)
#storing the output of sentiment analysis, language detection and ids in a dictionary and converting it to JSON
output={
'ID':res['ID'],
'userId':res['userId'],
'defID':res['defID']
}
output_json = json.dumps(output, ensure_ascii=False)
#-------------------------------------------------------------------------------------------------------
# Sending the processed output (output_json) in json format to another service bus
def send_output(sender):
message = ServiceBusMessage(
output_json,
content_type="Analysis", #setting the content type so that the service bus can route it.
application_properties={b'CODE':msg.application_properties[b'CODE']} #setting the tenant code
)
sender.send_messages(message)
servicebus_client = ServiceBusClient.from_connection_string(conn_str=CONNECTION_STR, logging_enable=True)
with servicebus_client:
sender = servicebus_client.get_topic_sender(topic_name=TOPIC_NAME_B)
with sender:
send_output(sender)
receiver.complete_message(msg)
Before to explain the issue I have with the function, I will explain what happen if I run this code locally.
If I send a message to the TOPIC_A, and head to that topic from azure portal, I can see the message in the RECEIVED tab, once I run my python code, that message from TOPIC_A get consumed and sent to TOPIC_B, where it stays in RECEIVED. And this is exactly the output I am expecting.
Now, I moved the entire logic under an azure function, I send the message to TOPIC_A..and instantly I see the message ending under DEADLETTERS, and in my azure function I get this error.
Result: Failure Exception: ImportError: cannot import name 'c_uamqp' from partially initialized module 'uamqp' (most likely due to a circular import) (/home/site/wwwroot/.python_packages/lib/site-packages/uamqp/__init__.py). Troubleshooting Guide: https://aka.ms/functions-modulenotfound Stack
while if I peek the message (TOPIC_A) in the dead letter I see this message:
deadletterreason "MaxDeliveryCountExceeded"
As far as I understand, is that the messages end so fast under dead letter, while my code is looking for the message under RECEIVED, it tries 10 times and then fails.
But the think that I am having trouble to understand, is why this is happening only when I use the azure function.
If anyone can help me to understand the problem, I will be great full.
Please if you need anymore details, just let me know.
UPDATE:
After different debug process, I can surely say that the issue is due to the uamqp
but I don't understand why is not working.
I am running my pipeline on a ubuntu worker.
my requirement.txt has:
azure-functions
azure-servicebus==7.0.0
boto3
azure-keyvault
azure-identity
uamqp
and in the pipeline output I can see that has been installed
uamqp-1.4.3
but yet I am getting the same exact error:
Result: Failure Exception: ImportError: cannot import name 'c_uamqp' from partially initialized module 'uamqp' (most likely due to a circular import) (/home/site/wwwroot/.python_packages/lib/site-packages/uamqp/__init__.py). Troubleshooting Guide: https://aka.ms/functions-modulenotfound Stack: File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/dispatcher.py", line 305, in _handle__function_load_request func = loader.load_function( File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/utils/wrappers.py", line 42, in call raise extend_exception_message(e, message) File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/utils/wrappers.py", line 40, in call return func(*args, **kwargs) File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/loader.py", line 83, in load_function mod = importlib.import_module(fullmodname) File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "<frozen importlib._bootstrap>", line 1030, in _gcd_import File "<frozen importlib._bootstrap>", line 1007, in _find_and_load File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 680, in _load_unlocked File "<frozen importlib._bootstrap_external>", line 850, in exec_module File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed File "/home/site/wwwroot/myfunction/testServiceBus.py", line 4, in <module> from azure.servicebus import ServiceBusClient, ServiceBusMessage File "/home/site/wwwroot/.python_packages/lib/site-packages/azure/servicebus/__init__.py", line 6, in <module> from uamqp import constants File "/home/site/wwwroot/.python_packages/lib/site-packages/uamqp/__init__.py", line 12, in <module> from uamqp import c_uamqp # pylint: disable=import-self
At first, my function was running from a __init__.py
as default file created by visual studio code. So I thought it might be a name clashing. So I changed my file name to testServiceBus.py
but I still have the same error.
this is my full configuration.
function.json
{
"scriptFile": "testServiceBus.py",
"entryPoint": "main",
"bindings": [
{
"name": "message",
"type": "serviceBusTrigger",
"direction": "in",
"topicName": "XXX",
"subscriptionName": "XXX",
"connection": "XXX"
}
]
}
host.json
{
"version": "2.0",
"extensions": {
"serviceBus": {
"messageHandlerOptions": {
"autoComplete": false
}
}
},
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[2.*, 3.0.0)"
}
}
thank you very much for any help or hint, totally lost here and I have no more ideas to try