testdeps.py 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. """Test cases for generating node-level dependencies (for fine-grained incremental checking)"""
  2. from __future__ import annotations
  3. import os
  4. from collections import defaultdict
  5. from mypy import build
  6. from mypy.errors import CompileError
  7. from mypy.modulefinder import BuildSource
  8. from mypy.nodes import Expression, MypyFile
  9. from mypy.options import Options
  10. from mypy.server.deps import get_dependencies
  11. from mypy.test.config import test_temp_dir
  12. from mypy.test.data import DataDrivenTestCase, DataSuite
  13. from mypy.test.helpers import assert_string_arrays_equal, find_test_files, parse_options
  14. from mypy.types import Type
  15. from mypy.typestate import type_state
  16. # Only dependencies in these modules are dumped
  17. dumped_modules = ["__main__", "pkg", "pkg.mod"]
  18. class GetDependenciesSuite(DataSuite):
  19. files = find_test_files(pattern="deps*.test")
  20. def run_case(self, testcase: DataDrivenTestCase) -> None:
  21. src = "\n".join(testcase.input)
  22. dump_all = "# __dump_all__" in src
  23. options = parse_options(src, testcase, incremental_step=1)
  24. options.use_builtins_fixtures = True
  25. options.show_traceback = True
  26. options.cache_dir = os.devnull
  27. options.export_types = True
  28. options.preserve_asts = True
  29. options.allow_empty_bodies = True
  30. messages, files, type_map = self.build(src, options)
  31. a = messages
  32. if files is None or type_map is None:
  33. if not a:
  34. a = ["Unknown compile error (likely syntax error in test case or fixture)"]
  35. else:
  36. deps: defaultdict[str, set[str]] = defaultdict(set)
  37. for module, file in files.items():
  38. if (module in dumped_modules or dump_all) and (module in testcase.test_modules):
  39. new_deps = get_dependencies(file, type_map, options.python_version, options)
  40. for source in new_deps:
  41. deps[source].update(new_deps[source])
  42. type_state.add_all_protocol_deps(deps)
  43. for source, targets in sorted(deps.items()):
  44. if source.startswith(("<enum", "<typing", "<mypy", "<_typeshed.")):
  45. # Remove noise.
  46. continue
  47. line = f"{source} -> {', '.join(sorted(targets))}"
  48. # Clean up output a bit
  49. line = line.replace("__main__", "m")
  50. a.append(line)
  51. assert_string_arrays_equal(
  52. testcase.output, a, f"Invalid output ({testcase.file}, line {testcase.line})"
  53. )
  54. def build(
  55. self, source: str, options: Options
  56. ) -> tuple[list[str], dict[str, MypyFile] | None, dict[Expression, Type] | None]:
  57. try:
  58. result = build.build(
  59. sources=[BuildSource("main", None, source)],
  60. options=options,
  61. alt_lib_path=test_temp_dir,
  62. )
  63. except CompileError as e:
  64. # TODO: Should perhaps not return None here.
  65. return e.messages, None, None
  66. return result.errors, result.files, result.types