blob: 243f215065dcc929c3ab00533820d88b1ce0e7da [file] [log] [blame]
Guido van Rossum2a862c62000-12-15 21:59:53 +00001"""Python part of the warnings subsystem."""
2
3import sys, re, types
4
Skip Montanaro40fc1602001-03-01 04:27:19 +00005__all__ = ["warn", "showwarning", "formatwarning", "filterwarnings",
6 "resetwarnings"]
7
Guido van Rossum2a862c62000-12-15 21:59:53 +00008defaultaction = "default"
9filters = []
10onceregistry = {}
11
12def warn(message, category=None, stacklevel=1):
13 """Issue a warning, or maybe ignore it or raise an exception."""
14 # Check category argument
15 if category is None:
16 category = UserWarning
17 assert issubclass(category, Warning)
18 # Get context information
19 try:
20 caller = sys._getframe(stacklevel)
21 except ValueError:
22 globals = sys.__dict__
23 lineno = 1
24 else:
25 globals = caller.f_globals
26 lineno = caller.f_lineno
27 module = globals['__name__']
28 filename = globals.get('__file__')
29 if filename:
30 fnl = filename.lower()
31 if fnl.endswith(".pyc") or fnl.endswith(".pyo"):
32 filename = filename[:-1]
33 else:
34 if module == "__main__":
35 filename = sys.argv[0]
36 if not filename:
37 filename = module
Guido van Rossum2a862c62000-12-15 21:59:53 +000038 registry = globals.setdefault("__warningregistry__", {})
Guido van Rossum9e263182001-02-28 21:43:40 +000039 warn_explicit(message, category, filename, lineno, module, registry)
40
41def warn_explicit(message, category, filename, lineno,
42 module=None, registry=None):
43 if module is None:
44 module = filename
45 if module[-3:].lower() == ".py":
46 module = module[:-3] # XXX What about leading pathname?
47 if registry is None:
48 registry = {}
Guido van Rossum2a862c62000-12-15 21:59:53 +000049 key = (message, category, lineno)
Guido van Rossum3756fa32001-02-28 22:26:36 +000050 # Quick test for common case
Guido van Rossum2a862c62000-12-15 21:59:53 +000051 if registry.get(key):
52 return
53 # Search the filters
54 for item in filters:
55 action, msg, cat, mod, ln = item
56 if (msg.match(message) and
57 issubclass(category, cat) and
58 mod.match(module) and
59 (ln == 0 or lineno == ln)):
60 break
61 else:
62 action = defaultaction
63 # Early exit actions
64 if action == "ignore":
65 registry[key] = 1
66 return
67 if action == "error":
68 raise category(message)
69 # Other actions
70 if action == "once":
71 registry[key] = 1
72 oncekey = (message, category)
73 if onceregistry.get(oncekey):
74 return
75 onceregistry[oncekey] = 1
76 elif action == "always":
77 pass
78 elif action == "module":
79 registry[key] = 1
80 altkey = (message, category, 0)
81 if registry.get(altkey):
82 return
83 registry[altkey] = 1
84 elif action == "default":
85 registry[key] = 1
86 else:
87 # Unrecognized actions are errors
88 raise RuntimeError(
89 "Unrecognized action (%s) in warnings.filters:\n %s" %
90 (`action`, str(item)))
91 # Print message and context
92 showwarning(message, category, filename, lineno)
93
94def showwarning(message, category, filename, lineno, file=None):
95 """Hook to write a warning to a file; replace if you like."""
96 if file is None:
97 file = sys.stderr
98 file.write(formatwarning(message, category, filename, lineno))
99
100def formatwarning(message, category, filename, lineno):
Guido van Rossum9464a7d2001-01-14 14:08:40 +0000101 """Function to format a warning the standard way."""
Guido van Rossum2a862c62000-12-15 21:59:53 +0000102 import linecache
103 s = "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message)
104 line = linecache.getline(filename, lineno).strip()
105 if line:
106 s = s + " " + line + "\n"
107 return s
108
Guido van Rossum9464a7d2001-01-14 14:08:40 +0000109def filterwarnings(action, message="", category=Warning, module="", lineno=0,
110 append=0):
Guido van Rossum2a862c62000-12-15 21:59:53 +0000111 """Insert an entry into the list of warnings filters (at the front).
112
113 Use assertions to check that all arguments have the right type."""
114 assert action in ("error", "ignore", "always", "default", "module",
115 "once"), "invalid action: %s" % `action`
116 assert isinstance(message, types.StringType), "message must be a string"
117 assert isinstance(category, types.ClassType), "category must be a class"
118 assert issubclass(category, Warning), "category must be a Warning subclass"
119 assert type(module) is types.StringType, "module must be a string"
120 assert type(lineno) is types.IntType and lineno >= 0, \
121 "lineno must be an int >= 0"
Guido van Rossum9464a7d2001-01-14 14:08:40 +0000122 item = (action, re.compile(message, re.I), category,
123 re.compile(module), lineno)
124 if append:
125 filters.append(item)
126 else:
127 filters.insert(0, item)
Guido van Rossum2a862c62000-12-15 21:59:53 +0000128
129def resetwarnings():
130 """Reset the list of warnings filters to its default state."""
131 filters[:] = []
132
133class _OptionError(Exception):
134 """Exception used by option processing helpers."""
135 pass
136
137# Helper to process -W options passed via sys.warnoptions
138def _processoptions(args):
139 for arg in args:
140 try:
141 _setoption(arg)
142 except _OptionError, msg:
143 print >>sys.stderr, "Invalid -W option ignored:", msg
144
145# Helper for _processoptions()
146def _setoption(arg):
Tim Peterse1190062001-01-15 03:34:38 +0000147 parts = arg.split(':')
148 if len(parts) > 5:
149 raise _OptionError("too many fields (max 5): %s" % `arg`)
150 while len(parts) < 5:
151 parts.append('')
152 action, message, category, module, lineno = [s.strip()
153 for s in parts]
154 action = _getaction(action)
155 message = re.escape(message)
156 category = _getcategory(category)
157 module = re.escape(module)
158 if module:
159 module = module + '$'
160 if lineno:
161 try:
162 lineno = int(lineno)
163 if lineno < 0:
164 raise ValueError
165 except (ValueError, OverflowError):
166 raise _OptionError("invalid lineno %s" % `lineno`)
167 else:
168 lineno = 0
169 filterwarnings(action, message, category, module, lineno)
Guido van Rossum2a862c62000-12-15 21:59:53 +0000170
171# Helper for _setoption()
172def _getaction(action):
173 if not action:
174 return "default"
175 if action == "all": return "always" # Alias
176 for a in ['default', 'always', 'ignore', 'module', 'once', 'error']:
177 if a.startswith(action):
178 return a
179 raise _OptionError("invalid action: %s" % `action`)
180
181# Helper for _setoption()
182def _getcategory(category):
183 if not category:
184 return Warning
185 if re.match("^[a-zA-Z0-9_]+$", category):
186 try:
187 cat = eval(category)
Guido van Rossumd1db30b2000-12-19 03:04:50 +0000188 except NameError:
189 raise _OptionError("unknown warning category: %s" % `category`)
Guido van Rossum2a862c62000-12-15 21:59:53 +0000190 else:
191 i = category.rfind(".")
192 module = category[:i]
193 klass = category[i+1:]
Guido van Rossumd1db30b2000-12-19 03:04:50 +0000194 try:
195 m = __import__(module, None, None, [klass])
196 except ImportError:
197 raise _OptionError("invalid module name: %s" % `module`)
198 try:
199 cat = getattr(m, klass)
200 except AttributeError:
201 raise _OptionError("unknown warning category: %s" % `category`)
Guido van Rossum2a862c62000-12-15 21:59:53 +0000202 if (not isinstance(cat, types.ClassType) or
203 not issubclass(cat, Warning)):
204 raise _OptionError("invalid warning category: %s" % `category`)
205 return cat
206
207# Self-test
208def _test():
209 import getopt
210 testoptions = []
211 try:
212 opts, args = getopt.getopt(sys.argv[1:], "W:")
213 except getopt.error, msg:
214 print >>sys.stderr, msg
215 return
216 for o, a in opts:
217 testoptions.append(a)
218 try:
219 _processoptions(testoptions)
220 except _OptionError, msg:
221 print >>sys.stderr, msg
222 return
223 for item in filters: print item
224 hello = "hello world"
225 warn(hello); warn(hello); warn(hello); warn(hello)
226 warn(hello, UserWarning)
227 warn(hello, DeprecationWarning)
228 for i in range(3):
229 warn(hello)
230 filterwarnings("error", "", Warning, "", 0)
231 try:
232 warn(hello)
233 except Exception, msg:
234 print "Caught", msg.__class__.__name__ + ":", msg
235 else:
236 print "No exception"
237 resetwarnings()
238 try:
239 filterwarnings("booh", "", Warning, "", 0)
240 except Exception, msg:
241 print "Caught", msg.__class__.__name__ + ":", msg
242 else:
243 print "No exception"
244
245# Module initialization
246if __name__ == "__main__":
247 import __main__
248 sys.modules['warnings'] = __main__
249 _test()
250else:
251 _processoptions(sys.warnoptions)