##// END OF EJS Templates
%tasks clear: clear task list
Ville M. Vainio -
Show More
@@ -1,175 +1,183 b''
1 1 """ Preliminary "job control" extensions for IPython
2 2
3 3 requires python 2.4 (or separate 'subprocess' module
4 4
5 5 This provides 2 features, launching background jobs and killing foreground jobs from another IPython instance.
6 6
7 7 Launching background jobs:
8 8
9 9 Usage:
10 10
11 11 [ipython]|2> import jobctrl
12 12 [ipython]|3> &ls
13 13 <3> <jobctrl.IpyPopen object at 0x00D87FD0>
14 14 [ipython]|4> _3.go
15 15 -----------> _3.go()
16 16 ChangeLog
17 17 IPython
18 18 MANIFEST.in
19 19 README
20 20 README_Windows.txt
21 21
22 22 ...
23 23
24 24 Killing foreground tasks:
25 25
26 26 Launch IPython instance, run a blocking command:
27 27
28 28 [Q:/ipython]|1> import jobctrl
29 29 [Q:/ipython]|2> cat
30 30
31 31 Now launch a new IPython prompt and kill the process:
32 32
33 33 IPython 0.8.3.svn.r2919 [on Py 2.5]
34 34 [Q:/ipython]|1> import jobctrl
35 35 [Q:/ipython]|2> %tasks
36 36 6020: 'cat ' (Q:\ipython)
37 37 [Q:/ipython]|3> %kill
38 38 SUCCESS: The process with PID 6020 has been terminated.
39 39 [Q:/ipython]|4>
40 40
41 41 (you don't need to specify PID for %kill if only one task is running)
42 42 """
43 43
44 44 from subprocess import Popen,PIPE
45 45 import os,shlex,sys,time
46 46
47 47 from IPython import genutils
48 48
49 49 import IPython.ipapi
50 50
51 51 if os.name == 'nt':
52 52 def kill_process(pid):
53 53 os.system('taskkill /F /PID %d' % pid)
54 54 else:
55 55 def kill_process(pid):
56 56 os.system('kill -9 %d' % pid)
57 57
58 58
59 59
60 60 class IpyPopen(Popen):
61 61 def go(self):
62 62 print self.communicate()[0]
63 63 def __repr__(self):
64 64 return '<IPython job "%s" PID=%d>' % (self.line, self.pid)
65 65
66 66 def kill(self):
67 67 kill_process(self.pid)
68 68
69 69 def startjob(job):
70 70 p = IpyPopen(shlex.split(job), stdout=PIPE, shell = False)
71 71 p.line = job
72 72 return p
73 73
74 74 def jobctrl_prefilter_f(self,line):
75 75 if line.startswith('&'):
76 76 pre,fn,rest = self.split_user_input(line[1:])
77 77
78 78 line = ip.IP.expand_aliases(fn,rest)
79 79 return '_ip.startjob(%s)' % genutils.make_quoted_expr(line)
80 80
81 81 raise IPython.ipapi.TryNext
82 82
83 83
84 84 def job_list(ip):
85 85 keys = ip.db.keys('tasks/*')
86 86 ents = [ip.db[k] for k in keys]
87 87 return ents
88 88
89 89 def magic_tasks(self,line):
90 90 """ Show a list of tasks.
91 91
92 92 A 'task' is a process that has been started in IPython when 'jobctrl' extension is enabled.
93 93 Tasks can be killed with %kill.
94
95 '%tasks clear' clears the task list (from stale tasks)
94 96 """
95 97 ip = self.getapi()
98 if line.strip() == 'clear':
99 for k in ip.db.keys('tasks/*'):
100 print "Clearing",ip.db[k]
101 del ip.db[k]
102 return
103
96 104 ents = job_list(ip)
97 105 if not ents:
98 106 print "No tasks running"
99 107 for pid,cmd,cwd,t in ents:
100 108 dur = int(time.time()-t)
101 109 print "%d: '%s' (%s) %d:%02d" % (pid,cmd,cwd, dur / 60,dur%60)
102 110
103 111 def magic_kill(self,line):
104 112 """ Kill a task
105 113
106 114 Without args, either kill one task (if only one running) or show list (if many)
107 115 With arg, assume it's the process id.
108 116
109 117 %kill is typically (much) more powerful than trying to terminate a process with ctrl+C.
110 118 """
111 119 ip = self.getapi()
112 120 jobs = job_list(ip)
113 121
114 122 if not line.strip():
115 123 if len(jobs) == 1:
116 124 kill_process(jobs[0][0])
117 125 else:
118 126 magic_tasks(self,line)
119 127 return
120 128
121 129 try:
122 130 pid = int(line)
123 131 kill_process(pid)
124 132 except ValueError:
125 133 magic_tasks(self,line)
126 134
127 135 if sys.platform == 'win32':
128 136 shell_internal_commands = 'break chcp cls copy ctty date del erase dir md mkdir path prompt rd rmdir time type ver vol'.split()
129 137 else:
130 138 # todo linux commands
131 139 shell_internal_commands = []
132 140
133 141
134 142 def jobctrl_shellcmd(ip,cmd):
135 143 """ os.system replacement that stores process info to db['tasks/t1234'] """
136 144 cmd = cmd.strip()
137 145 cmdname = cmd.split(None,1)[0]
138 146 if cmdname in shell_internal_commands:
139 147 use_shell = True
140 148 else:
141 149 use_shell = False
142 150
143 151 jobentry = None
144 152 try:
145 153 try:
146 154 p = Popen(cmd,shell = use_shell)
147 155 except WindowsError:
148 156 if use_shell:
149 157 # try with os.system
150 158 os.system(cmd)
151 159 return
152 160 else:
153 161 # have to go via shell, sucks
154 162 p = Popen(cmd,shell = True)
155 163
156 164 jobentry = 'tasks/t' + str(p.pid)
157 165 ip.db[jobentry] = (p.pid,cmd,os.getcwd(),time.time())
158 166 p.communicate()
159 167
160 168 finally:
161 169 if jobentry:
162 170 del ip.db[jobentry]
163 171
164 172
165 173 def install():
166 174 global ip
167 175 ip = IPython.ipapi.get()
168 176 # needed to make startjob visible as _ip.startjob('blah')
169 177 ip.startjob = startjob
170 178 ip.set_hook('input_prefilter', jobctrl_prefilter_f)
171 179 ip.set_hook('shell_hook', jobctrl_shellcmd)
172 180 ip.expose_magic('kill',magic_kill)
173 181 ip.expose_magic('tasks',magic_tasks)
174 182
175 183 install()
General Comments 0
You need to be logged in to leave comments. Login now