[pypy-svn] r40862 - in pypy/dist/pypy/module/posix: . test

arigo at codespeak.net arigo at codespeak.net
Tue Mar 20 20:24:09 CET 2007


Author: arigo
Date: Tue Mar 20 20:24:06 2007
New Revision: 40862

Modified:
   pypy/dist/pypy/module/posix/__init__.py
   pypy/dist/pypy/module/posix/app_posix.py
   pypy/dist/pypy/module/posix/test/test_posix_libfile.py
Log:
An app-level version of posix.popen().  It returns a subclass of 'file'
with its close() method overridden to wait on the child process.


Modified: pypy/dist/pypy/module/posix/__init__.py
==============================================================================
--- pypy/dist/pypy/module/posix/__init__.py	(original)
+++ pypy/dist/pypy/module/posix/__init__.py	Tue Mar 20 20:24:06 2007
@@ -70,6 +70,7 @@
         interpleveldefs['readlink'] = 'interp_posix.readlink'
     if hasattr(os, 'fork'):
         interpleveldefs['fork'] = 'interp_posix.fork'
+        appleveldefs['popen'] = 'app_posix.popen'
     if hasattr(os, 'waitpid'):
         interpleveldefs['waitpid'] = 'interp_posix.waitpid'
     if hasattr(os, 'execv'):

Modified: pypy/dist/pypy/module/posix/app_posix.py
==============================================================================
--- pypy/dist/pypy/module/posix/app_posix.py	(original)
+++ pypy/dist/pypy/module/posix/app_posix.py	Tue Mar 20 20:24:06 2007
@@ -1,5 +1,6 @@
 # NOT_RPYTHON
 
+import os
 from _structseq import structseqtype, structseqfield
 
 error = OSError
@@ -28,3 +29,65 @@
 
     return file.fdopen(fd, mode, buffering)
 
+
+# __________ only if we have os.fork() __________
+
+class popenfile(file):
+    _childpid = None
+
+    def close(self):
+        file.close(self)
+        pid = self._childpid
+        if pid is not None:
+            self._childpid = None
+            os.waitpid(pid, 0)
+    __del__ = close     # as in CPython, __del__ may call os.waitpid()
+
+def try_close(fd):
+    try:
+        os.close(fd)
+    except OSError:
+        pass
+
+def popen(command, mode='r', bufsize=-1):
+    """popen(command [, mode='r' [, bufsize]]) -> pipe
+    
+    Open a pipe to/from a command returning a file object."""
+
+    from popen2 import MAXFD
+
+    if not mode.startswith('r') and not mode.startswith('w'):
+        raise ValueError("invalid mode %r" % (mode,))
+    read_end, write_end = os.pipe()
+    try:
+        childpid = os.fork()
+        if childpid == 0:
+            # in the child
+            try:
+                if mode.startswith('r'):
+                    os.dup2(write_end, 1)
+                    os.close(read_end)
+                else:
+                    os.dup2(read_end, 0)
+                    os.close(write_end)
+                for i in range(3, MAXFD):
+                    try_close(i)
+                cmd = ['/bin/sh', '-c', command]
+                os.execvp(cmd[0], cmd)
+            finally:
+                os._exit(1)
+
+        if mode.startswith('r'):
+            os.close(write_end)
+            fd = read_end
+        else:
+            os.close(read_end)
+            fd = write_end
+        g = popenfile.fdopen(fd, mode, bufsize)
+        g._childpid = childpid
+        return g
+
+    except Exception, e:
+        try_close(write_end)
+        try_close(read_end)
+        raise Exception, e     # bare 'raise' does not work here :-(

Modified: pypy/dist/pypy/module/posix/test/test_posix_libfile.py
==============================================================================
--- pypy/dist/pypy/module/posix/test/test_posix_libfile.py	(original)
+++ pypy/dist/pypy/module/posix/test/test_posix_libfile.py	Tue Mar 20 20:24:06 2007
@@ -23,3 +23,30 @@
         f = posix.fdopen(fd, "r")
         result = f.read()
         assert result == "this is a test"
+
+    def test_popen(self):
+        import sys
+        if sys.platform.startswith('win'):
+            skip("unix specific")
+        path2 = self.path + '2'
+        posix = self.posix
+
+        f = posix.popen("echo hello")
+        data = f.read()
+        f.close()
+        assert data == 'hello\n'
+
+        f = posix.popen("cat > '%s'" % (path2,), 'w')
+        f.write('123\n')
+        f.close()
+        f = open(path2, 'r')
+        data = f.read()
+        f.close()
+        assert data == '123\n'
+
+        import time
+        start_time = time.time()
+        f = posix.popen("sleep 2")
+        f.close()   # should wait here
+        end_time = time.time()
+        assert end_time - start_time >= 1.9



More information about the Pypy-commit mailing list