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", value: "ModelItem",
_changed_fields: Optional[Dict[str, Any]] = None, _changed_fields: Optional[Dict[str, Any]] = None,
) -> None: ) -> None:
if self.accept_source(source): with self.write_lock:
value = self.convert_item(value) if self.accept_source(source):
value = self.convert_item(value)
if self.accept_item(value): if self.accept_item(value):
self.__setitem__((source.sync_id, key), value, _changed_fields) self.__setitem__(
self.filtered_out.pop((source.sync_id, key), None) (source.sync_id, key), value, _changed_fields,
else: )
self.filtered_out[source.sync_id, key] = value self.filtered_out.pop((source.sync_id, key), None)
self.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: for callback in self.items_changed_callbacks:
callback() callback()
def source_item_deleted(self, source: Model, key) -> None: def source_item_deleted(self, source: Model, key) -> None:
if self.accept_source(source): with self.write_lock:
try: if self.accept_source(source):
del self[source.sync_id, key] try:
except KeyError: del self[source.sync_id, key]
del self.filtered_out[source.sync_id, key] except KeyError:
del self.filtered_out[source.sync_id, key]
for callback in self.items_changed_callbacks: for callback in self.items_changed_callbacks:
callback() callback()
def source_cleared(self, source: Model) -> None: def source_cleared(self, source: Model) -> None:
if self.accept_source(source): with self.write_lock:
for source_sync_id, key in self.copy(): if self.accept_source(source):
if source_sync_id == source.sync_id: for source_sync_id, key in self.copy():
try: if source_sync_id == source.sync_id:
del self[source.sync_id, key] try:
except KeyError: del self[source.sync_id, key]
del self.filtered_out[source.sync_id, key] except KeyError:
del self.filtered_out[source.sync_id, key]
for callback in self.items_changed_callbacks: for callback in self.items_changed_callbacks:
callback() callback()
def refilter( def refilter(

View File

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