Coverage for src/robotide/namespace/cache.py: 95%

150 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 os 1aV

17import time 1aV

18 

19from ..robotapi import normpath, ALIAS_MARKER 1aV

20from ..spec.iteminfo import BlockKeywordInfo 1aV

21 

22BOOL_COND = '(boolean) condition' 1aV

23SELECTOR_FOR = ('Selector for `FOR`. See `BuiltIn.FOR` docs at ' 1aV

24 'https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#for-loops.') 

25ARG_VALUES = '*values' 1aV

26 

27 

28class LibraryCache(object): 1aV

29 

30 def __init__(self, settings, libraries_need_refresh_listener, 1aV

31 library_manager): 

32 self._library_manager = None 2a Y W X U S T mb+ , nbob- . pb/ : qbrbsbtbubd Z 0 ; = ? @ A e f g h i j k l m n o p q r s t u v B 1 2 4 C w x y [ ] ^ b H E I ! vbc wbxbF _

33 self._settings = settings 2a Y W X U S T mb+ , nbob- . pb/ : qbrbsbtbubd Z 0 ; = ? @ A e f g h i j k l m n o p q r s t u v B 1 2 4 C w x y [ ] ^ b H E I ! vbc wbxbF _

34 if library_manager: 2a Y W X U S T mb+ , nbob- . pb/ : qbrbsbtbubd Z 0 ; = ? @ A e f g h i j k l m n o p q r s t u v B 1 2 4 C w x y [ ] ^ b H E I ! vbc wbxbF _

35 self.set_library_manager(library_manager) 2a Y W X U S T mb+ , nbob- . pb/ : qbrbsbtbubd Z 0 ; = ? @ A e f g h i j k l m n o p q r s t u v B 1 2 4 C w x y [ ] ^ b H E I ! vbc wbxb_

36 self._libraries_need_refresh_listener = libraries_need_refresh_listener 2a Y W X U S T mb+ , nbob- . pb/ : qbrbsbtbubd Z 0 ; = ? @ A e f g h i j k l m n o p q r s t u v B 1 2 4 C w x y [ ] ^ b H E I ! vbc wbxbF _

37 self._library_keywords = {} 2a Y W X U S T mb+ , nbob- . pb/ : qbrbsbtbubd Z 0 ; = ? @ A e f g h i j k l m n o p q r s t u v B 1 2 4 C w x y [ ] ^ b H E I ! vbc wbxbF _

38 self.__default_libraries = None 2a Y W X U S T mb+ , nbob- . pb/ : qbrbsbtbubd Z 0 ; = ? @ A e f g h i j k l m n o p q r s t u v B 1 2 4 C w x y [ ] ^ b H E I ! vbc wbxbF _

39 self.__default_kws = None 2a Y W X U S T mb+ , nbob- . pb/ : qbrbsbtbubd Z 0 ; = ? @ A e f g h i j k l m n o p q r s t u v B 1 2 4 C w x y [ ] ^ b H E I ! vbc wbxbF _

40 

41 def set_library_manager(self, library_manager): 1aV

42 self._library_manager = library_manager 2a Y W X U S T mb+ , nbob- . pb/ : qbrbsbtbubd Z 0 ; = ? @ A e f g h i j k l m n o p q r s t u v B 1 2 4 C w x y [ ] ^ b H E I ! vbc wbxbF _

43 

44 def expire(self): 1aV

45 self.__init__(self._settings, self._libraries_need_refresh_listener, 1aYWXUST+,-./:dZ0;=?@AefghijklmnopqrstuvB124Cwxy[]^b_

46 self._library_manager) 

47 

48 @property 1aV

49 def _default_libraries(self): 1aV

50 if self.__default_libraries is None: 50 ↛ 52line 50 didn't jump to line 52 because the condition on line 50 was always true1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

51 self.__default_libraries = self._get_default_libraries() 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

52 return self.__default_libraries 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

53 

54 @property 1aV

55 def _default_kws(self): 1aV

56 if self.__default_kws is None: 2a J K D L d M N A e f g h i j k l m n o p q r s t u v B O C w x y z b H E I P Q R 3 ?bG ` { | } ~ abbbcb5 db6 c ebfbgbhbibjbkblb@b7 yb* F

57 self.__default_kws = self._build_default_kws() 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

58 return self.__default_kws 2a J K D L d M N A e f g h i j k l m n o p q r s t u v B O C w x y z b H E I P Q R 3 ?bG ` { | } ~ abbbcb5 db6 c ebfbgbhbibjbkblb@b7 yb* F

59 

60 def get_all_cached_library_names(self): 1aV

61 return [name for name, _ in self._library_keywords] 2[b

62 

63 def _get_library(self, name, args): 1aV

64 library_database = \ 1aJWXUSTKDL#$%8'(dMNAefghijklmnopqrstuvBOCwxyzbHEI!PQR3G56c7F

65 self._library_manager.get_new_connection_to_library_database() 

66 try: 1aJWXUSTKDL#$%8'(dMNAefghijklmnopqrstuvBOCwxyzbHEI!PQR3G56c7F

67 last_updated = library_database.get_library_last_updated(name, args) 1aJWXUSTKDL#$%8'(dMNAefghijklmnopqrstuvBOCwxyzbHEI!PQR3G56c7F

68 if last_updated: 1aJWXUSTKDL#$%8'(dMNAefghijklmnopqrstuvBOCwxyzbHEI!PQR3G56c7F

69 if time.time() - last_updated > 10.0: 1zb

70 self._library_manager.fetch_keywords( 1zb

71 name, args, self._libraries_need_refresh_listener) 

72 return library_database.fetch_library_keywords(name, args) 1zb

73 return self._library_manager.get_and_insert_keywords(name, args) 1aJWXUSTKDL#$%8'(dMNAefghijklmnopqrstuvBOCwxyHEI!PQR3G56c7F

74 finally: 

75 library_database.close() 1aJWXUSTKDL#$%8'(dMNAefghijklmnopqrstuvBOCwxyzbHEI!PQR3G56c7F

76 

77 @staticmethod 1aV

78 def _key(name, args): 1aV

79 return name, str(tuple(args or '')) 2a W X U S T D # $ % 8 ' ( z b 3 9 ) G ` { | } ~ abbbcb5 db6 c ebfbgbhbibjbkblb7 yb*

80 

81 def get_library_keywords(self, name, args=None, alias=None): 1aV

82 args_with_alias = self._alias_to_args(alias, args) 2a W X U S T D # $ % 8 ' ( z b 3 9 ) G ` { | } ~ abbbcb5 db6 c ebfbgbhbibjbkblb7 yb*

83 key = self._key(name, args_with_alias) 2a W X U S T D # $ % 8 ' ( z b 3 9 ) G ` { | } ~ abbbcb5 db6 c ebfbgbhbibjbkblb7 yb*

84 if key not in self._library_keywords: 2a W X U S T D # $ % 8 ' ( z b 3 9 ) G ` { | } ~ abbbcb5 db6 c ebfbgbhbibjbkblb7 yb*

85 self._library_keywords[key] = [k.with_alias(alias) for k in 1aWXUSTD#$%8'(zb3G56c7

86 self._get_library(name, args)] 

87 

88 return self._library_keywords[key] 2a W X U S T D # $ % 8 ' ( z b 3 9 ) G ` { | } ~ abbbcb5 db6 c ebfbgbhbibjbkblb7 yb*

89 

90 @staticmethod 1aV

91 def _alias_to_args(alias, args): 1aV

92 if alias: 2a W X U S T D # $ % 8 ' ( z b 3 9 ) G ` { | } ~ abbbcb5 db6 c ebfbgbhbibjbkblb7 yb*

93 if args: 2U S T 8 3 9 G ` { | } ~ abbbcbdbc ebfbgbhbibjbkblb

94 args = tuple(args) + (ALIAS_MARKER, alias) 1ST

95 else: 

96 args = (ALIAS_MARKER, alias) 2U S T 8 3 9 G ` { | } ~ abbbcbdbc ebfbgbhbibjbkblb

97 return args 2a W X U S T D # $ % 8 ' ( z b 3 9 ) G ` { | } ~ abbbcb5 db6 c ebfbgbhbibjbkblb7 yb*

98 

99 def get_default_keywords(self): 1aV

100 return self._default_kws[:] 2a J K D L d M N A e f g h i j k l m n o p q r s t u v B O C w x y z b H E I P Q R 3 ?bG ` { | } ~ abbbcb5 db6 c ebfbgbhbibjbkblb@b7 yb* F

101 

102 def _build_default_kws(self): 1aV

103 kws = [] 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

104 for keywords_in_library in self._default_libraries.values(): 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

105 kws.extend(keywords_in_library) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

106 obj1 = BlockKeywordInfo('FOR', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

107 'To create loops. Arguments: (scalars) loop_variables, selector: one of IN, IN RANGE,' 

108 ' IN ENUMERATE, IN ZIP, values=scalars, lists, dictionaries. See `BuiltIn.FOR` docs' 

109 ' at\n' 

110 ' https://robotframework.org/robotframework/latest/' 

111 'RobotFrameworkUserGuide.html#for-loops.', 

112 'ROBOT', 'BuiltIn', 'loop_variables', 'selector', ARG_VALUES) 

113 kws.append(obj1) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

114 obj2 = BlockKeywordInfo('END', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

115 'Ends `FOR` loops, `GROUP`, `IF/ELSE`, `WHILE` and `TRY/EXCEPT` blocks. See `BuiltIn.FOR` docs' 

116 ' at\n https://robotframework.org/robotframework/latest/' 

117 'RobotFrameworkUserGuide.html#for-loops.') 

118 kws.append(obj2) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

119 obj3 = BlockKeywordInfo(': FOR', '*DEPRECATED*\nSee `BuiltIn.FOR` docs.') 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

120 kws.append(obj3) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

121 obj4 = BlockKeywordInfo('WHILE', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

122 'To create loops. Arguments: (boolean) condition, (optional) limit=counter or time. See' 

123 ' `BuiltIn` docs at\n https://robotframework.org/robotframework/latest/' 

124 'RobotFrameworkUserGuide.html#while-loops.', 

125 'ROBOT', 'BuiltIn', BOOL_COND, '(optional) limit=') 

126 kws.append(obj4) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

127 obj5 = BlockKeywordInfo('BREAK', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

128 'To control loops. See `BuiltIn` docs at\n https://robotframework.org/robotframework/' 

129 'latest/RobotFrameworkUserGuide.html#loop-control-using-break-and-continue.') 

130 kws.append(obj5) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

131 obj6 = BlockKeywordInfo('CONTINUE', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

132 'To control loops. See `BuiltIn` docs at\n https://robotframework.org/robotframework/' 

133 'latest/RobotFrameworkUserGuide.html#loop-control-using-break-and-continue.') 

134 kws.append(obj6) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

135 obj7 = BlockKeywordInfo('IF', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

136 'To condition blocks of keywords. Arguments: (boolean) condition. See `BuiltIn` docs ' 

137 'at\n https://robotframework.org/robotframework/latest/' 

138 'RobotFrameworkUserGuide.html#if-else-syntax.', 

139 'ROBOT', 'BuiltIn', BOOL_COND) 

140 kws.append(obj7) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

141 obj8 = BlockKeywordInfo('ELSE', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

142 'To condition blocks of keywords. See `BuiltIn.IF` docs at\n ' 

143 'https://robotframework.org/robotframework/latest/' 

144 'RobotFrameworkUserGuide.html#if-else-syntax.\n Note: See also TRY/EXCEPT.') 

145 kws.append(obj8) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

146 obj9 = BlockKeywordInfo('ELSE IF', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

147 'To condition blocks of keywords. Arguments: (boolean) condition. See `BuiltIn.IF` ' 

148 'docs at\n https://robotframework.org/robotframework/latest/' 

149 'RobotFrameworkUserGuide.html#if-else-syntax.', 

150 'ROBOT', 'BuiltIn', BOOL_COND) 

151 kws.append(obj9) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

152 obj10 = BlockKeywordInfo('TRY', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

153 'To prevent test failures based on messages from keywords. See `BuiltIn.TRY` ' 

154 'docs at\n https://robotframework.org/robotframework/latest/' 

155 'RobotFrameworkUserGuide.html#try-except-syntax.') 

156 kws.append(obj10) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

157 obj11 = BlockKeywordInfo('EXCEPT', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

158 'To prevent test failures based on messages from keywords.\n\n' 

159 'Arguments: (optional) message=string, (optional) glob, regex, scalar, ' 

160 '(optional) type, glob or error capture setting.\n\n See `BuiltIn.TRY` docs at\n ' 

161 'https://robotframework.org/robotframework/latest/' 

162 'RobotFrameworkUserGuide.html#try-except-syntax.', 

163 'ROBOT', 'BuiltIn', '*options') 

164 kws.append(obj11) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

165 obj12 = BlockKeywordInfo('FINALLY', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

166 'To prevent test failures based on messages from keywords. See `BuiltIn.TRY` docs ' 

167 'at\n https://robotframework.org/robotframework/latest/' 

168 'RobotFrameworkUserGuide.html#try-except-syntax.') 

169 kws.append(obj12) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

170 obj13 = BlockKeywordInfo('RETURN', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

171 'To return from keywords, with optional return values.\n\nArguments: (optional) any.' 

172 '\n\n See `BuiltIn.RETURN` docs at\n https://robotframework.org/robotframework/' 

173 'latest/RobotFrameworkUserGuide.html#toc-entry-348.', 

174 'ROBOT', 'BuiltIn', '(optional) *values=') 

175 kws.append(obj13) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

176 obj14 = BlockKeywordInfo('IN', SELECTOR_FOR, 'ROBOT', 'BuiltIn', ARG_VALUES) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

177 kws.append(obj14) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

178 obj15 = BlockKeywordInfo('IN RANGE', SELECTOR_FOR, 'ROBOT', 'BuiltIn', ARG_VALUES) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

179 kws.append(obj15) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

180 obj16 = BlockKeywordInfo('IN ENUMERATE', SELECTOR_FOR, 'ROBOT', 'BuiltIn', ARG_VALUES) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

181 kws.append(obj16) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

182 obj17 = BlockKeywordInfo('IN ZIP', SELECTOR_FOR, 'ROBOT', 'BuiltIn', ARG_VALUES) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

183 kws.append(obj17) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

184 from robotide.context import APP 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

185 try: 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

186 robot_version = APP.robot_version 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

187 except AttributeError: 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

188 robot_version = b'7.0.1' 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

189 try: 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

190 rbt_version = int(str(robot_version, encoding='UTF-8').replace('.', '')) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

191 rbt_version = rbt_version if rbt_version > 99 else rbt_version * 10 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

192 except ValueError: 

193 rbt_version = 701 

194 var_note = "*NOTE:* This marker exists since version 7.0" + (f" and is an error to use because your " 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

195 f"version of Robot Framework is" 

196 f" {str(robot_version, encoding='UTF-8')}" 

197 if rbt_version < 700 else ".") 

198 var_doc = ('To create variables inside tests and user keywords.\n\nArguments: (scalar|list|dictionary) name of ' 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

199 'variable, value of appropriate type, (optional) separator=, (optional) scope=(LOCAL|TEST|' 

200 'SUITE|GLOBAL).\n\nThe separator is by default a space to join multiline string scalars, and should ' 

201 'be the last value.\n\nThe scope is LOCAL by default and should be the last value.' 

202 f'\n\n{var_note}\n\n See `BuiltIn.VAR` docs at\n https://robotframework.org/robotframework/latest/' 

203 'RobotFrameworkUserGuide.html#toc-entry-329.') 

204 

205 obj18 = BlockKeywordInfo('VAR', var_doc, 'ROBOT', 'BuiltIn', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

206 'name_of_variable', ARG_VALUES) 

207 kws.append(obj18) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

208 group_note = "*NOTE:* This marker exists since version 7.2" + (f" and is an error to use because your " 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

209 f"version of Robot Framework is" 

210 f" {str(robot_version, encoding='UTF-8')}" 

211 if rbt_version < 720 else ".") 

212 group_doc = ('The GROUP syntax allows grouping related keywords and control structures together. Must be closed' 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

213 ' with `END`.\n\nArguments: (optional) name of the group.\n\n' 

214 f'\n\n{group_note}\n\n See `BuiltIn.GROUP` docs at\n https://robotframework.org/robotframework/latest/' 

215 f'RobotFrameworkUserGuide.html#group-syntax') 

216 

217 obj19 = BlockKeywordInfo('GROUP', group_doc, 'ROBOT', 'BuiltIn', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

218 '*name_of_group') 

219 kws.append(obj19) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

220 return kws 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

221 

222 def _get_default_libraries(self): 1aV

223 default_libs = {} 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

224 for libsetting in self._settings['auto imports'] + ['BuiltIn']: 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

225 name, args = self._get_name_and_args(libsetting) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

226 default_libs[name] = self._get_library(name, args) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

227 return default_libs 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

228 

229 @staticmethod 1aV

230 def _get_name_and_args(libsetting): 1aV

231 parts = libsetting.split('|') 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

232 if len(parts) == 1: 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

233 return parts[0], None 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF

234 return parts[0], parts[1:] 1E

235 

236 

237class ExpiringCache(object): 1aV

238 

239 def __init__(self, timeout=0.5): 1aV

240 self._cache = {} 2a Y W X U S T mb+ , nbob- . pb/ : qbrbsbtbubd Z 0 ; = ? @ A e f g h i j k l m n o p q r s t u v B 1 2 4 C w x y [ ] ^ b zbAbvbc wbxbF _

241 self._timeout = timeout 2a Y W X U S T mb+ , nbob- . pb/ : qbrbsbtbubd Z 0 ; = ? @ A e f g h i j k l m n o p q r s t u v B 1 2 4 C w x y [ ] ^ b zbAbvbc wbxbF _

242 

243 def get(self, key): 1aV

244 if key in self._cache: 2a J BbCbDbEbY FbGbHbIbJbKbLbMbNbW X U S T ObK PbQbRbSbTbUbVbD WbL d Z 0 M XbYbZb0b1b2b3b4b5bN 6b7b8b9bA e f g h i j k l m n o p q r s t u v B O !b#b$b%b'b(b)b*b+b,b-b.b1 2 4 C w x y z zbAb9 /b:b;b=b) *

245 key_time, values = self._cache[key] 2a J BbCbDbEbY FbGbHbIbJbKbLbMbNbW X U S T ObK PbQbRbSbTbUbVbD WbL d Z 0 XbYbZb0b1b2b3b4b5b6b7b8b9be f g h i j k l m n o p q r s t u v !b#b$b%b'b(b)b*b+b,b-b.b1 2 4 w x y z zbAb9 /b:b;b=b)

246 if self._is_valid(key_time): 2a J BbCbDbEbY FbGbHbIbJbKbLbMbNbW X U S T ObK PbQbRbSbTbUbVbD WbL d Z 0 XbYbZb0b1b2b3b4b5b6b7b8b9be f g h i j k l m n o p q r s t u v !b#b$b%b'b(b)b*b+b,b-b.b1 2 4 w x y z zbAb9 /b:b;b=b)

247 return values 2a J BbCbDbEbY FbGbHbIbJbKbLbMbNbW X U S T ObK PbQbRbSbTbUbVbD WbL d Z 0 XbYbZb0b1b2b3b4b5b6b7b8b9be f g h i j k l m n o p q r s t u v !b#b$b%b'b(b)b*b+b,b-b.b1 2 4 w x y z zbAb9 /b:b;b=b)

248 return None 2a J Y W X U S T K D L d Z 0 M N A e f g h i j k l m n o p q r s t u v B O 1 2 C w x y z zb9 ) *

249 

250 def _is_valid(self, key_time): 1aV

251 return (time.time() - key_time) < self._timeout 2a J BbCbDbEbY FbGbHbIbJbKbLbMbNbW X U S T ObK PbQbRbSbTbUbVbD WbL d Z 0 XbYbZb0b1b2b3b4b5b6b7b8b9be f g h i j k l m n o p q r s t u v !b#b$b%b'b(b)b*b+b,b-b.b1 2 4 w x y z zbAb9 /b:b;b=b)

252 

253 def put(self, key, values): 1aV

254 self._cache[key] = (time.time(), values) 2a J Y W X U S T K D L d Z 0 M N A e f g h i j k l m n o p q r s t u v B O 1 2 C w x y z zbAb9 ) *

255 

256 def _get_from_cache(self, source, name): 1aV

257 try: 

258 return self._resource_files[name] 

259 except KeyError: 

260 path = normpath(os.path.join(os.path.dirname(source), name)) 

261 return self._resource_files[path]