[MERGE] Add __eq__ and __ne__ methods to Repository objects

Andrew Bennetts andrew at canonical.com
Mon Aug 6 03:08:08 BST 2007


This bundle adds __eq__ and __ne__ methods to Repository objects so that you can
use the “==” and “!=” operators to tell if two objects are the same repository
or not.  See the extensive repository_implementations tests I've added for the
exact semantics, although they should be fairly intuitive.  This will be useful
in places like bzrlib/fetch.py that want to know if two repository objects
refer to the same repository or not.

This was written in response to John's review of my fetch-refactor bundle: he
suggested I should get rid of the _same_repo methods I added in fetch.py and
make Repositories comparable instead.  I think he's right: it would be good to
get rid of that logic from fetch.py, and put it somewhere where other code can
use it.

-Andrew.

-------------- next part --------------
# Bazaar merge directive format 2 (Bazaar 0.19)
# revision_id: andrew.bennetts at canonical.com-20070806015955-\
#   m36ovt3lahluxjzx
# target_branch: http://bazaar-vcs.org/bzr/bzr.dev
# testament_sha1: e7033d669c79a34ed51147d278190537fb89d897
# timestamp: 2007-08-06 12:00:20 +1000
# source_branch: http://people.ubuntu.com/~andrew/bzr/repository-\
#   equality
# base_revision_id: pqm at pqm.ubuntu.com-20070803043116-l7u1uypblmx1uxnr
# 
# Begin patch
=== modified file 'NEWS'
--- NEWS	2007-08-02 07:22:05 +0000
+++ NEWS	2007-08-06 01:59:55 +0000
@@ -195,6 +195,9 @@
     * ``bzrlib.pack.make_readv_reader`` allows readv based access to pack
       files that are stored on a transport. (Robert Collins)
 
+    * ``Repository`` objects can now be compared with ``==`` and ``!=`` to
+      determine if they are the same repository.  (Andrew Bennetts)
+
   TESTING:
 
     * Remove selftest ``--clean-output``, ``--numbered-dirs`` and

=== modified file 'bzrlib/remote.py'
--- bzrlib/remote.py	2007-07-25 00:52:21 +0000
+++ bzrlib/remote.py	2007-08-06 01:59:55 +0000
@@ -249,6 +249,13 @@
         self._lock_count = 0
         self._leave_lock = False
 
+    def __eq__(self, other):
+        return (self.__class__ == other.__class__ and
+                self._client == other._client)
+        
+    def __ne__(self, other):
+        return not self == other
+
     def _ensure_real(self):
         """Ensure that there is a _real_repository set.
 

=== modified file 'bzrlib/repository.py'
--- bzrlib/repository.py	2007-07-31 02:07:34 +0000
+++ bzrlib/repository.py	2007-08-06 01:59:55 +0000
@@ -235,6 +235,15 @@
         return '%s(%r)' % (self.__class__.__name__, 
                            self.bzrdir.transport.base)
 
+    def __eq__(self, other):
+        if self.__class__ is not other.__class__:
+            return False
+        return (self.control_files._transport.base ==
+                other.control_files._transport.base)
+
+    def __ne__(self, other):
+        return not self == other
+
     def is_locked(self):
         return self.control_files.is_locked()
 

=== modified file 'bzrlib/smart/client.py'
--- bzrlib/smart/client.py	2007-07-14 15:22:37 +0000
+++ bzrlib/smart/client.py	2007-08-06 01:59:55 +0000
@@ -25,6 +25,12 @@
     def __init__(self, shared_medium):
         self._shared_medium = shared_medium
 
+    def __eq__(self, other):
+        return self._shared_medium == other._shared_medium
+
+    def __ne__(self, other):
+        return not self == other
+
     def get_smart_medium(self):
         return self._shared_medium.connection
 

=== modified file 'bzrlib/tests/repository_implementations/__init__.py'
--- bzrlib/tests/repository_implementations/__init__.py	2007-07-12 12:07:13 +0000
+++ bzrlib/tests/repository_implementations/__init__.py	2007-08-06 01:59:55 +0000
@@ -91,7 +91,7 @@
             return repo
         else:
             return super(TestCaseWithRepository, self).make_repository(
-                relpath, format)
+                relpath, format=format)
 
 
 

=== modified file 'bzrlib/tests/repository_implementations/test_repository.py'
--- bzrlib/tests/repository_implementations/test_repository.py	2007-07-25 00:52:21 +0000
+++ bzrlib/tests/repository_implementations/test_repository.py	2007-08-06 01:59:55 +0000
@@ -422,6 +422,109 @@
         self.assertEqual(repo._serializer.format_num, format)
 
 
+class TestRepositoryEquality(TestCaseWithRepository):
+
+    def strictAssertEqual(self, a, b):
+        """Like assertEqual, but also checks the `!=` operator is consistent.
+
+        i.e. if `a == b` *and* `a != b`, this method will fail.
+
+        This can happen when a class defines an `__eq__` but doesn't define an
+        `__ne__`.
+        """
+        self.assertEqual(a, b)
+        self.failIf(
+            a != b,
+            "%r and %r are both equal and not equal!  Class probably defines "
+            "__eq__ without also defining __ne__." % (a, b))
+
+    def strictAssertNotEqual(self, a, b):
+        """Like assertNotEqual, but also checks the `==` operator is consistent.
+
+        i.e. if `a != b` *and* `a == b`, this method will fail.
+
+        This can happen when a class defines an `__eq__` but doesn't define an
+        `__ne__`.
+
+        :seealso: strictAssertEqual
+        """
+        self.assertNotEqual(a, b)
+        self.failIf(
+            a == b,
+            "%r and %r are both equal and not equal!  Class probably defines "
+            "__eq__ without also defining __ne__." % (a, b))
+
+    def test_same_repo_instance_is_equal(self):
+        """A repository object is always equal to itself."""
+        repo = self.make_repository('.')
+        self.strictAssertEqual(repo, repo)
+
+    def test_same_repo_location_is_equal(self):
+        """Different repository objects connected to the same location are
+        equal.
+        """
+        repo = self.make_repository('.')
+        reopened_repo = repo.bzrdir.open_repository()
+        self.failIf(
+            repo is reopened_repo,
+            "This test depends on reopened_repo being a different instance of "
+            "the same repo.")
+        self.strictAssertEqual(repo, reopened_repo)
+
+    def test_different_repos_not_equal(self):
+        """Repositories at different locations are not equal."""
+        repo_one = self.make_repository('one')
+        repo_two = self.make_repository('two')
+        self.strictAssertNotEqual(repo_one, repo_two)
+
+    def test_same_bzrdir_different_control_files_not_equal(self):
+        """Repositories in the same bzrdir, but with different control files,
+        are not equal.
+
+        This can happens e.g. when upgrading a repository.  This test mimics how
+        CopyConverter creates a second repository in one bzrdir.
+        """
+        repo = self.make_repository('repo')
+        try:
+            control_transport = repo.control_files._transport
+        except AttributeError:
+            # This test only applies to repository formats with control_files.
+            return
+        if control_transport.base == repo.bzrdir.transport.base:
+            # This test only applies to repository formats where the repo
+            # control_files are separate from other bzrdir files, i.e. metadir
+            # formats.
+            return
+        control_transport.copy_tree('.', '../repository.backup')
+        backup_transport = control_transport.clone('../repository.backup')
+        backup_repo = repo._format.open(repo.bzrdir, _found=True,
+                                        _override_transport=backup_transport)
+
+        self.strictAssertNotEqual(repo, backup_repo)
+
+    def test_different_format_not_equal(self):
+        """Different format repositories are comparable and not equal.
+
+        Comparing different format repository objects should give a negative
+        result, rather than trigger an exception (which could happen with a
+        naive __eq__ implementation, e.g. due to missing attributes).
+        """
+        repo = self.make_repository('repo')
+        other_repo = self.make_repository('other', format='default')
+        if repo._format == other_repo._format:
+            # We're testing the default format!  So we have to use a non-default
+            # format for other_repo.
+            get_transport(self.get_vfs_only_url()).delete_tree('other')
+            other_repo = self.make_repository('other', format='metaweave')
+        # Make sure the other_repo is not a RemoteRepository.
+        other_bzrdir = bzrdir.BzrDir.open(self.get_vfs_only_url('other'))
+        other_repo = other_bzrdir.open_repository()
+        # Compare both ways, to make sure the __eq__ on both repositories cope
+        # with comparing against a different class.
+        self.strictAssertNotEqual(repo, other_repo)
+        self.strictAssertNotEqual(other_repo, repo)
+
+
 class TestRepositoryLocking(TestCaseWithRepository):
 
     def test_leave_lock_in_place(self):

# Begin bundle
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
a0qxw5fnsBeAQf/F3JFOFCQgrcjNwA==


More information about the bazaar mailing list