[docs]classFFProxy:"""Proxies a target object and runs its methods in the background. All method calls to this object are forwarded to the target and executed in a background thread. Method calls return immediately. Exceptions from the target method are turned into warnings. At most one method from the target object may be executed in the background; if a new call is submitted while the previous one is still executing, a warning is printed and the new call is dropped. This feature is typically used to wrap slow and non-critical RPCs in experiments. """def__init__(self,target):self.target=targetvalid_methods=inspect.getmembers(target,inspect.ismethod)self._valid_methods={m[0]forminvalid_methods}self._thread=None
[docs]defff_join(self):"""Waits until any background method finishes its execution."""ifself._threadisnotNone:self._thread.join()
def__getattr__(self,k):ifknotinself._valid_methods:raiseAttributeErrordefrun_in_thread(*args,**kwargs):ifself._threadisnotNoneandself._thread.is_alive():logger.warning("skipping fire-and-forget call to %r.%s as ""previous call did not complete",self.target,k)returndefthread_body():try:getattr(self.target,k)(*args,**kwargs)except:logger.warning("fire-and-forget call to %r.%s raised an ""exception:",self.target,k,exc_info=True)self._thread=threading.Thread(target=thread_body)self._thread.start()returnrun_in_thread