mycli 2.7.0__tar.gz → 2.8.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 (182) hide show
  1. {mycli-2.7.0 → mycli-2.8.0}/.github/workflows/publish.yml +1 -1
  2. {mycli-2.7.0 → mycli-2.8.0}/MANIFEST.in +1 -0
  3. {mycli-2.7.0/mycli.egg-info → mycli-2.8.0}/PKG-INFO +16 -1
  4. {mycli-2.7.0 → mycli-2.8.0}/changelog.md +16 -0
  5. {mycli-2.7.0 → mycli-2.8.0}/mycli/main_modes/repl.py +10 -2
  6. mycli-2.8.0/mycli/resources/completions/zsh/_mycli +66 -0
  7. {mycli-2.7.0 → mycli-2.8.0/mycli.egg-info}/PKG-INFO +16 -1
  8. {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/SOURCES.txt +3 -1
  9. {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/scm_file_list.json +3 -1
  10. mycli-2.8.0/mycli.egg-info/scm_version.json +8 -0
  11. {mycli-2.7.0 → mycli-2.8.0}/pyproject.toml +17 -0
  12. {mycli-2.7.0 → mycli-2.8.0}/test/features/environment.py +4 -18
  13. mycli-2.8.0/test/pytests/test_zsh_completion.py +118 -0
  14. mycli-2.7.0/mycli.egg-info/scm_version.json +0 -8
  15. {mycli-2.7.0 → mycli-2.8.0}/.git-blame-ignore-revs +0 -0
  16. {mycli-2.7.0 → mycli-2.8.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  17. {mycli-2.7.0 → mycli-2.8.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  18. {mycli-2.7.0 → mycli-2.8.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  19. {mycli-2.7.0 → mycli-2.8.0}/.github/dependabot.yml +0 -0
  20. {mycli-2.7.0 → mycli-2.8.0}/.github/workflows/ci.yml +0 -0
  21. {mycli-2.7.0 → mycli-2.8.0}/.github/workflows/lint.yml +0 -0
  22. {mycli-2.7.0 → mycli-2.8.0}/.github/workflows/typecheck.yml +0 -0
  23. {mycli-2.7.0 → mycli-2.8.0}/.gitignore +0 -0
  24. {mycli-2.7.0 → mycli-2.8.0}/AGENTS.md +0 -0
  25. {mycli-2.7.0 → mycli-2.8.0}/AUTHORS.rst +0 -0
  26. {mycli-2.7.0 → mycli-2.8.0}/CONTRIBUTING.md +0 -0
  27. {mycli-2.7.0 → mycli-2.8.0}/LICENSE.txt +0 -0
  28. {mycli-2.7.0 → mycli-2.8.0}/README.md +0 -0
  29. {mycli-2.7.0 → mycli-2.8.0}/SPONSORS.rst +0 -0
  30. {mycli-2.7.0 → mycli-2.8.0}/doc/key_bindings.rst +0 -0
  31. {mycli-2.7.0 → mycli-2.8.0}/doc/llm.md +0 -0
  32. {mycli-2.7.0 → mycli-2.8.0}/doc/screenshots/main.gif +0 -0
  33. {mycli-2.7.0 → mycli-2.8.0}/doc/screenshots/tables.png +0 -0
  34. {mycli-2.7.0 → mycli-2.8.0}/doc/screenshots/total_histogram.png +0 -0
  35. {mycli-2.7.0 → mycli-2.8.0}/doc/transforms.md +0 -0
  36. {mycli-2.7.0 → mycli-2.8.0}/mycli/AUTHORS +0 -0
  37. {mycli-2.7.0 → mycli-2.8.0}/mycli/SPONSORS +0 -0
  38. {mycli-2.7.0 → mycli-2.8.0}/mycli/TIPS +0 -0
  39. {mycli-2.7.0 → mycli-2.8.0}/mycli/__init__.py +0 -0
  40. {mycli-2.7.0 → mycli-2.8.0}/mycli/app_state.py +0 -0
  41. {mycli-2.7.0 → mycli-2.8.0}/mycli/cli_runner.py +0 -0
  42. {mycli-2.7.0 → mycli-2.8.0}/mycli/clibuffer.py +0 -0
  43. {mycli-2.7.0 → mycli-2.8.0}/mycli/client.py +0 -0
  44. {mycli-2.7.0 → mycli-2.8.0}/mycli/client_commands.py +0 -0
  45. {mycli-2.7.0 → mycli-2.8.0}/mycli/client_connection.py +0 -0
  46. {mycli-2.7.0 → mycli-2.8.0}/mycli/client_query.py +0 -0
  47. {mycli-2.7.0 → mycli-2.8.0}/mycli/clistyle.py +0 -0
  48. {mycli-2.7.0 → mycli-2.8.0}/mycli/clitoolbar.py +0 -0
  49. {mycli-2.7.0 → mycli-2.8.0}/mycli/compat.py +0 -0
  50. {mycli-2.7.0 → mycli-2.8.0}/mycli/completion_refresher.py +0 -0
  51. {mycli-2.7.0 → mycli-2.8.0}/mycli/config.py +0 -0
  52. {mycli-2.7.0 → mycli-2.8.0}/mycli/constants.py +0 -0
  53. {mycli-2.7.0 → mycli-2.8.0}/mycli/key_bindings.py +0 -0
  54. {mycli-2.7.0 → mycli-2.8.0}/mycli/lexer.py +0 -0
  55. {mycli-2.7.0 → mycli-2.8.0}/mycli/main.py +0 -0
  56. {mycli-2.7.0 → mycli-2.8.0}/mycli/main_modes/batch.py +0 -0
  57. {mycli-2.7.0 → mycli-2.8.0}/mycli/main_modes/checkup.py +0 -0
  58. {mycli-2.7.0 → mycli-2.8.0}/mycli/main_modes/execute.py +0 -0
  59. {mycli-2.7.0 → mycli-2.8.0}/mycli/main_modes/list_dsn.py +0 -0
  60. {mycli-2.7.0 → mycli-2.8.0}/mycli/myclirc +0 -0
  61. {mycli-2.7.0 → mycli-2.8.0}/mycli/output.py +0 -0
  62. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/__init__.py +0 -0
  63. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/batch_utils.py +0 -0
  64. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/cli_utils.py +0 -0
  65. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/completion_engine.py +0 -0
  66. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/filepaths.py +0 -0
  67. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/hybrid_redirection.py +0 -0
  68. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/interactive_utils.py +0 -0
  69. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/key_binding_utils.py +0 -0
  70. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/polars_transform.py +0 -0
  71. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/ptoolkit/__init__.py +0 -0
  72. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/ptoolkit/fzf.py +0 -0
  73. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/ptoolkit/history.py +0 -0
  74. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/ptoolkit/utils.py +0 -0
  75. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/__init__.py +0 -0
  76. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/dbcommands.py +0 -0
  77. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/delimitercommand.py +0 -0
  78. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/dsn_aliases.py +0 -0
  79. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/favoritequeries.py +0 -0
  80. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/iocommands.py +0 -0
  81. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/llm.py +0 -0
  82. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/main.py +0 -0
  83. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/utils.py +0 -0
  84. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/sql_utils.py +0 -0
  85. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/sqlresult.py +0 -0
  86. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/string_utils.py +0 -0
  87. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/tabular_output/__init__.py +0 -0
  88. {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/tabular_output/sql_format.py +0 -0
  89. {mycli-2.7.0 → mycli-2.8.0}/mycli/password_sources.py +0 -0
  90. {mycli-2.7.0 → mycli-2.8.0}/mycli/schema_prefetcher.py +0 -0
  91. {mycli-2.7.0 → mycli-2.8.0}/mycli/sqlcompleter.py +0 -0
  92. {mycli-2.7.0 → mycli-2.8.0}/mycli/sqlexecute.py +0 -0
  93. {mycli-2.7.0 → mycli-2.8.0}/mycli/ssh_tunnel.py +0 -0
  94. {mycli-2.7.0 → mycli-2.8.0}/mycli/types.py +0 -0
  95. {mycli-2.7.0 → mycli-2.8.0}/mycli/vault.py +0 -0
  96. {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/dependency_links.txt +0 -0
  97. {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/entry_points.txt +0 -0
  98. {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/requires.txt +0 -0
  99. {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/top_level.txt +0 -0
  100. {mycli-2.7.0 → mycli-2.8.0}/setup.cfg +0 -0
  101. {mycli-2.7.0 → mycli-2.8.0}/test/__init__.py +0 -0
  102. {mycli-2.7.0 → mycli-2.8.0}/test/features/__init__.py +0 -0
  103. {mycli-2.7.0 → mycli-2.8.0}/test/features/auto_vertical.feature +0 -0
  104. {mycli-2.7.0 → mycli-2.8.0}/test/features/basic_commands.feature +0 -0
  105. {mycli-2.7.0 → mycli-2.8.0}/test/features/connection.feature +0 -0
  106. {mycli-2.7.0 → mycli-2.8.0}/test/features/crud_database.feature +0 -0
  107. {mycli-2.7.0 → mycli-2.8.0}/test/features/crud_table.feature +0 -0
  108. {mycli-2.7.0 → mycli-2.8.0}/test/features/db_utils.py +0 -0
  109. {mycli-2.7.0 → mycli-2.8.0}/test/features/fixture_data/help_commands.txt +0 -0
  110. {mycli-2.7.0 → mycli-2.8.0}/test/features/fixture_utils.py +0 -0
  111. {mycli-2.7.0 → mycli-2.8.0}/test/features/iocommands.feature +0 -0
  112. {mycli-2.7.0 → mycli-2.8.0}/test/features/named_queries.feature +0 -0
  113. {mycli-2.7.0 → mycli-2.8.0}/test/features/specials.feature +0 -0
  114. {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/__init__.py +0 -0
  115. {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/auto_vertical.py +0 -0
  116. {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/basic_commands.py +0 -0
  117. {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/connection.py +0 -0
  118. {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/crud_database.py +0 -0
  119. {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/crud_table.py +0 -0
  120. {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/iocommands.py +0 -0
  121. {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/named_queries.py +0 -0
  122. {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/specials.py +0 -0
  123. {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/utils.py +0 -0
  124. {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/wrappers.py +0 -0
  125. {mycli-2.7.0 → mycli-2.8.0}/test/features/wrappager.py +0 -0
  126. {mycli-2.7.0 → mycli-2.8.0}/test/myclirc +0 -0
  127. {mycli-2.7.0 → mycli-2.8.0}/test/mylogin.cnf +0 -0
  128. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/conftest.py +0 -0
  129. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_app_state.py +0 -0
  130. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_batch_utils.py +0 -0
  131. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_checkup.py +0 -0
  132. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_cli_runner.py +0 -0
  133. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_cli_utils.py +0 -0
  134. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_clibuffer.py +0 -0
  135. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_client.py +0 -0
  136. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_client_commands.py +0 -0
  137. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_client_connection.py +0 -0
  138. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_client_query.py +0 -0
  139. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_clistyle.py +0 -0
  140. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_clitoolbar.py +0 -0
  141. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_completer_use_switch.py +0 -0
  142. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_completion_engine.py +0 -0
  143. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_completion_refresher.py +0 -0
  144. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_config.py +0 -0
  145. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_delimitercommand.py +0 -0
  146. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_dsn_aliases.py +0 -0
  147. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_favoritequeries.py +0 -0
  148. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_filepaths.py +0 -0
  149. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_hybrid_redirection.py +0 -0
  150. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_interactive_utils.py +0 -0
  151. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_key_binding_utils.py +0 -0
  152. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_key_bindings.py +0 -0
  153. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_lexer.py +0 -0
  154. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_main.py +0 -0
  155. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_main_modes_batch.py +0 -0
  156. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_main_modes_execute.py +0 -0
  157. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_main_modes_list_dsn.py +0 -0
  158. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_main_modes_repl.py +0 -0
  159. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_naive_completion.py +0 -0
  160. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_output.py +0 -0
  161. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_password_sources.py +0 -0
  162. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_polars_transform.py +0 -0
  163. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_ptoolkit_fzf.py +0 -0
  164. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_ptoolkit_history.py +0 -0
  165. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_ptoolkit_utils.py +0 -0
  166. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_schema_prefetcher.py +0 -0
  167. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_smart_completion_public_schema_only.py +0 -0
  168. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_dbcommands.py +0 -0
  169. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_init.py +0 -0
  170. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_iocommands.py +0 -0
  171. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_llm.py +0 -0
  172. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_main.py +0 -0
  173. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_utils.py +0 -0
  174. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_sql_utils.py +0 -0
  175. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_sqlcompleter.py +0 -0
  176. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_sqlexecute.py +0 -0
  177. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_sqlresult.py +0 -0
  178. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_ssh_tunnel.py +0 -0
  179. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_string_utils.py +0 -0
  180. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_tabular_output.py +0 -0
  181. {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_vault.py +0 -0
  182. {mycli-2.7.0 → mycli-2.8.0}/test/utils.py +0 -0
@@ -262,4 +262,4 @@ jobs:
262
262
  name: python-packages
263
263
  path: dist/
264
264
  - name: Publish to PyPI
265
- uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
265
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
@@ -1,4 +1,5 @@
1
1
  include LICENSE.txt *.md *.rst doc/screenshots/*
2
+ recursive-include mycli/resources/completions *
2
3
  recursive-include test *.cnf
3
4
  recursive-include test *.feature
4
5
  recursive-include test *.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycli
3
- Version: 2.7.0
3
+ Version: 2.8.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
@@ -9,6 +9,21 @@ Project-URL: Documentation, https://mycli.net/docs
9
9
  Project-URL: Source, https://github.com/dbcli/mycli
10
10
  Project-URL: Issues, https://github.com/dbcli/mycli/issues
11
11
  Project-URL: Changelog, https://github.com/dbcli/mycli/blob/main/changelog.md
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: SQL
24
+ Classifier: Topic :: Database
25
+ Classifier: Topic :: Database :: Front-Ends
26
+ Classifier: Topic :: Software Development
12
27
  Requires-Python: >=3.10
13
28
  Description-Content-Type: text/markdown
14
29
  License-File: LICENSE.txt
@@ -1,3 +1,19 @@
1
+ 2.8.0 (2026/07/31)
2
+ ==============
3
+
4
+ Features
5
+ ---------
6
+ * Render tab literals as four spaces in the REPL.
7
+ * Let cursor shape respond to vi modes: _eg_ bar for insert mode.
8
+ * Add zsh completions which can complete DSN aliases.
9
+
10
+
11
+ Internal
12
+ ---------
13
+ * Add Trove classifiers for PyPi.
14
+ * Don't let behave tests refer to real home-directory resources.
15
+
16
+
1
17
  2.7.0 (2026/07/25)
2
18
  ==============
3
19
 
@@ -23,6 +23,7 @@ import prompt_toolkit
23
23
  from prompt_toolkit.application.current import get_app
24
24
  from prompt_toolkit.auto_suggest import AutoSuggestFromHistory, ThreadedAutoSuggest
25
25
  from prompt_toolkit.completion import DynamicCompleter
26
+ from prompt_toolkit.cursor_shapes import CursorShape, ModalCursorShapeConfig
26
27
  from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
27
28
  from prompt_toolkit.filters import Condition, has_focus, is_done
28
29
  from prompt_toolkit.formatted_text import (
@@ -33,7 +34,11 @@ from prompt_toolkit.formatted_text import (
33
34
  to_plain_text,
34
35
  )
35
36
  from prompt_toolkit.key_binding import KeyBindings
36
- from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
37
+ from prompt_toolkit.layout.processors import (
38
+ ConditionalProcessor,
39
+ HighlightMatchingBracketProcessor,
40
+ TabsProcessor,
41
+ )
37
42
  from prompt_toolkit.lexers import PygmentsLexer
38
43
  from prompt_toolkit.output import ColorDepth
39
44
  from prompt_toolkit.shortcuts import CompleteStyle, PromptSession
@@ -583,7 +588,9 @@ def _build_prompt_session(
583
588
  ConditionalProcessor(
584
589
  processor=HighlightMatchingBracketProcessor(chars='[](){}'),
585
590
  filter=has_focus(DEFAULT_BUFFER) & ~is_done,
586
- )
591
+ ),
592
+ # renders tab as four spaces rather than ^I control character
593
+ TabsProcessor(char1=' ', char2=' '),
587
594
  ],
588
595
  tempfile_suffix='.sql',
589
596
  completer=DynamicCompleter(lambda: mycli.completer),
@@ -599,6 +606,7 @@ def _build_prompt_session(
599
606
  enable_system_prompt=True,
600
607
  enable_suspend=True,
601
608
  editing_mode=editing_mode,
609
+ cursor=ModalCursorShapeConfig() if editing_mode == EditingMode.VI else CursorShape.BLOCK,
602
610
  search_ignore_case=True,
603
611
  )
604
612
 
@@ -0,0 +1,66 @@
1
+ #compdef mycli
2
+
3
+ _mycli_dsn_aliases() {
4
+ local -a dsn_aliases
5
+
6
+ dsn_aliases=("${(@f)$(env MYCLI_LLM_OFF=1 command mycli --list-dsn 2>/dev/null)}")
7
+ if (( $#dsn_aliases )); then
8
+ compadd -V unsorted -a dsn_aliases
9
+ fi
10
+ }
11
+
12
+ _mycli_completion() {
13
+ local -a completions
14
+ local -a completions_with_descriptions
15
+ local -a response
16
+ (( ! $+commands[mycli] )) && return 1
17
+
18
+ case "${words[CURRENT]}" in
19
+ --dsn=*)
20
+ compset -P '--dsn='
21
+ _mycli_dsn_aliases
22
+ return
23
+ ;;
24
+ -d*)
25
+ compset -P '-d'
26
+ _mycli_dsn_aliases
27
+ return
28
+ ;;
29
+ esac
30
+
31
+ response=("${(@f)$(env MYCLI_LLM_OFF=1 COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) _MYCLI_COMPLETE=zsh_complete command mycli)}")
32
+
33
+ for type key descr in ${response}; do
34
+ if [[ "$type" == "plain" ]]; then
35
+ if [[ "$descr" == "_" ]]; then
36
+ completions+=("$key")
37
+ else
38
+ completions_with_descriptions+=("$key":"$descr")
39
+ fi
40
+ elif [[ "$type" == "dir" ]]; then
41
+ _path_files -/
42
+ elif [[ "$type" == "file" ]]; then
43
+ _path_files -f
44
+ fi
45
+ done
46
+
47
+ if [ -n "$completions_with_descriptions" ]; then
48
+ _describe -V unsorted completions_with_descriptions -U
49
+ fi
50
+
51
+ if [ -n "$completions" ]; then
52
+ compadd -U -V unsorted -a completions
53
+ fi
54
+
55
+ if (( CURRENT == 2 )) && [[ "${words[CURRENT]}" != -* ]]; then
56
+ _mycli_dsn_aliases
57
+ elif [[ "${words[CURRENT - 1]}" == '-d' || "${words[CURRENT - 1]}" == '--dsn' ]]; then
58
+ _mycli_dsn_aliases
59
+ fi
60
+ }
61
+
62
+ if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
63
+ _mycli_completion "$@"
64
+ else
65
+ compdef _mycli_completion mycli
66
+ fi
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycli
3
- Version: 2.7.0
3
+ Version: 2.8.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
@@ -9,6 +9,21 @@ Project-URL: Documentation, https://mycli.net/docs
9
9
  Project-URL: Source, https://github.com/dbcli/mycli
10
10
  Project-URL: Issues, https://github.com/dbcli/mycli/issues
11
11
  Project-URL: Changelog, https://github.com/dbcli/mycli/blob/main/changelog.md
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: SQL
24
+ Classifier: Topic :: Database
25
+ Classifier: Topic :: Database :: Front-Ends
26
+ Classifier: Topic :: Software Development
12
27
  Requires-Python: >=3.10
13
28
  Description-Content-Type: text/markdown
14
29
  License-File: LICENSE.txt
@@ -92,6 +92,7 @@ mycli/packages/special/main.py
92
92
  mycli/packages/special/utils.py
93
93
  mycli/packages/tabular_output/__init__.py
94
94
  mycli/packages/tabular_output/sql_format.py
95
+ mycli/resources/completions/zsh/_mycli
95
96
  test/__init__.py
96
97
  test/myclirc
97
98
  test/mylogin.cnf
@@ -174,4 +175,5 @@ test/pytests/test_sqlresult.py
174
175
  test/pytests/test_ssh_tunnel.py
175
176
  test/pytests/test_string_utils.py
176
177
  test/pytests/test_tabular_output.py
177
- test/pytests/test_vault.py
178
+ test/pytests/test_vault.py
179
+ test/pytests/test_zsh_completion.py
@@ -46,6 +46,7 @@
46
46
  "test/pytests/test_key_bindings.py",
47
47
  "test/pytests/test_clibuffer.py",
48
48
  "test/pytests/test_special_iocommands.py",
49
+ "test/pytests/test_zsh_completion.py",
49
50
  "test/pytests/test_completion_refresher.py",
50
51
  "test/pytests/test_favoritequeries.py",
51
52
  "test/pytests/test_sql_utils.py",
@@ -168,6 +169,7 @@
168
169
  "mycli/packages/special/dsn_aliases.py",
169
170
  "mycli/packages/special/delimitercommand.py",
170
171
  "mycli/packages/special/favoritequeries.py",
171
- "mycli/packages/special/utils.py"
172
+ "mycli/packages/special/utils.py",
173
+ "mycli/resources/completions/zsh/_mycli"
172
174
  ]
173
175
  }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2.8.0",
3
+ "distance": 0,
4
+ "node": "gc24ab82b2a895108cdb0a60b64ebce956037d163",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-07-31"
8
+ }
@@ -6,6 +6,23 @@ readme = "README.md"
6
6
  requires-python = ">=3.10"
7
7
  license = "BSD-3-Clause"
8
8
  authors = [{ name = "Mycli Core Team" }]
9
+ classifiers = [
10
+ 'Environment :: Console',
11
+ 'Intended Audience :: Developers',
12
+ 'Intended Audience :: Science/Research',
13
+ 'Operating System :: OS Independent',
14
+ 'Programming Language :: Python',
15
+ 'Programming Language :: Python :: 3',
16
+ 'Programming Language :: Python :: 3.10',
17
+ 'Programming Language :: Python :: 3.11',
18
+ 'Programming Language :: Python :: 3.12',
19
+ 'Programming Language :: Python :: 3.13',
20
+ 'Programming Language :: Python :: 3.14',
21
+ 'Programming Language :: SQL',
22
+ 'Topic :: Database',
23
+ 'Topic :: Database :: Front-Ends',
24
+ 'Topic :: Software Development',
25
+ ]
9
26
 
10
27
  dependencies = [
11
28
  "click ~= 8.4.2",
@@ -12,16 +12,12 @@ import pexpect
12
12
  from mycli.constants import DEFAULT_HOST, DEFAULT_PORT, DEFAULT_USER
13
13
  from steps.wrappers import run_cli, wait_prompt
14
14
 
15
- test_log_file = os.path.join(os.environ["HOME"], ".mycli.test.log")
16
-
15
+ fd, TEST_LOG_FILE = tempfile.mkstemp(prefix='mycli-behave-', suffix='.test.log')
16
+ os.close(fd)
17
17
 
18
18
  SELF_CONNECTING_FEATURES = ("test/features/connection.feature",)
19
19
 
20
20
 
21
- MYLOGIN_CNF_PATH = os.path.expanduser("~/.mylogin.cnf")
22
- MYLOGIN_CNF_BACKUP_PATH = f"{MYLOGIN_CNF_PATH}.backup"
23
-
24
-
25
21
  def get_db_name_from_context(context):
26
22
  return context.config.userdata.get("my_test_db", None) or "mycli_behave_tests"
27
23
 
@@ -111,19 +107,16 @@ def before_scenario(context, arg):
111
107
  # Skip flaky editor test in CI
112
108
  if os.getenv('GITHUB_ACTION') and 'skip_ci' in arg.tags:
113
109
  arg.skip('Skipped in CI')
114
- with open(test_log_file, "w") as f:
110
+ with open(TEST_LOG_FILE, "w") as f:
115
111
  f.write("")
116
112
  if arg.location.filename not in SELF_CONNECTING_FEATURES:
117
113
  run_cli(context)
118
114
  wait_prompt(context)
119
115
 
120
- if os.path.exists(MYLOGIN_CNF_PATH):
121
- shutil.move(MYLOGIN_CNF_PATH, MYLOGIN_CNF_BACKUP_PATH)
122
-
123
116
 
124
117
  def after_scenario(context, _):
125
118
  """Cleans up after each test complete."""
126
- with open(test_log_file) as f:
119
+ with open(TEST_LOG_FILE) as f:
127
120
  for line in f:
128
121
  if "error" in line.lower():
129
122
  raise RuntimeError(f"Error in log file: {line}")
@@ -139,13 +132,6 @@ def after_scenario(context, _):
139
132
  context.cli.sendcontrol("d")
140
133
  context.cli.expect_exact(pexpect.EOF, timeout=5)
141
134
 
142
- if os.path.exists(MYLOGIN_CNF_BACKUP_PATH):
143
- shutil.move(MYLOGIN_CNF_BACKUP_PATH, MYLOGIN_CNF_PATH)
144
- elif os.path.exists(MYLOGIN_CNF_PATH):
145
- # This file was moved in `before_scenario`.
146
- # If it exists now, it has been created during a test
147
- os.remove(MYLOGIN_CNF_PATH)
148
-
149
135
 
150
136
  # TODO: uncomment to debug a failure
151
137
  # def after_step(context, step):
@@ -0,0 +1,118 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+ import shutil
6
+ import stat
7
+ import subprocess
8
+
9
+ import pytest
10
+
11
+ ZSH = shutil.which('zsh')
12
+ COMPLETION_SCRIPT = Path(__file__).parents[2] / 'mycli' / 'resources' / 'completions' / 'zsh' / '_mycli'
13
+
14
+
15
+ @pytest.mark.skipif(ZSH is None, reason='zsh is not installed')
16
+ def test_zsh_completion_lists_dsn_aliases_in_supported_contexts(tmp_path: Path) -> None:
17
+ assert ZSH is not None
18
+ executable = tmp_path / 'mycli'
19
+ executable.write_text(
20
+ '''#!/bin/sh
21
+ if [ "${_MYCLI_COMPLETE:-}" = 'zsh_complete' ]; then
22
+ printf 'click\\n' >> "$MYCLI_LOG"
23
+ exit 0
24
+ fi
25
+ if [ "$1" = '--list-dsn' ]; then
26
+ printf 'list-dsn\\n' >> "$MYCLI_LOG"
27
+ printf 'prod\\nstaging\\n'
28
+ fi
29
+ ''',
30
+ encoding='utf-8',
31
+ )
32
+ executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
33
+ log_path = tmp_path / 'calls.log'
34
+ environment = {
35
+ **os.environ,
36
+ 'COMPLETION_SCRIPT': str(COMPLETION_SCRIPT),
37
+ 'MYCLI_LOG': str(log_path),
38
+ 'PATH': f'{tmp_path}{os.pathsep}{os.environ["PATH"]}',
39
+ }
40
+
41
+ result = subprocess.run(
42
+ [ZSH, '-fc', ZSH_COMPLETION_HARNESS],
43
+ check=True,
44
+ capture_output=True,
45
+ text=True,
46
+ env=environment,
47
+ )
48
+
49
+ assert result.stdout.splitlines() == [
50
+ 'prod,staging||0',
51
+ 'prod||0',
52
+ 'prod,staging||0',
53
+ 'prod,staging||0',
54
+ '-dprod|-P -d|0',
55
+ '--dsn=prod|-P --dsn=|0',
56
+ '||0',
57
+ '||0',
58
+ ]
59
+ assert log_path.read_text(encoding='utf-8').splitlines().count('list-dsn') == 6
60
+
61
+
62
+ ZSH_COMPLETION_HARNESS = r'''
63
+ compdef() { :; }
64
+ _describe() { :; }
65
+ _path_files() { :; }
66
+
67
+ typeset -a captured compset_calls
68
+ typeset PREFIX IPREFIX used_unambiguous
69
+ compadd() {
70
+ local array_name=''
71
+ local candidate
72
+
73
+ while (( $# )); do
74
+ case "$1" in
75
+ -U)
76
+ used_unambiguous=1
77
+ ;;
78
+ -a)
79
+ array_name="$2"
80
+ shift
81
+ ;;
82
+ esac
83
+ shift
84
+ done
85
+
86
+ for candidate in "${(@P)array_name}"; do
87
+ [[ "$candidate" == "$PREFIX"* ]] && captured+=("$IPREFIX$candidate")
88
+ done
89
+ }
90
+ compset() {
91
+ compset_calls+=("$*")
92
+ IPREFIX="$2"
93
+ PREFIX="${PREFIX#$IPREFIX}"
94
+ }
95
+
96
+ source "$COMPLETION_SCRIPT"
97
+
98
+ run_completion() {
99
+ captured=()
100
+ compset_calls=()
101
+ words=("$@")
102
+ CURRENT=$#
103
+ PREFIX="${words[CURRENT]}"
104
+ IPREFIX=''
105
+ used_unambiguous=0
106
+ _mycli_completion
107
+ print -r -- "${(j:,:)captured}|${(j:,:)compset_calls}|$used_unambiguous"
108
+ }
109
+
110
+ run_completion mycli ''
111
+ run_completion mycli pro
112
+ run_completion mycli -d ''
113
+ run_completion mycli --dsn ''
114
+ run_completion mycli -dpro
115
+ run_completion mycli --dsn=pro
116
+ run_completion mycli --host ''
117
+ run_completion mycli --host
118
+ '''
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "2.7.0",
3
- "distance": 0,
4
- "node": "g8f283a66e024160fc94f7d61dc2463497df14da7",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-07-25"
8
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes