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