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.
Files changed (33) hide show
  1. {strideutils-0.3.15 → strideutils-0.4.0}/PKG-INFO +1 -1
  2. {strideutils-0.3.15 → strideutils-0.4.0}/pyproject.toml +1 -1
  3. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/run_safely.py +25 -6
  4. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/stride_alerts.py +48 -23
  5. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/stride_config.py +22 -11
  6. {strideutils-0.3.15 → strideutils-0.4.0}/README.md +0 -0
  7. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/Makefile +0 -0
  8. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/__init__.py +0 -0
  9. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/coingecko.py +0 -0
  10. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/config_examples/strideutils_config.yaml.example +0 -0
  11. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/config_examples/strideutils_secrets.yaml.example +0 -0
  12. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/cryptography.py +0 -0
  13. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/exec_tx.py +0 -0
  14. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/invariant_helpers.py +0 -0
  15. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/sheets_connector.py +0 -0
  16. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/slack_connector.py +0 -0
  17. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/stride_requests.py +0 -0
  18. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/stride_upstash.py +0 -0
  19. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/twilio_connector.py +0 -0
  20. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils/types.py +0 -0
  21. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/__init__.pyi +0 -0
  22. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/coingecko.pyi +0 -0
  23. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/cryptography.pyi +0 -0
  24. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/exec_tx.pyi +0 -0
  25. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/invariant_helpers.pyi +0 -0
  26. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/run_safely.pyi +0 -0
  27. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/sheets_connector.pyi +0 -0
  28. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/slack_connector.pyi +0 -0
  29. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/stride_alerts.pyi +0 -0
  30. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/stride_config.pyi +0 -0
  31. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/stride_requests.pyi +0 -0
  32. {strideutils-0.3.15 → strideutils-0.4.0}/strideutils-stubs/stride_upstash.pyi +0 -0
  33. {strideutils-0.3.15 → strideutils-0.4.0}/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.0
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.0"
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 = 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 = 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
- msgs=msgs,
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.utcnow()
163
- if dt.hour == DEFAULT_UTC_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.utcnow()
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 run_epochly(f: Callable) -> Callable:
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.utcnow()
193
- if dt.hour in {1, 7, 13, 19}:
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
- 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