blob: a894bf2ee0ee3ebbba1f2d42da3d2c381e6ddd68 [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__", {})
37 key = (message, category, lineno)
38 if registry.get(key):
39 return
40 # Search the filters
41 for item in filters:
42 action, msg, cat, mod, ln = item
43 if (msg.match(message) and
44 issubclass(category, cat) and
45 mod.match(module) and
46 (ln == 0 or lineno == ln)):
47 break
48 else:
49 action = defaultaction
50 # Early exit actions
51 if action == "ignore":
52 registry[key] = 1
53 return
54 if action == "error":
55 raise category(message)
56 # Other actions
57 if action == "once":
58 registry[key] = 1
59 oncekey = (message, category)
60 if onceregistry.get(oncekey):
61 return
62 onceregistry[oncekey] = 1
63 elif action == "always":
64 pass
65 elif action == "module":
66 registry[key] = 1
67 altkey = (message, category, 0)
68 if registry.get(altkey):
69 return
70 registry[altkey] = 1
71 elif action == "default":
72 registry[key] = 1
73 else:
74 # Unrecognized actions are errors
75 raise RuntimeError(
76 "Unrecognized action (%s) in warnings.filters:\n %s" %
77 (`action`, str(item)))
78 # Print message and context
79 showwarning(message, category, filename, lineno)
80
81def showwarning(message, category, filename, lineno, file=None):
82 """Hook to write a warning to a file; replace if you like."""
83 if file is None:
84 file = sys.stderr
85 file.write(formatwarning(message, category, filename, lineno))
86
87def formatwarning(message, category, filename, lineno):
88 """Hook to format a warning the standard way."""
89 import linecache
90 s = "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message)
91 line = linecache.getline(filename, lineno).strip()
92 if line:
93 s = s + " " + line + "\n"
94 return s
95
96def filterwarnings(action, message="", category=Warning, module="", lineno=0):
97 """Insert an entry into the list of warnings filters (at the front).
98
99 Use assertions to check that all arguments have the right type."""
100 assert action in ("error", "ignore", "always", "default", "module",
101 "once"), "invalid action: %s" % `action`
102 assert isinstance(message, types.StringType), "message must be a string"
103 assert isinstance(category, types.ClassType), "category must be a class"
104 assert issubclass(category, Warning), "category must be a Warning subclass"
105 assert type(module) is types.StringType, "module must be a string"
106 assert type(lineno) is types.IntType and lineno >= 0, \
107 "lineno must be an int >= 0"
108 filters.insert(0, (action, re.compile(message, re.I), category,
109 re.compile(module), lineno))
110
111def resetwarnings():
112 """Reset the list of warnings filters to its default state."""
113 filters[:] = []
114
115class _OptionError(Exception):
116 """Exception used by option processing helpers."""
117 pass
118
119# Helper to process -W options passed via sys.warnoptions
120def _processoptions(args):
121 for arg in args:
122 try:
123 _setoption(arg)
124 except _OptionError, msg:
125 print >>sys.stderr, "Invalid -W option ignored:", msg
126
127# Helper for _processoptions()
128def _setoption(arg):
129 parts = arg.split(':')
130 if len(parts) > 5:
131 raise _OptionError("unparsable -W option %s" % `arg`)
132 while len(parts) < 5:
133 parts.append('')
134 action, message, category, module, lineno = [s.strip()
135 for s in parts]
136 action = _getaction(action)
137 message = re.escape(message)
138 category = _getcategory(category)
139 module = re.escape(module)
140 if module:
141 module = module + '$'
142 if lineno:
143 try:
144 lineno = int(lineno)
145 if lineno < 0:
146 raise ValueError
147 except (ValueError, OverflowError):
148 raise _OptionError("invalid lineno %s" % `lineno`)
149 else:
150 lineno = 0
151 filterwarnings(action, message, category, module, lineno)
152
153# Helper for _setoption()
154def _getaction(action):
155 if not action:
156 return "default"
157 if action == "all": return "always" # Alias
158 for a in ['default', 'always', 'ignore', 'module', 'once', 'error']:
159 if a.startswith(action):
160 return a
161 raise _OptionError("invalid action: %s" % `action`)
162
163# Helper for _setoption()
164def _getcategory(category):
165 if not category:
166 return Warning
167 if re.match("^[a-zA-Z0-9_]+$", category):
168 try:
169 cat = eval(category)
Guido van Rossumd1db30b2000-12-19 03:04:50 +0000170 except NameError:
171 raise _OptionError("unknown warning category: %s" % `category`)
Guido van Rossum2a862c62000-12-15 21:59:53 +0000172 else:
173 i = category.rfind(".")
174 module = category[:i]
175 klass = category[i+1:]
Guido van Rossumd1db30b2000-12-19 03:04:50 +0000176 try:
177 m = __import__(module, None, None, [klass])
178 except ImportError:
179 raise _OptionError("invalid module name: %s" % `module`)
180 try:
181 cat = getattr(m, klass)
182 except AttributeError:
183 raise _OptionError("unknown warning category: %s" % `category`)
Guido van Rossum2a862c62000-12-15 21:59:53 +0000184 if (not isinstance(cat, types.ClassType) or
185 not issubclass(cat, Warning)):
186 raise _OptionError("invalid warning category: %s" % `category`)
187 return cat
188
189# Self-test
190def _test():
191 import getopt
192 testoptions = []
193 try:
194 opts, args = getopt.getopt(sys.argv[1:], "W:")
195 except getopt.error, msg:
196 print >>sys.stderr, msg
197 return
198 for o, a in opts:
199 testoptions.append(a)
200 try:
201 _processoptions(testoptions)
202 except _OptionError, msg:
203 print >>sys.stderr, msg
204 return
205 for item in filters: print item
206 hello = "hello world"
207 warn(hello); warn(hello); warn(hello); warn(hello)
208 warn(hello, UserWarning)
209 warn(hello, DeprecationWarning)
210 for i in range(3):
211 warn(hello)
212 filterwarnings("error", "", Warning, "", 0)
213 try:
214 warn(hello)
215 except Exception, msg:
216 print "Caught", msg.__class__.__name__ + ":", msg
217 else:
218 print "No exception"
219 resetwarnings()
220 try:
221 filterwarnings("booh", "", Warning, "", 0)
222 except Exception, msg:
223 print "Caught", msg.__class__.__name__ + ":", msg
224 else:
225 print "No exception"
226
227# Module initialization
228if __name__ == "__main__":
229 import __main__
230 sys.modules['warnings'] = __main__
231 _test()
232else:
233 _processoptions(sys.warnoptions)