Kiln » TortoiseHg » TortoiseHg
Clone URL:  
Pushed to one repository · View In Graph Contained in 0.4rc1, 0.4rc2, and 0.4rc3

hggtk/datamine: allow grep searches to be interrupted

Introduce thread2 - interruptable subclass of threading.Thread

Changeset ca21a37af375

Parent 33753d10447f

by Steve Borho

Changes to 2 files · Browse files at ca21a37af375 Showing diff from parent 33753d10447f Diff from another changeset...

Change 1 of 6 Show Entire File hggtk/​datamine.py Stacked
 
9
10
11
12
 
13
14
15
 
36
37
38
39
40
 
 
 
 
 
41
42
43
 
264
265
266
267
 
268
 
269
270
271
 
282
283
284
285
 
286
287
 
288
289
290
 
299
300
301
 
302
303
304
 
316
317
318
 
 
 
 
 
 
 
 
319
320
321
 
9
10
11
 
12
13
14
15
 
36
37
38
 
 
39
40
41
42
43
44
45
46
 
267
268
269
 
270
271
272
273
274
275
 
286
287
288
 
289
290
 
291
292
293
294
 
303
304
305
306
307
308
309
 
321
322
323
324
325
326
327
328
329
330
331
332
333
334
@@ -9,7 +9,7 @@
 import pango  import Queue  import re -import threading +import threading, thread2  import time  from mercurial import hg, ui, util, revlog  from hglib import hgcmd_toq @@ -36,8 +36,11 @@
  pass     def get_tbbuttons(self): - return [ self.make_toolbutton(gtk.STOCK_FIND, 'New Search', - self._search_clicked, tip='Open new search tab') + return [ + self.make_toolbutton(gtk.STOCK_FIND, 'New Search', + self._search_clicked, tip='Open new search tab'), + self.make_toolbutton(gtk.STOCK_STOP, 'Stop Search', + self._stop_search, tip='Stop search on current tab')   ]     def prepare_display(self): @@ -264,8 +267,9 @@
  for x in excs:   if x: args.extend(['-X', x])   args.append(re) - thread = threading.Thread(target=hgcmd_toq, args=args) + thread = thread2.Thread(target=hgcmd_toq, args=args)   thread.start() + frame._mythread = thread     model.clear()   search_hbox.set_sensitive(False) @@ -282,9 +286,9 @@
  self.notebook.set_tab_label(frame, hbox)     gobject.timeout_add(50, self.grep_wait, thread, q, model, - search_hbox, regexp) + search_hbox, regexp, frame)   - def grep_wait(self, thread, q, model, search_hbox, regexp): + def grep_wait(self, thread, q, model, search_hbox, regexp, frame):   """   Handle all the messages currently in the queue (if any).   """ @@ -299,6 +303,7 @@
  if thread.isAlive():   return True   else: + frame._mythread = None   search_hbox.set_sensitive(True)   regexp.grab_focus()   self.stbar.end() @@ -316,6 +321,14 @@
  self.curpath = model[iter][self.COL_PATH]   self.stbar.set_status_text(model[iter][self.COL_TOOLTIP])   + def _stop_search(self, button, widget): + num = self.notebook.get_current_page() + frame = self.notebook.get_nth_page(num) + if hasattr(frame, '_mythread') and frame._mythread: + frame._mythread.terminate() + frame._mythread.join() + frame._mythread = None +   def close_page(self, button, widget):   '''Close page button has been pressed'''   num = self.notebook.page_num(widget)
Change 1 of 1 Show Entire File hggtk/​thread2.py Stacked
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@@ -0,0 +1,49 @@
+# Interuptible threads +# +# http://sebulba.wikispaces.com/recipe+thread2 + +import threading +import inspect +import ctypes + + +def _async_raise(tid, exctype): + """raises the exception, performs cleanup if needed""" + if not inspect.isclass(exctype): + raise TypeError("Only types can be raised (not instances)") + res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype)) + if res == 0: + raise ValueError("invalid thread id") + elif res != 1: + # """if it returns a number greater than one, you're in trouble, + # and you should call it again with exc=NULL to revert the effect""" + ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) + raise SystemError("PyThreadState_SetAsyncExc failed") + + +class Thread(threading.Thread): + def _get_my_tid(self): + """determines this (self's) thread id""" + if not self.isAlive(): + raise threading.ThreadError("the thread is not active") + + # do we have it cached? + if hasattr(self, "_thread_id"): + return self._thread_id + + # no, look for it in the _active dict + for tid, tobj in threading._active.items(): + if tobj is self: + self._thread_id = tid + return tid + + raise AssertionError("could not determine the thread's id") + + def raise_exc(self, exctype): + """raises the given exception type in the context of this thread""" + _async_raise(self._get_my_tid(), exctype) + + def terminate(self): + """raises SystemExit in the context of the given thread, which should + cause the thread to exit silently (unless caught)""" + self.raise_exc(SystemExit)