File: C:/Users/fred/anaconda3/Lib/site-packages/spyder/utils/registries.py
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
"""Spyder global registries for actions, toolbuttons, toolbars and menus."""
# Standard library imports
import inspect
import logging
from typing import Any, Optional, Dict
import warnings
import weakref
logger = logging.getLogger(__name__)
def get_caller(func):
"""
Get file and line where the methods that create actions, toolbuttons,
toolbars and menus are called.
"""
frames = []
for frame in inspect.stack():
if frame.code_context:
code_context = frame.code_context[0]
else:
code_context = ''
if func in code_context:
frames.append(f'{frame.filename}:{frame.lineno}')
frames = ', '.join(frames)
return frames
class SpyderRegistry:
"""General registry for global references (per plugin) in Spyder."""
def __init__(self, creation_func: str, obj_type: str = ''):
self.registry_map = {}
self.obj_type = obj_type
self.creation_func = creation_func
def register_reference(self, obj: Any, id_: str,
plugin: Optional[str] = None,
context: Optional[str] = None,
overwrite: Optional[bool] = False):
"""
Register a reference `obj` for a given plugin name on a given context.
Parameters
----------
obj: Any
Object to register as a reference.
id_: str
String identifier used to store the object reference.
plugin: Optional[str]
Plugin name used to store the reference. Should belong to
:class:`spyder.api.plugins.Plugins`. If None, then the object will
be stored under the global `main` key.
context: Optional[str]
Additional key used to store and identify the object reference.
In any Spyder plugin implementation, this context may refer to an
identifier of a widget. This context enables plugins to define
multiple actions with the same key that live on different widgets.
If None, this context will default to the special `__global`
identifier.
"""
plugin = plugin if plugin is not None else 'main'
context = context if context is not None else '__global'
plugin_contexts = self.registry_map.get(plugin, {})
context_references = plugin_contexts.get(
context, weakref.WeakValueDictionary())
if id_ in context_references:
try:
frames = get_caller(self.creation_func)
if not overwrite:
warnings.warn(
f'There already exists a '
f'reference {context_references[id_]} '
f'with id {id_} under the context {context} '
f'of plugin {plugin}. The new reference {obj} will '
f'overwrite the previous reference. Hint: {obj} '
f'should have a different id_. See {frames}')
except (RuntimeError, KeyError):
# Do not raise exception if a wrapped Qt Object was deleted.
# Or if the object reference dissapeared concurrently.
pass
logger.debug(f'Registering {obj} ({id_}) under context {context} for '
f'plugin {plugin}')
context_references[id_] = obj
plugin_contexts[context] = context_references
self.registry_map[plugin] = plugin_contexts
def get_reference(self, id_: str,
plugin: Optional[str] = None,
context: Optional[str] = None) -> Any:
"""
Retrieve a stored object reference under a given id of a specific
context of a given plugin name.
Parameters
----------
id_: str
String identifier used to retrieve the object.
plugin: Optional[str]
Plugin name used to store the reference. Should belong to
:class:`spyder.api.plugins.Plugins`. If None, then the object will
be retrieved from the global `main` key.
context: Optional[str]
Additional key that was used to store the object reference.
In any Spyder plugin implementation, this context may refer to an
identifier of a widget. This context enables plugins to define
multiple actions with the same key that live on different widgets.
If None, this context will default to the special `__global`
identifier.
Returns
-------
obj: Any
The object that was stored under the given identifier.
Raises
------
KeyError
If neither of `id_`, `plugin` or `context` were found in the
registry.
"""
plugin = plugin if plugin is not None else 'main'
context = context if context is not None else '__global'
plugin_contexts = self.registry_map[plugin]
context_references = plugin_contexts[context]
return context_references[id_]
def get_references(self, plugin: Optional[str] = None,
context: Optional[str] = None) -> Dict[str, Any]:
"""
Retrieve all stored object references under the context of a
given plugin name.
Parameters
----------
plugin: Optional[str]
Plugin name used to store the reference. Should belong to
:class:`spyder.api.plugins.Plugins`. If None, then the object will
be retrieved from the global `main` key.
context: Optional[str]
Additional key that was used to store the object reference.
In any Spyder plugin implementation, this context may refer to an
identifier of a widget. This context enables plugins to define
multiple actions with the same key that live on different widgets.
If None, this context will default to the special `__global`
identifier.
Returns
-------
objs: Dict[str, Any]
A dict that contains the actions mapped by their corresponding
identifiers.
"""
plugin = plugin if plugin is not None else 'main'
context = context if context is not None else '__global'
plugin_contexts = self.registry_map.get(plugin, {})
context_references = plugin_contexts.get(
context, weakref.WeakValueDictionary())
return context_references
def reset_registry(self):
self.registry_map = {}
def __str__(self) -> str:
return f'SpyderRegistry[{self.obj_type}, {self.registry_map}]'
ACTION_REGISTRY = SpyderRegistry('create_action', 'SpyderAction')
TOOLBUTTON_REGISTRY = SpyderRegistry('create_toolbutton', 'QToolButton')
TOOLBAR_REGISTRY = SpyderRegistry('create_toolbar', 'QToolBar')
MENU_REGISTRY = SpyderRegistry('create_menu', 'SpyderMenu')