# Buggy code
def to_str(s, encoding=None, errors="strict"):
    """
    Given str, bytes, bytearray, or unicode (py2), return str
    """
    # This shouldn't be six.string_types because if we're on PY2 and we already
    # have a string, we should just return it.
    if isinstance(s, str):
        return s
    if six.PY3:
        if isinstance(s, (bytes, bytearray)):
            if encoding:
                return s.decode(encoding, errors)
            else:
                try:
                    # Try UTF-8 first
                    return s.decode("utf-8", errors)
                except UnicodeDecodeError:
                    # Fall back to detected encoding
                    return s.decode(__salt_system_encoding__, errors)
        raise TypeError("expected str, bytes, or bytearray not {}".format(type(s)))
    else:
        if isinstance(s, bytearray):
            return str(s)  # future lint: disable=blacklisted-function
        if isinstance(s, unicode):  # pylint: disable=incompatible-py3-code,undefined-variable
            if encoding:
                return s.encode(encoding, errors)
            else:
                try:
                    # Try UTF-8 first
                    return s.encode("utf-8", errors)
                except UnicodeEncodeError:
                    # Fall back to detected encoding
                    return s.encode(__salt_system_encoding__, errors)
        raise TypeError("expected str, bytearray, or unicode")


# Traceback (TypeError)
2018-04-04 05:37:16,969 [salt.loaded.int.pillar.pillar_ldap:262 ][CRITICAL][5777] Failed to retrieve pillar data from LDAP:
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/salt/pillar/pillar_ldap.py", line 256, in _do_search
**connargs)['results']
File "/usr/lib/python2.7/site-packages/salt/modules/ldapmod.py", line 150, in search
results = _ldap.search_s(dn, int(scope), filter, attrs)
File "/usr/lib64/python2.7/site-packages/ldap/ldapobject.py", line 552, in search_s
return self.search_ext_s(base,scope,filterstr,attrlist,attrsonly,None,None,timeout=self.timeout)
File "/usr/lib64/python2.7/site-packages/ldap/ldapobject.py", line 545, in search_ext_s
msgid = self.search_ext(base,scope,filterstr,attrlist,attrsonly,serverctrls,clientctrls,timeout,sizelimit)
File "/usr/lib64/python2.7/site-packages/ldap/ldapobject.py", line 541, in search_ext
timeout,sizelimit,
File "/usr/lib64/python2.7/site-packages/ldap/ldapobject.py", line 99, in _ldap_call
result = func(*args,**kwargs)
TypeError: ('expected string in list', u'uid')

# Fixed code
def to_str(s, encoding=None, errors="strict", normalize=False):
    """
    Given str, bytes, bytearray, or unicode (py2), return str
    """

    def _normalize(s):
        try:
            return unicodedata.normalize("NFC", s) if normalize else s
        except TypeError:
            return s

    # This shouldn't be six.string_types because if we're on PY2 and we already
    # have a string, we should just return it.
    if isinstance(s, str):
        return _normalize(s)
    if six.PY3:
        if isinstance(s, (bytes, bytearray)):
            if encoding:
                return _normalize(s.decode(encoding, errors))
            else:
                try:
                    # Try UTF-8 first
                    return _normalize(s.decode("utf-8", errors))
                except UnicodeDecodeError:
                    # Fall back to detected encoding
                    return _normalize(s.decode(__salt_system_encoding__, errors))
        raise TypeError("expected str, bytes, or bytearray not {}".format(type(s)))
    else:
        if isinstance(s, bytearray):
            return str(s)  # future lint: disable=blacklisted-function
        if isinstance(s, unicode):  # pylint: disable=incompatible-py3-code,undefined-variable
            if encoding:
                return _normalize(s).encode(encoding, errors)
            else:
                try:
                    # Try UTF-8 first
                    return _normalize(s).encode("utf-8", errors)
                except UnicodeEncodeError:
                    # Fall back to detected encoding
                    return _normalize(s).encode(__salt_system_encoding__, errors)
        raise TypeError("expected str, bytearray, or unicode")