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.
Related
I am not able to send POST request to OKEx API. However, with GET method, everything is ok - I can check my account balance. As soon I send POST request I've received that error
{'msg': 'Invalid Sign', 'code': '50113'}
class OkexBot:
def __init__(self, APIKEY: str, APISECRET: str, PASS: str):
self.apikey = APIKEY
self.apisecret = APISECRET
self.password = PASS
self.baseURL = 'https://okex.com'
#staticmethod
def get_time():
return dt.datetime.utcnow().isoformat()[:-3] + 'Z'
#staticmethod
def signature(timestamp, method, request_path, body, secret_key):
if str(body) == '{}' or str(body) == 'None':
body = ''
else:
body = json.dumps(body)
message = str(timestamp) + str.upper(method) + request_path + str(body)
mac = hmac.new(bytes(secret_key, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
output = mac.digest()
return base64.b64encode(output)
def get_header(self, request='GET', endpoint='', body: dict = dict()):
cur_time = self.get_time()
header = dict()
header['CONTENT-TYPE'] = 'application/json'
header['OK-ACCESS-KEY'] = APIKEY
header['OK-ACCESS-SIGN'] = self.signature(cur_time, request, endpoint, body, APISECRET)
header['OK-ACCESS-TIMESTAMP'] = str(cur_time)
header['OK-ACCESS-PASSPHRASE'] = PASS
return header
def place_market_order(self, pair, side, amount, tdMode='cash'):
endpoint = '/api/v5/trade/order'
url = self.baseURL + endpoint
request = 'POST'
body = {
"instId": pair,
"tdMode": tdMode,
"side": side,
"ordType": "market",
"sz": str(amount)
}
body = json.dumps(body)
header = self.get_header(endpoint=endpoint, request=request, body=body)
response = requests.post(url=url, headers=header, data=body)
return response
I looked into this topic
How to sign an OKEx POST API request?
Unable to send authenticated OKEx API POST requests with body (in python). 401 Signature invalid. Authenticated GET requests work
Unable to send a post requests OKEX Invalid Signature
but nothing was helpful.
This code is vaid.
class OkexBot:
def __init__(self, APIKEY: str, APISECRET: str, PASS: str):
self.apikey = APIKEY
self.apisecret = APISECRET
self.password = PASS
self.baseURL = 'https://www.okex.com'
#staticmethod
def get_time():
return dt.datetime.utcnow().isoformat()[:-3] + 'Z'
#staticmethod
def signature(timestamp, method, request_path, body, secret_key):
message = timestamp + method + request_path + body
mac = hmac.new(bytes(secret_key, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
output = mac.digest()
return base64.b64encode(output)
def get_header(self, request='GET', endpoint='', body=''):
cur_time = self.get_time()
header = dict()
header['CONTENT-TYPE'] = "application/json"
header['OK-ACCESS-KEY'] = APIKEY
header['OK-ACCESS-SIGN'] = self.signature(cur_time, request, endpoint, body, APISECRET)
header['OK-ACCESS-TIMESTAMP'] = cur_time
header['OK-ACCESS-PASSPHRASE'] = PASS
return header
def place_market_order(self, pair, side, amount, tdMode='cash'):
endpoint = '/api/v5/trade/order'
url = self.baseURL + '/api/v5/trade/order'
request = 'POST'
body = {
"instId": pair,
"tdMode": tdMode,
"side": side,
"ordType": "market",
"sz": str(Decimal(str(amount)))
}
body = json.dumps(body)
header = self.get_header(request, endpoint, str(body))
print(header)
response = requests.post(url, headers=header, data=body)
return response
The below is a result of this question How to sign an OKEx API request? and some of the answers:
import hmac
import base64
import requests
import datetime
import json
from config import KEY, SECRET, PASS, ROOT_URL
def get_time():
now = datetime.datetime.utcnow()
t = now.isoformat("T", "milliseconds")
return t + "Z"
def signature(timestamp, request_type, endpoint, body, secret):
if body != '':
body = json.dumps(body)
message = str(timestamp) + str.upper(request_type) + endpoint + body
print(message)
mac = hmac.new(bytes(secret, encoding='utf-8'), bytes(message, encoding='utf-8'), digestmod='sha256')
d = mac.digest()
return base64.b64encode(d)
def get_header(request_type, endpoint, body):
time = get_time()
header = dict()
header['CONTENT-TYPE'] = 'application/json'
header['OK-ACCESS-KEY'] = KEY
header['OK-ACCESS-SIGN'] = signature(time, request_type, endpoint, body, SECRET)
header['OK-ACCESS-TIMESTAMP'] = str(time)
header['OK-ACCESS-PASSPHRASE'] = PASS
return header
def get(endpoint, body=''):
url = ROOT_URL + endpoint
header = get_header('GET', endpoint, body)
return requests.get(url, headers=header)
def post(endpoint, body=''):
url = ROOT_URL + endpoint
header = get_header('POST', endpoint, body)
return requests.post(url, headers=header)
where KEY, SECRET, PASS are the API key, secret key, and pass phrase respectively; The ROOT_URL is 'https://www.okex.com'.
The Problem
GET requests work absolutely fine, so when I run the following, there are no issues:
ENDPOINT = '/api/v5/account/balance'
BODY = ''
response = get(ENDPOINT)
response.json()
However, when I try to place an order via a POST request, like so:
ENDPOINT = '/api/v5/trade/order'
BODY = {"instId":"BTC-USDT",
"tdMode":"cash",
"side":"buy",
"ordType":"market",
"sz":"1"}
response = post(ENDPOINT, body=BODY)
response.json()
I get the following output, i.e. it won't accept the signature:
{'msg': 'Invalid Sign', 'code': '50113'}
Related Questions
In this one Can't figure out how to send a signed POST request to OKEx an answer was provided, but it does not work for me as I was already using the suggested URL. More or less the same question was asked here Unable to send a post requests OKEX Invalid Signature, but no activity likely due to the format, so I thought I would repost and elaborate.
OKEX Docs
The docs simply specify that The API endpoints of Trade require authentication (https://www.okex.com/docs-v5/en/?python#rest-api-authentication-signature). But they make no reference to there being any difference between the two methods. Away from that, I am including all required parameters in the body of the post request as far as I can see.
I would appreciate any input on this.
Many thanks!
I ran into the same POST problem and figured it out. I used new domain name okex.com. Here is my code.
def set_userinfo(self):
position_path = "/api/v5/account/set-position-mode"
try:
self.get_header("POST", position_path, {"posMode":"net_mode"})
resp = requests.post(url=self.base_url+position_path, headers=self.headers, json={"posMode":"long_short_mode"}).json()
except Exception as e:
log.error("OK set_userinfo error={} type={}".format(f'{e}', f'{type(e)}'))
def get_header(self, request_type, endpoint, body=''):
timestamp = self.get_time()
self.headers["OK-ACCESS-TIMESTAMP"] = timestamp
self.headers["OK-ACCESS-SIGN"] = self.signature(timestamp, request_type, endpoint, body)
def signature(self, timestamp, request_type, endpoint, body):
if body != '':
body = json.dumps(body)
message = str(timestamp) + str.upper(request_type) + endpoint + body
mac = hmac.new(bytes(self.secret_key, encoding='utf-8'), bytes(message, encoding='utf-8'), digestmod='sha256').digest()
return base64.b64encode(mac)
I have fix the same problem.
Both of the 'body' in signature() and in get_header() should be json.
So you should add following code:
if str(body) == '{}' or str(body) == 'None':
body = ''
else:
body = json.dumps(body)
I ran into the same problem and solved it using below code snippet, the idea is from https://stackoverflow.com/a/68115787/20497127, but I modified a little by adding POST functionality
APIKEY = "" # input key
APISECRET = "" #input secret
PASS = "" #input passphrase
BASE_URL = 'https://www.okx.com'
def send_signed_request(http_method, url_path, payload={}):
def get_time():
return dt.datetime.utcnow().isoformat()[:-3]+'Z'
def signature(timestamp, method, request_path, body, secret_key):
if str(body) == '{}' or str(body) == 'None':
body = ''
message = str(timestamp) + str.upper(method) + request_path + str(body)
mac = hmac.new(bytes(secret_key, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
d = mac.digest()
return base64.b64encode(d)
# set request header
def get_header(request='GET', endpoint='', body:dict=dict()):
cur_time = get_time()
header = dict()
header['CONTENT-TYPE'] = 'application/json'
header['OK-ACCESS-KEY'] = APIKEY
header['OK-ACCESS-SIGN'] = signature(cur_time, request, endpoint , body, APISECRET)
header['OK-ACCESS-TIMESTAMP'] = str(cur_time)
header['OK-ACCESS-PASSPHRASE'] = PASS
# demo trading: need to set x-simulated-trading=1, live trading is 0
header['x-simulated-trading'] = '1'
return header
url = BASE_URL + url_path
header = get_header(http_method, url_path, payload)
print(url)
print(header)
if http_method == 'GET':
response = requests.get(url, headers=header)
elif http_method == 'POST':
response = requests.post(url, headers=header, data=payload)
return response.json()
# this will run get requests
res = send_signed_request("GET", "/api/v5/account/balance", payload={})
# this will run post requests
data = {
"instId": "BTC-USDT",
"tdMode": "cross",
"side": "sell",
"ccy":"USDT",
"ordType": "limit",
"px": "100000",
"sz": "0.01"
}
res = send_signed_request("POST", "/api/v5/trade/order", payload=json.dumps(data))
I created this method after running into issues with my token timing out. The call worked when it was just a function and not part of a class. Here is the api class I created to call the msgraph. It successfully gets the token and stores it. I am just trying to call the method of get_users. The error is
<bound method msGraph.Decorators.refreshToken.<locals>.wrapper of <msgraph.msGraph object at 0x0197F628>>
class msGraph():
token = None
token_expiration = None
tenantid = None
user = None
apiurl = None
def __init__(self, tenantid):
self.tenantid = tenantid
try:
self.token = self.gettoken()
if self.token is None:
raise Exception("Request for access token failed.")
except Exception as e:
print(e)
else:
self.token_expiration = time.time() + 1800
def gettoken(self):
try:
apiurl = "https://login.microsoftonline.com/{}/oauth2/v2.0/token".format(self.tenantid)
headers = {'content-type': 'application/x-www-form-urlencoded'}
data = {'grant_type': 'client_credentials',
'client_id': 'xxx',
'client_secret': "xxx",
'scope': 'https://graph.microsoft.com/.default'}
newdata = urllib.parse.urlencode(data)
token = requests.post(apiurl, data=newdata, headers=headers).json()
except Exception as e:
print(e)
return None
else:
return token['access_token']
class Decorators():
#staticmethod
def refreshToken(decorated):
def wrapper(api, *args, **kwargs):
if time.time() > api.token_expiration:
api.gettoken
return decorated(api, *args, **kwargs)
return wrapper
#Decorators.refreshToken
def get_users(self):
try:
print('trying to get users')
apiurl = "https://graph.microsoft.com/v1.0/users"
authheader = "Bearer {}".format(self.token)
print(authheader)
header = {'Authorization': authheader, 'content-type': 'application/json'}
response = requests.get(apiurl, headers=header).json()
except Exception as e:
print(e)
return None
else:
users = response['value']
userList = []
for user in users:
if "#EXT" not in user['userPrincipalName']:
userList.append(user['userPrincipalName'])
return userList
This is how I am accessing it from the main script
main.py
from msgraph import msGraph
from processemails import userscan
tenant_id = "xxx"
company_Domain = "xxx"
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
api = msGraph(tenant_id)
userList = api.get_users()
for user in userList:
userscan(user, company_Domain, api)
quit()
I think you meant to write
userList = api.get_users()
with the parenthesis. Without the parenthesis, what you're assigning to userList is the function object, instead of the result of calling the function. And a function object is not an iterable, hence your error message.
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
I've been trying for hours, and I just don't know what I'm doing wrongly. It's just for planning/research (not performant) -- playing around with some code from github -- but I need to see it functional.
RPC_USER = username
RPC_PASS = pasword
rpc_id = ID HERE
jsonrpc = "2.0"
payload = {"jsonrpc": jsonrpc, "id": rpc_id, "method": method, "params": params}
authstr = base64.encodestring(bytes('%s:%s' % (RPC_USER, RPC_PASS), 'utf-8')).strip()
request_headers = {"Authorization": "Basic %s" % authstr, 'content-type': 'application/json'}
try:
response = requests.get(RPC_URL, headers = request_headers, data = json.dumps(payload)).json()
print(response['result'])
except Exception as e: print(str(e))
if response['id'] != rpc_id:
raise ValueError("invalid response id!")
I get an error as follows:
Here's the whole traceback:
Expecting value: line 1 column 1 (char 0) # prints the Exception
Traceback (most recent call last):
File "miner_2017.py", line 411, in <module>
solo_miner(bin2hex("------coinbase message here -----"), "-----bitcoin address here-----")
File "miner_2017.py", line 401, in solo_miner
mined_block, hps = block_mine(rpc_getblocktemplate(), coinbase_message, 0, address, timeout=60)
File "miner_2017.py", line 63, in rpc_getblocktemplate
try: return rpc("getblocktemplate", [{}])
File "miner_2017.py", line 52, in rpc
if response['id'] != rpc_id:
UnboundLocalError: local variable 'response' referenced before assignment
Which after doing some looking seems to be a problem with decoding the json object from a bytes object rather than a string object. I don't know how to fix this. It seems the "response" variable assignment was unsuccessful due to the json problem. How can I get the json object in string form from the request?
Would somebody help me out? Thanks
#!/usr/bin/env python
import getpass
import json
import requests
def instruct_wallet(method, params):
url = "http://127.0.0.1:8332/"
payload = json.dumps({"method": method, "params": params})
headers = {'content-type': "application/json", 'cache-control': "no-cache"}
try:
response = requests.request("POST", url, data=payload, headers=headers, auth=(rpc_user, rpc_password))
return json.loads(response.text)
except requests.exceptions.RequestException as e:
print e
except:
print 'No response from Wallet, check Bitcoin is running on this machine'
rpc_user='foo'
rpc_password='bar'
passphrase = getpass.getpass('Enter your wallet passphrase: ')
timeout = raw_input('Unlock for how many seconds: ')
answer = instruct_wallet('walletpassphrase', [passphrase, timeout])
if answer['error'] != None:
print answer['error']
else:
print answer['result']
I'm using something similar for Altcoins
import decimal
import itertools
import json
import requests
id_counter = itertools.count()
class BTCJsonRPC(object):
def __init__(self, url, user, passwd, log, method=None, timeout=30):
self.url = url
self._user = user
self._passwd = passwd
self._method_name = method
self._timeout = timeout
self._log = log
def __getattr__(self, method_name):
return BTCJsonRPC(self.url, self._user, self._passwd, self._log, method_name, timeout=self._timeout)
def __call__(self, *args):
# rpc json call
playload = json.dumps({'jsonrpc': '2.0', 'id': next(id_counter), "method": self._method_name, "params": args})
headers = {'Content-type': 'application/json'}
resp = None
try:
resp = requests.post(self.url, headers=headers, data=playload, timeout=self._timeout,
auth=(self._user, self._passwd))
resp = resp.json(parse_float=decimal.Decimal)
except Exception as e:
error_msg = resp.text if resp is not None else e
msg = u"{} {}:[{}] \n {}".format('post', self._method_name, args, error_msg)
self._log.error(msg)
return
if resp.get('error') is not None:
e = resp['error']
self._log.error('{}:[{}]\n {}:{}'.format(self._method_name, args, e['code'], e['message']))
return None
elif 'result' not in resp:
self._log.error('[{}]:[{}]\n MISSING JSON-RPC RESULT'.format(self._method_name, args, ))
return None
return resp['result']
I'm pretty sure you just need to change from using a GET to a POST, i.e.:
change
response = requests.get(RPC_URL, headers = request_headers, data = json.dumps(payload)).json()
to
response = requests.post(RPC_URL, headers=request_headers, data=json.dumps(payload)).json()
In fact, when I tried this with GET (without dumping the response to json), I got a 405 response. You should always take a look at your response object before doing further debugging with it.