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
« 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 1a'
17import queue 1a'
18from sqlite3 import OperationalError 1a'
19from threading import Thread 1a'
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'
28class LibraryManager(Thread): 1a'
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
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_
49 def _initiate_database_connection(self): 1a'
50 self._database = LibraryDatabase(self._database_name) 1a)*+,-./:;=(?]@[f
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
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$!#%
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)
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$!#
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))
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$!#%
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
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%
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)
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 []
156 def create_database(self): 1a'
157 self._messages.put(('create',), timeout=3) 1a)*+,-./:;=(?]@[f
159 def stop(self): 1a'
160 self._messages.put(False, timeout=3) 1a^(f_
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