"""The documentation repetition module.
Disclaimer
----------
Copyright 2021 Philipp S. Sommer, Helmholtz-Zentrum Geesthacht
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import six
import inspect
import re
from warnings import warn
from docrep.decorators import (
updates_docstring, reads_docstring, deprecated)
__version__ = '0.3.2'
__author__ = 'Philipp Sommer'
__all__ = [
"safe_modulo",
"delete_params",
"delete_types",
"delete_kwargs",
"keep_params",
"keep_types",
"DocstringProcessor",
]
substitution_pattern = re.compile(
r"""(?s)(?<!%)(%%)*%(?!%) # uneven number of %
\((?P<key>.*?)\)# key enclosed in brackets""", re.VERBOSE)
summary_patt = re.compile(r'(?s).*?(?=(\n\s*\n)|$)')
class _StrWithIndentation(object):
"""A convenience class that indents the given string if requested through
the __str__ method"""
def __init__(self, s, indent=0, *args, **kwargs):
self._indent = '\n' + ' ' * indent
self._s = s
def __str__(self):
return self._indent.join(self._s.splitlines())
def __repr__(self):
return repr(self._indent.join(self._s.splitlines()))
def safe_modulo(s, meta, checked='', print_warning=True, stacklevel=2):
"""Safe version of the modulo operation (%) of strings
Parameters
----------
s: str
string to apply the modulo operation with
meta: dict or tuple
meta informations to insert (usually via ``s % meta``)
checked: {'KEY', 'VALUE'}, optional
Security parameter for the recursive structure of this function. It can
be set to 'VALUE' if an error shall be raised when facing a TypeError
or ValueError or to 'KEY' if an error shall be raised when facing a
KeyError. This parameter is mainly for internal processes.
print_warning: bool
If True and a key is not existent in `s`, a warning is raised
stacklevel: int
The stacklevel for the :func:`warnings.warn` function
Examples
--------
The effects are demonstrated by this example::
>>> from docrep import safe_modulo
>>> s = "That's %(one)s string %(with)s missing 'with' and %s key"
>>> s % {'one': 1} # raises KeyError because of missing 'with'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'with'
>>> s % {'one': 1, 'with': 2} # raises TypeError because of '%s'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: not enough arguments for format string
>>> safe_modulo(s, {'one': 1})
"That's 1 string %(with)s missing 'with' and %s key"
"""
try:
return s % meta
except (ValueError, TypeError, KeyError):
# replace the missing fields by %%
keys = substitution_pattern.finditer(s)
for m in keys:
key = m.group('key')
if not isinstance(meta, dict) or key not in meta:
if print_warning:
warn("%r is not a valid key!" % key, SyntaxWarning,
stacklevel)
full = m.group()
s = s.replace(full, '%' + full)
if 'KEY' not in checked:
return safe_modulo(s, meta, checked=checked + 'KEY',
print_warning=print_warning,
stacklevel=stacklevel)
if not isinstance(meta, dict) or 'VALUE' in checked:
raise
s = re.sub(r"""(?<!%)(%%)*%(?!%) # uneven number of %
\s*(\w|$) # format strings""", r'%\g<0>', s,
flags=re.VERBOSE)
return safe_modulo(s, meta, checked=checked + 'VALUE',
print_warning=print_warning, stacklevel=stacklevel)
def delete_params(s, *params):
"""
Delete the given parameters from a string.
Same as :meth:`delete_params` but does not use the :attr:`params`
dictionary
Parameters
----------
s: str
The string of the parameters section
params: list of str
The names of the parameters to delete
Returns
-------
str
The modified string `s` without the descriptions of `params`
"""
patt = '(?s)' + '|'.join(
r'(?<=\n)' + s + r'\s*:.+?\n(?=\S+|$)' for s in params)
return re.sub(patt, '', '\n' + s.strip() + '\n').strip()
def delete_types(s, *types):
"""
Delete the given types from a string.
Same as :meth:`delete_types` but does not use the :attr:`params`
dictionary
Parameters
----------
s: str
The string of the returns like section
types: list of str
The type identifiers to delete
Returns
-------
str
The modified string `s` without the descriptions of `types`
"""
patt = '(?s)' + '|'.join(
r'(?<=\n)' + s + r'\n.+?\n(?=\S+|$)' for s in types)
return re.sub(patt, '', '\n' + s.strip() + '\n',).strip()
def delete_kwargs(s, args=None, kwargs=None):
"""
Delete the ``*args`` or ``**kwargs`` part from the parameters section.
Either `args` or `kwargs` must not be None.
Parameters
----------
s: str
The string to delete the args and kwargs from
args: None or str
The string for the args to delete
kwargs: None or str
The string for the kwargs to delete
Notes
-----
The type name of `args` in `s` has to be like ````*<args>```` (i.e. the
`args` argument preceeded by a ``'*'`` and enclosed by double ``'`'``).
Similarily, the type name of `kwargs` in `s` has to be like
````**<kwargs>````
"""
if not args and not kwargs:
return s
types = []
if args is not None:
types.append(r'`?`?\*%s`?`?' % args)
if kwargs is not None:
types.append(r'`?`?\*\*%s`?`?' % kwargs)
return delete_types(s, *types)
def keep_params(s, *params):
"""
Keep the given parameters from a string.
Same as :meth:`keep_params` but does not use the :attr:`params`
dictionary
Parameters
----------
s: str
The string of the parameters like section
params: list of str
The parameter names to keep
Returns
-------
str
The modified string `s` with only the descriptions of `params`
"""
patt = '(?s)' + '|'.join(
r'(?<=\n)' + s + r'\s*:.+?\n(?=\S+|$)' for s in params)
return ''.join(re.findall(patt, '\n' + s.strip() + '\n')).rstrip()
def keep_types(s, *types):
"""
Keep the given types from a string.
Same as :meth:`keep_types` but does not use the :attr:`params`
dictionary
Parameters
----------
s: str
The string of the returns like section
types: list of str
The type identifiers to keep
Returns
-------
str
The modified string `s` with only the descriptions of `types`
"""
patt = '(?s)' + '|'.join(
r'(?<=\n)' + s + r'\n.+?\n(?=\S+|$)' for s in types)
return ''.join(re.findall(patt, '\n' + s.strip() + '\n')).rstrip()
# assign delete_params a new name for the deprecation of the corresponding
# DocstringProcessor method
_delete_params_s = lambda s, params: delete_params(s, *params)
_delete_types_s = lambda s, types: delete_types(s, *types)
_delete_kwargs_s = delete_kwargs
_keep_params_s = lambda s, params: keep_params(s, *params)
_keep_types_s = lambda s, types: keep_types(s, *types)
class DocstringProcessor(object):
"""Class that is intended to process docstrings.
It is, but only to minor extends, inspired by the
:class:`matplotlib.docstring.Substitution` class.
Examples
--------
Create docstring processor via::
>>> from docrep import DocstringProcessor
>>> d = DocstringProcessor(doc_key='My doc string')
And then use it as a decorator to process the docstring::
>>> @d
... def doc_test():
... '''That's %(doc_key)s'''
... pass
>>> print(doc_test.__doc__)
That's My doc string
Use the :meth:`get_sections` method to extract Parameter sections (or
others) form the docstring for later usage (and make sure, that the
docstring is dedented)::
>>> @d.get_sections(base='docstring_example',
... sections=['Parameters', 'Examples'])
... @d.dedent
... def doc_test(a=1, b=2):
... '''
... That's %(doc_key)s
...
... Parameters
... ----------
... a: int, optional
... A dummy parameter description
... b: int, optional
... A second dummy parameter
...
... Examples
... --------
... Some dummy example doc'''
... print(a)
>>> @d.dedent
... def second_test(a=1, b=2):
... '''
... My second function where I want to use the docstring from
... above
...
... Parameters
... ----------
... %(docstring_example.parameters)s
...
... Examples
... --------
... %(docstring_example.examples)s'''
... pass
>>> print(second_test.__doc__)
My second function where I want to use the docstring from
above
<BLANKLINE>
Parameters
----------
a: int, optional
A dummy parameter description
b: int, optional
A second dummy parameter
<BLANKLINE>
Examples
--------
Some dummy example doc
Another example uses non-dedented docstrings::
>>> @d.get_sections(base='not_dedented')
... def doc_test2(a=1):
... '''That's the summary
...
... Parameters
... ----------
... a: int, optional
... A dummy parameter description'''
... print(a)
These sections must then be used with the :meth:`with_indent` method to
indent the inserted parameters::
>>> @d.with_indent(4)
... def second_test2(a=1):
... '''
... My second function where I want to use the docstring from
... above
...
... Parameters
... ----------
... %(not_dedented.parameters)s'''
... pass
"""
#: :class:`dict`. Dictionary containing the compiled patterns to identify
#: the Parameters, Other Parameters, Warnings and Notes sections in a
#: docstring
patterns = {}
#: :class:`dict`. Dictionary containing the parameters that are used in for
#: substitution.
params = {}
#: sections that behave the same as the `Parameter` section by defining a
#: list
param_like_sections = ['Parameters', 'Other Parameters', 'Returns',
'Raises']
#: sections that include (possibly not list-like) text
text_sections = ['Warnings', 'Notes', 'Examples', 'See Also',
'References']
#: The action on how to react on classes in python 2
#:
#: When calling::
#:
#: >>> @docstrings
#: ... class NewClass(object):
#: ... """%(replacement)s"""
#:
#: This normaly raises an AttributeError, because the ``__doc__`` attribute
#: of a class in python 2 is not writable. This attribute may be one of
#: ``'ignore', 'raise' or 'warn'``
python2_classes = 'ignore'
def __init__(self, *args, **kwargs):
"""
Parameters
----------
``*args``
Positional parameters that shall be used for the substitution. Note
that you can only provide either ``*args`` or ``**kwargs``,
furthermore most of the methods like `get_sections` require
``**kwargs`` to be provided (if any).
``**kwargs``
Initial parameters to use
"""
if args and kwargs:
raise ValueError("Only positional or keyword args are allowed")
self.params = args or kwargs
patterns = {}
all_sections = self.param_like_sections + self.text_sections
for section in self.param_like_sections:
patterns[section] = re.compile(
r'(?s)(?<=%s\n%s\n)(.+?)(?=\n\n\S+|$)' % (
section, '-'*len(section)))
all_sections_patt = '|'.join(
'%s\n%s\n' % (s, '-'*len(s)) for s in all_sections)
# examples and see also
for section in self.text_sections:
patterns[section] = re.compile(
'(?s)(?<=%s\n%s\n)(.+?)(?=%s|$)' % (
section, '-'*len(section), all_sections_patt))
self._extended_summary_patt = re.compile(
'(?s)(.+?)(?=%s|$)' % all_sections_patt)
self._all_sections_patt = re.compile(all_sections_patt)
self.patterns = patterns
@updates_docstring
def __call__(self, s):
"""
Substitute in a docstring of a function with :attr:`params`.
Parameters
----------
func: function
function with the documentation whose sections
shall be inserted from the :attr:`params` attribute
See Also
--------
dedent: also dedents the doc
with_indent: also indents the doc
"""
return safe_modulo(s, self.params, stacklevel=3)
@reads_docstring
def get_sections(self, s, base=None,
sections=['Parameters', 'Other Parameters']):
r"""Exctract sections out of a docstring.
This method extracts the specified `sections` out of the given string
if (and only if) the docstring follows the numpy documentation
guidelines [1]_. Note that the section either must appear in the
:attr:`param_like_sections` or the :attr:`text_sections` attribute.
Parameters
----------
s: str
Docstring to split
base: str
base to use in the :attr:`sections` attribute
sections: list of str
sections to look for. Each section must be followed by a newline
character ('\n') and a bar of '-' (following the numpy (napoleon)
docstring conventions).
Returns
-------
dict
A mapping from section identifier to section string
References
----------
.. [1] https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
See Also
--------
delete_params, keep_params, delete_types, keep_types, delete_kwargs:
For manipulating the docstring sections
save_docstring:
for saving an entire docstring
"""
params = self.params
# Remove the summary and dedent the rest
s = self._remove_summary(s)
ret = {}
for section in sections:
ret[section] = section_doc = self._get_section(s, section)
if base:
key = '%s.%s' % (base, section.lower().replace(' ', '_'))
params[key] = section_doc
return s
def _remove_summary(self, s):
# if the string does not start with one of the sections, we remove the
# summary
if not self._all_sections_patt.match(s.lstrip()):
# remove the summary
lines = summary_patt.sub('', s, 1).splitlines()
# look for the first line with content
first = next((i for i, l in enumerate(lines) if l.strip()), 0)
# dedent the lines
s = inspect.cleandoc('\n' + '\n'.join(lines[first:]))
return s
def _get_section(self, s, section):
try:
return self.patterns[section].search(s).group(0).rstrip()
except AttributeError:
return ''
@updates_docstring
def dedent(self, s, stacklevel=3):
"""
Dedent a string and substitute with the :attr:`params` attribute.
Parameters
----------
s: str
string to dedent and insert the sections of the :attr:`params`
attribute
stacklevel: int
The stacklevel for the warning raised in :func:`safe_module` when
encountering an invalid key in the string
"""
s = inspect.cleandoc(s)
return safe_modulo(s, self.params, stacklevel=stacklevel)
@updates_docstring
def with_indent(self, s, indent=0, stacklevel=3):
"""
Substitute a string with the indented :attr:`params`.
Parameters
----------
s: str
The string in which to substitute
indent: int
The number of spaces that the substitution should be indented
stacklevel: int
The stacklevel for the warning raised in :func:`safe_module` when
encountering an invalid key in the string
Returns
-------
str
The substituted string
See Also
--------
with_indent, dedent
"""
# we make a new dictionary with objects that indent the original
# strings if necessary. Note that the first line is not indented
d = {key: _StrWithIndentation(val, indent)
for key, val in six.iteritems(self.params)}
return safe_modulo(s, d, stacklevel=stacklevel)
def delete_params(self, base_key, *params):
"""
Delete a parameter from a parameter documentation.
This method deletes the given `param` from the `base_key` item in the
:attr:`params` dictionary and creates a new item with the original
documentation without the description of the param. This method works
for the ``'Parameters'`` sections.
The new docstring without the selected parts will be accessible as
``base_key + '.no_' + '|'.join(params)``, e.g.
``'original_key.no_param1|param2'``.
See the :meth:`keep_params` method for an example.
Parameters
----------
base_key: str
key in the :attr:`params` dictionary
``*params``
str. Parameter identifier of which the documentations shall be
deleted
See Also
--------
delete_types, keep_params
"""
self.params[
base_key + '.no_' + '|'.join(params)] = delete_params(
self.params[base_key], *params)
def delete_kwargs(self, base_key, args=None, kwargs=None):
"""
Delete the ``*args`` or ``**kwargs`` part from the parameters section.
Either `args` or `kwargs` must not be None. The resulting key will be
stored in
``base_key + 'no_args'``
if `args` is not None and `kwargs` is None
``base_key + 'no_kwargs'``
if `args` is None and `kwargs` is not None
``base_key + 'no_args_kwargs'``
if `args` is not None and `kwargs` is not None
Parameters
----------
base_key: str
The key in the :attr:`params` attribute to use
args: None or str
The string for the args to delete
kwargs: None or str
The string for the kwargs to delete
Notes
-----
The type name of `args` in the base has to be like ````*<args>````
(i.e. the `args` argument preceeded by a ``'*'`` and enclosed by double
``'`'``). Similarily, the type name of `kwargs` in `s` has to be like
````**<kwargs>````
"""
if not args and not kwargs:
warn("Neither args nor kwargs are given. I do nothing for %s" % (
base_key))
return
ext = '.no' + ('_args' if args else '') + ('_kwargs' if kwargs else '')
ret = delete_kwargs(self.params[base_key], args, kwargs)
self.params[base_key + ext] = ret
return ret
def delete_types(self, base_key, out_key, *types):
"""
Delete a parameter from a parameter documentation.
This method deletes the given `param` from the `base_key` item in the
:attr:`params` dictionary and creates a new item with the original
documentation without the description of the param. This method works
for ``'Results'`` like sections.
See the :meth:`keep_types` method for an example.
Parameters
----------
base_key: str
key in the :attr:`params` dictionary
out_key: str
Extension for the base key (the final key will be like
``'%s.%s' % (base_key, out_key)``
``*types``
str. The type identifier of which the documentations shall deleted
See Also
--------
delete_params
"""
self.params['%s.%s' % (base_key, out_key)] = delete_types(
self.params[base_key], *types)
def keep_params(self, base_key, *params):
"""
Keep only specific parameters from a parameter documentation.
This method extracts the given `param` from the `base_key` item in the
:attr:`params` dictionary and creates a new item with the original
documentation with only the description of the param. This method works
for ``'Parameters'`` like sections.
The new docstring with the selected parts will be accessible as
``base_key + '.' + '|'.join(params)``, e.g.
``'original_key.param1|param2'``
Parameters
----------
base_key: str
key in the :attr:`params` dictionary
``*params``
str. Parameter identifier of which the documentations shall be
in the new section
See Also
--------
keep_types, delete_params
Examples
--------
To extract just two parameters from a function and reuse their
docstrings, you can type::
>>> from docrep import DocstringProcessor
>>> d = DocstringProcessor()
>>> @d.get_sections(base='do_something')
... def do_something(a=1, b=2, c=3):
... '''
... That's %(doc_key)s
...
... Parameters
... ----------
... a: int, optional
... A dummy parameter description
... b: int, optional
... A second dummy parameter that will be excluded
... c: float, optional
... A third parameter'''
... print(a)
>>> d.keep_params('do_something.parameters', 'a', 'c')
>>> @d.dedent
... def do_less(a=1, c=4):
... '''
... My second function with only `a` and `c`
...
... Parameters
... ----------
... %(do_something.parameters.a|c)s'''
... pass
>>> print(do_less.__doc__)
My second function with only `a` and `c`
<BLANKLINE>
Parameters
----------
a: int, optional
A dummy parameter description
c: float, optional
A third parameter
Equivalently, you can use the :meth:`delete_params` method to remove
parameters::
>>> d.delete_params('do_something.parameters', 'b')
>>> @d.dedent
... def do_less(a=1, c=4):
... '''
... My second function with only `a` and `c`
...
... Parameters
... ----------
... %(do_something.parameters.no_b)s'''
... pass
"""
self.params[base_key + '.' + '|'.join(params)] = keep_params(
self.params[base_key], *params)
def keep_types(self, base_key, out_key, *types):
"""
Keep only specific parameters from a parameter documentation.
This method extracts the given `type` from the `base_key` item in the
:attr:`params` dictionary and creates a new item with the original
documentation with only the description of the type. This method works
for the ``'Results'`` sections.
Parameters
----------
base_key: str
key in the :attr:`params` dictionary
out_key: str
Extension for the base key (the final key will be like
``'%s.%s' % (base_key, out_key)``
``*types``
str. The type identifier of which the documentations shall be
in the new section
See Also
--------
delete_types, keep_params
Examples
--------
To extract just two return arguments from a function and reuse their
docstrings, you can type::
>>> from docrep import DocstringProcessor
>>> d = DocstringProcessor()
>>> @d.get_sections(base='do_something', sections=['Returns'])
... def do_something():
... '''
... That's %(doc_key)s
...
... Returns
... -------
... float
... A random number
... int
... A random integer'''
... return 1.0, 4
>>> d.keep_types('do_something.returns', 'int_only', 'int')
>>> @d.dedent
... def do_less():
... '''
... My second function that only returns an integer
...
... Returns
... -------
... %(do_something.returns.int_only)s'''
... return do_something()[1]
>>> print(do_less.__doc__)
My second function that only returns an integer
<BLANKLINE>
Returns
-------
int
A random integer
Equivalently, you can use the :meth:`delete_types` method to remove
parameters::
>>> d.delete_types('do_something.returns', 'no_float', 'float')
>>> @d.dedent
... def do_less():
... '''
... My second function with only `a` and `c`
...
... Returns
... ----------
... %(do_something.returns.no_float)s'''
... return do_something()[1]
"""
self.params['%s.%s' % (base_key, out_key)] = keep_types(
self.params[base_key], *types)
@reads_docstring
def get_docstring(self, s, base=None):
"""Get a docstring of a function.
Like the :meth:`get_sections` method this method serves as a
descriptor for functions but saves the entire docstring.
"""
if base is not None:
self.params[base] = s
return s
@reads_docstring
def get_summary(self, s, base=None):
"""
Get the summary of the given docstring.
This method extracts the summary from the given docstring `s` which is
basicly the part until two newlines appear
Parameters
----------
s: str
The docstring to use
base: str or None
A key under which the summary shall be stored in the :attr:`params`
attribute. If not None, the summary will be stored in
``base + '.summary'``. Otherwise, it will not be stored at all
Returns
-------
str
The extracted summary
"""
summary = summary_patt.search(s).group()
if base is not None:
self.params[base + '.summary'] = summary
return summary
@reads_docstring
def get_extended_summary(self, s, base=None):
"""Get the extended summary from a docstring.
This here is the extended summary
Parameters
----------
s: str
The docstring to use
base: str or None
A key under which the summary shall be stored in the :attr:`params`
attribute. If not None, the summary will be stored in
``base + '.summary_ext'``. Otherwise, it will not be stored at
all
Returns
-------
str
The extracted extended summary
"""
# Remove the summary and dedent
s = self._remove_summary(s)
ret = ''
if not self._all_sections_patt.match(s):
m = self._extended_summary_patt.match(s)
if m is not None:
ret = m.group().strip()
if base is not None:
self.params[base + '.summary_ext'] = ret
return ret
@reads_docstring
def get_full_description(self, s, base=None):
"""Get the full description from a docstring.
This here and the line above is the full description (i.e. the
combination of the :meth:`get_summary` and the
:meth:`get_extended_summary`) output
Parameters
----------
s: str
The docstring to use
base: str or None
A key under which the description shall be stored in the
:attr:`params` attribute. If not None, the summary will be stored
in ``base + '.full_desc'``. Otherwise, it will not be stored
at all
Returns
-------
str
The extracted full description
"""
summary = self.get_summary(s)
extended_summary = self.get_extended_summary(s)
ret = (summary + '\n\n' + extended_summary).strip()
if base is not None:
self.params[base + '.full_desc'] = ret
return ret
# ------------------ DEPRECATED METHODS -----------------------------------
@deprecated('dedent', "0.3.0", removed_in="0.4.0")
def dedents(self, *args, **kwargs):
pass
@deprecated('get_sections', "0.3.0", replace=False, removed_in="0.4.0")
def get_sectionsf(self, *args, **kwargs):
return self.get_sections(base=args[0], *args[1:], **kwargs)
@deprecated('with_indent', "0.3.0", removed_in="0.4.0")
def with_indents(self, *args, **kwargs):
pass
@staticmethod
@deprecated(_delete_params_s, "0.3.0", True, 'docrep.delete_params',
removed_in="0.4.0")
def delete_params_s(*args, **kwargs):
pass
@staticmethod
@deprecated(_delete_types_s, "0.3.0", True, 'docrep.delete_types',
removed_in="0.4.0")
def delete_types_s(*args, **kwargs):
pass
@classmethod
@deprecated(_delete_kwargs_s, "0.3.0", True, 'docrep.delete_kwargs',
removed_in="0.4.0")
def delete_kwargs_s(cls, *args, **kwargs):
pass
@staticmethod
@deprecated(_keep_params_s, "0.3.0", True, 'docrep.keep_params',
removed_in="0.4.0")
def keep_params_s(*args, **kwargs):
pass
@staticmethod
@deprecated(_keep_types_s, "0.3.0", True, 'docrep.keep_types',
removed_in="0.4.0")
def keep_types_s(*args, **kwargs):
pass
@deprecated('get_docstring', "0.3.0", replace=False, removed_in="0.4.0")
def save_docstring(self, *args, **kwargs):
return self.get_docstring(base=args[0], *args[1:], **kwargs)
@deprecated('get_summary', "0.3.0", replace=False, removed_in="0.4.0")
def get_summaryf(self, *args, **kwargs):
return self.get_summary(base=args[0], *args[1:], **kwargs)
@deprecated('get_full_description', "0.3.0", replace=False,
removed_in="0.4.0")
def get_full_descriptionf(self, *args, **kwargs):
return self.get_full_description(base=args[0], *args[1:], **kwargs)
@deprecated('get_extended_summary', "0.3.0", replace=False,
removed_in="0.4.0")
def get_extended_summaryf(self, *args, **kwargs):
return self.get_extended_summary(base=args[0], *args[1:], **kwargs)
@deprecated(inspect.cleandoc, "0.2.6", replacement_name='inspect.cleandoc',
removed_in="0.4.0")
def dedents(s):
pass