Rev 2420: (Andrew Bennetts) Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch). in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Mon Apr 16 09:02:57 BST 2007


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 2420
revision-id: pqm at pqm.ubuntu.com-20070416080254-bf3rfk77k5bgfdl7
parent: pqm at pqm.ubuntu.com-20070416054515-up6lmy0v3elcru80
parent: andrew.bennetts at canonical.com-20070416052458-t2shorpdp62yodh3
committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Mon 2007-04-16 09:02:54 +0100
message:
  (Andrew Bennetts) Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch).
added:
  bzrlib/smart/client.py         client.py-20061116014825-2k6ada6xgulslami-1
modified:
  bzrlib/tests/test_smart_transport.py test_ssh_transport.py-20060608202016-c25gvf1ob7ypbus6-2
  bzrlib/tests/test_transport_implementations.py test_transport_implementations.py-20051227111451-f97c5c7d5c49fce7
  bzrlib/transport/__init__.py   transport.py-20050711165921-4978aa7ce1285ad5
  bzrlib/transport/http/__init__.py http_transport.py-20050711212304-506c5fd1059ace96
  bzrlib/transport/memory.py     memory.py-20051016101338-cd008dbdf69f04fc
    ------------------------------------------------------------
    revno: 2414.1.3
    merged: andrew.bennetts at canonical.com-20070416052458-t2shorpdp62yodh3
    parent: andrew.bennetts at canonical.com-20070416024404-3wwt2juvu7ai8nvn
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: hpss-miscellany-2
    timestamp: Mon 2007-04-16 15:24:58 +1000
    message:
      Fix test failures under 'python2.4 -O' ('python2.5 -O' was already passing).
    ------------------------------------------------------------
    revno: 2414.1.2
    merged: andrew.bennetts at canonical.com-20070416024404-3wwt2juvu7ai8nvn
    parent: andrew.bennetts at canonical.com-20070413075917-jlvhfi0s607smiu6
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: hpss-miscellany-2
    timestamp: Mon 2007-04-16 12:44:04 +1000
    message:
      Deal with review comments.
    ------------------------------------------------------------
    revno: 2414.1.1
    merged: andrew.bennetts at canonical.com-20070413075917-jlvhfi0s607smiu6
    parent: pqm at pqm.ubuntu.com-20070413050623-10v4wozs1tu04kcu
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: hpss-miscellany-2
    timestamp: Fri 2007-04-13 17:59:17 +1000
    message:
      Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch).
=== added file 'bzrlib/smart/client.py'
--- a/bzrlib/smart/client.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/smart/client.py	2007-04-16 02:44:04 +0000
@@ -0,0 +1,68 @@
+# Copyright (C) 2006 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 urlparse import urlparse
+
+from bzrlib.smart import protocol
+from bzrlib.urlutils import unescape
+
+
+class SmartClient(object):
+
+    def __init__(self, medium):
+        self._medium = medium
+
+    def call(self, method, *args):
+        """Call a method on the remote server."""
+        result, protocol = self.call_expecting_body(method, *args)
+        protocol.cancel_read_body()
+        return result
+
+    def call_expecting_body(self, method, *args):
+        """Call a method and return the result and the protocol object.
+        
+        The body can be read like so::
+
+            result, smart_protocol = smart_client.call_expecting_body(...)
+            body = smart_protocol.read_body_bytes()
+        """
+        request = self._medium.get_request()
+        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
+        smart_protocol.call(method, *args)
+        return smart_protocol.read_response_tuple(expect_body=True), smart_protocol
+
+    def call_with_body_bytes(self, method, args, body):
+        """Call a method on the remote server with body bytes."""
+        if type(method) is not str:
+            raise TypeError('method must be a byte string, not %r' % (method,))
+        for arg in args:
+            if type(arg) is not str:
+                raise TypeError('args must be byte strings, not %r' % (args,))
+        if type(body) is not str:
+            raise TypeError('body must be byte string, not %r' % (body,))
+        request = self._medium.get_request()
+        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
+        smart_protocol.call_with_body_bytes((method, ) + args, body)
+        return smart_protocol.read_response_tuple()
+
+    def remote_path_from_transport(self, transport):
+        """Convert transport into a path suitable for using in a request.
+        
+        Note that the resulting remote path doesn't encode the host name or
+        anything but path, so it is only safe to use it in requests sent over
+        the medium from the matching transport.
+        """
+        return unescape(urlparse(transport.base)[2]).encode('utf8')

=== modified file 'bzrlib/tests/test_smart_transport.py'
--- a/bzrlib/tests/test_smart_transport.py	2007-04-13 05:15:36 +0000
+++ b/bzrlib/tests/test_smart_transport.py	2007-04-16 08:02:54 +0000
@@ -31,6 +31,7 @@
         urlutils,
         )
 from bzrlib.smart import (
+        client,
         medium,
         protocol,
         request,
@@ -1451,6 +1452,43 @@
             errors.ReadingCompleted, smart_protocol.read_body_bytes)
 
 
+class TestSmartClientUnicode(tests.TestCase):
+    """SmartClient tests for unicode arguments.
+
+    Unicode arguments to call_with_body_bytes are not correct (remote method
+    names, arguments, and bodies must all be expressed as byte strings), but
+    SmartClient should gracefully reject them, rather than getting into a broken
+    state that prevents future correct calls from working.  That is, it should
+    be possible to issue more requests on the medium afterwards, rather than
+    allowing one bad call to call_with_body_bytes to cause later calls to
+    mysteriously fail with TooManyConcurrentRequests.
+    """
+
+    def assertCallDoesNotBreakMedium(self, method, args, body):
+        """Call a medium with the given method, args and body, then assert that
+        the medium is left in a sane state, i.e. is capable of allowing further
+        requests.
+        """
+        input = StringIO("\n")
+        output = StringIO()
+        client_medium = medium.SmartSimplePipesClientMedium(input, output)
+        smart_client = client.SmartClient(client_medium)
+        self.assertRaises(TypeError,
+            smart_client.call_with_body_bytes, method, args, body)
+        self.assertEqual("", output.getvalue())
+        self.assertEqual(None, client_medium._current_request)
+
+    def test_call_with_body_bytes_unicode_method(self):
+        self.assertCallDoesNotBreakMedium(u'method', ('args',), 'body')
+
+    def test_call_with_body_bytes_unicode_args(self):
+        self.assertCallDoesNotBreakMedium('method', (u'args',), 'body')
+        self.assertCallDoesNotBreakMedium('method', ('arg1', u'arg2'), 'body')
+
+    def test_call_with_body_bytes_unicode_body(self):
+        self.assertCallDoesNotBreakMedium('method', ('args',), u'body')
+
+
 class LengthPrefixedBodyDecoder(tests.TestCase):
 
     # XXX: TODO: make accept_reading_trailer invoke translate_response or 

=== modified file 'bzrlib/tests/test_transport_implementations.py'
--- a/bzrlib/tests/test_transport_implementations.py	2007-04-11 02:01:18 +0000
+++ b/bzrlib/tests/test_transport_implementations.py	2007-04-16 02:44:04 +0000
@@ -22,6 +22,7 @@
 
 import os
 from cStringIO import StringIO
+from StringIO import StringIO as pyStringIO
 import stat
 import sys
 
@@ -35,10 +36,10 @@
                            TransportNotPossible, ConnectionError,
                            InvalidURL)
 from bzrlib.osutils import getcwd
+from bzrlib.smart import medium
 from bzrlib.symbol_versioning import zero_eleven
 from bzrlib.tests import TestCaseInTempDir, TestSkipped
 from bzrlib.tests.test_transport import TestTransportImplementation
-from bzrlib.smart import medium
 from bzrlib.transport import memory, remote
 import bzrlib.transport
 
@@ -369,6 +370,34 @@
                               dir_mode=0777, create_parent_dir=True)
         self.assertTransportMode(t, 'dir777', 0777)
 
+    def test_put_bytes_unicode(self):
+        # Expect put_bytes to raise AssertionError or UnicodeEncodeError if
+        # given unicode "bytes".  UnicodeEncodeError doesn't really make sense
+        # (we don't want to encode unicode here at all, callers should be
+        # strictly passing bytes to put_bytes), but we allow it for backwards
+        # compatibility.  At some point we should use a specific exception.
+        # See https://bugs.launchpad.net/bzr/+bug/106898.
+        t = self.get_transport()
+        if t.is_readonly():
+            return
+        unicode_string = u'\u1234'
+        self.assertRaises(
+            (AssertionError, UnicodeEncodeError),
+            t.put_bytes, 'foo', unicode_string)
+
+    def test_put_file_unicode(self):
+        # Like put_bytes, except with a StringIO.StringIO of a unicode string.
+        # This situation can happen (and has) if code is careless about the type
+        # of "string" they initialise/write to a StringIO with.  We cannot use
+        # cStringIO, because it never returns unicode from read.
+        # Like put_bytes, UnicodeEncodeError isn't quite the right exception to
+        # raise, but we raise it for hysterical raisins.
+        t = self.get_transport()
+        if t.is_readonly():
+            return
+        unicode_file = pyStringIO(u'\u1234')
+        self.assertRaises(UnicodeEncodeError, t.put_file, 'foo', unicode_file)
+
     def test_put_multi(self):
         t = self.get_transport()
 

=== modified file 'bzrlib/transport/__init__.py'
--- a/bzrlib/transport/__init__.py	2007-04-13 05:15:36 +0000
+++ b/bzrlib/transport/__init__.py	2007-04-16 08:02:54 +0000
@@ -618,8 +618,9 @@
         :param mode: Create the file with the given mode.
         :return: None
         """
-        assert isinstance(bytes, str), \
-            'bytes must be a plain string, not %s' % type(bytes)
+        if not isinstance(bytes, str):
+            raise AssertionError(
+                'bytes must be a plain string, not %s' % type(bytes))
         return self.put_file(relpath, StringIO(bytes), mode=mode)
 
     def put_bytes_non_atomic(self, relpath, bytes, mode=None,
@@ -640,8 +641,9 @@
                         create it, and then try again.
         :param dir_mode: Possible access permissions for new directories.
         """
-        assert isinstance(bytes, str), \
-            'bytes must be a plain string, not %s' % type(bytes)
+        if not isinstance(bytes, str):
+            raise AssertionError(
+                'bytes must be a plain string, not %s' % type(bytes))
         self.put_file_non_atomic(relpath, StringIO(bytes), mode=mode,
                                  create_parent_dir=create_parent_dir,
                                  dir_mode=dir_mode)
@@ -1243,8 +1245,6 @@
 register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
 register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
 register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
-register_lazy_transport('chroot+', 'bzrlib.transport.chroot',
-                        'ChrootTransportDecorator')
 register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
 register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
 register_lazy_transport('vfat+',

=== modified file 'bzrlib/transport/http/__init__.py'
--- a/bzrlib/transport/http/__init__.py	2007-04-09 04:49:55 +0000
+++ b/bzrlib/transport/http/__init__.py	2007-04-13 07:59:17 +0000
@@ -508,8 +508,8 @@
 class SmartClientHTTPMediumRequest(medium.SmartClientMediumRequest):
     """A SmartClientMediumRequest that works with an HTTP medium."""
 
-    def __init__(self, smart_medium):
-        medium.SmartClientMediumRequest.__init__(self, smart_medium)
+    def __init__(self, client_medium):
+        medium.SmartClientMediumRequest.__init__(self, client_medium)
         self._buffer = ''
 
     def _accept_bytes(self, bytes):

=== modified file 'bzrlib/transport/memory.py'
--- a/bzrlib/transport/memory.py	2007-03-14 16:57:31 +0000
+++ b/bzrlib/transport/memory.py	2007-04-13 07:59:17 +0000
@@ -128,7 +128,14 @@
         """See Transport.put_file()."""
         _abspath = self._abspath(relpath)
         self._check_parent(_abspath)
-        self._files[_abspath] = (f.read(), mode)
+        bytes = f.read()
+        if type(bytes) is not str:
+            # Although not strictly correct, we raise UnicodeEncodeError to be
+            # compatible with other transports.
+            raise UnicodeEncodeError(
+                'undefined', bytes, 0, 1,
+                'put_file must be given a file of bytes, not unicode.')
+        self._files[_abspath] = (bytes, mode)
 
     def mkdir(self, relpath, mode=None):
         """See Transport.mkdir()."""




More information about the bazaar-commits mailing list