1import inspect
2import logging
3import imp
4import os.path
5
6
7class IModuleLoader:
8    def __init__(self, **params):
9        self.set_params(**params)
10
11    def set_params(self, **params):
12        raise NotImplementedError
13
14    def load(self, registrant):
15        raise NotImplementedError
16
17
18class FileLoader(IModuleLoader):
19    def __init__(self, **params):
20        IModuleLoader.__init__(self, **params)
21        self.__logger = logging.getLogger("libraries.FileLoader")
22
23    def set_params(self, **params):
24        if "base_path" not in params:
25            return
26        elif "filename" not in params:
27            return
28
29        self.filename = params["filename"]
30        self.base_path = params["base_path"]
31        if self.base_path.endswith("/"):
32            self.base_path = self.base_path[:-1]
33
34    def load(self, registrant):
35        self.module_registrant = registrant
36
37        self._load_py_from_file(os.path.join(self.base_path, self.filename))
38
39    def _build_id(self, filename, objname):
40        filepath, filename = os.path.split(filename)
41
42        relative_path = os.path.relpath(filepath, self.base_path)
43        identifier = relative_path + "/" + objname
44        if identifier.startswith("./"):
45            identifier = identifier[2:]
46
47        return identifier
48
49    def _load_py_from_file(self, filename):
50        """
51        Opens "filename", inspects it and calls the registrant
52        """
53        self.__logger.debug("__load_py_from_file. START, file=%s" % (filename,))
54
55        dirname, filename = os.path.split(filename)
56        fn = os.path.splitext(filename)[0]
57        exten_file = None
58        module = None
59
60        try:
61            exten_file, filename, description = imp.find_module(fn, [dirname])
62            module = imp.load_module(fn, exten_file, filename, description)
63        except ImportError as msg:
64            self.__logger.critical(
65                "__load_py_from_file. Filename: %s Exception, msg=%s" % (filename, msg)
66            )
67            # raise msg
68            pass
69        except SyntaxError as msg:
70            # incorrect python syntax in file
71            self.__logger.critical(
72                "__load_py_from_file. Filename: %s Exception, msg=%s" % (filename, msg)
73            )
74            # raise msg
75            pass
76        finally:
77            if exten_file:
78                exten_file.close()
79
80        if module is None:
81            return
82
83        for objname in dir(module):
84            obj = getattr(module, objname)
85            self.__logger.debug("__load_py_from_file. inspecting=%s" % (objname,))
86            if inspect.isclass(obj):
87                if "__PLUGIN_MODULEMAN_MARK" in dir(obj):
88                    if self.module_registrant:
89                        self.module_registrant.register(
90                            self._build_id(filename, objname), obj
91                        )
92
93        self.__logger.debug("__load_py_from_file. END, loaded file=%s" % (filename,))
94
95
96class DirLoader(FileLoader):
97    def __init__(self, **params):
98        FileLoader.__init__(self, **params)
99        self.__logger = logging.getLogger("libraries.DirLoader")
100
101    def set_params(self, **params):
102        if "base_dir" not in params:
103            return
104        elif "base_path" not in params:
105            return
106
107        self.base_dir = params["base_dir"]
108        self.base_path = params["base_path"]
109        if self.base_path.endswith("/"):
110            self.base_path = self.base_path[:-1]
111
112    def load(self, registrant):
113        self.module_registrant = registrant
114        self.structure = self.__load_all(self.base_dir)
115
116    def _build_id(self, filename, objname):
117        filepath, filename = os.path.split(filename)
118
119        relative_path = os.path.relpath(
120            filepath, os.path.join(self.base_path, self.base_dir)
121        )
122        identifier = relative_path + "/" + objname
123        if identifier.startswith("./"):
124            identifier = identifier[2:]
125
126        return identifier
127
128    def __load_all(self, dir_name):
129        """
130        loads all plugins and creates a loaded list of scripts from directory plugins like:
131        [ ( category,[script1, script2,...] ), (category2,[script1, (subcategory,[script1,script2]),...]) ]
132        """
133        walked = []
134
135        current = os.path.join(self.base_path, dir_name)
136        if os.path.isdir(current):
137            dir_list = self.__walk_dir_tree(current)
138            walked.append((current, dir_list))
139            if self.module_registrant:
140                self.module_registrant.end_loading()
141
142        return walked
143
144    def __walk_dir_tree(self, dirname):
145        dir_list = []
146
147        self.__logger.debug("__walk_dir_tree. START dir=%s", dirname)
148
149        for f in os.listdir(dirname):
150            current = os.path.join(dirname, f)
151            if os.path.isfile(current) and f.endswith("py"):
152                if self.module_registrant:
153                    self._load_py_from_file(current)
154
155                dir_list.append(current)
156            elif os.path.isdir(current):
157                ret = self.__walk_dir_tree(current)
158                if ret:
159                    dir_list.append((f, ret))
160
161        return dir_list
162