mycli 2.3.0__tar.gz → 2.5.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.

Potentially problematic release.


This version of mycli might be problematic. Click here for more details.

Files changed (179) hide show
  1. {mycli-2.3.0 → mycli-2.5.0}/.github/workflows/ci.yml +12 -12
  2. {mycli-2.3.0 → mycli-2.5.0}/.github/workflows/lint.yml +1 -1
  3. {mycli-2.3.0 → mycli-2.5.0}/.github/workflows/publish.yml +14 -14
  4. {mycli-2.3.0 → mycli-2.5.0}/.github/workflows/typecheck.yml +3 -3
  5. {mycli-2.3.0 → mycli-2.5.0}/AGENTS.md +2 -0
  6. {mycli-2.3.0/mycli.egg-info → mycli-2.5.0}/PKG-INFO +8 -2
  7. {mycli-2.3.0 → mycli-2.5.0}/README.md +1 -0
  8. {mycli-2.3.0 → mycli-2.5.0}/changelog.md +36 -0
  9. mycli-2.5.0/doc/transforms.md +98 -0
  10. {mycli-2.3.0 → mycli-2.5.0}/mycli/TIPS +2 -0
  11. {mycli-2.3.0 → mycli-2.5.0}/mycli/cli_runner.py +37 -19
  12. {mycli-2.3.0 → mycli-2.5.0}/mycli/client.py +8 -2
  13. {mycli-2.3.0 → mycli-2.5.0}/mycli/client_connection.py +47 -24
  14. {mycli-2.3.0 → mycli-2.5.0}/mycli/clitoolbar.py +13 -13
  15. {mycli-2.3.0 → mycli-2.5.0}/mycli/main.py +0 -9
  16. {mycli-2.3.0 → mycli-2.5.0}/mycli/main_modes/repl.py +59 -10
  17. {mycli-2.3.0 → mycli-2.5.0}/mycli/myclirc +25 -3
  18. mycli-2.5.0/mycli/packages/polars_transform.py +220 -0
  19. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/special/__init__.py +2 -0
  20. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/special/iocommands.py +5 -0
  21. mycli-2.5.0/mycli/password_sources.py +77 -0
  22. {mycli-2.3.0 → mycli-2.5.0}/mycli/types.py +7 -0
  23. {mycli-2.3.0 → mycli-2.5.0/mycli.egg-info}/PKG-INFO +8 -2
  24. {mycli-2.3.0 → mycli-2.5.0}/mycli.egg-info/SOURCES.txt +5 -0
  25. {mycli-2.3.0 → mycli-2.5.0}/mycli.egg-info/requires.txt +7 -1
  26. {mycli-2.3.0 → mycli-2.5.0}/mycli.egg-info/scm_file_list.json +5 -0
  27. mycli-2.5.0/mycli.egg-info/scm_version.json +8 -0
  28. {mycli-2.3.0 → mycli-2.5.0}/pyproject.toml +7 -1
  29. {mycli-2.3.0 → mycli-2.5.0}/test/features/environment.py +10 -1
  30. {mycli-2.3.0 → mycli-2.5.0}/test/myclirc +25 -3
  31. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_cli_runner.py +94 -9
  32. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_client.py +32 -0
  33. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_client_connection.py +117 -50
  34. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_clitoolbar.py +3 -3
  35. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_main.py +33 -22
  36. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_main_modes_repl.py +301 -0
  37. mycli-2.5.0/test/pytests/test_password_sources.py +81 -0
  38. mycli-2.5.0/test/pytests/test_polars_transform.py +405 -0
  39. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_special_iocommands.py +17 -2
  40. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_tabular_output.py +4 -1
  41. mycli-2.3.0/mycli.egg-info/scm_version.json +0 -8
  42. {mycli-2.3.0 → mycli-2.5.0}/.git-blame-ignore-revs +0 -0
  43. {mycli-2.3.0 → mycli-2.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  44. {mycli-2.3.0 → mycli-2.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  45. {mycli-2.3.0 → mycli-2.5.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  46. {mycli-2.3.0 → mycli-2.5.0}/.github/dependabot.yml +0 -0
  47. {mycli-2.3.0 → mycli-2.5.0}/.gitignore +0 -0
  48. {mycli-2.3.0 → mycli-2.5.0}/AUTHORS.rst +0 -0
  49. {mycli-2.3.0 → mycli-2.5.0}/CONTRIBUTING.md +0 -0
  50. {mycli-2.3.0 → mycli-2.5.0}/LICENSE.txt +0 -0
  51. {mycli-2.3.0 → mycli-2.5.0}/MANIFEST.in +0 -0
  52. {mycli-2.3.0 → mycli-2.5.0}/SPONSORS.rst +0 -0
  53. {mycli-2.3.0 → mycli-2.5.0}/doc/key_bindings.rst +0 -0
  54. {mycli-2.3.0 → mycli-2.5.0}/doc/llm.md +0 -0
  55. {mycli-2.3.0 → mycli-2.5.0}/doc/screenshots/main.gif +0 -0
  56. {mycli-2.3.0 → mycli-2.5.0}/doc/screenshots/tables.png +0 -0
  57. {mycli-2.3.0 → mycli-2.5.0}/mycli/AUTHORS +0 -0
  58. {mycli-2.3.0 → mycli-2.5.0}/mycli/SPONSORS +0 -0
  59. {mycli-2.3.0 → mycli-2.5.0}/mycli/__init__.py +0 -0
  60. {mycli-2.3.0 → mycli-2.5.0}/mycli/app_state.py +0 -0
  61. {mycli-2.3.0 → mycli-2.5.0}/mycli/clibuffer.py +0 -0
  62. {mycli-2.3.0 → mycli-2.5.0}/mycli/client_commands.py +0 -0
  63. {mycli-2.3.0 → mycli-2.5.0}/mycli/client_query.py +0 -0
  64. {mycli-2.3.0 → mycli-2.5.0}/mycli/clistyle.py +0 -0
  65. {mycli-2.3.0 → mycli-2.5.0}/mycli/compat.py +0 -0
  66. {mycli-2.3.0 → mycli-2.5.0}/mycli/completion_refresher.py +0 -0
  67. {mycli-2.3.0 → mycli-2.5.0}/mycli/config.py +0 -0
  68. {mycli-2.3.0 → mycli-2.5.0}/mycli/constants.py +0 -0
  69. {mycli-2.3.0 → mycli-2.5.0}/mycli/key_bindings.py +0 -0
  70. {mycli-2.3.0 → mycli-2.5.0}/mycli/lexer.py +0 -0
  71. {mycli-2.3.0 → mycli-2.5.0}/mycli/main_modes/batch.py +0 -0
  72. {mycli-2.3.0 → mycli-2.5.0}/mycli/main_modes/checkup.py +0 -0
  73. {mycli-2.3.0 → mycli-2.5.0}/mycli/main_modes/execute.py +0 -0
  74. {mycli-2.3.0 → mycli-2.5.0}/mycli/main_modes/list_dsn.py +0 -0
  75. {mycli-2.3.0 → mycli-2.5.0}/mycli/output.py +0 -0
  76. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/__init__.py +0 -0
  77. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/batch_utils.py +0 -0
  78. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/cli_utils.py +0 -0
  79. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/completion_engine.py +0 -0
  80. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/filepaths.py +0 -0
  81. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/hybrid_redirection.py +0 -0
  82. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/interactive_utils.py +0 -0
  83. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/key_binding_utils.py +0 -0
  84. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/ptoolkit/__init__.py +0 -0
  85. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/ptoolkit/fzf.py +0 -0
  86. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/ptoolkit/history.py +0 -0
  87. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/ptoolkit/utils.py +0 -0
  88. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/special/dbcommands.py +0 -0
  89. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/special/delimitercommand.py +0 -0
  90. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/special/dsn_aliases.py +0 -0
  91. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/special/favoritequeries.py +0 -0
  92. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/special/llm.py +0 -0
  93. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/special/main.py +0 -0
  94. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/special/utils.py +0 -0
  95. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/sql_utils.py +0 -0
  96. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/sqlresult.py +0 -0
  97. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/string_utils.py +0 -0
  98. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/tabular_output/__init__.py +0 -0
  99. {mycli-2.3.0 → mycli-2.5.0}/mycli/packages/tabular_output/sql_format.py +0 -0
  100. {mycli-2.3.0 → mycli-2.5.0}/mycli/schema_prefetcher.py +0 -0
  101. {mycli-2.3.0 → mycli-2.5.0}/mycli/sqlcompleter.py +0 -0
  102. {mycli-2.3.0 → mycli-2.5.0}/mycli/sqlexecute.py +0 -0
  103. {mycli-2.3.0 → mycli-2.5.0}/mycli/ssh_tunnel.py +0 -0
  104. {mycli-2.3.0 → mycli-2.5.0}/mycli/vault.py +0 -0
  105. {mycli-2.3.0 → mycli-2.5.0}/mycli.egg-info/dependency_links.txt +0 -0
  106. {mycli-2.3.0 → mycli-2.5.0}/mycli.egg-info/entry_points.txt +0 -0
  107. {mycli-2.3.0 → mycli-2.5.0}/mycli.egg-info/top_level.txt +0 -0
  108. {mycli-2.3.0 → mycli-2.5.0}/setup.cfg +0 -0
  109. {mycli-2.3.0 → mycli-2.5.0}/test/__init__.py +0 -0
  110. {mycli-2.3.0 → mycli-2.5.0}/test/features/__init__.py +0 -0
  111. {mycli-2.3.0 → mycli-2.5.0}/test/features/auto_vertical.feature +0 -0
  112. {mycli-2.3.0 → mycli-2.5.0}/test/features/basic_commands.feature +0 -0
  113. {mycli-2.3.0 → mycli-2.5.0}/test/features/connection.feature +0 -0
  114. {mycli-2.3.0 → mycli-2.5.0}/test/features/crud_database.feature +0 -0
  115. {mycli-2.3.0 → mycli-2.5.0}/test/features/crud_table.feature +0 -0
  116. {mycli-2.3.0 → mycli-2.5.0}/test/features/db_utils.py +0 -0
  117. {mycli-2.3.0 → mycli-2.5.0}/test/features/fixture_data/help_commands.txt +0 -0
  118. {mycli-2.3.0 → mycli-2.5.0}/test/features/fixture_utils.py +0 -0
  119. {mycli-2.3.0 → mycli-2.5.0}/test/features/iocommands.feature +0 -0
  120. {mycli-2.3.0 → mycli-2.5.0}/test/features/named_queries.feature +0 -0
  121. {mycli-2.3.0 → mycli-2.5.0}/test/features/specials.feature +0 -0
  122. {mycli-2.3.0 → mycli-2.5.0}/test/features/steps/__init__.py +0 -0
  123. {mycli-2.3.0 → mycli-2.5.0}/test/features/steps/auto_vertical.py +0 -0
  124. {mycli-2.3.0 → mycli-2.5.0}/test/features/steps/basic_commands.py +0 -0
  125. {mycli-2.3.0 → mycli-2.5.0}/test/features/steps/connection.py +0 -0
  126. {mycli-2.3.0 → mycli-2.5.0}/test/features/steps/crud_database.py +0 -0
  127. {mycli-2.3.0 → mycli-2.5.0}/test/features/steps/crud_table.py +0 -0
  128. {mycli-2.3.0 → mycli-2.5.0}/test/features/steps/iocommands.py +0 -0
  129. {mycli-2.3.0 → mycli-2.5.0}/test/features/steps/named_queries.py +0 -0
  130. {mycli-2.3.0 → mycli-2.5.0}/test/features/steps/specials.py +0 -0
  131. {mycli-2.3.0 → mycli-2.5.0}/test/features/steps/utils.py +0 -0
  132. {mycli-2.3.0 → mycli-2.5.0}/test/features/steps/wrappers.py +0 -0
  133. {mycli-2.3.0 → mycli-2.5.0}/test/features/wrappager.py +0 -0
  134. {mycli-2.3.0 → mycli-2.5.0}/test/mylogin.cnf +0 -0
  135. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/conftest.py +0 -0
  136. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_app_state.py +0 -0
  137. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_batch_utils.py +0 -0
  138. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_checkup.py +0 -0
  139. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_cli_utils.py +0 -0
  140. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_clibuffer.py +0 -0
  141. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_client_commands.py +0 -0
  142. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_client_query.py +0 -0
  143. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_clistyle.py +0 -0
  144. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_completer_use_switch.py +0 -0
  145. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_completion_engine.py +0 -0
  146. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_completion_refresher.py +0 -0
  147. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_config.py +0 -0
  148. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_delimitercommand.py +0 -0
  149. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_dsn_aliases.py +0 -0
  150. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_favoritequeries.py +0 -0
  151. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_filepaths.py +0 -0
  152. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_hybrid_redirection.py +0 -0
  153. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_interactive_utils.py +0 -0
  154. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_key_binding_utils.py +0 -0
  155. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_key_bindings.py +0 -0
  156. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_lexer.py +0 -0
  157. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_main_modes_batch.py +0 -0
  158. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_main_modes_execute.py +0 -0
  159. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_main_modes_list_dsn.py +0 -0
  160. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_naive_completion.py +0 -0
  161. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_output.py +0 -0
  162. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_ptoolkit_fzf.py +0 -0
  163. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_ptoolkit_history.py +0 -0
  164. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_ptoolkit_utils.py +0 -0
  165. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_schema_prefetcher.py +0 -0
  166. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_smart_completion_public_schema_only.py +0 -0
  167. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_special_dbcommands.py +0 -0
  168. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_special_init.py +0 -0
  169. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_special_llm.py +0 -0
  170. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_special_main.py +0 -0
  171. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_special_utils.py +0 -0
  172. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_sql_utils.py +0 -0
  173. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_sqlcompleter.py +0 -0
  174. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_sqlexecute.py +0 -0
  175. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_sqlresult.py +0 -0
  176. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_ssh_tunnel.py +0 -0
  177. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_string_utils.py +0 -0
  178. {mycli-2.3.0 → mycli-2.5.0}/test/pytests/test_vault.py +0 -0
  179. {mycli-2.3.0 → mycli-2.5.0}/test/utils.py +0 -0
@@ -22,14 +22,14 @@ jobs:
22
22
  python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
23
23
 
24
24
  steps:
25
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
25
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
26
26
 
27
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
27
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
28
28
  with:
29
29
  version: "latest"
30
30
 
31
31
  - name: Set up Python ${{ matrix.python-version }}
32
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
32
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
33
33
  with:
34
34
  python-version: ${{ matrix.python-version }}
35
35
 
@@ -64,14 +64,14 @@ jobs:
64
64
  python-version: ["3.14"]
65
65
 
66
66
  steps:
67
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
67
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
68
68
 
69
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
69
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
70
70
  with:
71
71
  version: "latest"
72
72
 
73
73
  - name: Set up Python ${{ matrix.python-version }}
74
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
74
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
75
75
  with:
76
76
  python-version: ${{ matrix.python-version }}
77
77
 
@@ -114,14 +114,14 @@ jobs:
114
114
  python-version: ['3.14']
115
115
 
116
116
  steps:
117
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
117
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
118
118
 
119
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
119
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
120
120
  with:
121
121
  version: 'latest'
122
122
 
123
123
  - name: Set up Python ${{ matrix.python-version }}
124
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
124
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
125
125
  with:
126
126
  python-version: ${{ matrix.python-version }}
127
127
 
@@ -217,14 +217,14 @@ jobs:
217
217
  runs-on: ubuntu-latest
218
218
 
219
219
  steps:
220
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
220
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
221
221
 
222
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
222
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
223
223
  with:
224
224
  version: "latest"
225
225
 
226
226
  - name: Set up Python ${{ matrix.python-version }}
227
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
227
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
228
228
  with:
229
229
  python-version: '3.14'
230
230
 
@@ -18,7 +18,7 @@ jobs:
18
18
 
19
19
  steps:
20
20
  - name: Check out Git repository
21
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
21
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22
22
 
23
23
  - name: Run ruff check
24
24
  uses: astral-sh/ruff-action@278981a28ce3188b1e39527901f38254bf3aac89 # v4.1.0
@@ -12,7 +12,7 @@ jobs:
12
12
  runs-on: ubuntu-latest
13
13
 
14
14
  steps:
15
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
15
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
16
16
 
17
17
  - name: Require release changelog form
18
18
  run: |
@@ -28,13 +28,13 @@ jobs:
28
28
  python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
29
29
 
30
30
  steps:
31
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
32
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
31
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
32
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
33
33
  with:
34
34
  version: "latest"
35
35
 
36
36
  - name: Set up Python ${{ matrix.python-version }}
37
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
37
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
38
38
  with:
39
39
  python-version: ${{ matrix.python-version }}
40
40
 
@@ -72,14 +72,14 @@ jobs:
72
72
  python-version: ["3.14"]
73
73
 
74
74
  steps:
75
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
75
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
76
76
 
77
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
77
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
78
78
  with:
79
79
  version: "latest"
80
80
 
81
81
  - name: Set up Python ${{ matrix.python-version }}
82
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
82
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
83
83
  with:
84
84
  python-version: ${{ matrix.python-version }}
85
85
 
@@ -122,14 +122,14 @@ jobs:
122
122
  python-version: ['3.14']
123
123
 
124
124
  steps:
125
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
125
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
126
126
 
127
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
127
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
128
128
  with:
129
129
  version: 'latest'
130
130
 
131
131
  - name: Set up Python ${{ matrix.python-version }}
132
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
132
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
133
133
  with:
134
134
  python-version: ${{ matrix.python-version }}
135
135
 
@@ -225,13 +225,13 @@ jobs:
225
225
  needs: [tests, tests-macos, tests-windows, tests-wsl]
226
226
 
227
227
  steps:
228
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
229
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
228
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
229
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
230
230
  with:
231
231
  version: "latest"
232
232
 
233
233
  - name: Set up Python
234
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
234
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
235
235
  with:
236
236
  python-version: '3.14'
237
237
 
@@ -262,4 +262,4 @@ jobs:
262
262
  name: python-packages
263
263
  path: dist/
264
264
  - name: Publish to PyPI
265
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
265
+ uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
@@ -18,14 +18,14 @@ jobs:
18
18
 
19
19
  steps:
20
20
  - name: Check out Git repository
21
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
21
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22
22
 
23
23
  - name: Set up Python
24
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
24
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
25
25
  with:
26
26
  python-version: '3.14'
27
27
 
28
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
28
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
29
29
  with:
30
30
  version: 'latest'
31
31
 
@@ -48,6 +48,8 @@ A command line client for MySQL with auto-completion and syntax highlighting.
48
48
  ├── mycli/packages/sqlresult.py # the `SQLResult` dataclass for holding responses
49
49
  ├── mycli/packages/string_utils.py # generic string utilities
50
50
  ├── mycli/packages/tabular_output/ # extends cli_helper with additional output formats
51
+ ├── mycli/password_sources.py # password sources and precedence
52
+ ├── mycli/ssh_tunnel.py # connecting over a tunnel with `--ssh-jump`
51
53
  ├── mycli/schema_prefetcher.py # background prefetcher for multi-schema auto-completion
52
54
  ├── mycli/sqlcompleter.py # offers SQL completions
53
55
  ├── mycli/sqlexecute.py # runs SQL queries
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycli
3
- Version: 2.3.0
3
+ Version: 2.5.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
@@ -36,8 +36,11 @@ Requires-Dist: llm~=0.30.0; extra == "llm"
36
36
  Requires-Dist: pydantic_core~=2.41.5; extra == "llm"
37
37
  Requires-Dist: setuptools==82.*; extra == "llm"
38
38
  Requires-Dist: pip==26.*; extra == "llm"
39
+ Provides-Extra: dataframe
40
+ Requires-Dist: polars~=1.42.1; extra == "dataframe"
41
+ Requires-Dist: altair~=6.2.2; extra == "dataframe"
39
42
  Provides-Extra: all
40
- Requires-Dist: mycli[llm]; extra == "all"
43
+ Requires-Dist: mycli[dataframe,llm]; extra == "all"
41
44
  Provides-Extra: dev
42
45
  Requires-Dist: behave~=1.3.3; extra == "dev"
43
46
  Requires-Dist: coverage~=7.13.4; extra == "dev"
@@ -53,6 +56,8 @@ Requires-Dist: pydantic_core~=2.41.5; extra == "dev"
53
56
  Requires-Dist: setuptools==82.*; extra == "dev"
54
57
  Requires-Dist: pip==26.*; extra == "dev"
55
58
  Requires-Dist: ruff~=0.15.0; extra == "dev"
59
+ Requires-Dist: polars~=1.42.1; extra == "dev"
60
+ Requires-Dist: altair~=6.2.2; extra == "dev"
56
61
  Dynamic: license-file
57
62
 
58
63
  # mycli
@@ -128,6 +133,7 @@ Features
128
133
  * Pretty print tabular data (with colors!).
129
134
  * Support for SSL connections
130
135
  * Shell-style trailing redirects with `$>`, `$>>` and `$|` operators.
136
+ * [Polars](https://pola.rs) dataframe [transforms](doc/transforms.md) with `.|` and Parquet saves with `.>`.
131
137
  * Support for querying LLMs with context derived from your schema using `/llm`.
132
138
  * Support for storing passwords in the system keyring.
133
139
 
@@ -71,6 +71,7 @@ Features
71
71
  * Pretty print tabular data (with colors!).
72
72
  * Support for SSL connections
73
73
  * Shell-style trailing redirects with `$>`, `$>>` and `$|` operators.
74
+ * [Polars](https://pola.rs) dataframe [transforms](doc/transforms.md) with `.|` and Parquet saves with `.>`.
74
75
  * Support for querying LLMs with context derived from your schema using `/llm`.
75
76
  * Support for storing passwords in the system keyring.
76
77
 
@@ -1,3 +1,39 @@
1
+ 2.5.0 (2026/07/22)
2
+ ==============
3
+
4
+ Features
5
+ ---------
6
+ * Configurable sources of credentials to save to the keyring.
7
+ * Trailing operator `.|` to transform query results as dataframes.
8
+ * Trailing operator `.>` to save query results as parquet files.
9
+
10
+
11
+ Internal
12
+ ---------
13
+ * Don't modify checked-in `test/myclirc` in the behave test suite.
14
+
15
+
16
+ 2.4.0 (2026/07/18)
17
+ ==============
18
+
19
+ Features
20
+ ---------
21
+ * Make password sources and precedence configurable.
22
+ * Ability to deselect fallback interactive password prompt.
23
+ * Add `\e` prompt/toolbar format string for edit mode.
24
+
25
+
26
+ Bug Fixes
27
+ ---------
28
+ * Don't create `~/.myclirc` if the user adopted the XDG layout for myclirc.
29
+
30
+
31
+ Internal
32
+ ---------
33
+ * Make flaky `/watch` test even more lenient for CI.
34
+ * Add missing file to `AGENTS.md` project layout.
35
+
36
+
1
37
  2.3.0 (2026/07/16)
2
38
  ==============
3
39
 
@@ -0,0 +1,98 @@
1
+ # Transforms with Polars Dataframes
2
+
3
+ ## Installing
4
+
5
+ Install mycli with dataframe support using:
6
+
7
+ ```bash
8
+ pip install --upgrade 'mycli[dataframe]'
9
+ ```
10
+
11
+ or install the Polars and Altair libraries separately.
12
+
13
+ ## BETA STATUS
14
+
15
+ Dataframe transforms are new and experimental. The interface and functionality
16
+ may still change.
17
+
18
+ Here are some known limitations:
19
+
20
+ * transforms can't be mixed with `$|` shell redirection
21
+ * multiple transform steps are not permitted
22
+ * the Altair library is provided, but plots can't yet be displayed
23
+ * results from `UNION`s may be unable to be transformed
24
+
25
+ And there are inherent limitations to the post-processing model: the entire
26
+ SQL result must be transferred from the server and loaded into local memory.
27
+
28
+ ## Transforming
29
+
30
+ In the interactive REPL, append the `.|` operator and a Python expression to a
31
+ single SQL statement. The Python expression receives
32
+
33
+ * `df`, a Polars `DataFrame`
34
+ * `pl`, the Polars module
35
+ * `alt`, the Altair module
36
+
37
+ Spaces may be required around the operator.
38
+
39
+ Transform example:
40
+
41
+ ```sql
42
+ SELECT * FROM orders .| df.group_by('customer_id').len();
43
+ ```
44
+
45
+ which is equivalent to this native SQL:
46
+
47
+ ```sql
48
+ SELECT customer_id, COUNT(1) AS len FROM orders GROUP BY customer_id;
49
+ ```
50
+
51
+ Transform expressions run with normal Python privileges, and expressions
52
+ should not be run from untrusted sources. If the transform operation
53
+ returns a Polars `DataFrame` or `Series`, the result is rendered by mycli
54
+ as tabular output; other return types currently give a warning and cannot
55
+ be displayed.
56
+
57
+ Transform expressions are useful for operations such as medians which
58
+ cannot be done (or are simply awkward) in SQL. Example:
59
+
60
+ ```sql
61
+ SELECT * FROM orders .| df.describe();
62
+ ```
63
+
64
+ ## Saving
65
+
66
+ A query result, transformed `DataFrame`, or transformed `Series` can be
67
+ written directly to Parquet with the `.>` operator.
68
+
69
+ Save example:
70
+
71
+ ```sql
72
+ SELECT * FROM orders .> orders.parquet;
73
+ ```
74
+
75
+ The `.>` operator must be last, requires a `.parquet` destination, and
76
+ overwrites any existing file. Spaces may be required around the operator.
77
+ Destination paths containing whitespace must be quoted. A successful write
78
+ reports its destination and row count.
79
+
80
+ When `post_redirect_command` is set in `~/.myclirc`, the given command runs
81
+ after a successful Parquet save.
82
+
83
+ `.>` cannot be combined with `\x`, `\G`, or `\g` special display terminators.
84
+
85
+ ## Combining
86
+
87
+ Parquet saves may be combined with dataframe transforms. Again, `.>` must
88
+ be the last operator.
89
+
90
+ Combined transform and save example:
91
+
92
+ ```sql
93
+ SELECT * FROM orders .| df.group_by('customer_id').len() .> customer_counts.parquet;
94
+ ```
95
+
96
+ ```sql
97
+ SELECT * FROM orders .| df['order_id'] .> order_ids.parquet;
98
+ ```
@@ -288,6 +288,8 @@ set editor_command = "code --wait" in ~/.myclirc to edit queries using VS Code!
288
288
 
289
289
  set up an interactive output explorer using the "[explorer]" section of ~/.myclirc!
290
290
 
291
+ customize password sources/precedence with "password_sources" in ~/.myclirc!
292
+
291
293
  ###
292
294
  ### redirection
293
295
  ###
@@ -15,6 +15,7 @@ from mycli.main_modes.checkup import main_checkup
15
15
  from mycli.main_modes.execute import main_execute_from_cli
16
16
  from mycli.main_modes.list_dsn import main_list_dsn
17
17
  from mycli.packages.cli_utils import is_valid_connection_scheme
18
+ from mycli.password_sources import PasswordCandidates
18
19
  from mycli.vault import (
19
20
  DEFAULT_VAULT_EXECUTABLE,
20
21
  DEFAULT_VAULT_PASSWORD_FIELD,
@@ -165,10 +166,15 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
165
166
  database = cli_args.dbname or cli_args.database
166
167
 
167
168
  dsn_uri = None
169
+ dsn_password: str | None = None
168
170
 
169
171
  # Treat the database argument as a DSN alias only if it matches a configured alias
170
172
  # todo why is port tested but not socket?
171
- truthy_password = cli_args.password not in (None, EMPTY_PASSWORD_FLAG_SENTINEL)
173
+ truthy_password = (
174
+ cli_args.password not in (None, EMPTY_PASSWORD_FLAG_SENTINEL)
175
+ or cli_args.password_file is not None
176
+ or os.environ.get('MYSQL_PWD') is not None
177
+ )
172
178
  if (
173
179
  database
174
180
  and "://" not in database
@@ -235,9 +241,6 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
235
241
  database = dsn_database
236
242
  if not cli_args.user and dsn_user is not None:
237
243
  cli_args.user = dsn_user
238
- # todo: rationalize the behavior of empty-string passwords here
239
- if not cli_args.password and dsn_password is not None:
240
- cli_args.password = dsn_password
241
244
  if not cli_args.host:
242
245
  cli_args.host = dsn_host
243
246
  if not cli_args.port:
@@ -364,25 +367,40 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
364
367
  use_keyring = str_to_bool(cli_args.use_keyring)
365
368
  reset_keyring = False
366
369
 
367
- if cli_args.password is None and cli_args.vault_secret:
370
+ password_candidates = PasswordCandidates()
371
+ if cli_args.password == EMPTY_PASSWORD_FLAG_SENTINEL:
372
+ password_candidates.add_value('prompt', cli_args.password)
373
+ elif cli_args.password is not None:
374
+ password_candidates.add_value('literal', cli_args.password)
375
+ if cli_args.password_file:
376
+ password_candidates.add_loader('file', lambda: main_module.get_password_from_file(cli_args.password_file))
377
+ if os.environ.get('MYSQL_PWD') is not None:
378
+ password_candidates.add_value('environment', os.environ.get('MYSQL_PWD'))
379
+ if dsn_password is not None:
380
+ password_candidates.add_value('dsn', dsn_password)
381
+
382
+ if cli_args.vault_secret:
383
+ vault_secret = cli_args.vault_secret
368
384
  vault_config = mycli.config.get('vault_beta', {})
369
385
  vault_address = cli_args.vault_address or os.environ.get('VAULT_ADDR') or vault_config.get('address') or None
370
386
  vault_mount = cli_args.vault_mount or vault_config.get('default_mount') or None
371
387
  vault_field = cli_args.vault_password_field or vault_config.get('default_password_field') or DEFAULT_VAULT_PASSWORD_FIELD
372
388
  vault_executable = vault_config.get('vault_executable') or DEFAULT_VAULT_EXECUTABLE
373
- try:
374
- vault_password: str | None = get_field_from_vault(
375
- vault_field,
376
- cli_args.vault_secret,
377
- executable=vault_executable,
378
- mount=vault_mount,
379
- address=vault_address,
380
- )
381
- except VaultError as exc:
382
- click.secho(f'Error reading password from Vault: {exc}', err=True, fg='red')
383
- sys.exit(1)
384
- else:
385
- vault_password = None
389
+
390
+ def load_vault_password() -> str | None:
391
+ try:
392
+ return get_field_from_vault(
393
+ vault_field,
394
+ vault_secret,
395
+ executable=vault_executable,
396
+ mount=vault_mount,
397
+ address=vault_address,
398
+ )
399
+ except VaultError as exc:
400
+ click.secho(f'Error reading password from Vault: {exc}', err=True, fg='red')
401
+ sys.exit(1)
402
+
403
+ password_candidates.add_loader('vault', load_vault_password)
386
404
 
387
405
  if cli_args.user is None and cli_args.vault_secret:
388
406
  vault_config = mycli.config.get('vault_beta', {})
@@ -409,7 +427,7 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
409
427
  mycli.connect(
410
428
  database=database,
411
429
  user=vault_username if cli_args.user is None else cli_args.user,
412
- passwd=vault_password if cli_args.password is None else cli_args.password,
430
+ password_candidates=password_candidates,
413
431
  host=cli_args.host,
414
432
  port=cli_args.port,
415
433
  socket=cli_args.socket,
@@ -58,9 +58,9 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
58
58
 
59
59
  # check XDG_CONFIG_HOME exists and not an empty string
60
60
  xdg_config_home = os.environ.get("XDG_CONFIG_HOME", "~/.config")
61
+ xdg_config_file = os.path.join(os.path.expanduser(xdg_config_home), "mycli", "myclirc")
61
62
  system_config_files: list[str | IO[str]] = [
62
63
  "/etc/myclirc",
63
- os.path.join(os.path.expanduser(xdg_config_home), "mycli", "myclirc"),
64
64
  ]
65
65
 
66
66
  def __init__(
@@ -72,7 +72,7 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
72
72
  login_path: str | None = None,
73
73
  auto_vertical_output: bool = False,
74
74
  warn: bool | None = None,
75
- myclirc: str = "~/.myclirc",
75
+ myclirc: str | None = None,
76
76
  show_warnings: bool | None = None,
77
77
  cli_verbosity: int = 0,
78
78
  ) -> None:
@@ -87,6 +87,12 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
87
87
  self.sandbox_mode: bool = False
88
88
  self.checkpoint: IO | None = None
89
89
 
90
+ if myclirc is None:
91
+ if os.path.exists(self.xdg_config_file):
92
+ myclirc = self.xdg_config_file
93
+ else:
94
+ myclirc = '~/.myclirc'
95
+
90
96
  # Load config.
91
97
  config_files: list[str | IO[str]] = self.system_config_files + [myclirc]
92
98