Is there any pythonic way to deal with wrong user input? I'm creating a module to help people work with files, and I have functions like rename, move, basepath, etc. Example:
def move(file_path):
# do something and return
I would like to know how to handle exceptions (i.e. if I should wrap my code in a try-except block);
def move(file_path):
try:
# do something and return
except Exception as error:
# return error
If I should use the try-except, I would like to know how I should return from it. I have a background in functional programming, so I was thinking like this:
def move(file_path):
try:
# do something
return (True, something...)
except Exception as error:
return (False, error)
Other example:
def execute_query(database_cursor, query, fetch):
if type(database_cursor) != "":
return (1, "database_cursor isn't a valid database cursor")
cursor.execute(query)
if fetch == "*":
return self.cursor.fetchall()
yield self.cursor.fetchone()
In this case, I'm worried about the user sending input that is not a database.
Is there any convention for this functionality?
Thanks!
Update
How i'm doing:
from sys import exit
def testing_something(a, b, c):
try:
return 0, a + b + c
except Exception, error:
return 1, error
error, result = testing_something(1, 2, 3)
if error:
print error # raise error or treat.
sys.exit(error)
I think is very clever to do like this, now i can decide to raise it or to treat it.
In the past, I've used something like the following to ensure that a certain user input was "valid" in the sense that it was contained within a list or something. This was useful for validating manual file input for loading data into memory.
def validate_choice(selection, choices):
while selection not in choices:
selection = input("'%s' is not a valid entry. Try again: " % selection)
print("'%s' works! Returning..." % selection)
return selection
result1 = validate_choice('foo', ['foo', 'bar', 'baz'])
result2 = validate_choice('boogers', ['foo', 'bar', 'baz'])
If you're trying to coerce something of one type to another type, here's another example which coerces the user to enter either A) an integer or B) a number that can be coerced to an integer:
def str_to_int_validation(num):
parsed = False
while not parsed:
try:
num = input("Enter an integer: ")
num = int(num)
parsed = True
except ValueError:
print("'%s' is not an integer. Try again.")
return num
Hope that helps!
This you may consider Pythonic if you want, but it is nothing really but a hacky bit of code:
import os
import shutil
class MoveError(Exception): pass
def move (patha, pathb):
# Some checks
if not os.path.exists(patha):
return MoveError("'%s' does not exist!" % patha)
if os.path.exists(pathb):
return MoveError("'%s' already exists! I will not overwrite it!" % pathb)
print "Moving '%s' to '%s'..." % (patha, pathb)
try: shutil.move(patha, pathb)
except Exception, e:
return MoveError("Whoops, something nasty happened! Error is:\n%s" % str(e))
return "%i bytes moved" % os.path.getsize(pathb)
def help ():
print "This is some help!"
def quit ():
global running
print "Quitting!"
running = 0
commands = {"mv": move, "move": move, "help": help, "?": help, "q": quit, "quit": quit}
running = 1
while running:
inp = raw_input("--> ").split()
if not inp: continue
try: cmd = commands[inp[0]]
except:
print "Invalid command '%s'" % inp[0]
continue
result = cmd(*inp[1:])
if isinstance(result, Exception):
print "Error occurred!"
else: print "Done!"
if result is not None:
print result
Getting the command from commands dictionary could have also been:
cmd = commands.get(inp[0], None)
if not cmd: print "Command doesn't exist!"
or unefficient way:
if inp[0]not in commands:
print "No such command"
else: cmd = commands[inp[0]]
Now, we can start arguing over which of the three is more Pythonic. And that's just for this part of code.
But, it is dead true that returning exceptions, although it may be tempting is something to be done rarely. Usually only when you have to push something into some library's object to force it to catch the exception. (depends on design of the lib - and is very rarely needed). Your original design, starts well, with flag indicating error, but the second thing should be the error message then. Or go on like this, if you feel that you really need it for some reason. But you can always do:
def move (pa, pb):
raise MoveError, "I will not move anything!"
and then:
try:
commands["move"]("somefile", "somewhereelse") # To simulate call from user input
print "Done!"
except Exception, e:
print "An error occurred.\n%s" % str(e)
Related
I'm looking to use functions within my Python program to make it cleaner and more efficient, Within my function I either return true or false depending on a user's selection. Though in the case they enter an incorrect / invalid response, I'd like to return a return that way asking more questions don't be asked.
Edit:
To be a bit more descriptive; I'd like to re-create this:
def askquestion(question):
response = input(question, "Enter T or F")
if response == "T":
return True
elif response == "F":
return False
else:
return None
def askmultiple():
questionOne = askquestion("Do you fruits?")
if questionOne == None:
return # Exit the function, not asking more questions
questionTwo = askquestion("Do you Apples?")
if questionTwo == None:
return # Exit the function, not asking more questions
I want to cut out checking afterwards if it is None and just return return.
When you don't create a return statement at the end of a function that is equal to a sole return and those two are equal to return None call.
So you may organize your code like:
if returned_value is None:
# do something a
elif returned_value is False:
# do something else
else: # value is True
# do something b
You can try using a while loop to make sure that the user inputs a correct input.
For example:
while not response.isdigit():
response = input("That was not a number try again")
In this case, while the user input, "response" is not a number the python console will keep asking for a response. For a basic template,
while not (what you want):
(ask for input again)
I hope this helps you. :)
Use an exception flow.
def ask_question(prompt):
"""Asks a question, translating 'T' to True and 'F' to False"""
response = input(prompt)
table = {'T': True, 'F': False}
return table[response.upper()] # this allows `t` and `f` as valid answers, too.
def ask_multiple():
questions = [
"Do you fruits?",
"Do you apples?",
# and etc....
]
try:
for prompt in questions:
result = ask_question(prompt)
except KeyError as e:
pass # this is what happens when the user enters an incorrect response
Since table[response.upper()] will raise a KeyError if response.upper() is neither 'T' nor 'F', you can catch it down below and use that flow to move you out of the loop.
Another option is to write a validator that forces the user to answer correctly.
def ask_question(prompt):
while True:
response = input(prompt)
if response.upper() in ['T', 'F']:
break
return True if response.upper() == 'T' else False
I have three functions that "trickle down" and get called within another like so:
async def start_swings(self, server, user):
try:
while self.jailed[server.id][user.id]['current'] != 0:
await self.get_question(server, user)
else:
await self.escape_jail(server, user)
except KeyError: # User isn't jailed
pass
async def get_question(self, server, user):
rnd = random.choice(range(2))
if rnd == 0:
await self.swing_math(server, user)
elif rnd == 1:
await self.swing_reverse(server, user)
async def swing_math(self, server, user):
num1 = random.randint(1, 1000)
num2 = random.randint(1, 1000)
ops = ['+', '-']
op = random.choice(ops)
ans = int(eval(str(num1) + op + str(num2)))
await self.bot.send_message(user, "**\N{HAMMER} It's going to take you `{}` more swings to break free.**\n```What is {} {} {}?```\n*I might doze off if you take too long to answer. If you don't get a reply from me within a few seconds, type `>swing`. If you try to use `>swing` as a way to reset your current question, you'll get a cooldown penalty.*".format(self.jailed[server.id][user.id]['current'], num1, op, num2))
resp = await self.bot.wait_for_message(author=user)
resp = resp.clean_content
if resp == str(ans):
await self.bot.send_message(user, "Correct. \N{WHITE HEAVY CHECK MARK} Take a swing.")
self.jailed[server.id][user.id]['current'] -= 1
dataIO.save_json('data/jail/jailed.json', self.jailed)
elif resp == ">swing":
return
else:
await self.bot.send_message(user, "Wrong. \N{CROSS MARK} Let me put that brick back in place.")
self.jailed[server.id][user.id]['current'] += 1
dataIO.save_json('data/jail/jailed.json', self.jailed)
In the third function, you'll see this statement:
elif resp == ">swing":
return
How can I get that to effectively "double return" so that it instead returns to start_swings instead of get_questions? I am fairly new to Python and don't know exactly what to call this. I'm effectively trying to get execution of this script to stop completely at this area of the code:
elif resp == ">swing":
return
… rather than just having it return to the last function called.
You cannot "pop" the stack manually like you could do in old times basic, but you can use an exception (even if it's considered bad practice if the exception isn't thrown "exceptionnally"):
Inherit from Exception:
class DoubleReturnException(Exception):
pass
Install the handler:
while self.jailed[server.id][user.id]['current'] != 0:
try:
await self.get_question(server, user)
except DoubleReturnException:
pass
else:
Then instead of returning, just:
raise DoubleReturnException
to return to the except part of the handler (whatever the number of calls are).
Defining your own exception is necessary because it ensures that the program returns for this reason, and not for a ValueError or any other exception that the program could throw.
That must remain an exceptional case. Programming by throwing exceptions everywhere lead to "spaghetti code" and somehow reinstates the evil goto statement. You may find that interesting in the last stages of a stable product, when you cannot break all the (flawed) design for a particular case (but personally I never had to use tricks like that).
a = int(raw_input("A?"))
b = int(raw_input("B?"))
c = int(raw_input("C?"))
minusb = -b
bsquared = b**2
fourac = 4*a*c
twoa = 2*a
discriminant = bsquared + fourac
if discriminantt<0:
print "This quadratic have no real root."
elif determinant>=0:
answer1 = (minusb+((bsquared-fourac)**0.5))/twoa
answer2 = (minusb-((bsquared-fourac)**0.5))/twoa
print "X = %s and %s" % (answer1, answer2)
However, when the determinant is less than 0, instead of printing it simply runs an error message saying that answer1 and answer2 can't be done. How do you make the program stop running if discriminant<0?
PS: This is me simply trying to practice what I learnt online. Sorry if my code is terrible XD
a = int(raw_input("A?"))
b = int(raw_input("B?"))
c = int(raw_input("C?"))
minusb = -b
bsquared = b**2
fourac = 4*a*c
twoa = 2*a
determinant = bsquared-fourac
if determinant<0:
print "This quadratic have no real root."
elif determinant>=0:
answer1 = (minusb+((bsquared-fourac)**0.5))/twoa
answer2 = (minusb-((bsquared-fourac)**0.5))/twoa
print "X = %s and %s" % (answer1, answer2)
For a quadratic discrimant=bsqaured-4*a*c
depending on its value roots are real or imaginary
The Python way to catch is errors is to catch exceptions using the try/except construct, rather than trying to prevent the error to happen.
E.g.:
try:
answer1 = (minusb+((bsquared-fourac)**0.5))/twoa
answer2 = (minusb-((bsquared-fourac)**0.5))/twoa
print("X = %s and %s" % (answer1, answer2))
except ValueError as e:
print("oops: %s" % e)
The exception will inherit from the Exception class, but in practically all cases you will want to catch the specific exception, e.g. (in the above example) an exception of type ValueError.
You can easily get the type of exception you need to catch by
reading the documentation
just running the code (without an except clause) and reading the error message: it will give the type of the Exception being raised.
You can do whatever error-handling you deem appropriate in the exception handler. e.g. the following will exit the program if it catches the error:
import sys
try:
answer1 = (minusb+((bsquared-fourac)**0.5))/twoa
answer2 = (minusb-((bsquared-fourac)**0.5))/twoa
print("X = %s and %s" % (answer1, answer2))
except ValueError as e:
print("oops: %s" % e)
sys.exit(1)
also, print "foo" is no longer valid in Python3 (which you should use by all means); use print("foo") instead (which is valid in Python2 as well)
Just use
import sys
sys.exit(1)
Exit terminates the run. The 1 can be any number you prefer. usually 0 is used to say that the program was executed successfully, and 1 means that an error occured. Read more about this here How to exit from python
Look at your code, You are doing a mistake due to which your code is not working.
Look at these three lines of codes:
discriminant = bsquared + fourac
if discriminantt<0:
elif determinant>=0:
You are using three different variables unknowingly.
I am working on a program that will extract a text from a file like so:
NAME OF PROGRAM: text.txt
CONTENTS OF FILE:
1: 101010100101010101 1010010101010101 101010101010101
2: 0101010101 1010011010 10101010 10101010 10101010
3: 0001000101010 10101010 10101010 1010101010 10101
START LINE: 1
END LINE: 2
results.txt generated.
I am at the part where the program will ask for the name of the program and I plan to use exceptions when the name of the program has the length of zero.
The program should have ran like:
NAME OF PROGRAM:
THE NAME OF THE PROGRAM SHOULD NOT BE LESS THAN 1! [LEN_ERROR]
But the program run like so:
NAME OF PROGRAM:
THERE'S SOMETHING WRONG WITH YOUR INPUT! [INP_ERROR]
Here's the code:
class Program:
"""
Author : Alexander B. Falgui (alexbfalgui.github.io)
Program Name : Text Extractor
Description : Takes an integer or string as an input and generates a
text file with the extracted data.
Note: This program can be used, shared, modified by anyone.
"""
def __init__(self):
self.menu_file_loc = "menu"
return self.load_files()
def load_files(self):
#self.menu_file = open(self.menu_file_loc)
#self.read_mf = self.menu_file.read()
return self.main_menu()
def main_menu(self):
#print(self.read_mf)
print(""" [1] Extract Data\n [2] Exit""")
while (True):
try:
self.menu_input = input("CHOOSE AN OPTION> ")
if (self.menu_input == 1):
try:
self.program_name = raw_input("\nNAME OF THE PROGRAM: ")
self.program_name = open(self.program_name)
except IOError:
if (len(program_name) == 0):
print("THE NAME OF THE PROGRAM SHOULD NOT BE LESS THAN"),
print(" 1! [LEN_ERROR]")
print("%s does not exist" % self.program_name)
elif (self.menu_input == 0):
print("\n")
break
except SyntaxError:
continue
except NameError:
print("SOMETHING'S WRONG WITH YOUR INPUT. [INP_ERROR]\n")
# Run the program
Program()
Why did the program print the wrong exception and what can I do to fix that?
Please don't do except SyntaxError: continue, because you will silently go over any kind of syntax error.
To get more information about what's going wrong, you should except NameError as e to investigate further. See also https://docs.python.org/2/tutorial/errors.html
You should change the except NameError-part to the following:
except NameError as e:
print e
print("SOMETHING'S WRONG WITH YOUR INPUT. [INP_ERROR]\n")
and you will see what really goes wrong.
I'm not sure why you added those two exception handler at the end but you are getting a Name Exception because your refer to the program_name variable instead of self.program_name
Change your line if (len(program_name) == 0): to if (len(self.program_name) == 0): and it should work.
SomeDict = {'Sarah':20, 'Mark': 'hello', 'Jackie': 'bye'}
try:
result = ""
theKey = raw_input("Enter some key: ")
val = someDict[theKey]
except keyErrorr:
result "hello"
else:
result = result + "" + "done"
print result
I understand the try block you can insert and code to try and see what error comes up, and the error then can be caught by the except block. I am trying to figure out the best way to insert a if / else in the try and except block for the same key error that is present in this code. I was thinking that i could just replace the try and except with If/else or is it possible to just add a if/else in the try and except. Any help on how to insert a if/else into this code for key error would be greatly appreciated. So basically i want to add a if/else code into the try and except block for the same key error.
SomeDict = {'Sarah':20, 'Mark': 'hello', 'Jackie': 'bye'}
try:
result = "" #could i insert something like if result == "" : #for this line?
theKey = raw_input("Enter some key: ")
val = someDict[theKey]
except keyErrorr:
result "hello"
else:
result = result + "" + "done"
print result
One reasonable option is to initialize result = None, then test if result is None:.
It's better to use None than the empty string, since someday you might want a dictionary value to be the empty string, plus None is probably clearer to the casual reader of your code.
You could also just skip the try-except, and use if theKey in someDict:.
you can add another except without a specification what exception it should handle.
try:
# do something
except KeyError:
# do something because of the Keyerror
except:
# do what you need to do if the exception is not a KeyError
someDict = {'Sarah':20, 'Mark': 'hello', 'Jackie': 'bye'} # corrected dict name
result = ""
theKey = raw_input("Enter some key: ")
try: # just try the code where the error could be
val = someDict[theKey]
except KeyError: # corrected exception name and indent level
result = "hello" # corrected syntax
else: # corrected indent level
result = result + "" + "done" # why add "" ?
print result
does this work for you?