pystreamlet 0.1.0__tar.gz → 0.2.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.
- {pystreamlet-0.1.0 → pystreamlet-0.2.0}/PKG-INFO +9 -11
- {pystreamlet-0.1.0 → pystreamlet-0.2.0}/README.md +8 -10
- {pystreamlet-0.1.0 → pystreamlet-0.2.0}/pyproject.toml +11 -14
- {pystreamlet-0.1.0 → pystreamlet-0.2.0}/pyproject.toml.orig +2 -7
- pystreamlet-0.2.0/src/pystreamlet/__init__.py +30 -0
- {pystreamlet-0.1.0/src/streamlet → pystreamlet-0.2.0/src/pystreamlet}/async_stream.py +3 -3
- {pystreamlet-0.1.0/src/streamlet → pystreamlet-0.2.0/src/pystreamlet}/file_stream.py +1 -1
- {pystreamlet-0.1.0/src/streamlet → pystreamlet-0.2.0/src/pystreamlet}/stream.py +4 -4
- pystreamlet-0.1.0/src/streamlet/__init__.py +0 -30
- {pystreamlet-0.1.0 → pystreamlet-0.2.0}/LICENSE +0 -0
- {pystreamlet-0.1.0/src/streamlet → pystreamlet-0.2.0/src/pystreamlet}/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pystreamlet
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A fluent, lazy stream-processing library for Python, modeled after Java's Streams API
|
|
5
5
|
Keywords: stream,streams,lazy,itertools,functional,pipeline,async
|
|
6
6
|
Author: 0xthreadsafe
|
|
@@ -29,7 +29,7 @@ Description-Content-Type: text/markdown
|
|
|
29
29
|
A fluent, **lazy** stream-processing library for Python, modeled after Java's Streams API.
|
|
30
30
|
|
|
31
31
|
```python
|
|
32
|
-
from
|
|
32
|
+
from pystreamlet import Stream
|
|
33
33
|
|
|
34
34
|
(
|
|
35
35
|
Stream.from_iterable(range(100))
|
|
@@ -47,13 +47,11 @@ The goal is to make `itertools`' power reachable through clean, chainable syntax
|
|
|
47
47
|
pip install pystreamlet # or: uv add pystreamlet
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
`streamlit`. Requires **Python 3.11+**, has no dependencies, and ships type information
|
|
52
|
-
(`py.typed`).
|
|
50
|
+
Requires **Python 3.11+**, has no dependencies, and ships type information (`py.typed`).
|
|
53
51
|
|
|
54
|
-
> **Status: 0.
|
|
55
|
-
>
|
|
56
|
-
>
|
|
52
|
+
> **Status: 0.2.0, on PyPI.** Everything below works and is tested. Before 1.0.0 the API may
|
|
53
|
+
> still change — see [versioning](#versioning). Coming from 0.1.0? The import moved from
|
|
54
|
+
> `streamlet` to `pystreamlet`, matching the install name; nothing else changed.
|
|
57
55
|
|
|
58
56
|
## Why
|
|
59
57
|
|
|
@@ -65,7 +63,7 @@ from itertools import count, islice
|
|
|
65
63
|
|
|
66
64
|
list(islice((n**2 for n in count() if n % 2 == 0), 5))
|
|
67
65
|
|
|
68
|
-
#
|
|
66
|
+
# pystreamlet: read left to right, in the order it happens
|
|
69
67
|
(
|
|
70
68
|
Stream.iterate(0, lambda n: n + 1)
|
|
71
69
|
.filter(lambda n: n % 2 == 0)
|
|
@@ -261,7 +259,7 @@ something you opened would break the whoever-opens-closes convention.
|
|
|
261
259
|
```python
|
|
262
260
|
import asyncio
|
|
263
261
|
|
|
264
|
-
from
|
|
262
|
+
from pystreamlet import AsyncStream
|
|
265
263
|
|
|
266
264
|
|
|
267
265
|
async def fetch(url: str) -> str: ...
|
|
@@ -380,7 +378,7 @@ through trusted publishing, so no token lives in this repository.
|
|
|
380
378
|
- [x] `Stream.from_file` — context-managed resource streams
|
|
381
379
|
- [x] `AsyncStream` — bounded concurrent `map`
|
|
382
380
|
- [x] Docstrings, README examples
|
|
383
|
-
- [
|
|
381
|
+
- [x] PyPI release
|
|
384
382
|
|
|
385
383
|
## License
|
|
386
384
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
A fluent, **lazy** stream-processing library for Python, modeled after Java's Streams API.
|
|
4
4
|
|
|
5
5
|
```python
|
|
6
|
-
from
|
|
6
|
+
from pystreamlet import Stream
|
|
7
7
|
|
|
8
8
|
(
|
|
9
9
|
Stream.from_iterable(range(100))
|
|
@@ -21,13 +21,11 @@ The goal is to make `itertools`' power reachable through clean, chainable syntax
|
|
|
21
21
|
pip install pystreamlet # or: uv add pystreamlet
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
`streamlit`. Requires **Python 3.11+**, has no dependencies, and ships type information
|
|
26
|
-
(`py.typed`).
|
|
24
|
+
Requires **Python 3.11+**, has no dependencies, and ships type information (`py.typed`).
|
|
27
25
|
|
|
28
|
-
> **Status: 0.
|
|
29
|
-
>
|
|
30
|
-
>
|
|
26
|
+
> **Status: 0.2.0, on PyPI.** Everything below works and is tested. Before 1.0.0 the API may
|
|
27
|
+
> still change — see [versioning](#versioning). Coming from 0.1.0? The import moved from
|
|
28
|
+
> `streamlet` to `pystreamlet`, matching the install name; nothing else changed.
|
|
31
29
|
|
|
32
30
|
## Why
|
|
33
31
|
|
|
@@ -39,7 +37,7 @@ from itertools import count, islice
|
|
|
39
37
|
|
|
40
38
|
list(islice((n**2 for n in count() if n % 2 == 0), 5))
|
|
41
39
|
|
|
42
|
-
#
|
|
40
|
+
# pystreamlet: read left to right, in the order it happens
|
|
43
41
|
(
|
|
44
42
|
Stream.iterate(0, lambda n: n + 1)
|
|
45
43
|
.filter(lambda n: n % 2 == 0)
|
|
@@ -235,7 +233,7 @@ something you opened would break the whoever-opens-closes convention.
|
|
|
235
233
|
```python
|
|
236
234
|
import asyncio
|
|
237
235
|
|
|
238
|
-
from
|
|
236
|
+
from pystreamlet import AsyncStream
|
|
239
237
|
|
|
240
238
|
|
|
241
239
|
async def fetch(url: str) -> str: ...
|
|
@@ -354,7 +352,7 @@ through trusted publishing, so no token lives in this repository.
|
|
|
354
352
|
- [x] `Stream.from_file` — context-managed resource streams
|
|
355
353
|
- [x] `AsyncStream` — bounded concurrent `map`
|
|
356
354
|
- [x] Docstrings, README examples
|
|
357
|
-
- [
|
|
355
|
+
- [x] PyPI release
|
|
358
356
|
|
|
359
357
|
## License
|
|
360
358
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pystreamlet"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "A fluent, lazy stream-processing library for Python, modeled after Java's Streams API"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -40,14 +40,21 @@ Changelog = "https://github.com/0xthreadsafe/streamlet/blob/main/CHANGELOG.md"
|
|
|
40
40
|
Issues = "https://github.com/0xthreadsafe/streamlet/issues"
|
|
41
41
|
|
|
42
42
|
[project.scripts]
|
|
43
|
-
|
|
43
|
+
pystreamlet = "pystreamlet:main"
|
|
44
44
|
|
|
45
45
|
[build-system]
|
|
46
46
|
requires = ["uv_build>=0.10.4,<0.11.0"]
|
|
47
47
|
build-backend = "uv_build"
|
|
48
48
|
|
|
49
|
-
[
|
|
50
|
-
|
|
49
|
+
[dependency-groups]
|
|
50
|
+
dev = [
|
|
51
|
+
"mypy>=2.3.1",
|
|
52
|
+
"pytest>=9.1.1",
|
|
53
|
+
"ruff>=0.16.6",
|
|
54
|
+
"hypothesis>=6.0",
|
|
55
|
+
"pytest-asyncio>=0.24",
|
|
56
|
+
"pre-commit>=4.0",
|
|
57
|
+
]
|
|
51
58
|
|
|
52
59
|
[tool.ruff]
|
|
53
60
|
target-version = "py311"
|
|
@@ -105,13 +112,3 @@ show_error_codes = true
|
|
|
105
112
|
[tool.pytest.ini_options]
|
|
106
113
|
asyncio_mode = "auto"
|
|
107
114
|
asyncio_default_fixture_loop_scope = "function"
|
|
108
|
-
|
|
109
|
-
[dependency-groups]
|
|
110
|
-
dev = [
|
|
111
|
-
"mypy>=2.3.1",
|
|
112
|
-
"pytest>=9.1.1",
|
|
113
|
-
"ruff>=0.16.6",
|
|
114
|
-
"hypothesis>=6.0",
|
|
115
|
-
"pytest-asyncio>=0.24",
|
|
116
|
-
"pre-commit>=4.0",
|
|
117
|
-
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pystreamlet"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "A fluent, lazy stream-processing library for Python, modeled after Java's Streams API"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -31,17 +31,12 @@ Changelog = "https://github.com/0xthreadsafe/streamlet/blob/main/CHANGELOG.md"
|
|
|
31
31
|
Issues = "https://github.com/0xthreadsafe/streamlet/issues"
|
|
32
32
|
|
|
33
33
|
[project.scripts]
|
|
34
|
-
|
|
34
|
+
pystreamlet = "pystreamlet:main"
|
|
35
35
|
|
|
36
36
|
[build-system]
|
|
37
37
|
requires = ["uv_build>=0.10.4,<0.11.0"]
|
|
38
38
|
build-backend = "uv_build"
|
|
39
39
|
|
|
40
|
-
# The distribution is pystreamlet but the module stays streamlet, so the
|
|
41
|
-
# backend has to be told rather than inferring the path from the name.
|
|
42
|
-
[tool.uv.build-backend]
|
|
43
|
-
module-name = "streamlet"
|
|
44
|
-
|
|
45
40
|
[dependency-groups]
|
|
46
41
|
dev = [
|
|
47
42
|
"mypy>=2.3.1",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Streamlet — a fluent, lazy stream-processing library for Python.
|
|
2
|
+
|
|
3
|
+
Start from :class:`~pystreamlet.stream.Stream` for synchronous pipelines and
|
|
4
|
+
:class:`~pystreamlet.async_stream.AsyncStream` for async ones::
|
|
5
|
+
|
|
6
|
+
from pystreamlet import Stream
|
|
7
|
+
|
|
8
|
+
Stream.from_iterable(range(100)).filter(lambda n: n % 2 == 0).take(5).to_list()
|
|
9
|
+
|
|
10
|
+
:class:`~pystreamlet.file_stream.ResourceStream` (and its ``FileStream``
|
|
11
|
+
specialisation) covers sources that hold a resource open.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from pystreamlet.async_stream import AsyncStream
|
|
15
|
+
from pystreamlet.file_stream import ClosableIterable, FileStream, ResourceStream
|
|
16
|
+
from pystreamlet.stream import Stream, StreamConsumedError
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"AsyncStream",
|
|
20
|
+
"ClosableIterable",
|
|
21
|
+
"FileStream",
|
|
22
|
+
"ResourceStream",
|
|
23
|
+
"Stream",
|
|
24
|
+
"StreamConsumedError",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def main() -> None:
|
|
29
|
+
"""Entry point for the ``streamlet`` console script."""
|
|
30
|
+
print("Hello from pystreamlet!")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Lazy asynchronous streams.
|
|
2
2
|
|
|
3
|
-
``AsyncStream`` mirrors :class:`~
|
|
3
|
+
``AsyncStream`` mirrors :class:`~pystreamlet.stream.Stream`, but pulls from an
|
|
4
4
|
async source and is driven with ``async for`` / ``await``. Every operation
|
|
5
5
|
that takes a function accepts either a plain function or a coroutine
|
|
6
6
|
function, so ``map(str)`` and ``map(fetch)`` both work.
|
|
@@ -22,7 +22,7 @@ from collections.abc import (
|
|
|
22
22
|
from contextlib import asynccontextmanager
|
|
23
23
|
from typing import Any, Generic, TypeVar, cast, overload
|
|
24
24
|
|
|
25
|
-
from
|
|
25
|
+
from pystreamlet.stream import StreamConsumedError
|
|
26
26
|
|
|
27
27
|
T = TypeVar("T")
|
|
28
28
|
R = TypeVar("R")
|
|
@@ -91,7 +91,7 @@ class AsyncStream(Generic[T]):
|
|
|
91
91
|
|
|
92
92
|
total = await AsyncStream.from_iterable(urls).map(fetch).count()
|
|
93
93
|
|
|
94
|
-
Like :class:`~
|
|
94
|
+
Like :class:`~pystreamlet.stream.Stream`, a stream is consumed once;
|
|
95
95
|
iterating a second time raises :class:`StreamConsumedError`.
|
|
96
96
|
|
|
97
97
|
Cleanup runs eagerly: each stage closes the one behind it, so a bounded
|
|
@@ -8,7 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from types import TracebackType
|
|
9
9
|
from typing import IO, Protocol, Self, TypeVar, runtime_checkable
|
|
10
10
|
|
|
11
|
-
from
|
|
11
|
+
from pystreamlet.stream import Stream
|
|
12
12
|
|
|
13
13
|
T = TypeVar("T")
|
|
14
14
|
T_co = TypeVar("T_co", covariant=True)
|
|
@@ -16,7 +16,7 @@ from os import PathLike
|
|
|
16
16
|
from typing import TYPE_CHECKING, Any, Generic, TypeVar, overload
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
|
-
from
|
|
19
|
+
from pystreamlet.file_stream import ClosableIterable, FileStream, ResourceStream
|
|
20
20
|
|
|
21
21
|
T = TypeVar("T")
|
|
22
22
|
R = TypeVar("R")
|
|
@@ -95,7 +95,7 @@ class Stream(Generic[T]):
|
|
|
95
95
|
) -> FileStream:
|
|
96
96
|
"""Stream the lines of a file, closing the handle when done.
|
|
97
97
|
|
|
98
|
-
Returns a :class:`~
|
|
98
|
+
Returns a :class:`~pystreamlet.file_stream.FileStream`, which closes the
|
|
99
99
|
handle on every exit path and is also a context manager::
|
|
100
100
|
|
|
101
101
|
with Stream.from_file("app.log") as lines:
|
|
@@ -103,7 +103,7 @@ class Stream(Generic[T]):
|
|
|
103
103
|
|
|
104
104
|
Lines keep their trailing newline, matching ``open()``.
|
|
105
105
|
"""
|
|
106
|
-
from
|
|
106
|
+
from pystreamlet.file_stream import open_file_stream
|
|
107
107
|
|
|
108
108
|
return open_file_stream(path, encoding=encoding, errors=errors, newline=newline)
|
|
109
109
|
|
|
@@ -136,7 +136,7 @@ class Stream(Generic[T]):
|
|
|
136
136
|
TypeError: if the handle has no ``close()`` method.
|
|
137
137
|
|
|
138
138
|
"""
|
|
139
|
-
from
|
|
139
|
+
from pystreamlet.file_stream import resource_stream
|
|
140
140
|
|
|
141
141
|
return resource_stream(handle, close=close)
|
|
142
142
|
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"""Streamlet — a fluent, lazy stream-processing library for Python.
|
|
2
|
-
|
|
3
|
-
Start from :class:`~streamlet.stream.Stream` for synchronous pipelines and
|
|
4
|
-
:class:`~streamlet.async_stream.AsyncStream` for async ones::
|
|
5
|
-
|
|
6
|
-
from streamlet import Stream
|
|
7
|
-
|
|
8
|
-
Stream.from_iterable(range(100)).filter(lambda n: n % 2 == 0).take(5).to_list()
|
|
9
|
-
|
|
10
|
-
:class:`~streamlet.file_stream.ResourceStream` (and its ``FileStream``
|
|
11
|
-
specialisation) covers sources that hold a resource open.
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
from streamlet.async_stream import AsyncStream
|
|
15
|
-
from streamlet.file_stream import ClosableIterable, FileStream, ResourceStream
|
|
16
|
-
from streamlet.stream import Stream, StreamConsumedError
|
|
17
|
-
|
|
18
|
-
__all__ = [
|
|
19
|
-
"AsyncStream",
|
|
20
|
-
"ClosableIterable",
|
|
21
|
-
"FileStream",
|
|
22
|
-
"ResourceStream",
|
|
23
|
-
"Stream",
|
|
24
|
-
"StreamConsumedError",
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def main() -> None:
|
|
29
|
-
"""Entry point for the ``streamlet`` console script."""
|
|
30
|
-
print("Hello from streamlet!")
|
|
File without changes
|
|
File without changes
|