# Buggy code
def convert(self, mode=None, matrix=None, dither=None, palette=WEB, colors=256):
    """
    Returns a converted copy of this image. For the "P" mode, this
    method translates pixels through the palette.  If mode is
    omitted, a mode is chosen so that all information in the image
    and the palette can be represented without a palette.

    The current version supports all possible conversions between
    "L", "RGB" and "CMYK." The **matrix** argument only supports "L"
    and "RGB".

    When translating a color image to black and white (mode "L"),
    the library uses the ITU-R 601-2 luma transform::

        L = R * 299/1000 + G * 587/1000 + B * 114/1000

    The default method of converting a greyscale ("L") or "RGB"
    image into a bilevel (mode "1") image uses Floyd-Steinberg
    dither to approximate the original image luminosity levels. If
    dither is NONE, all non-zero values are set to 255 (white). To
    use other thresholds, use the :py:meth:`~PIL.Image.Image.point`
    method.

    :param mode: The requested mode. See: :ref:`concept-modes`.
    :param matrix: An optional conversion matrix.  If given, this
       should be 4- or 12-tuple containing floating point values.
    :param dither: Dithering method, used when converting from
       mode "RGB" to "P" or from "RGB" or "L" to "1".
       Available methods are NONE or FLOYDSTEINBERG (default).
    :param palette: Palette to use when converting from mode "RGB"
       to "P".  Available palettes are WEB or ADAPTIVE.
    :param colors: Number of colors to use for the ADAPTIVE palette.
       Defaults to 256.
    :rtype: :py:class:`~PIL.Image.Image`
    :returns: An :py:class:`~PIL.Image.Image` object.
    """

    self.load()

    if not mode and self.mode == "P":
        # determine default mode
        if self.palette:
            mode = self.palette.mode
        else:
            mode = "RGB"
    if not mode or (mode == self.mode and not matrix):
        return self.copy()

    if matrix:
        # matrix conversion
        if mode not in ("L", "RGB"):
            raise ValueError("illegal conversion")
        im = self.im.convert_matrix(mode, matrix)
        return self._new(im)

    if mode == "P" and self.mode == "RGBA":
        return self.quantize(colors)

    trns = None
    delete_trns = False
    # transparency handling
    if "transparency" in self.info and self.info["transparency"] is not None:
        if self.mode in ("L", "RGB") and mode == "RGBA":
            # Use transparent conversion to promote from transparent
            # color to an alpha channel.
            new_im = self._new(
                self.im.convert_transparent(mode, self.info["transparency"])
            )
            del new_im.info["transparency"]
            return new_im
        elif self.mode in ("L", "RGB", "P") and mode in ("L", "RGB", "P"):
            t = self.info["transparency"]
            if isinstance(t, bytes):
                # Dragons. This can't be represented by a single color
                warnings.warn(
                    "Palette images with Transparency  "
                    + " expressed in bytes should be converted "
                    + "to RGBA images"
                )
                delete_trns = True
            else:
                # get the new transparency color.
                # use existing conversions
                trns_im = Image()._new(core.new(self.mode, (1, 1)))
                if self.mode == "P":
                    trns_im.putpalette(self.palette)
                    if isinstance(t, tuple):
                        try:
                            t = trns_im.palette.getcolor(t)
                        except:
                            raise ValueError(
                                "Couldn't allocate a palette color for transparency"
                            )
                trns_im.putpixel((0, 0), t)

                if mode in ("L", "RGB"):
                    trns_im = trns_im.convert(mode)
                else:
                    # can't just retrieve the palette number, got to do it
                    # after quantization.
                    trns_im = trns_im.convert("RGB")
                trns = trns_im.getpixel((0, 0))

        elif self.mode == "P" and mode == "RGBA":
            t = self.info["transparency"]
            delete_trns = True

            if isinstance(t, bytes):
                self.im.putpalettealphas(t)
            elif isinstance(t, int):
                self.im.putpalettealpha(t, 0)
            else:
                raise ValueError("Transparency for P mode should" + " be bytes or int")

    if mode == "P" and palette == ADAPTIVE:
        im = self.im.quantize(colors)
        new = self._new(im)
        from . import ImagePalette

        new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB"))
        if delete_trns:
            # This could possibly happen if we requantize to fewer colors.
            # The transparency would be totally off in that case.
            del new.info["transparency"]
        if trns is not None:
            try:
                new.info["transparency"] = new.palette.getcolor(trns)
            except:
                # if we can't make a transparent color, don't leave the old
                # transparency hanging around to mess us up.
                del new.info["transparency"]
                warnings.warn("Couldn't allocate palette entry " + "for transparency")
        return new

    # colorspace conversion
    if dither is None:
        dither = FLOYDSTEINBERG

    try:
        im = self.im.convert(mode, dither)
    except ValueError:
        try:
            # normalize source image and try again
            im = self.im.convert(getmodebase(self.mode))
            im = im.convert(mode, dither)
        except KeyError:
            raise ValueError("illegal conversion")

    new_im = self._new(im)
    if delete_trns:
        # crash fail if we leave a bytes transparency in an rgb/l mode.
        del new_im.info["transparency"]
    if trns is not None:
        if new_im.mode == "P":
            try:
                new_im.info["transparency"] = new_im.palette.getcolor(trns)
            except:
                del new_im.info["transparency"]
                warnings.warn("Couldn't allocate palette entry " + "for transparency")
        else:
            new_im.info["transparency"] = trns
    return new_im


# Traceback (TypeError)
Traceback (most recent call last):
File "C:/base2_nl/py-grayscale/minimalExampleBug.py", line 5, in <module>
im.save('tbbn2c16-out.png')
File "C:\Python36\lib\site-packages\PIL\Image.py", line 1935, in save
save_handler(self, fp, filename)
File "C:\Python36\lib\site-packages\PIL\PngImagePlugin.py", line 790, in _save
transparency = max(0, min(65535, transparency))
TypeError: '<' not supported between instances of 'tuple' and 'int'

# Fixed code
def convert(self, mode=None, matrix=None, dither=None, palette=WEB, colors=256):
    """
    Returns a converted copy of this image. For the "P" mode, this
    method translates pixels through the palette.  If mode is
    omitted, a mode is chosen so that all information in the image
    and the palette can be represented without a palette.

    The current version supports all possible conversions between
    "L", "RGB" and "CMYK." The **matrix** argument only supports "L"
    and "RGB".

    When translating a color image to black and white (mode "L"),
    the library uses the ITU-R 601-2 luma transform::

        L = R * 299/1000 + G * 587/1000 + B * 114/1000

    The default method of converting a greyscale ("L") or "RGB"
    image into a bilevel (mode "1") image uses Floyd-Steinberg
    dither to approximate the original image luminosity levels. If
    dither is NONE, all non-zero values are set to 255 (white). To
    use other thresholds, use the :py:meth:`~PIL.Image.Image.point`
    method.

    :param mode: The requested mode. See: :ref:`concept-modes`.
    :param matrix: An optional conversion matrix.  If given, this
       should be 4- or 12-tuple containing floating point values.
    :param dither: Dithering method, used when converting from
       mode "RGB" to "P" or from "RGB" or "L" to "1".
       Available methods are NONE or FLOYDSTEINBERG (default).
    :param palette: Palette to use when converting from mode "RGB"
       to "P".  Available palettes are WEB or ADAPTIVE.
    :param colors: Number of colors to use for the ADAPTIVE palette.
       Defaults to 256.
    :rtype: :py:class:`~PIL.Image.Image`
    :returns: An :py:class:`~PIL.Image.Image` object.
    """

    self.load()

    if not mode and self.mode == "P":
        # determine default mode
        if self.palette:
            mode = self.palette.mode
        else:
            mode = "RGB"
    if not mode or (mode == self.mode and not matrix):
        return self.copy()

    has_transparency = self.info.get("transparency") is not None
    if matrix:
        # matrix conversion
        if mode not in ("L", "RGB"):
            raise ValueError("illegal conversion")
        im = self.im.convert_matrix(mode, matrix)
        new = self._new(im)
        if has_transparency and self.im.bands == 3:
            transparency = new.info["transparency"]

            def convert_transparency(m, v):
                v = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * 0.5
                return max(0, min(255, int(v)))

            if mode == "L":
                transparency = convert_transparency(matrix, transparency)
            elif len(mode) == 3:
                transparency = tuple(
                    [
                        convert_transparency(matrix[i * 4 : i * 4 + 4], transparency)
                        for i in range(0, len(transparency))
                    ]
                )
            new.info["transparency"] = transparency
        return new

    if mode == "P" and self.mode == "RGBA":
        return self.quantize(colors)

    trns = None
    delete_trns = False
    # transparency handling
    if has_transparency:
        if self.mode in ("L", "RGB") and mode == "RGBA":
            # Use transparent conversion to promote from transparent
            # color to an alpha channel.
            new_im = self._new(
                self.im.convert_transparent(mode, self.info["transparency"])
            )
            del new_im.info["transparency"]
            return new_im
        elif self.mode in ("L", "RGB", "P") and mode in ("L", "RGB", "P"):
            t = self.info["transparency"]
            if isinstance(t, bytes):
                # Dragons. This can't be represented by a single color
                warnings.warn(
                    "Palette images with Transparency  "
                    + " expressed in bytes should be converted "
                    + "to RGBA images"
                )
                delete_trns = True
            else:
                # get the new transparency color.
                # use existing conversions
                trns_im = Image()._new(core.new(self.mode, (1, 1)))
                if self.mode == "P":
                    trns_im.putpalette(self.palette)
                    if isinstance(t, tuple):
                        try:
                            t = trns_im.palette.getcolor(t)
                        except:
                            raise ValueError(
                                "Couldn't allocate a palette color for transparency"
                            )
                trns_im.putpixel((0, 0), t)

                if mode in ("L", "RGB"):
                    trns_im = trns_im.convert(mode)
                else:
                    # can't just retrieve the palette number, got to do it
                    # after quantization.
                    trns_im = trns_im.convert("RGB")
                trns = trns_im.getpixel((0, 0))

        elif self.mode == "P" and mode == "RGBA":
            t = self.info["transparency"]
            delete_trns = True

            if isinstance(t, bytes):
                self.im.putpalettealphas(t)
            elif isinstance(t, int):
                self.im.putpalettealpha(t, 0)
            else:
                raise ValueError("Transparency for P mode should" + " be bytes or int")

    if mode == "P" and palette == ADAPTIVE:
        im = self.im.quantize(colors)
        new = self._new(im)
        from . import ImagePalette

        new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB"))
        if delete_trns:
            # This could possibly happen if we requantize to fewer colors.
            # The transparency would be totally off in that case.
            del new.info["transparency"]
        if trns is not None:
            try:
                new.info["transparency"] = new.palette.getcolor(trns)
            except:
                # if we can't make a transparent color, don't leave the old
                # transparency hanging around to mess us up.
                del new.info["transparency"]
                warnings.warn("Couldn't allocate palette entry " + "for transparency")
        return new

    # colorspace conversion
    if dither is None:
        dither = FLOYDSTEINBERG

    try:
        im = self.im.convert(mode, dither)
    except ValueError:
        try:
            # normalize source image and try again
            im = self.im.convert(getmodebase(self.mode))
            im = im.convert(mode, dither)
        except KeyError:
            raise ValueError("illegal conversion")

    new_im = self._new(im)
    if delete_trns:
        # crash fail if we leave a bytes transparency in an rgb/l mode.
        del new_im.info["transparency"]
    if trns is not None:
        if new_im.mode == "P":
            try:
                new_im.info["transparency"] = new_im.palette.getcolor(trns)
            except:
                del new_im.info["transparency"]
                warnings.warn("Couldn't allocate palette entry " + "for transparency")
        else:
            new_im.info["transparency"] = trns
    return new_im