execsql2 2.0.1__py3-none-any.whl → 2.1.2__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 +170 -208
  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.1.data → execsql2-2.1.2.data}/data/execsql2_extras/execsql.conf +1 -1
  69. execsql2-2.1.2.dist-info/METADATA +300 -0
  70. execsql2-2.1.2.dist-info/RECORD +96 -0
  71. execsql2-2.0.1.dist-info/METADATA +0 -406
  72. execsql2-2.0.1.dist-info/RECORD +0 -95
  73. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/READ_ME.rst +0 -0
  74. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/config_settings.sqlite +0 -0
  75. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
  76. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/make_config_db.sql +0 -0
  77. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_compare.sql +0 -0
  78. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_glossary.sql +0 -0
  79. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_upsert.sql +0 -0
  80. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_compare.sql +0 -0
  81. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_glossary.sql +0 -0
  82. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_upsert.sql +0 -0
  83. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/script_template.sql +0 -0
  84. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_compare.sql +0 -0
  85. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_glossary.sql +0 -0
  86. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_upsert.sql +0 -0
  87. {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/WHEEL +0 -0
  88. {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/entry_points.txt +0 -0
  89. {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/licenses/LICENSE.txt +0 -0
  90. {execsql2-2.0.1.dist-info → execsql2-2.1.2.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__)
@@ -175,9 +190,9 @@ try:
175
190
  secondary_vno: int = int(_vparts[1]) if len(_vparts) > 1 else 0
176
191
  tertiary_vno: int = int(_vparts[2]) if len(_vparts) > 2 else 0
177
192
  except Exception:
178
- primary_vno = 1
179
- secondary_vno = 130
180
- tertiary_vno = 1
193
+ primary_vno = 0
194
+ secondary_vno = 0
195
+ tertiary_vno = 0
181
196
 
182
197
  # ---------------------------------------------------------------------------
183
198
  # Utility functions defined directly here to avoid circular imports.
@@ -186,192 +201,139 @@ except Exception:
186
201
 
187
202
  def xcmd_test(teststr: str) -> bool:
188
203
  """Evaluate a conditional test string and return a boolean result."""
189
- from execsql.parser import CondParser
190
- from execsql.exceptions import ErrInfo
204
+ import execsql.parser as _parser
205
+ import execsql.exceptions as _exc
191
206
 
192
- result = CondParser(teststr).parse().eval()
207
+ result = _parser.CondParser(teststr).parse().eval()
193
208
  if result is not None:
194
209
  return result
195
- raise ErrInfo(type="cmd", command_text=teststr, other_msg="Unrecognized conditional")
210
+ raise _exc.ErrInfo(type="cmd", command_text=teststr, other_msg="Unrecognized conditional")
196
211
 
197
212
 
198
213
  def endloop() -> None:
199
214
  """Complete the current loop being compiled and push it onto the command stack."""
200
- from execsql.exceptions import ErrInfo
215
+ import execsql.exceptions as _exc
201
216
 
202
217
  global compiling_loop
203
218
  if len(loopcommandstack) == 0:
204
- 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.")
205
220
  compiling_loop = False
206
221
  commandliststack.append(loopcommandstack[-1])
207
222
  loopcommandstack.pop()
208
223
 
209
224
 
210
225
  # ---------------------------------------------------------------------------
211
- # Deferred re-exports
212
- # Placed at the bottom so that circular imports resolve correctly:
213
- # all state variables above are defined before any module below is loaded.
214
- # Modules that `import execsql.state as _state` will receive a partial
215
- # module (containing only the variables above) if they are themselves
216
- # imported during this block — which is safe because those modules only
217
- # access _state.X inside function/method bodies, never at class-definition time.
226
+ # Test-support utilities
218
227
  # ---------------------------------------------------------------------------
219
228
 
220
- # Exceptions and parser — always safe (exceptions.py / parser.py import state
221
- # but only use _state inside function bodies, never at module level after init).
222
- from execsql.exceptions import ErrInfo # noqa: E402
223
- from execsql.parser import CondParser, NumericParser # noqa: E402
224
-
225
- # script.py defines the core execution data structures.
226
- from execsql.script import ( # noqa: E402
227
- IfLevels,
228
- CounterVars,
229
- SubVarSet,
230
- LocalSubVarSet,
231
- ScriptArgSubVarSet,
232
- MetaCommand,
233
- MetaCommandList,
234
- SqlStmt,
235
- MetacommandStmt,
236
- ScriptCmd,
237
- CommandList,
238
- CommandListWhileLoop,
239
- CommandListUntilLoop,
240
- ScriptFile,
241
- ScriptExecSpec,
242
- set_system_vars,
243
- substitute_vars,
244
- runscripts,
245
- current_script_line,
246
- read_sqlfile,
247
- )
248
-
249
- # xcmd_test also lives in conditions.py; keep the version defined above
250
- # (identical logic) so that state.xcmd_test works without an extra import.
251
- # write_warning / exit_now / fatal_error / exception_desc
252
- from execsql.utils.errors import ( # noqa: E402
253
- exception_desc,
254
- exit_now,
255
- fatal_error,
256
- file_size_date,
257
- write_warning,
258
- )
259
-
260
- # File-I/O helpers
261
- from execsql.utils.fileio import ( # noqa: E402
262
- check_dir,
263
- EncodedFile,
264
- FileWriter,
265
- Logger,
266
- TempFileMgr,
267
- filewriter_write,
268
- filewriter_close,
269
- filewriter_close_all_after_write,
270
- filewriter_open_as_new,
271
- filewriter_end,
272
- )
273
-
274
- # String utilities
275
- from execsql.utils.strings import ( # noqa: E402
276
- clean_words,
277
- fold_words,
278
- is_doublequoted,
279
- unquoted,
280
- unquoted2,
281
- get_subvarset,
282
- )
283
-
284
- # Other utilities
285
- from execsql.utils.crypto import Encrypt # noqa: E402
286
- from execsql.utils.timer import Timer # noqa: E402
287
- from execsql.utils.mail import Mailer, MailSpec # noqa: E402
288
- from execsql.utils.datetime import parse_datetime # noqa: E402
289
- from execsql.utils.auth import get_password # noqa: E402
290
-
291
- # Data models
292
- from execsql.models import DataTable # noqa: E402
293
- from execsql.types import DT_Boolean, DT_Date, DT_Timestamp, DT_TimestampTZ # noqa: E402
294
-
295
- # Export infrastructure
296
- from execsql.exporters.base import ExportRecord, ExportMetadata, WriteSpec # noqa: E402
297
- from execsql.exporters.pretty import prettyprint_query, prettyprint_rowset # noqa: E402
298
- from execsql.exporters.templates import report_query # noqa: E402
299
- from execsql.exporters.delimited import write_delimited_file, CsvFile # noqa: E402
300
- from execsql.exporters.html import write_query_to_html, write_query_to_cgi_html # noqa: E402
301
- from execsql.exporters.json import write_query_to_json, write_query_to_json_ts # noqa: E402
302
- from execsql.exporters.xml import write_query_to_xml # noqa: E402
303
- from execsql.exporters.ods import write_query_to_ods, write_queries_to_ods, OdsFile # noqa: E402
304
- from execsql.exporters.sqlite import write_query_to_sqlite # noqa: E402
305
- from execsql.exporters.duckdb import write_query_to_duckdb # noqa: E402
306
- from execsql.exporters.latex import write_query_to_latex # noqa: E402
307
- from execsql.exporters.feather import write_query_to_feather, write_query_to_hdf5 # noqa: E402
308
- from execsql.exporters.raw import write_query_raw, write_query_b64 as write_query_b # noqa: E402
309
- from execsql.exporters.values import write_query_to_values # noqa: E402
310
- from execsql.exporters.xls import XlsFile, XlsxFile # noqa: E402
311
-
312
- # Alias for HDF upstream used write_query_to_hdf
313
- write_query_to_hdf = write_query_to_hdf5
314
-
315
- # Import infrastructure
316
- from execsql.importers.csv import importtable, importfile # noqa: E402
317
- from execsql.importers.ods import ods_data, importods # noqa: E402
318
- from execsql.importers.xls import xls_data, importxls # noqa: E402
319
- from execsql.importers.feather import import_feather, import_parquet # noqa: E402
320
- from execsql.importers.base import import_data_table # noqa: E402
321
-
322
- # Database layer
323
- from execsql.db.base import DatabasePool # noqa: E402
324
- from execsql.db.postgres import PostgresDatabase # noqa: E402
325
- from execsql.db.sqlite import SQLiteDatabase # noqa: E402
326
- from execsql.db.mysql import MySQLDatabase # noqa: E402
327
- from execsql.db.duckdb import DuckDBDatabase # noqa: E402
328
- from execsql.db.firebird import FirebirdDatabase # noqa: E402
329
- from execsql.db.oracle import OracleDatabase # noqa: E402
330
- from execsql.db.access import AccessDatabase # noqa: E402
331
- from execsql.db.sqlserver import SqlServerDatabase # noqa: E402
332
- from execsql.db.dsn import DsnDatabase # noqa: E402
333
-
334
- # Database type descriptors
335
- from execsql.types import dbt_postgres, dbt_firebird # noqa: E402
336
-
337
- # GUI layer — pluggable backends: Textual (TUI), Tkinter (desktop), Console
338
- from execsql.utils.gui import ( # noqa: E402
339
- gui_console_isrunning,
340
- enable_gui,
341
- gui_console_on,
342
- gui_console_off,
343
- gui_console_hide,
344
- gui_console_show,
345
- gui_console_progress,
346
- gui_console_save,
347
- gui_console_status,
348
- gui_console_wait_user,
349
- gui_console_height,
350
- gui_console_width,
351
- gui_connect,
352
- gui_credentials,
353
- GuiSpec,
354
- ConsoleUIError,
355
- ActionSpec,
356
- EntrySpec,
357
- GUI_HALT,
358
- GUI_MSG,
359
- GUI_PAUSE,
360
- GUI_DISPLAY,
361
- GUI_ENTRY,
362
- GUI_COMPARE,
363
- GUI_SELECTROWS,
364
- GUI_SELECTSUB,
365
- GUI_ACTION,
366
- GUI_MAP,
367
- GUI_OPENFILE,
368
- GUI_SAVEFILE,
369
- GUI_DIRECTORY,
370
- QUERY_CONSOLE,
371
- GUI_CREDENTIALS,
372
- GUI_CONNECT,
373
- get_yn,
374
- get_yn_win,
375
- pause,
376
- pause_win,
377
- )
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