这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Internal
* Support only Python 3.9+ in `pyproject.toml`.
* Add linting suggestion to pull request template.
* Make CI names and properties more consistent.
* Enable typechecking for several files.
* Enable typechecking for most of the non-test codebase.
* CI: turn off fail-fast matrix strategy.
* Remove unused Python 2 compatibility code.
* Also run CI tests without installing SSH extra dependencies.
Expand Down
56 changes: 33 additions & 23 deletions mycli/completion_refresher.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
# type: ignore
from __future__ import annotations

from collections import OrderedDict
import threading
from typing import Callable

from mycli.packages.special.main import COMMANDS
from mycli.sqlcompleter import SQLCompleter
from mycli.sqlexecute import ServerSpecies, SQLExecute


class CompletionRefresher:
refreshers = OrderedDict()
refreshers: dict = {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing from OrderedDict to Dict didn't change behavior?

I'm trying to remember why this was an OrderedDict to begin with.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't change behavior because dict is ordered after Python 3.7. There are some small differences with OrderedDict per https://docs.python.org/3/library/collections.html#collections.OrderedDict , but one difference is that dict is supposed to be more performant for the common case.


def __init__(self):
self._completer_thread = None
def __init__(self) -> None:
self._completer_thread: threading.Thread | None = None
self._restart_refresh = threading.Event()

def refresh(self, executor, callbacks, completer_options=None):
def refresh(
self,
executor: SQLExecute,
callbacks: Callable | list[Callable],
completer_options: dict | None = None,
) -> list[tuple]:
"""Creates a SQLCompleter object and populates it with the relevant
completion suggestions in a background thread.

Expand All @@ -41,13 +46,18 @@ def refresh(self, executor, callbacks, completer_options=None):
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 is_refreshing(self) -> bool:
return bool(self._completer_thread and self._completer_thread.is_alive())

def _bg_refresh(self, sqlexecute, callbacks, completer_options):
def _bg_refresh(
self,
sqlexecute: SQLExecute,
callbacks: Callable | list[Callable],
completer_options: dict,
) -> None:
completer = SQLCompleter(**completer_options)

# Create a new pgexecute method to populate the completions.
# Create a new sqlexecute method to populate the completions.
e = sqlexecute
executor = SQLExecute(
e.dbname,
Expand Down Expand Up @@ -89,7 +99,7 @@ def _bg_refresh(self, sqlexecute, callbacks, completer_options):
callback(completer)


def refresher(name, refreshers=CompletionRefresher.refreshers):
def refresher(name: str, refreshers: dict = CompletionRefresher.refreshers) -> Callable:
"""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."""
Expand All @@ -102,54 +112,54 @@ def wrapper(wrapped):


@refresher("databases")
def refresh_databases(completer, executor):
def refresh_databases(completer: SQLCompleter, executor: SQLExecute) -> None:
completer.extend_database_names(executor.databases())


@refresher("schemata")
def refresh_schemata(completer, executor):
def refresh_schemata(completer: SQLCompleter, executor: SQLExecute) -> None:
# 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):
def refresh_tables(completer: SQLCompleter, executor: SQLExecute) -> None:
table_columns_dbresult = list(executor.table_columns())
completer.extend_relations(table_columns_dbresult, kind="tables")
completer.extend_columns(table_columns_dbresult, kind="tables")


@refresher("users")
def refresh_users(completer, executor):
def refresh_users(completer: SQLCompleter, executor: SQLExecute) -> None:
completer.extend_users(executor.users())


# @refresher('views')
# def refresh_views(completer, executor):
# def refresh_views(completer: SQLCompleter, executor: SQLExecute) -> None:
# completer.extend_relations(executor.views(), kind='views')
# completer.extend_columns(executor.view_columns(), kind='views')


@refresher("functions")
def refresh_functions(completer, executor):
def refresh_functions(completer: SQLCompleter, executor: SQLExecute) -> None:
completer.extend_functions(executor.functions())
if executor.server_info.species == ServerSpecies.TiDB:
if executor.server_info and 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())
def refresh_special(completer: SQLCompleter, executor: SQLExecute) -> None:
completer.extend_special_commands(list(COMMANDS.keys()))


@refresher("show_commands")
def refresh_show_commands(completer, executor):
def refresh_show_commands(completer: SQLCompleter, executor: SQLExecute) -> None:
completer.extend_show_items(executor.show_candidates())


@refresher("keywords")
def refresh_keywords(completer, executor):
if executor.server_info.species == ServerSpecies.TiDB:
def refresh_keywords(completer: SQLCompleter, executor: SQLExecute) -> None:
if executor.server_info and executor.server_info.species == ServerSpecies.TiDB:
completer.extend_keywords(completer.tidb_keywords, replace=True)
17 changes: 9 additions & 8 deletions mycli/sqlcompleter.py
Original file line number Diff line number Diff line change
Expand Up @@ -949,17 +949,17 @@ def extend_keywords(self, keywords: list[str], replace: bool = False) -> None:
self.keywords.extend(keywords)
self.all_completions.update(keywords)

def extend_show_items(self, show_items: list[tuple]) -> None:
def extend_show_items(self, show_items: Iterable[tuple]) -> None:
for show_item in show_items:
self.show_items.extend(show_item)
self.all_completions.update(show_item)

def extend_change_items(self, change_items: list[tuple]) -> None:
def extend_change_items(self, change_items: Iterable[tuple]) -> None:
for change_item in change_items:
self.change_items.extend(change_item)
self.all_completions.update(change_item)

def extend_users(self, users: list[tuple]) -> None:
def extend_users(self, users: Iterable[tuple]) -> None:
for user in users:
self.users.extend(user)
self.all_completions.update(user)
Expand All @@ -975,7 +975,7 @@ def extend_schemata(self, schema: str | None) -> None:
metadata[schema] = {}
self.all_completions.update(schema)

def extend_relations(self, data: list[tuple[str]], kind: Literal['tables', 'views']) -> None:
def extend_relations(self, data: list[tuple[str, str]], kind: Literal['tables', 'views']) -> None:
"""Extend metadata for tables or views

:param data: list of (rel_name, ) tuples
Expand Down Expand Up @@ -1015,10 +1015,11 @@ def extend_columns(self, column_data: list[tuple[str, str]], kind: Literal['tabl
metadata[self.dbname][relname].append(column)
self.all_completions.add(column)

def extend_functions(self, func_data: Iterable[str], builtin: bool = False) -> None:
def extend_functions(self, func_data: list[str] | Generator[tuple[str, str]], builtin: bool = False) -> None:
# if 'builtin' is set this is extending the list of builtin functions
if builtin:
self.functions.extend(func_data)
if isinstance(func_data, list):
self.functions.extend(func_data)
return

# 'func_data' is a generator object. It can throw an exception while
Expand All @@ -1038,8 +1039,8 @@ def extend_functions(self, func_data: Iterable[str], builtin: bool = False) -> N
metadata[self.dbname][func[0]] = None
self.all_completions.add(func[0])

def set_dbname(self, dbname: str) -> None:
self.dbname = dbname
def set_dbname(self, dbname: str | None) -> None:
self.dbname = dbname or ''

def reset_completions(self) -> None:
self.databases: list[str] = []
Expand Down