# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
import inspect
import sys

try:
    import importlib.metadata as importlib_metadata  # noqa F401
except ImportError:  # pragma: no cover
    # bw-compat shim for py37
    import importlib_metadata  # noqa F401


def fix_type_error(exc_info, callable, varargs, kwargs):
    """
    Given an exception, this will test if the exception was due to a
    signature error, and annotate the error with better information if
    so.

    Usage::

      try:
          val = callable(*args, **kw)
      except TypeError:
          exc_info = fix_type_error(None, callable, args, kw)
          raise exc_info[0], exc_info[1], exc_info[2]
    """
    if exc_info is None:
        exc_info = sys.exc_info()
    if (
        exc_info[0] != TypeError
        or str(exc_info[1]).find('arguments') == -1
        or getattr(exc_info[1], '_type_error_fixed', False)
    ):
        return exc_info
    exc_info[1]._type_error_fixed = True
    argspec = inspect.signature(callable)
    args = ', '.join(map(_short_repr, varargs))
    if kwargs and args:
        args += ', '
    if kwargs:
        kwargs = sorted(kwargs.items())
        args += ', '.join(['%s=...' % n for n, v in kwargs])
    gotspec = '(%s)' % args
    msg = f'{exc_info[1]}; got {gotspec}, wanted {argspec}'
    exc_info[1].args = (msg,)
    return exc_info


def _short_repr(v):
    v = repr(v)
    if len(v) > 12:
        v = v[:8] + '...' + v[-4:]
    return v


def fix_call(callable, *args, **kw):
    """
    Call ``callable(*args, **kw)`` fixing any type errors that come out.
    """
    try:
        val = callable(*args, **kw)
    except TypeError:
        exc_info = fix_type_error(None, callable, args, kw)
        raise exc_info[1] from None
    return val


def lookup_object(spec):
    """
    Looks up a module or object from a some.module:func_name specification.
    To just look up a module, omit the colon and everything after it.
    """
    parts, target = spec.split(':') if ':' in spec else (spec, None)
    module = __import__(parts)

    for part in parts.split('.')[1:] + ([target] if target else []):
        module = getattr(module, part)

    return module
