Search code examples
pythonpython-3.xexceptionpython-closures

Python variable scope - outer exception variable undefined if used in inner exception


Can someone please help me understand what is happening here. I have some understanding on how scope of variables work in python.
When this code runs, I get an error:

rec = True

    try:
        print("outer try")
        raise Exception("outer exception")
    except Exception as msg:
        try:
            rec = True
            print("inner try")
            raise Exception("inner exception")
        except Exception as msg:
            rec = False
            print(str(msg))
        
        if rec == False:
            print(str(msg))

Output with Error:
outer try
inner try
inner exception

---------------------------------------------------------------------------
Exception Traceback (most recent call last)
<ipython-input-2-6ce9b26112ed> in <module>
      5     print("outer try")
----> 6     raise Exception("outer exception")
      7 except Exception as msg:

Exception: outer exception

During handling of the above exception, another exception occurred:

NameError Traceback (most recent call last)
<ipython-input-2-6ce9b26112ed> in <module>
     15 
     16     if rec == False:
---> 17         print(str(msg))

NameError: name 'msg' is not defined

My understanding is that when the inner exception is called and finished, the "msg" variable becomes unset or removed from memory.

Now when I run this block of code, it runs successfully:
rec = True

try:
    print("outer try")
    raise Exception("outer exception")
except Exception as outer_msg:
    try:
        rec = True
        print("inner try")
        raise Exception("inner exception")
    except Exception as msg:
        rec = False
        print(str(msg))
    
    if rec == False:
        print(str(outer_msg))

Output:

outer try
inner try
inner exception
outer exception

Is this error related to "Scope of Variables" or "Closure"? If anyone has a link to a detailed explanation of this in python can you please help.


Solution

  • The block beginning

    except Exception as msg:
    

    creates the msg variable at the start of the block, and deletes it at the end of the block. The msg variable that existed already is in the same scope and has the same name, so it is overwritten and then deleted.

    You need to use separate names for your two exceptions if you want to track both, because they are in the same scope.

    See https://docs.python.org/3/reference/compound_stmts.html#the-try-statement which says:

    When an exception has been assigned using as target, it is cleared at the end of the except clause. This is as if

    except E as N:
        foo
    

    was translated to

    except E as N:
        try:
            foo
        finally:
            del N