execsql2 2.21.2__py3-none-any.whl → 2.22.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.
- execsql/api.py +29 -4
- execsql/cli/__init__.py +1 -1
- execsql/cli/run.py +37 -17
- execsql/config.py +40 -22
- execsql/data/execsql.conf.template +6 -0
- execsql/db/access.py +6 -5
- execsql/db/base.py +23 -5
- execsql/db/dsn.py +2 -0
- execsql/db/duckdb.py +1 -1
- execsql/db/mysql.py +6 -8
- execsql/db/sqlite.py +1 -1
- execsql/exceptions.py +14 -11
- execsql/exporters/base.py +1 -1
- execsql/exporters/delimited.py +22 -16
- execsql/exporters/duckdb.py +6 -1
- execsql/exporters/feather.py +1 -1
- execsql/exporters/json.py +5 -3
- execsql/exporters/latex.py +2 -2
- execsql/exporters/markdown.py +1 -0
- execsql/exporters/ods.py +7 -3
- execsql/exporters/raw.py +8 -8
- execsql/exporters/sqlite.py +11 -1
- execsql/exporters/templates.py +2 -1
- execsql/exporters/xls.py +10 -10
- execsql/exporters/xml.py +1 -0
- execsql/exporters/yaml.py +1 -0
- execsql/exporters/zip.py +9 -7
- execsql/format.py +6 -6
- execsql/gui/console.py +3 -3
- execsql/gui/desktop.py +28 -10
- execsql/gui/tui.py +8 -6
- execsql/importers/base.py +14 -8
- execsql/importers/csv.py +8 -3
- execsql/importers/xls.py +1 -0
- execsql/metacommands/conditions.py +14 -14
- execsql/metacommands/connect.py +2 -2
- execsql/metacommands/control.py +1 -1
- execsql/metacommands/data.py +2 -1
- execsql/metacommands/io_export.py +4 -0
- execsql/metacommands/io_fileops.py +85 -39
- execsql/metacommands/io_import.py +1 -0
- execsql/metacommands/io_write.py +1 -1
- execsql/metacommands/prompt.py +24 -22
- execsql/metacommands/system.py +14 -1
- execsql/models.py +56 -17
- execsql/parser.py +8 -8
- execsql/plugins.py +1 -1
- execsql/script/engine.py +4 -4
- execsql/script/executor.py +69 -28
- execsql/script/parser.py +13 -12
- execsql/script/variables.py +4 -2
- execsql/state.pyi +122 -0
- execsql/types.py +31 -26
- execsql/utils/auth.py +6 -2
- execsql/utils/datetime.py +3 -3
- execsql/utils/errors.py +7 -5
- execsql/utils/fileio.py +80 -32
- execsql/utils/gui.py +15 -11
- execsql/utils/mail.py +9 -2
- execsql/utils/numeric.py +1 -1
- execsql/utils/regex.py +9 -9
- {execsql2-2.21.2.dist-info → execsql2-2.22.1.dist-info}/METADATA +114 -132
- execsql2-2.22.1.dist-info/RECORD +123 -0
- execsql2-2.21.2.dist-info/RECORD +0 -122
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/README.md +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/config_settings.sqlite +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/make_config_db.sql +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/md_compare.sql +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/md_glossary.sql +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/md_upsert.sql +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/pg_compare.sql +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/pg_glossary.sql +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/pg_upsert.sql +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/script_template.sql +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/ss_compare.sql +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/ss_glossary.sql +0 -0
- {execsql2-2.21.2.data → execsql2-2.22.1.data}/data/execsql2_extras/ss_upsert.sql +0 -0
- {execsql2-2.21.2.dist-info → execsql2-2.22.1.dist-info}/WHEEL +0 -0
- {execsql2-2.21.2.dist-info → execsql2-2.22.1.dist-info}/entry_points.txt +0 -0
- {execsql2-2.21.2.dist-info → execsql2-2.22.1.dist-info}/licenses/LICENSE.txt +0 -0
- {execsql2-2.21.2.dist-info → execsql2-2.22.1.dist-info}/licenses/NOTICE +0 -0
execsql/script/parser.py
CHANGED
|
@@ -27,6 +27,7 @@ from __future__ import annotations
|
|
|
27
27
|
import re
|
|
28
28
|
from collections.abc import Iterable
|
|
29
29
|
from pathlib import Path
|
|
30
|
+
from typing import cast
|
|
30
31
|
|
|
31
32
|
from execsql.exceptions import ErrInfo
|
|
32
33
|
from execsql.utils.errors import write_warning
|
|
@@ -555,7 +556,7 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
555
556
|
other_msg=_unclosed_block_msg(block_stack[-1]),
|
|
556
557
|
)
|
|
557
558
|
frame = block_stack[-1]
|
|
558
|
-
script_node = frame.node
|
|
559
|
+
script_node = cast(ScriptBlock, frame.node)
|
|
559
560
|
if end_name is not None and end_name != script_node.name: # type: ignore[union-attr]
|
|
560
561
|
raise ErrInfo(
|
|
561
562
|
type="cmd",
|
|
@@ -619,8 +620,8 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
619
620
|
frame = block_stack[-1]
|
|
620
621
|
frame._in_else = False
|
|
621
622
|
frame._in_elseif = True
|
|
622
|
-
if_node = frame.node
|
|
623
|
-
if_node.elseif_clauses.append(
|
|
623
|
+
if_node = cast(IfBlock, frame.node)
|
|
624
|
+
if_node.elseif_clauses.append(
|
|
624
625
|
ElseIfClause(
|
|
625
626
|
condition=m.group("cond").strip(),
|
|
626
627
|
span=SourceSpan(source_name, file_lineno),
|
|
@@ -643,11 +644,11 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
643
644
|
span=SourceSpan(source_name, file_lineno),
|
|
644
645
|
)
|
|
645
646
|
frame = block_stack[-1]
|
|
646
|
-
if_node = frame.node
|
|
647
|
-
if frame._in_elseif and if_node.elseif_clauses:
|
|
648
|
-
if_node.elseif_clauses[-1].condition_modifiers.append(modifier)
|
|
647
|
+
if_node = cast(IfBlock, frame.node)
|
|
648
|
+
if frame._in_elseif and if_node.elseif_clauses:
|
|
649
|
+
if_node.elseif_clauses[-1].condition_modifiers.append(modifier)
|
|
649
650
|
else:
|
|
650
|
-
if_node.condition_modifiers.append(modifier)
|
|
651
|
+
if_node.condition_modifiers.append(modifier)
|
|
651
652
|
continue
|
|
652
653
|
|
|
653
654
|
# -- ORIF --
|
|
@@ -665,11 +666,11 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
665
666
|
span=SourceSpan(source_name, file_lineno),
|
|
666
667
|
)
|
|
667
668
|
frame = block_stack[-1]
|
|
668
|
-
if_node = frame.node
|
|
669
|
-
if frame._in_elseif and if_node.elseif_clauses:
|
|
670
|
-
if_node.elseif_clauses[-1].condition_modifiers.append(modifier)
|
|
669
|
+
if_node = cast(IfBlock, frame.node)
|
|
670
|
+
if frame._in_elseif and if_node.elseif_clauses:
|
|
671
|
+
if_node.elseif_clauses[-1].condition_modifiers.append(modifier)
|
|
671
672
|
else:
|
|
672
|
-
if_node.condition_modifiers.append(modifier)
|
|
673
|
+
if_node.condition_modifiers.append(modifier)
|
|
673
674
|
continue
|
|
674
675
|
|
|
675
676
|
# -- ELSE --
|
|
@@ -684,7 +685,7 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
684
685
|
frame = block_stack[-1]
|
|
685
686
|
frame._in_else = True
|
|
686
687
|
frame._in_elseif = False
|
|
687
|
-
frame.node.else_span = SourceSpan(source_name, file_lineno)
|
|
688
|
+
cast(IfBlock, frame.node).else_span = SourceSpan(source_name, file_lineno)
|
|
688
689
|
continue
|
|
689
690
|
|
|
690
691
|
# -- ENDIF --
|
execsql/script/variables.py
CHANGED
|
@@ -91,7 +91,7 @@ class SubVarSet:
|
|
|
91
91
|
self._lazy_providers: dict[str, Any] = {}
|
|
92
92
|
self.prefix_list: list[str] = ["$", "&", "@"]
|
|
93
93
|
# Don't construct/compile on init because deepcopy() can't handle compiled regexes.
|
|
94
|
-
self.var_rx = None
|
|
94
|
+
self.var_rx: re.Pattern[str] | None = None
|
|
95
95
|
|
|
96
96
|
@property
|
|
97
97
|
def substitutions(self) -> list[tuple]:
|
|
@@ -114,7 +114,9 @@ class SubVarSet:
|
|
|
114
114
|
def var_name_ok(self, varname: str) -> bool:
|
|
115
115
|
if self.var_rx is None:
|
|
116
116
|
self.compile_var_rx()
|
|
117
|
-
|
|
117
|
+
var_rx = self.var_rx
|
|
118
|
+
assert var_rx is not None
|
|
119
|
+
return var_rx.match(varname) is not None
|
|
118
120
|
|
|
119
121
|
def check_var_name(self, varname: str) -> None:
|
|
120
122
|
if not self.var_name_ok(varname.lower()):
|
execsql/state.pyi
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import threading
|
|
5
|
+
from contextlib import AbstractContextManager
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from execsql.config import ConfigData, StatObj
|
|
10
|
+
from execsql.db.base import DatabasePool
|
|
11
|
+
from execsql.exporters.base import ExportMetadata, WriteSpec
|
|
12
|
+
from execsql.script import CounterVars, MetaCommandList, ScriptExecSpec, SubVarSet
|
|
13
|
+
from execsql.utils.fileio import FileWriter, Logger, TempFileMgr
|
|
14
|
+
from execsql.utils.mail import MailSpec
|
|
15
|
+
from execsql.utils.timer import Timer
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class ExecFrame:
|
|
19
|
+
kind: str
|
|
20
|
+
label: str = ...
|
|
21
|
+
source: str = ...
|
|
22
|
+
line: int | None = ...
|
|
23
|
+
iteration: int = ...
|
|
24
|
+
params: dict[str, str] | None = ...
|
|
25
|
+
localvars: Any = ...
|
|
26
|
+
paramvals: Any = ...
|
|
27
|
+
paramnames: list[str] | None = ...
|
|
28
|
+
scope_ref: ExecFrame | None = ...
|
|
29
|
+
|
|
30
|
+
varlike: re.Pattern[str]
|
|
31
|
+
endloop_rx: re.Pattern[str]
|
|
32
|
+
loop_rx: re.Pattern[str]
|
|
33
|
+
defer_rx: re.Pattern[str]
|
|
34
|
+
stringtypes: type[str]
|
|
35
|
+
primary_vno: int
|
|
36
|
+
secondary_vno: int
|
|
37
|
+
tertiary_vno: int
|
|
38
|
+
|
|
39
|
+
conf: ConfigData
|
|
40
|
+
logfile_encoding: str
|
|
41
|
+
last_command: Any
|
|
42
|
+
upass: str | None
|
|
43
|
+
err_halt_writespec: WriteSpec | None
|
|
44
|
+
err_halt_email: MailSpec | None
|
|
45
|
+
err_halt_exec: ScriptExecSpec | None
|
|
46
|
+
cancel_halt_writespec: WriteSpec | None
|
|
47
|
+
cancel_halt_mailspec: MailSpec | None
|
|
48
|
+
cancel_halt_exec: ScriptExecSpec | None
|
|
49
|
+
cmds_run: int
|
|
50
|
+
exec_log: Logger
|
|
51
|
+
subvars: SubVarSet
|
|
52
|
+
status: StatObj
|
|
53
|
+
output: Any
|
|
54
|
+
filewriter: FileWriter
|
|
55
|
+
counters: CounterVars
|
|
56
|
+
timer: Timer
|
|
57
|
+
dbs: DatabasePool
|
|
58
|
+
tempfiles: TempFileMgr
|
|
59
|
+
export_metadata: ExportMetadata
|
|
60
|
+
metacommandlist: MetaCommandList
|
|
61
|
+
conditionallist: MetaCommandList
|
|
62
|
+
gui_console: Any
|
|
63
|
+
gui_manager_queue: Any
|
|
64
|
+
gui_manager_thread: threading.Thread | None
|
|
65
|
+
profile_data: list[tuple[Any, ...]] | None
|
|
66
|
+
step_mode: bool
|
|
67
|
+
ast_scripts: dict[str, Any]
|
|
68
|
+
include_chain: list[str]
|
|
69
|
+
ast_exec_stack: list[ExecFrame]
|
|
70
|
+
|
|
71
|
+
class RuntimeContext:
|
|
72
|
+
conf: ConfigData | None
|
|
73
|
+
logfile_encoding: str
|
|
74
|
+
last_command: Any
|
|
75
|
+
upass: str | None
|
|
76
|
+
err_halt_writespec: WriteSpec | None
|
|
77
|
+
err_halt_email: MailSpec | None
|
|
78
|
+
err_halt_exec: ScriptExecSpec | None
|
|
79
|
+
cancel_halt_writespec: WriteSpec | None
|
|
80
|
+
cancel_halt_mailspec: MailSpec | None
|
|
81
|
+
cancel_halt_exec: ScriptExecSpec | None
|
|
82
|
+
cmds_run: int
|
|
83
|
+
exec_log: Logger | None
|
|
84
|
+
subvars: SubVarSet | None
|
|
85
|
+
status: StatObj | None
|
|
86
|
+
output: Any
|
|
87
|
+
filewriter: FileWriter | None
|
|
88
|
+
counters: CounterVars | None
|
|
89
|
+
timer: Timer | None
|
|
90
|
+
dbs: DatabasePool | None
|
|
91
|
+
tempfiles: TempFileMgr | None
|
|
92
|
+
export_metadata: ExportMetadata | None
|
|
93
|
+
metacommandlist: MetaCommandList | None
|
|
94
|
+
conditionallist: MetaCommandList | None
|
|
95
|
+
gui_console: Any
|
|
96
|
+
gui_manager_queue: Any
|
|
97
|
+
gui_manager_thread: threading.Thread | None
|
|
98
|
+
profile_data: list[tuple[Any, ...]] | None
|
|
99
|
+
step_mode: bool
|
|
100
|
+
ast_scripts: dict[str, Any]
|
|
101
|
+
include_chain: list[str]
|
|
102
|
+
ast_exec_stack: list[ExecFrame]
|
|
103
|
+
def __init__(self) -> None: ...
|
|
104
|
+
def current_scope(self) -> ExecFrame | None: ...
|
|
105
|
+
def current_localvars(self) -> Any: ...
|
|
106
|
+
def current_paramvals(self) -> Any: ...
|
|
107
|
+
def outer_script_scopes(self) -> list[ExecFrame]: ...
|
|
108
|
+
|
|
109
|
+
def xcmd_test(teststr: str) -> bool: ...
|
|
110
|
+
def current_scope() -> ExecFrame | None: ...
|
|
111
|
+
def current_localvars() -> Any: ...
|
|
112
|
+
def current_paramvals() -> Any: ...
|
|
113
|
+
def outer_script_scopes() -> list[ExecFrame]: ...
|
|
114
|
+
def get_context() -> RuntimeContext: ...
|
|
115
|
+
def set_context(ctx: RuntimeContext) -> None: ...
|
|
116
|
+
def reset() -> None: ...
|
|
117
|
+
def initialize(config: ConfigData, dispatch_table: object, conditional_table: object) -> None: ...
|
|
118
|
+
|
|
119
|
+
class active_context(AbstractContextManager[RuntimeContext]):
|
|
120
|
+
def __init__(self, ctx: RuntimeContext) -> None: ...
|
|
121
|
+
def __enter__(self) -> RuntimeContext: ...
|
|
122
|
+
def __exit__(self, exc_type: object, exc: object, tb: object) -> None: ...
|
execsql/types.py
CHANGED
|
@@ -34,7 +34,9 @@ provided at module level for every supported DBMS:
|
|
|
34
34
|
import collections
|
|
35
35
|
import datetime
|
|
36
36
|
import re
|
|
37
|
+
from collections.abc import Callable
|
|
37
38
|
from decimal import Decimal
|
|
39
|
+
from typing import Any, cast
|
|
38
40
|
|
|
39
41
|
from execsql.exceptions import DataTypeError, DbTypeError
|
|
40
42
|
from execsql.utils.numeric import leading_zero_num
|
|
@@ -71,13 +73,13 @@ __all__ = [
|
|
|
71
73
|
class DataType:
|
|
72
74
|
"""Abstract base class for all data-type matchers used during column inference."""
|
|
73
75
|
|
|
74
|
-
data_type_name =
|
|
75
|
-
data_type =
|
|
76
|
+
data_type_name: str = "unknown"
|
|
77
|
+
data_type: type[Any] = object
|
|
76
78
|
lenspec = False # Is a length specification required for a (SQL) declaration of this data type?
|
|
77
79
|
varlen = False # Do we need to know if a set of data values varies in length?
|
|
78
80
|
precspec = False # Do we need to know the precision and scale of the data?
|
|
79
|
-
precision = None # Precision (total number of digits) for numeric values.
|
|
80
|
-
scale = None # Scale (number of digits to the right of the decimal point) for numeric values.
|
|
81
|
+
precision: int | None = None # Precision (total number of digits) for numeric values.
|
|
82
|
+
scale: int | None = None # Scale (number of digits to the right of the decimal point) for numeric values.
|
|
81
83
|
_CONV_ERR = "Can't convert %s"
|
|
82
84
|
|
|
83
85
|
def __repr__(self) -> str:
|
|
@@ -402,7 +404,7 @@ class DT_Integer(DataType):
|
|
|
402
404
|
if isinstance(data, str) and not self._INT_RX.match(data):
|
|
403
405
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data)
|
|
404
406
|
try:
|
|
405
|
-
i = int(data)
|
|
407
|
+
i = int(cast(Any, data))
|
|
406
408
|
except Exception as e:
|
|
407
409
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data) from e
|
|
408
410
|
if leading_zero_num(data):
|
|
@@ -440,7 +442,7 @@ class DT_Long(DataType):
|
|
|
440
442
|
if isinstance(data, str) and not data.isdigit():
|
|
441
443
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data)
|
|
442
444
|
try:
|
|
443
|
-
i = int(data)
|
|
445
|
+
i = int(cast(Any, data))
|
|
444
446
|
except Exception as e:
|
|
445
447
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data) from e
|
|
446
448
|
return i
|
|
@@ -466,7 +468,7 @@ class DT_Float(DataType):
|
|
|
466
468
|
if isinstance(data, str) and not self._FLOAT_RX.match(data):
|
|
467
469
|
return False
|
|
468
470
|
try:
|
|
469
|
-
float(data)
|
|
471
|
+
float(cast(Any, data))
|
|
470
472
|
except Exception:
|
|
471
473
|
return False
|
|
472
474
|
return True
|
|
@@ -481,7 +483,7 @@ class DT_Float(DataType):
|
|
|
481
483
|
if isinstance(data, str) and not self._FLOAT_RX.match(data):
|
|
482
484
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data)
|
|
483
485
|
try:
|
|
484
|
-
i = float(data)
|
|
486
|
+
i = float(cast(Any, data))
|
|
485
487
|
except Exception as e:
|
|
486
488
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data) from e
|
|
487
489
|
return i
|
|
@@ -503,11 +505,16 @@ class DT_Decimal(DataType):
|
|
|
503
505
|
# 'dec' should be Decimal.
|
|
504
506
|
x = dec.as_tuple()
|
|
505
507
|
digits = len(x.digits)
|
|
506
|
-
|
|
507
|
-
|
|
508
|
+
exponent = x.exponent
|
|
509
|
+
if not isinstance(exponent, int):
|
|
510
|
+
self.precision = digits
|
|
511
|
+
self.scale = 0
|
|
512
|
+
return
|
|
513
|
+
if exponent < 0 and abs(exponent) > digits:
|
|
514
|
+
self.precision = abs(exponent) + 1
|
|
508
515
|
else:
|
|
509
516
|
self.precision = digits
|
|
510
|
-
self.scale = abs(
|
|
517
|
+
self.scale = abs(exponent)
|
|
511
518
|
|
|
512
519
|
def _from_data(self, data: object) -> object:
|
|
513
520
|
if data is None:
|
|
@@ -643,22 +650,22 @@ class DbType:
|
|
|
643
650
|
# 3. a function to perform a dbms-specific modification of the type conversion result
|
|
644
651
|
# 4. the precision for numeric data types.
|
|
645
652
|
# 5. the scale for numeric data types.
|
|
646
|
-
self.dialect =
|
|
653
|
+
self.dialect: dict[type[DataType], tuple[str, bool, str | None, Callable[[Any], Any] | None, Any, Any]] = {}
|
|
647
654
|
# The dt_xlate dictionary translates one data type to another.
|
|
648
|
-
self.dt_xlate: dict = {}
|
|
655
|
+
self.dt_xlate: dict[type[DataType], type[DataType]] = {}
|
|
649
656
|
|
|
650
657
|
def __repr__(self) -> str:
|
|
651
658
|
return f"DbType({self.dbms_id!r}, {self.quotechars!r})"
|
|
652
659
|
|
|
653
660
|
def name_datatype(
|
|
654
661
|
self,
|
|
655
|
-
data_type:
|
|
662
|
+
data_type: type[DataType],
|
|
656
663
|
dbms_name: str,
|
|
657
664
|
length_required: bool = False,
|
|
658
|
-
casting_name:
|
|
659
|
-
conv_mod_fn:
|
|
660
|
-
precision:
|
|
661
|
-
scale:
|
|
665
|
+
casting_name: str | None = None,
|
|
666
|
+
conv_mod_fn: Callable[[Any], Any] | None = None,
|
|
667
|
+
precision: Any = None,
|
|
668
|
+
scale: Any = None,
|
|
662
669
|
) -> None:
|
|
663
670
|
"""Register a DBMS-specific SQL type name for a DataType class."""
|
|
664
671
|
# data_type is a DataType class object.
|
|
@@ -666,11 +673,9 @@ class DbType:
|
|
|
666
673
|
# length_required indicates whether length information is required.
|
|
667
674
|
# casting_name is an alternate to the data type name to use in SQL "cast(x as <casting_name>)" expressions.
|
|
668
675
|
# conv_mod_fn is a function that modifies the result of data_type().from_data(x).
|
|
669
|
-
if self.dialect is None:
|
|
670
|
-
self.dialect = {}
|
|
671
676
|
self.dialect[data_type] = (dbms_name, length_required, casting_name, conv_mod_fn, precision, scale)
|
|
672
677
|
|
|
673
|
-
def datatype_name(self, data_type:
|
|
678
|
+
def datatype_name(self, data_type: type[DataType]) -> str:
|
|
674
679
|
"""Return the DBMS-specific SQL type name for the given DataType class."""
|
|
675
680
|
# A convenience function to simplify access to data type names.
|
|
676
681
|
try:
|
|
@@ -690,7 +695,7 @@ class DbType:
|
|
|
690
695
|
return self.quotechars[0] + dbms_object + self.quotechars[1]
|
|
691
696
|
return dbms_object
|
|
692
697
|
|
|
693
|
-
def spec_type(self, data_type:
|
|
698
|
+
def spec_type(self, data_type: type[DataType]) -> type[DataType]:
|
|
694
699
|
"""Return the translated data type, or the original if no translation exists."""
|
|
695
700
|
# Returns a translated data type or the original if there is no translation.
|
|
696
701
|
if data_type in self.dt_xlate:
|
|
@@ -700,11 +705,11 @@ class DbType:
|
|
|
700
705
|
def column_spec(
|
|
701
706
|
self,
|
|
702
707
|
column_name: str,
|
|
703
|
-
data_type:
|
|
704
|
-
max_len:
|
|
708
|
+
data_type: type[DataType],
|
|
709
|
+
max_len: int | None = None,
|
|
705
710
|
is_nullable: bool = False,
|
|
706
|
-
precision:
|
|
707
|
-
scale:
|
|
711
|
+
precision: int | None = None,
|
|
712
|
+
scale: int | None = None,
|
|
708
713
|
) -> str:
|
|
709
714
|
"""Return a column specification string suitable for a CREATE TABLE statement."""
|
|
710
715
|
# Returns a column specification as it would be used in a CREATE TABLE statement.
|
execsql/utils/auth.py
CHANGED
|
@@ -26,6 +26,7 @@ Keyring service names follow the pattern
|
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
28
|
import getpass
|
|
29
|
+
from typing import Any, cast
|
|
29
30
|
|
|
30
31
|
import execsql.state as _state
|
|
31
32
|
|
|
@@ -181,6 +182,7 @@ def get_password(
|
|
|
181
182
|
if other_msg is not None:
|
|
182
183
|
prompt = f"{prompt}\n{other_msg}"
|
|
183
184
|
|
|
185
|
+
passwd = ""
|
|
184
186
|
# Try GUI path if a GUI manager thread is running.
|
|
185
187
|
use_gui = False
|
|
186
188
|
try:
|
|
@@ -189,9 +191,10 @@ def get_password(
|
|
|
189
191
|
gui_manager_thread = getattr(_state, "gui_manager_thread", None)
|
|
190
192
|
gui_manager_queue = getattr(_state, "gui_manager_queue", None)
|
|
191
193
|
if gui_manager_thread:
|
|
192
|
-
return_queue = queue.Queue()
|
|
194
|
+
return_queue: queue.Queue[Any] = queue.Queue()
|
|
193
195
|
from execsql.utils.gui import GuiSpec, QUERY_CONSOLE
|
|
194
196
|
|
|
197
|
+
assert gui_manager_queue is not None
|
|
195
198
|
gui_manager_queue.put(GuiSpec(QUERY_CONSOLE, {}, return_queue))
|
|
196
199
|
user_response = return_queue.get(block=True)
|
|
197
200
|
use_gui = user_response["console_running"]
|
|
@@ -215,10 +218,11 @@ def get_password(
|
|
|
215
218
|
"hidetext": True,
|
|
216
219
|
}
|
|
217
220
|
gui_manager_queue = getattr(_state, "gui_manager_queue", None)
|
|
221
|
+
assert gui_manager_queue is not None
|
|
218
222
|
gui_manager_queue.put(GuiSpec(GUI_DISPLAY, gui_args, return_queue))
|
|
219
223
|
user_response = return_queue.get(block=True)
|
|
220
224
|
btn = user_response["button"]
|
|
221
|
-
passwd = user_response["return_value"]
|
|
225
|
+
passwd = cast(str, user_response["return_value"])
|
|
222
226
|
if not btn and _state.status and _state.status.cancel_halt:
|
|
223
227
|
if _state.exec_log:
|
|
224
228
|
_state.exec_log.log_exit_halt(
|
execsql/utils/datetime.py
CHANGED
|
@@ -13,7 +13,7 @@ Delegates to ``dateutil.parser.parse()`` for robust, format-agnostic parsing.
|
|
|
13
13
|
|
|
14
14
|
import datetime
|
|
15
15
|
import re
|
|
16
|
-
from typing import Any
|
|
16
|
+
from typing import Any, cast
|
|
17
17
|
|
|
18
18
|
from dateutil import parser as _dateutil_parser
|
|
19
19
|
|
|
@@ -66,7 +66,7 @@ def parse_datetime(datestr: Any) -> datetime.datetime | None:
|
|
|
66
66
|
if _looks_time_only(datestr):
|
|
67
67
|
return None
|
|
68
68
|
try:
|
|
69
|
-
return _dateutil_parser.parse(datestr)
|
|
69
|
+
return cast(datetime.datetime, _dateutil_parser.parse(datestr))
|
|
70
70
|
except (ValueError, OverflowError, TypeError):
|
|
71
71
|
return None
|
|
72
72
|
|
|
@@ -90,7 +90,7 @@ def parse_datetimetz(data: Any) -> datetime.datetime | None:
|
|
|
90
90
|
if _looks_numeric(data):
|
|
91
91
|
return None
|
|
92
92
|
try:
|
|
93
|
-
dt = _dateutil_parser.parse(data)
|
|
93
|
+
dt = cast(datetime.datetime, _dateutil_parser.parse(data))
|
|
94
94
|
except (ValueError, OverflowError, TypeError):
|
|
95
95
|
return None
|
|
96
96
|
if dt.tzinfo is None or dt.tzinfo.utcoffset(dt) is None:
|
execsql/utils/errors.py
CHANGED
|
@@ -48,11 +48,13 @@ def exception_info() -> tuple:
|
|
|
48
48
|
exc_param = sys.exc_info()[1]
|
|
49
49
|
if isinstance(exc_param, str):
|
|
50
50
|
exc_message = exc_param
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
exc_message =
|
|
51
|
+
elif exc_param is not None:
|
|
52
|
+
message = getattr(exc_param, "message", None)
|
|
53
|
+
value = getattr(exc_param, "value", None)
|
|
54
|
+
if isinstance(message, str) and len(message) > 0:
|
|
55
|
+
exc_message = message
|
|
56
|
+
elif isinstance(value, str) and len(value) > 0:
|
|
57
|
+
exc_message = value
|
|
56
58
|
else:
|
|
57
59
|
exc_message = str(exc_param)
|
|
58
60
|
try:
|