Last active
February 11, 2022 14:20
-
-
Save sethmlarson/dba167c50ef1f441dfb9e5dbf033efb8 to your computer and use it in GitHub Desktop.
Dynamically determine stacklevel for use with warnings.warn(..., stacklevel=X)
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
import inspect | |
import sys | |
from pathlib import Path | |
def warn_stacklevel() -> int: | |
"""Dynamically determine stacklevel for warnings based on the call stack""" | |
try: | |
# Grab the root module from the current module '__name__' | |
module_name = __name__.partition(".")[0] | |
module_path = Path(sys.modules[module_name].__file__) | |
# If the module is a folder we're looking at | |
# subdirectories, otherwise we're looking for | |
# an exact match. | |
module_is_folder = module_path.name == "__init__.py" | |
if module_is_folder: | |
module_path = module_path.parent | |
# Look through frames until we find a file that | |
# isn't a part of our module, then return that stacklevel. | |
for level, frame in enumerate(inspect.stack()): | |
# Garbage collecting frames | |
frame_filename = Path(frame.filename) | |
del frame | |
if ( | |
# If the module is a folder we look at subdirectory | |
module_is_folder | |
and module_path not in frame_filename.parents | |
) or ( | |
# Otherwise we're looking for an exact match. | |
not module_is_folder | |
and module_path != frame_filename | |
): | |
return level | |
except KeyError: | |
pass | |
return 0 | |
# Use like so: | |
import warnings | |
warnings.warn("This is a warning!", stacklevel=warn_stacklevel()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment