Rev 2328: (Dmitry Vasiliev) Support for Putty SSH, and improved vendor support in http://bzr.arbash-meinel.com/branches/bzr/jam-integration
John Arbash Meinel
john at arbash-meinel.com
Fri Mar 9 00:45:32 GMT 2007
At http://bzr.arbash-meinel.com/branches/bzr/jam-integration
------------------------------------------------------------
revno: 2328
revision-id: john at arbash-meinel.com-20070309004524-fz8byjfqklnzvi6h
parent: pqm at pqm.ubuntu.com-20070309003830-14e9b007ad653905
parent: dima at hlabs.spb.ru-20070307134747-clcmwlfck4g9yqh3
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: jam-integration
timestamp: Thu 2007-03-08 18:45:24 -0600
message:
(Dmitry Vasiliev) Support for Putty SSH, and improved vendor support
added:
bzrlib/tests/test_ssh_transport.py test_ssh_transport.p-20070105153201-f7iq2bosvgjbdgc3-1
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/errors.py errors.py-20050309040759-20512168c4e14fbd
bzrlib/tests/__init__.py selftest.py-20050531073622-8d0e3c8845c97a64
bzrlib/tests/blackbox/test_pull.py test_pull.py-20051201144907-64959364f629947f
bzrlib/tests/test_sftp_transport.py testsftp.py-20051027032739-247570325fec7e7e
bzrlib/transport/sftp.py sftp.py-20051019050329-ab48ce71b7e32dfe
bzrlib/transport/ssh.py ssh.py-20060824042150-0s9787kng6zv1nwq-1
------------------------------------------------------------
revno: 2221.5.22
merged: dima at hlabs.spb.ru-20070307134747-clcmwlfck4g9yqh3
parent: dima at hlabs.spb.ru-20070307132025-kgv55i7xvp7rp7pv
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Wed 2007-03-07 16:47:47 +0300
message:
Updated note about registry.Registry
------------------------------------------------------------
revno: 2221.5.21
merged: dima at hlabs.spb.ru-20070307132025-kgv55i7xvp7rp7pv
parent: dima at hlabs.spb.ru-20070307124531-yom9s6p3rb1npeet
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Wed 2007-03-07 16:20:25 +0300
message:
Reverted trailing whitespace removal
------------------------------------------------------------
revno: 2221.5.20
merged: dima at hlabs.spb.ru-20070307124531-yom9s6p3rb1npeet
parent: dima at hlabs.spb.ru-20070307121040-ekqnz95gtv2iq4jb
parent: pqm at pqm.ubuntu.com-20070307121852-b60a661123a5063d
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Wed 2007-03-07 15:45:31 +0300
message:
Merged bzr.dev
------------------------------------------------------------
revno: 2221.5.19
merged: dima at hlabs.spb.ru-20070307121040-ekqnz95gtv2iq4jb
parent: dima at hlabs.spb.ru-20070302153843-9hks8px50lwukdcm
parent: pqm at pqm.ubuntu.com-20070307110538-3026a526f5178b00
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Wed 2007-03-07 15:10:40 +0300
message:
Merged bzr.dev
------------------------------------------------------------
revno: 2221.5.18
merged: dima at hlabs.spb.ru-20070302153843-9hks8px50lwukdcm
parent: dima at hlabs.spb.ru-20070302141015-sjn7kthbdgylt3bp
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Fri 2007-03-02 18:38:43 +0300
message:
Fixed variable name
------------------------------------------------------------
revno: 2221.5.17
merged: dima at hlabs.spb.ru-20070302141015-sjn7kthbdgylt3bp
parent: dima at hlabs.spb.ru-20070302134414-9mqpmmsii6llb9of
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Fri 2007-03-02 17:10:15 +0300
message:
Added comments for test_get_vendor_search_order
------------------------------------------------------------
revno: 2221.5.16
merged: dima at hlabs.spb.ru-20070302134414-9mqpmmsii6llb9of
parent: dima at hlabs.spb.ru-20070302132658-m844rgyc8smzhw3y
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Fri 2007-03-02 16:44:14 +0300
message:
Added comments for test_cached_vendor
------------------------------------------------------------
revno: 2221.5.15
merged: dima at hlabs.spb.ru-20070302132658-m844rgyc8smzhw3y
parent: dima at hlabs.spb.ru-20070302122819-c5cfkd4nnp5tzksp
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Fri 2007-03-02 16:26:58 +0300
message:
Added docstrings for all SSHVendorManager's methods
------------------------------------------------------------
revno: 2221.5.14
merged: dima at hlabs.spb.ru-20070302122819-c5cfkd4nnp5tzksp
parent: dima at hlabs.spb.ru-20070302122200-5oqpv72nae2tr8sl
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Fri 2007-03-02 15:28:19 +0300
message:
Wrapped long lines
------------------------------------------------------------
revno: 2221.5.13
merged: dima at hlabs.spb.ru-20070302122200-5oqpv72nae2tr8sl
parent: dima at hlabs.spb.ru-20070302120557-vasdqot44qytav50
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Fri 2007-03-02 15:22:00 +0300
message:
Fixed expected message in test
------------------------------------------------------------
revno: 2221.5.12
merged: dima at hlabs.spb.ru-20070302120557-vasdqot44qytav50
parent: dima at hlabs.spb.ru-20070302115124-xpya6q8087zrzb5z
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Fri 2007-03-02 15:05:57 +0300
message:
Added note about SSHVendorManager
------------------------------------------------------------
revno: 2221.5.11
merged: dima at hlabs.spb.ru-20070302115124-xpya6q8087zrzb5z
parent: dima at hlabs.spb.ru-20070206185152-qk1p2p0tjn9uf4nm
parent: pqm at pqm.ubuntu.com-20070301073000-0bfe1394fee5e712
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Putty SSH implementation support
timestamp: Fri 2007-03-02 14:51:24 +0300
message:
Merged bzr.dev
------------------------------------------------------------
revno: 2221.5.10
merged: dima at hlabs.spb.ru-20070206185152-qk1p2p0tjn9uf4nm
parent: dima at hlabs.spb.ru-20070206184530-4rtd41xpm9ksgbo5
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Small Fixes
timestamp: Tue 2007-02-06 21:51:52 +0300
message:
Imports placed in alphabetical order
------------------------------------------------------------
revno: 2221.5.9
merged: dima at hlabs.spb.ru-20070206184530-4rtd41xpm9ksgbo5
parent: dima at hlabs.spb.ru-20070206182410-knuhr3u2s4xm3upb
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Small Fixes
timestamp: Tue 2007-02-06 21:45:30 +0300
message:
Removed trailing whitespaces and wrapped all long lines
------------------------------------------------------------
revno: 2221.5.8
merged: dima at hlabs.spb.ru-20070206182410-knuhr3u2s4xm3upb
parent: dima at hlabs.spb.ru-20070203080847-3mnn4704lz6f2djs
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Small Fixes
timestamp: Tue 2007-02-06 21:24:10 +0300
message:
Added SSHVendorManager.clear_cache() method
------------------------------------------------------------
revno: 2221.5.7
merged: dima at hlabs.spb.ru-20070203080847-3mnn4704lz6f2djs
parent: dima at hlabs.spb.ru-20070113135005-he6e52rif5rym8hv
parent: pqm at pqm.ubuntu.com-20070202204950-910381483d737306
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Small Fixes
timestamp: Sat 2007-02-03 11:08:47 +0300
message:
Merged bzr.dev
------------------------------------------------------------
revno: 2221.5.6
merged: dima at hlabs.spb.ru-20070113135005-he6e52rif5rym8hv
parent: dima at hlabs.spb.ru-20070113132723-7b42wbkb488nf725
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Small Fixes
timestamp: Sat 2007-01-13 16:50:05 +0300
message:
Changed tests to make sure the vendor manager returns the same object as was registered
------------------------------------------------------------
revno: 2221.5.5
merged: dima at hlabs.spb.ru-20070113132723-7b42wbkb488nf725
parent: dima at hlabs.spb.ru-20070113131305-ncebrfkhqacbbnjf
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Small Fixes
timestamp: Sat 2007-01-13 16:27:23 +0300
message:
Added 'register_default_vendor' method to the SSHVendorManager
------------------------------------------------------------
revno: 2221.5.4
merged: dima at hlabs.spb.ru-20070113131305-ncebrfkhqacbbnjf
parent: dima at hlabs.spb.ru-20070113130821-78gaqgwsmyxprr12
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Small Fixes
timestamp: Sat 2007-01-13 16:13:05 +0300
message:
Removed trailing whitespace
------------------------------------------------------------
revno: 2221.5.3
merged: dima at hlabs.spb.ru-20070113130821-78gaqgwsmyxprr12
parent: dima at hlabs.spb.ru-20070105161006-r0x531easfpft0k6
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Small Fixes
timestamp: Sat 2007-01-13 16:08:21 +0300
message:
Fixed plink's arguments order. Added tests for such a case.
------------------------------------------------------------
revno: 2221.5.2
merged: dima at hlabs.spb.ru-20070105161006-r0x531easfpft0k6
parent: dima at hlabs.spb.ru-20070105154416-z0s48q431s86rm5j
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Small Fixes
timestamp: Fri 2007-01-05 19:10:06 +0300
message:
Added note to the NEWS file
------------------------------------------------------------
revno: 2221.5.1
merged: dima at hlabs.spb.ru-20070105154416-z0s48q431s86rm5j
parent: pqm at pqm.ubuntu.com-20070103073947-f9906c0b1f425aa9
committer: Dmitry Vasiliev <dima at hlabs.spb.ru>
branch nick: Small Fixes
timestamp: Fri 2007-01-05 18:44:16 +0300
message:
Added support for Putty's SSH implementation
-------------- next part --------------
=== added file 'bzrlib/tests/test_ssh_transport.py'
--- a/bzrlib/tests/test_ssh_transport.py 1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/test_ssh_transport.py 2007-03-02 14:10:15 +0000
@@ -0,0 +1,202 @@
+# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# 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.tests import TestCase
+from bzrlib.errors import SSHVendorNotFound, UnknownSSH
+from bzrlib.transport.ssh import (
+ OpenSSHSubprocessVendor,
+ PLinkSubprocessVendor,
+ SSHCorpSubprocessVendor,
+ SSHVendorManager,
+ )
+
+
+class TestSSHVendorManager(SSHVendorManager):
+
+ _ssh_version_string = ""
+
+ def set_ssh_version_string(self, version):
+ self._ssh_version_string = version
+
+ def _get_ssh_version_string(self, args):
+ return self._ssh_version_string
+
+
+class SSHVendorManagerTests(TestCase):
+
+ def test_register_vendor(self):
+ manager = TestSSHVendorManager()
+ self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
+ vendor = object()
+ manager.register_vendor("vendor", vendor)
+ self.assertIs(manager.get_vendor({"BZR_SSH": "vendor"}), vendor)
+
+ def test_default_vendor(self):
+ manager = TestSSHVendorManager()
+ self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
+ vendor = object()
+ manager.register_default_vendor(vendor)
+ self.assertIs(manager.get_vendor({}), vendor)
+
+ def test_get_vendor_by_environment(self):
+ manager = TestSSHVendorManager()
+ self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
+ self.assertRaises(UnknownSSH,
+ manager.get_vendor, {"BZR_SSH": "vendor"})
+ vendor = object()
+ manager.register_vendor("vendor", vendor)
+ self.assertIs(manager.get_vendor({"BZR_SSH": "vendor"}), vendor)
+
+ def test_get_vendor_by_inspection_openssh(self):
+ manager = TestSSHVendorManager()
+ self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
+ manager.set_ssh_version_string("OpenSSH")
+ self.assertIsInstance(manager.get_vendor({}), OpenSSHSubprocessVendor)
+
+ def test_get_vendor_by_inspection_sshcorp(self):
+ manager = TestSSHVendorManager()
+ self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
+ manager.set_ssh_version_string("SSH Secure Shell")
+ self.assertIsInstance(manager.get_vendor({}), SSHCorpSubprocessVendor)
+
+ def test_get_vendor_by_inspection_plink(self):
+ manager = TestSSHVendorManager()
+ self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
+ manager.set_ssh_version_string("plink")
+ self.assertIsInstance(manager.get_vendor({}), PLinkSubprocessVendor)
+
+ def test_cached_vendor(self):
+ manager = TestSSHVendorManager()
+ self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
+ vendor = object()
+ manager.register_vendor("vendor", vendor)
+ self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
+ # Once the vendor is found the result is cached (mainly because of the
+ # 'get_vendor' sometimes can be an expensive operation) and later
+ # invocations of the 'get_vendor' just returns the cached value.
+ self.assertIs(manager.get_vendor({"BZR_SSH": "vendor"}), vendor)
+ self.assertIs(manager.get_vendor({}), vendor)
+ # The cache can be cleared by the 'clear_cache' method
+ manager.clear_cache()
+ self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
+
+ def test_get_vendor_search_order(self):
+ # The 'get_vendor' method search for SSH vendors as following:
+ #
+ # 1. Check previously cached value
+ # 2. Check BZR_SSH environment variable
+ # 3. Check the system for known SSH vendors
+ # 4. Fall back to the default vendor if registered
+ #
+ # Let's now check the each check method in the reverse order
+ # clearing the cache between each invocation:
+
+ manager = TestSSHVendorManager()
+ # At first no vendors are found
+ self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
+
+ # If the default vendor is registered it will be returned
+ default_vendor = object()
+ manager.register_default_vendor(default_vendor)
+ self.assertIs(manager.get_vendor({}), default_vendor)
+
+ # If the known vendor is found in the system it will be returned
+ manager.clear_cache()
+ manager.set_ssh_version_string("OpenSSH")
+ self.assertIsInstance(manager.get_vendor({}), OpenSSHSubprocessVendor)
+
+ # If the BZR_SSH environment variable is found it will be treated as
+ # the vendor name
+ manager.clear_cache()
+ vendor = object()
+ manager.register_vendor("vendor", vendor)
+ self.assertIs(manager.get_vendor({"BZR_SSH": "vendor"}), vendor)
+
+ # Last cached value always checked first
+ self.assertIs(manager.get_vendor({}), vendor)
+
+
+class SubprocessVendorsTests(TestCase):
+
+ def test_openssh_command_arguments(self):
+ vendor = OpenSSHSubprocessVendor()
+ self.assertEqual(
+ vendor._get_vendor_specific_argv(
+ "user", "host", 100, command=["bzr"]),
+ ["ssh", "-oForwardX11=no", "-oForwardAgent=no",
+ "-oClearAllForwardings=yes", "-oProtocol=2",
+ "-oNoHostAuthenticationForLocalhost=yes",
+ "-p", "100",
+ "-l", "user",
+ "host", "bzr"]
+ )
+
+ def test_openssh_subsystem_arguments(self):
+ vendor = OpenSSHSubprocessVendor()
+ self.assertEqual(
+ vendor._get_vendor_specific_argv(
+ "user", "host", 100, subsystem="sftp"),
+ ["ssh", "-oForwardX11=no", "-oForwardAgent=no",
+ "-oClearAllForwardings=yes", "-oProtocol=2",
+ "-oNoHostAuthenticationForLocalhost=yes",
+ "-p", "100",
+ "-l", "user",
+ "-s", "host", "sftp"]
+ )
+
+ def test_sshcorp_command_arguments(self):
+ vendor = SSHCorpSubprocessVendor()
+ self.assertEqual(
+ vendor._get_vendor_specific_argv(
+ "user", "host", 100, command=["bzr"]),
+ ["ssh", "-x",
+ "-p", "100",
+ "-l", "user",
+ "host", "bzr"]
+ )
+
+ def test_sshcorp_subsystem_arguments(self):
+ vendor = SSHCorpSubprocessVendor()
+ self.assertEqual(
+ vendor._get_vendor_specific_argv(
+ "user", "host", 100, subsystem="sftp"),
+ ["ssh", "-x",
+ "-p", "100",
+ "-l", "user",
+ "-s", "sftp", "host"]
+ )
+
+ def test_plink_command_arguments(self):
+ vendor = PLinkSubprocessVendor()
+ self.assertEqual(
+ vendor._get_vendor_specific_argv(
+ "user", "host", 100, command=["bzr"]),
+ ["plink", "-x", "-a", "-ssh", "-2",
+ "-P", "100",
+ "-l", "user",
+ "host", "bzr"]
+ )
+
+ def test_plink_subsystem_arguments(self):
+ vendor = PLinkSubprocessVendor()
+ self.assertEqual(
+ vendor._get_vendor_specific_argv(
+ "user", "host", 100, subsystem="sftp"),
+ ["plink", "-x", "-a", "-ssh", "-2",
+ "-P", "100",
+ "-l", "user",
+ "-s", "host", "sftp"]
+ )
=== modified file 'NEWS'
--- a/NEWS 2007-03-07 12:00:12 +0000
+++ b/NEWS 2007-03-07 13:20:25 +0000
@@ -30,6 +30,8 @@
IMPROVEMENTS:
+ * Added support for Putty's SSH implementation. (Dmitry Vasiliev)
+
* Support for OS Windows 98. Also .bzr.log on any windows system
saved in My Documents folder. (Alexander Belchenko)
@@ -105,6 +107,9 @@
INTERNALS:
+ * Refactored SSH vendor registration into SSHVendorManager class.
+ (Dmitry Vasiliev)
+
* Internally revision ids and file ids are now passed around as utf-8
bytestrings, rather than treating them as Unicode strings. This has
performance benefits for Knits, since we no longer need to decode the
=== modified file 'bzrlib/errors.py'
--- a/bzrlib/errors.py 2007-03-05 04:55:34 +0000
+++ b/bzrlib/errors.py 2007-03-07 13:20:25 +0000
@@ -132,11 +132,12 @@
# readable explanation
def __init__(self, *args, **kwds):
- # XXX: Use the underlying BzrError to always generate the args attribute
- # if it doesn't exist. We can't use super here, because exceptions are
- # old-style classes in python2.4 (but new in 2.5). --bmc, 20060426
+ # XXX: Use the underlying BzrError to always generate the args
+ # attribute if it doesn't exist. We can't use super here, because
+ # exceptions are old-style classes in python2.4 (but new in 2.5).
+ # --bmc, 20060426
symbol_versioning.warn('BzrNewError was deprecated in bzr 0.13; '
- 'please convert %s to use BzrError instead'
+ 'please convert %s to use BzrError instead'
% self.__class__.__name__,
DeprecationWarning,
stacklevel=2)
@@ -223,8 +224,8 @@
class InventoryModified(BzrError):
- _fmt = ("The current inventory for the tree %(tree)r has been modified, "
- "so a clean inventory cannot be read without data loss.")
+ _fmt = ("The current inventory for the tree %(tree)r has been modified,"
+ " so a clean inventory cannot be read without data loss.")
internal_error = True
@@ -342,7 +343,7 @@
"""Used when renaming and both source and dest exist."""
_fmt = ("Could not rename %(source)s => %(dest)s because both files exist."
- "%(extra)s")
+ "%(extra)s")
def __init__(self, source, dest, extra=None):
BzrError.__init__(self)
@@ -425,7 +426,8 @@
class ShortReadvError(PathError):
- _fmt = "readv() read %(actual)s bytes rather than %(length)s bytes at %(offset)s for %(path)s%(extra)s"
+ _fmt = ("readv() read %(actual)s bytes rather than %(length)s bytes"
+ " at %(offset)s for %(path)s%(extra)s")
internal_error = True
@@ -482,7 +484,8 @@
class AtomicFileAlreadyClosed(PathError):
- _fmt = "'%(function)s' called on an AtomicFile after it was closed: %(path)s"
+ _fmt = ("'%(function)s' called on an AtomicFile after it was closed:"
+ " %(path)s")
def __init__(self, path, function):
PathError.__init__(self, path=path, extra=None)
@@ -491,7 +494,8 @@
class InaccessibleParent(PathError):
- _fmt = "Parent not accessible given base %(base)s and relative path %(path)s"
+ _fmt = ("Parent not accessible given base %(base)s and"
+ " relative path %(path)s")
def __init__(self, path, base):
PathError.__init__(self, path)
@@ -686,7 +690,8 @@
class OutSideTransaction(BzrError):
- _fmt = "A transaction related operation was attempted after the transaction finished."
+ _fmt = ("A transaction related operation was attempted after"
+ " the transaction finished.")
class ObjectNotLocked(LockError):
@@ -730,7 +735,8 @@
class LockBroken(LockError):
- _fmt = "Lock was broken while still open: %(lock)s - check storage consistency!"
+ _fmt = ("Lock was broken while still open: %(lock)s"
+ " - check storage consistency!")
internal_error = False
@@ -740,7 +746,8 @@
class LockBreakMismatch(LockError):
- _fmt = "Lock was released and re-acquired before being broken: %(lock)s: held by %(holder)r, wanted to break %(target)r"
+ _fmt = ("Lock was released and re-acquired before being broken:"
+ " %(lock)s: held by %(holder)r, wanted to break %(target)r")
internal_error = False
@@ -796,8 +803,8 @@
class NotLeftParentDescendant(BzrError):
- _fmt = "Revision %(old_revision)s is not the left parent of"\
- " %(new_revision)s, but branch %(branch_location)s expects this"
+ _fmt = ("Revision %(old_revision)s is not the left parent of"
+ " %(new_revision)s, but branch %(branch_location)s expects this")
internal_error = True
@@ -828,7 +835,8 @@
class InvalidRevisionSpec(BzrError):
- _fmt = "Requested revision: %(spec)r does not exist in branch: %(branch)s%(extra)s"
+ _fmt = ("Requested revision: %(spec)r does not exist in branch:"
+ " %(branch)s%(extra)s")
def __init__(self, spec, branch, extra=None):
BzrError.__init__(self, branch=branch, spec=spec)
@@ -845,9 +853,9 @@
class AppendRevisionsOnlyViolation(BzrError):
- _fmt = 'Operation denied because it would change the main history, '\
- 'which is not permitted by the append_revisions_only setting on'\
- ' branch "%(location)s".'
+ _fmt = ('Operation denied because it would change the main history,'
+ ' which is not permitted by the append_revisions_only setting on'
+ ' branch "%(location)s".')
def __init__(self, location):
import bzrlib.urlutils as urlutils
@@ -856,8 +864,9 @@
class DivergedBranches(BzrError):
-
- _fmt = "These branches have diverged. Use the merge command to reconcile them."""
+
+ _fmt = ("These branches have diverged."
+ " Use the merge command to reconcile them.")
internal_error = False
@@ -878,7 +887,8 @@
class UnrelatedBranches(BzrError):
- _fmt = "Branches have no common ancestor, and no merge base revision was specified."
+ _fmt = ("Branches have no common ancestor, and"
+ " no merge base revision was specified.")
internal_error = False
@@ -894,8 +904,8 @@
class NoCommonRoot(BzrError):
- _fmt = "Revisions are not derived from the same root: " \
- "%(revision_a)s %(revision_b)s."
+ _fmt = ("Revisions are not derived from the same root: "
+ "%(revision_a)s %(revision_b)s.")
def __init__(self, revision_a, revision_b):
BzrError.__init__(self, revision_a=revision_a, revision_b=revision_b)
@@ -924,8 +934,8 @@
def __init__(self, bases):
warn("BzrError AmbiguousBase has been deprecated as of bzrlib 0.8.",
DeprecationWarning)
- msg = "The correct base is unclear, because %s are all equally close" %\
- ", ".join(bases)
+ msg = ("The correct base is unclear, because %s are all equally close"
+ % ", ".join(bases))
BzrError.__init__(self, msg)
self.bases = bases
@@ -953,7 +963,8 @@
class BoundBranchOutOfDate(BzrError):
- _fmt = "Bound branch %(branch)s is out of date with master branch %(master)s."
+ _fmt = ("Bound branch %(branch)s is out of date"
+ " with master branch %(master)s.")
def __init__(self, branch, master):
BzrError.__init__(self)
@@ -963,7 +974,8 @@
class CommitToDoubleBoundBranch(BzrError):
- _fmt = "Cannot commit to branch %(branch)s. It is bound to %(master)s, which is bound to %(remote)s."
+ _fmt = ("Cannot commit to branch %(branch)s."
+ " It is bound to %(master)s, which is bound to %(remote)s.")
def __init__(self, branch, master, remote):
BzrError.__init__(self)
@@ -983,7 +995,8 @@
class BoundBranchConnectionFailure(BzrError):
- _fmt = "Unable to connect to target of bound branch %(branch)s => %(target)s: %(error)s"
+ _fmt = ("Unable to connect to target of bound branch %(branch)s"
+ " => %(target)s: %(error)s")
def __init__(self, branch, target, error):
BzrError.__init__(self)
@@ -1043,7 +1056,8 @@
class WeaveTextDiffers(WeaveError):
- _fmt = "Weaves differ on text content. Revision: {%(revision_id)s}, %(weave_a)s, %(weave_b)s"
+ _fmt = ("Weaves differ on text content. Revision:"
+ " {%(revision_id)s}, %(weave_a)s, %(weave_b)s")
def __init__(self, revision_id, weave_a, weave_b):
WeaveError.__init__(self)
@@ -1054,7 +1068,8 @@
class WeaveTextDiffers(WeaveError):
- _fmt = "Weaves differ on text content. Revision: {%(revision_id)s}, %(weave_a)s, %(weave_b)s"
+ _fmt = ("Weaves differ on text content. Revision:"
+ " {%(revision_id)s}, %(weave_a)s, %(weave_b)s")
def __init__(self, revision_id, weave_a, weave_b):
WeaveError.__init__(self)
@@ -1157,9 +1172,9 @@
class TooManyConcurrentRequests(BzrError):
- _fmt = ("The medium '%(medium)s' has reached its concurrent request limit. "
- "Be sure to finish_writing and finish_reading on the "
- "current request that is open.")
+ _fmt = ("The medium '%(medium)s' has reached its concurrent request limit."
+ " Be sure to finish_writing and finish_reading on the"
+ " current request that is open.")
internal_error = True
@@ -1292,8 +1307,8 @@
class CantReprocessAndShowBase(BzrError):
- _fmt = "Can't reprocess and show base, because reprocessing obscures " \
- "the relationship of conflicting lines to the base"
+ _fmt = ("Can't reprocess and show base, because reprocessing obscures "
+ "the relationship of conflicting lines to the base")
class GraphCycleError(BzrError):
@@ -1348,9 +1363,9 @@
class MustUseDecorated(Exception):
-
- _fmt = """A decorating function has requested its original command be used."""
-
+
+ _fmt = "A decorating function has requested its original command be used."
+
class NoBundleFound(BzrError):
@@ -1373,7 +1388,8 @@
class MissingText(BzrError):
- _fmt = "Branch %(base)s is missing revision %(text_revision)s of %(file_id)s"
+ _fmt = ("Branch %(base)s is missing revision"
+ " %(text_revision)s of %(file_id)s")
def __init__(self, branch, text_revision, file_id):
BzrError.__init__(self)
@@ -1496,7 +1512,8 @@
class BzrBadParameterUnicode(BzrBadParameter):
- _fmt = "Parameter %(param)s is unicode but only byte-strings are permitted."
+ _fmt = ("Parameter %(param)s is unicode but"
+ " only byte-strings are permitted.")
class BzrBadParameterContainsNewline(BzrBadParameter):
@@ -1600,8 +1617,8 @@
class CorruptRepository(BzrError):
- _fmt = """An error has been detected in the repository %(repo_path)s.
-Please run bzr reconcile on this repository."""
+ _fmt = ("An error has been detected in the repository %(repo_path)s.\n"
+ "Please run bzr reconcile on this repository.")
def __init__(self, repo):
BzrError.__init__(self)
@@ -1629,8 +1646,8 @@
class InvalidProgressBarType(BzrError):
- _fmt = """Environment variable BZR_PROGRESS_BAR='%(bar_type)s is not a supported type
-Select one of: %(valid_types)s"""
+ _fmt = ("Environment variable BZR_PROGRESS_BAR='%(bar_type)s"
+ " is not a supported type Select one of: %(valid_types)s")
def __init__(self, bar_type, valid_types):
BzrError.__init__(self, bar_type=bar_type, valid_types=valid_types)
@@ -1638,7 +1655,8 @@
class UnsupportedOperation(BzrError):
- _fmt = "The method %(mname)s is not supported on objects of type %(tname)s."
+ _fmt = ("The method %(mname)s is not supported on"
+ " objects of type %(tname)s.")
def __init__(self, method, method_self):
self.method = method
@@ -1651,7 +1669,9 @@
class NonAsciiRevisionId(UnsupportedOperation):
- """Raised when a commit is attempting to set a non-ascii revision id but cant."""
+ """Raised when a commit is attempting to set a non-ascii revision id
+ but cant.
+ """
class BinaryFile(BzrError):
@@ -1670,8 +1690,8 @@
class TestamentMismatch(BzrError):
- _fmt = """Testament did not match expected value.
- For revision_id {%(revision_id)s}, expected {%(expected)s}, measured
+ _fmt = """Testament did not match expected value.
+ For revision_id {%(revision_id)s}, expected {%(expected)s}, measured
{%(measured)s}"""
def __init__(self, revision_id, expected, measured):
@@ -1778,6 +1798,12 @@
self.vendor = vendor
+class SSHVendorNotFound(BzrError):
+
+ _fmt = ("Don't know how to handle SSH connections."
+ " Please set BZR_SSH environment variable.")
+
+
class GhostRevisionUnusableHere(BzrError):
_fmt = "Ghost revision {%(revision_id)s} cannot be used here."
@@ -1789,7 +1815,8 @@
class IllegalUseOfScopeReplacer(BzrError):
- _fmt = "ScopeReplacer object %(name)r was used incorrectly: %(msg)s%(extra)s"
+ _fmt = ("ScopeReplacer object %(name)r was used incorrectly:"
+ " %(msg)s%(extra)s")
internal_error = True
@@ -1817,7 +1844,8 @@
class ImportNameCollision(BzrError):
- _fmt = "Tried to import an object to the same name as an existing object. %(name)s"
+ _fmt = ("Tried to import an object to the same name as"
+ " an existing object. %(name)s")
internal_error = True
@@ -1874,7 +1902,8 @@
class TagsNotSupported(BzrError):
- _fmt = "Tags not supported by %(branch)s; you may be able to use bzr upgrade."
+ _fmt = ("Tags not supported by %(branch)s;"
+ " you may be able to use bzr upgrade.")
def __init__(self, branch):
self.branch = branch
=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py 2007-03-08 22:08:08 +0000
+++ b/bzrlib/tests/__init__.py 2007-03-09 00:45:24 +0000
@@ -1981,6 +1981,7 @@
'bzrlib.tests.test_smart_add',
'bzrlib.tests.test_smart_transport',
'bzrlib.tests.test_source',
+ 'bzrlib.tests.test_ssh_transport',
'bzrlib.tests.test_status',
'bzrlib.tests.test_store',
'bzrlib.tests.test_subsume',
=== modified file 'bzrlib/tests/blackbox/test_pull.py'
--- a/bzrlib/tests/blackbox/test_pull.py 2007-02-25 11:49:16 +0000
+++ b/bzrlib/tests/blackbox/test_pull.py 2007-03-02 12:22:00 +0000
@@ -260,7 +260,8 @@
tree_b.commit('commit d')
out = self.runbzr('pull ../branch_a', retcode=3)
self.assertEquals(out,
- ('','bzr: ERROR: These branches have diverged. Use the merge command to reconcile them.\n'))
+ ('','bzr: ERROR: These branches have diverged.'
+ ' Use the merge command to reconcile them.\n'))
self.assertEquals(branch_b.get_parent(), parent)
# test implicit --remember after resolving previous failure
uncommit(branch=branch_b, tree=tree_b)
=== modified file 'bzrlib/tests/test_sftp_transport.py'
--- a/bzrlib/tests/test_sftp_transport.py 2007-01-02 19:35:19 +0000
+++ b/bzrlib/tests/test_sftp_transport.py 2007-03-02 15:38:43 +0000
@@ -1,4 +1,5 @@
-# Copyright (C) 2005 Robey Pointer <robey at lag.net>, Canonical Ltd
+# Copyright (C) 2005 Robey Pointer <robey at lag.net>
+# Copyright (C) 2005, 2006, 2007 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -205,16 +206,16 @@
"""Test that if no 'ssh' is available we get builtin paramiko"""
from bzrlib.transport import ssh
# set '.' as the only location in the path, forcing no 'ssh' to exist
- orig_vendor = ssh._ssh_vendor
+ orig_vendor = ssh._ssh_vendor_manager._cached_ssh_vendor
orig_path = set_or_unset_env('PATH', '.')
try:
# No vendor defined yet, query for one
- ssh._ssh_vendor = None
+ ssh._ssh_vendor_manager.clear_cache()
vendor = ssh._get_ssh_vendor()
self.assertIsInstance(vendor, ssh.ParamikoVendor)
finally:
set_or_unset_env('PATH', orig_path)
- ssh._ssh_vendor = orig_vendor
+ ssh._ssh_vendor_manager._cached_ssh_vendor = orig_vendor
def test_abspath_root_sibling_server(self):
from bzrlib.transport.sftp import SFTPSiblingAbsoluteServer
@@ -338,15 +339,15 @@
s.bind(('localhost', 0))
self.bogus_url = 'sftp://%s:%s/' % s.getsockname()
- orig_vendor = bzrlib.transport.ssh._ssh_vendor
+ orig_vendor = bzrlib.transport.ssh._ssh_vendor_manager._cached_ssh_vendor
def reset():
- bzrlib.transport.ssh._ssh_vendor = orig_vendor
+ bzrlib.transport.ssh._ssh_vendor_manager._cached_ssh_vendor = orig_vendor
s.close()
self.addCleanup(reset)
def set_vendor(self, vendor):
import bzrlib.transport.ssh
- bzrlib.transport.ssh._ssh_vendor = vendor
+ bzrlib.transport.ssh._ssh_vendor_manager._cached_ssh_vendor = vendor
def test_bad_connection_paramiko(self):
"""Test that a real connection attempt raises the right error"""
=== modified file 'bzrlib/transport/sftp.py'
--- a/bzrlib/transport/sftp.py 2007-01-02 19:35:19 +0000
+++ b/bzrlib/transport/sftp.py 2007-03-07 13:20:25 +0000
@@ -1,5 +1,5 @@
# Copyright (C) 2005 Robey Pointer <robey at lag.net>
-# Copyright (C) 2005, 2006 Canonical Ltd
+# Copyright (C) 2005, 2006, 2007 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -1069,8 +1069,8 @@
event.wait(5.0)
def setUp(self):
- self._original_vendor = ssh._ssh_vendor
- ssh._ssh_vendor = self._vendor
+ self._original_vendor = ssh._ssh_vendor_manager._cached_ssh_vendor
+ ssh._ssh_vendor_manager._cached_ssh_vendor = self._vendor
if sys.platform == 'win32':
# Win32 needs to use the UNICODE api
self._homedir = getcwd()
@@ -1089,7 +1089,7 @@
def tearDown(self):
"""See bzrlib.transport.Server.tearDown."""
self._listener.stop()
- ssh._ssh_vendor = self._original_vendor
+ ssh._ssh_vendor_manager._cached_ssh_vendor = self._original_vendor
def get_bogus_url(self):
"""See bzrlib.transport.Server.get_bogus_url."""
=== modified file 'bzrlib/transport/ssh.py'
--- a/bzrlib/transport/ssh.py 2006-11-17 03:57:45 +0000
+++ b/bzrlib/transport/ssh.py 2007-03-07 13:47:47 +0000
@@ -1,5 +1,5 @@
# Copyright (C) 2005 Robey Pointer <robey at lag.net>
-# Copyright (C) 2005, 2006 Canonical Ltd
+# Copyright (C) 2005, 2006, 2007 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
from bzrlib.errors import (ConnectionError,
ParamikoNotPresent,
SocketConnectionError,
+ SSHVendorNotFound,
TransportError,
UnknownSSH,
)
@@ -58,55 +59,110 @@
# connect to an agent if we are on win32 and using Paramiko older than 1.6
_use_ssh_agent = (sys.platform != 'win32' or _paramiko_version >= (1, 6, 0))
-_ssh_vendors = {}
-
-def register_ssh_vendor(name, vendor):
- """Register SSH vendor."""
- _ssh_vendors[name] = vendor
-
-
-_ssh_vendor = None
-def _get_ssh_vendor():
- """Find out what version of SSH is on the system."""
- global _ssh_vendor
- if _ssh_vendor is not None:
- return _ssh_vendor
-
- if 'BZR_SSH' in os.environ:
- vendor_name = os.environ['BZR_SSH']
+
+class SSHVendorManager(object):
+ """Manager for manage SSH vendors."""
+
+ # Note, although at first sign the class interface seems similar to
+ # bzrlib.registry.Registry it is not possible/convenient to directly use
+ # the Registry because the class just has "get()" interface instead of the
+ # Registry's "get(key)".
+
+ def __init__(self):
+ self._ssh_vendors = {}
+ self._cached_ssh_vendor = None
+ self._default_ssh_vendor = None
+
+ def register_default_vendor(self, vendor):
+ """Register default SSH vendor."""
+ self._default_ssh_vendor = vendor
+
+ def register_vendor(self, name, vendor):
+ """Register new SSH vendor by name."""
+ self._ssh_vendors[name] = vendor
+
+ def clear_cache(self):
+ """Clear previously cached lookup result."""
+ self._cached_ssh_vendor = None
+
+ def _get_vendor_by_environment(self, environment=None):
+ """Return the vendor or None based on BZR_SSH environment variable.
+
+ :raises UnknownSSH: if the BZR_SSH environment variable contains
+ unknown vendor name
+ """
+ if environment is None:
+ environment = os.environ
+ if 'BZR_SSH' in environment:
+ vendor_name = environment['BZR_SSH']
+ try:
+ vendor = self._ssh_vendors[vendor_name]
+ except KeyError:
+ raise UnknownSSH(vendor_name)
+ return vendor
+ return None
+
+ def _get_ssh_version_string(self, args):
+ """Return SSH version string from the subprocess."""
try:
- _ssh_vendor = _ssh_vendors[vendor_name]
- except KeyError:
- raise UnknownSSH(vendor_name)
- return _ssh_vendor
-
- try:
- p = subprocess.Popen(['ssh', '-V'],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- **os_specific_subprocess_params())
- returncode = p.returncode
- stdout, stderr = p.communicate()
- except OSError:
- returncode = -1
- stdout = stderr = ''
- if 'OpenSSH' in stderr:
- mutter('ssh implementation is OpenSSH')
- _ssh_vendor = OpenSSHSubprocessVendor()
- elif 'SSH Secure Shell' in stderr:
- mutter('ssh implementation is SSH Corp.')
- _ssh_vendor = SSHCorpSubprocessVendor()
-
- if _ssh_vendor is not None:
- return _ssh_vendor
-
- # XXX: 20051123 jamesh
- # A check for putty's plink or lsh would go here.
-
- mutter('falling back to paramiko implementation')
- _ssh_vendor = ParamikoVendor()
- return _ssh_vendor
+ p = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ **os_specific_subprocess_params())
+ stdout, stderr = p.communicate()
+ except OSError:
+ stdout = stderr = ''
+ return stdout + stderr
+
+ def _get_vendor_by_version_string(self, version):
+ """Return the vendor or None based on output from the subprocess.
+
+ :param version: The output of 'ssh -V' like command.
+ """
+ vendor = None
+ if 'OpenSSH' in version:
+ mutter('ssh implementation is OpenSSH')
+ vendor = OpenSSHSubprocessVendor()
+ elif 'SSH Secure Shell' in version:
+ mutter('ssh implementation is SSH Corp.')
+ vendor = SSHCorpSubprocessVendor()
+ elif 'plink' in version:
+ mutter("ssh implementation is Putty's plink.")
+ vendor = PLinkSubprocessVendor()
+ return vendor
+
+ def _get_vendor_by_inspection(self):
+ """Return the vendor or None by checking for known SSH implementations."""
+ for args in [['ssh', '-V'], ['plink', '-V']]:
+ version = self._get_ssh_version_string(args)
+ vendor = self._get_vendor_by_version_string(version)
+ if vendor is not None:
+ return vendor
+ return None
+
+ def get_vendor(self, environment=None):
+ """Find out what version of SSH is on the system.
+
+ :raises SSHVendorNotFound: if no any SSH vendor is found
+ :raises UnknownSSH: if the BZR_SSH environment variable contains
+ unknown vendor name
+ """
+ if self._cached_ssh_vendor is None:
+ vendor = self._get_vendor_by_environment(environment)
+ if vendor is None:
+ vendor = self._get_vendor_by_inspection()
+ if vendor is None:
+ mutter('falling back to default implementation')
+ vendor = self._default_ssh_vendor
+ if vendor is None:
+ raise SSHVendorNotFound()
+ self._cached_ssh_vendor = vendor
+ return self._cached_ssh_vendor
+
+_ssh_vendor_manager = SSHVendorManager()
+_get_ssh_vendor = _ssh_vendor_manager.get_vendor
+register_default_ssh_vendor = _ssh_vendor_manager.register_default_vendor
+register_ssh_vendor = _ssh_vendor_manager.register_vendor
def _ignore_sigint():
@@ -115,7 +171,6 @@
# <https://launchpad.net/products/bzr/+bug/41433/+index>
import signal
signal.signal(signal.SIGINT, signal.SIG_IGN)
-
class LoopbackSFTP(object):
@@ -263,7 +318,11 @@
msg='Unable to invoke remote bzr')
if paramiko is not None:
- register_ssh_vendor('paramiko', ParamikoVendor())
+ vendor = ParamikoVendor()
+ register_ssh_vendor('paramiko', vendor)
+ register_ssh_vendor('none', vendor)
+ register_default_ssh_vendor(vendor)
+ del vendor
class SubprocessVendor(SSHVendor):
@@ -315,8 +374,6 @@
"""
raise NotImplementedError(self._get_vendor_specific_argv)
-register_ssh_vendor('none', ParamikoVendor())
-
class OpenSSHSubprocessVendor(SubprocessVendor):
"""SSH vendor that uses the 'ssh' executable from OpenSSH."""
@@ -369,6 +426,30 @@
register_ssh_vendor('ssh', SSHCorpSubprocessVendor())
+class PLinkSubprocessVendor(SubprocessVendor):
+ """SSH vendor that uses the 'plink' executable from Putty."""
+
+ def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
+ command=None):
+ assert subsystem is not None or command is not None, (
+ 'Must specify a command or subsystem')
+ if subsystem is not None:
+ assert command is None, (
+ 'subsystem and command are mutually exclusive')
+ args = ['plink', '-x', '-a', '-ssh', '-2']
+ if port is not None:
+ args.extend(['-P', str(port)])
+ if username is not None:
+ args.extend(['-l', username])
+ if subsystem is not None:
+ args.extend(['-s', host, subsystem])
+ else:
+ args.extend([host] + command)
+ return args
+
+register_ssh_vendor('plink', PLinkSubprocessVendor())
+
+
def _paramiko_auth(username, password, host, paramiko_transport):
# paramiko requires a username, but it might be none if nothing was supplied
# use the local username, just in case.
More information about the bazaar-commits
mailing list