I'm getting "errorMessage": "'function' object has no attribute 'loads'" but loads is part of urllib3. Also if you have any recommendations on how to write this better please let me know.
import os
import json
import urllib3
import boto3
def lambda_handler(event, context):
def json(get_data):
s3 = boto3.client('s3')
with open("FILE_NAME.json", "rb") as f:
s3.upload_fileobj(get_data,f, "<bucket_name", None)
class GetToken:
def __init__(self, API_TOKEN, URL, MY_HEADERS):
self.token = API_TOKEN
self.url = URL
self.headers = MY_HEADERS
def data(self):
http = urllib3.PoolManager()
response = http.request('GET', self.url, headers=self.headers)
if(response.status == 200):
get_json = json.loads(response.data.decode('utf-8'))
elif(response.status !=200):
print("Result not found")
return get_json
def members():
access_results = access()
base_token = access_results[0]
base_url = access_results[1]
base_headers = access_results[2]
member_url = base_url + 'members'
post_parameters = GetToken(base_token,member_url,base_headers)
members_json = post_parameters.data()
return json(members_json)
def access():
clubhouse_token = os.environ.get('CLUBHOUSE_API_TOKEN')
clubhouse_url = 'https://api.clubhouse.io/api/v3/'
my_headers = {'Clubhouse-Token': clubhouse_token, 'Content-Type': 'application/json'}
return clubhouse_token,clubhouse_url,my_headers
return members()
despite having a global import json, you also have a def json in closure scope (a nested function)
lookup in python is Locals, Closure, Globals, Builtins -- and so the closure-scoped function has higher precedence
rename that local def json to something else and it should fix your problem
Related
This is under Anime.py file
import requests
import json
import random
from funcy import join
def anime():
def __init__(self):
self.url = f'https://animechan.vercel.app/api/quotes/character?name=saitama'
def get_info(self):
r = requests.get(self.url)
response = r.json()
join(response)
values_of_key = [a_dict["quote"] for a_dict in response]
return values_of_key
I get a AttributeError: 'NoneType' object has no attribute 'get_info' error when I call this in main()
import requests
import Anime
from Anime import anime
def main():
results = Anime.anime()
results.get_info()
print(results.get_info)
main()
Youre attempting to use a class, but defining it as a function which returns nothing.
Your code should look as below.
class anime():
def __init__(self):
self.url = f'https://animechan.vercel.app/api/quotes/character?name=saitama'
def get_info(self):
r = requests.get(self.url)
response = r.json()
join(response)
values_of_key = [a_dict["quote"] for a_dict in response]
return values_of_key
I was working on mocking in Python. I want to mock a method from the class with a mocked object. Here is an example:
MyApp.py
import requests
class Client:
"""
Client to use in integration to fetch data from Chronicle Backstory
requires service_account_credentials : a json formatted string act as a token access
"""
def __init__(self, api_key, url, verify, proxies):
self.url = url
self.api_key = api_key
self.verify = verify
self.proxies = proxies
# a separate SDK that has initialized the request object
self.my_requests = requests
def http_request(self, method, url_suffix, params=None):
base_url = self.url + "/api/v4"
full_url = base_url + url_suffix
headers = {
'Authorization': self.api_key
}
validate_response(self.my_requests.request(
method,
full_url,
verify=self.verify,
proxies=self.proxies,
params=params,
headers=headers))
def validate_response(response):
status_code = response.status_code
resp_json = response.json()
if status_code != 200:
raise ValueError("Invalid argument value " + resp_json.get('detail', 'N/A'))
else:
response.raise_for_status()
return resp_json
def request_api(client):
response = client.http_request('GET', '')
return response
# initial flow of execution
def main():
client = Client()
print(request_api(client))
if __name__ in ('__main__', '__builtin__', 'builtins'):
main()
MyApp_test.py
from unittest import mock
def test_api_failure():
from mocking.MyApp import request_api
# lots of complexities here that we have to mock this object
client = mock.Mock()
client.http_request.call_real_method # something that I want to achieve.
client.my_requests.request.return_value = '{"status":"ok"}'
request_api(client)
It's clear that creating an object to call the method is not possible as object creation requires a lot of arguments.
I'm trying to get my targets from vuforia's API, but I can't pass the last value of the header "Authorization" which is an encoded data, the error that I'm getting is this:
Unicode-objects must be encoded before hashing
this is in try snippet of the code, I'm following the vuforia's documentation but still, something is wrong with my code and I don't have a clue what it is
import base64
import hashlib
import hmac
import requests
from flask import Flask, request
from email.utils import formatdate
import logging
app = Flask(__name__)
#app.route('/')
def hello_world():
try:
import http.client as http_client
except ImportError:
# Python 2
import httplib as http_client
http_client.HTTPConnection.debuglevel = 1
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
url = 'https://vws.vuforia.com/targets'
req = requests.Request('GET', url)
req.headers = setHeaders(req)
resp = requests.Session().send(req.prepare())
return resp.text
def compute_md5_hex(data):
"""Return the hex MD5 of the data"""
h = hashlib.md5()
h.update(data)
return h.hexdigest()
def compute_hmac_base64(key, data):
"""Return the Base64 encoded HMAC-SHA1 using the provide key"""
h = hmac.new(key, None, hashlib.sha1)
h.update(data)
return base64.b64encode(h.digest())
def setHeaders(request):
date = formatdate(None, localtime=False, usegmt=True)
accessKey = "ce1500fhfth429279173fd839f9d414532014a3da"
secret_key = b"5d3fdawd7211447c35be607ae5a08ec794a09d71d"
headers = {'Date': date, 'Authorization': "VWS " + accessKey + ":" + tmsSignature(request, secret_key)}
return headers
def tmsSignature(request, secretKey):
method = request.method
contentType = ""
hexDigest = "d41d8cd98f00b204e9800998ecf8427e"
if method == "GET" or method == "POST":
pass
# Do nothing because the strings are already set correctly
elif method == "POST" or method == "PUT":
contentType = "application/json"
# If this is a POST or PUT the request should have a request body
hexDigest = compute_md5_hex(request)
else:
print("ERROR: Invalid content type passed to Sig Builder")
# Date in the header and date used to calculate the hash must be the same
dateValue = formatdate(None, localtime=False, usegmt=True)
requestPath = str(request.url)
components_to_sign = list()
components_to_sign.append(method)
components_to_sign.append(str(hexDigest))
components_to_sign.append(str(contentType))
components_to_sign.append(str(dateValue))
components_to_sign.append(str(requestPath))
string_to_sign = "\n".join(components_to_sign)
shaHashed = ""
try:
shaHashed = compute_hmac_base64(secretKey, string_to_sign)
except Exception as e:
print("ERROR ", e)
return shaHashed
if __name__ == '__main__':
app.run()
Looking into your hmac_base64_key function, this particular call is the cause:
h.update(data)
Since that is the update function from the hmac library, that requires the input to be byte instead of string/unicode (check out the documentation on hmac which refers to hashlib for its update signature).
So it seems like the fix is simply:
h.update(data.encode("utf8")) # or other encoding you want to use
Note that you'll need to change the return value of compute_hmac_base64 (shaHashed) to string again since you're concatenating it with a string in setHeaders.
(I'm assuming a Python 3 code even though you have a check for Python 2 in your code by the way, since you've tagged this Python 3).
I'm trying to do a basic implementation of the WooCommerce API OAuth client in python following what the documentation says: http://docs.woocommercev2.apiary.io/introduction/authentication/over-http. This is what I have so far:
import requests
import random
import string
import time
from hashlib import sha1
import hmac
import binascii
import re
from urllib import quote, urlencode
def uksort(d, func):
s = {}
for k in sorted(d.keys(), cmp = func):
s[k] = d[k]
return s
class WooCommerce(object):
def __init__(self, consumer_key, consumer_secret, endpoint):
self.consumer_key = consumer_key
self.consumer_secret = consumer_secret
self.endpoint = endpoint
def _make_request(self, resource, params, method = "GET"):
oauth_params = {
"oauth_consumer_key": self.consumer_key,
"oauth_nonce": self._gen_nonce(),
"oauth_timestamp": self._gen_timestamp(),
"oauth_signature_method": "HMAC-SHA1",
}
oauth_params["oauth_signature"] = self._gen_signature(resource, dict(params.items() + oauth_params.items()), method)
params = dict(params.items() + oauth_params.items())
if method == "GET":
print self.endpoint + resource + "?" + urlencode(params)
def _gen_nonce(self):
ran_string = ''.join(random.choice(string.ascii_uppercase + string.digits) for i in range(32)).encode("base64")
alnum_hash = re.sub(r'[^a-zA-Z0-9]', "", ran_string)
return alnum_hash
def _gen_timestamp(self):
return int(time.time())
def _gen_signature(self, resource, params, method):
base_request_uri = quote(self.endpoint + resource, safe = "")
normalized_params = self._normalize_params(params)
sorted_params = uksort(normalized_params, cmp)
query_string = "%26".join([key + "%3D" + value for key, value in sorted_params.iteritems()])
raw_string = method + "&" + base_request_uri + "&" + query_string
hashed = hmac.new(self.consumer_secret, raw_string, sha1)
return binascii.b2a_base64(hashed.digest()).rstrip("\n")
def _normalize_params(self, params):
normalized = {}
for key, value in params.iteritems():
key = quote(str(key), safe = "")
value = quote(str(value), safe = "")
normalized[key] = value
return normalized
if __name__ == "__main__":
wc = WooCommerce("CONSUMER KEY HERE", "CONSUMER SECRET HERE", "YOUR ENDPOINT")
wc._make_request("/orders", {})
Which when ran, should produce a url similar to this:
http://www.example.com/wc-api/v2/orders?oauth_signature=0NqB%2BDDtJN2tf2XNkSmXLk2aHro%3D&oauth_consumer_key=CONSUMERKEYHERE40&oauth_signature_method=HMAC-SHA1&oauth_nonce=UzlURlhUTkZaQkM5SEFVNTJWWU5IQ0s3RFZENkZDSFY&oauth_timestamp=1412780008
But when the URL is opened, I always get this error:
{"errors":[{"code":"woocommerce_api_authentication_error","message":"Invalid Signature - provided signature does not match"}]}
Can anybody help me out with this?
I found out, even though the function is there, python wasn't keeping the insertion order of the dictionary. This caused the oauth_signature_method to come before the oauth_nonce causing it to be a different signature that the server's. To fix this, I remade the uksort function as this:
def uksort(dictionary):
return collections.OrderedDict(sorted(dictionary.items(), cmp = cmp))
I am trying to upload a file using multipart_encode to realize the MIME process. However, I met the following error AttributeError: multipart_yielder instance has no attribute '__len__'. Below are is my approach, I really appreciate if anyone can give me some suggestions.
url = "https://pi-user-files.s3-external-1.amazonaws.com/"
post_data = {}
#data is a dict
post_data['AWSAccessKeyId']=(data['ticket']['AWSAccessKeyId'])
post_data['success_action_redirect']=(data['ticket']['success_action_redirect'])
post_data['acl']=(data['ticket']['acl'])
post_data['key']=(data['ticket']['key'])
post_data['signature']=(data['ticket']['signature'])
post_data['policy']=(data['ticket']['policy'])
post_data['Content-Type']=(data['ticket']['Content-Type'])
#I would like to upload a text file "new 2"
post_data['file']=open("new 2.txt", "rb")
datagen, headers = multipart_encode(post_data)
request2 = urllib2.Request(url, datagen, headers)
result = urllib2.urlopen(request2)
If you want to send a file you should wrap other parameters with a MultipartParam object, example code for creating a send file request:
from poster.encode import multipart_encode, MultipartParam
import urllib2
def postFileRequest(url, paramName, fileObj, additionalHeaders={}, additionalParams={}):
items = []
#wrap post parameters
for name, value in additionalParams.items():
items.append(MultipartParam(name, value))
#add file
items.append(MultipartParam.from_file(paramName, fileObj))
datagen, headers = multipart_encode(items)
#add headers
for item, value in additionalHeaders.iteritems():
headers[item] = value
return urllib2.Request(url, datagen, headers)
Also I think you should execute register_openers() once at the beginning. Some details you can find in docs
The problem is that in httplib.py, the generator is not detected as such and is treated instead like a string that holds the full data to be sent (and therefore it tries to find its length):
if hasattr(data,'read') and not isinstance(data, array): # generator
if self.debuglevel > 0: print "sendIng a read()able"
....
A solution is to make the generator act like a read()able:
class GeneratorToReadable():
def __init__(self, datagen):
self.generator = datagen
self._end = False
self.data = ''
def read(self, n_bytes):
while not self._end and len(self.data) < n_bytes:
try:
next_chunk = self.generator.next()
if next_chunk:
self.data += next_chunk
else:
self._end = True
except StopIteration:
self._end = True
result = self.data[0:n_bytes]
self.data = self.data[n_bytes:]
return result
and use like so:
datagen, headers = multipart_encode(post_data)
readable = GeneratorToReadable(datagen)
req = urllib2.Request(url, readable, headers)
result = urllib2.urlopen(req)