Python "requests" with decorator - python

There is my code I'm trying to do:
import json
import requests
class Request(object):
def __init__(self, method, path):
self.method = method
## Q1
self.url = ''.join([***MyClass.host***, path])
self.h = {
"Content-Type": "application/json;charset=UTF-8"
}
def __call__(self, f):
def wrapper(*args, **kwargs):
# Q2
getattr(requests, self.method)(url = self.url, headers = h***, data = json.dumps(body)***)
return wrapper
class MyClass(object):
def __init__(self, host):
self.host = host
#Request(method = "post", path = "/add_mission")
def AddMission(self, mission):
body = {
"mission_id": mission
}
#Request(method = "get", path = "/system_info")
def GetInfo(self):
print("I want get info")
There are some questions hope someone can solve my problems:
1. How can my decorator "Request" get the variable "host" from "MyClass"? (In comment ## Q1)
2. How can I pass the function's variable "body" to decorator, is it possible? (In comment ## Q2)
Because I need to access different url(host+path) with [post, get, delete].
I'm not sure isn't decorator match on this case?
Or maybe there are better ways to deal with this case?

The host is available once you call the decorated function, via the invoking object's host attribute, so wait until you actually call the method to build the URL. You'll want to call the underlying function to get back a value, if any, to use as the payload.
import json
import requests
class Request(object):
def __init__(self, method, path):
self.method = method.upper()
self.h = {
"Content-Type": "application/json;charset=UTF-8"
}
self.path = path
def __call__(self, f):
def wrapper(obj, *args, **kwargs):
payload = f(*args, **kwargs)
args = {
'method': self.method,
'url': 'http://{}/{}'.format(obj.host, self.path),
'headers': self.h
}
if payload is not None:
args['json'] = payload
return requests.request(**args)
return wrapper
class MyClass(object):
def __init__(self, host):
self.host = host
#Request(method="post", path="/add_mission")
def AddMission(self, mission):
return {"mission_id": mission}
#Request(method="get", path="/system_info")
def GetInfo(self):
print("I want get info")
Then you can write, for example,
api = MyClass("www.example.com")
add_response = api.AddMission("mars")
info_response = api.GetInfo()
Each decorated function returns the Response object produced by the call to requests.request.
You might consider subclasses of Request for each method:
class GetRequest(Request):
def __init__(self, path):
super().__init__("GET", path)
class PostRequest(Request):
def __init__(self, path):
super().__init__("POST", path)
# etc

The code now has all types of requests covered, plus the possibility of having authentication on each request.
Code on github
Pros:
Easy to read, less code, therefore easier to maintain.
Easy to add new methods or to update the existing ones.
Cons:
Can't get it to work on pytest.
Less "standard" API might cause hard to debug errors.
Less "standard" API might need changes to the wrapper that can be tricky.
Big performance loss because of all the nested function calls (methods/decorators are functions)
import ujson
import requests
class Request(object):
def __init__(self, method, path, h={ "Content-Type": "application/json;charset=UTF-8" }):
self.path = path
#print(f"PATH {path}")
self.method = method.upper()
self.h = h
def __call__(self, f):
def wrapper(obj, *args, **kwargs):
payload = f(obj, *args, **kwargs)
#print(f"PAYLOAD {payload}")
if (payload is not None) and (self.method == "GET" or self.method == "DELETE"):
dic_key = list(payload.keys())[0]
self.path = "{}/?{}={}".format(self.path, dic_key, payload[dic_key])
payload = None
args = {
'method': self.method,
'url': 'http://{}/{}'.format(obj.host, self.path),
'headers': self.h
}
if payload is not None:
args['json'] = payload
return requests.request(**args)
return wrapper
class GetRequest(Request):
def __init__(self, path, h=None):
if h is None:
super().__init__("GET", path)
else:
super().__init__("GET", path, h)
class PostRequest(Request):
def __init__(self, path, h=None):
if h is None:
super().__init__("POST", path)
else:
super().__init__("POST", path, h)
class PutRequest(Request):
def __init__(self, path, h=None):
if h is None:
super().__init__("PUT", path)
else:
super().__init__("PUT", path, h)
class DeleteRequest(Request):
def __init__(self, path, h=None):
if h is None:
super().__init__("DELETE", path)
else:
super().__init__("DELETE", path, h)
myToken = 'Secret_key_1234567890'
class DecoRequests(object):
def __init__(self, host):
self.host = host
#GetRequest(path="userlist", h={'access_token':myToken})
def list_employees(self):
pass
#GetRequest(path="users", h={'access_token':myToken})
def list_employee(self, username):
return {"username":username}
#PostRequest(path="users", h={'access_token':myToken})
def add_employee(self, employee_data):
return employee_data
#PutRequest(path="users", h={'access_token':myToken})
def update_employee(self, employee_data):
return employee_data
#DeleteRequest(path="users", h={'access_token':myToken})
def rm_employee(self, username):
return {"username":username}

Related

Call function from child class in python

This is my python code.
class Model:
def __init__(self, name=None):
if name is None:
name = get_apnx_service_name(self.__class__.__name__)
print("Object created for", name)
self.url_suffix = "/{name}/".format(name=name)
self.name = name
self.apnx_session = sessiona()
def get_all(self, start_element=0, num_elements=100, method='GET', fields=None, params=None, **kwargs):
if params is None:
params = {}
if is_list(fields):
params['fields'] = join_list(fields)
params["start_element"] = start_element
params["num_elements"] = num_elements
res = self.apnx_session.request(method, url_suffix=self.url_suffix, params=params, **kwargs)
return res
class Split(Model):
def __init__(self, name="/budget-splitter/{li}/splits"):
super().__init__(name=name)
self.format_str = name
def get_all(self, li_id, method='GET', **kwargs):
self.url_suffix = self.format_str.format(li=li_id)
#super().get_all(**kwargs)**kwargs)
#here
I needed to change url_suffix in get_all() from Split class.I changed in Split class get_all method.I want to call the output of get_all() in child class.How can i do this.I don't want to do like this solution of writing same code again in Split class.
class Split(Model):
def __init__(self, name="/budget-splitter/{li}/splits"):
super().__init__(name=name)
self.format_str = name
def get_all(self, start_element=0, num_elements=100, method='GET', fields=None, params=None, **kwargs):
if params is None:
params = {}
if is_list(fields):
params['fields'] = join_list(fields)
params["start_element"] = start_element
params["num_elements"] = num_elements
res = self.apnx_session.request(method, url_suffix=self.url_suffix, params=params, **kwargs)
return res
SOLVED
using return super(Split, self).get_all(**kwargs)
What if an extra argument, li_id, were added to the constructor for class Split and url_suffix was initialized during construction (or should I say re-initialized after the base class has already initialized it)? It would then seem that there would be no need to override method get_all. Could this work?
class Split(Model):
def __init__(self, li_id, name="/budget-splitter/{li}/splits"):
super().__init__(name=name)
self.url_suffix = name.format(li=li_id)

How to dynamically add method to class with `functools.partial()`

I am having trouble with the right incantation to get a dynamic method added to a class using functools.partial in the following situation. The following has a Creator class to which I want to add a create_someclass method, which is partially parameterized by the creator class state.
import functools
class Creator:
def __init__(self, params):
self.params = params
class Stitch:
__tablename__ = 'stitch'
def __init__(self, params, name):
self.name = name
self.params = params
def create(self, clz, *args, **kwargs):
return clz(self.params, *args, **kwargs)
for clazz in [Stitch]:
setattr(Creator, 'create_%s' % clazz.__tablename__, functools.partial(create, clz=clazz))
creator = Creator('params')
# Neither of these work, but I'd like either one -- preferably the first one.
stitch = creator.create_stitch('myname')
# AttributeError: 'str' object has no attribute 'params'
stitch = creator.create_stitch(name='myname')
# TypeError: create() missing 1 required positional argument: 'self'
This is a problem for making partial for class methods, so in Python 3.4 we introduced partialmethod as the alternative. The way that works is the following:
import functools
class Creator:
def __init__(self, params):
self.params = params
class Stitch:
__tablename__ = 'stitch'
def __init__(self, params, name):
self.name = name
self.params = params
def create(self, clz, *args, **kwargs):
return clz(self.params, *args, **kwargs)
for clazz in [Stitch]:
setattr(Creator, 'create_%s' % clazz.__tablename__, functools.partialmethod(create, clz=clazz))
# use partialmethod instead here
creator = Creator('params')
stitch = creator.create_stitch(name='myname')
# works!
I think the problem is that create is a member function of Stitch (despite your bad indentation: create accesses the member variable params of Stitch), so you would need an object of type Stitch to use with create, which would then also be passed as the first argument to create. It would work like this:
import functools
class Creator:
def __init__(self, params):
self.params = params
class Stitch:
__tablename__ = 'stitch'
def __init__(self, params, name):
self.name = name
self.params = params
def create(self, clz, *args, **kwargs):
return clz(self.params, *args, **kwargs)
creator = Creator('params')
stitch1 = Stitch('pp', 'my_name')
print("stitch1= ", stitch1)
for clazz in [Stitch]:
setattr(Creator, 'create_%s' % clazz.__tablename__, functools.partial(stitch1.create, clazz))
stitch = creator.create_stitch('myname')
print("stitch= ", stitch)

Pythonic way to access Flask request arguements

In my Flask app I am setting a number of local variables that have come in via an API call, using the
from flask import request
.
.
submission_id = request.args.get('submission_id')
grader = request.args.get('grader')
grading_factor = float(request.args.get('grading_factor'))
answer_key = request.args.get('answer_key')
submission_key = request.args.get('submission_key')
What is a less repetitive or otherwise more Pythonic way of setting these 5 variables?
As I suggested in the comments, you could come up with a decorator that would map the request arguments to the corresponding function parameters, such as:
def map_args(func):
#functools.wraps(func)
def wrapper(**kwargs):
all_args = dict(request.args, **kwargs)
return func(**all_args)
return wrapper
Then:
#app.route('/mypath')
#map_args
def handler(submission_id, grader, grading_factor, ...):
"""the remaining logic"""
That's similar to what Flask does with the view_args, except that it does not do any type conversions, which this margin is too narrow to contain.
from flask import Flask, request
app = Flask(__name__)
class DotDict(object):
def __init__(self, inner):
self._inner = inner
def __getattr__(self, item):
return self._inner.get(item)
def get(self, item, default=None):
return self._inner.get(item, default)
class LazyAttribute(object):
def __init__(self, obj, attr):
self.obj = obj
self.attr = attr
def __getattribute__(self, item):
return getattr(getattr(object.__getattribute__(self, 'obj'),
object.__getattribute__(self, 'attr')),
item)
rargs = DotDict(LazyAttribute(request, 'args'))
#app.route("/")
def hello():
print rargs.a, rargs.c, rargs.get('d', 3)
return "Hello World!"
if __name__ == "__main__":
app.run(debug=True)
Accessing http://localhost:5000/?a=1 prints 1 None 3 in the terminal.
The LazyAttribute class is because calling just DotDict(request.args) outside of a request context throws an error. The alternative is to make a function:
def rargs():
return DotDict(request.args)
but I wanted to make usage as smooth as possible.
Firstly, I don't think there's anything wrong with the way you're doing it. But, there are a couple of different approaches you could take; the first being to the call to get the argument from request:
from flask import request
# ...
def getRequestArg(name, *args):
return request.args.get(name, *args)
submission_id = getRequestArg('submission_id')
grader = getRequestArg('grader')
grading_factor = float(getRequestArg('grading_factor'))
answer_key = getRequestArg('answer_key')
submission_key = getRequestArg('submission_key')
If you don't need each of these to be separate local variables, you could store them all in a dict:
from flask import request
# ...
args = {}
arg_names = ('submission_id', 'grader', 'grading_factor', 'answer_key', 'submission_key')
for arg in arg_names:
args[arg] = request.args.get(arg)

Storing state between decorators in python

I'll start by saying, I have a suspicion this is a solution that could be solved with a functional programming approach, but I don't know nearly enough of the concepts (but have been trying).
I've based my current solution on:
https://pythonconquerstheuniverse.wordpress.com/2012/04/29/python-decorators/
http://www.brianholdefehr.com/decorators-and-functional-python
https://github.com/mitsuhiko/click/blob/master/click/decorators.py
In the interest of learning (that's all this is!) how to build decorators, I decided to make a simple cache decorator.
But I get stuck in a loop where, I try to encapsulate the function I'm wrapping in a class, but every time I call the function I've wrapped, I call __call__ in wrapping class and so on ad infinitum.
I think I could have a nest of closures between the chain decorators, but I don't know how to collect all my variables in one scope.
I appreciate I could put all my arguments in a single decorator call, but my intention here is to learn how to chain decorators and store state between them.
Can anyone suggest a way (or ammend my way) to store state between chained decorators?
My intended design was:
# main.py
import http.client
from cache import cache
#cache.keys('domain', 'url')
#cache.lifetime(3600)
def make_http_request(domain,url='/'):
conn = httplib.HTTPConnection(domain)
conn.request("GET",url)
return conn.getresponse()
if __name__ == '__main__':
print(make_http_request('http://example.com/'))
with cache.py looking like
import hashlib
import os
import inspect
__author__ = 'drews'
def expand(path):
return os.path.abspath(os.path.expanduser(path))
class CacheManager():
"""Decorator to take the result and store it in a a file. If the result is needed again, then the file result is returned"""
def __init__(self, function, function_arg_name):
self.lifetime = 3600
self.cache_keys = None
self.cache_path = '~/.decorator_cache/'
self.f = function
self.arg_names = function_arg_name
def __call__(self, *args, **kwargs):
if len(args) > 0:
arg_names = self.arg_names
if 'self' in arg_names:
arg_names.remove('self')
key_args = dict(zip(arg_names, args))
key_args.update(kwargs)
else:
key_args = kwargs
self._initialise(cache_path=expand(self.cache_path))
key = self._build_key(key_args)
if self.key_exists(key):
result = self.get_key(key)
else:
result = self.f()
self.set_key(key, result)
return result
def _build_key(self, key_elements):
m = hashlib.md5()
for key in self.cache_keys:
m.update(key_elements[key].encode('utf-8'))
return m.hexdigest()
def _initialise(self, cache_path):
def initialise_path(path):
if not os.path.isdir(path):
(head, tail) = os.path.split(path)
if not os.path.isdir(head):
initialise_path(head)
os.mkdir(path)
initialise_path(cache_path)
def key_exists(self, key):
path = os.path.join(expand(self.cache_path), key)
return os.path.exists(path)
class CacheDefinitionDecorator(object):
def __init__(self, *args, **kwargs):
self.d_args = args
class CacheKeyDefinitionDecorator(CacheDefinitionDecorator):
def __call__(self, func, *args, **kwargs):
if not isinstance(func, CacheManager):
func = CacheManager(func,inspect.getargspec(func)[0])
func.cache_keys = self.d_args
return func
class CacheLifetimeDefintionDecorator(CacheDefinitionDecorator):
def __call__(self, func, *args, **kwargs):
if not isinstance(func, CacheManager):
func = CacheManager(func,inspect.getargspec(func)[0])
func.lifetime = self.d_args[0]
return func
class CacheStruct(object):
def __init__(self, **kwargs):
for item in kwargs:
setattr(self, item, kwargs[item])
cache = CacheStruct(
keys=CacheKeyDefinitionDecorator,
lifetime=CacheLifetimeDefintionDecorator
)

Django, Boto, S3: Why am I not getting a response suddenly?

This has been driving me crazy and I can't figure out where the issue is. AWS is having issues right now, but this problem was happening before all that and continues to exhibit same behavior.
Summary: I have an admin field that uploads images to S3 and stores the path in the database. On a fresh apache restart, it works fine for about 20 minutes. Then it stops working and just hangs on 'waiting for response from domain' That's it, no error message, just sits there, waiting, and won't work again until I restart apache. And after restarting apache it works flawlessly until it stops, which happens after 20 minutes best I can tell and is not cause by more or less traffic or a certain number of inserts, deletes, etc.
Here's code for the model:
class SampleImage(models.Model):
def __unicode__(self):
return self.name
name = models.CharField(max_length=50)
front_image = S3EnabledImageField(upload_to='samples')
back_image = S3EnabledImageField(upload_to='samples')
Code for S3EnabledImageField:
class S3EnabledImageField(models.ImageField):
def generate_filename(self, instance, filename):
path_join = os.path.join(self.get_directory_name(), self.get_filename(filename))
return path_join.replace("\\", "/")
def __init__(self, bucket=settings.BUCKET_NAME, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
if settings.USE_AMAZON_S3:
self.connection = S3Connection(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY)
if not self.connection.lookup(bucket):
self.connection.create_bucket(bucket)
self.bucket = self.connection.get_bucket(bucket)
kwargs['storage'] = S3Storage(self.bucket)
super(S3EnabledImageField, self).__init__(verbose_name, name, width_field, height_field, **kwargs)
Code for S3Storage:
class S3Storage(FileSystemStorage):
def __init__(self, bucket=None, location=None, base_url=None):
if location is None:
location = settings.MEDIA_ROOT
if base_url is None:
base_url = settings.MEDIA_URL
self.location = os.path.abspath(location)
self.bucket = bucket
self.base_url = base_url
def _open(self, name, mode='rb'):
class S3File(File):
def __init__(self, key):
self.key = key
def size(self):
return self.key.size
def read(self, *args, **kwargs):
return self.key.read(*args, **kwargs)
def write(self, content):
self.key.set_contents_from_string(content)
def close(self):
self.key.close()
return S3File(Key(self.bucket, name))
def _save(self, name, content):
key = Key(self.bucket, name)
if hasattr(content, 'temporary_file_path'):
key.set_contents_from_filename(content.temporary_file_path())
elif isinstance(content, File):
key.set_contents_from_file(content)
else:
key.set_contents_from_string(content)
key.make_public()
return name
def delete(self, name):
self.bucket.delete_key(name)
def exists(self, name):
return Key(self.bucket, name).exists()
def listdir(self, path):
return [key.name for key in self.bucket.list()]
def path(self, name):
raise NotImplementedError
def size(self, name):
return self.bucket.get_key(name).size
def url(self, name):
return Key(self.bucket, name).generate_url(100000)
def get_available_name(self, name):
return name
Not sure by looking at your code. But, sounds like there might be connections that aren't getting closed? At any rate, why not work with:
https://bitbucket.org/david/django-storages/wiki/S3Storage
?
django-storages has worked well for me.

Categories