Coverage for src/robotide/spec/librarymanager.py: 81%

131 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 1a'

17import queue 1a'

18from sqlite3 import OperationalError 1a'

19from threading import Thread 1a'

20 

21import robotide.robotapi 1a'

22from ..publish import RideLogException, RideLogMessage 1a'

23from ..spec.librarydatabase import LibraryDatabase 1a'

24from ..spec.libraryfetcher import get_import_result 1a'

25from ..spec.xmlreaders import get_path, SpecInitializer 1a'

26 

27 

28class LibraryManager(Thread): 1a'

29 

30 def __init__(self, database_name, spec_initializer=None): 1a'

31 self._database_name = database_name 1a)*+,-./:;=(?]@[f

32 self._database = None 1a)*+,-./:;=(?]@[f

33 self._messages = queue.Queue() 1a)*+,-./:;=(?]@[f

34 self._spec_initializer = spec_initializer or SpecInitializer() 1a)*+,-./:;=(?]@[f

35 Thread.__init__(self) 1a)*+,-./:;=(?]@[f

36 self.daemon = True 1a)*+,-./:;=(?]@[f

37 

38 def run(self): 1a'

39 self._initiate_database_connection() 1a)*+,-./:;=(?]@[f

40 while True: 1aophqrstu)v*+,-./lwxgyz:;=(?ABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678c]di9ej@[f

41 try: 1aophqrstu)v*+,-./lwxgyz:;=(?ABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678c]di9ej@[f

42 if not self._handle_message(): 1a^ophqrstu)v*+,-./lwxgyz:;=(?ABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678c]di9ej@[f_

43 break 1a^(m_

44 except Exception as err: 

45 msg = 'Library import handling threw an unexpected exception' 

46 RideLogException(message=msg, exception=err, level='WARN').publish() 

47 self._database.close() 1a^(m_

48 

49 def _initiate_database_connection(self): 1a'

50 self._database = LibraryDatabase(self._database_name) 1a)*+,-./:;=(?]@[f

51 

52 def get_new_connection_to_library_database(self): 1a'

53 library_database = LibraryDatabase(self._database_name) 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01kb2345678cdi9ejf

54 if self._database_name == ':memory:': 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01kb2345678cdi9ejf

55 # In memory database does not point to the right place. 

56 # this is here for unit tests. 

57 library_database.create_database() 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678cdi9ejf

58 return library_database 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01kb2345678cdi9ejf

59 

60 def _handle_message(self): 1a'

61 message = self._messages.get() 1a^ophqrstu)v*+,-./lwxgyz:;=(?ABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678c]di9ej@[fn$!#%_

62 if not message: 1a^ophqrstu)v*+,-./lwxgyz:;=(?ABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678c]di9ej@[fn$!#%_

63 return False 1a^(m_

64 msg_type = message[0] 1aophqrstu)v*+,-./lwxgyz:;=(?ABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678c]di9ej@[fn$!#%

65 if msg_type == 'fetch': 1aophqrstu)v*+,-./lwxgyz:;=(?ABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678c]di9ej@[fn$!#%

66 self._handle_fetch_keywords_message(message) 1akmbn$!#%

67 elif msg_type == 'insert': 1aophqrstu)v*+,-./lwxgyz:;=(?ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678c]di9ej@[f

68 self._handle_insert_keywords_message(message) 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678cdi9ejf

69 elif msg_type == 'create': 69 ↛ 71line 69 didn't jump to line 71 because the condition on line 69 was always true1a)*+,-./:;=(?]@[f

70 self._database.create_database() 1a)*+,-./:;=(?]@[f

71 return True 1aophqrstu)v*+,-./lwxgyz:;=(?ABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678cdi9ej@[fn$!#%

72 

73 def _handle_fetch_keywords_message(self, message): 1a'

74 _, library_name, library_args, callback = message 1akmbn$!#%

75 keywords = self._fetch_keywords(library_name, library_args) 1akmbn$!#%

76 self._update_database_and_call_callback_if_needed( 1akmbn$!#%

77 (library_name, library_args), keywords, callback) 

78 

79 def _fetch_keywords(self, library_name, library_args): 1a'

80 try: 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678cdi9ejfn$!#%

81 doc_paths = os.getenv('RIDE_DOC_PATH') 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678cdi9ejfn$!#%

82 collection = [] 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678cdi9ejfn$!#%

83 path = get_path(library_name.replace('/', os.sep), os.path.abspath('.')) 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678cdi9ejfn$!#%

84 if path: 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678cdi9ejfn$!#%

85 results = get_import_result(path, library_args) 1aophqrstuvlwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678cdi9ejfn$!#%

86 if results: 86 ↛ 88line 86 didn't jump to line 88 because the condition on line 86 was always true1aophqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678cdi9ejfn%

87 return results 1aophqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01kmb2345678cdi9ejfn%

88 if doc_paths: 88 ↛ 95line 88 didn't jump to line 95 because the condition on line 88 was always true1gkcde

89 for p in doc_paths.split(','): 1agkcde

90 path = get_path(library_name.replace('/', os.sep), p.strip()) 1agkcde

91 if path: 91 ↛ 92line 91 didn't jump to line 92 because the condition on line 91 was never true1agkcde

92 results = get_import_result(path, library_args) 

93 if results: 

94 collection.extend(results) 

95 if collection: 95 ↛ 96line 95 didn't jump to line 96 because the condition on line 95 was never true1gcde

96 return collection 

97 raise robotide.robotapi.DataError 1gcde

98 except Exception as err: 1ahlgbcdiej$!#

99 try: 1ahlgbcdiej$!#

100 print('FAILED', library_name, err) 1ahlgbcdiej$!#

101 except IOError: 

102 pass 

103 kws = self._spec_initializer.init_from_spec(library_name) 1ahlgbcdiej$!#

104 if not kws: 1ahlgbcdiej$!#

105 msg = 'Importing test library "%s" failed' % library_name 1ahlgbcdiej!#

106 RideLogException( 1ahlgbcdiej!#

107 message=msg, exception=err, level='WARN').publish() 

108 return kws 1ahlgbcdiej$!#

109 

110 def _handle_insert_keywords_message(self, message): 1a'

111 _, library_name, library_args, result_queue = message 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678cdi9ejf

112 keywords = self._fetch_keywords(library_name, library_args) 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678cdi9ejf

113 self._insert(library_name, library_args, keywords, 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678cdi9ejf

114 lambda res: result_queue.put(res, timeout=3)) 

115 

116 def _insert(self, library_name, library_args, keywords, callback): 1a'

117 self._database.insert_library_keywords( 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01b2345678cdi9ejfn$!#%

118 library_name, library_args, keywords or []) 

119 self._call(callback, keywords) 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01b2345678cdi9ejfn$!#%

120 

121 def _update_database_and_call_callback_if_needed( 1a'

122 self, library_key, keywords, callback): 

123 db_keywords = self._database.fetch_library_keywords(*library_key) 1akmbn$!#%

124 try: 1akmbn$!#%

125 if not db_keywords or self._keywords_differ(keywords, db_keywords): 1akmbn$!#%

126 self._insert( 1bn$!#%

127 library_key[0], library_key[1], keywords, callback) 

128 else: 

129 self._database.update_library_timestamp(*library_key) 1akmb

130 except OperationalError: 

131 pass 

132 

133 @staticmethod 1a'

134 def _call(callback, *args): 1a'

135 try: 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01b2345678cdi9ejfn$!#%

136 callback(*args) 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ01b2345678cdi9ejfn$!#%

137 except Exception as err: 1%

138 msg = 'Library import callback threw an unexpected exception' 1%

139 RideLogException(message=msg, exception=err, level='WARN').publish() 1%

140 

141 def fetch_keywords(self, library_name, library_args, callback): 1a'

142 self._messages.put(('fetch', library_name, library_args, callback), 1kbn$!#%

143 timeout=3) 

144 

145 def get_and_insert_keywords(self, library_name, library_args): 1a'

146 result_queue = queue.Queue(maxsize=1) 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678cdi9ejf

147 self._messages.put( 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678cdi9ejf

148 ('insert', library_name, library_args, result_queue), timeout=3) 

149 try: 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678cdi9ejf

150 return result_queue.get(timeout=5) 1aophqrstuvlwxgyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678cdi9ejf

151 except queue.Empty as e: 

152 RideLogMessage(u'Failed to read keywords from library db: {}' 

153 .format(str(e))).publish() 

154 return [] 

155 

156 def create_database(self): 1a'

157 self._messages.put(('create',), timeout=3) 1a)*+,-./:;=(?]@[f

158 

159 def stop(self): 1a'

160 self._messages.put(False, timeout=3) 1a^(f_

161 

162 @staticmethod 1a'

163 def _keywords_differ(keywords1, keywords2): 1a'

164 if keywords1 != keywords2 and None in (keywords1, keywords2): 164 ↛ 165line 164 didn't jump to line 165 because the condition on line 164 was never true1akmbn

165 return True 

166 if len(keywords1) != len(keywords2): 166 ↛ 167line 166 didn't jump to line 167 because the condition on line 166 was never true1akmbn

167 return True 

168 for k1, k2 in zip(keywords1, keywords2): 1akmbn

169 if k1.name != k2.name: 169 ↛ 170line 169 didn't jump to line 170 because the condition on line 169 was never true1akmbn

170 return True 

171 if k1.doc != k2.doc: 171 ↛ 172line 171 didn't jump to line 172 because the condition on line 171 was never true1akmbn

172 return True 

173 if k1.arguments != k2.arguments: 173 ↛ 174line 173 didn't jump to line 174 because the condition on line 173 was never true1akmbn

174 return True 

175 if k1.source != k2.source: 175 ↛ 176line 175 didn't jump to line 176 because the condition on line 175 was never true1akmbn

176 return True 

177 return False 1akmbn