sharedbox 0.7.0__tar.gz → 0.8.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.7.0 → sharedbox-0.8.0}/CHANGELOG.md +102 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/CLAUDE.md +30 -16
- {sharedbox-0.7.0 → sharedbox-0.8.0}/PKG-INFO +3 -1
- sharedbox-0.8.0/docs/examples/attach_metadata.py +78 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/send_items_through_a_stream.py +59 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_arrays.py +3 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/glossary.md +30 -0
- sharedbox-0.8.0/docs/how-to/attach-metadata.md +192 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/index.md +5 -0
- sharedbox-0.8.0/docs/how-to/migrate-from-0.7.md +83 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/send-items-through-a-stream.md +74 -7
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-arrays.md +1 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/arrays.md +5 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/errors.md +15 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/events.md +1 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/segment-layout.md +402 -52
- {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/box.hpp +192 -20
- {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/core.hpp +491 -22
- {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/sharedbox_c.cpp +37 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/sharedbox_c.h +15 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/stream.hpp +267 -55
- {sharedbox-0.7.0 → sharedbox-0.8.0}/includes/abbreviations.md +1 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/pyproject.toml +3 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/__init__.py +9 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_arrays.py +77 -23
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_box.py +223 -46
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_events.py +35 -7
- sharedbox-0.8.0/src/sharedbox/_exit.py +193 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_layout.py +6 -1
- sharedbox-0.8.0/src/sharedbox/_metadata.py +134 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/module.cpp +34 -24
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/segment.cpp +77 -3
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/segment.hpp +28 -7
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/stream.cpp +198 -20
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/types.cpp +132 -16
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/types.hpp +32 -5
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native.pyi +171 -11
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_stream.py +469 -79
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_types.py +53 -11
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_version.py +3 -3
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/ops.py +1 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/stream.py +2 -2
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/CMakeLists.txt +1 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_c_smoke.c +17 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_c_smoke_box.cpp +2 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_layout3.cpp +69 -3
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_mapping.cpp +3 -3
- sharedbox-0.8.0/tests/cpp/test_metadata.cpp +1022 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_stream.cpp +224 -2
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_stream_layout.cpp +43 -2
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_types.cpp +98 -2
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/crossproc.py +11 -1
- sharedbox-0.8.0/tests/signatures.py +12 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/streamproc.py +77 -3
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_read_into.py +2 -2
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_box.py +2 -2
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_events_bytes.py +1 -1
- sharedbox-0.8.0/tests/test_exit.py +257 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_fields.py +9 -8
- sharedbox-0.8.0/tests/test_metadata.py +509 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_segment.py +2 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_stream.py +6 -14
- sharedbox-0.8.0/tests/test_properties_stream.py +353 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_read_into.py +6 -6
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_refs.py +2 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream.py +390 -13
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream_async.py +71 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream_events.py +32 -1
- sharedbox-0.8.0/tests/test_stream_metadata.py +478 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream_processes.py +40 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream_writing.py +1 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_arrays.py +56 -35
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_arrays_bfloat16.py +1 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_arrays_torch.py +1 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_records.py +3 -3
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_value_reuse.py +1 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_writing.py +4 -4
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/type_checks/box_types.py +1 -1
- sharedbox-0.8.0/tests/type_checks/metadata_types.py +132 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/uv.lock +68 -1
- {sharedbox-0.7.0 → sharedbox-0.8.0}/zensical.toml +2 -0
- sharedbox-0.7.0/tests/test_exit.py +0 -53
- sharedbox-0.7.0/tests/test_properties_stream.py +0 -121
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.clang-format +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/dependabot.yml +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/pull_request_template.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/check-changelog.yaml +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/check-docs.yaml +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/ci.yaml +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/codspeed.yml +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/contention.yaml +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/ops.yaml +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/prepare-release.yaml +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/publish-docs.yaml +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/streams.yaml +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/.gitignore +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/CMakeLists.txt +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/LICENSE +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/README.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/benchmarks/test_bench_box.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/benchmarks/test_bench_stream.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/changelog.d/.gitkeep +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/cmake/sharedbox-config.cmake +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/cmake/sharedbox-require-cxx.cmake +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/assets/benchmarks/ops.json +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/assets/benchmarks/roundtrip.json +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/assets/benchmarks/stream-matrix.json +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/assets/benchmarks/stream-throughput.json +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/diagrams/style.d2 +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/change_several_fields_at_once.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/clean_up_segments.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/follow_a_whole_reference_graph.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/name_a_box.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/send_a_box_to_another_process.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/set_defaults_and_check_values.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_a_large_list.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_a_large_set_or_dict.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_collections.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_numpy_numbers.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_records.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_text_and_bytes.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/checking-a-process-is-alive.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/closing-and-lifetime.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/field-types.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/how-a-box-is-stored.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/how-the-module-is-built.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/index.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/limits.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/performance.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/reading-and-writing.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/references.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/waiting-for-changes.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/when-to-use-sharedbox.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/accept-a-box-in-c.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/accept-a-box-in-cpp.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/ai-contribution-policy.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/build-docs.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/change-several-fields-at-once.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/clean-up-segments.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/commits-and-prs.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/contribute.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/follow-a-whole-reference-graph.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/install-sharedbox.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/make-a-release.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/name-a-box.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/open-a-box-from-a-program.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/run-benchmarks.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/run-commit-checks.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/run-tests.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/send-a-box-to-another-process.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/set-defaults-and-check-values.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/set-up-development.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-a-large-list.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-a-large-set-or-dict.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-collections.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-numpy-numbers.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-records.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-text-and-bytes.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/write-docs.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/index.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/javascripts/charts.js +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/box.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/index.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/library-authors.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/references.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/streams.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/changelog.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/cpp-and-c-api.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/index.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/tutorials/index.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/tutorials/mailbox.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/tutorials/react-to-changes.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/tutorials/refer-to-another-box.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/tutorials/share-a-record.md +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/sharedbox.hpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/prek.toml +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/scripts/check_xrefs.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/scripts/release_notes.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/scripts/stream_compare.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/scripts/vscode_setup.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_bounded.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_follow.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/codec.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/codec.hpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/collection.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/collection.hpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/dict.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/dict.hpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/keys.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/keys.hpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/scalars.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/scalars.hpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/set.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/set.hpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/stream.hpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_refs.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/__init__.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/__main__.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/_app.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/cli.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/contention.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/plot.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/roundtrip.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/size.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/size_diff.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/py.typed +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/stubtest-allowlist.txt +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/conftest.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/bench_stream.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/compile_fail.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/consumer/CMakeLists.txt +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/consumer/consumer.c +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/table_builder.hpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_atomics.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_core_alone.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_fork.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_lock.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_result.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_slots.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_small_shm.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_values.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_wait.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_windows_h.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_write_in_place.cpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/unique.hpp +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/forged_open.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/forged_types.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/os_names.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/refs_future.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/stress_helpers.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_contention.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_crashes.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_follow.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_lock_timeout.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_memory.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_waiters.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_watchers.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_benchbox_all.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_benchbox_contention.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_benchbox_plot.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_benchbox_roundtrip.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_benchbox_stream.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_bounded_dict.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_bounded_list.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_bounded_set.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_box_sync_async.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_capsule.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_doc_examples.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_doc_tutorials.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_events_async.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_events_future.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_events_signals.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_events_sync.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_follow.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_fork.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_layout.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_codec.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_liveness.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_types.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_wait.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_waiters.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_bounded.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_bounded_dict.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_bounded_set.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_codec.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_forged.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_layout.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_numpy.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_stateful.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_types.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_refs_lazy.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream_fork.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_choices.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_collections.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_numpy.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_records_attrs.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_records_msgspec.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_scalars.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_unions.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/type_checks/array_types.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/type_checks/bounded_types.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/type_checks/events_types.py +0 -0
- {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/type_checks/stream_types.py +0 -0
|
@@ -9,6 +9,108 @@ Dates are marked as `DD-MM-YYYY`
|
|
|
9
9
|
|
|
10
10
|
<!-- towncrier release notes start -->
|
|
11
11
|
|
|
12
|
+
## [0.8.0](https://github.com/jacopoabramo/sharedbox/releases/tag/v0.8.0) - 30-10-2026
|
|
13
|
+
|
|
14
|
+
### Breaking
|
|
15
|
+
|
|
16
|
+
- `sharedbox.Shape` takes its dimensions as one tuple, `Shape((480, 640))`, in
|
|
17
|
+
place of one argument per dimension. See
|
|
18
|
+
[How to migrate from 0.7](https://jacopoabramo.github.io/sharedbox/how-to/migrate-from-0.7/). ([#88](https://github.com/jacopoabramo/sharedbox/pull/88))
|
|
19
|
+
- `SharedBox` reserves the names `metadata`, `metadata_version` and
|
|
20
|
+
`metadata_version_of`: a field or `InitVar` with one of them raises
|
|
21
|
+
`TypeError` when the class is defined. Rename the field:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
# 0.7
|
|
25
|
+
class Parcel(SharedBox):
|
|
26
|
+
metadata: Annotated[str, Capacity(64)] = ""
|
|
27
|
+
|
|
28
|
+
# 0.8
|
|
29
|
+
class Parcel(SharedBox):
|
|
30
|
+
label: Annotated[str, Capacity(64)] = ""
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`SharedBox.update` takes the keyword `metadata`, so a type checker refuses
|
|
34
|
+
`update(**values)` when the values of the dictionary have a type that the
|
|
35
|
+
box's metadata type does not accept, such as `str`:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
# 0.7
|
|
39
|
+
def restock(parcel: Parcel, values: dict[str, str]) -> None:
|
|
40
|
+
parcel.update(**values)
|
|
41
|
+
|
|
42
|
+
# 0.8
|
|
43
|
+
def restock(parcel: Parcel, values: dict[str, Any]) -> None:
|
|
44
|
+
parcel.update(**values)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
See
|
|
48
|
+
[How to migrate from 0.7](https://jacopoabramo.github.io/sharedbox/how-to/migrate-from-0.7/). ([#91](https://github.com/jacopoabramo/sharedbox/pull/91))
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- `MaxShape`: an array of a stream item declared with a maximum shape, as in
|
|
53
|
+
`Annotated[npt.NDArray[np.uint16], MaxShape((2048, 2048))]`, stores its own
|
|
54
|
+
shape with each item, as kind 75 of stream layout 1.2. `writing()` views the
|
|
55
|
+
slot in the shape of the template's array, `slot.send` publishes the first
|
|
56
|
+
rows of that view without a copy, and `receive_into` returns a view of the
|
|
57
|
+
stored shape over `out`. ([#88](https://github.com/jacopoabramo/sharedbox/pull/88))
|
|
58
|
+
- `SharedStream.sender(on_full="release")`: a send that has waited its whole
|
|
59
|
+
`timeout` releases the lossless readers a full ring behind and sends, as in
|
|
60
|
+
`stream.sender(on_full="release").send(item, timeout=0.1)`. Each released
|
|
61
|
+
reader raises `ReaderReleased`, whose `missed` counts the items it lost, and
|
|
62
|
+
goes on at the newest item; `ReceiveEvents.released` reports the count to
|
|
63
|
+
callbacks. `SharedSendStream.asend` and `awriting` take a `timeout`. In
|
|
64
|
+
`stream.hpp`, `send`, `send_with` and `begin` take an `on_full` policy, and a
|
|
65
|
+
released reader's `receive` gives `status::released`, in stream layout 1.3. ([#89](https://github.com/jacopoabramo/sharedbox/pull/89))
|
|
66
|
+
- `MetadataFullError` and `MetadataGoneError`: raised when an add finds no
|
|
67
|
+
room for a version, and when the version a read asks for is no longer held. ([#91](https://github.com/jacopoabramo/sharedbox/pull/91))
|
|
68
|
+
- `SharedStream.metadata`: a stream keeps metadata the same way.
|
|
69
|
+
`SharedStream.create(Reading, capacity=8, metadata={"unit": "kg"})` stores
|
|
70
|
+
version 1. `SharedSendStream.send`, `asend`, `send_nowait` and
|
|
71
|
+
`SendSlot.send` take `metadata=` and add a version that applies from that
|
|
72
|
+
item. `SharedReceiveStream.metadata` and `metadata_version` give the version
|
|
73
|
+
of the item the reader received last. `SharedStream.create` takes
|
|
74
|
+
`metadata_capacity` and `metadata_versions`, and `create` and `attach` take
|
|
75
|
+
`metadata_type`. `sharedbox` 0.7.0 and earlier cannot open a stream created
|
|
76
|
+
with metadata or a `metadata_capacity`. ([#91](https://github.com/jacopoabramo/sharedbox/pull/91))
|
|
77
|
+
- `box.hpp`, `stream.hpp` and `sharedbox_c.h`: box layout 3.2 and stream
|
|
78
|
+
layout 1.4 hold metadata after the record or the description table.
|
|
79
|
+
`handle` and `stream` gain `metadata_head` and `read_metadata`, and
|
|
80
|
+
`stream_reader` gains `metadata_version_at`. `handle` and `stream_sender`
|
|
81
|
+
gain `add_metadata`. `handle::write(values, doc, lock_timeout)` and
|
|
82
|
+
`write_large` with the same arguments write fields and add a version in one
|
|
83
|
+
write, and
|
|
84
|
+
`stream_sender::can_add_metadata` checks an add without making it.
|
|
85
|
+
`sharedbox_c.h` adds `sbx_metadata_head`, `sbx_read_metadata`,
|
|
86
|
+
`sbx_add_metadata` and `SBX_E_METADATA_FULL`. ([#91](https://github.com/jacopoabramo/sharedbox/pull/91))
|
|
87
|
+
- `SharedBox.metadata`: a box keeps metadata, JSON-like values stored as CBOR
|
|
88
|
+
in its segment, in numbered versions. `Scale.create(name, metadata={"unit":
|
|
89
|
+
"kg"})` stores version 1, and `SharedBox.update(metadata=...)` adds the next
|
|
90
|
+
one from any process, with or without field values, in one write.
|
|
91
|
+
`metadata`, `metadata_version` and `metadata_version_of` read the versions,
|
|
92
|
+
a map as a read-only mapping and a list as a tuple. `BoxEvents.metadata` is
|
|
93
|
+
emitted with each new version. The class keywords `metadata_capacity` and
|
|
94
|
+
`metadata_versions` size the room for versions, and
|
|
95
|
+
`class Scale(SharedBox[Calibration])` gives the metadata's type to type
|
|
96
|
+
checkers. ([#91](https://github.com/jacopoabramo/sharedbox/pull/91))
|
|
97
|
+
|
|
98
|
+
### Changed
|
|
99
|
+
|
|
100
|
+
- `sharedbox.DType` is a `StrEnum` with one member per element type:
|
|
101
|
+
`DType(numpy.uint8)` returns `DType.uint8`, which equals `"uint8"`. ([#88](https://github.com/jacopoabramo/sharedbox/pull/88))
|
|
102
|
+
|
|
103
|
+
### Fixed
|
|
104
|
+
|
|
105
|
+
- `SharedBox` and `SharedStream` at exit: the `atexit` hook of `sharedbox`
|
|
106
|
+
closes every box and stream the process still has open and releases the
|
|
107
|
+
native objects of `SharedBox` subclasses, their watches and events groups,
|
|
108
|
+
so nanobind reports no leaked instances when a box class defined at module
|
|
109
|
+
level outlives shutdown. A box or stream used after the hook raises
|
|
110
|
+
`BoxClosedError` or `StreamClosedError`, and creating or attaching a box
|
|
111
|
+
raises `BoxClosedError`. ([#90](https://github.com/jacopoabramo/sharedbox/pull/90))
|
|
112
|
+
|
|
113
|
+
|
|
12
114
|
## [0.7.0](https://github.com/jacopoabramo/sharedbox/releases/tag/v0.7.0) - 28-10-2026
|
|
13
115
|
|
|
14
116
|
### Added
|
|
@@ -13,11 +13,11 @@ the layout and the native module converts values.
|
|
|
13
13
|
```text
|
|
14
14
|
sharedbox/
|
|
15
15
|
|-- include/sharedbox/
|
|
16
|
-
| |-- core.hpp what every segment kind uses: result and error, liveness, names, mappings, the open step, waiter slots, the type codec
|
|
17
|
-
| |-- box.hpp box layout 3.
|
|
18
|
-
| |-- stream.hpp stream layout 1.
|
|
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.2 and its protocols: create, publish, open, lock, read, write, metadata, capsule handle
|
|
18
|
+
| |-- stream.hpp stream layout 1.4: create, open, the sender, readers, the gate, dead ends, metadata
|
|
19
19
|
| |-- sharedbox.hpp includes every kind; the header consumers include
|
|
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_
|
|
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)
|
|
22
22
|
|-- cmake/
|
|
23
23
|
| |-- sharedbox-config.cmake find_package(sharedbox) from an installed wheel; the build writes
|
|
@@ -28,11 +28,14 @@ sharedbox/
|
|
|
28
28
|
| |-- _box.py SharedBox: class keywords, fields, fields(), create/attach, __post_init__, update, snapshot, unlink, __sharedbox_box__
|
|
29
29
|
| |-- _layout.py Capacity, field() and Field, field offsets and kind codes, schema hash
|
|
30
30
|
| |-- _types.py annotation parsing into TypeSpec, the description table
|
|
31
|
-
| |-- _arrays.py Shape, DType, SupportsDLPack, register_array_type
|
|
31
|
+
| |-- _arrays.py Shape, MaxShape, DType, SupportsDLPack, register_array_type
|
|
32
32
|
| |-- _events.py BoxEvents, FieldWatch, the watcher thread
|
|
33
33
|
| |-- _follow.py BoxEvents.follow and unfollow: forwarding the events of boxes that references reach
|
|
34
34
|
| |-- _stream.py SharedStream, SharedSendStream, SharedReceiveStream, ReceiveEvents, the statistics classes, the worker threads
|
|
35
|
+
| |-- _metadata.py metadata documents: the type variable M, CBOR encoding, read-only decoding, the cache of decoded versions
|
|
35
36
|
| |-- _refs.py reference fields: BoxRef, the class registry, BrokenReferenceError, UnknownBoxClassError
|
|
37
|
+
| |-- _exit.py the exit handler: stops the watcher and stream threads, closes boxes, streams and ends,
|
|
38
|
+
| | replaces the native objects of watchers and box classes
|
|
36
39
|
| |-- _native.pyi hand-written stub for the extension
|
|
37
40
|
| |-- py.typed
|
|
38
41
|
| |-- benchmarks/ the benchbox command (extra: benchmarks)
|
|
@@ -67,7 +70,9 @@ sharedbox/
|
|
|
67
70
|
| |-- test_follow.py BoxEvents.follow, unfollow and nested
|
|
68
71
|
| |-- test_native_waiters.py waiter slots, dead owners, interrupt
|
|
69
72
|
| |-- test_box_sync_async.py a sync and an async watch on one box in two threads
|
|
73
|
+
| |-- test_metadata.py metadata of a box: documents, versions, events, typed access, reserved names
|
|
70
74
|
| |-- test_stream.py SharedStream, sender and reader, blocking and non-blocking calls
|
|
75
|
+
| |-- test_stream_metadata.py metadata of a stream: the version of each item, in one process and across processes
|
|
71
76
|
| |-- test_stream_async.py asyncio calls on a stream, cancellation
|
|
72
77
|
| |-- test_stream_events.py ReceiveEvents delivery
|
|
73
78
|
| |-- test_stream_processes.py streams across processes, dead senders and readers
|
|
@@ -91,7 +96,7 @@ sharedbox/
|
|
|
91
96
|
| |-- examples/ one script per how-to guide, included by the guide
|
|
92
97
|
| |-- javascripts/charts.js draws each div of class sbx-chart from a plotly figure JSON file
|
|
93
98
|
| |-- assets/benchmarks/ the figure JSON files of the performance page, from benchbox plot
|
|
94
|
-
| `-- reference/segment-layout.md core 1.0, box layout 3.1, names and protocols
|
|
99
|
+
| `-- reference/segment-layout.md core 1.0, box layout 3.2, stream layout 1.4, names and protocols
|
|
95
100
|
|-- includes/abbreviations.md acronym tooltips appended to every page
|
|
96
101
|
|-- zensical.toml site configuration and navigation
|
|
97
102
|
|-- .github/workflows/ci.yaml lint, docs check, C++ tests, cibuildwheel wheels, tests, stress,
|
|
@@ -135,18 +140,19 @@ class's identity (the `identity` class keyword, by default
|
|
|
135
140
|
always asks for a new name and raises `SegmentExistsError` if it is taken.
|
|
136
141
|
|
|
137
142
|
Every segment starts with the same 64-byte line, whatever its kind. Core
|
|
138
|
-
version 1.0, box layout 3.
|
|
143
|
+
version 1.0, box layout 3.2, from offset 0:
|
|
139
144
|
|
|
140
145
|
- Common line 0, written once at creation: `magic` (`SBX_BOX_` for a box,
|
|
141
146
|
`SBX_STRM` for a stream), `core_major` 1, `core_minor` 0, `kind_major` 3,
|
|
142
|
-
`kind_minor`
|
|
147
|
+
`kind_minor` 2, `schema_hash`, `create_id`, `creator_start`,
|
|
143
148
|
`creator_pidns`, `creator_pid`, `waiter_slots`, `size`.
|
|
144
149
|
- Box line 1, at offset 64: `seq` (sequence lock; the generation is
|
|
145
150
|
`seq >> 1`, there is no generation field), `writer_pid`, `wake_word`,
|
|
146
151
|
`waiters` and `sleepers`, which writes and waits change; then written
|
|
147
152
|
once at creation `field_count`, `record_size`, `record`, `tail` (always
|
|
148
|
-
128)
|
|
149
|
-
|
|
153
|
+
128), `types_size`, `metadata_size` and `metadata` (the offset of the
|
|
154
|
+
metadata area, 0 for none); last `metadata_head`, the newest metadata
|
|
155
|
+
version, 0 for none, which each add changes. The header is 128 bytes.
|
|
150
156
|
- The tail: `field_count` field table entries of 8 bytes (`u32 offset`,
|
|
151
157
|
`u32 capacity_and_kind`: top 8 bits the kind code, the low 24 the
|
|
152
158
|
capacity or size, or for kinds 64 and up the offset of the field's
|
|
@@ -155,10 +161,15 @@ version 1.0, box layout 3.1, from offset 0:
|
|
|
155
161
|
`asleep_on`: the offset of the count the slot's current wait added 1 to,
|
|
156
162
|
which freeing a dead owner's slot takes back), then the description
|
|
157
163
|
table of `types_size` bytes. Kinds are 0 to 5, 6 to 12 (fixed-size
|
|
158
|
-
scalars), 13 to 23 (fixed-width numbers and `datetime64`) and 64 to
|
|
159
|
-
(described types).
|
|
160
|
-
- The record, at a multiple of 64.
|
|
161
|
-
|
|
164
|
+
scalars), 13 to 23 (fixed-width numbers and `datetime64`) and 64 to 75
|
|
165
|
+
(described types; 75, an array up to a maximum shape, only in a stream).
|
|
166
|
+
- The record, at a multiple of 64. `record + record_size <= 2**32 - 4096`.
|
|
167
|
+
- The metadata area, when `metadata` is not 0, at the first multiple of 64
|
|
168
|
+
after the record, `metadata + metadata_size <= 2**32 - 4096`: a 64-byte
|
|
169
|
+
line (`entries`, `ring_size`), `entries` version entries of 32 bytes
|
|
170
|
+
(`seq`, `version`, `position`, `offset`, `length`), then a ring of
|
|
171
|
+
`ring_size` bytes holding one CBOR document per version. The mapping size
|
|
172
|
+
is rounded up to 4 KiB.
|
|
162
173
|
|
|
163
174
|
`magic` is stored last on create, with release ordering, after the initial
|
|
164
175
|
values are in the record; attach waits for it. Attach copies line 0 and the
|
|
@@ -174,8 +185,11 @@ layout 1.0 or 2.0, which 0.5 and earlier wrote.
|
|
|
174
185
|
A stream's segment starts with a 256-byte header: the common line, a line
|
|
175
186
|
of geometry written once, the sender's line and the readers' line. Then
|
|
176
187
|
come a 64-byte entry per reader, the waiter slots, `capacity` slots each
|
|
177
|
-
holding a `seq` and one item,
|
|
178
|
-
|
|
188
|
+
holding a `seq` and one item, the description table, and the metadata
|
|
189
|
+
area laid out as a box's. Stream layout 1.4 keeps `metadata_size` and
|
|
190
|
+
`metadata` in the geometry line and `metadata_head` in the sender's line.
|
|
191
|
+
Every offset is 64-bit. The full specification is in
|
|
192
|
+
`docs/reference/segment-layout.md`.
|
|
179
193
|
|
|
180
194
|
### Record encoding
|
|
181
195
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sharedbox
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Python containers using shared memory.
|
|
5
5
|
Author-Email: Jacopo Abramo <jacopo.abramo@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -14,7 +14,9 @@ Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
|
|
|
14
14
|
Classifier: Programming Language :: C++
|
|
15
15
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
16
|
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: cbor2>=6.1.5
|
|
17
18
|
Requires-Dist: psygnal>=0.16.1
|
|
19
|
+
Requires-Dist: typing-extensions>=4.16.0; python_full_version < "3.13"
|
|
18
20
|
Provides-Extra: benchmarks
|
|
19
21
|
Requires-Dist: numpy>=2.4.6; extra == "benchmarks"
|
|
20
22
|
Requires-Dist: plotly>=7.1.0; extra == "benchmarks"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""The script of the guide "How to attach metadata to a box or stream"."""
|
|
2
|
+
|
|
3
|
+
# --8<-- [start:box]
|
|
4
|
+
from typing import TypedDict
|
|
5
|
+
|
|
6
|
+
from sharedbox import SharedBox, SharedStream
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# --8<-- [start:calibration]
|
|
10
|
+
class Calibration(TypedDict):
|
|
11
|
+
unit: str
|
|
12
|
+
offset: float
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# --8<-- [end:calibration]
|
|
16
|
+
class Scale(SharedBox[Calibration]):
|
|
17
|
+
weight: float = 0.0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
scale = Scale.create("example-scale", metadata={"unit": "kg", "offset": 0.0})
|
|
21
|
+
print(scale.metadata_version) # 1
|
|
22
|
+
print(scale.metadata) # {'unit': 'kg', 'offset': 0.0}
|
|
23
|
+
# --8<-- [end:box]
|
|
24
|
+
|
|
25
|
+
# --8<-- [start:add]
|
|
26
|
+
scale.update(weight=1.2, metadata={"unit": "kg", "offset": 0.25})
|
|
27
|
+
print(scale.weight, scale.metadata_version) # 1.2 2
|
|
28
|
+
print(scale.metadata_version_of(1)["offset"]) # 0.0
|
|
29
|
+
print(scale.metadata_version_of(2)["offset"]) # 0.25
|
|
30
|
+
scale.update(metadata={"unit": "kg", "offset": 0.3})
|
|
31
|
+
print(scale.weight, scale.metadata_version) # 1.2 3
|
|
32
|
+
# --8<-- [end:add]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# --8<-- [start:capacity]
|
|
36
|
+
class Display(SharedBox[Calibration], metadata_capacity=4096, metadata_versions=8):
|
|
37
|
+
weight: float = 0.0
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
display = Display.create("example-display")
|
|
41
|
+
print(display.metadata) # None
|
|
42
|
+
display.update(metadata={"unit": "g", "offset": 0.0})
|
|
43
|
+
# --8<-- [end:capacity]
|
|
44
|
+
|
|
45
|
+
# --8<-- [start:read-only]
|
|
46
|
+
stored = scale.metadata_version_of(2)
|
|
47
|
+
try:
|
|
48
|
+
stored["offset"] = 0.5
|
|
49
|
+
except TypeError as error:
|
|
50
|
+
print(error) # 'mappingproxy' object does not support item assignment
|
|
51
|
+
scale.update(metadata={**stored, "offset": 0.5})
|
|
52
|
+
# --8<-- [end:read-only]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# --8<-- [start:stream]
|
|
56
|
+
with SharedStream.create(
|
|
57
|
+
float,
|
|
58
|
+
"example-scale:readings",
|
|
59
|
+
capacity=8,
|
|
60
|
+
metadata={"unit": "kg", "offset": 0.0},
|
|
61
|
+
metadata_type=Calibration,
|
|
62
|
+
) as stream:
|
|
63
|
+
reader = stream.reader()
|
|
64
|
+
with stream.sender() as sender:
|
|
65
|
+
sender.send(1.5)
|
|
66
|
+
sender.send(1.75, metadata={"unit": "kg", "offset": 0.25})
|
|
67
|
+
for weight in reader:
|
|
68
|
+
calibration = reader.metadata
|
|
69
|
+
if calibration is not None:
|
|
70
|
+
print(reader.metadata_version, weight - calibration["offset"])
|
|
71
|
+
# prints 1 1.5, then 2 1.5
|
|
72
|
+
SharedStream.unlink("example-scale:readings")
|
|
73
|
+
# --8<-- [end:stream]
|
|
74
|
+
|
|
75
|
+
scale.close()
|
|
76
|
+
display.close()
|
|
77
|
+
Scale.unlink("example-scale")
|
|
78
|
+
Display.unlink("example-display")
|
|
@@ -12,6 +12,8 @@ from sharedbox import (
|
|
|
12
12
|
Capacity,
|
|
13
13
|
DType,
|
|
14
14
|
EndOfStream,
|
|
15
|
+
MaxShape,
|
|
16
|
+
ReaderReleased,
|
|
15
17
|
Shape,
|
|
16
18
|
SharedReceiveStream,
|
|
17
19
|
SharedSendStream,
|
|
@@ -23,7 +25,7 @@ from sharedbox import (
|
|
|
23
25
|
class Letter:
|
|
24
26
|
index: int
|
|
25
27
|
recipient: Annotated[str, Capacity(32)]
|
|
26
|
-
scan: Annotated[np.ndarray, Shape(32, 32), DType("uint8")]
|
|
28
|
+
scan: Annotated[np.ndarray, Shape((32, 32)), DType("uint8")]
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
# --8<-- [end:types]
|
|
@@ -93,6 +95,50 @@ def send_filled(sender: SharedSendStream[Letter], count: int) -> None:
|
|
|
93
95
|
# --8<-- [end:writing]
|
|
94
96
|
|
|
95
97
|
|
|
98
|
+
# --8<-- [start:shapes]
|
|
99
|
+
@dataclass(frozen=True)
|
|
100
|
+
class Sack:
|
|
101
|
+
index: int
|
|
102
|
+
weights: Annotated[np.ndarray, MaxShape((64,)), DType("float32")]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def send_sacks(sender: SharedSendStream[Sack]) -> None:
|
|
106
|
+
sender.send(Sack(0, np.array([1.5, 0.25, 4.0], np.float32)))
|
|
107
|
+
template = Sack(1, np.empty(64, np.float32))
|
|
108
|
+
with sender.writing(template) as slot:
|
|
109
|
+
weights = slot.item.weights
|
|
110
|
+
weights[:5] = 2.5
|
|
111
|
+
slot.send(replace(slot.item, weights=weights[:5]))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def read_sacks(reader: SharedReceiveStream[Sack]) -> None:
|
|
115
|
+
weights = np.empty(64, np.float32)
|
|
116
|
+
for sack in reader.iter_into({"weights": weights}):
|
|
117
|
+
print(sack.index, sack.weights.shape)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# --8<-- [end:shapes]
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# --8<-- [start:release]
|
|
124
|
+
def sort_without_stalling(sender: SharedSendStream[Letter], count: int) -> None:
|
|
125
|
+
for i in range(count):
|
|
126
|
+
sender.send(Letter(i, "Ada", np.full((32, 32), i, np.uint8)), timeout=0.1)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def file_letters(reader: SharedReceiveStream[Letter]) -> None:
|
|
130
|
+
while True:
|
|
131
|
+
try:
|
|
132
|
+
for letter in reader:
|
|
133
|
+
print("filed", letter.index)
|
|
134
|
+
return
|
|
135
|
+
except ReaderReleased as released:
|
|
136
|
+
print("missed", released.missed)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# --8<-- [end:release]
|
|
140
|
+
|
|
141
|
+
|
|
96
142
|
# --8<-- [start:main]
|
|
97
143
|
if __name__ == "__main__":
|
|
98
144
|
context = mp.get_context("spawn")
|
|
@@ -127,3 +173,15 @@ if __name__ == "__main__":
|
|
|
127
173
|
for letter in reader:
|
|
128
174
|
print(letter.index, int(letter.scan[0, 0]))
|
|
129
175
|
SharedStream.unlink("example-sorting:filled")
|
|
176
|
+
with SharedStream.create(Sack, "example-sorting:sacks", capacity=4) as sacks:
|
|
177
|
+
sack_reader = sacks.reader()
|
|
178
|
+
with sacks.sender() as sack_sender:
|
|
179
|
+
send_sacks(sack_sender)
|
|
180
|
+
read_sacks(sack_reader)
|
|
181
|
+
SharedStream.unlink("example-sorting:sacks")
|
|
182
|
+
with SharedStream.create(Letter, "example-sorting:release", capacity=4) as stream:
|
|
183
|
+
reader = stream.reader()
|
|
184
|
+
with stream.sender(on_full="release") as sender:
|
|
185
|
+
sort_without_stalling(sender, 6)
|
|
186
|
+
file_letters(reader)
|
|
187
|
+
SharedStream.unlink("example-sorting:release")
|
|
@@ -13,7 +13,9 @@ from sharedbox import DType, Shape, SharedBox, SupportsDLPack, field
|
|
|
13
13
|
class Scanner(SharedBox, name="example-scanner"):
|
|
14
14
|
scan: npt.NDArray[np.uint8] = np.zeros((4, 6), np.uint8)
|
|
15
15
|
gain: npt.NDArray[np.float64] = field(shape=(2,), default=np.ones(2))
|
|
16
|
-
raw: Annotated[SupportsDLPack, Shape(3), DType("float32")] = np.zeros(
|
|
16
|
+
raw: Annotated[SupportsDLPack, Shape((3,)), DType("float32")] = np.zeros(
|
|
17
|
+
3, np.float32
|
|
18
|
+
)
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
# --8<-- [end:declare]
|
|
@@ -88,6 +88,27 @@ Lossy is a [reader](#reader) mode in which the reader skips the items the
|
|
|
88
88
|
[`missed`][sharedbox.SharedReceiveStream.missed]. See
|
|
89
89
|
[`reader`][sharedbox.SharedStream.reader].
|
|
90
90
|
|
|
91
|
+
### Metadata
|
|
92
|
+
|
|
93
|
+
Metadata is a value a [box](#box) or [stream](#stream) keeps in its
|
|
94
|
+
[segment](#segment) to say what its fields or items mean, such as their
|
|
95
|
+
units or the calibration they were taken with. Every process reads it from
|
|
96
|
+
the segment, and adding a new value adds a
|
|
97
|
+
[metadata version](#metadata-version). The `metadata` of
|
|
98
|
+
[`field`][sharedbox.field] is a different thing, kept in each process's
|
|
99
|
+
class. See
|
|
100
|
+
[How to attach metadata to a box or stream](../how-to/attach-metadata.md).
|
|
101
|
+
|
|
102
|
+
### Metadata version
|
|
103
|
+
|
|
104
|
+
A metadata version is one value of a box's or stream's
|
|
105
|
+
[metadata](#metadata), numbered from 1. Adding metadata adds a version and
|
|
106
|
+
leaves the earlier ones as they were, until the box or stream lets the
|
|
107
|
+
oldest go to make room for new ones. A stream item belongs to the newest
|
|
108
|
+
version its [sender](#sender) added with that item or before it. See
|
|
109
|
+
[`update`][sharedbox.SharedBox.update] and
|
|
110
|
+
[`send`][sharedbox.SharedSendStream.send].
|
|
111
|
+
|
|
91
112
|
### Reader
|
|
92
113
|
|
|
93
114
|
A reader is an object that receives the items of a [stream](#stream) in one
|
|
@@ -95,6 +116,15 @@ process. Each reader has its own position in the stream and a mode:
|
|
|
95
116
|
[lossless](#lossless), [lossy](#lossy) or [latest](#latest). See
|
|
96
117
|
[`SharedReceiveStream`][sharedbox.SharedReceiveStream].
|
|
97
118
|
|
|
119
|
+
### Released reader
|
|
120
|
+
|
|
121
|
+
A released reader is a [lossless](#lossless) [reader](#reader) that the
|
|
122
|
+
[sender](#sender) stopped waiting for: the sender was opened with
|
|
123
|
+
`on_full="release"`, and a send waited its whole timeout. Its next receive raises
|
|
124
|
+
[`ReaderReleased`][sharedbox.ReaderReleased] with the number of items it
|
|
125
|
+
lost, and it goes on at the newest item. See
|
|
126
|
+
[`sender`][sharedbox.SharedStream.sender].
|
|
127
|
+
|
|
98
128
|
### Reference field
|
|
99
129
|
|
|
100
130
|
A reference field is a [field](#field) annotated with another `SharedBox`
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/wrench
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# How to attach metadata to a box or stream
|
|
6
|
+
|
|
7
|
+
A [box](../explanation/glossary.md#box) or a
|
|
8
|
+
[stream](../explanation/glossary.md#stream) carries values, but not what
|
|
9
|
+
they mean, such as the unit of a weight or the calibration a reading was
|
|
10
|
+
taken with.
|
|
11
|
+
[Metadata](../explanation/glossary.md#metadata) carries that, in the same
|
|
12
|
+
[segment](../explanation/glossary.md#segment), so every process that opens
|
|
13
|
+
the box or stream reads it from there, in Python, C or C++. Each change
|
|
14
|
+
adds a new [metadata version](../explanation/glossary.md#metadata-version)
|
|
15
|
+
and keeps the older ones for a while, so a reader can tell which version
|
|
16
|
+
an item was sent under.
|
|
17
|
+
|
|
18
|
+
The `metadata` you pass to [`field`][sharedbox.field] is a different
|
|
19
|
+
thing: a mapping kept in each process's class and never stored in the
|
|
20
|
+
segment, from which `sharedbox` reads only its own keys, such as `"shape"`.
|
|
21
|
+
|
|
22
|
+
## Create a box with metadata
|
|
23
|
+
|
|
24
|
+
Give the type of the metadata as the type argument of `SharedBox`, and pass
|
|
25
|
+
the first value as `metadata` when you create the box:
|
|
26
|
+
|
|
27
|
+
```{.python}
|
|
28
|
+
--8<-- "docs/examples/attach_metadata.py:box"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Here the metadata is a `TypedDict`, so a type checker knows what
|
|
32
|
+
[`metadata`][sharedbox.SharedBox.metadata] holds and checks every value you
|
|
33
|
+
add. Without a type argument, as in `class Scale(SharedBox)`, the metadata
|
|
34
|
+
is a `Mapping[str, Any]`. `metadata` gives the value of the newest version,
|
|
35
|
+
or `None` for a box that has none, and
|
|
36
|
+
[`metadata_version`][sharedbox.SharedBox.metadata_version] gives its number,
|
|
37
|
+
counting from 1.
|
|
38
|
+
|
|
39
|
+
A value is `None`, a `bool`, an `int`, a `float`, a `str`, `bytes`, a list
|
|
40
|
+
or tuple, or a mapping with `str` keys, nested up to 32 levels;
|
|
41
|
+
[`update`][sharedbox.SharedBox.update] lists the limits. The
|
|
42
|
+
type argument must be a mapping type, but only a type checker holds you to
|
|
43
|
+
that: at run time a value that is not a mapping is stored all the same.
|
|
44
|
+
Calling the class takes the `metadata` keyword too, but a type checker
|
|
45
|
+
accepts it only in `create`, where it does not check the value.
|
|
46
|
+
|
|
47
|
+
## Add a version
|
|
48
|
+
|
|
49
|
+
Any process that has the box open adds a version by passing `metadata` to
|
|
50
|
+
[`update`][sharedbox.SharedBox.update]. The call may also name fields, and
|
|
51
|
+
then the new values and the new version go in as one write, so a reader
|
|
52
|
+
that reads the fields and then `metadata` gets the version written with
|
|
53
|
+
them, or a newer one. Without fields, the call only adds the version:
|
|
54
|
+
|
|
55
|
+
```{.python}
|
|
56
|
+
--8<-- "docs/examples/attach_metadata.py:add"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The first call writes `weight` and version 2 together, and the second adds
|
|
60
|
+
version 3. An `update` that raises, because a value is out of range or the
|
|
61
|
+
box has no room for the version, writes neither the fields nor the version,
|
|
62
|
+
and `metadata=None`, the default, adds no version. Every process that
|
|
63
|
+
listens to the `metadata` signal of the box's
|
|
64
|
+
[`events`][sharedbox.SharedBox.events] gets the new version's number, once
|
|
65
|
+
for several versions added close together, and gets it before the signals
|
|
66
|
+
of the fields written with it:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
scale.events.metadata.connect(lambda version: print("calibration", version))
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
[`metadata_version_of`][sharedbox.SharedBox.metadata_version_of] reads an
|
|
73
|
+
older version for as long as the box keeps it. The box keeps the last
|
|
74
|
+
`metadata_versions` versions, 64 unless the class sets that keyword, or
|
|
75
|
+
fewer when they no longer fit in its room for metadata. Reading a version
|
|
76
|
+
it has let go raises [`MetadataGoneError`][sharedbox.MetadataGoneError].
|
|
77
|
+
|
|
78
|
+
## Leave room for metadata added later
|
|
79
|
+
|
|
80
|
+
A box created with `metadata` gets 65536 bytes of room for its metadata,
|
|
81
|
+
and one created without it gets none, so `update(metadata=...)` on it raises
|
|
82
|
+
[`MetadataFullError`][sharedbox.MetadataFullError]. When the first version
|
|
83
|
+
comes after the box is created, give the class a `metadata_capacity`:
|
|
84
|
+
|
|
85
|
+
```{.python}
|
|
86
|
+
--8<-- "docs/examples/attach_metadata.py:capacity"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The capacity is the number of bytes all the kept versions share, 0 or at
|
|
90
|
+
least 24. One version can take up to a third of it, rounded down to a
|
|
91
|
+
multiple of 8, and a longer value raises `ValueError` and adds nothing. A
|
|
92
|
+
value is stored as CBOR, a compact binary form of JSON-like values, so
|
|
93
|
+
`{"unit": "kg", "offset": 0.0}` takes 19 bytes. `metadata_versions`, 2 to 65535, limits
|
|
94
|
+
how many versions the box keeps. A stream takes both as keywords of
|
|
95
|
+
[`create`][sharedbox.SharedStream.create].
|
|
96
|
+
|
|
97
|
+
## Attach metadata to a stream
|
|
98
|
+
|
|
99
|
+
A stream takes its first version as `metadata` in
|
|
100
|
+
[`create`][sharedbox.SharedStream.create], and its sender adds each later
|
|
101
|
+
one with the `metadata` of [`send`][sharedbox.SharedSendStream.send].
|
|
102
|
+
[`asend`][sharedbox.SharedSendStream.asend],
|
|
103
|
+
[`send_nowait`][sharedbox.SharedSendStream.send_nowait] and the `send` of
|
|
104
|
+
the slot that [`writing`][sharedbox.SharedSendStream.writing] gives take
|
|
105
|
+
the keyword too. A version applies from the item sent with it, and a
|
|
106
|
+
reader's [`metadata`][sharedbox.SharedReceiveStream.metadata] and
|
|
107
|
+
[`metadata_version`][sharedbox.SharedReceiveStream.metadata_version]
|
|
108
|
+
describe the item it received last, so the reader can tell which version
|
|
109
|
+
each item was sent under. The metadata of this stream is a `TypedDict` too:
|
|
110
|
+
|
|
111
|
+
```{.python}
|
|
112
|
+
--8<-- "docs/examples/attach_metadata.py:calibration"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```{.python}
|
|
116
|
+
--8<-- "docs/examples/attach_metadata.py:stream"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The reader prints version 1 for the first reading, which was sent before
|
|
120
|
+
version 2 existed, and version 2 for the reading sent with it. Before its
|
|
121
|
+
first item, a reader's `metadata_version` is 0
|
|
122
|
+
and its `metadata` is `None`, and the same holds for an item sent before the
|
|
123
|
+
stream had any version. The stream's own
|
|
124
|
+
[`metadata`][sharedbox.SharedStream.metadata] gives the newest version, as
|
|
125
|
+
a box's does.
|
|
126
|
+
|
|
127
|
+
A type checker takes the stream's metadata type from `metadata_type`,
|
|
128
|
+
given to `create` as above or to `attach` in another process, as in
|
|
129
|
+
`SharedStream.attach(float, name, metadata_type=Calibration)`. Without
|
|
130
|
+
it the metadata is a `Mapping[str, Any]`, whatever `metadata` you pass.
|
|
131
|
+
|
|
132
|
+
A stream keeps every version that an item still in its ring was sent
|
|
133
|
+
under, and keeps version 1 while an item sent before it is in the ring,
|
|
134
|
+
so that a reader can tell such an item from one whose version is gone. A
|
|
135
|
+
send with `metadata` that would let such a version go raises
|
|
136
|
+
`MetadataFullError` before it waits for room in the ring, so it sends
|
|
137
|
+
nothing and releases no reader; the slot's `send` in a `writing` block
|
|
138
|
+
raises it the same way and leaves the slot reserved. The same send
|
|
139
|
+
succeeds once the sender has sent enough further items that none of
|
|
140
|
+
those items is left in the ring.
|
|
141
|
+
Once a reader's last item has left the ring, though, its version can go,
|
|
142
|
+
and the reader's `metadata` then raises `MetadataGoneError`. A lossy or
|
|
143
|
+
latest reader meets this first, because the sender may overwrite its item
|
|
144
|
+
right after the reader receives it.
|
|
145
|
+
|
|
146
|
+
!!! warning "A callback on the main thread can see a later item's version"
|
|
147
|
+
The reader's `metadata` follows the item the reader received last,
|
|
148
|
+
which may be later than the item a callback was given. A callback of
|
|
149
|
+
[`reader.events`][sharedbox.SharedReceiveStream.events] connected with
|
|
150
|
+
`thread="main"` runs after the reader's thread has received its item,
|
|
151
|
+
and that thread may have received the next one by then. Read
|
|
152
|
+
`metadata` in a callback that runs on the reader's thread, and pass it
|
|
153
|
+
on with the item.
|
|
154
|
+
|
|
155
|
+
!!! warning "sharedbox 0.7 cannot open a stream with metadata"
|
|
156
|
+
`sharedbox` 0.7.0 and earlier refuse a stream created with `metadata`
|
|
157
|
+
or a `metadata_capacity`, and raise
|
|
158
|
+
[`SchemaMismatchError`][sharedbox.SchemaMismatchError] saying its
|
|
159
|
+
header is corrupt. They open a box with metadata, and a stream created
|
|
160
|
+
without it, and ignore the metadata. Create a stream without metadata
|
|
161
|
+
while a process on 0.7 still opens it.
|
|
162
|
+
|
|
163
|
+
## Change a value you read
|
|
164
|
+
|
|
165
|
+
A value you read is a read-only view, because every read of a version
|
|
166
|
+
through the same box or stream shares one decoded copy. A mapping reads
|
|
167
|
+
back as a `types.MappingProxyType` and a list as a tuple, nested the same
|
|
168
|
+
way. A type checker still sees the metadata's type, though, so it accepts
|
|
169
|
+
a change that fails when it runs. To change a key, build a new value and
|
|
170
|
+
add it as the next version:
|
|
171
|
+
|
|
172
|
+
```{.python}
|
|
173
|
+
--8<-- "docs/examples/attach_metadata.py:read-only"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Read metadata from C or C++
|
|
177
|
+
|
|
178
|
+
A C program reads a box's versions with `sbx_metadata_head` and
|
|
179
|
+
`sbx_read_metadata`, and adds one with `sbx_add_metadata`. In C++, `handle`
|
|
180
|
+
and `stream` read versions with `metadata_head` and `read_metadata`,
|
|
181
|
+
`handle` and `stream_sender` add them with `add_metadata`, and
|
|
182
|
+
`stream_reader::metadata_version_at` finds the version an item was sent
|
|
183
|
+
under; streams have no C calls. Each version is one CBOR value, which your
|
|
184
|
+
program decodes with a CBOR library of its own.
|
|
185
|
+
[Segment layout](../reference/segment-layout.md#metadata) lists the calls
|
|
186
|
+
and the CBOR a version may hold.
|
|
187
|
+
|
|
188
|
+
??? example "The whole script"
|
|
189
|
+
|
|
190
|
+
```{.python}
|
|
191
|
+
--8<-- "docs/examples/attach_metadata.py"
|
|
192
|
+
```
|
|
@@ -12,6 +12,8 @@ you're trying to do; if `sharedbox` is new to you, the
|
|
|
12
12
|
|
|
13
13
|
- [How to install sharedbox](install-sharedbox.md): the wheels, the
|
|
14
14
|
platforms and the `benchmarks` extra
|
|
15
|
+
- [How to migrate from 0.7](migrate-from-0.7.md): the changes code written
|
|
16
|
+
for 0.7 needs
|
|
15
17
|
|
|
16
18
|
### Use a box or a stream
|
|
17
19
|
|
|
@@ -42,6 +44,9 @@ items through a [stream](../explanation/glossary.md#stream).
|
|
|
42
44
|
one callback for changes in every box a box refers to
|
|
43
45
|
- [How to send items through a stream](send-items-through-a-stream.md): one
|
|
44
46
|
sender, readers that receive every item or only the newest, and asyncio
|
|
47
|
+
- [How to attach metadata to a box or stream](attach-metadata.md): units or
|
|
48
|
+
calibration that every process reads, and the version each item was sent
|
|
49
|
+
under
|
|
45
50
|
|
|
46
51
|
### C and C++
|
|
47
52
|
|