kurrentdbclient 1.2.2__tar.gz → 1.2.3__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.2 → kurrentdbclient-1.2.3}/PKG-INFO +131 -114
  2. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/README.md +128 -111
  3. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/__init__.py +1 -1
  4. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/common.py +8 -5
  5. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/exceptions.py +3 -0
  6. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/instrumentation/opentelemetry/grpc.py +3 -3
  7. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/instrumentation/opentelemetry/spanners.py +2 -2
  8. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/persistent.py +94 -52
  9. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/streams.py +85 -44
  10. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/pyproject.toml +3 -3
  11. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/LICENSE +0 -0
  12. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/.DS_Store +0 -0
  13. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/asyncio_client.py +0 -0
  14. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/client.py +0 -0
  15. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/connection.py +0 -0
  16. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/connection_spec.py +0 -0
  17. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/events.py +0 -0
  18. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/gossip.py +0 -0
  19. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/instrumentation/__init__.py +0 -0
  20. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/instrumentation/opentelemetry/__init__.py +0 -0
  21. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/instrumentation/opentelemetry/attributes.py +0 -0
  22. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/instrumentation/opentelemetry/package.py +0 -0
  23. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/instrumentation/opentelemetry/utils.py +0 -0
  24. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/instrumentation/opentelemetry/version.py +0 -0
  25. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/projections.py +0 -0
  26. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/google/rpc/code_pb2.py +0 -0
  27. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/google/rpc/code_pb2.pyi +0 -0
  28. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/google/rpc/code_pb2_grpc.py +0 -0
  29. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/kurrent/rpc/errors_pb2.py +0 -0
  30. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/kurrent/rpc/errors_pb2.pyi +0 -0
  31. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/kurrent/rpc/errors_pb2_grpc.py +0 -0
  32. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/kurrent/rpc/rpc_pb2.py +0 -0
  33. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/kurrent/rpc/rpc_pb2.pyi +0 -0
  34. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/kurrent/rpc/rpc_pb2_grpc.py +0 -0
  35. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/cluster_pb2.py +0 -0
  36. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/cluster_pb2.pyi +0 -0
  37. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/cluster_pb2_grpc.py +0 -0
  38. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/gossip_pb2.py +0 -0
  39. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/gossip_pb2.pyi +0 -0
  40. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/gossip_pb2_grpc.py +0 -0
  41. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/persistent_pb2.py +0 -0
  42. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/persistent_pb2.pyi +0 -0
  43. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/persistent_pb2_grpc.py +0 -0
  44. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/projections_pb2.py +0 -0
  45. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/projections_pb2.pyi +0 -0
  46. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/projections_pb2_grpc.py +0 -0
  47. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/shared_pb2.py +0 -0
  48. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/shared_pb2.pyi +0 -0
  49. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/shared_pb2_grpc.py +0 -0
  50. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/status_pb2.py +0 -0
  51. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/status_pb2.pyi +0 -0
  52. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/status_pb2_grpc.py +0 -0
  53. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/streams_pb2.py +0 -0
  54. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/streams_pb2.pyi +0 -0
  55. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v1/streams_pb2_grpc.py +0 -0
  56. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v2/streams/errors_pb2.py +0 -0
  57. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v2/streams/errors_pb2.pyi +0 -0
  58. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v2/streams/errors_pb2_grpc.py +0 -0
  59. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v2/streams/streams_pb2.py +0 -0
  60. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v2/streams/streams_pb2.pyi +0 -0
  61. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/protos/v2/streams/streams_pb2_grpc.py +0 -0
  62. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/py.typed +0 -0
  63. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/unpack_error_status.py +0 -0
  64. {kurrentdbclient-1.2.2 → kurrentdbclient-1.2.3}/kurrentdbclient/v2streams.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kurrentdbclient
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: Python gRPC Client for KurrentDB
5
5
  License: BSD-3-Clause
6
6
  License-File: LICENSE
@@ -21,8 +21,8 @@ Requires-Dist: googleapis-common-protos
21
21
  Requires-Dist: grpcio-status
22
22
  Requires-Dist: grpcio[protobuf] (>=1.75.1,<2.0)
23
23
  Requires-Dist: opentelemetry-api (>=1.28.0,<2.0) ; extra == "opentelemetry"
24
- Requires-Dist: opentelemetry-instrumentation (>=0.49b0) ; extra == "opentelemetry"
25
- Requires-Dist: opentelemetry-semantic-conventions (>=0.49b0) ; extra == "opentelemetry"
24
+ Requires-Dist: opentelemetry-instrumentation (>=0.62b1) ; extra == "opentelemetry"
25
+ Requires-Dist: opentelemetry-semantic-conventions (>=0.62b1) ; extra == "opentelemetry"
26
26
  Requires-Dist: typing_extensions
27
27
  Project-URL: Homepage, https://github.com/pyeventsourcing/kurrentdbclient
28
28
  Project-URL: Repository, https://github.com/pyeventsourcing/kurrentdbclient
@@ -52,9 +52,10 @@ with the KurrentDB team, and are officially supported by Kurrent Inc.
52
52
  Although not all aspects of the KurrentDB gRPC API are implemented, most
53
53
  features are presented in an easy-to-use interface.
54
54
 
55
- These clients have been tested to work with KurrentDB version 25.1 and 25.0, EventStoreDB
56
- versions 24.10 and EventStoreDB 23.10, both with and without SSL/TLS, in both single-server and
57
- cluster modes, across Python versions 3.10, 3.11, 3.12, 3.13, and 3.14.
55
+ These clients have been tested to work with KurrentDB versions 25.0, 25.1, 26.0,
56
+ and 26.1, and EventStoreDB versions 23.10 and 24.10, with and without SSL/TLS,
57
+ in both single-server and cluster modes, across Python versions 3.10, 3.11, 3.12,
58
+ 3.13, and 3.14.
58
59
 
59
60
  The test suite has 100% line and branch coverage. The code has typing annotations
60
61
  checked strictly with mypy. The code is formatted with black and isort, and checked
@@ -1715,10 +1716,10 @@ so far, in the order they were recorded. We can see the three events of `stream_
1715
1716
 
1716
1717
  ```python
1717
1718
  # Read all events (creates a streaming gRPC call).
1718
- read_response = client.read_all()
1719
+ with client.read_all() as read_response:
1720
+ # Convert the iterator into a sequence of recorded events.
1721
+ events = tuple(read_response)
1719
1722
 
1720
- # Convert the iterator into a sequence of recorded events.
1721
- events = tuple(read_response)
1722
1723
  assert len(events) > 3 # more than three
1723
1724
 
1724
1725
  # Convert the sequence of recorded events into a set of event IDs.
@@ -1737,17 +1738,14 @@ receive, `event2` is the second, and `event3` is the third.
1737
1738
 
1738
1739
  ```python
1739
1740
  # Read all events forwards from a commit position.
1740
- read_response = client.read_all(
1741
+ with client.read_all(
1741
1742
  commit_position=commit_position1
1742
- )
1743
-
1744
- # Step through the "read response" iterator.
1745
- assert next(read_response) == event1
1746
- assert next(read_response) == event2
1747
- assert next(read_response) == event3
1743
+ ) as read_response:
1748
1744
 
1749
- # Stop the iterator.
1750
- read_response.stop()
1745
+ # Step through the "read response" iterator.
1746
+ assert next(read_response) == event1
1747
+ assert next(read_response) == event2
1748
+ assert next(read_response) == event3
1751
1749
  ```
1752
1750
 
1753
1751
  The example below shows how to read all events recorded in the database in reverse
@@ -1757,31 +1755,27 @@ and the snapshot.
1757
1755
 
1758
1756
  ```python
1759
1757
  # Read all events backwards from the end.
1760
- read_response = client.read_all(
1758
+ with client.read_all(
1761
1759
  backwards=True
1762
- )
1760
+ ) as read_response:
1763
1761
 
1764
- # Step through the "read response" iterator.
1765
- assert next(read_response).type == "DogLearnedTrick"
1766
- assert next(read_response).type == "Snapshot"
1767
- assert next(read_response).type == "DogLearnedTrick"
1768
- assert next(read_response).type == "DogLearnedTrick"
1769
- assert next(read_response).type == "DogRegistered"
1770
-
1771
- # Stop the iterator.
1772
- read_response.stop()
1762
+ # Step through the "read response" iterator.
1763
+ assert next(read_response).type == "DogLearnedTrick"
1764
+ assert next(read_response).type == "Snapshot"
1765
+ assert next(read_response).type == "DogLearnedTrick"
1766
+ assert next(read_response).type == "DogLearnedTrick"
1767
+ assert next(read_response).type == "DogRegistered"
1773
1768
  ```
1774
1769
 
1775
1770
  The example below shows how to read a limited number of events
1776
1771
  forwards from a specific commit position.
1777
1772
 
1778
1773
  ```python
1779
- events = tuple(
1780
- client.read_all(
1781
- commit_position=commit_position1,
1782
- limit=1,
1783
- )
1784
- )
1774
+ with client.read_all(
1775
+ commit_position=commit_position1,
1776
+ limit=1,
1777
+ ) as read_response:
1778
+ events = tuple(read_response)
1785
1779
 
1786
1780
  assert len(events) == 1
1787
1781
  assert events[0] == event1
@@ -1792,12 +1786,11 @@ in the database backwards from the end. In this case, the limit is 1, and
1792
1786
  so we receive the last recorded event.
1793
1787
 
1794
1788
  ```python
1795
- events = tuple(
1796
- client.read_all(
1797
- backwards=True,
1798
- limit=1,
1799
- )
1800
- )
1789
+ with client.read_all(
1790
+ backwards=True,
1791
+ limit=1,
1792
+ ) as read_response:
1793
+ events = tuple(read_response)
1801
1794
 
1802
1795
  assert len(events) == 1
1803
1796
 
@@ -2096,16 +2089,17 @@ from the first recorded event in the database.
2096
2089
 
2097
2090
  ```python
2098
2091
  # Subscribe from the first recorded event in the database.
2099
- catchup_subscription = client.subscribe_to_all()
2092
+ with client.subscribe_to_all() as catchup_subscription:
2093
+ ...
2100
2094
  ```
2101
2095
 
2102
- The example below shows that catch-up subscriptions do not stop
2103
- automatically, but block when the last recorded event is received,
2104
- and then continue when subsequent events are recorded.
2096
+ The example below shows that catch-up subscriptions block when the
2097
+ last recorded event is received, and then continue when subsequent
2098
+ events are recorded.
2105
2099
 
2106
2100
  ```python
2107
- from time import sleep
2108
- from threading import Thread
2101
+ import time
2102
+ import threading
2109
2103
 
2110
2104
 
2111
2105
  # Append a new event to a new stream.
@@ -2119,30 +2113,53 @@ client.append_to_stream(
2119
2113
  )
2120
2114
 
2121
2115
 
2122
- # Receive events from the catch-up subscription in a different thread.
2123
- received_events = []
2124
-
2125
- def receive_events():
2126
- for event in catchup_subscription:
2127
- received_events.append(event)
2116
+ # Receive events from a catch-up subscription in a different thread.
2117
+ class SubscribeToAll(threading.Thread):
2118
+ def __init__(self, client, **subscription_kwargs):
2119
+ super().__init__()
2120
+ self._client = client
2121
+ self._is_running = threading.Event()
2122
+ self._subscription_kwargs = subscription_kwargs
2123
+ self._subscription = None
2124
+ self._seen_event_ids = set()
2125
+ self._last_commit_position = None
2126
+ self.start()
2128
2127
 
2128
+ def run(self):
2129
+ with self._client.subscribe_to_all(
2130
+ **self._subscription_kwargs
2131
+ ) as subscription:
2132
+ self._subscription = subscription
2133
+ self._is_running.set()
2134
+ for event in subscription:
2135
+ self._seen_event_ids.add(event.id)
2136
+ self._last_commit_position = event.commit_position
2129
2137
 
2130
- def wait_for_event(event):
2131
- for _ in range(100):
2132
- for received in reversed(received_events):
2133
- if event == received:
2138
+ def stop(self):
2139
+ if not self._is_running.wait(timeout=5):
2140
+ raise TimeoutError("Subscription thread didn't start")
2141
+ self._subscription.stop()
2142
+ self.join()
2143
+ self._subscription = None
2144
+
2145
+ def wait_for_event(self, event):
2146
+ for _ in range(100):
2147
+ if event.id in self._seen_event_ids:
2134
2148
  return
2149
+ else:
2150
+ time.sleep(0.1)
2135
2151
  else:
2136
- sleep(0.1)
2137
- else:
2138
- raise AssertionError("Event wasn't received")
2152
+ raise AssertionError("Event wasn't received")
2153
+
2154
+ @property
2155
+ def last_commit_position(self):
2156
+ return self._last_commit_position
2139
2157
 
2140
2158
 
2141
- thread = Thread(target=receive_events, daemon=True)
2142
- thread.start()
2159
+ subscription_to_all = SubscribeToAll(client=client)
2143
2160
 
2144
2161
  # Wait to receive event4.
2145
- wait_for_event(event4)
2162
+ subscription_to_all.wait_for_event(event4)
2146
2163
 
2147
2164
  # Append another event whilst the subscription is running.
2148
2165
  event5 = NewEvent(type='OrderUpdated', data=b'{}')
@@ -2152,17 +2169,16 @@ client.append_to_stream(
2152
2169
  events=[event5],
2153
2170
  )
2154
2171
 
2155
- # Wait for the subscription to block.
2156
- wait_for_event(event5)
2172
+ # Wait to receive event5.
2173
+ subscription_to_all.wait_for_event(event5)
2157
2174
 
2158
- # Stop the subscription.
2159
- catchup_subscription.stop()
2160
- thread.join()
2175
+ # Stop the subscription thread.
2176
+ subscription_to_all.stop()
2161
2177
  ```
2162
2178
 
2163
2179
  The example below shows how to subscribe to events recorded after a
2164
2180
  particular commit position, in this case from the commit position of
2165
- the last recorded event that was received above. Then, another event is
2181
+ the last recorded event that was received above. Another event is
2166
2182
  recorded before the subscription is restarted. And three more events are
2167
2183
  recorded whilst the subscription is running. These four events are
2168
2184
  received in the order they were recorded.
@@ -2180,15 +2196,13 @@ client.append_to_stream(
2180
2196
 
2181
2197
  # Restart subscribing to all events after the
2182
2198
  # commit position of the last received event.
2183
- catchup_subscription = client.subscribe_to_all(
2184
- commit_position=received_events[-1].commit_position
2199
+ subscription_to_all = SubscribeToAll(
2200
+ client=client,
2201
+ commit_position=subscription_to_all.last_commit_position
2185
2202
  )
2186
2203
 
2187
- thread = Thread(target=receive_events, daemon=True)
2188
- thread.start()
2189
-
2190
2204
  # Wait for event6.
2191
- wait_for_event(event6)
2205
+ subscription_to_all.wait_for_event(event6)
2192
2206
 
2193
2207
  # Append three more events to a new stream.
2194
2208
  stream_name3 = str(uuid.uuid4())
@@ -2203,13 +2217,12 @@ client.append_to_stream(
2203
2217
  )
2204
2218
 
2205
2219
  # Wait for events 7, 8 and 9.
2206
- wait_for_event(event7)
2207
- wait_for_event(event8)
2208
- wait_for_event(event9)
2220
+ subscription_to_all.wait_for_event(event7)
2221
+ subscription_to_all.wait_for_event(event8)
2222
+ subscription_to_all.wait_for_event(event9)
2209
2223
 
2210
- # Stop the subscription.
2211
- catchup_subscription.stop()
2212
- thread.join()
2224
+ # Stop the subscription thread.
2225
+ subscription_to_all.stop()
2213
2226
  ```
2214
2227
 
2215
2228
  The catch-up subscription call is ended as soon as the subscription object's
@@ -2263,7 +2276,10 @@ the first recorded event in a stream.
2263
2276
 
2264
2277
  ```python
2265
2278
  # Subscribe from the start of 'stream2'.
2266
- subscription = client.subscribe_to_stream(stream_name=stream_name2)
2279
+ with client.subscribe_to_stream(
2280
+ stream_name=stream_name2
2281
+ ) as subscription:
2282
+ ...
2267
2283
  ```
2268
2284
 
2269
2285
  The example below shows how to start a catch-up subscription from
@@ -2271,10 +2287,11 @@ a particular stream position.
2271
2287
 
2272
2288
  ```python
2273
2289
  # Subscribe to stream2, from the second recorded event.
2274
- subscription = client.subscribe_to_stream(
2290
+ with client.subscribe_to_stream(
2275
2291
  stream_name=stream_name2,
2276
2292
  stream_position=1,
2277
- )
2293
+ ) as subscription:
2294
+ ...
2278
2295
  ```
2279
2296
 
2280
2297
  ### Subscribe using secondary index<a id="subscribe-to-index"></a>
@@ -2528,7 +2545,10 @@ giving `RecordedEvent` objects. It also has `ack()`, `nack()` and `stop()`
2528
2545
  methods.
2529
2546
 
2530
2547
  ```python
2531
- subscription = client.read_subscription_to_all(group_name=group_name1)
2548
+ with client.read_subscription_to_all(
2549
+ group_name=group_name1
2550
+ ) as subscription:
2551
+ ...
2532
2552
  ```
2533
2553
 
2534
2554
  The `ack()` method should be used by a consumer to "acknowledge" to the server that
@@ -2550,15 +2570,18 @@ examples below.
2550
2570
  ```python
2551
2571
  received_events = []
2552
2572
 
2553
- for event in subscription:
2554
- received_events.append(event)
2573
+ with client.read_subscription_to_all(
2574
+ group_name=group_name1
2575
+ ) as subscription:
2576
+ for event in subscription:
2577
+ received_events.append(event)
2555
2578
 
2556
- # Acknowledge the received event.
2557
- subscription.ack(event)
2579
+ # Acknowledge the received event.
2580
+ subscription.ack(event)
2558
2581
 
2559
- # Stop when 'event9' has been received.
2560
- if event == event9:
2561
- subscription.stop()
2582
+ # Stop when 'event9' has been received.
2583
+ if event == event9:
2584
+ subscription.stop()
2562
2585
  ```
2563
2586
 
2564
2587
  The `nack()` should be used by a consumer to "negatively acknowledge" to the server that
@@ -2610,9 +2633,6 @@ class ExampleConsumer:
2610
2633
  self.subscription.ack(event)
2611
2634
  self.after_ack(event)
2612
2635
 
2613
- def stop(self):
2614
- self.subscription.stop()
2615
-
2616
2636
  def policy(self, event):
2617
2637
  # Raise an exception when we see "event5".
2618
2638
  if event == event5:
@@ -2630,17 +2650,17 @@ class ExampleConsumer:
2630
2650
  # Stop the consumer, so we can continue with the examples.
2631
2651
  self.stop()
2632
2652
 
2653
+ def stop(self):
2654
+ self.subscription.stop()
2655
+
2633
2656
 
2634
2657
  # Create subscription.
2635
2658
  group_name = f"group-{uuid.uuid4()}"
2636
2659
  client.create_subscription_to_all(group_name, commit_position=commit_position1)
2637
2660
 
2638
- # Read subscription.
2639
- subscription = client.read_subscription_to_all(group_name)
2640
-
2641
2661
  # Construct consumer.
2642
2662
  consumer = ExampleConsumer(
2643
- subscription=subscription,
2663
+ subscription=client.read_subscription_to_all(group_name),
2644
2664
  max_retry_count=5,
2645
2665
  final_action="park",
2646
2666
  )
@@ -2819,28 +2839,25 @@ This method returns a `PersistentSubscription` object, which is an iterator
2819
2839
  giving `RecordedEvent` objects, that also has `ack()`, `nack()` and `stop()`
2820
2840
  methods.
2821
2841
 
2822
- ```python
2823
- subscription = client.read_subscription_to_stream(
2824
- group_name=group_name2,
2825
- stream_name=stream_name2,
2826
- )
2827
- ```
2828
-
2829
2842
  The example below iterates over the subscription object, and calls `ack()`.
2830
2843
  The subscription's `stop()` method is called when we have received `event6`,
2831
2844
  stopping the iteration, so that we can continue with the examples below.
2832
2845
 
2833
2846
  ```python
2834
2847
  events = []
2835
- for event in subscription:
2836
- events.append(event)
2848
+ with client.read_subscription_to_stream(
2849
+ group_name=group_name2,
2850
+ stream_name=stream_name2,
2851
+ ) as subscription:
2852
+ for event in subscription:
2853
+ events.append(event)
2837
2854
 
2838
- # Acknowledge the received event.
2839
- subscription.ack(event)
2855
+ # Acknowledge the received event.
2856
+ subscription.ack(event)
2840
2857
 
2841
- # Stop when 'event6' has been received.
2842
- if event == event6:
2843
- subscription.stop()
2858
+ # Stop when 'event6' has been received.
2859
+ if event == event6:
2860
+ break
2844
2861
  ```
2845
2862
 
2846
2863
  We can check we received all the events that were appended to `stream_name2`
@@ -3192,7 +3209,7 @@ to process all the recorded events, the projection "state" is obtained.
3192
3209
  We can see that the projection has processed three events.
3193
3210
 
3194
3211
  ```python
3195
- sleep(1) # allow time for projection to process recorded events
3212
+ time.sleep(1) # allow time for projection to process recorded events
3196
3213
 
3197
3214
  projection_state = client.get_projection_state(projection_name)
3198
3215