Jokus
Jokus

Reputation: 11

Cleanly handle user input at the terminal without crashing

I have this simple project to do. This is the code I have so far, it works perfectly fine. But if someone types in a letter or an unknown symbol, the program crashes. How can I make this error proof and display or print a message if the wrong thing is entered?

def excercise5():

    print("Programming Excercise 5")
    print("This program calculates the cost of an order.")
    pound = eval(input("Enter the weight in pounds: "))
    shippingCost = (0.86 * pound) + 1.50
    coffee = (10.50 * pound) + shippingCost
    if pound == 1:
        print(pound,"pound of coffee costs $", coffee)
    else:
        print(pound,"pounds of coffee costs $", coffee)
    print()

excercise5()

Upvotes: 1

Views: 7117

Answers (5)

kxr
kxr

Reputation: 5539

Exceptions are the way to comfortably route and handle errors in non-trivial programs. But a clear concept is helpful to not make a random hack out of that when programs grow.
(E.g. catching builtin ValueError far away or returning / continuing by chance would quickly become hairy.)

There is the main difference between errors caused

  • by invalid / odd user input
  • by bugs
  • by dynamic system / environment limits.

A reasonable way of separating, routing and handling these errors is:

  • (A) Catch or compare for user input errors very early near the point of potential happening. React immediately for simple recoveries / repetitions. Otherwise (for breaking out) convert to an enriched exception which can be catched and distinguished further down or at the bottom of the call stack (or by the default handler sys.excepthook)

  • (B) Let crash down bug exceptions to the bottom of the call stack - unhandled; or possibly initiate comfortable bug presentation and feedback action.

  • (C) For system environment errors choose an approach between (A) and (B) depending on how much context, detail & comfort info you want to be present at the current stage of development.

This way this could become a scalable pattern for user oriented error handling in your example:

# Shows scalable user oriented error handling

import sys, traceback

DEBUG = 0

class UserInputError(Exception):
    pass

def excercise5():

    print("Programming Excercise 5")
    print("This program calculates the cost of an order.")

    # NOTE: eval() and input() was dangerous
    s = input("Enter the weight in pounds: ")
    try:        
        pound = float(s) 
    except ValueError as ev:
        raise UserInputError("Number required for weight, not %r" % s, ev)
    if pound < 0:
        raise UserInputError("Positive weight required, not %r" % pound)

    shippingCost = (0.86 * pound) + 1.50
    coffee = (10.50 * pound) + shippingCost
    if pound == 1:
        print(pound,"pound of coffee costs $", coffee)
    else:
        print(pound,"pounds of coffee costs $", coffee)
    print()

if __name__ == '__main__':
    try:
        excercise5()
    except UserInputError as ev:
        print("User input error (please retry):")
        print(" ", ev.args[0])
        if DEBUG and len(ev.args) > 1:
            print("  EXC:", ev.args[1], file=sys.stderr)
    except (EnvironmentError, KeyboardInterrupt) as ev:
        print("Execution error happend:")
        print(" ", traceback.format_exception_only(ev.__class__, ev)[0])
    except Exception:
        print("Please report this bug:")
        traceback.print_exc()

Upvotes: 0

me123
me123

Reputation: 1

Could you not use ascii. eg turn the string into a numerical value then ignore results which aren't within your numerical window eg 'if (c <= 47 and c >= 57):'. This should stop it crashing. I think:P

Upvotes: 0

Lev Levitsky
Lev Levitsky

Reputation: 65811

I'd advise against using eval. It is not good from the security point of view. Just do an explicit conversion to the desired type:

pound = float(input("Enter the weight in pounds: "))

To process invalid input:

try:
    pound = float(input("Enter the weight in pounds: "))
except ValueError:
    print('Invalid input.')
    return
# the rest of the code

Or:

try:
    pound = float(input("Enter the weight in pounds: "))
except ValueError:
    print('Invalid input.')
else:
    # the rest of the code

You can also wrap the input in an infinite loop that would terminate on successful conversion:

while True:
    try:
        pound = float(input("Enter the weight in pounds: "))
    except ValueError:
        print('Invalid input. Try again.')
    else:
        break
# do the rest with `pound`

Upvotes: 5

KevinA
KevinA

Reputation: 629

Surround the statement witha try/except

def excercise5():
    print("Programming Excercise 5")
    print("This program calculates the cost of an order.")
    pound = eval(input("Enter the weight in pounds: "))
    try:
        shippingCost = (0.86 * pound) + 1.50
        coffee = (10.50 * pound) + shippingCost
        if pound == 1:
            print(pound,"pound of coffee costs $", coffee)
        else:
            print(pound,"pounds of coffee costs $", coffee)
    except ValueError:
        print("Please Enter a valid number")
    print()

I should note: There is no way to "Error Proof" something, much like bullet proofing is impossible, a big enough bullet will penetrate anything, same with coding. All you can do is write good code. Nothing is absolute in coding.

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1123400

Use exception handling.

Python doesn't crash when someone gives you invalid input, it throws an exception instead. You can catch such exceptions and handle them instead of letting python exit the program.

In this case, since you only want a floating point number you really should not be using eval(); that will take a lot of different inputs and will throw a lot of different exceptions.

Use the float() function instead, it will only throw a ValueError if you give it incorrect input. Then catch that and display an error message:

try:
    pound = float(input("Enter the weight in pounds: "))
except ValueError:
    print('Not a valid number!')
    return

Upvotes: 0

Related Questions