r"""
    Makes a pretty printable / human-readable string representation of a
    dictionary. In most cases this string could be evaled.

    Args:
        dict_ (dict_): a dictionary

    Args:
        dict_ (dict_):  a dictionary
        **dictkw: stritems, strkeys, strvals, nl, newlines, truncate, nobr,
                  nobraces, align, trailing_sep, explicit, itemsep,
                  truncatekw, sorted_, indent_, key_order, precision,
                  with_comma, key_order_metric, maxlen, recursive, use_numpy,
                  with_dtype, force_dtype, packed

    Kwargs:
        sorted_ (None): returns str sorted by a metric (default = None)
        nl (int): prefered alias for newline. can be a coundown variable
            (default = None)
        key_order (None): overrides default ordering (default = None)
        key_order_metric (str): special sorting of items. Accepted values:
                None, 'strlen', 'val'
        precision (int): (default = 8)
        explicit (int): can be a countdown variable. if True, uses
            dict(a=b) syntax instead of {'a': b}
        nobr (bool): removes outer braces (default = False)

    Ignore:
        python -m utool.util_inspect recursive_parse_kwargs:2 --mod utool.util_str --func dict_str --verbinspect

    CommandLine:
        python -m utool.util_str --test-dict_str:1
        python -m utool.util_str --test-dict_str --truncate=False --no-checkwant
        python -m utool.util_str --test-dict_str --truncate=1 --no-checkwant
        python -m utool.util_str --test-dict_str --truncate=2 --no-checkwant

    Example:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_str import dict_str, dict_itemstr_list
        >>> import utool as ut
        >>> dict_ = {'foo': {'spam': 'barbarbarbarbar' * 3, 'eggs': 'jam'},
        >>>          'baz': 'barbarbarbarbar' * 3}
        >>> truncate = ut.get_argval('--truncate', type_=None, default=1)
        >>> result = dict_str(dict_, strvals=True, truncate=truncate,
        >>>                    truncatekw={'maxlen': 20})
        >>> print(result)
        {
            'baz': barbarbarbarbarbarbarbarbarbarbarbarbarbarbar,
            'foo': {
                'eggs': jam,
                's ~~~TRUNCATED~~~ r,
            },
        }

    Example:
        >>> # ENABLE_DOCTEST
        >>> import utool as ut
        >>> import numpy as np
        >>> a, b, c = 'a', 'b', 'c'
        >>> dict_ = {
        >>>     'float': 2.3333,
        >>>     'slice': slice(1, 2, None),
        >>>     'arr': np.eye(3),
        >>>     'list1': [1, 2, 3, 2.3333, a, b, c],
        >>>     'dict1': {2.3333: 2.3333, a: b, c: [a, b]},
        >>>     't': {c: {c: {c: {c : c}}}},
        >>>     'set1': {c, a, b},
        >>>     'set2': ut.oset([c, a, b]),
        >>>     'list2': [
        >>>         {a: {c, a, b}, 1: slice(1, 2, 3)},
        >>>         [1, 2, {c, a, 2.333}, {a: [b], b: {c}, c: 2.333}]
        >>>     ],
        >>> }
        >>> dictkw = dict(stritems=True, itemsep='', precision=2, nl=1,
        >>>               nobr=True, explicit=True)
        >>> result = ut.dict_str(dict_, **dictkw)
        >>> print(result)
        >>> dictkw = dict(stritems=0, precision=2, nl=True, nobr=False,
        >>>                  explicit=0)
        >>> result = ut.dict_str(dict_, **dictkw)
        >>> print(result)

def dict_str(dict_, **dictkw):
    r"""
    Makes a pretty printable / human-readable string representation of a
    dictionary. In most cases this string could be evaled.

    Args:
        dict_ (dict_): a dictionary

    Args:
        dict_ (dict_):  a dictionary
        **dictkw: stritems, strkeys, strvals, nl, newlines, truncate, nobr,
                  nobraces, align, trailing_sep, explicit, itemsep,
                  truncatekw, sorted_, indent_, key_order, precision,
                  with_comma, key_order_metric, maxlen, recursive, use_numpy,
                  with_dtype, force_dtype, packed

    Kwargs:
        sorted_ (None): returns str sorted by a metric (default = None)
        nl (int): prefered alias for newline. can be a coundown variable
            (default = None)
        key_order (None): overrides default ordering (default = None)
        key_order_metric (str): special sorting of items. Accepted values:
                None, 'strlen', 'val'
        precision (int): (default = 8)
        explicit (int): can be a countdown variable. if True, uses
            dict(a=b) syntax instead of {'a': b}
        nobr (bool): removes outer braces (default = False)

    Ignore:
        python -m utool.util_inspect recursive_parse_kwargs:2 --mod utool.util_str --func dict_str --verbinspect

    CommandLine:
        python -m utool.util_str --test-dict_str:1
        python -m utool.util_str --test-dict_str --truncate=False --no-checkwant
        python -m utool.util_str --test-dict_str --truncate=1 --no-checkwant
        python -m utool.util_str --test-dict_str --truncate=2 --no-checkwant

    Example:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_str import dict_str, dict_itemstr_list
        >>> import utool as ut
        >>> dict_ = {'foo': {'spam': 'barbarbarbarbar' * 3, 'eggs': 'jam'},
        >>>          'baz': 'barbarbarbarbar' * 3}
        >>> truncate = ut.get_argval('--truncate', type_=None, default=1)
        >>> result = dict_str(dict_, strvals=True, truncate=truncate,
        >>>                    truncatekw={'maxlen': 20})
        >>> print(result)
        {
            'baz': barbarbarbarbarbarbarbarbarbarbarbarbarbarbar,
            'foo': {
                'eggs': jam,
                's ~~~TRUNCATED~~~ r,
            },
        }

    Example:
        >>> # ENABLE_DOCTEST
        >>> import utool as ut
        >>> import numpy as np
        >>> a, b, c = 'a', 'b', 'c'
        >>> dict_ = {
        >>>     'float': 2.3333,
        >>>     'slice': slice(1, 2, None),
        >>>     'arr': np.eye(3),
        >>>     'list1': [1, 2, 3, 2.3333, a, b, c],
        >>>     'dict1': {2.3333: 2.3333, a: b, c: [a, b]},
        >>>     't': {c: {c: {c: {c : c}}}},
        >>>     'set1': {c, a, b},
        >>>     'set2': ut.oset([c, a, b]),
        >>>     'list2': [
        >>>         {a: {c, a, b}, 1: slice(1, 2, 3)},
        >>>         [1, 2, {c, a, 2.333}, {a: [b], b: {c}, c: 2.333}]
        >>>     ],
        >>> }
        >>> dictkw = dict(stritems=True, itemsep='', precision=2, nl=1,
        >>>               nobr=True, explicit=True)
        >>> result = ut.dict_str(dict_, **dictkw)
        >>> print(result)
        >>> dictkw = dict(stritems=0, precision=2, nl=True, nobr=False,
        >>>                  explicit=0)
        >>> result = ut.dict_str(dict_, **dictkw)
        >>> print(result)
    """
    import utool as ut

    stritems = dictkw.pop('si', dictkw.pop('stritems', False))
    if stritems:
        dictkw['strkeys'] = True
        dictkw['strvals'] = True

    dictkw['strkeys'] = dictkw.pop('sk', dictkw.pop('strkeys', False))
    dictkw['strvals'] = dictkw.pop('sv', dictkw.pop('strvals', False))

    newlines = dictkw.pop('nl', dictkw.pop('newlines', True))
    truncate = dictkw.pop('truncate', False)
    dictkw['nl'] = _rectify_countdown_or_bool(newlines)
    dictkw['truncate'] = _rectify_countdown_or_bool(truncate)

    nobraces = dictkw.pop('nobr', dictkw.pop('nobraces', False))
    align = dictkw.pop('align', False)

    # Doesn't actually put in trailing comma if on same line
    trailing_sep = dictkw.get('trailing_sep', True)
    explicit = dictkw.get('explicit', False)
    with_comma  = True
    itemsep = dictkw.get('itemsep', ' ')

    if len(dict_) == 0:
        return 'dict()' if explicit else '{}'

    itemstr_list = dict_itemstr_list(dict_, **dictkw)

    do_truncate = truncate is not False and (truncate is True or truncate == 0)
    if do_truncate:
        truncatekw = dictkw.get('truncatekw', {})
        itemstr_list = [truncate_str(item, **truncatekw) for item in itemstr_list]

    if nobraces:
        lbr, rbr = '', ''
    elif explicit:
        lbr, rbr = 'dict(', ')'
    else:
        lbr, rbr = '{', '}'

    if newlines:
        sep = ',\n' if with_comma else '\n'
        if nobraces:
            retstr =  sep.join(itemstr_list)
            if trailing_sep:
                retstr += ','
        else:
            parts = [ut.indent(itemstr, '    ') for itemstr in itemstr_list]
            body_str = sep.join(parts)
            if trailing_sep:
                body_str += ','
            retstr =  (lbr + '\n' + body_str + '\n' + rbr)
            if align:
                retstr = ut.align(retstr, ':')
    else:
        sep = ',' + itemsep if with_comma else itemsep
        # hack away last trailing comma
        sequence_str = sep.join(itemstr_list)
        retstr = lbr +  sequence_str + rbr
    # Is there a way to make truncate for dict_str compatible with list_str?
    return retstr