strideutils 0.3.15__tar.gz → 0.4.0__tar.gz
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.
- {strideutils-0.3.15 → strideutils-0.4.0}/PKG-INFO +1 -1
- {strideutils-0.3.15 → strideutils-0.4.0}/pyproject.toml +1 -1
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/run_safely.py +25 -6
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/stride_alerts.py +48 -23
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/stride_config.py +22 -11
- {strideutils-0.3.15 → strideutils-0.4.0}/README.md +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/Makefile +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/__init__.py +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/coingecko.py +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/config_examples/strideutils_config.yaml.example +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/config_examples/strideutils_secrets.yaml.example +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/cryptography.py +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/exec_tx.py +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/invariant_helpers.py +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/sheets_connector.py +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/slack_connector.py +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/stride_requests.py +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/stride_upstash.py +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/twilio_connector.py +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/types.py +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/__init__.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/coingecko.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/cryptography.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/exec_tx.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/invariant_helpers.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/run_safely.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/sheets_connector.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/slack_connector.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/stride_alerts.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/stride_config.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/stride_requests.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/stride_upstash.pyi +0 -0
- {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/twilio_connector.pyi +0 -0
|
@@ -6,7 +6,11 @@ from strideutils.stride_config import config
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def try_and_log_with_status(
|
|
9
|
-
*functions: Callable,
|
|
9
|
+
*functions: Callable,
|
|
10
|
+
slack_channel: str = 'alerts-status',
|
|
11
|
+
job_name: str = "alerts",
|
|
12
|
+
botname: Optional[str] = None,
|
|
13
|
+
print_status: bool = False,
|
|
10
14
|
):
|
|
11
15
|
"""
|
|
12
16
|
Wrap functions in a try/catch and log exceptions.
|
|
@@ -21,6 +25,7 @@ def try_and_log_with_status(
|
|
|
21
25
|
"""
|
|
22
26
|
if config.ENV == 'DEV':
|
|
23
27
|
slack_channel = 'alerts-debug'
|
|
28
|
+
print_status = True
|
|
24
29
|
|
|
25
30
|
tracebacks = []
|
|
26
31
|
msgs = []
|
|
@@ -29,16 +34,30 @@ def try_and_log_with_status(
|
|
|
29
34
|
# (to cover cases where click was used)
|
|
30
35
|
func_name = getattr(func, 'name', None) or func.__name__
|
|
31
36
|
try:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
if print_status:
|
|
38
|
+
print(f"Running {func_name}...", end="")
|
|
39
|
+
|
|
40
|
+
# If the function was wrapped in a run_daily/run_epochly decorator,
|
|
41
|
+
# the function will return a bool indicating if the function was executed
|
|
42
|
+
# If it was skipped, use a different emoji
|
|
43
|
+
output = func()
|
|
44
|
+
func_skipped = len(output) == 2 and not output[0]
|
|
45
|
+
|
|
46
|
+
emoji_print = "⚪" if func_skipped else "✅"
|
|
47
|
+
emoji_slack = ":white_circle:" if func_skipped else ":white_check_mark:"
|
|
48
|
+
msgs += [f'{emoji_slack} {func_name}']
|
|
49
|
+
|
|
50
|
+
if print_status:
|
|
51
|
+
print(emoji_print)
|
|
52
|
+
|
|
36
53
|
except Exception:
|
|
37
|
-
print("❌")
|
|
38
54
|
print(traceback.format_exc())
|
|
39
55
|
msgs += [f':x: {func_name} ']
|
|
40
56
|
tracebacks.append(f"{func_name} \n\n" + traceback.format_exc())
|
|
41
57
|
|
|
58
|
+
if print_status:
|
|
59
|
+
print("❌")
|
|
60
|
+
|
|
42
61
|
count_succeeded_msg = f"{len(functions) - len(tracebacks)}/{len(functions)} {job_name} succeeded"
|
|
43
62
|
if len(tracebacks) > 0:
|
|
44
63
|
status_msg = f':x: {count_succeeded_msg}'
|
|
@@ -2,7 +2,7 @@ import datetime
|
|
|
2
2
|
import os
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from functools import wraps
|
|
5
|
-
from typing import Callable, Iterable, List, Optional, Union, cast
|
|
5
|
+
from typing import Any, Callable, Iterable, List, Optional, Tuple, Union, cast
|
|
6
6
|
|
|
7
7
|
import pandas as pd
|
|
8
8
|
import tabulate
|
|
@@ -10,6 +10,8 @@ import tabulate
|
|
|
10
10
|
from strideutils import slack_connector, twilio_connector
|
|
11
11
|
from strideutils.stride_config import config
|
|
12
12
|
|
|
13
|
+
DEFAULT_DAILY_UTC_HOUR = 17
|
|
14
|
+
|
|
13
15
|
|
|
14
16
|
class AlertType(Enum):
|
|
15
17
|
DEBUG = 'debug'
|
|
@@ -38,7 +40,7 @@ def wrap_in_code_block(message: str) -> str:
|
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
def notify(
|
|
41
|
-
|
|
43
|
+
messages: Union[str, List[str]],
|
|
42
44
|
urgency,
|
|
43
45
|
tag: Optional[Union[str, Iterable[str]]] = None,
|
|
44
46
|
call: Optional[Union[str, Iterable[str]]] = None,
|
|
@@ -57,8 +59,8 @@ def notify(
|
|
|
57
59
|
"""
|
|
58
60
|
urgency = AlertType(urgency)
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
top_level_message = str(
|
|
62
|
+
messages_list: List = cast(List, [messages] if type(messages) is str else messages)
|
|
63
|
+
top_level_message = str(messages_list[0])
|
|
62
64
|
|
|
63
65
|
debug_msg = f"Read more: {debug_readme}{debug_readme_section}"
|
|
64
66
|
|
|
@@ -73,12 +75,12 @@ def notify(
|
|
|
73
75
|
channel = "#alerts-critical"
|
|
74
76
|
|
|
75
77
|
if beta_alert:
|
|
76
|
-
|
|
78
|
+
messages_list[0] += "\nβ _This alert is in beta and might be unstable_"
|
|
77
79
|
|
|
78
80
|
if tag:
|
|
79
|
-
|
|
81
|
+
messages_list[0] += f"\n{get_tags(tag)}"
|
|
80
82
|
|
|
81
|
-
slack_connector.post_msg(
|
|
83
|
+
slack_connector.post_msg(messages_list + [debug_msg], channel)
|
|
82
84
|
|
|
83
85
|
if call:
|
|
84
86
|
twilio_connector.send_calls(top_level_message, call)
|
|
@@ -110,7 +112,8 @@ def raise_alert(
|
|
|
110
112
|
def _decorator(f: Callable):
|
|
111
113
|
@wraps(f)
|
|
112
114
|
def _func(*args, **kwargs):
|
|
113
|
-
|
|
115
|
+
output = f(*args, **kwargs)
|
|
116
|
+
msgs = output[-1] if type(output) is Tuple else output
|
|
114
117
|
if urgency == 'debug':
|
|
115
118
|
debug_msg = (
|
|
116
119
|
'In debug mode. Tags and calls have been disabled' 'and the message is routed to alerts-debug.'
|
|
@@ -119,7 +122,7 @@ def raise_alert(
|
|
|
119
122
|
msgs += [debug_msg]
|
|
120
123
|
if msgs:
|
|
121
124
|
notify(
|
|
122
|
-
|
|
125
|
+
messages=msgs,
|
|
123
126
|
urgency=urgency,
|
|
124
127
|
tag=tag,
|
|
125
128
|
call=call,
|
|
@@ -150,18 +153,19 @@ def run_daily(utc_hour_or_alert: Union[int, Callable]) -> Callable:
|
|
|
150
153
|
def bar():
|
|
151
154
|
...
|
|
152
155
|
```
|
|
153
|
-
"""
|
|
154
|
-
DEFAULT_UTC_HOUR = 17
|
|
155
156
|
|
|
157
|
+
The returned function also returns a bool to indicate if the function was invoked
|
|
158
|
+
"""
|
|
156
159
|
# If used without an argument, act as an unparameterized decorator
|
|
157
160
|
if callable(utc_hour_or_alert):
|
|
158
161
|
alert = utc_hour_or_alert
|
|
159
162
|
|
|
160
163
|
@wraps(alert)
|
|
161
|
-
def _func(*args, **kwargs):
|
|
162
|
-
dt = datetime.datetime.
|
|
163
|
-
if dt.hour ==
|
|
164
|
-
return alert(*args, **kwargs)
|
|
164
|
+
def _func(*args, **kwargs) -> Tuple[bool, Any]:
|
|
165
|
+
dt = datetime.datetime.now(tz=datetime.timezone.utc)
|
|
166
|
+
if config.ENV == "DEV" or dt.hour == DEFAULT_DAILY_UTC_HOUR:
|
|
167
|
+
return True, alert(*args, **kwargs)
|
|
168
|
+
return False, None
|
|
165
169
|
|
|
166
170
|
return _func
|
|
167
171
|
|
|
@@ -173,24 +177,45 @@ def run_daily(utc_hour_or_alert: Union[int, Callable]) -> Callable:
|
|
|
173
177
|
def _decorator(f: Callable):
|
|
174
178
|
@wraps(f)
|
|
175
179
|
def _func(*args, **kwargs):
|
|
176
|
-
dt = datetime.datetime.
|
|
177
|
-
if dt.hour == utc_hour:
|
|
178
|
-
return f(*args, **kwargs)
|
|
180
|
+
dt = datetime.datetime.now(tz=datetime.timezone.utc)
|
|
181
|
+
if config.ENV == "DEV" or dt.hour == utc_hour:
|
|
182
|
+
return True, f(*args, **kwargs)
|
|
183
|
+
return False, None
|
|
179
184
|
|
|
180
185
|
return _func
|
|
181
186
|
|
|
182
187
|
return _decorator
|
|
183
188
|
|
|
184
189
|
|
|
185
|
-
def
|
|
190
|
+
def run_stride_epochly(f: Callable) -> Callable:
|
|
186
191
|
"""
|
|
187
192
|
Modifies an function to run once per stride epoch
|
|
193
|
+
Returns the new function returns a bool indicating if the function was executed
|
|
194
|
+
When running in DEV mode, the function always executes
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
@wraps(f)
|
|
198
|
+
def _func(*args, **kwargs) -> Tuple[bool, Any]:
|
|
199
|
+
dt = datetime.datetime.now(tz=datetime.timezone.utc)
|
|
200
|
+
if config.ENV == "DEV" or dt.hour in {1, 7, 13, 19}:
|
|
201
|
+
return True, f(*args, **kwargs)
|
|
202
|
+
return False, None
|
|
203
|
+
|
|
204
|
+
return _func
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def run_day_epochly(f: Callable) -> Callable:
|
|
208
|
+
"""
|
|
209
|
+
Modifies an function to run once per day epoch
|
|
210
|
+
Returns the new function returns a bool indicating if the function was executed
|
|
211
|
+
When running in DEV mode, the function always executes
|
|
188
212
|
"""
|
|
189
213
|
|
|
190
214
|
@wraps(f)
|
|
191
|
-
def _func(*args, **kwargs):
|
|
192
|
-
dt = datetime.datetime.
|
|
193
|
-
if
|
|
194
|
-
return f(*args, **kwargs)
|
|
215
|
+
def _func(*args, **kwargs) -> Tuple[bool, Any]:
|
|
216
|
+
dt = datetime.datetime.now(tz=datetime.timezone.utc)
|
|
217
|
+
if config.ENV == "DEV" or dt.hour == 19:
|
|
218
|
+
return True, f(*args, **kwargs)
|
|
219
|
+
return False, None
|
|
195
220
|
|
|
196
221
|
return _func
|
|
@@ -345,25 +345,36 @@ def _load_raw_secrets() -> dict[str, Optional[str]]:
|
|
|
345
345
|
def _parse_raw_configs(raw_config: dict[str, Any], raw_secrets: dict[str, Optional[str]]) -> Config:
|
|
346
346
|
"""
|
|
347
347
|
Builds the relevant config data classes from the raw jsons
|
|
348
|
+
|
|
349
|
+
When parsing into the dataclasses, only consider fields that are defined in the dataclass
|
|
350
|
+
so that the package maintains backwards compatibility when new fields are added
|
|
348
351
|
"""
|
|
349
|
-
|
|
350
|
-
|
|
352
|
+
host_chain_names = [field.name for field in fields(HostChainsConfig)]
|
|
353
|
+
app_chain_names = [field.name for field in fields(AppChainsConfig)]
|
|
351
354
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
355
|
+
host_chain_fields = [field.name for field in fields(HostChainConfig)]
|
|
356
|
+
app_chain_fields = [field.name for field in fields(ChainConfig)]
|
|
357
|
+
|
|
358
|
+
host_chain_config_dict = {}
|
|
359
|
+
for name, info in raw_config["host_zones"].items():
|
|
360
|
+
if name in host_chain_names:
|
|
361
|
+
filtered_info = {key: value for key, value in info.items() if key in host_chain_fields}
|
|
362
|
+
host_chain_config_dict[name] = HostChainConfig(**filtered_info)
|
|
363
|
+
|
|
364
|
+
app_chain_config_dict = {}
|
|
365
|
+
for name, info in raw_config["app_chains"].items():
|
|
366
|
+
if name in app_chain_names:
|
|
367
|
+
filtered_info = {key: value for key, value in info.items() if key in app_chain_fields}
|
|
368
|
+
app_chain_config_dict[name] = ChainConfig(**filtered_info)
|
|
358
369
|
|
|
359
370
|
stride_chain_config = StrideChainConfig(**raw_config["stride"])
|
|
360
371
|
host_zone_config = HostChainsConfig(**host_chain_config_dict)
|
|
361
372
|
app_chain_config = AppChainsConfig(**app_chain_config_dict)
|
|
362
373
|
|
|
363
374
|
chain_configs = {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
375
|
+
"stride": stride_chain_config,
|
|
376
|
+
"host_zones": host_zone_config,
|
|
377
|
+
"app_chains": app_chain_config,
|
|
367
378
|
}
|
|
368
379
|
|
|
369
380
|
# Load non-nested configs, then overwrite with the chain configs
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strideutils-0.3.15 → strideutils-0.4.0}/strideutils/config_examples/strideutils_config.yaml.example
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|