api.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. """This module makes it possible to use mypy as part of a Python application.
  2. Since mypy still changes, the API was kept utterly simple and non-intrusive.
  3. It just mimics command line activation without starting a new interpreter.
  4. So the normal docs about the mypy command line apply.
  5. Changes in the command line version of mypy will be immediately usable.
  6. Just import this module and then call the 'run' function with a parameter of
  7. type List[str], containing what normally would have been the command line
  8. arguments to mypy.
  9. Function 'run' returns a Tuple[str, str, int], namely
  10. (<normal_report>, <error_report>, <exit_status>),
  11. in which <normal_report> is what mypy normally writes to sys.stdout,
  12. <error_report> is what mypy normally writes to sys.stderr and exit_status is
  13. the exit status mypy normally returns to the operating system.
  14. Any pretty formatting is left to the caller.
  15. The 'run_dmypy' function is similar, but instead mimics invocation of
  16. dmypy. Note that run_dmypy is not thread-safe and modifies sys.stdout
  17. and sys.stderr during its invocation.
  18. Note that these APIs don't support incremental generation of error
  19. messages.
  20. Trivial example of code using this module:
  21. import sys
  22. from mypy import api
  23. result = api.run(sys.argv[1:])
  24. if result[0]:
  25. print('\nType checking report:\n')
  26. print(result[0]) # stdout
  27. if result[1]:
  28. print('\nError report:\n')
  29. print(result[1]) # stderr
  30. print('\nExit status:', result[2])
  31. """
  32. from __future__ import annotations
  33. import sys
  34. from io import StringIO
  35. from typing import Callable, TextIO
  36. def _run(main_wrapper: Callable[[TextIO, TextIO], None]) -> tuple[str, str, int]:
  37. stdout = StringIO()
  38. stderr = StringIO()
  39. try:
  40. main_wrapper(stdout, stderr)
  41. exit_status = 0
  42. except SystemExit as system_exit:
  43. assert isinstance(system_exit.code, int)
  44. exit_status = system_exit.code
  45. return stdout.getvalue(), stderr.getvalue(), exit_status
  46. def run(args: list[str]) -> tuple[str, str, int]:
  47. # Lazy import to avoid needing to import all of mypy to call run_dmypy
  48. from mypy.main import main
  49. return _run(
  50. lambda stdout, stderr: main(args=args, stdout=stdout, stderr=stderr, clean_exit=True)
  51. )
  52. def run_dmypy(args: list[str]) -> tuple[str, str, int]:
  53. from mypy.dmypy.client import main
  54. # A bunch of effort has been put into threading stdout and stderr
  55. # through the main API to avoid the threadsafety problems of
  56. # modifying sys.stdout/sys.stderr, but that hasn't been done for
  57. # the dmypy client, so we just do the non-threadsafe thing.
  58. def f(stdout: TextIO, stderr: TextIO) -> None:
  59. old_stdout = sys.stdout
  60. old_stderr = sys.stderr
  61. try:
  62. sys.stdout = stdout
  63. sys.stderr = stderr
  64. main(args)
  65. finally:
  66. sys.stdout = old_stdout
  67. sys.stderr = old_stderr
  68. return _run(f)