blob: 920448e50f3202302a1048022b8444d97f5f8b02 [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001"""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.
6import sys, types
7import 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.
20filters = []
21defaultaction = "default"
22onceregistry = {}
23
24def 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", ".pyo")):
50 filename = filename[:-1]
51 elif fnl.endswith("$py.class"):
52 filename = filename[:-9] + '.py'
53 else:
54 if module == "__main__":
55 try:
56 filename = sys.argv[0]
57 except AttributeError:
58 # embedded interpreters don't have sys.argv, see bug #839151
59 filename = '__main__'
60 if not filename:
61 filename = module
62 registry = globals.setdefault("__warningregistry__", {})
63 warn_explicit(message, category, filename, lineno, module, registry,
64 globals)
65
66def warn_explicit(message, category, filename, lineno,
67 module=None, registry=None, module_globals=None):
68 if module is None:
69 module = filename or "<unknown>"
70 if module[-3:].lower() == ".py":
71 module = module[:-3] # XXX What about leading pathname?
72 if registry is None:
73 registry = {}
74 if isinstance(message, Warning):
75 text = str(message)
76 category = message.__class__
77 else:
78 text = message
79 message = category(message)
80 key = (text, category, lineno)
81 # Quick test for common case
82 if registry.get(key):
83 return
84 # Search the filters
85 for item in filters:
86 action, msg, cat, mod, ln = item
87 if ((msg is None or msg.match(text)) and
88 issubclass(category, cat) and
89 (mod is None or mod.match(module)) and
90 (ln == 0 or lineno == ln)):
91 break
92 else:
93 action = defaultaction
94 # Early exit actions
95 if action == "ignore":
96 registry[key] = 1
97 return
98
99 # Prime the linecache for formatting, in case the
100 # "file" is actually in a zipfile or something.
101 linecache.getlines(filename, module_globals)
102
103 if action == "error":
104 raise message
105 # Other actions
106 if action == "once":
107 registry[key] = 1
108 oncekey = (text, category)
109 if onceregistry.get(oncekey):
110 return
111 onceregistry[oncekey] = 1
112 elif action == "always":
113 pass
114 elif action == "module":
115 registry[key] = 1
116 altkey = (text, category, 0)
117 if registry.get(altkey):
118 return
119 registry[altkey] = 1
120 elif action == "default":
121 registry[key] = 1
122 else:
123 # Unrecognized actions are errors
124 raise RuntimeError(
125 "Unrecognized action (%r) in warnings.filters:\n %s" %
126 (action, item))
127 # Print message and context
128 showwarning(message, category, filename, lineno)
129
130def showwarning(message, category, filename, lineno, file=None):
131 """Hook to write a warning to a file; replace if you like."""
132 if file is None:
133 file = sys.stderr
134 try:
135 file.write(formatwarning(message, category, filename, lineno))
136 except IOError:
137 pass # the file (probably stderr) is invalid - this warning gets lost.
138
139def formatwarning(message, category, filename, lineno):
140 """Function to format a warning the standard way."""
141 s = "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message)
142 line = linecache.getline(filename, lineno).strip()
143 if line:
144 s = s + " " + line + "\n"
145 return s
146
147def filterwarnings(action, message="", category=Warning, module="", lineno=0,
148 append=0):
149 """Insert an entry into the list of warnings filters (at the front).
150
151 Use assertions to check that all arguments have the right type."""
152 import re
153 assert action in ("error", "ignore", "always", "default", "module",
154 "once"), "invalid action: %r" % (action,)
155 assert isinstance(message, basestring), "message must be a string"
156 assert isinstance(category, (type, types.ClassType)), \
157 "category must be a class"
158 assert issubclass(category, Warning), "category must be a Warning subclass"
159 assert isinstance(module, basestring), "module must be a string"
160 assert isinstance(lineno, int) and lineno >= 0, \
161 "lineno must be an int >= 0"
162 item = (action, re.compile(message, re.I), category,
163 re.compile(module), lineno)
164 if append:
165 filters.append(item)
166 else:
167 filters.insert(0, item)
168
169def simplefilter(action, category=Warning, lineno=0, append=0):
170 """Insert a simple entry into the list of warnings filters (at the front).
171
172 A simple filter matches all modules and messages.
173 """
174 assert action in ("error", "ignore", "always", "default", "module",
175 "once"), "invalid action: %r" % (action,)
176 assert isinstance(lineno, int) and lineno >= 0, \
177 "lineno must be an int >= 0"
178 item = (action, None, category, None, lineno)
179 if append:
180 filters.append(item)
181 else:
182 filters.insert(0, item)
183
184def resetwarnings():
185 """Clear the list of warning filters, so that no filters are active."""
186 filters[:] = []
187
188class _OptionError(Exception):
189 """Exception used by option processing helpers."""
190 pass
191
192# Helper to process -W options passed via sys.warnoptions
193def _processoptions(args):
194 for arg in args:
195 try:
196 _setoption(arg)
197 except _OptionError, msg:
198 print >>sys.stderr, "Invalid -W option ignored:", msg
199
200# Helper for _processoptions()
201def _setoption(arg):
202 import re
203 parts = arg.split(':')
204 if len(parts) > 5:
205 raise _OptionError("too many fields (max 5): %r" % (arg,))
206 while len(parts) < 5:
207 parts.append('')
208 action, message, category, module, lineno = [s.strip()
209 for s in parts]
210 action = _getaction(action)
211 message = re.escape(message)
212 category = _getcategory(category)
213 module = re.escape(module)
214 if module:
215 module = module + '$'
216 if lineno:
217 try:
218 lineno = int(lineno)
219 if lineno < 0:
220 raise ValueError
221 except (ValueError, OverflowError):
222 raise _OptionError("invalid lineno %r" % (lineno,))
223 else:
224 lineno = 0
225 filterwarnings(action, message, category, module, lineno)
226
227# Helper for _setoption()
228def _getaction(action):
229 if not action:
230 return "default"
231 if action == "all": return "always" # Alias
232 for a in ('default', 'always', 'ignore', 'module', 'once', 'error'):
233 if a.startswith(action):
234 return a
235 raise _OptionError("invalid action: %r" % (action,))
236
237# Helper for _setoption()
238def _getcategory(category):
239 import re
240 if not category:
241 return Warning
242 if re.match("^[a-zA-Z0-9_]+$", category):
243 try:
244 cat = eval(category)
245 except NameError:
246 raise _OptionError("unknown warning category: %r" % (category,))
247 else:
248 i = category.rfind(".")
249 module = category[:i]
250 klass = category[i+1:]
251 try:
252 m = __import__(module, None, None, [klass])
253 except ImportError:
254 raise _OptionError("invalid module name: %r" % (module,))
255 try:
256 cat = getattr(m, klass)
257 except AttributeError:
258 raise _OptionError("unknown warning category: %r" % (category,))
259 if not issubclass(cat, Warning):
260 raise _OptionError("invalid warning category: %r" % (category,))
261 return cat
262
263# Module initialization
264_processoptions(sys.warnoptions)
265simplefilter("ignore", category=PendingDeprecationWarning, append=1)
266simplefilter("ignore", category=ImportWarning, append=1)