##// END OF EJS Templates
Moving extensions to either quarantine or deathrow....
Moving extensions to either quarantine or deathrow. When a module is moved to quarantine, it means that while we intend to keep it, it is currently broken or sufficiently untested that it can't be in the main IPython codebase. To be moved back into the main IPython codebase a module must: 1. Work fully. 2. Have a test suite. 3. Be a proper IPython extension and tie into the official APIs. 3. Have members of the IPython dev team who are willing to maintain it. When a module is moved to deathrow, it means that the code is either broken and not worth repairing, deprecated, replaced by newer functionality, or code that should be developed and maintained by a third party.

File last commit:

r2267:928c921b
r2267:928c921b
Show More
jobctrl.py
243 lines | 6.4 KiB | text/x-python | PythonLexer
ville
initialization (no svn history)
r988 """ Preliminary "job control" extensions for IPython
requires python 2.4 (or separate 'subprocess' module
This provides 2 features, launching background jobs and killing foreground jobs from another IPython instance.
Launching background jobs:
Usage:
[ipython]|2> import jobctrl
[ipython]|3> &ls
<3> <jobctrl.IpyPopen object at 0x00D87FD0>
[ipython]|4> _3.go
-----------> _3.go()
ChangeLog
IPython
MANIFEST.in
README
README_Windows.txt
...
Killing foreground tasks:
Launch IPython instance, run a blocking command:
[Q:/ipython]|1> import jobctrl
[Q:/ipython]|2> cat
Now launch a new IPython prompt and kill the process:
IPython 0.8.3.svn.r2919 [on Py 2.5]
[Q:/ipython]|1> import jobctrl
[Q:/ipython]|2> %tasks
6020: 'cat ' (Q:\ipython)
[Q:/ipython]|3> %kill
SUCCESS: The process with PID 6020 has been terminated.
[Q:/ipython]|4>
(you don't need to specify PID for %kill if only one task is running)
"""
Ville M. Vainio
jobctrl.py: added %jobqueue for queueued system jobs
r1030 from subprocess import *
ville
initialization (no svn history)
r988 import os,shlex,sys,time
Ville M. Vainio
jobctrl.py: added %jobqueue for queueued system jobs
r1030 import threading,Queue
ville
initialization (no svn history)
r988
Brian Granger
genutils.py => utils/genutils.py and updated imports and tests.
r2023 from IPython.utils import genutils
ville
initialization (no svn history)
r988
Brian Granger
ipapi.py => core/ipapi.py and imports updated.
r2027 from IPython.core import ipapi
Brian Granger
Continuing a massive refactor of everything.
r2205 from IPython.core.error import TryNext
ville
initialization (no svn history)
r988
if os.name == 'nt':
def kill_process(pid):
os.system('taskkill /F /PID %d' % pid)
else:
def kill_process(pid):
os.system('kill -9 %d' % pid)
class IpyPopen(Popen):
def go(self):
print self.communicate()[0]
def __repr__(self):
return '<IPython job "%s" PID=%d>' % (self.line, self.pid)
def kill(self):
kill_process(self.pid)
def startjob(job):
p = IpyPopen(shlex.split(job), stdout=PIPE, shell = False)
p.line = job
return p
Ville M. Vainio
jobctrl.py: added %jobqueue for queueued system jobs
r1030 class AsyncJobQ(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.q = Queue.Queue()
self.output = []
self.stop = False
def run(self):
while 1:
cmd,cwd = self.q.get()
if self.stop:
self.output.append("** Discarding: '%s' - %s" % (cmd,cwd))
continue
self.output.append("** Task started: '%s' - %s" % (cmd,cwd))
p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd = cwd)
out = p.stdout.read()
self.output.append("** Task complete: '%s'\n" % cmd)
self.output.append(out)
def add(self,cmd):
self.q.put_nowait((cmd, os.getcwd()))
def dumpoutput(self):
while self.output:
item = self.output.pop(0)
print item
_jobq = None
def jobqueue_f(self, line):
global _jobq
if not _jobq:
print "Starting jobqueue - do '&some_long_lasting_system_command' to enqueue"
_jobq = AsyncJobQ()
_jobq.setDaemon(True)
_jobq.start()
ip.jobq = _jobq.add
return
if line.strip() == 'stop':
print "Stopping and clearing jobqueue, %jobqueue start to start again"
_jobq.stop = True
return
if line.strip() == 'start':
_jobq.stop = False
return
ville
initialization (no svn history)
r988 def jobctrl_prefilter_f(self,line):
if line.startswith('&'):
pre,fn,rest = self.split_user_input(line[1:])
Brian Granger
Continuing a massive refactor of everything.
r2205 line = ip.expand_aliases(fn,rest)
Ville M. Vainio
jobctrl.py: added %jobqueue for queueued system jobs
r1030 if not _jobq:
Brian Granger
Massive refactoring of of the core....
r2245 return 'get_ipython().startjob(%s)' % genutils.make_quoted_expr(line)
return 'get_ipython().jobq(%s)' % genutils.make_quoted_expr(line)
ville
initialization (no svn history)
r988
Brian Granger
Continuing a massive refactor of everything.
r2205 raise TryNext
ville
initialization (no svn history)
r988
Ville M. Vainio
jobctrl.py: added %jobqueue for queueued system jobs
r1030 def jobq_output_hook(self):
if not _jobq:
return
_jobq.dumpoutput()
ville
initialization (no svn history)
r988
def job_list(ip):
keys = ip.db.keys('tasks/*')
ents = [ip.db[k] for k in keys]
return ents
def magic_tasks(self,line):
""" Show a list of tasks.
A 'task' is a process that has been started in IPython when 'jobctrl' extension is enabled.
Tasks can be killed with %kill.
Ville M. Vainio
%tasks clear: clear task list
r1029
'%tasks clear' clears the task list (from stale tasks)
ville
initialization (no svn history)
r988 """
ip = self.getapi()
Ville M. Vainio
%tasks clear: clear task list
r1029 if line.strip() == 'clear':
for k in ip.db.keys('tasks/*'):
print "Clearing",ip.db[k]
del ip.db[k]
return
ville
initialization (no svn history)
r988 ents = job_list(ip)
if not ents:
print "No tasks running"
for pid,cmd,cwd,t in ents:
dur = int(time.time()-t)
print "%d: '%s' (%s) %d:%02d" % (pid,cmd,cwd, dur / 60,dur%60)
def magic_kill(self,line):
""" Kill a task
Without args, either kill one task (if only one running) or show list (if many)
With arg, assume it's the process id.
%kill is typically (much) more powerful than trying to terminate a process with ctrl+C.
"""
ip = self.getapi()
jobs = job_list(ip)
if not line.strip():
if len(jobs) == 1:
kill_process(jobs[0][0])
else:
magic_tasks(self,line)
return
try:
pid = int(line)
kill_process(pid)
except ValueError:
magic_tasks(self,line)
if sys.platform == 'win32':
Ville M. Vainio
jobctrl: use shell for 'start' internal command
r1129 shell_internal_commands = 'break chcp cls copy ctty date del erase dir md mkdir path prompt rd rmdir start time type ver vol'.split()
Ville M. Vainio
fix jobctrl for linux (catch OSError instead of WindowsError)
r1232 PopenExc = WindowsError
ville
initialization (no svn history)
r988 else:
# todo linux commands
shell_internal_commands = []
Ville M. Vainio
fix jobctrl for linux (catch OSError instead of WindowsError)
r1232 PopenExc = OSError
ville
initialization (no svn history)
r988
def jobctrl_shellcmd(ip,cmd):
""" os.system replacement that stores process info to db['tasks/t1234'] """
vivainio2
ipy_leo: mb completer also works with %
r1022 cmd = cmd.strip()
ville
initialization (no svn history)
r988 cmdname = cmd.split(None,1)[0]
Ville M. Vainio
jobctrl: use shell if the command line has shell control chars (|><), so that "bzr log | less" etc. work
r1034 if cmdname in shell_internal_commands or '|' in cmd or '>' in cmd or '<' in cmd:
ville
initialization (no svn history)
r988 use_shell = True
else:
use_shell = False
vivainio2
jobctrl: use shell for subprocess if trying without it fails
r1021 jobentry = None
ville
initialization (no svn history)
r988 try:
vivainio2
jobctrl: use shell for subprocess if trying without it fails
r1021 try:
p = Popen(cmd,shell = use_shell)
Ville M. Vainio
fix jobctrl for linux (catch OSError instead of WindowsError)
r1232 except PopenExc :
vivainio2
jobctrl: use shell for subprocess if trying without it fails
r1021 if use_shell:
# try with os.system
os.system(cmd)
return
else:
# have to go via shell, sucks
p = Popen(cmd,shell = True)
jobentry = 'tasks/t' + str(p.pid)
ville
initialization (no svn history)
r988 ip.db[jobentry] = (p.pid,cmd,os.getcwd(),time.time())
vivainio2
jobctrl: use shell for subprocess if trying without it fails
r1021 p.communicate()
ville
initialization (no svn history)
r988 finally:
vivainio2
jobctrl: use shell for subprocess if trying without it fails
r1021 if jobentry:
del ip.db[jobentry]
ville
initialization (no svn history)
r988
def install():
global ip
Brian Granger
ipapi.py => core/ipapi.py and imports updated.
r2027 ip = ipapi.get()
ville
initialization (no svn history)
r988 # needed to make startjob visible as _ip.startjob('blah')
ip.startjob = startjob
ip.set_hook('input_prefilter', jobctrl_prefilter_f)
ip.set_hook('shell_hook', jobctrl_shellcmd)
Brian Granger
Continuing a massive refactor of everything.
r2205 ip.define_magic('kill',magic_kill)
ip.define_magic('tasks',magic_tasks)
ip.define_magic('jobqueue',jobqueue_f)
Ville M. Vainio
jobctrl.py: added %jobqueue for queueued system jobs
r1030 ip.set_hook('pre_prompt_hook', jobq_output_hook)
ville
initialization (no svn history)
r988 install()