Creating a Decorator - python

I'm getting a value from a HTTP GET request. I'm checking whether the value is not equal to none. If it is , then return message is sent else further processing is happening.
def api_servers():
installation_name = request.args.get('installation_name', '')
if installation_name == '':
data = {"description": "Installation Name is required"}
return HttpResponse(json.dumps(data), status=400, mimetype='application/json')
data = { "arms": arms_list }
return HttpResponse(json.dumps(data), status=200, mimetype='application/json')
Now, I want to check this condition using a decorator function. This is what I'm trying.
def wrapper(func):
def inner(): # I dont know what to pass in the arguments. Installation_name?
# Maybe? But then how do I pass Installation_name
if installation_name == '':
data = {"description": "Installation Name is required"}
return HttpResponse(json.dumps(data), status=400, mimetype='application/json')
else:
return func()
return inner
How can I achieve this via decorator?
EDIT
While I was proceeding, I found that I need another decorator which checks data is None or not.It also checks whether installation_name is empty or not and then I need to "recycle" both the installation_name and data. I re-wrote your code again. But the values are being interchanged, ie data gets installation_name and vice-versa. Can you please have a look? Thanks
def with_description(fn):
def inner(*args, **kwargs):
# Precheck
installation_name = 'inst1'
if installation_name == '':
return 1
# If precheck passes
return fn(installation_name, *args, **kwargs)
return inner
def with_description1(fn):
def inner(*args, **kwargs):
# Precheck
data = 'data1'
if data == '':
return 1
# If precheck passes
return fn(data, *args, **kwargs)
return inner
#with_description
#with_description1
def api_servers1(installation_name,data):
print installation_name,data
api_servers1()

Uhm, give this a try:
import functools
def with_description(fn):
#functools.wraps(fn) # This is not strictly necessary, just pulls info (docstrings, etc) from fn and pushes into the wrapper
def wrapper(*args, **kwargs):
# Precheck
installation_name = request.args.get('installation_name', '')
if installation_name == '':
data = {"description": "Installation Name is required"}
return HttpResponse(json.dumps(data), status=400, mimetype='application/json')
# If precheck passes, call the actual function
fn(*args, **kwargs)
return wrapper
With this you win that every function decorated actually gets called only when the precheck passes.
EDIT
If as commented, you want to recycle the installation name, you just have to pass it to the function call. One possibility would be the following:
fn(installation_name, *args, **kwargs)
And of course, the decorated function has to expect that argument.

Related

How to apply the same decorator chain to multiple functions

#extend_schema(
methods=['GET'],
responses={(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY})
#extend_schema(
methods=['PUT'],
request={STYLES_MIME_TYPE: OpenApiTypes.BINARY},
responses={(204, 'application/json'): OpenApiResponse(
response={'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
examples=[OpenApiExample(
'Returned style IDs example',
status_codes=['204'],
value=[101, 102, 103])])})
#api_view(['GET', 'PUT'])
#permission_classes([IsAuthenticated|ReadOnly])
#renderer_classes([StylesRenderer, StylesJSONRenderer])
#parser_classes([StylesParser])
def styles(request: Request, pid: int) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(pk=pid)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
#extend_schema(
methods=['GET'],
responses={(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY})
#extend_schema(
methods=['PUT'],
request={STYLES_MIME_TYPE: OpenApiTypes.BINARY},
responses={(204, 'application/json'): OpenApiResponse(
response={'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
examples=[OpenApiExample(
'Returned style IDs example',
status_codes=['204'],
value=[101, 102, 103])])})
#api_view(['GET', 'PUT'])
#permission_classes([IsAuthenticated|ReadOnly])
#renderer_classes([StylesRenderer, StylesJSONRenderer])
#parser_classes([StylesParser])
def styles_xref(request: Request, xref: uuid.UUID) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(xref=xref)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
This is Django, and obviously I want to use the same decorators for those 2 views. The only difference is that one looks up object by int ID, and the other by UUID xref field. How can I keep this DRY?
You could define a new decorator which returns a pre-decorated function with the chain you want. For example, we can first define three custom decorators:
import functools
# A decorator factory which returns a new decorator.
def decorator_factory(message):
def decorator(function):
# Wraps the decorated function.
#functools.wraps(function)
def wrapper(*args, **kwargs):
# Example behavior:
# - Prints a message before calling the decorated function.
print(message)
# Calls the decorated function.
return function(*args, **kwargs)
return wrapper
return decorator
# Defines three new decorators.
decorator_1 = decorator_factory("Ham")
decorator_2 = decorator_factory("Spam")
decorator_3 = decorator_factory("Eggs")
The way these decorators are presently invoked resembles the following, which quickly becomes repetitive for multiple functions:
#decorator_1
#decorator_2
#decorator_3
def f():
pass # Do something.
#decorator_1
#decorator_2
#decorator_3
def g():
pass # Do something.
#decorator_1
#decorator_2
#decorator_3
def h():
pass # Do something.
However, you can decorate a wrapper function within the body of a decorator:
def decorator_chain(function):
#functools.wraps(function)
#decorator_1
#decorator_2
#decorator_3
def wrapper(*args, **kwargs):
return function(*args, **kwargs)
return wrapper
Which simplifies the function definitions to:
#decorator_chain
def f():
pass # Do something.
#decorator_chain
def g():
pass # Do something.
#decorator_chain
def h():
pass # Do something.
In your provided example, this might look something like the following:
import functools
def decorator_chain(function):
#functools.wraps(function)
#extend_schema(
methods = ['GET'],
responses = {(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY}
)
#extend_schema(
methods = ['PUT'],
request = {STYLES_MIME_TYPE: OpenApiTypes.BINARY},
responses = {
(204, 'application/json'): OpenApiResponse(
response = {'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
examples = [
OpenApiExample(
'Returned style IDs example',
status_codes = ['204'],
value = [101, 102, 103]
)
]
)
}
)
#api_view(['GET', 'PUT'])
#permission_classes([IsAuthenticated | ReadOnly])
#renderer_classes([StylesRenderer, StylesJSONRenderer])
#parser_classes([StylesParser])
def wrapper(*args, **kwargs):
return function(*args, **kwargs)
return wrapper
#decorator_chain
def styles(request: Request, pid: int) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(pk=pid)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
#decorator_chain
def styles_xref(request: Request, xref: uuid.UUID) -> Response:
"""
Get or save styles for a project.
GET - protobuf binary response
POST - returnd IDs for saved styles
"""
try:
project = Project.objects.get(xref=xref)
return _handle_styles(request, project)
except Project.DoesNotExist:
raise Http404()
Using a decorator factory could even allow you to quickly create different variants of a given chain of decorators.
Modified example from GeeksForGeeks
Decorators are, in essence, ordinary functions. Thus, you can use partial method to provide a set of arguments to them without actually calling the method.
from functools import partial
def decorator(like):
print("Inside decorator")
def inner(func):
print(like)
func()
return inner
premade_decorator = partial(decorator, like = "geeksforgeeks")
#premade_decorator()
def my_func():
print("Inside actual function")
Now we know how to pre-apply parameters to a decorator, we can try chaining them:
def chain_decorator(dec_list):
def inner(func):
for dec in dec_list:
func = partial(dec(), func)
return func
return inner
#chain_decorator([premade_decorator1, premade_decorator2])
def my_func():
print("Inside actual function")
return 1
print(my_func())

can someone explain how this whole code work

I just started studying, and in the courses we parsed the code,
but I still could understand it
can someone explain how this whole code work
This code is correct, i just want to know how it's work
The object-oriented paradigm has several principles:
Data is structured in the form of objects, each of which has a certain type, that is, belongs to a class.
Classes are the result of formalizing the problem being solved, highlighting its main aspects.
Inside the object, the logic for working with information related to it is encapsulated.
Objects in the program interact with each other, exchange requests and responses.
Moreover, objects of the same type respond in a similar way to the same requests.
Objects can be organized into more complex structures, for example, include other objects, or inherit from one or more objects.
def search_object(func):
def wrapper(*args, **kwargs):
for object_ in args[0].objects:
if object_.get("id") == args[1]:
return func(*args, object_=object_, **kwargs)
return {"status": 404, "msg": "Not found"}
return wrapper
class CRUD:
def __init__(self):
self.objects = []
self.id = 0
def create(self, **kwargs):
self.id +=1
object_ = dict(id=self.id, **kwargs)
self.objects.append(object_)
return {"status": 201, "msg": "successfully created"}
#search_object
def retrieve(self, id, **kwargs):
return kwargs["object_"]
#search_object
def delete(self, id, **kwargs):
self.objects.pop(self.objects.index(kwargs["object_"]))
return {"status": 200, "msg": "Successfully deleted"}
#search_object
def update(self, id, **kwargs):
object_ = kwargs.pop("object_")
object_.update(**kwargs)
print(id, kwargs)
return {"status": 200, "msg": "Successfully updated"}
obj1 = CRUD()
obj1.create(name="hello", last_name="world")
obj1.create(name="tratata", last_name="vatafa")
obj1.create(name="blatata", last_name="pajata")
# print("Before object removing: ", obj1.objects)
# print("Retrieved: ", obj1.retrieve(2))
# print("Deleted: ", obj1.delete(2))
# print("After object removing: ", obj1.objects)
obj1.update(1, name="Petya", last_name="Ivanov")
print(obj1.objects)

AssertionError: expected view func if endpoint is not provided - python

trying to decorate end point with requires_fields decorator. this is the implementation.
import functools
from flask import request, jsonify
def requires_fields(fields):
required_fields = set(fields)
def wrapper(func):
#functools.wraps(func)
def decorated(*args, **kwargs):
current_fields = set(request.get_json().keys())
missing_fields = required_fields - current_fields
if missing_fields:
return jsonify({'error': 'missing fields', 'fields': list(missing_fields)}), 400 # Bad Request
resp = func(*args, **kwargs)
return resp
return wrapper
#app.route('/is_registered', methods=['POST'])
#requires_fields(['mobile'])
def is_registered():
_json = request.get_json()
keys = _json.keys()
customer = Customer()
registered = False
response = verify_required_params(['mobile'], keys)
if response:
return response
_mobile = _json['mobile']
validated = validate_mobile(_mobile)
cust, response_code = customer.get(_mobile)
if cust is not None and cust['id']:
registered = True
if not validated:
response = responses.get(MOBILE_NUMBER_NOT_VALID)
return jsonify(response)
if not registered:
response = responses.get(MOBILE_NUMBER_NOT_REGISTERED)
return jsonify(response)
response = responses.get(MOBILE_NUMBER_REGISTERED)
return jsonify(response)
It is giving me this error:
assert view_func is not None, "expected view func if endpoint is not provided."
AssertionError: expected view func if endpoint is not provided.
What is causing this issue? How can we fix this?
You forgot to return the decorated function from the wrapper function.
When you do #wrapper on top of a function test for instance, you are basically writing test = wrapper(test).
Since wrapper does not return anything, you get an error.
So basically you need to do:
def requires_fields(fields):
required_fields = set(fields)
def wrapper(func):
#functools.wraps(func)
def decorated(*args, **kwargs):
# ...
return decorated # you are missing this
return wrapper

How to use a dict returned by a decorator as a variable?

I am working on developing a flask app using flask-restx. In there, I would like to decorate the routes with a decorator named cognito_check_groups that checks the token, verifies it, and returns the claims.
I would like to know is there a way I can save the returning claims as a variable so that I can use its content further
My code:
def cognito_check_groups(groups: list):
def decorator(function):
def wrapper(*args, **kwargs):
_cognito_check_groups(groups)
return function(*args, **kwargs)
return wrapper
return decorator
def _cognito_check_groups(groups: list):
token = None
if 'x-access-token' in request.headers:
token = request.headers['x-access-token']
if not token:
return abort(
401,
'Missing token'
)
headers = jwt.get_unverified_headers(token)
kid = headers['kid']
key_index = -1
for i in range(len(keys)):
if kid == keys[i]['kid']:
key_index = i
break
if key_index == -1:
print('Public key not found in jwks.json')
return False
public_key = jwk.construct(keys[key_index])
message, encoded_signature = str(token).rsplit('.', 1)
decoded_signature = base64url_decode(encoded_signature.encode('utf-8'))
if not public_key.verify(message.encode("utf8"), decoded_signature):
print('Signature verification failed')
return False
print('Signature successfully verified')
claims = jwt.get_unverified_claims(token)
return claims
I have used the decorator as follows:
#crud_ns.route("/insert")
class InsertVendor(Resource):
#crud_ns.doc(
"Insert data into the DB",
responses={
200: "Values returned",
400: "Validation Error",
401: "Not authorized",
409: "Conflct"
},
)
#crud_ns.doc(security='apikey')
#crud_ns.expect(crud_insert_parser, validation=True)
#cognito_check_groups(['admin'])
def post(self):
args = crud_insert_parser.parse_args()
return Insert.insert(**args)
Although i dont know much about Flask, usually you could something like this:
def cognito_check_groups(groups: list):
def decorator(function):
def wrapper(*args, **kwargs):
claims = _cognito_check_groups(groups)
# add claims to kwargs!
kwargs.update({'claims': claims})
return function(*args, **kwargs)
return wrapper
return decorator
...
#cognito_check_groups(['admin'])
def post(self, *args, **kwargs):
claims = kwargs.get('claims', {})
args = crud_insert_parser.parse_args()
return Insert.insert(**args)

Return HTTP response in the function called by view

In view, I accepts json keys and values in request.body. I plan to check for the existence of the json keys required (typo maybe) in another function
def checkJsonKey(form, *args):
for key in enumerate(args):
if key not in form:
return HttpResponse(status = 400) #<--
Instead of doing checking on the returned value, can this function directly return response and terminate this view function?
In my view function,
form = json.loads(request.body)
checkJsonKey(form,"user_preference","model_id", "filename")
Here's how i would handle this, using an exception and then catching it, rather than returning the response, this allows you to return a number of error conditions from your function and handle them in a try/except catcher.
def checkJsonKey(form, *args):
if type(form) not dict:
raise ValueError("Not dict")
# assuming you actually want to deal with a list of the args, and not a turple pairs from enumerate
for key in list(args):
# assuming that form is a dict
if key not in form.keys():
raise ValueError("Key not found")
return True
this would be called as
form = json.loads(request.body)
try:
checkJsonKey(form,"user_preference","model_id", "filename")
except ValueError as e:
return HttpResponseBadRequest("%s" % e)
# rest of the code
if you wanted to get really fancy, you could define your own error class and specifically catch for that
Check if the value returned from checkJsonKey is None, if it is not, then it must have returned your Response and you can return that from the view function.
def my_view(request, *args, **kwargs):
form = json.loads(request.body)
response = checkJsonKey(form, *args)
if response is not None:
return response
It won't make much difference, but if you want another method, json.loads() returns a dictionary from the json string passed to it, of which existence of keys can easily be checked by calling the form[key] syntax. You can further check the docs of the json API here.
Try this:
def checkJsonKey(form, *args):
for key in enumerate(args):
if form[key] != None:
return False
return True
def my_view(request, *args, **kwargs):
form = json.loads(request.body)
response = checkJsonKey(form, *args)
if response:
return HttpResponse(status = 200)
else:
return HttpResponse(status = 400)

Categories

Resources