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