AttributeError 'bytes' object has no attribute 'get' - python

I'm managing an api and I would like to understand why I have the error "AttributeError 'bytes' object has no attribute 'get'"
import requests
from requests.auth import HTTPBasicAuth
incidents = []
limit = 100
offset = 0
got_all_events = False
while not got_all_events:
alerts = requests.get(f'***', auth=HTTPBasicAuth('***', '***'))
print(alerts)
for alert in alerts:
incident = {
'name': alert.get('hostname'),
'rawJSON': json.dumps(alert),
}
incidents.append(incident)
if len(alerts) == limit:
offset += limit
else:
got_all_events = True
print(incident)
The error is regarding this code line
'name': alert.get('hostname'),
From my api my array is defined like that:
{
"totalSize": 0,
"data": [
{
"hostname": "string",
"domain": "string",
"tags": "string",
"os": {},
}
],
"size": 0
}

Your dictionary has the hostname key nested in a subdictionary as part of a list, so the get is unable to find it.
The only valid targets for the get on alert are totalsize, data, and size.
You could run a get on alert with key data to return a list, then run another get on position 0 of that list with key hostname to get what you want.
A more straightforward approach would just be:
'name': alert["data"][0]["hostname"]

alerts is a requests.Response object, not something decoded from JSON. You have to extract the body first. The iterator for a Response yields a sequence of bytes values, which is where the bytes value you think should be a dict comes from.
response = requests.get(f'***', auth=HTTPBasicAuth('***', '***'))
alerts = response.json()
for alert in alerts:
incident = {
'name': alert.get('hostname'),
'rawJSON': json.dumps(alert),
}
incidents.append(incident)

Thanks for your help.
Now it is working like that:
r=alerts.json()
for alert in r['data']:
incident = {
'hostName': alert["hostname"],
}
incidents.append(incident)
print(json.dumps(incidents, indent=1))

Related

Passing the "continue" content of the wikipedia API gives me a badcontinue error

I'm trying to build a loop that gives me all the pageids of a category. Thus, I'm trying to use the "continue" parameter as intended.
Here is the initialization with a first query that is working :
import requests
URL = "https://fr.wikipedia.org/w/api.php"
PARAMS_FRANCE = {
"action": "query",
"cmtitle": 'Catégorie:Portail:France/Articles liés',
"list": "categorymembers",
"cmlimit": 500,
"format": "json"
}
R = S.get(url=URL, params=PARAMS_FRANCE)
DATA = R.json()
PAGES_FRANCE = DATA['query']['categorymembers']
idx_continue = DATA['continue']
But then, when it enters the loop :
while('continue' in DATA):
PARAMS_FRANCE = {
"action": "query",
"cmtitle": 'Catégorie:Portail:France/Articles liés',
"list": "categorymembers",
"cmlimit": 500,
"continue": idx_continue,
"format": "json"
}
R = S.get(url=URL, params=PARAMS_FRANCE)
DATA = R.json()
PAGES_FRANCE = DATA['query']['categorymembers']
idx_continue = DATA['continue']
It returns me the following error when I print DATA:
{
"error":{
"code":"badcontinue",
"info":"Invalid continue param. You should pass the original value returned by the previous query.",
"*":"See https://fr.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce&gt; for notice of API deprecations and breaking changes."
},
"servedby":"mw1390"
}
Except I gave all the content of continue so what could be the problem ?
Thank you
The correct way to use the continuation parameters is to merge them with the original parameters (ie. PARAMS_FRANCE.update(DATA['continue'])).
I have the same problem when I was trying to fetch the backlinks. I changed a little bit of your code, the setting of the parameters, like this:
PARAMS_RUSSIA2 = {
"action": "query",
"bltitle": 'Russia',
"list": "backlinks",
"bllimit": 500,
"blcontinue": idx_continue['blcontinue'],
"continue":idx_continue['continue'],
"format": "json"
}
In this way, I can get the second 500 results:)

Django adding data into model from nested json returning TypeError: 'NoneType' object is not subscriptable

I am using a third-party API to get data and add it into my database via objects.update_or_create() method. This data has many records and some of the fields in the response only exists for certain records.
Below the a snippet of the JSON that is returned from the API. However this data is only present for some of the records in the JSON response. When I try to add this data into my model, I am getting the following error:
'f_name': i.get('card_faces')[0].get('name'),
TypeError: 'NoneType' object is not subscriptable
I am trying to have it so that if the card_faces field exists, True is added to the card_face column in the database, and then the card_faces name to the database. If card_faces doesn't exist, then False is added to the card_face column in the database, and subsequent fields are null.
JSON:
{
"data": [
{
"name": "Emeria Captain"
},
{
"name": "Emeria's Call // Emeria, Shattered Skyclave",
"card_faces": [
{
"object": "card_face",
"name": "Emeria's Call"
},
{
"object": "card_face",
"name": "Emeria, Shattered Skyclave"
}
]
}
]
}
views.py:
for i in card_data:
Card.objects.update_or_create(
id=i.get('id'),
defaults={
'name': i.get('name'),
'card_faces': i.get('card_faces'),
'f_name': i.get('card_faces')[0].get('name'),
'b_name': i.get('card_faces')[1].get('name'),
}
)
If the card_faces field doesn't exist, then the result of .get('card_faces') will be None, which you can't then call index 0 on
Break apart your line and do a logic check instead - this solution assumes that if card_faces does exist, there will be an index 0 and 1; you haven't provided enough information to assume otherwise
card_faces = i.get('card_faces')
f_name = None
b_name = None
if card_faces:
f_name = card_faces[0].get('name')
b_name = card_faces[1].get('name')
defaults = {
'name': i.get('name'),
'card_faces': True if card_faces else False,
'f_name': f_name,
'b_name': b_name,
}

AWS Lambda python to get cloudwatch alarm info to trigger an alert to MS teams

I have setup few cloud watch alarms, when ever alarms goes off it triggers an lambda funciton. In lambda i am trying to read the json and notify it to MS team. But i was not able to get the alarm name from the json based.
Below is the json
{
'Records': [
{
'EventSource': 'aws:sns',
'EventVersion': '1.0',
'EventSubscriptionArn': 'arn:aws:sns:ap-southeast-1:123:-teams-lambda-trigger:123-971d-4f70-927e-123',
'Sns': {
'Type': 'Notification',
'MessageId': '12-d0b8-5a86-8b33-123',
'TopicArn': 'arn:aws:sns:ap-southeast-1:123:vip-prestogo-teams-lambda-trigger',
'Subject': 'ALARM: "AuthenticationFailedException-was101" in Asia Pacific (Singapore)',
'Message': '{"AlarmName":"AuthenticationFailedException-was101","AlarmDescription":"Found \\"AuthenticationFailedException\\" in 123","AWSAccountId":"123","NewStateValue":"ALARM","NewStateReason":"Threshold Crossed: 1 out of the last 1 datapoints [1.0 (30/08/20 07:38:00)] was greater than or equal to the threshold (-1.0) (minimum 1 datapoint for OK -> ALARM transition).","StateChangeTime":"2020-08-30T07:39:22.330+0000","Region":"Asia Pacific (Singapore)","AlarmArn":"arn:aws:cloudwatch:ap-southeast-1:123:alarm:AuthenticationFailedException-was101","OldStateValue":"OK","Trigger":{"MetricName":"AuthenticationFailedException-was101","Namespace":"AuthenticationFailedException-was101","StatisticType":"Statistic","Statistic":"AVERAGE","Unit":null,"Dimensions":[],"Period":60,"EvaluationPeriods":1,"ComparisonOperator":"GreaterThanOrEqualToThreshold","Threshold":-1.0,"TreatMissingData":"- TreatMissingData: notBreaching","EvaluateLowSampleCountPercentile":""}}',
'Timestamp': '2020-08-30T07:39:22.372Z',
'SignatureVersion': '1',
'Signature': '123/WJa6/3saRvSsz+eDW10LZaAlR7jMhnU4jE73UM/+123/123/123/123/123+j+pjE0nldGG+123/xouonYXLkBrfRQPtr1sv/RzrIJ/kTYr3EwSkGL032HNrOeWmdGZ9D4gIJ4ir/mbnbSZV7w==',
'SigningCertUrl': 'https://sns.ap-southeast-1.amazonaws.com/SimpleNotificationService.pem',
'UnsubscribeUrl': 'https://sns.ap-southeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-southeast-1:123:-teams-lambda-trigger:46235663-971d-4f70-927e-0a420040a154',
'MessageAttributes': {
}
}
}
]
}
trying like below but failed:
message = event['Records'][0]['Sns']['Message']
alarm =message['AlarmName']
I am getting error as below:
string indices must be integers: TypeError
Traceback (most recent call last):
File "/var/task/lambda_function.py", line 15, in lambda_handler
"text": event['Records'][0]['Sns']['Message']['AlarmDescription']
TypeError: string indices must be integers
Please help
This is the result that the "AlarmName" property is located in a json string. You need to parse this first so that the property can be accessed.
Use the json.parse function to retrieve the alarm name like the sample Lambda function below.
import boto3
import json
def lambda_handler(event, context):
message = event['Records'][0]['Sns']['Message']
message = json.loads(message)
alarm_name = message["AlarmName"]
print(alarm_name)
I tested this using the below event in Lambda
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:ap-southeast-1:123:-teams-lambda-trigger:123-971d-4f70-927e-123",
"Sns": {
"Type": "Notification",
"MessageId": "12-d0b8-5a86-8b33-123",
"TopicArn": "arn:aws:sns:ap-southeast-1:123:vip-prestogo-teams-lambda-trigger",
"Subject": "ALARM: \"AuthenticationFailedException-was101\" in Asia Pacific (Singapore)",
"Message": "{\"AlarmName\":\"AuthenticationFailedException-was101\",\"AlarmDescription\":\"Found \\\"AuthenticationFailedException\\\" in 123\",\"AWSAccountId\":\"123\",\"NewStateValue\":\"ALARM\",\"NewStateReason\":\"Threshold Crossed: 1 out of the last 1 datapoints [1.0 (30/08/20 07:38:00)] was greater than or equal to the threshold (-1.0) (minimum 1 datapoint for OK -> ALARM transition).\",\"StateChangeTime\":\"2020-08-30T07:39:22.330+0000\",\"Region\":\"Asia Pacific (Singapore)\",\"AlarmArn\":\"arn:aws:cloudwatch:ap-southeast-1:123:alarm:AuthenticationFailedException-was101\",\"OldStateValue\":\"OK\",\"Trigger\":{\"MetricName\":\"AuthenticationFailedException-was101\",\"Namespace\":\"AuthenticationFailedException-was101\",\"StatisticType\":\"Statistic\",\"Statistic\":\"AVERAGE\",\"Unit\":null,\"Dimensions\":[],\"Period\":60,\"EvaluationPeriods\":1,\"ComparisonOperator\":\"GreaterThanOrEqualToThreshold\",\"Threshold\":-1.0,\"TreatMissingData\":\"- TreatMissingData: notBreaching\",\"EvaluateLowSampleCountPercentile\":\"\"}}",
"Timestamp": "2020-08-30T07:39:22.372Z",
"SignatureVersion": "1",
"Signature": "123/WJa6/3saRvSsz+eDW10LZaAlR7jMhnU4jE73UM/+123/123/123/123/123+j+pjE0nldGG+123/xouonYXLkBrfRQPtr1sv/RzrIJ/kTYr3EwSkGL032HNrOeWmdGZ9D4gIJ4ir/mbnbSZV7w==",
"SigningCertUrl": "https://sns.ap-southeast-1.amazonaws.com/SimpleNotificationService.pem",
"UnsubscribeUrl": "https://sns.ap-southeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-southeast-1:123:-teams-lambda-trigger:46235663-971d-4f70-927e-0a420040a154",
"MessageAttributes": {
}
}
}
]
}
This Lambda function output the string AuthenticationFailedException-was101
Your event['Records'][0]['Sns']['Message'] is json string, not python dictionary. You have to parse it into the dictionary, using json.loads:
import json # if not already present
message = json.loads(event['Records'][0]['Sns']['Message'])
alarm_name = message['AlarmName']
message = event['Records'][0]['Sns']['Message'], your message is a string you need to convert it into dictionary. Whenever you have doubt like this you can use print(type(variable_name)). Your value of message may or may not have that particular key so you can use get() function to retrieve the value.
import json
message = json.loads(event['Records'][0]['Sns']['Message'])
alarm_name = message.get('AlarmName', None)
if alarm_name not None:
{your action}
else:
{some default action when key not present}

Comparing value in a JSON using Python

I receive a fairly uncomfortable JSON to work with, which looks as follows:
[
{
"attributes": [
{
"type": "COMMAND",
"name": "COMMAND",
"value": [
"buttonState"
]
},
{
"type": "std_msgs.msg.Bool",
"name": "buttonState",
"value": {
"data": false
}
}
],
"type": "sensor",
"id": "s_2"
}]
And I would like to compare a piece of data (more precisely - value of Button state) but I seem to fail. Tried following:
import requests
import json
yo = 1
switchPost = "http://192.168.0.104:7896/iot/d?k=123456789&i=san_1_switch&d=sw|{}"
robGet = "http://192.168.0.109:10100/robot/sen_2"
r = requests.get(robGet, headers={"content-type":"application/json"})
resp = json.loads(r.text)
for attrs in (resp['attributes']['value']):
if attrs['data'] == false:
yo = 100
break
g = requests.post(switchPost.format(yo), headers={"content-type":"text/plain"})
print(r.text)
Unfortunately, the error I receive is the following:
for attrs in (resp['attributes']['value']):
TypeError: list indices must be integers, not str
In your JSON, the fact that it is wrapped in [ then ] means it is a JSON array, but with just one element.
So, as your error message suggests, resp needs an integer as its index, for which element of the array you want. resp[0] then refers to
{
"attributes": [
{
"type": "COMMAND",
"name": "COMMAND",
"value": [
"buttonState"
]
},
{
"type": "std_msgs.msg.Bool",
"name": "buttonState",
"value": {
"data": false
}
}
],
"type": "sensor",
"id": "s_2"
}
(notice no [] now, so it's a JSON object)
Then you want resp[0]['attributes'] to refer to the single part of this object, 'attributes' which again refers to an array.
Therefore for attribute in resp[0]['attributes'] will allow you to loop through this array.
To get the boolean value you want, you'll then want to find which element of that array has 'name' of 'buttonState' and check the corresponding 'value'.
In all, you're probably looking for something like:
for attribute in resp[0]['attributes']:
if attribute['name'] == 'buttonState' and attribute['value']['data'] is False:
# Do your thing here
resp is a list so, to get first element, access it as resp[0]. Same with resp[0]['attributes']
So you can access it as follows
resp[0]['attributes'][0]['value']
You can restructure your for loop as follows
for d in resp[0]['attributes']:
if isinstance(d['value'], dict) and d['value'].get('data') == false:
yo = 100
break
The answer is in the error message I think:
TypeError: list indices must be integers, not str
The first entry in attributes has a value that is a list, so you can't get 'data' from that.
Since you have a mix of types, you might need to check if 'value' is a list or a dict.
Edit:
Jumped the gun here I think. #dennlinger gives an explanation to your error message. But you'll get it again once you're past that...

How to assert deeply in JSON with python

I am trigerring an API call, and the server response is in JSON format.
the response looks like this:
{
"status": 0,
"not_passed": 1,
"why": [
{
"code": 229,
"reason": "some reason",
}
]
}
I need to assert two thing.
Status and reason
fro status I am using:
r = requests.get_simple(url=Server.MY_SERVER, params=p)
data = json.loads(r.content)
assert data["status"] == 0
but it doesn't work for the 'reason', maybe because the 'reason' is deeper in the nested structure. How can I fix this one?
assert data['why'][0]['reason'] == 'something'
Of course this assumes that data['why'] exists, is a list, and contains a dict as its first element.

Categories