I'm new to Python and I have factory class that takes in API arguments and with my setup i get a NameError: 'self' is not defined. I understand that my functions calls are wrong. How would I refactor this to have the same logic
import requests
import json
class Call:
results = []
__response = None
__methods= dict(post=self.__post(), get=self.__get())
def __init__(self, root, endpoint, payload, header):
self.__root = root
self.__endpoint = endpoint
self.__payload = payload
self.__header = header
def __post(self):
self.__response = requests.post(
self.__root + self.__endpoint,
data = json.dumps(self.__payload),
headers = self.__header
)
self.__get_results()
def __get(self):
self.__response = requests.get(
self.__root + self.__endpoint,
data = json.dumps(self.__payload),
headers = self.__header
)
self.__get_results()
def __get_results(self):
if (self.__response.ok):
data = json.loads(self.__response.content)
results.append(
{
'result':data['result'],
'endpoint': self.__endpoint,
'status' : response.status_code
}
)
else:
results.append(
{
'result':'FAILED',
'endpoint': self.__endpoint,
'status' : response.status_code
}
)
def method(self, method):
return self.__methods[method]
login = Call(
Url.V1_PROD,
DriverEndpoint.LOGIN,
DriverPayload.LOGIN,
Request.HEADER
)
login.method('post')
You shouldn't use variable names starting with a __double __underscore, they are used to invoke name mangling, which you probably don't want.
Use a _single _underscore.
It's easier to declare your dictionary on the instance, in the __init__ method.
A common alternative would be to store the names of the methods you want to call, as strings, and use getattr to access the methods (see Call a Python method by name).
import requests
import json
class Call:
results = []
_response = None
def __init__(self, root, endpoint, payload, header):
self._root = root
self._endpoint = endpoint
self._payload = payload
self._header = header
# NO () after self._post, otherwise it would call the
# method and insert the return value in the dict
self._methods= dict(post=self._post, get=self._get)
def _post(self):
self._response = requests.post(
self._root + self._endpoint,
data = json.dumps(self._payload),
headers = self._header
)
self._get_results()
def _get(self):
self._response = requests.get(
self._root + self._endpoint,
data = json.dumps(self._payload),
headers = self._header
)
self._get_results()
def _get_results(self):
if (self.__response.ok):
data = json.loads(self.__response.content)
results.append(
{
'result':data['result'],
'endpoint': self._endpoint,
'status' : response.status_code
}
)
else:
results.append(
{
'result':'FAILED',
'endpoint': self._endpoint,
'status' : response.status_code
}
)
def method(self, method):
# Here, we have to effectively call the selected method,
# hence the () at the end
self._methods[method]()
login = Call(
Url.V1_PROD,
DriverEndpoint.LOGIN,
DriverPayload.LOGIN,
Request.HEADER
)
login.method('post')
__methods= dict(post=self.__post(), get=self.__get())
Your __methods variable is class variable, self is used to refer to an instance of Call, self cannot access to the class variable scope.
You can declare __method as an instance variable:
def __init__(self):
self.__methods= dict(post=self.__post(), get=self.__get())
def get_mothods(self):
return self.__methods
Related
I'm having trouble getting the updated value of a class variable.
When ConnectTestAPI is called after the p_request function is executed, the class variables which are result and orderNo should updated in the post function.
Then I want to receive the updated value of class variables by looping while statement in the p_request function.
However, despite setting the values of class variables with the post request, when the while statement is run, the corresponding values are still empty and 0 value respectively,
So, the while statement cannot be terminated and results in a time out error.
Here is my source code. Thank you in advance!
class ConnectTestAPI(APIView):
result=""
orderNo=0
def post(self, request):
data = request.data
ConnectTestAPI.result = data['result']
ConnectTestAPI.orderNo = data['orderNo']
print(ConnectTestAPI.result) # I could successfully get data from POST request here!
print(ConnectTestAPI.orderNo) # I could successfully get data from POST request here!
return HttpResponse("ok")
def p_request():
data = {
"a" : 1234,
"b" : 5678
}
data = json.dumps(data,ensure_ascii=False).encode('utf-8')
con = redis.StrictRedis(outside_server['ip'],outside_server['port'])
con.set("data_dict", data)
while True:
if ConnectTestAPI.result != "" and ConnectTestAPI.orderNo != 0:
break
res_result = ConnectTestAPI.result
res_orderNo = ConnectTestAPI.orderNo
return res_result, res_orderNo
You need to access the class variables using self:
class ConnectTestAPI(APIView):
result=""
orderNo=0
def post(self, request):
data = request.data
self.result = data['result']
self.orderNo = data['orderNo']
print(self.result) # I could successfully get data from POST request here!
print(self.orderNo) # I could successfully get data from POST request here!
return HttpResponse("ok")
def p_request():
data = {
"a" : 1234,
"b" : 5678
}
data = json.dumps(data,ensure_ascii=False).encode('utf-8')
con = redis.StrictRedis(outside_server['ip'],outside_server['port'])
con.set("data_dict", data)
while True:
if self.result != "" and self.orderNo != 0:
break
res_result = self.result
res_orderNo = self.orderNo
return res_result, res_orderNo
NOTE: This usage of class attributes is not recommended. You are mutating a class attribute which has side effects on all instances of that class. In your case, an ordinary attribute initialized within __ init__() would be ok:
class ConnectTestAPI(APIView):
def __init__(self):
self.result=""
self.orderNo=0
def post(self, request):
data = request.data
self.result = data['result']
self.orderNo = data['orderNo']
print(self.result) # I could successfully get data from POST request here!
print(self.orderNo) # I could successfully get data from POST request here!
return HttpResponse("ok")
def p_request():
data = {
"a" : 1234,
"b" : 5678
}
data = json.dumps(data,ensure_ascii=False).encode('utf-8')
con = redis.StrictRedis(outside_server['ip'],outside_server['port'])
con.set("data_dict", data)
while True:
if self.result != "" and self.orderNo != 0:
break
res_result = self.result
res_orderNo = self.orderNo
return res_result, res_orderNo
I wrote the following function. Because there are so many keywords, I try to use Threadpool
class AllFind():
def __init__(
self
):
self._session = Session()
def find_kewword(
self,
keyword,
):
url = "https://xxxxxxxx.yy"
params = {
"keyword" : keyword,
"version" : 5,
}
res = self._session.get(url, params=params)
res_count = res.json().get("count")
return res_count
I tried this code but it failed. In this way, only the result value for the end of the keyword_list is displayed. What is wrong?
obj = AllFind()
keyword_list = [1,23,4,5,6.....]
with ThreadPoolExecutor(max_workers=5) as executor:
task = executor.submit(obj.find_keyword, keyword_list)
I have this Python script to control a PfSense router via FauxAPI. The problem is that when i call a function it gives an error. I think i'm calling the function wrong. Does anyone know how to call them?
Here is a link to the API i'm using: https://github.com/ndejong/pfsense_fauxapi
I have tried calling config_get(self, section=none) but that does not seem to work.
import os
import json
import base64
import urllib
import requests
import datetime
import hashlib
class PfsenseFauxapiException(Exception):
pass
class PfsenseFauxapi:
host = '172.16.1.1'
proto = None
debug = None
version = None
apikey = 'key'
apisecret = 'secret'
use_verified_https = None
def __init__(self, host, apikey, apisecret, use_verified_https=False, debug=False):
self.proto = 'https'
self.base_url = 'fauxapi/v1'
self.version = __version__
self.host = host
self.apikey = apikey
self.apisecret = apisecret
self.use_verified_https = use_verified_https
self.debug = debug
if self.use_verified_https is False:
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
def config_get(self, section=None):
config = self._api_request('GET', 'config_get')
if section is None:
return config['data']['config']
elif section in config['data']['config']:
return config['data']['config'][section]
raise PfsenseFauxapiException('Unable to complete config_get request, section is unknown', section)
def config_set(self, config, section=None):
if section is None:
config_new = config
else:
config_new = self.config_get(section=None)
config_new[section] = config
return self._api_request('POST', 'config_set', data=config_new)
def config_patch(self, config):
return self._api_request('POST', 'config_patch', data=config)
def config_reload(self):
return self._api_request('GET', 'config_reload')
def config_backup(self):
return self._api_request('GET', 'config_backup')
def config_backup_list(self):
return self._api_request('GET', 'config_backup_list')
def config_restore(self, config_file):
return self._api_request('GET', 'config_restore', params={'config_file': config_file})
def send_event(self, command):
return self._api_request('POST', 'send_event', data=[command])
def system_reboot(self):
return self._api_request('GET', 'system_reboot')
def system_stats(self):
return self._api_request('GET', 'system_stats')
def interface_stats(self, interface):
return self._api_request('GET', 'interface_stats', params={'interface': interface})
def gateway_status(self):
return self._api_request('GET', 'gateway_status')
def rule_get(self, rule_number=None):
return self._api_request('GET', 'rule_get', params={'rule_number': rule_number})
def alias_update_urltables(self, table=None):
if table is not None:
return self._api_request('GET', 'alias_update_urltables', params={'table': table})
return self._api_request('GET', 'alias_update_urltables')
def function_call(self, data):
return self._api_request('POST', 'function_call', data=data)
def system_info(self):
return self._api_request('GET', 'system_info')
def _api_request(self, method, action, params=None, data=None):
if params is None:
params = {}
if self.debug:
params['__debug'] = 'true'
url = '{proto}://{host}/{base_url}/?action={action}&{params}'.format(
proto=self.proto, host=self.host, base_url=self.base_url, action=action, params=urllib.parse.urlencode(params))
if method.upper() == 'GET':
res = requests.get(
url,
headers={'fauxapi-auth': self._generate_auth()},
verify=self.use_verified_https
)
elif method.upper() == 'POST':
res = requests.post(
url,
headers={'fauxapi-auth': self._generate_auth()},
verify=self.use_verified_https,
data=json.dumps(data)
)
else:
raise PfsenseFauxapiException('Request method not supported!', method)
if res.status_code == 404:
raise PfsenseFauxapiException('Unable to find FauxAPI on target host, is it installed?')
elif res.status_code != 200:
raise PfsenseFauxapiException('Unable to complete {}() request'.format(action), json.loads(res.text))
return self._json_parse(res.text)
def _generate_auth(self):
# auth = apikey:timestamp:nonce:HASH(apisecret:timestamp:nonce)
nonce = base64.b64encode(os.urandom(40)).decode('utf-8').replace('=', '').replace('/', '').replace('+', '')[0:8]
timestamp = datetime.datetime.utcnow().strftime('%Y%m%dZ%H%M%S')
hash = hashlib.sha256('{}{}{}'.format(self.apisecret, timestamp, nonce).encode('utf-8')).hexdigest()
return '{}:{}:{}:{}'.format(self.apikey, timestamp, nonce, hash)
def _json_parse(self, data):
try:
return json.loads(data)
except json.JSONDecodeError:
pass
raise PfsenseFauxapiException('Unable to parse response data!', data)
Without having tested the above script myself, I can conclude that yes you are calling the function wrong. The above script is rather a class that must be instantiated before any function inside can be used.
For example you could first create an object with:
pfsense = PfsenseFauxapi(host='<host>', apikey='<API key>', apisecret='<API secret>')
replacing <host>, <API key> and <API secret> with the required values
Then call the function with:
pfsense.config_get() # self is not passed
where config_get can be replaced with any function
Also note
As soon as you call pfsense = PfsenseFauxapi(...), all the code in
the __init__ function is also run as it is the constructor (which
is used to initialize all the attributes of the class).
When a function has a parameter which is parameter=something, that something is the default value when nothing is passed for that parameter. Hence why use_verified_https, debug and section do not need to be passed (unless you want to change them of course)
Here is some more information on classes if you need.
You need to create an object of the class in order to call the functions of the class. For example
x = PfsenseFauxapi() (the init method is called during contructing the object)
and then go by x.'any function'. Maybe name the variable not x for a good naming quality.
I'm trying to create a common utility for file transfer, from source to destination. The arguments are separated into dir and filename, which is the result of os.path.dirname(some_file) and os.path.basename(some_file), respectively. The destination filename is the same as the source filename by default if not specified.
I have created a script that at least is working perfectly well with the specific requirements in my first project. But as you can notice, the get_src_des method is very repetitive that I want to improve the code re-usability out from the nasty if...elif... statement. Anyone has better idea to rewrite this method?
class FileTransfer:
def __init__(self, ftp_dir, local_dir, ftp_filename=None, local_filename=None):
self.ftp_dir = ftp_dir
self.ftp_filename = ftp_filename
self.local_dir = local_dir
self.local_filename = local_filename
self.ftp_dict = self.get_group(ftp_dir, ftp_filename)
self.local_dict = self.get_group(local_dir, local_filename)
#staticmethod
def get_group(dir, filename):
group = {
"dir": dir,
"filename": filename,
}
return group
def get_src_des(self, src):
if src == "ftp":
dict_src = self.ftp_dict
dict_des = self.local_dict
elif src == "local":
dict_src = self.local_dict
dict_des = self.ftp_dict
else:
dict_src = None
dict_des = None
return dict_src, dict_des
# other methods, such as download_from_src_to_des, upload_from_src_to_des, ...
Yes. This is classic use case for a dictionary.
You can rewrite your code as follows:
class FileTransfer:
def __init__(self, ftp_dir, local_dir, ftp_filename=None, local_filename=None):
self.ftp_dir = ftp_dir
self.ftp_filename = ftp_filename
self.local_dir = local_dir
self.local_filename = local_filename
self.ftp_dict = self.get_group(ftp_dir, ftp_filename)
self.local_dict = self.get_group(local_dir, local_filename)
self.param_dict = {
'ftp':(self.ftp_dict,self.local_dict),
'local' : (self.local_dict,self.ftp_dict)
}
#staticmethod
def get_group(dir, filename):
group = {
"dir": dir,
"filename": filename,
}
return group
def get_src_des(self, src):
if src in param_dict:
return param_dict[src]
else:
return (None,None)
Next time, instead of adding another elif statement, you just add another entry in your param_dict
I agree that that this could be done with a dictionary, as #Yakov Dan's answer suggests, but I would code it as shown below, which doesn't require any other changes to the class and is more dynamic.
The get_group() method could be written more concisely, as indicated.
class FileTransfer:
...
#staticmethod
def get_group(dir, filename):
return dict(dir=dir, filename=filename)
def get_src_des(self, src):
return {
'ftp': (self.ftp_dict, self.local_dict),
'local': (self.local_dict, self.ftp_dict)
}.get(src, (None, None))
I am trying to mock the streamed requests.get function for one object that uses it internally.
I particularly need to test following methods:
class MyObject(object)
def __iter__(self):
payload = {"op": "OPEN"}
response = requests.get("http://" + self.parsed_uri.uri_path, params=payload, stream=True)
return response.iter_lines()
def read(self, size=None):
if not size or size < 0:
payload = {"op": "OPEN", "offset": self.offset}
self.offset = 0
else:
payload = {"op": "OPEN", "offset": self.offset, "length": size}
self.offset = self.offset + size
response = requests.get("http://" + self.parsed_uri.uri_path, params=payload, stream=True)
return response.content
I need requests.get to return me something like "abc\n123"
And then my test method should look like:
#mock.patch('requests.get', mock.Mock(side_effect=mocked_requests_get))
def test_iter(self, mock_get):
object = MyObject(ParseUri("http://host/path"))
self.assertEqual(object.next(), "abc")
self.assertEqual(object.next(), "123")
#mock.patch('requests.get', mock.Mock(side_effect=mocked_requests_get))
def test_read(self, mock_get):
object = MyObject(ParseUri("http://host/path"))
self.assertEqual(object.read, "abc\n123")
I am trying to implement the mocked_requests_get, but unfortunately I can't make it work. Is it possible to use mock library this way? How should the mocked_requests_get look like?
UPD. Can't say what's really wrong with your code without full example. How I solves this with responses module:
import requests
import unittest
import responses
class Foo(object):
def __init__(self, uri):
self.uri = uri
def __iter__(self):
payload = {"op": "OPEN"}
response = requests.get("http://" + self.uri, params=payload,
stream=True)
return response.iter_lines()
class TestFoo(unittest.TestCase):
#responses.activate
def test_iter_lines(self):
responses.add(responses.GET, "http://local.host/", body='abc\n123')
r = iter(Foo("local.host"))
self.assertEqual(r.next(), "abc")
self.assertEqual(r.next(), "123")
if __name__ == '__main__':
unittest.main()
P.S. Maybe you want to return iterator before call this?
self.assertEqual(object.next(), "123")