mycli 2.2.0__tar.gz → 2.3.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.
- {mycli-2.2.0 → mycli-2.3.0}/AGENTS.md +2 -2
- {mycli-2.2.0/mycli.egg-info → mycli-2.3.0}/PKG-INFO +30 -13
- {mycli-2.2.0 → mycli-2.3.0}/README.md +29 -12
- {mycli-2.2.0 → mycli-2.3.0}/changelog.md +17 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/TIPS +7 -1
- {mycli-2.2.0 → mycli-2.3.0}/mycli/cli_runner.py +22 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/clibuffer.py +1 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/client.py +10 -1
- {mycli-2.2.0 → mycli-2.3.0}/mycli/client_connection.py +28 -1
- {mycli-2.2.0 → mycli-2.3.0}/mycli/client_query.py +2 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/completion_refresher.py +56 -21
- {mycli-2.2.0 → mycli-2.3.0}/mycli/main_modes/execute.py +3 -3
- {mycli-2.2.0 → mycli-2.3.0}/mycli/main_modes/repl.py +17 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/myclirc +28 -1
- {mycli-2.2.0 → mycli-2.3.0}/mycli/output.py +34 -5
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/__init__.py +4 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/iocommands.py +11 -1
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/main.py +9 -1
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/utils.py +21 -6
- {mycli-2.2.0 → mycli-2.3.0}/mycli/schema_prefetcher.py +27 -3
- {mycli-2.2.0 → mycli-2.3.0}/mycli/sqlexecute.py +6 -0
- {mycli-2.2.0 → mycli-2.3.0/mycli.egg-info}/PKG-INFO +30 -13
- {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/scm_file_list.json +129 -129
- mycli-2.3.0/mycli.egg-info/scm_version.json +8 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/fixture_data/help_commands.txt +8 -7
- {mycli-2.2.0 → mycli-2.3.0}/test/myclirc +27 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_cli_runner.py +201 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_client.py +10 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_client_connection.py +66 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_completion_refresher.py +133 -1
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_main_modes_repl.py +47 -1
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_output.py +102 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_schema_prefetcher.py +140 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_utils.py +20 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_sqlexecute.py +12 -5
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_tabular_output.py +5 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/utils.py +1 -0
- mycli-2.2.0/mycli.egg-info/scm_version.json +0 -8
- {mycli-2.2.0 → mycli-2.3.0}/.git-blame-ignore-revs +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/.github/dependabot.yml +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/.github/workflows/ci.yml +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/.github/workflows/lint.yml +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/.github/workflows/publish.yml +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/.github/workflows/typecheck.yml +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/.gitignore +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/AUTHORS.rst +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/CONTRIBUTING.md +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/LICENSE.txt +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/MANIFEST.in +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/SPONSORS.rst +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/doc/key_bindings.rst +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/doc/llm.md +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/doc/screenshots/main.gif +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/doc/screenshots/tables.png +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/AUTHORS +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/SPONSORS +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/__init__.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/app_state.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/client_commands.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/clistyle.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/clitoolbar.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/compat.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/config.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/constants.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/key_bindings.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/lexer.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/main.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/main_modes/batch.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/main_modes/checkup.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/main_modes/list_dsn.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/__init__.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/batch_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/cli_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/completion_engine.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/filepaths.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/hybrid_redirection.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/interactive_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/key_binding_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/ptoolkit/__init__.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/ptoolkit/fzf.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/ptoolkit/history.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/ptoolkit/utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/dbcommands.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/delimitercommand.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/dsn_aliases.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/favoritequeries.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/llm.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/sql_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/sqlresult.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/string_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/tabular_output/__init__.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/tabular_output/sql_format.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/sqlcompleter.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/ssh_tunnel.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/types.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli/vault.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/SOURCES.txt +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/dependency_links.txt +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/entry_points.txt +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/requires.txt +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/top_level.txt +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/pyproject.toml +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/setup.cfg +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/__init__.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/__init__.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/auto_vertical.feature +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/basic_commands.feature +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/connection.feature +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/crud_database.feature +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/crud_table.feature +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/db_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/environment.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/fixture_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/iocommands.feature +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/named_queries.feature +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/specials.feature +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/__init__.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/auto_vertical.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/basic_commands.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/connection.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/crud_database.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/crud_table.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/iocommands.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/named_queries.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/specials.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/wrappers.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/features/wrappager.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/mylogin.cnf +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/conftest.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_app_state.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_batch_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_checkup.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_cli_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_clibuffer.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_client_commands.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_client_query.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_clistyle.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_clitoolbar.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_completer_use_switch.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_completion_engine.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_config.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_delimitercommand.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_dsn_aliases.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_favoritequeries.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_filepaths.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_hybrid_redirection.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_interactive_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_key_binding_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_key_bindings.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_lexer.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_main.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_main_modes_batch.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_main_modes_execute.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_main_modes_list_dsn.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_naive_completion.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_ptoolkit_fzf.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_ptoolkit_history.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_ptoolkit_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_smart_completion_public_schema_only.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_dbcommands.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_init.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_iocommands.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_llm.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_main.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_sql_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_sqlcompleter.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_sqlresult.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_ssh_tunnel.py +0 -0
- {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_string_utils.py +0 -0
- {mycli-2.2.0 → mycli-2.3.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
|
|
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
|
|
@@ -53,11 +53,11 @@ A command line client for MySQL with auto-completion and syntax highlighting.
|
|
|
53
53
|
├── mycli/sqlexecute.py # runs SQL queries
|
|
54
54
|
├── mycli/types.py # shared types
|
|
55
55
|
├── mycli/vault.py # Vault integration
|
|
56
|
-
├── test/conftest.py # pytest configuration
|
|
57
56
|
├── test/features/ # behave tests
|
|
58
57
|
├── test/myclirc # mycli configuration used for tests
|
|
59
58
|
├── test/mylogin.cnf # `mylogin.cnf` example used for tests
|
|
60
59
|
├── test/pytests/ # pytest tests
|
|
60
|
+
├── test/pytests/conftest.py # pytest configuration
|
|
61
61
|
└── test/utils.py # shared utilities for tests
|
|
62
62
|
|
|
63
63
|
## Development
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mycli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.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
|
|
163
|
-
search. The `less` package is
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
106
|
-
search. The `less` package is
|
|
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
|
-
|
|
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
|
|
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
|
|
204
|
+
or set `--charset=utf8` when invoking mycli
|
|
188
205
|
|
|
189
206
|
### Configuration and Usage
|
|
190
207
|
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
2.3.0 (2026/07/16)
|
|
2
|
+
==============
|
|
3
|
+
|
|
4
|
+
Features
|
|
5
|
+
---------
|
|
6
|
+
* Set minimum times for transient toolbar messages.
|
|
7
|
+
* `editor_command` entry in `~/.myclirc` which overrides environment.
|
|
8
|
+
* Experimental: let Vault properties be set in DSNs.
|
|
9
|
+
* Add a `\x` special command to send output to an explorer rather than a pager.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
Documentation
|
|
13
|
+
---------
|
|
14
|
+
* Spellcheck myclirc commentary.
|
|
15
|
+
* Better ordering for `/help` table.
|
|
16
|
+
|
|
17
|
+
|
|
1
18
|
2.2.0 (2026/07/11)
|
|
2
19
|
==============
|
|
3
20
|
|
|
@@ -138,7 +138,9 @@ set environment variable MYSQL_DSN to set a default DSN!
|
|
|
138
138
|
### general
|
|
139
139
|
###
|
|
140
140
|
|
|
141
|
-
display
|
|
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,10 @@ 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
|
+
|
|
285
291
|
###
|
|
286
292
|
### redirection
|
|
287
293
|
###
|
|
@@ -41,6 +41,11 @@ KNOWN_DSN_QUERY_PARAMS = {
|
|
|
41
41
|
'ssl_mode',
|
|
42
42
|
'ssl_verify_server_cert',
|
|
43
43
|
'tls_version',
|
|
44
|
+
'vault_address',
|
|
45
|
+
'vault_mount',
|
|
46
|
+
'vault_secret',
|
|
47
|
+
'vault_password_field',
|
|
48
|
+
'vault_username_field',
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
|
|
@@ -277,6 +282,16 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
|
|
|
277
282
|
cli_args.character_set = cli_args.character_set or params[0]
|
|
278
283
|
if params := dsn_params.get('ssh_jump'):
|
|
279
284
|
cli_args.ssh_jump = cli_args.ssh_jump or params[0]
|
|
285
|
+
if params := dsn_params.get('vault_address'):
|
|
286
|
+
cli_args.vault_address = cli_args.vault_address or params[0]
|
|
287
|
+
if params := dsn_params.get('vault_mount'):
|
|
288
|
+
cli_args.vault_mount = cli_args.vault_mount or params[0]
|
|
289
|
+
if params := dsn_params.get('vault_secret'):
|
|
290
|
+
cli_args.vault_secret = cli_args.vault_secret or params[0]
|
|
291
|
+
if params := dsn_params.get('vault_password_field'):
|
|
292
|
+
cli_args.vault_password_field = cli_args.vault_password_field or params[0]
|
|
293
|
+
if params := dsn_params.get('vault_username_field'):
|
|
294
|
+
cli_args.vault_username_field = cli_args.vault_username_field or params[0]
|
|
280
295
|
|
|
281
296
|
keepalive_ticks = cli_args.keepalive_ticks if cli_args.keepalive_ticks is not None else mycli.default_keepalive_ticks
|
|
282
297
|
ssl_mode = cli_args.ssl_mode or mycli.ssl_mode
|
|
@@ -388,6 +403,7 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
|
|
|
388
403
|
sys.exit(1)
|
|
389
404
|
else:
|
|
390
405
|
vault_username = None
|
|
406
|
+
vault_username_from_vault = vault_username is not None
|
|
391
407
|
|
|
392
408
|
try:
|
|
393
409
|
mycli.connect(
|
|
@@ -407,6 +423,12 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
|
|
|
407
423
|
keepalive_ticks=keepalive_ticks,
|
|
408
424
|
ssh_jump=cli_args.ssh_jump,
|
|
409
425
|
ssh_cli_options=cli_args.ssh_options,
|
|
426
|
+
vault_address=cli_args.vault_address,
|
|
427
|
+
vault_mount=cli_args.vault_mount,
|
|
428
|
+
vault_secret=cli_args.vault_secret,
|
|
429
|
+
vault_password_field=cli_args.vault_password_field,
|
|
430
|
+
vault_username_field=cli_args.vault_username_field,
|
|
431
|
+
vault_username_from_vault=vault_username_from_vault,
|
|
410
432
|
)
|
|
411
433
|
|
|
412
434
|
if combined_init_cmd:
|
|
@@ -114,10 +114,15 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
|
|
|
114
114
|
self.dsn_alias: str | None = None
|
|
115
115
|
self.main_formatter = TabularOutputFormatter(format_name=c["main"]["table_format"])
|
|
116
116
|
self.redirect_formatter = TabularOutputFormatter(format_name=c["main"].get("redirect_format", "csv"))
|
|
117
|
+
self.explorer_formatter = TabularOutputFormatter(format_name=c['explorer'].get('format', self.main_formatter))
|
|
117
118
|
sql_format.register_new_formatter(self.main_formatter)
|
|
118
119
|
sql_format.register_new_formatter(self.redirect_formatter)
|
|
120
|
+
sql_format.register_new_formatter(self.explorer_formatter)
|
|
119
121
|
self.main_formatter.mycli = self
|
|
120
122
|
self.redirect_formatter.mycli = self
|
|
123
|
+
self.explorer_formatter.mycli = self
|
|
124
|
+
self.explorer_command = c['explorer'].get('explorer_command')
|
|
125
|
+
self.explorer_trim_footer = c['explorer'].as_bool('trim_footer')
|
|
121
126
|
self.syntax_style = c["main"]["syntax_style"]
|
|
122
127
|
self.verbosity = -1 if c["main"].as_bool("less_chatty") else 0
|
|
123
128
|
if cli_verbosity:
|
|
@@ -155,7 +160,7 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
|
|
|
155
160
|
self.echo("Error: Unable to open the audit log file. Your queries will not be logged.", err=True, fg="red")
|
|
156
161
|
self.logfile = False
|
|
157
162
|
|
|
158
|
-
self.completion_refresher = CompletionRefresher()
|
|
163
|
+
self.completion_refresher = CompletionRefresher(self._invalidate_prompt_session)
|
|
159
164
|
self.prefetch_schemas_mode = c["main"].get("prefetch_schemas_mode", "always") or "always"
|
|
160
165
|
raw_prefetch_list = c["main"].as_list("prefetch_schemas_list") if "prefetch_schemas_list" in c["main"] else []
|
|
161
166
|
self.prefetch_schemas_list = [s.strip() for s in raw_prefetch_list if s and s.strip()]
|
|
@@ -201,6 +206,10 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
|
|
|
201
206
|
self.destructive_keywords = destructive_keywords_from_config(c)
|
|
202
207
|
special.set_destructive_keywords(self.destructive_keywords)
|
|
203
208
|
|
|
209
|
+
def _invalidate_prompt_session(self) -> None:
|
|
210
|
+
if self.prompt_session:
|
|
211
|
+
self.prompt_session.app.invalidate()
|
|
212
|
+
|
|
204
213
|
def close(self) -> None:
|
|
205
214
|
try:
|
|
206
215
|
self.schema_prefetcher.stop()
|
|
@@ -63,6 +63,12 @@ class ClientConnectionMixin:
|
|
|
63
63
|
keepalive_ticks: int | None = None,
|
|
64
64
|
ssh_jump: str | None = None,
|
|
65
65
|
ssh_cli_options: str | None = None,
|
|
66
|
+
vault_address: str | None = None,
|
|
67
|
+
vault_mount: str | None = None,
|
|
68
|
+
vault_secret: str | None = None,
|
|
69
|
+
vault_password_field: str | None = None,
|
|
70
|
+
vault_username_field: str | None = None,
|
|
71
|
+
vault_username_from_vault: bool = False,
|
|
66
72
|
) -> None:
|
|
67
73
|
mylogin_cnf: dict[str, Any] = self.read_mylogin_cnf(self.mylogin_cnf)
|
|
68
74
|
# Fall back to .mylogin.cnf values only if user did not specify a value.
|
|
@@ -191,15 +197,36 @@ class ClientConnectionMixin:
|
|
|
191
197
|
assert not isinstance(passwd, int)
|
|
192
198
|
|
|
193
199
|
display_dsn = None
|
|
200
|
+
display_dsn_user = None if vault_username_from_vault else user
|
|
194
201
|
if self.ssh_tunnel:
|
|
195
202
|
display_dsn = format_connection_dsn(
|
|
196
|
-
user=
|
|
203
|
+
user=display_dsn_user,
|
|
197
204
|
host=remote_host,
|
|
198
205
|
port=remote_port,
|
|
199
206
|
socket=remote_socket,
|
|
200
207
|
database=database,
|
|
201
208
|
character_set=character_set,
|
|
202
209
|
ssh_jump=ssh_jump,
|
|
210
|
+
vault_address=vault_address,
|
|
211
|
+
vault_mount=vault_mount,
|
|
212
|
+
vault_secret=vault_secret,
|
|
213
|
+
vault_password_field=vault_password_field,
|
|
214
|
+
vault_username_field=vault_username_field,
|
|
215
|
+
)
|
|
216
|
+
elif vault_secret:
|
|
217
|
+
display_dsn = format_connection_dsn(
|
|
218
|
+
user=display_dsn_user,
|
|
219
|
+
host=host,
|
|
220
|
+
port=int_port,
|
|
221
|
+
socket=socket,
|
|
222
|
+
database=database,
|
|
223
|
+
character_set=character_set,
|
|
224
|
+
ssh_jump=ssh_jump,
|
|
225
|
+
vault_address=vault_address,
|
|
226
|
+
vault_mount=vault_mount,
|
|
227
|
+
vault_secret=vault_secret,
|
|
228
|
+
vault_password_field=vault_password_field,
|
|
229
|
+
vault_username_field=vault_username_field,
|
|
203
230
|
)
|
|
204
231
|
|
|
205
232
|
connection_info: dict[str, Any] = {
|
|
@@ -20,6 +20,7 @@ class ClientQueryMixin:
|
|
|
20
20
|
smart_completion: bool
|
|
21
21
|
main_formatter: Any
|
|
22
22
|
redirect_formatter: Any
|
|
23
|
+
explorer_formatter: Any
|
|
23
24
|
prompt_session: Any
|
|
24
25
|
null_string: str | None
|
|
25
26
|
numeric_alignment: str | None
|
|
@@ -86,6 +87,7 @@ class ClientQueryMixin:
|
|
|
86
87
|
for result in results:
|
|
87
88
|
self.main_formatter.query = query
|
|
88
89
|
self.redirect_formatter.query = query
|
|
90
|
+
self.explorer_formatter.query = query
|
|
89
91
|
output = self.format_sqlresult(
|
|
90
92
|
result,
|
|
91
93
|
is_expanded=special.is_expanded_output(),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import threading
|
|
2
|
+
from time import monotonic
|
|
2
3
|
from typing import Callable
|
|
3
4
|
|
|
4
5
|
import pymysql
|
|
@@ -8,13 +9,18 @@ from mycli.packages.sqlresult import SQLResult
|
|
|
8
9
|
from mycli.sqlcompleter import SQLCompleter
|
|
9
10
|
from mycli.sqlexecute import ServerSpecies, SQLExecute
|
|
10
11
|
|
|
12
|
+
MIN_COMPLETION_REFRESH_MESSAGE_SECONDS = 1.0
|
|
13
|
+
|
|
11
14
|
|
|
12
15
|
class CompletionRefresher:
|
|
13
16
|
refreshers: dict = {}
|
|
14
17
|
|
|
15
|
-
def __init__(self) -> None:
|
|
18
|
+
def __init__(self, invalidate_app: Callable[[], None] | None = None) -> None:
|
|
16
19
|
self._completer_thread: threading.Thread | None = None
|
|
17
20
|
self._restart_refresh = threading.Event()
|
|
21
|
+
self._refresh_visible_until = 0.0
|
|
22
|
+
self._visibility_timer: threading.Timer | None = None
|
|
23
|
+
self._invalidate_app = invalidate_app
|
|
18
24
|
|
|
19
25
|
def refresh(
|
|
20
26
|
self,
|
|
@@ -36,10 +42,14 @@ class CompletionRefresher:
|
|
|
36
42
|
if completer_options is None:
|
|
37
43
|
completer_options = {}
|
|
38
44
|
|
|
39
|
-
if self.
|
|
45
|
+
if self._thread_is_alive():
|
|
40
46
|
self._restart_refresh.set()
|
|
41
47
|
return [SQLResult(status="Auto-completion refresh restarted.")]
|
|
42
48
|
else:
|
|
49
|
+
if self._visibility_timer is not None:
|
|
50
|
+
self._visibility_timer.cancel()
|
|
51
|
+
self._visibility_timer = None
|
|
52
|
+
self._refresh_visible_until = monotonic() + MIN_COMPLETION_REFRESH_MESSAGE_SECONDS
|
|
43
53
|
self._completer_thread = threading.Thread(
|
|
44
54
|
target=self._bg_refresh, args=(executor, callbacks, completer_options), name="completion_refresh"
|
|
45
55
|
)
|
|
@@ -48,6 +58,9 @@ class CompletionRefresher:
|
|
|
48
58
|
return [SQLResult(status="Auto-completion refresh started in the background.")]
|
|
49
59
|
|
|
50
60
|
def is_refreshing(self) -> bool:
|
|
61
|
+
return self._thread_is_alive() or monotonic() < self._refresh_visible_until
|
|
62
|
+
|
|
63
|
+
def _thread_is_alive(self) -> bool:
|
|
51
64
|
return bool(self._completer_thread and self._completer_thread.is_alive())
|
|
52
65
|
|
|
53
66
|
def _bg_refresh(
|
|
@@ -73,31 +86,53 @@ class CompletionRefresher:
|
|
|
73
86
|
e.ssl,
|
|
74
87
|
)
|
|
75
88
|
except pymysql.err.OperationalError:
|
|
89
|
+
self._finish_refreshing()
|
|
76
90
|
return
|
|
77
91
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
refresher(
|
|
85
|
-
|
|
86
|
-
self._restart_refresh.
|
|
92
|
+
try:
|
|
93
|
+
# If callbacks is a single function then push it into a list.
|
|
94
|
+
if callable(callbacks):
|
|
95
|
+
callbacks = [callbacks]
|
|
96
|
+
|
|
97
|
+
while 1:
|
|
98
|
+
for refresher in self.refreshers.values():
|
|
99
|
+
refresher(completer, executor)
|
|
100
|
+
if self._restart_refresh.is_set():
|
|
101
|
+
self._restart_refresh.clear()
|
|
102
|
+
break
|
|
103
|
+
else:
|
|
104
|
+
# Break out of while loop if the for loop finishes natually
|
|
105
|
+
# without hitting the break statement.
|
|
87
106
|
break
|
|
88
|
-
else:
|
|
89
|
-
# Break out of while loop if the for loop finishes natually
|
|
90
|
-
# without hitting the break statement.
|
|
91
|
-
break
|
|
92
107
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
108
|
+
# Start over the refresh from the beginning if the for loop hit the
|
|
109
|
+
# break statement.
|
|
110
|
+
continue
|
|
96
111
|
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
for callback in callbacks:
|
|
113
|
+
callback(completer)
|
|
114
|
+
finally:
|
|
115
|
+
executor.close()
|
|
116
|
+
self._finish_refreshing()
|
|
99
117
|
|
|
100
|
-
|
|
118
|
+
def _finish_refreshing(self) -> None:
|
|
119
|
+
self._invalidate()
|
|
120
|
+
remaining = self._refresh_visible_until - monotonic()
|
|
121
|
+
if remaining <= 0:
|
|
122
|
+
return
|
|
123
|
+
if self._visibility_timer is not None:
|
|
124
|
+
self._visibility_timer.cancel()
|
|
125
|
+
self._visibility_timer = threading.Timer(remaining, self._invalidate_after_visibility_deadline)
|
|
126
|
+
self._visibility_timer.daemon = True
|
|
127
|
+
self._visibility_timer.start()
|
|
128
|
+
|
|
129
|
+
def _invalidate_after_visibility_deadline(self) -> None:
|
|
130
|
+
self._visibility_timer = None
|
|
131
|
+
self._invalidate()
|
|
132
|
+
|
|
133
|
+
def _invalidate(self) -> None:
|
|
134
|
+
if self._invalidate_app is not None:
|
|
135
|
+
self._invalidate_app()
|
|
101
136
|
|
|
102
137
|
|
|
103
138
|
def refresher(name: str, refreshers: dict = CompletionRefresher.refreshers) -> Callable:
|
|
@@ -24,15 +24,15 @@ def main_execute_from_cli(mycli: 'MyCli', cli_args: 'CliArgs') -> int:
|
|
|
24
24
|
execute_sql = cli_args.execute
|
|
25
25
|
if cli_args.format == 'csv':
|
|
26
26
|
mycli.main_formatter.format_name = 'csv'
|
|
27
|
-
if execute_sql.endswith(r'\G'):
|
|
27
|
+
if execute_sql.endswith((r'\G', r'\g', r'\x')):
|
|
28
28
|
execute_sql = execute_sql[:-2]
|
|
29
29
|
elif cli_args.format == 'tsv':
|
|
30
30
|
mycli.main_formatter.format_name = 'tsv'
|
|
31
|
-
if execute_sql.endswith(r'\G'):
|
|
31
|
+
if execute_sql.endswith((r'\G', r'\g', r'\x')):
|
|
32
32
|
execute_sql = execute_sql[:-2]
|
|
33
33
|
elif cli_args.format == 'table':
|
|
34
34
|
mycli.main_formatter.format_name = 'ascii'
|
|
35
|
-
if execute_sql.endswith(r'\G'):
|
|
35
|
+
if execute_sql.endswith((r'\G', r'\g', r'\x')):
|
|
36
36
|
execute_sql = execute_sql[:-2]
|
|
37
37
|
else:
|
|
38
38
|
mycli.main_formatter.format_name = 'tsv'
|
|
@@ -9,6 +9,7 @@ from importlib import resources
|
|
|
9
9
|
import os
|
|
10
10
|
import random
|
|
11
11
|
import re
|
|
12
|
+
import shutil
|
|
12
13
|
import subprocess
|
|
13
14
|
import sys
|
|
14
15
|
import time
|
|
@@ -42,6 +43,7 @@ import mycli as mycli_package
|
|
|
42
43
|
from mycli.clibuffer import cli_is_multiline
|
|
43
44
|
from mycli.clistyle import style_factory_ptoolkit
|
|
44
45
|
from mycli.clitoolbar import create_toolbar_tokens_func
|
|
46
|
+
from mycli.compat import WIN
|
|
45
47
|
from mycli.constants import (
|
|
46
48
|
DEFAULT_HOST,
|
|
47
49
|
DEFAULT_WIDTH,
|
|
@@ -613,6 +615,7 @@ def _one_iteration(
|
|
|
613
615
|
|
|
614
616
|
special.set_expanded_output(False)
|
|
615
617
|
special.set_forced_horizontal_output(False)
|
|
618
|
+
special.set_explorer_output(False)
|
|
616
619
|
|
|
617
620
|
try:
|
|
618
621
|
text = handle_editor_command(
|
|
@@ -716,6 +719,7 @@ def _one_iteration(
|
|
|
716
719
|
results = sqlexecute.run(text)
|
|
717
720
|
mycli.main_formatter.query = text
|
|
718
721
|
mycli.redirect_formatter.query = text
|
|
722
|
+
mycli.explorer_formatter.query = text
|
|
719
723
|
successful = True
|
|
720
724
|
_output_results(mycli, state, results, start)
|
|
721
725
|
special.unset_once_if_written(mycli.post_redirect_command)
|
|
@@ -854,12 +858,25 @@ def _tips_picker() -> str:
|
|
|
854
858
|
return random.choice(tips) if tips else r'\? or "help" for help!'
|
|
855
859
|
|
|
856
860
|
|
|
861
|
+
def _configure_editor(mycli: 'MyCli') -> None:
|
|
862
|
+
if configured_editor := mycli.config['editor'].get('editor_command'):
|
|
863
|
+
os.environ['VISUAL'] = configured_editor
|
|
864
|
+
elif not os.environ.get('VISUAL') and not os.environ.get('EDITOR'):
|
|
865
|
+
if shutil.which('code'):
|
|
866
|
+
os.environ['VISUAL'] = 'code --wait'
|
|
867
|
+
elif WIN:
|
|
868
|
+
os.environ['VISUAL'] = 'edit'
|
|
869
|
+
else:
|
|
870
|
+
os.environ['VISUAL'] = 'vi'
|
|
871
|
+
|
|
872
|
+
|
|
857
873
|
def main_repl(mycli: 'MyCli') -> None:
|
|
858
874
|
sqlexecute = mycli.sqlexecute
|
|
859
875
|
assert sqlexecute is not None
|
|
860
876
|
state = ReplState()
|
|
861
877
|
|
|
862
878
|
mycli.configure_pager()
|
|
879
|
+
_configure_editor(mycli)
|
|
863
880
|
if mycli.smart_completion and not mycli.sandbox_mode:
|
|
864
881
|
mycli.refresh_completions()
|
|
865
882
|
|