Last active
September 5, 2022 17:14
-
-
Save henryiii/df1a8312f5906816ef6b3a467c3b9f14 to your computer and use it in GitHub Desktop.
flake8 checker for error messages showing up unrendered in the traceback
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
Flake8 checker for raw literals inside raises. | |
Requires Python 3.10. This can be run without Flake8 as well. Try: | |
git ls-files '*.py' | xargs python3 plugins/flake8_errmsg.py | |
Local use: | |
[flake8:local-plugins] | |
extension = | |
EM1 = flake8_errmsg:ErrMsgASTPlugin | |
paths = | |
./plugins/ | |
""" | |
from __future__ import annotations | |
import ast | |
import sys | |
import traceback | |
from pathlib import Path | |
from typing import Iterator, NamedTuple | |
class Flake8ASTErrorInfo(NamedTuple): | |
line_number: int | |
offset: int | |
msg: str | |
cls: type # unused | |
class Visitor(ast.NodeVisitor): | |
def __init__(self): | |
self.errors: list[Flake8ASTErrorInfo] = [] | |
def visit_Raise(self, node): | |
match node.exc: | |
case ast.Call(args=[ast.Constant(value=str()), *_]): | |
msg = "EM101 exception must not use a string literal, assign to variable first" | |
self.errors.append( | |
Flake8ASTErrorInfo(node.lineno, node.col_offset, msg, type(self)) | |
) | |
case ast.Call(args=[ast.JoinedStr(), *_]): | |
msg = "EM102 exception must not use a f-string literal, assign to variable first" | |
self.errors.append( | |
Flake8ASTErrorInfo(node.lineno, node.col_offset, msg, type(self)) | |
) | |
case _: | |
pass | |
class ErrMsgASTPlugin: | |
name = "flake8_errmsg" | |
version = "0.1.0" | |
def __init__(self, tree: ast.AST) -> None: | |
self._tree = tree | |
def run(self) -> Iterator[Flake8ASTErrorInfo]: | |
visitor = Visitor() | |
visitor.visit(self._tree) | |
yield from visitor.errors | |
def main(path: str) -> None: | |
code = Path(path).read_text(encoding="utf-8") | |
try: | |
node = ast.parse(code) | |
except SyntaxError as e: | |
e.filename = path | |
print("Traceback:") | |
traceback.print_exception(e, limit=0) | |
raise SystemExit(1) from None | |
plugin = ErrMsgASTPlugin(node) | |
for err in plugin.run(): | |
print(f"{path}:{err.line_number}:{err.offset} {err.msg}") | |
if __name__ == "__main__": | |
for item in sys.argv[1:]: | |
main(item) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment