Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | """Python part of the warnings subsystem.""" |
2 | ||
3 | # Note: function level imports should *not* be used | |
4 | # in this module as it may cause import lock deadlock. | |
5 | # See bug 683658. | |
6 | import sys, types | |
7 | import linecache | |
8 | ||
9 | __all__ = ["warn", "showwarning", "formatwarning", "filterwarnings", | |
10 | "resetwarnings"] | |
11 | ||
12 | # filters contains a sequence of filter 5-tuples | |
13 | # The components of the 5-tuple are: | |
14 | # - an action: error, ignore, always, default, module, or once | |
15 | # - a compiled regex that must match the warning message | |
16 | # - a class representing the warning category | |
17 | # - a compiled regex that must match the module that is being warned | |
18 | # - a line number for the line being warning, or 0 to mean any line | |
19 | # If either if the compiled regexs are None, match anything. | |
20 | filters = [] | |
21 | defaultaction = "default" | |
22 | onceregistry = {} | |
23 | ||
24 | def warn(message, category=None, stacklevel=1): | |
25 | """Issue a warning, or maybe ignore it or raise an exception.""" | |
26 | # Check if message is already a Warning object | |
27 | if isinstance(message, Warning): | |
28 | category = message.__class__ | |
29 | # Check category argument | |
30 | if category is None: | |
31 | category = UserWarning | |
32 | assert issubclass(category, Warning) | |
33 | # Get context information | |
34 | try: | |
35 | caller = sys._getframe(stacklevel) | |
36 | except ValueError: | |
37 | globals = sys.__dict__ | |
38 | lineno = 1 | |
39 | else: | |
40 | globals = caller.f_globals | |
41 | lineno = caller.f_lineno | |
42 | if '__name__' in globals: | |
43 | module = globals['__name__'] | |
44 | else: | |
45 | module = "<string>" | |
46 | filename = globals.get('__file__') | |
47 | if filename: | |
48 | fnl = filename.lower() | |
49 | if fnl.endswith(".pyc") or fnl.endswith(".pyo"): | |
50 | filename = filename[:-1] | |
51 | else: | |
52 | if module == "__main__": | |
53 | try: | |
54 | filename = sys.argv[0] | |
55 | except AttributeError: | |
56 | # embedded interpreters don't have sys.argv, see bug #839151 | |
57 | filename = '__main__' | |
58 | if not filename: | |
59 | filename = module | |
60 | registry = globals.setdefault("__warningregistry__", {}) | |
61 | warn_explicit(message, category, filename, lineno, module, registry) | |
62 | ||
63 | def warn_explicit(message, category, filename, lineno, | |
64 | module=None, registry=None): | |
65 | if module is None: | |
66 | module = filename | |
67 | if module[-3:].lower() == ".py": | |
68 | module = module[:-3] # XXX What about leading pathname? | |
69 | if registry is None: | |
70 | registry = {} | |
71 | if isinstance(message, Warning): | |
72 | text = str(message) | |
73 | category = message.__class__ | |
74 | else: | |
75 | text = message | |
76 | message = category(message) | |
77 | key = (text, category, lineno) | |
78 | # Quick test for common case | |
79 | if registry.get(key): | |
80 | return | |
81 | # Search the filters | |
82 | for item in filters: | |
83 | action, msg, cat, mod, ln = item | |
84 | if ((msg is None or msg.match(text)) and | |
85 | issubclass(category, cat) and | |
86 | (mod is None or mod.match(module)) and | |
87 | (ln == 0 or lineno == ln)): | |
88 | break | |
89 | else: | |
90 | action = defaultaction | |
91 | # Early exit actions | |
92 | if action == "ignore": | |
93 | registry[key] = 1 | |
94 | return | |
95 | if action == "error": | |
96 | raise message | |
97 | # Other actions | |
98 | if action == "once": | |
99 | registry[key] = 1 | |
100 | oncekey = (text, category) | |
101 | if onceregistry.get(oncekey): | |
102 | return | |
103 | onceregistry[oncekey] = 1 | |
104 | elif action == "always": | |
105 | pass | |
106 | elif action == "module": | |
107 | registry[key] = 1 | |
108 | altkey = (text, category, 0) | |
109 | if registry.get(altkey): | |
110 | return | |
111 | registry[altkey] = 1 | |
112 | elif action == "default": | |
113 | registry[key] = 1 | |
114 | else: | |
115 | # Unrecognized actions are errors | |
116 | raise RuntimeError( | |
117 | "Unrecognized action (%r) in warnings.filters:\n %s" % | |
118 | (action, item)) | |
119 | # Print message and context | |
120 | showwarning(message, category, filename, lineno) | |
121 | ||
122 | def showwarning(message, category, filename, lineno, file=None): | |
123 | """Hook to write a warning to a file; replace if you like.""" | |
124 | if file is None: | |
125 | file = sys.stderr | |
126 | try: | |
127 | file.write(formatwarning(message, category, filename, lineno)) | |
128 | except IOError: | |
129 | pass # the file (probably stderr) is invalid - this warning gets lost. | |
130 | ||
131 | def formatwarning(message, category, filename, lineno): | |
132 | """Function to format a warning the standard way.""" | |
133 | s = "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message) | |
134 | line = linecache.getline(filename, lineno).strip() | |
135 | if line: | |
136 | s = s + " " + line + "\n" | |
137 | return s | |
138 | ||
139 | def filterwarnings(action, message="", category=Warning, module="", lineno=0, | |
140 | append=0): | |
141 | """Insert an entry into the list of warnings filters (at the front). | |
142 | ||
143 | Use assertions to check that all arguments have the right type.""" | |
144 | import re | |
145 | assert action in ("error", "ignore", "always", "default", "module", | |
146 | "once"), "invalid action: %r" % (action,) | |
147 | assert isinstance(message, basestring), "message must be a string" | |
148 | assert isinstance(category, types.ClassType), "category must be a class" | |
149 | assert issubclass(category, Warning), "category must be a Warning subclass" | |
150 | assert isinstance(module, basestring), "module must be a string" | |
151 | assert isinstance(lineno, int) and lineno >= 0, \ | |
152 | "lineno must be an int >= 0" | |
153 | item = (action, re.compile(message, re.I), category, | |
154 | re.compile(module), lineno) | |
155 | if append: | |
156 | filters.append(item) | |
157 | else: | |
158 | filters.insert(0, item) | |
159 | ||
160 | def simplefilter(action, category=Warning, lineno=0, append=0): | |
161 | """Insert a simple entry into the list of warnings filters (at the front). | |
162 | ||
163 | A simple filter matches all modules and messages. | |
164 | """ | |
165 | assert action in ("error", "ignore", "always", "default", "module", | |
166 | "once"), "invalid action: %r" % (action,) | |
167 | assert isinstance(lineno, int) and lineno >= 0, \ | |
168 | "lineno must be an int >= 0" | |
169 | item = (action, None, category, None, lineno) | |
170 | if append: | |
171 | filters.append(item) | |
172 | else: | |
173 | filters.insert(0, item) | |
174 | ||
175 | def resetwarnings(): | |
176 | """Clear the list of warning filters, so that no filters are active.""" | |
177 | filters[:] = [] | |
178 | ||
179 | class _OptionError(Exception): | |
180 | """Exception used by option processing helpers.""" | |
181 | pass | |
182 | ||
183 | # Helper to process -W options passed via sys.warnoptions | |
184 | def _processoptions(args): | |
185 | for arg in args: | |
186 | try: | |
187 | _setoption(arg) | |
188 | except _OptionError, msg: | |
189 | print >>sys.stderr, "Invalid -W option ignored:", msg | |
190 | ||
191 | # Helper for _processoptions() | |
192 | def _setoption(arg): | |
193 | import re | |
194 | parts = arg.split(':') | |
195 | if len(parts) > 5: | |
196 | raise _OptionError("too many fields (max 5): %r" % (arg,)) | |
197 | while len(parts) < 5: | |
198 | parts.append('') | |
199 | action, message, category, module, lineno = [s.strip() | |
200 | for s in parts] | |
201 | action = _getaction(action) | |
202 | message = re.escape(message) | |
203 | category = _getcategory(category) | |
204 | module = re.escape(module) | |
205 | if module: | |
206 | module = module + '$' | |
207 | if lineno: | |
208 | try: | |
209 | lineno = int(lineno) | |
210 | if lineno < 0: | |
211 | raise ValueError | |
212 | except (ValueError, OverflowError): | |
213 | raise _OptionError("invalid lineno %r" % (lineno,)) | |
214 | else: | |
215 | lineno = 0 | |
216 | filterwarnings(action, message, category, module, lineno) | |
217 | ||
218 | # Helper for _setoption() | |
219 | def _getaction(action): | |
220 | if not action: | |
221 | return "default" | |
222 | if action == "all": return "always" # Alias | |
223 | for a in ['default', 'always', 'ignore', 'module', 'once', 'error']: | |
224 | if a.startswith(action): | |
225 | return a | |
226 | raise _OptionError("invalid action: %r" % (action,)) | |
227 | ||
228 | # Helper for _setoption() | |
229 | def _getcategory(category): | |
230 | import re | |
231 | if not category: | |
232 | return Warning | |
233 | if re.match("^[a-zA-Z0-9_]+$", category): | |
234 | try: | |
235 | cat = eval(category) | |
236 | except NameError: | |
237 | raise _OptionError("unknown warning category: %r" % (category,)) | |
238 | else: | |
239 | i = category.rfind(".") | |
240 | module = category[:i] | |
241 | klass = category[i+1:] | |
242 | try: | |
243 | m = __import__(module, None, None, [klass]) | |
244 | except ImportError: | |
245 | raise _OptionError("invalid module name: %r" % (module,)) | |
246 | try: | |
247 | cat = getattr(m, klass) | |
248 | except AttributeError: | |
249 | raise _OptionError("unknown warning category: %r" % (category,)) | |
250 | if (not isinstance(cat, types.ClassType) or | |
251 | not issubclass(cat, Warning)): | |
252 | raise _OptionError("invalid warning category: %r" % (category,)) | |
253 | return cat | |
254 | ||
255 | # Module initialization | |
256 | _processoptions(sys.warnoptions) | |
257 | # XXX OverflowWarning should go away for Python 2.5. | |
258 | simplefilter("ignore", category=OverflowWarning, append=1) | |
259 | simplefilter("ignore", category=PendingDeprecationWarning, append=1) |