1from inspect import signature, Parameter
2from functools import wraps
3import warnings
4
5
6# This function was adapted from scikit-learn
7# github.com/scikit-learn/scikit-learn/blob/master/sklearn/utils/validation.py
8def _deprecate_positional_args(f):
9    """Decorator for methods that issues warnings for positional arguments.
10
11    Using the keyword-only argument syntax in pep 3102, arguments after the
12    * will issue a warning when passed as a positional argument.
13
14    Parameters
15    ----------
16    f : function
17        function to check arguments on
18
19    """
20    sig = signature(f)
21    kwonly_args = []
22    all_args = []
23
24    for name, param in sig.parameters.items():
25        if param.kind == Parameter.POSITIONAL_OR_KEYWORD:
26            all_args.append(name)
27        elif param.kind == Parameter.KEYWORD_ONLY:
28            kwonly_args.append(name)
29
30    @wraps(f)
31    def inner_f(*args, **kwargs):
32        extra_args = len(args) - len(all_args)
33        if extra_args > 0:
34            plural = "s" if extra_args > 1 else ""
35            article = "" if plural else "a "
36            warnings.warn(
37                "Pass the following variable{} as {}keyword arg{}: {}. "
38                "From version 0.12, the only valid positional argument "
39                "will be `data`, and passing other arguments without an "
40                "explicit keyword will result in an error or misinterpretation."
41                .format(plural, article, plural,
42                        ", ".join(kwonly_args[:extra_args])),
43                FutureWarning
44            )
45        kwargs.update({k: arg for k, arg in zip(sig.parameters, args)})
46        return f(**kwargs)
47    return inner_f
48
49
50def share_init_params_with_map(cls):
51    """Make cls.map a classmethod with same signature as cls.__init__."""
52    map_sig = signature(cls.map)
53    init_sig = signature(cls.__init__)
54
55    new = [v for k, v in init_sig.parameters.items() if k != "self"]
56    new.insert(0, map_sig.parameters["cls"])
57    cls.map.__signature__ = map_sig.replace(parameters=new)
58    cls.map.__doc__ = cls.__init__.__doc__
59
60    cls.map = classmethod(cls.map)
61
62    return cls
63