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'
_______________________________________________ Mailing list: https://launchpad.net/~duplicity-team Post to : [email protected] Unsubscribe : https://launchpad.net/~duplicity-team More help : https://help.launchpad.net/ListHelp

