Bugfix: With pygtk-1.99.14, only the first error message from a process
[rox-lib.git] / python / rox / processes.py
blob60ca749df5e6a94ae89e20d762c0f72bccef9363
1 """This module makes it easier to use other programs to process data."""
3 from rox import g
5 import os, sys
6 import signal
8 class Process:
9 """This represents another process. You should subclass this
10 and override the various methods. Use this when you want to
11 run another process in the background, but still be able to
12 communicate with it."""
13 def __init__(self):
14 self.child = None
16 def start(self):
17 """Create the subprocess. Calls pre_fork() and forks.
18 The parent then calls parent_post_fork() and returns,
19 while the child calls child_post_fork() and then
20 child_run()."""
22 assert self.child is None
24 try:
25 self.pre_fork()
26 stderr_r, stderr_w = os.pipe()
27 child = os.fork()
28 except:
29 os.close(stderr_r)
30 os.close(stderr_w)
31 self.start_error()
32 raise
34 if child == 0:
35 # This is the child process
36 try:
37 try:
38 os.setpgid(0, 0) # Start a new process group
39 os.close(stderr_r)
41 if stderr_w != 2:
42 os.dup2(stderr_w, 2)
43 os.close(stderr_w)
45 self.child_post_fork()
46 self.child_run()
47 raise Exception('child_run() returned!')
48 except:
49 import traceback
50 traceback.print_exc()
51 finally:
52 os._exit(1)
53 assert 0
55 self.child = child
57 # This is the parent process
58 os.close(stderr_w)
59 self.err_from_child = stderr_r
61 import gobject
62 if not hasattr(gobject, 'io_add_watch'):
63 self.tag = g.input_add_full(self.err_from_child,
64 g.gdk.INPUT_READ, self._got_errors)
65 else:
66 self.tag = gobject.io_add_watch(self.err_from_child,
67 gobject.IO_IN | gobject.IO_HUP | gobject.IO_ERR,
68 self._got_errors)
70 self.parent_post_fork()
72 def pre_fork(self):
73 """This is called in 'start' just before forking into
74 two processes. If you want to share a resource between
75 both processes (eg, a pipe), create it here.
76 Default method does nothing."""
78 def parent_post_fork(self):
79 """This is called in the parent after forking. Free the
80 child part of any resources allocated in pre_fork().
81 Also called if the fork or pre_fork() fails.
82 Default method does nothing."""
84 def child_post_fork(self):
85 """Called in the child after forking. Release the parent
86 part of any resources allocated in pre_fork().
87 Also called (in the parent) if the fork or pre_fork()
88 fails. Default method does nothing."""
90 def start_error(self):
91 """An error occurred before or during the fork (possibly
92 in pre_fork(). Clean up. Default method calls
93 parent_post_fork() and child_post_fork(). On returning,
94 the original exception will be raised."""
95 self.parent_post_fork()
96 self.child_post_fork()
98 def child_run(self):
99 """Called in the child process (after child_post_fork()).
100 Do whatever processing is required (perhaps exec another
101 process). If you don't exec, call os._exit(n) when done.
102 DO NOT make gtk calls in the child process, as it shares its
103 parent's connection to the X server until you exec()."""
104 os._exit(0)
106 def kill(self, sig = signal.SIGTERM):
107 """Send a signal to all processes in the child's process
108 group. The default, SIGTERM, requests all the processes
109 terminate. SIGKILL is more forceful."""
110 assert self.child is not None
111 os.kill(-self.child, sig)
113 def got_error_output(self, data):
114 """Read some characters from the child's stderr stream.
115 The default method copies to our stderr. Note that 'data'
116 isn't necessarily a complete line; it could be a single
117 character, or several lines, etc."""
118 sys.stderr.write(data)
120 def _got_errors(self, source, cond):
121 got = os.read(self.err_from_child, 100)
122 if got:
123 self.got_error_output(got)
124 return 1
126 os.close(self.err_from_child)
127 g.input_remove(self.tag)
128 del self.tag
130 pid, status = os.waitpid(self.child, 0)
131 self.child = None
132 self.child_died(status)
134 def child_died(self, status):
135 """Called when the child died (actually, when the child
136 closes its end of the stderr pipe). The child process has
137 already been reaped at this point; 'status' is the status
138 returned by os.waitpid."""