Source code for spyder.api.plugin_registration.mixins

# -----------------------------------------------------------------------------
# Copyright (c) 2021- Spyder Project Contributors
#
# Released under the terms of the MIT License
# (see LICENSE.txt in the project root directory for details)
# -----------------------------------------------------------------------------

"""
Spyder API plugin registration mixins.

.. deprecated:: 6.2

    This module will be moved to a private :mod:`!spyder.api._mixins` module
    and become a deprecated alias, raising a :exc:`DeprecationWarning`,
    that will be removed in Spyder 7.0.

    It is a private implementation detail of the plugin decorators in
    :mod:`spyder.api.plugin_registration.decorators`, and wasn't designed
    or intended to be used directly by external code. Plugins access its
    functionality through the :class:`~spyder.api.plugins.SpyderPluginV2`
    class instead.
"""

from __future__ import annotations

# Standard library imports
import logging

from spyder.api.exceptions import SpyderAPIError
from spyder.api.plugins import Plugins

logger = logging.getLogger(__name__)


[docs] class SpyderPluginObserver: """ Mixin to receive and respond to changes in Spyder plugin availability. This mixin enables a class to receive notifications when a plugin is available, by registering methods using the :func:`~spyder.api.plugin_registration.decorators.on_plugin_available` decorator. When any of the requested plugins is ready, the corresponding registered method is called. Normally inherited and initialized automatically through :class:`~spyder.api.plugins.SpyderPluginV2` rather than used directly. .. caution:: This mixin will only operate over the plugins listed under the :attr:`~spyder.api.plugins.SpyderPluginV2.REQUIRES` or :attr:`~spyder.api.plugins.SpyderPluginV2.OPTIONAL` class constants of the class inheriting the mixin. """
[docs] def __init__(self) -> None: """ Set up the plugin listeners for any decorated methods of the class. Called automatically by :meth:`SpyderPluginV2.__init__() <spyder.api.plugins.SpyderPluginV2.__init__>`. Returns ------- None Raises ------ SpyderAPIError If trying to watch a plugin that is not listed in the plugin class' :attr:`~spyder.api.plugins.SpyderPluginV2.REQUIRES` or :attr:`~spyder.api.plugins.SpyderPluginV2.OPTIONAL` class constants. """ self._plugin_listeners = {} self._plugin_teardown_listeners = {} for method_name in dir(self): method = getattr(self, method_name, None) if hasattr(method, "_plugin_listen"): plugin_listen = method._plugin_listen # Check if plugin is listed among REQUIRES and OPTIONAL. # Note: We can't do this validation for the Layout plugin # because it depends on all plugins through the Plugins.All # wildcard. if self.NAME != Plugins.Layout and ( plugin_listen not in self.REQUIRES + self.OPTIONAL ): raise SpyderAPIError( f"Method {method_name} of {self} is trying to watch " f"plugin {plugin_listen}, but that plugin is not " f"listed in REQUIRES nor OPTIONAL." ) logger.debug( f"Method {method_name} is watching plugin {plugin_listen}" ) self._plugin_listeners[plugin_listen] = method_name if hasattr(method, "_plugin_teardown"): plugin_teardown = method._plugin_teardown # Check if plugin is listed among REQUIRES and OPTIONAL. # Note: We can't do this validation for the Layout plugin # because it depends on all plugins through the Plugins.All # wildcard. if self.NAME != Plugins.Layout and ( plugin_teardown not in self.REQUIRES + self.OPTIONAL ): raise SpyderAPIError( f"Method {method_name} of {self} is trying to watch " f"plugin {plugin_teardown}, but that plugin is not " f"listed in REQUIRES nor OPTIONAL." ) logger.debug( f"Method {method_name} will handle plugin " f"teardown for {plugin_teardown}" ) self._plugin_teardown_listeners[plugin_teardown] = method_name
def _on_plugin_available(self, plugin: str) -> None: """ Handle plugin availability and redirect it to plugin-specific handlers. Parameters ---------- plugin: str Name of the plugin that was notified as available. Returns ------- None """ # Call plugin specific handler if plugin in self._plugin_listeners: method_name = self._plugin_listeners[plugin] method = getattr(self, method_name) logger.debug(f"Calling {method}") method() # Call global plugin handler if "__all" in self._plugin_listeners: method_name = self._plugin_listeners["__all"] method = getattr(self, method_name) method(plugin) def _on_plugin_teardown(self, plugin: str) -> None: """ Handle plugin teardown and redirect it to plugin-specific handlers. Parameters ---------- plugin: str Name of the plugin that is going through its teardown process. Returns ------- None """ # Call plugin specific handler if plugin in self._plugin_teardown_listeners: method_name = self._plugin_teardown_listeners[plugin] method = getattr(self, method_name) logger.debug(f"Calling {method}") method()