From 730efcae4966836e74930ec39c4368d6b082e401 Mon Sep 17 00:00:00 2001 From: s-leger Date: Sat, 29 May 2021 14:03:00 +0200 Subject: [PATCH] Bugfix Set changed size during iteration (#1506) Fix for remove RuntimeError: Set changed size during iteration Add docstring --- .../ifcopenshell/api/__init__.py | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index 9aff9e848c..17eb17cec7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -42,33 +42,67 @@ def run(usecase_path, ifc_file=None, should_run_listeners=True, **settings): def add_pre_listener(usecase_path, ifc_file, callback): + """ Add a pre listener + There are 2 kind of listeners: + when ifc file is defined the listener will only run for specified file + when ifc file is None, the listener will run globally only based on usecase + :param usecase_path: string, ifcopenshell api use case path + :param ifc_file: ifc file object or None for global listener + :param callback: callback function + :return: ifc_key, uuid of listener, this is the prefix only, postfix is the usecase_path dot separated. + """ ifc_key = registered_ifcs.setdefault(ifc_file, ifcopenshell.guid.new()) pre_listeners.setdefault(".".join([ifc_key, usecase_path]), set()).add(callback) return ifc_key def add_post_listener(usecase_path, ifc_file, callback): + """ Add a post listener + There are 2 kind of listeners: + when ifc file is defined the listener will only run for specified file + when ifc file is None, the listener will run globally only based on usecase + :param usecase_path: string, ifcopenshell api use case path + :param ifc_file: ifc file object or None for global listener + :param callback: callback function + :return: ifc_key, uuid of listener, this is the prefix only, postfix is the usecase_path dot separated. + """ ifc_key = registered_ifcs.setdefault(ifc_file, ifcopenshell.guid.new()) post_listeners.setdefault(".".join([ifc_key, usecase_path]), set()).add(callback) return ifc_key def remove_pre_listener(callback, usecase_path=""): + """ Remove a pre listener + :param callback: callback function to remove + :param usecase_path: string, optional, ifcopenshell api usecase path, may be prefixed with ifc_key, dot separated. + :return: + """ for listener_key, callbacks in pre_listeners.items(): if not listener_key.endswith(usecase_path): continue + to_remove = set() for fun in callbacks: if fun == callback: - pre_listeners[listener_key].remove(callback) + to_remove.add(callback) + for callback in to_remove: + pre_listeners[listener_key].remove(callback) def remove_post_listener(callback, usecase_path=""): + """ Remove a post listener + :param callback: callback function to remove + :param usecase_path: string, optional ifcopenshell api usecase path, may be prefixed with ifc_key, dot separated. + :return: + """ for listener_key, callbacks in post_listeners.items(): if not listener_key.endswith(usecase_path): continue + to_remove = set() for fun in callbacks: if fun == callback: - post_listeners[listener_key].remove(callback) + to_remove.add(callback) + for callback in to_remove: + post_listeners[listener_key].remove(callback) def remove_all_listeners():