Robot Framework Integrated Development Environment (RIDE)
librarymanager.py
Go to the documentation of this file.
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 
16 import os
17 import queue as Queue
18 from sqlite3 import OperationalError
19 from threading import Thread
20 
21 from ..publish import RideLogException, RideLogMessage
22 from ..spec.librarydatabase import LibraryDatabase
23 from ..spec.libraryfetcher import get_import_result
24 from ..spec.xmlreaders import get_path, SpecInitializer
25 
26 
27 class LibraryManager(Thread):
28 
29  def __init__(self, database_name, spec_initializer=None):
30  self._database_name_database_name = database_name
31  self._database_database = None
32  self._messages_messages = Queue.Queue()
33  self._spec_initializer_spec_initializer = spec_initializer or SpecInitializer()
34  Thread.__init__(self)
35  self.daemondaemon = True
36 
37  def run(self):
38  self._initiate_database_connection_initiate_database_connection()
39  while True:
40  try:
41  if not self._handle_message_handle_message():
42  break
43  except Exception as err:
44  msg = 'Library import handling threw an unexpected exception'
45  RideLogException(message=msg, exception=err, level='WARN').publish()
46  self._database_database.close()
47 
49  self._database_database = LibraryDatabase(self._database_name_database_name)
50 
52  library_database = LibraryDatabase(self._database_name_database_name)
53  if self._database_name_database_name == ':memory:':
54  # In memory database does not point to the right place..
55  # this is here for unit tests..
56  library_database.create_database()
57  return library_database
58 
59  def _handle_message(self):
60  message = self._messages_messages.get()
61  if not message:
62  return False
63  msg_type = message[0]
64  if msg_type == 'fetch':
65  self._handle_fetch_keywords_message_handle_fetch_keywords_message(message)
66  elif msg_type == 'insert':
67  self._handle_insert_keywords_message_handle_insert_keywords_message(message)
68  elif msg_type == 'create':
69  self._database_database.create_database()
70  return True
71 
72  def _handle_fetch_keywords_message(self, message):
73  _, library_name, library_args, callback = message
74  keywords = self._fetch_keywords_fetch_keywords(library_name, library_args)
75  self._update_database_and_call_callback_if_needed_update_database_and_call_callback_if_needed(
76  (library_name, library_args), keywords, callback)
77 
78  def _fetch_keywords(self, library_name, library_args):
79  try:
80  path = get_path(
81  library_name.replace('/', os.sep), os.path.abspath('.'))
82  return get_import_result(path, library_args)
83  except Exception as err:
84  try:
85  print('FAILED', library_name, err)
86  except IOError:
87  pass
88  kws = self._spec_initializer_spec_initializer.init_from_spec(library_name)
89  if not kws:
90  msg = 'Importing test library "%s" failed' % library_name
92  message=msg, exception=err, level='WARN').publish()
93  return kws
94 
95  def _handle_insert_keywords_message(self, message):
96  _, library_name, library_args, result_queue = message
97  keywords = self._fetch_keywords_fetch_keywords(library_name, library_args)
98  self._insert_insert(library_name, library_args, keywords,
99  lambda res: result_queue.put(res, timeout=3))
100 
101  def _insert(self, library_name, library_args, keywords, callback):
102  self._database_database.insert_library_keywords(
103  library_name, library_args, keywords or [])
104  self._call_call(callback, keywords)
105 
107  self, library_key, keywords, callback):
108  db_keywords = self._database_database.fetch_library_keywords(*library_key)
109  try:
110  if not db_keywords or self._keywords_differ_keywords_differ(keywords, db_keywords):
111  self._insert_insert(
112  library_key[0], library_key[1], keywords, callback)
113  else:
114  self._database_database.update_library_timestamp(*library_key)
115  except OperationalError:
116  pass
117 
118  def _call(self, callback, *args):
119  try:
120  callback(*args)
121  except Exception as err:
122  msg = 'Library import callback threw an unexpected exception'
123  RideLogException(message=msg, exception=err, level='WARN').publish()
124 
125  def fetch_keywords(self, library_name, library_args, callback):
126  self._messages_messages.put(('fetch', library_name, library_args, callback),
127  timeout=3)
128 
129  def get_and_insert_keywords(self, library_name, library_args):
130  result_queue = Queue.Queue(maxsize=1)
131  self._messages_messages.put(
132  ('insert', library_name, library_args, result_queue), timeout=3)
133  try:
134  return result_queue.get(timeout=5)
135  except Queue.Empty as e:
136  RideLogMessage(u'Failed to read keywords from library db: {}'
137  .format(str(e))).publish()
138  return []
139 
140  def create_database(self):
141  self._messages_messages.put(('create',), timeout=3)
142 
143  def stop(self):
144  self._messages_messages.put(False, timeout=3)
145 
146  def _keywords_differ(self, keywords1, keywords2):
147  if keywords1 != keywords2 and None in (keywords1, keywords2):
148  return True
149  if len(keywords1) != len(keywords2):
150  return True
151  for k1, k2 in zip(keywords1, keywords2):
152  if k1.name != k2.name:
153  return True
154  if k1.doc != k2.doc:
155  return True
156  # if k1.doc_format != k2.doc_format:
157  # return True
158  if k1.arguments != k2.arguments:
159  return True
160  if k1.source != k2.source:
161  return True
162  return False
This class represents a general purpose log message with a traceback appended to message text.
Definition: messages.py:117
This class represents a general purpose log message.
Definition: messages.py:96
def __init__(self, database_name, spec_initializer=None)
def _keywords_differ(self, keywords1, keywords2)
def _update_database_and_call_callback_if_needed(self, library_key, keywords, callback)
def fetch_keywords(self, library_name, library_args, callback)
def _fetch_keywords(self, library_name, library_args)
def get_and_insert_keywords(self, library_name, library_args)
def _insert(self, library_name, library_args, keywords, callback)
def get_import_result(path, args)
def get_path(name, basedir)
Definition: xmlreaders.py:96