2017-07-12 00:42:10 +08:00
|
|
|
# Authors: Alexandre Gramfort <alexandre.gramfort@inria.fr>
|
|
|
|
|
# Raghav RV <rvraghav93@gmail.com>
|
|
|
|
|
# License: BSD 3 clause
|
|
|
|
|
|
|
|
|
|
import inspect
|
|
|
|
|
import sys
|
|
|
|
|
import warnings
|
|
|
|
|
import importlib
|
|
|
|
|
|
|
|
|
|
from pkgutil import walk_packages
|
2018-12-14 17:53:12 +08:00
|
|
|
from inspect import getsource, isabstract, signature
|
2017-07-12 00:42:10 +08:00
|
|
|
|
|
|
|
|
import sklearn
|
2018-07-20 12:39:53 +08:00
|
|
|
from sklearn.utils import IS_PYPY
|
2017-07-12 00:42:10 +08:00
|
|
|
from sklearn.utils.testing import SkipTest
|
|
|
|
|
from sklearn.utils.testing import check_docstring_parameters
|
|
|
|
|
from sklearn.utils.testing import _get_func_name
|
|
|
|
|
from sklearn.utils.testing import ignore_warnings
|
|
|
|
|
from sklearn.utils.deprecation import _is_deprecated
|
|
|
|
|
|
2018-09-19 05:09:07 +08:00
|
|
|
import pytest
|
|
|
|
|
|
2017-08-02 06:33:17 +08:00
|
|
|
PUBLIC_MODULES = set([pckg[1] for pckg in walk_packages(prefix='sklearn.',
|
|
|
|
|
path=sklearn.__path__)
|
|
|
|
|
if not ("._" in pckg[1] or ".tests." in pckg[1])])
|
2017-07-12 00:42:10 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# functions to ignore args / docstring of
|
|
|
|
|
_DOCSTRING_IGNORES = [
|
|
|
|
|
'sklearn.utils.deprecation.load_mlcomp',
|
|
|
|
|
'sklearn.pipeline.make_pipeline',
|
|
|
|
|
'sklearn.pipeline.make_union',
|
|
|
|
|
'sklearn.utils.extmath.safe_sparse_dot',
|
2018-11-20 07:53:52 +08:00
|
|
|
'sklearn.utils._joblib'
|
2017-07-12 00:42:10 +08:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# Methods where y param should be ignored if y=None by default
|
|
|
|
|
_METHODS_IGNORE_NONE_Y = [
|
2017-07-20 07:01:11 +08:00
|
|
|
'fit',
|
|
|
|
|
'score',
|
|
|
|
|
'fit_predict',
|
|
|
|
|
'fit_transform',
|
|
|
|
|
'partial_fit',
|
|
|
|
|
'predict'
|
2017-07-12 00:42:10 +08:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2018-07-18 06:38:01 +08:00
|
|
|
# numpydoc 0.8.0's docscrape tool raises because of collections.abc under
|
|
|
|
|
# Python 3.7
|
2018-09-19 05:09:07 +08:00
|
|
|
@pytest.mark.filterwarnings('ignore::DeprecationWarning')
|
|
|
|
|
@pytest.mark.skipif(IS_PYPY, reason='test segfaults on PyPy')
|
2017-07-12 00:42:10 +08:00
|
|
|
def test_docstring_parameters():
|
|
|
|
|
# Test module docstring formatting
|
|
|
|
|
|
|
|
|
|
# Skip test if numpydoc is not found or if python version is < 3.5
|
|
|
|
|
try:
|
|
|
|
|
import numpydoc # noqa
|
|
|
|
|
assert sys.version_info >= (3, 5)
|
|
|
|
|
except (ImportError, AssertionError):
|
2017-07-20 07:01:11 +08:00
|
|
|
raise SkipTest("numpydoc is required to test the docstrings, "
|
|
|
|
|
"as well as python version >= 3.5")
|
2017-07-12 00:42:10 +08:00
|
|
|
|
|
|
|
|
from numpydoc import docscrape
|
|
|
|
|
|
|
|
|
|
incorrect = []
|
|
|
|
|
for name in PUBLIC_MODULES:
|
2018-11-13 23:25:11 +08:00
|
|
|
if name == 'sklearn.utils.fixes':
|
|
|
|
|
# We cannot always control these docstrings
|
|
|
|
|
continue
|
2017-07-12 00:42:10 +08:00
|
|
|
with warnings.catch_warnings(record=True):
|
|
|
|
|
module = importlib.import_module(name)
|
|
|
|
|
classes = inspect.getmembers(module, inspect.isclass)
|
2017-08-02 06:33:17 +08:00
|
|
|
# Exclude imported classes
|
|
|
|
|
classes = [cls for cls in classes if cls[1].__module__ == name]
|
2017-07-12 00:42:10 +08:00
|
|
|
for cname, cls in classes:
|
|
|
|
|
this_incorrect = []
|
2017-08-02 06:33:17 +08:00
|
|
|
if cname in _DOCSTRING_IGNORES or cname.startswith('_'):
|
2017-07-12 00:42:10 +08:00
|
|
|
continue
|
2017-08-02 06:33:17 +08:00
|
|
|
if isabstract(cls):
|
2017-07-12 00:42:10 +08:00
|
|
|
continue
|
|
|
|
|
with warnings.catch_warnings(record=True) as w:
|
|
|
|
|
cdoc = docscrape.ClassDoc(cls)
|
|
|
|
|
if len(w):
|
|
|
|
|
raise RuntimeError('Error for __init__ of %s in %s:\n%s'
|
|
|
|
|
% (cls, name, w[0]))
|
|
|
|
|
|
|
|
|
|
cls_init = getattr(cls, '__init__', None)
|
|
|
|
|
|
|
|
|
|
if _is_deprecated(cls_init):
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
elif cls_init is not None:
|
|
|
|
|
this_incorrect += check_docstring_parameters(
|
|
|
|
|
cls.__init__, cdoc, class_name=cname)
|
|
|
|
|
for method_name in cdoc.methods:
|
|
|
|
|
method = getattr(cls, method_name)
|
|
|
|
|
if _is_deprecated(method):
|
|
|
|
|
continue
|
|
|
|
|
param_ignore = None
|
|
|
|
|
# Now skip docstring test for y when y is None
|
|
|
|
|
# by default for API reason
|
|
|
|
|
if method_name in _METHODS_IGNORE_NONE_Y:
|
|
|
|
|
sig = signature(method)
|
|
|
|
|
if ('y' in sig.parameters and
|
|
|
|
|
sig.parameters['y'].default is None):
|
|
|
|
|
param_ignore = ['y'] # ignore y for fit and score
|
|
|
|
|
result = check_docstring_parameters(
|
|
|
|
|
method, ignore=param_ignore, class_name=cname)
|
|
|
|
|
this_incorrect += result
|
|
|
|
|
|
|
|
|
|
incorrect += this_incorrect
|
|
|
|
|
|
|
|
|
|
functions = inspect.getmembers(module, inspect.isfunction)
|
2017-08-02 06:33:17 +08:00
|
|
|
# Exclude imported functions
|
|
|
|
|
functions = [fn for fn in functions if fn[1].__module__ == name]
|
2017-07-12 00:42:10 +08:00
|
|
|
for fname, func in functions:
|
|
|
|
|
# Don't test private methods / functions
|
|
|
|
|
if fname.startswith('_'):
|
|
|
|
|
continue
|
2017-08-02 06:33:17 +08:00
|
|
|
if fname == "configuration" and name.endswith("setup"):
|
|
|
|
|
continue
|
2017-07-12 00:42:10 +08:00
|
|
|
name_ = _get_func_name(func)
|
|
|
|
|
if (not any(d in name_ for d in _DOCSTRING_IGNORES) and
|
|
|
|
|
not _is_deprecated(func)):
|
|
|
|
|
incorrect += check_docstring_parameters(func)
|
|
|
|
|
msg = '\n' + '\n'.join(sorted(list(set(incorrect))))
|
|
|
|
|
if len(incorrect) > 0:
|
2017-08-31 05:06:46 +08:00
|
|
|
raise AssertionError("Docstring Error: " + msg)
|
2017-07-12 00:42:10 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@ignore_warnings(category=DeprecationWarning)
|
|
|
|
|
def test_tabs():
|
|
|
|
|
# Test that there are no tabs in our source files
|
|
|
|
|
for importer, modname, ispkg in walk_packages(sklearn.__path__,
|
|
|
|
|
prefix='sklearn.'):
|
2018-07-20 12:39:53 +08:00
|
|
|
|
|
|
|
|
if IS_PYPY and ('_svmlight_format' in modname or
|
|
|
|
|
'feature_extraction._hashing' in modname):
|
|
|
|
|
continue
|
|
|
|
|
|
2017-07-12 00:42:10 +08:00
|
|
|
# because we don't import
|
|
|
|
|
mod = importlib.import_module(modname)
|
|
|
|
|
try:
|
|
|
|
|
source = getsource(mod)
|
|
|
|
|
except IOError: # user probably should have run "make clean"
|
|
|
|
|
continue
|
|
|
|
|
assert '\t' not in source, ('"%s" has tabs, please remove them ',
|
|
|
|
|
'or add it to theignore list'
|
|
|
|
|
% modname)
|