execsql2 2.0.0a2__py3-none-any.whl → 2.1.1__py3-none-any.whl

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 (90) hide show
  1. execsql/cli.py +322 -108
  2. execsql/config.py +134 -114
  3. execsql/db/access.py +89 -65
  4. execsql/db/base.py +97 -68
  5. execsql/db/dsn.py +45 -29
  6. execsql/db/duckdb.py +4 -5
  7. execsql/db/factory.py +27 -27
  8. execsql/db/firebird.py +30 -18
  9. execsql/db/mysql.py +38 -14
  10. execsql/db/oracle.py +58 -33
  11. execsql/db/postgres.py +68 -28
  12. execsql/db/sqlite.py +36 -27
  13. execsql/db/sqlserver.py +45 -30
  14. execsql/exceptions.py +68 -64
  15. execsql/exporters/__init__.py +1 -1
  16. execsql/exporters/base.py +42 -17
  17. execsql/exporters/delimited.py +60 -59
  18. execsql/exporters/duckdb.py +8 -12
  19. execsql/exporters/feather.py +32 -24
  20. execsql/exporters/html.py +33 -30
  21. execsql/exporters/json.py +18 -17
  22. execsql/exporters/latex.py +11 -13
  23. execsql/exporters/ods.py +50 -46
  24. execsql/exporters/parquet.py +32 -0
  25. execsql/exporters/pretty.py +16 -15
  26. execsql/exporters/raw.py +9 -11
  27. execsql/exporters/sqlite.py +38 -38
  28. execsql/exporters/templates.py +15 -72
  29. execsql/exporters/values.py +13 -12
  30. execsql/exporters/xls.py +26 -26
  31. execsql/exporters/xml.py +12 -12
  32. execsql/exporters/zip.py +0 -3
  33. execsql/gui/__init__.py +2 -2
  34. execsql/gui/console.py +0 -1
  35. execsql/gui/desktop.py +6 -7
  36. execsql/gui/tui.py +8 -14
  37. execsql/importers/base.py +6 -9
  38. execsql/importers/csv.py +10 -17
  39. execsql/importers/feather.py +16 -22
  40. execsql/importers/ods.py +3 -4
  41. execsql/importers/xls.py +5 -6
  42. execsql/metacommands/__init__.py +8 -8
  43. execsql/metacommands/conditions.py +41 -33
  44. execsql/metacommands/connect.py +113 -99
  45. execsql/metacommands/control.py +38 -26
  46. execsql/metacommands/data.py +35 -33
  47. execsql/metacommands/debug.py +13 -9
  48. execsql/metacommands/io.py +288 -229
  49. execsql/metacommands/prompt.py +179 -157
  50. execsql/metacommands/script_ext.py +11 -9
  51. execsql/metacommands/system.py +44 -25
  52. execsql/models.py +9 -16
  53. execsql/parser.py +10 -10
  54. execsql/script.py +183 -157
  55. execsql/state.py +173 -215
  56. execsql/types.py +46 -81
  57. execsql/utils/auth.py +114 -14
  58. execsql/utils/crypto.py +31 -4
  59. execsql/utils/datetime.py +7 -7
  60. execsql/utils/errors.py +34 -29
  61. execsql/utils/fileio.py +90 -55
  62. execsql/utils/gui.py +22 -23
  63. execsql/utils/mail.py +15 -17
  64. execsql/utils/numeric.py +2 -3
  65. execsql/utils/regex.py +9 -12
  66. execsql/utils/strings.py +10 -12
  67. execsql/utils/timer.py +0 -2
  68. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/execsql.conf +1 -1
  69. execsql2-2.1.1.dist-info/METADATA +295 -0
  70. execsql2-2.1.1.dist-info/RECORD +96 -0
  71. execsql2-2.0.0a2.dist-info/METADATA +0 -406
  72. execsql2-2.0.0a2.dist-info/RECORD +0 -95
  73. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/READ_ME.rst +0 -0
  74. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/config_settings.sqlite +0 -0
  75. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
  76. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/make_config_db.sql +0 -0
  77. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/md_compare.sql +0 -0
  78. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/md_glossary.sql +0 -0
  79. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/md_upsert.sql +0 -0
  80. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/pg_compare.sql +0 -0
  81. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/pg_glossary.sql +0 -0
  82. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/pg_upsert.sql +0 -0
  83. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/script_template.sql +0 -0
  84. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/ss_compare.sql +0 -0
  85. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/ss_glossary.sql +0 -0
  86. {execsql2-2.0.0a2.data → execsql2-2.1.1.data}/data/execsql2_extras/ss_upsert.sql +0 -0
  87. {execsql2-2.0.0a2.dist-info → execsql2-2.1.1.dist-info}/WHEEL +0 -0
  88. {execsql2-2.0.0a2.dist-info → execsql2-2.1.1.dist-info}/entry_points.txt +0 -0
  89. {execsql2-2.0.0a2.dist-info → execsql2-2.1.1.dist-info}/licenses/LICENSE.txt +0 -0
  90. {execsql2-2.0.0a2.dist-info → execsql2-2.1.1.dist-info}/licenses/NOTICE +0 -0
execsql/state.py CHANGED
@@ -28,24 +28,39 @@ Variable groups defined here:
28
28
  - **Utility functions** — ``xcmd_test()`` (evaluate a conditional string),
29
29
  ``endloop()`` (finalise a compiled loop).
30
30
 
31
- The bottom of this module re-exports frequently-used names from sibling
32
- modules so that ``_state.ErrInfo``, ``_state.runscripts``, etc. resolve
33
- correctly even though those modules themselves import ``execsql.state``.
31
+ All re-exports have been removed. Each module now imports directly from
32
+ its source module rather than accessing names via ``_state``.
34
33
  """
35
34
 
36
- import getpass
37
35
  import re
38
- from typing import TYPE_CHECKING, Any, Optional
36
+ from typing import TYPE_CHECKING, Any
39
37
 
40
38
  if TYPE_CHECKING:
41
- from execsql.config import ConfigData
39
+ import multiprocessing as _mp
40
+ import threading as _threading
41
+
42
+ from execsql.config import ConfigData, StatObj, WriteHooks
43
+ from execsql.db.base import DatabasePool
44
+ from execsql.exporters.base import ExportMetadata, WriteSpec
45
+ from execsql.script import (
46
+ CommandList,
47
+ CounterVars,
48
+ IfLevels,
49
+ MetaCommandList,
50
+ ScriptCmd,
51
+ ScriptExecSpec,
52
+ SubVarSet,
53
+ )
54
+ from execsql.utils.fileio import FileWriter, Logger, TempFileMgr
55
+ from execsql.utils.mail import MailSpec
56
+ from execsql.utils.timer import Timer
42
57
 
43
58
  # ---------------------------------------------------------------------------
44
59
  # Configuration / encoding
45
60
  # ---------------------------------------------------------------------------
46
61
 
47
62
  # Configuration data, initialized in main()
48
- conf: Optional[ConfigData] = None
63
+ conf: ConfigData | None = None
49
64
 
50
65
  # Default encodings
51
66
  logfile_encoding: str = "utf8" # Should never be changed; is not configurable.
@@ -55,41 +70,41 @@ logfile_encoding: str = "utf8" # Should never be changed; is not configurable.
55
70
  # ---------------------------------------------------------------------------
56
71
 
57
72
  # The last command run. This should be a ScriptCmd object.
58
- last_command: Any = None
73
+ last_command: ScriptCmd | None = None
59
74
 
60
75
  # The last user password entered via 'get_password()'
61
- upass: Optional[str] = None
76
+ upass: str | None = None
62
77
 
63
78
  # A compiled regex to match prefixed regular expressions, used to check
64
79
  # for unsubstituted variables.
65
80
  varlike = re.compile(r"!![$@&~#]?\w+!!", re.I)
66
81
 
67
82
  # A WriteSpec object for messages to be written when the program halts due to an error.
68
- err_halt_writespec: Any = None
83
+ err_halt_writespec: WriteSpec | None = None
69
84
 
70
85
  # A MailSpec object for email to be sent when the program halts due to an error.
71
- err_halt_email: Any = None
86
+ err_halt_email: MailSpec | None = None
72
87
 
73
88
  # A ScriptExecSpec object for a script to be executed when the program halts due to an error.
74
- err_halt_exec: Any = None
89
+ err_halt_exec: ScriptExecSpec | None = None
75
90
 
76
91
  # A WriteSpec object for messages to be written when the program halts due to user cancellation.
77
- cancel_halt_writespec: Any = None
92
+ cancel_halt_writespec: WriteSpec | None = None
78
93
 
79
94
  # A MailSpec object for email to be sent when the program halts due to user cancellation.
80
- cancel_halt_mailspec: Any = None
95
+ cancel_halt_mailspec: MailSpec | None = None
81
96
 
82
97
  # A ScriptExecSpec object for a script to be executed when the program halts due to user cancellation.
83
- cancel_halt_exec: Any = None
98
+ cancel_halt_exec: ScriptExecSpec | None = None
84
99
 
85
100
  # A stack of the CommandList objects currently in the queue to be executed.
86
- commandliststack: list = []
101
+ commandliststack: list[CommandList] = []
87
102
 
88
103
  # A dictionary of CommandList objects (ordinarily created by BEGIN/END SCRIPT metacommands).
89
- savedscripts: dict = {}
104
+ savedscripts: dict[str, CommandList] = {}
90
105
 
91
106
  # A stack of CommandList objects used when compiling the statements within a loop.
92
- loopcommandstack: list = []
107
+ loopcommandstack: list[CommandList] = []
93
108
 
94
109
  # A global flag to indicate that commands should be compiled into the topmost entry
95
110
  # in the loopcommandstack rather than executed.
@@ -114,54 +129,54 @@ defer_rx = re.compile(r"(!{([$@&~#]?[a-z0-9_]+)}!)", re.I)
114
129
  stringtypes: type = str
115
130
 
116
131
  # The execution log object; set at startup.
117
- exec_log: Any = None
132
+ exec_log: Logger | None = None
118
133
 
119
134
  # The substitution variable set; set at startup.
120
- subvars: Any = None
135
+ subvars: SubVarSet | None = None
121
136
 
122
137
  # The program execution status tracker; set at startup.
123
- status: Any = None
138
+ status: StatObj | None = None
124
139
 
125
140
  # ---------------------------------------------------------------------------
126
141
  # Runtime objects — initialized in main() to avoid circular imports at load time.
127
142
  # ---------------------------------------------------------------------------
128
143
 
129
144
  # Stack-based conditional state (IfLevels instance).
130
- if_stack: Any = None
145
+ if_stack: IfLevels | None = None
131
146
 
132
147
  # Global counter variables (CounterVars instance).
133
- counters: Any = None
148
+ counters: CounterVars | None = None
134
149
 
135
150
  # Elapsed-time tracker (Timer instance).
136
- timer: Any = None
151
+ timer: Timer | None = None
137
152
 
138
153
  # Redirectable output (WriteHooks instance).
139
- output: Any = None
154
+ output: WriteHooks | None = None
140
155
 
141
156
  # Database connection pool (DatabasePool instance).
142
- dbs: Any = None
157
+ dbs: DatabasePool | None = None
143
158
 
144
159
  # Temporary file manager (TempFileMgr instance).
145
- tempfiles: Any = None
160
+ tempfiles: TempFileMgr | None = None
146
161
 
147
162
  # Export metadata tracker (ExportMetadata instance).
148
- export_metadata: Any = None
163
+ export_metadata: ExportMetadata | None = None
149
164
 
150
165
  # Metacommand dispatch table (MetaCommandList instance).
151
- metacommandlist: Any = None
166
+ metacommandlist: MetaCommandList | None = None
152
167
 
153
168
  # Conditional predicate dispatch table (MetaCommandList instance).
154
- conditionallist: Any = None
169
+ conditionallist: MetaCommandList | None = None
155
170
 
156
171
  # Asynchronous file-writer subprocess (FileWriter instance).
157
- filewriter: Any = None
172
+ filewriter: FileWriter | None = None
158
173
 
159
174
  # GUI console object.
160
- gui_console: Any = None
175
+ gui_console: Any = None # Varies by backend (ConsoleUI, DesktopUI, TextualUI).
161
176
 
162
177
  # Queue and thread used to communicate with the GUI manager.
163
- gui_manager_queue: Any = None
164
- gui_manager_thread: Any = None
178
+ gui_manager_queue: _mp.Queue | None = None
179
+ gui_manager_thread: _threading.Thread | None = None
165
180
 
166
181
  # ---------------------------------------------------------------------------
167
182
  # Version numbers (parsed from package __version__)
@@ -170,18 +185,14 @@ gui_manager_thread: Any = None
170
185
  try:
171
186
  from execsql import __version__ as _pkg_version
172
187
 
173
- import re as _re
174
-
175
188
  _vparts = _pkg_version.split(".")
176
- # Strip pre-release suffix (e.g. "0a1" → "0") before converting to int.
177
- _int = lambda s: int(_re.match(r"\d+", s).group())
178
- primary_vno: int = _int(_vparts[0]) if len(_vparts) > 0 else 0
179
- secondary_vno: int = _int(_vparts[1]) if len(_vparts) > 1 else 0
180
- tertiary_vno: int = _int(_vparts[2]) if len(_vparts) > 2 else 0
189
+ primary_vno: int = int(_vparts[0]) if len(_vparts) > 0 else 0
190
+ secondary_vno: int = int(_vparts[1]) if len(_vparts) > 1 else 0
191
+ tertiary_vno: int = int(_vparts[2]) if len(_vparts) > 2 else 0
181
192
  except Exception:
182
- primary_vno = 1
183
- secondary_vno = 130
184
- tertiary_vno = 1
193
+ primary_vno = 0
194
+ secondary_vno = 0
195
+ tertiary_vno = 0
185
196
 
186
197
  # ---------------------------------------------------------------------------
187
198
  # Utility functions defined directly here to avoid circular imports.
@@ -190,192 +201,139 @@ except Exception:
190
201
 
191
202
  def xcmd_test(teststr: str) -> bool:
192
203
  """Evaluate a conditional test string and return a boolean result."""
193
- from execsql.parser import CondParser
194
- from execsql.exceptions import ErrInfo
204
+ import execsql.parser as _parser
205
+ import execsql.exceptions as _exc
195
206
 
196
- result = CondParser(teststr).parse().eval()
207
+ result = _parser.CondParser(teststr).parse().eval()
197
208
  if result is not None:
198
209
  return result
199
- raise ErrInfo(type="cmd", command_text=teststr, other_msg="Unrecognized conditional")
210
+ raise _exc.ErrInfo(type="cmd", command_text=teststr, other_msg="Unrecognized conditional")
200
211
 
201
212
 
202
213
  def endloop() -> None:
203
214
  """Complete the current loop being compiled and push it onto the command stack."""
204
- from execsql.exceptions import ErrInfo
215
+ import execsql.exceptions as _exc
205
216
 
206
217
  global compiling_loop
207
218
  if len(loopcommandstack) == 0:
208
- raise ErrInfo("error", other_msg="END LOOP metacommand without a matching preceding LOOP metacommand.")
219
+ raise _exc.ErrInfo("error", other_msg="END LOOP metacommand without a matching preceding LOOP metacommand.")
209
220
  compiling_loop = False
210
221
  commandliststack.append(loopcommandstack[-1])
211
222
  loopcommandstack.pop()
212
223
 
213
224
 
214
225
  # ---------------------------------------------------------------------------
215
- # Deferred re-exports
216
- # Placed at the bottom so that circular imports resolve correctly:
217
- # all state variables above are defined before any module below is loaded.
218
- # Modules that `import execsql.state as _state` will receive a partial
219
- # module (containing only the variables above) if they are themselves
220
- # imported during this block — which is safe because those modules only
221
- # access _state.X inside function/method bodies, never at class-definition time.
226
+ # Test-support utilities
222
227
  # ---------------------------------------------------------------------------
223
228
 
224
- # Exceptions and parser — always safe (exceptions.py / parser.py import state
225
- # but only use _state inside function bodies, never at module level after init).
226
- from execsql.exceptions import ErrInfo # noqa: E402
227
- from execsql.parser import CondParser, NumericParser # noqa: E402
228
-
229
- # script.py defines the core execution data structures.
230
- from execsql.script import ( # noqa: E402
231
- IfLevels,
232
- CounterVars,
233
- SubVarSet,
234
- LocalSubVarSet,
235
- ScriptArgSubVarSet,
236
- MetaCommand,
237
- MetaCommandList,
238
- SqlStmt,
239
- MetacommandStmt,
240
- ScriptCmd,
241
- CommandList,
242
- CommandListWhileLoop,
243
- CommandListUntilLoop,
244
- ScriptFile,
245
- ScriptExecSpec,
246
- set_system_vars,
247
- substitute_vars,
248
- runscripts,
249
- current_script_line,
250
- read_sqlfile,
251
- )
252
-
253
- # xcmd_test also lives in conditions.py; keep the version defined above
254
- # (identical logic) so that state.xcmd_test works without an extra import.
255
- # write_warning / exit_now / fatal_error / exception_desc
256
- from execsql.utils.errors import ( # noqa: E402
257
- exception_desc,
258
- exit_now,
259
- fatal_error,
260
- file_size_date,
261
- write_warning,
262
- )
263
-
264
- # File-I/O helpers
265
- from execsql.utils.fileio import ( # noqa: E402
266
- check_dir,
267
- EncodedFile,
268
- FileWriter,
269
- Logger,
270
- TempFileMgr,
271
- filewriter_write,
272
- filewriter_close,
273
- filewriter_close_all_after_write,
274
- filewriter_open_as_new,
275
- filewriter_end,
276
- )
277
-
278
- # String utilities
279
- from execsql.utils.strings import ( # noqa: E402
280
- clean_words,
281
- fold_words,
282
- is_doublequoted,
283
- unquoted,
284
- unquoted2,
285
- get_subvarset,
286
- )
287
-
288
- # Other utilities
289
- from execsql.utils.crypto import Encrypt # noqa: E402
290
- from execsql.utils.timer import Timer # noqa: E402
291
- from execsql.utils.mail import Mailer, MailSpec # noqa: E402
292
- from execsql.utils.datetime import parse_datetime # noqa: E402
293
- from execsql.utils.auth import get_password # noqa: E402
294
-
295
- # Data models
296
- from execsql.models import DataTable # noqa: E402
297
- from execsql.types import DT_Boolean, DT_Date, DT_Timestamp, DT_TimestampTZ # noqa: E402
298
-
299
- # Export infrastructure
300
- from execsql.exporters.base import ExportRecord, ExportMetadata, WriteSpec # noqa: E402
301
- from execsql.exporters.pretty import prettyprint_query, prettyprint_rowset # noqa: E402
302
- from execsql.exporters.templates import report_query # noqa: E402
303
- from execsql.exporters.delimited import write_delimited_file, CsvFile # noqa: E402
304
- from execsql.exporters.html import write_query_to_html, write_query_to_cgi_html # noqa: E402
305
- from execsql.exporters.json import write_query_to_json, write_query_to_json_ts # noqa: E402
306
- from execsql.exporters.xml import write_query_to_xml # noqa: E402
307
- from execsql.exporters.ods import write_query_to_ods, write_queries_to_ods, OdsFile # noqa: E402
308
- from execsql.exporters.sqlite import write_query_to_sqlite # noqa: E402
309
- from execsql.exporters.duckdb import write_query_to_duckdb # noqa: E402
310
- from execsql.exporters.latex import write_query_to_latex # noqa: E402
311
- from execsql.exporters.feather import write_query_to_feather, write_query_to_hdf5 # noqa: E402
312
- from execsql.exporters.raw import write_query_raw, write_query_b64 as write_query_b # noqa: E402
313
- from execsql.exporters.values import write_query_to_values # noqa: E402
314
- from execsql.exporters.xls import XlsFile, XlsxFile # noqa: E402
315
-
316
- # Alias for HDF upstream used write_query_to_hdf
317
- write_query_to_hdf = write_query_to_hdf5
318
-
319
- # Import infrastructure
320
- from execsql.importers.csv import importtable, importfile # noqa: E402
321
- from execsql.importers.ods import ods_data, importods # noqa: E402
322
- from execsql.importers.xls import xls_data, importxls # noqa: E402
323
- from execsql.importers.feather import import_feather, import_parquet # noqa: E402
324
- from execsql.importers.base import import_data_table # noqa: E402
325
-
326
- # Database layer
327
- from execsql.db.base import DatabasePool # noqa: E402
328
- from execsql.db.postgres import PostgresDatabase # noqa: E402
329
- from execsql.db.sqlite import SQLiteDatabase # noqa: E402
330
- from execsql.db.mysql import MySQLDatabase # noqa: E402
331
- from execsql.db.duckdb import DuckDBDatabase # noqa: E402
332
- from execsql.db.firebird import FirebirdDatabase # noqa: E402
333
- from execsql.db.oracle import OracleDatabase # noqa: E402
334
- from execsql.db.access import AccessDatabase # noqa: E402
335
- from execsql.db.sqlserver import SqlServerDatabase # noqa: E402
336
- from execsql.db.dsn import DsnDatabase # noqa: E402
337
-
338
- # Database type descriptors
339
- from execsql.types import dbt_postgres, dbt_firebird # noqa: E402
340
-
341
- # GUI layer — pluggable backends: Textual (TUI), Tkinter (desktop), Console
342
- from execsql.utils.gui import ( # noqa: E402
343
- gui_console_isrunning,
344
- enable_gui,
345
- gui_console_on,
346
- gui_console_off,
347
- gui_console_hide,
348
- gui_console_show,
349
- gui_console_progress,
350
- gui_console_save,
351
- gui_console_status,
352
- gui_console_wait_user,
353
- gui_console_height,
354
- gui_console_width,
355
- gui_connect,
356
- gui_credentials,
357
- GuiSpec,
358
- ConsoleUIError,
359
- ActionSpec,
360
- EntrySpec,
361
- GUI_HALT,
362
- GUI_MSG,
363
- GUI_PAUSE,
364
- GUI_DISPLAY,
365
- GUI_ENTRY,
366
- GUI_COMPARE,
367
- GUI_SELECTROWS,
368
- GUI_SELECTSUB,
369
- GUI_ACTION,
370
- GUI_MAP,
371
- GUI_OPENFILE,
372
- GUI_SAVEFILE,
373
- GUI_DIRECTORY,
374
- QUERY_CONSOLE,
375
- GUI_CREDENTIALS,
376
- GUI_CONNECT,
377
- get_yn,
378
- get_yn_win,
379
- pause,
380
- pause_win,
381
- )
229
+
230
+ def reset() -> None:
231
+ """Reset all module-level state to initial values.
232
+
233
+ Intended for use in tests. Clears mutable containers, resets counters,
234
+ and sets all lazy singletons back to ``None`` so that each test starts
235
+ from a clean slate.
236
+ """
237
+ global compiling_loop, loop_nest_level, cmds_run
238
+ global conf, last_command, upass
239
+ global err_halt_writespec, err_halt_email, err_halt_exec
240
+ global cancel_halt_writespec, cancel_halt_mailspec, cancel_halt_exec
241
+ global exec_log, subvars, status, if_stack, counters, timer
242
+ global output, dbs, tempfiles, export_metadata
243
+ global metacommandlist, conditionallist, filewriter
244
+ global gui_console, gui_manager_queue, gui_manager_thread
245
+
246
+ # Mutable containers — clear in-place (no rebind needed)
247
+ commandliststack.clear()
248
+ loopcommandstack.clear()
249
+ savedscripts.clear()
250
+
251
+ # Scalar flags and counters
252
+ compiling_loop = False
253
+ loop_nest_level = 0
254
+ cmds_run = 0
255
+
256
+ # Close open database connections before discarding the pool.
257
+ if dbs is not None:
258
+ try:
259
+ dbs.closeall()
260
+ except Exception:
261
+ pass
262
+
263
+ # Lazy singletons — reset to None
264
+ conf = None
265
+ last_command = None
266
+ upass = None
267
+ err_halt_writespec = None
268
+ err_halt_email = None
269
+ err_halt_exec = None
270
+ cancel_halt_writespec = None
271
+ cancel_halt_mailspec = None
272
+ cancel_halt_exec = None
273
+ exec_log = None
274
+ subvars = None
275
+ status = None
276
+ if_stack = None
277
+ counters = None
278
+ timer = None
279
+ output = None
280
+ dbs = None
281
+ tempfiles = None
282
+ export_metadata = None
283
+ metacommandlist = None
284
+ conditionallist = None
285
+ # filewriter is a multiprocessing.Process managed by atexit — do NOT null
286
+ # it here. Nulling it while the subprocess is alive creates two competing
287
+ # consumers on the shared fw_input queue, causing test-to-test races.
288
+ gui_console = None
289
+ gui_manager_queue = None
290
+ gui_manager_thread = None
291
+
292
+
293
+ def initialize(
294
+ config: ConfigData,
295
+ dispatch_table: object,
296
+ conditional_table: object,
297
+ ) -> None:
298
+ """Initialize the shared runtime singletons for a new execsql run.
299
+
300
+ Called once from :func:`execsql.cli._run` after configuration has been
301
+ loaded. Consolidates object construction in one place so that the
302
+ sequence is documented, testable, and not scattered across the CLI
303
+ entry-point.
304
+
305
+ Args:
306
+ config: A fully-populated :class:`execsql.config.ConfigData` instance.
307
+ dispatch_table: The metacommand dispatch table
308
+ (``execsql.metacommands.DISPATCH_TABLE``).
309
+ conditional_table: The conditional-predicate dispatch table
310
+ (``execsql.metacommands.conditions.CONDITIONAL_TABLE``).
311
+
312
+ Note:
313
+ ``subvars``, ``status``, ``output``, ``filewriter``, and ``exec_log``
314
+ are **not** set here because they require CLI-specific arguments
315
+ (script path, subprocess queues, local class definitions). Those are
316
+ assigned directly in ``_run()`` before and after this call.
317
+ """
318
+ global conf, if_stack, counters, timer, dbs, tempfiles
319
+ global export_metadata, metacommandlist, conditionallist
320
+
321
+ # These names are re-exported at the bottom of this module (after this
322
+ # function definition), so they are guaranteed to be available by the time
323
+ # initialize() is called from cli._run(). Using the module-level names
324
+ # avoids F811 "redefinition of unused name" from local imports.
325
+ import execsql.script as _script
326
+ import execsql.utils.timer as _timer_mod
327
+ import execsql.db.base as _db_base
328
+ import execsql.utils.fileio as _fileio_mod
329
+ import execsql.exporters.base as _exporters_base
330
+
331
+ conf = config
332
+ if_stack = _script.IfLevels()
333
+ counters = _script.CounterVars()
334
+ timer = _timer_mod.Timer()
335
+ dbs = _db_base.DatabasePool()
336
+ tempfiles = _fileio_mod.TempFileMgr()
337
+ export_metadata = _exporters_base.ExportMetadata()
338
+ metacommandlist = dispatch_table
339
+ conditionallist = conditional_table