Coverage for /builds/ase/ase/ase/gui/observer.py: 75.00%

28 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-08-02 00:12 +0000

1import warnings 

2import weakref 

3 

4 

5class Observers: 

6 def __init__(self): 

7 self.observer_weakrefs = [] 

8 

9 def register(self, observer): 

10 if hasattr(observer, '__self__'): # observer is an instance method 

11 # Since bound methods are shortlived we need to store the instance 

12 # it is bound to and use getattr() later: 

13 obj = observer.__self__ 

14 name = observer.__name__ 

15 else: 

16 obj = observer 

17 name = None 

18 self.observer_weakrefs.append((weakref.ref(obj), name)) 

19 

20 def notify(self): 

21 # We should probably add an event class to these callbacks. 

22 weakrefs_still_alive = [] 

23 for weak_ref, name in self.observer_weakrefs: 

24 observer = weak_ref() 

25 if observer is not None: 

26 weakrefs_still_alive.append((weak_ref, name)) 

27 if name is not None: 

28 # If the observer is an instance method we stored 

29 # self, for garbage collection reasons, and now need to 

30 # get the actual method: 

31 observer = getattr(observer, name) 

32 

33 try: 

34 observer() 

35 except Exception as ex: 

36 import traceback 

37 

38 tb = ''.join(traceback.format_exception(ex)) 

39 warnings.warn( 

40 f'Suppressed exception in observer {observer}: {tb}' 

41 ) 

42 continue 

43 

44 self.observer_weakrefs = weakrefs_still_alive