Sort ``fonts`` with the ordering preferences defined
        by ``sortBy``. ``sortBy`` must be one of the following:

        * sort description string
        * :class:`BaseInfo` attribute name
        * sort value function
        * list/tuple containing sort description strings, :class:`BaseInfo`
          attribute names and/or sort value functions
        * ``"magic"``

        Sort Description Strings
        ------------------------

        The sort description strings, and how they modify the sort, are:

        +--------------------+--------------------------------------+
        | ``"familyName"``   | Family names by alphabetical order.  |
        +--------------------+--------------------------------------+
        | ``"styleName"``    | Style names by alphabetical order.   |
        +--------------------+--------------------------------------+
        | ``"isItalic"``     | Italics before romans.               |
        +--------------------+--------------------------------------+
        | ``"isRoman"``      | Romans before italics.               |
        +--------------------+--------------------------------------+
        | ``"widthValue"``   | Width values by numerical order.     |
        +--------------------+--------------------------------------+
        | ``"weightValue"``  | Weight values by numerical order.    |
        +--------------------+--------------------------------------+
        | ``"monospace"``    | Monospaced before proportional.      |
        +--------------------+--------------------------------------+
        | ``"proportional"`` | Proportional before monospaced.      |
        +--------------------+--------------------------------------+

        ::

            >>> fonts.sortBy("familyName", "styleName")


        Font Info Attribute Names
        -------------------------

        Any :class:`BaseFont` attribute name may be included as
        a sort option. For example, to sort by x-height value,
        you'd use the ``"xHeight"`` attribute name.

        ::

            >>> fonts.sortBy("xHeight")


        Sort Value Function
        -------------------

        A sort value function must be a function that accepts
        one argument, ``font``. This function must return
        a sortable value for the given font. For example:

        ::

            def glyphCountSortValue(font):
                return len(font)

            >>> fonts.sortBy(glyphCountSortValue)

        A list of sort description strings and/or sort functions
        may also be provided. This should be in order of most
        to least important. For example, to sort by family name
        and then style name, do this:


        "magic"
        -------

        If "magic" is given for ``sortBy``, the fonts will be
        sorted based on this sort description sequence:

        * ``"familyName"``
        * ``"isProportional"``
        * ``"widthValue"``
        * ``"weightValue"``
        * ``"styleName"``
        * ``"isRoman"``

        ::

            >>> fonts.sortBy("magic")

def sortBy(self, sortOptions, reverse=False):
        """
        Sort ``fonts`` with the ordering preferences defined
        by ``sortBy``. ``sortBy`` must be one of the following:

        * sort description string
        * :class:`BaseInfo` attribute name
        * sort value function
        * list/tuple containing sort description strings, :class:`BaseInfo`
          attribute names and/or sort value functions
        * ``"magic"``

        Sort Description Strings
        ------------------------

        The sort description strings, and how they modify the sort, are:

        +--------------------+--------------------------------------+
        | ``"familyName"``   | Family names by alphabetical order.  |
        +--------------------+--------------------------------------+
        | ``"styleName"``    | Style names by alphabetical order.   |
        +--------------------+--------------------------------------+
        | ``"isItalic"``     | Italics before romans.               |
        +--------------------+--------------------------------------+
        | ``"isRoman"``      | Romans before italics.               |
        +--------------------+--------------------------------------+
        | ``"widthValue"``   | Width values by numerical order.     |
        +--------------------+--------------------------------------+
        | ``"weightValue"``  | Weight values by numerical order.    |
        +--------------------+--------------------------------------+
        | ``"monospace"``    | Monospaced before proportional.      |
        +--------------------+--------------------------------------+
        | ``"proportional"`` | Proportional before monospaced.      |
        +--------------------+--------------------------------------+

        ::

            >>> fonts.sortBy("familyName", "styleName")


        Font Info Attribute Names
        -------------------------

        Any :class:`BaseFont` attribute name may be included as
        a sort option. For example, to sort by x-height value,
        you'd use the ``"xHeight"`` attribute name.

        ::

            >>> fonts.sortBy("xHeight")


        Sort Value Function
        -------------------

        A sort value function must be a function that accepts
        one argument, ``font``. This function must return
        a sortable value for the given font. For example:

        ::

            def glyphCountSortValue(font):
                return len(font)

            >>> fonts.sortBy(glyphCountSortValue)

        A list of sort description strings and/or sort functions
        may also be provided. This should be in order of most
        to least important. For example, to sort by family name
        and then style name, do this:


        "magic"
        -------

        If "magic" is given for ``sortBy``, the fonts will be
        sorted based on this sort description sequence:

        * ``"familyName"``
        * ``"isProportional"``
        * ``"widthValue"``
        * ``"weightValue"``
        * ``"styleName"``
        * ``"isRoman"``

        ::

            >>> fonts.sortBy("magic")
        """
        from types import FunctionType
        from fontTools.misc.py23 import basestring
        valueGetters = dict(
            familyName=_sortValue_familyName,
            styleName=_sortValue_styleName,
            isRoman=_sortValue_isRoman,
            isItalic=_sortValue_isItalic,
            widthValue=_sortValue_widthValue,
            weightValue=_sortValue_weightValue,
            isProportional=_sortValue_isProportional,
            isMonospace=_sortValue_isMonospace
        )
        if isinstance(sortOptions, basestring) or isinstance(sortOptions, FunctionType):
            sortOptions = [sortOptions]
        if not isinstance(sortOptions, (list, tuple)):
            raise ValueError("sortOptions must a string, list or function.")
        if not sortOptions:
            raise ValueError("At least one sort option must be defined.")
        if sortOptions == ["magic"]:
            sortOptions = [
                "familyName",
                "isProportional",
                "widthValue",
                "weightValue",
                "styleName",
                "isRoman"
            ]
        sorter = []
        for originalIndex, font in enumerate(self):
            sortable = []
            for valueName in sortOptions:
                if isinstance(valueName, FunctionType):
                    value = valueName(font)
                elif valueName in valueGetters:
                    value = valueGetters[valueName](font)
                elif hasattr(font.info, valueName):
                    value = getattr(font.info, valueName)
                else:
                    raise ValueError("Unknown sort option: %s" % repr(valueGetter))
                sortable.append(value)
            sortable.append(originalIndex)
            sortable.append(font)
            sorter.append(tuple(sortable))
        sorter.sort()
        fonts = [i[-1] for i in sorter]
        del self[:]
        self.extend(fonts)
        if reverse:
            self.reverse()