duplicity-team team mailing list archive
-
duplicity-team team
-
Mailing list archive
-
Message #05359
[Merge] lp:~iamthefij/duplicity/b2-python3 into lp:duplicity
Ian Fijolek has proposed merging lp:~iamthefij/duplicity/b2-python3 into lp:duplicity.
Requested reviews:
duplicity-team (duplicity-team)
For more details, see:
https://code.launchpad.net/~iamthefij/duplicity/b2-python3/+merge/379251
Fix bytes/str error in b2 backend on Python 3
A nearly identical bug to #1863018 also affects the B2 backend. This is a very similar patch to the one just merged that fixes it.
The quote_plus function is removed as it no longer appears to be required. At least not with the latest b2 and in Python 3. When it is present, a FileNotFound exception is thrown because the / in the path gets double encoded.
--
Your team duplicity-team is requested to review the proposed merge of lp:~iamthefij/duplicity/b2-python3 into lp:duplicity.
=== modified file 'duplicity/backends/b2backend.py'
--- duplicity/backends/b2backend.py 2019-11-16 17:15:49 +0000
+++ duplicity/backends/b2backend.py 2020-02-15 08:26:57 +0000
@@ -32,9 +32,9 @@
import duplicity.backend
from duplicity.errors import BackendException, FatalBackendException
+from duplicity import util
from duplicity import log
from duplicity import progress
-from duplicity import util
class B2ProgressListener(object):
@@ -65,24 +65,20 @@
"""
duplicity.backend.Backend.__init__(self, parsed_url)
- global DownloadDestLocalFile, FileVersionInfoFactory
- try: # try to import the new b2sdk if available
- from b2sdk.api import B2Api
- from b2sdk.account_info import InMemoryAccountInfo
- from b2sdk.download_dest import DownloadDestLocalFile
- from b2sdk.exception import NonExistentBucket
- from b2sdk.file_version import FileVersionInfoFactory
+ # Import B2 API
+ try:
+ global b2
+ import b2
+ global b2sdk
+ import b2sdk
+ import b2.api
+ import b2.account_info
+ import b2.download_dest
+ import b2.file_version
except ImportError:
- try: # fall back to import the old b2 client
- from b2.api import B2Api
- from b2.account_info import InMemoryAccountInfo
- from b2.download_dest import DownloadDestLocalFile
- from b2.exception import NonExistentBucket
- from b2.file_version import FileVersionInfoFactory
- except ImportError:
- raise BackendException(u'B2 backend requires B2 Python SDK (pip install b2sdk)')
+ raise BackendException(u'B2 backend requires B2 Python APIs (pip install b2)')
- self.service = B2Api(InMemoryAccountInfo())
+ self.service = b2.api.B2Api(b2.account_info.InMemoryAccountInfo())
self.parsed_url.hostname = u'B2'
account_id = parsed_url.username
@@ -104,32 +100,33 @@
try:
self.bucket = self.service.get_bucket_by_name(bucket_name)
log.Log(u"Bucket found", log.INFO)
- except NonExistentBucket:
+ except b2.exception.NonExistentBucket:
try:
log.Log(u"Bucket not found, creating one", log.INFO)
self.bucket = self.service.create_bucket(bucket_name, u'allPrivate')
except:
raise FatalBackendException(u"Bucket cannot be created")
+ def _build_remote_filepath(self, remote_filename):
+ u"""Build remote filepath"""
+ return self.path + util.fsdecode(remote_filename)
+
def _get(self, remote_filename, local_path):
u"""
Download remote_filename to local_path
"""
- log.Log(u"Get: %s -> %s" % (self.path + util.fsdecode(remote_filename),
- util.fsdecode(local_path.name)),
- log.INFO)
- self.bucket.download_file_by_name(quote_plus(self.path + util.fsdecode(remote_filename), u'/'),
- DownloadDestLocalFile(util.fsdecode(local_path.name)))
+ remote_filename = self._build_remote_filepath(remote_filename)
+ log.Log(u"Get: %s -> %s" % (remote_filename, local_path.name), log.INFO)
+ self.bucket.download_file_by_name(remote_filename,
+ b2.download_dest.DownloadDestLocalFile(local_path.name))
def _put(self, source_path, remote_filename):
u"""
Copy source_path to remote_filename
"""
- log.Log(u"Put: %s -> %s" % (util.fsdecode(source_path.name),
- self.path + util.fsdecode(remote_filename)),
- log.INFO)
- self.bucket.upload_local_file(util.fsdecode(source_path.name),
- quote_plus(self.path + util.fsdecode(remote_filename), u'/'),
+ remote_filename = self._build_remote_filepath(remote_filename)
+ log.Log(u"Put: %s -> %s" % (source_path.name, remote_filename), log.INFO)
+ self.bucket.upload_local_file(source_path.name, remote_filename,
content_type=u'application/pgp-encrypted',
progress_listener=B2ProgressListener())
@@ -144,23 +141,25 @@
u"""
Delete filename from remote server
"""
- log.Log(u"Delete: %s" % self.path + util.fsdecode(filename), log.INFO)
- file_version_info = self.file_info(quote_plus(self.path + util.fsdecode(filename), u'/'))
+ filename = self._build_remote_filepath(filename)
+ log.Log(u"Delete: %s" % filename, log.INFO)
+ file_version_info = self.file_info(filename)
self.bucket.delete_file_version(file_version_info.id_, file_version_info.file_name)
def _query(self, filename):
u"""
Get size info of filename
"""
- log.Log(u"Query: %s" % self.path + util.fsdecode(filename), log.INFO)
- file_version_info = self.file_info(quote_plus(self.path + util.fsdecode(filename), u'/'))
+ filename = self._build_remote_filepath(filename)
+ log.Log(u"Query: %s" % filename, log.INFO)
+ file_version_info = self.file_info(filename)
return {u'size': file_version_info.size
if file_version_info is not None and file_version_info.size is not None else -1}
def file_info(self, filename):
- response = self.bucket.api.session.list_file_names(self.bucket.id_, filename, 1, None)
+ response = self.bucket.list_file_names(filename, 1)
for entry in response[u'files']:
- file_version_info = FileVersionInfoFactory.from_api_response(entry)
+ file_version_info = b2.file_version.FileVersionInfoFactory.from_api_response(entry)
if file_version_info.file_name == filename:
return file_version_info
raise BackendException(u'File not found')
Follow ups