Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect unreachable except branches #11514

Open
jcrist opened this issue Nov 10, 2021 · 5 comments
Open

Detect unreachable except branches #11514

jcrist opened this issue Nov 10, 2021 · 5 comments
Labels
feature topic-reachability Detecting unreachable code

Comments

@jcrist
Copy link

jcrist commented Nov 10, 2021

Since except blocks are checked in order, and the first matching block is executed, it's possible to accidentally write a try-except that will never execute some branches. For example:

def bad_reader(path: str) -> bytes:
    if path == "bad":
        raise PermissionError

    with open(path, "rb") as f:
        return f.read()


def load_data(path: str) -> bytes:
    try:
        return bad_reader(path)
    except OSError:
        print("OSError caught")
    except PermissionError:
        # This branch will never be hit, since `OSError` will always catch
        # permission error
        print("PermissionError caught")

    return b""


load_data("bad")

Since PermissionError is a subclass of OSError, the except PermissionError case will never execute. I'd expect/hope that these kind of mistakes would be detectable with type information. I think adding checks for this would make sense under the --warn-unreachable config flag, so a new CLI option may not be needed.

@jcrist jcrist added the feature label Nov 10, 2021
@sobolevn
Copy link
Member

Great idea!

@jcrist
Copy link
Author

jcrist commented Nov 10, 2021

If possible, I'd be interested in hacking on this as an interesting side project. I took a scan through the code and couldn't figure out where such an analysis pass might fit in to mypy. If this seems like a reasonable project for someone who's new-to-mypy-but-not-new-to-python, I'd welcome some advice on where/how this feature should be added. "No, this is a tricky feature that would be best handled by the core mypy team" is also a fine response. Happy to help if possible.

@sobolevn
Copy link
Member

sobolevn commented Nov 10, 2021

@jcrist yes, this looks rather simple. I can guide you, if you wish 🙂

So, here's how I see this:

  1. We need to add this hook to visit_try_stmt in checker.py:
    def visit_try_stmt(self, s: TryStmt) -> None:
  2. There under if self.options.warn_unreachable we check TryStmt.types prop:
    types: List[Optional[Expression]] # Except type expressions
    You can convert Expression to Type with self.expr_checker.accept(expression). It is a good idea to disable errors while doing that. Because we properly check expression types in a different place. Or maybe you can add this check to a place where we do check except types 🤔
  3. Then, we detect which cases are already handled via is_subtype function:
    def is_subtype(left: Type, right: Type,
  4. Lastly, we report errors via self.fail
  5. Tests can probably live in https://github.com/python/mypy/blob/fdcda966023840b854567df0643091653b179210/test-data/unit/check-unreachable-code.test Make sure you use the correct [fixture ...] with proper exception hierarchy!

I hope that this helps. Feel free to ping me if you have any questions 🙂
Btw, I am not a maintainer, so this can still be rejected during the review from real mypy experts.
But, you never know untill you try! 👍

@kreathon
Copy link
Contributor

@jcrist are you still working on this? If not I would give it a try :)

@jcrist
Copy link
Author

jcrist commented Jan 27, 2022

No, I never gave it a go - please feel free to pick this up.

@AlexWaygood AlexWaygood added the topic-reachability Detecting unreachable code label Mar 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature topic-reachability Detecting unreachable code
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants