|
1 | 1 | from __future__ import annotations
|
2 | 2 |
|
3 | 3 | import os
|
4 |
| -import warnings |
| 4 | +from inspect import currentframe |
5 | 5 | from logging import getLogger
|
6 |
| -from typing import Any, Callable, Generic, TypeVar, cast |
| 6 | +from types import FrameType |
| 7 | +from typing import Any, Callable, Generic, Iterator, TypeVar, cast |
| 8 | +from warnings import warn |
7 | 9 |
|
8 | 10 |
|
9 | 11 | _O = TypeVar("_O")
|
@@ -129,12 +131,32 @@ def __init__(self, new_opt: Option[_O], name: str) -> None:
|
129 | 131 |
|
130 | 132 | @property # type: ignore
|
131 | 133 | def _current(self) -> _O:
|
132 |
| - warnings.warn( |
| 134 | + warn( |
133 | 135 | f"{self.name!r} has been renamed to {self._new_opt.name!r}",
|
134 | 136 | DeprecationWarning,
|
| 137 | + stacklevel=_frame_depth_in_module() + 1, |
135 | 138 | )
|
136 | 139 | return self._new_opt.current
|
137 | 140 |
|
138 | 141 | @_current.setter
|
139 | 142 | def _current(self, new: _O) -> None:
|
140 | 143 | self._new_opt.current = new
|
| 144 | + |
| 145 | + |
| 146 | +def _frame_depth_in_module() -> int: |
| 147 | + depth = -1 # use -1 to ignore current frame |
| 148 | + for frame in _iter_frames(): |
| 149 | + if frame.f_globals.get("__name__") != __name__: |
| 150 | + break |
| 151 | + depth += 1 |
| 152 | + return depth |
| 153 | + |
| 154 | + |
| 155 | +def _iter_frames() -> Iterator[FrameType]: |
| 156 | + frame = currentframe() |
| 157 | + if frame is None: |
| 158 | + return # pragma: no cover |
| 159 | + frame = frame.f_back # ignore current frame |
| 160 | + while frame is not None: |
| 161 | + yield frame |
| 162 | + frame = frame.f_back |
0 commit comments