[PATCH 5 of 6] bundle2: add some distinction between mandatory and advisory part

pierre-yves.david at ens-lyon.org pierre-yves.david at ens-lyon.org
Tue Apr 1 14:35:26 CDT 2014


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at fb.com>
# Date 1395691322 25200
#      Mon Mar 24 13:02:02 2014 -0700
# Node ID 8f0da5aa2129a78b4e7891e29ec583d9296ac10c
# Parent  8b74b0f5ff191eb9796706e089b6c14a30c05d40
bundle2: add some distinction between mandatory and advisory part

Mandatory part cannot be ignored when unknown. We raise a simple KeyError
exception when this happen.

This is very early version of this logic, see inline comment for future
improvement lead.

diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -117,10 +117,23 @@ Binary format is as follow
     `chunksize` is a 32 bits integer, `chunkdata` are plain bytes (as much as
     `chunksize` says)` The payload part is concluded by a zero size chunk.
 
     The current implementation always produces either zero or one chunk.
     This is an implementation limitation that will ultimatly be lifted.
+
+Bundle processing
+============================
+
+Each part is processed in order using a "part handler". Handler are registered
+for a certain part type.
+
+The matching of a part to its handler is case insensitive. The case of the
+part type is used to know if a part is mandatory or advisory. If the Part type
+contains any uppercase char it is considered mandatory. When no handler is
+known for a Mandatory part, the process is aborted and an exception is raised.
+If the part is advisory and no handler is known, the part is ignored.
+
 """
 
 import util
 import struct
 import urllib
@@ -159,12 +172,13 @@ def parthandler(parttype):
         def myparttypehandler(...):
             '''process a part of type "my part".'''
             ...
     """
     def _decorator(func):
-        assert parttype not in parthandlermapping
-        parthandlermapping[parttype] = func
+        lparttype = parttype.lower() # enforce lower case matching.
+        assert lparttype not in parthandlermapping
+        parthandlermapping[lparttype] = func
         return func
     return _decorator
 
 def processbundle(repo, stream):
     """This function process a bundle, apply effect to/from a repo
@@ -177,12 +191,11 @@ def processbundle(repo, stream):
     Parts are processes in order.
 
     This is very early version of this function that will be strongly reworked
     before final usage.
 
-    All unknown parts are currently ignored (Mandatory parts logic will comes
-    later).
+    Unknown Mandatory part will abort the process.
     """
     ui = repo.ui
     # Extraction of the unbundler object will most likely change. It may be
     # done outside of this function, the unbundler would be passed as argument.
     # in all case the unbundler will eventually be created by a
@@ -198,10 +211,15 @@ def processbundle(repo, stream):
         key = parttype.lower()
         try:
             handler = parthandlermapping[key]
             ui.debug('found an handler for part %r\n' % parttype)
         except KeyError:
+            if key != parttype: # mandatory parts
+                # todo:
+                # - use a more precise exception
+                # - consume the bundle2 stream anyway.
+                raise
             ui.debug('ignoring unknown advisory part %r\n' % key)
             # todo: consume the part (once we use streamed parts)
             continue
         handler(repo, part)
 
diff --git a/tests/test-bundle2.t b/tests/test-bundle2.t
--- a/tests/test-bundle2.t
+++ b/tests/test-bundle2.t
@@ -27,10 +27,11 @@ Create an extension to test bundle2 API
   >     for line in part.data.split('\n'):
   >         repo.ui.write('    %s\n' % line)
   > 
   > @command('bundle2',
   >          [('', 'param', [], 'stream level parameter'),
+  >           ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
   >           ('', '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)
@@ -52,10 +53,14 @@ Create an extension to test bundle2 API
   >        part = bundle2.part('test:math',
   >                            [('pi', '3.14'), ('e', '2.72')],
   >                            [('cooking', 'raw')],
   >                            '42')
   >        bundler.addpart(part)
+  >     if opts['unknown']:
+  >        part = bundle2.part('test:UNKNOWN',
+  >                            data='some random content')
+  >        bundler.addpart(part)
   > 
   >     if path is None:
   >        file = sys.stdout
   >     else:
   >         file = open(path, 'w')
@@ -64,11 +69,14 @@ Create an extension to test bundle2 API
   >         file.write(chunk)
   > 
   > @command('unbundle2', [], '')
   > def cmdunbundle2(ui, repo):
   >     """process a bundle2 stream from stdin on the current repo"""
-  >     bundle2.processbundle(repo, sys.stdin)
+  >     try:
+  >         bundle2.processbundle(repo, sys.stdin)
+  >     except KeyError, exc:
+  >         raise util.Abort('missing support for %s' % exc)
   > 
   > @command('statbundle2', [], '')
   > def cmdstatbundle2(ui, repo):
   >     """print statistic on the bundle2 container read from stdin"""
   >     unbundler = bundle2.unbundle20(ui, sys.stdin)
@@ -375,5 +383,16 @@ Process the bundle
   payload chunk size: 2
   payload chunk size: 0
   ignoring unknown advisory part 'test:math'
   part header size: 0
   end of bundle2 stream
+
+
+  $ hg bundle2 --parts --unknown ../unknown.hg2
+
+  $ hg unbundle2 < ../unknown.hg2
+  The choir start singing:
+      Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
+      Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
+      Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
+  abort: missing support for 'test:unknown'
+  [255]


More information about the Mercurial-devel mailing list