Protect proxy/filter models with write_lock

This commit is contained in:
miruka 2020-05-30 18:13:45 -04:00
parent 9862e39108
commit de894ab4bb
3 changed files with 57 additions and 50 deletions

View File

@ -33,42 +33,47 @@ class ModelFilter(ModelProxy):
value: "ModelItem",
_changed_fields: Optional[Dict[str, Any]] = None,
) -> None:
if self.accept_source(source):
value = self.convert_item(value)
with self.write_lock:
if self.accept_source(source):
value = self.convert_item(value)
if self.accept_item(value):
self.__setitem__((source.sync_id, key), value, _changed_fields)
self.filtered_out.pop((source.sync_id, key), None)
else:
self.filtered_out[source.sync_id, key] = value
self.pop((source.sync_id, key), None)
if self.accept_item(value):
self.__setitem__(
(source.sync_id, key), value, _changed_fields,
)
self.filtered_out.pop((source.sync_id, key), None)
else:
self.filtered_out[source.sync_id, key] = value
self.pop((source.sync_id, key), None)
for callback in self.items_changed_callbacks:
callback()
for callback in self.items_changed_callbacks:
callback()
def source_item_deleted(self, source: Model, key) -> None:
if self.accept_source(source):
try:
del self[source.sync_id, key]
except KeyError:
del self.filtered_out[source.sync_id, key]
with self.write_lock:
if self.accept_source(source):
try:
del self[source.sync_id, key]
except KeyError:
del self.filtered_out[source.sync_id, key]
for callback in self.items_changed_callbacks:
callback()
for callback in self.items_changed_callbacks:
callback()
def source_cleared(self, source: Model) -> None:
if self.accept_source(source):
for source_sync_id, key in self.copy():
if source_sync_id == source.sync_id:
try:
del self[source.sync_id, key]
except KeyError:
del self.filtered_out[source.sync_id, key]
with self.write_lock:
if self.accept_source(source):
for source_sync_id, key in self.copy():
if source_sync_id == source.sync_id:
try:
del self[source.sync_id, key]
except KeyError:
del self.filtered_out[source.sync_id, key]
for callback in self.items_changed_callbacks:
callback()
for callback in self.items_changed_callbacks:
callback()
def refilter(

View File

@ -188,27 +188,28 @@ class Model(MutableMapping):
and one `ModelItemDeleted` pyotherside event is fired per sequence.
"""
try:
self._active_batch_remove_indice = []
yield None
finally:
indice = self._active_batch_remove_indice
groups = [list(group) for item, group in itertools.groupby(indice)]
last = None
with self.write_lock:
try:
self._active_batch_remove_indice = []
yield None
finally:
indice = self._active_batch_remove_indice
groups = [list(group) for item, group in itertools.groupby(indice)]
last = None
if groups:
last = groups[-1].pop()
if not groups[-1]:
del groups[-1]
if groups:
last = groups[-1].pop()
if not groups[-1]:
del groups[-1]
for grp in groups:
ModelItemDeleted(self.sync_id, index=grp[0], count=len(grp))
for grp in groups:
ModelItemDeleted(self.sync_id, index=grp[0], count=len(grp))
# Seems QML ListView has an horrible bug where removing a large
# amount of items at once will result in a corrupted empty display,
# this dumb workaround is the only way I've found
if last:
time.sleep(0.2)
ModelItemDeleted(self.sync_id, index=last, count=1)
# Seems QML ListView has an horrible bug where removing a large
# amount of items at once will result in a corrupted empty display,
# this dumb workaround is the only way I've found
if last:
time.sleep(0.2)
ModelItemDeleted(self.sync_id, index=last, count=1)
self._active_batch_remove_indice = None
self._active_batch_remove_indice = None

View File

@ -17,10 +17,11 @@ class ModelProxy(Model):
self.take_items_ownership = False
Model.proxies[sync_id] = self
for sync_id, model in Model.instances.items():
if sync_id != self.sync_id and self.accept_source(model):
for key, item in model.items():
self.source_item_set(model, key, item)
with self.write_lock:
for sync_id, model in Model.instances.items():
if sync_id != self.sync_id and self.accept_source(model):
for key, item in model.items():
self.source_item_set(model, key, item)
def accept_source(self, source: Model) -> bool: