[prev in list] [next in list] [prev in thread] [next in thread] 

List:       pykde
Subject:    Re: [PyQt] Any way to run code before each QThread?
From:       John Ehresman <jpe () wingware ! com>
Date:       2018-07-16 15:03:51
Message-ID: 8942d158-6bf4-8840-2732-690a3095e859 () wingware ! com
[Download RAW message or body]

On 7/15/18 8:19 PM, Ned Batchelder wrote:
> How does Wing wrap the run method?   How does it get a chance to do that?

Wing monkeypatches QThread to replace QThread.__init__ with it's own 
callable after the QtCore module is loaded.  Within the __init__ 
replacement, the .run method on the new instance is replaced by a 
replacement run callable that calls settrace before calling the saved 
run method.  This is done because user code does override the run method 
in subclasses of QThread and monkeypatching in __init__ captures the 
overridden run method in most cases.  I've attached a snippet of Wing's 
code; it's an extract and won't run as is but hopefully it's helpful.

This is an ugly hack and there is code that it won't work for, but it 
should work most of the time.

Cheers,

John

["qthreadhack.py" (text/x-python-script)]

import sys
import new

class CQThreadInitWrapper:
  """Used to wrap QThread.__init__ so we can debug QThreads"""
    
  def __init__(self, orig_new_thread, settrace, err):
        
    self.fOrigQThread = orig_new_thread
    self.fSetTrace = settrace
    self.fErr = err
    
  def __call__(self, qthread, *args, **kw):
    """This is the wrapper for QThread.__init__.  Calls the original
    __init__ then sets up a wrapper around run()."""
    
    self.fErr.out("CQThreadInitWrapper called")
    if kw:
      self.fOrigQThread(qthread, *args, **kw)
    elif args:
      self.fOrigQThread(qthread, *args)
    else:
      self.fOrigQThread(qthread)
    
    self._fOrigRun = qthread.run
    
    def run_wrapper(*args, **kw):
      self.fSetTrace(dbgtracer.bootstrap_new_thread)
      if kw:
        self._fOrigRun(*args, **kw)
      elif args:
        self._fOrigRun(*args)
      else:
        self._fOrigRun()
      dbgtracer.thread_exiting()
      
    qthread.run = run_wrapper

  # Since this callable class isn't a builtin function, __get__
  # is needed to to allow it to be used for an instance method. See
  # http://docs.python.org/3/howto/descriptor.html#functions-and-methods
  # Note python3's types.MethodType only takes 2 args (the above page
  # is incorrect as of Jan 21, 2014) 
  if sys.version_info >= (3, 0):
    def __get__(self, obj, objtype=None):
      "Simulate func_descr_get() in Objects/funcobject.c"
      
      # Just return the callable if no instance; case when
      # repr(QThread.__init__) is called.  Calling MethodType w/ obj = None
      # raises an exception
      if obj is None:
        return self
      
      return types.MethodType(self, obj)
    
  def __repr__(self):
    return "<wing debugger wrapper for " + repr(self.fOrigQThread) + ">"

wrapper = CQThreadInitWrapper(QtCore.__init__, sys.settrace, err_stream)
if sys.version_info >= (3, 0):
  QtCore.QThread.__init__ = wrapper
else:
  QtCore.QThread.__init__ = new.instancemethod(wrapper, None, QtCore.QThread)

[Attachment #4 (text/plain)]

_______________________________________________
PyQt mailing list    PyQt@riverbankcomputing.com
https://www.riverbankcomputing.com/mailman/listinfo/pyqt

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic