{"id":16401759,"url":"https://github.com/mreiche/python-streams","last_synced_at":"2026-03-07T01:35:19.715Z","repository":{"id":64775323,"uuid":"570071344","full_name":"mreiche/python-streams","owner":"mreiche","description":"Java-like streams for Python","archived":false,"fork":false,"pushed_at":"2025-05-23T18:07:36.000Z","size":114,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-25T05:02:11.458Z","etag":null,"topics":["python","streams"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mreiche.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-11-24T09:18:59.000Z","updated_at":"2025-05-22T07:03:30.000Z","dependencies_parsed_at":"2024-01-29T07:56:15.866Z","dependency_job_id":"81126e3b-acfc-4a41-a009-4d6ed1af0bec","html_url":"https://github.com/mreiche/python-streams","commit_stats":{"total_commits":9,"total_committers":2,"mean_commits":4.5,"dds":"0.11111111111111116","last_synced_commit":"553f3dd9fb44399df66f9433bc6daa64c9fbb301"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/mreiche/python-streams","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mreiche%2Fpython-streams","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mreiche%2Fpython-streams/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mreiche%2Fpython-streams/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mreiche%2Fpython-streams/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mreiche","download_url":"https://codeload.github.com/mreiche/python-streams/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mreiche%2Fpython-streams/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30205211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T19:07:06.838Z","status":"ssl_error","status_checked_at":"2026-03-06T18:57:34.882Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["python","streams"],"created_at":"2024-10-11T05:44:04.756Z","updated_at":"2026-03-07T01:35:19.695Z","avatar_url":"https://github.com/mreiche.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Tests Status](https://github.com/mreiche/python-streams/actions/workflows/tests.yml/badge.svg)](https://github.com/mreiche/python-streams/actions/workflows/tests.yml)\n[![Code Coverage Status](https://codecov.io/github/mreiche/python-streams/branch/main/graph/badge.svg)](https://app.codecov.io/github/mreiche/python-streams)\n[![PyPI version](https://badge.fury.io/py/tinystream.svg)](https://badge.fury.io/py/tinystream)\n\n# tinystream / python-streams\n\nThis is a simple and lightweight Streams API inspired by Java Streams with support for type hinting.\n\nThis package is released as `tinystream` at pypi.\n\n## Basic API\n\n```python\nfrom tinystream import Stream\n\nstream = Stream([1, 2, 3, 4, 5])  # Stream.of_many(*), Stream.of_dict()\n\nstream \\\n    .map(lambda x: x + 1) \\       # flatmap(), peek(), map_key(), map_kwargs(), map_keys()\n    .filter(lambda x: x \u003e 2) \\    # filter_key(), filter_type()\n    .sorted(reverse=True) \\       # sort()\n    .reverse() \\\n    .limit(2) \\\n    .concat([4]) \\\n    .sum()                        # reduce(), max(), min(), collect(), count(), find()\n```\n\n## Aggregators\n\nAggregators like `sum()`, `count()`, `max()` will `collect()` the data and end the stream. `collect()` also caches the data and can be called multiple times, since it returns only a `list`.\n\n## Built-in Optional support\n\nSome aggregators like `sum()`, `max()` are `Opt`:\n\n```python\nassert Stream((1, 2, 3, 4, 5)).sum().get() == 15\n```\n\n## More features\n\n### Type hinting\n\nYou can typehint datatypes like:\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass\nclass Node:\n    name: str\n    parent: \"Node\" = None\n\nparent = Node(name=\"B\")\nchild = Node(name=\"A\", parent=parent)\n```\n\nfor lambdas:\n\n```python\nstream = Stream([child])\nassert stream.map(lambda x: x.parent).type(Node).next().get().name == \"B\"\n```\n\nThis is not necessary when you pass a mapping function:\n```python\ndef map_parent(n: Node):\n    return n.parent\n\nassert stream.map(map_parent).next().get().name == \"B\"\n```\n\n### Typed dictionaries\n\nDictionaries are streamed as `tuple(key, value)`\n\n```python\nchildren = {\"a\": Node(name=\"Child\")} \nstream = Stream.of_dict(children)\nfor item in stream:\n    # item[0] is known as str\n    # item[1] is known as Node\n```\n\nThis is the same like (but without known types):\n```python\nstream = Stream(children)\n```\n\n### Filter by existing key\n```python\nitems_with_name = Stream([child]).filter_key(\"name\")\n```\n\n### Filter by key value\n```python\nitems_with_name = Stream([child]).filter_key_value(\"name\", \"Child\")\n```\n\n### Filter by type\n```python\nnodes_only = Stream([child]).filter_type(Node)\n```\n\n### Map object name attribute\n```python\nnames = Stream([child]).map_key(\"name\")\n```\n\n### Deep mapping of name attributes\n```python\nlist = [\n   {\"node\": Node(name=\"Node A\")},\n   {\"node\": Node(name=\"Node B\")},\n   {\"node\": Node(name=\"Node C\")},\n   {\"node\": Node(name=\"Node D\")},\n]\nnames = Stream(list).map_keys(\"node\", \"name\")\n```\n\n### Collected join\n\n```python\nall_names = Stream([child]).map_key(\"name\").join(\", \")\n```\n\n### Map kwargs\n```python\nlist = [\n   {\"name\": \"Node A\"},\n   {\"name\": \"Node B\"},\n]\n# Short cut for map(lambda x: Node(**x))\nnodes = Stream(list).map_kwargs(Node)\n```\n\n### Stream many\n\n```python\nmany = Stream.of_many([1, 2, 3], (4, 5, 6))\nmany = many.concat([7, 8, 9])\n```\n\n### End of stream\n```python\nstream = Stream([\"a\", \"b\", \"c\"]).on_end(lambda: print(\"Finished\"))\nchar = stream.next().get()\nif char == \"a\":\n    stream.end()\n```\n\n### Opt usage\n\nGet next value as `Opt`:\n\n```python\nassert Stream((1, 2, 3, 4, 5)).next().present\n```\n\nMapping:\n```python\nassert Opt(\"String\").map(str.lower).len == 6\n```\n\nGet default value:\n```python\nassert Opt(None).get(6) == 6\nassert Opt(None).get(lambda: 6) == 6\nassert Opt(None).if_absent(lambda: 3).present\n```\n\nFilter value:\n```python\nassert Opt(0).filter(lambda x: x \u003e 0).absent\n```\n\nYou can also access optional index elements of the stream, but this will `collect()` and end the stream.\n```python\nassert Stream([])[2].absent\n```\n\n## Examples\n\n### Write better code with `Stream`\n\n```python\ndata = {\n   \"ranges\": [\n      {\"days\": 3},\n      {\"weeks\": 1},\n   ]\n}\n\n# With tinystream Stream\nfor range_data in Opt(data).map_key(\"ranges\").stream().map_kwargs(timedelta):\n   pass\n\n# Vanilly Python\nif \"ranges\" in data:\n   range_data: timedelta\n   for range_data in map(lambda x: timedelta(**x), data[\"ranges\"]):\n      pass\n```\n\n### Write better code with `Opt`\n```python\n# tinystream Opt\nvar = Opt(my_dict).kmap(\"key\").filter(not_empty).get(\"default\")\n\n# Vanilla Python\nvar = my_dict[\"key\"] if \"key\" in my_dict and not_empty(my_dict[\"key\"]) else \"default\"\n```\n\n\n## Comparison with other libraries\n\nThere are a couple of other implementation to fulfill similar requirements.\n\n- https://github.com/vxgmichel/aiostream\n- https://github.com/python-streamz/streamz\n- https://pypi.org/project/fluentpy\n- https://github.com/ramsteak/streams \n- https://github.com/alemazzo/Python-Java-Stream  (*outdated*)\n- https://github.com/JaviOverflow/python-streams (*outdated*)\n- https://github.com/9seconds/streams/ (*outdated*)\n- https://github.com/tolsac/streampy (*outdated*)\n- Apache Spark\n\n## Run the tests\n\n```shell\nPYTHONPATH=\".\" pytest --cov=tinystream -n 4 tests/\n```\n\n## References\n\n- https://github.com/MichaelKim0407/tutorial-pip-package\n- https://packaging.python.org/en/latest/guides/making-a-pypi-friendly-readme/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmreiche%2Fpython-streams","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmreiche%2Fpython-streams","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmreiche%2Fpython-streams/lists"}