spooled 1.2.1__tar.gz → 1.2.2__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 (67) hide show
  1. {spooled-1.2.1 → spooled-1.2.2}/PKG-INFO +1 -1
  2. {spooled-1.2.1 → spooled-1.2.2}/pyproject.toml +1 -1
  3. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/_version.py +1 -1
  4. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/realtime/sse.py +33 -18
  5. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/realtime/unified.py +16 -9
  6. {spooled-1.2.1 → spooled-1.2.2}/.gitignore +0 -0
  7. {spooled-1.2.1 → spooled-1.2.2}/LICENSE +0 -0
  8. {spooled-1.2.1 → spooled-1.2.2}/README.md +0 -0
  9. {spooled-1.2.1 → spooled-1.2.2}/proto/spooled.proto +0 -0
  10. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/__init__.py +0 -0
  11. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/async_client.py +0 -0
  12. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/client.py +0 -0
  13. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/config.py +0 -0
  14. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/errors.py +0 -0
  15. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/grpc/__init__.py +0 -0
  16. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/grpc/client.py +0 -0
  17. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/grpc/stubs/__init__.py +0 -0
  18. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/grpc/stubs/spooled_pb2.py +0 -0
  19. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/grpc/stubs/spooled_pb2.pyi +0 -0
  20. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/grpc/stubs/spooled_pb2_grpc.py +0 -0
  21. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/py.typed +0 -0
  22. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/realtime/__init__.py +0 -0
  23. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/realtime/events.py +0 -0
  24. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/realtime/websocket.py +0 -0
  25. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/__init__.py +0 -0
  26. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/admin.py +0 -0
  27. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/api_keys.py +0 -0
  28. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/auth.py +0 -0
  29. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/base.py +0 -0
  30. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/billing.py +0 -0
  31. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/dashboard.py +0 -0
  32. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/health.py +0 -0
  33. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/ingest.py +0 -0
  34. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/jobs.py +0 -0
  35. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/metrics.py +0 -0
  36. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/organizations.py +0 -0
  37. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/queues.py +0 -0
  38. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/schedules.py +0 -0
  39. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/webhooks.py +0 -0
  40. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/workers.py +0 -0
  41. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/resources/workflows.py +0 -0
  42. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/__init__.py +0 -0
  43. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/admin.py +0 -0
  44. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/api_keys.py +0 -0
  45. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/auth.py +0 -0
  46. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/billing.py +0 -0
  47. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/common.py +0 -0
  48. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/dashboard.py +0 -0
  49. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/health.py +0 -0
  50. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/jobs.py +0 -0
  51. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/organizations.py +0 -0
  52. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/queues.py +0 -0
  53. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/schedules.py +0 -0
  54. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/webhooks.py +0 -0
  55. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/workers.py +0 -0
  56. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/types/workflows.py +0 -0
  57. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/utils/__init__.py +0 -0
  58. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/utils/async_http.py +0 -0
  59. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/utils/casing.py +0 -0
  60. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/utils/circuit_breaker.py +0 -0
  61. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/utils/http.py +0 -0
  62. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/utils/jwt.py +0 -0
  63. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/utils/retry.py +0 -0
  64. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/worker/__init__.py +0 -0
  65. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/worker/async_worker.py +0 -0
  66. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/worker/types.py +0 -0
  67. {spooled-1.2.1 → spooled-1.2.2}/src/spooled/worker/worker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: spooled
3
- Version: 1.2.1
3
+ Version: 1.2.2
4
4
  Summary: Official Python SDK for Spooled Cloud job queue service
5
5
  Project-URL: Homepage, https://spooled.cloud
6
6
  Project-URL: Documentation, https://spooled.cloud/docs/
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "spooled"
7
- version = "1.2.1"
7
+ version = "1.2.2"
8
8
  description = "Official Python SDK for Spooled Cloud job queue service"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -5,4 +5,4 @@ from importlib.metadata import PackageNotFoundError, version
5
5
  try:
6
6
  __version__ = version("spooled")
7
7
  except PackageNotFoundError: # pragma: no cover - source checkout without installed metadata
8
- __version__ = "1.2.1"
8
+ __version__ = "1.2.2"
@@ -304,16 +304,27 @@ class SSEClient:
304
304
 
305
305
  try:
306
306
  self._client = httpx.Client()
307
- self._response = self._client.stream(
308
- "GET",
309
- url,
310
- headers={
311
- "Authorization": f"Bearer {self._token}",
312
- "Accept": "text/event-stream",
313
- },
314
- ).__enter__()
315
-
316
- self._sse_client = sseclient.SSEClient(self._response.iter_lines()) # type: ignore[arg-type]
307
+ # send(stream=True) keeps the response open until close(). Entering
308
+ # client.stream(...) by hand dropped its context manager, which the
309
+ # garbage collector then exited — closing the stream before any
310
+ # event was read ("the stream has been closed").
311
+ self._response = self._client.send(
312
+ self._client.build_request(
313
+ "GET",
314
+ url,
315
+ headers={
316
+ "Authorization": f"Bearer {self._token}",
317
+ "Accept": "text/event-stream",
318
+ },
319
+ ),
320
+ stream=True,
321
+ )
322
+
323
+ # sseclient needs raw byte chunks: it finds event boundaries by the
324
+ # blank line between events. iter_lines() yields str lines with the
325
+ # newlines stripped, so the first line raised TypeError and the
326
+ # stream never produced an event.
327
+ self._sse_client = sseclient.SSEClient(self._response.iter_bytes()) # type: ignore[arg-type]
317
328
  self._set_state(SSEConnectionState.CONNECTED)
318
329
  self._reconnect_attempts = 0
319
330
  self._closed = False
@@ -650,14 +661,18 @@ class AsyncSSEClient:
650
661
 
651
662
  try:
652
663
  self._client = httpx.AsyncClient()
653
- self._response = await self._client.stream(
654
- "GET",
655
- url,
656
- headers={
657
- "Authorization": f"Bearer {self._token}",
658
- "Accept": "text/event-stream",
659
- },
660
- ).__aenter__()
664
+ # See SSEClient.connect: keep the response open until close().
665
+ self._response = await self._client.send(
666
+ self._client.build_request(
667
+ "GET",
668
+ url,
669
+ headers={
670
+ "Authorization": f"Bearer {self._token}",
671
+ "Accept": "text/event-stream",
672
+ },
673
+ ),
674
+ stream=True,
675
+ )
661
676
  self._set_state(SSEConnectionState.CONNECTED)
662
677
  self._reconnect_attempts = 0
663
678
  self._closed = False
@@ -510,17 +510,24 @@ class SpooledRealtime:
510
510
 
511
511
  try:
512
512
  self._http_client = httpx.Client()
513
- self._sse_response = self._http_client.stream(
514
- "GET",
515
- url,
516
- headers={
517
- "Authorization": f"Bearer {self._options.token}",
518
- "Accept": "text/event-stream",
519
- },
520
- ).__enter__()
513
+ # send(stream=True) keeps the response open until disconnect(); a
514
+ # hand-entered client.stream(...) was closed by the GC (see sse.py).
515
+ self._sse_response = self._http_client.send(
516
+ self._http_client.build_request(
517
+ "GET",
518
+ url,
519
+ headers={
520
+ "Authorization": f"Bearer {self._options.token}",
521
+ "Accept": "text/event-stream",
522
+ },
523
+ ),
524
+ stream=True,
525
+ )
521
526
 
522
527
  if HAS_SSE:
523
- self._sse_client = sseclient.SSEClient(self._sse_response.iter_lines())
528
+ # Raw byte chunks: sseclient splits events on the blank line,
529
+ # which iter_lines() strips (see realtime/sse.py).
530
+ self._sse_client = sseclient.SSEClient(self._sse_response.iter_bytes())
524
531
 
525
532
  self._set_state(ConnectionState.CONNECTED)
526
533
  self._reconnect_attempts = 0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes