As of python 3 an except: block only defines the name during the block itself
You can think of
try:
... # 1
except Exception as e:
... # 2
as the following:
try:
... # 1
except Exception as e:
try:
... # 2
finally:
del e
For inner functions (including lambdas as is your example) in pyflakes (the tool which produces the F error codes), the evaluation of names is deferred until the end of the function. This is to handle cases like this:
def f():
def g():
print(x) # it looks like `x` doesn't exist yet from a syntax perspective
x = 1
g()
and thirdly, pyflakes does not do any branch or context analysis for statements, you can ~kinda think of them all squished to the left without branches or blocks
so in your example, pyflakes roughly sees:
1/0
e = ... # sys.exc_info()[1]
print('Caught')
do_something(lambda: f(e))
del e
and since at the end of the function e is deld, pyflakes treats it as ~potentially undefined. while it's obvious to a human that it is called in a place where it is defined, you'd need to solve the halting problem in practice to know that it is or isn't undefined (you'd ~essentially need to run the code or analyze every possible branch)
For the "issue" (though I believe it to be unsolvable) there's this in the tracker: https://github.com/PyCQA/pyflakes/issues/265
disclaimer: I am the current maintainer of flake8 and one of the maintainers of pyflakes