G_IS_TASK Error With Completion Provider
This was originally reported by a user of Gaphor at https://github.com/gaphor/gaphor/issues/2242, and it is reproducible by a few users on Fedora, openSUSE, and Windows.
An issue filed with gtksourceview at gtksourceview#319 (closed) indicates that this is an issue with pygobject.
The error is:
ref task <Gio.Task object at 0x7fc8ca9b0e00 (GTask at 0x1fb7090)>
(python:10579): GLib-GIO-CRITICAL **: 20:36:24.265: g_task_get_source_object: assertion 'G_IS_TASK (task)' failed
(python:10579): GLib-GIO-CRITICAL **: 20:36:24.265: g_task_get_task_data: assertion 'G_IS_TASK (task)' failed
fish: Job 1, 'python main.py' terminated by signal SIGSEGV (Address boundary error)
An example sample app is here:
import gi
gi.require_version("Gtk", "4.0")
gi.require_version("GtkSource", "5")
from gi.repository import GObject, Gio, Gtk, GtkSource
class SourceViewWindow(Gtk.ApplicationWindow):
def __init__(self, **kargs):
super().__init__(**kargs, title="SourceView Demo")
self.set_default_size(500, 400)
self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.set_child(self.box)
self.create_source_view()
self.create_buttons()
def create_source_view(self):
scrolledwindow = Gtk.ScrolledWindow()
scrolledwindow.props.hexpand = True
scrolledwindow.props.vexpand = True
self.box.append(scrolledwindow)
self.source_view = GtkSource.View()
self.textbuffer = self.source_view.get_buffer()
self.textbuffer.set_text("This is some text inside of a GtkSource.View")
view_completion = self.source_view.get_completion()
view_completion.add_provider(ColorsCompletionProvider())
scrolledwindow.set_child(self.source_view)
def create_buttons(self):
grid = Gtk.Grid()
self.box.append(grid)
radio_spaces_or_tabs = Gtk.CheckButton(label="Spaces for tab")
grid.attach(radio_spaces_or_tabs, 0, 2, 1, 1)
radio_spaces_or_tabs.connect("toggled", self.on_spaces_or_tabs_toggled)
radio_line_nums = Gtk.CheckButton(label="Show line numbers")
grid.attach_next_to(
radio_line_nums, radio_spaces_or_tabs, Gtk.PositionType.RIGHT, 1, 1
)
radio_line_nums.connect("toggled", self.on_show_line_nums)
def on_spaces_or_tabs_toggled(self, widget):
self.source_view.props.insert_spaces_instead_of_tabs = widget.props.active
def on_show_line_nums(self, widget):
self.source_view.props.show_line_numbers = widget.props.active
class ColorsCompletionProvider(GObject.GObject, GtkSource.CompletionProvider):
def __init__(self):
super().__init__()
self._filter_data: FilterData = FilterData()
self._task = None
def do_activate(
self,
context: GtkSource.CompletionContext,
proposal: GtkSource.CompletionProposal,
) -> None:
buffer = context.get_buffer()
buffer.begin_user_action()
has_selection, begin, end = context.get_bounds()
if has_selection:
buffer.delete(begin, end)
buffer.insert(begin, proposal.text, len(proposal.text))
buffer.end_user_action()
def do_display(
self,
context: GtkSource.CompletionContext,
proposal: GtkSource.CompletionProposal,
cell: GtkSource.CompletionCell,
) -> None:
if cell.props.column == GtkSource.CompletionColumn.ICON:
pass
elif cell.props.column == GtkSource.CompletionColumn.TYPED_TEXT:
cell.set_text(proposal.text)
def do_get_priority(self, context: GtkSource.CompletionContext) -> int:
return -4
def do_get_title(self) -> str:
return "Colors"
def do_populate_async(self, context, cancellable, callback, user_data=None) -> None:
task = self._task = Gio.Task.new(self, cancellable, callback)
print("ref task", task)
store = Gio.ListStore.new(CssNamedColorProposal)
self._filter_data.word = context.get_word()
for color_name in ["blue", "green", "red", "yellow"]:
proposal = CssNamedColorProposal(color_name)
store.append(proposal)
def filter_fn(proposal, data):
return proposal.text.startswith(data.word)
store_filter = Gtk.CustomFilter.new(filter_fn, self._filter_data)
task.proposals = Gtk.FilterListModel.new(store, store_filter)
task.return_boolean(True)
def do_populate_finish(self, result: Gio.AsyncResult) -> Gio.ListModel:
# self._task = None
if result.propagate_boolean():
return result.proposals
def do_refilter(
self, context: GtkSource.CompletionContext, model: Gio.ListModel
) -> None:
word = context.get_word()
change = Gtk.FilterChange.DIFFERENT
if old_word := self._filter_data.word:
if word.startswith(old_word):
change = Gtk.FilterChange.MORE_STRICT
elif old_word.startswith(word):
change = Gtk.FilterChange.LESS_STRICT
self._filter_data.word = word
model.get_filter().changed(change)
class CssNamedColorProposal(GObject.Object, GtkSource.CompletionProposal):
def __init__(self, text: str):
super().__init__()
self.text: str = text
class FilterData:
word: str
def on_activate(app):
win = SourceViewWindow(application=app)
win.present()
GtkSource.init()
app = Gtk.Application(application_id="com.example.App")
app.connect("activate", on_activate)
app.run(None)