From patchwork Thu Mar 27 02:19:42 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [1,of,4] bundle2: support bundling of empty part (with a type) From: Pierre-Yves David X-Patchwork-Id: 4076 Message-Id: <447f461fcdc476a60412.1395886782@marginatus.alto.octopoid.net> To: mercurial-devel@selenic.com Date: Wed, 26 Mar 2014 19:19:42 -0700 # HG changeset patch # User Pierre-Yves David # Date 1395178173 25200 # Tue Mar 18 14:29:33 2014 -0700 # Node ID 447f461fcdc476a60412f7e91b29e38d5b46de22 # Parent 5c6341690ef3465393bc77b59aecffc201ebd9b7 bundle2: support bundling of empty part (with a type) Here start the work on bundle2 parts. Our first step is to be able to bundle a simplistic part that just have a type, no parameters, empty payload. diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -19,12 +19,10 @@ The format is architectured as follow - magic string - stream level parameters - payload parts (any number) - end of stream marker. -The current implementation accept some stream level option but no part. - Details on the Binary format ============================ All numbers are unsigned and big endian. @@ -69,11 +67,31 @@ Binary format is as follow :header size: (16 bits inter) The total number of Bytes used by the part headers. When the header is empty (size = 0) this is interpreted as the end of stream marker. - Currently forced to 0 in the current state of the implementation +:header: + + The header contains data about how to interpret the part. it contains two + piece of data: the part type, and the part parameters. + + The part type will be used to route the part to the proper application + level object that will interpret the part paylod. + + The part option will be handed as argument this application level object. + They are meant to convey information that will help the application level + object to interpret the part payload. + + The binary format of the header is has follow + + :typesize: (one byte) + :typename: alphanumerical part name + :option: we do not support option yet this denoted by two 16 bites zero. + +:payload: + + The payload is currently alway empty this is denoted by a 32bits zero. """ import util import struct import urllib @@ -86,19 +104,19 @@ from i18n import _ _unpack = struct.unpack _magicstring = 'HG20' _fstreamparamsize = '>H' +_fpartheadersize = '>H' +_fparttypesize = '>B' class bundle20(object): """represent an outgoing bundle2 container - Use the `addparam` method to add stream level parameter. Then call - `getchunks` to retrieve all the binary chunks of datathat compose the - bundle2 container. - - This object does not support payload part yet.""" + Use the `addparam` method to add stream level parameter. and `addpart` to + populate it. Then call `getchunks` to retrieve all the binary chunks of + datathat compose the bundle2 container.""" def __init__(self, ui): self.ui = ui self._params = [] self._parts = [] @@ -109,22 +127,30 @@ class bundle20(object): raise ValueError('empty parameter name') if name[0] not in string.letters: raise ValueError('non letter first character: %r' % name) self._params.append((name, value)) + def addpart(self, part): + """add a new part to the bundle2 container + + Parts contains the actuall applicative payload.""" + self._parts.append(part) + def getchunks(self): self.ui.debug('start emission of %s stream\n' % _magicstring) yield _magicstring param = self._paramchunk() self.ui.debug('bundle parameter: %s\n' % param) yield _pack(_fstreamparamsize, len(param)) if param: yield param - # no support for parts - # to be obviously fixed soon. - assert not self._parts + self.ui.debug('start of parts\n') + for part in self._parts: + self.ui.debug('bundling part: "%s"\n' % part.type) + for chunk in part.getchunks(): + yield chunk self.ui.debug('end of bundle\n') yield '\0\0' def _paramchunk(self): """return a encoded version of all stream parameters""" @@ -215,7 +241,28 @@ class unbundle20(object): """return None when an end of stream markers is reach""" headersize = self._readexact(2) assert headersize == '\0\0' return None +class part(object): + """A bundle2 part contains actual application level payload + The part have `type` used to route it to proper application level object + that interpret its content. + """ + def __init__(self, parttype): + self.type = parttype + + def getchunks(self): + ### header + header = [_pack(_fparttypesize, len(self.type)), + self.type, + '\0\0', # No option support for now. + ] + headerchunk = ''.join(header) + yield _pack(_fpartheadersize, len(headerchunk)) + yield headerchunk + # force empty part for now + yield '\0\0\0\0' + + diff --git a/tests/test-bundle2.t b/tests/test-bundle2.t --- a/tests/test-bundle2.t +++ b/tests/test-bundle2.t @@ -14,11 +14,12 @@ Create an extension to test bundle2 API > from mercurial import bundle2 > cmdtable = {} > command = cmdutil.command(cmdtable) > > @command('bundle2', - > [('', 'param', [], 'stream level parameter'),], + > [('', 'param', [], 'stream level parameter'), + > ('', 'parts', False, 'include some arbitrary parts to the bundle'),], > '[OUTPUTFILE]') > def cmdbundle2(ui, repo, path=None, **opts): > """write a bundle2 container on standard ouput""" > bundler = bundle2.bundle20(ui) > for p in opts['param']: @@ -26,10 +27,17 @@ Create an extension to test bundle2 API > try: > bundler.addparam(*p) > except ValueError, exc: > raise util.Abort('%s' % exc) > + > if opts['parts']: + > part = bundle2.part('test:empty') + > bundler.addpart(part) + > # add a second one to make sure we handle multiple parts + > part = bundle2.part('test:empty') + > bundler.addpart(part) + > > if path is None: > file = sys.stdout > else: > file = open(path, 'w') > @@ -176,10 +184,11 @@ Test debug output bundling debug $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2 start emission of HG20 stream bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple + start of parts end of bundle file content is ok $ cat ../out.hg2 @@ -213,5 +222,24 @@ empty parameter name bad parameter name $ hg bundle2 --param 42babar abort: non letter first character: '42babar' [255] + + +Test part +================= + + $ hg bundle2 --parts ../parts.hg2 --debug + start emission of HG20 stream + bundle parameter: + start of parts + bundling part: "test:empty" + bundling part: "test:empty" + end of bundle + + $ cat ../parts.hg2 + HG20\x00\x00\x00\r (esc) + test:empty\x00\x00\x00\x00\x00\x00\x00\r (esc) + test:empty\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc) + +