Coverage for bim2sim/plugins/__init__.py: 78%
59 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 17:09 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 17:09 +0000
1"""BIM2SIM Plugins"""
2from __future__ import annotations
4import importlib
5import logging
6import pkgutil
7import sys
8from abc import ABCMeta
9from inspect import isclass
10from pathlib import Path
11from typing import Set, Type, List, TYPE_CHECKING
13from bim2sim.tasks import common, bps
14from bim2sim.tasks.base import ITask
15from bim2sim.sim_settings import BuildingSimSettings
17if TYPE_CHECKING:
18 from bim2sim.sim_settings import BaseSimSettings
20logger = logging.getLogger(__name__)
23def add_plugins_to_path(root: Path):
24 """Add all directories under root to path."""
25 for folder in root.glob('*/'):
26 if folder.is_dir():
27 sys.path.append(str(folder))
28 logger.info("Added %s to path", folder)
31add_plugins_to_path(Path(__file__).parent)
34class Plugin:
35 """Base class for bim2sim Plugins.
37 Notes:
38 This class is used as a namespace. Instantiation is not necessary.
40 Attributes:
41 name: Name of the Plugin
42 sim_settings: simulation settings to use in Projects using this Plugin
43 tasks: Set of tasks made available by this Plugin
44 default_tasks: List of tasks, which should be executed
45 elements: Additional Elements made available by this Plugin
46 """
47 __metaclass__ = ABCMeta
49 name: str = None
50 sim_settings: Type[BaseSimSettings] = None
51 tasks: Set[Type[ITask]] = set()
52 default_tasks: List[Type[ITask]] = []
53 elements: set = set()
55 def __repr__(self):
56 return "<%s>" % self.__class__.__name__
59class PluginBPSBase(Plugin):
60 # TODO this plugin is currently not found as plugins need a
61 # "bimsim_pluginname" folder. This needs to be correct in #548.
62 # Maybe just use subclasses of plugin and extract dummys?
63 name = 'BPSBase'
64 sim_settings = BuildingSimSettings
65 default_tasks = [
66 common.load_ifc.LoadIFC,
67 common.CheckIfc,
68 common.create_elements,
69 bps.CreateSpaceBoundaries,
70 bps.DisaggregationCreationAndTypeCheck,
71 bps.CombineThermalZones
72 ]
75def available_plugins() -> List[str]:
76 """List all available plugins."""
77 plugins = []
78 for finder, name, is_pkg in pkgutil.iter_modules():
79 if is_pkg and name.startswith('bim2sim_'):
80 plugins.append(name)
81 return plugins
84def load_plugin(name: str) -> Type[Plugin]:
85 """Load Plugin from module.
87 Args:
88 name: name of plugin module. Prefix 'bim2sim_' may be omitted.
89 """
90 if not name.startswith('bim2sim_'):
91 name = 'bim2sim_' + name
92 try:
93 # module names are usually lower case
94 module = importlib.import_module(name.lower())
95 except ModuleNotFoundError:
96 if name.lower() != name:
97 module = importlib.import_module(name)
98 else:
99 raise
100 plugin = get_plugin(module)
101 logger.info("Loaded Plugin %s", plugin.name)
102 return plugin
105def get_plugin(module) -> Type[Plugin]:
106 """Get Plugin class from module."""
107 for name, value in module.__dict__.items():
108 if isclass(value) and issubclass(value, Plugin) and value is not Plugin:
109 return value
110 raise KeyError(f"Found no Plugin in {module.__file__}")
113if __name__ == '__main__':
114 print(available_plugins())