1"""
2Copy/paste from scrapy source at the moment, to ensure tests are working.
3Refactoring to come later
4"""
5import inspect
6from functools import partial
7
8from itemadapter import is_item
9
10
11_ITERABLE_SINGLE_VALUES = str, bytes
12
13
14def arg_to_iter(arg):
15    """Convert an argument to an iterable. The argument can be a None, single
16    value, or an iterable.
17
18    Exception: if arg is a dict, [arg] will be returned
19    """
20    if arg is None:
21        return []
22    elif (
23        hasattr(arg, '__iter__')
24        and not isinstance(arg, _ITERABLE_SINGLE_VALUES)
25        and not is_item(arg)
26    ):
27        return arg
28    else:
29        return [arg]
30
31
32def get_func_args(func, stripself=False):
33    """Return the argument name list of a callable"""
34    if inspect.isfunction(func):
35        func_args, _, _, _ = _getargspec_py23(func)
36    elif inspect.isclass(func):
37        return get_func_args(func.__init__, True)
38    elif inspect.ismethod(func):
39        return get_func_args(func.__func__, True)
40    elif inspect.ismethoddescriptor(func):
41        return []
42    elif isinstance(func, partial):
43        return [x for x in get_func_args(func.func)[len(func.args):]
44                if not (func.keywords and x in func.keywords)]
45    elif hasattr(func, '__call__'):
46        if inspect.isroutine(func):
47            return []
48        elif getattr(func, '__name__', None) == '__call__':
49            return []
50        else:
51            return get_func_args(func.__call__, True)
52    else:
53        raise TypeError('%s is not callable' % type(func))
54    if stripself:
55        func_args.pop(0)
56    return func_args
57
58
59def _getargspec_py23(func):
60    """_getargspec_py23(function) -> named tuple ArgSpec(args, varargs, keywords,
61                                                        defaults)
62
63    Was identical to inspect.getargspec() in python2, but uses
64    inspect.getfullargspec() for python3 behind the scenes to avoid
65    DeprecationWarning.
66
67    >>> def f(a, b=2, *ar, **kw):
68    ...     pass
69
70    >>> _getargspec_py23(f)
71    ArgSpec(args=['a', 'b'], varargs='ar', keywords='kw', defaults=(2,))
72    """
73    return inspect.ArgSpec(*inspect.getfullargspec(func)[:4])
74