kurrentdbclient 1.2__tar.gz → 1.2.1__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 (64) hide show
  1. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/PKG-INFO +2 -2
  2. kurrentdbclient-1.2.1/kurrentdbclient/.DS_Store +0 -0
  3. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/__init__.py +1 -1
  4. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/asyncio_client.py +59 -41
  5. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/client.py +2 -1
  6. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/common.py +6 -0
  7. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/exceptions.py +6 -0
  8. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/instrumentation/opentelemetry/utils.py +3 -0
  9. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/pyproject.toml +3 -3
  10. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/LICENSE +0 -0
  11. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/README.md +0 -0
  12. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/connection.py +0 -0
  13. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/connection_spec.py +0 -0
  14. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/events.py +0 -0
  15. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/gossip.py +0 -0
  16. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/instrumentation/__init__.py +0 -0
  17. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/instrumentation/opentelemetry/__init__.py +0 -0
  18. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/instrumentation/opentelemetry/attributes.py +0 -0
  19. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/instrumentation/opentelemetry/grpc.py +0 -0
  20. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/instrumentation/opentelemetry/package.py +0 -0
  21. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/instrumentation/opentelemetry/spanners.py +0 -0
  22. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/instrumentation/opentelemetry/version.py +0 -0
  23. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/persistent.py +0 -0
  24. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/projections.py +0 -0
  25. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/google/rpc/code_pb2.py +0 -0
  26. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/google/rpc/code_pb2.pyi +0 -0
  27. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/google/rpc/code_pb2_grpc.py +0 -0
  28. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/kurrent/rpc/errors_pb2.py +0 -0
  29. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/kurrent/rpc/errors_pb2.pyi +0 -0
  30. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/kurrent/rpc/errors_pb2_grpc.py +0 -0
  31. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/kurrent/rpc/rpc_pb2.py +0 -0
  32. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/kurrent/rpc/rpc_pb2.pyi +0 -0
  33. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/kurrent/rpc/rpc_pb2_grpc.py +0 -0
  34. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/cluster_pb2.py +0 -0
  35. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/cluster_pb2.pyi +0 -0
  36. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/cluster_pb2_grpc.py +0 -0
  37. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/gossip_pb2.py +0 -0
  38. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/gossip_pb2.pyi +0 -0
  39. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/gossip_pb2_grpc.py +0 -0
  40. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/persistent_pb2.py +0 -0
  41. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/persistent_pb2.pyi +0 -0
  42. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/persistent_pb2_grpc.py +0 -0
  43. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/projections_pb2.py +0 -0
  44. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/projections_pb2.pyi +0 -0
  45. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/projections_pb2_grpc.py +0 -0
  46. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/shared_pb2.py +0 -0
  47. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/shared_pb2.pyi +0 -0
  48. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/shared_pb2_grpc.py +0 -0
  49. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/status_pb2.py +0 -0
  50. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/status_pb2.pyi +0 -0
  51. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/status_pb2_grpc.py +0 -0
  52. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/streams_pb2.py +0 -0
  53. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/streams_pb2.pyi +0 -0
  54. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v1/streams_pb2_grpc.py +0 -0
  55. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v2/streams/errors_pb2.py +0 -0
  56. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v2/streams/errors_pb2.pyi +0 -0
  57. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v2/streams/errors_pb2_grpc.py +0 -0
  58. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v2/streams/streams_pb2.py +0 -0
  59. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v2/streams/streams_pb2.pyi +0 -0
  60. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/protos/v2/streams/streams_pb2_grpc.py +0 -0
  61. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/py.typed +0 -0
  62. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/streams.py +0 -0
  63. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/unpack_error_status.py +0 -0
  64. {kurrentdbclient-1.2 → kurrentdbclient-1.2.1}/kurrentdbclient/v2streams.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kurrentdbclient
3
- Version: 1.2
3
+ Version: 1.2.1
4
4
  Summary: Python gRPC Client for KurrentDB
5
5
  License: BSD-3-Clause
6
6
  License-File: LICENSE
7
7
  Author: John Bywater
8
8
  Author-email: john.bywater@appropriatesoftware.net
9
9
  Requires-Python: >=3.10
10
- Classifier: Development Status :: 4 - Beta
10
+ Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: License :: OSI Approved :: BSD License
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3.10
@@ -26,7 +26,7 @@ from kurrentdbclient.streams import (
26
26
  ReadResponse,
27
27
  )
28
28
 
29
- __version__ = "1.2"
29
+ __version__ = "1.2.1"
30
30
 
31
31
  __all__ = [
32
32
  "DEFAULT_EXCLUDE_FILTER",
@@ -52,6 +52,7 @@ from kurrentdbclient.exceptions import (
52
52
  GrpcError,
53
53
  NodeIsNotLeaderError,
54
54
  NotFoundError,
55
+ ProgrammingError,
55
56
  ServiceUnavailableError,
56
57
  )
57
58
 
@@ -70,6 +71,7 @@ def autoreconnect(f: _TCallable) -> _TCallable:
70
71
  async def autoreconnect_decorator(
71
72
  client: AsyncKurrentDBClient, *args: Any, **kwargs: Any
72
73
  ) -> Any:
74
+ await client.connect()
73
75
  try:
74
76
  return await f(client, *args, **kwargs)
75
77
 
@@ -129,22 +131,36 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
129
131
  private_key=private_key,
130
132
  certificate_chain=certificate_chain,
131
133
  )
134
+ self._connection: AsyncKurrentDBConnection | None = None
132
135
  self._is_reconnection_required = Event()
133
- self._reconnection_lock = Lock()
136
+ self._connection_lock = Lock()
137
+
138
+ async def connect(self) -> None:
139
+ if self._connection is None:
140
+ async with self._connection_lock:
141
+ if self._connection is None:
142
+ self._connection = await self._connect()
143
+ else: # for coverage with Python <= 3.11
144
+ pass
134
145
 
135
146
  @property
136
- def connection_target(self) -> str:
137
- return self._connection.grpc_target
147
+ def connection(self) -> AsyncKurrentDBConnection:
148
+ connection = self._connection
149
+ if connection is None:
150
+ msg = "Connection was never established"
151
+ raise ProgrammingError(msg)
152
+ return connection
138
153
 
139
- async def connect(self) -> None:
140
- self._connection = await self._connect()
154
+ @property
155
+ def connection_target(self) -> str:
156
+ return self.connection.grpc_target
141
157
 
142
158
  async def reconnect(self, grpc_target: str | None = None) -> None:
143
159
  self._is_reconnection_required.set()
144
- async with self._reconnection_lock:
160
+ async with self._connection_lock:
145
161
  if self._is_reconnection_required.is_set():
146
162
  new = await self._connect(grpc_target)
147
- old, self._connection = self._connection, new
163
+ old, self._connection = self.connection, new
148
164
  await old.close()
149
165
  self._is_reconnection_required.clear()
150
166
  else: # pragma: no cover
@@ -283,7 +299,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
283
299
  if isinstance(events, NewEvent):
284
300
  events = [events]
285
301
 
286
- return await self._connection.streams.batch_append(
302
+ return await self.connection.streams.batch_append(
287
303
  stream_name=stream_name,
288
304
  current_version=current_version,
289
305
  events=events,
@@ -308,7 +324,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
308
324
  timeout = timeout if timeout is not None else self._default_deadline
309
325
  if isinstance(events, NewEvents):
310
326
  events = [events]
311
- return await self._connection.v2streams.multi_append(
327
+ return await self.connection.v2streams.multi_append(
312
328
  events=events,
313
329
  timeout=timeout,
314
330
  metadata=self._call_metadata,
@@ -334,7 +350,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
334
350
  """
335
351
  Reads recorded events in "all streams" in the database.
336
352
  """
337
- return await self._connection.streams.read(
353
+ return await self.connection.streams.read(
338
354
  commit_position=commit_position,
339
355
  backwards=backwards,
340
356
  resolve_links=resolve_links,
@@ -442,7 +458,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
442
458
  """
443
459
  Reads recorded events from the named stream.
444
460
  """
445
- return await self._connection.streams.read(
461
+ return await self.connection.streams.read(
446
462
  stream_name=stream_name,
447
463
  stream_position=stream_position,
448
464
  backwards=backwards,
@@ -531,7 +547,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
531
547
  Starts a catch-up subscription, from which all
532
548
  recorded events in the database can be received.
533
549
  """
534
- return await self._connection.streams.read(
550
+ return await self.connection.streams.read(
535
551
  commit_position=commit_position,
536
552
  from_end=from_end,
537
553
  resolve_links=resolve_links,
@@ -637,7 +653,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
637
653
  Starts a catch-up subscription from which
638
654
  recorded events in a stream can be received.
639
655
  """
640
- return await self._connection.streams.read(
656
+ return await self.connection.streams.read(
641
657
  stream_name=stream_name,
642
658
  stream_position=stream_position,
643
659
  from_end=from_end,
@@ -662,7 +678,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
662
678
  ) -> None:
663
679
  # Todo: Reconsider using current_version=None to indicate "stream exists"?
664
680
  timeout = timeout if timeout is not None else self._default_deadline
665
- await self._connection.streams.delete(
681
+ await self.connection.streams.delete(
666
682
  stream_name=stream_name,
667
683
  current_version=current_version,
668
684
  timeout=timeout,
@@ -681,7 +697,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
681
697
  credentials: grpc.CallCredentials | None = None,
682
698
  ) -> None:
683
699
  timeout = timeout if timeout is not None else self._default_deadline
684
- await self._connection.streams.tombstone(
700
+ await self.connection.streams.tombstone(
685
701
  stream_name=stream_name,
686
702
  current_version=current_version,
687
703
  timeout=timeout,
@@ -699,12 +715,13 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
699
715
  credentials: grpc.CallCredentials | None = None,
700
716
  ) -> int | Literal[StreamState.NO_STREAM]:
701
717
  """
702
- Returns the current position of the end of a stream.
718
+ Returns the current position of the end of a stream,
719
+ or StreamState.NO_STREAM if the stream is not found.
703
720
  """
704
721
  try:
705
722
  events = [
706
723
  e
707
- async for e in await self._connection.streams.read(
724
+ async for e in await self.connection.streams.read(
708
725
  stream_name=stream_name,
709
726
  backwards=True,
710
727
  limit=1,
@@ -841,7 +858,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
841
858
  """
842
859
  timeout = timeout if timeout is not None else self._default_deadline
843
860
 
844
- await self._connection.persistent_subscriptions.create(
861
+ await self.connection.persistent_subscriptions.create(
845
862
  group_name=group_name,
846
863
  from_end=from_end,
847
864
  commit_position=commit_position,
@@ -973,7 +990,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
973
990
  """
974
991
  timeout = timeout if timeout is not None else self._default_deadline
975
992
 
976
- await self._connection.persistent_subscriptions.create(
993
+ await self.connection.persistent_subscriptions.create(
977
994
  group_name=group_name,
978
995
  stream_name=stream_name,
979
996
  from_end=from_end,
@@ -1011,7 +1028,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1011
1028
  """
1012
1029
  Reads a persistent subscription on all streams.
1013
1030
  """
1014
- return await self._connection.persistent_subscriptions.read(
1031
+ return await self.connection.persistent_subscriptions.read(
1015
1032
  group_name=group_name,
1016
1033
  event_buffer_size=event_buffer_size,
1017
1034
  max_ack_batch_size=max_ack_batch_size,
@@ -1039,7 +1056,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1039
1056
  """
1040
1057
  Reads a persistent subscription on one stream.
1041
1058
  """
1042
- return await self._connection.persistent_subscriptions.read(
1059
+ return await self.connection.persistent_subscriptions.read(
1043
1060
  group_name=group_name,
1044
1061
  stream_name=stream_name,
1045
1062
  event_buffer_size=event_buffer_size,
@@ -1064,7 +1081,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1064
1081
  """
1065
1082
  Gets info for a persistent subscription.
1066
1083
  """
1067
- return await self._connection.persistent_subscriptions.get_info(
1084
+ return await self.connection.persistent_subscriptions.get_info(
1068
1085
  group_name=group_name,
1069
1086
  stream_name=stream_name,
1070
1087
  timeout=timeout,
@@ -1083,7 +1100,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1083
1100
  """
1084
1101
  Lists all persistent subscriptions.
1085
1102
  """
1086
- return await self._connection.persistent_subscriptions.list(
1103
+ return await self.connection.persistent_subscriptions.list(
1087
1104
  timeout=timeout,
1088
1105
  metadata=self._call_metadata,
1089
1106
  credentials=credentials or self._call_credentials,
@@ -1101,7 +1118,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1101
1118
  """
1102
1119
  Lists persistent stream subscriptions.
1103
1120
  """
1104
- return await self._connection.persistent_subscriptions.list(
1121
+ return await self.connection.persistent_subscriptions.list(
1105
1122
  stream_name=stream_name,
1106
1123
  timeout=timeout,
1107
1124
  metadata=self._call_metadata,
@@ -1262,7 +1279,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1262
1279
  extra_statistics=extra_statistics,
1263
1280
  )
1264
1281
 
1265
- await self._connection.persistent_subscriptions.update(
1282
+ await self.connection.persistent_subscriptions.update(
1266
1283
  group_name=group_name,
1267
1284
  stream_name=stream_name,
1268
1285
  **kwargs,
@@ -1417,7 +1434,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1417
1434
  extra_statistics=extra_statistics,
1418
1435
  )
1419
1436
 
1420
- await self._connection.persistent_subscriptions.update(
1437
+ await self.connection.persistent_subscriptions.update(
1421
1438
  group_name=group_name,
1422
1439
  **kwargs,
1423
1440
  timeout=timeout if timeout is not None else self._default_deadline,
@@ -1437,7 +1454,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1437
1454
  ) -> None:
1438
1455
  timeout = timeout if timeout is not None else self._default_deadline
1439
1456
 
1440
- await self._connection.persistent_subscriptions.replay_parked(
1457
+ await self.connection.persistent_subscriptions.replay_parked(
1441
1458
  group_name=group_name,
1442
1459
  stream_name=stream_name,
1443
1460
  timeout=timeout,
@@ -1460,7 +1477,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1460
1477
  """
1461
1478
  timeout = timeout if timeout is not None else self._default_deadline
1462
1479
 
1463
- await self._connection.persistent_subscriptions.delete(
1480
+ await self.connection.persistent_subscriptions.delete(
1464
1481
  group_name=group_name,
1465
1482
  stream_name=stream_name,
1466
1483
  timeout=timeout,
@@ -1485,7 +1502,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1485
1502
  """
1486
1503
  timeout = timeout if timeout is not None else self._default_deadline
1487
1504
 
1488
- await self._connection.projections.create(
1505
+ await self.connection.projections.create(
1489
1506
  query=query,
1490
1507
  name=name,
1491
1508
  emit_enabled=emit_enabled,
@@ -1511,7 +1528,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1511
1528
  """
1512
1529
  timeout = timeout if timeout is not None else self._default_deadline
1513
1530
 
1514
- await self._connection.projections.update(
1531
+ await self.connection.projections.update(
1515
1532
  name=name,
1516
1533
  query=query,
1517
1534
  emit_enabled=emit_enabled,
@@ -1537,7 +1554,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1537
1554
  """
1538
1555
  timeout = timeout if timeout is not None else self._default_deadline
1539
1556
 
1540
- await self._connection.projections.delete(
1557
+ await self.connection.projections.delete(
1541
1558
  name=name,
1542
1559
  delete_emitted_streams=delete_emitted_streams,
1543
1560
  delete_state_stream=delete_state_stream,
@@ -1561,7 +1578,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1561
1578
  """
1562
1579
  timeout = timeout if timeout is not None else self._default_deadline
1563
1580
 
1564
- return await self._connection.projections.get_statistics(
1581
+ return await self.connection.projections.get_statistics(
1565
1582
  name=name,
1566
1583
  timeout=timeout,
1567
1584
  metadata=self._call_metadata,
@@ -1581,7 +1598,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1581
1598
  """
1582
1599
  timeout = timeout if timeout is not None else self._default_deadline
1583
1600
 
1584
- return await self._connection.projections.list_statistics(
1601
+ return await self.connection.projections.list_statistics(
1585
1602
  timeout=timeout,
1586
1603
  metadata=self._call_metadata,
1587
1604
  credentials=credentials or self._call_credentials,
@@ -1600,7 +1617,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1600
1617
  """
1601
1618
  timeout = timeout if timeout is not None else self._default_deadline
1602
1619
 
1603
- return await self._connection.projections.list_statistics(
1620
+ return await self.connection.projections.list_statistics(
1604
1621
  all=True,
1605
1622
  timeout=timeout,
1606
1623
  metadata=self._call_metadata,
@@ -1621,7 +1638,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1621
1638
  """
1622
1639
  timeout = timeout if timeout is not None else self._default_deadline
1623
1640
 
1624
- await self._connection.projections.disable(
1641
+ await self.connection.projections.disable(
1625
1642
  name=name,
1626
1643
  write_checkpoint=True,
1627
1644
  timeout=timeout,
@@ -1643,7 +1660,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1643
1660
  """
1644
1661
  timeout = timeout if timeout is not None else self._default_deadline
1645
1662
 
1646
- await self._connection.projections.disable(
1663
+ await self.connection.projections.disable(
1647
1664
  name=name,
1648
1665
  write_checkpoint=False,
1649
1666
  timeout=timeout,
@@ -1665,7 +1682,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1665
1682
  """
1666
1683
  timeout = timeout if timeout is not None else self._default_deadline
1667
1684
 
1668
- await self._connection.projections.enable(
1685
+ await self.connection.projections.enable(
1669
1686
  name=name,
1670
1687
  timeout=timeout,
1671
1688
  metadata=self._call_metadata,
@@ -1686,7 +1703,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1686
1703
  """
1687
1704
  timeout = timeout if timeout is not None else self._default_deadline
1688
1705
 
1689
- await self._connection.projections.reset(
1706
+ await self.connection.projections.reset(
1690
1707
  name=name,
1691
1708
  write_checkpoint=True,
1692
1709
  timeout=timeout,
@@ -1709,7 +1726,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1709
1726
  """
1710
1727
  timeout = timeout if timeout is not None else self._default_deadline
1711
1728
 
1712
- return await self._connection.projections.get_state(
1729
+ return await self.connection.projections.get_state(
1713
1730
  name=name,
1714
1731
  partition=partition,
1715
1732
  timeout=timeout,
@@ -1752,7 +1769,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1752
1769
  """
1753
1770
  timeout = timeout if timeout is not None else self._default_deadline
1754
1771
 
1755
- return await self._connection.projections.restart_subsystem(
1772
+ return await self.connection.projections.restart_subsystem(
1756
1773
  timeout=timeout,
1757
1774
  metadata=self._call_metadata,
1758
1775
  credentials=credentials or self._call_credentials,
@@ -1761,7 +1778,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1761
1778
  async def close(self) -> None:
1762
1779
  if not self._is_closed:
1763
1780
  try:
1764
- esdb_connection = self._connection
1781
+ esdb_connection = self.connection
1765
1782
  del self._connection
1766
1783
  except AttributeError: # pragma: no cover
1767
1784
  pass
@@ -1770,6 +1787,7 @@ class AsyncKurrentDBClient(BaseKurrentDBClient):
1770
1787
  self._is_closed = True
1771
1788
 
1772
1789
  async def __aenter__(self) -> Self:
1790
+ await self.connect()
1773
1791
  return self
1774
1792
 
1775
1793
  async def __aexit__(self, *args: object, **kwargs: Any) -> None:
@@ -733,7 +733,8 @@ class KurrentDBClient(BaseKurrentDBClient):
733
733
  credentials: grpc.CallCredentials | None = None,
734
734
  ) -> int | Literal[StreamState.NO_STREAM]:
735
735
  """
736
- Returns the current position of the end of a stream.
736
+ Returns the current position of the end of a stream,
737
+ or StreamState.NO_STREAM if the stream is not found.
737
738
  """
738
739
  try:
739
740
  last_event = next(
@@ -37,6 +37,7 @@ from kurrentdbclient.exceptions import (
37
37
  GrpcDeadlineExceededError,
38
38
  GrpcError,
39
39
  InternalError,
40
+ InvalidCommitPositionError,
40
41
  KurrentDBClientError,
41
42
  MaximumSubscriptionsReachedError,
42
43
  MultiAppendToSameStreamError,
@@ -319,6 +320,11 @@ def handle_rpc_error(e: grpc.RpcError) -> KurrentDBClientError: # noqa: PLR0911
319
320
  ):
320
321
  # Projections.Delete does this....
321
322
  return OperationFailedError(details_str)
323
+ # no cover <25.1: start
324
+ if "Unexpected FilteredReadAllResult: InvalidPosition" in details_str:
325
+ # Commit position does not exist.
326
+ return InvalidCommitPositionError(details_str)
327
+ # no cover <25.1: stop
322
328
  return UnknownError(details_str) # pragma: no cover
323
329
 
324
330
  if e.code() == grpc.StatusCode.ABORTED:
@@ -322,3 +322,9 @@ class InternalError(GrpcError):
322
322
  """
323
323
  Raised when a grpc INTERNAL error is encountered.
324
324
  """
325
+
326
+
327
+ class InvalidCommitPositionError(KurrentDBClientError):
328
+ """
329
+ Raised when reading from an commit position that does not exist.
330
+ """
@@ -18,6 +18,7 @@ import wrapt
18
18
  from opentelemetry.trace import Span, SpanKind, Status, StatusCode, Tracer
19
19
  from typing_extensions import ParamSpec
20
20
 
21
+ from kurrentdbclient import AsyncKurrentDBClient
21
22
  from kurrentdbclient.instrumentation.opentelemetry.attributes import Attributes
22
23
 
23
24
  if TYPE_CHECKING:
@@ -76,6 +77,8 @@ def apply_spanner(
76
77
  args: Any,
77
78
  kwargs: Any,
78
79
  ) -> R:
80
+ if isinstance(instance, AsyncKurrentDBClient):
81
+ await instance.connect()
79
82
  # Use the spanner function to execute the spanned function.
80
83
  with async_spanner(tracer, instance, original, *args, **kwargs) as result:
81
84
  return await result
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [project]
6
6
  name = "kurrentdbclient"
7
- version = "1.2"
7
+ version = "1.2.1"
8
8
  description = "Python gRPC Client for KurrentDB"
9
9
  license = { text = "BSD-3-Clause" }
10
10
  readme = "README.md"
@@ -14,8 +14,8 @@ authors = [
14
14
  ]
15
15
  classifiers = [
16
16
  # "Development Status :: 3 - Alpha",
17
- "Development Status :: 4 - Beta",
18
- # "Development Status :: 5 - Production/Stable",
17
+ # "Development Status :: 4 - Beta",
18
+ "Development Status :: 5 - Production/Stable",
19
19
  "License :: OSI Approved :: BSD License",
20
20
  "Programming Language :: Python :: 3",
21
21
  "Programming Language :: Python :: 3.10",
File without changes
File without changes