Mercurial > libervia-backend
comparison sat_frontends/quick_frontend/quick_widgets.py @ 4037:524856bd7b19
massive refactoring to switch from camelCase to snake_case:
historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a
pre-PEP8 code, to use the same coding style as in Twisted.
However, snake_case is more readable and it's better to follow PEP8 best practices, so it
has been decided to move on full snake_case. Because Libervia has a huge codebase, this
ended with a ugly mix of camelCase and snake_case.
To fix that, this patch does a big refactoring by renaming every function and method
(including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case.
This is a massive change, and may result in some bugs.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 08 Apr 2023 13:54:42 +0200 |
parents | be6d91572633 |
children | 4b842c1fb686 |
comparison
equal
deleted
inserted
replaced
4036:c4464d7ae97b | 4037:524856bd7b19 |
---|---|
66 for widget_map in self._widgets.values(): | 66 for widget_map in self._widgets.values(): |
67 for widget_instances in widget_map.values(): | 67 for widget_instances in widget_map.values(): |
68 for widget in widget_instances: | 68 for widget in widget_instances: |
69 yield widget | 69 yield widget |
70 | 70 |
71 def getRealClass(self, class_): | 71 def get_real_class(self, class_): |
72 """Return class registered for given class_ | 72 """Return class registered for given class_ |
73 | 73 |
74 @param class_: subclass of QuickWidget | 74 @param class_: subclass of QuickWidget |
75 @return: class actually used to create widget | 75 @return: class actually used to create widget |
76 """ | 76 """ |
84 raise exceptions.InternalError( | 84 raise exceptions.InternalError( |
85 "There is not class registered for {}".format(class_) | 85 "There is not class registered for {}".format(class_) |
86 ) | 86 ) |
87 return cls | 87 return cls |
88 | 88 |
89 def getWidgetInstances(self, widget): | 89 def get_widget_instances(self, widget): |
90 """Get all instance of a widget | 90 """Get all instance of a widget |
91 | 91 |
92 This is a helper method which call getWidgets | 92 This is a helper method which call get_widgets |
93 @param widget(QuickWidget): retrieve instances of this widget | 93 @param widget(QuickWidget): retrieve instances of this widget |
94 @return: iterator on widgets | 94 @return: iterator on widgets |
95 """ | 95 """ |
96 return self.getWidgets(widget.__class__, widget.target, widget.profiles) | 96 return self.get_widgets(widget.__class__, widget.target, widget.profiles) |
97 | 97 |
98 def getWidgets(self, class_, target=None, profiles=None, with_duplicates=True): | 98 def get_widgets(self, class_, target=None, profiles=None, with_duplicates=True): |
99 """Get all subclassed widgets instances | 99 """Get all subclassed widgets instances |
100 | 100 |
101 @param class_: subclass of QuickWidget, same parameter as used in | 101 @param class_: subclass of QuickWidget, same parameter as used in |
102 [getOrCreateWidget] | 102 [get_or_create_widget] |
103 @param target: if not None, construct a hash with this target and filter | 103 @param target: if not None, construct a hash with this target and filter |
104 corresponding widgets | 104 corresponding widgets |
105 recreated widgets are handled | 105 recreated widgets are handled |
106 @param profiles(iterable, None): if not None, filter on instances linked to these | 106 @param profiles(iterable, None): if not None, filter on instances linked to these |
107 profiles | 107 profiles |
108 @param with_duplicates(bool): if False, only first widget with a given hash is | 108 @param with_duplicates(bool): if False, only first widget with a given hash is |
109 returned | 109 returned |
110 @return: iterator on widgets | 110 @return: iterator on widgets |
111 """ | 111 """ |
112 class_ = self.getRealClass(class_) | 112 class_ = self.get_real_class(class_) |
113 try: | 113 try: |
114 widgets_map = self._widgets[class_.__name__] | 114 widgets_map = self._widgets[class_.__name__] |
115 except KeyError: | 115 except KeyError: |
116 return | 116 return |
117 else: | 117 else: |
118 if target is not None: | 118 if target is not None: |
119 filter_hash = str(class_.getWidgetHash(target, profiles)) | 119 filter_hash = str(class_.get_widget_hash(target, profiles)) |
120 else: | 120 else: |
121 filter_hash = None | 121 filter_hash = None |
122 if filter_hash is not None: | 122 if filter_hash is not None: |
123 for widget in widgets_map.get(filter_hash, []): | 123 for widget in widgets_map.get(filter_hash, []): |
124 yield widget | 124 yield widget |
131 if not with_duplicates: | 131 if not with_duplicates: |
132 # widgets are set by hashes, so if don't want duplicates | 132 # widgets are set by hashes, so if don't want duplicates |
133 # we only return the first widget of the list | 133 # we only return the first widget of the list |
134 break | 134 break |
135 | 135 |
136 def getWidget(self, class_, target=None, profiles=None): | 136 def get_widget(self, class_, target=None, profiles=None): |
137 """Get a widget without creating it if it doesn't exist. | 137 """Get a widget without creating it if it doesn't exist. |
138 | 138 |
139 if several instances of widgets with this hash exist, the first one is returned | 139 if several instances of widgets with this hash exist, the first one is returned |
140 @param class_: subclass of QuickWidget, same parameter as used in [getOrCreateWidget] | 140 @param class_: subclass of QuickWidget, same parameter as used in [get_or_create_widget] |
141 @param target: target depending of the widget, usually a JID instance | 141 @param target: target depending of the widget, usually a JID instance |
142 @param profiles (unicode, iterable[unicode], None): profile(s) to use (may or may not be | 142 @param profiles (unicode, iterable[unicode], None): profile(s) to use (may or may not be |
143 used, depending of the widget class) | 143 used, depending of the widget class) |
144 @return: a class_ instance or None if the widget doesn't exist | 144 @return: a class_ instance or None if the widget doesn't exist |
145 """ | 145 """ |
146 assert (target is not None) or (profiles is not None) | 146 assert (target is not None) or (profiles is not None) |
147 if profiles is not None and isinstance(profiles, str): | 147 if profiles is not None and isinstance(profiles, str): |
148 profiles = [profiles] | 148 profiles = [profiles] |
149 class_ = self.getRealClass(class_) | 149 class_ = self.get_real_class(class_) |
150 hash_ = class_.getWidgetHash(target, profiles) | 150 hash_ = class_.get_widget_hash(target, profiles) |
151 try: | 151 try: |
152 return self._widgets[class_.__name__][hash_][0] | 152 return self._widgets[class_.__name__][hash_][0] |
153 except KeyError: | 153 except KeyError: |
154 return None | 154 return None |
155 | 155 |
156 def getOrCreateWidget(self, class_, target, *args, **kwargs): | 156 def get_or_create_widget(self, class_, target, *args, **kwargs): |
157 """Get an existing widget or create a new one when necessary | 157 """Get an existing widget or create a new one when necessary |
158 | 158 |
159 If the widget is new, self.host.newWidget will be called with it. | 159 If the widget is new, self.host.new_widget will be called with it. |
160 @param class_(class): class of the widget to create | 160 @param class_(class): class of the widget to create |
161 @param target: target depending of the widget, usually a JID instance | 161 @param target: target depending of the widget, usually a JID instance |
162 @param args(list): optional args to create a new instance of class_ | 162 @param args(list): optional args to create a new instance of class_ |
163 @param kwargs(dict): optional kwargs to create a new instance of class_ | 163 @param kwargs(dict): optional kwargs to create a new instance of class_ |
164 if 'profile' key is present, it will be popped and put in 'profiles' | 164 if 'profile' key is present, it will be popped and put in 'profiles' |
165 if there is neither 'profile' nor 'profiles', None will be used for 'profiles' | 165 if there is neither 'profile' nor 'profiles', None will be used for 'profiles' |
166 if 'on_new_widget' is present it can have the following values: | 166 if 'on_new_widget' is present it can have the following values: |
167 C.WIDGET_NEW [default]: self.host.newWidget will be called on widget creation | 167 C.WIDGET_NEW [default]: self.host.new_widget will be called on widget creation |
168 [callable]: this method will be called instead of self.host.newWidget | 168 [callable]: this method will be called instead of self.host.new_widget |
169 None: do nothing | 169 None: do nothing |
170 if 'on_existing_widget' is present it can have the following values: | 170 if 'on_existing_widget' is present it can have the following values: |
171 C.WIDGET_KEEP [default]: return the existing widget | 171 C.WIDGET_KEEP [default]: return the existing widget |
172 C.WIDGET_RAISE: raise WidgetAlreadyExistsError | 172 C.WIDGET_RAISE: raise WidgetAlreadyExistsError |
173 C.WIDGET_RECREATE: create a new widget | 173 C.WIDGET_RECREATE: create a new widget |
174 if the existing widget has a "recreateArgs" method, it will be called with args list and kwargs dict | 174 if the existing widget has a "recreate_args" method, it will be called with args list and kwargs dict |
175 so the values can be completed to create correctly the new instance | 175 so the values can be completed to create correctly the new instance |
176 [callable]: this method will be called with existing widget as argument, the widget to use must be returned | 176 [callable]: this method will be called with existing widget as argument, the widget to use must be returned |
177 if 'force_hash' is present, the hash given in value will be used instead of the one returned by class_.getWidgetHash | 177 if 'force_hash' is present, the hash given in value will be used instead of the one returned by class_.get_widget_hash |
178 other keys will be used to instanciate class_ if the case happen (e.g. if type_ is present and class_ is a QuickChat subclass, | 178 other keys will be used to instanciate class_ if the case happen (e.g. if type_ is present and class_ is a QuickChat subclass, |
179 it will be used to create a new QuickChat instance). | 179 it will be used to create a new QuickChat instance). |
180 @return: a class_ instance, either new or already existing | 180 @return: a class_ instance, either new or already existing |
181 """ | 181 """ |
182 cls = self.getRealClass(class_) | 182 cls = self.get_real_class(class_) |
183 | 183 |
184 ## arguments management ## | 184 ## arguments management ## |
185 _args = [self.host, target] + list( | 185 _args = [self.host, target] + list( |
186 args | 186 args |
187 ) or [] # FIXME: check if it's really necessary to use optional args | 187 ) or [] # FIXME: check if it's really necessary to use optional args |
210 | 210 |
211 ## we get the hash ## | 211 ## we get the hash ## |
212 try: | 212 try: |
213 hash_ = _kwargs.pop("force_hash") | 213 hash_ = _kwargs.pop("force_hash") |
214 except KeyError: | 214 except KeyError: |
215 hash_ = cls.getWidgetHash(target, _kwargs["profiles"]) | 215 hash_ = cls.get_widget_hash(target, _kwargs["profiles"]) |
216 | 216 |
217 ## widget creation or retrieval ## | 217 ## widget creation or retrieval ## |
218 | 218 |
219 widgets_map = self._widgets.setdefault( | 219 widgets_map = self._widgets.setdefault( |
220 cls.__name__, {} | 220 cls.__name__, {} |
225 try: | 225 try: |
226 widget = widgets_map[hash_][0] | 226 widget = widgets_map[hash_][0] |
227 except KeyError: | 227 except KeyError: |
228 widget = None | 228 widget = None |
229 else: | 229 else: |
230 widget.addTarget(target) | 230 widget.add_target(target) |
231 | 231 |
232 if widget is None: | 232 if widget is None: |
233 # we need to create a new widget | 233 # we need to create a new widget |
234 log.debug(f"Creating new widget for target {target} {cls}") | 234 log.debug(f"Creating new widget for target {target} {cls}") |
235 widget = cls(*_args, **_kwargs) | 235 widget = cls(*_args, **_kwargs) |
236 widgets_map.setdefault(hash_, []).append(widget) | 236 widgets_map.setdefault(hash_, []).append(widget) |
237 self.host.callListeners("widgetNew", widget) | 237 self.host.call_listeners("widgetNew", widget) |
238 | 238 |
239 if on_new_widget == C.WIDGET_NEW: | 239 if on_new_widget == C.WIDGET_NEW: |
240 self.host.newWidget(widget) | 240 self.host.new_widget(widget) |
241 elif callable(on_new_widget): | 241 elif callable(on_new_widget): |
242 on_new_widget(widget) | 242 on_new_widget(widget) |
243 else: | 243 else: |
244 assert on_new_widget is None | 244 assert on_new_widget is None |
245 else: | 245 else: |
248 pass | 248 pass |
249 elif on_existing_widget == C.WIDGET_RAISE: | 249 elif on_existing_widget == C.WIDGET_RAISE: |
250 raise WidgetAlreadyExistsError(hash_) | 250 raise WidgetAlreadyExistsError(hash_) |
251 elif on_existing_widget == C.WIDGET_RECREATE: | 251 elif on_existing_widget == C.WIDGET_RECREATE: |
252 try: | 252 try: |
253 recreateArgs = widget.recreateArgs | 253 recreate_args = widget.recreate_args |
254 except AttributeError: | 254 except AttributeError: |
255 pass | 255 pass |
256 else: | 256 else: |
257 recreateArgs(_args, _kwargs) | 257 recreate_args(_args, _kwargs) |
258 widget = cls(*_args, **_kwargs) | 258 widget = cls(*_args, **_kwargs) |
259 widgets_map[hash_].append(widget) | 259 widgets_map[hash_].append(widget) |
260 log.debug("widget <{wid}> already exists, a new one has been recreated" | 260 log.debug("widget <{wid}> already exists, a new one has been recreated" |
261 .format(wid=widget)) | 261 .format(wid=widget)) |
262 elif callable(on_existing_widget): | 262 elif callable(on_existing_widget): |
272 raise exceptions.InternalError( | 272 raise exceptions.InternalError( |
273 "Unexpected on_existing_widget value ({})".format(on_existing_widget)) | 273 "Unexpected on_existing_widget value ({})".format(on_existing_widget)) |
274 | 274 |
275 return widget | 275 return widget |
276 | 276 |
277 def deleteWidget(self, widget_to_delete, *args, **kwargs): | 277 def delete_widget(self, widget_to_delete, *args, **kwargs): |
278 """Delete a widget instance | 278 """Delete a widget instance |
279 | 279 |
280 this method must be called by frontends when a widget is deleted | 280 this method must be called by frontends when a widget is deleted |
281 widget's onDelete method will be called before deletion, and deletion will be | 281 widget's on_delete method will be called before deletion, and deletion will be |
282 stopped if it returns False. | 282 stopped if it returns False. |
283 @param widget_to_delete(QuickWidget): widget which need to deleted | 283 @param widget_to_delete(QuickWidget): widget which need to deleted |
284 @param *args: extra arguments to pass to onDelete | 284 @param *args: extra arguments to pass to on_delete |
285 @param *kwargs: extra keywords arguments to pass to onDelete | 285 @param *kwargs: extra keywords arguments to pass to on_delete |
286 the extra arguments are not used by QuickFrontend, it's is up to | 286 the extra arguments are not used by QuickFrontend, it's is up to |
287 the frontend to use them or not. | 287 the frontend to use them or not. |
288 following extra arguments are well known: | 288 following extra arguments are well known: |
289 - "all_instances" can be used as kwarg, if it evaluate to True, | 289 - "all_instances" can be used as kwarg, if it evaluate to True, |
290 all instances of the widget will be deleted (if onDelete is | 290 all instances of the widget will be deleted (if on_delete is |
291 not returning False for any of the instance). This arguments | 291 not returning False for any of the instance). This arguments |
292 is not sent to onDelete methods. | 292 is not sent to on_delete methods. |
293 - "explicit_close" is used when the deletion is requested by | 293 - "explicit_close" is used when the deletion is requested by |
294 the user or a leave signal, "all_instances" is usually set at | 294 the user or a leave signal, "all_instances" is usually set at |
295 the same time. | 295 the same time. |
296 """ | 296 """ |
297 # TODO: all_instances must be independante kwargs, this is not possible with Python 2 | 297 # TODO: all_instances must be independante kwargs, this is not possible with Python 2 |
298 # but will be with Python 3 | 298 # but will be with Python 3 |
299 all_instances = kwargs.get('all_instances', False) | 299 all_instances = kwargs.get('all_instances', False) |
300 | 300 |
301 if all_instances: | 301 if all_instances: |
302 for w in self.getWidgetInstances(widget_to_delete): | 302 for w in self.get_widget_instances(widget_to_delete): |
303 if w.onDelete(**kwargs) == False: | 303 if w.on_delete(**kwargs) == False: |
304 log.debug( | 304 log.debug( |
305 f"Deletion of {widget_to_delete} cancelled by widget itself") | 305 f"Deletion of {widget_to_delete} cancelled by widget itself") |
306 return | 306 return |
307 else: | 307 else: |
308 if widget_to_delete.onDelete(**kwargs) == False: | 308 if widget_to_delete.on_delete(**kwargs) == False: |
309 log.debug(f"Deletion of {widget_to_delete} cancelled by widget itself") | 309 log.debug(f"Deletion of {widget_to_delete} cancelled by widget itself") |
310 return | 310 return |
311 | 311 |
312 if self.host.selected_widget == widget_to_delete: | 312 if self.host.selected_widget == widget_to_delete: |
313 self.host.selected_widget = None | 313 self.host.selected_widget = None |
314 | 314 |
315 class_ = self.getRealClass(widget_to_delete.__class__) | 315 class_ = self.get_real_class(widget_to_delete.__class__) |
316 try: | 316 try: |
317 widgets_map = self._widgets[class_.__name__] | 317 widgets_map = self._widgets[class_.__name__] |
318 except KeyError: | 318 except KeyError: |
319 log.error("no widgets_map found for class {cls}".format(cls=class_)) | 319 log.error("no widgets_map found for class {cls}".format(cls=class_)) |
320 return | 320 return |
321 widget_hash = str(class_.getWidgetHash(widget_to_delete.target, | 321 widget_hash = str(class_.get_widget_hash(widget_to_delete.target, |
322 widget_to_delete.profiles)) | 322 widget_to_delete.profiles)) |
323 try: | 323 try: |
324 widget_instances = widgets_map[widget_hash] | 324 widget_instances = widgets_map[widget_hash] |
325 except KeyError: | 325 except KeyError: |
326 log.error(f"no instance of {class_.__name__} found with hash {widget_hash!r}") | 326 log.error(f"no instance of {class_.__name__} found with hash {widget_hash!r}") |
340 # all instances with this hash have been deleted | 340 # all instances with this hash have been deleted |
341 # we remove the hash itself | 341 # we remove the hash itself |
342 del widgets_map[widget_hash] | 342 del widgets_map[widget_hash] |
343 log.debug("All instances of {cls} with hash {widget_hash!r} have been deleted" | 343 log.debug("All instances of {cls} with hash {widget_hash!r} have been deleted" |
344 .format(cls=class_, widget_hash=widget_hash)) | 344 .format(cls=class_, widget_hash=widget_hash)) |
345 self.host.callListeners("widgetDeleted", widget_to_delete) | 345 self.host.call_listeners("widgetDeleted", widget_to_delete) |
346 | 346 |
347 | 347 |
348 class QuickWidget(object): | 348 class QuickWidget(object): |
349 """generic widget base""" | 349 """generic widget base""" |
350 # FIXME: sometime a single target is used, sometimes several ones | 350 # FIXME: sometime a single target is used, sometimes several ones |
366 - None: no profile is managed by this widget class (rare) | 366 - None: no profile is managed by this widget class (rare) |
367 @raise: ValueError when (iterable) or None is given to profiles for a widget class which manage one unique profile. | 367 @raise: ValueError when (iterable) or None is given to profiles for a widget class which manage one unique profile. |
368 """ | 368 """ |
369 self.host = host | 369 self.host = host |
370 self.targets = set() | 370 self.targets = set() |
371 self.addTarget(target) | 371 self.add_target(target) |
372 self.profiles = set() | 372 self.profiles = set() |
373 self._sync = True | 373 self._sync = True |
374 if isinstance(profiles, str): | 374 if isinstance(profiles, str): |
375 self.addProfile(profiles) | 375 self.add_profile(profiles) |
376 elif profiles is None: | 376 elif profiles is None: |
377 if not self.PROFILES_ALLOW_NONE: | 377 if not self.PROFILES_ALLOW_NONE: |
378 raise ValueError("profiles can't have a value of None") | 378 raise ValueError("profiles can't have a value of None") |
379 else: | 379 else: |
380 for profile in profiles: | 380 for profile in profiles: |
381 self.addProfile(profile) | 381 self.add_profile(profile) |
382 if not self.profiles: | 382 if not self.profiles: |
383 raise ValueError("no profile found, use None for no profile classes") | 383 raise ValueError("no profile found, use None for no profile classes") |
384 | 384 |
385 @property | 385 @property |
386 def profile(self): | 386 def profile(self): |
400 return next(iter(self.targets)) | 400 return next(iter(self.targets)) |
401 | 401 |
402 @property | 402 @property |
403 def widget_hash(self): | 403 def widget_hash(self): |
404 """Return quick widget hash""" | 404 """Return quick widget hash""" |
405 return self.getWidgetHash(self.target, self.profiles) | 405 return self.get_widget_hash(self.target, self.profiles) |
406 | 406 |
407 # synchronisation state | 407 # synchronisation state |
408 | 408 |
409 @property | 409 @property |
410 def sync(self): | 410 def sync(self): |
427 """ | 427 """ |
428 pass | 428 pass |
429 | 429 |
430 # target/profile | 430 # target/profile |
431 | 431 |
432 def addTarget(self, target): | 432 def add_target(self, target): |
433 """Add a target if it doesn't already exists | 433 """Add a target if it doesn't already exists |
434 | 434 |
435 @param target: target to add | 435 @param target: target to add |
436 """ | 436 """ |
437 self.targets.add(target) | 437 self.targets.add(target) |
438 | 438 |
439 def addProfile(self, profile): | 439 def add_profile(self, profile): |
440 """Add a profile is if doesn't already exists | 440 """Add a profile is if doesn't already exists |
441 | 441 |
442 @param profile: profile to add | 442 @param profile: profile to add |
443 """ | 443 """ |
444 if self.profiles and not self.PROFILES_MULTIPLE: | 444 if self.profiles and not self.PROFILES_MULTIPLE: |
446 self.profiles.add(profile) | 446 self.profiles.add(profile) |
447 | 447 |
448 # widget identitication | 448 # widget identitication |
449 | 449 |
450 @staticmethod | 450 @staticmethod |
451 def getWidgetHash(target, profiles): | 451 def get_widget_hash(target, profiles): |
452 """Return the hash associated with this target for this widget class | 452 """Return the hash associated with this target for this widget class |
453 | 453 |
454 some widget classes can manage several target on the same instance | 454 some widget classes can manage several target on the same instance |
455 (e.g.: a chat widget with multiple resources on the same bare jid), | 455 (e.g.: a chat widget with multiple resources on the same bare jid), |
456 this method allow to return a hash associated to one or several targets | 456 this method allow to return a hash associated to one or several targets |
463 """ | 463 """ |
464 return str(target) # by defaut, there is one hash for one target | 464 return str(target) # by defaut, there is one hash for one target |
465 | 465 |
466 # widget life events | 466 # widget life events |
467 | 467 |
468 def onDelete(self, *args, **kwargs): | 468 def on_delete(self, *args, **kwargs): |
469 """Called when a widget is being deleted | 469 """Called when a widget is being deleted |
470 | 470 |
471 @return (boot, None): False to cancel deletion | 471 @return (boot, None): False to cancel deletion |
472 all other value continue deletion | 472 all other value continue deletion |
473 """ | 473 """ |
474 return True | 474 return True |
475 | 475 |
476 def onSelected(self): | 476 def on_selected(self): |
477 """Called when host.selected_widget is this instance""" | 477 """Called when host.selected_widget is this instance""" |
478 pass | 478 pass |