I wonder if there is a python hackish way to achieve the following:
I found myself using an assert like structure in my views a lot:
def view(request):
if not condition:
return HttpResponseServerError("error")
if not condition2:
return HttpResponseServerError("error2")
[...]
return HttpResponse("OK!")
So I thought about using an assert like function:
def view(request):
def err(msg=None):
msg = msg if msg else "Illegal Parameters"
resp = {"msg": msg}
resp = json.dumps(resp)
return HttpResponseServerError(resp)
def verify(exp, msg=None):
if not exp:
err(msg)
verify(condition, "error")
verify(condition2, "error2")
return HttpResponse("OK")
Obviously, this does not work, as the result of the error function is never returned. Furthermore, I would also need to return the Response all the way to the view function and run return verify(), which will make my code prevent from execution of course.
One possible solution would be a decorator that either returns an error or the view function after all asserts went through. However, I would like to prevent that, as I also need some of the values I am establishing (imagine parsing one number after another and then having to pass a list of numbers).
Another solution I could think of is to actually do use a decorator and make my function a generator, yielding the result of verify. The decorator is a loop over that generator and keeps going until a response is yielded.
But in this post I am really looking for a more hackish way, to let the nested function return a reponse instead of the parent function and therefore prevent execution.
I will post my yield "solution" in a separate answer so you can get the picture :)
What about an exception, and a nice decorator to catch it:
class AssertError(Exception):
pass
def assertLike(view):
def wrap(request, *args, **kwargs):
try:
return view(request, *args, **kwargs):
except AssertError as e:
return HttpResponseServerError(...)
return wrap
#assertLike
def createTask(request):
import json
....
if not exp:
raise AssertError()
....
return HttpResponse("Ok")
Here I present the generator based solution:
def assertLike(view):
def wrap(request, *args, **kwargs):
for response in view(request, *args, **kwargs):
if response:
return response
return wrap
#other_django_views
#another_django_view
#assertLike
def createTask(request):
import json
def err(msg=None):
msg = msg if msg else "Illegal Parameters"
resp = {"msg": msg}
resp = json.dumps(resp)
return HttpResponseServerError(resp)
def verify(exp, msg=None):
if not exp:
return err(msg)
# only react to ajax requests
yield verify(True, "This is not an error")
yield verify(False, "Here it should stop!")
yield HttpResponse("This is the final response!")
Related
Suppose we have two kinds of methods: one returns a list, the other returns an iterator. So they are very comparable in the sense that both return values are iterable.
I'd like to write a decorator that catches errors inside the iteration. The problem is that the iterator is returned without iteration and so no errors will be caught.
In the below code, the wrapped_properly decorator works around the issue by providing two separate wrappers, a default one (wrapper) and one specifically for generator functions (generatorfunctionwrapper). The approach feels quite complicated and verbose.
from inspect import isgeneratorfunction
from functools import wraps
def failing_generator():
for i in range(1, 5):
if i % 2 == 0:
print('I dont like even numbers.')
raise ValueError(i)
yield i
def wrapped_badly(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
try:
return fn(*args, **kwargs)
except ValueError as err:
print('Not to worry.')
return wrapper
def wrapped_properly(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
try:
return fn(*args, **kwargs)
except ValueError as err:
print('Not to worry.')
#wraps(fn)
def generatorfunctionwrapper(*args, **kwargs):
try:
yield from fn(*args, **kwargs)
except ValueError as err:
print('Not to worry.')
if isgeneratorfunction(fn):
return generatorfunctionwrapper
else:
return wrapper
for x in wrapped_properly(failing_generator)():
print(x)
# Prints:
# 1
# I dont like even numbers.
# Not to worry.
for x in wrapped_badly(failing_generator)():
print(x)
# Prints:
# 1
# I dont like even numbers.
# Traceback (most recent call last):
# ...
# ValueError: 2
Is there a better/more pythonic way to do this?
I would suggest returning an iterator no matter what iterable the original function returns.
def wrapped(fn):
def wrapper(*args, **kwargs):
try:
yield from iter(fn(*args, **kwargs))
except ValueError as err:
print('Not to worry')
return wrapper
My question is pretty strange i know, but i have to do something like this:
def on_update(self, context):
self.possibleContext=[r"\/file\/",r"\/anyFile\/"]
def inner(function, *args, **kwargs):
self.handlers.append(function)
#i define result, is the dictionary returned by telegram request
function(result, *args, **kwargs)
return update
return inner
There is the decorator, under the polling function that should call every decorator
def polling(self, timeout: int = 0, *args, **kwargs):
print(f"Started {self.bot['first_name']} on TeleLib V.{self.version}")
try:
update_id = self.getUpdates()[0]['update_id']
except IndexError:
update_id = None
while True:
try:
for funct in self.handlers:
#Here i should call the decorator, not the function, because the decorator call the function giving it an argument
except Error1:
#here some telegram errors exceptions
except Error2:
#ecc
for update in self.getUpdates(offset=update_id, timeout=timeout):
update_id = update['update_id'] + 1
self.polling()
return update
and here a sample function:
#client.on_update('ciikk')
def funzione(update):
client.sendMessage(update['message']['chat']['id'],update['message']['text'])
I'm writing a script that requests some data from an IMAP server using the imaplib library. Having initiated a connection (c), I make the following calls:
rv, data = c.login(EMAIL_ACCOUNT, EMAIL_PASS)
if rv != 'OK':
print('login error')
else:
print(rv, data)
rv, mailboxes = c.list()
if rv != 'OK':
print('mailbox error')
else:
print(rv, data)
rv, data = c.select(EMAIL_FOLDER)
if rv != 'OK':
print('folder error')
else:
print(rv, data)
How could I rewrite this to use some sort of a wrapper function to reuse the logic of checking the error code and printing the data? I assume the function would take an error message as an argument and also the command to execute (select, login, etc.). How could I call a select connection function by passing it's name in an argument?
The way I understood you would like to check Decorators for your task.
class Wrapper:
def __init__(self, error_message):
self.error_message = error_message
def __call__(self, wrapped):
def func(*args, **kwargs):
rv, data = wrapped(*args, **kwargs)
if rv=="OK":
return(rv, data)
else:
print(self.error_message)
return(rv, data)
return func
#Wrapper("Folder Error")
def select(email_folder):
return "OK", "OLOLO"
#Wrapper("Folder Error")
def select_err(email_folder):
return "FAIL", "OLOLO"
print select("")
print select_err("")
yields
('OK', 'OLOLO')
Folder Error
('FAIL', 'OLOLO')
You can check reply inside of Wrapper's __call__ function and treat it the way you want to. For exampl you can return "False" or raise errors if rv not equals to "OK"
But It might be overly complicated for your case.
To re-use any code, look at the things that stay the same (e.g. the fact that rv and data come out of your imaplib calls in that order, and that rv=='OK' means things are OK) and write the logic that involves them, once. Then look at the things that change (e.g. the exact error message that needs to be printed). Parameterize the things that change, as in this example where the description argument changes the error message:
def check(description, rvdata):
rv, data = rvdata
if rv == 'OK':
print(data)
return data
else:
print(description + ' error')
return None
data = check('login', c.login(EMAIL_ACCOUNT, EMAIL_PASS))
mailboxes = check('mailbox', c.list())
selection = check('folder', c.select(EMAIL_FOLDER))
I have a function that returns a future. I want to create a decorator to the function which waits for the future to complete and then return the result essentially converting the async function to blocking function (which I will use in my REST API). Is there a way to do that?
def sync(fn):
def wrapped(*args, **kwargs):
return IOLoop.instance().run_sync(lambda: fn(*args, **kwargs))
return wrapped
#gen.coroutine
def my_coro():
# ...
sync_fn = sync(my_coro)
result = sync_fn()
To resolve a future you need to yield it. Something like this might work:
from tornado import gen
def blocking(func):
def new_func(*args, **kwargs):
result = yield func(*args, **kwargs)
return result
return gen.coroutine(new_func)
I'm wondering if there's any pythonic or short-form method to achieve the following:
error_response = self.check_conditions(request)
# If we have an error response, return it, otherwise continue as normal.
if error_response:
return error_response
Something like:
(return self.check_conditions(request)) or pass
Alternatively, is it possible for a function to return the calling method, such as:
self.check_conditions(request)
def check_conditions(self, request):
error_response = do_stuff()
if error_response:
return_parent error_response
I get the feeling the second concept is breaking a ton of programming laws to prevent chaos and the apocalypse, just a thought though :)
No, there is no short form for a conditional return.
But, to get to the second part of your question:
There are exceptions in Python. You can write something like this:
class MyErrorResponse(Exception): pass
class MyClass:
...
def check_conditions(self, request):
error_response = do_stuff()
if error_response:
raise MyErrorResponse(error_response)
def do_the_main_stuff():
try:
self.check_conditions()
...
except MyErrorResponse as e:
return e.args[0]
That depends a lot on what check_conditions does under the hood. It's likely that you can move error handling down a level of abstraction and handle things directly:
Compare:
error = False
def foo(request):
global error
try:
result = do_something_with(request)
except SomeWellDefinedError:
error = True
def check_conditions(request):
foo(request)
return error
def main():
error_response = check_conditions(some_request)
if error_response:
# freak out!
With
def foo(request):
try:
result = do_something_with(request)
except SomeWellDefinedError:
# you can try to handle the error here, or...
raise # uh oh!
def main():
try:
foo(some_request)
except SomeWellDefinedError:
# handle the error here, instead!