datacurrent 1.0.0__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 (50) hide show
  1. datacurrent-1.0.0/CHANGELOG.md +59 -0
  2. datacurrent-1.0.0/LICENSE +20 -0
  3. datacurrent-1.0.0/MANIFEST.in +15 -0
  4. datacurrent-1.0.0/PKG-INFO +250 -0
  5. datacurrent-1.0.0/README.md +204 -0
  6. datacurrent-1.0.0/datacurrent/__init__.py +157 -0
  7. datacurrent-1.0.0/datacurrent/_version.py +6 -0
  8. datacurrent-1.0.0/datacurrent/buffering/__init__.py +8 -0
  9. datacurrent-1.0.0/datacurrent/buffering/backpressure.py +47 -0
  10. datacurrent-1.0.0/datacurrent/buffering/queue.py +304 -0
  11. datacurrent-1.0.0/datacurrent/concurrency/__init__.py +8 -0
  12. datacurrent-1.0.0/datacurrent/concurrency/limits.py +106 -0
  13. datacurrent-1.0.0/datacurrent/concurrency/tasks.py +86 -0
  14. datacurrent-1.0.0/datacurrent/config.py +86 -0
  15. datacurrent-1.0.0/datacurrent/core/__init__.py +17 -0
  16. datacurrent-1.0.0/datacurrent/core/_util.py +42 -0
  17. datacurrent-1.0.0/datacurrent/core/lifecycle.py +81 -0
  18. datacurrent-1.0.0/datacurrent/core/operators.py +844 -0
  19. datacurrent-1.0.0/datacurrent/core/pipeline.py +64 -0
  20. datacurrent-1.0.0/datacurrent/core/sink.py +70 -0
  21. datacurrent-1.0.0/datacurrent/core/stream.py +566 -0
  22. datacurrent-1.0.0/datacurrent/events/__init__.py +9 -0
  23. datacurrent-1.0.0/datacurrent/events/bus.py +380 -0
  24. datacurrent-1.0.0/datacurrent/events/routing.py +59 -0
  25. datacurrent-1.0.0/datacurrent/events/subscriptions.py +75 -0
  26. datacurrent-1.0.0/datacurrent/exceptions.py +191 -0
  27. datacurrent-1.0.0/datacurrent/integrations/__init__.py +18 -0
  28. datacurrent-1.0.0/datacurrent/integrations/fastapi.py +76 -0
  29. datacurrent-1.0.0/datacurrent/integrations/starlette.py +75 -0
  30. datacurrent-1.0.0/datacurrent/observability/__init__.py +17 -0
  31. datacurrent-1.0.0/datacurrent/observability/logging.py +128 -0
  32. datacurrent-1.0.0/datacurrent/observability/metrics.py +127 -0
  33. datacurrent-1.0.0/datacurrent/py.typed +1 -0
  34. datacurrent-1.0.0/datacurrent/reliability/__init__.py +14 -0
  35. datacurrent-1.0.0/datacurrent/reliability/retry.py +134 -0
  36. datacurrent-1.0.0/datacurrent/reliability/timeout.py +39 -0
  37. datacurrent-1.0.0/datacurrent/transports/__init__.py +31 -0
  38. datacurrent-1.0.0/datacurrent/transports/_deps.py +23 -0
  39. datacurrent-1.0.0/datacurrent/transports/base.py +80 -0
  40. datacurrent-1.0.0/datacurrent/transports/http.py +211 -0
  41. datacurrent-1.0.0/datacurrent/transports/memory.py +171 -0
  42. datacurrent-1.0.0/datacurrent/transports/sse.py +374 -0
  43. datacurrent-1.0.0/datacurrent/transports/websocket.py +277 -0
  44. datacurrent-1.0.0/datacurrent.egg-info/PKG-INFO +250 -0
  45. datacurrent-1.0.0/datacurrent.egg-info/SOURCES.txt +48 -0
  46. datacurrent-1.0.0/datacurrent.egg-info/dependency_links.txt +1 -0
  47. datacurrent-1.0.0/datacurrent.egg-info/requires.txt +23 -0
  48. datacurrent-1.0.0/datacurrent.egg-info/top_level.txt +1 -0
  49. datacurrent-1.0.0/pyproject.toml +123 -0
  50. datacurrent-1.0.0/setup.cfg +4 -0
@@ -0,0 +1,59 @@
1
+ # Changelog
2
+
3
+ All notable changes to DataCurrent are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [1.0.0] — 2026-11-08
8
+
9
+ First stable release. Built and verified locally; not published to PyPI.
10
+
11
+ ### Added
12
+
13
+ - `Stream` engine: construction from iterables, async iterables, factories,
14
+ queues, values and other streams; single-consumer semantics with explicit
15
+ `StreamConsumedError`; replayable sources for retries.
16
+ - Operators: `map` (bounded, ordered concurrency), `filter`, `flat_map`,
17
+ `take`, `skip`, `batch`, `buffer`, `distinct`, `enumerate`, `window`,
18
+ `debounce`, `throttle`, `zip`, `merge`, `concat`, `timeout`, `retry`,
19
+ `recover`, `tap`.
20
+ - Terminal operations: `collect`, `count`, `drain`, `first`, `last`, `reduce`.
21
+ - Bounded `BoundedQueue` with `block`, `drop_newest`, `drop_oldest` and `error`
22
+ overflow policies, high-watermark statistics and producer suspension.
23
+ - `ConcurrencyLimiter`, `TaskTracker`, `gather_limited` and `consume()` with
24
+ bounded concurrency and cancellation-safe cleanup.
25
+ - `RetryPolicy` with exponential backoff, jitter, attempt limits and exception
26
+ predicates; `retry_async` helper; `with_timeout`.
27
+ - In-memory `Channel` broadcast with per-subscriber bounded queues and drop
28
+ accounting.
29
+ - `EventBus` with `Event` envelopes, wildcard topic routing, handler isolation,
30
+ stream subscriptions, draining shutdown and statistics.
31
+ - Transports: `SSEStream` (WHATWG parser, `Last-Event-ID`, reconnection),
32
+ `HTTPStream` (chunked iteration, line iteration, byte limits) and
33
+ `WebSocketStream` (text/binary/JSON, heartbeats, size limits, reconnection),
34
+ backed by `httpx` and `websockets`, which install with the package.
35
+ - `StreamTransport` extension base for custom protocols.
36
+ - FastAPI / Starlette helpers: SSE responses, streaming responses, WebSocket
37
+ message streams and lifespan cleanup.
38
+ - Observability: `Metrics`, `Counter`, per-stream `StreamStats`, structured
39
+ logging with credential redaction.
40
+ - Complete exception hierarchy rooted at `DataCurrentError`.
41
+ - 279 tests: unit, property-based, stress, networking with local servers,
42
+ packaging and executable examples (100% line and branch coverage).
43
+ - 16 runnable examples, full documentation, reproducible benchmarks and a
44
+ cross-platform developer task runner.
45
+
46
+ ### Security
47
+
48
+ - TLS verification is not disabled anywhere by default.
49
+ - Credentials in URLs, headers and secrets are redacted from logs and errors.
50
+ - Message-size and body-size limits are configurable and enforced.
51
+ - Resource cleanup is guaranteed on cancellation and early exit.
52
+
53
+ ### Known limitations
54
+
55
+ - No durable broker adapters (Redis, Kafka, RabbitMQ, NATS) in this release.
56
+ - No OpenTelemetry exporter; metrics are exposed as snapshots only.
57
+ - asyncio only; AnyIO/Trio backends are not supported.
58
+ - The project name has not been trademark-cleared and the package is not on
59
+ PyPI.
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CoderProandTester
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,15 @@
1
+ include README.md
2
+ include LICENSE
3
+ include CHANGELOG.md
4
+ include MANIFEST.in
5
+ recursive-include datacurrent *.py py.typed
6
+ prune tests
7
+ prune examples
8
+ prune benchmarks
9
+ prune docs
10
+ prune scripts
11
+ prune .venv
12
+ prune dist
13
+ prune build
14
+ global-exclude *.py[cod]
15
+ global-exclude __pycache__
@@ -0,0 +1,250 @@
1
+ Metadata-Version: 2.4
2
+ Name: datacurrent
3
+ Version: 1.0.0
4
+ Summary: Python streaming, made simple: async streams, operators, backpressure and real-time transports.
5
+ Author: DataCurrent contributors
6
+ License: MIT
7
+ Keywords: streaming,async,asyncio,real-time,data pipeline,stream processing,backpressure,reactive,event bus,pubsub,websocket,server-sent events,http streaming,concurrency,observability,http,https
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: Framework :: AsyncIO
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Topic :: Internet
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.11
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: httpx>=0.27
27
+ Requires-Dist: websockets>=12
28
+ Provides-Extra: starlette
29
+ Requires-Dist: starlette>=0.36; extra == "starlette"
30
+ Provides-Extra: fastapi
31
+ Requires-Dist: fastapi>=0.110; extra == "fastapi"
32
+ Provides-Extra: all
33
+ Requires-Dist: starlette>=0.36; extra == "all"
34
+ Requires-Dist: fastapi>=0.110; extra == "all"
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest>=8; extra == "dev"
37
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
38
+ Requires-Dist: pytest-cov>=5; extra == "dev"
39
+ Requires-Dist: hypothesis>=6; extra == "dev"
40
+ Requires-Dist: build>=1.2; extra == "dev"
41
+ Requires-Dist: twine>=5; extra == "dev"
42
+ Requires-Dist: ruff>=0.6; extra == "dev"
43
+ Requires-Dist: mypy>=1.11; extra == "dev"
44
+ Requires-Dist: fastapi>=0.110; extra == "dev"
45
+ Dynamic: license-file
46
+
47
+ # DataCurrent
48
+
49
+ ### Python streaming, made simple.
50
+
51
+ DataCurrent is a production-ready asynchronous streaming library. It gives you
52
+ one consistent API for lists, async generators, queues, WebSockets, Server-Sent
53
+ Events and streaming HTTP responses.
54
+
55
+ **Version 1.0.0 · Stable / Production**
56
+
57
+ - 279 tests passing, with 100% line and branch coverage.
58
+ - Required networking dependencies install automatically.
59
+ - No broker, database, API key or hosted service.
60
+
61
+ ## Install
62
+
63
+ ```bash
64
+ pip install datacurrent
65
+ ```
66
+
67
+ Python 3.11 or newer is required. `httpx` and `websockets` are installed with the
68
+ package, so HTTP streaming, SSE and WebSocket clients work immediately.
69
+
70
+ Framework helpers are optional:
71
+
72
+ ```bash
73
+ pip install "datacurrent[fastapi]"
74
+ pip install "datacurrent[starlette]"
75
+ pip install "datacurrent[all]"
76
+ ```
77
+
78
+ ## Quick start
79
+
80
+ ```python
81
+ import asyncio
82
+ from datacurrent import Stream
83
+
84
+
85
+ async def main() -> None:
86
+ result = await (
87
+ Stream.from_iterable(range(10))
88
+ .filter(lambda number: number % 2 == 0)
89
+ .map(lambda number: number * number)
90
+ .take(4)
91
+ .collect()
92
+ )
93
+ print(result) # [0, 4, 16, 36]
94
+
95
+
96
+ asyncio.run(main())
97
+ ```
98
+
99
+ Streams are lazy and single-consumer:
100
+
101
+ ```python
102
+ stream = Stream.from_iterable([1, 2, 3])
103
+
104
+ async for item in stream:
105
+ print(item)
106
+
107
+ # A second iteration raises StreamConsumedError.
108
+ ```
109
+
110
+ Common terminals:
111
+
112
+ ```python
113
+ await Stream.from_iterable([1, 2, 3]).collect() # [1, 2, 3]
114
+ await Stream.from_iterable([1, 2, 3]).count() # 3
115
+ await Stream.from_iterable([3, 1, 2]).reduce(max) # 3
116
+ await Stream.empty().first("fallback") # "fallback"
117
+ ```
118
+
119
+ Async callbacks work everywhere a callback is accepted:
120
+
121
+ ```python
122
+ users = await Stream.from_iterable(user_ids).map(fetch_user, concurrency=8).collect()
123
+ ```
124
+
125
+ ## Operators
126
+
127
+ `map`, `filter`, `flat_map`, `take`, `skip`, `batch`, `buffer`, `distinct`,
128
+ `enumerate`, `window`, `debounce`, `throttle`, `timeout`, `retry`, `recover`,
129
+ `tap`, `zip`, `merge` and `concat`.
130
+
131
+ ```python
132
+ batches = Stream.from_iterable(range(1_000)).filter(lambda number: number % 3 == 0).batch(100)
133
+
134
+ async for batch in batches:
135
+ print(len(batch))
136
+ ```
137
+
138
+ ## Backpressure
139
+
140
+ Buffers are bounded and block the producer by default. Data is not silently
141
+ dropped.
142
+
143
+ ```python
144
+ stream = Stream.from_iterable(range(100_000)).buffer(size=64)
145
+ ```
146
+
147
+ Lossy behaviour must be explicit and is counted:
148
+
149
+ ```python
150
+ stream = Stream.from_iterable(range(10_000)).buffer(
151
+ size=16,
152
+ overflow="drop_oldest",
153
+ )
154
+ items = await stream.collect()
155
+ print(len(items), stream.stats["dropped"])
156
+ ```
157
+
158
+ Policies: `block`, `drop_newest`, `drop_oldest`, `error`.
159
+
160
+ ## Real-time data
161
+
162
+ ### WebSocket
163
+
164
+ ```python
165
+ from datacurrent import WebSocketStream
166
+
167
+ async with WebSocketStream.connect("wss://example.com/events") as stream:
168
+ await stream.send_json({"subscribe": "trades"})
169
+ async for message in stream:
170
+ print(message)
171
+ ```
172
+
173
+ ### Server-Sent Events
174
+
175
+ ```python
176
+ from datacurrent import SSEStream
177
+
178
+ async with SSEStream.connect("https://example.com/events") as stream:
179
+ async for event in stream:
180
+ print(event.event, event.json())
181
+ ```
182
+
183
+ ### Streaming HTTP
184
+
185
+ ```python
186
+ from datacurrent import HTTPStream
187
+
188
+ async with HTTPStream("https://example.com/feed.ndjson") as response:
189
+ async for line in response.iter_lines():
190
+ handle(line)
191
+ ```
192
+
193
+ ### Fan-out and events
194
+
195
+ ```python
196
+ from datacurrent import Channel, EventBus
197
+
198
+ channel = Channel(buffer_size=64)
199
+ first = channel.subscribe()
200
+ second = channel.subscribe()
201
+ await channel.publish("deploy finished")
202
+
203
+ bus = EventBus()
204
+
205
+
206
+ @bus.subscribe("user.*")
207
+ async def on_user(event):
208
+ print(event.topic, event.data)
209
+
210
+
211
+ await bus.publish("user.created", {"user_id": 123})
212
+ ```
213
+
214
+ ## Retries
215
+
216
+ ```python
217
+ from datacurrent import RetryPolicy
218
+
219
+ stream = Stream.from_factory(connect_and_read).retry(
220
+ RetryPolicy(max_attempts=3, base_delay=0.2, jitter=0.1)
221
+ )
222
+ ```
223
+
224
+ Retries require a replayable source, such as a factory or a list. A consumed
225
+ generator raises `NonReplayableStreamError` instead of silently restarting.
226
+
227
+ ## Why DataCurrent
228
+
229
+ - One model for iterables, queues, HTTP, SSE and WebSockets.
230
+ - Backpressure by default, with explicit and counted drop policies.
231
+ - Bounded concurrency and cancellation-safe cleanup.
232
+ - No infrastructure: no broker, database or external account.
233
+ - Explicit delivery semantics; no false durability claims.
234
+ - Typed API and sync or async callbacks throughout.
235
+
236
+ HTTP clients such as `httpx` and `aiohttp` handle requests; DataCurrent adds the
237
+ stream engine around them. Reactive libraries provide operator algebra, while
238
+ broker frameworks require Kafka, Redis or RabbitMQ. DataCurrent is the
239
+ in-process streaming layer that works without any of them.
240
+
241
+ ## Compatibility and limits
242
+
243
+ - Python 3.11+ on asyncio; no Trio or AnyIO backend.
244
+ - In-memory `Channel` and `EventBus` delivery is at most once.
245
+ - No durable broker adapters or OpenTelemetry exporter in 1.0.0.
246
+ - Not published to PyPI; the project name is provisional.
247
+
248
+ ## License
249
+
250
+ MIT.
@@ -0,0 +1,204 @@
1
+ # DataCurrent
2
+
3
+ ### Python streaming, made simple.
4
+
5
+ DataCurrent is a production-ready asynchronous streaming library. It gives you
6
+ one consistent API for lists, async generators, queues, WebSockets, Server-Sent
7
+ Events and streaming HTTP responses.
8
+
9
+ **Version 1.0.0 · Stable / Production**
10
+
11
+ - 279 tests passing, with 100% line and branch coverage.
12
+ - Required networking dependencies install automatically.
13
+ - No broker, database, API key or hosted service.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pip install datacurrent
19
+ ```
20
+
21
+ Python 3.11 or newer is required. `httpx` and `websockets` are installed with the
22
+ package, so HTTP streaming, SSE and WebSocket clients work immediately.
23
+
24
+ Framework helpers are optional:
25
+
26
+ ```bash
27
+ pip install "datacurrent[fastapi]"
28
+ pip install "datacurrent[starlette]"
29
+ pip install "datacurrent[all]"
30
+ ```
31
+
32
+ ## Quick start
33
+
34
+ ```python
35
+ import asyncio
36
+ from datacurrent import Stream
37
+
38
+
39
+ async def main() -> None:
40
+ result = await (
41
+ Stream.from_iterable(range(10))
42
+ .filter(lambda number: number % 2 == 0)
43
+ .map(lambda number: number * number)
44
+ .take(4)
45
+ .collect()
46
+ )
47
+ print(result) # [0, 4, 16, 36]
48
+
49
+
50
+ asyncio.run(main())
51
+ ```
52
+
53
+ Streams are lazy and single-consumer:
54
+
55
+ ```python
56
+ stream = Stream.from_iterable([1, 2, 3])
57
+
58
+ async for item in stream:
59
+ print(item)
60
+
61
+ # A second iteration raises StreamConsumedError.
62
+ ```
63
+
64
+ Common terminals:
65
+
66
+ ```python
67
+ await Stream.from_iterable([1, 2, 3]).collect() # [1, 2, 3]
68
+ await Stream.from_iterable([1, 2, 3]).count() # 3
69
+ await Stream.from_iterable([3, 1, 2]).reduce(max) # 3
70
+ await Stream.empty().first("fallback") # "fallback"
71
+ ```
72
+
73
+ Async callbacks work everywhere a callback is accepted:
74
+
75
+ ```python
76
+ users = await Stream.from_iterable(user_ids).map(fetch_user, concurrency=8).collect()
77
+ ```
78
+
79
+ ## Operators
80
+
81
+ `map`, `filter`, `flat_map`, `take`, `skip`, `batch`, `buffer`, `distinct`,
82
+ `enumerate`, `window`, `debounce`, `throttle`, `timeout`, `retry`, `recover`,
83
+ `tap`, `zip`, `merge` and `concat`.
84
+
85
+ ```python
86
+ batches = Stream.from_iterable(range(1_000)).filter(lambda number: number % 3 == 0).batch(100)
87
+
88
+ async for batch in batches:
89
+ print(len(batch))
90
+ ```
91
+
92
+ ## Backpressure
93
+
94
+ Buffers are bounded and block the producer by default. Data is not silently
95
+ dropped.
96
+
97
+ ```python
98
+ stream = Stream.from_iterable(range(100_000)).buffer(size=64)
99
+ ```
100
+
101
+ Lossy behaviour must be explicit and is counted:
102
+
103
+ ```python
104
+ stream = Stream.from_iterable(range(10_000)).buffer(
105
+ size=16,
106
+ overflow="drop_oldest",
107
+ )
108
+ items = await stream.collect()
109
+ print(len(items), stream.stats["dropped"])
110
+ ```
111
+
112
+ Policies: `block`, `drop_newest`, `drop_oldest`, `error`.
113
+
114
+ ## Real-time data
115
+
116
+ ### WebSocket
117
+
118
+ ```python
119
+ from datacurrent import WebSocketStream
120
+
121
+ async with WebSocketStream.connect("wss://example.com/events") as stream:
122
+ await stream.send_json({"subscribe": "trades"})
123
+ async for message in stream:
124
+ print(message)
125
+ ```
126
+
127
+ ### Server-Sent Events
128
+
129
+ ```python
130
+ from datacurrent import SSEStream
131
+
132
+ async with SSEStream.connect("https://example.com/events") as stream:
133
+ async for event in stream:
134
+ print(event.event, event.json())
135
+ ```
136
+
137
+ ### Streaming HTTP
138
+
139
+ ```python
140
+ from datacurrent import HTTPStream
141
+
142
+ async with HTTPStream("https://example.com/feed.ndjson") as response:
143
+ async for line in response.iter_lines():
144
+ handle(line)
145
+ ```
146
+
147
+ ### Fan-out and events
148
+
149
+ ```python
150
+ from datacurrent import Channel, EventBus
151
+
152
+ channel = Channel(buffer_size=64)
153
+ first = channel.subscribe()
154
+ second = channel.subscribe()
155
+ await channel.publish("deploy finished")
156
+
157
+ bus = EventBus()
158
+
159
+
160
+ @bus.subscribe("user.*")
161
+ async def on_user(event):
162
+ print(event.topic, event.data)
163
+
164
+
165
+ await bus.publish("user.created", {"user_id": 123})
166
+ ```
167
+
168
+ ## Retries
169
+
170
+ ```python
171
+ from datacurrent import RetryPolicy
172
+
173
+ stream = Stream.from_factory(connect_and_read).retry(
174
+ RetryPolicy(max_attempts=3, base_delay=0.2, jitter=0.1)
175
+ )
176
+ ```
177
+
178
+ Retries require a replayable source, such as a factory or a list. A consumed
179
+ generator raises `NonReplayableStreamError` instead of silently restarting.
180
+
181
+ ## Why DataCurrent
182
+
183
+ - One model for iterables, queues, HTTP, SSE and WebSockets.
184
+ - Backpressure by default, with explicit and counted drop policies.
185
+ - Bounded concurrency and cancellation-safe cleanup.
186
+ - No infrastructure: no broker, database or external account.
187
+ - Explicit delivery semantics; no false durability claims.
188
+ - Typed API and sync or async callbacks throughout.
189
+
190
+ HTTP clients such as `httpx` and `aiohttp` handle requests; DataCurrent adds the
191
+ stream engine around them. Reactive libraries provide operator algebra, while
192
+ broker frameworks require Kafka, Redis or RabbitMQ. DataCurrent is the
193
+ in-process streaming layer that works without any of them.
194
+
195
+ ## Compatibility and limits
196
+
197
+ - Python 3.11+ on asyncio; no Trio or AnyIO backend.
198
+ - In-memory `Channel` and `EventBus` delivery is at most once.
199
+ - No durable broker adapters or OpenTelemetry exporter in 1.0.0.
200
+ - Not published to PyPI; the project name is provisional.
201
+
202
+ ## License
203
+
204
+ MIT.
@@ -0,0 +1,157 @@
1
+ """DataCurrent — Python streaming, made simple.
2
+
3
+ Async streams, composable operators, real backpressure and modular real-time
4
+ transports for Python applications.
5
+
6
+ from datacurrent import Stream
7
+
8
+ stream = Stream.from_iterable([1, 2, 3]).map(lambda n: n * 2)
9
+ async for value in stream:
10
+ print(value)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from ._version import VERSION, __version__
16
+ from .buffering import BoundedQueue, BufferPolicy, BufferStats
17
+ from .concurrency import ConcurrencyLimiter, TaskTracker, gather_limited
18
+ from .config import (
19
+ DEFAULT_BUFFER_SIZE,
20
+ DEFAULT_CONCURRENCY,
21
+ DEFAULT_MAX_MESSAGE_SIZE,
22
+ DEFAULT_TIMEOUT,
23
+ OverflowPolicy,
24
+ StreamConfig,
25
+ )
26
+ from .core import (
27
+ Stream,
28
+ StreamIterator,
29
+ concat_sources,
30
+ consume,
31
+ drain_into,
32
+ merge_sources,
33
+ pipeline,
34
+ )
35
+ from .events import Event, EventBus, Subscription, matches
36
+ from .exceptions import (
37
+ BufferOverflowError,
38
+ ConfigurationError,
39
+ ConnectionFailedError,
40
+ DataCurrentError,
41
+ EmptyStreamError,
42
+ EventBusError,
43
+ MessageTooLargeError,
44
+ NonReplayableStreamError,
45
+ OptionalDependencyError,
46
+ QueueClosedError,
47
+ QueueEmptyError,
48
+ RetryError,
49
+ SSEProtocolError,
50
+ StreamClosedError,
51
+ StreamConsumedError,
52
+ StreamError,
53
+ StreamProtocolError,
54
+ StreamTimeoutError,
55
+ SubscriptionClosedError,
56
+ TransportError,
57
+ WebSocketClosedError,
58
+ )
59
+ from .observability import (
60
+ Counter,
61
+ Metrics,
62
+ StreamStats,
63
+ get_logger,
64
+ redact_headers,
65
+ redact_secret,
66
+ redact_url,
67
+ )
68
+ from .reliability import RetryPolicy, retry_async, with_timeout
69
+ from .transports import (
70
+ Channel,
71
+ HTTPStream,
72
+ SSEEvent,
73
+ SSEStream,
74
+ StreamTransport,
75
+ WebSocketStream,
76
+ format_heartbeat,
77
+ format_sse,
78
+ parse_sse,
79
+ )
80
+
81
+ __all__ = [
82
+ # Constants
83
+ "DEFAULT_BUFFER_SIZE",
84
+ "DEFAULT_CONCURRENCY",
85
+ "DEFAULT_MAX_MESSAGE_SIZE",
86
+ "DEFAULT_TIMEOUT",
87
+ "VERSION",
88
+ # Buffering
89
+ "BoundedQueue",
90
+ "BufferOverflowError",
91
+ "BufferPolicy",
92
+ "BufferStats",
93
+ # Transports
94
+ "Channel",
95
+ # Concurrency
96
+ "ConcurrencyLimiter",
97
+ "ConfigurationError",
98
+ "ConnectionFailedError",
99
+ "Counter",
100
+ # Exceptions
101
+ "DataCurrentError",
102
+ "EmptyStreamError",
103
+ # Events
104
+ "Event",
105
+ "EventBus",
106
+ "EventBusError",
107
+ "HTTPStream",
108
+ "MessageTooLargeError",
109
+ # Observability
110
+ "Metrics",
111
+ "NonReplayableStreamError",
112
+ "OptionalDependencyError",
113
+ "OverflowPolicy",
114
+ "QueueClosedError",
115
+ "QueueEmptyError",
116
+ "RetryError",
117
+ # Reliability
118
+ "RetryPolicy",
119
+ "SSEEvent",
120
+ "SSEProtocolError",
121
+ "SSEStream",
122
+ # Core engine
123
+ "Stream",
124
+ "StreamClosedError",
125
+ "StreamConfig",
126
+ "StreamConsumedError",
127
+ "StreamError",
128
+ "StreamIterator",
129
+ "StreamProtocolError",
130
+ "StreamStats",
131
+ "StreamTimeoutError",
132
+ "StreamTransport",
133
+ "Subscription",
134
+ "SubscriptionClosedError",
135
+ "TaskTracker",
136
+ "TransportError",
137
+ "WebSocketClosedError",
138
+ "WebSocketStream",
139
+ # Version
140
+ "__version__",
141
+ "concat_sources",
142
+ "consume",
143
+ "drain_into",
144
+ "format_heartbeat",
145
+ "format_sse",
146
+ "gather_limited",
147
+ "get_logger",
148
+ "matches",
149
+ "merge_sources",
150
+ "parse_sse",
151
+ "pipeline",
152
+ "redact_headers",
153
+ "redact_secret",
154
+ "redact_url",
155
+ "retry_async",
156
+ "with_timeout",
157
+ ]
@@ -0,0 +1,6 @@
1
+ """Single source of truth for the DataCurrent version."""
2
+
3
+ from __future__ import annotations
4
+
5
+ __version__ = "1.0.0"
6
+ VERSION = tuple(int(part) for part in __version__.split("."))