execsql2 2.5.0__py3-none-any.whl → 2.6.0__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 (24) hide show
  1. execsql/gui/tui.py +132 -0
  2. execsql/metacommands/__init__.py +180 -180
  3. execsql/state.py +261 -200
  4. {execsql2-2.5.0.dist-info → execsql2-2.6.0.dist-info}/METADATA +1 -1
  5. {execsql2-2.5.0.dist-info → execsql2-2.6.0.dist-info}/RECORD +24 -24
  6. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/README.md +0 -0
  7. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/config_settings.sqlite +0 -0
  8. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
  9. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/execsql.conf +0 -0
  10. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/make_config_db.sql +0 -0
  11. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/md_compare.sql +0 -0
  12. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/md_glossary.sql +0 -0
  13. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/md_upsert.sql +0 -0
  14. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/pg_compare.sql +0 -0
  15. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/pg_glossary.sql +0 -0
  16. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/pg_upsert.sql +0 -0
  17. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/script_template.sql +0 -0
  18. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/ss_compare.sql +0 -0
  19. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/ss_glossary.sql +0 -0
  20. {execsql2-2.5.0.data → execsql2-2.6.0.data}/data/execsql2_extras/ss_upsert.sql +0 -0
  21. {execsql2-2.5.0.dist-info → execsql2-2.6.0.dist-info}/WHEEL +0 -0
  22. {execsql2-2.5.0.dist-info → execsql2-2.6.0.dist-info}/entry_points.txt +0 -0
  23. {execsql2-2.5.0.dist-info → execsql2-2.6.0.dist-info}/licenses/LICENSE.txt +0 -0
  24. {execsql2-2.5.0.dist-info → execsql2-2.6.0.dist-info}/licenses/NOTICE +0 -0
execsql/state.py CHANGED
@@ -12,27 +12,25 @@ and then access attributes (``_state.conf``, ``_state.subvars``, etc.)
12
12
  inside function/method bodies — never at class-definition time — to avoid
13
13
  circular-import issues at load time.
14
14
 
15
- Variable groups defined here:
16
-
17
- - **Configuration** — ``conf`` (:class:`execsql.config.ConfigData`),
18
- ``logfile_encoding``.
19
- - **Runtime flags** — ``last_command``, ``upass``, ``varlike`` regex,
20
- halt/cancel write-spec and mail-spec objects.
21
- - **Execution stack** — ``commandliststack``, ``savedscripts``,
22
- ``loopcommandstack``, ``compiling_loop``, loop regexes and nesting counter.
23
- - **Lazy singletons** ``if_stack``, ``counters``, ``timer``, ``output``,
24
- ``dbs``, ``tempfiles``, ``export_metadata``, ``metacommandlist``,
25
- ``filewriter``, ``gui_console``, GUI queue/thread.
26
- - **Version** — ``primary_vno``, ``secondary_vno``, ``tertiary_vno`` parsed
27
- from ``__version__``.
28
- - **Utility functions** — ``xcmd_test()`` (evaluate a conditional string),
29
- ``endloop()`` (finalise a compiled loop).
30
-
31
- All re-exports have been removed. Each module now imports directly from
32
- its source module rather than accessing names via ``_state``.
15
+ Internally, all mutable state lives on a :class:`RuntimeContext` instance
16
+ (``_ctx``). The module's ``__class__`` is swapped to a custom
17
+ :class:`types.ModuleType` subclass that transparently proxies attribute
18
+ reads and writes to the active context. This means:
19
+
20
+ - External code continues to use ``_state.conf``, ``_state.subvars = ...``,
21
+ etc. with zero changes.
22
+ - Functions *within* this module use ``_ctx.conf``, ``_ctx.subvars``, etc.
23
+ directly, because Python's ``LOAD_GLOBAL`` / ``STORE_GLOBAL`` bytecodes
24
+ access ``__dict__`` directly and do not trigger ``__getattr__`` /
25
+ ``__setattr__`` on the module class.
26
+
27
+ Use :func:`get_context` / :func:`set_context` to obtain or replace the
28
+ active context programmatically.
33
29
  """
34
30
 
35
31
  import re
32
+ import sys
33
+ import types
36
34
  from typing import TYPE_CHECKING, Any
37
35
 
38
36
  if TYPE_CHECKING:
@@ -105,156 +103,238 @@ __all__ = [
105
103
  "endloop",
106
104
  "reset",
107
105
  "initialize",
106
+ # New public API
107
+ "RuntimeContext",
108
+ "get_context",
109
+ "set_context",
108
110
  ]
109
111
 
110
112
  # ---------------------------------------------------------------------------
111
- # Configuration / encoding
113
+ # Compile-time constants — immutable after module load, stay in __dict__
112
114
  # ---------------------------------------------------------------------------
113
115
 
114
- # Configuration data, initialized in main()
115
- conf: ConfigData | None = None
116
-
117
- # Default encodings
118
- logfile_encoding: str = "utf8" # Should never be changed; is not configurable.
119
-
120
- # ---------------------------------------------------------------------------
121
- # Runtime state variables
122
- # ---------------------------------------------------------------------------
123
-
124
- # The last command run. This should be a ScriptCmd object.
125
- last_command: ScriptCmd | None = None
126
-
127
- # The last user password entered via 'get_password()'
128
- upass: str | None = None
129
-
130
116
  # A compiled regex to match prefixed regular expressions, used to check
131
117
  # for unsubstituted variables.
132
118
  varlike = re.compile(r"!![$@&~#]?\w+!!", re.I)
133
119
 
134
- # A WriteSpec object for messages to be written when the program halts due to an error.
135
- err_halt_writespec: WriteSpec | None = None
136
-
137
- # A MailSpec object for email to be sent when the program halts due to an error.
138
- err_halt_email: MailSpec | None = None
139
-
140
- # A ScriptExecSpec object for a script to be executed when the program halts due to an error.
141
- err_halt_exec: ScriptExecSpec | None = None
142
-
143
- # A WriteSpec object for messages to be written when the program halts due to user cancellation.
144
- cancel_halt_writespec: WriteSpec | None = None
145
-
146
- # A MailSpec object for email to be sent when the program halts due to user cancellation.
147
- cancel_halt_mailspec: MailSpec | None = None
148
-
149
- # A ScriptExecSpec object for a script to be executed when the program halts due to user cancellation.
150
- cancel_halt_exec: ScriptExecSpec | None = None
151
-
152
- # A stack of the CommandList objects currently in the queue to be executed.
153
- commandliststack: list[CommandList] = []
154
-
155
- # A dictionary of CommandList objects (ordinarily created by BEGIN/END SCRIPT metacommands).
156
- savedscripts: dict[str, CommandList] = {}
157
-
158
- # A stack of CommandList objects used when compiling the statements within a loop.
159
- loopcommandstack: list[CommandList] = []
160
-
161
- # A global flag to indicate that commands should be compiled into the topmost entry
162
- # in the loopcommandstack rather than executed.
163
- compiling_loop: bool = False
164
-
165
120
  # Compiled regex for END LOOP metacommand, which is immediate.
166
121
  endloop_rx = re.compile(r"^\s*END\s+LOOP\s*$", re.I)
167
122
 
168
- # Compiled regex for *start of* LOOP metacommand, for testing while compiling commands within a loop.
123
+ # Compiled regex for *start of* LOOP metacommand, for testing while compiling
124
+ # commands within a loop.
169
125
  loop_rx = re.compile(r"\s*LOOP\s+", re.I)
170
126
 
171
- # Nesting counter, to ensure loops are only ended when nesting level is zero.
172
- loop_nest_level: int = 0
173
-
174
- # A count of all of the commands run.
175
- cmds_run: int = 0
176
-
177
127
  # Pattern for deferred substitution, e.g.: "!{somevar}!"
178
128
  defer_rx = re.compile(r"(!{([$@&~#]?[a-z0-9_]+)}!)", re.I)
179
129
 
180
130
  # The string type (str in Python 3).
181
131
  stringtypes: type = str
182
132
 
183
- # The execution log object; set at startup.
184
- exec_log: Logger | None = None
185
-
186
- # The substitution variable set; set at startup.
187
- subvars: SubVarSet | None = None
188
-
189
- # The program execution status tracker; set at startup.
190
- status: StatObj | None = None
191
-
192
133
  # ---------------------------------------------------------------------------
193
- # Runtime objects initialized in main() to avoid circular imports at load time.
134
+ # Version numbers (parsed from package __version__)
194
135
  # ---------------------------------------------------------------------------
195
136
 
196
- # Stack-based conditional state (IfLevels instance).
197
- if_stack: IfLevels | None = None
137
+ try:
138
+ from execsql import __version__ as _pkg_version
198
139
 
199
- # Global counter variables (CounterVars instance).
200
- counters: CounterVars | None = None
140
+ _vparts = _pkg_version.split(".")
141
+ primary_vno: int = int(_vparts[0]) if len(_vparts) > 0 else 0
142
+ secondary_vno: int = int(_vparts[1]) if len(_vparts) > 1 else 0
143
+ tertiary_vno: int = int(_vparts[2]) if len(_vparts) > 2 else 0
144
+ except Exception:
145
+ primary_vno = 0
146
+ secondary_vno = 0
147
+ tertiary_vno = 0
201
148
 
202
- # Elapsed-time tracker (Timer instance).
203
- timer: Timer | None = None
204
149
 
205
- # Redirectable output (WriteHooks instance).
206
- output: WriteHooks | None = None
150
+ # ---------------------------------------------------------------------------
151
+ # RuntimeContext holds all mutable state for a single execsql session
152
+ # ---------------------------------------------------------------------------
207
153
 
208
- # Database connection pool (DatabasePool instance).
209
- dbs: DatabasePool | None = None
154
+ _CONTEXT_ATTRS: frozenset[str] = frozenset(
155
+ {
156
+ # Configuration
157
+ "conf",
158
+ "logfile_encoding",
159
+ # Runtime flags
160
+ "last_command",
161
+ "upass",
162
+ "err_halt_writespec",
163
+ "err_halt_email",
164
+ "err_halt_exec",
165
+ "cancel_halt_writespec",
166
+ "cancel_halt_mailspec",
167
+ "cancel_halt_exec",
168
+ # Execution stack
169
+ "commandliststack",
170
+ "savedscripts",
171
+ "loopcommandstack",
172
+ "compiling_loop",
173
+ "loop_nest_level",
174
+ "cmds_run",
175
+ # I/O
176
+ "exec_log",
177
+ "subvars",
178
+ "status",
179
+ "output",
180
+ "filewriter",
181
+ # Lazy singletons
182
+ "if_stack",
183
+ "counters",
184
+ "timer",
185
+ "dbs",
186
+ "tempfiles",
187
+ "export_metadata",
188
+ "metacommandlist",
189
+ "conditionallist",
190
+ # GUI
191
+ "gui_console",
192
+ "gui_manager_queue",
193
+ "gui_manager_thread",
194
+ },
195
+ )
196
+
197
+
198
+ class RuntimeContext:
199
+ """All mutable runtime state for a single execsql execution session.
200
+
201
+ A fresh instance provides clean default values identical to the original
202
+ module-level declarations. Use :func:`get_context` to obtain the active
203
+ instance, or :func:`set_context` to replace it.
204
+ """
210
205
 
211
- # Temporary file manager (TempFileMgr instance).
212
- tempfiles: TempFileMgr | None = None
206
+ __slots__ = (
207
+ # Configuration
208
+ "conf",
209
+ "logfile_encoding",
210
+ # Runtime flags
211
+ "last_command",
212
+ "upass",
213
+ "err_halt_writespec",
214
+ "err_halt_email",
215
+ "err_halt_exec",
216
+ "cancel_halt_writespec",
217
+ "cancel_halt_mailspec",
218
+ "cancel_halt_exec",
219
+ # Execution stack
220
+ "commandliststack",
221
+ "savedscripts",
222
+ "loopcommandstack",
223
+ "compiling_loop",
224
+ "loop_nest_level",
225
+ "cmds_run",
226
+ # I/O
227
+ "exec_log",
228
+ "subvars",
229
+ "status",
230
+ "output",
231
+ "filewriter",
232
+ # Lazy singletons
233
+ "if_stack",
234
+ "counters",
235
+ "timer",
236
+ "dbs",
237
+ "tempfiles",
238
+ "export_metadata",
239
+ "metacommandlist",
240
+ "conditionallist",
241
+ # GUI
242
+ "gui_console",
243
+ "gui_manager_queue",
244
+ "gui_manager_thread",
245
+ )
213
246
 
214
- # Export metadata tracker (ExportMetadata instance).
215
- export_metadata: ExportMetadata | None = None
247
+ def __init__(self) -> None:
248
+ # Configuration
249
+ self.conf: ConfigData | None = None
250
+ self.logfile_encoding: str = "utf8"
251
+
252
+ # Runtime flags
253
+ self.last_command: ScriptCmd | None = None
254
+ self.upass: str | None = None
255
+ self.err_halt_writespec: WriteSpec | None = None
256
+ self.err_halt_email: MailSpec | None = None
257
+ self.err_halt_exec: ScriptExecSpec | None = None
258
+ self.cancel_halt_writespec: WriteSpec | None = None
259
+ self.cancel_halt_mailspec: MailSpec | None = None
260
+ self.cancel_halt_exec: ScriptExecSpec | None = None
261
+
262
+ # Execution stack
263
+ self.commandliststack: list[CommandList] = []
264
+ self.savedscripts: dict[str, CommandList] = {}
265
+ self.loopcommandstack: list[CommandList] = []
266
+ self.compiling_loop: bool = False
267
+ self.loop_nest_level: int = 0
268
+ self.cmds_run: int = 0
269
+
270
+ # I/O
271
+ self.exec_log: Logger | None = None
272
+ self.subvars: SubVarSet | None = None
273
+ self.status: StatObj | None = None
274
+ self.output: WriteHooks | None = None
275
+ self.filewriter: FileWriter | None = None
276
+
277
+ # Lazy singletons
278
+ self.if_stack: IfLevels | None = None
279
+ self.counters: CounterVars | None = None
280
+ self.timer: Timer | None = None
281
+ self.dbs: DatabasePool | None = None
282
+ self.tempfiles: TempFileMgr | None = None
283
+ self.export_metadata: ExportMetadata | None = None
284
+ self.metacommandlist: MetaCommandList | None = None
285
+ self.conditionallist: MetaCommandList | None = None
286
+
287
+ # GUI
288
+ self.gui_console: Any = None
289
+ self.gui_manager_queue: _mp.Queue | None = None
290
+ self.gui_manager_thread: _threading.Thread | None = None
216
291
 
217
- # Metacommand dispatch table (MetaCommandList instance).
218
- metacommandlist: MetaCommandList | None = None
219
292
 
220
- # Conditional predicate dispatch table (MetaCommandList instance).
221
- conditionallist: MetaCommandList | None = None
293
+ # ---------------------------------------------------------------------------
294
+ # Module proxy transparently delegates context attr access to _ctx
295
+ # ---------------------------------------------------------------------------
222
296
 
223
- # Asynchronous file-writer subprocess (FileWriter instance).
224
- filewriter: FileWriter | None = None
225
297
 
226
- # GUI console object.
227
- gui_console: Any = None # Varies by backend (ConsoleUI, DesktopUI, TextualUI).
298
+ class _StateModule(types.ModuleType):
299
+ """Module subclass that proxies mutable state attributes to the active RuntimeContext."""
228
300
 
229
- # Queue and thread used to communicate with the GUI manager.
230
- gui_manager_queue: _mp.Queue | None = None
231
- gui_manager_thread: _threading.Thread | None = None
301
+ def __getattr__(self, name: str) -> Any:
302
+ if name in _CONTEXT_ATTRS:
303
+ return getattr(self.__dict__["_ctx"], name)
304
+ raise AttributeError(f"module {self.__name__!r} has no attribute {name!r}")
232
305
 
233
- # ---------------------------------------------------------------------------
234
- # Version numbers (parsed from package __version__)
235
- # ---------------------------------------------------------------------------
306
+ def __setattr__(self, name: str, value: Any) -> None:
307
+ if name in _CONTEXT_ATTRS:
308
+ setattr(self.__dict__["_ctx"], name, value)
309
+ else:
310
+ super().__setattr__(name, value)
236
311
 
237
- try:
238
- from execsql import __version__ as _pkg_version
312
+ def __delattr__(self, name: str) -> None:
313
+ if name in _CONTEXT_ATTRS:
314
+ # Reset to the fresh-context default. Needed for
315
+ # unittest.mock.patch compatibility: patch checks
316
+ # ``name in target.__dict__`` to decide whether to restore
317
+ # via setattr (local) or delattr (non-local). Since context
318
+ # attrs live on _ctx, not __dict__, patch takes the delattr
319
+ # path. We reset to the default rather than truly deleting.
320
+ _defaults = RuntimeContext()
321
+ setattr(self.__dict__["_ctx"], name, getattr(_defaults, name))
322
+ else:
323
+ super().__delattr__(name)
324
+
325
+ def __dir__(self) -> list[str]:
326
+ return sorted(set(super().__dir__()) | _CONTEXT_ATTRS)
239
327
 
240
- _vparts = _pkg_version.split(".")
241
- primary_vno: int = int(_vparts[0]) if len(_vparts) > 0 else 0
242
- secondary_vno: int = int(_vparts[1]) if len(_vparts) > 1 else 0
243
- tertiary_vno: int = int(_vparts[2]) if len(_vparts) > 2 else 0
244
- except Exception:
245
- primary_vno = 0
246
- secondary_vno = 0
247
- tertiary_vno = 0
248
328
 
249
329
  # ---------------------------------------------------------------------------
250
- # Utility functions defined directly here to avoid circular imports.
330
+ # Utility functions use _ctx directly (LOAD_GLOBAL bypasses the proxy)
251
331
  # ---------------------------------------------------------------------------
252
332
 
253
333
 
254
334
  def xcmd_test(teststr: str) -> bool:
255
335
  """Evaluate a conditional test string and return a boolean result."""
256
- import execsql.parser as _parser
257
336
  import execsql.exceptions as _exc
337
+ import execsql.parser as _parser
258
338
 
259
339
  result = _parser.CondParser(teststr).parse().eval()
260
340
  if result is not None:
@@ -266,80 +346,60 @@ def endloop() -> None:
266
346
  """Complete the current loop being compiled and push it onto the command stack."""
267
347
  import execsql.exceptions as _exc
268
348
 
269
- global compiling_loop
270
- if len(loopcommandstack) == 0:
349
+ if len(_ctx.loopcommandstack) == 0:
271
350
  raise _exc.ErrInfo("error", other_msg="END LOOP metacommand without a matching preceding LOOP metacommand.")
272
- compiling_loop = False
273
- commandliststack.append(loopcommandstack[-1])
274
- loopcommandstack.pop()
351
+ _ctx.compiling_loop = False
352
+ _ctx.commandliststack.append(_ctx.loopcommandstack[-1])
353
+ _ctx.loopcommandstack.pop()
275
354
 
276
355
 
277
356
  # ---------------------------------------------------------------------------
278
- # Test-support utilities
357
+ # Context management
358
+ # ---------------------------------------------------------------------------
359
+
360
+
361
+ def get_context() -> RuntimeContext:
362
+ """Return the active :class:`RuntimeContext`."""
363
+ return _ctx
364
+
365
+
366
+ def set_context(ctx: RuntimeContext) -> None:
367
+ """Replace the active :class:`RuntimeContext`.
368
+
369
+ Args:
370
+ ctx: The new context to install. All subsequent ``_state.foo``
371
+ accesses will resolve against this instance.
372
+ """
373
+ global _ctx
374
+ _ctx = ctx
375
+
376
+
377
+ # ---------------------------------------------------------------------------
378
+ # Initialization and reset
279
379
  # ---------------------------------------------------------------------------
280
380
 
281
381
 
282
382
  def reset() -> None:
283
- """Reset all module-level state to initial values.
383
+ """Reset all mutable state to initial values.
284
384
 
285
- Intended for use in tests. Clears mutable containers, resets counters,
286
- and sets all lazy singletons back to ``None`` so that each test starts
287
- from a clean slate.
385
+ Intended for use in tests. Creates a fresh :class:`RuntimeContext`,
386
+ preserving only the ``filewriter`` subprocess (which is ``atexit``-managed
387
+ and must not be discarded while alive).
288
388
  """
289
- global compiling_loop, loop_nest_level, cmds_run
290
- global conf, last_command, upass
291
- global err_halt_writespec, err_halt_email, err_halt_exec
292
- global cancel_halt_writespec, cancel_halt_mailspec, cancel_halt_exec
293
- global exec_log, subvars, status, if_stack, counters, timer
294
- global output, dbs, tempfiles, export_metadata
295
- global metacommandlist, conditionallist, filewriter
296
- global gui_console, gui_manager_queue, gui_manager_thread
297
-
298
- # Mutable containers — clear in-place (no rebind needed)
299
- commandliststack.clear()
300
- loopcommandstack.clear()
301
- savedscripts.clear()
302
-
303
- # Scalar flags and counters
304
- compiling_loop = False
305
- loop_nest_level = 0
306
- cmds_run = 0
389
+ global _ctx
390
+
391
+ # Preserve filewriter — it's atexit-managed and must survive resets.
392
+ old_fw = _ctx.filewriter
307
393
 
308
394
  # Close open database connections before discarding the pool.
309
- if dbs is not None:
395
+ if _ctx.dbs is not None:
310
396
  try:
311
- dbs.closeall()
397
+ _ctx.dbs.closeall()
312
398
  except Exception:
313
399
  pass
314
400
 
315
- # Lazy singletons — reset to None
316
- conf = None
317
- last_command = None
318
- upass = None
319
- err_halt_writespec = None
320
- err_halt_email = None
321
- err_halt_exec = None
322
- cancel_halt_writespec = None
323
- cancel_halt_mailspec = None
324
- cancel_halt_exec = None
325
- exec_log = None
326
- subvars = None
327
- status = None
328
- if_stack = None
329
- counters = None
330
- timer = None
331
- output = None
332
- dbs = None
333
- tempfiles = None
334
- export_metadata = None
335
- metacommandlist = None
336
- conditionallist = None
337
- # filewriter is a multiprocessing.Process managed by atexit — do NOT null
338
- # it here. Nulling it while the subprocess is alive creates two competing
339
- # consumers on the shared fw_input queue, causing test-to-test races.
340
- gui_console = None
341
- gui_manager_queue = None
342
- gui_manager_thread = None
401
+ _ctx = RuntimeContext()
402
+ _ctx.filewriter = old_fw
343
403
 
344
404
 
345
405
  def initialize(
@@ -367,25 +427,26 @@ def initialize(
367
427
  (script path, subprocess queues, local class definitions). Those are
368
428
  assigned directly in ``_run()`` before and after this call.
369
429
  """
370
- global conf, if_stack, counters, timer, dbs, tempfiles
371
- global export_metadata, metacommandlist, conditionallist
372
-
373
- # These names are re-exported at the bottom of this module (after this
374
- # function definition), so they are guaranteed to be available by the time
375
- # initialize() is called from cli._run(). Using the module-level names
376
- # avoids F811 "redefinition of unused name" from local imports.
377
- import execsql.script as _script
378
- import execsql.utils.timer as _timer_mod
379
430
  import execsql.db.base as _db_base
380
- import execsql.utils.fileio as _fileio_mod
381
431
  import execsql.exporters.base as _exporters_base
432
+ import execsql.script as _script
433
+ import execsql.utils.fileio as _fileio_mod
434
+ import execsql.utils.timer as _timer_mod
435
+
436
+ _ctx.conf = config
437
+ _ctx.if_stack = _script.IfLevels()
438
+ _ctx.counters = _script.CounterVars()
439
+ _ctx.timer = _timer_mod.Timer()
440
+ _ctx.dbs = _db_base.DatabasePool()
441
+ _ctx.tempfiles = _fileio_mod.TempFileMgr()
442
+ _ctx.export_metadata = _exporters_base.ExportMetadata()
443
+ _ctx.metacommandlist = dispatch_table
444
+ _ctx.conditionallist = conditional_table
445
+
446
+
447
+ # ---------------------------------------------------------------------------
448
+ # Bootstrap — create the initial context and swap the module class
449
+ # ---------------------------------------------------------------------------
382
450
 
383
- conf = config
384
- if_stack = _script.IfLevels()
385
- counters = _script.CounterVars()
386
- timer = _timer_mod.Timer()
387
- dbs = _db_base.DatabasePool()
388
- tempfiles = _fileio_mod.TempFileMgr()
389
- export_metadata = _exporters_base.ExportMetadata()
390
- metacommandlist = dispatch_table
391
- conditionallist = conditional_table
451
+ _ctx = RuntimeContext()
452
+ sys.modules[__name__].__class__ = _StateModule
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: execsql2
3
- Version: 2.5.0
3
+ Version: 2.6.0
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
@@ -7,7 +7,7 @@ execsql/format.py,sha256=-6iknDddqbkapMo4NKmT5LAynDLqMW5kHgDWRg0KSws,11990
7
7
  execsql/models.py,sha256=DxkGp9iWbuZDWPGmnxZp9mvEeyOwxEJNx94fxQQiLfQ,13538
8
8
  execsql/parser.py,sha256=mbNSMiAMR1NvNvFtQAZq6nxBOupMGJZXSimLWLtZeNs,15537
9
9
  execsql/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- execsql/state.py,sha256=aZP2xH1D9IcVl6c3gmxAIJwZuTcgz1dpIxygfxb8kPA,13346
10
+ execsql/state.py,sha256=Kg_cMr0DDEjFkEQ02BKO2xxeH7N03aBRj8UjvzQK-C0,14445
11
11
  execsql/types.py,sha256=HVWb4umIB9lpxCGgqk3xy1hoGYPfN39xci5mHF0Izq4,31882
12
12
  execsql/cli/__init__.py,sha256=s4283f04Z-SS5CdwsJj6t_oHuyL5sQELsUGTh6x45NU,14908
13
13
  execsql/cli/dsn.py,sha256=svaZtrUXFRL2W5G6FRRiKtR6kehOp7urrVhIx_642Z8,2820
@@ -48,14 +48,14 @@ execsql/gui/__init__.py,sha256=oCb-cyhLZzVpWJ4WU5HbqEDBrV-lm0ytEwxemrOZyqs,2048
48
48
  execsql/gui/base.py,sha256=sfNRkDrf7FhIgMRUOdyZpRLS1Xk9RqNhrV0A1RP6PXU,6068
49
49
  execsql/gui/console.py,sha256=TuGzm7XFxa8iWrofGLwx5DuVIlr3wqqyP_pSnAJ1S3s,14271
50
50
  execsql/gui/desktop.py,sha256=LxRzX0R5iOfXO-OFGm2nzgUZqzGtMJYm0VgY3TV5feA,42152
51
- execsql/gui/tui.py,sha256=8ulz-ZucVgoqmYfhglPeWsSb3ez3OV5Ks6BVjp1mEEg,45978
51
+ execsql/gui/tui.py,sha256=lBwUrePXMF0eWbFqkJ8kBLEHEr3tc-wmxwbyFjs-gfk,50579
52
52
  execsql/importers/__init__.py,sha256=dDsxSVeQYXBvm9yGqN3QswyGbLWTwt08pvUuRJgZhl4,289
53
53
  execsql/importers/base.py,sha256=FGVz3ntN6xHL99rQixlQj3tAf570K_oU83UtbYE1lJg,4124
54
54
  execsql/importers/csv.py,sha256=Mu848WNzuhVO1ade-WurPyxqGOuVNRO8UwRF3-bav_I,4845
55
55
  execsql/importers/feather.py,sha256=g2B69d2uv9vmnXcmjFyTVsMP40LYEzFYkhk3gD26mGw,1900
56
56
  execsql/importers/ods.py,sha256=MJsdsjropzCvxAA3DDZfAL_AnmZ4yij7DnrjGyDJqHQ,2843
57
57
  execsql/importers/xls.py,sha256=e0Zfe47ZiCpA1Ae3XDJ1ko3sCiH3-8U6XLKi6NvD0jQ,3683
58
- execsql/metacommands/__init__.py,sha256=X0esqay79dBn3DOOt7S1hz8k1DghZ6c2dDc4JlXcM2M,13333
58
+ execsql/metacommands/__init__.py,sha256=pdEMN1PoJFDgx1p4TFbi4H1Vx6h_QIXcuE-23AEkwvI,10813
59
59
  execsql/metacommands/conditions.py,sha256=u-XdeIWj9QMht9hRGhvH0XlB9V09AliAPKDBHRXc02s,24540
60
60
  execsql/metacommands/connect.py,sha256=Nsm0D91i3RX-R2rzQQ-Br-gULaI6Uvdn9fqb7DOAVfE,14804
61
61
  execsql/metacommands/control.py,sha256=FCIWD-ZivHRZDqMS-2k37iR05HKHsv_7UPh5zJAg4I4,7693
@@ -86,24 +86,24 @@ execsql/utils/numeric.py,sha256=xh02ANSRk3nUpQ-rtm66ILoMqoi7HtzCoRMIOT9U8QI,1570
86
86
  execsql/utils/regex.py,sha256=diEzTZqU_HHwVMadPAvN1Vgzhl7I03eVaEFGCXyGGL8,3770
87
87
  execsql/utils/strings.py,sha256=5Dvzrk-9SIw2lpxXZQkiJbNyo1sy7iXXAtSULlZ0KG8,8488
88
88
  execsql/utils/timer.py,sha256=eDYf5VzCNFk7oo90InJucUm3XcBdhYMogjZMqeg9xzc,1899
89
- execsql2-2.5.0.data/data/execsql2_extras/README.md,sha256=sxwVyU0ZahCfANv56LahkyuM505kFjrMhe-1SvWE69E,4845
90
- execsql2-2.5.0.data/data/execsql2_extras/config_settings.sqlite,sha256=aY5cxR7Q7J6zJ4bC9lu5mHUrhy211Cq3MNKPQVCt02E,20480
91
- execsql2-2.5.0.data/data/execsql2_extras/example_config_prompt.sql,sha256=SY3Jxn1qcVm4kPW9xmmTfknHfvURXmeEYTbRjYrjGSw,7487
92
- execsql2-2.5.0.data/data/execsql2_extras/execsql.conf,sha256=_45iJ-KWZnB8uMW_gEg067MM5pmGJ-dVl7VbAZMunAE,9530
93
- execsql2-2.5.0.data/data/execsql2_extras/make_config_db.sql,sha256=WwyC6dK-Eh5CAVppiBCDHqiI1_wEI9U95Ytpr4lsZkg,8726
94
- execsql2-2.5.0.data/data/execsql2_extras/md_compare.sql,sha256=B8Wd7LZ0vnMY2qvA139JIEBkPObgRH2i5xj6PejTQt8,24092
95
- execsql2-2.5.0.data/data/execsql2_extras/md_glossary.sql,sha256=DJRHcU5NbFpxTTX-IwH3yRlsboj1q6BBGrUAHKn4Cuo,10796
96
- execsql2-2.5.0.data/data/execsql2_extras/md_upsert.sql,sha256=v_7GbWh_N1mBTmw3gvTrkagOVp2q0KmXvM8hE-DlFxY,112524
97
- execsql2-2.5.0.data/data/execsql2_extras/pg_compare.sql,sha256=9dWa8hnfy5dVJI-z2iGpd9JzQmI4j2ziMlEdpnr66ro,24352
98
- execsql2-2.5.0.data/data/execsql2_extras/pg_glossary.sql,sha256=pKjIIDsROAgJq2H-1qNEcRMAWManivcZ_AEVHzUUlic,9908
99
- execsql2-2.5.0.data/data/execsql2_extras/pg_upsert.sql,sha256=k7AFiGTLBy3nf-qO5QIaZrEYTAKvdxxU3JDLx9jqkzs,108315
100
- execsql2-2.5.0.data/data/execsql2_extras/script_template.sql,sha256=1Estacb_vm1FgK41k_G9nuduP1yiA-fQ1Kn4Z4mv5Ao,11153
101
- execsql2-2.5.0.data/data/execsql2_extras/ss_compare.sql,sha256=TsVxWm3cEpR5-EiMYXNhtaY0arSNeKZhsJdHdLA7xeI,24833
102
- execsql2-2.5.0.data/data/execsql2_extras/ss_glossary.sql,sha256=cLM7nN8JOIu9ZVP9oY9qdSK3hrnWJiDcX6nZmQQbQWI,13065
103
- execsql2-2.5.0.data/data/execsql2_extras/ss_upsert.sql,sha256=BCqmBykXBF-BpCgOFeG1qhf2XfScKsxPD17wd1hYfHw,120647
104
- execsql2-2.5.0.dist-info/METADATA,sha256=1GftiLPx-XfwxK2axlqnf1066YyOB-js3bk7ocHRlCQ,16573
105
- execsql2-2.5.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
106
- execsql2-2.5.0.dist-info/entry_points.txt,sha256=sUOxkM-dN1eBGGpSpDLsAaE0yNXYQKWZAfxPOlMkQyk,90
107
- execsql2-2.5.0.dist-info/licenses/LICENSE.txt,sha256=LBdhuxejF8_bLCHZ2kWfmDXpDGUu914Gbd6_3JjCRe0,676
108
- execsql2-2.5.0.dist-info/licenses/NOTICE,sha256=sqVrM73Ys9zfvWC_P797lHfTnoPW_ETeBSrUTFaob0A,339
109
- execsql2-2.5.0.dist-info/RECORD,,
89
+ execsql2-2.6.0.data/data/execsql2_extras/README.md,sha256=sxwVyU0ZahCfANv56LahkyuM505kFjrMhe-1SvWE69E,4845
90
+ execsql2-2.6.0.data/data/execsql2_extras/config_settings.sqlite,sha256=aY5cxR7Q7J6zJ4bC9lu5mHUrhy211Cq3MNKPQVCt02E,20480
91
+ execsql2-2.6.0.data/data/execsql2_extras/example_config_prompt.sql,sha256=SY3Jxn1qcVm4kPW9xmmTfknHfvURXmeEYTbRjYrjGSw,7487
92
+ execsql2-2.6.0.data/data/execsql2_extras/execsql.conf,sha256=_45iJ-KWZnB8uMW_gEg067MM5pmGJ-dVl7VbAZMunAE,9530
93
+ execsql2-2.6.0.data/data/execsql2_extras/make_config_db.sql,sha256=WwyC6dK-Eh5CAVppiBCDHqiI1_wEI9U95Ytpr4lsZkg,8726
94
+ execsql2-2.6.0.data/data/execsql2_extras/md_compare.sql,sha256=B8Wd7LZ0vnMY2qvA139JIEBkPObgRH2i5xj6PejTQt8,24092
95
+ execsql2-2.6.0.data/data/execsql2_extras/md_glossary.sql,sha256=DJRHcU5NbFpxTTX-IwH3yRlsboj1q6BBGrUAHKn4Cuo,10796
96
+ execsql2-2.6.0.data/data/execsql2_extras/md_upsert.sql,sha256=v_7GbWh_N1mBTmw3gvTrkagOVp2q0KmXvM8hE-DlFxY,112524
97
+ execsql2-2.6.0.data/data/execsql2_extras/pg_compare.sql,sha256=9dWa8hnfy5dVJI-z2iGpd9JzQmI4j2ziMlEdpnr66ro,24352
98
+ execsql2-2.6.0.data/data/execsql2_extras/pg_glossary.sql,sha256=pKjIIDsROAgJq2H-1qNEcRMAWManivcZ_AEVHzUUlic,9908
99
+ execsql2-2.6.0.data/data/execsql2_extras/pg_upsert.sql,sha256=k7AFiGTLBy3nf-qO5QIaZrEYTAKvdxxU3JDLx9jqkzs,108315
100
+ execsql2-2.6.0.data/data/execsql2_extras/script_template.sql,sha256=1Estacb_vm1FgK41k_G9nuduP1yiA-fQ1Kn4Z4mv5Ao,11153
101
+ execsql2-2.6.0.data/data/execsql2_extras/ss_compare.sql,sha256=TsVxWm3cEpR5-EiMYXNhtaY0arSNeKZhsJdHdLA7xeI,24833
102
+ execsql2-2.6.0.data/data/execsql2_extras/ss_glossary.sql,sha256=cLM7nN8JOIu9ZVP9oY9qdSK3hrnWJiDcX6nZmQQbQWI,13065
103
+ execsql2-2.6.0.data/data/execsql2_extras/ss_upsert.sql,sha256=BCqmBykXBF-BpCgOFeG1qhf2XfScKsxPD17wd1hYfHw,120647
104
+ execsql2-2.6.0.dist-info/METADATA,sha256=NV6dRQPFMR13RsyM3my0svP-jfb6qd6sLePtERmD8S0,16573
105
+ execsql2-2.6.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
106
+ execsql2-2.6.0.dist-info/entry_points.txt,sha256=sUOxkM-dN1eBGGpSpDLsAaE0yNXYQKWZAfxPOlMkQyk,90
107
+ execsql2-2.6.0.dist-info/licenses/LICENSE.txt,sha256=LBdhuxejF8_bLCHZ2kWfmDXpDGUu914Gbd6_3JjCRe0,676
108
+ execsql2-2.6.0.dist-info/licenses/NOTICE,sha256=sqVrM73Ys9zfvWC_P797lHfTnoPW_ETeBSrUTFaob0A,339
109
+ execsql2-2.6.0.dist-info/RECORD,,