Coverage for src/robotide/application/pluginloader.py: 87%

54 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-06 10:40 +0100

1# Copyright 2008-2015 Nokia Networks 

2# Copyright 2016- Robot Framework Foundation 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); 

5# you may not use this file except in compliance with the License. 

6# You may obtain a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, 

12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

13# See the License for the specific language governing permissions and 

14# limitations under the License. 

15 

16import importlib 1ab

17import importlib.util 1ab

18import inspect 1ab

19import os 1ab

20 

21from ..context import LOG 1ab

22from ..pluginapi import Plugin 1ab

23from .pluginconnector import plugin_factory 1ab

24 

25 

26class PluginLoader(object): 1ab

27 

28 def __init__(self, application, load_dirs, standard_classes): 1ab

29 self._load_errors = [] 

30 self.plugins = [plugin_factory(application, cls) for cls in standard_classes + self._find_classes(load_dirs)] 

31 if self._load_errors: 31 ↛ 32line 31 didn't jump to line 32 because the condition on line 31 was never true

32 LOG.error('\n\n'.join(self._load_errors)) 

33 

34 def enable_plugins(self): 1ab

35 for p in self.plugins: 1acd

36 p.enable_on_startup() 1acd

37 

38 def _find_classes(self, load_dirs): 1ab

39 classes = [] 

40 for path in self._find_python_files(load_dirs): 

41 for cls in self._import_classes(path): 

42 if self._is_plugin_class(path, cls): 

43 classes.append(cls) 

44 return classes 

45 

46 def _is_plugin_class(self, path, cls): 1ab

47 try: 

48 return issubclass(cls, Plugin) and cls is not Plugin 

49 except Exception as err: 

50 msg = "Finding classes from module '%s' failed: %s" 

51 self._load_errors.append(msg % (path, err)) 

52 

53 def _find_python_files(self, load_dirs): 1ab

54 files = [] 

55 for path in load_dirs: 

56 if not os.path.exists(path): 

57 continue 

58 for filename in os.listdir(path): 

59 full_path = os.path.join(path, filename) 

60 if filename[0].isalpha() and \ 

61 os.path.splitext(filename)[1].lower() == ".py": 

62 files.append(full_path) 

63 elif os.path.isdir(full_path): 

64 files.extend(self._find_python_files([full_path])) 

65 return files 

66 

67 def _import_classes(self, path): 1ab

68 _, filename = os.path.split(path) 

69 modulename = os.path.splitext(filename)[0] 

70 spec = importlib.util.spec_from_file_location(modulename, path) 

71 if spec is None: 71 ↛ 72line 71 didn't jump to line 72 because the condition on line 71 was never true

72 return [] 

73 try: 

74 m_module = importlib.util.module_from_spec(spec) 

75 spec.loader.exec_module(m_module) 

76 except Exception as err: 

77 self._load_errors.append("Importing plugin module '%s' failed:\n%s" 

78 % (path, err)) 

79 return [] 

80 return [cls for _, cls in 

81 inspect.getmembers(m_module, predicate=inspect.isclass)]