Ben Butterworth
Ben Butterworth

Reputation: 28470

Cannot abort (ctrl+c) in Python

I wrote this code to continually ask for more input, if the input is not an integer. However, when I try to abort it in the python interactive session, it keeps asking for input.

Why does it do this, even though I'm pressing Ctrl+C, which means abort.

def get_size(text):
    while True:
        try:
            i = int(input(text))
            if i >= 0 and i<24:
                break
        except:
            pass
    return i

a = get_size("Input: ")

Upvotes: 2

Views: 2418

Answers (3)

MSeifert
MSeifert

Reputation: 152557

When you press Ctrl + C the Python interpreter catches the interrupt and throws a KeyboardInterrupt exception. Because your bare except is equivalent to except BaseException and KeyboardInterrupt is a subclass of BaseException your except will catch the KeyboardInterrupt. You have no exception handling (like re-raising) in the except block so the program will continue.

At the very least change the except to an except Exception because the exceptions that are subclasses of BaseException but don't subclass Exception (KeyboardInterrupt, SystemExit, and GeneratorExit) are not really meant to be swallowed. In some rare cases it makes sense to catch them and do some clean-up before you re-raise them. But there's almost never a use-case for catching them and not raising them again.

The Python documentation actually contains a hierarchy visualization of built-in exceptions that might be handy:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      ...

You may notice that except Exception could also catch some exceptions that you probably cannot recover from. For example MemoryError, SyntaxError, or SystemError normally indicate that something went (really) wrong and these shouldn't be swallowed because these may not be "recoverable" (at least in most cases).

That means you should observe which Exceptions could be thrown by your code and under which circumstances and then decide which ones you can recover from.

In your case:

  • the input() is not supposed to fail, so you might as well put it outside the try.
  • Similarly, you wouldn't expect the comparisons to fail, so these can also be put outside the try block. Because you only want that code to run if the try succeeded you need to guard it, for example in the else block of the try.
  • The int() can fail because of a TypeError in case it's an unsupported type, however input always returns a string. String is an acceptable type for int() so one wouldn't expect that to happen.
  • So the only "expected" exception you'll likely encounter here is a ValueError. It is thrown if the string couldn't be interpreted as integer.

So I would use:

def get_size(text):
    while True:
        input_text = input(text)
        try:
            i = int(input_text)
        except ValueError:
            pass
        else:
            if 0 <= i < 24:
                return i

Or in case you don't want the else block, you could also continue in the except block:

def get_size(text):
    while True:
        input_text = input(text)
        try:
            i = int(input_text)
        except ValueError:
            continue
        if 0 <= i < 24:
            return i

Which one you use is mostly a matter of preference. Both should work identical.

To summarize it:

  • Identify the minimal amount of code that is allowed (from your point of view) to fail, don't put anything else inside the try block.
  • Make sure you only catch "recoverable" exceptions. In most cases the exception type is enough. But sometimes it could make sense to also check the exception message to make sure it's really the exception you want to catch.
  • Never use except: or except BaseException:. The only exception is if you really want to catch SystemExit, KeyboardInterrupt or GeneratorExit and know how to deal with them appropriately. You may get away with except Exception, but for any code that you want to use regularly (or in production code) you should invest the time to find the more appropriate exceptions.

Upvotes: 4

Alif Jahan
Alif Jahan

Reputation: 795

You made the try: except: block inside while loop. So whenever the script throwing an error the except is just passing and going back to while loop.

Upvotes: 0

ForceBru
ForceBru

Reputation: 44830

Ctrl+C means KeyboardInterrupt, but your except block catches and ignores it (as well as all other exceptions). Even Ctrl+D doesn't stop the execution, so you should just kill the process, to my mind.

Upvotes: 1

Related Questions