[pypy-svn] r38737 - in pypy/dist/pypy: interpreter module/_file module/_file/test module/sys rlib

arigo at codespeak.net arigo at codespeak.net
Tue Feb 13 18:55:49 CET 2007


Author: arigo
Date: Tue Feb 13 18:55:44 2007
New Revision: 38737

Modified:
   pypy/dist/pypy/interpreter/baseobjspace.py
   pypy/dist/pypy/interpreter/mixedmodule.py
   pypy/dist/pypy/module/_file/app_file.py
   pypy/dist/pypy/module/_file/test/test_file.py
   pypy/dist/pypy/module/_file/test/test_file_extra.py
   pypy/dist/pypy/module/sys/__init__.py
   pypy/dist/pypy/module/sys/app.py
   pypy/dist/pypy/rlib/streamio.py
Log:
Add flush-on-exit capability to all our file objects.

Done with a new internal dict sys.pypy__exithandlers__.  In
space.finish(), we pop key/value pairs from this dictionary and just
call the value, as long as the dict is not empty.  This is probably
generally useful for situations where a number of small things must be
"finished" in any order, but finishing them might possibly create more
stuff that must itself be "finished" too.  The fact that it's a
dictionary means that it's cheap to add and remove many things from it.



Modified: pypy/dist/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/dist/pypy/interpreter/baseobjspace.py	(original)
+++ pypy/dist/pypy/interpreter/baseobjspace.py	Tue Feb 13 18:55:44 2007
@@ -201,6 +201,12 @@
         w_exitfunc = self.sys.getdictvalue_w(self, 'exitfunc')
         if w_exitfunc is not None:
             self.call_function(w_exitfunc)
+        w_exithandlers = self.sys.getdictvalue_w(self, 'pypy__exithandlers__')
+        if w_exithandlers is not None:
+            while self.is_true(w_exithandlers):
+                w_key_value = self.call_method(w_exithandlers, 'popitem')
+                w_key, w_value = self.unpacktuple(w_key_value, 2)
+                self.call_function(w_value)
         if self.config.objspace.std.withdictmeasurement:
             from pypy.objspace.std.dictmultiobject import report
             report()

Modified: pypy/dist/pypy/interpreter/mixedmodule.py
==============================================================================
--- pypy/dist/pypy/interpreter/mixedmodule.py	(original)
+++ pypy/dist/pypy/interpreter/mixedmodule.py	Tue Feb 13 18:55:44 2007
@@ -216,10 +216,10 @@
     else:
         appname = name
     mod = Module(space, space.wrap(appname))
-    moddict = space.unwrap(mod.getdict())
     res = new.module(appname)
-    res.__dict__.update(moddict)
     sys.modules[appname] = res
+    moddict = space.unwrap(mod.getdict())
+    res.__dict__.update(moddict)
     return res
 
 def compilemodule(name, interactive=False):

Modified: pypy/dist/pypy/module/_file/app_file.py
==============================================================================
--- pypy/dist/pypy/module/_file/app_file.py	(original)
+++ pypy/dist/pypy/module/_file/app_file.py	Tue Feb 13 18:55:44 2007
@@ -1,5 +1,9 @@
 """NOT_RPYTHON"""
 
+import sys
+import _file
+
+
 class file(object):
     """file(name[, mode[, buffering]]) -> file object
 
@@ -22,32 +26,29 @@
     _closed = True   # Until the file is successfully opened
 
     def __init__(self, name, mode='r', buffering=-1):
-        import _file
-        self._name = name
-        self.softspace = 0    # Required according to file object docs
-        self.encoding = None  # This is not used internally by file objects
-        self._closed = False
-        self.stream = _file.open_file_as_stream(self._name, mode, buffering)
-        self._mode = mode
-        self.fd = self.stream.try_to_find_file_descriptor()
-        assert self.fd != -1
-        
+        stream = _file.open_file_as_stream(name, mode, buffering)
+        fd = stream.try_to_find_file_descriptor()
+        assert fd != -1
+        self._fdopenstream(fd, mode, buffering, name, stream)
+
     def fdopen(cls, fd, mode='r', buffering=-1):
         f = cls.__new__(cls)
-        f._fdopen(fd, mode, buffering, '<fdopen>')
+        stream = _file.fdopen_as_stream(fd, mode, buffering)
+        f._fdopenstream(fd, mode, buffering, '<fdopen>', stream)
         return f
     fdopen = classmethod(fdopen)
 
-    def _fdopen(self, fd, mode, buffering, name):
-        import _file
+    def _fdopenstream(self, fd, mode, buffering, name, stream):
         self.fd = fd
         self._name = name
         self.softspace = 0    # Required according to file object docs
         self.encoding = None  # This is not used internally by file objects
         self._closed = False
-        self.stream = _file.fdopen_as_stream(fd, mode, buffering)
+        self.stream = stream
         self._mode = mode
-        
+        if stream.flushable():
+            sys.pypy__exithandlers__[stream] = stream.flush
+
     def getnewlines(self):
         "end-of-line convention used in this file"
 
@@ -227,6 +228,7 @@
 may return an exit status upon closing."""
         if not self._closed and hasattr(self, 'stream'):
             self._closed = True
+            sys.pypy__exithandlers__.pop(self.stream, None)
             self.stream.close()
 
     __del__ = close

Modified: pypy/dist/pypy/module/_file/test/test_file.py
==============================================================================
--- pypy/dist/pypy/module/_file/test/test_file.py	(original)
+++ pypy/dist/pypy/module/_file/test/test_file.py	Tue Feb 13 18:55:44 2007
@@ -116,3 +116,21 @@
         assert res == "hello\n"
         assert type(res) is str
         f.close()
+
+
+def test_flush_at_exit():
+    from pypy import conftest
+    from pypy.tool.option import make_config, make_objspace
+    from pypy.tool.udir import udir
+
+    tmpfile = udir.join('test_flush_at_exit')
+    config = make_config(conftest.option)
+    space = make_objspace(config)
+    space.appexec([space.wrap(str(tmpfile))], """(tmpfile):
+        f = open(tmpfile, 'w')
+        f.write('42')
+        # no flush() and no close()
+        import sys; sys._keepalivesomewhereobscure = f
+    """)
+    space.finish()
+    assert tmpfile.read() == '42'

Modified: pypy/dist/pypy/module/_file/test/test_file_extra.py
==============================================================================
--- pypy/dist/pypy/module/_file/test/test_file_extra.py	(original)
+++ pypy/dist/pypy/module/_file/test/test_file_extra.py	Tue Feb 13 18:55:44 2007
@@ -1,4 +1,4 @@
-import os, random
+import os, random, sys
 from pypy.tool.udir import udir
 import py
 from pypy.interpreter.mixedmodule import testmodule
@@ -14,6 +14,9 @@
 def setup_module(mod):
     mod._file = testmodule("_file")
     udir.join('sample').write(SAMPLE)
+    # workaround for testing _file on top of CPython
+    if not hasattr(sys, 'pypy_objspaceclass'):
+        sys.pypy__exithandlers__ = {}
 
 
 class BaseROTests:

Modified: pypy/dist/pypy/module/sys/__init__.py
==============================================================================
--- pypy/dist/pypy/module/sys/__init__.py	(original)
+++ pypy/dist/pypy/module/sys/__init__.py	Tue Feb 13 18:55:44 2007
@@ -72,6 +72,7 @@
         '__excepthook__'        : 'app.excepthook', 
         'exit'                  : 'app.exit', 
         'exitfunc'              : 'app.exitfunc',
+        'pypy__exithandlers__'  : 'app.pypy__exithandlers__',  # internal
         'getfilesystemencoding' : 'app.getfilesystemencoding', 
         'callstats'             : 'app.callstats',
         'getdefaultencoding'    : 'app.getdefaultencoding', 

Modified: pypy/dist/pypy/module/sys/app.py
==============================================================================
--- pypy/dist/pypy/module/sys/app.py	(original)
+++ pypy/dist/pypy/module/sys/app.py	Tue Feb 13 18:55:44 2007
@@ -26,6 +26,8 @@
 def exitfunc():
     """Placeholder for sys.exitfunc(), which is called when PyPy exits."""
 
+pypy__exithandlers__ = {}
+
 #import __builtin__
 
 def getfilesystemencoding():

Modified: pypy/dist/pypy/rlib/streamio.py
==============================================================================
--- pypy/dist/pypy/rlib/streamio.py	(original)
+++ pypy/dist/pypy/rlib/streamio.py	Tue Feb 13 18:55:44 2007
@@ -4,7 +4,8 @@
 
 - This module contains various stream classes which provide a subset of the
   classic Python I/O API: read(n), write(s), tell(), seek(offset, whence=0),
-  readall(), readline(), truncate(size), flush(), close(), peek().
+  readall(), readline(), truncate(size), flush(), close(), peek(),
+  flushable(), try_to_find_file_descriptor().
 
 - This is not for general usage:
   * read(n) may return less than n bytes, just like os.read().
@@ -13,6 +14,7 @@
     there is no __del__() closing the stream for you.
   * some methods may raise NotImplementedError.
   * peek() returns some (or no) characters that have already been read ahead.
+  * flushable() returns True/False if flushing that stream is useful/pointless.
 
 - A 'basis stream' provides I/O using a low-level API, like the os, mmap or
   socket modules.
@@ -197,6 +199,9 @@
     def flush(self):
         pass
 
+    def flushable(self):
+        return False
+
     def close(self):
         pass
 
@@ -338,6 +343,10 @@
     def flush(self):
         self.mm.flush()
 
+    def flushable(self):
+        import mmap
+        return self.access == mmap.ACCESS_WRITE
+
     def try_to_find_file_descriptor(self):
         return self.fd
 
@@ -352,6 +361,7 @@
     ("readline", []),
     ("truncate", [int]),
     ("flush", []),
+    ("flushable", []),
     ("close", []),
     ("peek", []),
     ("try_to_find_file_descriptor", []),
@@ -613,10 +623,10 @@
     write      = PassThrough("write",     flush_buffers=True)
     truncate   = PassThrough("truncate",  flush_buffers=True)
     flush      = PassThrough("flush",     flush_buffers=True)
+    flushable  = PassThrough("flushable", flush_buffers=False)
     close      = PassThrough("close",     flush_buffers=False)
-
-    def try_to_find_file_descriptor(self):
-        return self.base.try_to_find_file_descriptor()
+    try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
+                                              flush_buffers=False)
 
 
 class BufferingOutputStream(Stream):
@@ -669,9 +679,11 @@
     truncate   = PassThrough("truncate", flush_buffers=True)
     flush      = PassThrough("flush",    flush_buffers=True)
     close      = PassThrough("close",    flush_buffers=True)
+    try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
+                                              flush_buffers=False)
 
-    def try_to_find_file_descriptor(self):
-        return self.base.try_to_find_file_descriptor()
+    def flushable(self):
+        return True
 
 
 class LineBufferingOutputStream(BufferingOutputStream):
@@ -688,9 +700,6 @@
             self.do_write(self.buf[:p])
             self.buf = self.buf[p:]
 
-    def try_to_find_file_descriptor(self):
-        return self.base.try_to_find_file_descriptor()
-
 
 # ____________________________________________________________
 
@@ -720,10 +729,10 @@
         return data
 
     flush    = PassThrough("flush", flush_buffers=False)
+    flushable= PassThrough("flushable", flush_buffers=False)
     close    = PassThrough("close", flush_buffers=False)
-
-    def try_to_find_file_descriptor(self):
-        return self.base.try_to_find_file_descriptor()
+    try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
+                                              flush_buffers=False)
 
 class TextInputFilter(Stream):
 
@@ -853,10 +862,10 @@
     write      = PassThrough("write",     flush_buffers=True)
     truncate   = PassThrough("truncate",  flush_buffers=True)
     flush      = PassThrough("flush",     flush_buffers=True)
+    flushable  = PassThrough("flushable", flush_buffers=False)
     close      = PassThrough("close",     flush_buffers=False)
-
-    def try_to_find_file_descriptor(self):
-        return self.base.try_to_find_file_descriptor()
+    try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
+                                              flush_buffers=False)
 
 
 class TextOutputFilter(Stream):
@@ -879,10 +888,10 @@
     readline   = PassThrough("readline",  flush_buffers=False)
     truncate   = PassThrough("truncate",  flush_buffers=False)
     flush      = PassThrough("flush",     flush_buffers=False)
+    flushable  = PassThrough("flushable", flush_buffers=False)
     close      = PassThrough("close",     flush_buffers=False)
-
-    def try_to_find_file_descriptor(self):
-        return self.base.try_to_find_file_descriptor()
+    try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
+                                              flush_buffers=False)
 
 
 # _________________________________________________
@@ -930,10 +939,10 @@
     write      = PassThrough("write",     flush_buffers=False)
     truncate   = PassThrough("truncate",  flush_buffers=False)
     flush      = PassThrough("flush",     flush_buffers=False)
+    flushable  = PassThrough("flushable", flush_buffers=False)
     close      = PassThrough("close",     flush_buffers=False)
-
-    def try_to_find_file_descriptor(self):
-        return self.base.try_to_find_file_descriptor()
+    try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
+                                              flush_buffers=False)
 
 class EncodingOutputFilter(Stream):
 
@@ -958,8 +967,7 @@
     readline   = PassThrough("readline",  flush_buffers=False)
     truncate   = PassThrough("truncate",  flush_buffers=False)
     flush      = PassThrough("flush",     flush_buffers=False)
+    flushable  = PassThrough("flushable", flush_buffers=False)
     close      = PassThrough("close",     flush_buffers=False)
-
-    def try_to_find_file_descriptor(self):
-        return self.base.try_to_find_file_descriptor()
-
+    try_to_find_file_descriptor = PassThrough("try_to_find_file_descriptor",
+                                              flush_buffers=False)



More information about the Pypy-commit mailing list