blob: ca1058b8d4087cf1e1de410a47808dcc7901dd1f [file] [log] [blame]
Christian Heimes05e8be12008-02-23 18:30:17 +00001import os
Éric Araujo1e3a68d2011-07-28 23:35:29 +02002import errno
Brett Cannon298bb962014-02-28 10:44:45 -05003import importlib.machinery
4import py_compile
Éric Araujo1e3a68d2011-07-28 23:35:29 +02005import shutil
Guido van Rossumfc2a0a82006-10-27 23:06:01 +00006import unittest
Guido van Rossumfc2a0a82006-10-27 23:06:01 +00007import tempfile
8
Benjamin Petersonee8712c2008-05-20 21:35:26 +00009from test import support
Guido van Rossumfc2a0a82006-10-27 23:06:01 +000010
Guido van Rossumfc2a0a82006-10-27 23:06:01 +000011import modulefinder
12
Guido van Rossumfc2a0a82006-10-27 23:06:01 +000013TEST_DIR = tempfile.mkdtemp()
Éric Araujo1e3a68d2011-07-28 23:35:29 +020014TEST_PATH = [TEST_DIR, os.path.dirname(tempfile.__file__)]
Guido van Rossumfc2a0a82006-10-27 23:06:01 +000015
16# Each test description is a list of 5 items:
17#
18# 1. a module name that will be imported by modulefinder
19# 2. a list of module names that modulefinder is required to find
20# 3. a list of module names that modulefinder should complain
21# about because they are not found
22# 4. a list of module names that modulefinder should complain
23# about because they MAY be not found
24# 5. a string specifying packages to create; the format is obvious imo.
25#
26# Each package will be created in TEST_DIR, and TEST_DIR will be
27# removed after the tests again.
28# Modulefinder searches in a path that contains TEST_DIR, plus
29# the standard Lib directory.
30
31maybe_test = [
32 "a.module",
33 ["a", "a.module", "sys",
34 "b"],
35 ["c"], ["b.something"],
36 """\
37a/__init__.py
38a/module.py
39 from b import something
40 from c import something
41b/__init__.py
42 from sys import *
Barryd42e5822020-04-14 20:16:06 +010043""",
44]
Guido van Rossumfc2a0a82006-10-27 23:06:01 +000045
46maybe_test_new = [
47 "a.module",
48 ["a", "a.module", "sys",
Thomas Wouters89f507f2006-12-13 04:49:30 +000049 "b", "__future__"],
Guido van Rossumfc2a0a82006-10-27 23:06:01 +000050 ["c"], ["b.something"],
51 """\
52a/__init__.py
53a/module.py
54 from b import something
55 from c import something
56b/__init__.py
Thomas Wouters89f507f2006-12-13 04:49:30 +000057 from __future__ import absolute_import
Guido van Rossumfc2a0a82006-10-27 23:06:01 +000058 from sys import *
59"""]
60
61package_test = [
62 "a.module",
63 ["a", "a.b", "a.c", "a.module", "mymodule", "sys"],
64 ["blahblah", "c"], [],
65 """\
66mymodule.py
67a/__init__.py
68 import blahblah
69 from a import b
70 import c
71a/module.py
72 import sys
73 from a import b as x
74 from a.c import sillyname
75a/b.py
76a/c.py
77 from a.module import x
78 import mymodule as sillyname
79 from sys import version_info
80"""]
81
82absolute_import_test = [
83 "a.module",
84 ["a", "a.module",
85 "b", "b.x", "b.y", "b.z",
Neal Norwitz2633c692007-02-26 22:22:47 +000086 "__future__", "sys", "gc"],
Guido van Rossumfc2a0a82006-10-27 23:06:01 +000087 ["blahblah", "z"], [],
88 """\
89mymodule.py
90a/__init__.py
91a/module.py
Thomas Wouters89f507f2006-12-13 04:49:30 +000092 from __future__ import absolute_import
Guido van Rossumfc2a0a82006-10-27 23:06:01 +000093 import sys # sys
94 import blahblah # fails
Neal Norwitz2633c692007-02-26 22:22:47 +000095 import gc # gc
Guido van Rossumfc2a0a82006-10-27 23:06:01 +000096 import b.x # b.x
97 from b import y # b.y
98 from b.z import * # b.z.*
Neal Norwitz2633c692007-02-26 22:22:47 +000099a/gc.py
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000100a/sys.py
101 import mymodule
102a/b/__init__.py
103a/b/x.py
104a/b/y.py
105a/b/z.py
106b/__init__.py
107 import z
108b/unused.py
109b/x.py
110b/y.py
111b/z.py
112"""]
113
114relative_import_test = [
115 "a.module",
Thomas Wouters89f507f2006-12-13 04:49:30 +0000116 ["__future__",
117 "a", "a.module",
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000118 "a.b", "a.b.y", "a.b.z",
119 "a.b.c", "a.b.c.moduleC",
120 "a.b.c.d", "a.b.c.e",
121 "a.b.x",
Neal Norwitz2633c692007-02-26 22:22:47 +0000122 "gc"],
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000123 [], [],
124 """\
125mymodule.py
126a/__init__.py
127 from .b import y, z # a.b.y, a.b.z
128a/module.py
Thomas Wouters89f507f2006-12-13 04:49:30 +0000129 from __future__ import absolute_import # __future__
Neal Norwitz2633c692007-02-26 22:22:47 +0000130 import gc # gc
131a/gc.py
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000132a/sys.py
133a/b/__init__.py
134 from ..b import x # a.b.x
135 #from a.b.c import moduleC
136 from .c import moduleC # a.b.moduleC
137a/b/x.py
138a/b/y.py
139a/b/z.py
140a/b/g.py
141a/b/c/__init__.py
142 from ..c import e # a.b.c.e
143a/b/c/moduleC.py
144 from ..c import d # a.b.c.d
145a/b/c/d.py
146a/b/c/e.py
147a/b/c/x.py
148"""]
149
150relative_import_test_2 = [
151 "a.module",
152 ["a", "a.module",
153 "a.sys",
154 "a.b", "a.b.y", "a.b.z",
155 "a.b.c", "a.b.c.d",
156 "a.b.c.e",
157 "a.b.c.moduleC",
158 "a.b.c.f",
159 "a.b.x",
160 "a.another"],
161 [], [],
162 """\
163mymodule.py
164a/__init__.py
165 from . import sys # a.sys
166a/another.py
167a/module.py
168 from .b import y, z # a.b.y, a.b.z
Neal Norwitz2633c692007-02-26 22:22:47 +0000169a/gc.py
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000170a/sys.py
171a/b/__init__.py
172 from .c import moduleC # a.b.c.moduleC
173 from .c import d # a.b.c.d
174a/b/x.py
175a/b/y.py
176a/b/z.py
177a/b/c/__init__.py
178 from . import e # a.b.c.e
179a/b/c/moduleC.py
180 #
181 from . import f # a.b.c.f
182 from .. import x # a.b.x
183 from ... import another # a.another
184a/b/c/d.py
185a/b/c/e.py
186a/b/c/f.py
187"""]
188
Benjamin Petersonc0747cf2008-11-03 20:31:38 +0000189relative_import_test_3 = [
190 "a.module",
191 ["a", "a.module"],
192 ["a.bar"],
193 [],
194 """\
195a/__init__.py
196 def foo(): pass
197a/module.py
198 from . import foo
199 from . import bar
200"""]
201
Brett Cannon73b969e2012-12-22 19:34:21 -0500202relative_import_test_4 = [
203 "a.module",
204 ["a", "a.module"],
205 [],
206 [],
207 """\
208a/__init__.py
209 def foo(): pass
210a/module.py
211 from . import *
212"""]
213
Brett Cannon298bb962014-02-28 10:44:45 -0500214bytecode_test = [
215 "a",
216 ["a"],
217 [],
218 [],
219 ""
220]
221
Brandt Bucher9d7b2c02019-04-07 01:00:41 -0700222syntax_error_test = [
223 "a.module",
224 ["a", "a.module", "b"],
225 ["b.module"], [],
226 """\
227a/__init__.py
228a/module.py
229 import b.module
230b/__init__.py
231b/module.py
232 ? # SyntaxError: invalid syntax
233"""]
234
235
236same_name_as_bad_test = [
237 "a.module",
238 ["a", "a.module", "b", "b.c"],
239 ["c"], [],
240 """\
241a/__init__.py
242a/module.py
243 import c
244 from b import c
245b/__init__.py
246b/c.py
247"""]
248
Barryd42e5822020-04-14 20:16:06 +0100249coding_default_utf8_test = [
250 "a_utf8",
251 ["a_utf8", "b_utf8"],
252 [], [],
253 """\
254a_utf8.py
255 # use the default of utf8
256 print('Unicode test A code point 2090 \u2090 that is not valid in cp1252')
257 import b_utf8
258b_utf8.py
259 # use the default of utf8
260 print('Unicode test B code point 2090 \u2090 that is not valid in cp1252')
261"""]
262
263coding_explicit_utf8_test = [
264 "a_utf8",
265 ["a_utf8", "b_utf8"],
266 [], [],
267 """\
268a_utf8.py
269 # coding=utf8
270 print('Unicode test A code point 2090 \u2090 that is not valid in cp1252')
271 import b_utf8
272b_utf8.py
273 # use the default of utf8
274 print('Unicode test B code point 2090 \u2090 that is not valid in cp1252')
275"""]
276
277coding_explicit_cp1252_test = [
278 "a_cp1252",
279 ["a_cp1252", "b_utf8"],
280 [], [],
281 b"""\
282a_cp1252.py
283 # coding=cp1252
284 # 0xe2 is not allowed in utf8
285 print('CP1252 test P\xe2t\xe9')
286 import b_utf8
Miss Islington (bot)75238862020-06-19 14:26:00 -0700287""" + """\
Barryd42e5822020-04-14 20:16:06 +0100288b_utf8.py
289 # use the default of utf8
290 print('Unicode test A code point 2090 \u2090 that is not valid in cp1252')
Miss Islington (bot)75238862020-06-19 14:26:00 -0700291""".encode('utf-8')]
Éric Araujo1e3a68d2011-07-28 23:35:29 +0200292
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000293def open_file(path):
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000294 dirname = os.path.dirname(path)
Éric Araujo1e3a68d2011-07-28 23:35:29 +0200295 try:
296 os.makedirs(dirname)
297 except OSError as e:
298 if e.errno != errno.EEXIST:
299 raise
Barryd42e5822020-04-14 20:16:06 +0100300 return open(path, 'wb')
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000301
Éric Araujo1e3a68d2011-07-28 23:35:29 +0200302
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000303def create_package(source):
304 ofi = None
Antoine Pitrou92f60ed2010-10-14 22:11:44 +0000305 try:
306 for line in source.splitlines():
Barryd42e5822020-04-14 20:16:06 +0100307 if type(line) != bytes:
308 line = line.encode('utf-8')
309 if line.startswith(b' ') or line.startswith(b'\t'):
310 ofi.write(line.strip() + b'\n')
Antoine Pitrou92f60ed2010-10-14 22:11:44 +0000311 else:
312 if ofi:
313 ofi.close()
Barryd42e5822020-04-14 20:16:06 +0100314 if type(line) == bytes:
315 line = line.decode('utf-8')
Antoine Pitrou92f60ed2010-10-14 22:11:44 +0000316 ofi = open_file(os.path.join(TEST_DIR, line.strip()))
317 finally:
318 if ofi:
319 ofi.close()
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000320
321class ModuleFinderTest(unittest.TestCase):
Barry9b0b5d22020-04-20 15:58:42 +0100322 def _do_test(self, info, report=False, debug=0, replace_paths=[], modulefinder_class=modulefinder.ModuleFinder):
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000323 import_this, modules, missing, maybe_missing, source = info
324 create_package(source)
325 try:
Barry9b0b5d22020-04-20 15:58:42 +0100326 mf = modulefinder_class(path=TEST_PATH, debug=debug,
Berker Peksag0a0d1da2014-07-07 14:58:12 +0300327 replace_paths=replace_paths)
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000328 mf.import_hook(import_this)
329 if report:
330 mf.report()
331## # This wouldn't work in general when executed several times:
332## opath = sys.path[:]
333## sys.path = TEST_PATH
334## try:
335## __import__(import_this)
336## except:
337## import traceback; traceback.print_exc()
338## sys.path = opath
339## return
Éric Araujo1e3a68d2011-07-28 23:35:29 +0200340 modules = sorted(set(modules))
341 found = sorted(mf.modules)
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000342 # check if we found what we expected, not more, not less
Éric Araujo1e3a68d2011-07-28 23:35:29 +0200343 self.assertEqual(found, modules)
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000344
345 # check for missing and maybe missing modules
346 bad, maybe = mf.any_missing_maybe()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000347 self.assertEqual(bad, missing)
348 self.assertEqual(maybe, maybe_missing)
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000349 finally:
Éric Araujo1e3a68d2011-07-28 23:35:29 +0200350 shutil.rmtree(TEST_DIR)
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000351
352 def test_package(self):
353 self._do_test(package_test)
354
355 def test_maybe(self):
356 self._do_test(maybe_test)
357
Éric Araujo1e3a68d2011-07-28 23:35:29 +0200358 def test_maybe_new(self):
359 self._do_test(maybe_test_new)
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000360
Éric Araujo1e3a68d2011-07-28 23:35:29 +0200361 def test_absolute_imports(self):
362 self._do_test(absolute_import_test)
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000363
Éric Araujo1e3a68d2011-07-28 23:35:29 +0200364 def test_relative_imports(self):
365 self._do_test(relative_import_test)
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000366
Éric Araujo1e3a68d2011-07-28 23:35:29 +0200367 def test_relative_imports_2(self):
368 self._do_test(relative_import_test_2)
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000369
Éric Araujo1e3a68d2011-07-28 23:35:29 +0200370 def test_relative_imports_3(self):
371 self._do_test(relative_import_test_3)
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000372
Brett Cannon73b969e2012-12-22 19:34:21 -0500373 def test_relative_imports_4(self):
374 self._do_test(relative_import_test_4)
375
Brandt Bucher9d7b2c02019-04-07 01:00:41 -0700376 def test_syntax_error(self):
377 self._do_test(syntax_error_test)
378
379 def test_same_name_as_bad(self):
380 self._do_test(same_name_as_bad_test)
381
Brett Cannon298bb962014-02-28 10:44:45 -0500382 def test_bytecode(self):
383 base_path = os.path.join(TEST_DIR, 'a')
384 source_path = base_path + importlib.machinery.SOURCE_SUFFIXES[0]
385 bytecode_path = base_path + importlib.machinery.BYTECODE_SUFFIXES[0]
386 with open_file(source_path) as file:
Barryd42e5822020-04-14 20:16:06 +0100387 file.write('testing_modulefinder = True\n'.encode('utf-8'))
Brett Cannon298bb962014-02-28 10:44:45 -0500388 py_compile.compile(source_path, cfile=bytecode_path)
389 os.remove(source_path)
390 self._do_test(bytecode_test)
391
Berker Peksag0a0d1da2014-07-07 14:58:12 +0300392 def test_replace_paths(self):
393 old_path = os.path.join(TEST_DIR, 'a', 'module.py')
394 new_path = os.path.join(TEST_DIR, 'a', 'spam.py')
395 with support.captured_stdout() as output:
396 self._do_test(maybe_test, debug=2,
397 replace_paths=[(old_path, new_path)])
398 output = output.getvalue()
Berker Peksaga90afbc2014-07-07 21:29:50 +0300399 expected = "co_filename %r changed to %r" % (old_path, new_path)
Berker Peksag0a0d1da2014-07-07 14:58:12 +0300400 self.assertIn(expected, output)
Benjamin Petersonc0747cf2008-11-03 20:31:38 +0000401
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300402 def test_extended_opargs(self):
403 extended_opargs_test = [
404 "a",
405 ["a", "b"],
406 [], [],
407 """\
408a.py
409 %r
410 import b
411b.py
412""" % list(range(2**16))] # 2**16 constants
413 self._do_test(extended_opargs_test)
414
Barryd42e5822020-04-14 20:16:06 +0100415 def test_coding_default_utf8(self):
416 self._do_test(coding_default_utf8_test)
417
418 def test_coding_explicit_utf8(self):
419 self._do_test(coding_explicit_utf8_test)
420
421 def test_coding_explicit_cp1252(self):
422 self._do_test(coding_explicit_cp1252_test)
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000423
Barry9b0b5d22020-04-20 15:58:42 +0100424 def test_load_module_api(self):
425 class CheckLoadModuleApi(modulefinder.ModuleFinder):
426 def __init__(self, *args, **kwds):
427 super().__init__(*args, **kwds)
428
429 def load_module(self, fqname, fp, pathname, file_info):
430 # confirm that the fileinfo is a tuple of 3 elements
431 suffix, mode, type = file_info
432 return super().load_module(fqname, fp, pathname, file_info)
433
434 self._do_test(absolute_import_test, modulefinder_class=CheckLoadModuleApi)
435
Guido van Rossumfc2a0a82006-10-27 23:06:01 +0000436if __name__ == "__main__":
437 unittest.main()