Last active
July 12, 2025 05:18
-
-
Save Spitfire1900/ae316b3d3e5a8571002c149a7ef95160 to your computer and use it in GitHub Desktop.
Prints the entire warning stack when a warning is raised by a called function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
This sample script emits the entire warning stacktrace like the following: | |
$ python3 -m warnings_with_stacktrace | |
2025-07-12 01:18:09 WARNING [warnings_with_stacktrace.py:76] Caught DeprecationWarning: This function is deprecated | |
File: /home/kyle/tmp/py_sandbox/warnings_with_stacktrace.py, line 36 | |
Full stack: | |
File "<frozen runpy>", line 198, in _run_module_as_main | |
File "<frozen runpy>", line 88, in _run_code | |
File "/home/kyle/tmp/py_sandbox/warnings_with_stacktrace.py", line 90, in <module> | |
main() | |
File "/home/kyle/tmp/py_sandbox/warnings_with_stacktrace.py", line 86, in main | |
print(deprecated_function()) | |
File "/home/kyle/tmp/py_sandbox/warnings_with_stacktrace.py", line 36, in deprecated_function | |
warnings.warn('This function is deprecated', DeprecationWarning) | |
5 | |
''' | |
import logging | |
import os | |
import sys | |
import traceback | |
import warnings | |
logging.basicConfig( | |
format='%(asctime)s %(levelname)s [%(filename)s:%(lineno)d] %(message)s', | |
datefmt='%Y-%m-%d %H:%M:%S', | |
) | |
logging.captureWarnings(True) | |
LOGGER = logging.getLogger(__name__) | |
LOGGER.setLevel(logging.WARN) | |
def deprecated_function(): | |
warnings.warn('This function is deprecated', DeprecationWarning) | |
return 5 | |
def custom_showwarning( | |
message, | |
category, | |
filename, | |
lineno, | |
file=None, # file outputted to | |
line=None, # source code of line that issued the warning | |
): | |
# Get full stack | |
stack = traceback.extract_stack() | |
LOGGER.debug('Handling warning for %s:%s', filename, lineno) | |
# Normalize filename to absolute path for consistent comparison | |
abs_filename = os.path.abspath(filename) | |
# Find the last frame that matches the filename and line number | |
cutoff_index = None | |
for i, frame in enumerate(stack): | |
LOGGER.debug('Frame %s: %s:%s', i, frame.filename, frame.lineno) | |
if os.path.abspath( | |
frame.filename) == abs_filename and frame.lineno == lineno: | |
cutoff_index = i | |
break | |
if cutoff_index is not None: | |
cleaned_stack = stack[:cutoff_index + 1] | |
else: | |
# Fall back if we don't find a match | |
cleaned_stack = stack | |
formatted_stack = ''.join(traceback.format_list(cleaned_stack)) | |
# log_message = 'jim' | |
log_message = (f'Caught {category.__name__}: {message}\n' | |
f'File: {filename}, line {lineno}\n' | |
f'Full stack:\n{formatted_stack}') | |
LOGGER.warning(log_message) | |
# Replace the default showwarning with yours | |
if not sys.warnoptions: | |
warnings.simplefilter('once') | |
warnings.showwarning = custom_showwarning | |
def main(): | |
print(deprecated_function()) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment