mycli 2.13.2__tar.gz → 2.15.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.13.2 → mycli-2.15.0}/.github/workflows/ci.yml +5 -5
- {mycli-2.13.2 → mycli-2.15.0}/.github/workflows/publish.yml +5 -5
- {mycli-2.13.2 → mycli-2.15.0}/.github/workflows/typecheck.yml +1 -1
- {mycli-2.13.2/mycli.egg-info → mycli-2.15.0}/PKG-INFO +1 -1
- {mycli-2.13.2 → mycli-2.15.0}/changelog.md +27 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/TIPS +4 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/boundary_tunnel.py +4 -3
- {mycli-2.13.2 → mycli-2.15.0}/mycli/clibuffer.py +1 -1
- {mycli-2.13.2 → mycli-2.15.0}/mycli/client.py +1 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/config.py +8 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/main_modes/repl.py +17 -6
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/completion_engine.py +35 -4
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/special/dsn_aliases.py +4 -1
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/special/favoritequeries.py +122 -6
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/special/iocommands.py +183 -51
- {mycli-2.13.2 → mycli-2.15.0}/mycli/sqlexecute.py +1 -1
- {mycli-2.13.2 → mycli-2.15.0/mycli.egg-info}/PKG-INFO +1 -1
- {mycli-2.13.2 → mycli-2.15.0}/mycli.egg-info/scm_file_list.json +145 -145
- mycli-2.15.0/mycli.egg-info/scm_version.json +8 -0
- mycli-2.15.0/test/features/fixture_data/help_commands.txt +42 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_clibuffer.py +6 -3
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_client_commands.py +10 -1
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_client_query.py +19 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_completion_engine.py +71 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_config.py +23 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_favoritequeries.py +211 -1
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_main_modes_repl.py +28 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_smart_completion_public_schema_only.py +4 -3
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_special_iocommands.py +438 -1
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_sqlexecute.py +10 -2
- mycli-2.13.2/mycli.egg-info/scm_version.json +0 -8
- mycli-2.13.2/test/features/fixture_data/help_commands.txt +0 -41
- {mycli-2.13.2 → mycli-2.15.0}/.git-blame-ignore-revs +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/.github/dependabot.yml +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/.github/workflows/lint.yml +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/.gitignore +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/AGENTS.md +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/AUTHORS.rst +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/CONTRIBUTING.md +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/LICENSE.txt +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/MANIFEST.in +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/README.md +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/SPONSORS.rst +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/doc/key_bindings.rst +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/doc/llm.md +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/doc/screenshots/main.gif +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/doc/screenshots/tables.png +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/doc/screenshots/total_histogram.png +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/doc/transforms.md +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/AUTHORS +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/SPONSORS +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/__init__.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/app_state.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/cli_runner.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/client_commands.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/client_connection.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/client_query.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/clistyle.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/clitoolbar.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/compat.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/completion_refresher.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/constants.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/key_bindings.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/lexer.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/main.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/main_modes/batch.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/main_modes/checkup.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/main_modes/completions.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/main_modes/execute.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/main_modes/list_dsn.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/myclirc +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/output.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/__init__.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/batch_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/cli_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/filepaths.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/hybrid_redirection.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/interactive_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/key_binding_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/polars_transform.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/ptoolkit/__init__.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/ptoolkit/fzf.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/ptoolkit/history.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/ptoolkit/utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/special/__init__.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/special/dbcommands.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/special/delimitercommand.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/special/llm.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/special/main.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/special/utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/sql_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/sqlresult.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/string_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/tabular_output/__init__.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/packages/tabular_output/sql_format.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/password_sources.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/resources/completions/bash/mycli +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/resources/completions/fish/mycli.fish +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/resources/completions/zsh/_mycli +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/schema_prefetcher.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/sqlcompleter.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/ssh_tunnel.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/types.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli/vault.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli.egg-info/SOURCES.txt +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli.egg-info/dependency_links.txt +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli.egg-info/entry_points.txt +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli.egg-info/requires.txt +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/mycli.egg-info/top_level.txt +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/pyproject.toml +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/setup.cfg +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/__init__.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/__init__.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/auto_vertical.feature +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/basic_commands.feature +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/connection.feature +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/crud_database.feature +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/crud_table.feature +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/db_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/environment.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/fixture_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/iocommands.feature +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/named_queries.feature +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/specials.feature +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/steps/__init__.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/steps/auto_vertical.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/steps/basic_commands.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/steps/connection.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/steps/crud_database.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/steps/crud_table.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/steps/iocommands.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/steps/named_queries.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/steps/specials.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/steps/utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/steps/wrappers.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/features/wrappager.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/myclirc +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/mylogin.cnf +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/conftest.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_app_state.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_bash_completion.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_batch_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_boundary_tunnel.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_checkup.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_cli_runner.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_cli_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_client.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_client_connection.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_clistyle.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_clitoolbar.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_completer_use_switch.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_completion_refresher.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_delimitercommand.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_dsn_aliases.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_filepaths.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_fish_completion.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_hybrid_redirection.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_interactive_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_key_binding_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_key_bindings.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_lexer.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_main.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_main_modes_batch.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_main_modes_execute.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_main_modes_list_dsn.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_naive_completion.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_output.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_password_sources.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_polars_transform.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_ptoolkit_fzf.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_ptoolkit_history.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_ptoolkit_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_schema_prefetcher.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_special_dbcommands.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_special_init.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_special_llm.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_special_main.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_special_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_sql_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_sqlcompleter.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_sqlresult.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_ssh_tunnel.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_string_utils.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_tabular_output.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_vault.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/pytests/test_zsh_completion.py +0 -0
- {mycli-2.13.2 → mycli-2.15.0}/test/utils.py +0 -0
|
@@ -24,7 +24,7 @@ jobs:
|
|
|
24
24
|
steps:
|
|
25
25
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
26
26
|
|
|
27
|
-
- uses: astral-sh/setup-uv@
|
|
27
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
28
28
|
with:
|
|
29
29
|
version: "latest"
|
|
30
30
|
|
|
@@ -66,7 +66,7 @@ jobs:
|
|
|
66
66
|
steps:
|
|
67
67
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
68
68
|
|
|
69
|
-
- uses: astral-sh/setup-uv@
|
|
69
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
70
70
|
with:
|
|
71
71
|
version: "latest"
|
|
72
72
|
|
|
@@ -116,7 +116,7 @@ jobs:
|
|
|
116
116
|
steps:
|
|
117
117
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
118
118
|
|
|
119
|
-
- uses: astral-sh/setup-uv@
|
|
119
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
120
120
|
with:
|
|
121
121
|
version: 'latest'
|
|
122
122
|
|
|
@@ -165,7 +165,7 @@ jobs:
|
|
|
165
165
|
run: |
|
|
166
166
|
mkdir -p /root/mycli
|
|
167
167
|
|
|
168
|
-
- uses: ubuntu/wsl/.github/actions/wsl-checkout@
|
|
168
|
+
- uses: ubuntu/wsl/.github/actions/wsl-checkout@e2114d0a5ebea62e6b2e1565f175871ed7558d20
|
|
169
169
|
with:
|
|
170
170
|
distro: Ubuntu-24.04
|
|
171
171
|
working-dir: /root/mycli
|
|
@@ -219,7 +219,7 @@ jobs:
|
|
|
219
219
|
steps:
|
|
220
220
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
221
221
|
|
|
222
|
-
- uses: astral-sh/setup-uv@
|
|
222
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
223
223
|
with:
|
|
224
224
|
version: "latest"
|
|
225
225
|
|
|
@@ -29,7 +29,7 @@ jobs:
|
|
|
29
29
|
|
|
30
30
|
steps:
|
|
31
31
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
32
|
-
- uses: astral-sh/setup-uv@
|
|
32
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
33
33
|
with:
|
|
34
34
|
version: "latest"
|
|
35
35
|
|
|
@@ -74,7 +74,7 @@ jobs:
|
|
|
74
74
|
steps:
|
|
75
75
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
76
76
|
|
|
77
|
-
- uses: astral-sh/setup-uv@
|
|
77
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
78
78
|
with:
|
|
79
79
|
version: "latest"
|
|
80
80
|
|
|
@@ -124,7 +124,7 @@ jobs:
|
|
|
124
124
|
steps:
|
|
125
125
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
126
126
|
|
|
127
|
-
- uses: astral-sh/setup-uv@
|
|
127
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
128
128
|
with:
|
|
129
129
|
version: 'latest'
|
|
130
130
|
|
|
@@ -173,7 +173,7 @@ jobs:
|
|
|
173
173
|
run: |
|
|
174
174
|
mkdir -p /root/mycli
|
|
175
175
|
|
|
176
|
-
- uses: ubuntu/wsl/.github/actions/wsl-checkout@
|
|
176
|
+
- uses: ubuntu/wsl/.github/actions/wsl-checkout@e2114d0a5ebea62e6b2e1565f175871ed7558d20
|
|
177
177
|
with:
|
|
178
178
|
distro: Ubuntu-24.04
|
|
179
179
|
working-dir: /root/mycli
|
|
@@ -226,7 +226,7 @@ jobs:
|
|
|
226
226
|
|
|
227
227
|
steps:
|
|
228
228
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
229
|
-
- uses: astral-sh/setup-uv@
|
|
229
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
230
230
|
with:
|
|
231
231
|
version: "latest"
|
|
232
232
|
|
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
2.15.0 (2026/08/20)
|
|
2
|
+
==============
|
|
3
|
+
|
|
4
|
+
Features
|
|
5
|
+
---------
|
|
6
|
+
* Add alternative interface `/favorite` for favorite queries.
|
|
7
|
+
* Add `/dsn edit` subcommand to edit an existing DSN.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Bug Fixes
|
|
11
|
+
---------
|
|
12
|
+
* Lower Boundary tunnel stabilization pause to 0.15 sec.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Documentation
|
|
16
|
+
---------
|
|
17
|
+
* TIPS: `/favorite run` composes with the `\G` and `\x` terminators.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
2.14.0 (2026/08/15)
|
|
21
|
+
==============
|
|
22
|
+
|
|
23
|
+
Features
|
|
24
|
+
---------
|
|
25
|
+
* Revert modal cursor shapes in vi edit mode.
|
|
26
|
+
|
|
27
|
+
|
|
1
28
|
2.13.2 (2026/08/12)
|
|
2
29
|
==============
|
|
3
30
|
|
|
@@ -64,6 +64,8 @@ edit a query in an external editor using /edit <filename>!
|
|
|
64
64
|
|
|
65
65
|
Favorite queries support Jinja templates and named arguments!
|
|
66
66
|
|
|
67
|
+
Manage favorite queries with /favorite!
|
|
68
|
+
|
|
67
69
|
/l lists databases!
|
|
68
70
|
|
|
69
71
|
/once <filename> appends the next result to <filename>!
|
|
@@ -146,6 +148,8 @@ display output vertically using \G at the end of a query!
|
|
|
146
148
|
|
|
147
149
|
display output in an interactive explorer \x at the end of a query!
|
|
148
150
|
|
|
151
|
+
"/favorite run" can be combined with the \G or \x terminators!
|
|
152
|
+
|
|
149
153
|
run SQL scripts in batch mode using the standard input!
|
|
150
154
|
|
|
151
155
|
indexed columns are tagged with a "*" in completion menus!
|
|
@@ -15,7 +15,10 @@ from typing import IO
|
|
|
15
15
|
|
|
16
16
|
from mycli.compat import WIN
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
# This should be as small as possible and yet still
|
|
19
|
+
# ward off SSL errors at connection time. We know
|
|
20
|
+
# that 0.1 is too small on at least one Mac.
|
|
21
|
+
TUNNEL_STABILIZATION_PAUSE = 0.15
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
class BoundaryTunnelError(RuntimeError):
|
|
@@ -148,8 +151,6 @@ class BoundaryTunnel:
|
|
|
148
151
|
self._raise_if_failed()
|
|
149
152
|
if self._is_listening():
|
|
150
153
|
self._ready.set()
|
|
151
|
-
# todo: if this works to ward off SSL errors at connection time,
|
|
152
|
-
# try making the pause as small as possible
|
|
153
154
|
time.sleep(TUNNEL_STABILIZATION_PAUSE)
|
|
154
155
|
return
|
|
155
156
|
time.sleep(0.05)
|
|
@@ -29,7 +29,7 @@ def _multiline_exception(text: str) -> bool:
|
|
|
29
29
|
# Multi-statement favorite query is a special case. Because there will
|
|
30
30
|
# be a semicolon separating statements, we can't consider semicolon an
|
|
31
31
|
# EOL. Let's consider an empty line an EOL instead.
|
|
32
|
-
if
|
|
32
|
+
if iocommands.is_favorite_save_command(text):
|
|
33
33
|
return orig.endswith("\n")
|
|
34
34
|
|
|
35
35
|
return (
|
|
@@ -122,6 +122,7 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
|
|
|
122
122
|
self.config,
|
|
123
123
|
myclirc,
|
|
124
124
|
c['main'].get('shared_favorites_file'),
|
|
125
|
+
system_config_files=self.system_config_files,
|
|
125
126
|
)
|
|
126
127
|
DsnAliases.instance = DsnAliases.from_config(
|
|
127
128
|
self.config,
|
|
@@ -48,6 +48,7 @@ def read_config_file(
|
|
|
48
48
|
f: str | IO[str],
|
|
49
49
|
list_values: bool = True,
|
|
50
50
|
preserve_quotes: bool = False,
|
|
51
|
+
raise_errors: bool = False,
|
|
51
52
|
) -> ConfigObj | LimiitedQuotePreservingConfigObj | None:
|
|
52
53
|
"""Read a config file.
|
|
53
54
|
|
|
@@ -57,6 +58,9 @@ def read_config_file(
|
|
|
57
58
|
not unquoted. We are disabling list_values when reading MySQL config files
|
|
58
59
|
so we can correctly interpret commas in passwords.
|
|
59
60
|
|
|
61
|
+
Set *raise_errors* to propagate parsing and I/O errors instead of logging
|
|
62
|
+
them and returning a partial config or ``None``.
|
|
63
|
+
|
|
60
64
|
"""
|
|
61
65
|
|
|
62
66
|
if isinstance(f, str):
|
|
@@ -68,10 +72,14 @@ def read_config_file(
|
|
|
68
72
|
else:
|
|
69
73
|
config = ConfigObj(f, interpolation=False, encoding="utf8", list_values=list_values)
|
|
70
74
|
except ConfigObjError as e:
|
|
75
|
+
if raise_errors:
|
|
76
|
+
raise
|
|
71
77
|
log(logger, logging.WARNING, "Unable to parse line {0} of config file '{1}'.".format(e.line_number, f))
|
|
72
78
|
log(logger, logging.WARNING, "Using successfully parsed config values.")
|
|
73
79
|
return e.config
|
|
74
80
|
except (IOError, OSError) as e:
|
|
81
|
+
if raise_errors:
|
|
82
|
+
raise
|
|
75
83
|
log(logger, logging.WARNING, "You don't have permission to read config file '{0}'.".format(e.filename))
|
|
76
84
|
return None
|
|
77
85
|
|
|
@@ -23,7 +23,6 @@ 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
|
|
27
26
|
from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
|
|
28
27
|
from prompt_toolkit.filters import Condition, has_focus, is_done
|
|
29
28
|
from prompt_toolkit.formatted_text import (
|
|
@@ -106,6 +105,7 @@ _PROMPT_TARGETS: dict[int, 'MyCli'] = {}
|
|
|
106
105
|
class ReplState:
|
|
107
106
|
iterations: int = 0
|
|
108
107
|
mutating: bool = False
|
|
108
|
+
buffer_text: str | None = None
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
@Condition
|
|
@@ -450,6 +450,9 @@ def _output_results(
|
|
|
450
450
|
mycli.logger.debug('status: %r', result.status)
|
|
451
451
|
mycli.logger.debug('command: %r', result.command)
|
|
452
452
|
threshold = 1000
|
|
453
|
+
if result.command is not None and result.command['name'] == 'set_buffer':
|
|
454
|
+
state.buffer_text = str(result.command['text'])
|
|
455
|
+
continue
|
|
453
456
|
if result.command is not None and result.command['name'] == 'watch':
|
|
454
457
|
if watch_count > 0:
|
|
455
458
|
try:
|
|
@@ -608,7 +611,6 @@ def _build_prompt_session(
|
|
|
608
611
|
enable_system_prompt=True,
|
|
609
612
|
enable_suspend=True,
|
|
610
613
|
editing_mode=editing_mode,
|
|
611
|
-
cursor=ModalCursorShapeConfig() if editing_mode == EditingMode.VI else CursorShape.BLOCK,
|
|
612
614
|
search_ignore_case=True,
|
|
613
615
|
)
|
|
614
616
|
|
|
@@ -632,10 +634,19 @@ def _one_iteration(
|
|
|
632
634
|
try:
|
|
633
635
|
assert mycli.prompt_session is not None
|
|
634
636
|
loaded_message_fn = partial(_get_prompt_message, mycli, mycli.prompt_session.app)
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
637
|
+
if state.buffer_text is None:
|
|
638
|
+
text = mycli.prompt_session.prompt(
|
|
639
|
+
inputhook=inputhook,
|
|
640
|
+
message=loaded_message_fn,
|
|
641
|
+
)
|
|
642
|
+
else:
|
|
643
|
+
default = state.buffer_text
|
|
644
|
+
state.buffer_text = None
|
|
645
|
+
text = mycli.prompt_session.prompt(
|
|
646
|
+
default=default,
|
|
647
|
+
inputhook=inputhook,
|
|
648
|
+
message=loaded_message_fn,
|
|
649
|
+
)
|
|
639
650
|
except KeyboardInterrupt:
|
|
640
651
|
return
|
|
641
652
|
|
|
@@ -8,6 +8,7 @@ import sqlparse
|
|
|
8
8
|
from sqlparse.sql import Comparison, Identifier, Token, Where
|
|
9
9
|
|
|
10
10
|
from mycli.packages.special.dsn_aliases import DSN_SUBCOMMANDS
|
|
11
|
+
from mycli.packages.special.favoritequeries import FAVORITE_SUBCOMMANDS
|
|
11
12
|
from mycli.packages.special.main import COMMANDS as SPECIAL_COMMANDS
|
|
12
13
|
from mycli.packages.special.main import parse_special_command
|
|
13
14
|
from mycli.packages.sql_utils import extract_tables, find_prev_keyword, last_word
|
|
@@ -831,6 +832,36 @@ def suggest_special(text: str) -> list[dict[str, Any]]:
|
|
|
831
832
|
if cmd in ["\\llm", "/llm", "\\ai", "/ai"]:
|
|
832
833
|
return [{"type": "llm"}]
|
|
833
834
|
|
|
835
|
+
if cmd.lower() in (r'\favorite', '/favorite'):
|
|
836
|
+
favorite_arguments = _arg.split(maxsplit=1)
|
|
837
|
+
if favorite_arguments and favorite_arguments[0].lower() in ('run', 'eval'):
|
|
838
|
+
if len(favorite_arguments) == 1 and not text[-1].isspace():
|
|
839
|
+
return []
|
|
840
|
+
expansion_arg = favorite_arguments[1] if len(favorite_arguments) == 2 else ''
|
|
841
|
+
return suggest_favorite_query_with_template(text, expansion_arg)
|
|
842
|
+
if favorite_arguments and favorite_arguments[0].lower() == 'save':
|
|
843
|
+
if len(favorite_arguments) == 1 and not text[-1].isspace():
|
|
844
|
+
return []
|
|
845
|
+
if len(favorite_arguments) == 1:
|
|
846
|
+
return [{'type': 'favoritequery'}]
|
|
847
|
+
save_arguments = favorite_arguments[1].split(maxsplit=1)
|
|
848
|
+
if len(save_arguments) == 1 and not text[-1].isspace():
|
|
849
|
+
return [{'type': 'favoritequery'}]
|
|
850
|
+
query = save_arguments[1] if len(save_arguments) == 2 else ''
|
|
851
|
+
if query and text[-1].isspace():
|
|
852
|
+
query += ' '
|
|
853
|
+
return suggest_type(query, query)
|
|
854
|
+
if favorite_arguments and favorite_arguments[0].lower() in ('edit', 'delete'):
|
|
855
|
+
if len(favorite_arguments) == 1:
|
|
856
|
+
return [] if not text[-1].isspace() else [{'type': 'favoritequery'}]
|
|
857
|
+
target_arguments = favorite_arguments[1].split()
|
|
858
|
+
if len(target_arguments) == 1 and not text[-1].isspace():
|
|
859
|
+
return [{'type': 'favoritequery'}]
|
|
860
|
+
return []
|
|
861
|
+
if favorite_arguments and favorite_arguments[0].lower() in FAVORITE_SUBCOMMANDS:
|
|
862
|
+
return []
|
|
863
|
+
return [{'type': 'special_subcommand', 'subcommands': list(FAVORITE_SUBCOMMANDS)}]
|
|
864
|
+
|
|
834
865
|
if cmd.lower() in (r'\config', '/config'):
|
|
835
866
|
config_arguments = _arg.split(maxsplit=1)
|
|
836
867
|
config_subcommands = ['help', 'get', 'search', 'edit']
|
|
@@ -848,10 +879,10 @@ def suggest_special(text: str) -> list[dict[str, Any]]:
|
|
|
848
879
|
|
|
849
880
|
if cmd.lower() in (r'\dsn', '/dsn'):
|
|
850
881
|
dsn_arguments = _arg.split(maxsplit=1)
|
|
851
|
-
|
|
852
|
-
if dsn_arguments and dsn_arguments[0].lower()
|
|
882
|
+
completing_alias_target = (len(dsn_arguments) == 1 and text[-1].isspace()) or (len(dsn_arguments) == 2 and not text[-1].isspace())
|
|
883
|
+
if dsn_arguments and dsn_arguments[0].lower() in ('edit', 'delete') and completing_alias_target:
|
|
853
884
|
return [{'type': 'dsn_alias'}]
|
|
854
|
-
if dsn_arguments and dsn_arguments[0].lower()
|
|
885
|
+
if dsn_arguments and dsn_arguments[0].lower() in ('edit', 'delete') and len(dsn_arguments) == 2:
|
|
855
886
|
return []
|
|
856
887
|
if dsn_arguments and dsn_arguments[0].lower() == 'save':
|
|
857
888
|
completing_option = (len(dsn_arguments) == 1 and text[-1].isspace()) or (
|
|
@@ -872,7 +903,7 @@ def suggest_special(text: str) -> list[dict[str, Any]]:
|
|
|
872
903
|
if completing_option:
|
|
873
904
|
return [{'type': 'special_subcommand', 'subcommands': [option]}]
|
|
874
905
|
return []
|
|
875
|
-
if dsn_arguments and dsn_arguments[0].lower() in DSN_SUBCOMMANDS - {'delete'}:
|
|
906
|
+
if dsn_arguments and dsn_arguments[0].lower() in DSN_SUBCOMMANDS - {'edit', 'delete'}:
|
|
876
907
|
return []
|
|
877
908
|
return [{'type': 'special_subcommand', 'subcommands': list(DSN_SUBCOMMANDS)}]
|
|
878
909
|
|
|
@@ -13,7 +13,7 @@ logger = logging.getLogger(__name__)
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from mycli.client import MyCli
|
|
15
15
|
|
|
16
|
-
DSN_SUBCOMMANDS = {'help', 'list', 'show', 'save', 'delete'}
|
|
16
|
+
DSN_SUBCOMMANDS = {'help', 'list', 'show', 'save', 'edit', 'delete'}
|
|
17
17
|
INVALID_DSN_ALIAS_ERROR = 'Error: DSN aliases cannot start with a dash.'
|
|
18
18
|
MISSING = object()
|
|
19
19
|
|
|
@@ -84,6 +84,9 @@ Examples:
|
|
|
84
84
|
│ rocks │ mysql://mycli@localhost/mysql?prompt=%5Cd%3E%5C_ │
|
|
85
85
|
└───────┴──────────────────────────────────────────────────┘
|
|
86
86
|
|
|
87
|
+
# Edit a DSN saved alias in an external editor.
|
|
88
|
+
mysql> /dsn edit rocks
|
|
89
|
+
|
|
87
90
|
# Delete a DSN alias.
|
|
88
91
|
mysql> /dsn delete rocks
|
|
89
92
|
"""
|
|
@@ -1,23 +1,85 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from collections.abc import Mapping
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
5
6
|
import re
|
|
6
|
-
from typing import Any
|
|
7
|
+
from typing import IO, Any
|
|
7
8
|
|
|
9
|
+
from configobj import ConfigObjError
|
|
8
10
|
from jinja2 import meta, nodes
|
|
9
11
|
from jinja2.sandbox import SandboxedEnvironment
|
|
10
12
|
|
|
11
|
-
from mycli.config import log, read_config_file
|
|
13
|
+
from mycli.config import log, read_config_file, read_config_files
|
|
12
14
|
|
|
13
15
|
logger = logging.getLogger(__name__)
|
|
14
16
|
|
|
15
17
|
MISSING = object()
|
|
18
|
+
FAVORITE_SUBCOMMANDS = ('help', 'list', 'reload', 'run', 'eval', 'save', 'edit', 'delete')
|
|
19
|
+
FAVORITE_COMMAND_HELP = '''
|
|
20
|
+
Favorite Queries are a way to save frequently used queries
|
|
21
|
+
with a short name.
|
|
22
|
+
Examples:
|
|
23
|
+
|
|
24
|
+
# Save a new favorite query.
|
|
25
|
+
> /favorite save simple SELECT * FROM abc WHERE a IS NOT NULL;
|
|
26
|
+
|
|
27
|
+
# When multi-line mode is on, pressing Return twice is needed to save.
|
|
28
|
+
# This supports multi-statement favorites.
|
|
29
|
+
|
|
30
|
+
# List all favorite queries.
|
|
31
|
+
> /favorite list
|
|
32
|
+
╒═══════════╤══════════════════════════════════════════════════╕
|
|
33
|
+
│ Name │ Query │
|
|
34
|
+
╞═══════════╪══════════════════════════════════════════════════╡
|
|
35
|
+
│ simple │ SELECT * FROM abc WHERE a IS NOT NULL │
|
|
36
|
+
│ find_user │ SELECT * FROM users WHERE name = '{{ kv.name }}' │
|
|
37
|
+
╘═══════════╧══════════════════════════════════════════════════╛
|
|
38
|
+
|
|
39
|
+
# Run a favorite query.
|
|
40
|
+
> /favorite run simple
|
|
41
|
+
╒════════╤════════╕
|
|
42
|
+
│ a │ b │
|
|
43
|
+
╞════════╪════════╡
|
|
44
|
+
│ 日本語 │ 日本語 │
|
|
45
|
+
╘════════╧════════╛
|
|
46
|
+
|
|
47
|
+
# Run a favorite query containing {{ kv.name }} in the template.
|
|
48
|
+
> /favorite run find_user --name=henry
|
|
49
|
+
> /favorite run find_user --name henry
|
|
50
|
+
|
|
51
|
+
# Run a favorite query containing positional parameter $1 in the
|
|
52
|
+
# template.
|
|
53
|
+
> /favorite run find_user henry
|
|
54
|
+
|
|
55
|
+
# Use -- to disambiguate positional parameters such as $1, especially
|
|
56
|
+
# if the positional value starts with a dash.
|
|
57
|
+
> /favorite run query --key=value -- positional-value
|
|
58
|
+
> /favorite run query -- --positional-value-which-looks-like-a-flag--
|
|
59
|
+
|
|
60
|
+
# Expand a favorite query into the command-line buffer without running it.
|
|
61
|
+
> /favorite eval find_user --name=henry
|
|
62
|
+
|
|
63
|
+
# Edit a favorite query in an external editor.
|
|
64
|
+
> /favorite edit simple
|
|
65
|
+
|
|
66
|
+
# Delete a favorite query.
|
|
67
|
+
> /favorite delete simple
|
|
68
|
+
simple: Deleted.
|
|
69
|
+
|
|
70
|
+
# Reload favorite queries from the configuration files.
|
|
71
|
+
> /favorite reload
|
|
72
|
+
|
|
73
|
+
See also the alternative interface /f, /fs, /fd.'''
|
|
16
74
|
|
|
17
75
|
favorite_query_template_environment = SandboxedEnvironment(autoescape=False)
|
|
18
76
|
favorite_query_variable_pattern = re.compile(r'^[A-Za-z_][A-Za-z0-9_-]*$')
|
|
19
77
|
|
|
20
78
|
|
|
79
|
+
class FavoriteQueryReloadError(Exception):
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
|
|
21
83
|
def analyze_favorite_query_template(query: str) -> tuple[set[str], bool]:
|
|
22
84
|
"""Return statically referenced keys and whether ``kv`` is used dynamically."""
|
|
23
85
|
parsed_template = favorite_query_template_environment.parse(query)
|
|
@@ -116,14 +178,24 @@ Examples:
|
|
|
116
178
|
# Delete a favorite query.
|
|
117
179
|
> /fd simple
|
|
118
180
|
simple: Deleted.
|
|
181
|
+
|
|
182
|
+
See also the alternative interface /favorite.
|
|
119
183
|
"""
|
|
120
184
|
|
|
121
185
|
# Class-level variable, for convenience to use as a singleton.
|
|
122
186
|
instance: FavoriteQueries
|
|
123
187
|
|
|
124
|
-
def __init__(
|
|
188
|
+
def __init__(
|
|
189
|
+
self,
|
|
190
|
+
config: Any,
|
|
191
|
+
config_file: str | None = None,
|
|
192
|
+
shared_favorites_file: str | None = None,
|
|
193
|
+
system_config_files: list[str | IO[str]] | None = None,
|
|
194
|
+
) -> None:
|
|
125
195
|
self.config = config
|
|
126
196
|
self.config_file = config_file
|
|
197
|
+
self.shared_favorites_file = shared_favorites_file
|
|
198
|
+
self.system_config_files = list(system_config_files or [])
|
|
127
199
|
|
|
128
200
|
@classmethod
|
|
129
201
|
def from_config(
|
|
@@ -131,10 +203,10 @@ Examples:
|
|
|
131
203
|
config: Any,
|
|
132
204
|
config_file: str | None = None,
|
|
133
205
|
shared_favorites_file: str | None = None,
|
|
206
|
+
system_config_files: list[str | IO[str]] | None = None,
|
|
134
207
|
) -> FavoriteQueries:
|
|
135
|
-
favorites = cls(config, config_file)
|
|
136
208
|
if not shared_favorites_file:
|
|
137
|
-
return
|
|
209
|
+
return cls(config, config_file, system_config_files=system_config_files)
|
|
138
210
|
|
|
139
211
|
shared_favorites_file = os.path.expanduser(shared_favorites_file)
|
|
140
212
|
if not os.path.isabs(shared_favorites_file):
|
|
@@ -143,7 +215,9 @@ Examples:
|
|
|
143
215
|
logging.WARNING,
|
|
144
216
|
f"Shared favorites file path must be absolute: '{shared_favorites_file}'.",
|
|
145
217
|
)
|
|
146
|
-
return
|
|
218
|
+
return cls(config, config_file, system_config_files=system_config_files)
|
|
219
|
+
|
|
220
|
+
favorites = cls(config, config_file, shared_favorites_file, system_config_files)
|
|
147
221
|
|
|
148
222
|
if not os.path.isfile(shared_favorites_file):
|
|
149
223
|
log(
|
|
@@ -164,6 +238,48 @@ Examples:
|
|
|
164
238
|
config[cls.section_name].update(configured_queries)
|
|
165
239
|
return favorites
|
|
166
240
|
|
|
241
|
+
def _reload_queries(self, path: str, description: str) -> dict[str, str]:
|
|
242
|
+
expanded_path = os.path.expanduser(path)
|
|
243
|
+
if not os.path.isfile(expanded_path):
|
|
244
|
+
raise FavoriteQueryReloadError(f"unable to read {description} file '{expanded_path}'")
|
|
245
|
+
try:
|
|
246
|
+
config = read_config_file(expanded_path, raise_errors=True)
|
|
247
|
+
except (ConfigObjError, OSError, UnicodeError) as exc:
|
|
248
|
+
raise FavoriteQueryReloadError(
|
|
249
|
+
f"unable to read {description} file '{expanded_path}': {exc}",
|
|
250
|
+
) from exc
|
|
251
|
+
|
|
252
|
+
assert config is not None
|
|
253
|
+
configured_queries = config.get(self.section_name, {})
|
|
254
|
+
if not isinstance(configured_queries, Mapping) or any(
|
|
255
|
+
not isinstance(name, str) or not isinstance(query, str) for name, query in configured_queries.items()
|
|
256
|
+
):
|
|
257
|
+
raise FavoriteQueryReloadError(
|
|
258
|
+
f"invalid [{self.section_name}] section in {description} file '{expanded_path}'",
|
|
259
|
+
)
|
|
260
|
+
return dict(configured_queries)
|
|
261
|
+
|
|
262
|
+
def reload(self) -> None:
|
|
263
|
+
if self.config_file is None:
|
|
264
|
+
raise FavoriteQueryReloadError('no user configuration file is configured')
|
|
265
|
+
|
|
266
|
+
user_queries = self._reload_queries(self.config_file, 'user configuration')
|
|
267
|
+
system_config = read_config_files(self.system_config_files, ignore_package_defaults=True)
|
|
268
|
+
system_queries = system_config.get(self.section_name, {})
|
|
269
|
+
if not isinstance(system_queries, Mapping) or any(
|
|
270
|
+
not isinstance(name, str) or not isinstance(query, str) for name, query in system_queries.items()
|
|
271
|
+
):
|
|
272
|
+
raise FavoriteQueryReloadError(f'invalid [{self.section_name}] section in system configuration files')
|
|
273
|
+
queries: dict[str, str] = {}
|
|
274
|
+
if self.shared_favorites_file is not None:
|
|
275
|
+
try:
|
|
276
|
+
queries.update(self._reload_queries(self.shared_favorites_file, 'shared favorites'))
|
|
277
|
+
except FavoriteQueryReloadError as exc:
|
|
278
|
+
log(logger, logging.WARNING, str(exc))
|
|
279
|
+
queries.update(system_queries)
|
|
280
|
+
queries.update(user_queries)
|
|
281
|
+
self.config[self.section_name] = queries
|
|
282
|
+
|
|
167
283
|
def _clean_query(self, query: str | None) -> str | None:
|
|
168
284
|
if not query:
|
|
169
285
|
return query
|