meerschaum 2.1.6__py3-none-any.whl → 2.2.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.
- meerschaum/__main__.py +1 -1
- meerschaum/_internal/arguments/_parser.py +3 -0
- meerschaum/_internal/entry.py +3 -2
- meerschaum/_internal/shell/Shell.py +1 -6
- meerschaum/actions/api.py +1 -1
- meerschaum/actions/install.py +7 -3
- meerschaum/actions/show.py +128 -42
- meerschaum/actions/sync.py +7 -3
- meerschaum/api/__init__.py +24 -14
- meerschaum/api/_oauth2.py +4 -4
- meerschaum/api/dash/callbacks/dashboard.py +93 -23
- meerschaum/api/dash/callbacks/jobs.py +55 -3
- meerschaum/api/dash/jobs.py +34 -8
- meerschaum/api/dash/keys.py +1 -1
- meerschaum/api/dash/pages/dashboard.py +14 -4
- meerschaum/api/dash/pipes.py +137 -26
- meerschaum/api/dash/plugins.py +25 -9
- meerschaum/api/resources/static/js/xterm.js +1 -1
- meerschaum/api/resources/templates/termpage.html +3 -0
- meerschaum/api/routes/_login.py +5 -4
- meerschaum/api/routes/_plugins.py +6 -3
- meerschaum/config/_dash.py +11 -0
- meerschaum/config/_default.py +3 -1
- meerschaum/config/_jobs.py +13 -4
- meerschaum/config/_paths.py +2 -0
- meerschaum/config/_shell.py +0 -1
- meerschaum/config/_sync.py +2 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +6 -7
- meerschaum/config/stack/grafana/__init__.py +1 -1
- meerschaum/config/static/__init__.py +4 -1
- meerschaum/connectors/__init__.py +2 -0
- meerschaum/connectors/api/_plugins.py +2 -1
- meerschaum/connectors/sql/SQLConnector.py +4 -2
- meerschaum/connectors/sql/_create_engine.py +9 -9
- meerschaum/connectors/sql/_fetch.py +8 -11
- meerschaum/connectors/sql/_instance.py +3 -1
- meerschaum/connectors/sql/_pipes.py +61 -39
- meerschaum/connectors/sql/_plugins.py +0 -2
- meerschaum/connectors/sql/_sql.py +7 -9
- meerschaum/core/Pipe/_dtypes.py +2 -1
- meerschaum/core/Pipe/_sync.py +26 -13
- meerschaum/core/User/_User.py +158 -16
- meerschaum/core/User/__init__.py +1 -1
- meerschaum/plugins/_Plugin.py +12 -3
- meerschaum/plugins/__init__.py +23 -1
- meerschaum/utils/daemon/Daemon.py +89 -36
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +140 -0
- meerschaum/utils/daemon/RotatingFile.py +130 -14
- meerschaum/utils/daemon/__init__.py +3 -0
- meerschaum/utils/dataframe.py +183 -8
- meerschaum/utils/dtypes/__init__.py +9 -5
- meerschaum/utils/formatting/_pipes.py +44 -10
- meerschaum/utils/misc.py +34 -2
- meerschaum/utils/packages/__init__.py +25 -8
- meerschaum/utils/packages/_packages.py +18 -20
- meerschaum/utils/process.py +13 -10
- meerschaum/utils/schedule.py +276 -30
- meerschaum/utils/threading.py +1 -0
- meerschaum/utils/typing.py +1 -1
- {meerschaum-2.1.6.dist-info → meerschaum-2.2.0.dist-info}/METADATA +59 -62
- {meerschaum-2.1.6.dist-info → meerschaum-2.2.0.dist-info}/RECORD +68 -66
- {meerschaum-2.1.6.dist-info → meerschaum-2.2.0.dist-info}/WHEEL +1 -1
- {meerschaum-2.1.6.dist-info → meerschaum-2.2.0.dist-info}/LICENSE +0 -0
- {meerschaum-2.1.6.dist-info → meerschaum-2.2.0.dist-info}/NOTICE +0 -0
- {meerschaum-2.1.6.dist-info → meerschaum-2.2.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.1.6.dist-info → meerschaum-2.2.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.1.6.dist-info → meerschaum-2.2.0.dist-info}/zip-safe +0 -0
@@ -6,8 +6,10 @@
|
|
6
6
|
Utility functions for working with data types.
|
7
7
|
"""
|
8
8
|
|
9
|
+
import traceback
|
9
10
|
from decimal import Decimal, Context, InvalidOperation
|
10
11
|
from meerschaum.utils.typing import Dict, Union, Any
|
12
|
+
from meerschaum.utils.warnings import warn
|
11
13
|
|
12
14
|
MRSM_PD_DTYPES: Dict[str, str] = {
|
13
15
|
'json': 'object',
|
@@ -37,9 +39,7 @@ def to_pandas_dtype(dtype: str) -> str:
|
|
37
39
|
from meerschaum.utils.dtypes.sql import get_pd_type_from_db_type
|
38
40
|
return get_pd_type_from_db_type(dtype)
|
39
41
|
|
40
|
-
import traceback
|
41
42
|
from meerschaum.utils.packages import attempt_import
|
42
|
-
from meerschaum.utils.warnings import warn
|
43
43
|
pandas = attempt_import('pandas', lazy=False)
|
44
44
|
|
45
45
|
try:
|
@@ -88,8 +88,12 @@ def are_dtypes_equal(
|
|
88
88
|
return False
|
89
89
|
return True
|
90
90
|
|
91
|
-
|
92
|
-
|
91
|
+
try:
|
92
|
+
if ldtype == rdtype:
|
93
|
+
return True
|
94
|
+
except Exception as e:
|
95
|
+
warn(f"Exception when comparing dtypes, returning False:\n{traceback.format_exc()}")
|
96
|
+
return False
|
93
97
|
|
94
98
|
### Sometimes pandas dtype objects are passed.
|
95
99
|
ldtype = str(ldtype)
|
@@ -177,7 +181,7 @@ def attempt_cast_to_numeric(value: Any) -> Any:
|
|
177
181
|
return value
|
178
182
|
|
179
183
|
|
180
|
-
def value_is_null(value: Any) ->
|
184
|
+
def value_is_null(value: Any) -> bool:
|
181
185
|
"""
|
182
186
|
Determine if a value is a null-like string.
|
183
187
|
"""
|
@@ -9,7 +9,7 @@ Formatting functions for printing pipes
|
|
9
9
|
from __future__ import annotations
|
10
10
|
import json
|
11
11
|
import meerschaum as mrsm
|
12
|
-
from meerschaum.utils.typing import PipesDict, Dict, Union, Optional, SuccessTuple, Any
|
12
|
+
from meerschaum.utils.typing import PipesDict, Dict, Union, Optional, SuccessTuple, Any, List
|
13
13
|
from meerschaum.config import get_config
|
14
14
|
|
15
15
|
def pprint_pipes(pipes: PipesDict) -> None:
|
@@ -481,22 +481,54 @@ def print_pipes_results(
|
|
481
481
|
)
|
482
482
|
|
483
483
|
|
484
|
-
def extract_stats_from_message(
|
484
|
+
def extract_stats_from_message(
|
485
|
+
message: str,
|
486
|
+
stat_keys: Optional[List[str]] = None,
|
487
|
+
) -> Dict[str, int]:
|
485
488
|
"""
|
486
|
-
Given a sync message, return the insert, update stats from within.
|
489
|
+
Given a sync message, return the insert, update, upsert stats from within.
|
490
|
+
|
491
|
+
Parameters
|
492
|
+
----------
|
493
|
+
message: str
|
494
|
+
The message to parse for statistics.
|
495
|
+
|
496
|
+
stat_keys: Optional[List[str]], default None
|
497
|
+
If provided, search for these words (case insensitive) in the message.
|
498
|
+
Defaults to `['inserted', 'updated', 'upserted']`.
|
499
|
+
|
500
|
+
Returns
|
501
|
+
-------
|
502
|
+
A dictionary mapping the stat keys to the total number of rows affected.
|
487
503
|
"""
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
504
|
+
stat_keys = stat_keys or ['inserted', 'updated', 'upserted']
|
505
|
+
lines_stats = [extract_stats_from_line(line, stat_keys) for line in message.split('\n')]
|
506
|
+
message_stats = {
|
507
|
+
stat_key: sum(stats.get(stat_key, 0) for stats in lines_stats)
|
508
|
+
for stat_key in stat_keys
|
492
509
|
}
|
510
|
+
return message_stats
|
493
511
|
|
494
|
-
|
495
|
-
|
512
|
+
|
513
|
+
def extract_stats_from_line(
|
514
|
+
line: str,
|
515
|
+
stat_keys: List[str],
|
516
|
+
) -> Dict[str, int]:
|
517
|
+
"""
|
518
|
+
Return the insert, update, upsert stats from a single line.
|
519
|
+
"""
|
520
|
+
stats = {key: 0 for key in stat_keys}
|
521
|
+
|
522
|
+
for stat_key in stat_keys:
|
523
|
+
search_key = stat_key.lower()
|
524
|
+
if search_key not in line.lower():
|
496
525
|
continue
|
497
526
|
|
498
527
|
### stat_text starts with the digits we want.
|
499
|
-
|
528
|
+
try:
|
529
|
+
stat_text = line.lower().split(search_key + ' ')[1]
|
530
|
+
except IndexError:
|
531
|
+
continue
|
500
532
|
|
501
533
|
### find the first non-digit value.
|
502
534
|
end_of_num_ix = -1
|
@@ -504,6 +536,8 @@ def extract_stats_from_message(message: str) -> Dict[str, int]:
|
|
504
536
|
if not char.isdigit():
|
505
537
|
end_of_num_ix = i
|
506
538
|
break
|
539
|
+
if i == len(stat_text) - 1:
|
540
|
+
end_of_num_ix = i + 1
|
507
541
|
if end_of_num_ix == -1:
|
508
542
|
continue
|
509
543
|
|
meerschaum/utils/misc.py
CHANGED
@@ -1234,7 +1234,7 @@ def truncate_string_sections(item: str, delimeter: str = '_', max_len: int = 128
|
|
1234
1234
|
|
1235
1235
|
|
1236
1236
|
def separate_negation_values(
|
1237
|
-
vals: List[str],
|
1237
|
+
vals: Union[List[str], Tuple[str]],
|
1238
1238
|
negation_prefix: Optional[str] = None,
|
1239
1239
|
) -> Tuple[List[str], List[str]]:
|
1240
1240
|
"""
|
@@ -1243,7 +1243,7 @@ def separate_negation_values(
|
|
1243
1243
|
|
1244
1244
|
Parameters
|
1245
1245
|
----------
|
1246
|
-
vals: List[str]
|
1246
|
+
vals: Union[List[str], Tuple[str]]
|
1247
1247
|
A list of strings to parse.
|
1248
1248
|
|
1249
1249
|
negation_prefix: Optional[str], default None
|
@@ -1263,6 +1263,38 @@ def separate_negation_values(
|
|
1263
1263
|
return _in_vals, _ex_vals
|
1264
1264
|
|
1265
1265
|
|
1266
|
+
def get_in_ex_params(params: Optional[Dict[str, Any]]) -> Dict[str, Tuple[List[Any], List[Any]]]:
|
1267
|
+
"""
|
1268
|
+
Translate a params dictionary into lists of include- and exclude-values.
|
1269
|
+
|
1270
|
+
Parameters
|
1271
|
+
----------
|
1272
|
+
params: Optional[Dict[str, Any]]
|
1273
|
+
A params query dictionary.
|
1274
|
+
|
1275
|
+
Returns
|
1276
|
+
-------
|
1277
|
+
A dictionary mapping keys to a tuple of lists for include and exclude values.
|
1278
|
+
|
1279
|
+
Examples
|
1280
|
+
--------
|
1281
|
+
>>> get_in_ex_params({'a': ['b', 'c', '_d', 'e', '_f']})
|
1282
|
+
{'a': (['b', 'c', 'e'], ['d', 'f'])}
|
1283
|
+
"""
|
1284
|
+
if not params:
|
1285
|
+
return {}
|
1286
|
+
return {
|
1287
|
+
col: separate_negation_values(
|
1288
|
+
(
|
1289
|
+
val
|
1290
|
+
if isinstance(val, (list, tuple))
|
1291
|
+
else [val]
|
1292
|
+
)
|
1293
|
+
)
|
1294
|
+
for col, val in params.items()
|
1295
|
+
}
|
1296
|
+
|
1297
|
+
|
1266
1298
|
def flatten_list(list_: List[Any]) -> List[Any]:
|
1267
1299
|
"""
|
1268
1300
|
Recursively flatten a list.
|
@@ -8,7 +8,7 @@ Functions for managing packages and virtual environments reside here.
|
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
10
|
|
11
|
-
import importlib.util, os, pathlib
|
11
|
+
import importlib.util, os, pathlib, re
|
12
12
|
from meerschaum.utils.typing import Any, List, SuccessTuple, Optional, Union, Tuple, Dict, Iterable
|
13
13
|
from meerschaum.utils.threading import Lock, RLock
|
14
14
|
from meerschaum.utils.packages._packages import packages, all_packages, get_install_names
|
@@ -35,6 +35,7 @@ _locks = {
|
|
35
35
|
}
|
36
36
|
_checked_for_updates = set()
|
37
37
|
_is_installed_first_check: Dict[str, bool] = {}
|
38
|
+
_MRSM_PACKAGE_ARCHIVES_PREFIX: str = "https://meerschaum.io/files/archives/"
|
38
39
|
|
39
40
|
def get_module_path(
|
40
41
|
import_name: str,
|
@@ -350,6 +351,7 @@ def determine_version(
|
|
350
351
|
with _locks['import_versions']:
|
351
352
|
if venv not in import_versions:
|
352
353
|
import_versions[venv] = {}
|
354
|
+
import importlib.metadata
|
353
355
|
import re, os
|
354
356
|
old_cwd = os.getcwd()
|
355
357
|
if debug:
|
@@ -639,6 +641,15 @@ def need_update(
|
|
639
641
|
|
640
642
|
### We might be depending on a prerelease.
|
641
643
|
### Sanity check that the required version is not greater than the installed version.
|
644
|
+
required_version = (
|
645
|
+
required_version.replace(_MRSM_PACKAGE_ARCHIVES_PREFIX, '')
|
646
|
+
.replace(' @ ', '').replace('wheels', '').replace('+mrsm', '').replace('/-', '')
|
647
|
+
.replace('-py3-none-any.whl', '')
|
648
|
+
)
|
649
|
+
|
650
|
+
if 'a' in required_version:
|
651
|
+
required_version = required_version.replace('a', '-dev').replace('+mrsm', '')
|
652
|
+
version = version.replace('a', '-dev').replace('+mrsm', '')
|
642
653
|
try:
|
643
654
|
return (
|
644
655
|
(not semver.Version.parse(version).match(required_version))
|
@@ -818,8 +829,11 @@ def pip_install(
|
|
818
829
|
check_wheel = False, debug = debug,
|
819
830
|
):
|
820
831
|
warn(
|
821
|
-
|
822
|
-
|
832
|
+
(
|
833
|
+
"Failed to install `setuptools` and `wheel` for virtual "
|
834
|
+
+ f"environment '{venv}'."
|
835
|
+
),
|
836
|
+
color = False,
|
823
837
|
)
|
824
838
|
|
825
839
|
if requirements_file_path is not None:
|
@@ -882,13 +896,16 @@ def pip_install(
|
|
882
896
|
f"Failed to clean up package '{_install_no_version}'.",
|
883
897
|
)
|
884
898
|
|
885
|
-
|
899
|
+
rc = run_python_package(
|
886
900
|
'pip',
|
887
901
|
_args + _packages,
|
888
902
|
venv = venv,
|
889
903
|
env = _get_pip_os_env(),
|
890
904
|
debug = debug,
|
891
|
-
)
|
905
|
+
)
|
906
|
+
if debug:
|
907
|
+
print(f"{rc=}")
|
908
|
+
success = rc == 0
|
892
909
|
|
893
910
|
msg = (
|
894
911
|
"Successfully " + ('un' if _uninstall else '') + "installed packages." if success
|
@@ -1379,13 +1396,13 @@ def get_modules_from_package(
|
|
1379
1396
|
Returns
|
1380
1397
|
-------
|
1381
1398
|
Either list of modules or tuple of lists.
|
1382
|
-
|
1383
1399
|
"""
|
1384
1400
|
from os.path import dirname, join, isfile, isdir, basename
|
1385
1401
|
import glob
|
1386
1402
|
|
1387
1403
|
pattern = '*' if recursive else '*.py'
|
1388
|
-
|
1404
|
+
package_path = dirname(package.__file__ or package.__path__[0])
|
1405
|
+
module_names = glob.glob(join(package_path, pattern), recursive=recursive)
|
1389
1406
|
_all = [
|
1390
1407
|
basename(f)[:-3] if isfile(f) else basename(f)
|
1391
1408
|
for f in module_names
|
@@ -1410,7 +1427,7 @@ def get_modules_from_package(
|
|
1410
1427
|
modules.append(m)
|
1411
1428
|
except Exception as e:
|
1412
1429
|
if debug:
|
1413
|
-
dprint(e)
|
1430
|
+
dprint(str(e))
|
1414
1431
|
finally:
|
1415
1432
|
if modules_venvs:
|
1416
1433
|
deactivate_venv(module_name.split('.')[-1], debug=debug)
|
@@ -30,7 +30,7 @@ packages: Dict[str, Dict[str, str]] = {
|
|
30
30
|
'more_termcolor' : 'more-termcolor>=1.1.3',
|
31
31
|
'humanfriendly' : 'humanfriendly>=10.0.0',
|
32
32
|
},
|
33
|
-
'
|
33
|
+
'core': {
|
34
34
|
'wheel' : 'wheel>=0.34.2',
|
35
35
|
'setuptools' : 'setuptools>=63.3.0',
|
36
36
|
'yaml' : 'PyYAML>=5.3.1',
|
@@ -49,21 +49,21 @@ packages: Dict[str, Dict[str, str]] = {
|
|
49
49
|
'daemon' : 'python-daemon>=0.2.3',
|
50
50
|
'fasteners' : 'fasteners>=0.18.0',
|
51
51
|
'psutil' : 'psutil>=5.8.0',
|
52
|
-
'
|
52
|
+
'watchfiles' : 'watchfiles>=0.21.0',
|
53
53
|
'dill' : 'dill>=0.3.3',
|
54
54
|
'virtualenv' : 'virtualenv>=20.1.0',
|
55
|
-
'
|
55
|
+
'apscheduler' : 'APScheduler>=4.0.0a5',
|
56
56
|
},
|
57
57
|
'drivers': {
|
58
58
|
'cryptography' : 'cryptography>=38.0.1',
|
59
|
-
'
|
59
|
+
'psycopg' : 'psycopg[binary]>=3.1.18',
|
60
60
|
'pymysql' : 'PyMySQL>=0.9.0',
|
61
61
|
'aiomysql' : 'aiomysql>=0.0.21',
|
62
62
|
'sqlalchemy_cockroachdb' : 'sqlalchemy-cockroachdb>=2.0.0',
|
63
|
-
'duckdb' : 'duckdb
|
63
|
+
'duckdb' : 'duckdb<0.10.3',
|
64
64
|
'duckdb_engine' : 'duckdb-engine>=0.9.2',
|
65
65
|
},
|
66
|
-
'
|
66
|
+
'drivers-extras': {
|
67
67
|
'pyodbc' : 'pyodbc>=4.0.30',
|
68
68
|
'cx_Oracle' : 'cx_Oracle>=8.3.0',
|
69
69
|
},
|
@@ -75,11 +75,11 @@ packages: Dict[str, Dict[str, str]] = {
|
|
75
75
|
'gadwall' : 'gadwall>=0.2.0',
|
76
76
|
},
|
77
77
|
'stack': {
|
78
|
-
'compose' : 'docker-compose>=1.
|
78
|
+
'compose' : 'docker-compose>=1.29.2',
|
79
79
|
},
|
80
80
|
'build': {
|
81
|
-
'cx_Freeze' : 'cx_Freeze>=
|
82
|
-
'PyInstaller' : 'pyinstaller
|
81
|
+
'cx_Freeze' : 'cx_Freeze>=7.0.0',
|
82
|
+
'PyInstaller' : 'pyinstaller>6.6.0',
|
83
83
|
},
|
84
84
|
'dev-tools': {
|
85
85
|
'twine' : 'twine>=3.2.0',
|
@@ -89,6 +89,7 @@ packages: Dict[str, Dict[str, str]] = {
|
|
89
89
|
'pytest' : 'pytest>=6.2.2',
|
90
90
|
'pytest_xdist' : 'pytest-xdist>=3.2.1',
|
91
91
|
'heartrate' : 'heartrate>=0.2.1',
|
92
|
+
'build' : 'build>=1.2.1',
|
92
93
|
},
|
93
94
|
'setup': {
|
94
95
|
},
|
@@ -119,8 +120,8 @@ packages: Dict[str, Dict[str, str]] = {
|
|
119
120
|
packages['sql'] = {
|
120
121
|
'numpy' : 'numpy>=1.18.5',
|
121
122
|
'pandas' : 'pandas[parquet]>=2.0.1',
|
122
|
-
'pyarrow' : 'pyarrow>=
|
123
|
-
'dask' : 'dask>=
|
123
|
+
'pyarrow' : 'pyarrow>=16.1.0',
|
124
|
+
'dask' : 'dask[dataframe]>=2024.5.1',
|
124
125
|
'pytz' : 'pytz',
|
125
126
|
'joblib' : 'joblib>=0.17.0',
|
126
127
|
'sqlalchemy' : 'SQLAlchemy>=2.0.5',
|
@@ -129,7 +130,7 @@ packages['sql'] = {
|
|
129
130
|
'asyncpg' : 'asyncpg>=0.21.0',
|
130
131
|
}
|
131
132
|
packages['sql'].update(packages['drivers'])
|
132
|
-
packages['sql'].update(packages['
|
133
|
+
packages['sql'].update(packages['core'])
|
133
134
|
packages['dash'] = {
|
134
135
|
'flask_compress' : 'Flask-Compress>=1.10.1',
|
135
136
|
'dash' : 'dash>=2.6.2',
|
@@ -141,17 +142,14 @@ packages['dash'] = {
|
|
141
142
|
'tornado' : 'tornado>=6.1.0',
|
142
143
|
}
|
143
144
|
packages['api'] = {
|
144
|
-
'uvicorn' : 'uvicorn[standard]>=0.
|
145
|
-
'gunicorn' : 'gunicorn>=
|
145
|
+
'uvicorn' : 'uvicorn[standard]>=0.29.0',
|
146
|
+
'gunicorn' : 'gunicorn>=22.0.0',
|
146
147
|
'dotenv' : 'python-dotenv>=0.20.0',
|
147
148
|
'websockets' : 'websockets>=11.0.3',
|
148
|
-
'fastapi' : 'fastapi>=0.
|
149
|
-
'passlib' : 'passlib>=1.7.4',
|
149
|
+
'fastapi' : 'fastapi>=0.111.0',
|
150
150
|
'fastapi_login' : 'fastapi-login>=1.7.2',
|
151
|
-
'multipart' : 'python-multipart>=0.0.
|
152
|
-
'pydantic' : 'pydantic<2.0.0',
|
151
|
+
'multipart' : 'python-multipart>=0.0.9',
|
153
152
|
'httpx' : 'httpx>=0.24.1',
|
154
|
-
'websockets' : 'websockets>=11.0.3',
|
155
153
|
}
|
156
154
|
packages['api'].update(packages['sql'])
|
157
155
|
packages['api'].update(packages['formatting'])
|
@@ -171,7 +169,7 @@ def get_install_names():
|
|
171
169
|
install_names[get_install_no_version(_install_name)] = _import_name
|
172
170
|
return install_names
|
173
171
|
|
174
|
-
skip_groups = {'docs', 'build', 'cli', 'dev-tools', 'portable', 'extras', 'stack', '
|
172
|
+
skip_groups = {'docs', 'build', 'cli', 'dev-tools', 'portable', 'extras', 'stack', 'drivers-extras'}
|
175
173
|
full = []
|
176
174
|
_full = {}
|
177
175
|
for group, import_names in packages.items():
|
meerschaum/utils/process.py
CHANGED
@@ -11,6 +11,7 @@ See `meerschaum.utils.pool` for multiprocessing and
|
|
11
11
|
from __future__ import annotations
|
12
12
|
import os, signal, subprocess, sys, platform
|
13
13
|
from meerschaum.utils.typing import Union, Optional, Any, Callable, Dict, Tuple
|
14
|
+
from meerschaum.config.static import STATIC_CONFIG
|
14
15
|
|
15
16
|
def run_process(
|
16
17
|
*args,
|
@@ -68,9 +69,18 @@ def run_process(
|
|
68
69
|
if platform.system() == 'Windows':
|
69
70
|
foreground = False
|
70
71
|
|
71
|
-
|
72
|
+
def print_line(line):
|
73
|
+
sys.stdout.write(line.decode('utf-8'))
|
74
|
+
sys.stdout.flush()
|
75
|
+
|
76
|
+
if capture_output or line_callback is not None:
|
77
|
+
kw['stdout'] = subprocess.PIPE
|
78
|
+
kw['stderr'] = subprocess.STDOUT
|
79
|
+
elif os.environ.get(STATIC_CONFIG['environment']['daemon_id']):
|
72
80
|
kw['stdout'] = subprocess.PIPE
|
73
81
|
kw['stderr'] = subprocess.STDOUT
|
82
|
+
if line_callback is None:
|
83
|
+
line_callback = print_line
|
74
84
|
|
75
85
|
if 'env' not in kw:
|
76
86
|
kw['env'] = os.environ
|
@@ -112,15 +122,6 @@ def run_process(
|
|
112
122
|
kw['preexec_fn'] = new_pgid
|
113
123
|
|
114
124
|
try:
|
115
|
-
# fork the child
|
116
|
-
# stdout, stderr = (
|
117
|
-
# (sys.stdout, sys.stderr) if not capture_output
|
118
|
-
# else (subprocess.PIPE, subprocess.PIPE)
|
119
|
-
# )
|
120
|
-
if capture_output:
|
121
|
-
kw['stdout'] = subprocess.PIPE
|
122
|
-
kw['stderr'] = subprocess.PIPE
|
123
|
-
|
124
125
|
child = subprocess.Popen(*args, **kw)
|
125
126
|
|
126
127
|
# we can't set the process group id from the parent since the child
|
@@ -197,6 +198,8 @@ def poll_process(
|
|
197
198
|
while proc.poll() is None:
|
198
199
|
line = proc.stdout.readline()
|
199
200
|
line_callback(line)
|
201
|
+
|
200
202
|
if timeout_seconds is not None:
|
201
203
|
watchdog_thread.cancel()
|
204
|
+
|
202
205
|
return proc.poll()
|