[MERGE] Support for Putty SSH implementation
Dmitry Vasiliev
dima at hlabs.spb.ru
Tue Jan 9 12:47:05 GMT 2007
Andrew Bennetts wrote:
> Dmitry Vasiliev wrote:
>> + def test_default_vendor(self):
>> + manager = TestSSHVendorManager()
>> + self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
>> + manager.register_vendor("default", "VENDOR")
>> + self.assertEqual(manager.get_vendor({}), "VENDOR")
>
> An explicit comment or docstring in the test stating that "default" as a vendor
> name is treated specially would be good. It's not hard to guess, but making the
> intent explicit is good, in case someone guesses incorrectly :)
I think it would be even better to add manager.register_default_vendor() method.
>> + def test_cached_vendor(self):
>> + manager = TestSSHVendorManager()
>> + self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
>> + manager.register_vendor("vendor", "VENDOR")
>> + self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
>> + self.assertEqual(manager.get_vendor({"BZR_SSH": "vendor"}), "VENDOR")
>> + self.assertEqual(manager.get_vendor({}), "VENDOR")
>
> The intented behaviour here isn't immediately obvious to me. Why should
> manager.get_vendor({"BZR_SSH": "vendor"}) change the result of a later
> manager.get_vendor({}) call?
>
> Basically, I'd like to see a comment (or docstring) in this test explain the
> intent. Something like "Once SSHVendorManager.get_vendor successfully returns
> a vendor, later invocations will return the same vendor, regardless of changes
> to the environment.", but that doesn't sound quite right to me. How you would
> explain this behaviour, and the reason for it?
Actually, I didn't change the behaviour in this case. I think the vendor
caching can speed up ssh vendor lookup for bzr as an utility but it can be
slightly bad idea for bzrlib as a library. The one possibly solution can be to
add clear_cache() method to the manager interface.
> I'd also be tempted to use something other than "VENDOR" here, e.g. "vendor =
> object()", so that you can add "self.assertIdentical(vendor,
> manager.get_vendor(...))", i.e. demonstrate that it's the exact same object.
> (You could technically do it with the string object, but string identity is
> mildly confusing due to interning, so using an object would be better.)
Good idea, I'll change it.
>> + def test_vendor_getting_methods_precedence(self):
>> + manager = TestSSHVendorManager()
>> + self.assertRaises(SSHVendorNotFound, manager.get_vendor, {})
>> +
>> + manager.register_vendor("default", "DEFAULT")
>> + self.assertEqual(manager.get_vendor({}), "DEFAULT")
>> +
>> + manager.ssh_vendor = None
>> + manager.set_ssh_version_string("OpenSSH")
>> + self.assertIsInstance(manager.get_vendor({}), OpenSSHSubprocessVendor)
>> +
>> + manager.ssh_vendor = None
>> + manager.register_vendor("vendor", "VENDOR")
>> + self.assertEqual(manager.get_vendor({"BZR_SSH": "vendor"}), "VENDOR")
>> +
>> + self.assertEqual(manager.get_vendor({}), "VENDOR")
>
> This test could use some comments as well. What's the "precendence" you're
> testing? Peeking at the implementation, I guess this is testing the order that
> get_vendor attempts to choose a vendor, i.e. by environment variable, by
> inspection, by default, and finally by giving up. Say so explicitly in the test
> (including naming the stages in the test)!
Ok, I'll add some comments to the test.
> Also, the purpose of the "manager.ssh_vendor = None" lines probably deserves a
> comment explaining that this is clearing the cache. Perhaps it would be clearer
> to create a fresh TestSSHVendorManager each time, although you'd have to repeat
> some registrations, so perhaps not.
>
> I'm mildly surprised that "ssh_vendor" isn't "_ssh_vendor", it doesn't look like
> something that most code ought to be mucking with.
Agreed, I'll add clear_cache() method and rename 'ssh_vendor' into '_ssh_vendor'.
>> + * Added support for Putty's SSH implementation. (Dmitry Vasiliev)
>> +
>
> You're being overly modest. You've done a nice cleanup of SSH vendor
> registration too! :) (although mentioning that probably belongs under the
> INTERNALS heading).
No problem for me to add some note under the INTERNALS. :-)
> bzrlib/errors.py
> ----------------
>
> [...]
>> +class SSHVendorNotFound(BzrError):
>> +
>> + _fmt = "Don't know how to handle SSH connections. Please set BZR_SSH environment variable."
>
> Lines should have no more than 79 characters (PEP 8).
It seems the errors.py doesn't strictly follow this rule. ;-)
I'll wrap all long lines in the error.py.
> bzrlib/transport/ssh.py
> ------------------------
>
> [...]
>> SocketConnectionError,
>> TransportError,
>> UnknownSSH,
>> + SSHVendorNotFound,
>> )
>
> I don't think this convention is documented anywhere (I don't see it in HACKING
> or PEP 8), but the other names being imported from bzrlib.errors there are in
> alphabetical order; I think it would be good to keep that ordering.
Ok, I'll fix it.
[SKIP]
>> +class SSHVendorManager(object):
>> + """Manager for manage SSH vendors."""
>
> Hmm, it's tempting to making this a bzrlib.registry.Registry, until you realise
> that the interface doesn't quite fit; instead of "get(key)" this essentially
> just has "get()", which chooses the best registered vendor for the current
> environment (I mean environment in the broader sense, not just the "os.environ"
> sense).
>
> It may be worth adding a brief comment highlighting this fundamental difference,
> to stop people (like me) from chasing foolish consistency by trying reuse
> Registry here :)
Ok
> [...]
>> + def _get_vendor_by_environment(self, environment=None):
[SKIP]
>
> A comment in English explaining the intent would be good. Even though it's not
> hard to reverse-engineer the intent from the current implementation, code
> evolves, and intent that seemed clear will get obscured unless it's stated
> explicitly.
>
> So, add a comment saying "If the user has set the BZR_SSH environment variable,
> use that to choose the vendor."
Agreed
>> + def _get_vendor_by_version_string(self, version):
[SKIP]
> A docstring mentioning that "version" is expected to be the output of executing
> a command like 'ssh -V' would be good.
>
>> + def get_vendor(self, environment=None):
[SKIP]
> The docstring for get_vendor ought to mention that it can raise
> SSHVendorNotFound or UnknownSSH.
I'll add docstrings.
--
Dmitry Vasiliev (dima at hlabs.spb.ru)
http://hlabs.spb.ru
More information about the bazaar
mailing list