execsql2 2.1.2__py3-none-any.whl → 2.2.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/cli/__init__.py +436 -0
- execsql/cli/dsn.py +86 -0
- execsql/cli/help.py +140 -0
- execsql/{cli.py → cli/run.py} +14 -589
- execsql/config.py +13 -1
- execsql/db/access.py +16 -12
- execsql/db/base.py +158 -90
- execsql/db/dsn.py +6 -5
- execsql/db/duckdb.py +2 -2
- execsql/db/firebird.py +23 -19
- execsql/db/mysql.py +8 -7
- execsql/db/oracle.py +11 -11
- execsql/db/postgres.py +28 -16
- execsql/db/sqlite.py +12 -11
- execsql/db/sqlserver.py +5 -3
- execsql/exceptions.py +7 -7
- execsql/exporters/base.py +6 -1
- execsql/exporters/delimited.py +44 -35
- execsql/exporters/duckdb.py +2 -2
- execsql/exporters/feather.py +6 -6
- execsql/exporters/html.py +83 -69
- execsql/exporters/json.py +50 -42
- execsql/exporters/latex.py +33 -27
- execsql/exporters/ods.py +4 -4
- execsql/exporters/parquet.py +2 -2
- execsql/exporters/pretty.py +11 -9
- execsql/exporters/raw.py +17 -13
- execsql/exporters/sqlite.py +2 -2
- execsql/exporters/templates.py +23 -15
- execsql/exporters/values.py +22 -20
- execsql/exporters/xls.py +4 -4
- execsql/exporters/xml.py +28 -13
- execsql/importers/base.py +4 -4
- execsql/importers/csv.py +6 -6
- execsql/importers/feather.py +4 -4
- execsql/importers/ods.py +4 -4
- execsql/importers/xls.py +4 -4
- execsql/metacommands/__init__.py +518 -67
- execsql/metacommands/conditions.py +101 -27
- execsql/metacommands/control.py +8 -4
- execsql/metacommands/data.py +6 -6
- execsql/metacommands/debug.py +6 -2
- execsql/metacommands/io.py +67 -1310
- execsql/metacommands/io_export.py +442 -0
- execsql/metacommands/io_fileops.py +287 -0
- execsql/metacommands/io_import.py +398 -0
- execsql/metacommands/io_write.py +248 -0
- execsql/metacommands/prompt.py +22 -66
- execsql/metacommands/system.py +7 -2
- execsql/py.typed +0 -0
- execsql/script.py +49 -5
- execsql/types.py +20 -20
- execsql/utils/fileio.py +15 -8
- {execsql2-2.1.2.dist-info → execsql2-2.2.1.dist-info}/METADATA +6 -6
- execsql2-2.2.1.dist-info/RECORD +104 -0
- execsql2-2.1.2.dist-info/RECORD +0 -96
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/READ_ME.rst +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/config_settings.sqlite +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/execsql.conf +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/make_config_db.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/md_compare.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/md_glossary.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/md_upsert.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/pg_compare.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/pg_glossary.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/pg_upsert.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/script_template.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/ss_compare.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/ss_glossary.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.2.1.data}/data/execsql2_extras/ss_upsert.sql +0 -0
- {execsql2-2.1.2.dist-info → execsql2-2.2.1.dist-info}/WHEEL +0 -0
- {execsql2-2.1.2.dist-info → execsql2-2.2.1.dist-info}/entry_points.txt +0 -0
- {execsql2-2.1.2.dist-info → execsql2-2.2.1.dist-info}/licenses/LICENSE.txt +0 -0
- {execsql2-2.1.2.dist-info → execsql2-2.2.1.dist-info}/licenses/NOTICE +0 -0
execsql/exporters/html.py
CHANGED
|
@@ -11,6 +11,7 @@ CSS styling.
|
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
13
13
|
import getpass
|
|
14
|
+
import html as html_mod
|
|
14
15
|
import os
|
|
15
16
|
import sys
|
|
16
17
|
import tempfile
|
|
@@ -42,12 +43,12 @@ def export_html(
|
|
|
42
43
|
f.write(f"<caption>{desc}</caption>\n")
|
|
43
44
|
f.write("<thead><tr>")
|
|
44
45
|
for h in hdrs:
|
|
45
|
-
f.write(f"<th>{h}</th>")
|
|
46
|
+
f.write(f"<th>{html_mod.escape(str(h))}</th>")
|
|
46
47
|
f.write("</tr></thead>\n<tbody>\n")
|
|
47
48
|
for r in rows:
|
|
48
49
|
f.write("<tr>")
|
|
49
50
|
for v in r:
|
|
50
|
-
f.write(f"<td>{v if v else ''}</td>")
|
|
51
|
+
f.write(f"<td>{html_mod.escape(str(v)) if v else ''}</td>")
|
|
51
52
|
f.write("</tr>\n")
|
|
52
53
|
f.write("</tbody>\n</table>\n")
|
|
53
54
|
|
|
@@ -67,42 +68,47 @@ def export_html(
|
|
|
67
68
|
f = ef.open("wt")
|
|
68
69
|
else:
|
|
69
70
|
f = ZipWriter(zipfile, outfile, append)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if conf.css_styles:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
71
|
+
try:
|
|
72
|
+
f.write('<!DOCTYPE html>\n<html>\n<head>\n<meta charset="utf-8" />\n')
|
|
73
|
+
if querytext:
|
|
74
|
+
descrip = f"Source: [{querytext}] with database {_state.dbs.current().name()} in script {str(Path(script).resolve())}, line {lno}"
|
|
75
|
+
else:
|
|
76
|
+
descrip = (
|
|
77
|
+
f"From database {_state.dbs.current().name()} in script {str(Path(script).resolve())}, line {lno}"
|
|
78
|
+
)
|
|
79
|
+
f.write(f'<meta name="description" content="{descrip}" />\n')
|
|
80
|
+
datecontent = datetime.datetime.now().strftime("%Y-%m-%d")
|
|
81
|
+
f.write(f'<meta name="created" content="{datecontent}" />\n')
|
|
82
|
+
f.write(f'<meta name="revised" content="{datecontent}" />\n')
|
|
83
|
+
f.write(f'<meta name="author" content="{getpass.getuser()}" />\n')
|
|
84
|
+
f.write("<title>Data Table</title>\n")
|
|
85
|
+
if conf.css_file or conf.css_styles:
|
|
86
|
+
if conf.css_file:
|
|
87
|
+
f.write(f'<link rel="stylesheet" type="text/css" href="{conf.css_file}">')
|
|
88
|
+
if conf.css_styles:
|
|
89
|
+
f.write(f'<style type="text/css">\n{conf.css_styles}\n</style>')
|
|
90
|
+
else:
|
|
91
|
+
f.write('<style type="text/css">\n')
|
|
92
|
+
f.write(
|
|
93
|
+
'table {font-family: "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Lucida Console", "Courier New", Courier, fixed; '
|
|
94
|
+
+ "border-top: 3px solid #814324; border-bottom: 3px solid #814324; "
|
|
95
|
+
+ "border-left: 2px solid #814324; border-right: 2px solid #814324; "
|
|
96
|
+
+ "border-collapse: collapse; }\n",
|
|
97
|
+
)
|
|
98
|
+
f.write("td {text-align: left; padding 0 10px; border-right: 1px dotted #814324; }\n")
|
|
99
|
+
f.write(
|
|
100
|
+
"th {padding: 2px 10px; text-align: center; border-bottom: 1px solid #814324; border-right: 1px dotted #814324;}\n",
|
|
101
|
+
)
|
|
102
|
+
f.write("tr.hdr {font-weight: bold;}\n")
|
|
103
|
+
f.write("thead tr {border-bottom: 1px solid #814324; background-color: #F3F1E2; }\n")
|
|
104
|
+
f.write("tbody tr { border-bottom: 1px dotted #814324; }\n")
|
|
105
|
+
f.write("</style>")
|
|
106
|
+
f.write("\n</head>\n<body>\n")
|
|
107
|
+
write_table(f)
|
|
108
|
+
f.write("</body>\n</html>\n")
|
|
109
|
+
finally:
|
|
110
|
+
if outfile.lower() != "stdout":
|
|
111
|
+
f.close()
|
|
106
112
|
elif not zipfile and append:
|
|
107
113
|
if outfile.lower() == "stdout":
|
|
108
114
|
f = sys.stdout
|
|
@@ -112,8 +118,10 @@ def export_html(
|
|
|
112
118
|
|
|
113
119
|
ef = EncodedFile(outfile, conf.output_encoding)
|
|
114
120
|
f = ef.open("wt")
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
try:
|
|
122
|
+
write_table(f)
|
|
123
|
+
finally:
|
|
124
|
+
f.close()
|
|
117
125
|
else:
|
|
118
126
|
filewriter_close(outfile)
|
|
119
127
|
from execsql.utils.fileio import EncodedFile
|
|
@@ -124,23 +132,25 @@ def export_html(
|
|
|
124
132
|
os.close(tempf) # Close the fd from mkstemp; EncodedFile opens its own handle
|
|
125
133
|
tf = EncodedFile(tempfname, conf.output_encoding)
|
|
126
134
|
t = tf.open("wt")
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
try:
|
|
136
|
+
remainder = ""
|
|
137
|
+
for line in f:
|
|
138
|
+
bodypos = line.lower().find("</body>")
|
|
139
|
+
if bodypos > -1:
|
|
140
|
+
t.write(line[0:bodypos])
|
|
141
|
+
t.write("\n")
|
|
142
|
+
remainder = line[bodypos:]
|
|
143
|
+
break
|
|
144
|
+
else:
|
|
145
|
+
t.write(line)
|
|
146
|
+
t.write("\n")
|
|
147
|
+
write_table(t)
|
|
148
|
+
t.write(remainder)
|
|
149
|
+
for line in f:
|
|
136
150
|
t.write(line)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
for line in f:
|
|
141
|
-
t.write(line)
|
|
142
|
-
t.close()
|
|
143
|
-
f.close()
|
|
151
|
+
finally:
|
|
152
|
+
t.close()
|
|
153
|
+
f.close()
|
|
144
154
|
os.unlink(outfile)
|
|
145
155
|
os.rename(tempfname, outfile)
|
|
146
156
|
|
|
@@ -162,12 +172,12 @@ def export_cgi_html(
|
|
|
162
172
|
f.write(f"<caption>{desc}</caption>\n")
|
|
163
173
|
f.write("<thead><tr>")
|
|
164
174
|
for h in hdrs:
|
|
165
|
-
f.write(f"<th>{h}</th>")
|
|
175
|
+
f.write(f"<th>{html_mod.escape(str(h))}</th>")
|
|
166
176
|
f.write("</tr></thead>\n<tbody>\n")
|
|
167
177
|
for r in rows:
|
|
168
178
|
f.write("<tr>")
|
|
169
179
|
for v in r:
|
|
170
|
-
f.write(f"<td>{v if v else ''}</td>")
|
|
180
|
+
f.write(f"<td>{html_mod.escape(str(v)) if v else ''}</td>")
|
|
171
181
|
f.write("</tr>\n")
|
|
172
182
|
f.write("</tbody>\n</table>\n")
|
|
173
183
|
|
|
@@ -184,10 +194,12 @@ def export_cgi_html(
|
|
|
184
194
|
f = ef.open("wt")
|
|
185
195
|
else:
|
|
186
196
|
f = ZipWriter(zipfile, outfile, append)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
197
|
+
try:
|
|
198
|
+
f.write("Content-Type: text/html\n\n")
|
|
199
|
+
write_table(f)
|
|
200
|
+
finally:
|
|
201
|
+
if outfile.lower() != "stdout":
|
|
202
|
+
f.close()
|
|
191
203
|
else:
|
|
192
204
|
if outfile == "stdout":
|
|
193
205
|
f = sys.stdout
|
|
@@ -196,9 +208,11 @@ def export_cgi_html(
|
|
|
196
208
|
|
|
197
209
|
ef = EncodedFile(outfile, conf.output_encoding)
|
|
198
210
|
f = ef.open("a")
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
211
|
+
try:
|
|
212
|
+
write_table(f)
|
|
213
|
+
finally:
|
|
214
|
+
if outfile.lower() != "stdout":
|
|
215
|
+
f.close()
|
|
202
216
|
|
|
203
217
|
|
|
204
218
|
def write_query_to_html(
|
|
@@ -213,8 +227,8 @@ def write_query_to_html(
|
|
|
213
227
|
hdrs, rows = db.select_rowsource(select_stmt)
|
|
214
228
|
except ErrInfo:
|
|
215
229
|
raise
|
|
216
|
-
except Exception:
|
|
217
|
-
raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
|
|
230
|
+
except Exception as e:
|
|
231
|
+
raise ErrInfo("db", select_stmt, exception_msg=exception_desc()) from e
|
|
218
232
|
export_html(outfile, hdrs, rows, append, select_stmt, desc, zipfile=zipfile)
|
|
219
233
|
|
|
220
234
|
|
|
@@ -230,6 +244,6 @@ def write_query_to_cgi_html(
|
|
|
230
244
|
hdrs, rows = db.select_rowsource(select_stmt)
|
|
231
245
|
except ErrInfo:
|
|
232
246
|
raise
|
|
233
|
-
except Exception:
|
|
234
|
-
raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
|
|
247
|
+
except Exception as e:
|
|
248
|
+
raise ErrInfo("db", select_stmt, exception_msg=exception_desc()) from e
|
|
235
249
|
export_cgi_html(outfile, hdrs, rows, append, select_stmt, desc, zipfile=zipfile)
|
execsql/exporters/json.py
CHANGED
|
@@ -34,8 +34,8 @@ def write_query_to_json(
|
|
|
34
34
|
hdrs, rows = db.select_rowsource(select_stmt)
|
|
35
35
|
except ErrInfo:
|
|
36
36
|
raise
|
|
37
|
-
except Exception:
|
|
38
|
-
raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
|
|
37
|
+
except Exception as e:
|
|
38
|
+
raise ErrInfo("db", select_stmt, exception_msg=exception_desc()) from e
|
|
39
39
|
if zipfile is None:
|
|
40
40
|
filewriter_close(outfile)
|
|
41
41
|
from execsql.utils.fileio import EncodedFile
|
|
@@ -48,20 +48,22 @@ def write_query_to_json(
|
|
|
48
48
|
f = ef.open("wt")
|
|
49
49
|
else:
|
|
50
50
|
f = ZipWriter(zipfile, outfile, append)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
51
|
+
try:
|
|
52
|
+
f.write("[")
|
|
53
|
+
uhdrs = [str(h) for h in hdrs]
|
|
54
|
+
first = True
|
|
55
|
+
for row in rows:
|
|
56
|
+
if first:
|
|
57
|
+
f.write("\n")
|
|
58
|
+
else:
|
|
59
|
+
f.write(",\n")
|
|
60
|
+
first = False
|
|
61
|
+
dictdata = dict(zip(uhdrs, [str(v) if isinstance(v, str) else v for v in row]))
|
|
62
|
+
jsondata = json.dumps(dictdata, separators=(",", ":"), default=str)
|
|
63
|
+
f.write(str(jsondata))
|
|
64
|
+
f.write("\n]\n")
|
|
65
|
+
finally:
|
|
66
|
+
f.close()
|
|
65
67
|
|
|
66
68
|
|
|
67
69
|
def write_query_to_json_ts(
|
|
@@ -73,13 +75,16 @@ def write_query_to_json_ts(
|
|
|
73
75
|
desc: str | None = None,
|
|
74
76
|
zipfile: str | None = None,
|
|
75
77
|
) -> None:
|
|
78
|
+
global json
|
|
79
|
+
import json
|
|
80
|
+
|
|
76
81
|
conf = _state.conf
|
|
77
82
|
try:
|
|
78
83
|
hdrs, rows = db.select_rowsource(select_stmt)
|
|
79
84
|
except ErrInfo:
|
|
80
85
|
raise
|
|
81
|
-
except Exception:
|
|
82
|
-
raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
|
|
86
|
+
except Exception as e:
|
|
87
|
+
raise ErrInfo("db", select_stmt, exception_msg=exception_desc()) from e
|
|
83
88
|
max_col_idx = len(hdrs) - 1
|
|
84
89
|
if zipfile is None:
|
|
85
90
|
filewriter_close(outfile)
|
|
@@ -93,27 +98,30 @@ def write_query_to_json_ts(
|
|
|
93
98
|
f = ef.open("wt")
|
|
94
99
|
else:
|
|
95
100
|
f = ZipWriter(zipfile, outfile, append)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
101
|
+
try:
|
|
102
|
+
f.write("{\n")
|
|
103
|
+
if desc is not None:
|
|
104
|
+
escaped_desc = json.dumps(desc)
|
|
105
|
+
f.write(f' "description": {escaped_desc},\n')
|
|
106
|
+
f.write(' "fields": [\n')
|
|
107
|
+
if write_types:
|
|
108
|
+
# Scan the data to determine data types.
|
|
109
|
+
tbl_desc = DataTable(hdrs, rows)
|
|
110
|
+
# Write the column descriptions to the header.
|
|
111
|
+
# Iterate over hdrs instead of tbl_desc.cols to preserve column order.
|
|
112
|
+
for i, h in enumerate(hdrs):
|
|
113
|
+
qcomma = "," if i < max_col_idx else ""
|
|
114
|
+
c = [col for col in tbl_desc.cols if col.name == h][0]
|
|
115
|
+
f.write(
|
|
116
|
+
f' {{\n "name": "{c.name}",\n "title": "{c.name.capitalize().replace("_", " ")}",\n "type": "{_state.to_json_type[c.dt[1]]}"\n }}{qcomma}\n',
|
|
117
|
+
)
|
|
118
|
+
else:
|
|
119
|
+
# Write the column descriptions to the header.
|
|
120
|
+
for i, h in enumerate(hdrs):
|
|
121
|
+
qcomma = "," if i < max_col_idx else ""
|
|
122
|
+
f.write(
|
|
123
|
+
f' {{\n "name": "{h}",\n "title": "{h.capitalize().replace("_", " ")}"\n }}{qcomma}\n',
|
|
124
|
+
)
|
|
125
|
+
f.write(" ]\n}\n")
|
|
126
|
+
finally:
|
|
127
|
+
f.close()
|
execsql/exporters/latex.py
CHANGED
|
@@ -60,12 +60,14 @@ def export_latex(
|
|
|
60
60
|
f = ef.open("wt")
|
|
61
61
|
else:
|
|
62
62
|
f = WriteableZipfile(zipfile).open(outfile, append)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
try:
|
|
64
|
+
f.write("\\documentclass{article}\n")
|
|
65
|
+
f.write("\\begin{document}\n")
|
|
66
|
+
write_table(f)
|
|
67
|
+
f.write("\\end{document}\n")
|
|
68
|
+
finally:
|
|
69
|
+
if outfile.lower() != "stdout":
|
|
70
|
+
f.close()
|
|
69
71
|
else:
|
|
70
72
|
if outfile.lower() == "stdout" or not Path(outfile).is_file():
|
|
71
73
|
if outfile.lower() == "stdout":
|
|
@@ -75,9 +77,11 @@ def export_latex(
|
|
|
75
77
|
else:
|
|
76
78
|
ef = EncodedFile(outfile, conf.output_encoding)
|
|
77
79
|
f = ef.open("wt")
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
try:
|
|
81
|
+
write_table(f)
|
|
82
|
+
finally:
|
|
83
|
+
if outfile.lower() != "stdout":
|
|
84
|
+
f.close()
|
|
81
85
|
else:
|
|
82
86
|
ef = EncodedFile(outfile, conf.output_encoding)
|
|
83
87
|
f = ef.open("rt")
|
|
@@ -85,23 +89,25 @@ def export_latex(
|
|
|
85
89
|
os.close(tempf) # Close the fd from mkstemp; EncodedFile opens its own handle
|
|
86
90
|
tf = EncodedFile(tempfname, conf.output_encoding)
|
|
87
91
|
t = tf.open("wt")
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
try:
|
|
93
|
+
remainder = ""
|
|
94
|
+
for line in f:
|
|
95
|
+
bodypos = line.lower().find("\\end{document}")
|
|
96
|
+
if bodypos > -1:
|
|
97
|
+
t.write(line[0:bodypos])
|
|
98
|
+
t.write("\n")
|
|
99
|
+
remainder = line[bodypos:]
|
|
100
|
+
break
|
|
101
|
+
else:
|
|
102
|
+
t.write(line)
|
|
103
|
+
t.write("\n")
|
|
104
|
+
write_table(t)
|
|
105
|
+
t.write(remainder)
|
|
106
|
+
for line in f:
|
|
97
107
|
t.write(line)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
for line in f:
|
|
102
|
-
t.write(line)
|
|
103
|
-
t.close()
|
|
104
|
-
f.close()
|
|
108
|
+
finally:
|
|
109
|
+
t.close()
|
|
110
|
+
f.close()
|
|
105
111
|
os.unlink(outfile)
|
|
106
112
|
os.rename(tempfname, outfile)
|
|
107
113
|
|
|
@@ -120,6 +126,6 @@ def write_query_to_latex(
|
|
|
120
126
|
hdrs, rows = db.select_rowsource(select_stmt)
|
|
121
127
|
except ErrInfo:
|
|
122
128
|
raise
|
|
123
|
-
except Exception:
|
|
124
|
-
raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
|
|
129
|
+
except Exception as e:
|
|
130
|
+
raise ErrInfo("db", select_stmt, exception_msg=exception_desc()) from e
|
|
125
131
|
export_latex(outfile, hdrs, rows, append, select_stmt, desc, zipfile=zipfile)
|
execsql/exporters/ods.py
CHANGED
|
@@ -342,8 +342,8 @@ def write_query_to_ods(
|
|
|
342
342
|
hdrs, rows = db.select_rowsource(select_stmt)
|
|
343
343
|
except ErrInfo:
|
|
344
344
|
raise
|
|
345
|
-
except Exception:
|
|
346
|
-
raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
|
|
345
|
+
except Exception as e:
|
|
346
|
+
raise ErrInfo("db", select_stmt, exception_msg=exception_desc()) from e
|
|
347
347
|
export_ods(outfile, hdrs, rows, append, select_stmt, sheetname, desc)
|
|
348
348
|
|
|
349
349
|
|
|
@@ -403,8 +403,8 @@ def write_queries_to_ods(
|
|
|
403
403
|
hdrs, rows = db.select_rowsource(select_stmt)
|
|
404
404
|
except ErrInfo:
|
|
405
405
|
raise
|
|
406
|
-
except Exception:
|
|
407
|
-
raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
|
|
406
|
+
except Exception as e:
|
|
407
|
+
raise ErrInfo("db", select_stmt, exception_msg=exception_desc()) from e
|
|
408
408
|
# Add the data to a new sheet.
|
|
409
409
|
tbl = wbk.new_sheet(sheet_name)
|
|
410
410
|
wbk.add_row_to_sheet(hdrs, tbl, header=True)
|
execsql/exporters/parquet.py
CHANGED
|
@@ -17,12 +17,12 @@ from execsql.utils.fileio import filewriter_close
|
|
|
17
17
|
def write_query_to_parquet(outfile: str, headers: list[str], rows: Any) -> None:
|
|
18
18
|
try:
|
|
19
19
|
import polars as pl
|
|
20
|
-
except ImportError:
|
|
20
|
+
except ImportError as e:
|
|
21
21
|
raise ErrInfo(
|
|
22
22
|
"exception",
|
|
23
23
|
exception_msg=exception_desc(),
|
|
24
24
|
other_msg="The polars Python package must be installed to export data to the parquet format.",
|
|
25
|
-
)
|
|
25
|
+
) from e
|
|
26
26
|
rows_list = list(rows)
|
|
27
27
|
if rows_list:
|
|
28
28
|
df = pl.DataFrame(rows_list, schema=headers, orient="row")
|
execsql/exporters/pretty.py
CHANGED
|
@@ -43,12 +43,12 @@ def prettyprint_rowset(
|
|
|
43
43
|
if not isinstance(rows, list):
|
|
44
44
|
try:
|
|
45
45
|
rows = list(rows)
|
|
46
|
-
except Exception:
|
|
46
|
+
except Exception as e:
|
|
47
47
|
raise ErrInfo(
|
|
48
48
|
"exception",
|
|
49
49
|
exception_msg=exception_desc(),
|
|
50
50
|
other_msg="Can't create a list in memory of the data to be displayed as formatted text.",
|
|
51
|
-
)
|
|
51
|
+
) from e
|
|
52
52
|
rcols = range(len(colhdrs))
|
|
53
53
|
rrows = range(len(rows))
|
|
54
54
|
colwidths = [max(0, len(colhdrs[j]), *(len(as_ucode(rows[i][j])) for i in rrows)) for j in rcols]
|
|
@@ -76,13 +76,15 @@ def prettyprint_rowset(
|
|
|
76
76
|
ofile = EncodedFile(output_dest, _state.conf.output_encoding).open("w")
|
|
77
77
|
else:
|
|
78
78
|
ofile = ZipWriter(zipfile, output_dest, append)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
try:
|
|
80
|
+
if desc is not None:
|
|
81
|
+
ofile.write(f"{desc}\n")
|
|
82
|
+
for row in rows:
|
|
83
|
+
ln = f"{margin}{row}\n"
|
|
84
|
+
ofile.write(ln)
|
|
85
|
+
finally:
|
|
86
|
+
if output_dest != "stdout":
|
|
87
|
+
ofile.close()
|
|
86
88
|
return None
|
|
87
89
|
|
|
88
90
|
|
execsql/exporters/raw.py
CHANGED
|
@@ -28,16 +28,18 @@ def write_query_raw(
|
|
|
28
28
|
of = open(outfile, mode) # noqa: SIM115
|
|
29
29
|
else:
|
|
30
30
|
of = ZipWriter(zipfile, outfile, append)
|
|
31
|
-
|
|
32
|
-
for
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if isinstance(col, str):
|
|
37
|
-
of.write(bytes(col, db_encoding))
|
|
31
|
+
try:
|
|
32
|
+
for row in rowsource:
|
|
33
|
+
for col in row:
|
|
34
|
+
if isinstance(col, bytearray):
|
|
35
|
+
of.write(col)
|
|
38
36
|
else:
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
if isinstance(col, str):
|
|
38
|
+
of.write(bytes(col, db_encoding))
|
|
39
|
+
else:
|
|
40
|
+
of.write(bytes(str(col), db_encoding))
|
|
41
|
+
finally:
|
|
42
|
+
of.close()
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
def write_query_b64(outfile: str, rowsource: Any, append: bool = False, zipfile: str | None = None) -> None:
|
|
@@ -50,7 +52,9 @@ def write_query_b64(outfile: str, rowsource: Any, append: bool = False, zipfile:
|
|
|
50
52
|
of = open(outfile, mode) # noqa: SIM115
|
|
51
53
|
else:
|
|
52
54
|
of = ZipWriter(zipfile, outfile, append)
|
|
53
|
-
|
|
54
|
-
for
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
try:
|
|
56
|
+
for row in rowsource:
|
|
57
|
+
for col in row:
|
|
58
|
+
of.write(base64.standard_b64decode(col))
|
|
59
|
+
finally:
|
|
60
|
+
of.close()
|
execsql/exporters/sqlite.py
CHANGED
|
@@ -77,6 +77,6 @@ def write_query_to_sqlite(
|
|
|
77
77
|
hdrs, rows = db.select_rowsource(select_stmt)
|
|
78
78
|
except ErrInfo:
|
|
79
79
|
raise
|
|
80
|
-
except Exception:
|
|
81
|
-
raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
|
|
80
|
+
except Exception as e:
|
|
81
|
+
raise ErrInfo("db", select_stmt, exception_msg=exception_desc()) from e
|
|
82
82
|
export_sqlite(outfile, hdrs, rows, append, tablename)
|
execsql/exporters/templates.py
CHANGED
|
@@ -29,8 +29,11 @@ class StrTemplateReport:
|
|
|
29
29
|
from execsql.utils.fileio import EncodedFile
|
|
30
30
|
|
|
31
31
|
inf = EncodedFile(self.infname, conf.script_encoding)
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
fh = inf.open("r")
|
|
33
|
+
try:
|
|
34
|
+
self.template = string.Template(fh.read())
|
|
35
|
+
finally:
|
|
36
|
+
fh.close()
|
|
34
37
|
|
|
35
38
|
def __repr__(self) -> str:
|
|
36
39
|
return f"StrTemplateReport({self.infname})"
|
|
@@ -58,10 +61,12 @@ class StrTemplateReport:
|
|
|
58
61
|
ofile = EncodedFile(output_dest, conf.output_encoding).open("w")
|
|
59
62
|
else:
|
|
60
63
|
ofile = ZipWriter(zipfile, output_dest, append)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
try:
|
|
65
|
+
for dd in data_dict_rows:
|
|
66
|
+
ofile.write(self.template.safe_substitute(dd))
|
|
67
|
+
finally:
|
|
68
|
+
if output_dest != "stdout":
|
|
69
|
+
ofile.close()
|
|
65
70
|
|
|
66
71
|
|
|
67
72
|
class JinjaTemplateReport:
|
|
@@ -70,6 +75,7 @@ class JinjaTemplateReport:
|
|
|
70
75
|
global jinja2
|
|
71
76
|
try:
|
|
72
77
|
import jinja2
|
|
78
|
+
from jinja2.sandbox import SandboxedEnvironment
|
|
73
79
|
except ImportError:
|
|
74
80
|
fatal_error(
|
|
75
81
|
"The jinja2 library is required to produce reports with the Jinja2 templating system. See http://jinja.pocoo.org/",
|
|
@@ -79,11 +85,14 @@ class JinjaTemplateReport:
|
|
|
79
85
|
from execsql.utils.fileio import EncodedFile
|
|
80
86
|
|
|
81
87
|
inf = EncodedFile(template_file, conf.script_encoding)
|
|
82
|
-
|
|
83
|
-
|
|
88
|
+
fh = inf.open("r")
|
|
89
|
+
try:
|
|
90
|
+
self.template = SandboxedEnvironment().from_string(fh.read())
|
|
91
|
+
finally:
|
|
92
|
+
fh.close()
|
|
84
93
|
|
|
85
94
|
def __repr__(self) -> str:
|
|
86
|
-
return f"
|
|
95
|
+
return f"JinjaTemplateReport({self.infname})"
|
|
87
96
|
|
|
88
97
|
def write_report(
|
|
89
98
|
self,
|
|
@@ -111,13 +120,12 @@ class JinjaTemplateReport:
|
|
|
111
120
|
try:
|
|
112
121
|
ofile.write(self.template.render(headers=headers, datatable=data_dict_rows))
|
|
113
122
|
except jinja2.TemplateSyntaxError as e:
|
|
114
|
-
raise ErrInfo("error", other_msg=e.message + f" on template line {e.lineno}")
|
|
123
|
+
raise ErrInfo("error", other_msg=e.message + f" on template line {e.lineno}") from e
|
|
115
124
|
except jinja2.TemplateError as e:
|
|
116
|
-
raise ErrInfo("error", other_msg=f"Jinja2 template error ({e.message})")
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
ofile.close()
|
|
125
|
+
raise ErrInfo("error", other_msg=f"Jinja2 template error ({e.message})") from e
|
|
126
|
+
finally:
|
|
127
|
+
if output_dest != "stdout":
|
|
128
|
+
ofile.close()
|
|
121
129
|
|
|
122
130
|
|
|
123
131
|
def report_query(
|