meerschaum 2.6.17__py3-none-any.whl → 2.7.0rc1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- meerschaum/actions/delete.py +65 -69
- meerschaum/actions/install.py +1 -2
- meerschaum/config/_default.py +1 -1
- meerschaum/config/_paths.py +2 -1
- meerschaum/config/_version.py +1 -1
- meerschaum/connectors/api/_pipes.py +4 -3
- meerschaum/connectors/sql/_pipes.py +63 -25
- meerschaum/connectors/sql/_sql.py +6 -1
- meerschaum/connectors/valkey/_pipes.py +12 -1
- meerschaum/core/Pipe/__init__.py +23 -13
- meerschaum/core/Pipe/_attributes.py +19 -0
- meerschaum/core/Pipe/_dtypes.py +1 -1
- meerschaum/core/Pipe/_sync.py +61 -21
- meerschaum/core/Pipe/_verify.py +8 -7
- meerschaum/plugins/_Plugin.py +11 -14
- meerschaum/utils/daemon/Daemon.py +18 -11
- meerschaum/utils/dataframe.py +175 -13
- meerschaum/utils/dtypes/__init__.py +103 -14
- meerschaum/utils/dtypes/sql.py +26 -0
- meerschaum/utils/misc.py +8 -8
- meerschaum/utils/sql.py +64 -11
- meerschaum/utils/venv/_Venv.py +4 -4
- meerschaum/utils/venv/__init__.py +33 -13
- {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/METADATA +1 -1
- {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/RECORD +31 -31
- {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/LICENSE +0 -0
- {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/NOTICE +0 -0
- {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/WHEEL +0 -0
- {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/top_level.txt +0 -0
- {meerschaum-2.6.17.dist-info → meerschaum-2.7.0rc1.dist-info}/zip-safe +0 -0
meerschaum/core/Pipe/__init__.py
CHANGED
@@ -106,6 +106,7 @@ class Pipe:
|
|
106
106
|
upsert,
|
107
107
|
static,
|
108
108
|
tzinfo,
|
109
|
+
enforce,
|
109
110
|
get_columns,
|
110
111
|
get_columns_types,
|
111
112
|
get_columns_indices,
|
@@ -132,6 +133,7 @@ class Pipe:
|
|
132
133
|
_persist_new_json_columns,
|
133
134
|
_persist_new_numeric_columns,
|
134
135
|
_persist_new_uuid_columns,
|
136
|
+
_persist_new_bytes_columns,
|
135
137
|
)
|
136
138
|
from ._verify import (
|
137
139
|
verify,
|
@@ -162,12 +164,14 @@ class Pipe:
|
|
162
164
|
upsert: Optional[bool] = None,
|
163
165
|
autoincrement: Optional[bool] = None,
|
164
166
|
static: Optional[bool] = None,
|
167
|
+
enforce: Optional[bool] = None,
|
165
168
|
mrsm_instance: Optional[Union[str, InstanceConnector]] = None,
|
166
169
|
cache: bool = False,
|
167
170
|
debug: bool = False,
|
168
171
|
connector_keys: Optional[str] = None,
|
169
172
|
metric_key: Optional[str] = None,
|
170
173
|
location_key: Optional[str] = None,
|
174
|
+
instance_keys: Optional[str] = None,
|
171
175
|
indexes: Union[Dict[str, str], List[str], None] = None,
|
172
176
|
):
|
173
177
|
"""
|
@@ -219,6 +223,10 @@ class Pipe:
|
|
219
223
|
static: Optional[bool], default None
|
220
224
|
If `True`, set `static` in the parameters.
|
221
225
|
|
226
|
+
enforce: Optionanl[bool], default None
|
227
|
+
If `False`, skip data type enforcement.
|
228
|
+
Default behavior is `True`.
|
229
|
+
|
222
230
|
temporary: bool, default False
|
223
231
|
If `True`, prevent instance tables (pipes, users, plugins) from being created.
|
224
232
|
|
@@ -319,11 +327,13 @@ class Pipe:
|
|
319
327
|
if isinstance(static, bool):
|
320
328
|
self._attributes['parameters']['static'] = static
|
321
329
|
|
330
|
+
if isinstance(enforce, bool):
|
331
|
+
self._attributes['parameters']['enforce'] = enforce
|
332
|
+
|
322
333
|
### NOTE: The parameters dictionary is {} by default.
|
323
334
|
### A Pipe may be registered without parameters, then edited,
|
324
335
|
### or a Pipe may be registered with parameters set in-memory first.
|
325
|
-
|
326
|
-
_mrsm_instance = mrsm_instance if mrsm_instance is not None else instance
|
336
|
+
_mrsm_instance = mrsm_instance if mrsm_instance is not None else (instance or instance_keys)
|
327
337
|
if _mrsm_instance is None:
|
328
338
|
_mrsm_instance = get_config('meerschaum', 'instance', patch=True)
|
329
339
|
|
@@ -341,10 +351,10 @@ class Pipe:
|
|
341
351
|
Return the four keys needed to reconstruct this pipe.
|
342
352
|
"""
|
343
353
|
return {
|
344
|
-
'
|
345
|
-
'
|
346
|
-
'
|
347
|
-
'
|
354
|
+
'connector_keys': self.connector_keys,
|
355
|
+
'metric_key': self.metric_key,
|
356
|
+
'location_key': self.location_key,
|
357
|
+
'instance_keys': self.instance_keys,
|
348
358
|
}
|
349
359
|
|
350
360
|
def keys(self) -> List[str]:
|
@@ -385,7 +395,7 @@ class Pipe:
|
|
385
395
|
warnings.simplefilter('ignore')
|
386
396
|
try:
|
387
397
|
conn = parse_instance_keys(self.connector_keys)
|
388
|
-
except Exception
|
398
|
+
except Exception:
|
389
399
|
conn = None
|
390
400
|
if conn:
|
391
401
|
self._connector = conn
|
@@ -429,7 +439,7 @@ class Pipe:
|
|
429
439
|
_fetch_patch = {
|
430
440
|
'fetch': ({
|
431
441
|
'definition': (
|
432
|
-
|
442
|
+
"SELECT * FROM "
|
433
443
|
+ sql_item_name(
|
434
444
|
str(self.target),
|
435
445
|
self.instance_connector.flavor,
|
@@ -467,7 +477,7 @@ class Pipe:
|
|
467
477
|
and self.location_key == other.location_key
|
468
478
|
and self.instance_keys == other.instance_keys
|
469
479
|
)
|
470
|
-
except Exception
|
480
|
+
except Exception:
|
471
481
|
return False
|
472
482
|
|
473
483
|
def __hash__(self):
|
@@ -496,11 +506,11 @@ class Pipe:
|
|
496
506
|
Define the state dictionary (pickling).
|
497
507
|
"""
|
498
508
|
return {
|
499
|
-
'
|
500
|
-
'
|
501
|
-
'
|
509
|
+
'connector_keys': self.connector_keys,
|
510
|
+
'metric_key': self.metric_key,
|
511
|
+
'location_key': self.location_key,
|
502
512
|
'parameters': self.parameters,
|
503
|
-
'
|
513
|
+
'instance_keys': self.instance_keys,
|
504
514
|
}
|
505
515
|
|
506
516
|
def __setstate__(self, _state: Dict[str, Any]):
|
@@ -289,6 +289,25 @@ def tzinfo(self) -> Union[None, timezone]:
|
|
289
289
|
return None
|
290
290
|
|
291
291
|
|
292
|
+
@property
|
293
|
+
def enforce(self) -> bool:
|
294
|
+
"""
|
295
|
+
Return the `enforce` parameter for the pipe.
|
296
|
+
"""
|
297
|
+
if 'enforce' not in self.parameters:
|
298
|
+
self.parameters['enforce'] = True
|
299
|
+
|
300
|
+
return self.parameters['enforce']
|
301
|
+
|
302
|
+
|
303
|
+
@enforce.setter
|
304
|
+
def enforce(self, _enforce: bool) -> None:
|
305
|
+
"""
|
306
|
+
Set the `enforce` parameter for the pipe.
|
307
|
+
"""
|
308
|
+
self.parameters['_enforce'] = _enforce
|
309
|
+
|
310
|
+
|
292
311
|
def get_columns(self, *args: str, error: bool = False) -> Union[str, Tuple[str]]:
|
293
312
|
"""
|
294
313
|
Check if the requested columns are defined.
|
meerschaum/core/Pipe/_dtypes.py
CHANGED
meerschaum/core/Pipe/_sync.py
CHANGED
@@ -368,10 +368,11 @@ def sync(
|
|
368
368
|
### Cast to a dataframe and ensure datatypes are what we expect.
|
369
369
|
df = self.enforce_dtypes(df, chunksize=chunksize, debug=debug)
|
370
370
|
|
371
|
-
### Capture `numeric`, `uuid`, and `
|
371
|
+
### Capture `numeric`, `uuid`, `json`, and `bytes` columns.
|
372
372
|
self._persist_new_json_columns(df, debug=debug)
|
373
373
|
self._persist_new_numeric_columns(df, debug=debug)
|
374
374
|
self._persist_new_uuid_columns(df, debug=debug)
|
375
|
+
self._persist_new_bytes_columns(df, debug=debug)
|
375
376
|
|
376
377
|
if debug:
|
377
378
|
dprint(
|
@@ -617,11 +618,13 @@ def filter_existing(
|
|
617
618
|
filter_unseen_df,
|
618
619
|
add_missing_cols_to_df,
|
619
620
|
get_unhashable_cols,
|
620
|
-
get_numeric_cols,
|
621
621
|
)
|
622
622
|
from meerschaum.utils.dtypes import (
|
623
623
|
to_pandas_dtype,
|
624
624
|
none_if_null,
|
625
|
+
to_datetime,
|
626
|
+
are_dtypes_equal,
|
627
|
+
value_is_null,
|
625
628
|
)
|
626
629
|
from meerschaum.config import get_config
|
627
630
|
pd = import_pandas()
|
@@ -669,29 +672,36 @@ def filter_existing(
|
|
669
672
|
### begin is the oldest data in the new dataframe
|
670
673
|
begin, end = None, None
|
671
674
|
dt_col = pipe_columns.get('datetime', None)
|
675
|
+
primary_key = pipe_columns.get('primary', None)
|
672
676
|
dt_type = self.dtypes.get(dt_col, 'datetime64[ns, UTC]') if dt_col else None
|
677
|
+
|
678
|
+
if autoincrement and primary_key == dt_col and dt_col not in df.columns:
|
679
|
+
if enforce_dtypes:
|
680
|
+
df = self.enforce_dtypes(df, chunksize=chunksize, debug=debug)
|
681
|
+
return df, get_empty_df(), df
|
682
|
+
|
673
683
|
try:
|
674
|
-
min_dt_val = df[dt_col].min(skipna=True) if dt_col else None
|
684
|
+
min_dt_val = df[dt_col].min(skipna=True) if dt_col and dt_col in df.columns else None
|
675
685
|
if is_dask and min_dt_val is not None:
|
676
686
|
min_dt_val = min_dt_val.compute()
|
677
687
|
min_dt = (
|
678
|
-
|
679
|
-
if min_dt_val is not None and 'datetime'
|
688
|
+
to_datetime(min_dt_val, as_pydatetime=True)
|
689
|
+
if min_dt_val is not None and are_dtypes_equal(dt_type, 'datetime')
|
680
690
|
else min_dt_val
|
681
691
|
)
|
682
692
|
except Exception:
|
683
693
|
min_dt = None
|
684
|
-
|
685
|
-
|
694
|
+
|
695
|
+
if not are_dtypes_equal('datetime', str(type(min_dt))) or value_is_null(min_dt):
|
696
|
+
if not are_dtypes_equal('int', str(type(min_dt))):
|
686
697
|
min_dt = None
|
687
698
|
|
688
699
|
if isinstance(min_dt, datetime):
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
)
|
700
|
+
rounded_min_dt = round_time(min_dt, to='down')
|
701
|
+
try:
|
702
|
+
begin = rounded_min_dt - timedelta(minutes=1)
|
703
|
+
except OverflowError:
|
704
|
+
begin = rounded_min_dt
|
695
705
|
elif dt_type and 'int' in dt_type.lower():
|
696
706
|
begin = min_dt
|
697
707
|
elif dt_col is None:
|
@@ -699,11 +709,11 @@ def filter_existing(
|
|
699
709
|
|
700
710
|
### end is the newest data in the new dataframe
|
701
711
|
try:
|
702
|
-
max_dt_val = df[dt_col].max(skipna=True) if dt_col else None
|
712
|
+
max_dt_val = df[dt_col].max(skipna=True) if dt_col and dt_col in df.columns else None
|
703
713
|
if is_dask and max_dt_val is not None:
|
704
714
|
max_dt_val = max_dt_val.compute()
|
705
715
|
max_dt = (
|
706
|
-
|
716
|
+
to_datetime(max_dt_val, as_pydatetime=True)
|
707
717
|
if max_dt_val is not None and 'datetime' in str(dt_type)
|
708
718
|
else max_dt_val
|
709
719
|
)
|
@@ -712,8 +722,8 @@ def filter_existing(
|
|
712
722
|
traceback.print_exc()
|
713
723
|
max_dt = None
|
714
724
|
|
715
|
-
if ('datetime'
|
716
|
-
if 'int'
|
725
|
+
if not are_dtypes_equal('datetime', str(type(max_dt))) or value_is_null(max_dt):
|
726
|
+
if not are_dtypes_equal('int', str(type(max_dt))):
|
717
727
|
max_dt = None
|
718
728
|
|
719
729
|
if isinstance(max_dt, datetime):
|
@@ -723,7 +733,7 @@ def filter_existing(
|
|
723
733
|
to='down'
|
724
734
|
) + timedelta(minutes=1)
|
725
735
|
)
|
726
|
-
elif dt_type and 'int' in dt_type.lower():
|
736
|
+
elif dt_type and 'int' in dt_type.lower() and max_dt is not None:
|
727
737
|
end = max_dt + 1
|
728
738
|
|
729
739
|
if max_dt is not None and min_dt is not None and min_dt > max_dt:
|
@@ -738,7 +748,7 @@ def filter_existing(
|
|
738
748
|
|
739
749
|
unique_index_vals = {
|
740
750
|
col: df[col].unique()
|
741
|
-
for col in pipe_columns
|
751
|
+
for col in (pipe_columns if not primary_key else [primary_key])
|
742
752
|
if col in df.columns and col != dt_col
|
743
753
|
} if not date_bound_only else {}
|
744
754
|
filter_params_index_limit = get_config('pipes', 'sync', 'filter_params_index_limit')
|
@@ -777,14 +787,15 @@ def filter_existing(
|
|
777
787
|
|
778
788
|
### Separate new rows from changed ones.
|
779
789
|
on_cols = [
|
780
|
-
col
|
790
|
+
col
|
791
|
+
for col_key, col in pipe_columns.items()
|
781
792
|
if (
|
782
793
|
col
|
783
794
|
and
|
784
795
|
col_key != 'value'
|
785
796
|
and col in backtrack_df.columns
|
786
797
|
)
|
787
|
-
]
|
798
|
+
] if not primary_key else [primary_key]
|
788
799
|
self_dtypes = self.dtypes
|
789
800
|
on_cols_dtypes = {
|
790
801
|
col: to_pandas_dtype(typ)
|
@@ -1020,3 +1031,32 @@ def _persist_new_json_columns(self, df, debug: bool = False) -> SuccessTuple:
|
|
1020
1031
|
return edit_success, edit_msg
|
1021
1032
|
|
1022
1033
|
return True, "Success"
|
1034
|
+
|
1035
|
+
|
1036
|
+
def _persist_new_bytes_columns(self, df, debug: bool = False) -> SuccessTuple:
|
1037
|
+
"""
|
1038
|
+
Check for new `bytes` columns and update the parameters.
|
1039
|
+
"""
|
1040
|
+
from meerschaum.utils.dataframe import get_bytes_cols
|
1041
|
+
bytes_cols = get_bytes_cols(df)
|
1042
|
+
existing_bytes_cols = [col for col, typ in self.dtypes.items() if typ == 'bytes']
|
1043
|
+
new_bytes_cols = [col for col in bytes_cols if col not in existing_bytes_cols]
|
1044
|
+
if not new_bytes_cols:
|
1045
|
+
return True, "Success"
|
1046
|
+
|
1047
|
+
self._attributes_sync_time = None
|
1048
|
+
dt_col = self.columns.get('datetime', None)
|
1049
|
+
dtypes = self.parameters.get('dtypes', {})
|
1050
|
+
if dt_col not in dtypes:
|
1051
|
+
dtypes[dt_col] = 'datetime'
|
1052
|
+
dtypes.update({col: 'bytes' for col in bytes_cols})
|
1053
|
+
self.parameters['dtypes'] = dtypes
|
1054
|
+
|
1055
|
+
if not self.temporary:
|
1056
|
+
edit_success, edit_msg = self.edit(interactive=False, debug=debug)
|
1057
|
+
if not edit_success:
|
1058
|
+
warn(f"Unable to update bytes dtypes for {self}:\n{edit_msg}")
|
1059
|
+
|
1060
|
+
return edit_success, edit_msg
|
1061
|
+
|
1062
|
+
return True, "Success"
|
meerschaum/core/Pipe/_verify.py
CHANGED
@@ -7,9 +7,10 @@ Verify the contents of a pipe by resyncing its interval.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from datetime import datetime, timedelta
|
10
|
-
|
10
|
+
|
11
|
+
import meerschaum as mrsm
|
12
|
+
from meerschaum.utils.typing import SuccessTuple, Any, Optional, Union, Tuple, Dict
|
11
13
|
from meerschaum.utils.warnings import warn, info
|
12
|
-
from meerschaum.utils.debug import dprint
|
13
14
|
|
14
15
|
|
15
16
|
def verify(
|
@@ -94,9 +95,6 @@ def verify(
|
|
94
95
|
else 1
|
95
96
|
)
|
96
97
|
|
97
|
-
sync_less_than_begin = not bounded and begin is None
|
98
|
-
sync_greater_than_end = not bounded and end is None
|
99
|
-
|
100
98
|
cannot_determine_bounds = not self.exists(debug=debug)
|
101
99
|
|
102
100
|
if cannot_determine_bounds:
|
@@ -164,7 +162,7 @@ def verify(
|
|
164
162
|
)
|
165
163
|
|
166
164
|
info(
|
167
|
-
f"Syncing {len(chunk_bounds)} chunk" + ('s' if len(chunk_bounds) != 1 else '')
|
165
|
+
f"Verifying {self}:\n Syncing {len(chunk_bounds)} chunk" + ('s' if len(chunk_bounds) != 1 else '')
|
168
166
|
+ f" ({'un' if not bounded else ''}bounded)"
|
169
167
|
+ f" of size '{interval_str(chunk_interval)}'"
|
170
168
|
+ f" between '{begin_to_print}' and '{end_to_print}'."
|
@@ -187,7 +185,7 @@ def verify(
|
|
187
185
|
return chunk_begin_and_end, bounds_success_tuples[chunk_begin_and_end]
|
188
186
|
|
189
187
|
chunk_begin, chunk_end = chunk_begin_and_end
|
190
|
-
|
188
|
+
chunk_success, chunk_msg = self.sync(
|
191
189
|
begin=chunk_begin,
|
192
190
|
end=chunk_end,
|
193
191
|
params=params,
|
@@ -195,6 +193,9 @@ def verify(
|
|
195
193
|
debug=debug,
|
196
194
|
**kwargs
|
197
195
|
)
|
196
|
+
chunk_msg = chunk_msg.strip()
|
197
|
+
mrsm.pprint((chunk_success, chunk_msg))
|
198
|
+
return chunk_begin_and_end, (chunk_success, chunk_msg)
|
198
199
|
|
199
200
|
### If we have more than one chunk, attempt to sync the first one and return if its fails.
|
200
201
|
if len(chunk_bounds) > 1:
|
meerschaum/plugins/_Plugin.py
CHANGED
@@ -255,11 +255,11 @@ class Plugin:
|
|
255
255
|
|
256
256
|
|
257
257
|
def install(
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
258
|
+
self,
|
259
|
+
skip_deps: bool = False,
|
260
|
+
force: bool = False,
|
261
|
+
debug: bool = False,
|
262
|
+
) -> SuccessTuple:
|
263
263
|
"""
|
264
264
|
Extract a plugin's tar archive to the plugins directory.
|
265
265
|
|
@@ -359,7 +359,7 @@ class Plugin:
|
|
359
359
|
is_same_version = new_version and old_version and (
|
360
360
|
packaging_version.parse(old_version) == packaging_version.parse(new_version)
|
361
361
|
)
|
362
|
-
except Exception
|
362
|
+
except Exception:
|
363
363
|
is_new_version, is_same_version = True, False
|
364
364
|
|
365
365
|
### Determine where to permanently store the new plugin.
|
@@ -404,7 +404,7 @@ class Plugin:
|
|
404
404
|
dprint(f"Moving '{src_file}' to '{dst_dir}'...")
|
405
405
|
try:
|
406
406
|
shutil.move(src_file, dst_dir)
|
407
|
-
except Exception
|
407
|
+
except Exception:
|
408
408
|
success, msg = False, (
|
409
409
|
f"Failed to install plugin '{self}': " +
|
410
410
|
f"Could not move file '{src_file}' to '{dst_dir}'"
|
@@ -817,10 +817,10 @@ class Plugin:
|
|
817
817
|
|
818
818
|
|
819
819
|
def install_dependencies(
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
820
|
+
self,
|
821
|
+
force: bool = False,
|
822
|
+
debug: bool = False,
|
823
|
+
) -> bool:
|
824
824
|
"""
|
825
825
|
If specified, install dependencies.
|
826
826
|
|
@@ -841,12 +841,9 @@ class Plugin:
|
|
841
841
|
Returns
|
842
842
|
-------
|
843
843
|
A bool indicating success.
|
844
|
-
|
845
844
|
"""
|
846
845
|
from meerschaum.utils.packages import pip_install, venv_contains_package
|
847
|
-
from meerschaum.utils.debug import dprint
|
848
846
|
from meerschaum.utils.warnings import warn, info
|
849
|
-
from meerschaum.connectors.parse import parse_repo_keys
|
850
847
|
_deps = self.get_dependencies(debug=debug)
|
851
848
|
if not _deps and self.requirements_file_path is None:
|
852
849
|
return True
|
@@ -465,6 +465,7 @@ class Daemon:
|
|
465
465
|
self._write_stop_file('kill')
|
466
466
|
return True, "Process has already stopped."
|
467
467
|
|
468
|
+
psutil = attempt_import('psutil')
|
468
469
|
process = self.process
|
469
470
|
try:
|
470
471
|
process.terminate()
|
@@ -473,10 +474,16 @@ class Daemon:
|
|
473
474
|
except Exception as e:
|
474
475
|
return False, f"Failed to kill job {self} with exception: {e}"
|
475
476
|
|
477
|
+
try:
|
478
|
+
if process.status():
|
479
|
+
return False, "Failed to stop daemon '{self}' ({process})."
|
480
|
+
except psutil.NoSuchProcess:
|
481
|
+
pass
|
482
|
+
|
476
483
|
if self.pid_path.exists():
|
477
484
|
try:
|
478
485
|
self.pid_path.unlink()
|
479
|
-
except Exception
|
486
|
+
except Exception:
|
480
487
|
pass
|
481
488
|
|
482
489
|
self._write_stop_file('kill')
|
@@ -534,7 +541,7 @@ class Daemon:
|
|
534
541
|
if not timeout:
|
535
542
|
try:
|
536
543
|
success = self.process.status() == 'stopped'
|
537
|
-
except psutil.NoSuchProcess
|
544
|
+
except psutil.NoSuchProcess:
|
538
545
|
success = True
|
539
546
|
msg = "Success" if success else f"Failed to suspend daemon '{self.daemon_id}'."
|
540
547
|
if success:
|
@@ -677,11 +684,11 @@ class Daemon:
|
|
677
684
|
raise SystemExit(0)
|
678
685
|
|
679
686
|
def _send_signal(
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
687
|
+
self,
|
688
|
+
signal_to_send,
|
689
|
+
timeout: Union[float, int, None] = None,
|
690
|
+
check_timeout_interval: Union[float, int, None] = None,
|
691
|
+
) -> SuccessTuple:
|
685
692
|
"""Send a signal to the daemon process.
|
686
693
|
|
687
694
|
Parameters
|
@@ -709,7 +716,7 @@ class Daemon:
|
|
709
716
|
)
|
710
717
|
|
711
718
|
os.kill(pid, signal_to_send)
|
712
|
-
except Exception
|
719
|
+
except Exception:
|
713
720
|
return False, f"Failed to send signal {signal_to_send}:\n{traceback.format_exc()}"
|
714
721
|
|
715
722
|
timeout = self.get_timeout_seconds(timeout)
|
@@ -745,7 +752,7 @@ class Daemon:
|
|
745
752
|
if _already_exists and not allow_dirty_run:
|
746
753
|
error(
|
747
754
|
f"Daemon '{self.daemon_id}' already exists. " +
|
748
|
-
|
755
|
+
"To allow this daemon to run, do one of the following:\n"
|
749
756
|
+ " - Execute `daemon.cleanup()`.\n"
|
750
757
|
+ f" - Delete the directory '{self.path}'.\n"
|
751
758
|
+ " - Pass `allow_dirty_run=True` to `daemon.run()`.\n",
|
@@ -764,7 +771,7 @@ class Daemon:
|
|
764
771
|
if '_process' not in self.__dict__ or self.__dict__['_process'].pid != int(pid):
|
765
772
|
try:
|
766
773
|
self._process = psutil.Process(int(pid))
|
767
|
-
except Exception
|
774
|
+
except Exception:
|
768
775
|
if self.pid_path.exists():
|
769
776
|
self.pid_path.unlink()
|
770
777
|
return None
|
@@ -788,7 +795,7 @@ class Daemon:
|
|
788
795
|
if self.pid_path.exists():
|
789
796
|
try:
|
790
797
|
self.pid_path.unlink()
|
791
|
-
except Exception
|
798
|
+
except Exception:
|
792
799
|
pass
|
793
800
|
return 'stopped'
|
794
801
|
|