make_stub_ignores.py 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. from __future__ import annotations
  2. import os.path
  3. import re
  4. import shutil
  5. import subprocess
  6. import sys
  7. import tempfile
  8. # Examples:
  9. # Cannot find implementation or library stub for module named "codeowners"
  10. # Skipping analyzing "uwsgidecorators": module is installed, but missing library stubs or py.typed marker
  11. PAT = re.compile('(?:library stub for module named |Skipping analyzing )"([^"]+)"')
  12. def main() -> int:
  13. shutil.rmtree(".mypy_cache", ignore_errors=True)
  14. with open("pyproject.toml") as f:
  15. contents = f.read()
  16. msg_stubs = "missing 3rd party stubs"
  17. before, stubs_begin, rest = contents.partition(f"# begin: {msg_stubs}\n")
  18. _, stubs_end, rest = rest.partition(f"# end: {msg_stubs}\n")
  19. msg_ignore = "sentry modules with typing issues"
  20. between, ignore_begin, rest = rest.partition(f"# begin: {msg_ignore}\n")
  21. ignore, ignore_end, rest = rest.partition(f"# end: {msg_ignore}\n")
  22. with tempfile.TemporaryDirectory() as tmpdir:
  23. cfg = os.path.join(tmpdir, "mypy.toml")
  24. with open(cfg, "w") as f:
  25. f.write(before + stubs_begin + stubs_end + between + ignore_begin + ignore_end + rest)
  26. seen = set()
  27. out = subprocess.run(("mypy", "--config", cfg, *sys.argv[1:]), capture_output=True)
  28. for line in out.stdout.decode().splitlines():
  29. match = PAT.search(line)
  30. if match is not None and match[1] not in seen:
  31. seen.add(match[1])
  32. mods: list[str] = []
  33. for mod in sorted(seen):
  34. if not mods or not mod.startswith(f"{mods[-1]}."):
  35. mods.append(mod)
  36. mods_s = "".join(f' "{mod}.*",\n' for mod in mods)
  37. stubs = (
  38. f"# - add .pyi files to fixtures/stubs-for-mypy\n"
  39. f"# - or find a 3rd party stub\n"
  40. f"[[tool.mypy.overrides]]\n"
  41. f"module = [\n{mods_s}]\n"
  42. f"ignore_missing_imports = true\n"
  43. )
  44. with open("pyproject.toml", "w") as f:
  45. f.write(
  46. before
  47. + stubs_begin
  48. + stubs
  49. + stubs_end
  50. + between
  51. + ignore_begin
  52. + ignore
  53. + ignore_end
  54. + rest
  55. )
  56. return 0
  57. if __name__ == "__main__":
  58. raise SystemExit(main())