bec-widgets 2.1.2__py3-none-any.whl → 2.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v2.1.3 (2025-05-07)
5
+
6
+ ### Bug Fixes
7
+
8
+ - **bec-dispatcher**: Fix reference to boundmethods to avoid duplicated subscriptions
9
+ ([`cf59d31`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/cf59d311132cd1a21f1893c19cc9f2a7e45101d0))
10
+
11
+
4
12
  ## v2.1.2 (2025-05-06)
5
13
 
6
14
  ### Bug Fixes
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.1.2
3
+ Version: 2.1.3
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
@@ -4,8 +4,9 @@ import collections
4
4
  import random
5
5
  import string
6
6
  from collections.abc import Callable
7
- from typing import TYPE_CHECKING, Union
7
+ from typing import TYPE_CHECKING, DefaultDict, Hashable, Union
8
8
 
9
+ import louie
9
10
  import redis
10
11
  from bec_lib.client import BECClient
11
12
  from bec_lib.logger import bec_logger
@@ -41,15 +42,25 @@ class QtThreadSafeCallback(QObject):
41
42
  self.cb_info = cb_info
42
43
 
43
44
  self.cb = cb
45
+ self.cb_ref = louie.saferef.safe_ref(cb)
44
46
  self.cb_signal.connect(self.cb)
47
+ self.topics = set()
45
48
 
46
49
  def __hash__(self):
47
50
  # make 2 differents QtThreadSafeCallback to look
48
51
  # identical when used as dictionary keys, if the
49
52
  # callback is the same
50
- return f"{id(self.cb)}{self.cb_info}".__hash__()
53
+ return f"{id(self.cb_ref)}{self.cb_info}".__hash__()
54
+
55
+ def __eq__(self, other):
56
+ if not isinstance(other, QtThreadSafeCallback):
57
+ return False
58
+ return self.cb_ref == other.cb_ref and self.cb_info == other.cb_info
51
59
 
52
60
  def __call__(self, msg_content, metadata):
61
+ if self.cb_ref() is None:
62
+ # callback has been deleted
63
+ return
53
64
  self.cb_signal.emit(msg_content, metadata)
54
65
 
55
66
 
@@ -96,7 +107,7 @@ class BECDispatcher:
96
107
  cls,
97
108
  client=None,
98
109
  config: str | ServiceConfig | None = None,
99
- gui_id: str = None,
110
+ gui_id: str | None = None,
100
111
  *args,
101
112
  **kwargs,
102
113
  ):
@@ -109,7 +120,9 @@ class BECDispatcher:
109
120
  if self._initialized:
110
121
  return
111
122
 
112
- self._slots = collections.defaultdict(set)
123
+ self._registered_slots: DefaultDict[Hashable, QtThreadSafeCallback] = (
124
+ collections.defaultdict()
125
+ )
113
126
  self.client = client
114
127
 
115
128
  if self.client is None:
@@ -162,10 +175,13 @@ class BECDispatcher:
162
175
  topics (EndpointInfo | str | list): A topic or list of topics that can typically be acquired via bec_lib.MessageEndpoints
163
176
  cb_info (dict | None): A dictionary containing information about the callback. Defaults to None.
164
177
  """
165
- slot = QtThreadSafeCallback(cb=slot, cb_info=cb_info)
166
- self.client.connector.register(topics, cb=slot, **kwargs)
178
+ qt_slot = QtThreadSafeCallback(cb=slot, cb_info=cb_info)
179
+ if qt_slot not in self._registered_slots:
180
+ self._registered_slots[qt_slot] = qt_slot
181
+ qt_slot = self._registered_slots[qt_slot]
182
+ self.client.connector.register(topics, cb=qt_slot, **kwargs)
167
183
  topics_str, _ = self.client.connector._convert_endpointinfo(topics)
168
- self._slots[slot].update(set(topics_str))
184
+ qt_slot.topics.update(set(topics_str))
169
185
 
170
186
  def disconnect_slot(self, slot: Callable, topics: Union[str, list]):
171
187
  """
@@ -178,16 +194,16 @@ class BECDispatcher:
178
194
  # find the right slot to disconnect from ;
179
195
  # slot callbacks are wrapped in QtThreadSafeCallback objects,
180
196
  # but the slot we receive here is the original callable
181
- for connected_slot in self._slots:
197
+ for connected_slot in self._registered_slots.values():
182
198
  if connected_slot.cb == slot:
183
199
  break
184
200
  else:
185
201
  return
186
202
  self.client.connector.unregister(topics, cb=connected_slot)
187
203
  topics_str, _ = self.client.connector._convert_endpointinfo(topics)
188
- self._slots[connected_slot].difference_update(set(topics_str))
189
- if not self._slots[connected_slot]:
190
- del self._slots[connected_slot]
204
+ self._registered_slots[connected_slot].topics.difference_update(set(topics_str))
205
+ if not self._registered_slots[connected_slot].topics:
206
+ del self._registered_slots[connected_slot]
191
207
 
192
208
  def disconnect_topics(self, topics: Union[str, list]):
193
209
  """
@@ -198,11 +214,16 @@ class BECDispatcher:
198
214
  """
199
215
  self.client.connector.unregister(topics)
200
216
  topics_str, _ = self.client.connector._convert_endpointinfo(topics)
201
- for slot in list(self._slots.keys()):
202
- slot_topics = self._slots[slot]
203
- slot_topics.difference_update(set(topics_str))
204
- if not slot_topics:
205
- del self._slots[slot]
217
+
218
+ remove_slots = []
219
+ for connected_slot in self._registered_slots.values():
220
+ connected_slot.topics.difference_update(set(topics_str))
221
+
222
+ if not connected_slot.topics:
223
+ remove_slots.append(connected_slot)
224
+
225
+ for connected_slot in remove_slots:
226
+ self._registered_slots.pop(connected_slot, None)
206
227
 
207
228
  def disconnect_all(self, *args, **kwargs):
208
229
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.1.2
3
+ Version: 2.1.3
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
@@ -2,11 +2,11 @@
2
2
  .gitlab-ci.yml,sha256=1nMYldzVk0tFkBWYTcUjumOrdSADASheWOAc0kOFDYs,9509
3
3
  .pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
4
4
  .readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
5
- CHANGELOG.md,sha256=-NKbjMkJw5Y_NGzR5o_NSDUEBJk2lbLqIbrlq0EKzps,271406
5
+ CHANGELOG.md,sha256=CQ4p9tYotE-eBhFhSkRIIUaDLVfwrwgfRRWl0qY-tbI,271638
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=5sfOavPZf4UvV9AqHkiwbsUmR1ANqIBkHSMDcMKRdO0,1224
7
+ PKG-INFO,sha256=2vaRvuGO7whPtxpWzC6vor_q5_343FZEy_YP4-5YQ3s,1224
8
8
  README.md,sha256=KgdKusjlvEvFtdNZCeDMO91y77MWK2iDcYMDziksOr4,2553
9
- pyproject.toml,sha256=BqRAPn18d8SH92IHfQeZcguEYPoXMIptC2JFn-Y-M6w,2568
9
+ pyproject.toml,sha256=Dryj1F5zorGqGgBIYF6ayR-7gpQhVnlEtOmKlMNuqmM,2568
10
10
  .git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
11
11
  .gitlab/issue_templates/bug_report_template.md,sha256=gAuyEwl7XlnebBrkiJ9AqffSNOywmr8vygUFWKTuQeI,386
12
12
  .gitlab/issue_templates/documentation_update_template.md,sha256=FHLdb3TS_D9aL4CYZCjyXSulbaW5mrN2CmwTaeLPbNw,860
@@ -49,7 +49,7 @@ bec_widgets/tests/utils.py,sha256=GbQtN7qf9n-8FoAfNddZ4aAqA7oBo_hGAlnKELd6Xzw,69
49
49
  bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
50
50
  bec_widgets/utils/bec_connector.py,sha256=Ao_DN6oiHlpbVDHDK0RLpAgz2zDbhmwYee8yc3y7m2g,18875
51
51
  bec_widgets/utils/bec_designer.py,sha256=ehNl_i743rijmhPiIGNd1bihE7-l4oJzTVoa4yjPjls,5426
52
- bec_widgets/utils/bec_dispatcher.py,sha256=Zv2D0Ah0ZmXBNk8AbdHD2osHgTXqkvw51I_iCrSmVYA,8990
52
+ bec_widgets/utils/bec_dispatcher.py,sha256=y9EFIgU3JqIs7R10XnLh0I1_Skts9sbPe3ijOVJumYs,9837
53
53
  bec_widgets/utils/bec_plugin_helper.py,sha256=efg97QDAqhZYeqE1wk3vEgkFuhNewg4rvJrtB7mRCAE,3139
54
54
  bec_widgets/utils/bec_signal_proxy.py,sha256=Yc08fdBEDABrowwNPSngT9-28R8FD4ml7oTL6BoMyEE,3263
55
55
  bec_widgets/utils/bec_table.py,sha256=nA2b8ukSeUfquFMAxGrUVOqdrzMoDYD6O_4EYbOG2zk,717
@@ -377,8 +377,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=O
377
377
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
378
378
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
379
379
  bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
380
- bec_widgets-2.1.2.dist-info/METADATA,sha256=5sfOavPZf4UvV9AqHkiwbsUmR1ANqIBkHSMDcMKRdO0,1224
381
- bec_widgets-2.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
382
- bec_widgets-2.1.2.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
383
- bec_widgets-2.1.2.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
384
- bec_widgets-2.1.2.dist-info/RECORD,,
380
+ bec_widgets-2.1.3.dist-info/METADATA,sha256=2vaRvuGO7whPtxpWzC6vor_q5_343FZEy_YP4-5YQ3s,1224
381
+ bec_widgets-2.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
382
+ bec_widgets-2.1.3.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
383
+ bec_widgets-2.1.3.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
384
+ bec_widgets-2.1.3.dist-info/RECORD,,
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_widgets"
7
- version = "2.1.2"
7
+ version = "2.1.3"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [