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