This patch combines https://code.launchpad.net/~jelmer/brz/enable-gio/+merge/419150 and https://bazaar.launchpad.net/~jelmer/brz/fix-gio/revision/7570. === modified file 'breezy/transport/gio_transport.py' --- a/breezy/transport/gio_transport.py 2022-04-09 12:17:41 +0000 +++ b/breezy/transport/gio_transport.py 2022-04-09 12:33:51 +0000 @@ -52,11 +52,7 @@ from ..tests.test_server import TestServer try: - import glib -except ImportError as e: - raise errors.DependencyNotPresent('glib', e) -try: - import gio + from gi.repository import Gio as gio except ImportError as e: raise errors.DependencyNotPresent('gio', e) @@ -57,6 +57,9 @@ raise errors.DependencyNotPresent('gio', e) +from gi.repository.GLib import GError + + class GioLocalURLServer(TestServer): """A pretend server for local transports, using file:// urls. @@ -81,7 +84,7 @@ def __init__(self, transport, relpath): FileStream.__init__(self, transport, relpath) self.gio_file = transport._get_GIO(relpath) - self.stream = self.gio_file.create() + self.stream = self.gio_file.create(0, None) def _close(self): self.stream.close() @@ -90,7 +93,7 @@ try: # Using pump_string_file seems to make things crash osutils.pumpfile(BytesIO(bytes), self.stream) - except gio.Error as e: + except GError as e: # self.transport._translate_gio_error(e,self.relpath) raise errors.BzrError(str(e)) @@ -98,12 +101,12 @@ class GioStatResult(object): def __init__(self, f): - info = f.query_info('standard::size,standard::type') + info = f.query_info('standard::size,standard::type', 0, None) self.st_size = info.get_size() type = info.get_file_type() - if (type == gio.FILE_TYPE_REGULAR): + if type == gio.FileType.REGULAR: self.st_mode = stat.S_IFREG - elif type == gio.FILE_TYPE_DIRECTORY: + elif type == gio.FileType.DIRECTORY: self.st_mode = stat.S_IFDIR @@ -122,7 +125,7 @@ user, netloc = netloc.rsplit('@', 1) # Seems it is not possible to list supported backends for GIO # so a hardcoded list it is then. - gio_backends = ['dav', 'file', 'ftp', 'obex', 'sftp', 'ssh', 'smb'] + gio_backends = ['dav', 'file', 'ftp', 'obex', 'sftp', 'ssh', 'smb', 'http'] if scheme not in gio_backends: raise urlutils.InvalidURL(base, extra="GIO support is only available for " + @@ -138,13 +141,10 @@ _from_transport=_from_transport) def _relpath_to_url(self, relpath): - full_url = urlutils.join(self.url, relpath) - if isinstance(full_url, str): - raise urlutils.InvalidURL(full_url) - return full_url + return urlutils.join(self.url, relpath) def _get_GIO(self, relpath): - """Return the ftplib.GIO instance for this object.""" + """Return the GIO instance for this object.""" # Ensures that a connection is established connection = self._get_connection() if connection is None: @@ -152,7 +152,7 @@ connection, credentials = self._create_connection() self._set_connection(connection, credentials) fileurl = self._relpath_to_url(relpath) - file = gio.File(fileurl) + file = gio.File.new_for_uri(fileurl) return file def _auth_cb(self, op, message, default_user, default_domain, flags): @@ -197,7 +197,7 @@ try: obj.mount_enclosing_volume_finish(res) self.loop.quit() - except gio.Error as e: + except GError as e: self.loop.quit() raise errors.BzrError( "Failed to mount the given location: " + str(e)) @@ -209,12 +209,12 @@ user, password = credentials try: - connection = gio.File(self.url) + connection = gio.File.new_for_uri(self.url) mount = None try: mount = connection.find_enclosing_mount() - except gio.Error as e: - if (e.code == gio.ERROR_NOT_MOUNTED): + except GError as e: + if e.code == gio.IOErrorEnum.NOT_MOUNTED: self.loop = glib.MainLoop() ui.ui_factory.show_message('Mounting %s using GIO' % self.url) @@ -227,7 +227,7 @@ m = connection.mount_enclosing_volume(op, self._mount_done_cb) self.loop.run() - except gio.Error as e: + except GError as e: raise errors.TransportError(msg="Error setting up connection:" " %s" % str(e), orig_error=e) return connection, (user, password) @@ -257,8 +257,8 @@ if stat.S_ISREG(st.st_mode) or stat.S_ISDIR(st.st_mode): return True return False - except gio.Error as e: - if e.code == gio.ERROR_NOT_FOUND: + except GError as e: + if e.code == gio.IOErrorEnum.NOT_FOUND: return False else: self._translate_gio_error(e, relpath) @@ -281,10 +281,10 @@ buf = fin.read() fin.close() return BytesIO(buf) - except gio.Error as e: + except GError as e: # If we get a not mounted here it might mean # that a bad path has been entered (or that mount failed) - if (e.code == gio.ERROR_NOT_MOUNTED): + if e.code == gio.IOErrorEnum.NOT_MOUNTED: raise errors.PathError(relpath, extra='Failed to get file, make sure the path is correct. ' + str(e)) @@ -307,19 +307,19 @@ closed = True try: f = self._get_GIO(tmppath) - fout = f.create() + fout = f.create(0, None) closed = False length = self._pump(fp, fout) fout.close() closed = True self.stat(tmppath) dest = self._get_GIO(relpath) - f.move(dest, flags=gio.FILE_COPY_OVERWRITE) + f.move(dest, flags=gio.FileCopyFlags.OVERWRITE) f = None if mode is not None: self._setmode(relpath, mode) return length - except gio.Error as e: + except GError as e: self._translate_gio_error(e, relpath) finally: if not closed and fout is not None: @@ -335,7 +335,7 @@ f = self._get_GIO(relpath) f.make_directory() self._setmode(relpath, mode) - except gio.Error as e: + except GError as e: self._translate_gio_error(e, relpath) def open_write_stream(self, relpath, mode=None): @@ -369,14 +369,11 @@ f.delete() else: raise errors.NotADirectory(relpath) - except gio.Error as e: + except GError as e: self._translate_gio_error(e, relpath) except errors.NotADirectory as e: # just pass it forward raise e - except Exception as e: - mutter('failed to rmdir %s: %s' % (relpath, e)) - raise errors.PathError(relpath) def append_file(self, relpath, file, mode=None): """Append the text in the file-like object into the final @@ -392,7 +389,7 @@ result = 0 fo = self._get_GIO(tmppath) fi = self._get_GIO(relpath) - fout = fo.create() + fout = fo.create(0, None) try: info = GioStatResult(fi) result = info.st_size @@ -400,11 +397,11 @@ self._pump(fin, fout) fin.close() # This separate except is to catch and ignore the - # gio.ERROR_NOT_FOUND for the already existing file. + # gio.IOErrorEnum.NOT_FOUND for the already existing file. # It is valid to open a non-existing file for append. # This is caused by the broken gio append_to... - except gio.Error as e: - if e.code != gio.ERROR_NOT_FOUND: + except GError as e: + if e.code != gio.IOErrorEnum.NOT_FOUND: self._translate_gio_error(e, relpath) length = self._pump(file, fout) fout.close() @@ -413,9 +410,11 @@ raise errors.BzrError("Failed to append size after " "(%d) is not original (%d) + written (%d) total (%d)" % (info.st_size, result, length, result + length)) - fo.move(fi, flags=gio.FILE_COPY_OVERWRITE) + fo.move( + fi, flags=gio.FileCopyFlags.OVERWRITE, cancellable=None, + progress_callback=None) return result - except gio.Error as e: + except GError as e: self._translate_gio_error(e, relpath) def _setmode(self, relpath, mode): @@ -429,8 +428,8 @@ try: f = self._get_GIO(relpath) f.set_attribute_uint32(gio.FILE_ATTRIBUTE_UNIX_MODE, mode) - except gio.Error as e: - if e.code == gio.ERROR_NOT_SUPPORTED: + except GError as e: + if e.code == gio.IOErrorEnum.NOT_SUPPORTED: # Command probably not available on this server mutter("GIO Could not set permissions to %s on %s. %s", oct(mode), self._remote_path(relpath), str(e)) @@ -444,8 +443,8 @@ mutter("GIO move (rename): %s => %s", rel_from, rel_to) f = self._get_GIO(rel_from) t = self._get_GIO(rel_to) - f.move(t) - except gio.Error as e: + f.move(t, flags=0, cancellable=None, progress_callback=None) + except GError as e: self._translate_gio_error(e, rel_from) def move(self, rel_from, rel_to): @@ -455,8 +454,8 @@ mutter("GIO move: %s => %s", rel_from, rel_to) f = self._get_GIO(rel_from) t = self._get_GIO(rel_to) - f.move(t, flags=gio.FILE_COPY_OVERWRITE) - except gio.Error as e: + f.move(t, flags=gio.FileCopyFlags.OVERWRITE) + except GError as e: self._translate_gio_error(e, relfrom) def delete(self, relpath): @@ -466,7 +465,7 @@ mutter("GIO delete: %s", relpath) f = self._get_GIO(relpath) f.delete() - except gio.Error as e: + except GError as e: self._translate_gio_error(e, relpath) def external_url(self): @@ -489,11 +488,11 @@ try: entries = [] f = self._get_GIO(relpath) - children = f.enumerate_children(gio.FILE_ATTRIBUTE_STANDARD_NAME) + children = f.enumerate_children(gio.FILE_ATTRIBUTE_STANDARD_NAME, 0, None) for child in children: entries.append(urlutils.escape(child.get_name())) return entries - except gio.Error as e: + except GError as e: self._translate_gio_error(e, relpath) def iter_files_recursive(self): @@ -519,7 +518,7 @@ mutter("GIO stat: %s", relpath) f = self._get_GIO(relpath) return GioStatResult(f) - except gio.Error as e: + except GError as e: self._translate_gio_error(e, relpath, extra='error w/ stat') def lock_read(self, relpath): @@ -556,21 +555,21 @@ mutter("GIO Error: %s %s" % (str(err), path)) if extra is None: extra = str(err) - if err.code == gio.ERROR_NOT_FOUND: + if err.code == gio.IOErrorEnum.NOT_FOUND: raise errors.NoSuchFile(path, extra=extra) - elif err.code == gio.ERROR_EXISTS: + elif err.code == gio.IOErrorEnum.EXISTS: raise errors.FileExists(path, extra=extra) - elif err.code == gio.ERROR_NOT_DIRECTORY: + elif err.code == gio.IOErrorEnum.NOT_DIRECTORY: raise errors.NotADirectory(path, extra=extra) - elif err.code == gio.ERROR_NOT_EMPTY: + elif err.code == gio.IOErrorEnum.NOT_EMPTY: raise errors.DirectoryNotEmpty(path, extra=extra) - elif err.code == gio.ERROR_BUSY: + elif err.code == gio.IOErrorEnum.BUSY: raise errors.ResourceBusy(path, extra=extra) - elif err.code == gio.ERROR_PERMISSION_DENIED: + elif err.code == gio.IOErrorEnum.PERMISSION_DENIED: raise errors.PermissionDenied(path, extra=extra) - elif err.code == gio.ERROR_HOST_NOT_FOUND: + elif err.code == gio.IOErrorEnum.HOST_NOT_FOUND: raise errors.PathError(path, extra=extra) - elif err.code == gio.ERROR_IS_DIRECTORY: + elif err.code == gio.IOErrorEnum.IS_DIRECTORY: raise errors.PathError(path, extra=extra) else: mutter('unable to understand error for path: %s: %s', path, err)