testmodulefinder.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. from __future__ import annotations
  2. import os
  3. from mypy.modulefinder import FindModuleCache, ModuleNotFoundReason, SearchPaths
  4. from mypy.options import Options
  5. from mypy.test.config import package_path
  6. from mypy.test.helpers import Suite, assert_equal
  7. data_path = os.path.relpath(os.path.join(package_path, "modulefinder"))
  8. class ModuleFinderSuite(Suite):
  9. def setUp(self) -> None:
  10. self.search_paths = SearchPaths(
  11. python_path=(),
  12. mypy_path=(
  13. os.path.join(data_path, "nsx-pkg1"),
  14. os.path.join(data_path, "nsx-pkg2"),
  15. os.path.join(data_path, "nsx-pkg3"),
  16. os.path.join(data_path, "nsy-pkg1"),
  17. os.path.join(data_path, "nsy-pkg2"),
  18. os.path.join(data_path, "pkg1"),
  19. os.path.join(data_path, "pkg2"),
  20. ),
  21. package_path=(),
  22. typeshed_path=(),
  23. )
  24. options = Options()
  25. options.namespace_packages = True
  26. self.fmc_ns = FindModuleCache(self.search_paths, fscache=None, options=options)
  27. options = Options()
  28. options.namespace_packages = False
  29. self.fmc_nons = FindModuleCache(self.search_paths, fscache=None, options=options)
  30. def test__no_namespace_packages__nsx(self) -> None:
  31. """
  32. If namespace_packages is False, we shouldn't find nsx
  33. """
  34. found_module = self.fmc_nons.find_module("nsx")
  35. assert_equal(ModuleNotFoundReason.NOT_FOUND, found_module)
  36. def test__no_namespace_packages__nsx_a(self) -> None:
  37. """
  38. If namespace_packages is False, we shouldn't find nsx.a.
  39. """
  40. found_module = self.fmc_nons.find_module("nsx.a")
  41. assert_equal(ModuleNotFoundReason.NOT_FOUND, found_module)
  42. def test__no_namespace_packages__find_a_in_pkg1(self) -> None:
  43. """
  44. Find find pkg1/a.py for "a" with namespace_packages False.
  45. """
  46. found_module = self.fmc_nons.find_module("a")
  47. expected = os.path.join(data_path, "pkg1", "a.py")
  48. assert_equal(expected, found_module)
  49. def test__no_namespace_packages__find_b_in_pkg2(self) -> None:
  50. found_module = self.fmc_ns.find_module("b")
  51. expected = os.path.join(data_path, "pkg2", "b", "__init__.py")
  52. assert_equal(expected, found_module)
  53. def test__find_nsx_as_namespace_pkg_in_pkg1(self) -> None:
  54. """
  55. There's no __init__.py in any of the nsx dirs, return
  56. the path to the first one found in mypypath.
  57. """
  58. found_module = self.fmc_ns.find_module("nsx")
  59. expected = os.path.join(data_path, "nsx-pkg1", "nsx")
  60. assert_equal(expected, found_module)
  61. def test__find_nsx_a_init_in_pkg1(self) -> None:
  62. """
  63. Find nsx-pkg1/nsx/a/__init__.py for "nsx.a" in namespace mode.
  64. """
  65. found_module = self.fmc_ns.find_module("nsx.a")
  66. expected = os.path.join(data_path, "nsx-pkg1", "nsx", "a", "__init__.py")
  67. assert_equal(expected, found_module)
  68. def test__find_nsx_b_init_in_pkg2(self) -> None:
  69. """
  70. Find nsx-pkg2/nsx/b/__init__.py for "nsx.b" in namespace mode.
  71. """
  72. found_module = self.fmc_ns.find_module("nsx.b")
  73. expected = os.path.join(data_path, "nsx-pkg2", "nsx", "b", "__init__.py")
  74. assert_equal(expected, found_module)
  75. def test__find_nsx_c_c_in_pkg3(self) -> None:
  76. """
  77. Find nsx-pkg3/nsx/c/c.py for "nsx.c.c" in namespace mode.
  78. """
  79. found_module = self.fmc_ns.find_module("nsx.c.c")
  80. expected = os.path.join(data_path, "nsx-pkg3", "nsx", "c", "c.py")
  81. assert_equal(expected, found_module)
  82. def test__find_nsy_a__init_pyi(self) -> None:
  83. """
  84. Prefer nsy-pkg1/a/__init__.pyi file over __init__.py.
  85. """
  86. found_module = self.fmc_ns.find_module("nsy.a")
  87. expected = os.path.join(data_path, "nsy-pkg1", "nsy", "a", "__init__.pyi")
  88. assert_equal(expected, found_module)
  89. def test__find_nsy_b__init_py(self) -> None:
  90. """
  91. There is a nsy-pkg2/nsy/b.pyi, but also a nsy-pkg2/nsy/b/__init__.py.
  92. We expect to find the latter when looking up "nsy.b" as
  93. a package is preferred over a module.
  94. """
  95. found_module = self.fmc_ns.find_module("nsy.b")
  96. expected = os.path.join(data_path, "nsy-pkg2", "nsy", "b", "__init__.py")
  97. assert_equal(expected, found_module)
  98. def test__find_nsy_c_pyi(self) -> None:
  99. """
  100. There is a nsy-pkg2/nsy/c.pyi and nsy-pkg2/nsy/c.py
  101. We expect to find the former when looking up "nsy.b" as
  102. .pyi is preferred over .py.
  103. """
  104. found_module = self.fmc_ns.find_module("nsy.c")
  105. expected = os.path.join(data_path, "nsy-pkg2", "nsy", "c.pyi")
  106. assert_equal(expected, found_module)
  107. def test__find_a_in_pkg1(self) -> None:
  108. found_module = self.fmc_ns.find_module("a")
  109. expected = os.path.join(data_path, "pkg1", "a.py")
  110. assert_equal(expected, found_module)
  111. def test__find_b_init_in_pkg2(self) -> None:
  112. found_module = self.fmc_ns.find_module("b")
  113. expected = os.path.join(data_path, "pkg2", "b", "__init__.py")
  114. assert_equal(expected, found_module)
  115. def test__find_d_nowhere(self) -> None:
  116. found_module = self.fmc_ns.find_module("d")
  117. assert_equal(ModuleNotFoundReason.NOT_FOUND, found_module)
  118. class ModuleFinderSitePackagesSuite(Suite):
  119. def setUp(self) -> None:
  120. self.package_dir = os.path.relpath(
  121. os.path.join(package_path, "modulefinder-site-packages")
  122. )
  123. package_paths = (
  124. os.path.join(self.package_dir, "baz"),
  125. os.path.join(self.package_dir, "..", "not-a-directory"),
  126. os.path.join(self.package_dir, "..", "modulefinder-src"),
  127. self.package_dir,
  128. )
  129. self.search_paths = SearchPaths(
  130. python_path=(),
  131. mypy_path=(os.path.join(data_path, "pkg1"),),
  132. package_path=tuple(package_paths),
  133. typeshed_path=(),
  134. )
  135. options = Options()
  136. options.namespace_packages = True
  137. self.fmc_ns = FindModuleCache(self.search_paths, fscache=None, options=options)
  138. options = Options()
  139. options.namespace_packages = False
  140. self.fmc_nons = FindModuleCache(self.search_paths, fscache=None, options=options)
  141. def path(self, *parts: str) -> str:
  142. return os.path.join(self.package_dir, *parts)
  143. def test__packages_with_ns(self) -> None:
  144. cases = [
  145. # Namespace package with py.typed
  146. ("ns_pkg_typed", self.path("ns_pkg_typed")),
  147. ("ns_pkg_typed.a", self.path("ns_pkg_typed", "a.py")),
  148. ("ns_pkg_typed.b", self.path("ns_pkg_typed", "b")),
  149. ("ns_pkg_typed.b.c", self.path("ns_pkg_typed", "b", "c.py")),
  150. ("ns_pkg_typed.a.a_var", ModuleNotFoundReason.NOT_FOUND),
  151. # Namespace package without py.typed
  152. ("ns_pkg_untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  153. ("ns_pkg_untyped.a", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  154. ("ns_pkg_untyped.b", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  155. ("ns_pkg_untyped.b.c", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  156. ("ns_pkg_untyped.a.a_var", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  157. # Namespace package without stub package
  158. ("ns_pkg_w_stubs", self.path("ns_pkg_w_stubs")),
  159. ("ns_pkg_w_stubs.typed", self.path("ns_pkg_w_stubs-stubs", "typed", "__init__.pyi")),
  160. (
  161. "ns_pkg_w_stubs.typed_inline",
  162. self.path("ns_pkg_w_stubs", "typed_inline", "__init__.py"),
  163. ),
  164. ("ns_pkg_w_stubs.untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  165. # Regular package with py.typed
  166. ("pkg_typed", self.path("pkg_typed", "__init__.py")),
  167. ("pkg_typed.a", self.path("pkg_typed", "a.py")),
  168. ("pkg_typed.b", self.path("pkg_typed", "b", "__init__.py")),
  169. ("pkg_typed.b.c", self.path("pkg_typed", "b", "c.py")),
  170. ("pkg_typed.a.a_var", ModuleNotFoundReason.NOT_FOUND),
  171. # Regular package without py.typed
  172. ("pkg_untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  173. ("pkg_untyped.a", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  174. ("pkg_untyped.b", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  175. ("pkg_untyped.b.c", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  176. ("pkg_untyped.a.a_var", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  177. # Top-level Python file in site-packages
  178. ("standalone", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  179. ("standalone.standalone_var", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  180. # Packages found by following .pth files
  181. ("baz_pkg", self.path("baz", "baz_pkg", "__init__.py")),
  182. ("ns_baz_pkg.a", self.path("baz", "ns_baz_pkg", "a.py")),
  183. ("neighbor_pkg", self.path("..", "modulefinder-src", "neighbor_pkg", "__init__.py")),
  184. ("ns_neighbor_pkg.a", self.path("..", "modulefinder-src", "ns_neighbor_pkg", "a.py")),
  185. # Something that doesn't exist
  186. ("does_not_exist", ModuleNotFoundReason.NOT_FOUND),
  187. # A regular package with an installed set of stubs
  188. ("foo.bar", self.path("foo-stubs", "bar.pyi")),
  189. # A regular, non-site-packages module
  190. ("a", os.path.join(data_path, "pkg1", "a.py")),
  191. ]
  192. for module, expected in cases:
  193. template = "Find(" + module + ") got {}; expected {}"
  194. actual = self.fmc_ns.find_module(module)
  195. assert_equal(actual, expected, template)
  196. def test__packages_without_ns(self) -> None:
  197. cases = [
  198. # Namespace package with py.typed
  199. ("ns_pkg_typed", ModuleNotFoundReason.NOT_FOUND),
  200. ("ns_pkg_typed.a", ModuleNotFoundReason.NOT_FOUND),
  201. ("ns_pkg_typed.b", ModuleNotFoundReason.NOT_FOUND),
  202. ("ns_pkg_typed.b.c", ModuleNotFoundReason.NOT_FOUND),
  203. ("ns_pkg_typed.a.a_var", ModuleNotFoundReason.NOT_FOUND),
  204. # Namespace package without py.typed
  205. ("ns_pkg_untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  206. ("ns_pkg_untyped.a", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  207. ("ns_pkg_untyped.b", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  208. ("ns_pkg_untyped.b.c", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  209. ("ns_pkg_untyped.a.a_var", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  210. # Namespace package without stub package
  211. ("ns_pkg_w_stubs", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  212. ("ns_pkg_w_stubs.typed", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  213. (
  214. "ns_pkg_w_stubs.typed_inline",
  215. self.path("ns_pkg_w_stubs", "typed_inline", "__init__.py"),
  216. ),
  217. ("ns_pkg_w_stubs.untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  218. # Regular package with py.typed
  219. ("pkg_typed", self.path("pkg_typed", "__init__.py")),
  220. ("pkg_typed.a", self.path("pkg_typed", "a.py")),
  221. ("pkg_typed.b", self.path("pkg_typed", "b", "__init__.py")),
  222. ("pkg_typed.b.c", self.path("pkg_typed", "b", "c.py")),
  223. ("pkg_typed.a.a_var", ModuleNotFoundReason.NOT_FOUND),
  224. # Regular package without py.typed
  225. ("pkg_untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  226. ("pkg_untyped.a", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  227. ("pkg_untyped.b", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  228. ("pkg_untyped.b.c", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  229. ("pkg_untyped.a.a_var", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  230. # Top-level Python file in site-packages
  231. ("standalone", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  232. ("standalone.standalone_var", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS),
  233. # Packages found by following .pth files
  234. ("baz_pkg", self.path("baz", "baz_pkg", "__init__.py")),
  235. ("ns_baz_pkg.a", ModuleNotFoundReason.NOT_FOUND),
  236. ("neighbor_pkg", self.path("..", "modulefinder-src", "neighbor_pkg", "__init__.py")),
  237. ("ns_neighbor_pkg.a", ModuleNotFoundReason.NOT_FOUND),
  238. # Something that doesn't exist
  239. ("does_not_exist", ModuleNotFoundReason.NOT_FOUND),
  240. # A regular package with an installed set of stubs
  241. ("foo.bar", self.path("foo-stubs", "bar.pyi")),
  242. # A regular, non-site-packages module
  243. ("a", os.path.join(data_path, "pkg1", "a.py")),
  244. ]
  245. for module, expected in cases:
  246. template = "Find(" + module + ") got {}; expected {}"
  247. actual = self.fmc_nons.find_module(module)
  248. assert_equal(actual, expected, template)