mycli 2.4.0__tar.gz → 2.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. {mycli-2.4.0 → mycli-2.5.1}/.github/workflows/ci.yml +12 -12
  2. {mycli-2.4.0 → mycli-2.5.1}/.github/workflows/lint.yml +1 -1
  3. {mycli-2.4.0 → mycli-2.5.1}/.github/workflows/publish.yml +14 -14
  4. {mycli-2.4.0 → mycli-2.5.1}/.github/workflows/typecheck.yml +3 -3
  5. {mycli-2.4.0/mycli.egg-info → mycli-2.5.1}/PKG-INFO +10 -4
  6. {mycli-2.4.0 → mycli-2.5.1}/README.md +3 -2
  7. {mycli-2.4.0 → mycli-2.5.1}/changelog.md +23 -0
  8. mycli-2.5.1/doc/transforms.md +98 -0
  9. {mycli-2.4.0 → mycli-2.5.1}/mycli/client_connection.py +28 -8
  10. {mycli-2.4.0 → mycli-2.5.1}/mycli/main_modes/repl.py +50 -9
  11. {mycli-2.4.0 → mycli-2.5.1}/mycli/myclirc +9 -3
  12. mycli-2.5.1/mycli/packages/polars_transform.py +220 -0
  13. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/special/__init__.py +2 -0
  14. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/special/iocommands.py +5 -0
  15. {mycli-2.4.0 → mycli-2.5.1}/mycli/types.py +7 -0
  16. {mycli-2.4.0 → mycli-2.5.1/mycli.egg-info}/PKG-INFO +10 -4
  17. {mycli-2.4.0 → mycli-2.5.1}/mycli.egg-info/SOURCES.txt +3 -0
  18. {mycli-2.4.0 → mycli-2.5.1}/mycli.egg-info/requires.txt +7 -1
  19. {mycli-2.4.0 → mycli-2.5.1}/mycli.egg-info/scm_file_list.json +3 -0
  20. mycli-2.5.1/mycli.egg-info/scm_version.json +8 -0
  21. {mycli-2.4.0 → mycli-2.5.1}/pyproject.toml +7 -1
  22. {mycli-2.4.0 → mycli-2.5.1}/test/features/environment.py +10 -1
  23. {mycli-2.4.0 → mycli-2.5.1}/test/myclirc +9 -3
  24. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_client_connection.py +8 -2
  25. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_main_modes_repl.py +277 -0
  26. mycli-2.5.1/test/pytests/test_polars_transform.py +405 -0
  27. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_special_iocommands.py +13 -0
  28. mycli-2.4.0/mycli.egg-info/scm_version.json +0 -8
  29. {mycli-2.4.0 → mycli-2.5.1}/.git-blame-ignore-revs +0 -0
  30. {mycli-2.4.0 → mycli-2.5.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  31. {mycli-2.4.0 → mycli-2.5.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  32. {mycli-2.4.0 → mycli-2.5.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  33. {mycli-2.4.0 → mycli-2.5.1}/.github/dependabot.yml +0 -0
  34. {mycli-2.4.0 → mycli-2.5.1}/.gitignore +0 -0
  35. {mycli-2.4.0 → mycli-2.5.1}/AGENTS.md +0 -0
  36. {mycli-2.4.0 → mycli-2.5.1}/AUTHORS.rst +0 -0
  37. {mycli-2.4.0 → mycli-2.5.1}/CONTRIBUTING.md +0 -0
  38. {mycli-2.4.0 → mycli-2.5.1}/LICENSE.txt +0 -0
  39. {mycli-2.4.0 → mycli-2.5.1}/MANIFEST.in +0 -0
  40. {mycli-2.4.0 → mycli-2.5.1}/SPONSORS.rst +0 -0
  41. {mycli-2.4.0 → mycli-2.5.1}/doc/key_bindings.rst +0 -0
  42. {mycli-2.4.0 → mycli-2.5.1}/doc/llm.md +0 -0
  43. {mycli-2.4.0 → mycli-2.5.1}/doc/screenshots/main.gif +0 -0
  44. {mycli-2.4.0 → mycli-2.5.1}/doc/screenshots/tables.png +0 -0
  45. {mycli-2.4.0 → mycli-2.5.1}/mycli/AUTHORS +0 -0
  46. {mycli-2.4.0 → mycli-2.5.1}/mycli/SPONSORS +0 -0
  47. {mycli-2.4.0 → mycli-2.5.1}/mycli/TIPS +0 -0
  48. {mycli-2.4.0 → mycli-2.5.1}/mycli/__init__.py +0 -0
  49. {mycli-2.4.0 → mycli-2.5.1}/mycli/app_state.py +0 -0
  50. {mycli-2.4.0 → mycli-2.5.1}/mycli/cli_runner.py +0 -0
  51. {mycli-2.4.0 → mycli-2.5.1}/mycli/clibuffer.py +0 -0
  52. {mycli-2.4.0 → mycli-2.5.1}/mycli/client.py +0 -0
  53. {mycli-2.4.0 → mycli-2.5.1}/mycli/client_commands.py +0 -0
  54. {mycli-2.4.0 → mycli-2.5.1}/mycli/client_query.py +0 -0
  55. {mycli-2.4.0 → mycli-2.5.1}/mycli/clistyle.py +0 -0
  56. {mycli-2.4.0 → mycli-2.5.1}/mycli/clitoolbar.py +0 -0
  57. {mycli-2.4.0 → mycli-2.5.1}/mycli/compat.py +0 -0
  58. {mycli-2.4.0 → mycli-2.5.1}/mycli/completion_refresher.py +0 -0
  59. {mycli-2.4.0 → mycli-2.5.1}/mycli/config.py +0 -0
  60. {mycli-2.4.0 → mycli-2.5.1}/mycli/constants.py +0 -0
  61. {mycli-2.4.0 → mycli-2.5.1}/mycli/key_bindings.py +0 -0
  62. {mycli-2.4.0 → mycli-2.5.1}/mycli/lexer.py +0 -0
  63. {mycli-2.4.0 → mycli-2.5.1}/mycli/main.py +0 -0
  64. {mycli-2.4.0 → mycli-2.5.1}/mycli/main_modes/batch.py +0 -0
  65. {mycli-2.4.0 → mycli-2.5.1}/mycli/main_modes/checkup.py +0 -0
  66. {mycli-2.4.0 → mycli-2.5.1}/mycli/main_modes/execute.py +0 -0
  67. {mycli-2.4.0 → mycli-2.5.1}/mycli/main_modes/list_dsn.py +0 -0
  68. {mycli-2.4.0 → mycli-2.5.1}/mycli/output.py +0 -0
  69. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/__init__.py +0 -0
  70. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/batch_utils.py +0 -0
  71. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/cli_utils.py +0 -0
  72. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/completion_engine.py +0 -0
  73. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/filepaths.py +0 -0
  74. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/hybrid_redirection.py +0 -0
  75. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/interactive_utils.py +0 -0
  76. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/key_binding_utils.py +0 -0
  77. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/ptoolkit/__init__.py +0 -0
  78. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/ptoolkit/fzf.py +0 -0
  79. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/ptoolkit/history.py +0 -0
  80. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/ptoolkit/utils.py +0 -0
  81. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/special/dbcommands.py +0 -0
  82. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/special/delimitercommand.py +0 -0
  83. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/special/dsn_aliases.py +0 -0
  84. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/special/favoritequeries.py +0 -0
  85. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/special/llm.py +0 -0
  86. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/special/main.py +0 -0
  87. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/special/utils.py +0 -0
  88. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/sql_utils.py +0 -0
  89. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/sqlresult.py +0 -0
  90. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/string_utils.py +0 -0
  91. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/tabular_output/__init__.py +0 -0
  92. {mycli-2.4.0 → mycli-2.5.1}/mycli/packages/tabular_output/sql_format.py +0 -0
  93. {mycli-2.4.0 → mycli-2.5.1}/mycli/password_sources.py +0 -0
  94. {mycli-2.4.0 → mycli-2.5.1}/mycli/schema_prefetcher.py +0 -0
  95. {mycli-2.4.0 → mycli-2.5.1}/mycli/sqlcompleter.py +0 -0
  96. {mycli-2.4.0 → mycli-2.5.1}/mycli/sqlexecute.py +0 -0
  97. {mycli-2.4.0 → mycli-2.5.1}/mycli/ssh_tunnel.py +0 -0
  98. {mycli-2.4.0 → mycli-2.5.1}/mycli/vault.py +0 -0
  99. {mycli-2.4.0 → mycli-2.5.1}/mycli.egg-info/dependency_links.txt +0 -0
  100. {mycli-2.4.0 → mycli-2.5.1}/mycli.egg-info/entry_points.txt +0 -0
  101. {mycli-2.4.0 → mycli-2.5.1}/mycli.egg-info/top_level.txt +0 -0
  102. {mycli-2.4.0 → mycli-2.5.1}/setup.cfg +0 -0
  103. {mycli-2.4.0 → mycli-2.5.1}/test/__init__.py +0 -0
  104. {mycli-2.4.0 → mycli-2.5.1}/test/features/__init__.py +0 -0
  105. {mycli-2.4.0 → mycli-2.5.1}/test/features/auto_vertical.feature +0 -0
  106. {mycli-2.4.0 → mycli-2.5.1}/test/features/basic_commands.feature +0 -0
  107. {mycli-2.4.0 → mycli-2.5.1}/test/features/connection.feature +0 -0
  108. {mycli-2.4.0 → mycli-2.5.1}/test/features/crud_database.feature +0 -0
  109. {mycli-2.4.0 → mycli-2.5.1}/test/features/crud_table.feature +0 -0
  110. {mycli-2.4.0 → mycli-2.5.1}/test/features/db_utils.py +0 -0
  111. {mycli-2.4.0 → mycli-2.5.1}/test/features/fixture_data/help_commands.txt +0 -0
  112. {mycli-2.4.0 → mycli-2.5.1}/test/features/fixture_utils.py +0 -0
  113. {mycli-2.4.0 → mycli-2.5.1}/test/features/iocommands.feature +0 -0
  114. {mycli-2.4.0 → mycli-2.5.1}/test/features/named_queries.feature +0 -0
  115. {mycli-2.4.0 → mycli-2.5.1}/test/features/specials.feature +0 -0
  116. {mycli-2.4.0 → mycli-2.5.1}/test/features/steps/__init__.py +0 -0
  117. {mycli-2.4.0 → mycli-2.5.1}/test/features/steps/auto_vertical.py +0 -0
  118. {mycli-2.4.0 → mycli-2.5.1}/test/features/steps/basic_commands.py +0 -0
  119. {mycli-2.4.0 → mycli-2.5.1}/test/features/steps/connection.py +0 -0
  120. {mycli-2.4.0 → mycli-2.5.1}/test/features/steps/crud_database.py +0 -0
  121. {mycli-2.4.0 → mycli-2.5.1}/test/features/steps/crud_table.py +0 -0
  122. {mycli-2.4.0 → mycli-2.5.1}/test/features/steps/iocommands.py +0 -0
  123. {mycli-2.4.0 → mycli-2.5.1}/test/features/steps/named_queries.py +0 -0
  124. {mycli-2.4.0 → mycli-2.5.1}/test/features/steps/specials.py +0 -0
  125. {mycli-2.4.0 → mycli-2.5.1}/test/features/steps/utils.py +0 -0
  126. {mycli-2.4.0 → mycli-2.5.1}/test/features/steps/wrappers.py +0 -0
  127. {mycli-2.4.0 → mycli-2.5.1}/test/features/wrappager.py +0 -0
  128. {mycli-2.4.0 → mycli-2.5.1}/test/mylogin.cnf +0 -0
  129. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/conftest.py +0 -0
  130. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_app_state.py +0 -0
  131. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_batch_utils.py +0 -0
  132. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_checkup.py +0 -0
  133. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_cli_runner.py +0 -0
  134. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_cli_utils.py +0 -0
  135. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_clibuffer.py +0 -0
  136. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_client.py +0 -0
  137. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_client_commands.py +0 -0
  138. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_client_query.py +0 -0
  139. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_clistyle.py +0 -0
  140. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_clitoolbar.py +0 -0
  141. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_completer_use_switch.py +0 -0
  142. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_completion_engine.py +0 -0
  143. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_completion_refresher.py +0 -0
  144. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_config.py +0 -0
  145. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_delimitercommand.py +0 -0
  146. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_dsn_aliases.py +0 -0
  147. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_favoritequeries.py +0 -0
  148. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_filepaths.py +0 -0
  149. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_hybrid_redirection.py +0 -0
  150. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_interactive_utils.py +0 -0
  151. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_key_binding_utils.py +0 -0
  152. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_key_bindings.py +0 -0
  153. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_lexer.py +0 -0
  154. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_main.py +0 -0
  155. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_main_modes_batch.py +0 -0
  156. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_main_modes_execute.py +0 -0
  157. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_main_modes_list_dsn.py +0 -0
  158. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_naive_completion.py +0 -0
  159. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_output.py +0 -0
  160. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_password_sources.py +0 -0
  161. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_ptoolkit_fzf.py +0 -0
  162. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_ptoolkit_history.py +0 -0
  163. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_ptoolkit_utils.py +0 -0
  164. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_schema_prefetcher.py +0 -0
  165. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_smart_completion_public_schema_only.py +0 -0
  166. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_special_dbcommands.py +0 -0
  167. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_special_init.py +0 -0
  168. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_special_llm.py +0 -0
  169. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_special_main.py +0 -0
  170. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_special_utils.py +0 -0
  171. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_sql_utils.py +0 -0
  172. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_sqlcompleter.py +0 -0
  173. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_sqlexecute.py +0 -0
  174. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_sqlresult.py +0 -0
  175. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_ssh_tunnel.py +0 -0
  176. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_string_utils.py +0 -0
  177. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_tabular_output.py +0 -0
  178. {mycli-2.4.0 → mycli-2.5.1}/test/pytests/test_vault.py +0 -0
  179. {mycli-2.4.0 → mycli-2.5.1}/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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycli
3
- Version: 2.4.0
3
+ Version: 2.5.1
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
@@ -64,8 +69,8 @@ A command line client for MySQL with auto-completion and syntax highlighting.
64
69
  Homepage: [https://mycli.net](https://mycli.net)
65
70
  Documentation: [https://mycli.net/docs](https://mycli.net/docs)
66
71
 
67
- ![Completion](doc/screenshots/tables.png)
68
- ![CompletionGif](doc/screenshots/main.gif)
72
+ ![Completion](https://raw.githubusercontent.com/dbcli/mycli/main/doc/screenshots/tables.png)
73
+ ![CompletionGif](https://raw.githubusercontent.com/dbcli/mycli/main/doc/screenshots/main.gif)
69
74
 
70
75
  Mycli is compatible with MySQL, MariaDB, Percona, TiDB, and Apache Doris.
71
76
 
@@ -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
 
@@ -7,8 +7,8 @@ A command line client for MySQL with auto-completion and syntax highlighting.
7
7
  Homepage: [https://mycli.net](https://mycli.net)
8
8
  Documentation: [https://mycli.net/docs](https://mycli.net/docs)
9
9
 
10
- ![Completion](doc/screenshots/tables.png)
11
- ![CompletionGif](doc/screenshots/main.gif)
10
+ ![Completion](https://raw.githubusercontent.com/dbcli/mycli/main/doc/screenshots/tables.png)
11
+ ![CompletionGif](https://raw.githubusercontent.com/dbcli/mycli/main/doc/screenshots/main.gif)
12
12
 
13
13
  Mycli is compatible with MySQL, MariaDB, Percona, TiDB, and Apache Doris.
14
14
 
@@ -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,26 @@
1
+ 2.5.1 (2026/07/22)
2
+ ==============
3
+
4
+ Documentation
5
+ ---------
6
+ * Attempt to make `README.md` images display on PyPi.
7
+
8
+
9
+ 2.5.0 (2026/07/22)
10
+ ==============
11
+
12
+ Features
13
+ ---------
14
+ * Configurable sources of credentials to save to the keyring.
15
+ * Trailing operator `.|` to transform query results as dataframes.
16
+ * Trailing operator `.>` to save query results as parquet files.
17
+
18
+
19
+ Internal
20
+ ---------
21
+ * Don't modify checked-in `test/myclirc` in the behave test suite.
22
+
23
+
1
24
  2.4.0 (2026/07/18)
2
25
  ==============
3
26
 
@@ -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
+ ```
@@ -80,6 +80,7 @@ class ClientConnectionMixin:
80
80
  user_connection_config = self.config_without_package_defaults.get('connection', {})
81
81
  self.keepalive_ticks = keepalive_ticks
82
82
  self.ssh_tunnel = None
83
+ self.selected_password = None
83
84
 
84
85
  int_port = port and int(port)
85
86
  if not int_port:
@@ -92,6 +93,11 @@ class ClientConnectionMixin:
92
93
  else:
93
94
  password_source_precedence = ['prompt']
94
95
 
96
+ if self.config.get('main', {}).get('keyring_sources'):
97
+ keyring_sources = self.config.get('main').as_list('keyring_sources')
98
+ else:
99
+ keyring_sources = []
100
+
95
101
  if ssh_jump:
96
102
  remote_host = host or DEFAULT_HOST
97
103
  remote_port = int_port or DEFAULT_PORT
@@ -185,9 +191,9 @@ class ClientConnectionMixin:
185
191
  if use_keyring and not reset_keyring:
186
192
  password_candidates.add_loader('keyring', lambda: keyring.get_password(keyring_domain, keyring_identifier))
187
193
 
188
- selected_password = password_candidates.resolve(password_source_precedence)
189
- passwd = selected_password.value if selected_password is not None else None
190
- keyring_retrieved_cleanly = selected_password is not None and selected_password.source == 'keyring'
194
+ self.selected_password = password_candidates.resolve(password_source_precedence)
195
+ passwd = self.selected_password.value if self.selected_password is not None else None
196
+ keyring_retrieved_cleanly = self.selected_password is not None and self.selected_password.source == 'keyring'
191
197
 
192
198
  # prompt for password if requested by user
193
199
  if passwd == EMPTY_PASSWORD_FLAG_SENTINEL:
@@ -253,15 +259,25 @@ class ClientConnectionMixin:
253
259
  connection_info['port'] = int_port
254
260
  connection_info['socket'] = socket
255
261
 
256
- def _update_keyring(password: str | None, keyring_retrieved_cleanly: bool):
262
+ def _update_keyring(
263
+ password: str | None,
264
+ password_source: str | None,
265
+ keyring_retrieved_cleanly: bool,
266
+ ):
257
267
  if not password:
258
268
  return
269
+ if password_source not in keyring_sources:
270
+ return
259
271
  if reset_keyring or (use_keyring and not keyring_retrieved_cleanly):
260
272
  try:
261
273
  saved_pw = keyring.get_password(keyring_domain, keyring_identifier)
262
274
  if password != saved_pw or reset_keyring:
263
275
  keyring.set_password(keyring_domain, keyring_identifier, password)
264
- click.secho(f'Password saved to the system keyring at {keyring_domain}/{keyring_identifier}', err=True)
276
+ click.secho(
277
+ f'Password from source: "{password_source}" '
278
+ f'saved to the system keyring at {keyring_domain}/{keyring_identifier}',
279
+ err=True,
280
+ )
265
281
  except Exception as e:
266
282
  click.secho(f'Password not saved to the system keyring: {e}', err=True, fg='red')
267
283
 
@@ -273,7 +289,11 @@ class ClientConnectionMixin:
273
289
  ) -> None:
274
290
  try:
275
291
  if keyring_save_eligible:
276
- _update_keyring(connection_info["password"], keyring_retrieved_cleanly=keyring_retrieved_cleanly)
292
+ _update_keyring(
293
+ connection_info["password"],
294
+ self.selected_password.source if self.selected_password else None,
295
+ keyring_retrieved_cleanly=keyring_retrieved_cleanly,
296
+ )
277
297
  self.sqlexecute = SQLExecute(**connection_info)
278
298
  except pymysql.OperationalError as e1:
279
299
  if e1.args[0] == HANDSHAKE_ERROR and ssl is not None and ssl.get("mode", None) == "auto":
@@ -295,8 +315,8 @@ class ClientConnectionMixin:
295
315
  f"Enter password for {user}", hide_input=True, show_default=False, default='', type=str, err=True
296
316
  ),
297
317
  )
298
- fallback_password = password_candidates.resolve(['fallback'])
299
- connection_info["password"] = fallback_password.value if fallback_password is not None else None
318
+ self.selected_password = password_candidates.resolve(['fallback'])
319
+ connection_info["password"] = self.selected_password.value if self.selected_password else None
300
320
  keyring_retrieved_cleanly = False
301
321
  _connect(
302
322
  retry_password=True,
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Iterable
3
4
  from dataclasses import dataclass
4
5
  from datetime import datetime
5
6
  import functools
@@ -14,7 +15,7 @@ import subprocess
14
15
  import sys
15
16
  import time
16
17
  import traceback
17
- from typing import TYPE_CHECKING, Any, Generator
18
+ from typing import TYPE_CHECKING, Any
18
19
  from xml.parsers.expat import ExpatError
19
20
 
20
21
  import click
@@ -61,6 +62,13 @@ from mycli.packages.key_binding_utils import (
61
62
  handle_clip_command,
62
63
  handle_editor_command,
63
64
  )
65
+ from mycli.packages.polars_transform import (
66
+ PolarsTransform,
67
+ PolarsTransformError,
68
+ parse_polars_transform,
69
+ prepare_polars_transform,
70
+ run_polars_transform,
71
+ )
64
72
  from mycli.packages.ptoolkit.history import FileHistoryWithTimestamp
65
73
  from mycli.packages.special.utils import format_uptime, get_ssl_version, get_uptime, get_warning_count
66
74
  from mycli.packages.sql_utils import (
@@ -420,7 +428,7 @@ def _get_continuation(
420
428
  def _output_results(
421
429
  mycli: 'MyCli',
422
430
  state: ReplState,
423
- results: Generator[SQLResult],
431
+ results: Iterable[SQLResult],
424
432
  start: float,
425
433
  ) -> None:
426
434
  sqlexecute = mycli.sqlexecute
@@ -687,7 +695,15 @@ def _one_iteration(
687
695
  if not text:
688
696
  return
689
697
 
690
- if is_redirect_command(text):
698
+ original_text = text
699
+ try:
700
+ polars_pipeline = parse_polars_transform(text)
701
+ except PolarsTransformError as exc:
702
+ mycli.echo(str(exc), err=True, fg='red')
703
+ return
704
+ if polars_pipeline is not None:
705
+ text = polars_pipeline.sql
706
+ elif is_redirect_command(text):
691
707
  sql_part, command_part, file_operator_part, file_part = get_redirect_components(text)
692
708
  text = sql_part or ''
693
709
  try:
@@ -719,19 +735,41 @@ def _one_iteration(
719
735
  successful = False
720
736
  try:
721
737
  mycli.logger.debug('sql: %r', text)
738
+ polars_transform: PolarsTransform | None = (
739
+ prepare_polars_transform(text, polars_pipeline.expression) if polars_pipeline is not None else None
740
+ )
741
+ query_history_text = original_text if polars_transform is not None else text
722
742
  special.write_tee(mycli.last_prompt_message, nl=False)
723
- special.write_tee(text)
724
- mycli.log_query(text)
743
+ special.write_tee(query_history_text)
744
+ mycli.log_query(query_history_text)
725
745
 
726
746
  start = time.time()
727
747
  results = sqlexecute.run(text)
728
748
  mycli.main_formatter.query = text
729
749
  mycli.redirect_formatter.query = text
730
750
  mycli.explorer_formatter.query = text
751
+ if polars_transform is not None:
752
+ assert polars_pipeline is not None
753
+ if polars_pipeline.parquet_path is None:
754
+ polars_result = run_polars_transform(polars_transform, results)
755
+ else:
756
+ polars_result = run_polars_transform(polars_transform, results, polars_pipeline.parquet_path)
757
+ if polars_pipeline.parquet_path is None:
758
+ if polars_pipeline.output_mode == 'explorer':
759
+ special.set_explorer_output(True)
760
+ elif polars_pipeline.output_mode == 'expanded':
761
+ special.set_expanded_output(True)
762
+ _output_results(mycli, state, iter([polars_result]), start)
763
+ if polars_pipeline.parquet_path is not None:
764
+ special.run_post_redirect_hook(
765
+ mycli.post_redirect_command,
766
+ polars_pipeline.parquet_path,
767
+ )
768
+ else:
769
+ _output_results(mycli, state, results, start)
770
+ special.unset_once_if_written(mycli.post_redirect_command)
771
+ special.flush_pipe_once_if_written(mycli.post_redirect_command)
731
772
  successful = True
732
- _output_results(mycli, state, results, start)
733
- special.unset_once_if_written(mycli.post_redirect_command)
734
- special.flush_pipe_once_if_written(mycli.post_redirect_command)
735
773
  except pymysql.err.InterfaceError:
736
774
  if not mycli.reconnect():
737
775
  return
@@ -764,6 +802,9 @@ def _one_iteration(
764
802
  mycli.echo('Did not get a connection id, skip cancelling query', err=True, fg='red')
765
803
  except NotImplementedError:
766
804
  mycli.echo('Not Yet Implemented.', fg='yellow')
805
+ except PolarsTransformError as exc:
806
+ mycli.logger.error('sql: %r, error: %r', text, exc)
807
+ mycli.echo(str(exc), err=True, fg='red')
767
808
  except pymysql.OperationalError as e1:
768
809
  mycli.logger.debug('Exception: %r', e1)
769
810
  if e1.args[0] == ER_MUST_CHANGE_PASSWORD:
@@ -814,7 +855,7 @@ def _one_iteration(
814
855
  if mycli.logfile is False:
815
856
  mycli.echo('Warning: This query was not logged.', err=True, fg='red')
816
857
 
817
- query = Query(text, successful, state.mutating)
858
+ query = Query(original_text if polars_pipeline is not None else text, successful, state.mutating)
818
859
  mycli.query_history.append(query)
819
860
 
820
861
 
@@ -93,9 +93,10 @@ numeric_alignment = right
93
93
  # to hex rendering if not possible.
94
94
  binary_display = hex
95
95
 
96
- # A command to run after a successful output redirect, with {} to be replaced
97
- # with the escaped filename. Mac example: echo {} | pbcopy. Escaping is not
98
- # reliable/safe on Windows.
96
+ # A command to run after a successful Parquet save with ".>" or shell output
97
+ # redirect with "$>". The sequence "{}" will be replaced by the escaped
98
+ # destination filename. Mac example: echo {} | pbcopy.
99
+ # Escaping is not reliable/safe on Windows.
99
100
  post_redirect_command =
100
101
 
101
102
  # Syntax coloring style. Possible values (many support the "-dark" suffix):
@@ -232,6 +233,11 @@ password_sources = prompt, literal, file, environment, dsn, boundary, vault, log
232
233
  # Recommended: auto
233
234
  use_keyring = False
234
235
 
236
+ # Comma-separated password sources eligible to be saved in the keyring.
237
+ # Valid choices: same as password_sources (above).
238
+ # use_keyring must be True or auto.
239
+ keyring_sources = prompt, literal, file, environment, dsn, login_path, fallback
240
+
235
241
  [editor]
236
242
 
237
243
  # Command line to invoke for /edit, instead of respecting the $VISUAL/$EDITOR