From: <pj...@us...> - 2009-04-03 06:06:53
|
Revision: 6154 http://jython.svn.sourceforge.net/jython/?rev=6154&view=rev Author: pjenvey Date: 2009-04-03 06:06:39 +0000 (Fri, 03 Apr 2009) Log Message: ----------- o rewrite popen2/os.{popen,system} to use subprocess. fixes popen2 deadlocking o bump test_communicate_pipe_buf to a larger size as per #1124's test o deprecate javashell which is no longer used thanks Pekka Klarck fixes #1124 Modified Paths: -------------- trunk/jython/Lib/javashell.py trunk/jython/Lib/os.py trunk/jython/Lib/popen2.py trunk/jython/Lib/test/test_subprocess.py Modified: trunk/jython/Lib/javashell.py =================================================================== --- trunk/jython/Lib/javashell.py 2009-04-03 06:02:36 UTC (rev 6153) +++ trunk/jython/Lib/javashell.py 2009-04-03 06:06:39 UTC (rev 6154) @@ -20,8 +20,11 @@ import subprocess import sys import types +import warnings +warnings.warn('The javashell module is deprecated. Use the subprocess module.', + DeprecationWarning, 2) -__all__ = [ "shellexecute", "environ", "putenv", "getenv" ] +__all__ = ["shellexecute"] def __warn( *args ): print " ".join( [str( arg ) for arg in args ]) Modified: trunk/jython/Lib/os.py =================================================================== --- trunk/jython/Lib/os.py 2009-04-03 06:02:36 UTC (rev 6153) +++ trunk/jython/Lib/os.py 2009-04-03 06:06:39 UTC (rev 6154) @@ -727,27 +727,28 @@ except: raise OSError(errno.EBADF, strerror(errno.EBADF)) -# Provide lazy popen*, and system objects -# Do these lazily, as most jython programs don't need them, -# and they are very expensive to initialize - -def system( *args, **kwargs ): +def system(command): """system(command) -> exit_status Execute the command (a string) in a subshell. """ - # allow lazy import of popen2 and javashell - import popen2 - return popen2.system( *args, **kwargs ) + import subprocess + return subprocess.call(command, shell=True) -def popen( *args, **kwargs ): +def popen(command, mode='r', bufsize=-1): """popen(command [, mode='r' [, bufsize]]) -> pipe Open a pipe to/from a command returning a file object. """ - # allow lazy import of popen2 and javashell - import popen2 - return popen2.popen( *args, **kwargs ) + import subprocess + if mode == 'r': + return subprocess.Popen(command, bufsize=bufsize, shell=True, + stdout=subprocess.PIPE).stdout + elif mode == 'w': + return subprocess.Popen(command, bufsize=bufsize, shell=True, + stdin=subprocess.PIPE).stdin + else: + raise OSError(errno.EINVAL, strerror(errno.EINVAL)) # os module versions of the popen# methods have different return value # order than popen2 functions Modified: trunk/jython/Lib/popen2.py =================================================================== --- trunk/jython/Lib/popen2.py 2009-04-03 06:02:36 UTC (rev 6153) +++ trunk/jython/Lib/popen2.py 2009-04-03 06:06:39 UTC (rev 6154) @@ -7,27 +7,15 @@ """ import os +import subprocess import sys __all__ = ["popen2", "popen3", "popen4"] -try: - MAXFD = os.sysconf('SC_OPEN_MAX') -except (AttributeError, ValueError): - MAXFD = 256 +MAXFD = subprocess.MAXFD +_active = subprocess._active +_cleanup = subprocess._cleanup -_active = [] - -def _cleanup(): - for inst in _active[:]: - if inst.poll(_deadstate=sys.maxint) >= 0: - try: - _active.remove(inst) - except ValueError: - # This can happen if two threads create a new Popen instance. - # It's harmless that it was already removed, so ignore. - pass - class Popen3: """Class representing a child process. Normally instances are created by the factory functions popen2() and popen3().""" @@ -44,73 +32,36 @@ process. The default is false. If the 'bufsize' parameter is specified, it specifies the size of the I/O buffers to/from the child process.""" - _cleanup() + stderr = subprocess.PIPE if capturestderr else None + PIPE = subprocess.PIPE + self._popen = subprocess.Popen(cmd, bufsize=bufsize, shell=True, + stdin=PIPE, stdout=PIPE, stderr=stderr) + self._setup(cmd) + + def _setup(self, cmd): + """Setup the Popen attributes.""" self.cmd = cmd - p2cread, p2cwrite = os.pipe() - c2pread, c2pwrite = os.pipe() - if capturestderr: - errout, errin = os.pipe() - self.pid = os.fork() - if self.pid == 0: - # Child - os.dup2(p2cread, 0) - os.dup2(c2pwrite, 1) - if capturestderr: - os.dup2(errin, 2) - self._run_child(cmd) - os.close(p2cread) - self.tochild = os.fdopen(p2cwrite, 'w', bufsize) - os.close(c2pwrite) - self.fromchild = os.fdopen(c2pread, 'r', bufsize) - if capturestderr: - os.close(errin) - self.childerr = os.fdopen(errout, 'r', bufsize) - else: - self.childerr = None + self.pid = self._popen.pid + self.tochild = self._popen.stdin + self.fromchild = self._popen.stdout + self.childerr = self._popen.stderr def __del__(self): - # In case the child hasn't been waited on, check if it's done. - self.poll(_deadstate=sys.maxint) - if self.sts < 0: - if _active is not None: - # Child is still running, keep us alive until we can wait on it. - _active.append(self) + self._popen.__del__() - def _run_child(self, cmd): - if isinstance(cmd, basestring): - cmd = ['/bin/sh', '-c', cmd] - for i in xrange(3, MAXFD): - try: - os.close(i) - except OSError: - pass - try: - os.execvp(cmd[0], cmd) - finally: - os._exit(1) - def poll(self, _deadstate=None): """Return the exit status of the child process if it has finished, or -1 if it hasn't finished yet.""" if self.sts < 0: - try: - pid, sts = os.waitpid(self.pid, os.WNOHANG) - # pid will be 0 if self.pid hasn't terminated - if pid == self.pid: - self.sts = sts - except os.error: - if _deadstate is not None: - self.sts = _deadstate + result = self._popen.poll(_deadstate) + if result is not None: + self.sts = result return self.sts def wait(self): """Wait for and return the exit status of the child process.""" if self.sts < 0: - pid, sts = os.waitpid(self.pid, 0) - # This used to be a test, but it is believed to be - # always true, so I changed it to an assertion - mvl - assert pid == self.pid - self.sts = sts + self.sts = self._popen.wait() return self.sts @@ -118,21 +69,11 @@ childerr = None def __init__(self, cmd, bufsize=-1): - _cleanup() - self.cmd = cmd - p2cread, p2cwrite = os.pipe() - c2pread, c2pwrite = os.pipe() - self.pid = os.fork() - if self.pid == 0: - # Child - os.dup2(p2cread, 0) - os.dup2(c2pwrite, 1) - os.dup2(c2pwrite, 2) - self._run_child(cmd) - os.close(p2cread) - self.tochild = os.fdopen(p2cwrite, 'w', bufsize) - os.close(c2pwrite) - self.fromchild = os.fdopen(c2pread, 'r', bufsize) + PIPE = subprocess.PIPE + self._popen = subprocess.Popen(cmd, bufsize=bufsize, shell=True, + stdin=PIPE, stdout=PIPE, + stderr=subprocess.STDOUT) + self._setup(cmd) if sys.platform[:3] == "win" or sys.platform == "os2emx": @@ -207,7 +148,7 @@ assert not _active, "Active pipes when test starts " + repr([c.cmd for c in _active]) cmd = "cat" teststr = "ab cd\n" - if os.name == "nt": + if os.name in ("nt", "java"): cmd = "more" # "more" doesn't act the same way across Windows flavors, # sometimes adding an extra newline at the start or the Modified: trunk/jython/Lib/test/test_subprocess.py =================================================================== --- trunk/jython/Lib/test/test_subprocess.py 2009-04-03 06:02:36 UTC (rev 6153) +++ trunk/jython/Lib/test/test_subprocess.py 2009-04-03 06:06:39 UTC (rev 6154) @@ -323,8 +323,10 @@ # communicate() with writes larger than pipe_buf # This test will probably deadlock rather than fail, if # communicate() does not work properly. - if mswindows or jython: + if mswindows: pipe_buf = 512 + elif jython: + pipe_buf = 16384 else: x, y = os.pipe() pipe_buf = os.fpathconf(x, "PC_PIPE_BUF") This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |