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.
- execsql/cli.py +322 -108
- execsql/config.py +134 -114
- execsql/db/access.py +89 -65
- execsql/db/base.py +97 -68
- execsql/db/dsn.py +45 -29
- execsql/db/duckdb.py +4 -5
- execsql/db/factory.py +27 -27
- execsql/db/firebird.py +30 -18
- execsql/db/mysql.py +38 -14
- execsql/db/oracle.py +58 -33
- execsql/db/postgres.py +68 -28
- execsql/db/sqlite.py +36 -27
- execsql/db/sqlserver.py +45 -30
- execsql/exceptions.py +68 -64
- execsql/exporters/__init__.py +1 -1
- execsql/exporters/base.py +42 -17
- execsql/exporters/delimited.py +60 -59
- execsql/exporters/duckdb.py +8 -12
- execsql/exporters/feather.py +32 -24
- execsql/exporters/html.py +33 -30
- execsql/exporters/json.py +18 -17
- execsql/exporters/latex.py +11 -13
- execsql/exporters/ods.py +50 -46
- execsql/exporters/parquet.py +32 -0
- execsql/exporters/pretty.py +16 -15
- execsql/exporters/raw.py +9 -11
- execsql/exporters/sqlite.py +38 -38
- execsql/exporters/templates.py +15 -72
- execsql/exporters/values.py +13 -12
- execsql/exporters/xls.py +26 -26
- execsql/exporters/xml.py +12 -12
- execsql/exporters/zip.py +0 -3
- execsql/gui/__init__.py +2 -2
- execsql/gui/console.py +0 -1
- execsql/gui/desktop.py +6 -7
- execsql/gui/tui.py +8 -14
- execsql/importers/base.py +6 -9
- execsql/importers/csv.py +10 -17
- execsql/importers/feather.py +16 -22
- execsql/importers/ods.py +3 -4
- execsql/importers/xls.py +5 -6
- execsql/metacommands/__init__.py +8 -8
- execsql/metacommands/conditions.py +41 -33
- execsql/metacommands/connect.py +113 -99
- execsql/metacommands/control.py +38 -26
- execsql/metacommands/data.py +35 -33
- execsql/metacommands/debug.py +13 -9
- execsql/metacommands/io.py +288 -229
- execsql/metacommands/prompt.py +179 -157
- execsql/metacommands/script_ext.py +11 -9
- execsql/metacommands/system.py +44 -25
- execsql/models.py +9 -16
- execsql/parser.py +10 -10
- execsql/script.py +183 -157
- execsql/state.py +170 -208
- execsql/types.py +46 -81
- execsql/utils/auth.py +114 -14
- execsql/utils/crypto.py +31 -4
- execsql/utils/datetime.py +7 -7
- execsql/utils/errors.py +34 -29
- execsql/utils/fileio.py +90 -55
- execsql/utils/gui.py +22 -23
- execsql/utils/mail.py +15 -17
- execsql/utils/numeric.py +2 -3
- execsql/utils/regex.py +9 -12
- execsql/utils/strings.py +10 -12
- execsql/utils/timer.py +0 -2
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/execsql.conf +1 -1
- execsql2-2.1.2.dist-info/METADATA +300 -0
- execsql2-2.1.2.dist-info/RECORD +96 -0
- execsql2-2.0.1.dist-info/METADATA +0 -406
- execsql2-2.0.1.dist-info/RECORD +0 -95
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/READ_ME.rst +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/config_settings.sqlite +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/make_config_db.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_compare.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_glossary.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_upsert.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_compare.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_glossary.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_upsert.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/script_template.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_compare.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_glossary.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_upsert.sql +0 -0
- {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/WHEEL +0 -0
- {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/entry_points.txt +0 -0
- {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/licenses/LICENSE.txt +0 -0
- {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/licenses/NOTICE +0 -0
execsql/importers/csv.py
CHANGED
|
@@ -9,38 +9,33 @@ a delimited file), used by the ``IMPORT`` metacommand with ``FORMAT csv``,
|
|
|
9
9
|
``FORMAT tsv``, and ``FORMAT txt``.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import os
|
|
15
|
-
import re
|
|
16
|
-
from typing import Any, Optional
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
17
14
|
|
|
18
15
|
from execsql.exceptions import ErrInfo
|
|
19
16
|
from execsql.db.base import Database
|
|
20
|
-
from execsql.importers.base import import_data_table
|
|
21
17
|
import execsql.state as _state
|
|
18
|
+
from execsql.types import dbt_firebird
|
|
22
19
|
|
|
23
20
|
|
|
24
21
|
def importtable(
|
|
25
22
|
db: Database,
|
|
26
|
-
schemaname:
|
|
23
|
+
schemaname: str | None,
|
|
27
24
|
tablename: str,
|
|
28
25
|
filename: str,
|
|
29
26
|
is_new: Any,
|
|
30
27
|
skip_header_line: bool = True,
|
|
31
|
-
quotechar:
|
|
32
|
-
delimchar:
|
|
33
|
-
encoding:
|
|
28
|
+
quotechar: str | None = None,
|
|
29
|
+
delimchar: str | None = None,
|
|
30
|
+
encoding: str | None = None,
|
|
34
31
|
junk_header_lines: int = 0,
|
|
35
32
|
) -> None:
|
|
36
33
|
from execsql.utils.errors import exception_info
|
|
37
34
|
|
|
38
35
|
conf = _state.conf
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if not os.path.isfile(filename):
|
|
36
|
+
if not Path(filename).is_file():
|
|
42
37
|
raise ErrInfo(type="error", other_msg=f"Non-existent file ({filename}) used with the IMPORT metacommand")
|
|
43
|
-
enc =
|
|
38
|
+
enc = encoding if encoding else conf.import_encoding
|
|
44
39
|
|
|
45
40
|
# Lazy import of CsvFile
|
|
46
41
|
from execsql.exporters.delimited import CsvFile
|
|
@@ -57,8 +52,6 @@ def importtable(
|
|
|
57
52
|
try:
|
|
58
53
|
db.drop_table(db.schema_qualified_table_name(schemaname, tablename))
|
|
59
54
|
except Exception:
|
|
60
|
-
from execsql.utils.fileio import Logger
|
|
61
|
-
|
|
62
55
|
_state.exec_log.log_status_info(f"Could not drop existing table ({tablename}) for IMPORT metacommand")
|
|
63
56
|
# Don't raise an exception; this may not be a problem because the table may not already exist.
|
|
64
57
|
try:
|
|
@@ -104,7 +97,7 @@ def importtable(
|
|
|
104
97
|
|
|
105
98
|
def importfile(
|
|
106
99
|
db: Database,
|
|
107
|
-
schemaname:
|
|
100
|
+
schemaname: str | None,
|
|
108
101
|
tablename: str,
|
|
109
102
|
columname: str,
|
|
110
103
|
filename: str,
|
execsql/importers/feather.py
CHANGED
|
@@ -3,13 +3,12 @@ from __future__ import annotations
|
|
|
3
3
|
"""
|
|
4
4
|
Feather and Parquet import for execsql.
|
|
5
5
|
|
|
6
|
-
Provides :func:`import_feather` (Apache Arrow Feather v2 via
|
|
7
|
-
and :func:`import_parquet` (Parquet format via ``
|
|
8
|
-
``IMPORT … FORMAT feather`` and ``FORMAT parquet``.
|
|
6
|
+
Provides :func:`import_feather` (Apache Arrow Feather v2 / Arrow IPC via
|
|
7
|
+
``polars``) and :func:`import_parquet` (Parquet format via ``polars``),
|
|
8
|
+
used by ``IMPORT … FORMAT feather`` and ``FORMAT parquet``.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
import
|
|
12
|
-
from typing import Any, Optional
|
|
11
|
+
from typing import Any
|
|
13
12
|
|
|
14
13
|
from execsql.exceptions import ErrInfo
|
|
15
14
|
from execsql.db.base import Database
|
|
@@ -18,7 +17,7 @@ from execsql.importers.base import import_data_table
|
|
|
18
17
|
|
|
19
18
|
def import_feather(
|
|
20
19
|
db: Database,
|
|
21
|
-
schemaname:
|
|
20
|
+
schemaname: str | None,
|
|
22
21
|
tablename: str,
|
|
23
22
|
filename: str,
|
|
24
23
|
is_new: Any,
|
|
@@ -26,25 +25,22 @@ def import_feather(
|
|
|
26
25
|
from execsql.utils.errors import exception_info
|
|
27
26
|
|
|
28
27
|
try:
|
|
29
|
-
import
|
|
30
|
-
import pandas as pd
|
|
31
|
-
import pyarrow.feather
|
|
28
|
+
import polars as pl
|
|
32
29
|
except Exception:
|
|
33
30
|
raise ErrInfo(
|
|
34
31
|
"exception",
|
|
35
32
|
exception_msg=exception_info(),
|
|
36
|
-
other_msg="The
|
|
33
|
+
other_msg="The polars Python library must be installed to import data from the Feather format.",
|
|
37
34
|
)
|
|
38
|
-
df =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
data = df.values.tolist()
|
|
35
|
+
df = pl.read_ipc(filename)
|
|
36
|
+
hdrs = df.columns
|
|
37
|
+
data = [list(row) for row in df.rows()]
|
|
42
38
|
import_data_table(db, schemaname, tablename, is_new, hdrs, data)
|
|
43
39
|
|
|
44
40
|
|
|
45
41
|
def import_parquet(
|
|
46
42
|
db: Database,
|
|
47
|
-
schemaname:
|
|
43
|
+
schemaname: str | None,
|
|
48
44
|
tablename: str,
|
|
49
45
|
filename: str,
|
|
50
46
|
is_new: Any,
|
|
@@ -52,16 +48,14 @@ def import_parquet(
|
|
|
52
48
|
from execsql.utils.errors import exception_info
|
|
53
49
|
|
|
54
50
|
try:
|
|
55
|
-
import
|
|
56
|
-
import pandas as pd
|
|
51
|
+
import polars as pl
|
|
57
52
|
except Exception:
|
|
58
53
|
raise ErrInfo(
|
|
59
54
|
"exception",
|
|
60
55
|
exception_msg=exception_info(),
|
|
61
|
-
other_msg="The
|
|
56
|
+
other_msg="The polars Python library must be installed to import data from the Parquet format.",
|
|
62
57
|
)
|
|
63
|
-
df =
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
data = df.values.tolist()
|
|
58
|
+
df = pl.read_parquet(filename)
|
|
59
|
+
hdrs = df.columns
|
|
60
|
+
data = [list(row) for row in df.rows()]
|
|
67
61
|
import_data_table(db, schemaname, tablename, is_new, hdrs, data)
|
execsql/importers/ods.py
CHANGED
|
@@ -9,8 +9,7 @@ the ``IMPORT … FORMAT ods`` metacommand. Requires ``odfpy``
|
|
|
9
9
|
(``execsql2[ods]``).
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
import
|
|
13
|
-
from typing import Any, List, Optional
|
|
12
|
+
from typing import Any
|
|
14
13
|
|
|
15
14
|
from execsql.exceptions import ErrInfo
|
|
16
15
|
from execsql.db.base import Database
|
|
@@ -39,7 +38,7 @@ def ods_data(
|
|
|
39
38
|
except Exception:
|
|
40
39
|
raise ErrInfo(type="cmd", other_msg=f"{sheetname} is not a worksheet in {filename}.")
|
|
41
40
|
colhdrs = alldata[0]
|
|
42
|
-
if any(
|
|
41
|
+
if any(x is None or len(x.strip()) == 0 for x in colhdrs):
|
|
43
42
|
if conf.del_empty_cols:
|
|
44
43
|
blanks = [i for i in range(len(colhdrs)) if colhdrs[i] is None or len(colhdrs[i].strip()) == 0]
|
|
45
44
|
while len(blanks) > 0:
|
|
@@ -70,7 +69,7 @@ def ods_data(
|
|
|
70
69
|
|
|
71
70
|
def importods(
|
|
72
71
|
db: Database,
|
|
73
|
-
schemaname:
|
|
72
|
+
schemaname: str | None,
|
|
74
73
|
tablename: str,
|
|
75
74
|
is_new: Any,
|
|
76
75
|
filename: str,
|
execsql/importers/xls.py
CHANGED
|
@@ -9,8 +9,7 @@ reader) and the XLSX equivalent using ``openpyxl``. Used by
|
|
|
9
9
|
``execsql2[excel]``.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
import
|
|
13
|
-
from typing import Any, List, Optional
|
|
12
|
+
from typing import Any
|
|
14
13
|
|
|
15
14
|
from execsql.exceptions import ErrInfo
|
|
16
15
|
from execsql.db.base import Database
|
|
@@ -22,7 +21,7 @@ def xls_data(
|
|
|
22
21
|
filename: str,
|
|
23
22
|
sheetname: str,
|
|
24
23
|
junk_header_rows: int,
|
|
25
|
-
encoding:
|
|
24
|
+
encoding: str | None = None,
|
|
26
25
|
) -> tuple:
|
|
27
26
|
"""Returns the data from the specified worksheet as a list of headers and a list of lists of rows."""
|
|
28
27
|
from execsql.utils.strings import clean_words, trim_words, fold_words, dedup_words
|
|
@@ -59,7 +58,7 @@ def xls_data(
|
|
|
59
58
|
if len(alldata) == 1:
|
|
60
59
|
return alldata[0], []
|
|
61
60
|
colhdrs = alldata[0]
|
|
62
|
-
if any(
|
|
61
|
+
if any(x is None or (isinstance(x, str) and len(x.strip()) == 0) for x in colhdrs):
|
|
63
62
|
if conf.del_empty_cols:
|
|
64
63
|
blanks = [i for i in range(len(colhdrs)) if colhdrs[i] is None or len(colhdrs[i].strip()) == 0]
|
|
65
64
|
while len(blanks) > 0:
|
|
@@ -90,13 +89,13 @@ def xls_data(
|
|
|
90
89
|
|
|
91
90
|
def importxls(
|
|
92
91
|
db: Database,
|
|
93
|
-
schemaname:
|
|
92
|
+
schemaname: str | None,
|
|
94
93
|
tablename: str,
|
|
95
94
|
is_new: Any,
|
|
96
95
|
filename: str,
|
|
97
96
|
sheetname: str,
|
|
98
97
|
junk_header_rows: int,
|
|
99
|
-
encoding:
|
|
98
|
+
encoding: str | None,
|
|
100
99
|
) -> None:
|
|
101
100
|
hdrs, data = xls_data(filename, sheetname, junk_header_rows, encoding)
|
|
102
101
|
import_data_table(db, schemaname, tablename, is_new, hdrs, data)
|
execsql/metacommands/__init__.py
CHANGED
|
@@ -12,7 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
|
|
13
13
|
import re
|
|
14
14
|
|
|
15
|
-
import execsql.state as _state
|
|
15
|
+
import execsql.state as _state # noqa: F401
|
|
16
16
|
|
|
17
17
|
# Handler imports — grouped by module for readability.
|
|
18
18
|
from execsql.metacommands.connect import (
|
|
@@ -29,7 +29,7 @@ from execsql.metacommands.connect import (
|
|
|
29
29
|
x_connect_user_ora,
|
|
30
30
|
x_connect_duckdb,
|
|
31
31
|
x_connect_sqlite,
|
|
32
|
-
x_connect_dsn,
|
|
32
|
+
x_connect_dsn, # noqa: F401
|
|
33
33
|
x_use,
|
|
34
34
|
x_disconnect,
|
|
35
35
|
x_autocommit_on,
|
|
@@ -188,13 +188,13 @@ from execsql.metacommands.system import (
|
|
|
188
188
|
x_cancel_halt_write,
|
|
189
189
|
x_cancel_halt_email_clear,
|
|
190
190
|
x_cancel_halt_email,
|
|
191
|
-
x_cancel_halt_exec,
|
|
191
|
+
x_cancel_halt_exec, # noqa: F401
|
|
192
192
|
x_cancel_halt_exec_clear,
|
|
193
193
|
x_error_halt_write_clear,
|
|
194
194
|
x_error_halt_write,
|
|
195
195
|
x_error_halt_email_clear,
|
|
196
196
|
x_error_halt_email,
|
|
197
|
-
x_error_halt_exec,
|
|
197
|
+
x_error_halt_exec, # noqa: F401
|
|
198
198
|
x_error_halt_exec_clear,
|
|
199
199
|
x_write_warnings,
|
|
200
200
|
x_gui_level,
|
|
@@ -204,17 +204,18 @@ from execsql.metacommands.system import (
|
|
|
204
204
|
# Regex helper functions (from utils/regex.py)
|
|
205
205
|
from execsql.utils.regex import (
|
|
206
206
|
ins_rxs,
|
|
207
|
-
ins_quoted_rx,
|
|
207
|
+
ins_quoted_rx, # noqa: F401
|
|
208
208
|
ins_schema_rxs,
|
|
209
209
|
ins_table_rxs,
|
|
210
210
|
ins_table_list_rxs,
|
|
211
211
|
ins_fn_rxs,
|
|
212
212
|
)
|
|
213
|
+
from execsql.script import MetaCommandList
|
|
213
214
|
|
|
214
215
|
|
|
215
|
-
def build_dispatch_table() ->
|
|
216
|
+
def build_dispatch_table() -> MetaCommandList:
|
|
216
217
|
"""Construct and return the complete metacommand dispatch table."""
|
|
217
|
-
mcl =
|
|
218
|
+
mcl = MetaCommandList()
|
|
218
219
|
|
|
219
220
|
# ------------------------------------------------------------------
|
|
220
221
|
# DEBUG metacommands
|
|
@@ -1040,7 +1041,6 @@ def build_dispatch_table() -> _state.MetaCommandList:
|
|
|
1040
1041
|
x_halt,
|
|
1041
1042
|
)
|
|
1042
1043
|
for errmsg_delim in (r"\[", r"\#", r"\`", r"\'", r"\~", r'"'):
|
|
1043
|
-
close = errmsg_delim.replace("\\", "") if errmsg_delim.startswith("\\") else errmsg_delim
|
|
1044
1044
|
# Use the same open/close bracket pair for the errmsg capture
|
|
1045
1045
|
open_c = errmsg_delim if not errmsg_delim.startswith("\\") else errmsg_delim[1:]
|
|
1046
1046
|
close_c = "]" if open_c == "[" else open_c
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
from execsql.exceptions import ErrInfo
|
|
2
3
|
|
|
3
4
|
"""
|
|
4
5
|
Conditional test handler functions for execsql.
|
|
@@ -15,10 +16,19 @@ at registration time.
|
|
|
15
16
|
|
|
16
17
|
import os
|
|
17
18
|
import time
|
|
18
|
-
from
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any
|
|
21
|
+
from collections.abc import Callable
|
|
19
22
|
|
|
20
23
|
import execsql.state as _state
|
|
21
|
-
from execsql.utils.regex import ins_fn_rxs
|
|
24
|
+
from execsql.utils.regex import ins_fn_rxs
|
|
25
|
+
from execsql.parser import CondParser
|
|
26
|
+
from execsql.script import MetaCommandList
|
|
27
|
+
from execsql.types import DT_Boolean, DT_Date, DT_Timestamp, DT_TimestampTZ
|
|
28
|
+
from execsql.utils.datetime import parse_datetime
|
|
29
|
+
from execsql.utils.errors import exception_desc
|
|
30
|
+
from execsql.utils.gui import gui_console_isrunning
|
|
31
|
+
from execsql.utils.strings import unquoted
|
|
22
32
|
|
|
23
33
|
|
|
24
34
|
def xf_contains(**kwargs: Any) -> bool:
|
|
@@ -53,10 +63,10 @@ def xf_hasrows(**kwargs: Any) -> bool:
|
|
|
53
63
|
sql = f"select count(*) from {queryname};"
|
|
54
64
|
try:
|
|
55
65
|
hdrs, rec = _state.dbs.current().select_data(sql)
|
|
56
|
-
except
|
|
66
|
+
except ErrInfo:
|
|
57
67
|
raise
|
|
58
68
|
except Exception:
|
|
59
|
-
raise
|
|
69
|
+
raise ErrInfo("db", sql, exception_msg=exception_desc())
|
|
60
70
|
nrows = rec[0][0]
|
|
61
71
|
return nrows > 0
|
|
62
72
|
|
|
@@ -71,12 +81,12 @@ def xf_dialogcanceled(**kwargs: Any) -> bool:
|
|
|
71
81
|
|
|
72
82
|
def xf_fileexists(**kwargs: Any) -> bool:
|
|
73
83
|
filename = kwargs["filename"]
|
|
74
|
-
return
|
|
84
|
+
return Path(filename.strip()).is_file()
|
|
75
85
|
|
|
76
86
|
|
|
77
87
|
def xf_direxists(**kwargs: Any) -> bool:
|
|
78
88
|
dirname = kwargs["dirname"]
|
|
79
|
-
return
|
|
89
|
+
return Path(dirname.strip()).is_dir()
|
|
80
90
|
|
|
81
91
|
|
|
82
92
|
def xf_schemaexists(**kwargs: Any) -> bool:
|
|
@@ -115,7 +125,7 @@ def xf_sub_empty(**kwargs: Any) -> bool:
|
|
|
115
125
|
else:
|
|
116
126
|
subvarset = _state.commandliststack[-1].paramvals
|
|
117
127
|
if not subvarset.sub_exists(varname):
|
|
118
|
-
raise
|
|
128
|
+
raise ErrInfo(
|
|
119
129
|
type="cmd",
|
|
120
130
|
command_text=kwargs["metacommandline"],
|
|
121
131
|
other_msg=f"Unrecognized substitution variable name: {varname}",
|
|
@@ -136,10 +146,10 @@ def xf_equals(**kwargs: Any) -> bool:
|
|
|
136
146
|
converters = (
|
|
137
147
|
int,
|
|
138
148
|
float,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
149
|
+
DT_Timestamp().from_data,
|
|
150
|
+
DT_TimestampTZ().from_data,
|
|
151
|
+
DT_Date().from_data,
|
|
152
|
+
DT_Boolean().from_data,
|
|
143
153
|
)
|
|
144
154
|
for convf in converters:
|
|
145
155
|
try:
|
|
@@ -171,7 +181,7 @@ def xf_iszero(**kwargs: Any) -> bool:
|
|
|
171
181
|
try:
|
|
172
182
|
v = float(val)
|
|
173
183
|
except Exception:
|
|
174
|
-
raise
|
|
184
|
+
raise ErrInfo(
|
|
175
185
|
type="cmd",
|
|
176
186
|
command_text=kwargs["metacommandline"],
|
|
177
187
|
other_msg=f"The value {{{val}}} is not numeric.",
|
|
@@ -186,7 +196,7 @@ def xf_isgt(**kwargs: Any) -> bool:
|
|
|
186
196
|
v1 = float(val1)
|
|
187
197
|
v2 = float(val2)
|
|
188
198
|
except Exception:
|
|
189
|
-
raise
|
|
199
|
+
raise ErrInfo(
|
|
190
200
|
type="cmd",
|
|
191
201
|
command_text=kwargs["metacommandline"],
|
|
192
202
|
other_msg=f"Values {{{val1}}} and {{{val2}}} are not both numeric.",
|
|
@@ -201,7 +211,7 @@ def xf_isgte(**kwargs: Any) -> bool:
|
|
|
201
211
|
v1 = float(val1)
|
|
202
212
|
v2 = float(val2)
|
|
203
213
|
except Exception:
|
|
204
|
-
raise
|
|
214
|
+
raise ErrInfo(
|
|
205
215
|
type="cmd",
|
|
206
216
|
command_text=kwargs["metacommandline"],
|
|
207
217
|
other_msg=f"Values {{{val1}}} and {{{val2}}} are not both numeric.",
|
|
@@ -210,11 +220,11 @@ def xf_isgte(**kwargs: Any) -> bool:
|
|
|
210
220
|
|
|
211
221
|
|
|
212
222
|
def xf_boolliteral(**kwargs: Any) -> bool:
|
|
213
|
-
return
|
|
223
|
+
return unquoted(kwargs["value"].strip()).lower() in ("true", "yes", "1")
|
|
214
224
|
|
|
215
225
|
|
|
216
226
|
def xf_istrue(**kwargs: Any) -> bool:
|
|
217
|
-
return
|
|
227
|
+
return unquoted(kwargs["value"].strip()).lower() in ("yes", "y", "true", "t", "1")
|
|
218
228
|
|
|
219
229
|
|
|
220
230
|
def xf_dbms(**kwargs: Any) -> bool:
|
|
@@ -249,33 +259,33 @@ def xf_metacommanderror(**kwargs: Any) -> bool:
|
|
|
249
259
|
|
|
250
260
|
|
|
251
261
|
def xf_console(**kwargs: Any) -> bool:
|
|
252
|
-
return
|
|
262
|
+
return gui_console_isrunning()
|
|
253
263
|
|
|
254
264
|
|
|
255
265
|
def xf_newer_file(**kwargs: Any) -> bool:
|
|
256
266
|
file1 = kwargs["file1"]
|
|
257
267
|
file2 = kwargs["file2"]
|
|
258
|
-
if not
|
|
259
|
-
raise
|
|
260
|
-
if not
|
|
261
|
-
raise
|
|
268
|
+
if not Path(file1).exists():
|
|
269
|
+
raise ErrInfo(type="cmd", other_msg=f"File {file1} does not exist.")
|
|
270
|
+
if not Path(file2).exists():
|
|
271
|
+
raise ErrInfo(type="cmd", other_msg=f"File {file2} does not exist.")
|
|
262
272
|
return os.stat(file1).st_mtime > os.stat(file2).st_mtime
|
|
263
273
|
|
|
264
274
|
|
|
265
275
|
def xf_newer_date(**kwargs: Any) -> bool:
|
|
266
276
|
file1 = kwargs["file1"]
|
|
267
|
-
datestr =
|
|
268
|
-
if not
|
|
269
|
-
raise
|
|
270
|
-
dt_value =
|
|
277
|
+
datestr = unquoted(kwargs["datestr"])
|
|
278
|
+
if not Path(file1).exists():
|
|
279
|
+
raise ErrInfo(type="cmd", other_msg=f"File {file1} does not exist.")
|
|
280
|
+
dt_value = parse_datetime(datestr)
|
|
271
281
|
if not dt_value:
|
|
272
|
-
raise
|
|
282
|
+
raise ErrInfo(type="cmd", other_msg=f"{datestr} can't be interpreted as a date/time.")
|
|
273
283
|
return os.stat(file1).st_mtime > time.mktime(dt_value.timetuple())
|
|
274
284
|
|
|
275
285
|
|
|
276
286
|
def build_conditional_table() -> Any:
|
|
277
287
|
"""Construct and return the conditional predicate dispatch table."""
|
|
278
|
-
mcl =
|
|
288
|
+
mcl = MetaCommandList()
|
|
279
289
|
|
|
280
290
|
# CONTAINS
|
|
281
291
|
mcl.add(
|
|
@@ -607,16 +617,16 @@ CONDITIONAL_TABLE = build_conditional_table()
|
|
|
607
617
|
|
|
608
618
|
|
|
609
619
|
def xcmd_test(teststr: str) -> bool:
|
|
610
|
-
result =
|
|
620
|
+
result = CondParser(teststr).parse().eval()
|
|
611
621
|
if result is not None:
|
|
612
622
|
return result
|
|
613
623
|
else:
|
|
614
|
-
raise
|
|
624
|
+
raise ErrInfo(type="cmd", command_text=teststr, other_msg="Unrecognized conditional")
|
|
615
625
|
|
|
616
626
|
|
|
617
627
|
def file_size_date(filename: str) -> tuple[int, str]:
|
|
618
628
|
"""Returns the file size and date (as string) of the given file."""
|
|
619
|
-
s_file =
|
|
629
|
+
s_file = str(Path(filename).resolve())
|
|
620
630
|
f_stat = os.stat(s_file)
|
|
621
631
|
return f_stat.st_size, time.strftime("%Y-%m-%d %H:%M", time.gmtime(f_stat.st_mtime))
|
|
622
632
|
|
|
@@ -632,8 +642,6 @@ def chainfuncs(*funcs: Callable) -> Callable:
|
|
|
632
642
|
|
|
633
643
|
|
|
634
644
|
def as_none(item: Any) -> Any:
|
|
635
|
-
if isinstance(item, str) and len(item) == 0:
|
|
636
|
-
return None
|
|
637
|
-
elif isinstance(item, int) and item == 0:
|
|
645
|
+
if isinstance(item, str) and len(item) == 0 or isinstance(item, int) and item == 0:
|
|
638
646
|
return None
|
|
639
647
|
return item
|