fpstreams 2.2.1__tar.gz → 2.3.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.
- {fpstreams-2.2.1 → fpstreams-2.3.0}/CHANGELOG.md +30 -1
- {fpstreams-2.2.1 → fpstreams-2.3.0}/PKG-INFO +10 -1
- {fpstreams-2.2.1 → fpstreams-2.3.0}/README.md +9 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/pyproject.toml +1 -1
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/Cargo.lock +1 -1
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/Cargo.toml +1 -1
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/__init__.py +1 -1
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/_rows_fusion.py +50 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/sorted_streams.py +22 -1
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/sync_ops.py +39 -16
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/explain.py +15 -1
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/sync.py +3 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/storage/spill_store.py +5 -1
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/streams/async_flow.py +94 -2
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/streams/flow.py +84 -28
- {fpstreams-2.2.1 → fpstreams-2.3.0}/LICENSE +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/build.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/common.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/float/affine_pair.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/float/endpoints.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/float.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/integer/endpoints.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/integer.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/lib.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/numeric_mean.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/numpy_export.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/numpy_group/buffer.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/numpy_group.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/pair/expr.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/pair/prefix.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/pair/row_filter.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/pair/unique.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/pair/value_filter.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/pair/value_map.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/pair.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/pivot.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/records.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational/adapters/namedtuple.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational/adapters.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational/global_numeric.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational/group_numeric.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational/group_pair_expr.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational/join_callable/many.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational/join_callable/unique.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational/join_callable.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational/join_exact.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational/join_i64.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational_fixed/composite.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational_fixed/global_multi/same_field.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational_fixed/global_multi.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational_fixed/group_multi.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational_fixed/single.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/relational_fixed.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/scalar_sort.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/scalar_unique.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/select.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/adapters.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/group_exact/pairs.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/group_exact/rows.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/group_exact.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/group_fixed.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/join_callable/behavior.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/join_callable/direct.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/join_callable.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/join_exact.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/numeric/buffers.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/numeric/kernels.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/numeric/reductions.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/numeric.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests/numpy_group.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/tests.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/unnest.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/rust/src/unpivot.rs +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/_native.pyi +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/_provenance.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/aggregate.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/async_flow.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/collecting/__init__.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/collecting/_collector_base.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/collecting/aggregate_program.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/collecting/aggregation.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/collecting/collector.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/collecting/program.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/collecting/reducer.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/collecting/statistics.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/collectors.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/column.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/errors.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/exceptions.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/__init__.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/_pair_aggregate.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/_pair_dict.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/_pair_row_filter.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/_scalar_fusion.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/arrow.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/async_iterators.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/async_map.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/async_merge.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/async_ops.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/async_prefetch.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/async_queue.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/async_scheduler.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/async_timers.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/native.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/numpy_group.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/numpy_prefix.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/physical.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/relational/__init__.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/relational/arrow_global.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/relational/arrow_group.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/relational/arrow_group_rows.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/relational/join.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/sorting.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/execution/sync.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/expr.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/expressions/__init__.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/expressions/_codegen.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/expressions/_row_codegen.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/expressions/program.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/expressions/row.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/expressions/row_eval.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/expressions/row_ir.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/expressions/scalar.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/expressions/selectors.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/expressions/typed_ir.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/functional.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/gather.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/io_safety.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/option.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/physical/__init__.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/physical/async_plan.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/physical/compiled.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/physical/kernel_cache.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/physical/plan.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/physical/relational.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/__init__.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/_pair_stages.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/arrow.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/arrow_source.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/async_.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/async_utils.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/compiler.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/gather.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/logical.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/native.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/numpy.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/pair_i64_expression.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/plan_cache.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/semantic_analyzer.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/semantic_rules.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/semantics.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/planning/source.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/primitives/__init__.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/primitives/option.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/primitives/result.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/py.typed +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/result.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/__init__.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/_distinct.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/failpoints.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/files.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/iterators.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/limits.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/metrics.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/query.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/report.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/resources.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/spill.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/runtime/tasks.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/storage/__init__.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/storage/codec.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/streams/__init__.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/streams/_async_identity.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/streams/_flow_structural_list.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/streams/_flow_unique_list.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/streams/async_terminals.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/streams/flow_terminals.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/streams/pairs.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/__init__.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/_text_sources.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/arrow.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/dataframe.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/factory.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/grouped.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/io.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/join.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/numpy.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/polars.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/records.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/rows.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/spill.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/spill_io.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/spill_limits.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/sql.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/tabular/sqlite_sink.py +0 -0
- {fpstreams-2.2.1 → fpstreams-2.3.0}/src/fpstreams/testing.py +0 -0
|
@@ -3,7 +3,36 @@
|
|
|
3
3
|
This file records user-visible and compatibility-relevant changes in fpstreams 2,
|
|
4
4
|
including changed defaults.
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## 2.3.0 - 2026-10-15
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- Async callback return types resolve correctly for `AsyncFlow.map()`,
|
|
11
|
+
`map_async()`, and `map_batches()`. Mixed immediate and awaitable returns
|
|
12
|
+
retain the resolved element type.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Supported single-stage Python `with_columns()` and `select()` queries reuse a
|
|
17
|
+
loop template with separate callback bindings for each execution, reducing
|
|
18
|
+
fixed setup cost while retaining computed-column live selectors and projection
|
|
19
|
+
snapshots for each row.
|
|
20
|
+
- Explicit sorted grouping with one key avoids a temporary selector generator
|
|
21
|
+
and wrapper tuple per row while preserving live callbacks, key validation,
|
|
22
|
+
lookahead, and callback error behavior.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `Flow.map_batches(function, size=...)` and `AsyncFlow.map_batches(...)` call a
|
|
27
|
+
mapper once per tuple batch, including a shorter final batch. Each call emits
|
|
28
|
+
one result. Sync batches inherit `parallel()` settings; async batches support
|
|
29
|
+
concurrency, ordering, per-batch timeouts, and bounded buffering.
|
|
30
|
+
- `Flow.map_parallel()` and `Flow.parallel()` accept a caller-owned `executor`.
|
|
31
|
+
Queries cancel their queued work and wait for their own running mappers while
|
|
32
|
+
leaving the shared pool open. Existing worker/backend defaults remain in place
|
|
33
|
+
when no executor is supplied.
|
|
34
|
+
- Streaming analytics examples show generator input, valid weighted denominators,
|
|
35
|
+
parameterized SQLite selection, and atomic writes across multiple batches.
|
|
7
36
|
|
|
8
37
|
## 2.2.1 - 2026-10-07
|
|
9
38
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fpstreams
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -66,6 +66,15 @@ Lazy Python pipelines for iterables and records, with bounded async concurrency.
|
|
|
66
66
|
|
|
67
67
|
Python 3.11 or newer is required.
|
|
68
68
|
|
|
69
|
+
The current stable release is [2.3.0](https://github.com/steventimes/fpstreams/releases/tag/v2.3.0).
|
|
70
|
+
It adds explicit `map_batches()` for sync and async pipelines, reusable mapping
|
|
71
|
+
executors, and async callback typing fixes. See the
|
|
72
|
+
[changelog](https://github.com/steventimes/fpstreams/blob/master/CHANGELOG.md) for its scope and the
|
|
73
|
+
[performance guide](https://steventimes.github.io/fpstreams/user-guide/performance/#process-mapping-and-explicit-batches)
|
|
74
|
+
for batch and executor examples. The
|
|
75
|
+
[roadmap](https://steventimes.github.io/fpstreams/roadmap/#next-development-steps)
|
|
76
|
+
lists the next optimization candidates and their acceptance criteria.
|
|
77
|
+
|
|
69
78
|
## Install
|
|
70
79
|
|
|
71
80
|
Install the latest stable release:
|
|
@@ -11,6 +11,15 @@ Lazy Python pipelines for iterables and records, with bounded async concurrency.
|
|
|
11
11
|
|
|
12
12
|
Python 3.11 or newer is required.
|
|
13
13
|
|
|
14
|
+
The current stable release is [2.3.0](https://github.com/steventimes/fpstreams/releases/tag/v2.3.0).
|
|
15
|
+
It adds explicit `map_batches()` for sync and async pipelines, reusable mapping
|
|
16
|
+
executors, and async callback typing fixes. See the
|
|
17
|
+
[changelog](https://github.com/steventimes/fpstreams/blob/master/CHANGELOG.md) for its scope and the
|
|
18
|
+
[performance guide](https://steventimes.github.io/fpstreams/user-guide/performance/#process-mapping-and-explicit-batches)
|
|
19
|
+
for batch and executor examples. The
|
|
20
|
+
[roadmap](https://steventimes.github.io/fpstreams/roadmap/#next-development-steps)
|
|
21
|
+
lists the next optimization candidates and their acceptance criteria.
|
|
22
|
+
|
|
14
23
|
## Install
|
|
15
24
|
|
|
16
25
|
Install the latest stable release:
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import ast
|
|
6
6
|
from collections.abc import Callable, Iterator
|
|
7
7
|
from itertools import chain
|
|
8
|
+
from types import FunctionType
|
|
8
9
|
from typing import Any, cast
|
|
9
10
|
|
|
10
11
|
from ..errors import SelectionError
|
|
@@ -74,6 +75,33 @@ RowsFilterSink = Callable[[list[Any], Iterator[Any]], tuple[Any | None, bool]]
|
|
|
74
75
|
I64RowFilterPlan = tuple[str, tuple[tuple[int, int], ...], bool]
|
|
75
76
|
|
|
76
77
|
|
|
78
|
+
def _row_map_loop_template(
|
|
79
|
+
_source: Iterator[Any],
|
|
80
|
+
_fpstreams_slot_0: Callable[[Any], tuple[bool, Any]],
|
|
81
|
+
_fpstreams_slot_1: Callable[[Any], type[Any]],
|
|
82
|
+
_fpstreams_slot_2: type[dict[str, Any]],
|
|
83
|
+
_fpstreams_slot_3: type[StopIteration],
|
|
84
|
+
_fpstreams_slot_4: Callable[[Any], Any],
|
|
85
|
+
_fpstreams_slot_5: type[StopIteration],
|
|
86
|
+
) -> Iterator[Any]:
|
|
87
|
+
"""Keep the one-stage generated loop's dispatch, bindings, and row lifetimes."""
|
|
88
|
+
for _row in _source:
|
|
89
|
+
if _fpstreams_slot_1(_row) is not _fpstreams_slot_2:
|
|
90
|
+
try:
|
|
91
|
+
_fallback = _fpstreams_slot_0(_row)
|
|
92
|
+
except _fpstreams_slot_3:
|
|
93
|
+
return
|
|
94
|
+
if _fallback[0]:
|
|
95
|
+
yield _fallback[1]
|
|
96
|
+
continue
|
|
97
|
+
_current = _row
|
|
98
|
+
try:
|
|
99
|
+
_current = _fpstreams_slot_4(_current)
|
|
100
|
+
except _fpstreams_slot_5:
|
|
101
|
+
return
|
|
102
|
+
yield _current
|
|
103
|
+
|
|
104
|
+
|
|
77
105
|
def execute_rows_fusion(
|
|
78
106
|
iterator: Iterator[Any],
|
|
79
107
|
operations: tuple[RowsOperation, ...],
|
|
@@ -391,6 +419,28 @@ class _RowsLoopBuilder:
|
|
|
391
419
|
|
|
392
420
|
def compile(self) -> RowsLoop:
|
|
393
421
|
"""Compile a generator whose globals expose no builtins or source-text interpolation."""
|
|
422
|
+
if (
|
|
423
|
+
len(self._operations) == 1
|
|
424
|
+
and isinstance(operation := self._operations[0], MapOp)
|
|
425
|
+
and self._descriptors[0].kind in {"select", "with_columns"}
|
|
426
|
+
):
|
|
427
|
+
slots = (
|
|
428
|
+
_fallback_processor(self._operations),
|
|
429
|
+
type,
|
|
430
|
+
dict,
|
|
431
|
+
StopIteration,
|
|
432
|
+
operation.function,
|
|
433
|
+
StopIteration,
|
|
434
|
+
)
|
|
435
|
+
namespace: dict[str, Any] = {"__builtins__": {}, "_fpstreams_slots": slots}
|
|
436
|
+
loop = FunctionType(
|
|
437
|
+
_row_map_loop_template.__code__,
|
|
438
|
+
namespace,
|
|
439
|
+
name="_fpstreams_rows_loop",
|
|
440
|
+
argdefs=slots,
|
|
441
|
+
)
|
|
442
|
+
namespace["_fpstreams_rows_loop"] = loop
|
|
443
|
+
return cast(RowsLoop, loop)
|
|
394
444
|
body = self._loop_body()
|
|
395
445
|
return cast(
|
|
396
446
|
RowsLoop,
|
|
@@ -21,6 +21,11 @@ class _End:
|
|
|
21
21
|
|
|
22
22
|
_EOF = _End()
|
|
23
23
|
_UNSET = object()
|
|
24
|
+
_BUILTIN_TUPLE: type[Any] = tuple
|
|
25
|
+
_BUILTIN_LEN = len
|
|
26
|
+
_BUILTIN_TYPE = type
|
|
27
|
+
_BUILTIN_STOP_ITERATION = StopIteration
|
|
28
|
+
_BUILTIN_RUNTIME_ERROR = RuntimeError
|
|
24
29
|
|
|
25
30
|
|
|
26
31
|
class _KeyShape:
|
|
@@ -146,8 +151,24 @@ def aggregate_sorted_rows(
|
|
|
146
151
|
"""Finish adjacent groups without buffering their rows or future groups."""
|
|
147
152
|
source = values if isinstance(values, Source) else Source.from_iterable(values)
|
|
148
153
|
|
|
154
|
+
single = (
|
|
155
|
+
_BUILTIN_TYPE(keys) is cast(Any, _BUILTIN_TUPLE)
|
|
156
|
+
and _BUILTIN_LEN(keys) == 1
|
|
157
|
+
and _BUILTIN_TYPE(keys[0]) is cast(Any, _BUILTIN_TUPLE)
|
|
158
|
+
and _BUILTIN_LEN(keys[0]) == 2
|
|
159
|
+
)
|
|
160
|
+
|
|
149
161
|
def select_key(row: Any) -> Any:
|
|
150
|
-
|
|
162
|
+
if single and tuple is _BUILTIN_TUPLE:
|
|
163
|
+
try:
|
|
164
|
+
selected_key = keys[0][1](row)
|
|
165
|
+
except _BUILTIN_STOP_ITERATION as error:
|
|
166
|
+
raise _BUILTIN_RUNTIME_ERROR("generator raised StopIteration") from error
|
|
167
|
+
if len is _BUILTIN_LEN:
|
|
168
|
+
return selected_key
|
|
169
|
+
selected = (selected_key,)
|
|
170
|
+
else:
|
|
171
|
+
selected = tuple(selector(row) for _name, selector in keys)
|
|
151
172
|
return selected[0] if len(selected) == 1 else selected
|
|
152
173
|
|
|
153
174
|
cursor = _SortedCursor(source, select_key, side="input")
|
|
@@ -8,9 +8,10 @@ up those local resources themselves.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
from collections import deque
|
|
11
|
-
from collections.abc import Callable, Iterator
|
|
11
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
12
12
|
from concurrent.futures import (
|
|
13
13
|
FIRST_COMPLETED,
|
|
14
|
+
Executor,
|
|
14
15
|
Future,
|
|
15
16
|
ProcessPoolExecutor,
|
|
16
17
|
ThreadPoolExecutor,
|
|
@@ -86,29 +87,30 @@ def _map_parallel(iterator: Iterator[Any], operation: ParallelMapOp) -> Iterator
|
|
|
86
87
|
At most buffer futures remain queued. Ordered mode awaits futures in submission
|
|
87
88
|
order; unordered mode yields completed sets as they become available. Callback
|
|
88
89
|
failures propagate through Future.result, and early exit cancels queued work
|
|
89
|
-
before waiting for
|
|
90
|
+
before waiting for this map's running callbacks. Only an owned pool is shut down.
|
|
90
91
|
"""
|
|
91
|
-
executor =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
max_workers=operation.workers
|
|
96
|
-
|
|
92
|
+
executor = operation.executor
|
|
93
|
+
owned = executor is None
|
|
94
|
+
if executor is None:
|
|
95
|
+
executor = (
|
|
96
|
+
ThreadPoolExecutor(max_workers=operation.workers)
|
|
97
|
+
if operation.backend == "thread"
|
|
98
|
+
else ProcessPoolExecutor(
|
|
99
|
+
max_workers=operation.workers,
|
|
100
|
+
mp_context=get_context("spawn"),
|
|
101
|
+
)
|
|
97
102
|
)
|
|
98
|
-
)
|
|
99
103
|
if operation.ordered:
|
|
100
104
|
pending: deque[Future[Any]] = deque()
|
|
101
105
|
try:
|
|
102
106
|
for item in iterator:
|
|
103
107
|
pending.append(executor.submit(operation.function, item))
|
|
104
108
|
if len(pending) >= operation.buffer:
|
|
105
|
-
yield pending
|
|
109
|
+
yield _pop_parallel_result(pending)
|
|
106
110
|
while pending:
|
|
107
|
-
yield pending
|
|
111
|
+
yield _pop_parallel_result(pending)
|
|
108
112
|
finally:
|
|
109
|
-
|
|
110
|
-
future.cancel()
|
|
111
|
-
executor.shutdown(wait=True, cancel_futures=True)
|
|
113
|
+
_close_parallel_work(executor, pending, owned=owned)
|
|
112
114
|
return
|
|
113
115
|
|
|
114
116
|
pending_set: set[Future[Any]] = set()
|
|
@@ -124,9 +126,30 @@ def _map_parallel(iterator: Iterator[Any], operation: ParallelMapOp) -> Iterator
|
|
|
124
126
|
for future in done:
|
|
125
127
|
yield future.result()
|
|
126
128
|
finally:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
_close_parallel_work(executor, pending_set, owned=owned)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _pop_parallel_result(pending: deque[Future[Any]]) -> Any:
|
|
133
|
+
"""Keep the awaited future registered until its result settles."""
|
|
134
|
+
result = pending[0].result()
|
|
135
|
+
pending.popleft()
|
|
136
|
+
return result
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _close_parallel_work(
|
|
140
|
+
executor: Executor, futures: Iterable[Future[Any]], *, owned: bool
|
|
141
|
+
) -> None:
|
|
142
|
+
"""Cancel this map's queued work and wait without taking ownership of a borrowed pool."""
|
|
143
|
+
pending = tuple(futures)
|
|
144
|
+
for future in pending:
|
|
145
|
+
future.cancel()
|
|
146
|
+
if owned:
|
|
129
147
|
executor.shutdown(wait=True, cancel_futures=True)
|
|
148
|
+
return
|
|
149
|
+
running = tuple(future for future in pending if not future.cancelled())
|
|
150
|
+
if running:
|
|
151
|
+
# Waiting does not surface unconsumed callback errors or swallow caller interrupts.
|
|
152
|
+
wait(running)
|
|
130
153
|
|
|
131
154
|
|
|
132
155
|
def _tap(iterator: Iterator[Any], operation: TapOp) -> Iterator[Any]:
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
|
|
5
6
|
from dataclasses import dataclass
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
@@ -24,6 +25,17 @@ from .sync import FilterOp, MapOp, Operation, ParallelMapOp, TapOp
|
|
|
24
25
|
_FUSABLE = (MapOp, FilterOp, TapOp)
|
|
25
26
|
|
|
26
27
|
|
|
28
|
+
def _parallel_backend(operation: ParallelMapOp) -> str:
|
|
29
|
+
"""Describe the executor actually used, including caller-owned pools."""
|
|
30
|
+
if operation.executor is None:
|
|
31
|
+
return operation.backend
|
|
32
|
+
if isinstance(operation.executor, ThreadPoolExecutor):
|
|
33
|
+
return "thread"
|
|
34
|
+
if isinstance(operation.executor, ProcessPoolExecutor):
|
|
35
|
+
return "process"
|
|
36
|
+
return "executor"
|
|
37
|
+
|
|
38
|
+
|
|
27
39
|
def _python_stages(operations: tuple[Operation, ...]) -> list[dict[str, Any]]:
|
|
28
40
|
"""Return Python stages, fusing adjacent map/filter/tap nodes."""
|
|
29
41
|
stages: list[dict[str, Any]] = []
|
|
@@ -49,7 +61,9 @@ def _python_stages(operations: tuple[Operation, ...]) -> list[dict[str, Any]]:
|
|
|
49
61
|
stages.append(
|
|
50
62
|
{
|
|
51
63
|
"engine": (
|
|
52
|
-
|
|
64
|
+
_parallel_backend(operation)
|
|
65
|
+
if isinstance(operation, ParallelMapOp)
|
|
66
|
+
else "python"
|
|
53
67
|
),
|
|
54
68
|
"operations": [operation.name],
|
|
55
69
|
"fused": False,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from collections.abc import Callable
|
|
6
|
+
from concurrent.futures import Executor
|
|
6
7
|
from dataclasses import dataclass
|
|
7
8
|
from os import PathLike
|
|
8
9
|
from typing import Any, Literal
|
|
@@ -22,6 +23,7 @@ class ParallelSettings:
|
|
|
22
23
|
backend: ParallelBackend
|
|
23
24
|
ordered: bool
|
|
24
25
|
buffer: int
|
|
26
|
+
executor: Executor | None = None
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
@dataclass(frozen=True, slots=True)
|
|
@@ -42,6 +44,7 @@ class ParallelMapOp:
|
|
|
42
44
|
ordered: bool
|
|
43
45
|
buffer: int
|
|
44
46
|
name: str = "map_parallel"
|
|
47
|
+
executor: Executor | None = None
|
|
45
48
|
|
|
46
49
|
|
|
47
50
|
@dataclass(frozen=True, slots=True)
|
|
@@ -271,7 +271,11 @@ class SpillStore:
|
|
|
271
271
|
yield from self.read_path(run.path)
|
|
272
272
|
|
|
273
273
|
def read_path(self, path: Path) -> Iterator[object]:
|
|
274
|
-
"""Read a
|
|
274
|
+
"""Read a query-owned framed path through tracked I/O.
|
|
275
|
+
|
|
276
|
+
Callers supply paths produced by this store. This reader does not validate
|
|
277
|
+
registration or make pickle safe for externally supplied files.
|
|
278
|
+
"""
|
|
275
279
|
from ..runtime.failpoints import hit
|
|
276
280
|
from ..runtime.resources import _add_cleanup_failure
|
|
277
281
|
|
|
@@ -6,7 +6,7 @@ import asyncio
|
|
|
6
6
|
import operator
|
|
7
7
|
import os
|
|
8
8
|
from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable, Iterable
|
|
9
|
-
from typing import Any, Generic, TypeVar, cast
|
|
9
|
+
from typing import Any, Generic, TypeVar, cast, overload
|
|
10
10
|
|
|
11
11
|
from ..expressions.selectors import Selector, compile_selector
|
|
12
12
|
from ..planning.async_ import (
|
|
@@ -340,6 +340,28 @@ class AsyncFlow(AsyncFlowTerminalsMixin[T], Generic[T]):
|
|
|
340
340
|
)
|
|
341
341
|
)
|
|
342
342
|
|
|
343
|
+
@overload
|
|
344
|
+
def map_async(
|
|
345
|
+
self,
|
|
346
|
+
function: Callable[[T], Awaitable[R]],
|
|
347
|
+
*,
|
|
348
|
+
concurrency: int = 8,
|
|
349
|
+
ordered: bool = True,
|
|
350
|
+
timeout: float | None = None,
|
|
351
|
+
buffer: int | None = None,
|
|
352
|
+
) -> AsyncFlow[R]: ...
|
|
353
|
+
|
|
354
|
+
@overload
|
|
355
|
+
def map_async(
|
|
356
|
+
self,
|
|
357
|
+
function: Callable[[T], R | Awaitable[R]],
|
|
358
|
+
*,
|
|
359
|
+
concurrency: int = 8,
|
|
360
|
+
ordered: bool = True,
|
|
361
|
+
timeout: float | None = None,
|
|
362
|
+
buffer: int | None = None,
|
|
363
|
+
) -> AsyncFlow[R]: ...
|
|
364
|
+
|
|
343
365
|
def map_async(
|
|
344
366
|
self,
|
|
345
367
|
function: Callable[[T], R | Awaitable[R]],
|
|
@@ -353,12 +375,14 @@ class AsyncFlow(AsyncFlowTerminalsMixin[T], Generic[T]):
|
|
|
353
375
|
|
|
354
376
|
Concurrency is bounded. Ordered mode delays later completed results until earlier inputs
|
|
355
377
|
finish, while the buffer permits completed work to refill active mapper slots.
|
|
378
|
+
Callbacks run on the event loop. Timeout and cancellation are cooperative and cannot
|
|
379
|
+
interrupt a callback that blocks the loop.
|
|
356
380
|
|
|
357
381
|
Args:
|
|
358
382
|
function: Sync or async mapper called once for each source item.
|
|
359
383
|
concurrency: Maximum mapper calls in flight.
|
|
360
384
|
ordered: Emit in source order when true, otherwise in task-completion order.
|
|
361
|
-
timeout: Optional per-item
|
|
385
|
+
timeout: Optional cooperative per-item timeout while awaiting the mapper result.
|
|
362
386
|
buffer: Maximum submitted results not yet emitted, or twice ``concurrency`` by default.
|
|
363
387
|
|
|
364
388
|
Returns:
|
|
@@ -379,6 +403,12 @@ class AsyncFlow(AsyncFlowTerminalsMixin[T], Generic[T]):
|
|
|
379
403
|
operation = _MapAsync(function, concurrency, ordered, timeout, buffer)
|
|
380
404
|
return cast(AsyncFlow[R], self._append(operation))
|
|
381
405
|
|
|
406
|
+
@overload
|
|
407
|
+
def map(self, function: Callable[[T], Awaitable[R]]) -> AsyncFlow[R]: ...
|
|
408
|
+
|
|
409
|
+
@overload
|
|
410
|
+
def map(self, function: Callable[[T], R | Awaitable[R]]) -> AsyncFlow[R]: ...
|
|
411
|
+
|
|
382
412
|
def map(self, function: Callable[[T], R | Awaitable[R]]) -> AsyncFlow[R]:
|
|
383
413
|
"""Apply a synchronous or asynchronous function to each item in order.
|
|
384
414
|
|
|
@@ -912,6 +942,68 @@ class AsyncFlow(AsyncFlowTerminalsMixin[T], Generic[T]):
|
|
|
912
942
|
|
|
913
943
|
batch = chunk
|
|
914
944
|
|
|
945
|
+
@overload
|
|
946
|
+
def map_batches(
|
|
947
|
+
self,
|
|
948
|
+
function: Callable[[tuple[T, ...]], Awaitable[R]],
|
|
949
|
+
*,
|
|
950
|
+
size: int,
|
|
951
|
+
concurrency: int = 1,
|
|
952
|
+
ordered: bool = True,
|
|
953
|
+
timeout: float | None = None,
|
|
954
|
+
buffer: int | None = None,
|
|
955
|
+
) -> AsyncFlow[R]: ...
|
|
956
|
+
|
|
957
|
+
@overload
|
|
958
|
+
def map_batches(
|
|
959
|
+
self,
|
|
960
|
+
function: Callable[[tuple[T, ...]], R | Awaitable[R]],
|
|
961
|
+
*,
|
|
962
|
+
size: int,
|
|
963
|
+
concurrency: int = 1,
|
|
964
|
+
ordered: bool = True,
|
|
965
|
+
timeout: float | None = None,
|
|
966
|
+
buffer: int | None = None,
|
|
967
|
+
) -> AsyncFlow[R]: ...
|
|
968
|
+
|
|
969
|
+
def map_batches(
|
|
970
|
+
self,
|
|
971
|
+
function: Callable[[tuple[T, ...]], R | Awaitable[R]],
|
|
972
|
+
*,
|
|
973
|
+
size: int,
|
|
974
|
+
concurrency: int = 1,
|
|
975
|
+
ordered: bool = True,
|
|
976
|
+
timeout: float | None = None,
|
|
977
|
+
buffer: int | None = None,
|
|
978
|
+
) -> AsyncFlow[R]:
|
|
979
|
+
"""Apply a sync or async function to consecutive tuples, one result per batch.
|
|
980
|
+
|
|
981
|
+
Batches are lazy and include a shorter final batch. Empty input does not call the
|
|
982
|
+
function. Results stay intact; use `flat_map(iter)` to expand returned iterables.
|
|
983
|
+
Awaitable results are resolved before emission. Early exit cancels pending callbacks
|
|
984
|
+
and closes the source through the existing async map scheduler.
|
|
985
|
+
Callbacks run on the event loop: timeout and cancellation are cooperative and cannot
|
|
986
|
+
interrupt a callback that blocks the loop.
|
|
987
|
+
|
|
988
|
+
Args:
|
|
989
|
+
function: Receives up to `size` consecutive items and returns a result or awaitable.
|
|
990
|
+
size: Maximum input items per batch.
|
|
991
|
+
concurrency: Maximum batch callbacks in flight; one by default.
|
|
992
|
+
ordered: Emit in batch order when true, otherwise in completion order.
|
|
993
|
+
timeout: Optional cooperative per-batch timeout while awaiting the callback.
|
|
994
|
+
buffer: Maximum submitted batches not yet emitted; twice concurrency by default.
|
|
995
|
+
|
|
996
|
+
Returns:
|
|
997
|
+
A lazy async flow containing one resolved result per batch.
|
|
998
|
+
|
|
999
|
+
Raises:
|
|
1000
|
+
TypeError: If size does not support the integer index protocol.
|
|
1001
|
+
ValueError: If size, concurrency, buffer, or timeout is not positive.
|
|
1002
|
+
"""
|
|
1003
|
+
return self.chunk(size).map_async(
|
|
1004
|
+
function, concurrency=concurrency, ordered=ordered, timeout=timeout, buffer=buffer
|
|
1005
|
+
)
|
|
1006
|
+
|
|
915
1007
|
def window(self, size: int, *, step: int = 1) -> AsyncFlow[tuple[T, ...]]:
|
|
916
1008
|
"""Emit sliding tuples of size with the requested step.
|
|
917
1009
|
|