|
7 | 7 | import sys
|
8 | 8 | import types
|
9 | 9 |
|
10 |
| -__all__ = ["warn", "warn_explicit", "showwarning", |
11 |
| - "formatwarning", "filterwarnings", "simplefilter", |
| 10 | +__all__ = ["warn", "warn_explicit", "warn_explicit_with_fix", |
| 11 | + "showwarning", "showwarningwithfix", "formatwarning", |
| 12 | + "formatwarningwithfix", "filterwarnings", "simplefilter", |
12 | 13 | "resetwarnings", "catch_warnings"]
|
13 | 14 |
|
14 | 15 |
|
@@ -37,6 +38,21 @@ def _show_warning(message, category, filename, lineno, file=None, line=None):
|
37 | 38 | # triggered.
|
38 | 39 | showwarning = _show_warning
|
39 | 40 |
|
| 41 | +def _show_warning_with_fix(message, fix, category, filename, lineno, file=None, line=None): |
| 42 | + """Hook to write a warning to a file; replace if you like.""" |
| 43 | + if file is None: |
| 44 | + file = sys.stderr |
| 45 | + if file is None: |
| 46 | + # sys.stderr is None - warnings get lost |
| 47 | + return |
| 48 | + try: |
| 49 | + file.write(formatwarningwithfix(message, fix, category, filename, lineno, line)) |
| 50 | + except (IOError, UnicodeError): |
| 51 | + pass # the file (probably stderr) is invalid - this warning gets lost. |
| 52 | +# Keep a working version around in case the deprecation of the old API is |
| 53 | +# triggered. |
| 54 | +showwarningwithfix = _show_warning_with_fix |
| 55 | + |
40 | 56 | def formatwarning(message, category, filename, lineno, line=None):
|
41 | 57 | """Function to format a warning the standard way."""
|
42 | 58 | try:
|
@@ -64,6 +80,34 @@ def formatwarning(message, category, filename, lineno, line=None):
|
64 | 80 | s = "%s:%s" % (filename, s)
|
65 | 81 | return s
|
66 | 82 |
|
| 83 | +def formatwarningwithfix(message, fix, category, filename, lineno, line=None): |
| 84 | + """Function to format a warning the standard way with a fix.""" |
| 85 | + try: |
| 86 | + unicodetype = unicode |
| 87 | + except NameError: |
| 88 | + unicodetype = () |
| 89 | + try: |
| 90 | + message = str(message) |
| 91 | + fix = str(fix) |
| 92 | + except UnicodeEncodeError: |
| 93 | + pass |
| 94 | + s = "%s: %s: %s: %s\n" % (lineno, category.__name__, message, fix) |
| 95 | + line = linecache.getline(filename, lineno) if line is None else line |
| 96 | + if line: |
| 97 | + line = line.strip() |
| 98 | + if isinstance(s, unicodetype) and isinstance(line, str): |
| 99 | + line = unicode(line, 'latin1') |
| 100 | + s += " %s\n" % line |
| 101 | + if isinstance(s, unicodetype) and isinstance(filename, str): |
| 102 | + enc = sys.getfilesystemencoding() |
| 103 | + if enc: |
| 104 | + try: |
| 105 | + filename = unicode(filename, enc) |
| 106 | + except UnicodeDecodeError: |
| 107 | + pass |
| 108 | + s = "%s:%s" % (filename, s) |
| 109 | + return s |
| 110 | + |
67 | 111 | def filterwarnings(action, message="", category=Warning, module="", lineno=0,
|
68 | 112 | append=0):
|
69 | 113 | """Insert an entry into the list of warnings filters (at the front).
|
@@ -299,6 +343,71 @@ def warn_explicit(message, category, filename, lineno,
|
299 | 343 | # Print message and context
|
300 | 344 | showwarning(message, category, filename, lineno)
|
301 | 345 |
|
| 346 | +def warn_explicit_with_fix(message, fix, category, filename, lineno, |
| 347 | + module=None, registry=None, module_globals=None): |
| 348 | + lineno = int(lineno) |
| 349 | + if module is None: |
| 350 | + module = filename or "<unknown>" |
| 351 | + if module[-3:].lower() == ".py": |
| 352 | + module = module[:-3] # XXX What about leading pathname? |
| 353 | + if registry is None: |
| 354 | + registry = {} |
| 355 | + if isinstance(message, Warning): |
| 356 | + text = str(message) |
| 357 | + category = message.__class__ |
| 358 | + else: |
| 359 | + text = message |
| 360 | + message = category(message) |
| 361 | + key = (text, category, lineno) |
| 362 | + # Quick test for common case |
| 363 | + if registry.get(key): |
| 364 | + return |
| 365 | + # Search the filters |
| 366 | + for item in filters: |
| 367 | + action, msg, cat, mod, ln = item |
| 368 | + if ((msg is None or msg.match(text)) and |
| 369 | + issubclass(category, cat) and |
| 370 | + (mod is None or mod.match(module)) and |
| 371 | + (ln == 0 or lineno == ln)): |
| 372 | + break |
| 373 | + else: |
| 374 | + action = defaultaction |
| 375 | + # Early exit actions |
| 376 | + if action == "ignore": |
| 377 | + registry[key] = 1 |
| 378 | + return |
| 379 | + |
| 380 | + # Prime the linecache for formatting, in case the |
| 381 | + # "file" is actually in a zipfile or something. |
| 382 | + linecache.getlines(filename, module_globals) |
| 383 | + |
| 384 | + if action == "error": |
| 385 | + raise message |
| 386 | + # Other actions |
| 387 | + if action == "once": |
| 388 | + registry[key] = 1 |
| 389 | + oncekey = (text, category) |
| 390 | + if onceregistry.get(oncekey): |
| 391 | + return |
| 392 | + onceregistry[oncekey] = 1 |
| 393 | + elif action == "always": |
| 394 | + pass |
| 395 | + elif action == "module": |
| 396 | + registry[key] = 1 |
| 397 | + altkey = (text, category, 0) |
| 398 | + if registry.get(altkey): |
| 399 | + return |
| 400 | + registry[altkey] = 1 |
| 401 | + elif action == "default": |
| 402 | + registry[key] = 1 |
| 403 | + else: |
| 404 | + # Unrecognized actions are errors |
| 405 | + raise RuntimeError( |
| 406 | + "Unrecognized action (%r) in warnings.filters:\n %s" % |
| 407 | + (action, item)) |
| 408 | + # Print message and context |
| 409 | + showwarningwithfix(message, fix, category, filename, lineno) |
| 410 | + |
302 | 411 |
|
303 | 412 | class WarningMessage(object):
|
304 | 413 |
|
@@ -395,7 +504,7 @@ def __exit__(self, *exc_info):
|
395 | 504 | _warnings_defaults = False
|
396 | 505 | try:
|
397 | 506 | from _warnings import (filters, default_action, once_registry,
|
398 |
| - warn, warn_explicit) |
| 507 | + warn, warn_explicit, warn_explicit_with_fix) |
399 | 508 | defaultaction = default_action
|
400 | 509 | onceregistry = once_registry
|
401 | 510 | _warnings_defaults = True
|
|
0 commit comments