Search code examples
pythonpython-3.xtry-catchexcept

Python 3 try except issue


I'm having a little issue with my code, mainly (I think) with the try/except part. My code will generate a wordlist of the users choice. Here is the code:

def gen_wordlist():
    filename = input("Please enter the name of the wordlist:  ")

    try:
        my_file = open(filename, 'r')

    except FileNotFoundError:
        retry = input("No file named "+filename+". Would you like to try again (y/n)")
        if retry == 'y' or retry == 'Y':
            gen_wordlist()
        else:
            print("Goodbye :-)")
            sys.exit()

    words = my_file.read()
    my_file.close()
    return(words.split())


words = gen_wordlist()

If I enter a valid filename on the first attempt, it works as it should. However, if I enter an invalid filename and choose to try again, I get the following error, even if my second attempt is definitely a valid filename:

Traceback (most recent call last):
  File "TEST.py", line 20, in <module>
    words = gen_wordlist()
  File "TEST.py", line 15, in gen_wordlist
    words = my_file.read()
UnboundLocalError: local variable 'my_file' referenced before assignment

I can't work out why though. Surely, when I select 'y', the code just executes from the beginning of the gen_wordlist() function, and should work the same as if I had entered a valid filename on the first attempt, right?


Solution

  • If the open() call raises a FileNotFoundError exception, my_file was never set and all other references trying to use that name will fail.

    That includes the code after gen_wordlist() was called in the exception handler. Sure, the new call may succeed, but that call then returns back to this frame where my_file was not set.

    You want to return here too, instead:

    if retry == 'y' or retry == 'Y':
        return gen_wordlist()
    

    Otherwise you'll ignore the result of the successful recursive call here too.

    It's not a great idea to use recursion to handle errors in user input. Use a loop instead:

    my_file = None
    
    while my_file is None:
        filename = input("Please enter the name of the wordlist:  ")
    
        try:
            my_file = open(filename, 'r')
        except FileNotFoundError:
            retry = input("No file named " + filename + ". Would you like to try again (y/n)")
            if retry.lower() == 'y':
                # back to the top of the loop
                continue
    
            print("Goodbye :-)")
            sys.exit()
    

    This while loop then runs until my_file has successfully been set to a file object.