Skip to content

Commit ce26e78

Browse files
datapythonistaPingviinituutti
authored andcommitted
Fix docstring of functions created with the deprecate() function (pandas-dev#24215)
* Fix docstring of functions created with the deprecate() function * Allowing deprecate to be used with functions without docstring
1 parent 36da7ad commit ce26e78

File tree

2 files changed

+92
-18
lines changed

2 files changed

+92
-18
lines changed

pandas/tests/util/test_deprecate.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from textwrap import dedent
2+
3+
import pytest
4+
5+
from pandas.util._decorators import deprecate
6+
7+
import pandas.util.testing as tm
8+
9+
10+
def new_func():
11+
"""
12+
This is the summary. The deprecate directive goes next.
13+
14+
This is the extended summary. The deprecate directive goes before this.
15+
"""
16+
return 'new_func called'
17+
18+
19+
def new_func_no_docstring():
20+
return 'new_func_no_docstring called'
21+
22+
23+
def new_func_wrong_docstring():
24+
"""Summary should be in the next line."""
25+
return 'new_func_wrong_docstring called'
26+
27+
28+
def new_func_with_deprecation():
29+
"""
30+
This is the summary. The deprecate directive goes next.
31+
32+
.. deprecated:: 1.0
33+
Use new_func instead.
34+
35+
This is the extended summary. The deprecate directive goes before this.
36+
"""
37+
pass
38+
39+
40+
def test_deprecate_ok():
41+
depr_func = deprecate('depr_func', new_func, '1.0',
42+
msg='Use new_func instead.')
43+
44+
with tm.assert_produces_warning(FutureWarning):
45+
result = depr_func()
46+
47+
assert result == 'new_func called'
48+
assert depr_func.__doc__ == dedent(new_func_with_deprecation.__doc__)
49+
50+
51+
def test_deprecate_no_docstring():
52+
depr_func = deprecate('depr_func', new_func_no_docstring, '1.0',
53+
msg='Use new_func instead.')
54+
with tm.assert_produces_warning(FutureWarning):
55+
result = depr_func()
56+
assert result == 'new_func_no_docstring called'
57+
58+
59+
def test_deprecate_wrong_docstring():
60+
with pytest.raises(AssertionError, match='deprecate needs a correctly '
61+
'formatted docstring'):
62+
deprecate('depr_func', new_func_wrong_docstring, '1.0',
63+
msg='Use new_func instead.')

pandas/util/_decorators.py

+29-18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from functools import WRAPPER_ASSIGNMENTS, update_wrapper, wraps
1+
from functools import wraps
22
import inspect
3-
from textwrap import dedent, wrap
3+
from textwrap import dedent
44
import warnings
55

66
from pandas._libs.properties import cache_readonly # noqa
@@ -39,26 +39,37 @@ def deprecate(name, alternative, version, alt_name=None,
3939
warning_msg = msg or '{} is deprecated, use {} instead'.format(name,
4040
alt_name)
4141

42-
# adding deprecated directive to the docstring
43-
msg = msg or 'Use `{alt_name}` instead.'.format(alt_name=alt_name)
44-
msg = '\n '.join(wrap(msg, 70))
45-
46-
@Substitution(version=version, msg=msg)
47-
@Appender(alternative.__doc__)
42+
@wraps(alternative)
4843
def wrapper(*args, **kwargs):
49-
"""
50-
.. deprecated:: %(version)s
51-
52-
%(msg)s
53-
54-
"""
5544
warnings.warn(warning_msg, klass, stacklevel=stacklevel)
5645
return alternative(*args, **kwargs)
5746

58-
# Since we are using Substitution to create the required docstring,
59-
# remove that from the attributes that should be assigned to the wrapper
60-
assignments = tuple(x for x in WRAPPER_ASSIGNMENTS if x != '__doc__')
61-
update_wrapper(wrapper, alternative, assigned=assignments)
47+
# adding deprecated directive to the docstring
48+
msg = msg or 'Use `{alt_name}` instead.'.format(alt_name=alt_name)
49+
doc_error_msg = ('deprecate needs a correctly formatted docstring in '
50+
'the target function (should have a one liner short '
51+
'summary, and opening quotes should be in their own '
52+
'line). Found:\n{}'.format(alternative.__doc__))
53+
54+
# when python is running in optimized mode (i.e. `-OO`), docstrings are
55+
# removed, so we check that a docstring with correct formatting is used
56+
# but we allow empty docstrings
57+
if alternative.__doc__:
58+
if alternative.__doc__.count('\n') < 3:
59+
raise AssertionError(doc_error_msg)
60+
empty1, summary, empty2, doc = alternative.__doc__.split('\n', 3)
61+
if empty1 or empty2 and not summary:
62+
raise AssertionError(doc_error_msg)
63+
wrapper.__doc__ = dedent("""
64+
{summary}
65+
66+
.. deprecated:: {depr_version}
67+
{depr_msg}
68+
69+
{rest_of_docstring}""").format(summary=summary.strip(),
70+
depr_version=version,
71+
depr_msg=msg,
72+
rest_of_docstring=dedent(doc))
6273

6374
return wrapper
6475

0 commit comments

Comments
 (0)