moose-lib 0.6.50__py3-none-any.whl → 0.6.52__py3-none-any.whl

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.

moose_lib/main.py CHANGED
@@ -19,6 +19,8 @@ from string import Formatter
19
19
  from temporalio.client import Client as TemporalClient, TLSConfig
20
20
  from temporalio.common import RetryPolicy, WorkflowIDConflictPolicy, WorkflowIDReusePolicy
21
21
  from datetime import timedelta
22
+ from time import perf_counter
23
+ from humanfriendly import format_timespan
22
24
  from .config.runtime import RuntimeClickHouseConfig
23
25
 
24
26
  from moose_lib.commons import EnhancedJSONEncoder
@@ -182,6 +184,7 @@ class QueryClient:
182
184
  def execute(self, input, variables, row_type: Type[BaseModel] = None):
183
185
  params = {}
184
186
  values = {}
187
+ preview_params = {}
185
188
 
186
189
  for i, (_, variable_name, _, _) in enumerate(Formatter().parse(input)):
187
190
  if variable_name:
@@ -196,20 +199,63 @@ class QueryClient:
196
199
 
197
200
  params[variable_name] = f'{{p{i}: {t}}}'
198
201
  values[f'p{i}'] = value
202
+ preview_params[variable_name] = self._format_value_for_preview(value)
203
+
199
204
  clickhouse_query = input.format_map(params)
205
+ preview_query = input.format_map(preview_params)
200
206
 
201
207
  # We are not using the result of the ping
202
208
  # but this ensures that if the clickhouse cloud service is idle, we
203
209
  # wake it up, before we send the query.
204
210
  self.ch_client.ping()
205
211
 
212
+ print(f"[QueryClient] | Query: {' '.join(preview_query.split())}")
213
+ start = perf_counter()
206
214
  val = self.ch_client.query(clickhouse_query, values)
215
+ secs = perf_counter() - start
216
+ if secs < 1:
217
+ print(f"[QueryClient] | Query completed: {secs * 1000:.0f} ms")
218
+ else:
219
+ print(f"[QueryClient] | Query completed: {format_timespan(secs)}")
207
220
 
208
221
  if row_type is None:
209
222
  return list(val.named_results())
210
223
  else:
211
224
  return list(row_type(**row) for row in val.named_results())
212
225
 
226
+ def _format_value_for_preview(self, value: Any) -> str:
227
+ """Format a Python value as a ClickHouse SQL literal for preview logging.
228
+
229
+ This does not affect execution; it's only for human-readable query logs.
230
+ """
231
+ # NULL handling
232
+ if value is None:
233
+ return 'NULL'
234
+
235
+ # Booleans (ClickHouse accepts true/false)
236
+ if isinstance(value, bool):
237
+ return 'true' if value else 'false'
238
+
239
+ # Numbers
240
+ if isinstance(value, (int, float)) and not isinstance(value, bool):
241
+ return str(value)
242
+
243
+ # Strings
244
+ if isinstance(value, str):
245
+ # Escape backslashes and single quotes for ClickHouse single-quoted strings
246
+ escaped = value.replace('\\', '\\\\').replace("'", "\\'")
247
+ return f"'{escaped}'"
248
+
249
+ # Lists / tuples (format as [item1, item2, ...])
250
+ if isinstance(value, (list, tuple)):
251
+ formatted_items = ', '.join(self._format_value_for_preview(v) for v in value)
252
+ return f"[{formatted_items}]"
253
+
254
+ # Fallback: stringify and single-quote
255
+ fallback = str(value)
256
+ escaped_fallback = fallback.replace('\\', '\\\\').replace("'", "\\'")
257
+ return f"'{escaped_fallback}'"
258
+
213
259
  def close(self):
214
260
  """Close the ClickHouse client connection."""
215
261
  if self.ch_client:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moose_lib
3
- Version: 0.6.50
3
+ Version: 0.6.52
4
4
  Home-page: https://www.fiveonefour.com/moose
5
5
  Author: Fiveonefour Labs Inc.
6
6
  Author-email: support@fiveonefour.com
@@ -4,7 +4,7 @@ moose_lib/commons.py,sha256=FUpRv8D3-LeGcjhcqtDyiimz5izwpCq53h50ydxC_uA,3711
4
4
  moose_lib/data_models.py,sha256=PgoFXTzf4C66FlKUowMT4VOrwSRR9_bk36P43ub4LzU,10274
5
5
  moose_lib/dmv2-serializer.py,sha256=CL_Pvvg8tJOT8Qk6hywDNzY8MYGhMVdTOw8arZi3jng,49
6
6
  moose_lib/internal.py,sha256=p9WtKScfx012FgBdDs114iqzGbEwPzTMRabcQPjC-tA,20662
7
- moose_lib/main.py,sha256=XcVX_sTnt5QbrPXKNLCKZGCvpFpE8oiqSG2S1So9ztI,16713
7
+ moose_lib/main.py,sha256=dLcRE4jshP6ViDVLj--Y83QRsEp0dpwh7WilSEZ4ICk,18541
8
8
  moose_lib/query_param.py,sha256=kxcR09BMIsEg4o2qetjKrVu1YFRaLfMEzwzyGsKUpvA,6474
9
9
  moose_lib/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  moose_lib/clients/redis_client.py,sha256=UBCdxwgZpIOIOy2EnPyxJIAYjw_qmNwGsJQCQ66SxUI,8117
@@ -34,7 +34,7 @@ tests/conftest.py,sha256=ZVJNbnr4DwbcqkTmePW6U01zAzE6QD0kNAEZjPG1f4s,169
34
34
  tests/test_moose.py,sha256=mBsx_OYWmL8ppDzL_7Bd7xR6qf_i3-pCIO3wm2iQNaA,2136
35
35
  tests/test_redis_client.py,sha256=d9_MLYsJ4ecVil_jPB2gW3Q5aWnavxmmjZg2uYI3LVo,3256
36
36
  tests/test_s3queue_config.py,sha256=F05cnD61S2wBKPabcpEJxf55-DJGF4nLqwBb6aFbprc,9741
37
- moose_lib-0.6.50.dist-info/METADATA,sha256=gc8rCqTWfCxE1FJ7Z902Pjz-_kvpkXui3tgMOjmxJ9M,730
38
- moose_lib-0.6.50.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
39
- moose_lib-0.6.50.dist-info/top_level.txt,sha256=XEns2-4aCmGp2XjJAeEH9TAUcGONLnSLy6ycT9FSJh8,16
40
- moose_lib-0.6.50.dist-info/RECORD,,
37
+ moose_lib-0.6.52.dist-info/METADATA,sha256=EtoU8nt5NRq_fR4ThRtZ6NrUO7svAc1W6BXkQMl5jY8,730
38
+ moose_lib-0.6.52.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
39
+ moose_lib-0.6.52.dist-info/top_level.txt,sha256=XEns2-4aCmGp2XjJAeEH9TAUcGONLnSLy6ycT9FSJh8,16
40
+ moose_lib-0.6.52.dist-info/RECORD,,