1- #util.py
2- #A part of NonVisual Desktop Access (NVDA)
3- #Copyright (C) 2017 NV Access Limited
4- #This file is covered by the GNU General Public License.
5- #See the file COPYING for more details.
1+ # A part of NonVisual Desktop Access (NVDA)
2+ # Copyright (C) 2017-2023 NV Access Limited, Leonard de Ruijter
3+ # This file is covered by the GNU General Public License.
4+ # See the file COPYING for more details.
65
76"""Utilities used withing the extension points framework. Generally it is expected that the class in __init__.py are
87used, however for more advanced requirements these utilities can be used directly.
98"""
9+
1010import weakref
11- import collections
1211import inspect
13-
14-
15- class AnnotatableWeakref (weakref .ref ):
12+ from typing import (
13+ Callable ,
14+ Generator ,
15+ Generic ,
16+ Optional ,
17+ OrderedDict ,
18+ Tuple ,
19+ TypeVar ,
20+ Union ,
21+ )
22+
23+ HandlerT = TypeVar ("HandlerT" , bound = Callable )
24+ HandlerKeyT = Union [int , Tuple [int , int ]]
25+
26+
27+ class AnnotatableWeakref (weakref .ref , Generic [HandlerT ]):
1628 """A weakref.ref which allows annotation with custom attributes.
1729 """
30+ handlerKey : int
1831
1932
20- class BoundMethodWeakref (object ):
33+ class BoundMethodWeakref (Generic [ HandlerT ] ):
2134 """Weakly references a bound instance method.
2235 Instance methods are bound dynamically each time they are fetched.
2336 weakref.ref on a bound instance method doesn't work because
@@ -26,8 +39,9 @@ class BoundMethodWeakref(object):
2639 which can then be used to bind an instance method.
2740 To get the actual method, you call an instance as you would a weakref.ref.
2841 """
42+ handlerKey : Tuple [int , int ]
2943
30- def __init__ (self , target , onDelete ):
44+ def __init__ (self , target : HandlerT , onDelete ):
3145 def onRefDelete (weak ):
3246 """Calls onDelete for our BoundMethodWeakref when one of the individual weakrefs (instance or function) dies.
3347 """
@@ -37,7 +51,7 @@ def onRefDelete(weak):
3751 self .weakInst = weakref .ref (inst , onRefDelete )
3852 self .weakFunc = weakref .ref (func , onRefDelete )
3953
40- def __call__ (self ):
54+ def __call__ (self ) -> Optional [ HandlerT ] :
4155 inst = self .weakInst ()
4256 if not inst :
4357 return
@@ -46,7 +60,8 @@ def __call__(self):
4660 # Get an instancemethod by binding func to inst.
4761 return func .__get__ (inst )
4862
49- def _getHandlerKey (handler ):
63+
64+ def _getHandlerKey (handler : HandlerT ) -> HandlerKeyT :
5065 """Get a key which identifies a handler function.
5166 This is needed because we store weak references, not the actual functions.
5267 We store the key on the weak reference.
@@ -58,7 +73,7 @@ def _getHandlerKey(handler):
5873 return id (handler )
5974
6075
61- class HandlerRegistrar (object ):
76+ class HandlerRegistrar (Generic [ HandlerT ] ):
6277 """Base class to Facilitate registration and unregistration of handler functions.
6378 The handlers are stored using weak references and are automatically unregistered
6479 if the handler dies.
@@ -75,12 +90,16 @@ def __init__(self):
7590 #: Registered handler functions.
7691 #: This is an OrderedDict where the keys are unique identifiers (as returned by _getHandlerKey)
7792 #: and the values are weak references.
78- self ._handlers = collections .OrderedDict ()
93+ self ._handlers = OrderedDict [
94+ HandlerKeyT ,
95+ Union [BoundMethodWeakref [HandlerT ], AnnotatableWeakref [HandlerT ]]
96+ ]()
7997
80- def register (self , handler ):
98+ def register (self , handler : HandlerT ):
8199 """You can register functions, bound instance methods, class methods, static methods or lambdas.
82- However, the callable must be kept alive by your code otherwise it will be de-registered. This is due to the use
83- of weak references. This is especially relevant when using lambdas.
100+ However, the callable must be kept alive by your code otherwise it will be de-registered.
101+ This is due to the use of weak references.
102+ This is especially relevant when using lambdas.
84103 """
85104 if inspect .isfunction (handler ):
86105 sig = inspect .signature (handler )
@@ -95,7 +114,24 @@ def register(self, handler):
95114 weak .handlerKey = key
96115 self ._handlers [key ] = weak
97116
98- def unregister (self , handler ):
117+ def moveToEnd (self , handler : HandlerT , last : bool = False ) -> bool :
118+ """Move a registered handler to the start or end of the collection with registered handlers.
119+ This can be used to modify the order in which handlers are called.
120+ @param last: Whether to move the handler to the end.
121+ If C{False} (default), the handler is moved to the start.
122+ @returns: Whether the handler was found.
123+ """
124+ if isinstance (handler , (AnnotatableWeakref , BoundMethodWeakref )):
125+ key = handler .handlerKey
126+ else :
127+ key = _getHandlerKey (handler )
128+ try :
129+ self ._handlers .move_to_end (key = key , last = last )
130+ except KeyError :
131+ return False
132+ return True
133+
134+ def unregister (self , handler : Union [AnnotatableWeakref [HandlerT ], BoundMethodWeakref [HandlerT ], HandlerT ]):
99135 if isinstance (handler , (AnnotatableWeakref , BoundMethodWeakref )):
100136 key = handler .handlerKey
101137 else :
@@ -107,7 +143,7 @@ def unregister(self, handler):
107143 return True
108144
109145 @property
110- def handlers (self ):
146+ def handlers (self ) -> Generator [ HandlerT , None , None ] :
111147 """Generator of registered handler functions.
112148 This should be used when you want to call the handlers.
113149 """
0 commit comments