ยปCore Development>Code coverage>Lib/importlib/test/source/test_file_loader.py

Python code coverage for Lib/importlib/test/source/test_file_loader.py

#countcontent
1n/afrom ... import _bootstrap
2n/aimport importlib
3n/aimport importlib.abc
4n/afrom .. import abc
5n/afrom .. import util
6n/afrom . import util as source_util
7n/a
8n/aimport errno
9n/aimport imp
10n/aimport marshal
11n/aimport os
12n/aimport py_compile
13n/aimport shutil
14n/aimport stat
15n/aimport sys
16n/aimport unittest
17n/a
18n/afrom test.support import make_legacy_pyc
19n/a
20n/a
21n/aclass SimpleTest(unittest.TestCase):
22n/a
23n/a """Should have no issue importing a source module [basic]. And if there is
24n/a a syntax error, it should raise a SyntaxError [syntax error].
25n/a
26n/a """
27n/a
28n/a def test_load_module_API(self):
29n/a # If fullname is not specified that assume self.name is desired.
30n/a class TesterMixin(importlib.abc.Loader):
31n/a def load_module(self, fullname): return fullname
32n/a
33n/a class Tester(importlib.abc.FileLoader, TesterMixin):
34n/a def get_code(self, _): pass
35n/a def get_source(self, _): pass
36n/a def is_package(self, _): pass
37n/a
38n/a name = 'mod_name'
39n/a loader = Tester(name, 'some_path')
40n/a self.assertEqual(name, loader.load_module())
41n/a self.assertEqual(name, loader.load_module(None))
42n/a self.assertEqual(name, loader.load_module(name))
43n/a with self.assertRaises(ImportError):
44n/a loader.load_module(loader.name + 'XXX')
45n/a
46n/a def test_get_filename_API(self):
47n/a # If fullname is not set then assume self.path is desired.
48n/a class Tester(importlib.abc.FileLoader):
49n/a def get_code(self, _): pass
50n/a def get_source(self, _): pass
51n/a def is_package(self, _): pass
52n/a
53n/a path = 'some_path'
54n/a name = 'some_name'
55n/a loader = Tester(name, path)
56n/a self.assertEqual(path, loader.get_filename(name))
57n/a self.assertEqual(path, loader.get_filename())
58n/a self.assertEqual(path, loader.get_filename(None))
59n/a with self.assertRaises(ImportError):
60n/a loader.get_filename(name + 'XXX')
61n/a
62n/a # [basic]
63n/a def test_module(self):
64n/a with source_util.create_modules('_temp') as mapping:
65n/a loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
66n/a module = loader.load_module('_temp')
67n/a self.assertIn('_temp', sys.modules)
68n/a check = {'__name__': '_temp', '__file__': mapping['_temp'],
69n/a '__package__': ''}
70n/a for attr, value in check.items():
71n/a self.assertEqual(getattr(module, attr), value)
72n/a
73n/a def test_package(self):
74n/a with source_util.create_modules('_pkg.__init__') as mapping:
75n/a loader = _bootstrap.SourceFileLoader('_pkg',
76n/a mapping['_pkg.__init__'])
77n/a module = loader.load_module('_pkg')
78n/a self.assertIn('_pkg', sys.modules)
79n/a check = {'__name__': '_pkg', '__file__': mapping['_pkg.__init__'],
80n/a '__path__': [os.path.dirname(mapping['_pkg.__init__'])],
81n/a '__package__': '_pkg'}
82n/a for attr, value in check.items():
83n/a self.assertEqual(getattr(module, attr), value)
84n/a
85n/a
86n/a def test_lacking_parent(self):
87n/a with source_util.create_modules('_pkg.__init__', '_pkg.mod')as mapping:
88n/a loader = _bootstrap.SourceFileLoader('_pkg.mod',
89n/a mapping['_pkg.mod'])
90n/a module = loader.load_module('_pkg.mod')
91n/a self.assertIn('_pkg.mod', sys.modules)
92n/a check = {'__name__': '_pkg.mod', '__file__': mapping['_pkg.mod'],
93n/a '__package__': '_pkg'}
94n/a for attr, value in check.items():
95n/a self.assertEqual(getattr(module, attr), value)
96n/a
97n/a def fake_mtime(self, fxn):
98n/a """Fake mtime to always be higher than expected."""
99n/a return lambda name: fxn(name) + 1
100n/a
101n/a def test_module_reuse(self):
102n/a with source_util.create_modules('_temp') as mapping:
103n/a loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
104n/a module = loader.load_module('_temp')
105n/a module_id = id(module)
106n/a module_dict_id = id(module.__dict__)
107n/a with open(mapping['_temp'], 'w') as file:
108n/a file.write("testing_var = 42\n")
109n/a module = loader.load_module('_temp')
110n/a self.assertIn('testing_var', module.__dict__,
111n/a "'testing_var' not in "
112n/a "{0}".format(list(module.__dict__.keys())))
113n/a self.assertEqual(module, sys.modules['_temp'])
114n/a self.assertEqual(id(module), module_id)
115n/a self.assertEqual(id(module.__dict__), module_dict_id)
116n/a
117n/a def test_state_after_failure(self):
118n/a # A failed reload should leave the original module intact.
119n/a attributes = ('__file__', '__path__', '__package__')
120n/a value = '<test>'
121n/a name = '_temp'
122n/a with source_util.create_modules(name) as mapping:
123n/a orig_module = imp.new_module(name)
124n/a for attr in attributes:
125n/a setattr(orig_module, attr, value)
126n/a with open(mapping[name], 'w') as file:
127n/a file.write('+++ bad syntax +++')
128n/a loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
129n/a with self.assertRaises(SyntaxError):
130n/a loader.load_module(name)
131n/a for attr in attributes:
132n/a self.assertEqual(getattr(orig_module, attr), value)
133n/a
134n/a # [syntax error]
135n/a def test_bad_syntax(self):
136n/a with source_util.create_modules('_temp') as mapping:
137n/a with open(mapping['_temp'], 'w') as file:
138n/a file.write('=')
139n/a loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
140n/a with self.assertRaises(SyntaxError):
141n/a loader.load_module('_temp')
142n/a self.assertNotIn('_temp', sys.modules)
143n/a
144n/a def test_file_from_empty_string_dir(self):
145n/a # Loading a module found from an empty string entry on sys.path should
146n/a # not only work, but keep all attributes relative.
147n/a file_path = '_temp.py'
148n/a with open(file_path, 'w') as file:
149n/a file.write("# test file for importlib")
150n/a try:
151n/a with util.uncache('_temp'):
152n/a loader = _bootstrap.SourceFileLoader('_temp', file_path)
153n/a mod = loader.load_module('_temp')
154n/a self.assertEqual(file_path, mod.__file__)
155n/a self.assertEqual(imp.cache_from_source(file_path),
156n/a mod.__cached__)
157n/a finally:
158n/a os.unlink(file_path)
159n/a pycache = os.path.dirname(imp.cache_from_source(file_path))
160n/a shutil.rmtree(pycache)
161n/a
162n/a def test_timestamp_overflow(self):
163n/a # When a modification timestamp is larger than 2**32, it should be
164n/a # truncated rather than raise an OverflowError.
165n/a with source_util.create_modules('_temp') as mapping:
166n/a source = mapping['_temp']
167n/a compiled = imp.cache_from_source(source)
168n/a with open(source, 'w') as f:
169n/a f.write("x = 5")
170n/a try:
171n/a os.utime(source, (2 ** 33 - 5, 2 ** 33 - 5))
172n/a except OverflowError:
173n/a self.skipTest("cannot set modification time to large integer")
174n/a except OSError as e:
175n/a if e.errno != getattr(errno, 'EOVERFLOW', None):
176n/a raise
177n/a self.skipTest("cannot set modification time to large integer ({})".format(e))
178n/a loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
179n/a mod = loader.load_module('_temp')
180n/a # Sanity checks.
181n/a self.assertEqual(mod.__cached__, compiled)
182n/a self.assertEqual(mod.x, 5)
183n/a # The pyc file was created.
184n/a os.stat(compiled)
185n/a
186n/a
187n/aclass BadBytecodeTest(unittest.TestCase):
188n/a
189n/a def import_(self, file, module_name):
190n/a loader = self.loader(module_name, file)
191n/a module = loader.load_module(module_name)
192n/a self.assertIn(module_name, sys.modules)
193n/a
194n/a def manipulate_bytecode(self, name, mapping, manipulator, *,
195n/a del_source=False):
196n/a """Manipulate the bytecode of a module by passing it into a callable
197n/a that returns what to use as the new bytecode."""
198n/a try:
199n/a del sys.modules['_temp']
200n/a except KeyError:
201n/a pass
202n/a py_compile.compile(mapping[name])
203n/a if not del_source:
204n/a bytecode_path = imp.cache_from_source(mapping[name])
205n/a else:
206n/a os.unlink(mapping[name])
207n/a bytecode_path = make_legacy_pyc(mapping[name])
208n/a if manipulator:
209n/a with open(bytecode_path, 'rb') as file:
210n/a bc = file.read()
211n/a new_bc = manipulator(bc)
212n/a with open(bytecode_path, 'wb') as file:
213n/a if new_bc is not None:
214n/a file.write(new_bc)
215n/a return bytecode_path
216n/a
217n/a def _test_empty_file(self, test, *, del_source=False):
218n/a with source_util.create_modules('_temp') as mapping:
219n/a bc_path = self.manipulate_bytecode('_temp', mapping,
220n/a lambda bc: b'',
221n/a del_source=del_source)
222n/a test('_temp', mapping, bc_path)
223n/a
224n/a @source_util.writes_bytecode_files
225n/a def _test_partial_magic(self, test, *, del_source=False):
226n/a # When their are less than 4 bytes to a .pyc, regenerate it if
227n/a # possible, else raise ImportError.
228n/a with source_util.create_modules('_temp') as mapping:
229n/a bc_path = self.manipulate_bytecode('_temp', mapping,
230n/a lambda bc: bc[:3],
231n/a del_source=del_source)
232n/a test('_temp', mapping, bc_path)
233n/a
234n/a def _test_magic_only(self, test, *, del_source=False):
235n/a with source_util.create_modules('_temp') as mapping:
236n/a bc_path = self.manipulate_bytecode('_temp', mapping,
237n/a lambda bc: bc[:4],
238n/a del_source=del_source)
239n/a test('_temp', mapping, bc_path)
240n/a
241n/a def _test_partial_timestamp(self, test, *, del_source=False):
242n/a with source_util.create_modules('_temp') as mapping:
243n/a bc_path = self.manipulate_bytecode('_temp', mapping,
244n/a lambda bc: bc[:7],
245n/a del_source=del_source)
246n/a test('_temp', mapping, bc_path)
247n/a
248n/a def _test_partial_size(self, test, *, del_source=False):
249n/a with source_util.create_modules('_temp') as mapping:
250n/a bc_path = self.manipulate_bytecode('_temp', mapping,
251n/a lambda bc: bc[:11],
252n/a del_source=del_source)
253n/a test('_temp', mapping, bc_path)
254n/a
255n/a def _test_no_marshal(self, *, del_source=False):
256n/a with source_util.create_modules('_temp') as mapping:
257n/a bc_path = self.manipulate_bytecode('_temp', mapping,
258n/a lambda bc: bc[:12],
259n/a del_source=del_source)
260n/a file_path = mapping['_temp'] if not del_source else bc_path
261n/a with self.assertRaises(EOFError):
262n/a self.import_(file_path, '_temp')
263n/a
264n/a def _test_non_code_marshal(self, *, del_source=False):
265n/a with source_util.create_modules('_temp') as mapping:
266n/a bytecode_path = self.manipulate_bytecode('_temp', mapping,
267n/a lambda bc: bc[:12] + marshal.dumps(b'abcd'),
268n/a del_source=del_source)
269n/a file_path = mapping['_temp'] if not del_source else bytecode_path
270n/a with self.assertRaises(ImportError) as cm:
271n/a self.import_(file_path, '_temp')
272n/a self.assertEqual(cm.exception.name, '_temp')
273n/a self.assertEqual(cm.exception.path, bytecode_path)
274n/a
275n/a def _test_bad_marshal(self, *, del_source=False):
276n/a with source_util.create_modules('_temp') as mapping:
277n/a bytecode_path = self.manipulate_bytecode('_temp', mapping,
278n/a lambda bc: bc[:12] + b'<test>',
279n/a del_source=del_source)
280n/a file_path = mapping['_temp'] if not del_source else bytecode_path
281n/a with self.assertRaises(EOFError):
282n/a self.import_(file_path, '_temp')
283n/a
284n/a def _test_bad_magic(self, test, *, del_source=False):
285n/a with source_util.create_modules('_temp') as mapping:
286n/a bc_path = self.manipulate_bytecode('_temp', mapping,
287n/a lambda bc: b'\x00\x00\x00\x00' + bc[4:])
288n/a test('_temp', mapping, bc_path)
289n/a
290n/a
291n/aclass SourceLoaderBadBytecodeTest(BadBytecodeTest):
292n/a
293n/a loader = _bootstrap.SourceFileLoader
294n/a
295n/a @source_util.writes_bytecode_files
296n/a def test_empty_file(self):
297n/a # When a .pyc is empty, regenerate it if possible, else raise
298n/a # ImportError.
299n/a def test(name, mapping, bytecode_path):
300n/a self.import_(mapping[name], name)
301n/a with open(bytecode_path, 'rb') as file:
302n/a self.assertGreater(len(file.read()), 12)
303n/a
304n/a self._test_empty_file(test)
305n/a
306n/a def test_partial_magic(self):
307n/a def test(name, mapping, bytecode_path):
308n/a self.import_(mapping[name], name)
309n/a with open(bytecode_path, 'rb') as file:
310n/a self.assertGreater(len(file.read()), 12)
311n/a
312n/a self._test_partial_magic(test)
313n/a
314n/a @source_util.writes_bytecode_files
315n/a def test_magic_only(self):
316n/a # When there is only the magic number, regenerate the .pyc if possible,
317n/a # else raise EOFError.
318n/a def test(name, mapping, bytecode_path):
319n/a self.import_(mapping[name], name)
320n/a with open(bytecode_path, 'rb') as file:
321n/a self.assertGreater(len(file.read()), 12)
322n/a
323n/a self._test_magic_only(test)
324n/a
325n/a @source_util.writes_bytecode_files
326n/a def test_bad_magic(self):
327n/a # When the magic number is different, the bytecode should be
328n/a # regenerated.
329n/a def test(name, mapping, bytecode_path):
330n/a self.import_(mapping[name], name)
331n/a with open(bytecode_path, 'rb') as bytecode_file:
332n/a self.assertEqual(bytecode_file.read(4), imp.get_magic())
333n/a
334n/a self._test_bad_magic(test)
335n/a
336n/a @source_util.writes_bytecode_files
337n/a def test_partial_timestamp(self):
338n/a # When the timestamp is partial, regenerate the .pyc, else
339n/a # raise EOFError.
340n/a def test(name, mapping, bc_path):
341n/a self.import_(mapping[name], name)
342n/a with open(bc_path, 'rb') as file:
343n/a self.assertGreater(len(file.read()), 12)
344n/a
345n/a self._test_partial_timestamp(test)
346n/a
347n/a @source_util.writes_bytecode_files
348n/a def test_partial_size(self):
349n/a # When the size is partial, regenerate the .pyc, else
350n/a # raise EOFError.
351n/a def test(name, mapping, bc_path):
352n/a self.import_(mapping[name], name)
353n/a with open(bc_path, 'rb') as file:
354n/a self.assertGreater(len(file.read()), 12)
355n/a
356n/a self._test_partial_size(test)
357n/a
358n/a @source_util.writes_bytecode_files
359n/a def test_no_marshal(self):
360n/a # When there is only the magic number and timestamp, raise EOFError.
361n/a self._test_no_marshal()
362n/a
363n/a @source_util.writes_bytecode_files
364n/a def test_non_code_marshal(self):
365n/a self._test_non_code_marshal()
366n/a # XXX ImportError when sourceless
367n/a
368n/a # [bad marshal]
369n/a @source_util.writes_bytecode_files
370n/a def test_bad_marshal(self):
371n/a # Bad marshal data should raise a ValueError.
372n/a self._test_bad_marshal()
373n/a
374n/a # [bad timestamp]
375n/a @source_util.writes_bytecode_files
376n/a def test_old_timestamp(self):
377n/a # When the timestamp is older than the source, bytecode should be
378n/a # regenerated.
379n/a zeros = b'\x00\x00\x00\x00'
380n/a with source_util.create_modules('_temp') as mapping:
381n/a py_compile.compile(mapping['_temp'])
382n/a bytecode_path = imp.cache_from_source(mapping['_temp'])
383n/a with open(bytecode_path, 'r+b') as bytecode_file:
384n/a bytecode_file.seek(4)
385n/a bytecode_file.write(zeros)
386n/a self.import_(mapping['_temp'], '_temp')
387n/a source_mtime = os.path.getmtime(mapping['_temp'])
388n/a source_timestamp = importlib._w_long(source_mtime)
389n/a with open(bytecode_path, 'rb') as bytecode_file:
390n/a bytecode_file.seek(4)
391n/a self.assertEqual(bytecode_file.read(4), source_timestamp)
392n/a
393n/a # [bytecode read-only]
394n/a @source_util.writes_bytecode_files
395n/a def test_read_only_bytecode(self):
396n/a # When bytecode is read-only but should be rewritten, fail silently.
397n/a with source_util.create_modules('_temp') as mapping:
398n/a # Create bytecode that will need to be re-created.
399n/a py_compile.compile(mapping['_temp'])
400n/a bytecode_path = imp.cache_from_source(mapping['_temp'])
401n/a with open(bytecode_path, 'r+b') as bytecode_file:
402n/a bytecode_file.seek(0)
403n/a bytecode_file.write(b'\x00\x00\x00\x00')
404n/a # Make the bytecode read-only.
405n/a os.chmod(bytecode_path,
406n/a stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
407n/a try:
408n/a # Should not raise IOError!
409n/a self.import_(mapping['_temp'], '_temp')
410n/a finally:
411n/a # Make writable for eventual clean-up.
412n/a os.chmod(bytecode_path, stat.S_IWUSR)
413n/a
414n/a
415n/aclass SourcelessLoaderBadBytecodeTest(BadBytecodeTest):
416n/a
417n/a loader = _bootstrap.SourcelessFileLoader
418n/a
419n/a def test_empty_file(self):
420n/a def test(name, mapping, bytecode_path):
421n/a with self.assertRaises(ImportError) as cm:
422n/a self.import_(bytecode_path, name)
423n/a self.assertEqual(cm.exception.name, name)
424n/a self.assertEqual(cm.exception.path, bytecode_path)
425n/a
426n/a self._test_empty_file(test, del_source=True)
427n/a
428n/a def test_partial_magic(self):
429n/a def test(name, mapping, bytecode_path):
430n/a with self.assertRaises(ImportError) as cm:
431n/a self.import_(bytecode_path, name)
432n/a self.assertEqual(cm.exception.name, name)
433n/a self.assertEqual(cm.exception.path, bytecode_path)
434n/a self._test_partial_magic(test, del_source=True)
435n/a
436n/a def test_magic_only(self):
437n/a def test(name, mapping, bytecode_path):
438n/a with self.assertRaises(EOFError):
439n/a self.import_(bytecode_path, name)
440n/a
441n/a self._test_magic_only(test, del_source=True)
442n/a
443n/a def test_bad_magic(self):
444n/a def test(name, mapping, bytecode_path):
445n/a with self.assertRaises(ImportError) as cm:
446n/a self.import_(bytecode_path, name)
447n/a self.assertEqual(cm.exception.name, name)
448n/a self.assertEqual(cm.exception.path, bytecode_path)
449n/a
450n/a self._test_bad_magic(test, del_source=True)
451n/a
452n/a def test_partial_timestamp(self):
453n/a def test(name, mapping, bytecode_path):
454n/a with self.assertRaises(EOFError):
455n/a self.import_(bytecode_path, name)
456n/a
457n/a self._test_partial_timestamp(test, del_source=True)
458n/a
459n/a def test_partial_size(self):
460n/a def test(name, mapping, bytecode_path):
461n/a with self.assertRaises(EOFError):
462n/a self.import_(bytecode_path, name)
463n/a
464n/a self._test_partial_size(test, del_source=True)
465n/a
466n/a def test_no_marshal(self):
467n/a self._test_no_marshal(del_source=True)
468n/a
469n/a def test_non_code_marshal(self):
470n/a self._test_non_code_marshal(del_source=True)
471n/a
472n/a
473n/adef test_main():
474n/a from test.support import run_unittest
475n/a run_unittest(SimpleTest,
476n/a SourceLoaderBadBytecodeTest,
477n/a SourcelessLoaderBadBytecodeTest
478n/a )
479n/a
480n/a
481n/aif __name__ == '__main__':
482n/a test_main()