strideutils 0.3.15__tar.gz → 0.4.1__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.
Files changed (33) hide show
  1. {strideutils-0.3.15 → strideutils-0.4.1}/PKG-INFO +1 -1
  2. {strideutils-0.3.15 → strideutils-0.4.1}/pyproject.toml +1 -1
  3. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/run_safely.py +25 -6
  4. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/stride_alerts.py +49 -23
  5. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/stride_config.py +22 -11
  6. {strideutils-0.3.15 → strideutils-0.4.1}/README.md +0 -0
  7. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/Makefile +0 -0
  8. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/__init__.py +0 -0
  9. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/coingecko.py +0 -0
  10. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/config_examples/strideutils_config.yaml.example +0 -0
  11. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/config_examples/strideutils_secrets.yaml.example +0 -0
  12. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/cryptography.py +0 -0
  13. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/exec_tx.py +0 -0
  14. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/invariant_helpers.py +0 -0
  15. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/sheets_connector.py +0 -0
  16. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/slack_connector.py +0 -0
  17. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/stride_requests.py +0 -0
  18. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/stride_upstash.py +0 -0
  19. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/twilio_connector.py +0 -0
  20. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils/types.py +0 -0
  21. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/__init__.pyi +0 -0
  22. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/coingecko.pyi +0 -0
  23. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/cryptography.pyi +0 -0
  24. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/exec_tx.pyi +0 -0
  25. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/invariant_helpers.pyi +0 -0
  26. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/run_safely.pyi +0 -0
  27. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/sheets_connector.pyi +0 -0
  28. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/slack_connector.pyi +0 -0
  29. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/stride_alerts.pyi +0 -0
  30. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/stride_config.pyi +0 -0
  31. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/stride_requests.pyi +0 -0
  32. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/stride_upstash.pyi +0 -0
  33. {strideutils-0.3.15 → strideutils-0.4.1}/strideutils-stubs/twilio_connector.pyi +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: strideutils
3
- Version: 0.3.15
3
+ Version: 0.4.1
4
4
  Summary:
5
5
  Author: Your Name
6
6
  Author-email: you@example.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "strideutils"
3
- version = "0.3.15"
3
+ version = "0.4.1"
4
4
  description = ""
5
5
  authors = ["Your Name <you@example.com>"]
6
6
  readme = "README.md"
@@ -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, slack_channel: str = 'alerts-status', job_name: str = "alerts", botname: Optional[str] = None
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
- print(f"Running {func_name}...", end="")
33
- func()
34
- print("✅")
35
- msgs += [f':white_check_mark: {func_name}']
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 = type(output) is tuple and 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
- msgs: Union[str, List[str]],
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
- msgs: List = cast(List, [msgs] if type(msgs) is str else msgs)
61
- top_level_message = str(msgs[0])
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
- msgs[0] += "\nβ _This alert is in beta and might be unstable_"
78
+ messages_list[0] += "\nβ _This alert is in beta and might be unstable_"
77
79
 
78
80
  if tag:
79
- msgs[0] += f"\n{get_tags(tag)}"
81
+ messages_list[0] += f"\n{get_tags(tag)}"
80
82
 
81
- slack_connector.post_msg(msgs + [debug_msg], channel)
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
- msgs = f(*args, **kwargs)
115
+ output = f(*args, **kwargs)
116
+ msgs: List = output[-1] if type(output) is tuple and len(output) == 2 else output # type: ignore
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
- msgs=msgs,
125
+ messages=msgs,
123
126
  urgency=urgency,
124
127
  tag=tag,
125
128
  call=call,
@@ -127,6 +130,7 @@ def raise_alert(
127
130
  debug_readme=debug_readme,
128
131
  debug_readme_section=debug_readme_section,
129
132
  )
133
+ return output
130
134
 
131
135
  return _func
132
136
 
@@ -150,18 +154,19 @@ def run_daily(utc_hour_or_alert: Union[int, Callable]) -> Callable:
150
154
  def bar():
151
155
  ...
152
156
  ```
153
- """
154
- DEFAULT_UTC_HOUR = 17
155
157
 
158
+ The returned function also returns a bool to indicate if the function was invoked
159
+ """
156
160
  # If used without an argument, act as an unparameterized decorator
157
161
  if callable(utc_hour_or_alert):
158
162
  alert = utc_hour_or_alert
159
163
 
160
164
  @wraps(alert)
161
- def _func(*args, **kwargs):
162
- dt = datetime.datetime.utcnow()
163
- if dt.hour == DEFAULT_UTC_HOUR:
164
- return alert(*args, **kwargs)
165
+ def _func(*args, **kwargs) -> Tuple[bool, Any]:
166
+ dt = datetime.datetime.now(tz=datetime.timezone.utc)
167
+ if config.ENV == "DEV" or dt.hour == DEFAULT_DAILY_UTC_HOUR:
168
+ return True, alert(*args, **kwargs)
169
+ return False, None
165
170
 
166
171
  return _func
167
172
 
@@ -173,24 +178,45 @@ def run_daily(utc_hour_or_alert: Union[int, Callable]) -> Callable:
173
178
  def _decorator(f: Callable):
174
179
  @wraps(f)
175
180
  def _func(*args, **kwargs):
176
- dt = datetime.datetime.utcnow()
177
- if dt.hour == utc_hour:
178
- return f(*args, **kwargs)
181
+ dt = datetime.datetime.now(tz=datetime.timezone.utc)
182
+ if config.ENV == "DEV" or dt.hour == utc_hour:
183
+ return True, f(*args, **kwargs)
184
+ return False, None
179
185
 
180
186
  return _func
181
187
 
182
188
  return _decorator
183
189
 
184
190
 
185
- def run_epochly(f: Callable) -> Callable:
191
+ def run_stride_epochly(f: Callable) -> Callable:
186
192
  """
187
193
  Modifies an function to run once per stride epoch
194
+ Returns the new function returns a bool indicating if the function was executed
195
+ When running in DEV mode, the function always executes
196
+ """
197
+
198
+ @wraps(f)
199
+ def _func(*args, **kwargs) -> Tuple[bool, Any]:
200
+ dt = datetime.datetime.now(tz=datetime.timezone.utc)
201
+ if config.ENV == "DEV" or dt.hour in {1, 7, 13, 19}:
202
+ return True, f(*args, **kwargs)
203
+ return False, None
204
+
205
+ return _func
206
+
207
+
208
+ def run_day_epochly(f: Callable) -> Callable:
209
+ """
210
+ Modifies an function to run once per day epoch
211
+ Returns the new function returns a bool indicating if the function was executed
212
+ When running in DEV mode, the function always executes
188
213
  """
189
214
 
190
215
  @wraps(f)
191
- def _func(*args, **kwargs):
192
- dt = datetime.datetime.utcnow()
193
- if dt.hour in {1, 7, 13, 19}:
194
- return f(*args, **kwargs)
216
+ def _func(*args, **kwargs) -> Tuple[bool, Any]:
217
+ dt = datetime.datetime.now(tz=datetime.timezone.utc)
218
+ if config.ENV == "DEV" or dt.hour == 19:
219
+ return True, f(*args, **kwargs)
220
+ return False, None
195
221
 
196
222
  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
- host_chain_fields = [field.name for field in fields(HostChainsConfig)]
350
- app_chain_fields = [field.name for field in fields(AppChainsConfig)]
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
- host_chain_config_dict = {
353
- name: HostChainConfig(**info) for name, info in raw_config["host_zones"].items() if name in host_chain_fields
354
- }
355
- app_chain_config_dict = {
356
- name: ChainConfig(**info) for name, info in raw_config["app_chains"].items() if name in app_chain_fields
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
- 'stride': stride_chain_config,
365
- 'host_zones': host_zone_config,
366
- 'app_chains': app_chain_config,
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