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