/
registries.py
184 lines (157 loc) · 6.97 KB
/
registries.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# -*- 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')