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

1"""BIM2SIM Plugins""" 

2from __future__ import annotations 

3 

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 

12 

13from bim2sim.tasks import common, bps 

14from bim2sim.tasks.base import ITask 

15from bim2sim.sim_settings import BuildingSimSettings 

16 

17if TYPE_CHECKING: 

18 from bim2sim.sim_settings import BaseSimSettings 

19 

20logger = logging.getLogger(__name__) 

21 

22 

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) 

29 

30 

31add_plugins_to_path(Path(__file__).parent) 

32 

33 

34class Plugin: 

35 """Base class for bim2sim Plugins. 

36 

37 Notes: 

38 This class is used as a namespace. Instantiation is not necessary. 

39 

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 

48 

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() 

54 

55 def __repr__(self): 

56 return "<%s>" % self.__class__.__name__ 

57 

58 

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 ] 

73 

74 

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 

82 

83 

84def load_plugin(name: str) -> Type[Plugin]: 

85 """Load Plugin from module. 

86 

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 

103 

104 

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__}") 

111 

112 

113if __name__ == '__main__': 

114 print(available_plugins())