nebu 0.1.19__py3-none-any.whl → 0.1.20__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.
- nebu/processors/consumer.py +62 -20
- {nebu-0.1.19.dist-info → nebu-0.1.20.dist-info}/METADATA +1 -1
- {nebu-0.1.19.dist-info → nebu-0.1.20.dist-info}/RECORD +6 -6
- {nebu-0.1.19.dist-info → nebu-0.1.20.dist-info}/WHEEL +0 -0
- {nebu-0.1.19.dist-info → nebu-0.1.20.dist-info}/licenses/LICENSE +0 -0
- {nebu-0.1.19.dist-info → nebu-0.1.20.dist-info}/top_level.txt +0 -0
nebu/processors/consumer.py
CHANGED
@@ -6,10 +6,11 @@ import sys
|
|
6
6
|
import time
|
7
7
|
import traceback
|
8
8
|
from datetime import datetime
|
9
|
-
from typing import Dict, TypeVar
|
9
|
+
from typing import Any, Dict, TypeVar, cast
|
10
10
|
|
11
11
|
import redis
|
12
12
|
import socks
|
13
|
+
from redis import ConnectionError, ResponseError
|
13
14
|
|
14
15
|
# Define TypeVar for generic models
|
15
16
|
T = TypeVar("T")
|
@@ -247,7 +248,7 @@ except Exception as e:
|
|
247
248
|
try:
|
248
249
|
r.xgroup_create(REDIS_STREAM, REDIS_CONSUMER_GROUP, id="0", mkstream=True)
|
249
250
|
print(f"Created consumer group {REDIS_CONSUMER_GROUP} for stream {REDIS_STREAM}")
|
250
|
-
except
|
251
|
+
except ResponseError as e:
|
251
252
|
if "BUSYGROUP" in str(e):
|
252
253
|
print(f"Consumer group {REDIS_CONSUMER_GROUP} already exists")
|
253
254
|
else:
|
@@ -262,25 +263,19 @@ def process_message(message_id: bytes, message_data: Dict[bytes, bytes]) -> None
|
|
262
263
|
user_id = None
|
263
264
|
|
264
265
|
try:
|
265
|
-
#
|
266
|
-
|
267
|
-
|
268
|
-
# Raise an error if 'data' field is missing
|
269
|
-
raise ValueError("Message missing 'data' field")
|
266
|
+
# Assign message_data directly to raw_payload.
|
267
|
+
# Cast to Dict[str, Any] to inform type checker of the expected type due to decode_responses=True.
|
268
|
+
raw_payload = cast(Dict[str, Any], message_data)
|
270
269
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
raw_payload = json.loads(raw_payload_str)
|
275
|
-
except (json.JSONDecodeError, UnicodeDecodeError) as decode_error:
|
276
|
-
# Raise a specific error for decoding/parsing issues
|
277
|
-
raise ValueError(f"Invalid JSON payload: {decode_error}") from decode_error
|
270
|
+
# Validate that raw_payload is a dictionary as expected - Removed, cast handles this for type checker
|
271
|
+
# if not isinstance(raw_payload, dict):
|
272
|
+
# raise TypeError(f"Expected message_data to be a dictionary, but got {type(raw_payload)}")
|
278
273
|
|
279
274
|
print(f"Raw payload: {raw_payload}")
|
280
275
|
|
281
276
|
# Extract fields from the parsed payload
|
282
277
|
# These fields are extracted for completeness and potential future use
|
283
|
-
|
278
|
+
kind = raw_payload.get("kind", "") # kind
|
284
279
|
msg_id = raw_payload.get("id", "") # msg_id
|
285
280
|
content_raw = raw_payload.get("content", {})
|
286
281
|
created_at = raw_payload.get("created_at", 0) # created_at
|
@@ -290,6 +285,31 @@ def process_message(message_id: bytes, message_data: Dict[bytes, bytes]) -> None
|
|
290
285
|
handle = raw_payload.get("handle") # handle
|
291
286
|
adapter = raw_payload.get("adapter") # adapter
|
292
287
|
|
288
|
+
# --- Health Check Logic based on kind ---
|
289
|
+
if kind == "HealthCheck":
|
290
|
+
print(f"Received HealthCheck message {message_id.decode('utf-8')}")
|
291
|
+
health_response = {
|
292
|
+
"kind": "StreamResponseMessage", # Respond with a standard message kind
|
293
|
+
"id": message_id.decode("utf-8"),
|
294
|
+
"content": {"status": "healthy", "checked_message_id": msg_id},
|
295
|
+
"status": "success",
|
296
|
+
"created_at": datetime.now().isoformat(),
|
297
|
+
"user_id": user_id, # Include user_id if available
|
298
|
+
}
|
299
|
+
if return_stream:
|
300
|
+
# Assert type again closer to usage for type checker clarity
|
301
|
+
assert isinstance(return_stream, str)
|
302
|
+
r.xadd(return_stream, {"data": json.dumps(health_response)})
|
303
|
+
print(f"Sent health check response to {return_stream}")
|
304
|
+
|
305
|
+
# Assert types again closer to usage for type checker clarity
|
306
|
+
assert isinstance(REDIS_STREAM, str)
|
307
|
+
assert isinstance(REDIS_CONSUMER_GROUP, str)
|
308
|
+
r.xack(REDIS_STREAM, REDIS_CONSUMER_GROUP, message_id)
|
309
|
+
print(f"Acknowledged HealthCheck message {message_id.decode('utf-8')}")
|
310
|
+
return # Exit early for health checks
|
311
|
+
# --- End Health Check Logic ---
|
312
|
+
|
293
313
|
# Parse the content field if it's a string
|
294
314
|
if isinstance(content_raw, str):
|
295
315
|
try:
|
@@ -310,7 +330,7 @@ def process_message(message_id: bytes, message_data: Dict[bytes, bytes]) -> None
|
|
310
330
|
content_model = local_namespace[content_type_name](**content)
|
311
331
|
print(f"Content model: {content_model}")
|
312
332
|
input_obj = local_namespace["V1StreamMessage"](
|
313
|
-
kind=
|
333
|
+
kind=kind,
|
314
334
|
id=msg_id,
|
315
335
|
content=content_model,
|
316
336
|
created_at=created_at,
|
@@ -324,7 +344,7 @@ def process_message(message_id: bytes, message_data: Dict[bytes, bytes]) -> None
|
|
324
344
|
print(f"Error creating content type model: {e}")
|
325
345
|
# Fallback to using raw content
|
326
346
|
input_obj = local_namespace["V1StreamMessage"](
|
327
|
-
kind=
|
347
|
+
kind=kind,
|
328
348
|
id=msg_id,
|
329
349
|
content=content,
|
330
350
|
created_at=created_at,
|
@@ -338,7 +358,7 @@ def process_message(message_id: bytes, message_data: Dict[bytes, bytes]) -> None
|
|
338
358
|
# Just use the raw content
|
339
359
|
print(f"Using raw content")
|
340
360
|
input_obj = local_namespace["V1StreamMessage"](
|
341
|
-
kind=
|
361
|
+
kind=kind,
|
342
362
|
id=msg_id,
|
343
363
|
content=content,
|
344
364
|
created_at=created_at,
|
@@ -383,12 +403,17 @@ def process_message(message_id: bytes, message_data: Dict[bytes, bytes]) -> None
|
|
383
403
|
|
384
404
|
# Send the result to the return stream
|
385
405
|
if return_stream:
|
406
|
+
# Assert type again closer to usage for type checker clarity
|
407
|
+
assert isinstance(return_stream, str)
|
386
408
|
r.xadd(return_stream, {"data": json.dumps(response)})
|
387
409
|
print(
|
388
410
|
f"Processed message {message_id.decode('utf-8')}, result sent to {return_stream}"
|
389
411
|
)
|
390
412
|
|
391
413
|
# Acknowledge the message
|
414
|
+
# Assert types again closer to usage for type checker clarity
|
415
|
+
assert isinstance(REDIS_STREAM, str)
|
416
|
+
assert isinstance(REDIS_CONSUMER_GROUP, str)
|
392
417
|
r.xack(REDIS_STREAM, REDIS_CONSUMER_GROUP, message_id)
|
393
418
|
|
394
419
|
except Exception as e:
|
@@ -410,11 +435,18 @@ def process_message(message_id: bytes, message_data: Dict[bytes, bytes]) -> None
|
|
410
435
|
|
411
436
|
# Send the error to the return stream
|
412
437
|
if return_stream:
|
438
|
+
# Assert type again closer to usage for type checker clarity
|
439
|
+
assert isinstance(return_stream, str)
|
413
440
|
r.xadd(return_stream, {"data": json.dumps(error_response)})
|
414
441
|
else:
|
442
|
+
# Assert type again closer to usage for type checker clarity
|
443
|
+
assert isinstance(REDIS_STREAM, str)
|
415
444
|
r.xadd(f"{REDIS_STREAM}.errors", {"data": json.dumps(error_response)})
|
416
445
|
|
417
446
|
# Still acknowledge the message so we don't reprocess it
|
447
|
+
# Assert types again closer to usage for type checker clarity
|
448
|
+
assert isinstance(REDIS_STREAM, str)
|
449
|
+
assert isinstance(REDIS_CONSUMER_GROUP, str)
|
418
450
|
r.xack(REDIS_STREAM, REDIS_CONSUMER_GROUP, message_id)
|
419
451
|
|
420
452
|
|
@@ -424,9 +456,13 @@ consumer_name = f"consumer-{os.getpid()}"
|
|
424
456
|
|
425
457
|
while True:
|
426
458
|
try:
|
459
|
+
# Assert types just before use in the loop
|
460
|
+
assert isinstance(REDIS_STREAM, str)
|
461
|
+
assert isinstance(REDIS_CONSUMER_GROUP, str)
|
462
|
+
|
427
463
|
# Read from stream with blocking
|
428
464
|
streams = {REDIS_STREAM: ">"} # '>' means read only new messages
|
429
|
-
messages = r.xreadgroup(
|
465
|
+
messages = r.xreadgroup( # type: ignore[arg-type]
|
430
466
|
REDIS_CONSUMER_GROUP, consumer_name, streams, count=1, block=5000
|
431
467
|
)
|
432
468
|
|
@@ -434,6 +470,12 @@ while True:
|
|
434
470
|
# No messages received, continue waiting
|
435
471
|
continue
|
436
472
|
|
473
|
+
# Assert that messages is a list (expected synchronous return type)
|
474
|
+
assert isinstance(
|
475
|
+
messages, list
|
476
|
+
), f"Expected list from xreadgroup, got {type(messages)}"
|
477
|
+
assert len(messages) > 0 # Ensure the list is not empty before indexing
|
478
|
+
|
437
479
|
stream_name, stream_messages = messages[0]
|
438
480
|
|
439
481
|
for message_id, message_data in stream_messages:
|
@@ -441,7 +483,7 @@ while True:
|
|
441
483
|
print(f"Message data: {message_data}")
|
442
484
|
process_message(message_id, message_data)
|
443
485
|
|
444
|
-
except
|
486
|
+
except ConnectionError as e:
|
445
487
|
print(f"Redis connection error: {e}")
|
446
488
|
time.sleep(5) # Wait before retrying
|
447
489
|
|
@@ -6,15 +6,15 @@ nebu/containers/container.py,sha256=yb7KaPTVXnEEAlrpdlUi4HNqF6P7z9bmwAILGlq6iqU,
|
|
6
6
|
nebu/containers/decorator.py,sha256=uFtzlAXRHYZECJ-NPusY7oN9GXvdHrHDd_JNrIGr8aQ,3244
|
7
7
|
nebu/containers/models.py,sha256=0j6NGy4yto-enRDh_4JH_ZTbHrLdSpuMOqNQPnIrwC4,6815
|
8
8
|
nebu/containers/server.py,sha256=yFa2Y9PzBn59E1HftKiv0iapPonli2rbGAiU6r-wwe0,2513
|
9
|
-
nebu/processors/consumer.py,sha256=
|
9
|
+
nebu/processors/consumer.py,sha256=kX4UT8Z5c_LWU3MOcM5qXSzHEA0CYjUZ4dRJfhUKP-M,19202
|
10
10
|
nebu/processors/decorate.py,sha256=AeG1c1n8JtcexxAEf2sF2L2eKwVDaNQ5gvPs6EpazKo,34789
|
11
11
|
nebu/processors/default.py,sha256=W4slJenG59rvyTlJ7gRp58eFfXcNOTT2Hfi6zzJAobI,365
|
12
12
|
nebu/processors/models.py,sha256=GvnI8UJrQSjHo2snP07cPfisCH90cEGTY-PZV5_AtXI,3654
|
13
13
|
nebu/processors/processor.py,sha256=oy2YdI-cy6qQWxrZhpZahJV46oWZlu_Im-jm811R_oo,9667
|
14
14
|
nebu/redis/models.py,sha256=coPovAcVXnOU1Xh_fpJL4PO3QctgK9nBe5QYoqEcnxg,1230
|
15
15
|
nebu/services/service.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
-
nebu-0.1.
|
17
|
-
nebu-0.1.
|
18
|
-
nebu-0.1.
|
19
|
-
nebu-0.1.
|
20
|
-
nebu-0.1.
|
16
|
+
nebu-0.1.20.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
17
|
+
nebu-0.1.20.dist-info/METADATA,sha256=mmrQRfaJ6jXFMa02a7rp4ghX1wKW8m3TLHeBCsmk64g,1678
|
18
|
+
nebu-0.1.20.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
19
|
+
nebu-0.1.20.dist-info/top_level.txt,sha256=uLIbEKJeGSHWOAJN5S0i5XBGwybALlF9bYoB1UhdEgQ,5
|
20
|
+
nebu-0.1.20.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|