Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/pandas/util/_decorators.py : 69%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from functools import wraps
2import inspect
3from textwrap import dedent
4from typing import (
5 Any,
6 Callable,
7 List,
8 Mapping,
9 Optional,
10 Tuple,
11 Type,
12 TypeVar,
13 Union,
14 cast,
15)
16import warnings
18from pandas._libs.properties import cache_readonly # noqa
20FuncType = Callable[..., Any]
21F = TypeVar("F", bound=FuncType)
24def deprecate(
25 name: str,
26 alternative: Callable[..., Any],
27 version: str,
28 alt_name: Optional[str] = None,
29 klass: Optional[Type[Warning]] = None,
30 stacklevel: int = 2,
31 msg: Optional[str] = None,
32) -> Callable[..., Any]:
33 """
34 Return a new function that emits a deprecation warning on use.
36 To use this method for a deprecated function, another function
37 `alternative` with the same signature must exist. The deprecated
38 function will emit a deprecation warning, and in the docstring
39 it will contain the deprecation directive with the provided version
40 so it can be detected for future removal.
42 Parameters
43 ----------
44 name : str
45 Name of function to deprecate.
46 alternative : func
47 Function to use instead.
48 version : str
49 Version of pandas in which the method has been deprecated.
50 alt_name : str, optional
51 Name to use in preference of alternative.__name__.
52 klass : Warning, default FutureWarning
53 stacklevel : int, default 2
54 msg : str
55 The message to display in the warning.
56 Default is '{name} is deprecated. Use {alt_name} instead.'
57 """
59 alt_name = alt_name or alternative.__name__
60 klass = klass or FutureWarning
61 warning_msg = msg or f"{name} is deprecated, use {alt_name} instead"
63 @wraps(alternative)
64 def wrapper(*args, **kwargs) -> Callable[..., Any]:
65 warnings.warn(warning_msg, klass, stacklevel=stacklevel)
66 return alternative(*args, **kwargs)
68 # adding deprecated directive to the docstring
69 msg = msg or f"Use `{alt_name}` instead."
70 doc_error_msg = (
71 "deprecate needs a correctly formatted docstring in "
72 "the target function (should have a one liner short "
73 "summary, and opening quotes should be in their own "
74 f"line). Found:\n{alternative.__doc__}"
75 )
77 # when python is running in optimized mode (i.e. `-OO`), docstrings are
78 # removed, so we check that a docstring with correct formatting is used
79 # but we allow empty docstrings
80 if alternative.__doc__:
81 if alternative.__doc__.count("\n") < 3:
82 raise AssertionError(doc_error_msg)
83 empty1, summary, empty2, doc = alternative.__doc__.split("\n", 3)
84 if empty1 or empty2 and not summary:
85 raise AssertionError(doc_error_msg)
86 wrapper.__doc__ = dedent(
87 f"""
88 {summary.strip()}
90 .. deprecated:: {version}
91 {msg}
93 {dedent(doc)}"""
94 )
96 return wrapper
99def deprecate_kwarg(
100 old_arg_name: str,
101 new_arg_name: Optional[str],
102 mapping: Optional[Union[Mapping[Any, Any], Callable[[Any], Any]]] = None,
103 stacklevel: int = 2,
104) -> Callable[..., Any]:
105 """
106 Decorator to deprecate a keyword argument of a function.
108 Parameters
109 ----------
110 old_arg_name : str
111 Name of argument in function to deprecate
112 new_arg_name : str or None
113 Name of preferred argument in function. Use None to raise warning that
114 ``old_arg_name`` keyword is deprecated.
115 mapping : dict or callable
116 If mapping is present, use it to translate old arguments to
117 new arguments. A callable must do its own value checking;
118 values not found in a dict will be forwarded unchanged.
120 Examples
121 --------
122 The following deprecates 'cols', using 'columns' instead
124 >>> @deprecate_kwarg(old_arg_name='cols', new_arg_name='columns')
125 ... def f(columns=''):
126 ... print(columns)
127 ...
128 >>> f(columns='should work ok')
129 should work ok
131 >>> f(cols='should raise warning')
132 FutureWarning: cols is deprecated, use columns instead
133 warnings.warn(msg, FutureWarning)
134 should raise warning
136 >>> f(cols='should error', columns="can\'t pass do both")
137 TypeError: Can only specify 'cols' or 'columns', not both
139 >>> @deprecate_kwarg('old', 'new', {'yes': True, 'no': False})
140 ... def f(new=False):
141 ... print('yes!' if new else 'no!')
142 ...
143 >>> f(old='yes')
144 FutureWarning: old='yes' is deprecated, use new=True instead
145 warnings.warn(msg, FutureWarning)
146 yes!
148 To raise a warning that a keyword will be removed entirely in the future
150 >>> @deprecate_kwarg(old_arg_name='cols', new_arg_name=None)
151 ... def f(cols='', another_param=''):
152 ... print(cols)
153 ...
154 >>> f(cols='should raise warning')
155 FutureWarning: the 'cols' keyword is deprecated and will be removed in a
156 future version please takes steps to stop use of 'cols'
157 should raise warning
158 >>> f(another_param='should not raise warning')
159 should not raise warning
161 >>> f(cols='should raise warning', another_param='')
162 FutureWarning: the 'cols' keyword is deprecated and will be removed in a
163 future version please takes steps to stop use of 'cols'
164 should raise warning
165 """
167 if mapping is not None and not hasattr(mapping, "get") and not callable(mapping):
168 raise TypeError(
169 "mapping from old to new argument values must be dict or callable!"
170 )
172 def _deprecate_kwarg(func: F) -> F:
173 @wraps(func)
174 def wrapper(*args, **kwargs) -> Callable[..., Any]:
175 old_arg_value = kwargs.pop(old_arg_name, None)
177 if old_arg_value is not None:
178 if new_arg_name is None:
179 msg = (
180 f"the {repr(old_arg_name)} keyword is deprecated and "
181 "will be removed in a future version. Please take "
182 f"steps to stop the use of {repr(old_arg_name)}"
183 )
184 warnings.warn(msg, FutureWarning, stacklevel=stacklevel)
185 kwargs[old_arg_name] = old_arg_value
186 return func(*args, **kwargs)
188 elif mapping is not None:
189 if callable(mapping):
190 new_arg_value = mapping(old_arg_value)
191 else:
192 new_arg_value = mapping.get(old_arg_value, old_arg_value)
193 msg = (
194 f"the {old_arg_name}={repr(old_arg_value)} keyword is "
195 "deprecated, use "
196 f"{new_arg_name}={repr(new_arg_value)} instead"
197 )
198 else:
199 new_arg_value = old_arg_value
200 msg = (
201 f"the {repr(old_arg_name)}' keyword is deprecated, "
202 f"use {repr(new_arg_name)} instead"
203 )
205 warnings.warn(msg, FutureWarning, stacklevel=stacklevel)
206 if kwargs.get(new_arg_name) is not None:
207 msg = (
208 f"Can only specify {repr(old_arg_name)} "
209 f"or {repr(new_arg_name)}, not both"
210 )
211 raise TypeError(msg)
212 else:
213 kwargs[new_arg_name] = new_arg_value
214 return func(*args, **kwargs)
216 return cast(F, wrapper)
218 return _deprecate_kwarg
221def rewrite_axis_style_signature(
222 name: str, extra_params: List[Tuple[str, Any]]
223) -> Callable[..., Any]:
224 def decorate(func: F) -> F:
225 @wraps(func)
226 def wrapper(*args, **kwargs) -> Callable[..., Any]:
227 return func(*args, **kwargs)
229 kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
230 params = [
231 inspect.Parameter("self", kind),
232 inspect.Parameter(name, kind, default=None),
233 inspect.Parameter("index", kind, default=None),
234 inspect.Parameter("columns", kind, default=None),
235 inspect.Parameter("axis", kind, default=None),
236 ]
238 for pname, default in extra_params:
239 params.append(inspect.Parameter(pname, kind, default=default))
241 sig = inspect.Signature(params)
243 # https://github.com/python/typing/issues/598
244 func.__signature__ = sig # type: ignore
245 return cast(F, wrapper)
247 return decorate
250# Substitution and Appender are derived from matplotlib.docstring (1.1.0)
251# module http://matplotlib.org/users/license.html
254class Substitution:
255 """
256 A decorator to take a function's docstring and perform string
257 substitution on it.
259 This decorator should be robust even if func.__doc__ is None
260 (for example, if -OO was passed to the interpreter)
262 Usage: construct a docstring.Substitution with a sequence or
263 dictionary suitable for performing substitution; then
264 decorate a suitable function with the constructed object. e.g.
266 sub_author_name = Substitution(author='Jason')
268 @sub_author_name
269 def some_function(x):
270 "%(author)s wrote this function"
272 # note that some_function.__doc__ is now "Jason wrote this function"
274 One can also use positional arguments.
276 sub_first_last_names = Substitution('Edgar Allen', 'Poe')
278 @sub_first_last_names
279 def some_function(x):
280 "%s %s wrote the Raven"
281 """
283 def __init__(self, *args, **kwargs):
284 if args and kwargs:
285 raise AssertionError("Only positional or keyword args are allowed")
287 self.params = args or kwargs
289 def __call__(self, func: F) -> F:
290 func.__doc__ = func.__doc__ and func.__doc__ % self.params
291 return func
293 def update(self, *args, **kwargs) -> None:
294 """
295 Update self.params with supplied args.
296 """
297 if isinstance(self.params, dict):
298 self.params.update(*args, **kwargs)
301class Appender:
302 """
303 A function decorator that will append an addendum to the docstring
304 of the target function.
306 This decorator should be robust even if func.__doc__ is None
307 (for example, if -OO was passed to the interpreter).
309 Usage: construct a docstring.Appender with a string to be joined to
310 the original docstring. An optional 'join' parameter may be supplied
311 which will be used to join the docstring and addendum. e.g.
313 add_copyright = Appender("Copyright (c) 2009", join='\n')
315 @add_copyright
316 def my_dog(has='fleas'):
317 "This docstring will have a copyright below"
318 pass
319 """
321 addendum: Optional[str]
323 def __init__(self, addendum: Optional[str], join: str = "", indents: int = 0):
324 if indents > 0:
325 self.addendum = indent(addendum, indents=indents)
326 else:
327 self.addendum = addendum
328 self.join = join
330 def __call__(self, func: F) -> F:
331 func.__doc__ = func.__doc__ if func.__doc__ else ""
332 self.addendum = self.addendum if self.addendum else ""
333 docitems = [func.__doc__, self.addendum]
334 func.__doc__ = dedent(self.join.join(docitems))
335 return func
338def indent(text: Optional[str], indents: int = 1) -> str:
339 if not text or not isinstance(text, str):
340 return ""
341 jointext = "".join(["\n"] + [" "] * indents)
342 return jointext.join(text.split("\n"))