moose-lib 0.6.85__tar.gz → 0.6.86__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.

Potentially problematic release.


This version of moose-lib might be problematic. Click here for more details.

Files changed (47) hide show
  1. {moose_lib-0.6.85 → moose_lib-0.6.86}/PKG-INFO +1 -1
  2. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/clients/redis_client.py +103 -20
  3. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib.egg-info/PKG-INFO +1 -1
  4. {moose_lib-0.6.85 → moose_lib-0.6.86}/README.md +0 -0
  5. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/__init__.py +0 -0
  6. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/blocks.py +0 -0
  7. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/clients/__init__.py +0 -0
  8. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/commons.py +0 -0
  9. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/config/__init__.py +0 -0
  10. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/config/config_file.py +0 -0
  11. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/config/runtime.py +0 -0
  12. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/data_models.py +0 -0
  13. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/__init__.py +0 -0
  14. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/_registry.py +0 -0
  15. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/consumption.py +0 -0
  16. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/ingest_api.py +0 -0
  17. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/ingest_pipeline.py +0 -0
  18. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/life_cycle.py +0 -0
  19. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/materialized_view.py +0 -0
  20. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/olap_table.py +0 -0
  21. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/registry.py +0 -0
  22. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/sql_resource.py +0 -0
  23. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/stream.py +0 -0
  24. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/types.py +0 -0
  25. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/view.py +0 -0
  26. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2/workflow.py +0 -0
  27. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/dmv2_serializer.py +0 -0
  28. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/internal.py +0 -0
  29. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/main.py +0 -0
  30. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/query_builder.py +0 -0
  31. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/query_param.py +0 -0
  32. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/streaming/__init__.py +0 -0
  33. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/streaming/streaming_function_runner.py +0 -0
  34. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/utilities/__init__.py +0 -0
  35. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib/utilities/sql.py +0 -0
  36. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib.egg-info/SOURCES.txt +0 -0
  37. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib.egg-info/dependency_links.txt +0 -0
  38. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib.egg-info/requires.txt +0 -0
  39. {moose_lib-0.6.85 → moose_lib-0.6.86}/moose_lib.egg-info/top_level.txt +0 -0
  40. {moose_lib-0.6.85 → moose_lib-0.6.86}/setup.cfg +0 -0
  41. {moose_lib-0.6.85 → moose_lib-0.6.86}/setup.py +0 -0
  42. {moose_lib-0.6.85 → moose_lib-0.6.86}/tests/__init__.py +0 -0
  43. {moose_lib-0.6.85 → moose_lib-0.6.86}/tests/conftest.py +0 -0
  44. {moose_lib-0.6.85 → moose_lib-0.6.86}/tests/test_moose.py +0 -0
  45. {moose_lib-0.6.85 → moose_lib-0.6.86}/tests/test_query_builder.py +0 -0
  46. {moose_lib-0.6.85 → moose_lib-0.6.86}/tests/test_redis_client.py +0 -0
  47. {moose_lib-0.6.85 → moose_lib-0.6.86}/tests/test_s3queue_config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moose_lib
3
- Version: 0.6.85
3
+ Version: 0.6.86
4
4
  Home-page: https://www.fiveonefour.com/moose
5
5
  Author: Fiveonefour Labs Inc.
6
6
  Author-email: support@fiveonefour.com
@@ -4,11 +4,12 @@ import os
4
4
  import redis
5
5
  import threading
6
6
  from pydantic import BaseModel
7
- from typing import Optional, TypeVar, Type, Union, TypeAlias
7
+ from typing import Optional, TypeVar, Type, Union, TypeAlias, List, Any
8
8
 
9
9
 
10
- T = TypeVar('T')
11
- SupportedValue: TypeAlias = Union[str, BaseModel]
10
+ T = TypeVar("T")
11
+ SupportedValue: TypeAlias = Union[str, BaseModel, List[Any]]
12
+
12
13
 
13
14
  class MooseCache:
14
15
  """
@@ -18,6 +19,7 @@ class MooseCache:
18
19
  Example:
19
20
  cache = MooseCache() # Gets or creates the singleton instance
20
21
  """
22
+
21
23
  _instance = None
22
24
  _redis_url: str
23
25
  _key_prefix: str
@@ -36,10 +38,10 @@ class MooseCache:
36
38
  if self._client is not None:
37
39
  return
38
40
 
39
- self._redis_url = os.getenv('MOOSE_REDIS_CONFIG__URL', 'redis://127.0.0.1:6379')
40
- prefix = os.getenv('MOOSE_REDIS_CONFIG__KEY_PREFIX', 'MS')
41
+ self._redis_url = os.getenv("MOOSE_REDIS_CONFIG__URL", "redis://127.0.0.1:6379")
42
+ prefix = os.getenv("MOOSE_REDIS_CONFIG__KEY_PREFIX", "MS")
41
43
  # 30 seconds of inactivity before disconnecting
42
- self._idle_timeout = int(os.getenv('MOOSE_REDIS_CONFIG__IDLE_TIMEOUT', '30'))
44
+ self._idle_timeout = int(os.getenv("MOOSE_REDIS_CONFIG__IDLE_TIMEOUT", "30"))
43
45
  self._key_prefix = f"{prefix}::moosecache::"
44
46
 
45
47
  self._ensure_connected()
@@ -65,14 +67,16 @@ class MooseCache:
65
67
  self._clear_disconnect_timer()
66
68
  self._disconnect_timer.start()
67
69
 
68
- def set(self, key: str, value: SupportedValue, ttl_seconds: Optional[int] = None) -> None:
70
+ def set(
71
+ self, key: str, value: SupportedValue, ttl_seconds: Optional[int] = None
72
+ ) -> None:
69
73
  """
70
- Sets a value in the cache. Only accepts strings or Pydantic models.
74
+ Sets a value in the cache. Accepts strings, Pydantic models, or lists.
71
75
  Objects are automatically JSON stringified.
72
76
 
73
77
  Args:
74
78
  key: The key to store the value under
75
- value: The value to store. Must be a string or Pydantic model
79
+ value: The value to store. Must be a string, Pydantic model, or list
76
80
  ttl_seconds: Optional time-to-live in seconds. If not provided, defaults to 1 hour (3600 seconds).
77
81
  Must be a non-negative number. If 0, the key will expire immediately.
78
82
 
@@ -85,12 +89,15 @@ class MooseCache:
85
89
  baz: int
86
90
  qux: bool
87
91
  cache.set("foo:config", Config(baz=123, qux=True))
92
+
93
+ ### Store a list
94
+ cache.set("foo:list", [{"id": 1}, {"id": 2}])
88
95
  """
89
96
  try:
90
97
  # Validate value type
91
- if not isinstance(value, (str, BaseModel)):
98
+ if not isinstance(value, (str, BaseModel, list)):
92
99
  raise TypeError(
93
- f"Value must be a string or Pydantic model. Got {type(value).__name__}"
100
+ f"Value must be a string, Pydantic model, or list. Got {type(value).__name__}"
94
101
  )
95
102
 
96
103
  # Validate TTL
@@ -99,27 +106,39 @@ class MooseCache:
99
106
 
100
107
  self._ensure_connected()
101
108
  prefixed_key = self._get_prefixed_key(key)
109
+ metadata_key = f"{prefixed_key}:__type__"
102
110
 
103
111
  if isinstance(value, str):
104
112
  string_value = value
105
- else:
113
+ value_type = "str"
114
+ elif isinstance(value, BaseModel):
106
115
  string_value = value.model_dump_json()
116
+ value_type = f"pydantic:{value.__class__.__name__}"
117
+ else: # list
118
+ string_value = json.dumps(value)
119
+ value_type = "list"
107
120
 
108
121
  # Use provided TTL or default to 1 hour
109
122
  ttl = ttl_seconds if ttl_seconds is not None else 3600
110
- self._client.setex(prefixed_key, ttl, string_value)
123
+
124
+ # Store the value and its type metadata
125
+ pipe = self._client.pipeline()
126
+ pipe.setex(prefixed_key, ttl, string_value)
127
+ pipe.setex(metadata_key, ttl, value_type)
128
+ pipe.execute()
129
+
111
130
  except Exception as e:
112
131
  print(f"Error setting cache key {key}: {e}")
113
132
  raise
114
133
 
115
134
  def get(self, key: str, type_hint: Type[T] = str) -> Optional[T]:
116
135
  """
117
- Retrieves a value from the cache. Only supports strings or Pydantic models.
136
+ Retrieves a value from the cache. Supports strings, Pydantic models, or lists.
118
137
  The type_hint parameter determines how the value will be parsed and returned.
119
138
 
120
139
  Args:
121
140
  key: The key to retrieve
122
- type_hint: Type hint for the return value. Must be str or a Pydantic model class.
141
+ type_hint: Type hint for the return value. Must be str, list, or a Pydantic model class.
123
142
  Defaults to str.
124
143
 
125
144
  Returns:
@@ -134,25 +153,82 @@ class MooseCache:
134
153
  baz: int
135
154
  qux: bool
136
155
  config = cache.get("foo:config", Config)
156
+
157
+ ### Get a list
158
+ items = cache.get("foo:list", list)
137
159
  """
138
160
  try:
139
161
  # Validate type_hint
140
162
  if not isinstance(type_hint, type):
141
163
  raise TypeError("type_hint must be a type")
142
- if not (type_hint is str or issubclass(type_hint, BaseModel)):
164
+ if not (
165
+ type_hint is str
166
+ or type_hint is list
167
+ or issubclass(type_hint, BaseModel)
168
+ ):
143
169
  raise TypeError(
144
- "type_hint must be str or a Pydantic model class. "
170
+ "type_hint must be str, list, or a Pydantic model class. "
145
171
  f"Got {type_hint.__name__}"
146
172
  )
147
173
 
148
174
  self._ensure_connected()
149
175
  prefixed_key = self._get_prefixed_key(key)
150
- value = self._client.get(prefixed_key)
176
+ metadata_key = f"{prefixed_key}:__type__"
177
+
178
+ # Get both the value and metadata in a single pipeline call
179
+ pipe = self._client.pipeline()
180
+ pipe.get(prefixed_key)
181
+ pipe.get(metadata_key)
182
+ results = pipe.execute()
183
+
184
+ value, stored_type = results[0], results[1]
151
185
 
152
186
  if value is None:
153
187
  return None
154
- elif type_hint is str:
188
+
189
+ # If we have metadata, use it to determine the correct deserialization
190
+ if stored_type:
191
+ if stored_type == "str":
192
+ if type_hint is str:
193
+ return value
194
+ elif type_hint is list:
195
+ # Type mismatch: stored as string but requested as list
196
+ raise ValueError(f"Value was stored as string but requested as list")
197
+ else:
198
+ raise ValueError(f"Value was stored as string but requested as {type_hint.__name__}")
199
+
200
+ elif stored_type == "list":
201
+ parsed_value = json.loads(value)
202
+ if type_hint is list:
203
+ return parsed_value
204
+ elif type_hint is str:
205
+ # Type mismatch: stored as list but requested as string
206
+ raise ValueError(f"Value was stored as list but requested as string")
207
+ else:
208
+ raise ValueError(f"Value was stored as list but requested as {type_hint.__name__}")
209
+
210
+ elif stored_type.startswith("pydantic:"):
211
+ parsed_value = json.loads(value)
212
+ if isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
213
+ return type_hint.model_validate(parsed_value)
214
+ elif type_hint is str:
215
+ # Type mismatch: stored as Pydantic but requested as string
216
+ raise ValueError(f"Value was stored as Pydantic model but requested as string")
217
+ elif type_hint is list:
218
+ # Type mismatch: stored as Pydantic but requested as list
219
+ raise ValueError(f"Value was stored as Pydantic model but requested as list")
220
+ else:
221
+ return type_hint.model_validate(parsed_value)
222
+
223
+ # Backwards compatibility: no metadata found, use legacy behavior
224
+ # But remove the problematic auto-detection for strings
225
+ if type_hint is str:
155
226
  return value
227
+ elif type_hint is list:
228
+ try:
229
+ return json.loads(value)
230
+ except (json.JSONDecodeError, ValueError) as e:
231
+ raise ValueError(f"Failed to parse cached value as list: {e}")
156
232
  elif isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
157
233
  try:
158
234
  parsed = json.loads(value)
@@ -179,7 +255,13 @@ class MooseCache:
179
255
  try:
180
256
  self._ensure_connected()
181
257
  prefixed_key = self._get_prefixed_key(key)
182
- self._client.delete(prefixed_key)
258
+ metadata_key = f"{prefixed_key}:__type__"
259
+
260
+ # Delete both the value and its metadata
261
+ pipe = self._client.pipeline()
262
+ pipe.delete(prefixed_key)
263
+ pipe.delete(metadata_key)
264
+ pipe.execute()
183
265
  except Exception as e:
184
266
  print(f"Error deleting cache key {key}: {e}")
185
267
  raise
@@ -198,6 +280,7 @@ class MooseCache:
198
280
  try:
199
281
  self._ensure_connected()
200
282
  prefixed_key = self._get_prefixed_key(key_prefix)
283
+ # Get both data keys and metadata keys
201
284
  keys = self._client.keys(f"{prefixed_key}*")
202
285
  if keys:
203
286
  self._client.delete(*keys)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moose_lib
3
- Version: 0.6.85
3
+ Version: 0.6.86
4
4
  Home-page: https://www.fiveonefour.com/moose
5
5
  Author: Fiveonefour Labs Inc.
6
6
  Author-email: support@fiveonefour.com
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes