execsql2 2.0.1__py3-none-any.whl → 2.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. execsql/cli.py +322 -108
  2. execsql/config.py +134 -114
  3. execsql/db/access.py +89 -65
  4. execsql/db/base.py +97 -68
  5. execsql/db/dsn.py +45 -29
  6. execsql/db/duckdb.py +4 -5
  7. execsql/db/factory.py +27 -27
  8. execsql/db/firebird.py +30 -18
  9. execsql/db/mysql.py +38 -14
  10. execsql/db/oracle.py +58 -33
  11. execsql/db/postgres.py +68 -28
  12. execsql/db/sqlite.py +36 -27
  13. execsql/db/sqlserver.py +45 -30
  14. execsql/exceptions.py +68 -64
  15. execsql/exporters/__init__.py +1 -1
  16. execsql/exporters/base.py +42 -17
  17. execsql/exporters/delimited.py +60 -59
  18. execsql/exporters/duckdb.py +8 -12
  19. execsql/exporters/feather.py +32 -24
  20. execsql/exporters/html.py +33 -30
  21. execsql/exporters/json.py +18 -17
  22. execsql/exporters/latex.py +11 -13
  23. execsql/exporters/ods.py +50 -46
  24. execsql/exporters/parquet.py +32 -0
  25. execsql/exporters/pretty.py +16 -15
  26. execsql/exporters/raw.py +9 -11
  27. execsql/exporters/sqlite.py +38 -38
  28. execsql/exporters/templates.py +15 -72
  29. execsql/exporters/values.py +13 -12
  30. execsql/exporters/xls.py +26 -26
  31. execsql/exporters/xml.py +12 -12
  32. execsql/exporters/zip.py +0 -3
  33. execsql/gui/__init__.py +2 -2
  34. execsql/gui/console.py +0 -1
  35. execsql/gui/desktop.py +6 -7
  36. execsql/gui/tui.py +8 -14
  37. execsql/importers/base.py +6 -9
  38. execsql/importers/csv.py +10 -17
  39. execsql/importers/feather.py +16 -22
  40. execsql/importers/ods.py +3 -4
  41. execsql/importers/xls.py +5 -6
  42. execsql/metacommands/__init__.py +8 -8
  43. execsql/metacommands/conditions.py +41 -33
  44. execsql/metacommands/connect.py +113 -99
  45. execsql/metacommands/control.py +38 -26
  46. execsql/metacommands/data.py +35 -33
  47. execsql/metacommands/debug.py +13 -9
  48. execsql/metacommands/io.py +288 -229
  49. execsql/metacommands/prompt.py +179 -157
  50. execsql/metacommands/script_ext.py +11 -9
  51. execsql/metacommands/system.py +44 -25
  52. execsql/models.py +9 -16
  53. execsql/parser.py +10 -10
  54. execsql/script.py +183 -157
  55. execsql/state.py +170 -208
  56. execsql/types.py +46 -81
  57. execsql/utils/auth.py +114 -14
  58. execsql/utils/crypto.py +31 -4
  59. execsql/utils/datetime.py +7 -7
  60. execsql/utils/errors.py +34 -29
  61. execsql/utils/fileio.py +90 -55
  62. execsql/utils/gui.py +22 -23
  63. execsql/utils/mail.py +15 -17
  64. execsql/utils/numeric.py +2 -3
  65. execsql/utils/regex.py +9 -12
  66. execsql/utils/strings.py +10 -12
  67. execsql/utils/timer.py +0 -2
  68. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/execsql.conf +1 -1
  69. execsql2-2.1.2.dist-info/METADATA +300 -0
  70. execsql2-2.1.2.dist-info/RECORD +96 -0
  71. execsql2-2.0.1.dist-info/METADATA +0 -406
  72. execsql2-2.0.1.dist-info/RECORD +0 -95
  73. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/READ_ME.rst +0 -0
  74. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/config_settings.sqlite +0 -0
  75. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
  76. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/make_config_db.sql +0 -0
  77. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_compare.sql +0 -0
  78. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_glossary.sql +0 -0
  79. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_upsert.sql +0 -0
  80. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_compare.sql +0 -0
  81. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_glossary.sql +0 -0
  82. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_upsert.sql +0 -0
  83. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/script_template.sql +0 -0
  84. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_compare.sql +0 -0
  85. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_glossary.sql +0 -0
  86. {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_upsert.sql +0 -0
  87. {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/WHEEL +0 -0
  88. {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/entry_points.txt +0 -0
  89. {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/licenses/LICENSE.txt +0 -0
  90. {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/licenses/NOTICE +0 -0
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+ from execsql.exceptions import ErrInfo
2
3
 
3
4
  """
4
5
  Data import and export metacommand handlers for execsql.
@@ -14,11 +15,17 @@ import math
14
15
  from typing import Any
15
16
 
16
17
  import execsql.state as _state
18
+ from execsql.parser import NumericParser
19
+ from execsql.script import current_script_line
20
+ from execsql.utils.crypto import Encrypt
21
+ from execsql.utils.errors import exception_desc, exit_now
22
+ from execsql.utils.gui import GUI_SELECTSUB, GuiSpec, enable_gui
23
+ from execsql.utils.strings import get_subvarset, unquoted
17
24
 
18
25
 
19
26
  def x_sub(**kwargs: Any) -> None:
20
27
  varname = kwargs["match"]
21
- subvarset, varname = _state.get_subvarset(varname, kwargs["metacommandline"])
28
+ subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
22
29
  subvarset.add_substitution(varname, kwargs["repl"])
23
30
  return None
24
31
 
@@ -26,21 +33,21 @@ def x_sub(**kwargs: Any) -> None:
26
33
  def x_sub_add(**kwargs: Any) -> None:
27
34
  varname = kwargs["match"]
28
35
  increment_expr = kwargs["increment"]
29
- subvarset, varname = _state.get_subvarset(varname, kwargs["metacommandline"])
30
- subvarset.increment_by(varname, _state.NumericParser(increment_expr).parse().eval())
36
+ subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
37
+ subvarset.increment_by(varname, NumericParser(increment_expr).parse().eval())
31
38
  return None
32
39
 
33
40
 
34
41
  def x_sub_append(**kwargs: Any) -> None:
35
42
  varname = kwargs["match"]
36
- subvarset, varname = _state.get_subvarset(varname, kwargs["metacommandline"])
43
+ subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
37
44
  subvarset.append_substitution(varname, kwargs["repl"])
38
45
  return None
39
46
 
40
47
 
41
48
  def x_sub_empty(**kwargs: Any) -> None:
42
49
  varname = kwargs["match"]
43
- subvarset, varname = _state.get_subvarset(varname, kwargs["metacommandline"])
50
+ subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
44
51
  subvarset.add_substitution(varname, "")
45
52
  return None
46
53
 
@@ -62,7 +69,7 @@ def x_sub_local(**kwargs: Any) -> None:
62
69
 
63
70
  def x_sub_tempfile(**kwargs: Any) -> None:
64
71
  varname = kwargs["match"]
65
- subvarset, varname = _state.get_subvarset(varname, kwargs["metacommandline"])
72
+ subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
66
73
  subvarset.add_substitution(varname, _state.tempfiles.new_temp_fn())
67
74
  return None
68
75
 
@@ -78,7 +85,7 @@ def x_sub_ini(**kwargs: Any) -> None:
78
85
  varsect = cp.items(ini_sect)
79
86
  for sub, repl in varsect:
80
87
  if not _state.subvars.var_name_ok(sub):
81
- raise _state.ErrInfo(type="error", other_msg=f"Invalid variable name in SUB_INI file: {sub}")
88
+ raise ErrInfo(type="error", other_msg=f"Invalid variable name in SUB_INI file: {sub}")
82
89
  _state.subvars.add_substitution(sub, repl)
83
90
 
84
91
 
@@ -94,15 +101,15 @@ def x_sub_querystring(**kwargs: Any) -> None:
94
101
 
95
102
  def x_sub_encrypt(**kwargs: Any) -> None:
96
103
  varname = kwargs["match"]
97
- subvarset, varname = _state.get_subvarset(varname, kwargs["metacommandline"])
98
- subvarset.add_substitution(varname, _state.Encrypt().encrypt(kwargs["plaintext"]))
104
+ subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
105
+ subvarset.add_substitution(varname, Encrypt().encrypt(kwargs["plaintext"]))
99
106
  return None
100
107
 
101
108
 
102
109
  def x_sub_decrypt(**kwargs: Any) -> None:
103
110
  varname = kwargs["match"]
104
- subvarset, varname = _state.get_subvarset(varname, kwargs["metacommandline"])
105
- subvarset.add_substitution(varname, _state.Encrypt().decrypt(kwargs["crypttext"]))
111
+ subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
112
+ subvarset.add_substitution(varname, Encrypt().decrypt(kwargs["crypttext"]))
106
113
  return None
107
114
 
108
115
 
@@ -110,21 +117,16 @@ def x_subdata(**kwargs: Any) -> None:
110
117
  varname = kwargs["match"]
111
118
  sql = f"select * from {kwargs['datasource']};"
112
119
  db = _state.dbs.current()
113
- script, line_no = _state.current_script_line()
114
- errmsg = (
115
- f"There are no data in {kwargs['datasource']} to use with the SUBDATA metacommand "
116
- f"(script {script}, line {line_no})."
117
- )
118
- subvarset, varname = _state.get_subvarset(varname, kwargs["metacommandline"])
120
+ subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
119
121
  subvarset.remove_substitution(varname)
120
122
  try:
121
- hdrs, rec = db.select_rowsource(sql)
122
- except _state.ErrInfo:
123
+ _, rec = db.select_rowsource(sql)
124
+ except ErrInfo:
123
125
  raise
124
126
  except Exception:
125
- raise _state.ErrInfo(
127
+ raise ErrInfo(
126
128
  type="exception",
127
- exception_msg=_state.exception_desc(),
129
+ exception_msg=exception_desc(),
128
130
  other_msg=f"Can't get headers and rows from {sql}.",
129
131
  )
130
132
  try:
@@ -144,19 +146,19 @@ def x_subdata(**kwargs: Any) -> None:
144
146
  def x_selectsub(**kwargs: Any) -> None:
145
147
  sql = f"select * from {kwargs['datasource']};"
146
148
  db = _state.dbs.current()
147
- script, line_no = _state.current_script_line()
149
+ script, line_no = current_script_line()
148
150
  nodatamsg = (
149
151
  f"There are no data in {kwargs['datasource']} to use with the SELECT_SUB metacommand "
150
152
  f"(script {script}, line {line_no})."
151
153
  )
152
154
  try:
153
155
  hdrs, rec = db.select_rowsource(sql)
154
- except _state.ErrInfo:
156
+ except ErrInfo:
155
157
  raise
156
158
  except Exception:
157
- raise _state.ErrInfo(
159
+ raise ErrInfo(
158
160
  type="exception",
159
- exception_msg=_state.exception_desc(),
161
+ exception_msg=exception_desc(),
160
162
  other_msg=f"Can't get headers and rows from {sql}.",
161
163
  )
162
164
  for subvar in hdrs:
@@ -170,7 +172,7 @@ def x_selectsub(**kwargs: Any) -> None:
170
172
  except StopIteration:
171
173
  row1 = None
172
174
  except Exception:
173
- raise _state.ErrInfo(type="exception", exception_msg=_state.exception_desc(), other_msg=nodatamsg)
175
+ raise ErrInfo(type="exception", exception_msg=exception_desc(), other_msg=nodatamsg)
174
176
  if row1:
175
177
  for i, item in enumerate(row1):
176
178
  if item is None:
@@ -194,20 +196,20 @@ def x_prompt_selectsub(**kwargs: Any) -> None:
194
196
  table = kwargs["table"]
195
197
  msg = kwargs["msg"]
196
198
  cont = kwargs["cont"]
197
- help_url = _state.unquoted(kwargs["help"])
199
+ help_url = unquoted(kwargs["help"])
198
200
  db = _state.dbs.current()
199
201
  sq_name = db.schema_qualified_table_name(schema, table)
200
202
  sql = f"select * from {sq_name};"
201
203
  hdrs, rows = db.select_data(sql)
202
204
  if len(rows) == 0:
203
- raise _state.ErrInfo(type="error", other_msg=f"The table {sq_name} has no rows to display.")
205
+ raise ErrInfo(type="error", other_msg=f"The table {sq_name} has no rows to display.")
204
206
  for subvar in hdrs:
205
207
  subvar = "@" + subvar
206
208
  _state.subvars.remove_substitution(subvar)
207
209
  btns = [("OK", 1, "O")]
208
210
  if cont:
209
211
  btns.append(("Continue", 2, "<Return>"))
210
- _state.enable_gui()
212
+ enable_gui()
211
213
  return_queue = _queue.Queue()
212
214
  gui_args = {
213
215
  "title": "Select data",
@@ -217,14 +219,14 @@ def x_prompt_selectsub(**kwargs: Any) -> None:
217
219
  "rowset": rows,
218
220
  "help_url": help_url,
219
221
  }
220
- _state.gui_manager_queue.put(_state.GuiSpec(_state.GUI_SELECTSUB, gui_args, return_queue))
222
+ _state.gui_manager_queue.put(GuiSpec(GUI_SELECTSUB, gui_args, return_queue))
221
223
  user_response = return_queue.get(block=True)
222
224
  btn_val = user_response["button"]
223
225
  return_val = user_response["return_value"]
224
226
  selected_row = None
225
227
  if btn_val and btn_val == 1:
226
228
  selected_row = rows[int(return_val[0])]
227
- script, line_no = _state.current_script_line()
229
+ script, line_no = current_script_line()
228
230
  if btn_val is None or (btn_val == 1 and selected_row is None):
229
231
  if _state.status.cancel_halt:
230
232
  _state.exec_log.log_exit_halt(
@@ -232,7 +234,7 @@ def x_prompt_selectsub(**kwargs: Any) -> None:
232
234
  line_no,
233
235
  msg=f"Halted from prompt for row of {sq_name} on line {line_no} of {script}",
234
236
  )
235
- _state.exit_now(2, None)
237
+ exit_now(2, None)
236
238
  else:
237
239
  if btn_val == 1:
238
240
  for i, item in enumerate(selected_row):
@@ -347,7 +349,7 @@ def x_reset_counters(**kwargs: Any) -> None:
347
349
  def x_set_counter(**kwargs: Any) -> None:
348
350
  ctr_no = int(kwargs["counter_no"])
349
351
  ctr_expr = kwargs["value"]
350
- _state.counters.set_counter(ctr_no, int(math.floor(_state.NumericParser(ctr_expr).parse().eval())))
352
+ _state.counters.set_counter(ctr_no, int(math.floor(NumericParser(ctr_expr).parse().eval())))
351
353
 
352
354
 
353
355
  def x_max_int(**kwargs: Any) -> None:
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+ from execsql.utils.errors import fatal_error
2
3
 
3
4
  """
4
5
  Debug metacommand handlers for execsql.
@@ -11,6 +12,7 @@ metacommand list to the log/console for troubleshooting.
11
12
  from typing import Any
12
13
 
13
14
  import execsql.state as _state
15
+ from execsql.utils.fileio import EncodedFile, filewriter_open_as_new, filewriter_write
14
16
 
15
17
 
16
18
  def x_debug_write_metacommands(**kwargs: Any) -> None:
@@ -18,7 +20,7 @@ def x_debug_write_metacommands(**kwargs: Any) -> None:
18
20
  if output_dest is None or output_dest == "stdout":
19
21
  ofile = _state.output
20
22
  else:
21
- ofile = _state.EncodedFile(output_dest, _state.conf.output_encoding).open("w")
23
+ ofile = EncodedFile(output_dest, _state.conf.output_encoding).open("w")
22
24
  for m in _state.metacommandlist:
23
25
  ofile.write(f"({m.hitcount}) {m.rx.pattern}\n")
24
26
 
@@ -38,7 +40,9 @@ def x_debug_iflevels(**kwargs: Any) -> None:
38
40
  if len(_state.if_stack.if_levels) == 0:
39
41
  _state.output.write("If levels: None\n")
40
42
  else:
41
- _state.output.write("If levels: [%s]\n" % ",".join([str(tf.tf_value) for tf in _state.if_stack.if_levels]))
43
+ _state.output.write(
44
+ "If levels: [{}]\n".format(",".join([str(tf.tf_value) for tf in _state.if_stack.if_levels])),
45
+ )
42
46
  return None
43
47
 
44
48
 
@@ -46,7 +50,7 @@ def x_debug_write_odbc_drivers(**kwargs: Any) -> None:
46
50
  try:
47
51
  import pyodbc
48
52
  except Exception:
49
- _state.fatal_error("The pyodbc module is required.")
53
+ fatal_error("The pyodbc module is required.")
50
54
  output_dest = kwargs["filename"]
51
55
  append = kwargs["append"]
52
56
 
@@ -55,8 +59,8 @@ def x_debug_write_odbc_drivers(**kwargs: Any) -> None:
55
59
  _state.output.write(txt)
56
60
  else:
57
61
  if not append:
58
- _state.filewriter_open_as_new(output_dest)
59
- _state.filewriter_write(output_dest, txt)
62
+ filewriter_open_as_new(output_dest)
63
+ filewriter_write(output_dest, txt)
60
64
 
61
65
  for d in pyodbc.drivers():
62
66
  write(f"{d}\n")
@@ -126,13 +130,13 @@ def x_debug_write_subvars(**kwargs: Any) -> None:
126
130
  user = kwargs["user"]
127
131
  local = kwargs["local"]
128
132
  if output_dest is not None and output_dest != "stdout" and append is None:
129
- _state.filewriter_open_as_new(output_dest)
133
+ filewriter_open_as_new(output_dest)
130
134
 
131
135
  def write(txt: str) -> None:
132
136
  if output_dest is None or output_dest == "stdout":
133
137
  _state.output.write(txt)
134
138
  else:
135
- _state.filewriter_write(output_dest, txt)
139
+ filewriter_write(output_dest, txt)
136
140
 
137
141
  for s in _state.commandliststack[-1].localvars.substitutions:
138
142
  write(f"Substitution [{s[0]}] = [{s[1]}]\n")
@@ -153,13 +157,13 @@ def x_debug_write_config(**kwargs: Any) -> None:
153
157
  f"Config; GUI level = {conf.gui_level}",
154
158
  ]
155
159
  if output_dest is not None and output_dest != "stdout" and append is None:
156
- _state.filewriter_open_as_new(output_dest)
160
+ filewriter_open_as_new(output_dest)
157
161
 
158
162
  def write(txt: str) -> None:
159
163
  if output_dest is None or output_dest == "stdout":
160
164
  _state.output.write(txt)
161
165
  else:
162
- _state.filewriter_write(output_dest, txt)
166
+ filewriter_write(output_dest, txt)
163
167
 
164
168
  for line in lines:
165
169
  write(f"{line}\n")