Issue a warning when a deprecated feature is used outside of Django. Skip the warning when called from within Django, to avoid cascading warnings when one deprecated feature is implemented on top of another. Examine the stack to determine the "effective caller" (the code using the
(
message,
category,
*,
skip_name_prefixes=None,
skip_frames=0,
internal_modules=None,
)
| 31 | |
| 32 | |
| 33 | def warn_about_external_use( |
| 34 | message, |
| 35 | category, |
| 36 | *, |
| 37 | skip_name_prefixes=None, |
| 38 | skip_frames=0, |
| 39 | internal_modules=None, |
| 40 | ): |
| 41 | """Issue a warning when a deprecated feature is used outside of Django. |
| 42 | |
| 43 | Skip the warning when called from within Django, to avoid cascading |
| 44 | warnings when one deprecated feature is implemented on top of another. |
| 45 | |
| 46 | Examine the stack to determine the "effective caller" (the code using the |
| 47 | deprecated feature). By default, this is the third frame from the top |
| 48 | (ignoring this helper plus the call site). |
| 49 | |
| 50 | Provide `skip_name_prefixes` (a string or tuple of strings) to skip |
| 51 | additional frames by prefix-matching each frame's fully qualified name |
| 52 | (dotted module path plus qualname). `skip_name_prefixes` can be used to |
| 53 | skip specific functions, all methods in a class, or everything in a module. |
| 54 | Skipping stops at the first non-matching frame. Useful when a shared helper |
| 55 | is called through multiple paths of varying depth with a common prefix. |
| 56 | |
| 57 | Provide `skip_frames` (an integer) to skip a fixed number of additional |
| 58 | frames (e.g., exactly one decorator or shared helper function). If both |
| 59 | options are provided, `skip_name_prefixes` is applied first. |
| 60 | |
| 61 | Provide `internal_modules` (a tuple of names, defaulting to ("django",)) to |
| 62 | customize what counts as "internal". A frame is internal when its fully |
| 63 | qualified name starts with one of these names followed by a dot. |
| 64 | |
| 65 | The warning is issued only if the effective caller (the first non-skipped |
| 66 | frame) is outside Django, attributed to that frame. If all frames are |
| 67 | skipped, it falls back to the base of the stack. |
| 68 | |
| 69 | Note: To unconditionally issue a warning identifying the first caller |
| 70 | outside Django as its source, don't use this function. Instead, use:: |
| 71 | |
| 72 | warnings.warn(..., skip_file_prefixes=django_file_prefixes()) |
| 73 | |
| 74 | to avoid unnecessary stack inspection overhead. |
| 75 | """ |
| 76 | |
| 77 | if internal_modules is None: |
| 78 | internal_modules = ("django",) |
| 79 | if not isinstance(internal_modules, tuple): |
| 80 | raise TypeError("internal_modules must be a tuple of module names") |
| 81 | internal_prefixes = tuple(f"{mod}." for mod in internal_modules) |
| 82 | |
| 83 | def get_fq_name(frame): |
| 84 | mod_name = frame.f_globals.get("__name__", "__main__") |
| 85 | return f"{mod_name}.{frame.f_code.co_qualname}" |
| 86 | |
| 87 | def back(frame, level): |
| 88 | if frame is not None: |
| 89 | return frame.f_back, level + 1 |
| 90 | return None, level |
no test coverage detected