mycli 1.29.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. doc/key_bindings.rst +65 -0
  2. mycli/AUTHORS +107 -0
  3. mycli/SPONSORS +31 -0
  4. mycli/__init__.py +3 -0
  5. mycli/clibuffer.py +55 -0
  6. mycli/clistyle.py +145 -0
  7. mycli/clitoolbar.py +52 -0
  8. mycli/compat.py +6 -0
  9. mycli/completion_refresher.py +153 -0
  10. mycli/config.py +334 -0
  11. mycli/key_bindings.py +135 -0
  12. mycli/lexer.py +11 -0
  13. mycli/magic.py +63 -0
  14. mycli/main.py +1468 -0
  15. mycli/myclirc +159 -0
  16. mycli/packages/__init__.py +0 -0
  17. mycli/packages/completion_engine.py +293 -0
  18. mycli/packages/filepaths.py +106 -0
  19. mycli/packages/paramiko_stub/__init__.py +31 -0
  20. mycli/packages/parseutils.py +263 -0
  21. mycli/packages/prompt_utils.py +53 -0
  22. mycli/packages/special/__init__.py +12 -0
  23. mycli/packages/special/dbcommands.py +154 -0
  24. mycli/packages/special/delimitercommand.py +77 -0
  25. mycli/packages/special/favoritequeries.py +62 -0
  26. mycli/packages/special/iocommands.py +540 -0
  27. mycli/packages/special/main.py +119 -0
  28. mycli/packages/special/utils.py +48 -0
  29. mycli/packages/tabular_output/__init__.py +0 -0
  30. mycli/packages/tabular_output/sql_format.py +63 -0
  31. mycli/packages/toolkit/__init__.py +0 -0
  32. mycli/packages/toolkit/fzf.py +45 -0
  33. mycli/packages/toolkit/history.py +52 -0
  34. mycli/sqlcompleter.py +1261 -0
  35. mycli/sqlexecute.py +449 -0
  36. mycli-1.29.1.dist-info/AUTHORS.rst +3 -0
  37. mycli-1.29.1.dist-info/LICENSE.txt +34 -0
  38. mycli-1.29.1.dist-info/METADATA +254 -0
  39. mycli-1.29.1.dist-info/RECORD +88 -0
  40. mycli-1.29.1.dist-info/WHEEL +5 -0
  41. mycli-1.29.1.dist-info/entry_points.txt +2 -0
  42. mycli-1.29.1.dist-info/top_level.txt +3 -0
  43. test/__init__.py +0 -0
  44. test/conftest.py +38 -0
  45. test/features/__init__.py +0 -0
  46. test/features/auto_vertical.feature +12 -0
  47. test/features/basic_commands.feature +19 -0
  48. test/features/connection.feature +35 -0
  49. test/features/crud_database.feature +30 -0
  50. test/features/crud_table.feature +49 -0
  51. test/features/db_utils.py +74 -0
  52. test/features/environment.py +145 -0
  53. test/features/fixture_data/help.txt +24 -0
  54. test/features/fixture_data/help_commands.txt +31 -0
  55. test/features/fixture_utils.py +28 -0
  56. test/features/iocommands.feature +47 -0
  57. test/features/named_queries.feature +24 -0
  58. test/features/specials.feature +7 -0
  59. test/features/steps/__init__.py +0 -0
  60. test/features/steps/auto_vertical.py +47 -0
  61. test/features/steps/basic_commands.py +98 -0
  62. test/features/steps/connection.py +53 -0
  63. test/features/steps/crud_database.py +109 -0
  64. test/features/steps/crud_table.py +118 -0
  65. test/features/steps/iocommands.py +97 -0
  66. test/features/steps/named_queries.py +87 -0
  67. test/features/steps/specials.py +27 -0
  68. test/features/steps/utils.py +12 -0
  69. test/features/steps/wrappers.py +105 -0
  70. test/features/wrappager.py +16 -0
  71. test/myclirc +161 -0
  72. test/mylogin.cnf +0 -0
  73. test/test.txt +1 -0
  74. test/test_clistyle.py +28 -0
  75. test/test_completion_engine.py +595 -0
  76. test/test_completion_refresher.py +88 -0
  77. test/test_config.py +202 -0
  78. test/test_dbspecial.py +35 -0
  79. test/test_main.py +555 -0
  80. test/test_naive_completion.py +53 -0
  81. test/test_parseutils.py +174 -0
  82. test/test_plan.wiki +38 -0
  83. test/test_prompt_utils.py +11 -0
  84. test/test_smart_completion_public_schema_only.py +397 -0
  85. test/test_special_iocommands.py +313 -0
  86. test/test_sqlexecute.py +288 -0
  87. test/test_tabular_output.py +108 -0
  88. test/utils.py +87 -0
doc/key_bindings.rst ADDED
@@ -0,0 +1,65 @@
1
+ *************
2
+ Key Bindings:
3
+ *************
4
+
5
+ Most key bindings are simply inherited from `prompt-toolkit <https://python-prompt-toolkit.readthedocs.io/en/master/index.html>`_ .
6
+
7
+ The following key bindings are special to mycli:
8
+
9
+ ###
10
+ F2
11
+ ###
12
+
13
+ Enable/Disable SmartCompletion Mode.
14
+
15
+ ###
16
+ F3
17
+ ###
18
+
19
+ Enable/Disable Multiline Mode.
20
+
21
+ ###
22
+ F4
23
+ ###
24
+
25
+ Toggle between Vi and Emacs mode.
26
+
27
+ ###
28
+ Tab
29
+ ###
30
+
31
+ Force autocompletion at cursor.
32
+
33
+ #######
34
+ C-space
35
+ #######
36
+
37
+ Initialize autocompletion at cursor.
38
+
39
+ If the autocompletion menu is not showing, display it with the appropriate completions for the context.
40
+
41
+ If the menu is showing, select the next completion.
42
+
43
+ #########
44
+ ESC Enter
45
+ #########
46
+
47
+ Introduce a line break in multi-line mode, or dispatch the command in single-line mode.
48
+
49
+ The sequence ESC-Enter is often sent by Alt-Enter.
50
+
51
+ ##################
52
+ C-x p (Emacs-mode)
53
+ ##################
54
+
55
+ Prettify and indent current statement, usually into multiple lines.
56
+
57
+ Only accepts buffers containing single SQL statements.
58
+
59
+ ##################
60
+ C-x u (Emacs-mode)
61
+ ##################
62
+
63
+ Unprettify and dedent current statement, usually into one line.
64
+
65
+ Only accepts buffers containing single SQL statements.
mycli/AUTHORS ADDED
@@ -0,0 +1,107 @@
1
+ Core Developers:
2
+ ----------------
3
+
4
+ * Thomas Roten
5
+ * Irina Truong
6
+ * Matheus Rosa
7
+ * Darik Gamble
8
+ * Dick Marinus
9
+ * Amjith Ramanujam
10
+
11
+ Contributors:
12
+ -------------
13
+
14
+ * 0xflotus
15
+ * Abirami P
16
+ * Adam Chainz
17
+ * Aljosha Papsch
18
+ * Andy Teijelo Pérez
19
+ * Angelo Lupo
20
+ * Artem Bezsmertnyi
21
+ * bitkeen
22
+ * bjarnagin
23
+ * BuonOmo
24
+ * caitinggui
25
+ * Carlos Afonso
26
+ * Casper Langemeijer
27
+ * chainkite
28
+ * Claude Becker
29
+ * Colin Caine
30
+ * cxbig
31
+ * Daniel Black
32
+ * Daniel West
33
+ * Daniël van Eeden
34
+ * Fabrizio Gennari
35
+ * François Pietka
36
+ * Frederic Aoustin
37
+ * Georgy Frolov
38
+ * Heath Naylor
39
+ * Huachao Mao
40
+ * Ishaan Bhimwal
41
+ * Jakub Boukal
42
+ * jbruno
43
+ * Jerome Provensal
44
+ * Jialong Liu
45
+ * Johannes Hoff
46
+ * John Sterling
47
+ * Jonathan Bruno
48
+ * Jonathan Lloyd
49
+ * Jonathan Slenders
50
+ * Kacper Kwapisz
51
+ * Karthikeyan Singaravelan
52
+ * kevinhwang91
53
+ * KITAGAWA Yasutaka
54
+ * Klaus Wünschel
55
+ * laixintao
56
+ * Lennart Weller
57
+ * Martijn Engler
58
+ * Massimiliano Torromeo
59
+ * Michał Górny
60
+ * Mike Palandra
61
+ * Mikhail Borisov
62
+ * Morgan Mitchell
63
+ * mrdeathless
64
+ * Nathan Huang
65
+ * Nicolas Palumbo
66
+ * Phil Cohen
67
+ * QiaoHou Peng
68
+ * Roland Walker
69
+ * Ryan Smith
70
+ * Scrappy Soft
71
+ * Seamile
72
+ * Shoma Suzuki
73
+ * spacewander
74
+ * Steve Robbins
75
+ * Takeshi D. Itoh
76
+ * Terje Røsten
77
+ * Terseus
78
+ * Tyler Kuipers
79
+ * ushuz
80
+ * William GARCIA
81
+ * xeron
82
+ * Yang Zou
83
+ * Yasuhiro Matsumoto
84
+ * Yuanchun Shang
85
+ * Zach DeCook
86
+ * Zane C. Bowers-Hadley
87
+ * zer09
88
+ * Zhaolong Zhu
89
+ * Zhidong
90
+ * Zhongyang Guan
91
+ * Arvind Mishra
92
+ * Kevin Schmeichel
93
+ * Mel Dafert
94
+ * Thomas Copper
95
+ * Will Wang
96
+ * Alfred Wingate
97
+ * Zhanze Wang
98
+ * Houston Wong
99
+ * Mohamed Rezk
100
+ * Ryosuke Kazami
101
+ * Cornel Cruceru
102
+
103
+
104
+ Created by:
105
+ -----------
106
+
107
+ Amjith Ramanujam
mycli/SPONSORS ADDED
@@ -0,0 +1,31 @@
1
+ Many thanks to the following Kickstarter backers.
2
+
3
+ * Tech Blue Software
4
+ * jweiland.net
5
+
6
+ # Silver Sponsors
7
+
8
+ * Whitane Tech
9
+ * Open Query Pty Ltd
10
+ * Prathap Ramamurthy
11
+ * Lincoln Loop
12
+
13
+ # Sponsors
14
+
15
+ * Nathan Taggart
16
+ * Iryna Cherniavska
17
+ * Sudaraka Wijesinghe
18
+ * www.mysqlfanboy.com
19
+ * Steve Robbins
20
+ * Norbert Spichtig
21
+ * orpharion bestheneme
22
+ * Daniel Black
23
+ * Anonymous
24
+ * Magnus udd
25
+ * Anonymous
26
+ * Lewis Peckover
27
+ * Cyrille Tabary
28
+ * Heath Naylor
29
+ * Ted Pennings
30
+ * Chris Anderton
31
+ * Jonathan Slenders
mycli/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ import importlib.metadata
2
+
3
+ __version__ = importlib.metadata.version("mycli")
mycli/clibuffer.py ADDED
@@ -0,0 +1,55 @@
1
+ from prompt_toolkit.enums import DEFAULT_BUFFER
2
+ from prompt_toolkit.filters import Condition
3
+ from prompt_toolkit.application import get_app
4
+ from .packages import special
5
+
6
+
7
+ def cli_is_multiline(mycli):
8
+ @Condition
9
+ def cond():
10
+ doc = get_app().layout.get_buffer_by_name(DEFAULT_BUFFER).document
11
+
12
+ if not mycli.multi_line:
13
+ return False
14
+ else:
15
+ return not _multiline_exception(doc.text)
16
+
17
+ return cond
18
+
19
+
20
+ def _multiline_exception(text):
21
+ orig = text
22
+ text = text.strip()
23
+
24
+ # Multi-statement favorite query is a special case. Because there will
25
+ # be a semicolon separating statements, we can't consider semicolon an
26
+ # EOL. Let's consider an empty line an EOL instead.
27
+ if text.startswith("\\fs"):
28
+ return orig.endswith("\n")
29
+
30
+ return (
31
+ # Special Command
32
+ text.startswith("\\")
33
+ or
34
+ # Delimiter declaration
35
+ text.lower().startswith("delimiter")
36
+ or
37
+ # Ended with the current delimiter (usually a semi-column)
38
+ text.endswith(special.get_current_delimiter())
39
+ or text.endswith("\\g")
40
+ or text.endswith("\\G")
41
+ or text.endswith(r"\e")
42
+ or text.endswith(r"\clip")
43
+ or
44
+ # Exit doesn't need semi-column`
45
+ (text == "exit")
46
+ or
47
+ # Quit doesn't need semi-column
48
+ (text == "quit")
49
+ or
50
+ # To all teh vim fans out there
51
+ (text == ":q")
52
+ or
53
+ # just a plain enter without any text
54
+ (text == "")
55
+ )
mycli/clistyle.py ADDED
@@ -0,0 +1,145 @@
1
+ import logging
2
+
3
+ import pygments.styles
4
+ from pygments.token import string_to_tokentype, Token
5
+ from pygments.style import Style as PygmentsStyle
6
+ from pygments.util import ClassNotFound
7
+ from prompt_toolkit.styles.pygments import style_from_pygments_cls
8
+ from prompt_toolkit.styles import merge_styles, Style
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ # map Pygments tokens (ptk 1.0) to class names (ptk 2.0).
13
+ TOKEN_TO_PROMPT_STYLE = {
14
+ Token.Menu.Completions.Completion.Current: "completion-menu.completion.current",
15
+ Token.Menu.Completions.Completion: "completion-menu.completion",
16
+ Token.Menu.Completions.Meta.Current: "completion-menu.meta.completion.current",
17
+ Token.Menu.Completions.Meta: "completion-menu.meta.completion",
18
+ Token.Menu.Completions.MultiColumnMeta: "completion-menu.multi-column-meta",
19
+ Token.Menu.Completions.ProgressButton: "scrollbar.arrow", # best guess
20
+ Token.Menu.Completions.ProgressBar: "scrollbar", # best guess
21
+ Token.SelectedText: "selected",
22
+ Token.SearchMatch: "search",
23
+ Token.SearchMatch.Current: "search.current",
24
+ Token.Toolbar: "bottom-toolbar",
25
+ Token.Toolbar.Off: "bottom-toolbar.off",
26
+ Token.Toolbar.On: "bottom-toolbar.on",
27
+ Token.Toolbar.Search: "search-toolbar",
28
+ Token.Toolbar.Search.Text: "search-toolbar.text",
29
+ Token.Toolbar.System: "system-toolbar",
30
+ Token.Toolbar.Arg: "arg-toolbar",
31
+ Token.Toolbar.Arg.Text: "arg-toolbar.text",
32
+ Token.Toolbar.Transaction.Valid: "bottom-toolbar.transaction.valid",
33
+ Token.Toolbar.Transaction.Failed: "bottom-toolbar.transaction.failed",
34
+ Token.Output.Header: "output.header",
35
+ Token.Output.OddRow: "output.odd-row",
36
+ Token.Output.EvenRow: "output.even-row",
37
+ Token.Output.Null: "output.null",
38
+ Token.Prompt: "prompt",
39
+ Token.Continuation: "continuation",
40
+ }
41
+
42
+ # reverse dict for cli_helpers, because they still expect Pygments tokens.
43
+ PROMPT_STYLE_TO_TOKEN = {v: k for k, v in TOKEN_TO_PROMPT_STYLE.items()}
44
+
45
+ # all tokens that the Pygments MySQL lexer can produce
46
+ OVERRIDE_STYLE_TO_TOKEN = {
47
+ "sql.comment": Token.Comment,
48
+ "sql.comment.multi-line": Token.Comment.Multiline,
49
+ "sql.comment.single-line": Token.Comment.Single,
50
+ "sql.comment.optimizer-hint": Token.Comment.Special,
51
+ "sql.escape": Token.Error,
52
+ "sql.keyword": Token.Keyword,
53
+ "sql.datatype": Token.Keyword.Type,
54
+ "sql.literal": Token.Literal,
55
+ "sql.literal.date": Token.Literal.Date,
56
+ "sql.symbol": Token.Name,
57
+ "sql.quoted-schema-object": Token.Name.Quoted,
58
+ "sql.quoted-schema-object.escape": Token.Name.Quoted.Escape,
59
+ "sql.constant": Token.Name.Constant,
60
+ "sql.function": Token.Name.Function,
61
+ "sql.variable": Token.Name.Variable,
62
+ "sql.number": Token.Number,
63
+ "sql.number.binary": Token.Number.Bin,
64
+ "sql.number.float": Token.Number.Float,
65
+ "sql.number.hex": Token.Number.Hex,
66
+ "sql.number.integer": Token.Number.Integer,
67
+ "sql.operator": Token.Operator,
68
+ "sql.punctuation": Token.Punctuation,
69
+ "sql.string": Token.String,
70
+ "sql.string.double-quouted": Token.String.Double,
71
+ "sql.string.escape": Token.String.Escape,
72
+ "sql.string.single-quoted": Token.String.Single,
73
+ "sql.whitespace": Token.Text,
74
+ }
75
+
76
+
77
+ def parse_pygments_style(token_name, style_object, style_dict):
78
+ """Parse token type and style string.
79
+
80
+ :param token_name: str name of Pygments token. Example: "Token.String"
81
+ :param style_object: pygments.style.Style instance to use as base
82
+ :param style_dict: dict of token names and their styles, customized to this cli
83
+
84
+ """
85
+ token_type = string_to_tokentype(token_name)
86
+ try:
87
+ other_token_type = string_to_tokentype(style_dict[token_name])
88
+ return token_type, style_object.styles[other_token_type]
89
+ except AttributeError:
90
+ return token_type, style_dict[token_name]
91
+
92
+
93
+ def style_factory(name, cli_style):
94
+ try:
95
+ style = pygments.styles.get_style_by_name(name)
96
+ except ClassNotFound:
97
+ style = pygments.styles.get_style_by_name("native")
98
+
99
+ prompt_styles = []
100
+ # prompt-toolkit used pygments tokens for styling before, switched to style
101
+ # names in 2.0. Convert old token types to new style names, for backwards compatibility.
102
+ for token in cli_style:
103
+ if token.startswith("Token."):
104
+ # treat as pygments token (1.0)
105
+ token_type, style_value = parse_pygments_style(token, style, cli_style)
106
+ if token_type in TOKEN_TO_PROMPT_STYLE:
107
+ prompt_style = TOKEN_TO_PROMPT_STYLE[token_type]
108
+ prompt_styles.append((prompt_style, style_value))
109
+ else:
110
+ # we don't want to support tokens anymore
111
+ logger.error("Unhandled style / class name: %s", token)
112
+ else:
113
+ # treat as prompt style name (2.0). See default style names here:
114
+ # https://github.com/jonathanslenders/python-prompt-toolkit/blob/master/prompt_toolkit/styles/defaults.py
115
+ prompt_styles.append((token, cli_style[token]))
116
+
117
+ override_style = Style([("bottom-toolbar", "noreverse")])
118
+ return merge_styles([style_from_pygments_cls(style), override_style, Style(prompt_styles)])
119
+
120
+
121
+ def style_factory_output(name, cli_style):
122
+ try:
123
+ style = pygments.styles.get_style_by_name(name).styles
124
+ except ClassNotFound:
125
+ style = pygments.styles.get_style_by_name("native").styles
126
+
127
+ for token in cli_style:
128
+ if token.startswith("Token."):
129
+ token_type, style_value = parse_pygments_style(token, style, cli_style)
130
+ style.update({token_type: style_value})
131
+ elif token in PROMPT_STYLE_TO_TOKEN:
132
+ token_type = PROMPT_STYLE_TO_TOKEN[token]
133
+ style.update({token_type: cli_style[token]})
134
+ elif token in OVERRIDE_STYLE_TO_TOKEN:
135
+ token_type = OVERRIDE_STYLE_TO_TOKEN[token]
136
+ style.update({token_type: cli_style[token]})
137
+ else:
138
+ # TODO: cli helpers will have to switch to ptk.Style
139
+ logger.error("Unhandled style / class name: %s", token)
140
+
141
+ class OutputStyle(PygmentsStyle):
142
+ default_style = ""
143
+ styles = style
144
+
145
+ return OutputStyle
mycli/clitoolbar.py ADDED
@@ -0,0 +1,52 @@
1
+ from prompt_toolkit.key_binding.vi_state import InputMode
2
+ from prompt_toolkit.application import get_app
3
+ from prompt_toolkit.enums import EditingMode
4
+ from .packages import special
5
+
6
+
7
+ def create_toolbar_tokens_func(mycli, show_fish_help):
8
+ """Return a function that generates the toolbar tokens."""
9
+
10
+ def get_toolbar_tokens():
11
+ result = [("class:bottom-toolbar", " ")]
12
+
13
+ if mycli.multi_line:
14
+ delimiter = special.get_current_delimiter()
15
+ result.append(
16
+ (
17
+ "class:bottom-toolbar",
18
+ " ({} [{}] will end the line) ".format("Semi-colon" if delimiter == ";" else "Delimiter", delimiter),
19
+ )
20
+ )
21
+
22
+ if mycli.multi_line:
23
+ result.append(("class:bottom-toolbar.on", "[F3] Multiline: ON "))
24
+ else:
25
+ result.append(("class:bottom-toolbar.off", "[F3] Multiline: OFF "))
26
+ if mycli.prompt_app.editing_mode == EditingMode.VI:
27
+ result.append(("class:bottom-toolbar.on", "Vi-mode ({})".format(_get_vi_mode())))
28
+
29
+ if mycli.toolbar_error_message:
30
+ result.append(("class:bottom-toolbar", " " + mycli.toolbar_error_message))
31
+ mycli.toolbar_error_message = None
32
+
33
+ if show_fish_help():
34
+ result.append(("class:bottom-toolbar", " Right-arrow to complete suggestion"))
35
+
36
+ if mycli.completion_refresher.is_refreshing():
37
+ result.append(("class:bottom-toolbar", " Refreshing completions..."))
38
+
39
+ return result
40
+
41
+ return get_toolbar_tokens
42
+
43
+
44
+ def _get_vi_mode():
45
+ """Get the current vi mode for display."""
46
+ return {
47
+ InputMode.INSERT: "I",
48
+ InputMode.NAVIGATION: "N",
49
+ InputMode.REPLACE: "R",
50
+ InputMode.REPLACE_SINGLE: "R",
51
+ InputMode.INSERT_MULTIPLE: "M",
52
+ }[get_app().vi_state.input_mode]
mycli/compat.py ADDED
@@ -0,0 +1,6 @@
1
+ """Platform and Python version compatibility support."""
2
+
3
+ import sys
4
+
5
+
6
+ WIN = sys.platform in ("win32", "cygwin")
@@ -0,0 +1,153 @@
1
+ import threading
2
+ from .packages.special.main import COMMANDS
3
+ from collections import OrderedDict
4
+
5
+ from .sqlcompleter import SQLCompleter
6
+ from .sqlexecute import SQLExecute, ServerSpecies
7
+
8
+
9
+ class CompletionRefresher(object):
10
+ refreshers = OrderedDict()
11
+
12
+ def __init__(self):
13
+ self._completer_thread = None
14
+ self._restart_refresh = threading.Event()
15
+
16
+ def refresh(self, executor, callbacks, completer_options=None):
17
+ """Creates a SQLCompleter object and populates it with the relevant
18
+ completion suggestions in a background thread.
19
+
20
+ executor - SQLExecute object, used to extract the credentials to connect
21
+ to the database.
22
+ callbacks - A function or a list of functions to call after the thread
23
+ has completed the refresh. The newly created completion
24
+ object will be passed in as an argument to each callback.
25
+ completer_options - dict of options to pass to SQLCompleter.
26
+
27
+ """
28
+ if completer_options is None:
29
+ completer_options = {}
30
+
31
+ if self.is_refreshing():
32
+ self._restart_refresh.set()
33
+ return [(None, None, None, "Auto-completion refresh restarted.")]
34
+ else:
35
+ self._completer_thread = threading.Thread(
36
+ target=self._bg_refresh, args=(executor, callbacks, completer_options), name="completion_refresh"
37
+ )
38
+ self._completer_thread.daemon = True
39
+ self._completer_thread.start()
40
+ return [(None, None, None, "Auto-completion refresh started in the background.")]
41
+
42
+ def is_refreshing(self):
43
+ return self._completer_thread and self._completer_thread.is_alive()
44
+
45
+ def _bg_refresh(self, sqlexecute, callbacks, completer_options):
46
+ completer = SQLCompleter(**completer_options)
47
+
48
+ # Create a new pgexecute method to populate the completions.
49
+ e = sqlexecute
50
+ executor = SQLExecute(
51
+ e.dbname,
52
+ e.user,
53
+ e.password,
54
+ e.host,
55
+ e.port,
56
+ e.socket,
57
+ e.charset,
58
+ e.local_infile,
59
+ e.ssl,
60
+ e.ssh_user,
61
+ e.ssh_host,
62
+ e.ssh_port,
63
+ e.ssh_password,
64
+ e.ssh_key_filename,
65
+ )
66
+
67
+ # If callbacks is a single function then push it into a list.
68
+ if callable(callbacks):
69
+ callbacks = [callbacks]
70
+
71
+ while 1:
72
+ for refresher in self.refreshers.values():
73
+ refresher(completer, executor)
74
+ if self._restart_refresh.is_set():
75
+ self._restart_refresh.clear()
76
+ break
77
+ else:
78
+ # Break out of while loop if the for loop finishes natually
79
+ # without hitting the break statement.
80
+ break
81
+
82
+ # Start over the refresh from the beginning if the for loop hit the
83
+ # break statement.
84
+ continue
85
+
86
+ for callback in callbacks:
87
+ callback(completer)
88
+
89
+
90
+ def refresher(name, refreshers=CompletionRefresher.refreshers):
91
+ """Decorator to add the decorated function to the dictionary of
92
+ refreshers. Any function decorated with a @refresher will be executed as
93
+ part of the completion refresh routine."""
94
+
95
+ def wrapper(wrapped):
96
+ refreshers[name] = wrapped
97
+ return wrapped
98
+
99
+ return wrapper
100
+
101
+
102
+ @refresher("databases")
103
+ def refresh_databases(completer, executor):
104
+ completer.extend_database_names(executor.databases())
105
+
106
+
107
+ @refresher("schemata")
108
+ def refresh_schemata(completer, executor):
109
+ # schemata - In MySQL Schema is the same as database. But for mycli
110
+ # schemata will be the name of the current database.
111
+ completer.extend_schemata(executor.dbname)
112
+ completer.set_dbname(executor.dbname)
113
+
114
+
115
+ @refresher("tables")
116
+ def refresh_tables(completer, executor):
117
+ table_columns_dbresult = list(executor.table_columns())
118
+ completer.extend_relations(table_columns_dbresult, kind="tables")
119
+ completer.extend_columns(table_columns_dbresult, kind="tables")
120
+
121
+
122
+ @refresher("users")
123
+ def refresh_users(completer, executor):
124
+ completer.extend_users(executor.users())
125
+
126
+
127
+ # @refresher('views')
128
+ # def refresh_views(completer, executor):
129
+ # completer.extend_relations(executor.views(), kind='views')
130
+ # completer.extend_columns(executor.view_columns(), kind='views')
131
+
132
+
133
+ @refresher("functions")
134
+ def refresh_functions(completer, executor):
135
+ completer.extend_functions(executor.functions())
136
+ if executor.server_info.species == ServerSpecies.TiDB:
137
+ completer.extend_functions(completer.tidb_functions, builtin=True)
138
+
139
+
140
+ @refresher("special_commands")
141
+ def refresh_special(completer, executor):
142
+ completer.extend_special_commands(COMMANDS.keys())
143
+
144
+
145
+ @refresher("show_commands")
146
+ def refresh_show_commands(completer, executor):
147
+ completer.extend_show_items(executor.show_candidates())
148
+
149
+
150
+ @refresher("keywords")
151
+ def refresh_keywords(completer, executor):
152
+ if executor.server_info.species == ServerSpecies.TiDB:
153
+ completer.extend_keywords(completer.tidb_keywords, replace=True)