← Back to team overview

duplicity-team team mailing list archive

[Merge] lp:~mterry/duplicity/unicode-decode into lp:duplicity

 

Kenneth Loafman has proposed merging lp:~mterry/duplicity/unicode-decode into lp:duplicity.

Requested reviews:
  duplicity-team (duplicity-team)
Related bugs:
  Bug #989496 in Duplicity: "UnicodeDecodeError during backup due to non-utf8 translation"
  https://bugs.launchpad.net/duplicity/+bug/989496

For more details, see:
https://code.launchpad.net/~mterry/duplicity/unicode-decode/+merge/133812

always load po files as utf8

-- 
https://code.launchpad.net/~mterry/duplicity/unicode-decode/+merge/133812
Your team duplicity-team is requested to review the proposed merge of lp:~mterry/duplicity/unicode-decode into lp:duplicity.
=== modified file 'bin/duplicity'
=== modified file 'duplicity/backends/u1backend.py'
--- duplicity/backends/u1backend.py	2012-11-03 22:12:45 +0000
+++ duplicity/backends/u1backend.py	2012-11-11 01:45:26 +0000
@@ -19,6 +19,7 @@
 # You should have received a copy of the GNU General Public License
 # along with duplicity.  If not, see <http://www.gnu.org/licenses/>.
 
+import gettext
 import duplicity.backend
 from duplicity.errors import BackendException
 from duplicity import log
@@ -144,6 +145,7 @@
 
         self.volume_uri = "%s/volumes/~/%s" % (self.api_base, path)
         self.meta_base = "%s/~/%s/" % (self.api_base, path)
+<<<<<<< TREE
 
         self.client=OAuthHttpClient();
 
@@ -172,12 +174,129 @@
         resp, content = self.client.request(self.volume_uri,ignore=[404])
         if resp['status']=='404':
             resp, content = self.client.request(self.volume_uri,"PUT")
+=======
+        # This next line *should* work, but isn't set up correctly server-side yet
+        #self.content_base = self.api_base
+        self.content_base = "https://files.%s"; % self.parsed_url.hostname
+
+        ensure_dbus()
+
+        if not self.login():
+            from duplicity import log
+            log.FatalError(_("Could not obtain Ubuntu One credentials"),
+                           log.ErrorCode.backend_error)
+
+        # Create volume in case it doesn't exist yet
+        self.create_volume()
+
+    def login(self):
+        from gobject import MainLoop
+        from dbus.mainloop.glib import DBusGMainLoop
+        from ubuntuone.platform.credentials import CredentialsManagementTool
+        gettext.install('duplicity', codeset='utf8')  # workaround LP: #1050061
+
+        self.login_success = False
+
+        DBusGMainLoop(set_as_default=True)
+        loop = MainLoop()
+
+        def quit(result):
+            loop.quit()
+            if result:
+                self.login_success = True
+
+        cd = CredentialsManagementTool()
+        d = cd.login()
+        d.addCallbacks(quit)
+        loop.run()
+        return self.login_success
+>>>>>>> MERGE-SOURCE
 
     def quote(self, url):
+<<<<<<< TREE
         return urllib.quote(url, safe="/~").replace(" ","%20")
 
     def put(self, source_path, remote_filename = None):
+=======
+        import urllib
+        return urllib.quote(url, safe="/~")
+
+    def parse_error(self, headers, ignore=None):
+        from duplicity import log
+
+        status = int(headers[0].get('status'))
+        if status >= 200 and status < 300:
+            return None
+
+        if ignore and status in ignore:
+            return None
+
+        if status == 400:
+            code = log.ErrorCode.backend_permission_denied
+        elif status == 404:
+            code = log.ErrorCode.backend_not_found
+        elif status == 507:
+            code = log.ErrorCode.backend_no_space
+        else:
+            code = log.ErrorCode.backend_error
+        return code
+
+    def handle_error(self, raise_error, op, headers, file1=None, file2=None, ignore=None):
+        from duplicity import log
+        from duplicity import util
+        import json
+
+        code = self.parse_error(headers, ignore)
+        if code is None:
+            return
+
+        status = int(headers[0].get('status'))
+
+        if file1:
+            file1 = file1.encode("utf8")
+        else:
+            file1 = None
+        if file2:
+            file2 = file2.encode("utf8")
+        else:
+            file2 = None
+        extra = ' '.join([util.escape(x) for x in [file1, file2] if x])
+        extra = ' '.join([op, extra])
+        msg = _("Got status code %s") % status
+        if headers[0].get('x-oops-id') is not None:
+            msg += '\nOops-ID: %s' % headers[0].get('x-oops-id')
+        if headers[0].get('content-type') == 'application/json':
+            node = json.loads(headers[1])
+            if node.get('error'):
+                msg = node.get('error')
+
+        if raise_error:
+            if status == 503:
+                raise TemporaryLoadException(msg)
+            else:
+                raise BackendException(msg)
+        else:
+            log.FatalError(msg, code, extra)
+
+    @retry
+    def create_volume(self, raise_errors=False):
+        import ubuntuone.couch.auth as auth
+        gettext.install('duplicity', codeset='utf8')  # workaround LP: #1050061
+        
+        answer = auth.request(self.volume_uri, http_method="PUT")
+        self.handle_error(raise_errors, 'put', answer, self.volume_uri)
+
+    @retry
+    def put(self, source_path, remote_filename = None, raise_errors=False):
+>>>>>>> MERGE-SOURCE
         """Copy file to remote"""
+<<<<<<< TREE
+=======
+        import json
+        import mimetypes
+        import ubuntuone.couch.auth as auth
+        gettext.install('duplicity', codeset='utf8')  # workaround LP: #1050061
+>>>>>>> MERGE-SOURCE
         if not remote_filename:
             remote_filename = source_path.get_filename()
         remote_full = self.meta_base + self.quote(remote_filename)
@@ -211,8 +330,14 @@
 
     def get(self, filename, local_path):
         """Get file and put in local_path (Path object)"""
+<<<<<<< TREE
 
         # get with path returns content_path
+=======
+        import json
+        import ubuntuone.couch.auth as auth
+        gettext.install('duplicity', codeset='utf8')  # workaround LP: #1050061
+>>>>>>> MERGE-SOURCE
         remote_full = self.meta_base + self.quote(filename)
         resp, content = self.client.request(remote_full)
 
@@ -229,6 +354,13 @@
 
     def list(self):
         """List files in that directory"""
+<<<<<<< TREE
+=======
+        import json
+        import urllib
+        import ubuntuone.couch.auth as auth
+        gettext.install('duplicity', codeset='utf8')  # workaround LP: #1050061
+>>>>>>> MERGE-SOURCE
         remote_full = self.meta_base + "?include_children=true"
         resp, content = self.client.request(remote_full)
 
@@ -242,6 +374,11 @@
     def delete(self, filename_list):
         """Delete all files in filename list"""
         import types
+<<<<<<< TREE
+=======
+        import ubuntuone.couch.auth as auth
+        gettext.install('duplicity', codeset='utf8')  # workaround LP: #1050061
+>>>>>>> MERGE-SOURCE
         assert type(filename_list) is not types.StringType
 
         for filename in filename_list:
@@ -250,6 +387,13 @@
 
     def _query_file_info(self, filename):
         """Query attributes on filename"""
+<<<<<<< TREE
+=======
+        import json
+        from duplicity import log
+        import ubuntuone.couch.auth as auth
+        gettext.install('duplicity', codeset='utf8')  # workaround LP: #1050061
+>>>>>>> MERGE-SOURCE
         remote_full = self.meta_base + self.quote(filename)
         resp, content = self.client.request(remote_full)
 

=== added file 'testing/tests/unicode.py'
--- testing/tests/unicode.py	1970-01-01 00:00:00 +0000
+++ testing/tests/unicode.py	2012-11-11 01:45:26 +0000
@@ -0,0 +1,66 @@
+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
+#
+# Copyright 2012 Canonical Ltd
+#
+# This file is part of duplicity.
+#
+# Duplicity 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.
+#
+# Duplicity 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 duplicity; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import unittest
+from mock import patch
+
+
+class U1UnicodeTest(unittest.TestCase):
+    """Test Unicode support when using U1"""
+
+    # Patch out some bits that would cause us to actually talk to U1 or try
+    # to log in to U1 credentials
+    @patch('duplicity.backends.u1backend.U1Backend.create_volume')
+    @patch('gobject.MainLoop')
+    def test_gettext(self, vol_mock, loop_mock):
+        """Verify workaround of ubuntuone's gettext.install (LP: #1050061)"""
+        self.assertEqual(_,  _.im_self.gettext)
+        import duplicity.backend
+        import duplicity.backends.u1backend
+        try:
+            # Patch here because if we patched as a decorator, we'd
+            # accidentally import before we got to the first assertEqual, which
+            # is too early.
+            tool = 'ubuntuone.platform.credentials.CredentialsManagementTool'
+            url = duplicity.backend.ParsedUrl('u1+http://bucket/volume')
+            with patch(tool) as tool_mock:
+                # fake immediate success when adding a callback
+                tool_mock().login().addCallbacks = lambda f: f(True)
+                duplicity.backends.u1backend.U1Backend(url)
+        except ImportError:
+            self.skipTest("ubuntuone not installed")
+        self.assertEqual(_,  _.im_self.gettext)
+
+
+class UTF8Test(unittest.TestCase):
+
+    def setUp(self):
+        import sys
+        if 'duplicity' in sys.modules:
+            del(sys.modules["duplicity"])
+
+    @patch('gettext.install')
+    def test_module_install(self, inst_mock):
+        """Make sure we convert po files to utf8"""
+        import duplicity
+        inst_mock.assert_called_once_with('duplicity', codeset='utf8')
+
+if __name__ == "__main__":
+    unittest.main()

=== renamed file 'testing/tests/unicode.py' => 'testing/tests/unicode.py.moved'

Follow ups