0

Below is my code to calculate calories based on input, after the print statements I want to exit the code and loop infinitely until the user inputs the right values.

import sys
def main():
    while True:
        try: 
            print("Please enter Age,Weight,Heart Rate and Workout Time ")
            Age,Weight,HR,Time = map(eval,input().split(' '))
            men = ( (Age * 0.2017) - (Weight * 0.09036) + (HR * 0.6309) - 55.0969 ) * Time / 4.184
            women =  ( (Age * 0.074) - (Weight * 0.05741) + (HR * 0.4472) - 20.4022 ) * Time / 4.184
            print("Men:",men," Calories \nWomen:",women,"Calories \n")
            exit()
        except:
            print("Please enter numbers only \n \n")

if __name__ == '__main__':
    main()

The code goes to the except even if the input values are right and does not exit the code, what am I missing?

Swayam Shah
  • 119
  • 1
  • 8

1 Answers1

5

exit() (some forms; it's not actually supposed to be used in scripts, so it can be overridden to do weird things; you want sys.exit()) is implemented by raising a SystemExit exception (exiting without bubbling out would mean cleanup blocks in finally and with statements don't execute, which would be bad, so they use the exception mechanism to do the cleanup). SystemExit is special in two ways:

  1. It doesn't dump a traceback and exit with a fixed status 1 if unhandled (it just silently exits the program with the status code it was provided)
  2. It's not a child of Exception, so except Exception:, which is very broad, still won't catch it

But you used a bare except, which catches everything, including SystemExit. Don't do that.

In this specific case, the correct solution is really to just replace exit() with break, which will pop you out of the while loop and allow the program to run to completion with no need for an exception at all. And use a narrower except clause while you're at it.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • good answer. do you have a citation for "not actually supposed to be used in scripts"? I assume you're referring to a PEP of some sort? (I'm just curious, no criticism here) – en_Knight Sep 03 '21 at 02:58
  • 2
    @en_Knight: It's one of [the constants added by the `site` module](https://docs.python.org/3/library/constants.html#constants-added-by-the-site-module), which is documented with: "The `site` module (which is imported automatically during startup, except if the `-S` command-line option is given) adds several constants to the built-in namespace. They are useful for the interactive interpreter shell and should not be used in programs." It's not guaranteed to exist, and stuff like IPython overrides it in ways that might subtly break code. `sys.exit()` has neither problem. – ShadowRanger Sep 03 '21 at 03:01
  • thanks, I didn't know that! Amusingly, the first hit when i google'd it had a comment "sys.exit() is not a reliable way to close down. If it is called inside a thread, it will terminate only that thread unless it is in the main thread" (from https://stackoverflow.com/questions/19747371/python-exit-commands-why-so-many-and-when-should-each-be-used). But that's (A) way above what the OP is trying to do and (B) a little bit picky, since you should probably have a more sophisticated shut down procedure, IMO, in something multithreaded and complicated – en_Knight Sep 03 '21 at 03:03
  • 1
    @en_Knight: Yeah, but nothing save `os._exit` will end a multithreaded program with other running non-daemon threads. And you almost never want `os._exit` because of that behavior; it doesn't throw an exception, it just ends the process *now*, and doesn't invoke all the `finally` and `with` cleanup that's supposed to be "guaranteed" to run. `os._exit` burns the world down. If `sys.exit` isn't doing what you want, it's because you're not using daemon threads appropriately, or your main thread is failing to tell your non-daemon threads to stop cleanly when it's exiting, or both. – ShadowRanger Sep 03 '21 at 03:07
  • yup, I agree with all that – en_Knight Sep 03 '21 at 03:20