blob: 0c0c84c310bd3ed6d67c48bed5922ebcce3e44d3 [file] [log] [blame]
Brett Cannon23cbd8a2009-01-18 00:24:28 +00001from contextlib import contextmanager
Brett Cannon23cbd8a2009-01-18 00:24:28 +00002import imp
3import os.path
Brett Cannona3d056e2009-04-02 05:17:54 +00004from test import support
Brett Cannon534b2cd2009-02-07 02:06:43 +00005import unittest
Brett Cannon23cbd8a2009-01-18 00:24:28 +00006import sys
Brett Cannon23cbd8a2009-01-18 00:24:28 +00007
8
Brett Cannon4dc31932009-07-20 01:05:40 +00009CASE_INSENSITIVE_FS = True
10# Windows is the only OS that is *always* case-insensitive
11# (OS X *can* be case-sensitive).
12if sys.platform not in ('win32', 'cygwin'):
13 changed_name = __file__.upper()
14 if changed_name == __file__:
15 changed_name = __file__.lower()
16 if not os.path.exists(changed_name):
17 CASE_INSENSITIVE_FS = False
18
19
20def case_insensitive_tests(test):
Brett Cannon1262e7c2009-05-11 01:47:11 +000021 """Class decorator that nullifies tests requiring a case-insensitive
Brett Cannon2c5c79c2009-01-18 06:55:05 +000022 file system."""
Brett Cannon4dc31932009-07-20 01:05:40 +000023 return unittest.skipIf(not CASE_INSENSITIVE_FS,
24 "requires a case-insensitive filesystem")(test)
Brett Cannon2c5c79c2009-01-18 06:55:05 +000025
26
Brett Cannon23cbd8a2009-01-18 00:24:28 +000027@contextmanager
28def uncache(*names):
29 """Uncache a module from sys.modules.
30
31 A basic sanity check is performed to prevent uncaching modules that either
32 cannot/shouldn't be uncached.
33
34 """
35 for name in names:
36 if name in ('sys', 'marshal', 'imp'):
37 raise ValueError(
38 "cannot uncache {0} as it will break _importlib".format(name))
39 try:
40 del sys.modules[name]
41 except KeyError:
42 pass
43 try:
44 yield
45 finally:
46 for name in names:
47 try:
48 del sys.modules[name]
49 except KeyError:
50 pass
51
52@contextmanager
53def import_state(**kwargs):
54 """Context manager to manage the various importers and stored state in the
55 sys module.
56
57 The 'modules' attribute is not supported as the interpreter state stores a
58 pointer to the dict that the interpreter uses internally;
59 reassigning to sys.modules does not have the desired effect.
60
61 """
62 originals = {}
63 try:
64 for attr, default in (('meta_path', []), ('path', []),
65 ('path_hooks', []),
66 ('path_importer_cache', {})):
67 originals[attr] = getattr(sys, attr)
68 if attr in kwargs:
69 new_value = kwargs[attr]
70 del kwargs[attr]
71 else:
72 new_value = default
73 setattr(sys, attr, new_value)
74 if len(kwargs):
75 raise ValueError(
76 'unrecognized arguments: {0}'.format(kwargs.keys()))
77 yield
78 finally:
79 for attr, value in originals.items():
80 setattr(sys, attr, value)
81
82
Brett Cannon23cbd8a2009-01-18 00:24:28 +000083class mock_modules:
84
85 """A mock importer/loader."""
86
87 def __init__(self, *names):
88 self.modules = {}
89 for name in names:
90 if not name.endswith('.__init__'):
91 import_name = name
92 else:
93 import_name = name[:-len('.__init__')]
94 if '.' not in name:
95 package = None
96 elif import_name == name:
97 package = name.rsplit('.', 1)[0]
98 else:
99 package = import_name
100 module = imp.new_module(import_name)
101 module.__loader__ = self
102 module.__file__ = '<mock __file__>'
103 module.__package__ = package
104 module.attr = name
105 if import_name != name:
106 module.__path__ = ['<mock __path__>']
107 self.modules[import_name] = module
108
109 def __getitem__(self, name):
110 return self.modules[name]
111
112 def find_module(self, fullname, path=None):
113 if fullname not in self.modules:
114 return None
115 else:
116 return self
117
118 def load_module(self, fullname):
119 if fullname not in self.modules:
120 raise ImportError
121 else:
122 sys.modules[fullname] = self.modules[fullname]
123 return self.modules[fullname]
124
125 def __enter__(self):
126 self._uncache = uncache(*self.modules.keys())
127 self._uncache.__enter__()
128 return self
129
130 def __exit__(self, *exc_info):
131 self._uncache.__exit__(None, None, None)