sharedbox 0.8.0__tar.gz → 0.9.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.
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.github/workflows/ci.yaml +1 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/CHANGELOG.md +57 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/CLAUDE.md +32 -13
- {sharedbox-0.8.0 → sharedbox-0.9.0}/PKG-INFO +1 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/clean_up_segments.py +5 -1
- sharedbox-0.9.0/docs/examples/request_work_from_the_owner.py +95 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/send_items_through_a_stream.py +25 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/store_a_large_list.py +1 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/store_a_large_set_or_dict.py +2 -2
- sharedbox-0.9.0/docs/examples/store_a_value_with_its_time.py +49 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/store_arrays.py +20 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/field-types.md +1 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/glossary.md +6 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/attach-metadata.md +4 -3
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/clean-up-segments.md +14 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/index.md +5 -0
- sharedbox-0.9.0/docs/how-to/request-work-from-the-owner.md +158 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/send-items-through-a-stream.md +37 -2
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/store-a-large-list.md +2 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/store-a-large-set-or-dict.md +4 -3
- sharedbox-0.9.0/docs/how-to/store-a-value-with-its-time.md +80 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/store-arrays.md +42 -8
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/api/arrays.md +1 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/api/box.md +40 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/api/errors.md +25 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/api/index.md +1 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/segment-layout.md +274 -24
- {sharedbox-0.8.0 → sharedbox-0.9.0}/include/sharedbox/box.hpp +484 -22
- {sharedbox-0.8.0 → sharedbox-0.9.0}/include/sharedbox/core.hpp +292 -12
- {sharedbox-0.8.0 → sharedbox-0.9.0}/include/sharedbox/sharedbox_c.h +4 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/include/sharedbox/stream.hpp +85 -24
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/__init__.py +26 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_arrays.py +5 -5
- sharedbox-0.9.0/src/sharedbox/_attr.py +360 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_bounded.py +160 -28
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_box.py +79 -21
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_events.py +281 -40
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_exit.py +1 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_follow.py +1 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_layout.py +223 -16
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/module.cpp +122 -14
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/segment.cpp +143 -14
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/segment.hpp +22 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/stream.cpp +110 -72
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/types.cpp +245 -12
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/types.hpp +41 -6
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native.pyi +146 -11
- sharedbox-0.9.0/src/sharedbox/_request.py +385 -0
- sharedbox-0.9.0/src/sharedbox/_status.py +49 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_stream.py +148 -22
- sharedbox-0.9.0/src/sharedbox/_timed.py +34 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_types.py +178 -20
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_version.py +3 -3
- {sharedbox-0.8.0 → sharedbox-0.9.0}/stubtest-allowlist.txt +1 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/CMakeLists.txt +1 -1
- sharedbox-0.9.0/tests/cpp/table_builder.hpp +175 -0
- sharedbox-0.9.0/tests/cpp/test_attr.cpp +621 -0
- sharedbox-0.9.0/tests/cpp/test_layout3.cpp +632 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_mapping.cpp +3 -3
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_stream.cpp +175 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_stream_layout.cpp +36 -2
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_types.cpp +105 -36
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_write_in_place.cpp +15 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/crossproc.py +53 -2
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/forged_types.py +4 -2
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/streamproc.py +12 -0
- sharedbox-0.9.0/tests/test_attr.py +869 -0
- sharedbox-0.9.0/tests/test_attr_fields.py +402 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_bounded_dict.py +39 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_bounded_list.py +51 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_bounded_set.py +37 -0
- sharedbox-0.9.0/tests/test_box_max_shape.py +292 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_native_segment.py +53 -1
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_properties_numpy.py +38 -2
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_properties_types.py +10 -1
- sharedbox-0.9.0/tests/test_segment_status.py +173 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_stream.py +2 -11
- sharedbox-0.9.0/tests/test_stream_fetch.py +222 -0
- sharedbox-0.9.0/tests/test_timed.py +249 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_types_numpy.py +1 -1
- sharedbox-0.9.0/tests/type_checks/attr_types.py +49 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/type_checks/bounded_types.py +10 -2
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/type_checks/box_types.py +12 -0
- sharedbox-0.9.0/tests/type_checks/timed_types.py +15 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/zensical.toml +2 -0
- sharedbox-0.8.0/tests/cpp/table_builder.hpp +0 -49
- sharedbox-0.8.0/tests/cpp/test_layout3.cpp +0 -254
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.clang-format +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.github/dependabot.yml +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.github/pull_request_template.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.github/workflows/check-changelog.yaml +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.github/workflows/check-docs.yaml +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.github/workflows/codspeed.yml +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.github/workflows/contention.yaml +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.github/workflows/ops.yaml +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.github/workflows/prepare-release.yaml +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.github/workflows/publish-docs.yaml +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.github/workflows/streams.yaml +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/.gitignore +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/CMakeLists.txt +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/LICENSE +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/README.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/benchmarks/test_bench_box.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/benchmarks/test_bench_stream.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/changelog.d/.gitkeep +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/cmake/sharedbox-config.cmake +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/cmake/sharedbox-require-cxx.cmake +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/assets/benchmarks/ops.json +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/assets/benchmarks/roundtrip.json +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/assets/benchmarks/stream-matrix.json +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/assets/benchmarks/stream-throughput.json +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/diagrams/style.d2 +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/attach_metadata.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/change_several_fields_at_once.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/follow_a_whole_reference_graph.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/name_a_box.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/send_a_box_to_another_process.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/set_defaults_and_check_values.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/store_collections.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/store_numpy_numbers.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/store_records.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/examples/store_text_and_bytes.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/checking-a-process-is-alive.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/closing-and-lifetime.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/how-a-box-is-stored.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/how-the-module-is-built.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/index.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/limits.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/performance.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/reading-and-writing.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/references.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/waiting-for-changes.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/explanation/when-to-use-sharedbox.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/accept-a-box-in-c.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/accept-a-box-in-cpp.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/ai-contribution-policy.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/build-docs.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/change-several-fields-at-once.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/commits-and-prs.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/contribute.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/follow-a-whole-reference-graph.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/install-sharedbox.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/make-a-release.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/migrate-from-0.7.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/name-a-box.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/open-a-box-from-a-program.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/run-benchmarks.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/run-commit-checks.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/run-tests.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/send-a-box-to-another-process.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/set-defaults-and-check-values.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/set-up-development.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/store-collections.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/store-numpy-numbers.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/store-records.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/store-text-and-bytes.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/how-to/write-docs.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/index.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/javascripts/charts.js +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/api/events.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/api/library-authors.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/api/references.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/api/streams.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/changelog.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/cpp-and-c-api.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/reference/index.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/tutorials/index.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/tutorials/mailbox.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/tutorials/react-to-changes.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/tutorials/refer-to-another-box.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/docs/tutorials/share-a-record.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/include/sharedbox/sharedbox.hpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/include/sharedbox/sharedbox_c.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/includes/abbreviations.md +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/prek.toml +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/pyproject.toml +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/scripts/check_xrefs.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/scripts/release_notes.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/scripts/stream_compare.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/scripts/vscode_setup.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_metadata.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/codec.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/codec.hpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/collection.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/collection.hpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/dict.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/dict.hpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/keys.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/keys.hpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/scalars.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/scalars.hpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/set.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/set.hpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_native/stream.hpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/_refs.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/__init__.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/__main__.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/_app.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/cli.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/contention.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/ops.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/plot.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/roundtrip.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/size.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/size_diff.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/stream.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/src/sharedbox/py.typed +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/conftest.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/bench_stream.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/compile_fail.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/consumer/CMakeLists.txt +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/consumer/consumer.c +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_atomics.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_c_smoke.c +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_c_smoke_box.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_core_alone.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_fork.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_lock.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_metadata.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_result.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_slots.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_small_shm.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_values.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_wait.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/test_windows_h.cpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/cpp/unique.hpp +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/forged_open.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/os_names.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/refs_future.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/signatures.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/stress/stress_helpers.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/stress/test_stress_contention.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/stress/test_stress_crashes.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/stress/test_stress_follow.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/stress/test_stress_lock_timeout.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/stress/test_stress_memory.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/stress/test_stress_read_into.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/stress/test_stress_waiters.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/stress/test_stress_watchers.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_benchbox_all.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_benchbox_contention.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_benchbox_plot.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_benchbox_roundtrip.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_benchbox_stream.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_box.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_box_sync_async.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_capsule.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_doc_examples.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_doc_tutorials.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_events_async.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_events_bytes.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_events_future.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_events_signals.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_events_sync.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_exit.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_fields.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_follow.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_fork.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_layout.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_metadata.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_native_codec.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_native_liveness.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_native_stream.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_native_types.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_native_wait.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_native_waiters.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_properties_bounded.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_properties_bounded_dict.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_properties_bounded_set.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_properties_codec.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_properties_forged.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_properties_layout.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_properties_stateful.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_properties_stream.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_read_into.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_refs.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_refs_lazy.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_stream_async.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_stream_events.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_stream_fork.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_stream_metadata.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_stream_processes.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_stream_writing.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_types_arrays.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_types_arrays_bfloat16.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_types_arrays_torch.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_types_choices.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_types_collections.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_types_records.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_types_records_attrs.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_types_records_msgspec.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_types_scalars.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_types_unions.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_value_reuse.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/test_writing.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/type_checks/array_types.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/type_checks/events_types.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/type_checks/metadata_types.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/tests/type_checks/stream_types.py +0 -0
- {sharedbox-0.8.0 → sharedbox-0.9.0}/uv.lock +0 -0
|
@@ -114,7 +114,7 @@ jobs:
|
|
|
114
114
|
ref: ${{ github.event_name == 'release' && github.event.release.tag_name || '' }}
|
|
115
115
|
|
|
116
116
|
- name: Build wheels
|
|
117
|
-
uses: pypa/cibuildwheel@v4.3.
|
|
117
|
+
uses: pypa/cibuildwheel@v4.3.5
|
|
118
118
|
env:
|
|
119
119
|
CIBW_BUILD: ${{ matrix.python }}-${{ matrix.platform_id }}
|
|
120
120
|
CIBW_CONTAINER_ENGINE: "docker; create_args: --shm-size=1g"
|
|
@@ -9,6 +9,63 @@ Dates are marked as `DD-MM-YYYY`
|
|
|
9
9
|
|
|
10
10
|
<!-- towncrier release notes start -->
|
|
11
11
|
|
|
12
|
+
## [0.9.0](https://github.com/jacopoabramo/sharedbox/releases/tag/v0.9.0) - 04-11-2026
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- `segment_status`: `segment_status("lab:counts")` returns a `SegmentStatus`,
|
|
17
|
+
`missing` when no box or stream is published under the name, `running` while the
|
|
18
|
+
process that created it runs and `creator_exited` once it has ended.
|
|
19
|
+
`core.hpp` adds `segment_status` and `segment_state` for C++. ([#94](https://github.com/jacopoabramo/sharedbox/pull/94))
|
|
20
|
+
- `SharedReceiveStream.fetch`, `fetch_into` and `fetch_future`: `reader.fetch(120)`
|
|
21
|
+
returns the item at position 120, waiting until the sender has sent it, and
|
|
22
|
+
leaves the reader's receive cursor and `missed` unchanged. `ItemGoneError`, a
|
|
23
|
+
`LookupError` whose `oldest` is the oldest position the ring held, is raised
|
|
24
|
+
once the ring no longer holds the item. `stream.hpp` adds
|
|
25
|
+
`stream_reader::fetch` and `fetch_with` for C++. ([#95](https://github.com/jacopoabramo/sharedbox/pull/95))
|
|
26
|
+
- `MaxShape` on box fields: a box field declared as
|
|
27
|
+
`Annotated[npt.NDArray[np.uint16], MaxShape((2048, 2048))]` holds an array of
|
|
28
|
+
any shape up to that maximum, as kind 75 of box layout 3.3, and every read
|
|
29
|
+
returns an array of the stored shape. A write stores the shape and only the
|
|
30
|
+
elements in use. `SharedBox.writing` takes `shape`, so
|
|
31
|
+
`box.writing("image", shape=(480, 640))` sets the shape and yields a view of
|
|
32
|
+
it under one lock. `read_into` raises `TypeError` for a `MaxShape` field. ([#97](https://github.com/jacopoabramo/sharedbox/pull/97))
|
|
33
|
+
- `BoundedList`, `BoundedSet` and `BoundedDict` take their element types as the
|
|
34
|
+
first arguments when the class fixes none: `BoundedList(int, [1, 2], capacity=4)`
|
|
35
|
+
equals `BoundedList[int]([1, 2], capacity=4)`, and
|
|
36
|
+
`BoundedDict(str, float, {"a": 1.0}, capacity=8)` takes the key and value types. ([#97](https://github.com/jacopoabramo/sharedbox/pull/97))
|
|
37
|
+
- `Timed` and `Timed.now`: a box field, record member, collection element or
|
|
38
|
+
stream item declared as `Timed[float]` stores the value with a timestamp in
|
|
39
|
+
nanoseconds since the Unix epoch, and `box.temperature = Timed.now(21.5)` stamps
|
|
40
|
+
it with the system clock when the write holds the box's lock, or just before a
|
|
41
|
+
stream send publishes the item. A `Timed` with a timestamp of its own is stored as
|
|
42
|
+
given. Timed values are kind 77, and streams use stream layout 1.5. ([#99](https://github.com/jacopoabramo/sharedbox/pull/99))
|
|
43
|
+
- `Attr`, `attr`, `AttrValue`, `RequestFuture` and `RequestServer`: a box
|
|
44
|
+
field declared as `exposure: Attr[float] = attr(timed=True, settable=True)`
|
|
45
|
+
holds a value its owner writes with
|
|
46
|
+
`box.exposure.publish(value=..., setpoint=...)` and any process reads with
|
|
47
|
+
`.value`, `.setpoint`, `.timestamp_ns` or `read()`, or watches with
|
|
48
|
+
`watch` and `events`. An attribute with arguments, such as
|
|
49
|
+
`move: Attr[float, [float]] = attr()`, or a settable one carries a typed
|
|
50
|
+
request to the one process that serves it: `box.move.serve(handler)`
|
|
51
|
+
answers with a sync or async handler, and `box.move(5.0)` or
|
|
52
|
+
`box.exposure.set(0.02)` returns a `RequestFuture` waited on with
|
|
53
|
+
`.result(timeout)` or `await`. Of the requests sent while the handler
|
|
54
|
+
runs, only the newest is handled next. `RequestError`,
|
|
55
|
+
`RequestSupersededError` and `ServerExitedError` report a failed, replaced
|
|
56
|
+
or abandoned request, and `AlreadyServedError` a second server. Attribute
|
|
57
|
+
fields are kind 76 of box layout 3.3, and `box.hpp` adds `attr_publish`,
|
|
58
|
+
`attr_read`, `attr_published`, `attr_value_at` and the request protocol
|
|
59
|
+
steps on `handle` for C++, and `type_view` gives an attribute's flags and
|
|
60
|
+
where its value, timestamp and setpoint start. ([#103](https://github.com/jacopoabramo/sharedbox/pull/103))
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- On Windows, an operating system failure to create or open a box or stream raises
|
|
65
|
+
`OSError` with the Windows error code in `winerror`, such as `PermissionError`
|
|
66
|
+
for a segment the process may not open, instead of `RuntimeError`. ([#96](https://github.com/jacopoabramo/sharedbox/pull/96))
|
|
67
|
+
|
|
68
|
+
|
|
12
69
|
## [0.8.0](https://github.com/jacopoabramo/sharedbox/releases/tag/v0.8.0) - 30-10-2026
|
|
13
70
|
|
|
14
71
|
### Breaking
|
|
@@ -14,8 +14,8 @@ the layout and the native module converts values.
|
|
|
14
14
|
sharedbox/
|
|
15
15
|
|-- include/sharedbox/
|
|
16
16
|
| |-- core.hpp what every segment kind uses: result and error, liveness, names, mappings, the open step, waiter slots, the type codec, the metadata area and its CBOR check
|
|
17
|
-
| |-- box.hpp box layout 3.
|
|
18
|
-
| |-- stream.hpp stream layout 1.
|
|
17
|
+
| |-- box.hpp box layout 3.3 and its protocols: create, publish, open, lock, read, write, metadata, capsule handle
|
|
18
|
+
| |-- stream.hpp stream layout 1.5: create, open, the sender, readers, the gate, dead ends, metadata
|
|
19
19
|
| |-- sharedbox.hpp includes every kind; the header consumers include
|
|
20
20
|
| |-- sharedbox_c.h minimal C interface: sbx_open, sbx_import, sbx_read, sbx_write, sbx_schema_hash, sbx_release, sbx_field_desc, typed sbx_read_* and sbx_write_*, sbx_metadata_head, sbx_read_metadata, sbx_add_metadata
|
|
21
21
|
| `-- sharedbox_c.cpp its implementation, compiled by the consumer (CMake target sharedbox::c)
|
|
@@ -26,15 +26,19 @@ sharedbox/
|
|
|
26
26
|
|-- src/sharedbox/
|
|
27
27
|
| |-- __init__.py re-exports the public API, get_include()
|
|
28
28
|
| |-- _box.py SharedBox: class keywords, fields, fields(), create/attach, __post_init__, update, snapshot, unlink, __sharedbox_box__
|
|
29
|
-
| |-- _layout.py Capacity, field() and Field, field offsets and kind codes, schema hash
|
|
29
|
+
| |-- _layout.py Capacity, field(), attr() and Field, field offsets and kind codes, schema hash
|
|
30
30
|
| |-- _types.py annotation parsing into TypeSpec, the description table
|
|
31
31
|
| |-- _arrays.py Shape, MaxShape, DType, SupportsDLPack, register_array_type
|
|
32
|
-
| |--
|
|
32
|
+
| |-- _timed.py Timed: a value with its timestamp in ns, Timed.now and the mark a write replaces
|
|
33
|
+
| |-- _attr.py Attr and AttrValue: the handle of an attribute field, publish, read, requests and serve
|
|
34
|
+
| |-- _request.py request fields: RequestFuture, RequestServer, the request errors, serving threads and tasks
|
|
35
|
+
| |-- _events.py BoxEvents, FieldWatch, the watcher thread, which also completes request futures and kicks request servers
|
|
33
36
|
| |-- _follow.py BoxEvents.follow and unfollow: forwarding the events of boxes that references reach
|
|
34
37
|
| |-- _stream.py SharedStream, SharedSendStream, SharedReceiveStream, ReceiveEvents, the statistics classes, the worker threads
|
|
35
38
|
| |-- _metadata.py metadata documents: the type variable M, CBOR encoding, read-only decoding, the cache of decoded versions
|
|
36
39
|
| |-- _refs.py reference fields: BoxRef, the class registry, BrokenReferenceError, UnknownBoxClassError
|
|
37
|
-
| |--
|
|
40
|
+
| |-- _status.py segment_status and SegmentStatus: whether a name holds a segment and its creator runs
|
|
41
|
+
| |-- _exit.py the exit handler: stops the watcher, request server and stream threads, closes boxes, streams and ends,
|
|
38
42
|
| | replaces the native objects of watchers and box classes
|
|
39
43
|
| |-- _native.pyi hand-written stub for the extension
|
|
40
44
|
| |-- py.typed
|
|
@@ -71,12 +75,17 @@ sharedbox/
|
|
|
71
75
|
| |-- test_native_waiters.py waiter slots, dead owners, interrupt
|
|
72
76
|
| |-- test_box_sync_async.py a sync and an async watch on one box in two threads
|
|
73
77
|
| |-- test_metadata.py metadata of a box: documents, versions, events, typed access, reserved names
|
|
78
|
+
| |-- test_segment_status.py segment_status: missing, running and an exited creator
|
|
74
79
|
| |-- test_stream.py SharedStream, sender and reader, blocking and non-blocking calls
|
|
75
80
|
| |-- test_stream_metadata.py metadata of a stream: the version of each item, in one process and across processes
|
|
76
81
|
| |-- test_stream_async.py asyncio calls on a stream, cancellation
|
|
77
82
|
| |-- test_stream_events.py ReceiveEvents delivery
|
|
83
|
+
| |-- test_stream_fetch.py fetching an item by its position, in one process and across processes
|
|
78
84
|
| |-- test_stream_processes.py streams across processes, dead senders and readers
|
|
79
85
|
| |-- test_stream_fork.py stream ends inherited through fork
|
|
86
|
+
| |-- test_timed.py Timed fields and stream items: stamps, given times, where a Timed may go
|
|
87
|
+
| |-- test_attr.py attribute requests across processes: answers, superseding, a killed server, close, async handlers
|
|
88
|
+
| |-- test_attr_fields.py Attr in the layout: publish, read, watch, refused operations, where an Attr may go
|
|
80
89
|
| |-- streamproc.py helpers that run a stream end in another process
|
|
81
90
|
| |-- test_properties_*.py Hypothesis property tests, streams included
|
|
82
91
|
| |-- stress/ stress tests, marker stress, deselected by default
|
|
@@ -96,7 +105,7 @@ sharedbox/
|
|
|
96
105
|
| |-- examples/ one script per how-to guide, included by the guide
|
|
97
106
|
| |-- javascripts/charts.js draws each div of class sbx-chart from a plotly figure JSON file
|
|
98
107
|
| |-- assets/benchmarks/ the figure JSON files of the performance page, from benchbox plot
|
|
99
|
-
| `-- reference/segment-layout.md core 1.0, box layout 3.
|
|
108
|
+
| `-- reference/segment-layout.md core 1.0, box layout 3.3, stream layout 1.5, names and protocols
|
|
100
109
|
|-- includes/abbreviations.md acronym tooltips appended to every page
|
|
101
110
|
|-- zensical.toml site configuration and navigation
|
|
102
111
|
|-- .github/workflows/ci.yaml lint, docs check, C++ tests, cibuildwheel wheels, tests, stress,
|
|
@@ -140,11 +149,11 @@ class's identity (the `identity` class keyword, by default
|
|
|
140
149
|
always asks for a new name and raises `SegmentExistsError` if it is taken.
|
|
141
150
|
|
|
142
151
|
Every segment starts with the same 64-byte line, whatever its kind. Core
|
|
143
|
-
version 1.0, box layout 3.
|
|
152
|
+
version 1.0, box layout 3.3, from offset 0:
|
|
144
153
|
|
|
145
154
|
- Common line 0, written once at creation: `magic` (`SBX_BOX_` for a box,
|
|
146
155
|
`SBX_STRM` for a stream), `core_major` 1, `core_minor` 0, `kind_major` 3,
|
|
147
|
-
`kind_minor`
|
|
156
|
+
`kind_minor` 3, `schema_hash`, `create_id`, `creator_start`,
|
|
148
157
|
`creator_pidns`, `creator_pid`, `waiter_slots`, `size`.
|
|
149
158
|
- Box line 1, at offset 64: `seq` (sequence lock; the generation is
|
|
150
159
|
`seq >> 1`, there is no generation field), `writer_pid`, `wake_word`,
|
|
@@ -161,8 +170,13 @@ version 1.0, box layout 3.2, from offset 0:
|
|
|
161
170
|
`asleep_on`: the offset of the count the slot's current wait added 1 to,
|
|
162
171
|
which freeing a dead owner's slot takes back), then the description
|
|
163
172
|
table of `types_size` bytes. Kinds are 0 to 5, 6 to 12 (fixed-size
|
|
164
|
-
scalars), 13 to 23 (fixed-width numbers and `datetime64`) and 64 to
|
|
165
|
-
(described types; 75, an array up to a maximum shape,
|
|
173
|
+
scalars), 13 to 23 (fixed-width numbers and `datetime64`), and 64 to 77
|
|
174
|
+
(described types; 75, an array up to a maximum shape, in a stream
|
|
175
|
+
item or a box field, written as its shape line and the elements in use;
|
|
176
|
+
76, an attribute, a box field only, holding the request part, the value
|
|
177
|
+
part with `published` and the setpoint; 77, a value with an
|
|
178
|
+
`i64` timestamp in ns, wherever a record may go, its timestamp `-2**63`
|
|
179
|
+
replaced by the time of the write).
|
|
166
180
|
- The record, at a multiple of 64. `record + record_size <= 2**32 - 4096`.
|
|
167
181
|
- The metadata area, when `metadata` is not 0, at the first multiple of 64
|
|
168
182
|
after the record, `metadata + metadata_size <= 2**32 - 4096`: a 64-byte
|
|
@@ -186,7 +200,7 @@ A stream's segment starts with a 256-byte header: the common line, a line
|
|
|
186
200
|
of geometry written once, the sender's line and the readers' line. Then
|
|
187
201
|
come a 64-byte entry per reader, the waiter slots, `capacity` slots each
|
|
188
202
|
holding a `seq` and one item, the description table, and the metadata
|
|
189
|
-
area laid out as a box's. Stream layout 1.
|
|
203
|
+
area laid out as a box's. Stream layout 1.5 keeps `metadata_size` and
|
|
190
204
|
`metadata` in the geometry line and `metadata_head` in the sender's line.
|
|
191
205
|
Every offset is 64-bit. The full specification is in
|
|
192
206
|
`docs/reference/segment-layout.md`.
|
|
@@ -204,8 +218,10 @@ capacity counts elements, 1 to 1048576.
|
|
|
204
218
|
|
|
205
219
|
### Lifecycle
|
|
206
220
|
|
|
207
|
-
- `close()` interrupts and stops the watcher thread, stops
|
|
208
|
-
|
|
221
|
+
- `close()` interrupts and stops the watcher thread, stops the box's
|
|
222
|
+
request servers, releasing each field, and cancels its pending request
|
|
223
|
+
futures, stops forwarding started with `events.follow`, and detaches
|
|
224
|
+
this box. Later reads and
|
|
209
225
|
writes raise `BoxClosedError`. Garbage collection closes a box too, but
|
|
210
226
|
forwarding it started keeps running until the cycle collector frees its
|
|
211
227
|
events groups.
|
|
@@ -354,6 +370,9 @@ A read decodes the stored bytes; a value that cannot be changed (a
|
|
|
354
370
|
`datetime`, or a frozen record, tuple or frozenset holding only such values)
|
|
355
371
|
is reused until its field is written. `update(**values)` writes several
|
|
356
372
|
fields at once; `watch(field)` and `events` report changes from any process.
|
|
373
|
+
A field declared `Attr[T]` with `attr()` is published by its owner with
|
|
374
|
+
`publish` and read by any process, and may take requests the owner serves;
|
|
375
|
+
see `docs/how-to/request-work-from-the-owner.md`.
|
|
357
376
|
C++ and C code take a box through `__sharedbox_box__` or open it by name; see
|
|
358
377
|
`docs/how-to/accept-a-box-in-cpp.md`, `docs/how-to/accept-a-box-in-c.md` and
|
|
359
378
|
`docs/how-to/open-a-box-from-a-program.md`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""The script of the guide "How to clean up segments"."""
|
|
2
2
|
|
|
3
3
|
# --8<-- [start:close]
|
|
4
|
-
from sharedbox import LockTimeoutError, SharedBox
|
|
4
|
+
from sharedbox import LockTimeoutError, SharedBox, segment_status
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class Delivery(SharedBox, name="example-delivery"):
|
|
@@ -18,6 +18,10 @@ delivery.close()
|
|
|
18
18
|
Delivery.unlink()
|
|
19
19
|
# --8<-- [end:unlink]
|
|
20
20
|
|
|
21
|
+
# --8<-- [start:status]
|
|
22
|
+
print(segment_status("example-delivery")) # missing
|
|
23
|
+
# --8<-- [end:status]
|
|
24
|
+
|
|
21
25
|
creator = Delivery(True)
|
|
22
26
|
|
|
23
27
|
# --8<-- [start:force-unlock]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""The script of the guide "How to request work from the process that owns a box"."""
|
|
2
|
+
|
|
3
|
+
# --8<-- [start:declare]
|
|
4
|
+
import asyncio
|
|
5
|
+
import threading
|
|
6
|
+
|
|
7
|
+
from sharedbox import Attr, RequestError, RequestSupersededError, SharedBox, attr
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Camera(SharedBox, name="example-camera"):
|
|
11
|
+
temperature: Attr[float] = attr(timed=True)
|
|
12
|
+
exposure: Attr[float] = attr(timed=True, settable=True, default=0.01)
|
|
13
|
+
move: Attr[float, [float]] = attr()
|
|
14
|
+
home: Attr[None] = attr()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# --8<-- [end:declare]
|
|
18
|
+
|
|
19
|
+
# --8<-- [start:publish]
|
|
20
|
+
camera = Camera()
|
|
21
|
+
camera.temperature.publish(value=21.5)
|
|
22
|
+
print(camera.temperature.value) # 21.5
|
|
23
|
+
reading = camera.temperature.read()
|
|
24
|
+
print(reading["value"], reading["timestamp_ns"] > 0) # 21.5 True
|
|
25
|
+
# --8<-- [end:publish]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# --8<-- [start:serve]
|
|
29
|
+
def set_exposure(value: float) -> float:
|
|
30
|
+
accepted = min(max(value, 0.001), 1.0)
|
|
31
|
+
camera.exposure.publish(setpoint=accepted)
|
|
32
|
+
camera.exposure.publish(value=accepted)
|
|
33
|
+
return accepted
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def move(target: float) -> float:
|
|
37
|
+
if target > 100.0:
|
|
38
|
+
raise ValueError(f"{target} is out of range")
|
|
39
|
+
return target
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
exposure_server = camera.exposure.serve(set_exposure)
|
|
43
|
+
move_server = camera.move.serve(move)
|
|
44
|
+
# --8<-- [end:serve]
|
|
45
|
+
|
|
46
|
+
# --8<-- [start:request]
|
|
47
|
+
print(camera.exposure.set(5.0).result(timeout=5)) # 1.0
|
|
48
|
+
print(camera.exposure.setpoint) # 1.0
|
|
49
|
+
print(camera.move(5.0).result(timeout=5)) # 5.0
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
async def main() -> None:
|
|
53
|
+
print(await camera.exposure.set(0.02)) # 0.02
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
asyncio.run(main())
|
|
57
|
+
# --8<-- [end:request]
|
|
58
|
+
|
|
59
|
+
# --8<-- [start:error]
|
|
60
|
+
try:
|
|
61
|
+
camera.move(250.0).result(timeout=5)
|
|
62
|
+
except RequestError as error:
|
|
63
|
+
print(error) # Camera.move: ValueError: 250.0 is out of range
|
|
64
|
+
# --8<-- [end:error]
|
|
65
|
+
|
|
66
|
+
move_server.stop()
|
|
67
|
+
|
|
68
|
+
# --8<-- [start:supersede]
|
|
69
|
+
started = threading.Event()
|
|
70
|
+
release = threading.Event()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def slow_move(target: float) -> float:
|
|
74
|
+
started.set()
|
|
75
|
+
release.wait()
|
|
76
|
+
return target
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
move_server = camera.move.serve(slow_move)
|
|
80
|
+
first = camera.move(1.0)
|
|
81
|
+
started.wait()
|
|
82
|
+
second = camera.move(2.0)
|
|
83
|
+
third = camera.move(3.0)
|
|
84
|
+
release.set()
|
|
85
|
+
print(first.result(timeout=5), third.result(timeout=5)) # 1.0 3.0
|
|
86
|
+
try:
|
|
87
|
+
second.result(timeout=5)
|
|
88
|
+
except RequestSupersededError:
|
|
89
|
+
print("the second request was superseded")
|
|
90
|
+
# --8<-- [end:supersede]
|
|
91
|
+
|
|
92
|
+
move_server.stop()
|
|
93
|
+
exposure_server.stop()
|
|
94
|
+
camera.close()
|
|
95
|
+
Camera.unlink()
|
|
@@ -12,6 +12,7 @@ from sharedbox import (
|
|
|
12
12
|
Capacity,
|
|
13
13
|
DType,
|
|
14
14
|
EndOfStream,
|
|
15
|
+
ItemGoneError,
|
|
15
16
|
MaxShape,
|
|
16
17
|
ReaderReleased,
|
|
17
18
|
Shape,
|
|
@@ -43,6 +44,23 @@ def register(stream: SharedStream[Letter], ready: "mp.synchronize.Event") -> Non
|
|
|
43
44
|
# --8<-- [end:reader]
|
|
44
45
|
|
|
45
46
|
|
|
47
|
+
# --8<-- [start:fetch]
|
|
48
|
+
def fetch_letters(reader: SharedReceiveStream[Letter], first: int, last: int) -> None:
|
|
49
|
+
position = first
|
|
50
|
+
while position <= last:
|
|
51
|
+
try:
|
|
52
|
+
letter = reader.fetch(position, timeout=5)
|
|
53
|
+
except ItemGoneError as gone:
|
|
54
|
+
print("skipped", position, "to", gone.oldest - 1)
|
|
55
|
+
position = gone.oldest
|
|
56
|
+
continue
|
|
57
|
+
print("fetched", letter.index)
|
|
58
|
+
position += 1
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# --8<-- [end:fetch]
|
|
62
|
+
|
|
63
|
+
|
|
46
64
|
# --8<-- [start:tasks]
|
|
47
65
|
async def produce(sender: SharedSendStream[Letter]) -> None:
|
|
48
66
|
async with sender:
|
|
@@ -185,3 +203,10 @@ if __name__ == "__main__":
|
|
|
185
203
|
sort_without_stalling(sender, 6)
|
|
186
204
|
file_letters(reader)
|
|
187
205
|
SharedStream.unlink("example-sorting:release")
|
|
206
|
+
with SharedStream.create(Letter, "example-sorting:fetch", capacity=8) as stream:
|
|
207
|
+
reader = stream.reader(mode="lossy")
|
|
208
|
+
with stream.sender() as sender:
|
|
209
|
+
for i in range(6):
|
|
210
|
+
sender.send(Letter(i, "Ada", np.full((32, 32), i, np.uint8)))
|
|
211
|
+
fetch_letters(reader, 2, 4)
|
|
212
|
+
SharedStream.unlink("example-sorting:fetch")
|
|
@@ -15,7 +15,7 @@ class Recording(SharedBox, name="example-recording"):
|
|
|
15
15
|
# --8<-- [end:declare]
|
|
16
16
|
|
|
17
17
|
# --8<-- [start:create]
|
|
18
|
-
recording = Recording(BoundedList
|
|
18
|
+
recording = Recording(BoundedList(float, [0.0] * 50000, capacity=65536))
|
|
19
19
|
# --8<-- [end:create]
|
|
20
20
|
|
|
21
21
|
# --8<-- [start:change]
|
|
@@ -13,7 +13,7 @@ class Catalogue(SharedBox, name="example-catalogue"):
|
|
|
13
13
|
# --8<-- [end:declare]
|
|
14
14
|
|
|
15
15
|
# --8<-- [start:create]
|
|
16
|
-
catalogue = Catalogue(BoundedSet
|
|
16
|
+
catalogue = Catalogue(BoundedSet(int, range(50000), capacity=65536))
|
|
17
17
|
# --8<-- [end:create]
|
|
18
18
|
|
|
19
19
|
# --8<-- [start:change]
|
|
@@ -80,7 +80,7 @@ class Scores(SharedBox, name="example-scores"):
|
|
|
80
80
|
by_id: Annotated[BoundedDict[int, float], Capacity(4096)]
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
scores = Scores(BoundedDict
|
|
83
|
+
scores = Scores(BoundedDict(int, float, dict.fromkeys(range(3000), 1.0), capacity=4096))
|
|
84
84
|
by_id = scores.by_id
|
|
85
85
|
print(by_id[2999], by_id.get(5000), 5000 in by_id) # 1.0 None False
|
|
86
86
|
# --8<-- [end:dict-declare]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""The script of the guide "How to store a value with its time"."""
|
|
2
|
+
# ruff: noqa: RUF012
|
|
3
|
+
|
|
4
|
+
# --8<-- [start:declare]
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
from sharedbox import Capacity, SharedBox, SharedStream, Timed
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Thermometer(SharedBox, name="example-thermometer"):
|
|
11
|
+
temperature: Timed[float] = Timed(0.0, 0)
|
|
12
|
+
status: Timed[Annotated[str, Capacity(16)]] = Timed("", 0)
|
|
13
|
+
history: Annotated[list[Timed[float]], Capacity(4)] = []
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# --8<-- [end:declare]
|
|
17
|
+
|
|
18
|
+
# --8<-- [start:stamp]
|
|
19
|
+
thermometer = Thermometer()
|
|
20
|
+
thermometer.temperature = Timed.now(21.5)
|
|
21
|
+
reading = thermometer.temperature
|
|
22
|
+
print(reading.value) # 21.5
|
|
23
|
+
print(reading.timestamp_ns > 0) # True
|
|
24
|
+
print(type(reading.timestamp)) # <class 'float'>
|
|
25
|
+
# --8<-- [end:stamp]
|
|
26
|
+
|
|
27
|
+
# --8<-- [start:given]
|
|
28
|
+
device_ns = 1_762_000_000_123_456_789
|
|
29
|
+
thermometer.status = Timed("ready", device_ns)
|
|
30
|
+
print(thermometer.status) # Timed(value='ready', timestamp_ns=1762000000123456789)
|
|
31
|
+
# --8<-- [end:given]
|
|
32
|
+
|
|
33
|
+
# --8<-- [start:collections]
|
|
34
|
+
thermometer.history = [Timed.now(21.4), Timed.now(21.5)]
|
|
35
|
+
first, second = thermometer.history
|
|
36
|
+
print(first.timestamp_ns == second.timestamp_ns) # True
|
|
37
|
+
# --8<-- [end:collections]
|
|
38
|
+
|
|
39
|
+
# --8<-- [start:stream]
|
|
40
|
+
with SharedStream.create(Timed[float], "example-temperatures", capacity=4) as stream:
|
|
41
|
+
reader = stream.reader()
|
|
42
|
+
with stream.sender() as sender:
|
|
43
|
+
sender.send(Timed.now(21.6))
|
|
44
|
+
print(reader.receive().value) # 21.6
|
|
45
|
+
# --8<-- [end:stream]
|
|
46
|
+
|
|
47
|
+
thermometer.close()
|
|
48
|
+
Thermometer.unlink()
|
|
49
|
+
SharedStream.unlink("example-temperatures")
|
|
@@ -7,7 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
import numpy.typing as npt
|
|
9
9
|
|
|
10
|
-
from sharedbox import DType, Shape, SharedBox, SupportsDLPack, field
|
|
10
|
+
from sharedbox import DType, MaxShape, Shape, SharedBox, SupportsDLPack, field
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class Scanner(SharedBox, name="example-scanner"):
|
|
@@ -54,6 +54,23 @@ with scanner.writing("scan") as scan:
|
|
|
54
54
|
print(int(scanner.scan[0].sum())) # 1530
|
|
55
55
|
# --8<-- [end:writing]
|
|
56
56
|
|
|
57
|
+
|
|
58
|
+
# --8<-- [start:max-shape]
|
|
59
|
+
class Sorter(SharedBox, name="example-sorter"):
|
|
60
|
+
label: Annotated[npt.NDArray[np.uint8], MaxShape((8, 12))] = np.zeros(
|
|
61
|
+
(0, 12), np.uint8
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
sorter = Sorter()
|
|
66
|
+
print(sorter.label.shape) # (0, 12)
|
|
67
|
+
sorter.label = np.ones((3, 12), np.uint8)
|
|
68
|
+
print(sorter.label.shape) # (3, 12)
|
|
69
|
+
with sorter.writing("label", shape=(5, 12)) as label:
|
|
70
|
+
label[:] = 2
|
|
71
|
+
print(sorter.label.shape, int(sorter.label.sum())) # (5, 12) 120
|
|
72
|
+
# --8<-- [end:max-shape]
|
|
73
|
+
|
|
57
74
|
# --8<-- [start:any-library]
|
|
58
75
|
raw = scanner.raw
|
|
59
76
|
print(np.from_dlpack(raw)) # [0. 0. 0.]
|
|
@@ -85,5 +102,7 @@ scanner.close()
|
|
|
85
102
|
Scanner.unlink()
|
|
86
103
|
tray.close()
|
|
87
104
|
Tray.unlink()
|
|
105
|
+
sorter.close()
|
|
106
|
+
Sorter.unlink()
|
|
88
107
|
binder.close()
|
|
89
108
|
Binder.unlink()
|
|
@@ -57,6 +57,7 @@ containers: "records, collections and arrays" {
|
|
|
57
57
|
"set, frozenset, tuple[T, ...]": "as a list"
|
|
58
58
|
"dict": "as a list, with a key and value in each slot"
|
|
59
59
|
"array uint8, shape (2, 2)": "01 02 03 04, in C order"
|
|
60
|
+
"Timed[int] = Timed(5, t)": "05 00 00 00 00 00 00 00 | t, nanoseconds as an int"
|
|
60
61
|
"SharedBox subclass": "create id | schema hash | name, 256 bytes"
|
|
61
62
|
}
|
|
62
63
|
```
|
|
@@ -11,6 +11,12 @@ word you don't know, its link brings you to this page.
|
|
|
11
11
|
Acronyms are explained in tooltips across the site: hover over a word with
|
|
12
12
|
a dotted underline to read what it stands for.
|
|
13
13
|
|
|
14
|
+
### Attribute
|
|
15
|
+
|
|
16
|
+
An attribute is a [field](#field) declared with [`Attr`][sharedbox.Attr].
|
|
17
|
+
It holds a value its owner publishes and, when it takes requests, a request
|
|
18
|
+
part through which other processes ask the owner to act.
|
|
19
|
+
|
|
14
20
|
### Box
|
|
15
21
|
|
|
16
22
|
A box is a record that several processes share. You describe it with a
|
|
@@ -105,8 +105,9 @@ the slot that [`writing`][sharedbox.SharedSendStream.writing] gives take
|
|
|
105
105
|
the keyword too. A version applies from the item sent with it, and a
|
|
106
106
|
reader's [`metadata`][sharedbox.SharedReceiveStream.metadata] and
|
|
107
107
|
[`metadata_version`][sharedbox.SharedReceiveStream.metadata_version]
|
|
108
|
-
describe the item it
|
|
109
|
-
each item was sent under. The metadata of this stream
|
|
108
|
+
describe the item it returned last, by a receive or a fetch, so the reader
|
|
109
|
+
can tell which version each item was sent under. The metadata of this stream
|
|
110
|
+
is a `TypedDict` too:
|
|
110
111
|
|
|
111
112
|
```{.python}
|
|
112
113
|
--8<-- "docs/examples/attach_metadata.py:calibration"
|
|
@@ -144,7 +145,7 @@ latest reader meets this first, because the sender may overwrite its item
|
|
|
144
145
|
right after the reader receives it.
|
|
145
146
|
|
|
146
147
|
!!! warning "A callback on the main thread can see a later item's version"
|
|
147
|
-
The reader's `metadata` follows the item the reader
|
|
148
|
+
The reader's `metadata` follows the item the reader returned last,
|
|
148
149
|
which may be later than the item a callback was given. A callback of
|
|
149
150
|
[`reader.events`][sharedbox.SharedReceiveStream.events] connected with
|
|
150
151
|
`thread="main"` runs after the reader's thread has received its item,
|
|
@@ -45,7 +45,20 @@ handle is closed. Either way you write the same code, and it works on both.
|
|
|
45
45
|
## Remove a segment left by a crash
|
|
46
46
|
|
|
47
47
|
On Linux, a process that crashed before calling `unlink` leaves its segment
|
|
48
|
-
behind as a file in `/dev/shm`.
|
|
48
|
+
behind as a file in `/dev/shm`. For one name,
|
|
49
|
+
[`segment_status`][sharedbox.segment_status] says whether a box or stream
|
|
50
|
+
is published under it and whether the process that created it still runs:
|
|
51
|
+
|
|
52
|
+
```{.python}
|
|
53
|
+
--8<-- "docs/examples/clean_up_segments.py:status"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
It returns `missing`, `running` or `creator_exited`. A segment whose creator
|
|
57
|
+
has exited is one to remove once no other process uses it. A process that
|
|
58
|
+
crashed in the middle of `create` leaves a segment that was never published,
|
|
59
|
+
which reads as `missing` although the name is still taken; creating under
|
|
60
|
+
that name raises `SegmentExistsError`, which says so. To see every segment at
|
|
61
|
+
once, list the files:
|
|
49
62
|
|
|
50
63
|
```bash
|
|
51
64
|
ls /dev/shm/SBX:*
|
|
@@ -26,6 +26,8 @@ items through a [stream](../explanation/glossary.md#stream).
|
|
|
26
26
|
capacity and handle values that do not fit
|
|
27
27
|
- [How to store records](store-records.md): dataclasses, named tuples and
|
|
28
28
|
typed dicts as one field
|
|
29
|
+
- [How to store a value with its time](store-a-value-with-its-time.md):
|
|
30
|
+
a timestamp the write takes, or one you already have
|
|
29
31
|
- [How to store lists, sets and dicts](store-collections.md): collections
|
|
30
32
|
with a capacity in elements
|
|
31
33
|
- [How to store arrays](store-arrays.md): a shape, a dtype and any DLPack
|
|
@@ -38,6 +40,9 @@ items through a [stream](../explanation/glossary.md#stream).
|
|
|
38
40
|
write with `update` and read with `snapshot`
|
|
39
41
|
- [How to send a box to another process](send-a-box-to-another-process.md):
|
|
40
42
|
as an argument, by name, or to a pool
|
|
43
|
+
- [How to request work from the process that owns a box](request-work-from-the-owner.md):
|
|
44
|
+
attributes the owner publishes, requests it answers, and what happens
|
|
45
|
+
when one fails
|
|
41
46
|
- [How to clean up segments](clean-up-segments.md): close, unlink, and
|
|
42
47
|
recover after a crash
|
|
43
48
|
- [How to follow a whole reference graph](follow-a-whole-reference-graph.md):
|