make_stub_ignores.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  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. # google's weird namespace package breaks mypy's errors
  33. assert {s for s in seen if s.startswith("google.cloud.")}, "google.cloud got typed!"
  34. seen.add("google.cloud")
  35. mods: list[str] = []
  36. for mod in sorted(seen):
  37. if not mods or not mod.startswith(f"{mods[-1]}."):
  38. mods.append(mod)
  39. mods_s = "".join(f' "{mod}.*",\n' for mod in mods)
  40. stubs = (
  41. f"# - add .pyi files to fixtures/stubs-for-mypy\n"
  42. f"# - or find a 3rd party stub\n"
  43. f"[[tool.mypy.overrides]]\n"
  44. f"module = [\n{mods_s}]\n"
  45. f"ignore_missing_imports = true\n"
  46. )
  47. with open("pyproject.toml", "w") as f:
  48. f.write(
  49. before
  50. + stubs_begin
  51. + stubs
  52. + stubs_end
  53. + between
  54. + ignore_begin
  55. + ignore
  56. + ignore_end
  57. + rest
  58. )
  59. return 0
  60. if __name__ == "__main__":
  61. raise SystemExit(main())