python (3.11.7)
1 import os
2 import errno
3 import importlib.machinery
4 import py_compile
5 import shutil
6 import unittest
7 import tempfile
8
9 from test import support
10
11 import modulefinder
12
13 # Each test description is a list of 5 items:
14 #
15 # 1. a module name that will be imported by modulefinder
16 # 2. a list of module names that modulefinder is required to find
17 # 3. a list of module names that modulefinder should complain
18 # about because they are not found
19 # 4. a list of module names that modulefinder should complain
20 # about because they MAY be not found
21 # 5. a string specifying packages to create; the format is obvious imo.
22 #
23 # Each package will be created in test_dir, and test_dir will be
24 # removed after the tests again.
25 # Modulefinder searches in a path that contains test_dir, plus
26 # the standard Lib directory.
27
28 maybe_test = [
29 "a.module",
30 ["a", "a.module", "sys",
31 "b"],
32 ["c"], ["b.something"],
33 """\
34 a/__init__.py
35 a/module.py
36 from b import something
37 from c import something
38 b/__init__.py
39 from sys import *
40 """,
41 ]
42
43 maybe_test_new = [
44 "a.module",
45 ["a", "a.module", "sys",
46 "b", "__future__"],
47 ["c"], ["b.something"],
48 """\
49 a/__init__.py
50 a/module.py
51 from b import something
52 from c import something
53 b/__init__.py
54 from __future__ import absolute_import
55 from sys import *
56 """]
57
58 package_test = [
59 "a.module",
60 ["a", "a.b", "a.c", "a.module", "mymodule", "sys"],
61 ["blahblah", "c"], [],
62 """\
63 mymodule.py
64 a/__init__.py
65 import blahblah
66 from a import b
67 import c
68 a/module.py
69 import sys
70 from a import b as x
71 from a.c import sillyname
72 a/b.py
73 a/c.py
74 from a.module import x
75 import mymodule as sillyname
76 from sys import version_info
77 """]
78
79 absolute_import_test = [
80 "a.module",
81 ["a", "a.module",
82 "b", "b.x", "b.y", "b.z",
83 "__future__", "sys", "gc"],
84 ["blahblah", "z"], [],
85 """\
86 mymodule.py
87 a/__init__.py
88 a/module.py
89 from __future__ import absolute_import
90 import sys # sys
91 import blahblah # fails
92 import gc # gc
93 import b.x # b.x
94 from b import y # b.y
95 from b.z import * # b.z.*
96 a/gc.py
97 a/sys.py
98 import mymodule
99 a/b/__init__.py
100 a/b/x.py
101 a/b/y.py
102 a/b/z.py
103 b/__init__.py
104 import z
105 b/unused.py
106 b/x.py
107 b/y.py
108 b/z.py
109 """]
110
111 relative_import_test = [
112 "a.module",
113 ["__future__",
114 "a", "a.module",
115 "a.b", "a.b.y", "a.b.z",
116 "a.b.c", "a.b.c.moduleC",
117 "a.b.c.d", "a.b.c.e",
118 "a.b.x",
119 "gc"],
120 [], [],
121 """\
122 mymodule.py
123 a/__init__.py
124 from .b import y, z # a.b.y, a.b.z
125 a/module.py
126 from __future__ import absolute_import # __future__
127 import gc # gc
128 a/gc.py
129 a/sys.py
130 a/b/__init__.py
131 from ..b import x # a.b.x
132 #from a.b.c import moduleC
133 from .c import moduleC # a.b.moduleC
134 a/b/x.py
135 a/b/y.py
136 a/b/z.py
137 a/b/g.py
138 a/b/c/__init__.py
139 from ..c import e # a.b.c.e
140 a/b/c/moduleC.py
141 from ..c import d # a.b.c.d
142 a/b/c/d.py
143 a/b/c/e.py
144 a/b/c/x.py
145 """]
146
147 relative_import_test_2 = [
148 "a.module",
149 ["a", "a.module",
150 "a.sys",
151 "a.b", "a.b.y", "a.b.z",
152 "a.b.c", "a.b.c.d",
153 "a.b.c.e",
154 "a.b.c.moduleC",
155 "a.b.c.f",
156 "a.b.x",
157 "a.another"],
158 [], [],
159 """\
160 mymodule.py
161 a/__init__.py
162 from . import sys # a.sys
163 a/another.py
164 a/module.py
165 from .b import y, z # a.b.y, a.b.z
166 a/gc.py
167 a/sys.py
168 a/b/__init__.py
169 from .c import moduleC # a.b.c.moduleC
170 from .c import d # a.b.c.d
171 a/b/x.py
172 a/b/y.py
173 a/b/z.py
174 a/b/c/__init__.py
175 from . import e # a.b.c.e
176 a/b/c/moduleC.py
177 #
178 from . import f # a.b.c.f
179 from .. import x # a.b.x
180 from ... import another # a.another
181 a/b/c/d.py
182 a/b/c/e.py
183 a/b/c/f.py
184 """]
185
186 relative_import_test_3 = [
187 "a.module",
188 ["a", "a.module"],
189 ["a.bar"],
190 [],
191 """\
192 a/__init__.py
193 def foo(): pass
194 a/module.py
195 from . import foo
196 from . import bar
197 """]
198
199 relative_import_test_4 = [
200 "a.module",
201 ["a", "a.module"],
202 [],
203 [],
204 """\
205 a/__init__.py
206 def foo(): pass
207 a/module.py
208 from . import *
209 """]
210
211 bytecode_test = [
212 "a",
213 ["a"],
214 [],
215 [],
216 ""
217 ]
218
219 syntax_error_test = [
220 "a.module",
221 ["a", "a.module", "b"],
222 ["b.module"], [],
223 """\
224 a/__init__.py
225 a/module.py
226 import b.module
227 b/__init__.py
228 b/module.py
229 ? # SyntaxError: invalid syntax
230 """]
231
232
233 same_name_as_bad_test = [
234 "a.module",
235 ["a", "a.module", "b", "b.c"],
236 ["c"], [],
237 """\
238 a/__init__.py
239 a/module.py
240 import c
241 from b import c
242 b/__init__.py
243 b/c.py
244 """]
245
246 coding_default_utf8_test = [
247 "a_utf8",
248 ["a_utf8", "b_utf8"],
249 [], [],
250 """\
251 a_utf8.py
252 # use the default of utf8
253 print('Unicode test A code point 2090 \u2090 that is not valid in cp1252')
254 import b_utf8
255 b_utf8.py
256 # use the default of utf8
257 print('Unicode test B code point 2090 \u2090 that is not valid in cp1252')
258 """]
259
260 coding_explicit_utf8_test = [
261 "a_utf8",
262 ["a_utf8", "b_utf8"],
263 [], [],
264 """\
265 a_utf8.py
266 # coding=utf8
267 print('Unicode test A code point 2090 \u2090 that is not valid in cp1252')
268 import b_utf8
269 b_utf8.py
270 # use the default of utf8
271 print('Unicode test B code point 2090 \u2090 that is not valid in cp1252')
272 """]
273
274 coding_explicit_cp1252_test = [
275 "a_cp1252",
276 ["a_cp1252", "b_utf8"],
277 [], [],
278 b"""\
279 a_cp1252.py
280 # coding=cp1252
281 # 0xe2 is not allowed in utf8
282 print('CP1252 test P\xe2t\xe9')
283 import b_utf8
284 """ + """\
285 b_utf8.py
286 # use the default of utf8
287 print('Unicode test A code point 2090 \u2090 that is not valid in cp1252')
288 """.encode('utf-8')]
289
290 def open_file(path):
291 dirname = os.path.dirname(path)
292 try:
293 os.makedirs(dirname)
294 except OSError as e:
295 if e.errno != errno.EEXIST:
296 raise
297 return open(path, 'wb')
298
299
300 def create_package(test_dir, source):
301 ofi = None
302 try:
303 for line in source.splitlines():
304 if type(line) != bytes:
305 line = line.encode('utf-8')
306 if line.startswith(b' ') or line.startswith(b'\t'):
307 ofi.write(line.strip() + b'\n')
308 else:
309 if ofi:
310 ofi.close()
311 if type(line) == bytes:
312 line = line.decode('utf-8')
313 ofi = open_file(os.path.join(test_dir, line.strip()))
314 finally:
315 if ofi:
316 ofi.close()
317
318 class ESC[4;38;5;81mModuleFinderTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
319 def setUp(self):
320 self.test_dir = tempfile.mkdtemp()
321 self.test_path = [self.test_dir, os.path.dirname(tempfile.__file__)]
322
323 def tearDown(self):
324 shutil.rmtree(self.test_dir)
325
326 def _do_test(self, info, report=False, debug=0, replace_paths=[], modulefinder_class=modulefinder.ModuleFinder):
327 import_this, modules, missing, maybe_missing, source = info
328 create_package(self.test_dir, source)
329 mf = modulefinder_class(path=self.test_path, debug=debug,
330 replace_paths=replace_paths)
331 mf.import_hook(import_this)
332 if report:
333 mf.report()
334 ## # This wouldn't work in general when executed several times:
335 ## opath = sys.path[:]
336 ## sys.path = self.test_path
337 ## try:
338 ## __import__(import_this)
339 ## except:
340 ## import traceback; traceback.print_exc()
341 ## sys.path = opath
342 ## return
343 modules = sorted(set(modules))
344 found = sorted(mf.modules)
345 # check if we found what we expected, not more, not less
346 self.assertEqual(found, modules)
347
348 # check for missing and maybe missing modules
349 bad, maybe = mf.any_missing_maybe()
350 self.assertEqual(bad, missing)
351 self.assertEqual(maybe, maybe_missing)
352
353 def test_package(self):
354 self._do_test(package_test)
355
356 def test_maybe(self):
357 self._do_test(maybe_test)
358
359 def test_maybe_new(self):
360 self._do_test(maybe_test_new)
361
362 def test_absolute_imports(self):
363 self._do_test(absolute_import_test)
364
365 def test_relative_imports(self):
366 self._do_test(relative_import_test)
367
368 def test_relative_imports_2(self):
369 self._do_test(relative_import_test_2)
370
371 def test_relative_imports_3(self):
372 self._do_test(relative_import_test_3)
373
374 def test_relative_imports_4(self):
375 self._do_test(relative_import_test_4)
376
377 def test_syntax_error(self):
378 self._do_test(syntax_error_test)
379
380 def test_same_name_as_bad(self):
381 self._do_test(same_name_as_bad_test)
382
383 def test_bytecode(self):
384 base_path = os.path.join(self.test_dir, 'a')
385 source_path = base_path + importlib.machinery.SOURCE_SUFFIXES[0]
386 bytecode_path = base_path + importlib.machinery.BYTECODE_SUFFIXES[0]
387 with open_file(source_path) as file:
388 file.write('testing_modulefinder = True\n'.encode('utf-8'))
389 py_compile.compile(source_path, cfile=bytecode_path)
390 os.remove(source_path)
391 self._do_test(bytecode_test)
392
393 def test_replace_paths(self):
394 old_path = os.path.join(self.test_dir, 'a', 'module.py')
395 new_path = os.path.join(self.test_dir, 'a', 'spam.py')
396 with support.captured_stdout() as output:
397 self._do_test(maybe_test, debug=2,
398 replace_paths=[(old_path, new_path)])
399 output = output.getvalue()
400 expected = "co_filename %r changed to %r" % (old_path, new_path)
401 self.assertIn(expected, output)
402
403 def test_extended_opargs(self):
404 extended_opargs_test = [
405 "a",
406 ["a", "b"],
407 [], [],
408 """\
409 a.py
410 %r
411 import b
412 b.py
413 """ % list(range(2**16))] # 2**16 constants
414 self._do_test(extended_opargs_test)
415
416 def test_coding_default_utf8(self):
417 self._do_test(coding_default_utf8_test)
418
419 def test_coding_explicit_utf8(self):
420 self._do_test(coding_explicit_utf8_test)
421
422 def test_coding_explicit_cp1252(self):
423 self._do_test(coding_explicit_cp1252_test)
424
425 def test_load_module_api(self):
426 class ESC[4;38;5;81mCheckLoadModuleApi(ESC[4;38;5;149mmodulefinderESC[4;38;5;149m.ESC[4;38;5;149mModuleFinder):
427 def __init__(self, *args, **kwds):
428 super().__init__(*args, **kwds)
429
430 def load_module(self, fqname, fp, pathname, file_info):
431 # confirm that the fileinfo is a tuple of 3 elements
432 suffix, mode, type = file_info
433 return super().load_module(fqname, fp, pathname, file_info)
434
435 self._do_test(absolute_import_test, modulefinder_class=CheckLoadModuleApi)
436
437 if __name__ == "__main__":
438 unittest.main()