mycli 2.5.1__tar.gz → 2.7.0__tar.gz

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 (183) hide show
  1. {mycli-2.5.1/mycli.egg-info → mycli-2.7.0}/PKG-INFO +16 -21
  2. {mycli-2.5.1 → mycli-2.7.0}/README.md +2 -1
  3. {mycli-2.5.1 → mycli-2.7.0}/changelog.md +68 -0
  4. mycli-2.7.0/doc/screenshots/total_histogram.png +0 -0
  5. mycli-2.7.0/doc/transforms.md +128 -0
  6. {mycli-2.5.1 → mycli-2.7.0}/mycli/TIPS +12 -2
  7. {mycli-2.5.1 → mycli-2.7.0}/mycli/app_state.py +11 -0
  8. {mycli-2.5.1 → mycli-2.7.0}/mycli/cli_runner.py +4 -4
  9. {mycli-2.5.1 → mycli-2.7.0}/mycli/client.py +12 -1
  10. {mycli-2.5.1 → mycli-2.7.0}/mycli/client_commands.py +1 -1
  11. {mycli-2.5.1 → mycli-2.7.0}/mycli/client_query.py +1 -0
  12. {mycli-2.5.1 → mycli-2.7.0}/mycli/clistyle.py +1 -0
  13. mycli-2.7.0/mycli/compat.py +27 -0
  14. {mycli-2.5.1 → mycli-2.7.0}/mycli/completion_refresher.py +5 -0
  15. {mycli-2.5.1 → mycli-2.7.0}/mycli/main_modes/checkup.py +34 -21
  16. {mycli-2.5.1 → mycli-2.7.0}/mycli/main_modes/repl.py +22 -7
  17. {mycli-2.5.1 → mycli-2.7.0}/mycli/myclirc +28 -0
  18. {mycli-2.5.1 → mycli-2.7.0}/mycli/output.py +26 -1
  19. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/completion_engine.py +37 -5
  20. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/hybrid_redirection.py +17 -4
  21. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/polars_transform.py +144 -35
  22. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/special/dsn_aliases.py +2 -0
  23. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/special/iocommands.py +4 -4
  24. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/special/main.py +11 -11
  25. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/sqlresult.py +8 -1
  26. {mycli-2.5.1 → mycli-2.7.0}/mycli/schema_prefetcher.py +8 -0
  27. {mycli-2.5.1 → mycli-2.7.0}/mycli/sqlcompleter.py +85 -7
  28. {mycli-2.5.1 → mycli-2.7.0}/mycli/sqlexecute.py +19 -0
  29. {mycli-2.5.1 → mycli-2.7.0}/mycli/types.py +6 -0
  30. {mycli-2.5.1 → mycli-2.7.0/mycli.egg-info}/PKG-INFO +16 -21
  31. {mycli-2.5.1 → mycli-2.7.0}/mycli.egg-info/SOURCES.txt +1 -0
  32. {mycli-2.5.1 → mycli-2.7.0}/mycli.egg-info/requires.txt +13 -19
  33. {mycli-2.5.1 → mycli-2.7.0}/mycli.egg-info/scm_file_list.json +131 -130
  34. mycli-2.7.0/mycli.egg-info/scm_version.json +8 -0
  35. {mycli-2.5.1 → mycli-2.7.0}/pyproject.toml +20 -22
  36. {mycli-2.5.1 → mycli-2.7.0}/test/myclirc +69 -43
  37. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_app_state.py +72 -0
  38. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_checkup.py +18 -10
  39. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_client.py +100 -0
  40. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_client_query.py +12 -2
  41. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_clistyle.py +12 -1
  42. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_completer_use_switch.py +3 -0
  43. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_completion_engine.py +40 -4
  44. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_completion_refresher.py +4 -0
  45. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_hybrid_redirection.py +50 -2
  46. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_main_modes_repl.py +140 -12
  47. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_naive_completion.py +13 -0
  48. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_output.py +90 -1
  49. mycli-2.7.0/test/pytests/test_polars_transform.py +868 -0
  50. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_schema_prefetcher.py +11 -0
  51. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_smart_completion_public_schema_only.py +143 -0
  52. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_special_main.py +3 -3
  53. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_sqlcompleter.py +90 -0
  54. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_sqlexecute.py +39 -0
  55. mycli-2.5.1/doc/transforms.md +0 -98
  56. mycli-2.5.1/mycli/compat.py +0 -5
  57. mycli-2.5.1/mycli.egg-info/scm_version.json +0 -8
  58. mycli-2.5.1/test/pytests/test_polars_transform.py +0 -405
  59. {mycli-2.5.1 → mycli-2.7.0}/.git-blame-ignore-revs +0 -0
  60. {mycli-2.5.1 → mycli-2.7.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  61. {mycli-2.5.1 → mycli-2.7.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  62. {mycli-2.5.1 → mycli-2.7.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  63. {mycli-2.5.1 → mycli-2.7.0}/.github/dependabot.yml +0 -0
  64. {mycli-2.5.1 → mycli-2.7.0}/.github/workflows/ci.yml +0 -0
  65. {mycli-2.5.1 → mycli-2.7.0}/.github/workflows/lint.yml +0 -0
  66. {mycli-2.5.1 → mycli-2.7.0}/.github/workflows/publish.yml +0 -0
  67. {mycli-2.5.1 → mycli-2.7.0}/.github/workflows/typecheck.yml +0 -0
  68. {mycli-2.5.1 → mycli-2.7.0}/.gitignore +0 -0
  69. {mycli-2.5.1 → mycli-2.7.0}/AGENTS.md +0 -0
  70. {mycli-2.5.1 → mycli-2.7.0}/AUTHORS.rst +0 -0
  71. {mycli-2.5.1 → mycli-2.7.0}/CONTRIBUTING.md +0 -0
  72. {mycli-2.5.1 → mycli-2.7.0}/LICENSE.txt +0 -0
  73. {mycli-2.5.1 → mycli-2.7.0}/MANIFEST.in +0 -0
  74. {mycli-2.5.1 → mycli-2.7.0}/SPONSORS.rst +0 -0
  75. {mycli-2.5.1 → mycli-2.7.0}/doc/key_bindings.rst +0 -0
  76. {mycli-2.5.1 → mycli-2.7.0}/doc/llm.md +0 -0
  77. {mycli-2.5.1 → mycli-2.7.0}/doc/screenshots/main.gif +0 -0
  78. {mycli-2.5.1 → mycli-2.7.0}/doc/screenshots/tables.png +0 -0
  79. {mycli-2.5.1 → mycli-2.7.0}/mycli/AUTHORS +0 -0
  80. {mycli-2.5.1 → mycli-2.7.0}/mycli/SPONSORS +0 -0
  81. {mycli-2.5.1 → mycli-2.7.0}/mycli/__init__.py +0 -0
  82. {mycli-2.5.1 → mycli-2.7.0}/mycli/clibuffer.py +0 -0
  83. {mycli-2.5.1 → mycli-2.7.0}/mycli/client_connection.py +0 -0
  84. {mycli-2.5.1 → mycli-2.7.0}/mycli/clitoolbar.py +0 -0
  85. {mycli-2.5.1 → mycli-2.7.0}/mycli/config.py +0 -0
  86. {mycli-2.5.1 → mycli-2.7.0}/mycli/constants.py +0 -0
  87. {mycli-2.5.1 → mycli-2.7.0}/mycli/key_bindings.py +0 -0
  88. {mycli-2.5.1 → mycli-2.7.0}/mycli/lexer.py +0 -0
  89. {mycli-2.5.1 → mycli-2.7.0}/mycli/main.py +0 -0
  90. {mycli-2.5.1 → mycli-2.7.0}/mycli/main_modes/batch.py +0 -0
  91. {mycli-2.5.1 → mycli-2.7.0}/mycli/main_modes/execute.py +0 -0
  92. {mycli-2.5.1 → mycli-2.7.0}/mycli/main_modes/list_dsn.py +0 -0
  93. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/__init__.py +0 -0
  94. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/batch_utils.py +0 -0
  95. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/cli_utils.py +0 -0
  96. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/filepaths.py +0 -0
  97. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/interactive_utils.py +0 -0
  98. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/key_binding_utils.py +0 -0
  99. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/ptoolkit/__init__.py +0 -0
  100. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/ptoolkit/fzf.py +0 -0
  101. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/ptoolkit/history.py +0 -0
  102. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/ptoolkit/utils.py +0 -0
  103. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/special/__init__.py +0 -0
  104. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/special/dbcommands.py +0 -0
  105. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/special/delimitercommand.py +0 -0
  106. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/special/favoritequeries.py +0 -0
  107. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/special/llm.py +0 -0
  108. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/special/utils.py +0 -0
  109. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/sql_utils.py +0 -0
  110. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/string_utils.py +0 -0
  111. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/tabular_output/__init__.py +0 -0
  112. {mycli-2.5.1 → mycli-2.7.0}/mycli/packages/tabular_output/sql_format.py +0 -0
  113. {mycli-2.5.1 → mycli-2.7.0}/mycli/password_sources.py +0 -0
  114. {mycli-2.5.1 → mycli-2.7.0}/mycli/ssh_tunnel.py +0 -0
  115. {mycli-2.5.1 → mycli-2.7.0}/mycli/vault.py +0 -0
  116. {mycli-2.5.1 → mycli-2.7.0}/mycli.egg-info/dependency_links.txt +0 -0
  117. {mycli-2.5.1 → mycli-2.7.0}/mycli.egg-info/entry_points.txt +0 -0
  118. {mycli-2.5.1 → mycli-2.7.0}/mycli.egg-info/top_level.txt +0 -0
  119. {mycli-2.5.1 → mycli-2.7.0}/setup.cfg +0 -0
  120. {mycli-2.5.1 → mycli-2.7.0}/test/__init__.py +0 -0
  121. {mycli-2.5.1 → mycli-2.7.0}/test/features/__init__.py +0 -0
  122. {mycli-2.5.1 → mycli-2.7.0}/test/features/auto_vertical.feature +0 -0
  123. {mycli-2.5.1 → mycli-2.7.0}/test/features/basic_commands.feature +0 -0
  124. {mycli-2.5.1 → mycli-2.7.0}/test/features/connection.feature +0 -0
  125. {mycli-2.5.1 → mycli-2.7.0}/test/features/crud_database.feature +0 -0
  126. {mycli-2.5.1 → mycli-2.7.0}/test/features/crud_table.feature +0 -0
  127. {mycli-2.5.1 → mycli-2.7.0}/test/features/db_utils.py +0 -0
  128. {mycli-2.5.1 → mycli-2.7.0}/test/features/environment.py +0 -0
  129. {mycli-2.5.1 → mycli-2.7.0}/test/features/fixture_data/help_commands.txt +0 -0
  130. {mycli-2.5.1 → mycli-2.7.0}/test/features/fixture_utils.py +0 -0
  131. {mycli-2.5.1 → mycli-2.7.0}/test/features/iocommands.feature +0 -0
  132. {mycli-2.5.1 → mycli-2.7.0}/test/features/named_queries.feature +0 -0
  133. {mycli-2.5.1 → mycli-2.7.0}/test/features/specials.feature +0 -0
  134. {mycli-2.5.1 → mycli-2.7.0}/test/features/steps/__init__.py +0 -0
  135. {mycli-2.5.1 → mycli-2.7.0}/test/features/steps/auto_vertical.py +0 -0
  136. {mycli-2.5.1 → mycli-2.7.0}/test/features/steps/basic_commands.py +0 -0
  137. {mycli-2.5.1 → mycli-2.7.0}/test/features/steps/connection.py +0 -0
  138. {mycli-2.5.1 → mycli-2.7.0}/test/features/steps/crud_database.py +0 -0
  139. {mycli-2.5.1 → mycli-2.7.0}/test/features/steps/crud_table.py +0 -0
  140. {mycli-2.5.1 → mycli-2.7.0}/test/features/steps/iocommands.py +0 -0
  141. {mycli-2.5.1 → mycli-2.7.0}/test/features/steps/named_queries.py +0 -0
  142. {mycli-2.5.1 → mycli-2.7.0}/test/features/steps/specials.py +0 -0
  143. {mycli-2.5.1 → mycli-2.7.0}/test/features/steps/utils.py +0 -0
  144. {mycli-2.5.1 → mycli-2.7.0}/test/features/steps/wrappers.py +0 -0
  145. {mycli-2.5.1 → mycli-2.7.0}/test/features/wrappager.py +0 -0
  146. {mycli-2.5.1 → mycli-2.7.0}/test/mylogin.cnf +0 -0
  147. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/conftest.py +0 -0
  148. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_batch_utils.py +0 -0
  149. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_cli_runner.py +0 -0
  150. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_cli_utils.py +0 -0
  151. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_clibuffer.py +0 -0
  152. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_client_commands.py +0 -0
  153. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_client_connection.py +0 -0
  154. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_clitoolbar.py +0 -0
  155. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_config.py +0 -0
  156. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_delimitercommand.py +0 -0
  157. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_dsn_aliases.py +0 -0
  158. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_favoritequeries.py +0 -0
  159. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_filepaths.py +0 -0
  160. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_interactive_utils.py +0 -0
  161. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_key_binding_utils.py +0 -0
  162. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_key_bindings.py +0 -0
  163. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_lexer.py +0 -0
  164. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_main.py +0 -0
  165. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_main_modes_batch.py +0 -0
  166. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_main_modes_execute.py +0 -0
  167. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_main_modes_list_dsn.py +0 -0
  168. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_password_sources.py +0 -0
  169. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_ptoolkit_fzf.py +0 -0
  170. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_ptoolkit_history.py +0 -0
  171. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_ptoolkit_utils.py +0 -0
  172. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_special_dbcommands.py +0 -0
  173. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_special_init.py +0 -0
  174. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_special_iocommands.py +0 -0
  175. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_special_llm.py +0 -0
  176. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_special_utils.py +0 -0
  177. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_sql_utils.py +0 -0
  178. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_sqlresult.py +0 -0
  179. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_ssh_tunnel.py +0 -0
  180. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_string_utils.py +0 -0
  181. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_tabular_output.py +0 -0
  182. {mycli-2.5.1 → mycli-2.7.0}/test/pytests/test_vault.py +0 -0
  183. {mycli-2.5.1 → mycli-2.7.0}/test/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycli
3
- Version: 2.5.1
3
+ Version: 2.7.0
4
4
  Summary: CLI for MySQL Database. With auto-completion and syntax highlighting.
5
5
  Author: Mycli Core Team
6
6
  License-Expression: BSD-3-Clause
@@ -13,18 +13,18 @@ Requires-Python: >=3.10
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE.txt
15
15
  License-File: AUTHORS.rst
16
- Requires-Dist: click~=8.3.1
16
+ Requires-Dist: click~=8.4.2
17
17
  Requires-Dist: clickdc~=0.1.1
18
- Requires-Dist: cryptography~=46.0.5
19
- Requires-Dist: Pygments~=2.19.2
18
+ Requires-Dist: cryptography~=49.0.0
19
+ Requires-Dist: Pygments~=2.20.0
20
20
  Requires-Dist: prompt_toolkit<4.0.0,>=3.0.41
21
21
  Requires-Dist: PyMySQL~=1.1.2
22
22
  Requires-Dist: sqlparse<0.6.0,>=0.3.0
23
- Requires-Dist: sqlglot~=30.8.0
24
- Requires-Dist: sqlglotc~=30.8.0
23
+ Requires-Dist: sqlglot~=30.12.0
24
+ Requires-Dist: sqlglotc~=30.12.0
25
25
  Requires-Dist: configobj~=5.0.9
26
- Requires-Dist: cli_helpers[styles]~=2.15.0
27
- Requires-Dist: wcwidth~=0.6.0
26
+ Requires-Dist: cli_helpers[styles]~=2.15.1
27
+ Requires-Dist: wcwidth~=0.8.2
28
28
  Requires-Dist: pyperclip~=1.11.0
29
29
  Requires-Dist: pycryptodomex~=3.23.0
30
30
  Requires-Dist: pyfzf~=0.3.1
@@ -32,32 +32,26 @@ Requires-Dist: rapidfuzz~=3.14.3
32
32
  Requires-Dist: keyring~=25.7.0
33
33
  Requires-Dist: yaspin~=3.4.0
34
34
  Provides-Extra: llm
35
- Requires-Dist: llm~=0.30.0; extra == "llm"
36
- Requires-Dist: pydantic_core~=2.41.5; extra == "llm"
37
- Requires-Dist: setuptools==82.*; extra == "llm"
38
- Requires-Dist: pip==26.*; extra == "llm"
35
+ Requires-Dist: llm~=0.31.1; extra == "llm"
36
+ Requires-Dist: setuptools==83.*; extra == "llm"
37
+ Requires-Dist: pip~=26.1.2; extra == "llm"
39
38
  Provides-Extra: dataframe
40
39
  Requires-Dist: polars~=1.42.1; extra == "dataframe"
41
40
  Requires-Dist: altair~=6.2.2; extra == "dataframe"
41
+ Requires-Dist: vl-convert-python~=1.9.0; extra == "dataframe"
42
42
  Provides-Extra: all
43
43
  Requires-Dist: mycli[dataframe,llm]; extra == "all"
44
44
  Provides-Extra: dev
45
45
  Requires-Dist: behave~=1.3.3; extra == "dev"
46
46
  Requires-Dist: coverage~=7.13.4; extra == "dev"
47
- Requires-Dist: mypy~=1.19.1; extra == "dev"
47
+ Requires-Dist: mypy~=2.3.0; extra == "dev"
48
48
  Requires-Dist: pexpect~=4.9.0; extra == "dev"
49
- Requires-Dist: pytest~=9.0.2; extra == "dev"
49
+ Requires-Dist: pytest~=9.1.1; extra == "dev"
50
50
  Requires-Dist: pytest-cov~=7.0.0; extra == "dev"
51
51
  Requires-Dist: pytest-random-order~=1.2.0; extra == "dev"
52
52
  Requires-Dist: tox~=4.35.0; extra == "dev"
53
53
  Requires-Dist: pdbpp~=0.11.7; extra == "dev"
54
- Requires-Dist: llm~=0.30.0; extra == "dev"
55
- Requires-Dist: pydantic_core~=2.41.5; extra == "dev"
56
- Requires-Dist: setuptools==82.*; extra == "dev"
57
- Requires-Dist: pip==26.*; extra == "dev"
58
54
  Requires-Dist: ruff~=0.15.0; extra == "dev"
59
- Requires-Dist: polars~=1.42.1; extra == "dev"
60
- Requires-Dist: altair~=6.2.2; extra == "dev"
61
55
  Dynamic: license-file
62
56
 
63
57
  # mycli
@@ -67,6 +61,7 @@ Dynamic: license-file
67
61
  A command line client for MySQL with auto-completion and syntax highlighting.
68
62
 
69
63
  Homepage: [https://mycli.net](https://mycli.net)
64
+
70
65
  Documentation: [https://mycli.net/docs](https://mycli.net/docs)
71
66
 
72
67
  ![Completion](https://raw.githubusercontent.com/dbcli/mycli/main/doc/screenshots/tables.png)
@@ -133,7 +128,7 @@ Features
133
128
  * Pretty print tabular data (with colors!).
134
129
  * Support for SSL connections
135
130
  * Shell-style trailing redirects with `$>`, `$>>` and `$|` operators.
136
- * [Polars](https://pola.rs) dataframe [transforms](doc/transforms.md) with `.|` and Parquet saves with `.>`.
131
+ * [Polars](https://pola.rs) dataframe [transforms and plots](doc/transforms.md) with `.|`, and Parquet saves with `.>`.
137
132
  * Support for querying LLMs with context derived from your schema using `/llm`.
138
133
  * Support for storing passwords in the system keyring.
139
134
 
@@ -5,6 +5,7 @@
5
5
  A command line client for MySQL with auto-completion and syntax highlighting.
6
6
 
7
7
  Homepage: [https://mycli.net](https://mycli.net)
8
+
8
9
  Documentation: [https://mycli.net/docs](https://mycli.net/docs)
9
10
 
10
11
  ![Completion](https://raw.githubusercontent.com/dbcli/mycli/main/doc/screenshots/tables.png)
@@ -71,7 +72,7 @@ Features
71
72
  * Pretty print tabular data (with colors!).
72
73
  * Support for SSL connections
73
74
  * Shell-style trailing redirects with `$>`, `$>>` and `$|` operators.
74
- * [Polars](https://pola.rs) dataframe [transforms](doc/transforms.md) with `.|` and Parquet saves with `.>`.
75
+ * [Polars](https://pola.rs) dataframe [transforms and plots](doc/transforms.md) with `.|`, and Parquet saves with `.>`.
75
76
  * Support for querying LLMs with context derived from your schema using `/llm`.
76
77
  * Support for storing passwords in the system keyring.
77
78
 
@@ -1,3 +1,71 @@
1
+ 2.7.0 (2026/07/25)
2
+ ==============
3
+
4
+ Features
5
+ ---------
6
+ * Show purpose in tabular `--checkup` output.
7
+ * Add optional dependencies to `--checkup`.
8
+ * Improve completions for `/command`s.
9
+ * Highlight indexed columns in completions with a suffix and/or a text style.
10
+
11
+
12
+ Bugfixes
13
+ ---------
14
+ * Ignoring `min_completion_trigger` only applies to first token after `/`.
15
+
16
+
17
+ Documentation
18
+ ---------
19
+ * Add `.|` and `.>` to TIPS.
20
+
21
+
22
+ Internal
23
+ ---------
24
+ * Update `cli_helpers` dependency to v2.15.1
25
+ * Update `click` to v8.4.2
26
+ * Update `cryptography` to v49.0.0
27
+ * Update `pygments` to v2.20.0
28
+ * Update `llm` to v0.31.1
29
+ * Update `setuptools` to v83.x
30
+ * Update `pip` to v26.1.2
31
+ * Update `pytest` to v9.1.1
32
+ * Add dependency cooldown period
33
+ * Exclude `cli_helpers` and `openai` from cooldown
34
+ * Update `sqlglot`/`sqlglotc` to v30.12.0
35
+ * Update `wcwidth` to v0.8.2
36
+ * Remove duplication in dev dependencies.
37
+ * Update `mypy` to v2.3.0.
38
+
39
+
40
+ 2.6.1 (2026/07/24)
41
+ ==============
42
+
43
+ Features
44
+ ---------
45
+ * Subcommand completions for the `/dsn` command.
46
+ * Allow file target of `$>` redirection to be quoted.
47
+ * Display of inline plots returned from `.|` operations.
48
+ * Don't attempt inline plots in the Windows console.
49
+ * Save plots, like parquets, with the `.>` operator.
50
+ * Display many native Python types returned from `.|` operations.
51
+
52
+
53
+ Bug Fixes
54
+ ---------
55
+ * Keep Vault username and password fields from being confused.
56
+ * Allow `\g` to terminate `.>` expressions.
57
+
58
+
59
+ Documentation
60
+ ---------
61
+ * `README.md` formatting.
62
+
63
+
64
+ Internal
65
+ ---------
66
+ * Sync test myclirc file closer to package myclirc file.
67
+
68
+
1
69
  2.5.1 (2026/07/22)
2
70
  ==============
3
71
 
@@ -0,0 +1,128 @@
1
+ # Transforms, Plots, and Parquets with Polars Dataframes
2
+
3
+ ## Installing
4
+
5
+ Install mycli with dataframe support using:
6
+
7
+ ```bash
8
+ pip install --upgrade 'mycli[dataframe]'
9
+ ```
10
+
11
+ or install these libraries separately:
12
+
13
+ * [`polars`](https://pypi.org/project/polars/)
14
+ * [`altair`](https://pypi.org/project/altair/)
15
+ * [`vl-convert-python`](https://pypi.org/project/vl-convert-python/)
16
+
17
+ ## BETA STATUS
18
+
19
+ Dataframe transforms and plots are new and experimental. The interface and
20
+ functionality may still change.
21
+
22
+ Here are some known limitations:
23
+
24
+ * transforms can't be composed with `$|` shell redirection
25
+ * composing multiple transform operations is not supported
26
+ * results from `UNION`s may be unable to be transformed
27
+
28
+ And there are inherent limitations to the post-processing model: the entire
29
+ SQL result must be transferred from the server and loaded into local memory.
30
+
31
+ ## Transforming
32
+
33
+ In the interactive REPL, append the `.|` operator and a Python expression to a
34
+ single SQL statement. The Python expression receives
35
+
36
+ * `df`, a Polars `DataFrame`
37
+ * `pl`, the Polars module
38
+ * `alt`, the Altair module
39
+
40
+ Spaces may be required around the `.|` operator.
41
+
42
+ Transform example:
43
+
44
+ ```sql
45
+ SELECT * FROM orders .| df.group_by('customer_id').len();
46
+ ```
47
+
48
+ which is equivalent to this native SQL:
49
+
50
+ ```sql
51
+ SELECT customer_id, COUNT(1) AS len FROM orders GROUP BY customer_id;
52
+ ```
53
+
54
+ Transform expressions run with normal Python privileges, and expressions
55
+ should not be run from untrusted sources.
56
+
57
+ If the transform operation returns a Polars `DataFrame` or `Series`, or a
58
+ Python type which can be rendered as a table, the result is rendered by mycli
59
+ as tabular output. `None` return values will be silently ignored, and other
60
+ return types will result in a warning message.
61
+
62
+ Transform expressions are useful for operations such as medians which
63
+ cannot be done (or are awkward) in SQL. Example:
64
+
65
+ ```sql
66
+ SELECT * FROM orders .| df.describe();
67
+ ```
68
+
69
+ ## Plotting
70
+
71
+ If the dataframe transform operation returns an Altair plot, the result can
72
+ be rendered as an inline PNG in many terminals.
73
+
74
+ Example:
75
+
76
+ ```sql
77
+ SELECT * FROM orders .| df['total'].plot.hist();
78
+ ```
79
+
80
+ <img src="https://raw.githubusercontent.com/dbcli/mycli/main/doc/screenshots/total_histogram.png" height=400>
81
+
82
+ Image size, display protocol, and other properties can be configured in
83
+ the `[dataframe]` section of `~/.myclirc`.
84
+
85
+ ## Saving
86
+
87
+ A query result, transformed `DataFrame`, or transformed `Series` can be
88
+ written directly to a Parquet file with the `.>` operator. An Altair plot can
89
+ also be written to a file with the same operator.
90
+
91
+ Save example:
92
+
93
+ ```sql
94
+ SELECT * FROM orders .> orders.parquet;
95
+ ```
96
+
97
+ The `.>` operator must be last, requires a `.parquet`, `.png`, `.pdf`, `.svg`,
98
+ or `.html` file extension on the destination, and overwrites any existing file.
99
+
100
+ Spaces may be required around the operator. Destination paths containing
101
+ whitespace must be quoted. A successful write reports its destination and row
102
+ count if appropriate.
103
+
104
+ When saving a plot, the format is deduced from the file extension.
105
+
106
+ When `post_redirect_command` is set in `~/.myclirc`, the given command runs
107
+ after a successful file save.
108
+
109
+ `.>` cannot be combined with the `\x` or `\G` special display terminators.
110
+
111
+ ## Combining
112
+
113
+ Parquet saves may be combined with dataframe transforms. Again, the save
114
+ operator `.>` must be the last operator.
115
+
116
+ Combined transform and save examples:
117
+
118
+ ```sql
119
+ SELECT * FROM orders .| df.group_by('customer_id').len() .> customer_counts.parquet;
120
+ ```
121
+
122
+ ```sql
123
+ SELECT * FROM orders .| df['order_id'] .> order_ids.parquet;
124
+ ```
125
+
126
+ ```sql
127
+ SELECT * FROM orders .| df['total'].plot.hist() .> total_histogram.png;
128
+ ```
@@ -144,6 +144,8 @@ display output in an interactive explorer \x at the end of a query!
144
144
 
145
145
  run SQL scripts in batch mode using the standard input!
146
146
 
147
+ indexed columns are tagged with a "*" in completion menus!
148
+
147
149
  ###
148
150
  ### keystrokes
149
151
  ###
@@ -296,8 +298,16 @@ customize password sources/precedence with "password_sources" in ~/.myclirc!
296
298
 
297
299
  redirect query output to a shell command with "$| <command>"!
298
300
 
299
- redirect query output to a file with "$> <filename>"!
301
+ redirect query output to a CSV file with "$> <filename>"!
300
302
 
301
- append query output to a file with "$>> <filename>"!
303
+ append query output to a CSV file with "$>> <filename>"!
302
304
 
303
305
  run a command after shell redirects with "post_redirect_command" in ~/.myclirc!
306
+
307
+ ###
308
+ ### transforms
309
+ ###
310
+
311
+ transform or plot query output with a dataframe using ".| df"!
312
+
313
+ save query output to a Parquet file with ".> output.parquet"!
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any
6
6
  from configobj import ConfigObj
7
7
 
8
8
  from mycli.config import strip_matching_quotes
9
+ from mycli.types import ImageProtocol
9
10
 
10
11
  if TYPE_CHECKING:
11
12
  from mycli.client import MyCli
@@ -46,6 +47,16 @@ def normalize_ssl_mode(
46
47
  return ssl_mode, error_notice
47
48
 
48
49
 
50
+ def normalize_image_protocol(image_protocol: str | None) -> tuple[ImageProtocol, str | None]:
51
+ if image_protocol == 'iterm2':
52
+ return 'iterm2', None
53
+ if image_protocol == 'kitty':
54
+ return 'kitty', None
55
+ if image_protocol in ('none', '', None):
56
+ return 'none', None
57
+ return 'none', f'Invalid config option provided for image_protocol ({image_protocol}); disabling.'
58
+
59
+
49
60
  def configure_prompt_state(
50
61
  mycli: MyCli,
51
62
  config: ConfigObj,
@@ -384,13 +384,13 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
384
384
  vault_config = mycli.config.get('vault_beta', {})
385
385
  vault_address = cli_args.vault_address or os.environ.get('VAULT_ADDR') or vault_config.get('address') or None
386
386
  vault_mount = cli_args.vault_mount or vault_config.get('default_mount') or None
387
- vault_field = cli_args.vault_password_field or vault_config.get('default_password_field') or DEFAULT_VAULT_PASSWORD_FIELD
387
+ vault_password_field = cli_args.vault_password_field or vault_config.get('default_password_field') or DEFAULT_VAULT_PASSWORD_FIELD
388
388
  vault_executable = vault_config.get('vault_executable') or DEFAULT_VAULT_EXECUTABLE
389
389
 
390
390
  def load_vault_password() -> str | None:
391
391
  try:
392
392
  return get_field_from_vault(
393
- vault_field,
393
+ vault_password_field,
394
394
  vault_secret,
395
395
  executable=vault_executable,
396
396
  mount=vault_mount,
@@ -406,11 +406,11 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
406
406
  vault_config = mycli.config.get('vault_beta', {})
407
407
  vault_address = cli_args.vault_address or os.environ.get('VAULT_ADDR') or vault_config.get('address') or None
408
408
  vault_mount = cli_args.vault_mount or vault_config.get('default_mount') or None
409
- vault_field = cli_args.vault_username_field or vault_config.get('default_username_field') or DEFAULT_VAULT_USERNAME_FIELD
409
+ vault_username_field = cli_args.vault_username_field or vault_config.get('default_username_field') or DEFAULT_VAULT_USERNAME_FIELD
410
410
  vault_executable = vault_config.get('vault_executable') or DEFAULT_VAULT_EXECUTABLE
411
411
  try:
412
412
  vault_username = get_field_from_vault(
413
- vault_field,
413
+ vault_username_field,
414
414
  cli_args.vault_secret,
415
415
  executable=vault_executable,
416
416
  mount=vault_mount,
@@ -17,6 +17,7 @@ from mycli.app_state import (
17
17
  configure_prompt_state,
18
18
  destructive_keywords_from_config,
19
19
  llm_prompt_truncation,
20
+ normalize_image_protocol,
20
21
  normalize_ssl_mode,
21
22
  )
22
23
  from mycli.client_commands import ClientCommandsMixin
@@ -145,6 +146,12 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
145
146
  self.null_string = c['main'].get('null_string')
146
147
  self.numeric_alignment = c['main'].get('numeric_alignment', 'right') or 'right'
147
148
  self.binary_display = c['main'].get('binary_display')
149
+ self.image_protocol, image_protocol_error = normalize_image_protocol(c['dataframe'].get('image_protocol'))
150
+ if image_protocol_error:
151
+ self.echo(image_protocol_error, err=True, fg='red')
152
+ self.plot_scale_factor = c['dataframe'].as_float('plot_scale_factor')
153
+ self.plot_ppi = c['dataframe'].as_int('plot_ppi')
154
+ self.plot_theme = c['dataframe'].get('plot_theme', 'carbong90') or 'carbong90'
148
155
  self.llm_prompt_field_truncate, self.llm_prompt_section_truncate = llm_prompt_truncation(c)
149
156
 
150
157
  self.ssl_mode, ssl_mode_error = normalize_ssl_mode(c, self.config_without_package_defaults)
@@ -176,6 +183,7 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
176
183
  self.initialize_logging()
177
184
 
178
185
  keyword_casing = c["main"].get("keyword_casing", "auto")
186
+ indexed_column_suffix = c['main'].get('indexed_column_suffix', '')
179
187
 
180
188
  self.highlight_preview = c['search'].as_bool('highlight_preview')
181
189
 
@@ -184,7 +192,10 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
184
192
  # Initialize completer.
185
193
  self.smart_completion = c["main"].as_bool("smart_completion")
186
194
  self.completer = SQLCompleter(
187
- self.smart_completion, supported_formats=self.main_formatter.supported_formats, keyword_casing=keyword_casing
195
+ self.smart_completion,
196
+ supported_formats=self.main_formatter.supported_formats,
197
+ keyword_casing=keyword_casing,
198
+ indexed_column_suffix=indexed_column_suffix,
188
199
  )
189
200
  self._completer_lock = threading.Lock()
190
201
 
@@ -52,7 +52,7 @@ class ClientCommandsMixin:
52
52
  "rehash",
53
53
  "/rehash",
54
54
  "Refresh auto-completions.",
55
- arg_type=ArgType.NO_QUERY,
55
+ arg_type=ArgType.NO_ARGUMENT,
56
56
  aliases=[SpecialCommandAlias("\\#", case_sensitive=False)],
57
57
  )
58
58
  special.register_special_command(
@@ -52,6 +52,7 @@ class ClientQueryMixin:
52
52
  "smart_completion": self.smart_completion,
53
53
  "supported_formats": self.main_formatter.supported_formats,
54
54
  "keyword_casing": self.completer.keyword_casing,
55
+ "indexed_column_suffix": self.completer.indexed_column_suffix,
55
56
  },
56
57
  )
57
58
 
@@ -15,6 +15,7 @@ logger = logging.getLogger(__name__)
15
15
  TOKEN_TO_PROMPT_STYLE: dict[Token, str] = {
16
16
  Token.Menu.Completions.Completion.Current: "completion-menu.completion.current",
17
17
  Token.Menu.Completions.Completion: "completion-menu.completion",
18
+ Token.Menu.Completions.Completion.Indexed: "completion-menu.completion.indexed",
18
19
  Token.Menu.Completions.Meta.Current: "completion-menu.meta.completion.current",
19
20
  Token.Menu.Completions.Meta: "completion-menu.meta.completion",
20
21
  Token.Menu.Completions.MultiColumnMeta: "completion-menu.multi-column-meta",
@@ -0,0 +1,27 @@
1
+ """Platform and Python version compatibility support."""
2
+
3
+ from importlib import import_module
4
+ import sys
5
+
6
+ WIN: bool = sys.platform in ("win32", "cygwin")
7
+
8
+
9
+ def _is_win32() -> bool:
10
+ return sys.platform == 'win32'
11
+
12
+
13
+ def is_windows_console(output: object | None) -> bool:
14
+ """Return whether output uses a native Windows console backend."""
15
+ if not _is_win32() or output is None:
16
+ return False
17
+
18
+ output_types = tuple(
19
+ getattr(import_module(module_name), class_name)
20
+ for module_name, class_name in (
21
+ ('prompt_toolkit.output.conemu', 'ConEmuOutput'),
22
+ ('prompt_toolkit.output.win32', 'Win32Output'),
23
+ ('prompt_toolkit.output.windows10', 'Windows10_Output'),
24
+ )
25
+ )
26
+
27
+ return isinstance(output, output_types)
@@ -167,6 +167,11 @@ def refresh_tables(completer: SQLCompleter, executor: SQLExecute) -> None:
167
167
  completer.extend_columns(table_columns_dbresult, kind="tables")
168
168
 
169
169
 
170
+ @refresher("indexed_columns")
171
+ def refresh_indexed_columns(completer: SQLCompleter, executor: SQLExecute) -> None:
172
+ completer.extend_indexed_columns(executor.indexed_columns())
173
+
174
+
170
175
  @refresher("foreign_keys")
171
176
  def refresh_foreign_keys(completer: SQLCompleter, executor: SQLExecute) -> None:
172
177
  completer.extend_foreign_keys(executor.foreign_keys())
@@ -6,6 +6,8 @@ import sys
6
6
  import urllib.error
7
7
  import urllib.request
8
8
 
9
+ from tabulate import tabulate
10
+
9
11
  from mycli.constants import REPO_URL
10
12
 
11
13
  PYPI_API_BASE = 'https://pypi.org/pypi'
@@ -24,15 +26,20 @@ def pypi_api_fetch(fragment: str) -> dict:
24
26
 
25
27
  def _dependencies_checkup() -> None:
26
28
  print('\n### Key Python dependencies:\n')
27
- for dependency in [
28
- 'cli_helpers',
29
- 'click',
30
- 'prompt_toolkit',
31
- 'pygments',
32
- 'pymysql',
33
- 'sqlglot',
34
- 'sqlglotc',
35
- 'tabulate',
29
+ table = []
30
+ for dependency, purpose in [
31
+ ('cli_helpers', 'required'),
32
+ ('click', 'required'),
33
+ ('prompt_toolkit', 'required'),
34
+ ('pygments', 'required'),
35
+ ('pymysql', 'required'),
36
+ ('sqlglot', 'required'),
37
+ ('sqlglotc', 'required'),
38
+ ('tabulate', 'required'),
39
+ ('llm', 'optional for /llm command'),
40
+ ('polars', 'optional for .| operator'),
41
+ ('altair', 'optional for .| operator'),
42
+ ('vl-convert-python', 'optional for .| operator'),
36
43
  ]:
37
44
  try:
38
45
  installed_version = importlib.metadata.version(dependency)
@@ -40,32 +47,38 @@ def _dependencies_checkup() -> None:
40
47
  installed_version = None
41
48
  pypi_profile = pypi_api_fetch(f'/{dependency}/json')
42
49
  latest_version = pypi_profile.get('info', {}).get('version', None)
43
- print(f'{dependency} version {installed_version} (latest {latest_version})')
50
+ table.append([dependency, installed_version, latest_version, purpose])
51
+ print(tabulate(table, headers=['library', 'version', 'latest', 'purpose']))
44
52
 
45
53
 
46
54
  def _executables_checkup() -> None:
47
55
  print('\n### External executables:\n')
48
- for executable in [
49
- 'less',
50
- 'fzf',
51
- 'pygmentize',
56
+ table = []
57
+ for executable, purpose in [
58
+ ('less', 'required for paging'),
59
+ ('fzf', r'optional for history search and \x'),
60
+ ('pygmentize', 'optional for history search'),
52
61
  ]:
62
+ # executable, purpose = external
53
63
  if shutil.which(executable):
54
- print(f'The "{executable}" executable was found — good!')
64
+ table.append([executable, 'found', purpose])
55
65
  else:
56
- print(f'The recommended "{executable}" executable was not found — some functionality will suffer.')
66
+ table.append([executable, 'MISSING', purpose])
67
+ print(tabulate(table, headers=['executable', 'status', 'purpose']))
57
68
 
58
69
 
59
70
  def _environment_checkup() -> None:
60
71
  print('\n### Environment variables:\n')
61
- for variable in [
62
- 'EDITOR',
63
- 'VISUAL',
72
+ table = []
73
+ for variable, purpose in [
74
+ ('EDITOR', r'optional for \edit and C-x C-e'),
75
+ ('VISUAL', r'optional for \edit and C-x C-e'),
64
76
  ]:
65
77
  if value := os.environ.get(variable):
66
- print(f'The ${variable} environment variable was set to "{value}" — good!')
78
+ table.append([f'${variable}', value, purpose])
67
79
  else:
68
- print(f'The ${variable} environment variable was not set — some functionality will suffer.')
80
+ table.append([f'${variable}', 'UNSET', purpose])
81
+ print(tabulate(table, headers=['variable', 'setting', 'purpose']))
69
82
 
70
83
 
71
84
  def _configuration_checkup(mycli) -> None:
@@ -112,7 +112,7 @@ def complete_while_typing_filter() -> bool:
112
112
  return True
113
113
  app = get_app()
114
114
  text = app.current_buffer.text.lstrip()
115
- if text.startswith('/') and not text.startswith('/*'):
115
+ if text.startswith('/') and not text.startswith('/*') and ' ' not in text:
116
116
  return True
117
117
  text_len = len(text)
118
118
  if text_len < MIN_COMPLETION_TRIGGER:
@@ -750,20 +750,35 @@ def _one_iteration(
750
750
  mycli.explorer_formatter.query = text
751
751
  if polars_transform is not None:
752
752
  assert polars_pipeline is not None
753
- if polars_pipeline.parquet_path is None:
754
- polars_result = run_polars_transform(polars_transform, results)
753
+ if polars_pipeline.output_path is None:
754
+ polars_result = run_polars_transform(
755
+ polars_transform,
756
+ results,
757
+ image_protocol=mycli.image_protocol,
758
+ plot_scale_factor=mycli.plot_scale_factor,
759
+ plot_ppi=mycli.plot_ppi,
760
+ plot_theme=mycli.plot_theme,
761
+ )
755
762
  else:
756
- polars_result = run_polars_transform(polars_transform, results, polars_pipeline.parquet_path)
757
- if polars_pipeline.parquet_path is None:
763
+ polars_result = run_polars_transform(
764
+ polars_transform,
765
+ results,
766
+ polars_pipeline.output_path,
767
+ image_protocol=mycli.image_protocol,
768
+ plot_scale_factor=mycli.plot_scale_factor,
769
+ plot_ppi=mycli.plot_ppi,
770
+ plot_theme=mycli.plot_theme,
771
+ )
772
+ if polars_pipeline.output_path is None:
758
773
  if polars_pipeline.output_mode == 'explorer':
759
774
  special.set_explorer_output(True)
760
775
  elif polars_pipeline.output_mode == 'expanded':
761
776
  special.set_expanded_output(True)
762
777
  _output_results(mycli, state, iter([polars_result]), start)
763
- if polars_pipeline.parquet_path is not None:
778
+ if polars_pipeline.output_path is not None:
764
779
  special.run_post_redirect_hook(
765
780
  mycli.post_redirect_command,
766
- polars_pipeline.parquet_path,
781
+ polars_pipeline.output_path,
767
782
  )
768
783
  else:
769
784
  _output_results(mycli, state, results, start)