Reduce amount of mutters.
[jelmer/subvertpy.git] / transport.py
index 5978b607240f37984937a5e6c2bf975e54943e43..f1b9db1758938804253c7d1b05a41a8fc5f926ab 100644 (file)
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-from bzrlib.errors import NoSuchFile, NotBranchError
+from bzrlib.errors import NoSuchFile, NotBranchError, TransportNotPossible
+from bzrlib.trace import mutter
 from bzrlib.transport import Transport
+import bzrlib.urlutils as urlutils
 
 from cStringIO import StringIO
 import os
+from tempfile import mktemp
 
+from svn.core import SubversionException, Pool
 import svn.ra
+import svn.core
 
-from scheme import NoBranchingScheme
+# Some older versions of the Python bindings need to be 
+# explicitly initialized
+svn.ra.initialize()
 
-def _create_auth_baton():
-    """ Create a Subversion authentication baton.  """
+svn_config = svn.core.svn_config_get_config(None)
+
+
+def _create_auth_baton(pool):
+    """Create a Subversion authentication baton. """
     import svn.client
     # Give the client context baton a suite of authentication
     # providers.h
     providers = [
-        svn.client.svn_client_get_simple_provider(),
-        svn.client.svn_client_get_ssl_client_cert_file_provider(),
-        svn.client.svn_client_get_ssl_client_cert_pw_file_provider(),
-        svn.client.svn_client_get_ssl_server_trust_file_provider(),
-        svn.client.svn_client_get_username_provider(),
+        svn.client.get_simple_provider(pool),
+        svn.client.get_username_provider(pool),
+        svn.client.get_ssl_client_cert_file_provider(pool),
+        svn.client.get_ssl_client_cert_pw_file_provider(pool),
+        svn.client.get_ssl_server_trust_file_provider(pool),
         ]
-    return svn.core.svn_auth_open(providers)
+    return svn.core.svn_auth_open(providers, pool)
 
 
 # Don't run any tests on SvnTransport as it is not intended to be 
@@ -45,85 +55,118 @@ def get_test_permutations():
     return []
 
 
-class SvnRaCallbacks(svn.ra.callbacks2_t):
-    def __init__(self):
-        svn.ra.callbacks2_t.__init__(self)
-        self.auth_baton = _create_auth_baton()
+def svn_to_bzr_url(url):
+    """Convert a Subversion URL to a URL understood by Bazaar.
 
-    def open_tmp_file(self):
-        print "foo"
+    This will optionally prefix the URL with "svn+".
+    """
+    if not (url.startswith("svn+") or url.startswith("svn:")):
+        url = "svn+%s" % url
+    return url
 
-    def progress(self, f, c, pool):
-        print "%s: %d / %d" % (self, f, c)
 
+def bzr_to_svn_url(url):
+    """Convert a Bazaar URL to a URL understood by Subversion.
 
-class SvnRaTransport(Transport):
-    """Fake transport for Subversion-related namespaces. This implements 
-    just as much of Transport as is necessary to fool Bazaar-NG. """
-    def __init__(self, url="", ra=None, root_url=None, scheme=None):
-        Transport.__init__(self, url)
-
-        if url.startswith("svn://") or \
-           url.startswith("svn+ssh://"):
-            self.svn_url = url
-        else:
-            self.svn_url = url[4:] # Skip svn+
-
-        # The SVN libraries don't like trailing slashes...
-        self.svn_url = self.svn_url.rstrip('/')
-
-        callbacks = SvnRaCallbacks()
-
-        if not ra:
-            self.ra = svn.ra.open2(self.svn_url.encode('utf8'), callbacks, None, None)
-            self.svn_root_url = svn.ra.get_repos_root(self.ra)
-            if self.svn_root_url != self.svn_url:
-                svn.ra.reparent(self.ra, self.svn_root_url.encode('utf8'))
-        else:
-            self.ra = ra
-            self.svn_root_url = root_url
+    This will possibly remove the svn+ prefix.
+    """
+    if (url.startswith("svn+http://") or 
+        url.startswith("svn+file://") or
+        url.startswith("svn+https://")):
+        url = url[len("svn+"):] # Skip svn+
 
-        self.root_url = self.svn_root_url
-        if not self.root_url.startswith("svn+"):
-            self.root_url = "svn+%s" % self.root_url
+    # The SVN libraries don't like trailing slashes...
+    return url.rstrip('/')
 
-        # Browsed above this directory
-        if not self.svn_url.startswith(self.svn_root_url):
-            raise NotBranchError(url)
 
-        self.path = self.svn_url[len(self.svn_root_url)+1:]
+class SvnRaCallbacks(svn.ra.callbacks2_t):
+    """Remote access callbacks implementation for bzr-svn."""
+    def __init__(self, pool):
+        svn.ra.callbacks2_t.__init__(self)
+        self.auth_baton = _create_auth_baton(pool)
+        self.pool = pool
+    
+    def open_tmp_file(self, pool):
+        return mktemp(prefix='bzr-svn')
 
-        if not scheme:
-            scheme = NoBranchingScheme()
+class SvnRaTransport(Transport):
+    """Fake transport for Subversion-related namespaces.
+    
+    This implements just as much of Transport as is necessary 
+    to fool Bazaar-NG. """
+    def __init__(self, url="", ra=None):
+        self.pool = Pool()
+        bzr_url = url
+        self.svn_url = bzr_to_svn_url(url)
+        Transport.__init__(self, bzr_url)
+
+               # Only Subversion 1.4 has reparent()
+        if ra is None or not hasattr(svn.ra, 'reparent'):
+            self.callbacks = SvnRaCallbacks(self.pool)
+            try:
+                mutter('opening SVN RA connection to %r' % self.svn_url)
+                self.ra = svn.ra.open2(self.svn_url.encode('utf8'), self.callbacks, svn_config, None)
+            except SubversionException, (_, num):
+                if num == svn.core.SVN_ERR_RA_ILLEGAL_URL:
+                    raise NotBranchError(path=url)
+                if num == svn.core.SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED:
+                    raise NotBranchError(path=url)
+                if num == svn.core.SVN_ERR_BAD_URL:
+                    raise NotBranchError(path=url)
+                raise
 
-        self._scheme = scheme
-        self.is_branch_root = scheme.is_branch(self.path)
+        else:
+            self.ra = ra
+            mutter('svn reparent %r' % self.svn_url)
+            svn.ra.reparent(self.ra, self.svn_url.encode('utf8'))
 
     def has(self, relpath):
+        """See Transport.has()."""
+        # TODO: Raise TransportNotPossible here instead and 
+        # catch it in bzrdir.py
         return False
 
     def get(self, relpath):
-        raise NoSuchFile(relpath)
+        """See Transport.get()."""
+        # TODO: Raise TransportNotPossible here instead and 
+        # catch it in bzrdir.py
+        raise NoSuchFile(path=relpath)
 
     def stat(self, relpath):
-        return os.stat('.') #FIXME
+        """See Transport.stat()."""
+        raise TransportNotPossible('stat not supported on Subversion')
+
+    def get_root(self):
+        """Open a connection to the root of this repository.
+        
+        :return: A new instance of SvnRaTransport connected to the root.
+        """
+        return SvnRaTransport(svn_to_bzr_url(svn.ra.get_repos_root(self.ra)), ra=self.ra)
 
     def listable(self):
+        """See Transport.listable().
+        
+        :return: False.
+        """
         return False
 
+    # There is no real way to do locking directly on the transport 
+    # nor is there a need to.
+    class PhonyLock:
+        def unlock(self):
+            pass
+
+    def lock_write(self, relpath):
+        """See Transport.lock_write()."""
+        return self.PhonyLock()
+
     def lock_read(self, relpath):
-        class PhonyLock:
-            def unlock(self):
-                pass
-        return PhonyLock()
+        """See Transport.lock_read()."""
+        return self.PhonyLock()
 
-    def clone(self, path):
-        parts = self.svn_url.split("/")
-        
-        # FIXME: Handle more complicated paths
-        if path == '..':
-            parts.pop()
-        elif path != '.':
-            parts.append(path)
+    def clone(self, offset=None):
+        """See Transport.clone()."""
+        if offset is None:
+            return self.__class__(self.base)
 
-        return SvnRaTransport("/".join(parts), ra=self.ra, root_url=self.svn_root_url)
+        return SvnRaTransport(urlutils.join(self.base, offset), ra=self.ra)