meerschaum 2.7.2__py3-none-any.whl → 2.7.3__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.
@@ -240,6 +240,8 @@ def parse_synonyms(
240
240
  args_dict['instance'] = args_dict['mrsm_instance']
241
241
  if args_dict.get('skip_check_existing', None):
242
242
  args_dict['check_existing'] = False
243
+ if args_dict.get('skip_enforce_dtypes', None):
244
+ args_dict['enforce_dtypes'] = False
243
245
  if args_dict.get('venv', None) in ('None', '[None]'):
244
246
  args_dict['venv'] = None
245
247
  chunk_minutes = args_dict.get('chunk_minutes', None)
@@ -87,7 +87,7 @@ def parse_datetime(dt_str: str) -> Union[datetime, int, str]:
87
87
  dt = round_time(dt, round_delta)
88
88
  else:
89
89
  dt = dateutil_parser.parse(dt_str)
90
- except Exception as e:
90
+ except Exception:
91
91
  dt = None
92
92
  if dt is None:
93
93
  from meerschaum.utils.warnings import error
@@ -121,7 +121,7 @@ def parse_help(sysargs: Union[List[str], Dict[str, Any]]) -> None:
121
121
  """Parse the `--help` flag to determine which help message to print."""
122
122
  from meerschaum._internal.arguments._parse_arguments import parse_arguments, parse_line
123
123
  from meerschaum.actions import actions, get_subactions
124
- import importlib, inspect, textwrap
124
+ import textwrap
125
125
  from copy import deepcopy
126
126
  if isinstance(sysargs, list):
127
127
  args = parse_arguments(sysargs)
@@ -140,14 +140,14 @@ def parse_help(sysargs: Union[List[str], Dict[str, Any]]) -> None:
140
140
  if len(args['action']) > 1:
141
141
  try:
142
142
  subaction = get_subactions(args['action'][0])[args['action'][1]]
143
- except Exception as e:
143
+ except Exception:
144
144
  subaction = None
145
145
  if subaction is not None:
146
146
  return print(textwrap.dedent(subaction.__doc__))
147
147
 
148
148
  try:
149
149
  doc = actions[args['action'][0]].__doc__
150
- except Exception as e:
150
+ except Exception:
151
151
  doc = None
152
152
  if doc is None:
153
153
  doc = "No help available for '" + f"{args['action'][0]}" + "'."
@@ -309,7 +309,7 @@ groups['sync'].add_argument(
309
309
  )
310
310
  groups['sync'].add_argument(
311
311
  '--chunksize', type=int, help=(
312
- "Specify the database chunksize. Defaults to 10,000."
312
+ "Specify the database chunksize. Defaults to 100,000."
313
313
  ),
314
314
  )
315
315
  groups['sync'].add_argument(
@@ -336,19 +336,25 @@ groups['sync'].add_argument(
336
336
  )
337
337
  groups['sync'].add_argument(
338
338
  '--bounded', '--bound', action="store_true",
339
- help = (
339
+ help=(
340
340
  "When verifying, do not sync outside "
341
- + "the existing oldest and newest datetime bounds."
341
+ "the existing oldest and newest datetime bounds."
342
342
  )
343
343
  )
344
344
  groups['sync'].add_argument(
345
345
  '--skip-check-existing', '--allow-duplicates', action='store_true',
346
- help = (
347
- "Skip checking for duplicate rows when syncing. " +
348
- "This drastically improves performance when all rows to be synced are unique. " +
349
- "For example, this setting is highly recommended for use with IoT devices."
346
+ help=(
347
+ "Skip checking for duplicate rows when syncing. "
348
+ "This improves performance when all rows to be synced are unique. "
350
349
  )
351
350
  )
351
+ groups['sync'].add_argument(
352
+ '--skip-enforce-dtypes', action='store_true',
353
+ help=(
354
+ "Skip enforcing incoming data to a pipe's dtypes. "
355
+ "This improves performance when all rows are expected to already be of the correct type."
356
+ ),
357
+ )
352
358
  groups['sync'].add_argument(
353
359
  '--cache', action='store_true',
354
360
  help = (
@@ -137,7 +137,7 @@ def _ask_with_rowcounts(
137
137
  )
138
138
  total_num_rows = sum([rc for p, rc in pipes_rowcounts.items()])
139
139
  question = (
140
- f"Are you sure you want to delete {total_num_rows} rows across {len(pipes)} pipe"
140
+ f"Are you sure you want to delete {total_num_rows:,} rows across {len(pipes)} pipe"
141
141
  + ('s' if len(pipes) != 1 else '')
142
142
  + " in the following range?\n"
143
143
  )
@@ -498,7 +498,7 @@ def _edit_jobs(
498
498
  num_edited += 1
499
499
 
500
500
  msg = (
501
- "Successfully edit job"
501
+ "Successfully edited job"
502
502
  + ('s' if len(jobs) != 1 else '')
503
503
  + ' '
504
504
  + items_str(list(jobs.keys()))
@@ -10,9 +10,9 @@ from __future__ import annotations
10
10
  from meerschaum.utils.typing import Union, Any, Sequence, SuccessTuple, Optional, Tuple, List
11
11
 
12
12
  def verify(
13
- action: Optional[List[str]] = None,
14
- **kwargs: Any
15
- ) -> SuccessTuple:
13
+ action: Optional[List[str]] = None,
14
+ **kwargs: Any
15
+ ) -> SuccessTuple:
16
16
  """
17
17
  Verify the states of pipes, packages, and more.
18
18
  """
@@ -36,19 +36,17 @@ def _verify_pipes(**kwargs) -> SuccessTuple:
36
36
 
37
37
 
38
38
  def _verify_packages(
39
- debug: bool = False,
40
- venv: Optional[str] = 'mrsm',
41
- **kw
42
- ) -> SuccessTuple:
39
+ debug: bool = False,
40
+ venv: Optional[str] = 'mrsm',
41
+ **kw
42
+ ) -> SuccessTuple:
43
43
  """
44
44
  Verify the versions of packages.
45
45
  """
46
46
  from meerschaum.utils.packages import (
47
- attempt_import, need_update, all_packages, is_installed, venv_contains_package,
47
+ attempt_import, all_packages, is_installed, venv_contains_package,
48
48
  _monkey_patch_get_distribution, manually_import_module,
49
49
  )
50
- from meerschaum.utils.formatting import pprint
51
- from meerschaum.utils.debug import dprint
52
50
 
53
51
  venv_packages, base_packages, miss_packages = [], [], []
54
52
 
@@ -80,10 +78,10 @@ def _verify_packages(
80
78
 
81
79
 
82
80
  def _verify_venvs(
83
- action: Optional[List[str]],
84
- debug: bool = False,
85
- **kw
86
- ) -> SuccessTuple:
81
+ action: Optional[List[str]],
82
+ debug: bool = False,
83
+ **kw
84
+ ) -> SuccessTuple:
87
85
  """
88
86
  Verify your virtual environments.
89
87
  """
@@ -94,15 +92,14 @@ def _verify_venvs(
94
92
 
95
93
 
96
94
  def _verify_plugins(
97
- action: Optional[List[str]] = None,
98
- **kwargs: Any
99
- ) -> SuccessTuple:
95
+ action: Optional[List[str]] = None,
96
+ **kwargs: Any
97
+ ) -> SuccessTuple:
100
98
  """
101
99
  Verify that all of the available plugins are able to be imported as expected.
102
100
  """
103
- from meerschaum.utils.formatting import print_options, UNICODE, print_tuple
104
- from meerschaum.plugins import import_plugins, get_plugins_names, Plugin
105
- from meerschaum.config import get_config
101
+ from meerschaum.utils.formatting import print_options, print_tuple
102
+ from meerschaum.plugins import get_plugins_names, Plugin
106
103
  from meerschaum.utils.misc import items_str
107
104
 
108
105
  plugins_names_to_verify = action or get_plugins_names()
@@ -135,7 +132,7 @@ def _verify_plugins(
135
132
  f"Successfully imported {len(plugins_names_to_verify)} plugins."
136
133
  if success
137
134
  else (
138
- f"Failed to import plugin"
135
+ "Failed to import plugin"
139
136
  + ('s' if len(failed_to_import) != 1 else '')
140
137
  + f" {items_str(failed_to_import)}."
141
138
  )
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.7.2"
5
+ __version__ = "2.7.3"
@@ -9,6 +9,7 @@ Implement the Connector fetch() method
9
9
  from __future__ import annotations
10
10
 
11
11
  from datetime import datetime, timedelta
12
+
12
13
  import meerschaum as mrsm
13
14
  from meerschaum.utils.typing import Optional, Union, Callable, Any, List, Dict
14
15
 
@@ -144,37 +145,37 @@ def get_pipe_metadef(
144
145
  -------
145
146
  A pipe's meta definition fetch query string.
146
147
  """
147
- from meerschaum.utils.debug import dprint
148
- from meerschaum.utils.warnings import warn, error
148
+ from meerschaum.utils.warnings import warn
149
149
  from meerschaum.utils.sql import sql_item_name, dateadd_str, build_where
150
+ from meerschaum.utils.dtypes.sql import get_db_type_from_pd_type
150
151
  from meerschaum.utils.misc import is_int
151
152
  from meerschaum.config import get_config
152
153
 
153
- definition = get_pipe_query(pipe)
154
-
155
- if not pipe.columns.get('datetime', None):
156
- _dt = pipe.guess_datetime()
157
- dt_name = sql_item_name(_dt, self.flavor, None) if _dt else None
154
+ dt_col = pipe.columns.get('datetime', None)
155
+ if not dt_col:
156
+ dt_col = pipe.guess_datetime()
157
+ dt_name = sql_item_name(dt_col, self.flavor, None) if dt_col else None
158
158
  is_guess = True
159
159
  else:
160
- _dt = pipe.get_columns('datetime')
161
- dt_name = sql_item_name(_dt, self.flavor, None)
160
+ dt_name = sql_item_name(dt_col, self.flavor, None)
162
161
  is_guess = False
162
+ dt_typ = pipe.dtypes.get(dt_col, 'datetime') if dt_col else None
163
+ db_dt_typ = get_db_type_from_pd_type(dt_typ, self.flavor) if dt_typ else None
163
164
 
164
165
  if begin not in (None, '') or end is not None:
165
166
  if is_guess:
166
- if _dt is None:
167
+ if dt_col is None:
167
168
  warn(
168
169
  f"Unable to determine a datetime column for {pipe}."
169
170
  + "\n Ignoring begin and end...",
170
- stack = False,
171
+ stack=False,
171
172
  )
172
173
  begin, end = '', None
173
174
  else:
174
175
  warn(
175
176
  f"A datetime wasn't specified for {pipe}.\n"
176
- + f" Using column \"{_dt}\" for datetime bounds...",
177
- stack = False
177
+ + f" Using column \"{dt_col}\" for datetime bounds...",
178
+ stack=False
178
179
  )
179
180
 
180
181
  apply_backtrack = begin == '' and check_existing
@@ -200,6 +201,7 @@ def get_pipe_metadef(
200
201
  datepart='minute',
201
202
  number=((-1 * btm) if apply_backtrack else 0),
202
203
  begin=begin,
204
+ db_type=db_dt_typ,
203
205
  )
204
206
  if begin
205
207
  else None
@@ -210,11 +212,13 @@ def get_pipe_metadef(
210
212
  datepart='minute',
211
213
  number=0,
212
214
  begin=end,
215
+ db_type=db_dt_typ,
213
216
  )
214
217
  if end
215
218
  else None
216
219
  )
217
220
 
221
+ definition_name = sql_item_name('definition', self.flavor, None)
218
222
  meta_def = (
219
223
  _simple_fetch_query(pipe, self.flavor) if (
220
224
  (not (pipe.columns or {}).get('id', None))
@@ -225,26 +229,26 @@ def get_pipe_metadef(
225
229
  has_where = 'where' in meta_def.lower()[meta_def.lower().rfind('definition'):]
226
230
  if dt_name and (begin_da or end_da):
227
231
  definition_dt_name = (
228
- dateadd_str(self.flavor, 'minute', 0, f"definition.{dt_name}")
232
+ dateadd_str(self.flavor, 'minute', 0, f"{definition_name}.{dt_name}", db_type=db_dt_typ)
229
233
  if not is_int((begin_da or end_da))
230
- else f"definition.{dt_name}"
234
+ else f"{definition_name}.{dt_name}"
231
235
  )
232
236
  meta_def += "\n" + ("AND" if has_where else "WHERE") + " "
233
237
  has_where = True
234
238
  if begin_da:
235
- meta_def += f"{definition_dt_name} >= {begin_da}"
239
+ meta_def += f"\n {definition_dt_name}\n >=\n {begin_da}\n"
236
240
  if begin_da and end_da:
237
- meta_def += " AND "
241
+ meta_def += " AND"
238
242
  if end_da:
239
- meta_def += f"{definition_dt_name} < {end_da}"
243
+ meta_def += f"\n {definition_dt_name}\n <\n {end_da}\n"
240
244
 
241
245
  if params is not None:
242
246
  params_where = build_where(params, self, with_where=False)
243
- meta_def += "\n" + ("AND" if has_where else "WHERE") + " "
247
+ meta_def += "\n " + ("AND" if has_where else "WHERE") + " "
244
248
  has_where = True
245
249
  meta_def += params_where
246
250
 
247
- return meta_def
251
+ return meta_def.rstrip()
248
252
 
249
253
 
250
254
  def get_pipe_query(pipe: mrsm.Pipe, warn: bool = True) -> Union[str, None]:
@@ -256,7 +260,11 @@ def get_pipe_query(pipe: mrsm.Pipe, warn: bool = True) -> Union[str, None]:
256
260
  - query
257
261
  - sql
258
262
  """
263
+ import re
264
+ import textwrap
259
265
  from meerschaum.utils.warnings import warn as _warn
266
+ from meerschaum.utils.misc import parse_arguments_str
267
+ from meerschaum.utils.sql import sql_item_name
260
268
  if pipe.parameters.get('fetch', {}).get('definition', None):
261
269
  definition = pipe.parameters['fetch']['definition']
262
270
  elif pipe.parameters.get('definition', None):
@@ -272,7 +280,23 @@ def get_pipe_query(pipe: mrsm.Pipe, warn: bool = True) -> Union[str, None]:
272
280
  + " Set the key `query` in `pipe.parameters` to a valid SQL query."
273
281
  )
274
282
  return None
275
- return definition
283
+
284
+ def replace_pipe_match(pipe_match):
285
+ try:
286
+ args_str = pipe_match.group(1)
287
+ args, kwargs = parse_arguments_str(args_str)
288
+ pipe = mrsm.Pipe(*args, **kwargs)
289
+ except Exception as e:
290
+ if warn:
291
+ _warn(f"Failed to parse pipe from SQL definition:\n{e}")
292
+ raise e
293
+
294
+ target = pipe.target
295
+ schema = pipe.instance_connector.get_pipe_schema(pipe)
296
+ return sql_item_name(target, pipe.instance_connector.flavor, schema)
297
+
298
+ definition = re.sub(r'\{\{Pipe\((.*?)\)\}\}', replace_pipe_match, definition)
299
+ return textwrap.dedent(definition.lstrip().rstrip())
276
300
 
277
301
 
278
302
  def set_pipe_query(pipe: mrsm.Pipe, query: str) -> None:
@@ -331,11 +355,7 @@ def _join_fetch_query(
331
355
  pipe_instance_name = sql_item_name(
332
356
  pipe.target, pipe.instance_connector.flavor, pipe.instance_connector.schema
333
357
  )
334
- # pipe_remote_name = sql_item_name(pipe.target, pipe.connector.flavor)
335
358
  sync_times_table = pipe.target + "_sync_times"
336
- sync_times_instance_name = sql_item_name(
337
- sync_times_table, pipe.instance_connector.flavor, None
338
- )
339
359
  sync_times_remote_name = sql_item_name(
340
360
  sync_times_table, pipe.connector.flavor, None
341
361
  )
@@ -393,4 +413,3 @@ def _join_fetch_query(
393
413
  WHERE definition.{dt_remote_name} > st.{dt_remote_name}
394
414
  """ + (f" OR st.{id_remote_name} IS NULL" if new_ids else "")
395
415
  return query
396
-
@@ -96,15 +96,15 @@ def _drop_temporary_tables(self, debug: bool = False) -> SuccessTuple:
96
96
  sqlalchemy.select(temp_tables_table.c.table)
97
97
  .where(temp_tables_table.c.ready_to_drop.is_not(None))
98
98
  )
99
- tables_to_drop = [
99
+ tables_to_drop = {
100
100
  table
101
101
  for table, ready_to_drop in _in_memory_temp_tables.items()
102
102
  if ready_to_drop
103
- ]
103
+ }
104
104
  if not tables_to_drop:
105
105
  df = self.read(query, silent=True, debug=debug)
106
106
  tables_to_drop = (
107
- list(df['table'])
107
+ set(df['table'])
108
108
  if df is not None
109
109
  else []
110
110
  )
@@ -126,7 +126,7 @@ def _drop_temporary_tables(self, debug: bool = False) -> SuccessTuple:
126
126
  sqlalchemy.delete(temp_tables_table)
127
127
  .where(temp_tables_table.c.table.in_(dropped_tables))
128
128
  )
129
- delete_result = self.exec(delete_query, silent=True, debug=debug)
129
+ _ = self.exec(delete_query, silent=True, debug=debug)
130
130
 
131
131
  success = len(failed_tables) == 0
132
132
  msg = (