meerschaum 2.9.5__py3-none-any.whl → 3.0.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 (200) hide show
  1. meerschaum/__init__.py +5 -2
  2. meerschaum/_internal/__init__.py +1 -0
  3. meerschaum/_internal/arguments/_parse_arguments.py +4 -4
  4. meerschaum/_internal/arguments/_parser.py +33 -4
  5. meerschaum/_internal/cli/__init__.py +6 -0
  6. meerschaum/_internal/cli/daemons.py +103 -0
  7. meerschaum/_internal/cli/entry.py +220 -0
  8. meerschaum/_internal/cli/workers.py +435 -0
  9. meerschaum/_internal/docs/index.py +48 -2
  10. meerschaum/_internal/entry.py +50 -14
  11. meerschaum/_internal/shell/Shell.py +121 -29
  12. meerschaum/_internal/shell/__init__.py +4 -1
  13. meerschaum/_internal/static.py +359 -0
  14. meerschaum/_internal/term/TermPageHandler.py +1 -2
  15. meerschaum/_internal/term/__init__.py +40 -6
  16. meerschaum/_internal/term/tools.py +33 -8
  17. meerschaum/actions/__init__.py +6 -4
  18. meerschaum/actions/api.py +53 -13
  19. meerschaum/actions/attach.py +1 -0
  20. meerschaum/actions/bootstrap.py +8 -8
  21. meerschaum/actions/delete.py +4 -2
  22. meerschaum/actions/edit.py +171 -25
  23. meerschaum/actions/login.py +8 -8
  24. meerschaum/actions/register.py +143 -6
  25. meerschaum/actions/reload.py +22 -5
  26. meerschaum/actions/restart.py +14 -0
  27. meerschaum/actions/show.py +184 -31
  28. meerschaum/actions/start.py +166 -17
  29. meerschaum/actions/stop.py +38 -2
  30. meerschaum/actions/sync.py +7 -2
  31. meerschaum/actions/tag.py +9 -8
  32. meerschaum/actions/verify.py +5 -8
  33. meerschaum/api/__init__.py +45 -15
  34. meerschaum/api/_events.py +46 -4
  35. meerschaum/api/_oauth2.py +162 -9
  36. meerschaum/api/_tokens.py +102 -0
  37. meerschaum/api/dash/__init__.py +0 -3
  38. meerschaum/api/dash/callbacks/__init__.py +1 -0
  39. meerschaum/api/dash/callbacks/custom.py +4 -3
  40. meerschaum/api/dash/callbacks/dashboard.py +198 -118
  41. meerschaum/api/dash/callbacks/jobs.py +14 -7
  42. meerschaum/api/dash/callbacks/login.py +10 -1
  43. meerschaum/api/dash/callbacks/pipes.py +194 -14
  44. meerschaum/api/dash/callbacks/plugins.py +0 -1
  45. meerschaum/api/dash/callbacks/register.py +10 -3
  46. meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
  47. meerschaum/api/dash/callbacks/tokens.py +389 -0
  48. meerschaum/api/dash/components.py +36 -15
  49. meerschaum/api/dash/jobs.py +1 -1
  50. meerschaum/api/dash/keys.py +35 -93
  51. meerschaum/api/dash/pages/__init__.py +2 -1
  52. meerschaum/api/dash/pages/dashboard.py +1 -20
  53. meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
  54. meerschaum/api/dash/pages/login.py +2 -2
  55. meerschaum/api/dash/pages/pipes.py +16 -5
  56. meerschaum/api/dash/pages/settings/password_reset.py +1 -1
  57. meerschaum/api/dash/pages/tokens.py +53 -0
  58. meerschaum/api/dash/pipes.py +382 -95
  59. meerschaum/api/dash/sessions.py +12 -0
  60. meerschaum/api/dash/tokens.py +603 -0
  61. meerschaum/api/dash/websockets.py +1 -1
  62. meerschaum/api/dash/webterm.py +18 -6
  63. meerschaum/api/models/__init__.py +23 -3
  64. meerschaum/api/models/_actions.py +22 -0
  65. meerschaum/api/models/_pipes.py +91 -7
  66. meerschaum/api/models/_tokens.py +81 -0
  67. meerschaum/api/resources/static/js/terminado.js +3 -0
  68. meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
  69. meerschaum/api/resources/templates/termpage.html +13 -0
  70. meerschaum/api/routes/__init__.py +1 -0
  71. meerschaum/api/routes/_actions.py +3 -4
  72. meerschaum/api/routes/_connectors.py +3 -7
  73. meerschaum/api/routes/_jobs.py +26 -35
  74. meerschaum/api/routes/_login.py +120 -15
  75. meerschaum/api/routes/_misc.py +5 -10
  76. meerschaum/api/routes/_pipes.py +178 -143
  77. meerschaum/api/routes/_plugins.py +38 -28
  78. meerschaum/api/routes/_tokens.py +236 -0
  79. meerschaum/api/routes/_users.py +47 -35
  80. meerschaum/api/routes/_version.py +3 -3
  81. meerschaum/api/routes/_webterm.py +3 -3
  82. meerschaum/config/__init__.py +100 -30
  83. meerschaum/config/_default.py +132 -64
  84. meerschaum/config/_edit.py +38 -32
  85. meerschaum/config/_formatting.py +2 -0
  86. meerschaum/config/_patch.py +10 -8
  87. meerschaum/config/_paths.py +133 -13
  88. meerschaum/config/_read_config.py +87 -36
  89. meerschaum/config/_sync.py +6 -3
  90. meerschaum/config/_version.py +1 -1
  91. meerschaum/config/environment.py +262 -0
  92. meerschaum/config/stack/__init__.py +37 -15
  93. meerschaum/config/static.py +18 -0
  94. meerschaum/connectors/_Connector.py +11 -6
  95. meerschaum/connectors/__init__.py +41 -22
  96. meerschaum/connectors/api/_APIConnector.py +34 -6
  97. meerschaum/connectors/api/_actions.py +2 -2
  98. meerschaum/connectors/api/_jobs.py +12 -1
  99. meerschaum/connectors/api/_login.py +33 -7
  100. meerschaum/connectors/api/_misc.py +2 -2
  101. meerschaum/connectors/api/_pipes.py +23 -32
  102. meerschaum/connectors/api/_plugins.py +2 -2
  103. meerschaum/connectors/api/_request.py +1 -1
  104. meerschaum/connectors/api/_tokens.py +146 -0
  105. meerschaum/connectors/api/_users.py +70 -58
  106. meerschaum/connectors/instance/_InstanceConnector.py +83 -0
  107. meerschaum/connectors/instance/__init__.py +10 -0
  108. meerschaum/connectors/instance/_pipes.py +442 -0
  109. meerschaum/connectors/instance/_plugins.py +159 -0
  110. meerschaum/connectors/instance/_tokens.py +317 -0
  111. meerschaum/connectors/instance/_users.py +188 -0
  112. meerschaum/connectors/parse.py +5 -2
  113. meerschaum/connectors/sql/_SQLConnector.py +22 -5
  114. meerschaum/connectors/sql/_cli.py +12 -11
  115. meerschaum/connectors/sql/_create_engine.py +12 -168
  116. meerschaum/connectors/sql/_fetch.py +2 -18
  117. meerschaum/connectors/sql/_pipes.py +295 -278
  118. meerschaum/connectors/sql/_plugins.py +29 -0
  119. meerschaum/connectors/sql/_sql.py +46 -21
  120. meerschaum/connectors/sql/_users.py +36 -2
  121. meerschaum/connectors/sql/tables/__init__.py +254 -122
  122. meerschaum/connectors/valkey/_ValkeyConnector.py +5 -7
  123. meerschaum/connectors/valkey/_pipes.py +60 -31
  124. meerschaum/connectors/valkey/_plugins.py +2 -26
  125. meerschaum/core/Pipe/__init__.py +115 -85
  126. meerschaum/core/Pipe/_attributes.py +425 -124
  127. meerschaum/core/Pipe/_bootstrap.py +54 -24
  128. meerschaum/core/Pipe/_cache.py +555 -0
  129. meerschaum/core/Pipe/_clear.py +0 -11
  130. meerschaum/core/Pipe/_data.py +96 -68
  131. meerschaum/core/Pipe/_deduplicate.py +0 -13
  132. meerschaum/core/Pipe/_delete.py +12 -21
  133. meerschaum/core/Pipe/_drop.py +11 -23
  134. meerschaum/core/Pipe/_dtypes.py +49 -19
  135. meerschaum/core/Pipe/_edit.py +14 -4
  136. meerschaum/core/Pipe/_fetch.py +1 -1
  137. meerschaum/core/Pipe/_index.py +8 -14
  138. meerschaum/core/Pipe/_show.py +5 -5
  139. meerschaum/core/Pipe/_sync.py +123 -204
  140. meerschaum/core/Pipe/_verify.py +4 -4
  141. meerschaum/{plugins → core/Plugin}/_Plugin.py +16 -12
  142. meerschaum/core/Plugin/__init__.py +1 -1
  143. meerschaum/core/Token/_Token.py +220 -0
  144. meerschaum/core/Token/__init__.py +12 -0
  145. meerschaum/core/User/_User.py +35 -10
  146. meerschaum/core/User/__init__.py +9 -1
  147. meerschaum/core/__init__.py +1 -0
  148. meerschaum/jobs/_Executor.py +88 -4
  149. meerschaum/jobs/_Job.py +149 -38
  150. meerschaum/jobs/__init__.py +3 -2
  151. meerschaum/jobs/systemd.py +8 -3
  152. meerschaum/models/__init__.py +35 -0
  153. meerschaum/models/pipes.py +247 -0
  154. meerschaum/models/tokens.py +38 -0
  155. meerschaum/models/users.py +26 -0
  156. meerschaum/plugins/__init__.py +301 -88
  157. meerschaum/plugins/bootstrap.py +510 -4
  158. meerschaum/utils/_get_pipes.py +97 -30
  159. meerschaum/utils/daemon/Daemon.py +199 -43
  160. meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
  161. meerschaum/utils/daemon/RotatingFile.py +63 -36
  162. meerschaum/utils/daemon/StdinFile.py +53 -13
  163. meerschaum/utils/daemon/__init__.py +47 -6
  164. meerschaum/utils/daemon/_names.py +6 -3
  165. meerschaum/utils/dataframe.py +479 -81
  166. meerschaum/utils/debug.py +49 -19
  167. meerschaum/utils/dtypes/__init__.py +476 -34
  168. meerschaum/utils/dtypes/sql.py +369 -29
  169. meerschaum/utils/formatting/__init__.py +5 -2
  170. meerschaum/utils/formatting/_jobs.py +1 -1
  171. meerschaum/utils/formatting/_pipes.py +52 -50
  172. meerschaum/utils/formatting/_pprint.py +1 -0
  173. meerschaum/utils/formatting/_shell.py +44 -18
  174. meerschaum/utils/misc.py +268 -186
  175. meerschaum/utils/packages/__init__.py +25 -40
  176. meerschaum/utils/packages/_packages.py +42 -34
  177. meerschaum/utils/pipes.py +213 -0
  178. meerschaum/utils/process.py +2 -2
  179. meerschaum/utils/prompt.py +175 -144
  180. meerschaum/utils/schedule.py +2 -1
  181. meerschaum/utils/sql.py +134 -47
  182. meerschaum/utils/threading.py +42 -0
  183. meerschaum/utils/typing.py +1 -4
  184. meerschaum/utils/venv/_Venv.py +2 -2
  185. meerschaum/utils/venv/__init__.py +7 -7
  186. meerschaum/utils/warnings.py +19 -13
  187. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/METADATA +94 -96
  188. meerschaum-3.0.0.dist-info/RECORD +289 -0
  189. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/WHEEL +1 -1
  190. meerschaum-3.0.0.dist-info/licenses/NOTICE +2 -0
  191. meerschaum/api/models/_interfaces.py +0 -15
  192. meerschaum/api/models/_locations.py +0 -15
  193. meerschaum/api/models/_metrics.py +0 -15
  194. meerschaum/config/_environment.py +0 -145
  195. meerschaum/config/static/__init__.py +0 -186
  196. meerschaum-2.9.5.dist-info/RECORD +0 -263
  197. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/entry_points.txt +0 -0
  198. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/licenses/LICENSE +0 -0
  199. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/top_level.txt +0 -0
  200. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/zip-safe +0 -0
meerschaum/utils/sql.py CHANGED
@@ -41,13 +41,33 @@ version_queries = {
41
41
  }
42
42
  SKIP_IF_EXISTS_FLAVORS = {'mssql', 'oracle'}
43
43
  DROP_IF_EXISTS_FLAVORS = {
44
- 'timescaledb', 'postgresql', 'postgis', 'citus', 'mssql', 'mysql', 'mariadb', 'sqlite',
44
+ 'timescaledb',
45
+ 'timescaledb-ha',
46
+ 'postgresql',
47
+ 'postgis',
48
+ 'citus',
49
+ 'mssql',
50
+ 'mysql',
51
+ 'mariadb',
52
+ 'sqlite',
45
53
  }
46
54
  DROP_INDEX_IF_EXISTS_FLAVORS = {
47
- 'mssql', 'timescaledb', 'postgresql', 'postgis', 'sqlite', 'citus',
55
+ 'mssql',
56
+ 'timescaledb',
57
+ 'timescaledb-ha',
58
+ 'postgresql',
59
+ 'postgis',
60
+ 'sqlite',
61
+ 'citus',
48
62
  }
49
63
  SKIP_AUTO_INCREMENT_FLAVORS = {'citus', 'duckdb'}
50
- COALESCE_UNIQUE_INDEX_FLAVORS = {'timescaledb', 'postgresql', 'postgis', 'citus'}
64
+ COALESCE_UNIQUE_INDEX_FLAVORS = {
65
+ 'timescaledb',
66
+ 'timescaledb-ha',
67
+ 'postgresql',
68
+ 'postgis',
69
+ 'citus',
70
+ }
51
71
  UPDATE_QUERIES = {
52
72
  'default': """
53
73
  UPDATE {target_table_name} AS f
@@ -67,6 +87,12 @@ UPDATE_QUERIES = {
67
87
  FROM {patch_table_name}
68
88
  ON CONFLICT ({join_cols_str}) DO {update_or_nothing} {sets_subquery_none_excluded}
69
89
  """,
90
+ 'timescaledb-ha-upsert': """
91
+ INSERT INTO {target_table_name} ({patch_cols_str})
92
+ SELECT {patch_cols_str}
93
+ FROM {patch_table_name}
94
+ ON CONFLICT ({join_cols_str}) DO {update_or_nothing} {sets_subquery_none_excluded}
95
+ """,
70
96
  'postgresql-upsert': """
71
97
  INSERT INTO {target_table_name} ({patch_cols_str})
72
98
  SELECT {patch_cols_str}
@@ -233,7 +259,7 @@ columns_types_queries = {
233
259
  DATA_TYPE AS [type],
234
260
  NUMERIC_PRECISION AS [numeric_precision],
235
261
  NUMERIC_SCALE AS [numeric_scale]
236
- FROM {db_prefix}INFORMATION_SCHEMA.COLUMNS
262
+ FROM {db_prefix}INFORMATION_SCHEMA.COLUMNS WITH (NOLOCK)
237
263
  WHERE TABLE_NAME IN (
238
264
  '{table}',
239
265
  '{table_trunc}'
@@ -286,6 +312,7 @@ columns_types_queries = {
286
312
  }
287
313
  hypertable_queries = {
288
314
  'timescaledb': 'SELECT hypertable_size(\'{table_name}\')',
315
+ 'timescaledb-ha': 'SELECT hypertable_size(\'{table_name}\')',
289
316
  'citus': 'SELECT citus_table_size(\'{table_name}\')',
290
317
  }
291
318
  columns_indices_queries = {
@@ -377,18 +404,18 @@ columns_indices_queries = {
377
404
  ELSE CAST(0 AS BIT)
378
405
  END AS [clustered]
379
406
  FROM
380
- sys.schemas s
381
- INNER JOIN sys.tables t
407
+ sys.schemas s WITH (NOLOCK)
408
+ INNER JOIN sys.tables t WITH (NOLOCK)
382
409
  ON s.schema_id = t.schema_id
383
- INNER JOIN sys.indexes i
410
+ INNER JOIN sys.indexes i WITH (NOLOCK)
384
411
  ON t.object_id = i.object_id
385
- INNER JOIN sys.index_columns ic
412
+ INNER JOIN sys.index_columns ic WITH (NOLOCK)
386
413
  ON i.object_id = ic.object_id
387
414
  AND i.index_id = ic.index_id
388
- INNER JOIN sys.columns c
415
+ INNER JOIN sys.columns c WITH (NOLOCK)
389
416
  ON ic.object_id = c.object_id
390
417
  AND ic.column_id = c.column_id
391
- LEFT JOIN sys.key_constraints kc
418
+ LEFT JOIN sys.key_constraints kc WITH (NOLOCK)
392
419
  ON kc.parent_object_id = i.object_id
393
420
  AND kc.type = 'PK'
394
421
  AND kc.name = i.name
@@ -483,49 +510,83 @@ reset_autoincrement_queries: Dict[str, Union[str, List[str]]] = {
483
510
  ),
484
511
  }
485
512
  table_wrappers = {
486
- 'default' : ('"', '"'),
487
- 'timescaledb': ('"', '"'),
488
- 'citus' : ('"', '"'),
489
- 'duckdb' : ('"', '"'),
490
- 'postgresql' : ('"', '"'),
491
- 'postgis' : ('"', '"'),
492
- 'sqlite' : ('"', '"'),
493
- 'mysql' : ('`', '`'),
494
- 'mariadb' : ('`', '`'),
495
- 'mssql' : ('[', ']'),
496
- 'cockroachdb': ('"', '"'),
497
- 'oracle' : ('"', '"'),
513
+ 'default' : ('"', '"'),
514
+ 'timescaledb' : ('"', '"'),
515
+ 'timescaledb-ha': ('"', '"'),
516
+ 'citus' : ('"', '"'),
517
+ 'duckdb' : ('"', '"'),
518
+ 'postgresql' : ('"', '"'),
519
+ 'postgis' : ('"', '"'),
520
+ 'sqlite' : ('"', '"'),
521
+ 'mysql' : ('`', '`'),
522
+ 'mariadb' : ('`', '`'),
523
+ 'mssql' : ('[', ']'),
524
+ 'cockroachdb' : ('"', '"'),
525
+ 'oracle' : ('"', '"'),
498
526
  }
499
527
  max_name_lens = {
500
- 'default' : 64,
501
- 'mssql' : 128,
502
- 'oracle' : 30,
503
- 'postgresql' : 64,
504
- 'postgis' : 64,
505
- 'timescaledb': 64,
506
- 'citus' : 64,
507
- 'cockroachdb': 64,
508
- 'sqlite' : 1024, ### Probably more, but 1024 seems more than reasonable.
509
- 'mysql' : 64,
510
- 'mariadb' : 64,
528
+ 'default' : 64,
529
+ 'mssql' : 128,
530
+ 'oracle' : 30,
531
+ 'postgresql' : 64,
532
+ 'postgis' : 64,
533
+ 'timescaledb' : 64,
534
+ 'timescaledb-ha': 64,
535
+ 'citus' : 64,
536
+ 'cockroachdb' : 64,
537
+ 'sqlite' : 1024, ### Probably more, but 1024 seems more than reasonable.
538
+ 'mysql' : 64,
539
+ 'mariadb' : 64,
540
+ }
541
+ json_flavors = {
542
+ 'postgresql',
543
+ 'postgis',
544
+ 'timescaledb',
545
+ 'timescaledb-ha',
546
+ 'citus',
547
+ 'cockroachdb',
548
+ }
549
+ NO_SCHEMA_FLAVORS = {
550
+ 'oracle',
551
+ 'sqlite',
552
+ 'mysql',
553
+ 'mariadb',
554
+ 'duckdb',
511
555
  }
512
- json_flavors = {'postgresql', 'postgis', 'timescaledb', 'citus', 'cockroachdb'}
513
- NO_SCHEMA_FLAVORS = {'oracle', 'sqlite', 'mysql', 'mariadb', 'duckdb'}
514
556
  DEFAULT_SCHEMA_FLAVORS = {
515
557
  'postgresql': 'public',
516
558
  'postgis': 'public',
517
559
  'timescaledb': 'public',
560
+ 'timescaledb-ha': 'public',
518
561
  'citus': 'public',
519
562
  'cockroachdb': 'public',
520
563
  'mysql': 'mysql',
521
564
  'mariadb': 'mysql',
522
565
  'mssql': 'dbo',
523
566
  }
524
- OMIT_NULLSFIRST_FLAVORS = {'mariadb', 'mysql', 'mssql'}
567
+ OMIT_NULLSFIRST_FLAVORS = {
568
+ 'mariadb',
569
+ 'mysql',
570
+ 'mssql',
571
+ }
525
572
 
526
- SINGLE_ALTER_TABLE_FLAVORS = {'duckdb', 'sqlite', 'mssql', 'oracle'}
527
- NO_CTE_FLAVORS = {'mysql', 'mariadb'}
528
- NO_SELECT_INTO_FLAVORS = {'sqlite', 'oracle', 'mysql', 'mariadb', 'duckdb'}
573
+ SINGLE_ALTER_TABLE_FLAVORS = {
574
+ 'duckdb',
575
+ 'sqlite',
576
+ 'mssql',
577
+ 'oracle',
578
+ }
579
+ NO_CTE_FLAVORS = {
580
+ 'mysql',
581
+ 'mariadb',
582
+ }
583
+ NO_SELECT_INTO_FLAVORS = {
584
+ 'sqlite',
585
+ 'oracle',
586
+ 'mysql',
587
+ 'mariadb',
588
+ 'duckdb',
589
+ }
529
590
 
530
591
 
531
592
  def clean(substring: str) -> None:
@@ -560,6 +621,7 @@ def dateadd_str(
560
621
  - `'postgresql'`
561
622
  - `'postgis'`
562
623
  - `'timescaledb'`
624
+ - `'timescaledb-ha'`
563
625
  - `'citus'`
564
626
  - `'cockroachdb'`
565
627
  - `'duckdb'`
@@ -663,7 +725,14 @@ def dateadd_str(
663
725
  )
664
726
 
665
727
  da = ""
666
- if flavor in ('postgresql', 'postgis', 'timescaledb', 'cockroachdb', 'citus'):
728
+ if flavor in (
729
+ 'postgresql',
730
+ 'postgis',
731
+ 'timescaledb',
732
+ 'timescaledb-ha',
733
+ 'cockroachdb',
734
+ 'citus',
735
+ ):
667
736
  begin = (
668
737
  f"CAST({begin} AS {db_type})" if begin != 'now'
669
738
  else f"CAST(NOW() AT TIME ZONE 'utc' AS {db_type})"
@@ -831,7 +900,7 @@ def sql_item_name(item: str, flavor: str, schema: Optional[str] = None) -> str:
831
900
  ### NOTE: System-reserved words must be quoted.
832
901
  if truncated_item.lower() in (
833
902
  'float', 'varchar', 'nvarchar', 'clob',
834
- 'boolean', 'integer', 'table', 'row',
903
+ 'boolean', 'integer', 'table', 'row', 'date',
835
904
  ):
836
905
  wrappers = ('"', '"')
837
906
  else:
@@ -969,7 +1038,7 @@ def build_where(
969
1038
  ```
970
1039
  """
971
1040
  import json
972
- from meerschaum.config.static import STATIC_CONFIG
1041
+ from meerschaum._internal.static import STATIC_CONFIG
973
1042
  from meerschaum.utils.warnings import warn
974
1043
  from meerschaum.utils.dtypes import value_is_null, none_if_null
975
1044
  negation_prefix = STATIC_CONFIG['system']['fetch_pipes_keys']['negation_prefix']
@@ -1517,6 +1586,8 @@ def get_update_queries(
1517
1586
  datetime_col: Optional[str] = None,
1518
1587
  schema: Optional[str] = None,
1519
1588
  patch_schema: Optional[str] = None,
1589
+ target_cols_types: Optional[Dict[str, str]] = None,
1590
+ patch_cols_types: Optional[Dict[str, str]] = None,
1520
1591
  identity_insert: bool = False,
1521
1592
  null_indices: bool = True,
1522
1593
  cast_columns: bool = True,
@@ -1557,6 +1628,14 @@ def get_update_queries(
1557
1628
  If provided, use this schema when quoting the patch table.
1558
1629
  Defaults to `schema`.
1559
1630
 
1631
+ target_cols_types: Optional[Dict[str, Any]], default None
1632
+ If provided, use these as the columns-types dictionary for the target table.
1633
+ Default will infer from the database context.
1634
+
1635
+ patch_cols_types: Optional[Dict[str, Any]], default None
1636
+ If provided, use these as the columns-types dictionary for the target table.
1637
+ Default will infer from the database context.
1638
+
1560
1639
  identity_insert: bool, default False
1561
1640
  If `True`, include `SET IDENTITY_INSERT` queries before and after the update queries.
1562
1641
  Only applies for MSSQL upserts.
@@ -1603,14 +1682,14 @@ def get_update_queries(
1603
1682
  flavor=flavor,
1604
1683
  schema=schema,
1605
1684
  debug=debug,
1606
- )
1685
+ ) if not target_cols_types else target_cols_types
1607
1686
  patch_table_columns = get_table_cols_types(
1608
1687
  patch,
1609
1688
  connectable,
1610
1689
  flavor=flavor,
1611
1690
  schema=patch_schema,
1612
1691
  debug=debug,
1613
- )
1692
+ ) if not patch_cols_types else patch_cols_types
1614
1693
 
1615
1694
  patch_cols_str = ', '.join(
1616
1695
  [
@@ -2112,7 +2191,11 @@ def _get_create_table_query_from_dtypes(
2112
2191
  )
2113
2192
  elif flavor == 'oracle':
2114
2193
  query += f"\n {col_name} {col_db_type} {auto_increment_str} PRIMARY KEY,"
2115
- elif flavor == 'timescaledb' and datetime_column and datetime_column != primary_key:
2194
+ elif (
2195
+ flavor in ('timescaledb', 'timescaledb-ha')
2196
+ and datetime_column
2197
+ and datetime_column != primary_key
2198
+ ):
2116
2199
  query += f"\n {col_name} {col_db_type}{auto_increment_str} NOT NULL,"
2117
2200
  elif flavor == 'mssql':
2118
2201
  query += f"\n {col_name} {col_db_type}{auto_increment_str} NOT NULL,"
@@ -2125,7 +2208,7 @@ def _get_create_table_query_from_dtypes(
2125
2208
  col_name = sql_item_name(col, schema=None, flavor=flavor)
2126
2209
  query += f"\n {col_name} {db_type},"
2127
2210
  if (
2128
- flavor == 'timescaledb'
2211
+ flavor in ('timescaledb', 'timescaledb-ha')
2129
2212
  and datetime_column
2130
2213
  and primary_key
2131
2214
  and datetime_column != primary_key
@@ -2248,7 +2331,11 @@ def _get_create_table_query_from_cte(
2248
2331
  "ADD PRIMARY KEY ({primary_key_name})"
2249
2332
  ),
2250
2333
  ]
2251
- elif flavor == 'timescaledb' and datetime_column and datetime_column != primary_key:
2334
+ elif (
2335
+ flavor in ('timescaledb', 'timescaledb-ha')
2336
+ and datetime_column
2337
+ and datetime_column != primary_key
2338
+ ):
2252
2339
  create_table_queries = [
2253
2340
  (
2254
2341
  "SELECT *\n"
@@ -10,6 +10,9 @@ from __future__ import annotations
10
10
  from meerschaum.utils.typing import Optional
11
11
 
12
12
  import threading
13
+ import ctypes
14
+ import signal
15
+
13
16
  Lock = threading.Lock
14
17
  RLock = threading.RLock
15
18
  Event = threading.Event
@@ -54,6 +57,45 @@ class Thread(threading.Thread):
54
57
  """Set the return to the result of the target."""
55
58
  self._return = self._target(*self._args, **self._kwargs)
56
59
 
60
+ def send_signal(self, signalnum):
61
+ """
62
+ Send a signal to the thread.
63
+ """
64
+ if not self.is_alive():
65
+ return
66
+
67
+ if signalnum == signal.SIGINT:
68
+ self.raise_exception(KeyboardInterrupt())
69
+ elif signalnum == signal.SIGTERM:
70
+ self.raise_exception(SystemExit())
71
+ else:
72
+ signal.pthread_kill(self.ident, signalnum)
73
+
74
+ def raise_exception(self, exc: BaseException):
75
+ """
76
+ Raise an exception in the thread.
77
+
78
+ This uses a CPython-specific implementation and is not guaranteed to be stable.
79
+ It may also be deprecated in future Python versions.
80
+ """
81
+ if not self.is_alive():
82
+ return
83
+
84
+ if not hasattr(ctypes.pythonapi, 'PyThreadState_SetAsyncExc'):
85
+ return
86
+
87
+ exc_class = exc if isinstance(exc, type) else type(exc)
88
+
89
+ ident = self.ident
90
+ if ident is None:
91
+ return
92
+
93
+ ret = ctypes.pythonapi.PyThreadState_SetAsyncExc(
94
+ ctypes.c_ulong(ident),
95
+ ctypes.py_object(exc_class)
96
+ )
97
+ if ret > 1:
98
+ ctypes.pythonapi.PyThreadState_SetAsyncExc(ident, 0)
57
99
 
58
100
  class Worker(threading.Thread):
59
101
  """Wrapper for `threading.Thread` for working with `queue.Queue` objects."""
@@ -76,10 +76,7 @@ import collections.abc
76
76
  collections.Iterable = collections.abc.Iterable
77
77
 
78
78
  SuccessTuple = Tuple[bool, str]
79
- InstanceConnector = Union[
80
- 'meerschaum.connectors.sql.SQLConnector',
81
- 'meerschaum.connectors.api.APIConnector'
82
- ]
79
+ InstanceConnector = 'meerschaum.connectors.InstanceConnector'
83
80
  PipesDict = Dict[
84
81
  str, Dict[ ### connector_keys : metrics
85
82
  str, Dict[ ### metric_key : locations
@@ -35,13 +35,13 @@ class Venv:
35
35
 
36
36
  def __init__(
37
37
  self,
38
- venv: Union[str, 'meerschaum.plugins.Plugin', None] = 'mrsm',
38
+ venv: Union[str, 'meerschaum.core.Plugin', None] = 'mrsm',
39
39
  debug: bool = False,
40
40
  ) -> None:
41
41
  from meerschaum.utils.venv import activate_venv, deactivate_venv, active_venvs
42
42
  ### For some weird threading issue,
43
43
  ### we can't use `isinstance` here.
44
- if 'meerschaum.plugins._Plugin' in str(type(venv)):
44
+ if '_Plugin' in str(type(venv)):
45
45
  self._venv = venv.name
46
46
  self._activate = venv.activate_venv
47
47
  self._deactivate = venv.deactivate_venv
@@ -8,6 +8,9 @@ Manage virtual environments.
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import sys
12
+ import pathlib
13
+
11
14
  from meerschaum.utils.typing import Optional, Union, Dict, List, Tuple
12
15
  from meerschaum.utils.threading import RLock, get_ident
13
16
 
@@ -229,7 +232,6 @@ def verify_venv(
229
232
  """
230
233
  Verify that the virtual environment matches the expected state.
231
234
  """
232
- import pathlib
233
235
  import platform
234
236
  import os
235
237
  import shutil
@@ -382,11 +384,10 @@ def init_venv(
382
384
  import sys
383
385
  import platform
384
386
  import os
385
- import pathlib
386
387
  import shutil
387
388
  import time
388
389
 
389
- from meerschaum.config.static import STATIC_CONFIG
390
+ from meerschaum._internal.static import STATIC_CONFIG
390
391
  from meerschaum.config._paths import (
391
392
  VIRTENV_RESOURCES_PATH,
392
393
  VENVS_CACHE_RESOURCES_PATH,
@@ -659,6 +660,7 @@ def venv_exec(
659
660
  cmd_list,
660
661
  stdout=stdout,
661
662
  stderr=stderr,
663
+ stdin=sys.stdin,
662
664
  env=env,
663
665
  **group_kwargs
664
666
  )
@@ -682,7 +684,7 @@ def venv_target_path(
682
684
  venv: Union[str, None],
683
685
  allow_nonexistent: bool = False,
684
686
  debug: bool = False,
685
- ) -> 'pathlib.Path':
687
+ ) -> pathlib.Path:
686
688
  """
687
689
  Return a virtual environment's site-package path.
688
690
 
@@ -702,10 +704,9 @@ def venv_target_path(
702
704
  import os
703
705
  import sys
704
706
  import platform
705
- import pathlib
706
707
  import site
707
708
  from meerschaum.config._paths import VIRTENV_RESOURCES_PATH
708
- from meerschaum.config.static import STATIC_CONFIG
709
+ from meerschaum._internal.static import STATIC_CONFIG
709
710
 
710
711
  ### Check sys.path for a user-writable site-packages directory.
711
712
  if venv is None:
@@ -839,7 +840,6 @@ def get_module_venv(module) -> Union[str, None]:
839
840
  -------
840
841
  The name of a venv or `None`.
841
842
  """
842
- import pathlib
843
843
  from meerschaum.config.paths import VIRTENV_RESOURCES_PATH
844
844
  module_path = pathlib.Path(module.__file__).resolve()
845
845
  try:
@@ -100,11 +100,11 @@ def warn(*args, stacklevel=2, stack=True, color: bool = True, **kw) -> None:
100
100
 
101
101
 
102
102
  def exception_with_traceback(
103
- message: str,
104
- exception_class = Exception,
105
- stacklevel = 1,
106
- tb_type = 'single'
107
- ):
103
+ message: str,
104
+ exception_class = Exception,
105
+ stacklevel = 1,
106
+ tb_type = 'single'
107
+ ):
108
108
  """Traceback construction help found here:
109
109
  https://stackoverflow.com/questions/27138440/how-to-create-a-traceback-object
110
110
  """
@@ -165,12 +165,16 @@ def error(
165
165
  Raise an exception with supressed traceback.
166
166
  """
167
167
  from meerschaum.utils.formatting import (
168
- CHARSET, ANSI, get_console, fill_ansi, highlight_pipes, _init
168
+ CHARSET,
169
+ ANSI,
170
+ get_console,
171
+ fill_ansi,
172
+ highlight_pipes,
173
+ _init,
169
174
  )
170
175
  from meerschaum.utils.packages import import_rich, attempt_import
171
176
  from meerschaum.config import get_config
172
- import types, inspect
173
- rich = import_rich()
177
+ _ = import_rich()
174
178
  rich_traceback = attempt_import('rich.traceback')
175
179
  error_config = get_config('formatting', 'errors', patch=True)
176
180
  message = ' ' + error_config[CHARSET]['icon'] + ' ' + str(message)
@@ -186,23 +190,25 @@ def error(
186
190
  exception_class, exception, exception.__traceback__
187
191
  )
188
192
  rtb = rich_traceback.Traceback(trace)
189
- except Exception as e:
193
+ except Exception:
190
194
  trace, rtb = None, None
191
195
  if trace is None or rtb is None:
192
196
  nopretty = True
193
197
  if not nopretty and stack:
194
198
  if get_console() is not None:
195
199
  get_console().print(rtb)
196
- frame = sys._getframe(len(inspect.stack()) - 1)
197
200
  if raise_:
198
201
  raise color_exception
199
202
 
200
203
 
201
204
  def info(message: str, icon: bool = True, **kw):
202
205
  """Print an informative message."""
203
- from meerschaum.utils.packages import import_rich, attempt_import
204
206
  from meerschaum.utils.formatting import (
205
- CHARSET, ANSI, highlight_pipes, fill_ansi, _init
207
+ CHARSET,
208
+ ANSI,
209
+ highlight_pipes,
210
+ fill_ansi,
211
+ _init,
206
212
  )
207
213
  from meerschaum.config import get_config
208
214
  info_config = get_config('formatting', 'info', patch=True)
@@ -215,4 +221,4 @@ def info(message: str, icon: bool = True, **kw):
215
221
  message = fill_ansi(lines[0], **info_config['ansi']['rich']) + (
216
222
  '\n' + '\n'.join(lines[1:]) if len(lines) > 1 else ''
217
223
  )
218
- print(message)
224
+ print(message, flush=True)