import threading from .packages.special.main import COMMANDS from collections import OrderedDict from .sqlcompleter import SQLCompleter from .sqlexecute import SQLExecute, ServerSpecies class CompletionRefresher(object): refreshers = OrderedDict() def __init__(self): self._completer_thread = None self._restart_refresh = threading.Event() def refresh(self, executor, callbacks, completer_options=None): """Creates a SQLCompleter object and populates it with the relevant completion suggestions in a background thread. executor - SQLExecute object, used to extract the credentials to connect to the database. callbacks - A function or a list of functions to call after the thread has completed the refresh. The newly created completion object will be passed in as an argument to each callback. completer_options - dict of options to pass to SQLCompleter. """ if completer_options is None: completer_options = {} if self.is_refreshing(): self._restart_refresh.set() return [(None, None, None, 'Auto-completion refresh restarted.')] else: self._completer_thread = threading.Thread( target=self._bg_refresh, args=(executor, callbacks, completer_options), name='completion_refresh') self._completer_thread.daemon = True self._completer_thread.start() return [(None, None, None, 'Auto-completion refresh started in the background.')] def is_refreshing(self): return self._completer_thread and self._completer_thread.is_alive() def _bg_refresh(self, sqlexecute, callbacks, completer_options): completer = SQLCompleter(**completer_options) # Create a new pgexecute method to populate the completions. e = sqlexecute executor = SQLExecute(e.dbname, e.user, e.password, e.host, e.port, e.socket, e.charset, e.local_infile, e.ssl, e.ssh_user, e.ssh_host, e.ssh_port, e.ssh_password, e.ssh_key_filename) # If callbacks is a single function then push it into a list. if callable(callbacks): callbacks = [callbacks] while 1: for refresher in self.refreshers.values(): refresher(completer, executor) if self._restart_refresh.is_set(): self._restart_refresh.clear() break else: # Break out of while loop if the for loop finishes natually # without hitting the break statement. break # Start over the refresh from the beginning if the for loop hit the # break statement. continue for callback in callbacks: callback(completer) def refresher(name, refreshers=CompletionRefresher.refreshers): """Decorator to add the decorated function to the dictionary of refreshers. Any function decorated with a @refresher will be executed as part of the completion refresh routine.""" def wrapper(wrapped): refreshers[name] = wrapped return wrapped return wrapper @refresher('databases') def refresh_databases(completer, executor): completer.extend_database_names(executor.databases()) @refresher('schemata') def refresh_schemata(completer, executor): # schemata - In MySQL Schema is the same as database. But for mycli # schemata will be the name of the current database. completer.extend_schemata(executor.dbname) completer.set_dbname(executor.dbname) @refresher('tables') def refresh_tables(completer, executor): completer.extend_relations(executor.tables(), kind='tables') completer.extend_columns(executor.table_columns(), kind='tables') @refresher('users') def refresh_users(completer, executor): completer.extend_users(executor.users()) # @refresher('views') # def refresh_views(completer, executor): # completer.extend_relations(executor.views(), kind='views') # completer.extend_columns(executor.view_columns(), kind='views') @refresher('functions') def refresh_functions(completer, executor): completer.extend_functions(executor.functions()) if executor.server_info.species == ServerSpecies.TiDB: completer.extend_functions(completer.tidb_functions, builtin=True) @refresher('special_commands') def refresh_special(completer, executor): completer.extend_special_commands(COMMANDS.keys()) @refresher('show_commands') def refresh_show_commands(completer, executor): completer.extend_show_items(executor.show_candidates()) @refresher('keywords') def refresh_keywords(completer, executor): if executor.server_info.species == ServerSpecies.TiDB: completer.extend_keywords(completer.tidb_keywords, replace=True)