meerschaum 2.7.5__py3-none-any.whl → 2.7.7__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- meerschaum/_internal/shell/Shell.py +4 -6
- meerschaum/_internal/shell/ShellCompleter.py +6 -5
- meerschaum/actions/clear.py +6 -3
- meerschaum/actions/copy.py +33 -27
- meerschaum/actions/drop.py +100 -22
- meerschaum/actions/index.py +71 -0
- meerschaum/actions/register.py +8 -12
- meerschaum/actions/sql.py +1 -1
- meerschaum/actions/sync.py +22 -18
- meerschaum/api/dash/pipes.py +2 -3
- meerschaum/api/routes/_pipes.py +18 -0
- meerschaum/api/routes/_plugins.py +1 -1
- meerschaum/api/routes/_users.py +62 -61
- meerschaum/config/_default.py +5 -0
- meerschaum/config/_version.py +1 -1
- meerschaum/connectors/api/_misc.py +3 -2
- meerschaum/connectors/api/_pipes.py +28 -9
- meerschaum/connectors/sql/_SQLConnector.py +7 -3
- meerschaum/connectors/sql/_create_engine.py +1 -1
- meerschaum/connectors/sql/_fetch.py +4 -9
- meerschaum/connectors/sql/_instance.py +3 -3
- meerschaum/connectors/sql/_pipes.py +292 -76
- meerschaum/connectors/sql/_plugins.py +11 -16
- meerschaum/connectors/sql/_sql.py +13 -9
- meerschaum/connectors/sql/_uri.py +9 -9
- meerschaum/connectors/sql/_users.py +10 -12
- meerschaum/connectors/sql/tables/__init__.py +13 -14
- meerschaum/core/Pipe/__init__.py +12 -2
- meerschaum/core/Pipe/_attributes.py +32 -38
- meerschaum/core/Pipe/_drop.py +73 -2
- meerschaum/core/Pipe/_index.py +68 -0
- meerschaum/jobs/_Job.py +1 -0
- meerschaum/plugins/__init__.py +7 -3
- meerschaum/utils/daemon/Daemon.py +5 -1
- meerschaum/utils/daemon/__init__.py +2 -2
- meerschaum/utils/dtypes/sql.py +2 -2
- meerschaum/utils/misc.py +7 -6
- meerschaum/utils/packages/__init__.py +31 -27
- meerschaum/utils/packages/_packages.py +1 -1
- meerschaum/utils/prompt.py +54 -36
- meerschaum/utils/sql.py +80 -34
- meerschaum/utils/venv/__init__.py +12 -3
- {meerschaum-2.7.5.dist-info → meerschaum-2.7.7.dist-info}/METADATA +17 -5
- {meerschaum-2.7.5.dist-info → meerschaum-2.7.7.dist-info}/RECORD +50 -48
- {meerschaum-2.7.5.dist-info → meerschaum-2.7.7.dist-info}/WHEEL +1 -1
- {meerschaum-2.7.5.dist-info → meerschaum-2.7.7.dist-info}/LICENSE +0 -0
- {meerschaum-2.7.5.dist-info → meerschaum-2.7.7.dist-info}/NOTICE +0 -0
- {meerschaum-2.7.5.dist-info → meerschaum-2.7.7.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.7.5.dist-info → meerschaum-2.7.7.dist-info}/top_level.txt +0 -0
- {meerschaum-2.7.5.dist-info → meerschaum-2.7.7.dist-info}/zip-safe +0 -0
meerschaum/jobs/_Job.py
CHANGED
meerschaum/plugins/__init__.py
CHANGED
@@ -126,8 +126,8 @@ def pre_sync_hook(
|
|
126
126
|
|
127
127
|
|
128
128
|
def post_sync_hook(
|
129
|
-
|
130
|
-
|
129
|
+
function: Callable[[Any], Any],
|
130
|
+
) -> Callable[[Any], Any]:
|
131
131
|
"""
|
132
132
|
Register a function as a sync hook to be executed upon completion of a sync.
|
133
133
|
|
@@ -143,10 +143,14 @@ def post_sync_hook(
|
|
143
143
|
Examples
|
144
144
|
--------
|
145
145
|
>>> from meerschaum.plugins import post_sync_hook
|
146
|
+
>>> from meerschaum.utils.misc import interval_str
|
147
|
+
>>> from datetime import timedelta
|
146
148
|
>>>
|
147
149
|
>>> @post_sync_hook
|
148
150
|
... def log_sync(pipe, success_tuple, duration=None, **kwargs):
|
149
|
-
...
|
151
|
+
... duration_delta = timedelta(seconds=duration)
|
152
|
+
... duration_text = interval_str(duration_delta)
|
153
|
+
... print(f"It took {duration_text} to sync {pipe}.")
|
150
154
|
>>>
|
151
155
|
"""
|
152
156
|
with _locks['_post_sync_hooks']:
|
@@ -1017,7 +1017,8 @@ class Daemon:
|
|
1017
1017
|
|
1018
1018
|
def read_pickle(self) -> Daemon:
|
1019
1019
|
"""Read a Daemon's pickle file and return the `Daemon`."""
|
1020
|
-
import pickle
|
1020
|
+
import pickle
|
1021
|
+
import traceback
|
1021
1022
|
if not self.pickle_path.exists():
|
1022
1023
|
error(f"Pickle file does not exist for daemon '{self.daemon_id}'.")
|
1023
1024
|
|
@@ -1053,6 +1054,9 @@ class Daemon:
|
|
1053
1054
|
if self._properties is None:
|
1054
1055
|
self._properties = {}
|
1055
1056
|
|
1057
|
+
if self._properties.get('result', None) is None:
|
1058
|
+
_ = self._properties.pop('result', None)
|
1059
|
+
|
1056
1060
|
if _file_properties is not None:
|
1057
1061
|
self._properties = apply_patch_to_config(
|
1058
1062
|
_file_properties,
|
@@ -37,7 +37,7 @@ def daemon_entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
|
|
37
37
|
filtered_sysargs = [arg for arg in sysargs if arg not in ('-d', '--daemon')]
|
38
38
|
try:
|
39
39
|
label = shlex.join(filtered_sysargs) if sysargs else None
|
40
|
-
except Exception
|
40
|
+
except Exception:
|
41
41
|
label = ' '.join(filtered_sysargs) if sysargs else None
|
42
42
|
|
43
43
|
name = _args.get('name', None)
|
@@ -45,7 +45,7 @@ def daemon_entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
|
|
45
45
|
if name:
|
46
46
|
try:
|
47
47
|
daemon = Daemon(daemon_id=name)
|
48
|
-
except Exception
|
48
|
+
except Exception:
|
49
49
|
daemon = None
|
50
50
|
|
51
51
|
if daemon is not None:
|
meerschaum/utils/dtypes/sql.py
CHANGED
@@ -536,7 +536,7 @@ def get_db_type_from_pd_type(
|
|
536
536
|
from meerschaum.utils.packages import attempt_import
|
537
537
|
from meerschaum.utils.dtypes import are_dtypes_equal, MRSM_ALIAS_DTYPES
|
538
538
|
from meerschaum.utils.misc import parse_arguments_str
|
539
|
-
sqlalchemy_types = attempt_import('sqlalchemy.types')
|
539
|
+
sqlalchemy_types = attempt_import('sqlalchemy.types', lazy=False)
|
540
540
|
|
541
541
|
types_registry = (
|
542
542
|
PD_TO_DB_DTYPES_FLAVORS
|
@@ -559,7 +559,7 @@ def get_db_type_from_pd_type(
|
|
559
559
|
found_db_type = True
|
560
560
|
|
561
561
|
if not found_db_type:
|
562
|
-
warn(f"Unknown Pandas data type '{pd_type}'. Falling back to 'TEXT'.")
|
562
|
+
warn(f"Unknown Pandas data type '{pd_type}'. Falling back to 'TEXT'.", stacklevel=3)
|
563
563
|
return (
|
564
564
|
'TEXT'
|
565
565
|
if not as_sqlalchemy
|
meerschaum/utils/misc.py
CHANGED
@@ -90,20 +90,21 @@ def add_method_to_class(
|
|
90
90
|
|
91
91
|
|
92
92
|
def generate_password(length: int = 12) -> str:
|
93
|
-
"""
|
93
|
+
"""
|
94
|
+
Generate a secure password of given length.
|
94
95
|
|
95
96
|
Parameters
|
96
97
|
----------
|
97
|
-
length
|
98
|
+
length: int, default 12
|
98
99
|
The length of the password.
|
99
100
|
|
100
101
|
Returns
|
101
102
|
-------
|
102
103
|
A random password string.
|
103
|
-
|
104
104
|
"""
|
105
|
-
import secrets
|
106
|
-
|
105
|
+
import secrets
|
106
|
+
import string
|
107
|
+
return ''.join((secrets.choice(string.ascii_letters + string.digits) for i in range(length)))
|
107
108
|
|
108
109
|
def is_int(s : str) -> bool:
|
109
110
|
"""
|
@@ -121,7 +122,7 @@ def is_int(s : str) -> bool:
|
|
121
122
|
"""
|
122
123
|
try:
|
123
124
|
float(s)
|
124
|
-
except Exception
|
125
|
+
except Exception:
|
125
126
|
return False
|
126
127
|
|
127
128
|
return float(s).is_integer()
|
@@ -337,14 +337,14 @@ def get_install_no_version(install_name: str) -> str:
|
|
337
337
|
|
338
338
|
import_versions = {}
|
339
339
|
def determine_version(
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
340
|
+
path: pathlib.Path,
|
341
|
+
import_name: Optional[str] = None,
|
342
|
+
venv: Optional[str] = 'mrsm',
|
343
|
+
search_for_metadata: bool = True,
|
344
|
+
split: bool = True,
|
345
|
+
warn: bool = False,
|
346
|
+
debug: bool = False,
|
347
|
+
) -> Union[str, None]:
|
348
348
|
"""
|
349
349
|
Determine a module's `__version__` string from its filepath.
|
350
350
|
|
@@ -381,11 +381,8 @@ def determine_version(
|
|
381
381
|
with _locks['import_versions']:
|
382
382
|
if venv not in import_versions:
|
383
383
|
import_versions[venv] = {}
|
384
|
-
import
|
385
|
-
import re, os
|
384
|
+
import os
|
386
385
|
old_cwd = os.getcwd()
|
387
|
-
if debug:
|
388
|
-
from meerschaum.utils.debug import dprint
|
389
386
|
from meerschaum.utils.warnings import warn as warn_function
|
390
387
|
if import_name is None:
|
391
388
|
import_name = path.parent.stem if path.stem == '__init__' else path.stem
|
@@ -395,7 +392,10 @@ def determine_version(
|
|
395
392
|
_version = None
|
396
393
|
module_parent_dir = (
|
397
394
|
path.parent.parent if path.stem == '__init__' else path.parent
|
398
|
-
) if path is not None else venv_target_path(venv, debug=debug)
|
395
|
+
) if path is not None else venv_target_path(venv, allow_nonexistent=True, debug=debug)
|
396
|
+
|
397
|
+
if not module_parent_dir.exists():
|
398
|
+
return None
|
399
399
|
|
400
400
|
installed_dir_name = _import_to_dir_name(import_name)
|
401
401
|
clean_installed_dir_name = installed_dir_name.lower().replace('-', '_')
|
@@ -403,7 +403,11 @@ def determine_version(
|
|
403
403
|
### First, check if a dist-info directory exists.
|
404
404
|
_found_versions = []
|
405
405
|
if search_for_metadata:
|
406
|
-
|
406
|
+
try:
|
407
|
+
filenames = os.listdir(module_parent_dir)
|
408
|
+
except FileNotFoundError:
|
409
|
+
filenames = []
|
410
|
+
for filename in filenames:
|
407
411
|
if not filename.endswith('.dist-info'):
|
408
412
|
continue
|
409
413
|
filename_lower = filename.lower()
|
@@ -430,7 +434,7 @@ def determine_version(
|
|
430
434
|
try:
|
431
435
|
os.chdir(module_parent_dir)
|
432
436
|
_version = importlib_metadata.metadata(import_name)['Version']
|
433
|
-
except Exception
|
437
|
+
except Exception:
|
434
438
|
_version = None
|
435
439
|
finally:
|
436
440
|
os.chdir(old_cwd)
|
@@ -698,7 +702,7 @@ def need_update(
|
|
698
702
|
(not semver.Version.parse(version).match(required_version))
|
699
703
|
if required_version else False
|
700
704
|
)
|
701
|
-
except AttributeError
|
705
|
+
except AttributeError:
|
702
706
|
pip_install(_import_to_install_name('semver'), venv='mrsm', debug=debug)
|
703
707
|
semver = manually_import_module('semver', venv='mrsm', debug=debug)
|
704
708
|
return (
|
@@ -724,10 +728,10 @@ def need_update(
|
|
724
728
|
|
725
729
|
|
726
730
|
def get_pip(
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
+
venv: Optional[str] = 'mrsm',
|
732
|
+
color: bool = True,
|
733
|
+
debug: bool = False,
|
734
|
+
) -> bool:
|
731
735
|
"""
|
732
736
|
Download and run the get-pip.py script.
|
733
737
|
|
@@ -747,7 +751,8 @@ def get_pip(
|
|
747
751
|
A bool indicating success.
|
748
752
|
|
749
753
|
"""
|
750
|
-
import sys
|
754
|
+
import sys
|
755
|
+
import subprocess
|
751
756
|
from meerschaum.utils.misc import wget
|
752
757
|
from meerschaum.config._paths import CACHE_RESOURCES_PATH
|
753
758
|
from meerschaum.config.static import STATIC_CONFIG
|
@@ -755,7 +760,7 @@ def get_pip(
|
|
755
760
|
dest = CACHE_RESOURCES_PATH / 'get-pip.py'
|
756
761
|
try:
|
757
762
|
wget(url, dest, color=False, debug=debug)
|
758
|
-
except Exception
|
763
|
+
except Exception:
|
759
764
|
print(f"Failed to fetch pip from '{url}'. Please install pip and restart Meerschaum.")
|
760
765
|
sys.exit(1)
|
761
766
|
if venv is not None:
|
@@ -776,6 +781,7 @@ def pip_install(
|
|
776
781
|
_uninstall: bool = False,
|
777
782
|
_from_completely_uninstall: bool = False,
|
778
783
|
_install_uv_pip: bool = True,
|
784
|
+
_use_uv_pip: bool = True,
|
779
785
|
color: bool = True,
|
780
786
|
silent: bool = False,
|
781
787
|
debug: bool = False,
|
@@ -835,10 +841,7 @@ def pip_install(
|
|
835
841
|
from meerschaum.utils.warnings import warn
|
836
842
|
if args is None:
|
837
843
|
args = ['--upgrade'] if not _uninstall else []
|
838
|
-
if color
|
839
|
-
ANSI, UNICODE = True, True
|
840
|
-
else:
|
841
|
-
ANSI, UNICODE = False, False
|
844
|
+
ANSI = True if color else False
|
842
845
|
if check_wheel:
|
843
846
|
have_wheel = venv_contains_package('wheel', venv=venv, debug=debug)
|
844
847
|
|
@@ -877,7 +880,8 @@ def pip_install(
|
|
877
880
|
)
|
878
881
|
|
879
882
|
use_uv_pip = (
|
880
|
-
|
883
|
+
_use_uv_pip
|
884
|
+
and venv_contains_package('uv', venv=None, debug=debug)
|
881
885
|
and uv_bin is not None
|
882
886
|
and venv is not None
|
883
887
|
and is_uv_enabled()
|
@@ -136,7 +136,7 @@ packages['sql'] = {
|
|
136
136
|
'numpy' : 'numpy>=1.18.5',
|
137
137
|
'pandas' : 'pandas[parquet]>=2.0.1',
|
138
138
|
'pyarrow' : 'pyarrow>=16.1.0',
|
139
|
-
'dask' : 'dask[complete]>=2024.
|
139
|
+
'dask' : 'dask[complete]>=2024.12.1',
|
140
140
|
'partd' : 'partd>=1.4.2',
|
141
141
|
'pytz' : 'pytz',
|
142
142
|
'joblib' : 'joblib>=0.17.0',
|
meerschaum/utils/prompt.py
CHANGED
@@ -7,7 +7,9 @@ Functions for interacting with the user.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
+
|
10
11
|
import os
|
12
|
+
import meerschaum as mrsm
|
11
13
|
from meerschaum.utils.typing import Any, Union, Optional, Tuple, List
|
12
14
|
|
13
15
|
|
@@ -63,9 +65,8 @@ def prompt(
|
|
63
65
|
|
64
66
|
"""
|
65
67
|
from meerschaum.utils.packages import attempt_import
|
66
|
-
from meerschaum.utils.formatting import
|
68
|
+
from meerschaum.utils.formatting import ANSI, CHARSET, highlight_pipes, fill_ansi
|
67
69
|
from meerschaum.config import get_config
|
68
|
-
from meerschaum.config.static import _static_config
|
69
70
|
from meerschaum.utils.misc import filter_keywords
|
70
71
|
from meerschaum.utils.daemon import running_in_daemon
|
71
72
|
noask = check_noask(noask)
|
@@ -175,8 +176,6 @@ def yes_no(
|
|
175
176
|
```
|
176
177
|
"""
|
177
178
|
from meerschaum.utils.warnings import error, warn
|
178
|
-
from meerschaum.utils.formatting import ANSI, UNICODE
|
179
|
-
from meerschaum.utils.packages import attempt_import
|
180
179
|
|
181
180
|
default = options[0] if yes else default
|
182
181
|
noask = yes or check_noask(noask)
|
@@ -195,7 +194,7 @@ def yes_no(
|
|
195
194
|
success = False
|
196
195
|
|
197
196
|
if not success:
|
198
|
-
error(
|
197
|
+
error("Error getting response. Aborting...", stack=False)
|
199
198
|
if answer == "":
|
200
199
|
answer = default
|
201
200
|
|
@@ -205,19 +204,20 @@ def yes_no(
|
|
205
204
|
|
206
205
|
return answer.lower() == options[0].lower()
|
207
206
|
|
207
|
+
|
208
208
|
def choose(
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
209
|
+
question: str,
|
210
|
+
choices: List[Union[str, Tuple[str, str]]],
|
211
|
+
default: Union[str, List[str], None] = None,
|
212
|
+
numeric: bool = True,
|
213
|
+
multiple: bool = False,
|
214
|
+
as_indices: bool = False,
|
215
|
+
delimiter: str = ',',
|
216
|
+
icon: bool = True,
|
217
|
+
warn: bool = True,
|
218
|
+
noask: bool = False,
|
219
|
+
**kw
|
220
|
+
) -> Union[str, Tuple[str], None]:
|
221
221
|
"""
|
222
222
|
Present a list of options and return the user's choice.
|
223
223
|
|
@@ -418,8 +418,8 @@ def choose(
|
|
418
418
|
valid = (len(answers) > 1 or not (len(answers) == 1 and answers[0] is None))
|
419
419
|
for a in answers:
|
420
420
|
if (
|
421
|
-
not
|
422
|
-
and not
|
421
|
+
a not in {_original for _new, _original in altered_choices.items()}
|
422
|
+
and a not in _choices
|
423
423
|
and a != default
|
424
424
|
and not noask
|
425
425
|
):
|
@@ -428,21 +428,22 @@ def choose(
|
|
428
428
|
if valid:
|
429
429
|
break
|
430
430
|
if warn:
|
431
|
-
_warn(
|
431
|
+
_warn("Please pick a valid choice.", stack=False)
|
432
432
|
|
433
433
|
if not multiple:
|
434
434
|
if not numeric:
|
435
435
|
return answer
|
436
436
|
try:
|
437
437
|
_answer = choices[int(answer) - 1]
|
438
|
-
if as_indices and isinstance(
|
438
|
+
if as_indices and isinstance(_answer, tuple):
|
439
439
|
return _answer[0]
|
440
440
|
return _answer
|
441
|
-
except Exception
|
441
|
+
except Exception:
|
442
442
|
_warn(f"Could not cast answer '{answer}' to an integer.", stacklevel=3)
|
443
443
|
|
444
444
|
if not numeric:
|
445
445
|
return answers
|
446
|
+
|
446
447
|
_answers = []
|
447
448
|
for a in answers:
|
448
449
|
try:
|
@@ -451,17 +452,17 @@ def choose(
|
|
451
452
|
if isinstance(_answer_to_return, tuple) and as_indices:
|
452
453
|
_answer_to_return = _answer_to_return[0]
|
453
454
|
_answers.append(_answer_to_return)
|
454
|
-
except Exception
|
455
|
+
except Exception:
|
455
456
|
_warn(f"Could not cast answer '{a}' to an integer.", stacklevel=3)
|
456
457
|
return _answers
|
457
458
|
|
458
459
|
|
459
460
|
def get_password(
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
461
|
+
username: Optional[str] = None,
|
462
|
+
minimum_length: Optional[int] = None,
|
463
|
+
confirm: bool = True,
|
464
|
+
**kw: Any
|
465
|
+
) -> str:
|
465
466
|
"""
|
466
467
|
Prompt the user for a password.
|
467
468
|
|
@@ -493,15 +494,15 @@ def get_password(
|
|
493
494
|
from meerschaum.utils.warnings import warn
|
494
495
|
while True:
|
495
496
|
password = prompt(
|
496
|
-
|
497
|
-
is_password
|
497
|
+
"Password" + (f" for user '{username}':" if username is not None else ":"),
|
498
|
+
is_password=True,
|
498
499
|
**kw
|
499
500
|
)
|
500
501
|
if minimum_length is not None and len(password) < minimum_length:
|
501
502
|
warn(
|
502
503
|
"Password is too short. " +
|
503
504
|
f"Please enter a password that is at least {minimum_length} characters.",
|
504
|
-
stack
|
505
|
+
stack=False
|
505
506
|
)
|
506
507
|
continue
|
507
508
|
|
@@ -509,12 +510,12 @@ def get_password(
|
|
509
510
|
return password
|
510
511
|
|
511
512
|
_password = prompt(
|
512
|
-
|
513
|
-
is_password
|
513
|
+
"Confirm password" + (f" for user '{username}':" if username is not None else ":"),
|
514
|
+
is_password=True,
|
514
515
|
**kw
|
515
516
|
)
|
516
517
|
if password != _password:
|
517
|
-
warn(
|
518
|
+
warn("Passwords do not match! Please try again.", stack=False)
|
518
519
|
continue
|
519
520
|
else:
|
520
521
|
return password
|
@@ -550,13 +551,13 @@ def get_email(username: Optional[str] = None, allow_omit: bool = True, **kw: Any
|
|
550
551
|
from meerschaum.utils.misc import is_valid_email
|
551
552
|
while True:
|
552
553
|
email = prompt(
|
553
|
-
|
554
|
+
"Email" + (f" for user '{username}'" if username is not None else "") +
|
554
555
|
(" (empty to omit):" if allow_omit else ": "),
|
555
556
|
**kw
|
556
557
|
)
|
557
558
|
if (allow_omit and email == '') or is_valid_email(email):
|
558
559
|
return email
|
559
|
-
warn(
|
560
|
+
warn("Invalid email! Please try again.", stack=False)
|
560
561
|
|
561
562
|
|
562
563
|
def check_noask(noask: bool = False) -> bool:
|
@@ -571,3 +572,20 @@ def check_noask(noask: bool = False) -> bool:
|
|
571
572
|
os.environ.get(NOASK, 'false').lower()
|
572
573
|
in ('1', 'true')
|
573
574
|
)
|
575
|
+
|
576
|
+
|
577
|
+
def get_connectors_completer(*types: str):
|
578
|
+
"""
|
579
|
+
Return a prompt-toolkit Completer object to pass into `prompt()`.
|
580
|
+
"""
|
581
|
+
from meerschaum.utils.misc import get_connector_labels
|
582
|
+
prompt_toolkit_completion = mrsm.attempt_import('prompt_toolkit.completion', lazy=False)
|
583
|
+
Completer = prompt_toolkit_completion.Completer
|
584
|
+
Completion = prompt_toolkit_completion.Completion
|
585
|
+
|
586
|
+
class ConnectorCompleter(Completer):
|
587
|
+
def get_completions(self, document, complete_event):
|
588
|
+
for label in get_connector_labels(*types):
|
589
|
+
yield Completion(label, start_position=(-1 * len(document.text)))
|
590
|
+
|
591
|
+
return ConnectorCompleter()
|