Robert Collins | 7 Oct 09:47

[MERGE] Fix two bugs in api versioning support - 279447 and 279451

This tries to make the api support in bzr more suitable for bzr-svn.

-Rob
-- 
GPG key available at: <http://www.robertcollins.net/keys.txt>.
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: robertc <at> robertcollins.net-20081007064146-\
#   d1w8jhf0zqn1mb3y
# target_branch: http://bazaar-vcs.org/bzr/bzr.dev

# testament_sha1: 7b86672ecc8bc10a0d1fe37ee0a6814f7fc30fcd
# timestamp: 2008-10-07 18:45:03 +1100
# source_branch: http://people.ubuntu.com/~robertc/baz2.0/api

# base_revision_id: pqm <at> pqm.ubuntu.com-20081006223227-11nq4m186th9ljeq
# 
# Begin patch
=== modified file 'NEWS'
--- NEWS	2008-10-03 00:18:15 +0000
+++ NEWS	2008-10-07 06:41:46 +0000
@@ -79,6 +79,12 @@
 
   BUG FIXES:
 
+    * API versioning support now has a multiple-version checking api
+      ``require_any_api``. (Robert Collins, #279447)
+
+    * A failure to load a plugin due to an IncompatibleAPI exception is
+      now correctly reported. (Robert Collins, #279451)
+
     * Branching from a shared repository on a smart server into a new
       repository now preserves the repository format.
       (Andrew Bennetts, #269214)

=== modified file 'bzrlib/api.py'
--- bzrlib/api.py	2007-06-26 08:52:20 +0000
+++ bzrlib/api.py	2008-10-07 04:51:25 +0000
@@ -80,3 +80,24 @@
     minimum = get_minimum_api_version(object_with_api)
     if wanted_api < minimum or wanted_api > current:
         raise IncompatibleAPI(object_with_api, wanted_api, minimum, current)
+
+def require_any_api(object_with_api, wanted_api_list):
+    """Check if object_with_api supports the api version wanted_api.
+
+    :param object_with_api: An object which exports an API minimum and current
+        version. See get_minimum_api_version and get_current_api_version for
+        details.
+    :param wanted_api: A list of API versions, any of which being available is
+        sufficent.
+    :return None:
+    :raises IncompatibleAPI: When the wanted_api is not supported by
+        object_with_api.
+
+    Added in bzrlib 1.9.
+    """
+    for api in wanted_api_list[:-1]:
+        try:
+            return require_api(object_with_api, api)
+        except IncompatibleAPI:
+            pass
+    require_api(object_with_api, wanted_api_list[-1])

=== modified file 'bzrlib/plugin.py'
--- bzrlib/plugin.py	2008-09-08 12:59:00 +0000
+++ bzrlib/plugin.py	2008-10-07 06:41:46 +0000
@@ -43,6 +43,7 @@
 from bzrlib import (
     config,
     debug,
+    errors,
     osutils,
     trace,
     )
@@ -172,7 +173,10 @@
 
 
 def load_from_dir(d):
-    """Load the plugins in directory d."""
+    """Load the plugins in directory d.
+    
+    d must be in the plugins module path already.
+    """
     # Get the list of valid python suffixes for __init__.py?
     # this includes .py, .pyc, and .pyo (depending on if we are running -O)
     # but it doesn't include compiled modules (.so, .dll, etc)
@@ -210,7 +214,13 @@
             exec "import bzrlib.plugins.%s" % name in {}
         except KeyboardInterrupt:
             raise
+        except errors.IncompatibleAPI, e:
+            trace.warning("Unable to load plugin %r. It requested API version "
+                "%s of module %s but the minimum exported version is %s, and "
+                "the maximum is %s" %
+                (name, e.wanted, e.api, e.minimum, e.current))
         except Exception, e:
+            trace.warning("%s" % e)
             ## import pdb; pdb.set_trace()
             if re.search('\.|-| ', name):
                 sanitised_name = re.sub('[-. ]', '_', name)

=== modified file 'bzrlib/tests/test_api.py'
--- bzrlib/tests/test_api.py	2007-06-26 08:52:20 +0000
+++ bzrlib/tests/test_api.py	2008-10-07 04:51:25 +0000
@@ -71,6 +71,31 @@
         self.assertEqual(bzrlib.version_info[0:3],
             bzrlib.api.get_current_api_version(an_object))
 
+    def test_require_any_api_wanted_one(self):
+        an_object = TrivialObject()
+        an_object.api_minimum_version = (1, 2, 3)
+        an_object.api_current_version = (4, 5, 6)
+        bzrlib.api.require_any_api(an_object, [(1, 2, 3)])
+
+    def test_require_any_api_wanted_first_compatible(self):
+        an_object = TrivialObject()
+        an_object.api_minimum_version = (1, 2, 3)
+        an_object.api_current_version = (4, 5, 6)
+        bzrlib.api.require_any_api(an_object, [(1, 2, 3), (5, 6, 7)])
+
+    def test_require_any_api_wanted_second_compatible(self):
+        an_object = TrivialObject()
+        an_object.api_minimum_version = (1, 2, 3)
+        an_object.api_current_version = (4, 5, 6)
+        bzrlib.api.require_any_api(an_object, [(5, 6, 7), (1, 2, 3)])
+
+    def test_require_any_api_wanted_none_compatible(self):
+        an_object = TrivialObject()
+        an_object.api_minimum_version = (1, 2, 3)
+        an_object.api_current_version = (4, 5, 6)
+        self.assertRaises(IncompatibleAPI, bzrlib.api.require_any_api,
+            an_object, [(1, 2, 2), (5, 6, 7)])
+
     def test_require_api_wanted_is_minimum_is_ok(self):
         an_object = TrivialObject()
         an_object.api_minimum_version = (1, 2, 3)

=== modified file 'bzrlib/tests/test_plugins.py'
--- bzrlib/tests/test_plugins.py	2008-08-11 12:50:16 +0000
+++ bzrlib/tests/test_plugins.py	2008-10-07 06:41:46 +0000
@@ -193,30 +193,60 @@
                 del bzrlib.plugins.ts_plugin
         self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
-    def test_plugin_with_bad_name_does_not_load(self):
-        # Create badly-named plugin
-        file('bzr-bad plugin-name..py', 'w').close()
-
+    def load_and_capture(self, name):
+        """Load plugins from '.' capturing the output.
+        
+        :param name: The name of the plugin.
+        :return: A string with the log from the plugin loading call.
+        """
         # Capture output
         stream = StringIO()
-        handler = logging.StreamHandler(stream)
-        log = logging.getLogger('bzr')
-        log.addHandler(handler)
-
-        bzrlib.plugin.load_from_dir('.')
-
-        # Stop capturing output
-        handler.flush()
-        handler.close()
-        log.removeHandler(handler)
-
-        self.assertContainsRe(stream.getvalue(),
+        try:
+            handler = logging.StreamHandler(stream)
+            log = logging.getLogger('bzr')
+            log.addHandler(handler)
+            try:
+                try:
+                    bzrlib.plugin.load_from_path(['.'])
+                finally:
+                    if 'bzrlib.plugins.%s' % name in sys.modules:
+                        del sys.modules['bzrlib.plugins.%s' % name]
+                    if getattr(bzrlib.plugins, name, None):
+                        delattr(bzrlib.plugins, name)
+            finally:
+                # Stop capturing output
+                handler.flush()
+                handler.close()
+                log.removeHandler(handler)
+            return stream.getvalue()
+        finally:
+            stream.close()
+    
+    def test_plugin_with_bad_api_version_reports(self):
+        # This plugin asks for bzrlib api version 1.0.0, which is not supported
+        # anymore.
+        name = 'wants100.py'
+        f = file(name, 'w')
+        try:
+            f.write("import bzrlib.api\n"
+                "bzrlib.api.require_any_api(bzrlib, [(1, 0, 0)])\n")
+        finally:
+            f.close()
+
+        log = self.load_and_capture(name)
+        self.assertContainsRe(log,
+            r"It requested API version")
+
+    def test_plugin_with_bad_name_does_not_load(self):
+        # The file name here invalid for a python module.
+        name = 'bzr-bad plugin-name..py'
+        file(name, 'w').close()
+        log = self.load_and_capture(name)
+        self.assertContainsRe(log,
             r"Unable to load 'bzr-bad plugin-name\.' in '\.' as a plugin "
             "because the file path isn't a valid module name; try renaming "
             "it to 'bad_plugin_name_'\.")
 
-        stream.close()
-
 
 class TestPlugins(TestCaseInTempDir):
 

# Begin bundle
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
gLgWhIJ4/uLuSKcKEgItAoSA

Gmane