Fix #1511. Prevent double registration of listener callbacks, and simplify listeners.

This commit is contained in:
Dion Moult
2021-06-05 19:46:41 +10:00
parent ece6e6c8ca
commit 082ee3c95d
2 changed files with 52 additions and 72 deletions
@@ -5,23 +5,15 @@ import ifcopenshell
import ifcopenshell.api
registered_ifcs = {}
pre_listeners = {}
post_listeners = {}
def run(usecase_path, ifc_file=None, should_run_listeners=True, **settings):
ifc_key = registered_ifcs.setdefault(ifc_file, ifcopenshell.guid.new())
if should_run_listeners:
for listener in pre_listeners.get(".".join([ifc_key, usecase_path]), []):
for listener in pre_listeners.get(usecase_path, {}).values():
listener(usecase_path, ifc_file, settings)
if None in registered_ifcs and ifc_key != registered_ifcs[None]:
global_key = registered_ifcs[None]
for listener in pre_listeners.get(".".join([global_key, usecase_path]), []):
listener(usecase_path, ifc_file, settings)
def serialise_entity_instance(entity):
return {"cast_type": "entity_instance", "value": entity.id(), "Name": getattr(entity, "Name", None)}
@@ -35,16 +27,16 @@ def run(usecase_path, ifc_file=None, should_run_listeners=True, **settings):
vcs_settings[key] = [serialise_entity_instance(i) for i in value]
if "add_representation" in usecase_path:
pass
# print(ifc_key, usecase_path, "{ ... settings too complex right now ... }")
# print(usecase_path, "{ ... settings too complex right now ... }")
elif "owner." in usecase_path:
pass
else:
pass
# print(vcs_settings)
# try:
# print(ifc_key, usecase_path, json.dumps(vcs_settings))
# print(usecase_path, json.dumps(vcs_settings))
# except:
# print(ifc_key, usecase_path, vcs_settings)
# print(usecase_path, vcs_settings)
importlib.import_module(f"ifcopenshell.api.{usecase_path}")
module, usecase = usecase_path.split(".")
@@ -56,82 +48,52 @@ def run(usecase_path, ifc_file=None, should_run_listeners=True, **settings):
result = usecase_class(**settings).execute()
if should_run_listeners:
for listener in post_listeners.get(".".join([ifc_key, usecase_path]), []):
for listener in post_listeners.get(usecase_path, {}).values():
listener(usecase_path, ifc_file, settings)
if None in registered_ifcs and ifc_key != registered_ifcs[None]:
global_key = registered_ifcs[None]
for listener in post_listeners.get(".".join([global_key, usecase_path]), []):
listener(usecase_path, ifc_file, settings)
return result
def add_pre_listener(usecase_path, ifc_file, callback):
def add_pre_listener(usecase_path, name, 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 name: string, name of 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
pre_listeners.setdefault(usecase_path, {})[name] = callback
def add_post_listener(usecase_path, ifc_file, callback):
def add_post_listener(usecase_path, name, 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 name: string, name of 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
post_listeners.setdefault(usecase_path, {})[name] = callback
def remove_pre_listener(callback, usecase_path=""):
def remove_pre_listener(usecase_path, name, callback):
"""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:
:param usecase_path: string, ifcopenshell api use case path
:param name: string, name of listener
:param callback: callback function
"""
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:
to_remove.add(callback)
for callback in to_remove:
pre_listeners[listener_key].remove(callback)
pre_listeners.get(usecase_path, {}).pop(name, None)
def remove_post_listener(callback, usecase_path=""):
def remove_post_listener(usecase_path, name, callback):
"""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:
:param usecase_path: string, ifcopenshell api use case path
:param name: string, name of listener
:param callback: callback function
"""
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:
to_remove.add(callback)
for callback in to_remove:
post_listeners[listener_key].remove(callback)
post_listeners.get(usecase_path, {}).pop(name, None)
def remove_all_listeners():
registered_ifcs.clear()
pre_listeners.clear()
post_listeners.clear()