Patrick Mézard  committed e2090fa

editor: use SimpleStringIO in apply_text()

The design is a little ugly as the data stored in _openfiles will be a
string or a SimpleStringIO depending on the file having been edited or
not but this is a simple way to avoid allocating large blocks of data.
This is also a bet the output stream passed to apply_text() is only
being written and never seeked or read.

  • Participants
  • Parent commits e1cb987
  • Branches default

Comments (0)

Files changed (3)

File hgsubversion/

 import errno
-import cStringIO
 import sys
 import tempfile
 import shutil
 import util
 import svnexternals
-class NeverClosingStringIO(object):
-    def __init__(self):
-        self._fp = cStringIO.StringIO()
-    def __getattr__(self, name):
-        return getattr(self._fp, name)
-    def close(self):
-        # svn 1.7 apply_delta driver now calls close() on passed file
-        # object which prevent us from calling getvalue() afterwards.
-        pass
 class EditingError(Exception):
         # A mapping of svn paths to CopiedFile entries
         self._svncopies = {}
         # A mapping of batons to (path, data, isexec, islink, copypath) tuples
+        # data is a SimpleStringIO if the file was edited, a string
+        # otherwise.
         self._openfiles = {}
         # A mapping of file paths to batons
         self._openpaths = {}
                     % file_baton)
         path, data, isexec, islink, copypath = self._openfiles.pop(file_baton)
         del self._openpaths[path]
+        if not isinstance(data, basestring):
+            # Files can be opened, properties changed and apply_text
+            # never called, in which case data is still a string.
+            data = data.getvalue()
         self.current.set(path, data, isexec, islink, copypath)
         if file_baton not in self._openfiles:
             raise EditingError('trying to patch a closed file %s' % file_baton)
         path, base, isexec, islink, copypath = self._openfiles[file_baton]
+        if not isinstance(base, basestring):
+            raise EditingError('trying to edit a file again: %s' % path)
         if not self.meta.is_path_valid(path):
             return lambda x: None
-        target = NeverClosingStringIO()
+        target = svnwrap.SimpleStringIO(closing=False) = target
         handler = svnwrap.apply_txdelta(base, target)
                 # window being None means commit this file
                 if not window:
                     self._openfiles[file_baton] = (
-                        path, target.getvalue(), isexec, islink, copypath)
+                        path, target, isexec, islink, copypath)
             except svnwrap.SubversionException, e: # pragma: no cover
                 if e.args[1] == svnwrap.ERR_INCOMPLETE_DATA:

File hgsubversion/svnwrap/

     write in 16kB blocks (svn 1.7.5) which should be friendly to memory
-    def __init__(self):
+    def __init__(self, closing=True):
         self._blocks = []
+        self._closing = closing
     def write(self, s):
         return ''.join(self._blocks)
     def close(self):
-        del self._blocks
+        if self._closing:
+            del self._blocks

File hgsubversion/

                 raise NotImplementedError()
             def apply_textdelta(self, file_baton, base_checksum, pool=None):
-                stream = editor.NeverClosingStringIO()
+                stream = svnwrap.SimpleStringIO(closing=False)
                 handler = svnwrap.apply_txdelta('', stream)
                 if not callable(handler):
                     raise hgutil.Abort('Error in Subversion bindings: '