execsql2 2.12.0__tar.gz → 2.12.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 (297) hide show
  1. {execsql2-2.12.0 → execsql2-2.12.1}/CHANGELOG.md +16 -0
  2. {execsql2-2.12.0 → execsql2-2.12.1}/PKG-INFO +1 -1
  3. {execsql2-2.12.0 → execsql2-2.12.1}/pyproject.toml +2 -2
  4. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/base.py +37 -23
  5. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/script/engine.py +13 -4
  6. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/script/variables.py +2 -25
  7. {execsql2-2.12.0 → execsql2-2.12.1}/tests/db/test_base.py +5 -5
  8. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_engine.py +3 -4
  9. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_script.py +12 -16
  10. {execsql2-2.12.0 → execsql2-2.12.1}/uv.lock +1 -1
  11. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/agents/dba.md +0 -0
  12. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/agents/herald.md +0 -0
  13. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/agents/inspector.md +0 -0
  14. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/agents/oracle.md +0 -0
  15. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/agents/patcher.md +0 -0
  16. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/agents/qa.md +0 -0
  17. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/agents/scribe.md +0 -0
  18. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/commands/code-oracle.md +0 -0
  19. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/commands/migrate.md +0 -0
  20. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/commands/review-changes.md +0 -0
  21. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/commands/test-module.md +0 -0
  22. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/commands/update-changelog.md +0 -0
  23. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/commands/where-is.md +0 -0
  24. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/project_context.md +0 -0
  25. {execsql2-2.12.0 → execsql2-2.12.1}/.claude/state/status.md +0 -0
  26. {execsql2-2.12.0 → execsql2-2.12.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  27. {execsql2-2.12.0 → execsql2-2.12.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  28. {execsql2-2.12.0 → execsql2-2.12.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  29. {execsql2-2.12.0 → execsql2-2.12.1}/.github/workflows/ci-cd.yml +0 -0
  30. {execsql2-2.12.0 → execsql2-2.12.1}/.gitignore +0 -0
  31. {execsql2-2.12.0 → execsql2-2.12.1}/.pre-commit-config.yaml +0 -0
  32. {execsql2-2.12.0 → execsql2-2.12.1}/.pre-commit-hooks.yaml +0 -0
  33. {execsql2-2.12.0 → execsql2-2.12.1}/.python-version +0 -0
  34. {execsql2-2.12.0 → execsql2-2.12.1}/.readthedocs.yaml +0 -0
  35. {execsql2-2.12.0 → execsql2-2.12.1}/CLAUDE.md +0 -0
  36. {execsql2-2.12.0 → execsql2-2.12.1}/CONTRIBUTING.md +0 -0
  37. {execsql2-2.12.0 → execsql2-2.12.1}/LICENSE.txt +0 -0
  38. {execsql2-2.12.0 → execsql2-2.12.1}/NOTICE +0 -0
  39. {execsql2-2.12.0 → execsql2-2.12.1}/README.md +0 -0
  40. {execsql2-2.12.0 → execsql2-2.12.1}/SECURITY.md +0 -0
  41. {execsql2-2.12.0 → execsql2-2.12.1}/docs/about/contributors.md +0 -0
  42. {execsql2-2.12.0 → execsql2-2.12.1}/docs/about/copyright.md +0 -0
  43. {execsql2-2.12.0 → execsql2-2.12.1}/docs/about/divergence.md +0 -0
  44. {execsql2-2.12.0 → execsql2-2.12.1}/docs/api/cli.md +0 -0
  45. {execsql2-2.12.0 → execsql2-2.12.1}/docs/api/db.md +0 -0
  46. {execsql2-2.12.0 → execsql2-2.12.1}/docs/api/exporters.md +0 -0
  47. {execsql2-2.12.0 → execsql2-2.12.1}/docs/api/importers.md +0 -0
  48. {execsql2-2.12.0 → execsql2-2.12.1}/docs/api/index.md +0 -0
  49. {execsql2-2.12.0 → execsql2-2.12.1}/docs/api/metacommands.md +0 -0
  50. {execsql2-2.12.0 → execsql2-2.12.1}/docs/dev/adding_db_adapters.md +0 -0
  51. {execsql2-2.12.0 → execsql2-2.12.1}/docs/dev/adding_exporters.md +0 -0
  52. {execsql2-2.12.0 → execsql2-2.12.1}/docs/dev/adding_importers.md +0 -0
  53. {execsql2-2.12.0 → execsql2-2.12.1}/docs/dev/adding_metacommands.md +0 -0
  54. {execsql2-2.12.0 → execsql2-2.12.1}/docs/dev/architecture.md +0 -0
  55. {execsql2-2.12.0 → execsql2-2.12.1}/docs/getting-started/installation.md +0 -0
  56. {execsql2-2.12.0 → execsql2-2.12.1}/docs/getting-started/requirements.md +0 -0
  57. {execsql2-2.12.0 → execsql2-2.12.1}/docs/getting-started/syntax.md +0 -0
  58. {execsql2-2.12.0 → execsql2-2.12.1}/docs/guides/debugging.md +0 -0
  59. {execsql2-2.12.0 → execsql2-2.12.1}/docs/guides/documentation.md +0 -0
  60. {execsql2-2.12.0 → execsql2-2.12.1}/docs/guides/encoding.md +0 -0
  61. {execsql2-2.12.0 → execsql2-2.12.1}/docs/guides/examples.md +0 -0
  62. {execsql2-2.12.0 → execsql2-2.12.1}/docs/guides/formatter.md +0 -0
  63. {execsql2-2.12.0 → execsql2-2.12.1}/docs/guides/logging.md +0 -0
  64. {execsql2-2.12.0 → execsql2-2.12.1}/docs/guides/sql_syntax.md +0 -0
  65. {execsql2-2.12.0 → execsql2-2.12.1}/docs/guides/usage.md +0 -0
  66. {execsql2-2.12.0 → execsql2-2.12.1}/docs/guides/using_scripts.md +0 -0
  67. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/Compare_planets.png +0 -0
  68. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/actions.png +0 -0
  69. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/actions2.png +0 -0
  70. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/checkboxes.png +0 -0
  71. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/connect.b64 +0 -0
  72. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/connect.png +0 -0
  73. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/create_conf.png +0 -0
  74. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/data_error1_screenshot.jpg +0 -0
  75. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/entry_form.png +0 -0
  76. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/execsql_console.png +0 -0
  77. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/execsql_logo_01.png +0 -0
  78. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/fatals.png +0 -0
  79. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/logo_small.png +0 -0
  80. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/pause_terminal.png +0 -0
  81. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/pause_terminal_sm.b64 +0 -0
  82. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/pause_terminal_sm.png +0 -0
  83. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/prompt_compare.png +0 -0
  84. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/set_build_commands.jpg +0 -0
  85. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/unit_conversions.b64 +0 -0
  86. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/unit_conversions_029.png +0 -0
  87. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/unmatched.png +0 -0
  88. {execsql2-2.12.0 → execsql2-2.12.1}/docs/images/vim_execsql_highlight.png +0 -0
  89. {execsql2-2.12.0 → execsql2-2.12.1}/docs/index.md +0 -0
  90. {execsql2-2.12.0 → execsql2-2.12.1}/docs/reference/configuration.md +0 -0
  91. {execsql2-2.12.0 → execsql2-2.12.1}/docs/reference/metacommands.md +0 -0
  92. {execsql2-2.12.0 → execsql2-2.12.1}/docs/reference/security.md +0 -0
  93. {execsql2-2.12.0 → execsql2-2.12.1}/docs/reference/substitution_vars.md +0 -0
  94. {execsql2-2.12.0 → execsql2-2.12.1}/extras/vscode-execsql/README.md +0 -0
  95. {execsql2-2.12.0 → execsql2-2.12.1}/extras/vscode-execsql/package.json +0 -0
  96. {execsql2-2.12.0 → execsql2-2.12.1}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
  97. {execsql2-2.12.0 → execsql2-2.12.1}/justfile +0 -0
  98. {execsql2-2.12.0 → execsql2-2.12.1}/scripts/generate_vscode_grammar.py +0 -0
  99. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/__init__.py +0 -0
  100. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/__main__.py +0 -0
  101. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/cli/__init__.py +0 -0
  102. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/cli/dsn.py +0 -0
  103. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/cli/help.py +0 -0
  104. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/cli/lint.py +0 -0
  105. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/cli/run.py +0 -0
  106. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/config.py +0 -0
  107. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/constants.py +0 -0
  108. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/__init__.py +0 -0
  109. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/access.py +0 -0
  110. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/dsn.py +0 -0
  111. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/duckdb.py +0 -0
  112. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/factory.py +0 -0
  113. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/firebird.py +0 -0
  114. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/mysql.py +0 -0
  115. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/oracle.py +0 -0
  116. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/postgres.py +0 -0
  117. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/sqlite.py +0 -0
  118. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/db/sqlserver.py +0 -0
  119. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/debug/__init__.py +0 -0
  120. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/debug/repl.py +0 -0
  121. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exceptions.py +0 -0
  122. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/__init__.py +0 -0
  123. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/base.py +0 -0
  124. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/delimited.py +0 -0
  125. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/duckdb.py +0 -0
  126. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/feather.py +0 -0
  127. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/html.py +0 -0
  128. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/json.py +0 -0
  129. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/latex.py +0 -0
  130. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/markdown.py +0 -0
  131. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/ods.py +0 -0
  132. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/parquet.py +0 -0
  133. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/pretty.py +0 -0
  134. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/protocol.py +0 -0
  135. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/raw.py +0 -0
  136. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/sqlite.py +0 -0
  137. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/templates.py +0 -0
  138. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/values.py +0 -0
  139. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/xls.py +0 -0
  140. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/xlsx.py +0 -0
  141. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/xml.py +0 -0
  142. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/yaml.py +0 -0
  143. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/exporters/zip.py +0 -0
  144. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/format.py +0 -0
  145. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/gui/__init__.py +0 -0
  146. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/gui/base.py +0 -0
  147. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/gui/console.py +0 -0
  148. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/gui/desktop.py +0 -0
  149. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/gui/tui.py +0 -0
  150. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/importers/__init__.py +0 -0
  151. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/importers/base.py +0 -0
  152. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/importers/csv.py +0 -0
  153. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/importers/feather.py +0 -0
  154. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/importers/ods.py +0 -0
  155. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/importers/xls.py +0 -0
  156. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/__init__.py +0 -0
  157. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/conditions.py +0 -0
  158. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/connect.py +0 -0
  159. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/control.py +0 -0
  160. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/data.py +0 -0
  161. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/debug.py +0 -0
  162. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/dispatch.py +0 -0
  163. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/io.py +0 -0
  164. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/io_export.py +0 -0
  165. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/io_fileops.py +0 -0
  166. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/io_import.py +0 -0
  167. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/io_write.py +0 -0
  168. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/prompt.py +0 -0
  169. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/script_ext.py +0 -0
  170. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/metacommands/system.py +0 -0
  171. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/models.py +0 -0
  172. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/parser.py +0 -0
  173. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/py.typed +0 -0
  174. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/script/__init__.py +0 -0
  175. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/script/control.py +0 -0
  176. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/state.py +0 -0
  177. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/types.py +0 -0
  178. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/__init__.py +0 -0
  179. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/auth.py +0 -0
  180. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/crypto.py +0 -0
  181. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/datetime.py +0 -0
  182. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/errors.py +0 -0
  183. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/fileio.py +0 -0
  184. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/gui.py +0 -0
  185. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/mail.py +0 -0
  186. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/numeric.py +0 -0
  187. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/regex.py +0 -0
  188. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/strings.py +0 -0
  189. {execsql2-2.12.0 → execsql2-2.12.1}/src/execsql/utils/timer.py +0 -0
  190. {execsql2-2.12.0 → execsql2-2.12.1}/templates/README.md +0 -0
  191. {execsql2-2.12.0 → execsql2-2.12.1}/templates/config_settings.sqlite +0 -0
  192. {execsql2-2.12.0 → execsql2-2.12.1}/templates/example_config_prompt.sql +0 -0
  193. {execsql2-2.12.0 → execsql2-2.12.1}/templates/execsql.conf +0 -0
  194. {execsql2-2.12.0 → execsql2-2.12.1}/templates/make_config_db.sql +0 -0
  195. {execsql2-2.12.0 → execsql2-2.12.1}/templates/md_compare.sql +0 -0
  196. {execsql2-2.12.0 → execsql2-2.12.1}/templates/md_glossary.sql +0 -0
  197. {execsql2-2.12.0 → execsql2-2.12.1}/templates/md_upsert.sql +0 -0
  198. {execsql2-2.12.0 → execsql2-2.12.1}/templates/pg_compare.sql +0 -0
  199. {execsql2-2.12.0 → execsql2-2.12.1}/templates/pg_glossary.sql +0 -0
  200. {execsql2-2.12.0 → execsql2-2.12.1}/templates/pg_upsert.sql +0 -0
  201. {execsql2-2.12.0 → execsql2-2.12.1}/templates/script_template.sql +0 -0
  202. {execsql2-2.12.0 → execsql2-2.12.1}/templates/ss_compare.sql +0 -0
  203. {execsql2-2.12.0 → execsql2-2.12.1}/templates/ss_glossary.sql +0 -0
  204. {execsql2-2.12.0 → execsql2-2.12.1}/templates/ss_upsert.sql +0 -0
  205. {execsql2-2.12.0 → execsql2-2.12.1}/tests/__init__.py +0 -0
  206. {execsql2-2.12.0 → execsql2-2.12.1}/tests/cli/__init__.py +0 -0
  207. {execsql2-2.12.0 → execsql2-2.12.1}/tests/cli/test_cli.py +0 -0
  208. {execsql2-2.12.0 → execsql2-2.12.1}/tests/cli/test_cli_e2e.py +0 -0
  209. {execsql2-2.12.0 → execsql2-2.12.1}/tests/cli/test_cli_run.py +0 -0
  210. {execsql2-2.12.0 → execsql2-2.12.1}/tests/cli/test_lint.py +0 -0
  211. {execsql2-2.12.0 → execsql2-2.12.1}/tests/cli/test_ping.py +0 -0
  212. {execsql2-2.12.0 → execsql2-2.12.1}/tests/cli/test_profile.py +0 -0
  213. {execsql2-2.12.0 → execsql2-2.12.1}/tests/conftest.py +0 -0
  214. {execsql2-2.12.0 → execsql2-2.12.1}/tests/db/__init__.py +0 -0
  215. {execsql2-2.12.0 → execsql2-2.12.1}/tests/db/test_duckdb.py +0 -0
  216. {execsql2-2.12.0 → execsql2-2.12.1}/tests/db/test_factory.py +0 -0
  217. {execsql2-2.12.0 → execsql2-2.12.1}/tests/db/test_postgres.py +0 -0
  218. {execsql2-2.12.0 → execsql2-2.12.1}/tests/db/test_sqlite.py +0 -0
  219. {execsql2-2.12.0 → execsql2-2.12.1}/tests/db/test_sqlite_extra.py +0 -0
  220. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/__init__.py +0 -0
  221. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_base.py +0 -0
  222. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_db.py +0 -0
  223. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_delimited.py +0 -0
  224. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_duckdb_exporter.py +0 -0
  225. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_exporters.py +0 -0
  226. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_feather.py +0 -0
  227. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_html_latex.py +0 -0
  228. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_json.py +0 -0
  229. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_markdown.py +0 -0
  230. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_ods.py +0 -0
  231. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_parquet.py +0 -0
  232. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_sqlite_exporter.py +0 -0
  233. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_templates.py +0 -0
  234. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_xls_xlsx.py +0 -0
  235. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_xlsx.py +0 -0
  236. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_xml.py +0 -0
  237. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_yaml.py +0 -0
  238. {execsql2-2.12.0 → execsql2-2.12.1}/tests/exporters/test_zip.py +0 -0
  239. {execsql2-2.12.0 → execsql2-2.12.1}/tests/gui/__init__.py +0 -0
  240. {execsql2-2.12.0 → execsql2-2.12.1}/tests/gui/test_backends.py +0 -0
  241. {execsql2-2.12.0 → execsql2-2.12.1}/tests/importers/__init__.py +0 -0
  242. {execsql2-2.12.0 → execsql2-2.12.1}/tests/importers/test_csv_importer.py +0 -0
  243. {execsql2-2.12.0 → execsql2-2.12.1}/tests/importers/test_feather_importer.py +0 -0
  244. {execsql2-2.12.0 → execsql2-2.12.1}/tests/importers/test_ods_importer.py +0 -0
  245. {execsql2-2.12.0 → execsql2-2.12.1}/tests/importers/test_xls_importer.py +0 -0
  246. {execsql2-2.12.0 → execsql2-2.12.1}/tests/integration/__init__.py +0 -0
  247. {execsql2-2.12.0 → execsql2-2.12.1}/tests/integration/conftest.py +0 -0
  248. {execsql2-2.12.0 → execsql2-2.12.1}/tests/integration/test_dsn.py +0 -0
  249. {execsql2-2.12.0 → execsql2-2.12.1}/tests/integration/test_duckdb.py +0 -0
  250. {execsql2-2.12.0 → execsql2-2.12.1}/tests/integration/test_mysql.py +0 -0
  251. {execsql2-2.12.0 → execsql2-2.12.1}/tests/integration/test_postgres.py +0 -0
  252. {execsql2-2.12.0 → execsql2-2.12.1}/tests/integration/test_sqlite.py +0 -0
  253. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/__init__.py +0 -0
  254. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_assert.py +0 -0
  255. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_breakpoint.py +0 -0
  256. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_connect.py +0 -0
  257. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_io_export.py +0 -0
  258. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_io_import.py +0 -0
  259. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_metacommands.py +0 -0
  260. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_metacommands_connect.py +0 -0
  261. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_metacommands_data.py +0 -0
  262. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_metacommands_extended.py +0 -0
  263. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
  264. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_metacommands_io.py +0 -0
  265. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
  266. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_metacommands_script_ext.py +0 -0
  267. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_metacommands_system.py +0 -0
  268. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_metacommands_system_extra.py +0 -0
  269. {execsql2-2.12.0 → execsql2-2.12.1}/tests/metacommands/test_row_count.py +0 -0
  270. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_config.py +0 -0
  271. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_config_data.py +0 -0
  272. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_constants.py +0 -0
  273. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_error_messages.py +0 -0
  274. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_exceptions.py +0 -0
  275. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_format.py +0 -0
  276. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_mail.py +0 -0
  277. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_models.py +0 -0
  278. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_package.py +0 -0
  279. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_parser.py +0 -0
  280. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_registry.py +0 -0
  281. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_state.py +0 -0
  282. {execsql2-2.12.0 → execsql2-2.12.1}/tests/test_types.py +0 -0
  283. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/__init__.py +0 -0
  284. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_auth.py +0 -0
  285. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_auth_extra.py +0 -0
  286. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_crypto.py +0 -0
  287. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_datetime.py +0 -0
  288. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_errors.py +0 -0
  289. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_errors_extra.py +0 -0
  290. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_fileio.py +0 -0
  291. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_fileio_extra.py +0 -0
  292. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_numeric.py +0 -0
  293. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_regex.py +0 -0
  294. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_strings.py +0 -0
  295. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_timer.py +0 -0
  296. {execsql2-2.12.0 → execsql2-2.12.1}/tests/utils/test_timer_extra.py +0 -0
  297. {execsql2-2.12.0 → execsql2-2.12.1}/zensical.toml +0 -0
@@ -13,6 +13,22 @@ ______________________________________________________________________
13
13
 
14
14
  ______________________________________________________________________
15
15
 
16
+ ## [2.12.1] - 2026-04-02
17
+
18
+ ### Changed
19
+
20
+ - Performance: removed dead `_compiled_patterns` dict from `SubVarSet` — eliminated 3 unused regex compilations per `add_substitution` call (~20 calls per statement in typical scripts).
21
+ - Performance: cached `source_dir` and `source_name` on `ScriptCmd` at construction time — eliminated per-statement `Path.resolve()` filesystem calls.
22
+ - Performance: `select_rowdict()` now uses batched `fetchmany()` instead of row-at-a-time `fetchone()`, matching `select_rowsource()` behavior for template exports.
23
+ - Performance: removed redundant `$CURRENT_TIME` set in `set_system_vars()` — now set once per statement in `run_and_increment()`.
24
+ - Performance: removed no-op `copy.copy()` on immutable string in `substitute_vars()`.
25
+
26
+ ### Fixed
27
+
28
+ - Fixed cursor leak in `select_rowsource()` — generator now closes the cursor in a `finally` block when exhausted or abandoned.
29
+
30
+ ______________________________________________________________________
31
+
16
32
  ## [2.12.0] - 2026-04-01
17
33
 
18
34
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: execsql2
3
- Version: 2.12.0
3
+ Version: 2.12.1
4
4
  Summary: Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables.
5
5
  Project-URL: Repository, https://github.com/geocoug/execsql
6
6
  Project-URL: Issues, https://github.com/geocoug/execsql/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "execsql2"
7
- version = "2.12.0"
7
+ version = "2.12.1"
8
8
  description = "Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables."
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  license = { file = "LICENSE.txt" }
@@ -158,7 +158,7 @@ skip-magic-trailing-comma = false
158
158
  line-ending = "auto"
159
159
 
160
160
  [tool.bumpversion]
161
- current_version = "2.12.0"
161
+ current_version = "2.12.1"
162
162
  commit = true
163
163
  commit_args = "--no-verify"
164
164
  tag = true
@@ -234,18 +234,22 @@ class Database(ABC):
234
234
  pass # Non-critical: some drivers lack rowcount support.
235
235
 
236
236
  def decode_row() -> Generator:
237
- while True:
238
- rows = curs.fetchmany()
239
- if not rows:
240
- break
241
- else:
242
- for row in rows:
243
- if self.encoding:
244
- yield [
245
- c.decode(self.encoding, "backslashreplace") if isinstance(c, bytes) else c for c in row
246
- ]
247
- else:
248
- yield row
237
+ try:
238
+ while True:
239
+ rows = curs.fetchmany()
240
+ if not rows:
241
+ break
242
+ else:
243
+ for row in rows:
244
+ if self.encoding:
245
+ yield [
246
+ c.decode(self.encoding, "backslashreplace") if isinstance(c, bytes) else c
247
+ for c in row
248
+ ]
249
+ else:
250
+ yield row
251
+ finally:
252
+ curs.close()
249
253
 
250
254
  return [d[0] for d in curs.description], decode_row()
251
255
 
@@ -253,6 +257,10 @@ class Database(ABC):
253
257
  """Execute *sql* and return ``(column_names, row_iterator)`` where each row is a ``dict``."""
254
258
  # Return an iterable that yields dictionaries of row data
255
259
  curs = self.cursor()
260
+ try:
261
+ curs.arraysize = _state.conf.export_row_buffer
262
+ except Exception:
263
+ pass # Non-critical: not all drivers support arraysize.
256
264
  try:
257
265
  curs.execute(sql)
258
266
  except Exception:
@@ -264,18 +272,24 @@ class Database(ABC):
264
272
  pass # Non-critical: some drivers lack rowcount support.
265
273
  hdrs = [d[0] for d in curs.description]
266
274
 
267
- def dict_row() -> dict | None:
268
- row = curs.fetchone()
269
- if row:
270
- if self.encoding:
271
- r = [c.decode(self.encoding, "backslashreplace") if isinstance(c, bytes) else c for c in row]
272
- else:
273
- r = row
274
- return dict(zip(hdrs, r))
275
- else:
276
- return None
275
+ def dict_rows() -> Generator:
276
+ try:
277
+ while True:
278
+ rows = curs.fetchmany()
279
+ if not rows:
280
+ break
281
+ for row in rows:
282
+ if self.encoding:
283
+ r = [
284
+ c.decode(self.encoding, "backslashreplace") if isinstance(c, bytes) else c for c in row
285
+ ]
286
+ else:
287
+ r = row
288
+ yield dict(zip(hdrs, r))
289
+ finally:
290
+ curs.close()
277
291
 
278
- return hdrs, iter(dict_row, None)
292
+ return hdrs, dict_rows()
279
293
 
280
294
  def schema_exists(self, schema_name: str) -> bool:
281
295
  """Return ``True`` if *schema_name* exists in this database."""
@@ -397,6 +397,15 @@ class ScriptCmd:
397
397
  self.line_no = command_line_no
398
398
  self.command_type = command_type
399
399
  self.command = script_command
400
+ # MIGRATION NOTE: differs from monolith (execsql.py) — source_dir and source_name are
401
+ # resolved once at construction rather than on every statement execution. For absolute
402
+ # paths (the common case) the result is identical. For relative paths the value is
403
+ # anchored to the CWD at script-load time rather than at each statement's execution time;
404
+ # the original per-statement resolve could yield inconsistent values across statements of
405
+ # the same script if a CD metacommand ran between them.
406
+ _p = Path(command_source_name)
407
+ self.source_dir: str = str(_p.resolve().parent) + os.sep
408
+ self.source_name: str = _p.name
400
409
 
401
410
  def __repr__(self) -> str:
402
411
  return f"ScriptCmd({self.source!r}, {self.line_no!r}, {self.command_type!r}, {repr(self.command)!r})"
@@ -498,9 +507,9 @@ class CommandList:
498
507
  _state.subvars.add_substitution("$CURRENT_SCRIPT", cmditem.source)
499
508
  _state.subvars.add_substitution(
500
509
  "$CURRENT_SCRIPT_PATH",
501
- str(Path(cmditem.source).resolve().parent) + os.sep,
510
+ cmditem.source_dir,
502
511
  )
503
- _state.subvars.add_substitution("$CURRENT_SCRIPT_NAME", Path(cmditem.source).name)
512
+ _state.subvars.add_substitution("$CURRENT_SCRIPT_NAME", cmditem.source_name)
504
513
  _state.subvars.add_substitution("$CURRENT_SCRIPT_LINE", str(cmditem.line_no))
505
514
  _state.subvars.add_substitution("$SCRIPT_LINE", str(cmditem.line_no))
506
515
  if _state.step_mode:
@@ -709,7 +718,7 @@ def set_system_vars() -> None:
709
718
  "ON" if _state.conf.gui_wait_on_error_halt else "OFF",
710
719
  )
711
720
  _state.subvars.add_substitution("$CONSOLE_WAIT_WHEN_DONE_STATE", "ON" if _state.conf.gui_wait_on_exit else "OFF")
712
- _state.subvars.add_substitution("$CURRENT_TIME", datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))
721
+ # $CURRENT_TIME is set per-statement in run_and_increment() for accuracy.
713
722
  _state.subvars.add_substitution("$CURRENT_DIR", str(Path(".").resolve()))
714
723
  _state.subvars.add_substitution("$CURRENT_PATH", str(Path(".").resolve()) + os.sep)
715
724
  _state.subvars.add_substitution("$CURRENT_ALIAS", _state.dbs.current_alias())
@@ -742,7 +751,7 @@ def substitute_vars(command_str: str, localvars: SubVarSet | None = None) -> str
742
751
  subs = _state.subvars.merge(localvars)
743
752
  else:
744
753
  subs = _state.subvars
745
- cmdstr = copy.copy(command_str)
754
+ cmdstr = command_str
746
755
  subs_made = True
747
756
  iterations = 0
748
757
  while subs_made:
@@ -89,7 +89,6 @@ class SubVarSet:
89
89
  # compatibility with external code.
90
90
  def __init__(self) -> None:
91
91
  self._subs_dict: dict[str, Any] = {}
92
- self._compiled_patterns: dict[str, tuple] = {}
93
92
  self.prefix_list: list[str] = ["$", "&", "@"]
94
93
  # Don't construct/compile on init because deepcopy() can't handle compiled regexes.
95
94
  self.var_rx = None
@@ -106,21 +105,6 @@ class SubVarSet:
106
105
  self._subs_dict = dict(value)
107
106
  else:
108
107
  self._subs_dict = dict(value)
109
- self._rebuild_all_patterns()
110
-
111
- def _compile_patterns_for(self, varname: str) -> tuple:
112
- """Compile and return the three regex patterns (plain, single-quoted, double-quoted) for *varname*."""
113
- match_escaped = "\\" + varname if varname[0] == "$" else varname
114
- pat = re.compile(f"!!{match_escaped}!!", re.I)
115
- patq = re.compile(f"!'!{match_escaped}!'!", re.I)
116
- patdq = re.compile(f'!"!{match_escaped}!"!', re.I)
117
- return (pat, patq, patdq)
118
-
119
- def _rebuild_all_patterns(self) -> None:
120
- """Rebuild compiled patterns for every variable currently stored."""
121
- self._compiled_patterns = {}
122
- for varname in self._subs_dict:
123
- self._compiled_patterns[varname] = self._compile_patterns_for(varname)
124
108
 
125
109
  def compile_var_rx(self) -> None:
126
110
  """Compile the variable-name validation regex from the current prefix list."""
@@ -141,14 +125,12 @@ class SubVarSet:
141
125
  self.check_var_name(template_str)
142
126
  old_sub = template_str.lower()
143
127
  self._subs_dict.pop(old_sub, None)
144
- self._compiled_patterns.pop(old_sub, None)
145
128
 
146
129
  def add_substitution(self, varname: str, repl_str: Any) -> None:
147
- """Add or overwrite a substitution variable, compiling its match patterns."""
130
+ """Add or overwrite a substitution variable."""
148
131
  self.check_var_name(varname)
149
132
  varname = varname.lower()
150
133
  self._subs_dict[varname] = repl_str
151
- self._compiled_patterns[varname] = self._compile_patterns_for(varname)
152
134
 
153
135
  def append_substitution(self, varname: str, repl_str: str) -> None:
154
136
  self.check_var_name(varname)
@@ -186,15 +168,10 @@ class SubVarSet:
186
168
  return template_str.lower() in self._subs_dict
187
169
 
188
170
  def merge(self, other_subvars: SubVarSet | None) -> SubVarSet:
189
- """Return a new SubVarSet with this object's variables merged with other_subvars.
190
-
191
- Copies dictionaries and pre-compiled patterns directly instead of
192
- re-adding variables one at a time, avoiding O(V) regex recompilation.
193
- """
171
+ """Return a new SubVarSet with this object's variables merged with other_subvars."""
194
172
  if other_subvars is not None:
195
173
  newsubs = SubVarSet()
196
174
  newsubs._subs_dict = {**self._subs_dict, **other_subvars._subs_dict}
197
- newsubs._compiled_patterns = {**self._compiled_patterns, **other_subvars._compiled_patterns}
198
175
  newsubs.prefix_list = list(set(self.prefix_list + other_subvars.prefix_list))
199
176
  newsubs.compile_var_rx()
200
177
  return newsubs
@@ -628,9 +628,9 @@ class TestSelectRowdictEncoding:
628
628
  mock_curs = MagicMock()
629
629
  mock_curs.description = [("name",)]
630
630
  mock_curs.rowcount = 1
631
- mock_curs.fetchone.side_effect = [
632
- (b"encoded value",),
633
- None,
631
+ mock_curs.fetchmany.side_effect = [
632
+ [(b"encoded value",)],
633
+ [],
634
634
  ]
635
635
 
636
636
  with patch.object(db, "cursor", return_value=mock_curs):
@@ -648,7 +648,7 @@ class TestSelectRowdictEncoding:
648
648
  mock_curs = MagicMock()
649
649
  mock_curs.description = [("score",)]
650
650
  mock_curs.rowcount = 1
651
- mock_curs.fetchone.side_effect = [(99,), None]
651
+ mock_curs.fetchmany.side_effect = [[(99,)], []]
652
652
 
653
653
  with patch.object(db, "cursor", return_value=mock_curs):
654
654
  _, it = db.select_rowdict("SELECT score FROM fake;")
@@ -664,7 +664,7 @@ class TestSelectRowdictEncoding:
664
664
  mock_curs = MagicMock()
665
665
  mock_curs.description = [("x",)]
666
666
  mock_curs.rowcount = 1
667
- mock_curs.fetchone.side_effect = [("plain",), None]
667
+ mock_curs.fetchmany.side_effect = [[("plain",)], []]
668
668
 
669
669
  with patch.object(db, "cursor", return_value=mock_curs):
670
670
  _, it = db.select_rowdict("SELECT x FROM fake;")
@@ -706,7 +706,8 @@ class TestSetSystemVars:
706
706
  set_system_vars()
707
707
  assert _state.subvars.varvalue("$DB_USER") == "testuser"
708
708
 
709
- def test_populates_current_time(self, engine_state):
709
+ def test_current_time_not_set_by_set_system_vars(self, engine_state):
710
+ """$CURRENT_TIME is set per-statement in run_and_increment, not in set_system_vars."""
710
711
  pool = MagicMock()
711
712
  pool.current.return_value = self._make_db()
712
713
  pool.current_alias.return_value = "main"
@@ -715,9 +716,7 @@ class TestSetSystemVars:
715
716
  mock_timer.elapsed.return_value = 0
716
717
  _state.timer = mock_timer
717
718
  set_system_vars()
718
- t = _state.subvars.varvalue("$CURRENT_TIME")
719
- assert t is not None
720
- assert len(t) == 16 # "YYYY-MM-DD HH:MM"
719
+ assert _state.subvars.varvalue("$CURRENT_TIME") is None
721
720
 
722
721
  def test_populates_version_numbers(self, engine_state):
723
722
  pool = MagicMock()
@@ -471,59 +471,55 @@ class TestSubVarSet:
471
471
  sv.remove_substitution("$x")
472
472
  assert sv.sub_exists("$x") is False
473
473
 
474
- # -- compiled regex patterns ---------------------------------------------
474
+ # -- substitution ---------------------------------------------------------
475
475
 
476
- def test_compiled_patterns_created_on_add(self):
476
+ def test_add_stores_in_subs_dict(self):
477
477
  sv = SubVarSet()
478
478
  sv.add_substitution("$foo", "bar")
479
- assert "$foo" in sv._compiled_patterns
480
- pat, patq, patdq = sv._compiled_patterns["$foo"]
481
- assert pat.search("!!$foo!!") is not None
482
- assert patq.search("!'!$foo!'!") is not None
483
- assert patdq.search('!"!$foo!"!') is not None
479
+ assert "$foo" in sv._subs_dict
484
480
 
485
- def test_compiled_patterns_removed_on_remove(self):
481
+ def test_remove_clears_from_subs_dict(self):
486
482
  sv = SubVarSet()
487
483
  sv.add_substitution("$foo", "bar")
488
484
  sv.remove_substitution("$foo")
489
- assert "$foo" not in sv._compiled_patterns
485
+ assert "$foo" not in sv._subs_dict
490
486
 
491
- def test_substitute_uses_compiled_patterns(self):
487
+ def test_substitute_expands_token(self):
492
488
  sv = SubVarSet()
493
489
  sv.add_substitution("$x", "replaced")
494
490
  result, changed = sv.substitute("value is !!$x!!")
495
491
  assert changed is True
496
492
  assert result == "value is replaced"
497
493
 
498
- def test_substitute_case_insensitive_with_compiled(self):
494
+ def test_substitute_case_insensitive(self):
499
495
  sv = SubVarSet()
500
496
  sv.add_substitution("$myvar", "val")
501
497
  result, changed = sv.substitute("!!$MYVAR!!")
502
498
  assert changed is True
503
499
  assert result == "val"
504
500
 
505
- def test_substitute_single_quoted_with_compiled(self):
501
+ def test_substitute_single_quoted(self):
506
502
  sv = SubVarSet()
507
503
  sv.add_substitution("$v", "it's")
508
504
  result, changed = sv.substitute("!'!$v!'!")
509
505
  assert changed is True
510
506
  assert "it''s" in result
511
507
 
512
- def test_substitute_double_quoted_with_compiled(self):
508
+ def test_substitute_double_quoted(self):
513
509
  sv = SubVarSet()
514
510
  sv.add_substitution("$v", "hello")
515
511
  result, changed = sv.substitute('!"!$v!"!')
516
512
  assert changed is True
517
513
  assert '"hello"' in result
518
514
 
519
- def test_merge_preserves_compiled_patterns(self):
515
+ def test_merge_preserves_variables(self):
520
516
  sv1 = SubVarSet()
521
517
  sv1.add_substitution("$a", "1")
522
518
  sv2 = SubVarSet()
523
519
  sv2.add_substitution("$b", "2")
524
520
  merged = sv1.merge(sv2)
525
- assert "$a" in merged._compiled_patterns
526
- assert "$b" in merged._compiled_patterns
521
+ assert "$a" in merged._subs_dict
522
+ assert "$b" in merged._subs_dict
527
523
  result, changed = merged.substitute_all("!!$a!! !!$b!!")
528
524
  assert result == "1 2"
529
525
 
@@ -657,7 +657,7 @@ wheels = [
657
657
 
658
658
  [[package]]
659
659
  name = "execsql2"
660
- version = "2.12.0"
660
+ version = "2.12.1"
661
661
  source = { editable = "." }
662
662
  dependencies = [
663
663
  { name = "rich" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes