hydrus/include/HydrusPubSub.py

217 lines
6.6 KiB
Python
Raw Normal View History

2013-07-10 20:25:57 +00:00
import HydrusConstants as HC
2015-11-25 22:00:57 +00:00
import HydrusData
2016-01-06 21:17:20 +00:00
import HydrusExceptions
2013-02-19 00:11:43 +00:00
import Queue
import threading
import traceback
import weakref
2017-05-10 21:33:58 +00:00
import HydrusGlobals as HG
2013-02-19 00:11:43 +00:00
2014-07-23 21:21:37 +00:00
class HydrusPubSub( object ):
2013-02-19 00:11:43 +00:00
2015-08-26 21:18:39 +00:00
def __init__( self, controller, binding_errors_to_ignore = None ):
if binding_errors_to_ignore is None:
binding_errors_to_ignore = []
self._controller = controller
self._binding_errors_to_ignore = binding_errors_to_ignore
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
self._doing_work = False
2013-11-13 21:30:38 +00:00
self._pubsubs = []
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
self._pub_event = threading.Event()
2013-02-19 00:11:43 +00:00
self._lock = threading.Lock()
self._topics_to_objects = {}
self._topics_to_method_names = {}
2013-11-13 21:30:38 +00:00
def _GetCallables( self, topic ):
2013-02-19 00:11:43 +00:00
2013-11-13 21:30:38 +00:00
callables = []
if topic in self._topics_to_objects:
2013-02-19 00:11:43 +00:00
2013-11-13 21:30:38 +00:00
try:
2013-02-19 00:11:43 +00:00
2013-11-13 21:30:38 +00:00
objects = self._topics_to_objects[ topic ]
for object in objects:
2013-02-19 00:11:43 +00:00
2013-11-13 21:30:38 +00:00
method_names = self._topics_to_method_names[ topic ]
2013-02-19 00:11:43 +00:00
2013-11-13 21:30:38 +00:00
for method_name in method_names:
2013-02-19 00:11:43 +00:00
2013-11-13 21:30:38 +00:00
if hasattr( object, method_name ):
2013-02-19 00:11:43 +00:00
2013-11-13 21:30:38 +00:00
try:
callable = getattr( object, method_name )
2013-02-19 00:11:43 +00:00
2013-11-13 21:30:38 +00:00
callables.append( callable )
except TypeError as e:
2017-03-29 19:39:34 +00:00
if '_wxPyDeadObject' not in HydrusData.ToUnicode( e ): raise
2013-02-19 00:11:43 +00:00
2015-08-26 21:18:39 +00:00
except Exception as e:
2016-01-13 22:08:19 +00:00
if not isinstance( e, self._binding_errors_to_ignore ):
2015-08-26 21:18:39 +00:00
raise
2013-02-19 00:11:43 +00:00
2013-11-13 21:30:38 +00:00
except: pass
return callables
2017-07-27 00:47:13 +00:00
def DoingWork( self ):
2013-11-13 21:30:38 +00:00
2017-07-27 00:47:13 +00:00
return self._doing_work
2013-02-19 00:11:43 +00:00
2015-08-26 21:18:39 +00:00
def Process( self ):
2013-11-13 21:30:38 +00:00
2015-08-26 21:18:39 +00:00
# only do one list of callables at a time
2013-11-13 21:30:38 +00:00
# we don't want to map a topic to its callables until the previous topic's callables have been fully executed
2014-08-27 22:15:22 +00:00
# e.g. when we start a message with a pubsub, it'll take a while (in independant thread-time) for wx to create
2013-11-13 21:30:38 +00:00
# the dialog and hence map the new callable to the topic. this was leading to messages not being updated
# because the (short) processing thread finished and entirely pubsubbed before wx had a chance to boot the
# message.
2017-07-27 00:47:13 +00:00
self._doing_work = True
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
try:
2013-02-19 00:11:43 +00:00
2017-07-27 00:47:13 +00:00
callables = []
with self._lock:
2013-11-13 21:30:38 +00:00
2017-07-27 00:47:13 +00:00
if len( self._pubsubs ) == 0:
return
2013-11-13 21:30:38 +00:00
2017-07-27 00:47:13 +00:00
pubsubs = self._pubsubs
2015-11-25 22:00:57 +00:00
2017-07-27 00:47:13 +00:00
self._pubsubs = []
2015-11-25 22:00:57 +00:00
2017-06-14 21:19:11 +00:00
2017-07-27 00:47:13 +00:00
for ( topic, args, kwargs ) in pubsubs:
2015-11-25 22:00:57 +00:00
2016-01-06 21:17:20 +00:00
try:
2017-07-27 00:47:13 +00:00
callables = self._GetCallables( topic )
2016-01-06 21:17:20 +00:00
2017-07-27 00:47:13 +00:00
# do this _outside_ the lock, lol
2016-01-06 21:17:20 +00:00
2017-07-27 00:47:13 +00:00
pubsub_profilable = topic != 'message'
if HG.pubsub_profile_mode and pubsub_profilable:
summary = 'Profiling ' + HydrusData.ConvertIntToPrettyString( len( callables ) ) + ' x ' + topic
HydrusData.ShowText( summary )
per_summary = 'Profiling ' + topic
for callable in callables:
try:
HydrusData.Profile( per_summary, 'callable( *args, **kwargs )', globals(), locals() )
except HydrusExceptions.ShutdownException:
return False
else:
for callable in callables:
try:
callable( *args, **kwargs )
except HydrusExceptions.ShutdownException:
return False
except Exception as e:
HydrusData.ShowException( e )
2016-01-06 21:17:20 +00:00
2015-11-25 22:00:57 +00:00
2015-08-26 21:18:39 +00:00
2017-07-27 00:47:13 +00:00
finally:
self._doing_work = False
2013-11-13 21:30:38 +00:00
def pub( self, topic, *args, **kwargs ):
with self._lock:
2014-08-06 20:29:17 +00:00
self._pubsubs.append( ( topic, args, kwargs ) )
2015-08-26 21:18:39 +00:00
2017-07-27 00:47:13 +00:00
self._pub_event.set()
2013-02-19 00:11:43 +00:00
2015-08-26 21:18:39 +00:00
def pubimmediate( self, topic, *args, **kwargs ):
2013-02-19 00:11:43 +00:00
with self._lock:
2015-08-26 21:18:39 +00:00
callables = self._GetCallables( topic )
2013-02-19 00:11:43 +00:00
2017-06-14 21:19:11 +00:00
for callable in callables:
callable( *args, **kwargs )
2015-08-26 21:18:39 +00:00
2013-11-13 21:30:38 +00:00
2015-08-26 21:18:39 +00:00
def sub( self, object, method_name, topic ):
2013-11-13 21:30:38 +00:00
with self._lock:
2014-06-18 21:53:48 +00:00
2015-08-26 21:18:39 +00:00
if topic not in self._topics_to_objects: self._topics_to_objects[ topic ] = weakref.WeakSet()
if topic not in self._topics_to_method_names: self._topics_to_method_names[ topic ] = set()
2013-11-13 21:30:38 +00:00
2015-08-26 21:18:39 +00:00
self._topics_to_objects[ topic ].add( object )
self._topics_to_method_names[ topic ].add( method_name )
2013-11-13 21:30:38 +00:00
2017-03-08 23:23:12 +00:00
2017-07-27 00:47:13 +00:00
def WaitOnPub( self ):
self._pub_event.wait( 3 )
self._pub_event.clear()
def WorkToDo( self ):
with self._lock:
return len( self._pubsubs ) > 0