Skip to content

Instantly share code, notes, and snippets.

@Spitfire1900
Last active July 12, 2025 05:18
Show Gist options
  • Save Spitfire1900/ae316b3d3e5a8571002c149a7ef95160 to your computer and use it in GitHub Desktop.
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 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