flake8_plugin.py 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. from __future__ import annotations
  2. import ast
  3. from typing import Any, Generator
  4. S001_fmt = (
  5. "S001 Avoid using the {} mock call as it is "
  6. "confusing and prone to causing invalid test "
  7. "behavior."
  8. )
  9. S001_methods = frozenset(("not_called", "called_once", "called_once_with"))
  10. S002_msg = "S002 print functions or statements are not allowed."
  11. S003_msg = "S003 Use ``from sentry.utils import json`` instead."
  12. S003_modules = {"json", "simplejson"}
  13. class SentryVisitor(ast.NodeVisitor):
  14. def __init__(self) -> None:
  15. self.errors: list[tuple[int, int, str]] = []
  16. def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
  17. if node.module and not node.level and node.module.split(".")[0] in S003_modules:
  18. self.errors.append((node.lineno, node.col_offset, S003_msg))
  19. self.generic_visit(node)
  20. def visit_Import(self, node: ast.Import) -> None:
  21. for alias in node.names:
  22. if alias.name.split(".")[0] in S003_modules:
  23. self.errors.append((node.lineno, node.col_offset, S003_msg))
  24. self.generic_visit(node)
  25. def visit_Attribute(self, node: ast.Attribute) -> None:
  26. if node.attr in S001_methods:
  27. self.errors.append((node.lineno, node.col_offset, S001_fmt.format(node.attr)))
  28. self.generic_visit(node)
  29. def visit_Name(self, node: ast.Name) -> None:
  30. if node.id == "print":
  31. self.errors.append((node.lineno, node.col_offset, S002_msg))
  32. self.generic_visit(node)
  33. class SentryCheck:
  34. name = "sentry-flake8"
  35. version = "0"
  36. def __init__(self, tree: ast.AST) -> None:
  37. self.tree = tree
  38. def run(self) -> Generator[tuple[int, int, str, type[Any]], None, None]:
  39. visitor = SentryVisitor()
  40. visitor.visit(self.tree)
  41. for e in visitor.errors:
  42. yield (*e, type(self))