]> Tony Duckles's Git Repositories (git.nynim.org) - svn2svn.git/blob - svn2svn/run/breakhandler.py
Prevent KeyboardInterrupt's during SVN commit
[svn2svn.git] / svn2svn / run / breakhandler.py
1 '''
2 Trap keyboard interrupts. No rights reserved; use at your own risk.
3
4 @author: Stacy Prowell (http://stacyprowell.com)
5 @url: http://stacyprowell.com/blog/2009/03/30/trapping-ctrlc-in-python/
6 '''
7 import signal
8
9 class BreakHandler:
10 '''
11 Trap CTRL-C, set a flag, and keep going. This is very useful for
12 gracefully exiting database loops while simulating transactions.
13
14 To use this, make an instance and then enable it. You can check
15 whether a break was trapped using the trapped property.
16
17 # Create and enable a break handler.
18 ih = BreakHandler()
19 ih.enable()
20 for x in big_set:
21 complex_operation_1()
22 complex_operation_2()
23 complex_operation_3()
24 # Check whether there was a break.
25 if ih.trapped:
26 # Stop the loop.
27 break
28 ih.disable()
29 # Back to usual operation...
30 '''
31
32 def __init__(self, emphatic=9):
33 '''
34 Create a new break handler.
35
36 @param emphatic: This is the number of times that the user must
37 press break to *disable* the handler. If you press
38 break this number of times, the handler is automagically
39 disabled, and one more break will trigger an old
40 style keyboard interrupt. The default is nine. This
41 is a Good Idea, since if you happen to lose your
42 connection to the handler you can *still* disable it.
43 '''
44 self._count = 0
45 self._enabled = False
46 self._emphatic = emphatic
47 self._oldhandler = None
48 return
49
50 def _reset(self):
51 '''
52 Reset the trapped status and count. You should not need to use this
53 directly; instead you can disable the handler and then re-enable it.
54 This is better, in case someone presses CTRL-C during this operation.
55 '''
56 self._count = 0
57 return
58
59 def enable(self):
60 '''
61 Enable trapping of the break. This action also resets the
62 handler count and trapped properties.
63 '''
64 if not self._enabled:
65 self._reset()
66 self._enabled = True
67 self._oldhandler = signal.signal(signal.SIGINT, self)
68 return
69
70 def disable(self):
71 '''
72 Disable trapping the break. You can check whether a break
73 was trapped using the count and trapped properties.
74 '''
75 if self._enabled:
76 self._enabled = False
77 signal.signal(signal.SIGINT, self._oldhandler)
78 self._oldhandler = None
79 return
80
81 def __call__(self, signame, sf):
82 '''
83 An break just occurred. Save information about it and keep
84 going.
85 '''
86 self._count += 1
87 # If we've exceeded the "emphatic" count disable this handler.
88 if self._count >= self._emphatic:
89 self.disable()
90 return
91
92 def __del__(self):
93 '''
94 Python is reclaiming this object, so make sure we are disabled.
95 '''
96 self.disable()
97 return
98
99 @property
100 def count(self):
101 '''
102 The number of breaks trapped.
103 '''
104 return self._count
105
106 @property
107 def trapped(self):
108 '''
109 Whether a break was trapped.
110 '''
111 return self._count > 0