AWS boto3 invoke lambda returns None payload - python

I'm not quite sure why this piece of code fails, please I'd be happy to hear your thoughts. I've used examples from boto3 and its works in general, but I'd get an AttributeError all of a sudden in some cases. Please explain what I am doing wrong because if a payload is None, I will get a JSON decoding error but not a None object.
Here is a simplified version of a code that causes the exception.
import boto3
import json
client = boto3.client('lambda', region_name='...')
res = client.invoke(
FunctionName='func',
InvocationType='RequestResponse',
Payload=json.dumps({'param': 123})
)
payload = json.loads(res["Payload"].read().decode('utf-8'))
for k in payload.keys():
print(f'{k} = {payload[k]}')
The error
----
[ERROR] AttributeError: 'NoneType' object has no attribute 'keys'
Traceback (most recent call last):
.....

Just replicated your issue in my environment by creating a lambda that doesn't return anything and calling it using boto3. The "null" object passes through the json loads without error but doesn't have any keys since it's not a dictionary.
def lambda_handler(event, context):
pass
I created my code just like yours and got the same error. Weirdly, I was able to get the json error by attempting to print out the streaming body with this line
print(res["Payload"].read().decode('utf-8'))
before loading it. I have no idea why this happens.
Edit: Looks like once you read from the StreamingBody object it's empty from then on. https://botocore.amazonaws.com/v1/documentation/api/latest/reference/response.html#botocore.response.StreamingBody. My recommendation is to read the data from the streaming body and check for "null" and process as normal.

Related

Parse SQS stringify json message Python

I have a SQS queue which triggers a lambda function where as a message I pass the stringify json. I am trying to get the whole message body from the Records, but throws an error,
[ERROR] KeyError: 'efsPathIn'
Traceback (most recent call last):
File "/var/task/lambda_function.py", line 20, in lambda_handler
key = bodyDict['efsPathIn']
I'm sending this stringify json as a message in SQS queue,
{"Records": [{"body": "{\"efsPathIn\": \"163939.jpeg\",\"imageWidth\": \"492\",\"imageHeight\": \"640\",\"bucketOut\":\"output-bucket\",\"keyOut\":\"163939.webp\"}"}]}
And the code is which extracts the values,
for item in event['Records']:
body = str(item['body'])
bodyDict = json.loads(body)
key = bodyDict['efsPathIn']
bucket_out = bodyDict['bucketOut']
width = bodyDict['imageWidth']
height = bodyDict['imageHeight']
key_out = bodyDict['keyOut']
I've tried with json.dumps(item['body']) also which further loads the json, but still getting the same error.
When I test from AWS Lambda test console using the above mentioned json message, the function gets successfully executed, but I get this error while sending a message from a SQS queue.
json.dumps() is for converting a Python object into a JSON string. You have a JSON string that you are need to convert into a Python object. You should be calling json.loads() like so:
body = json.loads(item['body'])
After which you will have a Python object that you can do things like: body['efsPathIn']

Tweepy Client.like() throws AttributeError: 'NoneType' object has no attribute 'partition'

Regarding the tweepy docs for using Twitter API v2 i should be able to like a tweet with the following code.
import tweepy
from keys import keys
# bearer token for twitter developers
client = tweepy.Client(bearer_token=keys["bearer_token"])
# checks for latest tweets
def like_tweets():
like = client.like(1466906017120153601)
print(like)
like_tweets()
I both tried to pass the tweet id as a string and as an integer. I checked the tweet id manually for correctness and also tried different tweet ids.
But i get the following error everytime:
File "C:\Users\myname\pathtopython\Python\Python37\site-packages\tweepy\client.py", line 387, in like
id = self.access_token.partition('-')[0]
AttributeError: 'NoneType' object has no attribute 'partition'
Do you have ideas or suggestions how to solve this problem?
I was not able to encode the exact error, but found out, that this method need's the whole access data, including the consumer_token, consumer_secret, access_token and access_token_secret.
I just came across the same issue.
The solution was to specify the other tokens when creating the client:
from os import environ
import tweepy
def new_client():
return tweepy.Client(
consumer_key=environ['CONSUMER_KEY'],
consumer_secret=environ['CONSUMER_SECRET'],
bearer_token=environ['BEARER_TOKEN'],
access_token=environ['ACCESS_TOKEN'],
access_token_secret=environ['ACCESS_TOKEN_SECRET'],
)
client = new_client()
And then calling
client.like(tweet_id=tweet_id)
(letting the default option user_auth=True)

Extracting values from a dictionary by key, string indices must be integers

I'm trying to extract values from dictionary recieved with websocket-client via key and for some reason it throws me an error "String indices must be integers".
no matter how im trying to do it im constantly getting the same error unless i'm writing it as lines of code then it works, unfotunately that's not what I'm after...
Example:
ws = websocket.WebSocket()
ws.connect("websocket link")
info = ws.recv()
print(info["c"])
ws.close()
Output:
Traceback (most recent call last):
File "C:\Python\project\venv\example\example.py", line 14, in <mod
ule>
print(info["c"])
TypeError: string indices must be integers
While if im taking the same dictionary and writing it down suddenly it works...
Example:
example = {"a":"hello","b":123,"c":"whatever"}
print(example["c"])
Output:
whatever
Any help is appreciated, thanks!
SOLUTION
firstly you have to import the websocket and json module as you receive dictionary json object and then you have to load that json objects.
import websocket
import json
ws = websocket.WebSocket()
ws.connect("websocket link")
info = json.loads(ws.recv())
print(info["c"])
ws.close()
Likely the dictionary you receive from the web socket is a json object:
import websocket
import json
ws = websocket.WebSocket()
ws.connect("websocket link")
info = json.loads(ws.recv())
print(info["c"])
ws.close()
firstly you have to import the websocket and json module as you receive dictionary json object
and then you have to load that json objects.
import websocket
import json
and then load
info = json.loads(ws.recv())

boto3 lambda payload always returns NULL [python, sync invoked]

I am using Lambda to trigger a step function. It works fine in terms of triggering the step function, but I also need the lambda to return the state machine execution arn (NOT state machine arn). I need the execution arn because I am implementing the whole process as a github action workflow, so I need it to check the status (running/success/failed/aborted) of the state machine.
My code of getting the lambda return for github action, wrapped as docker-compose service:
client = boto3.client("lambda", region_name="us-west-1")
lambda_response = client.invoke(
FunctionName="my-lambda",
InvocationType="RequestResponse",
Payload=json.dumps({"detail-type": "gh-action"}),
)
payload = json.loads(lambda_response["Payload"].read()) # tried .decode() too
print("payload:", payload) # payload prints None as a whole, not {"sfn_exe_arn": None}
The relevant part of my lambda function:
try:
client = boto3.client("stepfunctions")
response = client.start_execution(
stateMachineArn=STATE_MACHINE_ARN,
name=run_name,
input=json.dumps(
{"runName": run_name, "model_name": MODEL_NAME, "queries": QUERIES}
),
)
sfn_exe_arn = response["executionArn"]
except Exception as e:
raise e
return {"sfn_exe_arn": sfn_exe_arn}
# this `sfn_exe_arn` can print out with expected value in console
# but it does not return when called in github action
When I invoke this lambda from the console, most of the time it returns as expected, which is {"sfn_exe_arn": sfn_exe_arn}, but sometime it also returns null.
When I invoke this lambda as part of github action workflow, the return is always null (the lambda_response is returned, just the payload part is always null)
Can anyone help me understand why there is this gap? apparently my lambda got the executionArn, but it just doesn't return to the client.invoke()
The entire lambda_response (it is named response in the screenshot):
You have to decode the bytestream that you get from StreamingBody.read()
add .decode() to the bytes object you get from reading the reponse payload.
payload = json.loads(lambda_response["Payload"].read().decode())
My bad, I didn't try thoroughly other posts' answers. Thanks #jarmod pointed out the solution in the comments: you need to assign StreamingBody to a variable before read it. Link: Lambda Return Payload botocore.response.StreamingBody object prints but then empty in variable

converting Logging data to JSON

I'm trying to convert logging data to JSON to show it in ChatBot.I tried logging.Formatter but data still isn't showing in chatbot it throws an error.
I tried userInput = logging.Formatter(user_input).
After this I tried userInput = logging.Formatter(user_input)
user = userInput.json() but it didn't work as well.
How can I convert logging data to json for chatbot. Where I'm committing mistake kindly correct it. Code is below
this is the error I'm getting.
An error has occurred: Invalid Lambda Response: Received error response from Lambda: Unhandled
If you just want to see the logging info in the chatbot, you can convert that object into string and return it to chatbot.
def response(message):
return {
"dialogAction":{
"type":"Close",
"fulfillmentState":"Fulfilled",
"message":{
"contentType":"PlainText",
"content":message
}
}
}
return response(str(logging_info_object))
Hope it helps.

Categories