ยปCore Development>Code coverage>Lib/test/test_importlib/util.py

Python code coverage for Lib/test/test_importlib/util.py

#countcontent
1n/aimport builtins
2n/aimport contextlib
3n/aimport errno
4n/aimport functools
5n/aimport importlib
6n/afrom importlib import machinery, util, invalidate_caches
7n/aimport os
8n/aimport os.path
9n/afrom test import support
10n/aimport unittest
11n/aimport sys
12n/aimport tempfile
13n/aimport types
14n/a
15n/a
16n/aBUILTINS = types.SimpleNamespace()
17n/aBUILTINS.good_name = None
18n/aBUILTINS.bad_name = None
19n/aif 'errno' in sys.builtin_module_names:
20n/a BUILTINS.good_name = 'errno'
21n/aif 'importlib' not in sys.builtin_module_names:
22n/a BUILTINS.bad_name = 'importlib'
23n/a
24n/aEXTENSIONS = types.SimpleNamespace()
25n/aEXTENSIONS.path = None
26n/aEXTENSIONS.ext = None
27n/aEXTENSIONS.filename = None
28n/aEXTENSIONS.file_path = None
29n/aEXTENSIONS.name = '_testcapi'
30n/a
31n/adef _extension_details():
32n/a global EXTENSIONS
33n/a for path in sys.path:
34n/a for ext in machinery.EXTENSION_SUFFIXES:
35n/a filename = EXTENSIONS.name + ext
36n/a file_path = os.path.join(path, filename)
37n/a if os.path.exists(file_path):
38n/a EXTENSIONS.path = path
39n/a EXTENSIONS.ext = ext
40n/a EXTENSIONS.filename = filename
41n/a EXTENSIONS.file_path = file_path
42n/a return
43n/a
44n/a_extension_details()
45n/a
46n/a
47n/adef import_importlib(module_name):
48n/a """Import a module from importlib both w/ and w/o _frozen_importlib."""
49n/a fresh = ('importlib',) if '.' in module_name else ()
50n/a frozen = support.import_fresh_module(module_name)
51n/a source = support.import_fresh_module(module_name, fresh=fresh,
52n/a blocked=('_frozen_importlib', '_frozen_importlib_external'))
53n/a return {'Frozen': frozen, 'Source': source}
54n/a
55n/a
56n/adef specialize_class(cls, kind, base=None, **kwargs):
57n/a # XXX Support passing in submodule names--load (and cache) them?
58n/a # That would clean up the test modules a bit more.
59n/a if base is None:
60n/a base = unittest.TestCase
61n/a elif not isinstance(base, type):
62n/a base = base[kind]
63n/a name = '{}_{}'.format(kind, cls.__name__)
64n/a bases = (cls, base)
65n/a specialized = types.new_class(name, bases)
66n/a specialized.__module__ = cls.__module__
67n/a specialized._NAME = cls.__name__
68n/a specialized._KIND = kind
69n/a for attr, values in kwargs.items():
70n/a value = values[kind]
71n/a setattr(specialized, attr, value)
72n/a return specialized
73n/a
74n/a
75n/adef split_frozen(cls, base=None, **kwargs):
76n/a frozen = specialize_class(cls, 'Frozen', base, **kwargs)
77n/a source = specialize_class(cls, 'Source', base, **kwargs)
78n/a return frozen, source
79n/a
80n/a
81n/adef test_both(test_class, base=None, **kwargs):
82n/a return split_frozen(test_class, base, **kwargs)
83n/a
84n/a
85n/aCASE_INSENSITIVE_FS = True
86n/a# Windows is the only OS that is *always* case-insensitive
87n/a# (OS X *can* be case-sensitive).
88n/aif sys.platform not in ('win32', 'cygwin'):
89n/a changed_name = __file__.upper()
90n/a if changed_name == __file__:
91n/a changed_name = __file__.lower()
92n/a if not os.path.exists(changed_name):
93n/a CASE_INSENSITIVE_FS = False
94n/a
95n/asource_importlib = import_importlib('importlib')['Source']
96n/a__import__ = {'Frozen': staticmethod(builtins.__import__),
97n/a 'Source': staticmethod(source_importlib.__import__)}
98n/a
99n/a
100n/adef case_insensitive_tests(test):
101n/a """Class decorator that nullifies tests requiring a case-insensitive
102n/a file system."""
103n/a return unittest.skipIf(not CASE_INSENSITIVE_FS,
104n/a "requires a case-insensitive filesystem")(test)
105n/a
106n/a
107n/adef submodule(parent, name, pkg_dir, content=''):
108n/a path = os.path.join(pkg_dir, name + '.py')
109n/a with open(path, 'w') as subfile:
110n/a subfile.write(content)
111n/a return '{}.{}'.format(parent, name), path
112n/a
113n/a
114n/a@contextlib.contextmanager
115n/adef uncache(*names):
116n/a """Uncache a module from sys.modules.
117n/a
118n/a A basic sanity check is performed to prevent uncaching modules that either
119n/a cannot/shouldn't be uncached.
120n/a
121n/a """
122n/a for name in names:
123n/a if name in ('sys', 'marshal', 'imp'):
124n/a raise ValueError(
125n/a "cannot uncache {0}".format(name))
126n/a try:
127n/a del sys.modules[name]
128n/a except KeyError:
129n/a pass
130n/a try:
131n/a yield
132n/a finally:
133n/a for name in names:
134n/a try:
135n/a del sys.modules[name]
136n/a except KeyError:
137n/a pass
138n/a
139n/a
140n/a@contextlib.contextmanager
141n/adef temp_module(name, content='', *, pkg=False):
142n/a conflicts = [n for n in sys.modules if n.partition('.')[0] == name]
143n/a with support.temp_cwd(None) as cwd:
144n/a with uncache(name, *conflicts):
145n/a with support.DirsOnSysPath(cwd):
146n/a invalidate_caches()
147n/a
148n/a location = os.path.join(cwd, name)
149n/a if pkg:
150n/a modpath = os.path.join(location, '__init__.py')
151n/a os.mkdir(name)
152n/a else:
153n/a modpath = location + '.py'
154n/a if content is None:
155n/a # Make sure the module file gets created.
156n/a content = ''
157n/a if content is not None:
158n/a # not a namespace package
159n/a with open(modpath, 'w') as modfile:
160n/a modfile.write(content)
161n/a yield location
162n/a
163n/a
164n/a@contextlib.contextmanager
165n/adef import_state(**kwargs):
166n/a """Context manager to manage the various importers and stored state in the
167n/a sys module.
168n/a
169n/a The 'modules' attribute is not supported as the interpreter state stores a
170n/a pointer to the dict that the interpreter uses internally;
171n/a reassigning to sys.modules does not have the desired effect.
172n/a
173n/a """
174n/a originals = {}
175n/a try:
176n/a for attr, default in (('meta_path', []), ('path', []),
177n/a ('path_hooks', []),
178n/a ('path_importer_cache', {})):
179n/a originals[attr] = getattr(sys, attr)
180n/a if attr in kwargs:
181n/a new_value = kwargs[attr]
182n/a del kwargs[attr]
183n/a else:
184n/a new_value = default
185n/a setattr(sys, attr, new_value)
186n/a if len(kwargs):
187n/a raise ValueError(
188n/a 'unrecognized arguments: {0}'.format(kwargs.keys()))
189n/a yield
190n/a finally:
191n/a for attr, value in originals.items():
192n/a setattr(sys, attr, value)
193n/a
194n/a
195n/aclass _ImporterMock:
196n/a
197n/a """Base class to help with creating importer mocks."""
198n/a
199n/a def __init__(self, *names, module_code={}):
200n/a self.modules = {}
201n/a self.module_code = {}
202n/a for name in names:
203n/a if not name.endswith('.__init__'):
204n/a import_name = name
205n/a else:
206n/a import_name = name[:-len('.__init__')]
207n/a if '.' not in name:
208n/a package = None
209n/a elif import_name == name:
210n/a package = name.rsplit('.', 1)[0]
211n/a else:
212n/a package = import_name
213n/a module = types.ModuleType(import_name)
214n/a module.__loader__ = self
215n/a module.__file__ = '<mock __file__>'
216n/a module.__package__ = package
217n/a module.attr = name
218n/a if import_name != name:
219n/a module.__path__ = ['<mock __path__>']
220n/a self.modules[import_name] = module
221n/a if import_name in module_code:
222n/a self.module_code[import_name] = module_code[import_name]
223n/a
224n/a def __getitem__(self, name):
225n/a return self.modules[name]
226n/a
227n/a def __enter__(self):
228n/a self._uncache = uncache(*self.modules.keys())
229n/a self._uncache.__enter__()
230n/a return self
231n/a
232n/a def __exit__(self, *exc_info):
233n/a self._uncache.__exit__(None, None, None)
234n/a
235n/a
236n/aclass mock_modules(_ImporterMock):
237n/a
238n/a """Importer mock using PEP 302 APIs."""
239n/a
240n/a def find_module(self, fullname, path=None):
241n/a if fullname not in self.modules:
242n/a return None
243n/a else:
244n/a return self
245n/a
246n/a def load_module(self, fullname):
247n/a if fullname not in self.modules:
248n/a raise ImportError
249n/a else:
250n/a sys.modules[fullname] = self.modules[fullname]
251n/a if fullname in self.module_code:
252n/a try:
253n/a self.module_code[fullname]()
254n/a except Exception:
255n/a del sys.modules[fullname]
256n/a raise
257n/a return self.modules[fullname]
258n/a
259n/a
260n/aclass mock_spec(_ImporterMock):
261n/a
262n/a """Importer mock using PEP 451 APIs."""
263n/a
264n/a def find_spec(self, fullname, path=None, parent=None):
265n/a try:
266n/a module = self.modules[fullname]
267n/a except KeyError:
268n/a return None
269n/a spec = util.spec_from_file_location(
270n/a fullname, module.__file__, loader=self,
271n/a submodule_search_locations=getattr(module, '__path__', None))
272n/a return spec
273n/a
274n/a def create_module(self, spec):
275n/a if spec.name not in self.modules:
276n/a raise ImportError
277n/a return self.modules[spec.name]
278n/a
279n/a def exec_module(self, module):
280n/a try:
281n/a self.module_code[module.__spec__.name]()
282n/a except KeyError:
283n/a pass
284n/a
285n/a
286n/adef writes_bytecode_files(fxn):
287n/a """Decorator to protect sys.dont_write_bytecode from mutation and to skip
288n/a tests that require it to be set to False."""
289n/a if sys.dont_write_bytecode:
290n/a return lambda *args, **kwargs: None
291n/a @functools.wraps(fxn)
292n/a def wrapper(*args, **kwargs):
293n/a original = sys.dont_write_bytecode
294n/a sys.dont_write_bytecode = False
295n/a try:
296n/a to_return = fxn(*args, **kwargs)
297n/a finally:
298n/a sys.dont_write_bytecode = original
299n/a return to_return
300n/a return wrapper
301n/a
302n/a
303n/adef ensure_bytecode_path(bytecode_path):
304n/a """Ensure that the __pycache__ directory for PEP 3147 pyc file exists.
305n/a
306n/a :param bytecode_path: File system path to PEP 3147 pyc file.
307n/a """
308n/a try:
309n/a os.mkdir(os.path.dirname(bytecode_path))
310n/a except OSError as error:
311n/a if error.errno != errno.EEXIST:
312n/a raise
313n/a
314n/a
315n/a@contextlib.contextmanager
316n/adef create_modules(*names):
317n/a """Temporarily create each named module with an attribute (named 'attr')
318n/a that contains the name passed into the context manager that caused the
319n/a creation of the module.
320n/a
321n/a All files are created in a temporary directory returned by
322n/a tempfile.mkdtemp(). This directory is inserted at the beginning of
323n/a sys.path. When the context manager exits all created files (source and
324n/a bytecode) are explicitly deleted.
325n/a
326n/a No magic is performed when creating packages! This means that if you create
327n/a a module within a package you must also create the package's __init__ as
328n/a well.
329n/a
330n/a """
331n/a source = 'attr = {0!r}'
332n/a created_paths = []
333n/a mapping = {}
334n/a state_manager = None
335n/a uncache_manager = None
336n/a try:
337n/a temp_dir = tempfile.mkdtemp()
338n/a mapping['.root'] = temp_dir
339n/a import_names = set()
340n/a for name in names:
341n/a if not name.endswith('__init__'):
342n/a import_name = name
343n/a else:
344n/a import_name = name[:-len('.__init__')]
345n/a import_names.add(import_name)
346n/a if import_name in sys.modules:
347n/a del sys.modules[import_name]
348n/a name_parts = name.split('.')
349n/a file_path = temp_dir
350n/a for directory in name_parts[:-1]:
351n/a file_path = os.path.join(file_path, directory)
352n/a if not os.path.exists(file_path):
353n/a os.mkdir(file_path)
354n/a created_paths.append(file_path)
355n/a file_path = os.path.join(file_path, name_parts[-1] + '.py')
356n/a with open(file_path, 'w') as file:
357n/a file.write(source.format(name))
358n/a created_paths.append(file_path)
359n/a mapping[name] = file_path
360n/a uncache_manager = uncache(*import_names)
361n/a uncache_manager.__enter__()
362n/a state_manager = import_state(path=[temp_dir])
363n/a state_manager.__enter__()
364n/a yield mapping
365n/a finally:
366n/a if state_manager is not None:
367n/a state_manager.__exit__(None, None, None)
368n/a if uncache_manager is not None:
369n/a uncache_manager.__exit__(None, None, None)
370n/a support.rmtree(temp_dir)
371n/a
372n/a
373n/adef mock_path_hook(*entries, importer):
374n/a """A mock sys.path_hooks entry."""
375n/a def hook(entry):
376n/a if entry not in entries:
377n/a raise ImportError
378n/a return importer
379n/a return hook
380n/a
381n/a
382n/aclass CASEOKTestBase:
383n/a
384n/a def caseok_env_changed(self, *, should_exist):
385n/a possibilities = b'PYTHONCASEOK', 'PYTHONCASEOK'
386n/a if any(x in self.importlib._bootstrap_external._os.environ
387n/a for x in possibilities) != should_exist:
388n/a self.skipTest('os.environ changes not reflected in _os.environ')