Try two expressions with `try except` - python

I have two expressions. I need to try one expression, if it is raise an exception try another, but if the second raises an exception too - to raise the exception.
I tried this, but it is looks ugly and I am not sure it is the best way to solve this issue:
try:
image = self.images.order_by(func.random()).limit(1)
except:
try:
image = self.images.order_by(func.rand()).limit(1)
except ProgrammingError:
raise ProgrammingError(
'The database engine must be PostgtreSQL or MySQL')
How do you do it?

Making a separate function is very helpful.
def get_random_image(self):
for rand in func.random, func.rand:
try:
return self.images.order_by(rand()).limit(1)
except ProgrammingError:
pass
raise ProgrammingError('This database engine is not supported')

Use a loop:
for methname in ("random", "rand"):
try:
image = self.images.order_by(getattr(func, methname)()).limit(1)
break
except ProgrammingError:
continue
else:
raise ProgrammingError("The database engine must be PostgtreSQL or MySQL")
The loop's else clause is executed only if the loop terminates normally (i.e., without a break) which is why we break after doing the image assignment. If you consider this too tricksy, because the else clause is so infrequently used with for, then this would also work:
image = None
for methname in ("random", "rand"):
try:
image = self.images.order_by(getattr(func, methname)()).limit(1)
except ProgrammingError:
continue
if not image:
raise ProgrammingError("The database engine must be PostgtreSQL or MySQL")

In this particular case, I'd actually try and detect the database before selecting the function. Can you reach the database connection from your code? If so, just switch on the drivername:
random = None
if drivername == 'postgres':
random = func.random
elif drivername == 'mysql':
random = func.rand
else:
raise ValueError('This module requires that you use PostgreSQL or MySQL')
Then, when selecting images, use the random value:
image = self.images.order_by(random()).limit(1)

Actually it MIGHT be a design flaw. Raising exceptions is to act on an event that should normally not occur. If you want to do something functionally into an except (except for handling the exception), than it looks like the first statement that you want to try is not a statement that should get an exception at all.
So instead of:
try:
do statement 1
except ...
try:
do statement 2
except:
think about :
if (statement_1 result == ...)
try:
do statement 2
except:

If you want to check if rand or random is a function of a class, you also could use
if 'rand' in dir(some object of a class)

Related

Using nested Django transaction.atomic() to handle exceptions?

Django's docs say this about transaction.atomic() and exceptions:
https://docs.djangoproject.com/en/1.10/topics/db/transactions/#django.db.transaction.atomic
Avoid catching exceptions inside atomic!
...
The correct way to catch database errors is around an atomic block as shown above. If necessary, add an extra atomic block for this purpose. This pattern has another advantage: it delimits explicitly which operations will be rolled back if an exception occurs.
...
What does "If necessary, add an extra atomic block for this purpose." look like? Can I do this or does this cause "unexpected behavior"?
valid = True
errors = []
objects = MyModel.objects.all()
try:
with transaction.atomic():
for obj in objects:
try:
# Update and save obj here...
except:
errors.append("obj {} had errors".format(obj.pk))
valid = False
if not valid:
raise Exception('batch update failed.')
except Exception as ex:
# Handle it..
Do they mean to write it like this? If so, why is this different?
valid = True
errors = []
objects = MyModel.objects.all()
try:
with transaction.atomic():
for obj in objects:
try:
with transaction.atomic(): # Here's my 'extra atomic block'
# Update and save obj here...
except:
errors.append("obj {} had errors".format(obj.pk))
valid = False
if not valid:
raise Exception('batch update failed.')
except Exception as ex:
# Handle it..
Django triggers rolback only when transaction block catches DatabaseError (or its subclass), So you shouldn't catch it before it. If you add second transaction block (your 2nd example), error is caught, transaction is marked to rollbacked, and then you can do whatever you want.
I think that you should be good it yo re-raise exactly the same error, but this is just a guess.

Checking multiple fields: continue an IF loop if a try/except criteria is successful

Still very much a programming learner and I've found a gaping hole in my understanding so I'd appreciate any feedback on this. I have a Django-based HTML form that allows you to define additional drivers to a vehicle routing problem. It is critical that any error in the format of every input field is captured and rejected before it is stored. My approach is a series of if and elif clauses for a prototype list of expected errors, and part of it works just fine. However, I get to a test of whether I can parse a field into a datetime object using a try / except block and my if / elif chain terminates on a successful try (because elif is satisfied). The problem is, I still need to run the remaining tests on success, and using continue does not work on any indentation I can find.
I cannot think of a while loop or for loop to achieve the same effect. I tried putting it in a function, as listed below, but that was clutching at straws.
I have two questions 1) Am I well off-the-mark on how to check data entry; I'd love direction on this? 2) Do I have to break this down into multiple, separate tests to return the error of driverinfomessage?
The barebones is below. On a success of starttime test the loop ends and never checks endtime (EDIT: Although I'm aware that the continue statements will throw an error; they're there as expected behaviour)
def is_timestamp(stamp_string):
is_time = False
try:
stamp_string.datetime.datetime.strptime(stamp_string, "%H:%M")
is_time = True
except:
is_time = False
return is_time
if drivername == '':
driverinfomessage = "No driver name"
elif starttime != '':
test = is_timestamp(starttime)
if test:
continue
else:
driverinfomessage = "Invalid start time"
elif endtime != '':
test = is_timestamp(endtime)
if test:
continue
else:
driverinfomessage = "invalid end time"
I can post more of the function if needed. Thanks in advance.
Throw exceptions out of your main code, handle them outside.
In Python you often code the main happy scenario as if all goes nice
and let exceptions to be thrown in the case of problems.
The check_and_process it example of such "happy code".
The calling function (test_it here) is using try - except pair (there can
be even more except section), each expecting known exceptions.
This way your code is kept clean and still is able to handle exceptional
situation.
class FieldException(Exception):
pass
def get_timestamp(fieldname, stamp_string):
try:
return stamp_string.datetime.date.strptime(stamp_string, "%H:%M")
except:
msg = "Field {fieldname} expects time in HH:MM format."
raise FieldException(msg.format(fieldname=fieldname))
def check_and_process(driver_name, start_time_str, end_time_str):
if not driver_name:
raise FieldException("Driver name is required")
start_time = get_timestamp("start time", start_time_str)
end_time = get_timestamp("end time", end_time_str)
print("processing {}, {} and {}".format(driver_name, start_time, end_time))
def test_it():
try:
check_and_process("Johny", "12:34", "23:55")
# check_and_process("Johny", "12:34", "25:62")
# check_and_process("", "12:34", "25:62")
except FieldException as e:
print(e.msg)
Note, that it is very important to catch and handle all expected exceptions and
follow these rules:
never use pass as the only content in except block as it hides problems you shall discover (and later resolve)
learn, what expected types of exceptions can be thrown and catch those and process as needed.
let other (unexpected) exception to raise and break your program. It will be the fastest way to correction of it.
You might be aiming at something like the following:
if drivername == '':
driverinfomessage = "No driver name"
elif not is_timestamp(starttime):
driverinfomessage = "Invalid start time"
elif not is_timestamp(endtime):
driverinfomessage = "invalid end time"

How to gain program control back after handling the exception?

 am developing a python application . I have validated customer id from database. Means if the entered custid is present in database, i am raising an exception. In exception class i am printing the message . So far it is printing the message. But i am not sure how to get control back to the statement where i am taking the input. 
main app
Custid=input("enter custid)
Validate_custid(Custid)
Print(Custid)
validate_custid module
From connections import cursor
From customExceptions import invalidcustidException
Def validate_custid(custid):
Cursor.execute("select count(custid) from customer where custid=:custid",{"custid":custid})
For row in cursor:
Count=row[0]
If Count==0:
Raise invalidcustidException
So far its printing the message in exception.now i want my program to take custid as input whenever this exception occurs. The process should iterate until user enters valid custid.
You should use a try-except block with else statement:
while True:
custid = input('Input custom Id: ')
try:
# Put your code that may be throw an exception here
validate_custid(custid)
except InvalidcustidException as err:
# Handle the exception here
print(err.strerror)
continue # start a new loop
else:
# The part of code that will execute when no exceptions thrown
print('Your custom id {} is valid.'.format(custid))
break # escape the while loop
Take a look at here: https://docs.python.org/3.4/tutorial/errors.html#handling-exceptions
You'll want a try except block.
try:
# portion of code that may throw exception
except invalidcuspidError:
# stuff you want to do when exception thrown
See https://docs.python.org/2/tutorial/errors.html for more.
What you are trying to do is called exception handling. I think the Python docs explain this better than me, so here you go: https://docs.python.org/2/tutorial/errors.html#handling-exceptions

Python: Break-up large function into segments

I am creating a Bot for Reddit. I currently only have 1 very large function and I am looking to create sub-functions to make it more readable.
Here is a rough break-down of what it does
def replybot():
submissions = reversed(list(subreddit.get_new(limit=MAXPOSTS)))
for post in submissions:
try:
author = post.author.name
except AttributeError:
print "AttributeError: Author is deleted"
continue # Author is deleted. We don't care about this post.
# DOES PID EXIST IN DB? IF NOT ADD IT
cur.execute('SELECT * FROM oldposts WHERE ID=?', [pid])
sql.commit()
if cur.fetchone(): # Post is already in the database
continue
cur.execute('INSERT INTO oldposts VALUES(?)', [pid])
sql.commit()
...
I am looking to break the code up into segments i.e. put
try:
author = post.author.name
except AttributeError:
print "AttributeError: Author is deleted"
continue # Author is deleted. We don't care about this post.
in it's own function and call it from within replybot() but I run into the issue of calling continue. I get SyntaxError: 'continue' not properly in loop
Is there a way for me to do this?
If you take the inner part of a loop and convert it to its own function, it's no longer in a loop. The equivalent of continue in a loop, for a function, is return (i.e. terminate this iteration (which is now a function call) early).
Raise the error again instead of trying to continue. Either simply let it bubble to the main loop, or if you need better error handling, make your own custom error. For instance:
class MyNotFatalError(Exception):
pass
def do_something():
try:
a, b, c = 1, 2
except ValueError:
raise MyNotFatalError('Something went wrong')
# In your main function
for post in submissions:
try:
do_something()
do_some_other_thing()
do_some_more()
except MyNotFatalError as err:
continue # we could print out the error text here
do_some_last_thing()
It is probably better that way because you only catch errors you know you want to catch, and still let the program crash when there are actual bugs.
If you had simply caught ValueError that would also intercept and hide all other possible sources of the same kind of error.
as Claudiu said, when you broke inner commands into it's own function; It's not no longer in the loop and your code will be look like this:
def isNotAuthorDeleted(post):
try:
author = post.author.name
return author
except AttributeError:
print "AttributeError: Author is deleted"
return false
and your loop will be:
for post in submissions:
if not isNotAuthorDeleted(post):
continue

Python error exception handling with less return statements?

I have a function with a try catch block where I have:
def testfunction():
try:
good = myfunction()
return good
except ExceptionOne:
error="Error ExceptionOne".
return error
except ExceptionTwo:
error="Error ExceptionTwo".
return error
except ExceptionThree:
error="Error ExceptionThree".
return error
Is there a way such that I could structure this in such a way where have a single return statement for all the exceptions?
What about something like:
def testfunction():
try:
good = myfunction() # or `return myfunction()` if the case is actually this simple
except (ExceptionOne, ExceptionTwo, ExceptionThree) as err:
error = 'Error %s' % err.__class__.__name__
return error
return good
Of course, I'm not exactly sure what the purpose of this is. Why not just let the exception propogate and handle it higher up? As it is, you need logic to check the return value to see if it's good or not anyway, I think it would be cleaner if that logic was bound up in exception handling rather than in type checking or string value checking ...

Categories

Resources