# Buggy code
def set_string_function(f, repr=True):
    """
    Set a Python function to be used when pretty printing arrays.

    Parameters
    ----------
    f : function or None
        Function to be used to pretty print arrays. The function should expect
        a single array argument and return a string of the representation of
        the array. If None, the function is reset to the default NumPy function
        to print arrays.
    repr : bool, optional
        If True (default), the function for pretty printing (``__repr__``)
        is set, if False the function that returns the default string
        representation (``__str__``) is set.

    See Also
    --------
    set_printoptions, get_printoptions

    Examples
    --------
    >>> def pprint(arr):
    ...     return 'HA! - What are you going to do now?'
    ...
    >>> np.set_string_function(pprint)
    >>> a = np.arange(10)
    >>> a
    HA! - What are you going to do now?
    >>> print(a)
    [0 1 2 3 4 5 6 7 8 9]

    We can reset the function to the default:

    >>> np.set_string_function(None)
    >>> a
    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

    `repr` affects either pretty printing or normal string representation.
    Note that ``__repr__`` is still affected by setting ``__str__``
    because the width of each array element in the returned string becomes
    equal to the length of the result of ``__str__()``.

    >>> x = np.arange(4)
    >>> np.set_string_function(lambda x:'random', repr=False)
    >>> x.__str__()
    'random'
    >>> x.__repr__()
    'array([     0,      1,      2,      3])'

    """
    if f is None:
        if repr:
            return multiarray.set_string_function(array_repr, 1)
        else:
            return multiarray.set_string_function(array_str, 0)
    else:
        return multiarray.set_string_function(f, repr)


# Traceback (TypeError)
In [1]: import numpy as np

In [2]: class Sub(np.ndarray):
...:     def __array_function__(*args, **kwargs):
...:         return NotImplemented
...:

In [3]: repr(np.array(1).view(Sub))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-d6ff958f4fcf> in <module>()
----> 1 repr(np.array(1).view(Sub))

~/dev/numpy/numpy/core/overrides.py in public_api(*args, **kwargs)
149             relevant_args = dispatcher(*args, **kwargs)
150             return array_function_implementation_or_override(
--> 151                 implementation, public_api, relevant_args, args, kwargs)
152         return public_api
153

~/dev/numpy/numpy/core/overrides.py in array_function_implementation_or_override(implementation, public_api, relevant_args, args, kwargs)
108     raise TypeError('no implementation found for {} on types that implement '
109                     '__array_function__: {}'
--> 110                     .format(public_api, list(map(type, overloaded_args))))
111
112

TypeError: no implementation found for <function array_repr at 0x105692e18> on types that implement __array_function__: [<class '__main__.Sub'>]

# Fixed code
def set_string_function(f, repr=True):
    """
    Set a Python function to be used when pretty printing arrays.

    Parameters
    ----------
    f : function or None
        Function to be used to pretty print arrays. The function should expect
        a single array argument and return a string of the representation of
        the array. If None, the function is reset to the default NumPy function
        to print arrays.
    repr : bool, optional
        If True (default), the function for pretty printing (``__repr__``)
        is set, if False the function that returns the default string
        representation (``__str__``) is set.

    See Also
    --------
    set_printoptions, get_printoptions

    Examples
    --------
    >>> def pprint(arr):
    ...     return 'HA! - What are you going to do now?'
    ...
    >>> np.set_string_function(pprint)
    >>> a = np.arange(10)
    >>> a
    HA! - What are you going to do now?
    >>> print(a)
    [0 1 2 3 4 5 6 7 8 9]

    We can reset the function to the default:

    >>> np.set_string_function(None)
    >>> a
    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

    `repr` affects either pretty printing or normal string representation.
    Note that ``__repr__`` is still affected by setting ``__str__``
    because the width of each array element in the returned string becomes
    equal to the length of the result of ``__str__()``.

    >>> x = np.arange(4)
    >>> np.set_string_function(lambda x:'random', repr=False)
    >>> x.__str__()
    'random'
    >>> x.__repr__()
    'array([     0,      1,      2,      3])'

    """
    if f is None:
        if repr:
            return multiarray.set_string_function(_default_array_repr, 1)
        else:
            return multiarray.set_string_function(_default_array_str, 0)
    else:
        return multiarray.set_string_function(f, repr)