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.
- {mycli-2.7.0 → mycli-2.8.0}/.github/workflows/publish.yml +1 -1
- {mycli-2.7.0 → mycli-2.8.0}/MANIFEST.in +1 -0
- {mycli-2.7.0/mycli.egg-info → mycli-2.8.0}/PKG-INFO +16 -1
- {mycli-2.7.0 → mycli-2.8.0}/changelog.md +16 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/main_modes/repl.py +10 -2
- mycli-2.8.0/mycli/resources/completions/zsh/_mycli +66 -0
- {mycli-2.7.0 → mycli-2.8.0/mycli.egg-info}/PKG-INFO +16 -1
- {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/SOURCES.txt +3 -1
- {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/scm_file_list.json +3 -1
- mycli-2.8.0/mycli.egg-info/scm_version.json +8 -0
- {mycli-2.7.0 → mycli-2.8.0}/pyproject.toml +17 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/environment.py +4 -18
- mycli-2.8.0/test/pytests/test_zsh_completion.py +118 -0
- mycli-2.7.0/mycli.egg-info/scm_version.json +0 -8
- {mycli-2.7.0 → mycli-2.8.0}/.git-blame-ignore-revs +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/.github/dependabot.yml +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/.github/workflows/ci.yml +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/.github/workflows/lint.yml +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/.github/workflows/typecheck.yml +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/.gitignore +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/AGENTS.md +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/AUTHORS.rst +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/CONTRIBUTING.md +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/LICENSE.txt +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/README.md +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/SPONSORS.rst +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/doc/key_bindings.rst +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/doc/llm.md +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/doc/screenshots/main.gif +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/doc/screenshots/tables.png +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/doc/screenshots/total_histogram.png +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/doc/transforms.md +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/AUTHORS +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/SPONSORS +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/TIPS +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/__init__.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/app_state.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/cli_runner.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/clibuffer.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/client.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/client_commands.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/client_connection.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/client_query.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/clistyle.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/clitoolbar.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/compat.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/completion_refresher.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/config.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/constants.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/key_bindings.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/lexer.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/main.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/main_modes/batch.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/main_modes/checkup.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/main_modes/execute.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/main_modes/list_dsn.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/myclirc +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/output.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/__init__.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/batch_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/cli_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/completion_engine.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/filepaths.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/hybrid_redirection.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/interactive_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/key_binding_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/polars_transform.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/ptoolkit/__init__.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/ptoolkit/fzf.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/ptoolkit/history.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/ptoolkit/utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/__init__.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/dbcommands.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/delimitercommand.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/dsn_aliases.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/favoritequeries.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/iocommands.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/llm.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/main.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/special/utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/sql_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/sqlresult.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/string_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/tabular_output/__init__.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/packages/tabular_output/sql_format.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/password_sources.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/schema_prefetcher.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/sqlcompleter.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/sqlexecute.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/ssh_tunnel.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/types.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli/vault.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/dependency_links.txt +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/entry_points.txt +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/requires.txt +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/mycli.egg-info/top_level.txt +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/setup.cfg +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/__init__.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/__init__.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/auto_vertical.feature +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/basic_commands.feature +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/connection.feature +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/crud_database.feature +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/crud_table.feature +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/db_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/fixture_data/help_commands.txt +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/fixture_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/iocommands.feature +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/named_queries.feature +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/specials.feature +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/__init__.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/auto_vertical.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/basic_commands.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/connection.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/crud_database.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/crud_table.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/iocommands.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/named_queries.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/specials.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/steps/wrappers.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/features/wrappager.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/myclirc +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/mylogin.cnf +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/conftest.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_app_state.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_batch_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_checkup.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_cli_runner.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_cli_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_clibuffer.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_client.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_client_commands.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_client_connection.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_client_query.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_clistyle.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_clitoolbar.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_completer_use_switch.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_completion_engine.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_completion_refresher.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_config.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_delimitercommand.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_dsn_aliases.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_favoritequeries.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_filepaths.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_hybrid_redirection.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_interactive_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_key_binding_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_key_bindings.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_lexer.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_main.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_main_modes_batch.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_main_modes_execute.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_main_modes_list_dsn.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_main_modes_repl.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_naive_completion.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_output.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_password_sources.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_polars_transform.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_ptoolkit_fzf.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_ptoolkit_history.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_ptoolkit_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_schema_prefetcher.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_smart_completion_public_schema_only.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_dbcommands.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_init.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_iocommands.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_llm.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_main.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_special_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_sql_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_sqlcompleter.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_sqlexecute.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_sqlresult.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_ssh_tunnel.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_string_utils.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_tabular_output.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/pytests/test_vault.py +0 -0
- {mycli-2.7.0 → mycli-2.8.0}/test/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mycli
|
|
3
|
-
Version: 2.
|
|
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
|
|
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.
|
|
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
|
}
|
|
@@ -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
|
-
|
|
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(
|
|
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(
|
|
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
|
+
'''
|
|
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
|
|
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
|
|
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
|