Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

#! /usr/bin/env python3 

# -*- coding: utf-8 -*- 

 

#       Copyright 2011-2013, Marten de Vries 

# 

#       This file is part of OpenTeacher. 

# 

#       OpenTeacher is free software: you can redistribute it and/or modify 

#       it under the terms of the GNU General Public License as published by 

#       the Free Software Foundation, either version 3 of the License, or 

#       (at your option) any later version. 

# 

#       OpenTeacher is distributed in the hope that it will be useful, 

#       but WITHOUT ANY WARRANTY; without even the implied warranty of 

#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 

#       GNU General Public License for more details. 

# 

#       You should have received a copy of the GNU General Public License 

#       along with OpenTeacher.  If not, see <http://www.gnu.org/licenses/>. 

 

import os 

import itertools 

import logging 

 

logger = logging.getLogger(__name__) 

 

class ModulesModule: 

        """This module has two purposes: 

           1) selecting modules via its default() and sort() methods. 

           2) updating OT to self.profile (which should be set by a module 

              other than this one, normally the execute module, before this 

              module should be used by any module.) 

 

           Lowest (positive) priority: 1000 

 

        """ 

        def __init__(self, moduleManager, *args, **kwargs): 

                super().__init__(*args, **kwargs) 

                self._mm = moduleManager 

 

                self.type = "modules" 

                self.requires = ( 

                        self._mm.mods(type="event"), 

                ) 

                self._mtimeCache = {} 

 

        def _getPriority(self, mod): 

                try: 

                        return mod.priorities[self.profile] 

                except (AttributeError, KeyError): 

                        try: 

                                return mod.priorities["default"] 

                        except (AttributeError, KeyError): 

                                return self._getFallbackPriority(mod) 

 

        def _getFallbackPriority(self, mod): 

                try: 

                        return self._mtimeCache[mod] 

                except KeyError: 

                        #return a negative priority to the sort algorithm so the 

                        #module gets on top of the list. The negative integer 

                        #needs to be a number, that makes sure the last 

                        #installed module is on the top of the list. This just 

                        #uses seconds since installation. 

                        path = mod.__class__.__file__ 

                        priority = - int(os.path.getmtime(path)) 

                        #store so mtime is not requested repeatedly for the same 

                        #file. 

                        self._mtimeCache[mod] = priority 

                        return priority 

 

        def sort(self, *args, **kwargs): 

                """Sorts the modules returned by self._mm.mods(*args, **kwargs) 

                   based on their priority in the current profile. 

 

                """ 

                mods = self._mm.mods(*args, **kwargs) 

                return sorted(mods, key=self._getPriority) 

 

        def default(self, *args, **kwargs): 

                """Selects one of the modules returned by 

                   self._mm.mods(*args, **kwargs) based on their priority and 

                   the current profile OT's running in. 

 

                   Raises IndexError if no modules remain after filtering with 

                   the arguments. 

 

                """ 

                mods = self._mm.mods(*args, **kwargs) 

                try: 

                        return min(mods, key=self._getPriority) 

                except ValueError: 

                        raise IndexError() 

 

        #Enabling/disabling modules 

        def _hasPositivePriority(self, mod): 

                try: 

                        return mod.priorities[self.profile] >= 0 

                except (AttributeError, KeyError): 

                        try: 

                                return mod.priorities["default"] >= 0 

                        except (AttributeError, KeyError): 

                                return True #If no priorities-stuff, just enable 

 

        def updateToProfile(self): 

                """Enable()s and disable()s modules until only modules that have 

                   a positive priority in the current profile remain. This takes 

                   into account dependencies: if a module depends on one that 

                   can't be enabled due to its priority in the current profile, 

                   that module isn't enabled either. 

 

                """ 

                #build dependency tree by topological sorting 

                #http://en.wikipedia.org/wiki/Topological_sort ; second algorithm 

 

                self._filterCache = {} 

                self._sorted_tree = [] 

                self._visited_mods = set() 

                self._allMods = set(self._mm.mods) 

 

                self._potentialRequirements = set( 

                        (potentialRequirement, dep_mod) 

                        for dep_mod in self._allMods 

                        for potentialRequirement in itertools.chain( 

                                self._depFor(dep_mod, "requires"), 

                                self._depFor(dep_mod, "uses") 

                        ) 

                ) 

 

                mods_without_dependencies = ( 

                        mod 

                        for mod in self._allMods 

                        if not ( 

                                getattr(mod, "requires", None) and 

                                getattr(mod, "uses", None) 

                        ) 

                ) 

                for mod in mods_without_dependencies: 

                        self._visit(mod) 

 

                logger.debug("sorted module tree: %s" % self._sorted_tree) 

 

                self._enableModules() 

                self._disableModules() 

 

                del self._filterCache 

                del self._sorted_tree 

                del self._visited_mods 

                del self._allMods 

 

        def _visit(self, mod): 

                if mod in self._visited_mods: 

                        return 

                self._visited_mods.add(mod) 

 

                depMods = ( 

                        depMod 

                        for requirement, depMod in self._potentialRequirements 

                        if mod in requirement 

                ) 

                for depMod in depMods: 

                        self._visit(depMod) 

                self._sorted_tree.append(mod) 

 

        def _depFor(self, mod, type): 

                attribute = getattr(mod, type, ()) 

                try: 

                        dep = self._filterCache[attribute] 

                except KeyError: 

                        dep = self._filterCache[attribute] = set( 

                                frozenset(dep) for dep in attribute 

                        ) 

                return dep 

 

        def _enableModules(self): 

                #enable modules 

                for mod in reversed(self._sorted_tree): 

                        active = getattr(mod, "active", False) #False -> default 

                        shouldTryEnabling = not active and hasattr(mod, "enable") and self._hasPositivePriority(mod) 

                        if not shouldTryEnabling: 

                                continue 

                        depsactive = self._dependenciesActive(mod) 

                        if depsactive: 

                                logger.debug("Enabling %s" % mod) 

                                mod.enable() 

                        else: 

                                logger.debug("Dependenc(y/ies) inactive for %s" % mod) 

 

        def _dependenciesActive(self, mod): 

                return all( 

                        any( 

                                getattr(depMod, "active", False) 

                                for depMod in selector 

                        ) 

                        for selector in getattr(mod, "requires", []) 

                ) 

 

        def _disableModules(self): 

                #disable modules 

                for mod in self._sorted_tree: 

                        active = getattr(mod, "active", False) #False -> default 

                        shouldBeDisabled = active and hasattr(mod, "disable") and not self._hasPositivePriority(mod) 

204                        if shouldBeDisabled: 

                                logger.debug("Disabling %s" % mod) 

                                mod.disable() 

 

def init(moduleManager): 

        return ModulesModule(moduleManager)