Rev 2946: Unlock while in a write group now aborts the write group, unlocks, and errors. Also includes the knit extraction one-liner tweak. in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Fri Oct 26 09:56:14 BST 2007


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 2946
revision-id: pqm at pqm.ubuntu.com-20071026085609-c3r8skfrmjj21j0m
parent: pqm at pqm.ubuntu.com-20071026074806-v0hw6v1dbm6hu9oj
parent: andrew.bennetts at canonical.com-20071026081256-7py6j5tfe37w2eq2
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Fri 2007-10-26 09:56:09 +0100
message:
  Unlock while in a write group now aborts the write group, unlocks, and errors.  Also includes the knit extraction one-liner tweak.
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/knit.py                 knit.py-20051212171256-f056ac8f0fbe1bd9
  bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
  bzrlib/repofmt/pack_repo.py    pack_repo.py-20070813041115-gjv5ma7ktfqwsjgn-1
  bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/repository_implementations/test_write_group.py test_write_group.py-20070716105516-89n34xtogq5frn0m-1
  bzrlib/tests/test_selftest.py  test_selftest.py-20051202044319-c110a115d8c0456a
  doc/developers/HACKING.txt     HACKING-20050805200004-2a5dc975d870f78c
    ------------------------------------------------------------
    revno: 2592.1.34.1.247
    merged: andrew.bennetts at canonical.com-20071026081256-7py6j5tfe37w2eq2
    parent: andrew.bennetts at canonical.com-20071026072544-1c9p65ogzvtb61ay
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: mbp-writegroups
    timestamp: Fri 2007-10-26 18:12:56 +1000
    message:
      Fix test_callCatchWarnings to pass when run with Python 2.4.
    ------------------------------------------------------------
    revno: 2592.1.34.1.246
    merged: andrew.bennetts at canonical.com-20071026072544-1c9p65ogzvtb61ay
    parent: andrew.bennetts at canonical.com-20071026063453-4eyjd2b5o391db20
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: mbp-writegroups
    timestamp: Fri 2007-10-26 17:25:44 +1000
    message:
      Override warnings.filters in callCatchWarnings, to insulate it from -Werror.
    ------------------------------------------------------------
    revno: 2592.1.34.1.245
    merged: andrew.bennetts at canonical.com-20071026063453-4eyjd2b5o391db20
    parent: mbp at sourcefrog.net-20071025012451-pvqf4umf0efueoqr
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: mbp-writegroups
    timestamp: Fri 2007-10-26 16:34:53 +1000
    message:
      Commit Martin's faster knit extraction one-liner that was approved by Robert and John.
    ------------------------------------------------------------
    revno: 2592.1.34.1.244
    merged: mbp at sourcefrog.net-20071025012451-pvqf4umf0efueoqr
    parent: mbp at sourcefrog.net-20071024073815-0a1sdilg17ay4qpb
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: writegroups
    timestamp: Thu 2007-10-25 11:24:51 +1000
    message:
      unlock while in a write group now aborts the write group, unlocks, and errors.
      
      Also, RemoteRepository.unlock() sends the unlock rpc even if there was an error
      unlocking the vfs-based repository.
    ------------------------------------------------------------
    revno: 2592.1.34.1.243
    merged: mbp at sourcefrog.net-20071024073815-0a1sdilg17ay4qpb
    parent: mbp at sourcefrog.net-20071024073652-ct7z9cseabreb6fc
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: writegroups
    timestamp: Wed 2007-10-24 17:38:15 +1000
    message:
      Rename TestCase._capture_warnings
    ------------------------------------------------------------
    revno: 2592.1.34.1.242
    merged: mbp at sourcefrog.net-20071024073652-ct7z9cseabreb6fc
    parent: mbp at sourcefrog.net-20071024055951-6bmkfmgqh2188cij
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: writegroups
    timestamp: Wed 2007-10-24 17:36:52 +1000
    message:
      New method TestCase.call_catch_warnings
=== modified file 'NEWS'
--- a/NEWS	2007-10-25 09:20:51 +0000
+++ b/NEWS	2007-10-26 08:56:09 +0000
@@ -314,6 +314,9 @@
      makes this behavior available to other users.
      (Martin Pool)
 
+   * New method ``TestCase.call_catch_warnings`` for testing methods that 
+     raises a Python warning.  (Martin Pool)
+
 
 bzr 0.91 2007-09-26
 ===================

=== modified file 'bzrlib/knit.py'
--- a/bzrlib/knit.py	2007-10-24 06:48:13 +0000
+++ b/bzrlib/knit.py	2007-10-26 08:56:09 +0000
@@ -836,7 +836,7 @@
                 if method == 'fulltext':
                     next = None
                 else:
-                    next = self.get_parents(cursor)[0]
+                    next = self.get_parents_with_ghosts(cursor)[0]
                 index_memo = self._index.get_position(cursor)
                 component_data[cursor] = (method, index_memo, next)
                 cursor = next

=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py	2007-10-17 09:39:41 +0000
+++ b/bzrlib/remote.py	2007-10-25 01:24:51 +0000
@@ -492,26 +492,32 @@
             raise errors.UnexpectedSmartServerResponse(response)
 
     def unlock(self):
-        if self._lock_count == 1 and self._lock_mode == 'w':
-            # don't unlock if inside a write group.
-            if self.is_in_write_group():
-                raise errors.BzrError(
-                    'Must end write groups before releasing write locks.')
         self._lock_count -= 1
-        if not self._lock_count:
-            mode = self._lock_mode
-            self._lock_mode = None
+        if self._lock_count > 0:
+            return
+        old_mode = self._lock_mode
+        self._lock_mode = None
+        try:
+            # The real repository is responsible at present for raising an
+            # exception if it's in an unfinished write group.  However, it
+            # normally will *not* actually remove the lock from disk - that's
+            # done by the server on receiving the Repository.unlock call.
+            # This is just to let the _real_repository stay up to date.
             if self._real_repository is not None:
                 self._real_repository.unlock()
-            if mode != 'w':
+        finally:
+            # The rpc-level lock should be released even if there was a
+            # problem releasing the vfs-based lock.
+            if old_mode == 'w':
                 # Only write-locked repositories need to make a remote method
                 # call to perfom the unlock.
-                return
-            assert self._lock_token, 'Locked, but no token!'
-            token = self._lock_token
-            self._lock_token = None
-            if not self._leave_lock:
-                self._unlock(token)
+                assert self._lock_token, \
+                    '%s is locked, but has no token' \
+                    % self
+                old_token = self._lock_token
+                self._lock_token = None
+                if not self._leave_lock:
+                    self._unlock(old_token)
 
     def break_lock(self):
         # should hand off to the network

=== modified file 'bzrlib/repofmt/pack_repo.py'
--- a/bzrlib/repofmt/pack_repo.py	2007-10-25 06:17:57 +0000
+++ b/bzrlib/repofmt/pack_repo.py	2007-10-26 08:56:09 +0000
@@ -1535,8 +1535,12 @@
 
     def unlock(self):
         if self._write_lock_count == 1 and self._write_group is not None:
+            self.abort_write_group()
+            self._transaction = None
+            self._write_lock_count = 0
             raise errors.BzrError(
-                'Must end write groups before releasing write locks.')
+                'Must end write group before releasing write lock on %s'
+                % self)
         if self._write_lock_count:
             self._write_lock_count -= 1
             if not self._write_lock_count:

=== modified file 'bzrlib/repository.py'
--- a/bzrlib/repository.py	2007-10-26 06:58:43 +0000
+++ b/bzrlib/repository.py	2007-10-26 08:56:09 +0000
@@ -885,6 +885,8 @@
         if (self.control_files._lock_count == 1 and
             self.control_files._lock_mode == 'w'):
             if self._write_group is not None:
+                self.abort_write_group()
+                self.control_files.unlock()
                 raise errors.BzrError(
                     'Must end write groups before releasing write locks.')
         self.control_files.unlock()

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2007-10-22 01:23:51 +0000
+++ b/bzrlib/tests/__init__.py	2007-10-26 07:25:44 +0000
@@ -1032,7 +1032,7 @@
         else:
             self.fail('Unexpected success.  Should have failed: %s' % reason)
 
-    def _capture_warnings(self, a_callable, *args, **kwargs):
+    def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
         """A helper for callDeprecated and applyDeprecated.
 
         :param a_callable: A callable to call.
@@ -1080,7 +1080,7 @@
         :param kwargs: The keyword arguments for the callable
         :return: The result of a_callable(``*args``, ``**kwargs``)
         """
-        call_warnings, result = self._capture_warnings(a_callable,
+        call_warnings, result = self._capture_deprecation_warnings(a_callable,
             *args, **kwargs)
         expected_first_warning = symbol_versioning.deprecation_string(
             a_callable, deprecation_format)
@@ -1090,6 +1090,37 @@
         self.assertEqual(expected_first_warning, call_warnings[0])
         return result
 
+    def callCatchWarnings(self, fn, *args, **kw):
+        """Call a callable that raises python warnings.
+
+        The caller's responsible for examining the returned warnings.
+
+        If the callable raises an exception, the exception is not
+        caught and propagates up to the caller.  In that case, the list
+        of warnings is not available.
+
+        :returns: ([warning_object, ...], fn_result)
+        """
+        # XXX: This is not perfect, because it completely overrides the
+        # warnings filters, and some code may depend on suppressing particular
+        # warnings.  It's the easiest way to insulate ourselves from -Werror,
+        # though.  -- Andrew, 20071062
+        wlist = []
+        def _catcher(message, category, filename, lineno, file=None):
+            # despite the name, 'message' is normally(?) a Warning subclass
+            # instance
+            wlist.append(message)
+        saved_showwarning = warnings.showwarning
+        saved_filters = warnings.filters
+        try:
+            warnings.showwarning = _catcher
+            warnings.filters = []
+            result = fn(*args, **kw)
+        finally:
+            warnings.showwarning = saved_showwarning
+            warnings.filters = saved_filters
+        return wlist, result
+
     def callDeprecated(self, expected, callable, *args, **kwargs):
         """Assert that a callable is deprecated in a particular way.
 
@@ -1099,14 +1130,15 @@
         and will ensure that that is issued for the function being called.
 
         Note that this only captures warnings raised by symbol_versioning.warn,
-        not other callers that go direct to the warning module.
+        not other callers that go direct to the warning module.  To catch
+        general warnings, use callCatchWarnings.
 
         :param expected: a list of the deprecation warnings expected, in order
         :param callable: The callable to call
         :param args: The positional arguments for the callable
         :param kwargs: The keyword arguments for the callable
         """
-        call_warnings, result = self._capture_warnings(callable,
+        call_warnings, result = self._capture_deprecation_warnings(callable,
             *args, **kwargs)
         self.assertEqual(expected, call_warnings)
         return result

=== modified file 'bzrlib/tests/repository_implementations/test_write_group.py'
--- a/bzrlib/tests/repository_implementations/test_write_group.py	2007-07-17 16:04:00 +0000
+++ b/bzrlib/tests/repository_implementations/test_write_group.py	2007-10-25 01:24:51 +0000
@@ -61,7 +61,7 @@
         self.assertEqual(None, repo.commit_write_group())
         repo.unlock()
 
-    def test_unlock_after_start_errors(self):
+    def test_unlock_in_write_group(self):
         repo = self.make_repository('.')
         repo.lock_write()
         repo.start_write_group()
@@ -69,9 +69,11 @@
         # really to be sure it's used right, not for signalling
         # semantic information.
         self.assertRaises(errors.BzrError, repo.unlock)
-        self.assertTrue(repo.is_locked())
-        repo.commit_write_group()
-        repo.unlock()
+        # after this error occurs, the repository is unlocked, and the write
+        # group is gone.  you've had your chance, and you blew it. ;-)
+        self.assertFalse(repo.is_locked())
+        self.assertRaises(errors.BzrError, repo.commit_write_group)
+        self.assertRaises(errors.BzrError, repo.unlock)
 
     def test_is_in_write_group(self):
         repo = self.make_repository('.')

=== modified file 'bzrlib/tests/test_selftest.py'
--- a/bzrlib/tests/test_selftest.py	2007-10-02 11:24:53 +0000
+++ b/bzrlib/tests/test_selftest.py	2007-10-26 08:12:56 +0000
@@ -1525,6 +1525,22 @@
         self.callDeprecated([], testfunc, be_deprecated=False)
 
 
+class TestWarningTests(TestCase):
+    """Tests for calling methods that raise warnings."""
+
+    def test_callCatchWarnings(self):
+        def meth(a, b):
+            warnings.warn("this is your last warning")
+            return a + b
+        wlist, result = self.callCatchWarnings(meth, 1, 2)
+        self.assertEquals(3, result)
+        # would like just to compare them, but UserWarning doesn't implement
+        # eq well
+        w0, = wlist
+        self.assertIsInstance(w0, UserWarning)
+        self.assertEquals("this is your last warning", str(w0))
+
+
 class TestConvenienceMakers(TestCaseWithTransport):
     """Test for the make_* convenience functions."""
 

=== modified file 'doc/developers/HACKING.txt'
--- a/doc/developers/HACKING.txt	2007-10-02 06:13:56 +0000
+++ b/doc/developers/HACKING.txt	2007-10-24 07:36:52 +0000
@@ -470,6 +470,23 @@
 they're displayed or handled.
 
 
+Testing warnings
+----------------
+
+The Python ``warnings`` module is used to indicate a non-fatal code
+problem.  Code that's expected to raise a warning can be tested through
+callCatchWarnings.
+
+The test suite can be run with ``-Werror`` to check no unexpected errors
+occur.
+
+However, warnings should be used with discretion.  It's not an appropriate
+way to give messages to the user, because the warning is normally shown
+only once per source line that causes the problem.  You should also think
+about whether the warning is serious enought that it should be visible to
+users who may not be able to fix it.
+
+
 Interface implementation testing and test scenarios
 ---------------------------------------------------
 




More information about the bazaar-commits mailing list