meerschaum 2.2.0rc4__py3-none-any.whl → 2.2.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 (42) hide show
  1. meerschaum/_internal/entry.py +36 -11
  2. meerschaum/_internal/shell/Shell.py +40 -16
  3. meerschaum/_internal/term/__init__.py +3 -2
  4. meerschaum/_internal/term/tools.py +1 -1
  5. meerschaum/actions/api.py +65 -31
  6. meerschaum/actions/python.py +56 -24
  7. meerschaum/actions/start.py +2 -4
  8. meerschaum/actions/uninstall.py +5 -9
  9. meerschaum/actions/upgrade.py +11 -3
  10. meerschaum/api/__init__.py +1 -0
  11. meerschaum/api/dash/callbacks/__init__.py +4 -0
  12. meerschaum/api/dash/callbacks/custom.py +39 -0
  13. meerschaum/api/dash/callbacks/dashboard.py +39 -6
  14. meerschaum/api/dash/callbacks/login.py +3 -1
  15. meerschaum/api/dash/components.py +5 -2
  16. meerschaum/api/dash/pipes.py +145 -97
  17. meerschaum/config/_default.py +1 -0
  18. meerschaum/config/_paths.py +12 -12
  19. meerschaum/config/_version.py +1 -1
  20. meerschaum/config/paths.py +10 -0
  21. meerschaum/config/static/__init__.py +1 -1
  22. meerschaum/connectors/__init__.py +9 -2
  23. meerschaum/connectors/sql/_cli.py +7 -1
  24. meerschaum/connectors/sql/_pipes.py +6 -0
  25. meerschaum/core/Pipe/__init__.py +5 -0
  26. meerschaum/core/Pipe/_sync.py +2 -3
  27. meerschaum/plugins/__init__.py +67 -9
  28. meerschaum/utils/daemon/Daemon.py +7 -2
  29. meerschaum/utils/misc.py +6 -0
  30. meerschaum/utils/packages/__init__.py +212 -53
  31. meerschaum/utils/packages/_packages.py +3 -2
  32. meerschaum/utils/process.py +12 -2
  33. meerschaum/utils/schedule.py +10 -3
  34. meerschaum/utils/venv/__init__.py +46 -11
  35. {meerschaum-2.2.0rc4.dist-info → meerschaum-2.2.2.dist-info}/METADATA +13 -9
  36. {meerschaum-2.2.0rc4.dist-info → meerschaum-2.2.2.dist-info}/RECORD +42 -40
  37. {meerschaum-2.2.0rc4.dist-info → meerschaum-2.2.2.dist-info}/WHEEL +1 -1
  38. {meerschaum-2.2.0rc4.dist-info → meerschaum-2.2.2.dist-info}/LICENSE +0 -0
  39. {meerschaum-2.2.0rc4.dist-info → meerschaum-2.2.2.dist-info}/NOTICE +0 -0
  40. {meerschaum-2.2.0rc4.dist-info → meerschaum-2.2.2.dist-info}/entry_points.txt +0 -0
  41. {meerschaum-2.2.0rc4.dist-info → meerschaum-2.2.2.dist-info}/top_level.txt +0 -0
  42. {meerschaum-2.2.0rc4.dist-info → meerschaum-2.2.2.dist-info}/zip-safe +0 -0
@@ -94,6 +94,8 @@ omit_actions = {
94
94
  'repo',
95
95
  'instance',
96
96
  }
97
+
98
+ ### Map endpoints to page layouts.
97
99
  _paths = {
98
100
  'login' : pages.login.layout,
99
101
  '' : pages.dashboard.layout,
@@ -101,7 +103,8 @@ _paths = {
101
103
  'register': pages.register.layout,
102
104
  }
103
105
  _required_login = {''}
104
-
106
+
107
+
105
108
  @dash_app.callback(
106
109
  Output('page-layout-div', 'children'),
107
110
  Output('session-store', 'data'),
@@ -147,16 +150,31 @@ def update_page_layout_div(
147
150
  else:
148
151
  session_store_to_return = dash.no_update
149
152
 
150
- _path = (
153
+ base_path = (
151
154
  pathname.rstrip('/') + '/'
152
155
  ).replace(
153
156
  (dash_endpoint + '/'),
154
157
  ''
155
158
  ).rstrip('/').split('/')[0]
159
+
160
+ complete_path = (
161
+ pathname.rstrip('/') + '/'
162
+ ).replace(
163
+ dash_endpoint + '/',
164
+ ''
165
+ ).rstrip('/')
166
+
167
+ if complete_path in _paths:
168
+ path_str = complete_path
169
+ elif base_path in _paths:
170
+ path_str = base_path
171
+ else:
172
+ path_str = ''
173
+
156
174
  path = (
157
- _path
158
- if no_auth or _path not in _required_login else (
159
- _path
175
+ path_str
176
+ if no_auth or path_str not in _required_login else (
177
+ path_str
160
178
  if session_id in active_sessions
161
179
  else 'login'
162
180
  )
@@ -868,10 +886,25 @@ dash_app.clientside_callback(
868
886
  location = "None";
869
887
  }
870
888
 
889
+ var subaction = "pipes";
890
+ if (action == "python"){
891
+ subaction = (
892
+ '"' + "pipe = mrsm.Pipe('"
893
+ + pipe_meta.connector
894
+ + "', '"
895
+ + pipe_meta.metric
896
+ + "'"
897
+ );
898
+ if (location != "None"){
899
+ subaction += ", '" + location + "'";
900
+ }
901
+ subaction += ", instance='" + pipe_meta.instance + "')" + '"';
902
+ }
903
+
871
904
  iframe.contentWindow.postMessage(
872
905
  {
873
906
  action: action,
874
- subaction: "pipes",
907
+ subaction: subaction,
875
908
  connector_keys: [pipe_meta.connector],
876
909
  metric_keys: [pipe_meta.metric],
877
910
  location_keys: [location],
@@ -45,6 +45,7 @@ def show_registration_disabled_collapse(n_clicks, is_open):
45
45
  State('username-input', 'value'),
46
46
  State('password-input', 'value'),
47
47
  State('location', 'href'),
48
+ State('location', 'pathname'),
48
49
  )
49
50
  def login_button_click(
50
51
  username_submit,
@@ -53,6 +54,7 @@ def login_button_click(
53
54
  username,
54
55
  password,
55
56
  location_href,
57
+ location_pathname,
56
58
  ):
57
59
  """
58
60
  When the user submits the login form, check the login.
@@ -69,4 +71,4 @@ def login_button_click(
69
71
  except HTTPException:
70
72
  form_class += ' is-invalid'
71
73
  session_data = None
72
- return session_data, form_class, (dash.no_update if not session_data else endpoints['dash'])
74
+ return session_data, form_class, dash.no_update
@@ -12,7 +12,7 @@ from meerschaum.utils.packages import attempt_import, import_dcc, import_html
12
12
  from meerschaum.utils.typing import SuccessTuple, List
13
13
  from meerschaum.config.static import STATIC_CONFIG
14
14
  from meerschaum.utils.misc import remove_ansi
15
- from meerschaum.actions import get_shell
15
+ from meerschaum._internal.shell.Shell import get_shell_intro
16
16
  from meerschaum.api import endpoints, CHECK_UPDATE
17
17
  from meerschaum.connectors import instance_types
18
18
  from meerschaum.utils.misc import get_connector_labels
@@ -69,7 +69,10 @@ bottom_buttons_content = dbc.Card(
69
69
  ])
70
70
  )
71
71
  )
72
- console_div = html.Div(id='console-div', children=[html.Pre(get_shell().intro, id='console-pre')])
72
+ console_div = html.Div(
73
+ id = 'console-div',
74
+ children = [html.Pre(get_shell_intro(), id='console-pre')],
75
+ )
73
76
 
74
77
  location = dcc.Location(id='location', refresh=False)
75
78
 
@@ -22,7 +22,7 @@ from meerschaum.api.dash import (
22
22
  dash_app, debug, _get_pipes
23
23
  )
24
24
  from meerschaum.api.dash.connectors import get_web_connector
25
- from meerschaum.api.dash.components import alert_from_success_tuple
25
+ from meerschaum.api.dash.components import alert_from_success_tuple, build_cards_grid
26
26
  from meerschaum.api.dash.users import is_session_authenticated
27
27
  from meerschaum.config import get_config
28
28
  import meerschaum as mrsm
@@ -101,6 +101,132 @@ def pipes_from_state(
101
101
  return False, str(e)
102
102
  return _pipes
103
103
 
104
+
105
+ def build_pipe_card(
106
+ pipe: mrsm.Pipe,
107
+ authenticated: bool = False,
108
+ _build_children_num: int = 10,
109
+ ) -> 'dbc.Card':
110
+ """
111
+ Return a card for the given pipe.
112
+
113
+ Parameters
114
+ ----------
115
+ pipe: mrsm.Pipe
116
+ The pipe from which to build the card.
117
+
118
+ authenticated: bool, default False
119
+ If `True`, allow editing functionality to the card.
120
+
121
+ Returns
122
+ -------
123
+ A dash bootstrap components Card representation of the pipe.
124
+ """
125
+ meta_str = json.dumps(pipe.meta)
126
+ footer_children = dbc.Row(
127
+ [
128
+ dbc.Col(
129
+ (
130
+ dbc.DropdownMenu(
131
+ label = "Manage",
132
+ children = [
133
+ dbc.DropdownMenuItem(
134
+ 'Open in Python',
135
+ id = {
136
+ 'type': 'manage-pipe-button',
137
+ 'index': meta_str,
138
+ 'action': 'python',
139
+ },
140
+ ),
141
+ dbc.DropdownMenuItem(
142
+ 'Delete',
143
+ id = {
144
+ 'type': 'manage-pipe-button',
145
+ 'index': meta_str,
146
+ 'action': 'delete',
147
+ },
148
+ ),
149
+ dbc.DropdownMenuItem(
150
+ 'Drop',
151
+ id = {
152
+ 'type': 'manage-pipe-button',
153
+ 'index': meta_str,
154
+ 'action': 'drop',
155
+ },
156
+ ),
157
+ dbc.DropdownMenuItem(
158
+ 'Clear',
159
+ id = {
160
+ 'type': 'manage-pipe-button',
161
+ 'index': meta_str,
162
+ 'action': 'clear',
163
+ },
164
+ ),
165
+ dbc.DropdownMenuItem(
166
+ 'Verify',
167
+ id = {
168
+ 'type': 'manage-pipe-button',
169
+ 'index': meta_str,
170
+ 'action': 'verify',
171
+ },
172
+ ),
173
+ dbc.DropdownMenuItem(
174
+ 'Sync',
175
+ id = {
176
+ 'type': 'manage-pipe-button',
177
+ 'index': meta_str,
178
+ 'action': 'sync',
179
+ },
180
+ ),
181
+ ],
182
+ direction = "up",
183
+ menu_variant = "dark",
184
+ size = 'sm',
185
+ color = 'secondary',
186
+ )
187
+ ) if authenticated else [],
188
+ width = 2,
189
+ ),
190
+ dbc.Col(width=6),
191
+ dbc.Col(
192
+ dbc.Button(
193
+ 'Download CSV',
194
+ size = 'sm',
195
+ color = 'link',
196
+ style = {'float': 'right'},
197
+ id = {'type': 'pipe-download-csv-button', 'index': meta_str},
198
+ ),
199
+ width = 4,
200
+ ),
201
+ ],
202
+ justify = 'start',
203
+ )
204
+ card_body_children = [
205
+ html.H5(
206
+ html.B(str(pipe)),
207
+ className = 'card-title',
208
+ style = {'font-family': ['monospace']}
209
+ ),
210
+ html.Div(
211
+ dbc.Accordion(
212
+ accordion_items_from_pipe(
213
+ pipe,
214
+ authenticated = authenticated,
215
+ _build_children_num = _build_children_num,
216
+ ),
217
+ flush = True,
218
+ start_collapsed = True,
219
+ id = {'type': 'pipe-accordion', 'index': meta_str},
220
+ )
221
+ )
222
+
223
+ ]
224
+ return dbc.Card([
225
+ dbc.CardBody(children=card_body_children),
226
+ dbc.CardFooter(children=footer_children),
227
+ ])
228
+
229
+
104
230
  def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
105
231
  """
106
232
  Returns a tuple:
@@ -120,99 +246,7 @@ def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
120
246
  overflow_pipes = pipes[max_num_pipes_cards:]
121
247
 
122
248
  for pipe in pipes[:max_num_pipes_cards]:
123
- meta_str = json.dumps(pipe.meta)
124
- footer_children = dbc.Row(
125
- [
126
- dbc.Col(
127
- (
128
- dbc.DropdownMenu(
129
- label = "Manage",
130
- children = [
131
- dbc.DropdownMenuItem(
132
- 'Delete',
133
- id = {
134
- 'type': 'manage-pipe-button',
135
- 'index': meta_str,
136
- 'action': 'delete',
137
- },
138
- ),
139
- dbc.DropdownMenuItem(
140
- 'Drop',
141
- id = {
142
- 'type': 'manage-pipe-button',
143
- 'index': meta_str,
144
- 'action': 'drop',
145
- },
146
- ),
147
- dbc.DropdownMenuItem(
148
- 'Clear',
149
- id = {
150
- 'type': 'manage-pipe-button',
151
- 'index': meta_str,
152
- 'action': 'clear',
153
- },
154
- ),
155
- dbc.DropdownMenuItem(
156
- 'Verify',
157
- id = {
158
- 'type': 'manage-pipe-button',
159
- 'index': meta_str,
160
- 'action': 'verify',
161
- },
162
- ),
163
- dbc.DropdownMenuItem(
164
- 'Sync',
165
- id = {
166
- 'type': 'manage-pipe-button',
167
- 'index': meta_str,
168
- 'action': 'sync',
169
- },
170
- ),
171
- ],
172
- direction = "up",
173
- menu_variant = "dark",
174
- size = 'sm',
175
- color = 'secondary',
176
- )
177
- ) if authenticated else [],
178
- width = 2,
179
- ),
180
- dbc.Col(width=6),
181
- dbc.Col(
182
- dbc.Button(
183
- 'Download CSV',
184
- size = 'sm',
185
- color = 'link',
186
- style = {'float': 'right'},
187
- id = {'type': 'pipe-download-csv-button', 'index': meta_str},
188
- ),
189
- width = 4,
190
- ),
191
- ],
192
- justify = 'start',
193
- )
194
- card_body_children = [
195
- html.H5(
196
- html.B(str(pipe)),
197
- className = 'card-title',
198
- style = {'font-family': ['monospace']}
199
- ),
200
- html.Div(
201
- dbc.Accordion(
202
- accordion_items_from_pipe(pipe, authenticated=authenticated),
203
- flush = True,
204
- start_collapsed = True,
205
- id = {'type': 'pipe-accordion', 'index': meta_str},
206
- )
207
- )
208
-
209
- ]
210
- cards.append(
211
- dbc.Card([
212
- dbc.CardBody(children=card_body_children),
213
- dbc.CardFooter(children=footer_children),
214
- ])
215
- )
249
+ cards.append(build_pipe_card(pipe, authenticated=authenticated))
216
250
 
217
251
  if overflow_pipes:
218
252
  cards.append(
@@ -239,7 +273,8 @@ def accordion_items_from_pipe(
239
273
  pipe: mrsm.Pipe,
240
274
  active_items: Optional[List[str]] = None,
241
275
  authenticated: bool = False,
242
- ) -> List[dbc.AccordionItem]:
276
+ _build_children_num: int = 10,
277
+ ) -> 'List[dbc.AccordionItem]':
243
278
  """
244
279
  Build the accordion items for a given pipe.
245
280
  """
@@ -401,7 +436,7 @@ def accordion_items_from_pipe(
401
436
  size = 'sm',
402
437
  style = {'text-decoration': 'none', 'margin-left': '10px'},
403
438
  )
404
- items_bodies['parameters'] = html.Div([
439
+ parameters_div_children = [
405
440
  parameters_editor,
406
441
  html.Br(),
407
442
  dbc.Row([
@@ -422,8 +457,21 @@ def accordion_items_from_pipe(
422
457
  width=True,
423
458
  )
424
459
  ]),
460
+ ]
461
+ if _build_children_num > 0 and pipe.children:
462
+ children_cards = [
463
+ build_pipe_card(
464
+ child_pipe,
465
+ authenticated = authenticated,
466
+ _build_children_num = (_build_children_num - 1),
467
+ )
468
+ for child_pipe in pipe.children
469
+ ]
470
+ children_grid = build_cards_grid(children_cards, num_columns=1)
471
+ chidren_div_items = [html.Br(), html.H3('Children Pipes'), html.Br(), children_grid]
472
+ parameters_div_children.extend([html.Br()] + chidren_div_items)
425
473
 
426
- ])
474
+ items_bodies['parameters'] = html.Div(parameters_div_children)
427
475
 
428
476
  if 'sql' in active_items:
429
477
  query = dedent((get_pipe_query(pipe, warn=False) or "")).lstrip().rstrip()
@@ -110,6 +110,7 @@ default_system_config = {
110
110
  'space': False,
111
111
  'join_fetch': False,
112
112
  'inplace_sync': True,
113
+ 'uv_pip': True,
113
114
  },
114
115
  }
115
116
  default_pipes_config = {
@@ -82,24 +82,24 @@ for _plugin_path in _plugins_paths_to_remove:
82
82
 
83
83
  ENVIRONMENT_VENVS_DIR = STATIC_CONFIG['environment']['venvs']
84
84
  if ENVIRONMENT_VENVS_DIR in os.environ:
85
- VENVS_DIR_PATH = Path(os.environ[ENVIRONMENT_VENVS_DIR]).resolve()
86
- if not VENVS_DIR_PATH.exists():
85
+ _VENVS_DIR_PATH = Path(os.environ[ENVIRONMENT_VENVS_DIR]).resolve()
86
+ if not _VENVS_DIR_PATH.exists():
87
87
  try:
88
- VENVS_DIR_PATH.mkdir(parents=True, exist_ok=True)
88
+ _VENVS_DIR_PATH.mkdir(parents=True, exist_ok=True)
89
89
  except Exception as e:
90
90
  print(
91
91
  f"Invalid path set for environment variable '{ENVIRONMENT_VENVS_DIR}':\n"
92
- + f"{VENVS_DIR_PATH}"
92
+ + f"{_VENVS_DIR_PATH}"
93
93
  )
94
- VENVS_DIR_PATH = (_ROOT_DIR_PATH / 'venvs').resolve()
95
- print(f"Will use the following path for venvs instead:\n{VENVS_DIR_PATH}")
94
+ _VENVS_DIR_PATH = (_ROOT_DIR_PATH / 'venvs').resolve()
95
+ print(f"Will use the following path for venvs instead:\n{_VENVS_DIR_PATH}")
96
96
  else:
97
- VENVS_DIR_PATH = _ROOT_DIR_PATH / 'venvs'
97
+ _VENVS_DIR_PATH = _ROOT_DIR_PATH / 'venvs'
98
98
 
99
99
  paths = {
100
- 'PACKAGE_ROOT_PATH' : str(Path(__file__).parent.parent.resolve()),
101
- 'ROOT_DIR_PATH' : str(_ROOT_DIR_PATH),
102
- 'VIRTENV_RESOURCES_PATH' : str(VENVS_DIR_PATH),
100
+ 'PACKAGE_ROOT_PATH' : Path(__file__).parent.parent.resolve().as_posix(),
101
+ 'ROOT_DIR_PATH' : _ROOT_DIR_PATH.as_posix(),
102
+ 'VIRTENV_RESOURCES_PATH' : _VENVS_DIR_PATH.as_posix(),
103
103
  'CONFIG_DIR_PATH' : ('{ROOT_DIR_PATH}', 'config'),
104
104
  'DEFAULT_CONFIG_DIR_PATH' : ('{ROOT_DIR_PATH}', 'default_config'),
105
105
  'PATCH_DIR_PATH' : ('{ROOT_DIR_PATH}', 'patch_config'),
@@ -114,6 +114,7 @@ paths = {
114
114
 
115
115
  'SHELL_RESOURCES_PATH' : ('{ROOT_DIR_PATH}', ),
116
116
  'SHELL_HISTORY_PATH' : ('{SHELL_RESOURCES_PATH}', '.mrsm_history'),
117
+ 'PYTHON_RESOURCES_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'python'),
117
118
 
118
119
  'API_RESOURCES_PATH' : ('{PACKAGE_ROOT_PATH}', 'api', 'resources'),
119
120
  'API_STATIC_PATH' : ('{API_RESOURCES_PATH}', 'static'),
@@ -186,7 +187,6 @@ def __getattr__(name: str) -> Path:
186
187
  if name.endswith('RESOURCES_PATH') or name == 'CONFIG_DIR_PATH':
187
188
  path.mkdir(parents=True, exist_ok=True)
188
189
  elif 'FILENAME' in name:
189
- path = str(path)
190
+ path = path.as_posix()
190
191
 
191
192
  return path
192
-
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.2.0rc4"
5
+ __version__ = "2.2.2"
@@ -0,0 +1,10 @@
1
+ #! /usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # vim:fenc=utf-8
4
+
5
+ """
6
+ External API for importing Meerschaum paths.
7
+ """
8
+
9
+ from meerschaum.config._paths import __getattr__, paths
10
+ __all__ = tuple(paths.keys())
@@ -110,7 +110,7 @@ STATIC_CONFIG: Dict[str, Any] = {
110
110
  'pbkdf2_sha256',
111
111
  ],
112
112
  'default': 'pbkdf2_sha256',
113
- 'pbkdf2_sha256__default_rounds': 3_000_000,
113
+ 'pbkdf2_sha256__default_rounds': 1_000_000,
114
114
  },
115
115
  'min_username_length': 1,
116
116
  'max_username_length': 26,
@@ -23,7 +23,14 @@ from meerschaum.connectors.sql.SQLConnector import SQLConnector
23
23
  from meerschaum.connectors.api.APIConnector import APIConnector
24
24
  from meerschaum.connectors.sql._create_engine import flavor_configs as sql_flavor_configs
25
25
 
26
- __all__ = ("Connector", "SQLConnector", "APIConnector", "get_connector", "is_connected")
26
+ __all__ = (
27
+ "Connector",
28
+ "SQLConnector",
29
+ "APIConnector",
30
+ "get_connector",
31
+ "is_connected",
32
+ "poll",
33
+ )
27
34
 
28
35
  ### store connectors partitioned by
29
36
  ### type, label for reuse
@@ -321,7 +328,7 @@ def load_plugin_connectors():
321
328
  continue
322
329
  with open(plugin.__file__, encoding='utf-8') as f:
323
330
  text = f.read()
324
- if 'make_connector' in text:
331
+ if 'make_connector' in text or 'Connector' in text:
325
332
  to_import.append(plugin.name)
326
333
  if not to_import:
327
334
  return
@@ -42,9 +42,13 @@ def cli(
42
42
  env = copy.deepcopy(dict(os.environ))
43
43
  env[f'MRSM_SQL_{self.label.upper()}'] = json.dumps(self.meta)
44
44
  cli_code = (
45
+ "import sys\n"
45
46
  "import meerschaum as mrsm\n"
46
47
  f"conn = mrsm.get_connector('sql:{self.label}')\n"
47
- f"conn._cli_exit()"
48
+ "success, msg = conn._cli_exit()\n"
49
+ "mrsm.pprint((success, msg))\n"
50
+ "if not success:\n"
51
+ " raise Exception(msg)"
48
52
  )
49
53
  try:
50
54
  _ = venv_exec(cli_code, venv=None, debug=debug, capture_output=False)
@@ -91,6 +95,8 @@ def _cli_exit(
91
95
  cli_arg_str = self.DATABASE_URL
92
96
  if self.flavor in ('sqlite', 'duckdb'):
93
97
  cli_arg_str = str(self.database)
98
+ if cli_arg_str.startswith('postgresql+psycopg://'):
99
+ cli_arg_str = cli_arg_str.replace('postgresql+psycopg://', 'postgresql://')
94
100
 
95
101
  ### Define the script to execute to launch the CLI.
96
102
  ### The `mssqlcli` script is manually written to avoid telemetry
@@ -385,6 +385,9 @@ def get_create_index_queries(
385
385
  -------
386
386
  A dictionary of column names mapping to lists of queries.
387
387
  """
388
+ ### NOTE: Due to recent breaking changes in DuckDB, indices don't behave properly.
389
+ if self.flavor == 'duckdb':
390
+ return {}
388
391
  from meerschaum.utils.sql import (
389
392
  sql_item_name,
390
393
  get_distinct_col_count,
@@ -554,6 +557,9 @@ def get_drop_index_queries(
554
557
  -------
555
558
  A dictionary of column names mapping to lists of queries.
556
559
  """
560
+ ### NOTE: Due to breaking changes within DuckDB, indices must be skipped.
561
+ if self.flavor == 'duckdb':
562
+ return {}
557
563
  if not pipe.exists(debug=debug):
558
564
  return {}
559
565
  from meerschaum.utils.sql import sql_item_name, table_exists, hypertable_queries
@@ -436,6 +436,11 @@ class Pipe:
436
436
  def __repr__(self, **kw) -> str:
437
437
  return pipe_repr(self, **kw)
438
438
 
439
+ def __pt_repr__(self):
440
+ from meerschaum.utils.packages import attempt_import
441
+ prompt_toolkit_formatted_text = attempt_import('prompt_toolkit.formatted_text', lazy=False)
442
+ return prompt_toolkit_formatted_text.ANSI(self.__repr__())
443
+
439
444
  def __getstate__(self) -> Dict[str, Any]:
440
445
  """
441
446
  Define the state dictionary (pickling).
@@ -215,9 +215,8 @@ def sync(
215
215
 
216
216
  ### Activate and invoke `sync(pipe)` for plugin connectors with `sync` methods.
217
217
  try:
218
- if p.connector.type == 'plugin' and p.connector.sync is not None:
219
- connector_plugin = Plugin(p.connector.label)
220
- with Venv(connector_plugin, debug=debug):
218
+ if getattr(p.connector, 'sync', None) is not None:
219
+ with Venv(get_connector_plugin(p.connector), debug=debug):
221
220
  return_tuple = p.connector.sync(p, debug=debug, **kw)
222
221
  p._exists = None
223
222
  if not isinstance(return_tuple, tuple):