{"action":"created","issue":{"url":"https://api.github.com/repos/pyflakes/pyflakes/issues/59","repository_url":"https://api.github.com/repos/pyflakes/pyflakes","labels_url":"https://api.github.com/repos/pyflakes/pyflakes/issues/59/labels{/name}","comments_url":"https://api.github.com/repos/pyflakes/pyflakes/issues/59/comments","events_url":"https://api.github.com/repos/pyflakes/pyflakes/issues/59/events","html_url":"https://github.com/pyflakes/pyflakes/pull/59","id":140097837,"number":59,"title":"Warn against reusing exception names after the except: block on Python 3","user":{"login":"ambv","id":55281,"avatar_url":"https://avatars.githubusercontent.com/u/55281?v=3","gravatar_id":"","url":"https://api.github.com/users/ambv","html_url":"https://github.com/ambv","followers_url":"https://api.github.com/users/ambv/followers","following_url":"https://api.github.com/users/ambv/following{/other_user}","gists_url":"https://api.github.com/users/ambv/gists{/gist_id}","starred_url":"https://api.github.com/users/ambv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ambv/subscriptions","organizations_url":"https://api.github.com/users/ambv/orgs","repos_url":"https://api.github.com/users/ambv/repos","events_url":"https://api.github.com/users/ambv/events{/privacy}","received_events_url":"https://api.github.com/users/ambv/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":16,"created_at":"2016-03-11T06:05:41Z","updated_at":"2016-03-14T12:55:59Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/pyflakes/pyflakes/pulls/59","html_url":"https://github.com/pyflakes/pyflakes/pull/59","diff_url":"https://github.com/pyflakes/pyflakes/pull/59.diff","patch_url":"https://github.com/pyflakes/pyflakes/pull/59.patch"},"body":"The following code is invalid on Python 3:\r\n```python\r\ntry:\r\n    raise ValueError('ve')\r\nexcept ValueError as exc:\r\n    pass\r\n\r\nprint(\"Last exception:\", exc)    # raises an UnboundLocalError\r\n```\r\n\r\nThis pull request makes the Checker warn against this problem. There are new tests that pass on both Python 2 and Python 3."},"comment":{"url":"https://api.github.com/repos/pyflakes/pyflakes/issues/comments/196298928","html_url":"https://github.com/pyflakes/pyflakes/pull/59#issuecomment-196298928","issue_url":"https://api.github.com/repos/pyflakes/pyflakes/issues/59","id":196298928,"user":{"login":"bitglue","id":1232472,"avatar_url":"https://avatars.githubusercontent.com/u/1232472?v=3","gravatar_id":"","url":"https://api.github.com/users/bitglue","html_url":"https://github.com/bitglue","followers_url":"https://api.github.com/users/bitglue/followers","following_url":"https://api.github.com/users/bitglue/following{/other_user}","gists_url":"https://api.github.com/users/bitglue/gists{/gist_id}","starred_url":"https://api.github.com/users/bitglue/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bitglue/subscriptions","organizations_url":"https://api.github.com/users/bitglue/orgs","repos_url":"https://api.github.com/users/bitglue/repos","events_url":"https://api.github.com/users/bitglue/events{/privacy}","received_events_url":"https://api.github.com/users/bitglue/received_events","type":"User","site_admin":false},"created_at":"2016-03-14T12:55:59Z","updated_at":"2016-03-14T12:55:59Z","body":"> To add a warning called \"variable 'x' (defined in enclosing scope on line N) redefined as exception name\" that would cover local shadowing and global shadowing.\r\n\r\nKeep in mind that pyflakes doesn't have warnings. It only has errors. It is important to me, and I suspect many other people who put pyflakes in their automated testing process, that it continues to not emit errors for any code which does not have actual errors.\r\n\r\nIf I'm understanding correctly, you are proposing this change as a solution for example5. Here it is, for discussion:\r\n\r\n```python\r\ndef example5():\r\n    e5 = 'old local value'\r\n    try:\r\n        raise ValueError('ve5')\r\n    except Exception as e5:\r\n        pass\r\n\r\n    print(e5)\r\n    # py2: prints 've5'\r\n    # py3: raises UnboundLocalError\r\n```\r\n\r\nI think in this case we can only be sure there's an error on Python 3 because we can be sure the `except:` block won't raise an exception or exit. It is somewhat common to have just `pass` in an `except:` block, so if you wanted to special-case that scenario and emit an error in that case, that would be fine. It may also be so complex it's not worth the effort, and that's fine too. There are plenty of kinds of errors that pyflakes does not catch: it just tries to catch the obvious ones.\r\n\r\nHowever, many times there will be something other than `pass` in that `except:` block. I don't think even complex heuristics will be sufficient here. Firstly, because _any_ statement _might_ raise an exception. In some cases the author may put a statement there he knows raises an exception, but pyflakes probably doesn't know that.\r\n\r\nSecondly, consider this:\r\n\r\n```python\r\ndef example5b():\r\n    e5 = 'old local value'\r\n    failed = False\r\n    try:\r\n        raise ValueError('ve5')\r\n    except Exception as e5:\r\n        failed = True\r\n\r\n    if not failed:\r\n        print(e5)\r\n```\r\n\r\nThis prints nothing but also does not have any undefined names in Python 2 or 3. Pyflakes does not currently have any concept of conditional branches, so it treats this code as if the `if not failed:` isn't even there, identically to example 5. If you try to handle conditional branches like this in any accurate way you end up being a Python interpreter.\r\n\r\nI'm reluctant to concede that because this case is complicated, pyflakes should just emit an error because it _might_ be a problem and the author _might_ have made a mistake. This just isn't in the design principles, and it's a big part of what differentiates pyflakes from pylint.\r\n\r\n> If you strongly feel that 2. has no place in pyflakes, please suggest where in the linter stack does it belong and I'll add it there.\r\n\r\nMy personal belief is this sort of thing belongs in unit tests. Pyflakes is there to catch problems that a unit test won't, like unused imports, or dead code. It just happens to catch some other errors, like undefined names, because it's easy for pyflakes to do so.\r\n\r\nI've changed my mind on issues like this before. The single most persuasive argument you can make is to run your proposed change against a lot of existing Python code and demonstrate that there are no false positives."}}