Patchwork [2,of,5,V2] bundle2: a very first version of bundle2 unbundler

login
register
mail settings
Submitter Pierre-Yves David
Date March 20, 2014, 6:39 p.m.
Message ID <82f930ee78d4cdb25789.1395340783@marginatus.alto.octopoid.net>
Download mbox | patch
Permalink /patch/4006/
State Accepted
Commit 520df53ad26a9105f5d4a7647243567bb7a0e697
Headers show

Comments

Pierre-Yves David - March 20, 2014, 6:39 p.m.
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@fb.com>
# Date 1395178122 25200
#      Tue Mar 18 14:28:42 2014 -0700
# Node ID 82f930ee78d4cdb25789e0b78a61b3dec99990d3
# Parent  14623bafac62835e44ee4dee806978e1fd50540b
bundle2: a very first version of bundle2 unbundler

This changeset introduce an unbundler class to match the bundle2 bundler. It is
currently able to unbundle an empty bundle2 only and will gain more feature at
the same pace than the bundler.

It also comes with its special extension command in test.

Patch

diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -58,10 +58,14 @@  Binary format is as follow
   (size = 0) this is interpreted as the end of stream marker.
 
   Currently forced to 0 in the current state of the implementation
 """
 
+import util
+import changegroup
+
+
 _magicstring = 'HG20'
 
 class bundler(object):
     """represent an outgoing bundle2 container
 
@@ -80,5 +84,50 @@  class bundler(object):
         yield '\0\0'
         # no support for parts
         # to be obviously fixed soon.
         assert not self._parts
         yield '\0\0'
+
+class unbundler(object):
+    """interpret a bundle2 stream
+
+    (this will eventually yield parts)"""
+
+    def __init__(self, fp):
+        # assume the magic string is ok and drop it
+        # to be obviously fixed soon.
+        self._fp = fp
+        self._readexact(4)
+
+    def _unpack(self, format):
+        """unpack this struct format from the stream"""
+        data = self._readexact(struct.calcsize(format))
+        return _unpack(format, data)
+
+    def _readexact(self, size):
+        """read exactly <size> bytes from the stream"""
+        return changegroup.readexactly(self._fp, size)
+
+    @util.propertycache
+    def params(self):
+        """dictionnary of stream level parameters"""
+        paramsize = self._readexact(2)
+        assert paramsize == '\0\0'
+        return {}
+
+    def __iter__(self):
+        """yield all parts contained in the stream"""
+        # make sure param have been loaded
+        self.params
+        part = self._readpart()
+        while part is not None:
+            yield part
+            part = self._readpart()
+
+    def _readpart(self):
+        """return None when an end of stream markers is reach"""
+        headersize = self._readexact(2)
+        assert headersize == '\0\0'
+        return None
+
+
+
diff --git a/tests/test-bundle2.t b/tests/test-bundle2.t
--- a/tests/test-bundle2.t
+++ b/tests/test-bundle2.t
@@ -6,10 +6,11 @@  Create an extension to test bundle2 API
   > 
   > Current bundle2 implementation is far too limited to be used in any core
   > code. We still need to be able to test it while it grow up.
   > """
   > 
+  > import sys
   > from mercurial import cmdutil
   > from mercurial import bundle2
   > cmdtable = {}
   > command = cmdutil.command(cmdtable)
   > 
@@ -17,10 +18,18 @@  Create an extension to test bundle2 API
   > def cmdbundle2(ui, repo):
   >     """write a bundle2 container on standard ouput"""
   >     bundler = bundle2.bundler()
   >     for chunk in bundler.getchunks():
   >         ui.write(chunk)
+  > 
+  > @command('unbundle2', [], '')
+  > def cmdunbundle2(ui, repo):
+  >     """read a bundle2 container from standard input"""
+  >     unbundler = bundle2.unbundler(sys.stdin)
+  >     ui.write('options count: %i\n' % len(unbundler.params))
+  >     parts = list(unbundler)
+  >     ui.write('parts count:   %i\n' % len(parts))
   > EOF
   $ cat >> $HGRCPATH << EOF
   > [extensions]
   > bundle2=$TESTTMP/bundle2.py
   > EOF
@@ -32,5 +41,11 @@  The extension requires a repo (currently
 
 Test simple generation of empty bundle
 
   $ hg bundle2
   HG20\x00\x00\x00\x00 (no-eol) (esc)
+
+Test parsing of an empty bundle
+
+  $ hg bundle2 | hg unbundle2
+  options count: 0
+  parts count:   0