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
« 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.
16import os 1aV
17import time 1aV
19from ..robotapi import normpath, ALIAS_MARKER 1aV
20from ..spec.iteminfo import BlockKeywordInfo 1aV
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
28class LibraryCache(object): 1aV
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 _
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 _
44 def expire(self): 1aV
45 self.__init__(self._settings, self._libraries_need_refresh_listener, 1aYWXUST+,-./:dZ0;=?@AefghijklmnopqrstuvB124Cwxy[]^b_
46 self._library_manager)
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
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
60 def get_all_cached_library_names(self): 1aV
61 return [name for name, _ in self._library_keywords] 2[b
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
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*
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)]
88 return self._library_keywords[key] 2a W X U S T D # $ % 8 ' ( z b 3 9 ) G ` { | } ~ abbbcb5 db6 c ebfbgbhbibjbkblb7 yb*
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*
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
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.')
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')
217 obj19 = BlockKeywordInfo('GROUP', group_doc, 'ROBOT', 'BuiltIn', 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF
218 '*name_of_group')
219 kws.append(obj19) 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF
220 return kws 1aJKDLdMNAefghijklmnopqrstuvBOCwxyzbHEIPQRGcF
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
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
237class ExpiringCache(object): 1aV
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 _
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 ) *
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)
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 ) *
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]