fpstreams 2.3.0__tar.gz → 2.4.1__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.3.0 → fpstreams-2.4.1}/CHANGELOG.md +136 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/PKG-INFO +29 -12
- {fpstreams-2.3.0 → fpstreams-2.4.1}/README.md +28 -11
- {fpstreams-2.3.0 → fpstreams-2.4.1}/pyproject.toml +1 -1
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/Cargo.lock +1 -1
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/Cargo.toml +1 -1
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/__init__.py +7 -2
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/collecting/aggregation.py +152 -9
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/collecting/collector.py +47 -12
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/collecting/program.py +13 -4
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/collecting/reducer.py +23 -12
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/arrow.py +180 -48
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/async_iterators.py +78 -1
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/async_ops.py +4 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/async_scheduler.py +11 -2
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/relational/__init__.py +31 -553
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/relational/arrow_group.py +3 -1
- fpstreams-2.4.1/src/fpstreams/execution/relational/collector_group.py +203 -0
- fpstreams-2.4.1/src/fpstreams/execution/relational/native_group.py +428 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/sync_ops.py +58 -1
- fpstreams-2.4.1/src/fpstreams/execution/window_reduce.py +135 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/physical/async_plan.py +2 -1
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/arrow.py +11 -16
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/async_.py +20 -2
- fpstreams-2.4.1/src/fpstreams/planning/compiler.py +424 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/explain.py +6 -0
- fpstreams-2.4.1/src/fpstreams/planning/relational.py +320 -0
- fpstreams-2.3.0/src/fpstreams/planning/compiler.py → fpstreams-2.4.1/src/fpstreams/planning/relational_specs.py +4 -652
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/semantic_rules.py +25 -1
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/sync.py +22 -2
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/__init__.py +2 -1
- fpstreams-2.4.1/src/fpstreams/runtime/executor.py +118 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/limits.py +7 -2
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/report.py +100 -5
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/tasks.py +5 -2
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/storage/__init__.py +3 -1
- fpstreams-2.4.1/src/fpstreams/storage/checkpoint.py +165 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/storage/codec.py +2 -0
- fpstreams-2.4.1/src/fpstreams/streams/_flow_frequencies.py +203 -0
- fpstreams-2.4.1/src/fpstreams/streams/_flow_materialize.py +406 -0
- fpstreams-2.4.1/src/fpstreams/streams/_flow_numeric.py +354 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/streams/async_flow.py +179 -10
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/streams/async_terminals.py +25 -5
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/streams/flow.py +142 -14
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/streams/flow_terminals.py +206 -788
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/streams/pairs.py +9 -5
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/arrow.py +156 -55
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/grouped.py +4 -6
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/io.py +35 -10
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/records.py +37 -1
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/rows.py +16 -47
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/testing.py +37 -22
- {fpstreams-2.3.0 → fpstreams-2.4.1}/LICENSE +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/build.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/common.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/float/affine_pair.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/float/endpoints.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/float.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/integer/endpoints.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/integer.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/lib.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/numeric_mean.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/numpy_export.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/numpy_group/buffer.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/numpy_group.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/pair/expr.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/pair/prefix.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/pair/row_filter.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/pair/unique.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/pair/value_filter.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/pair/value_map.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/pair.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/pivot.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/records.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational/adapters/namedtuple.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational/adapters.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational/global_numeric.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational/group_numeric.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational/group_pair_expr.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational/join_callable/many.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational/join_callable/unique.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational/join_callable.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational/join_exact.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational/join_i64.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational_fixed/composite.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational_fixed/global_multi/same_field.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational_fixed/global_multi.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational_fixed/group_multi.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational_fixed/single.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/relational_fixed.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/scalar_sort.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/scalar_unique.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/select.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/adapters.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/group_exact/pairs.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/group_exact/rows.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/group_exact.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/group_fixed.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/join_callable/behavior.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/join_callable/direct.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/join_callable.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/join_exact.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/numeric/buffers.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/numeric/kernels.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/numeric/reductions.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/numeric.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests/numpy_group.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/tests.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/unnest.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/rust/src/unpivot.rs +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/_native.pyi +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/_provenance.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/aggregate.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/async_flow.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/collecting/__init__.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/collecting/_collector_base.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/collecting/aggregate_program.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/collecting/statistics.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/collectors.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/column.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/errors.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/exceptions.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/__init__.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/_pair_aggregate.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/_pair_dict.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/_pair_row_filter.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/_rows_fusion.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/_scalar_fusion.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/async_map.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/async_merge.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/async_prefetch.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/async_queue.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/async_timers.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/native.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/numpy_group.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/numpy_prefix.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/physical.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/relational/arrow_global.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/relational/arrow_group_rows.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/relational/join.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/sorted_streams.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/sorting.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/execution/sync.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/expr.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/expressions/__init__.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/expressions/_codegen.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/expressions/_row_codegen.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/expressions/program.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/expressions/row.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/expressions/row_eval.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/expressions/row_ir.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/expressions/scalar.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/expressions/selectors.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/expressions/typed_ir.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/functional.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/gather.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/io_safety.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/option.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/physical/__init__.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/physical/compiled.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/physical/kernel_cache.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/physical/plan.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/physical/relational.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/__init__.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/_pair_stages.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/arrow_source.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/async_utils.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/gather.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/logical.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/native.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/numpy.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/pair_i64_expression.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/plan_cache.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/semantic_analyzer.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/semantics.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/planning/source.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/primitives/__init__.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/primitives/option.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/primitives/result.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/py.typed +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/result.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/_distinct.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/failpoints.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/files.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/iterators.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/metrics.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/query.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/resources.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/runtime/spill.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/storage/spill_store.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/streams/__init__.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/streams/_async_identity.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/streams/_flow_structural_list.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/streams/_flow_unique_list.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/__init__.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/_text_sources.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/dataframe.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/factory.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/join.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/numpy.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/polars.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/spill.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/spill_io.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/spill_limits.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/sql.py +0 -0
- {fpstreams-2.3.0 → fpstreams-2.4.1}/src/fpstreams/tabular/sqlite_sink.py +0 -0
|
@@ -3,6 +3,142 @@
|
|
|
3
3
|
This file records user-visible and compatibility-relevant changes in fpstreams 2,
|
|
4
4
|
including changed defaults.
|
|
5
5
|
|
|
6
|
+
## Unreleased
|
|
7
|
+
|
|
8
|
+
## 2.4.1 - 2026-10-18
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Separate relational candidate recognition and tree compilation from the general
|
|
13
|
+
compiler. Native grouping, Collector grouping, and synchronous terminal helpers
|
|
14
|
+
now live in domain modules; public interfaces and execution rules stay the same.
|
|
15
|
+
|
|
16
|
+
## 2.4.0 - 2026-10-18
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `ExecutionReport.to_dict()` and `ExecutionEvent.to_dict()` export plain
|
|
21
|
+
metadata. Report and plan dictionaries carry an independent schema version;
|
|
22
|
+
reports include metric units, query-owned resource scope, and event limits.
|
|
23
|
+
|
|
24
|
+
- `Flow.collapse(predicate, collector)` and its async equivalent reduce each
|
|
25
|
+
adjacent run with existing Collector state. Completion stops further steps
|
|
26
|
+
within a run while predicates continue to find its boundary. Collector hooks
|
|
27
|
+
remain synchronous, and awaitable finisher results are emitted as data.
|
|
28
|
+
|
|
29
|
+
- `Flow.window_reduce(size, combine, step=1)` and `AsyncFlow.window_reduce(...)`
|
|
30
|
+
reuse ordered partial aggregates across count-based windows. The synchronous
|
|
31
|
+
combine function must be associative and nonmutating. Cached entries are
|
|
32
|
+
bounded by `size + 1`; byte sizes and individual stack transfers are not.
|
|
33
|
+
Nonoverlapping windows use a running fold without retaining a raw window buffer.
|
|
34
|
+
|
|
35
|
+
- `flow.unfold(seed, function)` and `aflow.unfold(...)` generate values lazily
|
|
36
|
+
from `(value, next_state)` transitions until the callback returns `None`.
|
|
37
|
+
Class constructors expose the same operation; async transitions may be awaitable.
|
|
38
|
+
- `Flow.map_accum(initial, function)` and `AsyncFlow.map_accum(...)` thread
|
|
39
|
+
state through ordered callbacks returning `(next_state, output)`, emitting
|
|
40
|
+
one output per input through the existing gather/scan execution paths.
|
|
41
|
+
- `agg.min_by(selector)` and `agg.max_by(selector)` retain a whole winning
|
|
42
|
+
item in one pass. Ties keep the first item and empty input returns `None`;
|
|
43
|
+
the factories reuse existing grouped, async, and spill aggregation.
|
|
44
|
+
- `agg.top(count, key=...)` and `agg.bottom(count, key=...)` retain a bounded
|
|
45
|
+
list of whole items per group, ordered by key with stable ties. They compose
|
|
46
|
+
with the existing global, async, sorted, and spill aggregation paths.
|
|
47
|
+
- `run_with_report()` includes up to 64 immutable plan and route events in
|
|
48
|
+
`report.events`, with `events_truncated` marking further observations. Child
|
|
49
|
+
plans keep their own labels without replacing the outer route. Flow, Rows,
|
|
50
|
+
Pairs, and AsyncFlow use the same recorder.
|
|
51
|
+
- `BoundedExecutor(executor, max_pending=...)` shares one unfinished-task budget
|
|
52
|
+
across independent queries. It accepts existing thread or process pools,
|
|
53
|
+
releases capacity on completion or cancellation, and keeps the borrowed pool open.
|
|
54
|
+
- `FileCheckpoint(directory, version=...)` stores verified outputs for stable
|
|
55
|
+
file partitions. Each successful callback publishes an output file and its
|
|
56
|
+
metadata together; later runs verify input and output hashes before reuse.
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- `filter_map()` avoids intermediate tuple adaptation in synchronous pipelines
|
|
61
|
+
and the extra predicate callback in async serial fusion. It calls the mapper
|
|
62
|
+
once per input and keeps every result except `None`.
|
|
63
|
+
- Proven primitive Arrow comparison chains can share a selection mask,
|
|
64
|
+
filtering payload once or counting directly from the mask. Offline Optuna
|
|
65
|
+
tuning favored nonempty batches with at least one column in the measured shapes.
|
|
66
|
+
- Spill decoding releases encoded buffers before yielding decoded records
|
|
67
|
+
and releases completed decoded frames before reading the next frame.
|
|
68
|
+
|
|
69
|
+
- `Collectors.head()` and `tail()` accept integer-index objects, with matching
|
|
70
|
+
`SupportsIndex` annotations. Partitioned reducer execution and reducer-law
|
|
71
|
+
layouts use the same integer protocol.
|
|
72
|
+
- `Collectors.teeing()` can combine partial states when both downstream
|
|
73
|
+
collectors provide combiners. It keeps their live callbacks and carries no
|
|
74
|
+
inferred reducer laws or native metadata.
|
|
75
|
+
- Named `aggregate()` and `Pairs.aggregate_values()` accept any `Collector`,
|
|
76
|
+
including composed `Collectors` and existing `agg` factory results. Global,
|
|
77
|
+
async, grouped, sorted, and spill paths share the existing collector lifecycle.
|
|
78
|
+
- Multi-collector Python aggregation reuses its state-list references within
|
|
79
|
+
each row while retaining live callbacks and their declaration order.
|
|
80
|
+
- Retained primitive Arrow tables and record batches can keep their native
|
|
81
|
+
buffers when `arrow_batches(schema=...)` receives the matching output schema,
|
|
82
|
+
including metadata. Safe direct-field projections and primitive filters can
|
|
83
|
+
execute columnarly. Casts, Python callbacks, and unsupported sources retain
|
|
84
|
+
the existing conversion path.
|
|
85
|
+
- Arrow execution accepts chains of safe direct comparisons followed by a
|
|
86
|
+
direct projection. Parquet keeps only the leading comparison as an automatic
|
|
87
|
+
scan hint; all filters remain in the ordered residual program.
|
|
88
|
+
- Default local Parquet scans with one fragment can omit safe unused fields
|
|
89
|
+
from validation scans, retaining query dependencies and fields whose Python
|
|
90
|
+
conversion can fail. Sources with possible scanner casts retain the full
|
|
91
|
+
caller-visible projection before automatic filtering and projection.
|
|
92
|
+
|
|
93
|
+
### Fixed
|
|
94
|
+
|
|
95
|
+
- Query-wide task/file limits and task-scope limits reject noninteger values,
|
|
96
|
+
including NaN, before resource acquisition. Integer-index values are
|
|
97
|
+
normalized once during construction.
|
|
98
|
+
|
|
99
|
+
- `Collectors.first()` and `head(count)` preserve completed state when used
|
|
100
|
+
inside `grouping_by` or `partitioning_by`. Previously, `first()` returned the
|
|
101
|
+
last group item and `head()` could retain the whole group, even with a zero
|
|
102
|
+
count. Grouping adapters continue to read all input and step custom collectors.
|
|
103
|
+
- `Flow` and `AsyncFlow` validate `take`, `limit`, `drop`, and `skip` counts
|
|
104
|
+
through the integer index protocol before building the operation. Noninteger
|
|
105
|
+
values, including NaN and infinity, raise `TypeError` before opening the source.
|
|
106
|
+
This prevents async prefix operations from silently rounding counts or losing
|
|
107
|
+
their limit.
|
|
108
|
+
- `Flow.collect()` and `AsyncFlow.collect()` preserve a single streaming
|
|
109
|
+
collector's finished result type in type checkers. Named collectors return
|
|
110
|
+
a dictionary, while optional or dynamically forwarded arguments retain the
|
|
111
|
+
general result union. Runtime dispatch and validation are unchanged.
|
|
112
|
+
- `Collectors.head()` and `tail()` reject floats, including NaN and infinity,
|
|
113
|
+
at construction. Partitioned reducer execution validates `partition_size`
|
|
114
|
+
before creating state or opening input, preventing non-integer values from
|
|
115
|
+
silently removing partition boundaries.
|
|
116
|
+
- Partitioned reducer execution closes its input iterator on exhaustion and
|
|
117
|
+
failure, before merging states or finishing. Cleanup failures preserve an
|
|
118
|
+
active source or callback exception.
|
|
119
|
+
- Collector programs reject missing combiners before invoking any merge
|
|
120
|
+
callback, preventing avoidable partial mutations on invalid merge requests.
|
|
121
|
+
- Reducer law checks build independent states for each merge expression,
|
|
122
|
+
preventing in-place combiners from producing false identity or associativity
|
|
123
|
+
results. The input iterator closes before the examples are replayed.
|
|
124
|
+
- Unsafe Arrow batches evaluate filters in row order and stop when downstream
|
|
125
|
+
consumers stop, preserving Python exception order and early-exit behavior.
|
|
126
|
+
- Arrow prefix fallbacks reuse converted rows, so custom extension scalars
|
|
127
|
+
cross the Python conversion boundary once per input batch.
|
|
128
|
+
- Automatic Parquet hints preserve conversion errors in discarded fields and
|
|
129
|
+
rows, including casts across fragments and supplied dataset schemas.
|
|
130
|
+
Types whose Python conversion can fail are validated before automatic
|
|
131
|
+
filtering or projection; explicit source `columns` and `filter` still define
|
|
132
|
+
which values the source exposes.
|
|
133
|
+
- `scan_csv()` validates every caller-visible column before query projection,
|
|
134
|
+
preserving conversion errors in later CSV blocks. It opens the typed source
|
|
135
|
+
once; explicit source conversion options still control included columns.
|
|
136
|
+
- An explicit zero-column Arrow schema preserves the input row count in
|
|
137
|
+
`arrow_batches()` and `to_arrow()`, including partial final batches.
|
|
138
|
+
- Polars batch exports preserve the height of zero-column inputs. Parquet
|
|
139
|
+
writes reject nonempty rows without columns, preserving an existing target
|
|
140
|
+
instead of publishing a file that loses the input row count.
|
|
141
|
+
|
|
6
142
|
## 2.3.0 - 2026-10-15
|
|
7
143
|
|
|
8
144
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fpstreams
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.1
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -66,9 +66,10 @@ 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.
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
The current stable release is [2.4.1](https://github.com/steventimes/fpstreams/releases/tag/v2.4.1).
|
|
70
|
+
This maintenance release reorganizes planner and execution internals with no public API changes.
|
|
71
|
+
Version 2.4.0 added stateful pipeline operations, composed aggregation, shared execution
|
|
72
|
+
limits, file recovery, and versioned execution metadata. See the
|
|
72
73
|
[changelog](https://github.com/steventimes/fpstreams/blob/master/CHANGELOG.md) for its scope and the
|
|
73
74
|
[performance guide](https://steventimes.github.io/fpstreams/user-guide/performance/#process-mapping-and-explicit-batches)
|
|
74
75
|
for batch and executor examples. The
|
|
@@ -369,23 +370,39 @@ python_result = pipeline.with_engine("python").to_list()
|
|
|
369
370
|
native_result = pipeline.with_engine("native").to_list()
|
|
370
371
|
~~~
|
|
371
372
|
|
|
372
|
-
Use `run_with_report()` to
|
|
373
|
-
|
|
374
|
-
`flow([("a", 1)]).pairs().run_with_report("group_values")`.
|
|
373
|
+
Use `run_with_report()` to get a terminal's value, recorded route, and query-owned
|
|
374
|
+
resource counts in one execution. Pairs supports terminals such as
|
|
375
|
+
`flow([("a", 1)]).pairs().run_with_report("group_values")`. Version 2.4.0 also
|
|
376
|
+
includes up to 64 child plan and route observations in `report.events`. See the
|
|
375
377
|
[execution report guide](https://steventimes.github.io/fpstreams/user-guide/execution-reports/)
|
|
376
378
|
for supported terminals and the limits of route reporting.
|
|
377
379
|
|
|
380
|
+
In 2.4.0, `report.to_dict()` exports versioned metadata with metric units,
|
|
381
|
+
resource scope, and event limits. It excludes the terminal value and does not
|
|
382
|
+
rerun the query. See the
|
|
383
|
+
[metadata export guide](https://steventimes.github.io/fpstreams/user-guide/execution-reports/#metadata-export-unreleased)
|
|
384
|
+
for JSON storage and caller-managed OpenLineage integration. Report export
|
|
385
|
+
requires 2.4.0 or newer.
|
|
386
|
+
|
|
387
|
+
Version 2.4.0 also includes `BoundedExecutor` for a shared submission limit
|
|
388
|
+
across queries and `FileCheckpoint` for verified file-partition recovery. See
|
|
389
|
+
the [resource controls](https://steventimes.github.io/fpstreams/user-guide/performance/#share-a-submission-budget-unreleased)
|
|
390
|
+
and [file-partition recipe](https://steventimes.github.io/fpstreams/user-guide/analytics/#resume-file-partitions-unreleased)
|
|
391
|
+
for ownership, memory, and retry semantics. These additions require 2.4.0 or newer.
|
|
392
|
+
|
|
393
|
+
In 2.4.0, `flow.unfold` / `aflow.unfold` generate values from explicit state
|
|
394
|
+
transitions. `Flow.map_accum` and `AsyncFlow.map_accum` update state while mapping
|
|
395
|
+
each input to one output. See the
|
|
396
|
+
[state examples](https://steventimes.github.io/fpstreams/reference/flow/#state-driven-generation-and-mapping-unreleased)
|
|
397
|
+
for termination, tuple ordering, and mutable-state ownership. These APIs require
|
|
398
|
+
2.4.0 or newer.
|
|
399
|
+
|
|
378
400
|
A forced native plan raises `NativeUnsupportedError` if its complete types or
|
|
379
401
|
operations cannot run natively. In particular, the presence of an internal
|
|
380
402
|
native relational specialization does not make the complete relation eligible
|
|
381
403
|
for `with_engine("native")`. An unsupported forced relational plan fails before
|
|
382
404
|
claiming its one-shot sources; `auto` selects a legal fallback path.
|
|
383
405
|
|
|
384
|
-
[`run_with_report()`](https://steventimes.github.io/fpstreams/user-guide/execution-reports/)
|
|
385
|
-
returns a terminal's value, recorded route, and query-owned resource counts in
|
|
386
|
-
one execution. It records the outer plan and some direct paths; it does not
|
|
387
|
-
identify every internal kernel or runtime fallback.
|
|
388
|
-
|
|
389
406
|
Identity list and tuple materialization stays in Python. Large, supported integer
|
|
390
407
|
sums and numeric range reductions may use Rust in `auto` mode. `count()` uses a
|
|
391
408
|
known exact size in O(1) when no operation changes cardinality and the source is
|
|
@@ -11,9 +11,10 @@ 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.
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
The current stable release is [2.4.1](https://github.com/steventimes/fpstreams/releases/tag/v2.4.1).
|
|
15
|
+
This maintenance release reorganizes planner and execution internals with no public API changes.
|
|
16
|
+
Version 2.4.0 added stateful pipeline operations, composed aggregation, shared execution
|
|
17
|
+
limits, file recovery, and versioned execution metadata. See the
|
|
17
18
|
[changelog](https://github.com/steventimes/fpstreams/blob/master/CHANGELOG.md) for its scope and the
|
|
18
19
|
[performance guide](https://steventimes.github.io/fpstreams/user-guide/performance/#process-mapping-and-explicit-batches)
|
|
19
20
|
for batch and executor examples. The
|
|
@@ -314,23 +315,39 @@ python_result = pipeline.with_engine("python").to_list()
|
|
|
314
315
|
native_result = pipeline.with_engine("native").to_list()
|
|
315
316
|
~~~
|
|
316
317
|
|
|
317
|
-
Use `run_with_report()` to
|
|
318
|
-
|
|
319
|
-
`flow([("a", 1)]).pairs().run_with_report("group_values")`.
|
|
318
|
+
Use `run_with_report()` to get a terminal's value, recorded route, and query-owned
|
|
319
|
+
resource counts in one execution. Pairs supports terminals such as
|
|
320
|
+
`flow([("a", 1)]).pairs().run_with_report("group_values")`. Version 2.4.0 also
|
|
321
|
+
includes up to 64 child plan and route observations in `report.events`. See the
|
|
320
322
|
[execution report guide](https://steventimes.github.io/fpstreams/user-guide/execution-reports/)
|
|
321
323
|
for supported terminals and the limits of route reporting.
|
|
322
324
|
|
|
325
|
+
In 2.4.0, `report.to_dict()` exports versioned metadata with metric units,
|
|
326
|
+
resource scope, and event limits. It excludes the terminal value and does not
|
|
327
|
+
rerun the query. See the
|
|
328
|
+
[metadata export guide](https://steventimes.github.io/fpstreams/user-guide/execution-reports/#metadata-export-unreleased)
|
|
329
|
+
for JSON storage and caller-managed OpenLineage integration. Report export
|
|
330
|
+
requires 2.4.0 or newer.
|
|
331
|
+
|
|
332
|
+
Version 2.4.0 also includes `BoundedExecutor` for a shared submission limit
|
|
333
|
+
across queries and `FileCheckpoint` for verified file-partition recovery. See
|
|
334
|
+
the [resource controls](https://steventimes.github.io/fpstreams/user-guide/performance/#share-a-submission-budget-unreleased)
|
|
335
|
+
and [file-partition recipe](https://steventimes.github.io/fpstreams/user-guide/analytics/#resume-file-partitions-unreleased)
|
|
336
|
+
for ownership, memory, and retry semantics. These additions require 2.4.0 or newer.
|
|
337
|
+
|
|
338
|
+
In 2.4.0, `flow.unfold` / `aflow.unfold` generate values from explicit state
|
|
339
|
+
transitions. `Flow.map_accum` and `AsyncFlow.map_accum` update state while mapping
|
|
340
|
+
each input to one output. See the
|
|
341
|
+
[state examples](https://steventimes.github.io/fpstreams/reference/flow/#state-driven-generation-and-mapping-unreleased)
|
|
342
|
+
for termination, tuple ordering, and mutable-state ownership. These APIs require
|
|
343
|
+
2.4.0 or newer.
|
|
344
|
+
|
|
323
345
|
A forced native plan raises `NativeUnsupportedError` if its complete types or
|
|
324
346
|
operations cannot run natively. In particular, the presence of an internal
|
|
325
347
|
native relational specialization does not make the complete relation eligible
|
|
326
348
|
for `with_engine("native")`. An unsupported forced relational plan fails before
|
|
327
349
|
claiming its one-shot sources; `auto` selects a legal fallback path.
|
|
328
350
|
|
|
329
|
-
[`run_with_report()`](https://steventimes.github.io/fpstreams/user-guide/execution-reports/)
|
|
330
|
-
returns a terminal's value, recorded route, and query-owned resource counts in
|
|
331
|
-
one execution. It records the outer plan and some direct paths; it does not
|
|
332
|
-
identify every internal kernel or runtime fallback.
|
|
333
|
-
|
|
334
351
|
Identity list and tuple materialization stays in Python. Large, supported integer
|
|
335
352
|
sums and numeric range reductions may use Rust in `auto` mode. `count()` uses a
|
|
336
353
|
known exact size in O(1) when no operation changes cardinality and the source is
|
|
@@ -27,7 +27,9 @@ from .errors import (
|
|
|
27
27
|
from .expressions import Expr, FExpr, RowExpr, coalesce, col, fitem, item, lit, when
|
|
28
28
|
from .functional import curry, pipe, retry
|
|
29
29
|
from .primitives import Err, Ok, Option, Result
|
|
30
|
-
from .runtime.
|
|
30
|
+
from .runtime.executor import BoundedExecutor
|
|
31
|
+
from .runtime.report import ExecutionEvent, ExecutionReport, ExecutionResult
|
|
32
|
+
from .storage.checkpoint import FileCheckpoint
|
|
31
33
|
from .streams import (
|
|
32
34
|
AsyncFlow,
|
|
33
35
|
AsyncStream,
|
|
@@ -42,7 +44,7 @@ from .streams import (
|
|
|
42
44
|
from .tabular import Rows, SpillLimits
|
|
43
45
|
from .tabular.factory import rows as rows
|
|
44
46
|
|
|
45
|
-
__version__ = "2.
|
|
47
|
+
__version__ = "2.4.1"
|
|
46
48
|
Stream = Flow
|
|
47
49
|
ParallelStream = Flow
|
|
48
50
|
|
|
@@ -50,6 +52,7 @@ __all__ = [
|
|
|
50
52
|
"Aggregator",
|
|
51
53
|
"AsyncFlow",
|
|
52
54
|
"AsyncStream",
|
|
55
|
+
"BoundedExecutor",
|
|
53
56
|
"BufferLimitError",
|
|
54
57
|
"Collector",
|
|
55
58
|
"Collectors",
|
|
@@ -58,10 +61,12 @@ __all__ = [
|
|
|
58
61
|
"EmptyFlowError",
|
|
59
62
|
"EmptyInputPolicy",
|
|
60
63
|
"Err",
|
|
64
|
+
"ExecutionEvent",
|
|
61
65
|
"ExecutionReport",
|
|
62
66
|
"ExecutionResult",
|
|
63
67
|
"Expr",
|
|
64
68
|
"FExpr",
|
|
69
|
+
"FileCheckpoint",
|
|
65
70
|
"Flow",
|
|
66
71
|
"FlowConsumedError",
|
|
67
72
|
"FlowError",
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import heapq
|
|
6
|
+
import operator
|
|
5
7
|
from collections.abc import Callable, Iterable, Mapping
|
|
6
8
|
from dataclasses import dataclass
|
|
7
9
|
from types import CodeType, FunctionType, MappingProxyType
|
|
@@ -12,6 +14,8 @@ from ..runtime._distinct import DistinctKey, HashFailure
|
|
|
12
14
|
from . import _collector_base
|
|
13
15
|
from ._collector_base import Aggregator as Aggregator
|
|
14
16
|
from .collector import (
|
|
17
|
+
Collector,
|
|
18
|
+
CollectorItems,
|
|
15
19
|
finish_collectors,
|
|
16
20
|
initialize_collectors,
|
|
17
21
|
step_collectors,
|
|
@@ -561,7 +565,9 @@ _CANONICAL_GROUP_SELECTOR_MATCHES_STEP = _group_selector_matches_step
|
|
|
561
565
|
_CANONICAL_GROUP_SELECTOR_MATCHES_STEP_CODE = _group_selector_matches_step.__code__
|
|
562
566
|
|
|
563
567
|
|
|
564
|
-
def native_group_aggregation(
|
|
568
|
+
def native_group_aggregation(
|
|
569
|
+
aggregation: Collector[Any, Any, Any],
|
|
570
|
+
) -> NativeGroupAggregation | None:
|
|
565
571
|
"""Recognize a factory result without trusting user-constructible native metadata."""
|
|
566
572
|
if (
|
|
567
573
|
_GROUP_STEP_KINDS is not _CANONICAL_GROUP_STEP_KINDS
|
|
@@ -620,7 +626,7 @@ def native_group_aggregation(aggregation: Aggregator) -> NativeGroupAggregation
|
|
|
620
626
|
)
|
|
621
627
|
|
|
622
628
|
|
|
623
|
-
def project_count_aggregation(aggregation:
|
|
629
|
+
def project_count_aggregation(aggregation: Collector[Any, Any, Any]) -> bool:
|
|
624
630
|
"""Recognize only the constant-size count reducer built by :meth:`agg.count`.
|
|
625
631
|
|
|
626
632
|
Native metadata and reducer laws are user-constructible, so neither is enough
|
|
@@ -647,7 +653,7 @@ def project_count_aggregation(aggregation: Aggregator) -> bool:
|
|
|
647
653
|
)
|
|
648
654
|
|
|
649
655
|
|
|
650
|
-
def native_aggregation_is_live(aggregation:
|
|
656
|
+
def native_aggregation_is_live(aggregation: Collector[Any, Any, Any]) -> bool:
|
|
651
657
|
"""Recognize an unchanged project-created whole-value native aggregation."""
|
|
652
658
|
if project_count_aggregation(aggregation):
|
|
653
659
|
return True
|
|
@@ -709,14 +715,16 @@ class _DistinctState:
|
|
|
709
715
|
return len(self.hashable) + len(self.unhashable)
|
|
710
716
|
|
|
711
717
|
|
|
712
|
-
AggregationItems =
|
|
718
|
+
AggregationItems = CollectorItems
|
|
713
719
|
|
|
714
720
|
|
|
715
|
-
def prepare_aggregations(
|
|
721
|
+
def prepare_aggregations(
|
|
722
|
+
aggregations: Mapping[str, Collector[Any, Any, Any]],
|
|
723
|
+
) -> AggregationItems:
|
|
716
724
|
"""Validate named aggregators and freeze mapping order as `(name, aggregator)` pairs.
|
|
717
725
|
|
|
718
|
-
The mapping must be nonempty, names must be truthy, and every value must be
|
|
719
|
-
:class:`
|
|
726
|
+
The mapping must be nonempty, names must be truthy, and every value must be a
|
|
727
|
+
:class:`Collector`, including aggregators built by :data:`agg`.
|
|
720
728
|
"""
|
|
721
729
|
if not aggregations:
|
|
722
730
|
raise ValueError("aggregate requires at least one named aggregation")
|
|
@@ -724,8 +732,8 @@ def prepare_aggregations(aggregations: Mapping[str, Aggregator]) -> AggregationI
|
|
|
724
732
|
for name, aggregation in items:
|
|
725
733
|
if not name:
|
|
726
734
|
raise ValueError("aggregate names cannot be empty")
|
|
727
|
-
if not isinstance(aggregation,
|
|
728
|
-
raise TypeError(f"aggregate {name!r} must be an Aggregator")
|
|
735
|
+
if not isinstance(aggregation, Collector):
|
|
736
|
+
raise TypeError(f"aggregate {name!r} must be an Aggregator or Collector")
|
|
729
737
|
return items
|
|
730
738
|
|
|
731
739
|
|
|
@@ -799,6 +807,75 @@ def finish_native_aggregations(
|
|
|
799
807
|
return result
|
|
800
808
|
|
|
801
809
|
|
|
810
|
+
def _extremum_by(selector: Selector, better: Callable[[Any, Any], bool]) -> Aggregator:
|
|
811
|
+
"""Retain a winning item and its once-selected key without comparing payloads."""
|
|
812
|
+
select = compile_selector(selector)
|
|
813
|
+
|
|
814
|
+
def step(state: Any, row: Any) -> Any:
|
|
815
|
+
key = select(row)
|
|
816
|
+
return (key, row) if state is _MISSING or better(key, state[0]) else state
|
|
817
|
+
|
|
818
|
+
return Aggregator(
|
|
819
|
+
lambda: _MISSING,
|
|
820
|
+
step,
|
|
821
|
+
lambda state: None if state is _MISSING else state[1],
|
|
822
|
+
)
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
@dataclass(slots=True, eq=False)
|
|
826
|
+
class _RankedItem:
|
|
827
|
+
"""Order heap entries by key and encounter index, never by their payload."""
|
|
828
|
+
|
|
829
|
+
key: Any
|
|
830
|
+
index: int
|
|
831
|
+
value: Any
|
|
832
|
+
largest: bool
|
|
833
|
+
|
|
834
|
+
def __lt__(self, other: _RankedItem) -> bool:
|
|
835
|
+
if self.key is other.key or self.key == other.key:
|
|
836
|
+
return self.index > other.index
|
|
837
|
+
return bool(self.key < other.key if self.largest else other.key < self.key)
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
@dataclass(slots=True)
|
|
841
|
+
class _RankedState:
|
|
842
|
+
"""Retain only the requested winners and the next encounter index."""
|
|
843
|
+
|
|
844
|
+
heap: list[_RankedItem]
|
|
845
|
+
index: int = 0
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
def _ranked(count: int, key: Selector | None, *, largest: bool) -> Aggregator:
|
|
849
|
+
size = operator.index(count)
|
|
850
|
+
if size < 0:
|
|
851
|
+
raise ValueError("count must be non-negative")
|
|
852
|
+
select = _identity if key is None else compile_selector(key)
|
|
853
|
+
if size == 0:
|
|
854
|
+
return Aggregator(list, lambda state, _row: state, done=lambda _state: True)
|
|
855
|
+
|
|
856
|
+
def step(state: _RankedState, row: Any) -> _RankedState:
|
|
857
|
+
selected = select(row)
|
|
858
|
+
index = state.index
|
|
859
|
+
state.index += 1
|
|
860
|
+
if len(state.heap) < size:
|
|
861
|
+
heapq.heappush(state.heap, _RankedItem(selected, index, row, largest))
|
|
862
|
+
else:
|
|
863
|
+
worst = state.heap[0]
|
|
864
|
+
# Later ties cannot displace an earlier item. Avoid allocating an
|
|
865
|
+
# entry for every discarded row while keeping key comparisons live.
|
|
866
|
+
if worst.key is selected or worst.key == selected:
|
|
867
|
+
return state
|
|
868
|
+
if worst.key < selected if largest else selected < worst.key:
|
|
869
|
+
heapq.heapreplace(state.heap, _RankedItem(selected, index, row, largest))
|
|
870
|
+
return state
|
|
871
|
+
|
|
872
|
+
return Aggregator(
|
|
873
|
+
lambda: _RankedState([]),
|
|
874
|
+
step,
|
|
875
|
+
lambda state: [item.value for item in sorted(state.heap, reverse=True)],
|
|
876
|
+
)
|
|
877
|
+
|
|
878
|
+
|
|
802
879
|
class _AggFactory:
|
|
803
880
|
"""Construct built-in aggregators for `Flow.aggregate` and tabular grouping."""
|
|
804
881
|
|
|
@@ -1066,6 +1143,72 @@ class _AggFactory:
|
|
|
1066
1143
|
selector,
|
|
1067
1144
|
)
|
|
1068
1145
|
|
|
1146
|
+
def min_by(self, selector: Selector) -> Aggregator:
|
|
1147
|
+
"""Keep the first whole input item with the smallest selected key.
|
|
1148
|
+
|
|
1149
|
+
The selector runs once per item. Each subsequent key is compared with the
|
|
1150
|
+
retained key using `<`; ties keep the earlier item. One key and one item
|
|
1151
|
+
are retained per aggregation state. The source is fully consumed.
|
|
1152
|
+
|
|
1153
|
+
Args:
|
|
1154
|
+
selector: Required field, path, index, expression, or callable selecting a key.
|
|
1155
|
+
|
|
1156
|
+
Returns:
|
|
1157
|
+
An aggregator returning the original winning item, or `None` for empty input.
|
|
1158
|
+
Keys follow Python comparison rules, including errors and NaN behavior.
|
|
1159
|
+
"""
|
|
1160
|
+
return _extremum_by(selector, operator.lt)
|
|
1161
|
+
|
|
1162
|
+
def max_by(self, selector: Selector) -> Aggregator:
|
|
1163
|
+
"""Keep the first whole input item with the largest selected key.
|
|
1164
|
+
|
|
1165
|
+
The selector runs once per item. Each subsequent key is compared with the
|
|
1166
|
+
retained key using `>`; ties keep the earlier item. One key and one item
|
|
1167
|
+
are retained per aggregation state. The source is fully consumed.
|
|
1168
|
+
|
|
1169
|
+
Args:
|
|
1170
|
+
selector: Required field, path, index, expression, or callable selecting a key.
|
|
1171
|
+
|
|
1172
|
+
Returns:
|
|
1173
|
+
An aggregator returning the original winning item, or `None` for empty input.
|
|
1174
|
+
Keys follow Python comparison rules, including errors and NaN behavior.
|
|
1175
|
+
"""
|
|
1176
|
+
return _extremum_by(selector, operator.gt)
|
|
1177
|
+
|
|
1178
|
+
def top(self, count: int, *, key: Selector | None = None) -> Aggregator:
|
|
1179
|
+
"""Keep up to `count` whole items in descending key order.
|
|
1180
|
+
|
|
1181
|
+
Equal keys retain input order. The key runs once per item and payloads
|
|
1182
|
+
are never compared. Each state retains at most `count` items. Keys use
|
|
1183
|
+
Python heap and sorting comparisons; use consistently ordered keys.
|
|
1184
|
+
|
|
1185
|
+
Args:
|
|
1186
|
+
count: Non-negative integer number of items to retain.
|
|
1187
|
+
key: Optional selector; `None` compares whole items.
|
|
1188
|
+
|
|
1189
|
+
Returns:
|
|
1190
|
+
An aggregator returning a list, empty for empty input or zero count.
|
|
1191
|
+
Zero count does not invoke the key or pull an independent source.
|
|
1192
|
+
"""
|
|
1193
|
+
return _ranked(count, key, largest=True)
|
|
1194
|
+
|
|
1195
|
+
def bottom(self, count: int, *, key: Selector | None = None) -> Aggregator:
|
|
1196
|
+
"""Keep up to `count` whole items in ascending key order.
|
|
1197
|
+
|
|
1198
|
+
Equal keys retain input order. The key runs once per item and payloads
|
|
1199
|
+
are never compared. Each state retains at most `count` items. Keys use
|
|
1200
|
+
Python heap and sorting comparisons; use consistently ordered keys.
|
|
1201
|
+
|
|
1202
|
+
Args:
|
|
1203
|
+
count: Non-negative integer number of items to retain.
|
|
1204
|
+
key: Optional selector; `None` compares whole items.
|
|
1205
|
+
|
|
1206
|
+
Returns:
|
|
1207
|
+
An aggregator returning a list, empty for empty input or zero count.
|
|
1208
|
+
Zero count does not invoke the key or pull an independent source.
|
|
1209
|
+
"""
|
|
1210
|
+
return _ranked(count, key, largest=False)
|
|
1211
|
+
|
|
1069
1212
|
def first(self, selector: Selector | None = None) -> Aggregator:
|
|
1070
1213
|
"""Build an aggregator that stops after the first selected value.
|
|
1071
1214
|
|