# Buggy code
def init_pluginsystem(
    settings, safe_mode=False, ignore_blacklist=True, connectivity_checker=None
):
    """Initializes the plugin manager based on the settings."""

    import os

    # we need this so that octoprint.plugins is in sys.modules and no warnings are caused when loading bundled plugins
    import octoprint.plugins

    logger = log.getLogger(__name__ + ".startup")

    plugin_folders = [
        (
            os.path.abspath(
                os.path.join(os.path.dirname(os.path.realpath(__file__)), "plugins")
            ),
            "octoprint.plugins",
            True,
        ),
        settings.getBaseFolder("plugins"),
    ]
    plugin_entry_points = ["octoprint.plugin"]
    plugin_disabled_list = settings.get(["plugins", "_disabled"])

    plugin_blacklist = []
    if not ignore_blacklist and settings.getBoolean(
        ["server", "pluginBlacklist", "enabled"]
    ):
        plugin_blacklist = get_plugin_blacklist(
            settings, connectivity_checker=connectivity_checker
        )

    plugin_validators = []
    if safe_mode:

        def validator(phase, plugin_info):
            if phase in ("before_import", "before_load", "before_enable"):
                setattr(plugin_info, "safe_mode_victim", not plugin_info.bundled)
                if not plugin_info.bundled:
                    return False
            return True

        plugin_validators.append(validator)

    compatibility_ignored_list = settings.get(["plugins", "_forcedCompatible"])

    from octoprint.plugin import plugin_manager

    pm = plugin_manager(
        init=True,
        plugin_folders=plugin_folders,
        plugin_entry_points=plugin_entry_points,
        plugin_disabled_list=plugin_disabled_list,
        plugin_blacklist=plugin_blacklist,
        plugin_validators=plugin_validators,
        compatibility_ignored_list=compatibility_ignored_list,
    )

    settings_overlays = dict()
    disabled_from_overlays = dict()

    def handle_plugin_loaded(name, plugin):
        if plugin.instance and hasattr(plugin.instance, "__plugin_settings_overlay__"):
            plugin.needs_restart = True

            # plugin has a settings overlay, inject it
            overlay_definition = getattr(plugin.instance, "__plugin_settings_overlay__")
            if isinstance(overlay_definition, (tuple, list)):
                overlay_definition, order = overlay_definition
            else:
                order = None

            overlay = settings.load_overlay(overlay_definition)

            if "plugins" in overlay and "_disabled" in overlay["plugins"]:
                disabled_plugins = overlay["plugins"]["_disabled"]
                del overlay["plugins"]["_disabled"]
                disabled_from_overlays[name] = (disabled_plugins, order)

            settings_overlays[name] = overlay
            logger.debug("Found settings overlay on plugin {}".format(name))

    def handle_plugins_loaded(
        startup=False, initialize_implementations=True, force_reload=None
    ):
        if not startup:
            return

        sorted_disabled_from_overlays = sorted(
            [
                (key, value[0], value[1])
                for key, value in disabled_from_overlays.items()
            ],
            key=lambda x: (x[2] is None, x[2], x[0]),
        )

        disabled_list = pm.plugin_disabled_list
        already_processed = []
        for name, addons, _ in sorted_disabled_from_overlays:
            if not name in disabled_list and not name.endswith("disabled"):
                for addon in addons:
                    if addon in disabled_list:
                        continue

                    if addon in already_processed:
                        logger.info(
                            "Plugin {} wants to disable plugin {}, but that was already processed".format(
                                name, addon
                            )
                        )

                    if not addon in already_processed and not addon in disabled_list:
                        disabled_list.append(addon)
                        logger.info(
                            "Disabling plugin {} as defined by plugin {}".format(
                                addon, name
                            )
                        )
                already_processed.append(name)

    def handle_plugin_enabled(name, plugin):
        if name in settings_overlays:
            settings.add_overlay(settings_overlays[name])
            logger.info("Added settings overlay from plugin {}".format(name))

    pm.on_plugin_loaded = handle_plugin_loaded
    pm.on_plugins_loaded = handle_plugins_loaded
    pm.on_plugin_enabled = handle_plugin_enabled
    pm.reload_plugins(startup=True, initialize_implementations=False)
    return pm


# Traceback (TypeError)
2019-10-08 14:14:51,399 - octoprint.server.preemptive_cache - INFO - Preemptively caching / (ui _default) for {'base_url': 'http://192.168.0.113:5000/', 'path': '/', 'query_string': 'l10n=en'}
[2019-10-08 14:14:51,556] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "c:\octoprint\venv\lib\site-packages\flask\_compat.py", line 33, in reraise
raise value
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 181, in index
fetch_template_data(refresh=force_refresh)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 755, in fetch_template_data
sorted_missing = sorted(missing_in_order, key=key_func)
TypeError: '<' not supported between instances of 'str' and 'NoneType'
[2019-10-08 14:14:51,743] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "c:\octoprint\venv\lib\site-packages\flask\_compat.py", line 33, in reraise
raise value
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 181, in index
fetch_template_data(refresh=force_refresh)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 755, in fetch_template_data
sorted_missing = sorted(missing_in_order, key=key_func)
TypeError: '<' not supported between instances of 'str' and 'NoneType'
[2019-10-08 14:14:51,884] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "c:\octoprint\venv\lib\site-packages\flask\_compat.py", line 33, in reraise
raise value
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 181, in index
fetch_template_data(refresh=force_refresh)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 755, in fetch_template_data
sorted_missing = sorted(missing_in_order, key=key_func)
TypeError: '<' not supported between instances of 'str' and 'NoneType'

# Fixed code
def init_pluginsystem(
    settings, safe_mode=False, ignore_blacklist=True, connectivity_checker=None
):
    """Initializes the plugin manager based on the settings."""

    import os

    # we need this so that octoprint.plugins is in sys.modules and no warnings are caused when loading bundled plugins
    import octoprint.plugins

    logger = log.getLogger(__name__ + ".startup")

    plugin_folders = [
        (
            os.path.abspath(
                os.path.join(os.path.dirname(os.path.realpath(__file__)), "plugins")
            ),
            "octoprint.plugins",
            True,
        ),
        settings.getBaseFolder("plugins"),
    ]
    plugin_entry_points = ["octoprint.plugin"]
    plugin_disabled_list = settings.get(["plugins", "_disabled"])

    plugin_blacklist = []
    if not ignore_blacklist and settings.getBoolean(
        ["server", "pluginBlacklist", "enabled"]
    ):
        plugin_blacklist = get_plugin_blacklist(
            settings, connectivity_checker=connectivity_checker
        )

    plugin_validators = []
    if safe_mode:

        def validator(phase, plugin_info):
            if phase in ("before_import", "before_load", "before_enable"):
                setattr(plugin_info, "safe_mode_victim", not plugin_info.bundled)
                if not plugin_info.bundled:
                    return False
            return True

        plugin_validators.append(validator)

    compatibility_ignored_list = settings.get(["plugins", "_forcedCompatible"])

    from octoprint.plugin import plugin_manager

    pm = plugin_manager(
        init=True,
        plugin_folders=plugin_folders,
        plugin_entry_points=plugin_entry_points,
        plugin_disabled_list=plugin_disabled_list,
        plugin_blacklist=plugin_blacklist,
        plugin_validators=plugin_validators,
        compatibility_ignored_list=compatibility_ignored_list,
    )

    settings_overlays = dict()
    disabled_from_overlays = dict()

    def handle_plugin_loaded(name, plugin):
        if plugin.instance and hasattr(plugin.instance, "__plugin_settings_overlay__"):
            plugin.needs_restart = True

            # plugin has a settings overlay, inject it
            overlay_definition = getattr(plugin.instance, "__plugin_settings_overlay__")
            if isinstance(overlay_definition, (tuple, list)):
                overlay_definition, order = overlay_definition
            else:
                order = None

            overlay = settings.load_overlay(overlay_definition)

            if "plugins" in overlay and "_disabled" in overlay["plugins"]:
                disabled_plugins = overlay["plugins"]["_disabled"]
                del overlay["plugins"]["_disabled"]
                disabled_from_overlays[name] = (disabled_plugins, order)

            settings_overlays[name] = overlay
            logger.debug("Found settings overlay on plugin {}".format(name))

    def handle_plugins_loaded(
        startup=False, initialize_implementations=True, force_reload=None
    ):
        if not startup:
            return

        from octoprint.util import sv

        sorted_disabled_from_overlays = sorted(
            [
                (key, value[0], value[1])
                for key, value in disabled_from_overlays.items()
            ],
            key=lambda x: (x[2] is None, sv(x[2]), sv(x[0])),
        )

        disabled_list = pm.plugin_disabled_list
        already_processed = []
        for name, addons, _ in sorted_disabled_from_overlays:
            if not name in disabled_list and not name.endswith("disabled"):
                for addon in addons:
                    if addon in disabled_list:
                        continue

                    if addon in already_processed:
                        logger.info(
                            "Plugin {} wants to disable plugin {}, but that was already processed".format(
                                name, addon
                            )
                        )

                    if not addon in already_processed and not addon in disabled_list:
                        disabled_list.append(addon)
                        logger.info(
                            "Disabling plugin {} as defined by plugin {}".format(
                                addon, name
                            )
                        )
                already_processed.append(name)

    def handle_plugin_enabled(name, plugin):
        if name in settings_overlays:
            settings.add_overlay(settings_overlays[name])
            logger.info("Added settings overlay from plugin {}".format(name))

    pm.on_plugin_loaded = handle_plugin_loaded
    pm.on_plugins_loaded = handle_plugins_loaded
    pm.on_plugin_enabled = handle_plugin_enabled
    pm.reload_plugins(startup=True, initialize_implementations=False)
    return pm