Package: watchify
Watchify⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⢠⡆⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀
                                ⠀⠀⠀⠀⠀⠀⠈⣷⣄⠀⠀⠀⠀⣾⣷⠀⠀⠀⠀⣠⣾⠃⠀⠀⠀⠀⠀⠀⠀⠀
                                ⠀⠀⠀⠀⠀⠀⠀⢿⠿⠃⠀⠀⠀⠉⠉⠁⠀⠀⠐⠿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀
                                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⣤⣤⣶⣶⣶⣤⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                                ⠀⠀⠀⠀⠀⢀⣤⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⣄⠀⠀⠀⠀⠀⠀
                                ⠀⠀⠀⣠⣶⣿⣿⡿⣿⣿⣿⡿⠋⠉⠀⠀⠉⠙⢿⣿⣿⡿⣿⣿⣷⣦⡀⠀⠀⠀
                                ⠀⢀⣼⣿⣿⠟⠁⢠⣿⣿⠏⠀⠀⢠⣤⣤⡀⠀⠀⢻⣿⣿⡀⠙⢿⣿⣿⣦⠀⠀
                                ⣰⣿⣿⡟⠁⠀⠀⢸⣿⣿⠀⠀⠀⢿⣿⣿⡟⠀⠀⠈⣿⣿⡇⠀⠀⠙⣿⣿⣷⡄
                                ⠈⠻⣿⣿⣦⣄⠀⠸⣿⣿⣆⠀⠀⠀⠉⠉⠀⠀⠀⣸⣿⣿⠃⢀⣤⣾⣿⣿⠟⠁
                                ⠀⠀⠈⠻⣿⣿⣿⣶⣿⣿⣿⣦⣄⠀⠀⠀⢀⣠⣾⣿⣿⣿⣾⣿⣿⡿⠋⠁⠀⠀
                                ⠀⠀⠀⠀⠀⠙⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠛⠁⠀⠀⠀⠀⠀
                                ⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠛⠿⠿⠿⠿⠿⠿⠛⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀
                                ⠀⠀⠀⠀⠀⠀⠀⢰⣷⡦⠀⠀⠀⢀⣀⣀⠀⠀⠀⢴⣾⡇⠀⠀⠀⠀⠀⠀⠀⠀
                                ⠀⠀⠀⠀⠀⠀⠀⣸⠟⠁⠀⠀⠀⠘⣿⡇⠀⠀⠀⠀⠙⢷⠀⠀⠀⠀⠀⠀⠀⠀
                                ⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠀⠀⠀⠀⠻⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀Aboutwatchifyis anevent-driveninter-object communication tool, promoting thesegregation of
complex functionalitiesinto smaller ones while enabling their relationship through a third-party entity.
This approach promotesloosely coupledimplementations, simplifyingSingle Responsibility Principlecommitment yet keeping acohesive code.
As a result, the code becomes more flexible, maintainable, testable, and overrall healthier.Install$pipinstallwatchifyUsagefromwatchifyimportAbstractWatcher,Watchersfromwatchify.loggerimportloggerclassFood:defcook(self,name:str)->None:self.name=nameclassCatWatcher(AbstractWatcher):defpush(self,food:Food,*args,**kwargs)->None:iffood.name=='fish':logger.debug(f'Cat loves %s!',food.name)else:logger.debug(f'Cat hates %s!',food.name)classMonkeyWatcher(AbstractWatcher):defpush(self,food:Food,*args,**kwargs)->None:iffood.name=='banana':logger.debug(f'Monkey loves %s!',food.name)else:logger.debug(f'Monkey hates %s!',food.name)>>>food,watchers=Food(),Watchers()>>>watchers.attach_many([CatWatcher(),MonkeyWatcher()])<Watchersobject:Observers[CatWatcher,MonkeyWatcher]>>>>food.cook('fish')>>>watchers.notify(food)[watchers][DEBUG][2077-12-2700:00:00,111]>>>Notifyingwatcher:CatWatcherobject...[watchers][DEBUG][2077-12-2700:00:00,112]>>>Catlovesfish![watchers][DEBUG][2077-12-2700:00:00,113]>>>Notifyingwatcher:MonkeyWatcherobject...[watchers][DEBUG][2077-12-2700:00:00,114]>>>Monkeyhatesfish!>>>food.cook('banana')>>>watchers.notify(food)[watchers][DEBUG][2077-12-2700:00:00,115]>>>Notifyingwatcher:CatWatcherobject...[watchers][DEBUG][2077-12-2700:00:00,116]>>>Cathatesbanana![watchers][DEBUG][2077-12-2700:00:00,117]>>>Notifyingwatcher:MonkeyWatcherobject...[watchers][DEBUG][2077-12-2700:00:00,118]>>>Monkeylovesbanana!Or usingWatchersSpy, you can notify listeners automatically whenever a specified constraint is
met, so you don't need to manually invokenotifyas shown above:fromwatchifyimportAbstractWatcher,WatchersSpyfromwatchify.loggerimportlogger# [...]>>>food,watchers=Food(),WatchersSpy()>>>watchers.attach_many([CatWatcher(),MonkeyWatcher()])<WatchersSpyobject:Observers[CatWatcher,MonkeyWatcher]>>>>watchers.spy(food,'cook')Spying(sender'=<Food object>',method='cook',constraint='after')>>>food.cook('fish')[watchers][DEBUG][2077-12-2700:00:00,111]>>>Notifyingwatcher:CatWatcherobject...[watchers][DEBUG][2077-12-2700:00:00,112]>>>Catlovesfish![watchers][DEBUG][2077-12-2700:00:00,113]>>>Notifyingwatcher:MonkeyWatcherobject...[watchers][DEBUG][2077-12-2700:00:00,114]>>>Monkeyhatesfish!>>>food.cook('banana')[watchers][DEBUG][2077-12-2700:00:00,115]>>>Notifyingwatcher:CatWatcherobject...[watchers][DEBUG][2077-12-2700:00:00,116]>>>Cathatesbanana![watchers][DEBUG][2077-12-2700:00:00,117]>>>Notifyingwatcher:MonkeyWatcherobject...[watchers][DEBUG][2077-12-2700:00:00,118]>>>Monkeylovesbanana!