mycli 2.2.0__tar.gz → 2.4.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 (176) hide show
  1. {mycli-2.2.0 → mycli-2.4.0}/AGENTS.md +4 -2
  2. {mycli-2.2.0/mycli.egg-info → mycli-2.4.0}/PKG-INFO +30 -13
  3. {mycli-2.2.0 → mycli-2.4.0}/README.md +29 -12
  4. {mycli-2.2.0 → mycli-2.4.0}/changelog.md +38 -0
  5. {mycli-2.2.0 → mycli-2.4.0}/mycli/TIPS +9 -1
  6. {mycli-2.2.0 → mycli-2.4.0}/mycli/cli_runner.py +59 -19
  7. {mycli-2.2.0 → mycli-2.4.0}/mycli/clibuffer.py +1 -0
  8. {mycli-2.2.0 → mycli-2.4.0}/mycli/client.py +18 -3
  9. {mycli-2.2.0 → mycli-2.4.0}/mycli/client_connection.py +52 -22
  10. {mycli-2.2.0 → mycli-2.4.0}/mycli/client_query.py +2 -0
  11. {mycli-2.2.0 → mycli-2.4.0}/mycli/clitoolbar.py +13 -13
  12. {mycli-2.2.0 → mycli-2.4.0}/mycli/completion_refresher.py +56 -21
  13. {mycli-2.2.0 → mycli-2.4.0}/mycli/main.py +0 -9
  14. {mycli-2.2.0 → mycli-2.4.0}/mycli/main_modes/execute.py +3 -3
  15. {mycli-2.2.0 → mycli-2.4.0}/mycli/main_modes/repl.py +26 -1
  16. {mycli-2.2.0 → mycli-2.4.0}/mycli/myclirc +44 -1
  17. {mycli-2.2.0 → mycli-2.4.0}/mycli/output.py +34 -5
  18. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/special/__init__.py +4 -0
  19. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/special/iocommands.py +11 -1
  20. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/special/main.py +9 -1
  21. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/special/utils.py +21 -6
  22. mycli-2.4.0/mycli/password_sources.py +77 -0
  23. {mycli-2.2.0 → mycli-2.4.0}/mycli/schema_prefetcher.py +27 -3
  24. {mycli-2.2.0 → mycli-2.4.0}/mycli/sqlexecute.py +6 -0
  25. {mycli-2.2.0 → mycli-2.4.0/mycli.egg-info}/PKG-INFO +30 -13
  26. {mycli-2.2.0 → mycli-2.4.0}/mycli.egg-info/SOURCES.txt +2 -0
  27. {mycli-2.2.0 → mycli-2.4.0}/mycli.egg-info/scm_file_list.json +131 -129
  28. mycli-2.4.0/mycli.egg-info/scm_version.json +8 -0
  29. {mycli-2.2.0 → mycli-2.4.0}/test/features/fixture_data/help_commands.txt +8 -7
  30. {mycli-2.2.0 → mycli-2.4.0}/test/myclirc +43 -0
  31. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_cli_runner.py +295 -9
  32. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_client.py +42 -0
  33. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_client_connection.py +176 -49
  34. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_clitoolbar.py +3 -3
  35. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_completion_refresher.py +133 -1
  36. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_main.py +33 -22
  37. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_main_modes_repl.py +71 -1
  38. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_output.py +102 -0
  39. mycli-2.4.0/test/pytests/test_password_sources.py +81 -0
  40. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_schema_prefetcher.py +140 -0
  41. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_special_iocommands.py +4 -2
  42. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_special_utils.py +20 -0
  43. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_sqlexecute.py +12 -5
  44. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_tabular_output.py +9 -1
  45. {mycli-2.2.0 → mycli-2.4.0}/test/utils.py +1 -0
  46. mycli-2.2.0/mycli.egg-info/scm_version.json +0 -8
  47. {mycli-2.2.0 → mycli-2.4.0}/.git-blame-ignore-revs +0 -0
  48. {mycli-2.2.0 → mycli-2.4.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  49. {mycli-2.2.0 → mycli-2.4.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  50. {mycli-2.2.0 → mycli-2.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  51. {mycli-2.2.0 → mycli-2.4.0}/.github/dependabot.yml +0 -0
  52. {mycli-2.2.0 → mycli-2.4.0}/.github/workflows/ci.yml +0 -0
  53. {mycli-2.2.0 → mycli-2.4.0}/.github/workflows/lint.yml +0 -0
  54. {mycli-2.2.0 → mycli-2.4.0}/.github/workflows/publish.yml +0 -0
  55. {mycli-2.2.0 → mycli-2.4.0}/.github/workflows/typecheck.yml +0 -0
  56. {mycli-2.2.0 → mycli-2.4.0}/.gitignore +0 -0
  57. {mycli-2.2.0 → mycli-2.4.0}/AUTHORS.rst +0 -0
  58. {mycli-2.2.0 → mycli-2.4.0}/CONTRIBUTING.md +0 -0
  59. {mycli-2.2.0 → mycli-2.4.0}/LICENSE.txt +0 -0
  60. {mycli-2.2.0 → mycli-2.4.0}/MANIFEST.in +0 -0
  61. {mycli-2.2.0 → mycli-2.4.0}/SPONSORS.rst +0 -0
  62. {mycli-2.2.0 → mycli-2.4.0}/doc/key_bindings.rst +0 -0
  63. {mycli-2.2.0 → mycli-2.4.0}/doc/llm.md +0 -0
  64. {mycli-2.2.0 → mycli-2.4.0}/doc/screenshots/main.gif +0 -0
  65. {mycli-2.2.0 → mycli-2.4.0}/doc/screenshots/tables.png +0 -0
  66. {mycli-2.2.0 → mycli-2.4.0}/mycli/AUTHORS +0 -0
  67. {mycli-2.2.0 → mycli-2.4.0}/mycli/SPONSORS +0 -0
  68. {mycli-2.2.0 → mycli-2.4.0}/mycli/__init__.py +0 -0
  69. {mycli-2.2.0 → mycli-2.4.0}/mycli/app_state.py +0 -0
  70. {mycli-2.2.0 → mycli-2.4.0}/mycli/client_commands.py +0 -0
  71. {mycli-2.2.0 → mycli-2.4.0}/mycli/clistyle.py +0 -0
  72. {mycli-2.2.0 → mycli-2.4.0}/mycli/compat.py +0 -0
  73. {mycli-2.2.0 → mycli-2.4.0}/mycli/config.py +0 -0
  74. {mycli-2.2.0 → mycli-2.4.0}/mycli/constants.py +0 -0
  75. {mycli-2.2.0 → mycli-2.4.0}/mycli/key_bindings.py +0 -0
  76. {mycli-2.2.0 → mycli-2.4.0}/mycli/lexer.py +0 -0
  77. {mycli-2.2.0 → mycli-2.4.0}/mycli/main_modes/batch.py +0 -0
  78. {mycli-2.2.0 → mycli-2.4.0}/mycli/main_modes/checkup.py +0 -0
  79. {mycli-2.2.0 → mycli-2.4.0}/mycli/main_modes/list_dsn.py +0 -0
  80. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/__init__.py +0 -0
  81. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/batch_utils.py +0 -0
  82. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/cli_utils.py +0 -0
  83. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/completion_engine.py +0 -0
  84. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/filepaths.py +0 -0
  85. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/hybrid_redirection.py +0 -0
  86. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/interactive_utils.py +0 -0
  87. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/key_binding_utils.py +0 -0
  88. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/ptoolkit/__init__.py +0 -0
  89. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/ptoolkit/fzf.py +0 -0
  90. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/ptoolkit/history.py +0 -0
  91. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/ptoolkit/utils.py +0 -0
  92. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/special/dbcommands.py +0 -0
  93. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/special/delimitercommand.py +0 -0
  94. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/special/dsn_aliases.py +0 -0
  95. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/special/favoritequeries.py +0 -0
  96. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/special/llm.py +0 -0
  97. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/sql_utils.py +0 -0
  98. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/sqlresult.py +0 -0
  99. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/string_utils.py +0 -0
  100. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/tabular_output/__init__.py +0 -0
  101. {mycli-2.2.0 → mycli-2.4.0}/mycli/packages/tabular_output/sql_format.py +0 -0
  102. {mycli-2.2.0 → mycli-2.4.0}/mycli/sqlcompleter.py +0 -0
  103. {mycli-2.2.0 → mycli-2.4.0}/mycli/ssh_tunnel.py +0 -0
  104. {mycli-2.2.0 → mycli-2.4.0}/mycli/types.py +0 -0
  105. {mycli-2.2.0 → mycli-2.4.0}/mycli/vault.py +0 -0
  106. {mycli-2.2.0 → mycli-2.4.0}/mycli.egg-info/dependency_links.txt +0 -0
  107. {mycli-2.2.0 → mycli-2.4.0}/mycli.egg-info/entry_points.txt +0 -0
  108. {mycli-2.2.0 → mycli-2.4.0}/mycli.egg-info/requires.txt +0 -0
  109. {mycli-2.2.0 → mycli-2.4.0}/mycli.egg-info/top_level.txt +0 -0
  110. {mycli-2.2.0 → mycli-2.4.0}/pyproject.toml +0 -0
  111. {mycli-2.2.0 → mycli-2.4.0}/setup.cfg +0 -0
  112. {mycli-2.2.0 → mycli-2.4.0}/test/__init__.py +0 -0
  113. {mycli-2.2.0 → mycli-2.4.0}/test/features/__init__.py +0 -0
  114. {mycli-2.2.0 → mycli-2.4.0}/test/features/auto_vertical.feature +0 -0
  115. {mycli-2.2.0 → mycli-2.4.0}/test/features/basic_commands.feature +0 -0
  116. {mycli-2.2.0 → mycli-2.4.0}/test/features/connection.feature +0 -0
  117. {mycli-2.2.0 → mycli-2.4.0}/test/features/crud_database.feature +0 -0
  118. {mycli-2.2.0 → mycli-2.4.0}/test/features/crud_table.feature +0 -0
  119. {mycli-2.2.0 → mycli-2.4.0}/test/features/db_utils.py +0 -0
  120. {mycli-2.2.0 → mycli-2.4.0}/test/features/environment.py +0 -0
  121. {mycli-2.2.0 → mycli-2.4.0}/test/features/fixture_utils.py +0 -0
  122. {mycli-2.2.0 → mycli-2.4.0}/test/features/iocommands.feature +0 -0
  123. {mycli-2.2.0 → mycli-2.4.0}/test/features/named_queries.feature +0 -0
  124. {mycli-2.2.0 → mycli-2.4.0}/test/features/specials.feature +0 -0
  125. {mycli-2.2.0 → mycli-2.4.0}/test/features/steps/__init__.py +0 -0
  126. {mycli-2.2.0 → mycli-2.4.0}/test/features/steps/auto_vertical.py +0 -0
  127. {mycli-2.2.0 → mycli-2.4.0}/test/features/steps/basic_commands.py +0 -0
  128. {mycli-2.2.0 → mycli-2.4.0}/test/features/steps/connection.py +0 -0
  129. {mycli-2.2.0 → mycli-2.4.0}/test/features/steps/crud_database.py +0 -0
  130. {mycli-2.2.0 → mycli-2.4.0}/test/features/steps/crud_table.py +0 -0
  131. {mycli-2.2.0 → mycli-2.4.0}/test/features/steps/iocommands.py +0 -0
  132. {mycli-2.2.0 → mycli-2.4.0}/test/features/steps/named_queries.py +0 -0
  133. {mycli-2.2.0 → mycli-2.4.0}/test/features/steps/specials.py +0 -0
  134. {mycli-2.2.0 → mycli-2.4.0}/test/features/steps/utils.py +0 -0
  135. {mycli-2.2.0 → mycli-2.4.0}/test/features/steps/wrappers.py +0 -0
  136. {mycli-2.2.0 → mycli-2.4.0}/test/features/wrappager.py +0 -0
  137. {mycli-2.2.0 → mycli-2.4.0}/test/mylogin.cnf +0 -0
  138. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/conftest.py +0 -0
  139. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_app_state.py +0 -0
  140. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_batch_utils.py +0 -0
  141. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_checkup.py +0 -0
  142. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_cli_utils.py +0 -0
  143. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_clibuffer.py +0 -0
  144. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_client_commands.py +0 -0
  145. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_client_query.py +0 -0
  146. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_clistyle.py +0 -0
  147. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_completer_use_switch.py +0 -0
  148. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_completion_engine.py +0 -0
  149. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_config.py +0 -0
  150. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_delimitercommand.py +0 -0
  151. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_dsn_aliases.py +0 -0
  152. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_favoritequeries.py +0 -0
  153. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_filepaths.py +0 -0
  154. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_hybrid_redirection.py +0 -0
  155. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_interactive_utils.py +0 -0
  156. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_key_binding_utils.py +0 -0
  157. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_key_bindings.py +0 -0
  158. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_lexer.py +0 -0
  159. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_main_modes_batch.py +0 -0
  160. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_main_modes_execute.py +0 -0
  161. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_main_modes_list_dsn.py +0 -0
  162. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_naive_completion.py +0 -0
  163. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_ptoolkit_fzf.py +0 -0
  164. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_ptoolkit_history.py +0 -0
  165. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_ptoolkit_utils.py +0 -0
  166. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_smart_completion_public_schema_only.py +0 -0
  167. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_special_dbcommands.py +0 -0
  168. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_special_init.py +0 -0
  169. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_special_llm.py +0 -0
  170. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_special_main.py +0 -0
  171. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_sql_utils.py +0 -0
  172. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_sqlcompleter.py +0 -0
  173. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_sqlresult.py +0 -0
  174. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_ssh_tunnel.py +0 -0
  175. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_string_utils.py +0 -0
  176. {mycli-2.2.0 → mycli-2.4.0}/test/pytests/test_vault.py +0 -0
@@ -31,7 +31,7 @@ A command line client for MySQL with auto-completion and syntax highlighting.
31
31
  ├── mycli/main_modes/checkup.py # `--checkup` mode
32
32
  ├── mycli/main_modes/execute.py # `--execute` mode
33
33
  ├── mycli/main_modes/list_dsn.py # `--list-dsn` mode
34
- ├── mycli/main_modes/repl.py # interactive REPL made
34
+ ├── mycli/main_modes/repl.py # interactive REPL mode
35
35
  ├── mycli/myclirc # project-level configuration file
36
36
  ├── mycli/output.py # `OutputMixin` mixin for feedback and output of query results
37
37
  ├── mycli/packages/ # application packages
@@ -48,16 +48,18 @@ A command line client for MySQL with auto-completion and syntax highlighting.
48
48
  ├── mycli/packages/sqlresult.py # the `SQLResult` dataclass for holding responses
49
49
  ├── mycli/packages/string_utils.py # generic string utilities
50
50
  ├── mycli/packages/tabular_output/ # extends cli_helper with additional output formats
51
+ ├── mycli/password_sources.py # password sources and precedence
52
+ ├── mycli/ssh_tunnel.py # connecting over a tunnel with `--ssh-jump`
51
53
  ├── mycli/schema_prefetcher.py # background prefetcher for multi-schema auto-completion
52
54
  ├── mycli/sqlcompleter.py # offers SQL completions
53
55
  ├── mycli/sqlexecute.py # runs SQL queries
54
56
  ├── mycli/types.py # shared types
55
57
  ├── mycli/vault.py # Vault integration
56
- ├── test/conftest.py # pytest configuration
57
58
  ├── test/features/ # behave tests
58
59
  ├── test/myclirc # mycli configuration used for tests
59
60
  ├── test/mylogin.cnf # `mylogin.cnf` example used for tests
60
61
  ├── test/pytests/ # pytest tests
62
+ ├── test/pytests/conftest.py # pytest configuration
61
63
  └── test/utils.py # shared utilities for tests
62
64
 
63
65
  ## Development
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycli
3
- Version: 2.2.0
3
+ Version: 2.4.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
@@ -89,13 +89,13 @@ You might need `sudo` on Linux.
89
89
  pip install --upgrade 'mycli[all]'
90
90
  ```
91
91
 
92
- or, only on macOS (`fzf` and `pygments` are optional):
92
+ or, only on macOS (`fzf` and `pygments` are optional but recommended):
93
93
 
94
94
  ```bash
95
95
  brew update && brew install mycli fzf pygments
96
96
  ```
97
97
 
98
- or, only on Debian or Ubuntu (`fzf` and `pygments` are optional):
98
+ or, only on Debian or Ubuntu (`fzf` and `pygments` are optional but recommended):
99
99
 
100
100
  ```bash
101
101
  sudo apt-get install mycli fzf python3-pygments
@@ -115,6 +115,7 @@ Features
115
115
  * Auto-completion as you type for SQL keywords as well as tables, views,
116
116
  columns, enums, and more!
117
117
  * Fuzzy history search using [fzf](https://github.com/junegunn/fzf).
118
+ * Output explorer using [fzf](https://github.com/junegunn/fzf) or other tools.
118
119
  * Syntax highlighting using [Pygments](https://pygments.org/).
119
120
  * Smart-completion (enabled by default) will suggest context-sensitive completion.
120
121
  - `SELECT * FROM <tab>` will only show table names.
@@ -158,15 +159,15 @@ These are some alternative ways to install mycli that are not managed by our
158
159
  team but provided by OS package maintainers. OS packages could be somewhat
159
160
  out of date.
160
161
 
161
- If present, the `fzf` package can be used for fuzzy history search, and
162
- `pygemtize` can be used for syntax highlighting within the fuzzy history
163
- search. The `less` package is also expected, but almost always already
164
- installed.
162
+ If present, the `fzf` package can be used for fuzzy history search, and as an
163
+ output "explorer" with the `\x` special command. `pygemtize` can be used for
164
+ syntax highlighting within the fuzzy history search. The `less` package is
165
+ also expected, but almost always already installed.
165
166
 
166
167
  ### Arch, Manjaro
167
168
 
168
169
  You can install the `mycli` package available in the AUR. `fzf` and
169
- `python-pygments` are optional:
170
+ `python-pygments` are optional but recommended:
170
171
 
171
172
  ```bash
172
173
  yay -S mycli fzf python-pygments
@@ -175,7 +176,8 @@ yay -S mycli fzf python-pygments
175
176
  ### Debian, Ubuntu
176
177
 
177
178
  On Debian and Ubuntu distributions, you can easily install the mycli package
178
- using apt. The `fzf` and `python3-pygments` packages are optional:
179
+ using apt. The `fzf` and `python3-pygments` packages are optional but
180
+ recommended:
179
181
 
180
182
  ```bash
181
183
  sudo apt-get install mycli fzf python3-pygments
@@ -184,7 +186,7 @@ sudo apt-get install mycli fzf python3-pygments
184
186
  ### Fedora
185
187
 
186
188
  Fedora has a package available for mycli; install it using dnf. The `fzf` and
187
- `python-pygments` packages are optional:
189
+ `python-pygments` packages are optional but recommended:
188
190
 
189
191
  ```bash
190
192
  sudo dnf install mycli fzf python-pygments
@@ -196,7 +198,9 @@ sudo dnf install mycli fzf python-pygments
196
198
 
197
199
  Install the `less` pager, for example by `scoop install less`.
198
200
 
199
- Follow the instructions on this blogpost: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
201
+ Install the `fzf` fuzzy finder, for example by `scoop install fzf`.
202
+
203
+ Follow the instructions on this blog post: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
200
204
 
201
205
  The libraries used in mycli are Windows-compatible, but there are known
202
206
  limitations according to the test suite. The basics work without any
@@ -209,11 +213,24 @@ PRs to address shortcomings on Windows would be welcome!
209
213
  Mycli is more compatible with WSL than with native Windows, though still
210
214
  not 100% perfect. This is a good option for using mycli on Windows.
211
215
 
216
+ With WSL, you are probably using an Ubuntu distribution, in which case
217
+ recommended dependencies can be installed by
218
+
219
+ ```bash
220
+ sudo apt-get install fzf python3-pygments
221
+ ```
222
+
223
+ and mycli can be installed by `apt-get` or by `pip` (recommended):
224
+
225
+ ```bash
226
+ pip install --upgrade 'mycli[all]'
227
+ ```
228
+
212
229
  PRs to complete WSL support would be welcome!
213
230
 
214
231
  ### Thanks
215
232
 
216
- This project was funded through kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
233
+ This project was funded through Kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
217
234
 
218
235
  A special thanks to [Jonathan Slenders](https://twitter.com/jonathan_s) for
219
236
  creating [Python Prompt Toolkit](https://github.com/jonathanslenders/python-prompt-toolkit),
@@ -241,7 +258,7 @@ following in `~/.myclirc`:
241
258
  default_character_set = utf8
242
259
  ```
243
260
 
244
- or set `--charset=utf8` when invoking MyCLI.
261
+ or set `--charset=utf8` when invoking mycli
245
262
 
246
263
  ### Configuration and Usage
247
264
 
@@ -32,13 +32,13 @@ You might need `sudo` on Linux.
32
32
  pip install --upgrade 'mycli[all]'
33
33
  ```
34
34
 
35
- or, only on macOS (`fzf` and `pygments` are optional):
35
+ or, only on macOS (`fzf` and `pygments` are optional but recommended):
36
36
 
37
37
  ```bash
38
38
  brew update && brew install mycli fzf pygments
39
39
  ```
40
40
 
41
- or, only on Debian or Ubuntu (`fzf` and `pygments` are optional):
41
+ or, only on Debian or Ubuntu (`fzf` and `pygments` are optional but recommended):
42
42
 
43
43
  ```bash
44
44
  sudo apt-get install mycli fzf python3-pygments
@@ -58,6 +58,7 @@ Features
58
58
  * Auto-completion as you type for SQL keywords as well as tables, views,
59
59
  columns, enums, and more!
60
60
  * Fuzzy history search using [fzf](https://github.com/junegunn/fzf).
61
+ * Output explorer using [fzf](https://github.com/junegunn/fzf) or other tools.
61
62
  * Syntax highlighting using [Pygments](https://pygments.org/).
62
63
  * Smart-completion (enabled by default) will suggest context-sensitive completion.
63
64
  - `SELECT * FROM <tab>` will only show table names.
@@ -101,15 +102,15 @@ These are some alternative ways to install mycli that are not managed by our
101
102
  team but provided by OS package maintainers. OS packages could be somewhat
102
103
  out of date.
103
104
 
104
- If present, the `fzf` package can be used for fuzzy history search, and
105
- `pygemtize` can be used for syntax highlighting within the fuzzy history
106
- search. The `less` package is also expected, but almost always already
107
- installed.
105
+ If present, the `fzf` package can be used for fuzzy history search, and as an
106
+ output "explorer" with the `\x` special command. `pygemtize` can be used for
107
+ syntax highlighting within the fuzzy history search. The `less` package is
108
+ also expected, but almost always already installed.
108
109
 
109
110
  ### Arch, Manjaro
110
111
 
111
112
  You can install the `mycli` package available in the AUR. `fzf` and
112
- `python-pygments` are optional:
113
+ `python-pygments` are optional but recommended:
113
114
 
114
115
  ```bash
115
116
  yay -S mycli fzf python-pygments
@@ -118,7 +119,8 @@ yay -S mycli fzf python-pygments
118
119
  ### Debian, Ubuntu
119
120
 
120
121
  On Debian and Ubuntu distributions, you can easily install the mycli package
121
- using apt. The `fzf` and `python3-pygments` packages are optional:
122
+ using apt. The `fzf` and `python3-pygments` packages are optional but
123
+ recommended:
122
124
 
123
125
  ```bash
124
126
  sudo apt-get install mycli fzf python3-pygments
@@ -127,7 +129,7 @@ sudo apt-get install mycli fzf python3-pygments
127
129
  ### Fedora
128
130
 
129
131
  Fedora has a package available for mycli; install it using dnf. The `fzf` and
130
- `python-pygments` packages are optional:
132
+ `python-pygments` packages are optional but recommended:
131
133
 
132
134
  ```bash
133
135
  sudo dnf install mycli fzf python-pygments
@@ -139,7 +141,9 @@ sudo dnf install mycli fzf python-pygments
139
141
 
140
142
  Install the `less` pager, for example by `scoop install less`.
141
143
 
142
- Follow the instructions on this blogpost: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
144
+ Install the `fzf` fuzzy finder, for example by `scoop install fzf`.
145
+
146
+ Follow the instructions on this blog post: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
143
147
 
144
148
  The libraries used in mycli are Windows-compatible, but there are known
145
149
  limitations according to the test suite. The basics work without any
@@ -152,11 +156,24 @@ PRs to address shortcomings on Windows would be welcome!
152
156
  Mycli is more compatible with WSL than with native Windows, though still
153
157
  not 100% perfect. This is a good option for using mycli on Windows.
154
158
 
159
+ With WSL, you are probably using an Ubuntu distribution, in which case
160
+ recommended dependencies can be installed by
161
+
162
+ ```bash
163
+ sudo apt-get install fzf python3-pygments
164
+ ```
165
+
166
+ and mycli can be installed by `apt-get` or by `pip` (recommended):
167
+
168
+ ```bash
169
+ pip install --upgrade 'mycli[all]'
170
+ ```
171
+
155
172
  PRs to complete WSL support would be welcome!
156
173
 
157
174
  ### Thanks
158
175
 
159
- This project was funded through kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
176
+ This project was funded through Kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
160
177
 
161
178
  A special thanks to [Jonathan Slenders](https://twitter.com/jonathan_s) for
162
179
  creating [Python Prompt Toolkit](https://github.com/jonathanslenders/python-prompt-toolkit),
@@ -184,7 +201,7 @@ following in `~/.myclirc`:
184
201
  default_character_set = utf8
185
202
  ```
186
203
 
187
- or set `--charset=utf8` when invoking MyCLI.
204
+ or set `--charset=utf8` when invoking mycli
188
205
 
189
206
  ### Configuration and Usage
190
207
 
@@ -1,3 +1,41 @@
1
+ 2.4.0 (2026/07/18)
2
+ ==============
3
+
4
+ Features
5
+ ---------
6
+ * Make password sources and precedence configurable.
7
+ * Ability to deselect fallback interactive password prompt.
8
+ * Add `\e` prompt/toolbar format string for edit mode.
9
+
10
+
11
+ Bug Fixes
12
+ ---------
13
+ * Don't create `~/.myclirc` if the user adopted the XDG layout for myclirc.
14
+
15
+
16
+ Internal
17
+ ---------
18
+ * Make flaky `/watch` test even more lenient for CI.
19
+ * Add missing file to `AGENTS.md` project layout.
20
+
21
+
22
+ 2.3.0 (2026/07/16)
23
+ ==============
24
+
25
+ Features
26
+ ---------
27
+ * Set minimum times for transient toolbar messages.
28
+ * `editor_command` entry in `~/.myclirc` which overrides environment.
29
+ * Experimental: let Vault properties be set in DSNs.
30
+ * Add a `\x` special command to send output to an explorer rather than a pager.
31
+
32
+
33
+ Documentation
34
+ ---------
35
+ * Spellcheck myclirc commentary.
36
+ * Better ordering for `/help` table.
37
+
38
+
1
39
  2.2.0 (2026/07/11)
2
40
  ==============
3
41
 
@@ -138,7 +138,9 @@ set environment variable MYSQL_DSN to set a default DSN!
138
138
  ### general
139
139
  ###
140
140
 
141
- display query output vertically using \G at the end of a query!
141
+ display output vertically using \G at the end of a query!
142
+
143
+ display output in an interactive explorer \x at the end of a query!
142
144
 
143
145
  run SQL scripts in batch mode using the standard input!
144
146
 
@@ -282,6 +284,12 @@ use "min_completion_trigger" in ~/.myclirc to defer completions!
282
284
 
283
285
  colorize search previews with "highlight_preview" in ~/.myclirc!
284
286
 
287
+ set editor_command = "code --wait" in ~/.myclirc to edit queries using VS Code!
288
+
289
+ set up an interactive output explorer using the "[explorer]" section of ~/.myclirc!
290
+
291
+ customize password sources/precedence with "password_sources" in ~/.myclirc!
292
+
285
293
  ###
286
294
  ### redirection
287
295
  ###
@@ -15,6 +15,7 @@ from mycli.main_modes.checkup import main_checkup
15
15
  from mycli.main_modes.execute import main_execute_from_cli
16
16
  from mycli.main_modes.list_dsn import main_list_dsn
17
17
  from mycli.packages.cli_utils import is_valid_connection_scheme
18
+ from mycli.password_sources import PasswordCandidates
18
19
  from mycli.vault import (
19
20
  DEFAULT_VAULT_EXECUTABLE,
20
21
  DEFAULT_VAULT_PASSWORD_FIELD,
@@ -41,6 +42,11 @@ KNOWN_DSN_QUERY_PARAMS = {
41
42
  'ssl_mode',
42
43
  'ssl_verify_server_cert',
43
44
  'tls_version',
45
+ 'vault_address',
46
+ 'vault_mount',
47
+ 'vault_secret',
48
+ 'vault_password_field',
49
+ 'vault_username_field',
44
50
  }
45
51
 
46
52
 
@@ -160,10 +166,15 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
160
166
  database = cli_args.dbname or cli_args.database
161
167
 
162
168
  dsn_uri = None
169
+ dsn_password: str | None = None
163
170
 
164
171
  # Treat the database argument as a DSN alias only if it matches a configured alias
165
172
  # todo why is port tested but not socket?
166
- truthy_password = cli_args.password not in (None, EMPTY_PASSWORD_FLAG_SENTINEL)
173
+ truthy_password = (
174
+ cli_args.password not in (None, EMPTY_PASSWORD_FLAG_SENTINEL)
175
+ or cli_args.password_file is not None
176
+ or os.environ.get('MYSQL_PWD') is not None
177
+ )
167
178
  if (
168
179
  database
169
180
  and "://" not in database
@@ -230,9 +241,6 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
230
241
  database = dsn_database
231
242
  if not cli_args.user and dsn_user is not None:
232
243
  cli_args.user = dsn_user
233
- # todo: rationalize the behavior of empty-string passwords here
234
- if not cli_args.password and dsn_password is not None:
235
- cli_args.password = dsn_password
236
244
  if not cli_args.host:
237
245
  cli_args.host = dsn_host
238
246
  if not cli_args.port:
@@ -277,6 +285,16 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
277
285
  cli_args.character_set = cli_args.character_set or params[0]
278
286
  if params := dsn_params.get('ssh_jump'):
279
287
  cli_args.ssh_jump = cli_args.ssh_jump or params[0]
288
+ if params := dsn_params.get('vault_address'):
289
+ cli_args.vault_address = cli_args.vault_address or params[0]
290
+ if params := dsn_params.get('vault_mount'):
291
+ cli_args.vault_mount = cli_args.vault_mount or params[0]
292
+ if params := dsn_params.get('vault_secret'):
293
+ cli_args.vault_secret = cli_args.vault_secret or params[0]
294
+ if params := dsn_params.get('vault_password_field'):
295
+ cli_args.vault_password_field = cli_args.vault_password_field or params[0]
296
+ if params := dsn_params.get('vault_username_field'):
297
+ cli_args.vault_username_field = cli_args.vault_username_field or params[0]
280
298
 
281
299
  keepalive_ticks = cli_args.keepalive_ticks if cli_args.keepalive_ticks is not None else mycli.default_keepalive_ticks
282
300
  ssl_mode = cli_args.ssl_mode or mycli.ssl_mode
@@ -349,25 +367,40 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
349
367
  use_keyring = str_to_bool(cli_args.use_keyring)
350
368
  reset_keyring = False
351
369
 
352
- if cli_args.password is None and cli_args.vault_secret:
370
+ password_candidates = PasswordCandidates()
371
+ if cli_args.password == EMPTY_PASSWORD_FLAG_SENTINEL:
372
+ password_candidates.add_value('prompt', cli_args.password)
373
+ elif cli_args.password is not None:
374
+ password_candidates.add_value('literal', cli_args.password)
375
+ if cli_args.password_file:
376
+ password_candidates.add_loader('file', lambda: main_module.get_password_from_file(cli_args.password_file))
377
+ if os.environ.get('MYSQL_PWD') is not None:
378
+ password_candidates.add_value('environment', os.environ.get('MYSQL_PWD'))
379
+ if dsn_password is not None:
380
+ password_candidates.add_value('dsn', dsn_password)
381
+
382
+ if cli_args.vault_secret:
383
+ vault_secret = cli_args.vault_secret
353
384
  vault_config = mycli.config.get('vault_beta', {})
354
385
  vault_address = cli_args.vault_address or os.environ.get('VAULT_ADDR') or vault_config.get('address') or None
355
386
  vault_mount = cli_args.vault_mount or vault_config.get('default_mount') or None
356
387
  vault_field = cli_args.vault_password_field or vault_config.get('default_password_field') or DEFAULT_VAULT_PASSWORD_FIELD
357
388
  vault_executable = vault_config.get('vault_executable') or DEFAULT_VAULT_EXECUTABLE
358
- try:
359
- vault_password: str | None = get_field_from_vault(
360
- vault_field,
361
- cli_args.vault_secret,
362
- executable=vault_executable,
363
- mount=vault_mount,
364
- address=vault_address,
365
- )
366
- except VaultError as exc:
367
- click.secho(f'Error reading password from Vault: {exc}', err=True, fg='red')
368
- sys.exit(1)
369
- else:
370
- vault_password = None
389
+
390
+ def load_vault_password() -> str | None:
391
+ try:
392
+ return get_field_from_vault(
393
+ vault_field,
394
+ vault_secret,
395
+ executable=vault_executable,
396
+ mount=vault_mount,
397
+ address=vault_address,
398
+ )
399
+ except VaultError as exc:
400
+ click.secho(f'Error reading password from Vault: {exc}', err=True, fg='red')
401
+ sys.exit(1)
402
+
403
+ password_candidates.add_loader('vault', load_vault_password)
371
404
 
372
405
  if cli_args.user is None and cli_args.vault_secret:
373
406
  vault_config = mycli.config.get('vault_beta', {})
@@ -388,12 +421,13 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
388
421
  sys.exit(1)
389
422
  else:
390
423
  vault_username = None
424
+ vault_username_from_vault = vault_username is not None
391
425
 
392
426
  try:
393
427
  mycli.connect(
394
428
  database=database,
395
429
  user=vault_username if cli_args.user is None else cli_args.user,
396
- passwd=vault_password if cli_args.password is None else cli_args.password,
430
+ password_candidates=password_candidates,
397
431
  host=cli_args.host,
398
432
  port=cli_args.port,
399
433
  socket=cli_args.socket,
@@ -407,6 +441,12 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
407
441
  keepalive_ticks=keepalive_ticks,
408
442
  ssh_jump=cli_args.ssh_jump,
409
443
  ssh_cli_options=cli_args.ssh_options,
444
+ vault_address=cli_args.vault_address,
445
+ vault_mount=cli_args.vault_mount,
446
+ vault_secret=cli_args.vault_secret,
447
+ vault_password_field=cli_args.vault_password_field,
448
+ vault_username_field=cli_args.vault_username_field,
449
+ vault_username_from_vault=vault_username_from_vault,
410
450
  )
411
451
 
412
452
  if combined_init_cmd:
@@ -42,6 +42,7 @@ def _multiline_exception(text: str) -> bool:
42
42
  # or ended with certain commands
43
43
  "\\g",
44
44
  "\\G",
45
+ "\\x",
45
46
  r"\e",
46
47
  r"\edit",
47
48
  r"\clip",
@@ -58,9 +58,9 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
58
58
 
59
59
  # check XDG_CONFIG_HOME exists and not an empty string
60
60
  xdg_config_home = os.environ.get("XDG_CONFIG_HOME", "~/.config")
61
+ xdg_config_file = os.path.join(os.path.expanduser(xdg_config_home), "mycli", "myclirc")
61
62
  system_config_files: list[str | IO[str]] = [
62
63
  "/etc/myclirc",
63
- os.path.join(os.path.expanduser(xdg_config_home), "mycli", "myclirc"),
64
64
  ]
65
65
 
66
66
  def __init__(
@@ -72,7 +72,7 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
72
72
  login_path: str | None = None,
73
73
  auto_vertical_output: bool = False,
74
74
  warn: bool | None = None,
75
- myclirc: str = "~/.myclirc",
75
+ myclirc: str | None = None,
76
76
  show_warnings: bool | None = None,
77
77
  cli_verbosity: int = 0,
78
78
  ) -> None:
@@ -87,6 +87,12 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
87
87
  self.sandbox_mode: bool = False
88
88
  self.checkpoint: IO | None = None
89
89
 
90
+ if myclirc is None:
91
+ if os.path.exists(self.xdg_config_file):
92
+ myclirc = self.xdg_config_file
93
+ else:
94
+ myclirc = '~/.myclirc'
95
+
90
96
  # Load config.
91
97
  config_files: list[str | IO[str]] = self.system_config_files + [myclirc]
92
98
 
@@ -114,10 +120,15 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
114
120
  self.dsn_alias: str | None = None
115
121
  self.main_formatter = TabularOutputFormatter(format_name=c["main"]["table_format"])
116
122
  self.redirect_formatter = TabularOutputFormatter(format_name=c["main"].get("redirect_format", "csv"))
123
+ self.explorer_formatter = TabularOutputFormatter(format_name=c['explorer'].get('format', self.main_formatter))
117
124
  sql_format.register_new_formatter(self.main_formatter)
118
125
  sql_format.register_new_formatter(self.redirect_formatter)
126
+ sql_format.register_new_formatter(self.explorer_formatter)
119
127
  self.main_formatter.mycli = self
120
128
  self.redirect_formatter.mycli = self
129
+ self.explorer_formatter.mycli = self
130
+ self.explorer_command = c['explorer'].get('explorer_command')
131
+ self.explorer_trim_footer = c['explorer'].as_bool('trim_footer')
121
132
  self.syntax_style = c["main"]["syntax_style"]
122
133
  self.verbosity = -1 if c["main"].as_bool("less_chatty") else 0
123
134
  if cli_verbosity:
@@ -155,7 +166,7 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
155
166
  self.echo("Error: Unable to open the audit log file. Your queries will not be logged.", err=True, fg="red")
156
167
  self.logfile = False
157
168
 
158
- self.completion_refresher = CompletionRefresher()
169
+ self.completion_refresher = CompletionRefresher(self._invalidate_prompt_session)
159
170
  self.prefetch_schemas_mode = c["main"].get("prefetch_schemas_mode", "always") or "always"
160
171
  raw_prefetch_list = c["main"].as_list("prefetch_schemas_list") if "prefetch_schemas_list" in c["main"] else []
161
172
  self.prefetch_schemas_list = [s.strip() for s in raw_prefetch_list if s and s.strip()]
@@ -201,6 +212,10 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
201
212
  self.destructive_keywords = destructive_keywords_from_config(c)
202
213
  special.set_destructive_keywords(self.destructive_keywords)
203
214
 
215
+ def _invalidate_prompt_session(self) -> None:
216
+ if self.prompt_session:
217
+ self.prompt_session.app.invalidate()
218
+
204
219
  def close(self) -> None:
205
220
  try:
206
221
  self.schema_prefetcher.stop()