2

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.

4
  • 2
    FYI: comments in Python use # not //. Commented Jan 13, 2017 at 17:57
  • I'd recommend you to read PEP 8, it answers some of the questions you're having. Commented Jan 13, 2017 at 18:14
  • 2
    This has nothing to do with functional programming. Commented Jan 13, 2017 at 18:40
  • Use isinstance() to check for database, not type(). As for other things, this depends on your programming style, there is no 'Pythonic' convention. It can be anything from try-except with dictionaries, or ifs with dicts or lists or whatever way is most optimized for specific case. Sometimes you will ask for choice repetition within a loop, and sometimes from recursion. Things vary. Of course, you will not mix styles in same code. Also, there is no need to return a boolean if you are going to return an exception. Use: result = func(); if isinstance(result, Exception): print "Whoops!" Commented Jan 13, 2017 at 19:04

2 Answers 2

1

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!

Sign up to request clarification or add additional context in comments.

Comments

0

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)

9 Comments

You raise exceptions and catch them; you don't return them and check their types.
Look at move() function. It returns an exception when it fails.
Yes, and that is a bad design.
I do not argue that. This is how OP started. But it is also a valid code, that is sometimes necessary to be as such. Why would the OP want it is not my concern. May have a reason or two. You so wisely pointed out that this should be avoided. You may consider writing an answer as you are obviously so Pythonic. First you complain that I am not doing it, when I point out that I am, then you suddenly remember that it is a bad design. Nice!
I never said you should: I said that you should raise them instead of returning them.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.