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.
Files changed (283) hide show
  1. {sharedbox-0.7.0 → sharedbox-0.8.0}/CHANGELOG.md +102 -0
  2. {sharedbox-0.7.0 → sharedbox-0.8.0}/CLAUDE.md +30 -16
  3. {sharedbox-0.7.0 → sharedbox-0.8.0}/PKG-INFO +3 -1
  4. sharedbox-0.8.0/docs/examples/attach_metadata.py +78 -0
  5. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/send_items_through_a_stream.py +59 -1
  6. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_arrays.py +3 -1
  7. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/glossary.md +30 -0
  8. sharedbox-0.8.0/docs/how-to/attach-metadata.md +192 -0
  9. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/index.md +5 -0
  10. sharedbox-0.8.0/docs/how-to/migrate-from-0.7.md +83 -0
  11. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/send-items-through-a-stream.md +74 -7
  12. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-arrays.md +1 -1
  13. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/arrays.md +5 -0
  14. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/errors.md +15 -0
  15. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/events.md +1 -0
  16. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/segment-layout.md +402 -52
  17. {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/box.hpp +192 -20
  18. {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/core.hpp +491 -22
  19. {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/sharedbox_c.cpp +37 -0
  20. {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/sharedbox_c.h +15 -0
  21. {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/stream.hpp +267 -55
  22. {sharedbox-0.7.0 → sharedbox-0.8.0}/includes/abbreviations.md +1 -0
  23. {sharedbox-0.7.0 → sharedbox-0.8.0}/pyproject.toml +3 -1
  24. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/__init__.py +9 -1
  25. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_arrays.py +77 -23
  26. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_box.py +223 -46
  27. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_events.py +35 -7
  28. sharedbox-0.8.0/src/sharedbox/_exit.py +193 -0
  29. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_layout.py +6 -1
  30. sharedbox-0.8.0/src/sharedbox/_metadata.py +134 -0
  31. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/module.cpp +34 -24
  32. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/segment.cpp +77 -3
  33. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/segment.hpp +28 -7
  34. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/stream.cpp +198 -20
  35. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/types.cpp +132 -16
  36. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/types.hpp +32 -5
  37. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native.pyi +171 -11
  38. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_stream.py +469 -79
  39. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_types.py +53 -11
  40. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_version.py +3 -3
  41. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/ops.py +1 -1
  42. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/stream.py +2 -2
  43. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/CMakeLists.txt +1 -1
  44. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_c_smoke.c +17 -0
  45. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_c_smoke_box.cpp +2 -1
  46. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_layout3.cpp +69 -3
  47. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_mapping.cpp +3 -3
  48. sharedbox-0.8.0/tests/cpp/test_metadata.cpp +1022 -0
  49. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_stream.cpp +224 -2
  50. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_stream_layout.cpp +43 -2
  51. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_types.cpp +98 -2
  52. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/crossproc.py +11 -1
  53. sharedbox-0.8.0/tests/signatures.py +12 -0
  54. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/streamproc.py +77 -3
  55. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_read_into.py +2 -2
  56. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_box.py +2 -2
  57. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_events_bytes.py +1 -1
  58. sharedbox-0.8.0/tests/test_exit.py +257 -0
  59. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_fields.py +9 -8
  60. sharedbox-0.8.0/tests/test_metadata.py +509 -0
  61. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_segment.py +2 -1
  62. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_stream.py +6 -14
  63. sharedbox-0.8.0/tests/test_properties_stream.py +353 -0
  64. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_read_into.py +6 -6
  65. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_refs.py +2 -1
  66. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream.py +390 -13
  67. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream_async.py +71 -1
  68. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream_events.py +32 -1
  69. sharedbox-0.8.0/tests/test_stream_metadata.py +478 -0
  70. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream_processes.py +40 -0
  71. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream_writing.py +1 -1
  72. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_arrays.py +56 -35
  73. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_arrays_bfloat16.py +1 -1
  74. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_arrays_torch.py +1 -1
  75. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_records.py +3 -3
  76. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_value_reuse.py +1 -1
  77. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_writing.py +4 -4
  78. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/type_checks/box_types.py +1 -1
  79. sharedbox-0.8.0/tests/type_checks/metadata_types.py +132 -0
  80. {sharedbox-0.7.0 → sharedbox-0.8.0}/uv.lock +68 -1
  81. {sharedbox-0.7.0 → sharedbox-0.8.0}/zensical.toml +2 -0
  82. sharedbox-0.7.0/tests/test_exit.py +0 -53
  83. sharedbox-0.7.0/tests/test_properties_stream.py +0 -121
  84. {sharedbox-0.7.0 → sharedbox-0.8.0}/.clang-format +0 -0
  85. {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/dependabot.yml +0 -0
  86. {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/pull_request_template.md +0 -0
  87. {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/check-changelog.yaml +0 -0
  88. {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/check-docs.yaml +0 -0
  89. {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/ci.yaml +0 -0
  90. {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/codspeed.yml +0 -0
  91. {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/contention.yaml +0 -0
  92. {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/ops.yaml +0 -0
  93. {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/prepare-release.yaml +0 -0
  94. {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/publish-docs.yaml +0 -0
  95. {sharedbox-0.7.0 → sharedbox-0.8.0}/.github/workflows/streams.yaml +0 -0
  96. {sharedbox-0.7.0 → sharedbox-0.8.0}/.gitignore +0 -0
  97. {sharedbox-0.7.0 → sharedbox-0.8.0}/CMakeLists.txt +0 -0
  98. {sharedbox-0.7.0 → sharedbox-0.8.0}/LICENSE +0 -0
  99. {sharedbox-0.7.0 → sharedbox-0.8.0}/README.md +0 -0
  100. {sharedbox-0.7.0 → sharedbox-0.8.0}/benchmarks/test_bench_box.py +0 -0
  101. {sharedbox-0.7.0 → sharedbox-0.8.0}/benchmarks/test_bench_stream.py +0 -0
  102. {sharedbox-0.7.0 → sharedbox-0.8.0}/changelog.d/.gitkeep +0 -0
  103. {sharedbox-0.7.0 → sharedbox-0.8.0}/cmake/sharedbox-config.cmake +0 -0
  104. {sharedbox-0.7.0 → sharedbox-0.8.0}/cmake/sharedbox-require-cxx.cmake +0 -0
  105. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/assets/benchmarks/ops.json +0 -0
  106. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/assets/benchmarks/roundtrip.json +0 -0
  107. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/assets/benchmarks/stream-matrix.json +0 -0
  108. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/assets/benchmarks/stream-throughput.json +0 -0
  109. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/diagrams/style.d2 +0 -0
  110. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/change_several_fields_at_once.py +0 -0
  111. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/clean_up_segments.py +0 -0
  112. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/follow_a_whole_reference_graph.py +0 -0
  113. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/name_a_box.py +0 -0
  114. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/send_a_box_to_another_process.py +0 -0
  115. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/set_defaults_and_check_values.py +0 -0
  116. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_a_large_list.py +0 -0
  117. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_a_large_set_or_dict.py +0 -0
  118. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_collections.py +0 -0
  119. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_numpy_numbers.py +0 -0
  120. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_records.py +0 -0
  121. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/examples/store_text_and_bytes.py +0 -0
  122. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/checking-a-process-is-alive.md +0 -0
  123. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/closing-and-lifetime.md +0 -0
  124. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/field-types.md +0 -0
  125. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/how-a-box-is-stored.md +0 -0
  126. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/how-the-module-is-built.md +0 -0
  127. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/index.md +0 -0
  128. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/limits.md +0 -0
  129. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/performance.md +0 -0
  130. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/reading-and-writing.md +0 -0
  131. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/references.md +0 -0
  132. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/waiting-for-changes.md +0 -0
  133. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/explanation/when-to-use-sharedbox.md +0 -0
  134. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/accept-a-box-in-c.md +0 -0
  135. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/accept-a-box-in-cpp.md +0 -0
  136. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/ai-contribution-policy.md +0 -0
  137. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/build-docs.md +0 -0
  138. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/change-several-fields-at-once.md +0 -0
  139. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/clean-up-segments.md +0 -0
  140. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/commits-and-prs.md +0 -0
  141. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/contribute.md +0 -0
  142. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/follow-a-whole-reference-graph.md +0 -0
  143. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/install-sharedbox.md +0 -0
  144. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/make-a-release.md +0 -0
  145. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/name-a-box.md +0 -0
  146. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/open-a-box-from-a-program.md +0 -0
  147. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/run-benchmarks.md +0 -0
  148. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/run-commit-checks.md +0 -0
  149. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/run-tests.md +0 -0
  150. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/send-a-box-to-another-process.md +0 -0
  151. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/set-defaults-and-check-values.md +0 -0
  152. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/set-up-development.md +0 -0
  153. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-a-large-list.md +0 -0
  154. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-a-large-set-or-dict.md +0 -0
  155. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-collections.md +0 -0
  156. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-numpy-numbers.md +0 -0
  157. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-records.md +0 -0
  158. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/store-text-and-bytes.md +0 -0
  159. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/how-to/write-docs.md +0 -0
  160. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/index.md +0 -0
  161. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/javascripts/charts.js +0 -0
  162. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/box.md +0 -0
  163. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/index.md +0 -0
  164. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/library-authors.md +0 -0
  165. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/references.md +0 -0
  166. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/api/streams.md +0 -0
  167. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/changelog.md +0 -0
  168. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/cpp-and-c-api.md +0 -0
  169. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/reference/index.md +0 -0
  170. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/tutorials/index.md +0 -0
  171. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/tutorials/mailbox.py +0 -0
  172. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/tutorials/react-to-changes.md +0 -0
  173. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/tutorials/refer-to-another-box.md +0 -0
  174. {sharedbox-0.7.0 → sharedbox-0.8.0}/docs/tutorials/share-a-record.md +0 -0
  175. {sharedbox-0.7.0 → sharedbox-0.8.0}/include/sharedbox/sharedbox.hpp +0 -0
  176. {sharedbox-0.7.0 → sharedbox-0.8.0}/prek.toml +0 -0
  177. {sharedbox-0.7.0 → sharedbox-0.8.0}/scripts/check_xrefs.py +0 -0
  178. {sharedbox-0.7.0 → sharedbox-0.8.0}/scripts/release_notes.py +0 -0
  179. {sharedbox-0.7.0 → sharedbox-0.8.0}/scripts/stream_compare.py +0 -0
  180. {sharedbox-0.7.0 → sharedbox-0.8.0}/scripts/vscode_setup.py +0 -0
  181. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_bounded.py +0 -0
  182. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_follow.py +0 -0
  183. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/codec.cpp +0 -0
  184. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/codec.hpp +0 -0
  185. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/collection.cpp +0 -0
  186. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/collection.hpp +0 -0
  187. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/dict.cpp +0 -0
  188. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/dict.hpp +0 -0
  189. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/keys.cpp +0 -0
  190. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/keys.hpp +0 -0
  191. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/scalars.cpp +0 -0
  192. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/scalars.hpp +0 -0
  193. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/set.cpp +0 -0
  194. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/set.hpp +0 -0
  195. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_native/stream.hpp +0 -0
  196. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/_refs.py +0 -0
  197. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/__init__.py +0 -0
  198. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/__main__.py +0 -0
  199. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/_app.py +0 -0
  200. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/cli.py +0 -0
  201. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/contention.py +0 -0
  202. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/plot.py +0 -0
  203. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/roundtrip.py +0 -0
  204. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/size.py +0 -0
  205. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/benchmarks/size_diff.py +0 -0
  206. {sharedbox-0.7.0 → sharedbox-0.8.0}/src/sharedbox/py.typed +0 -0
  207. {sharedbox-0.7.0 → sharedbox-0.8.0}/stubtest-allowlist.txt +0 -0
  208. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/conftest.py +0 -0
  209. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/bench_stream.cpp +0 -0
  210. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/compile_fail.cpp +0 -0
  211. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/consumer/CMakeLists.txt +0 -0
  212. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/consumer/consumer.c +0 -0
  213. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/table_builder.hpp +0 -0
  214. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_atomics.cpp +0 -0
  215. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_core_alone.cpp +0 -0
  216. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_fork.cpp +0 -0
  217. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_lock.cpp +0 -0
  218. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_result.cpp +0 -0
  219. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_slots.cpp +0 -0
  220. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_small_shm.cpp +0 -0
  221. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_values.cpp +0 -0
  222. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_wait.cpp +0 -0
  223. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_windows_h.cpp +0 -0
  224. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/test_write_in_place.cpp +0 -0
  225. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/cpp/unique.hpp +0 -0
  226. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/forged_open.py +0 -0
  227. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/forged_types.py +0 -0
  228. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/os_names.py +0 -0
  229. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/refs_future.py +0 -0
  230. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/stress_helpers.py +0 -0
  231. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_contention.py +0 -0
  232. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_crashes.py +0 -0
  233. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_follow.py +0 -0
  234. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_lock_timeout.py +0 -0
  235. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_memory.py +0 -0
  236. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_waiters.py +0 -0
  237. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/stress/test_stress_watchers.py +0 -0
  238. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_benchbox_all.py +0 -0
  239. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_benchbox_contention.py +0 -0
  240. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_benchbox_plot.py +0 -0
  241. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_benchbox_roundtrip.py +0 -0
  242. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_benchbox_stream.py +0 -0
  243. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_bounded_dict.py +0 -0
  244. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_bounded_list.py +0 -0
  245. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_bounded_set.py +0 -0
  246. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_box_sync_async.py +0 -0
  247. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_capsule.py +0 -0
  248. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_doc_examples.py +0 -0
  249. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_doc_tutorials.py +0 -0
  250. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_events_async.py +0 -0
  251. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_events_future.py +0 -0
  252. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_events_signals.py +0 -0
  253. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_events_sync.py +0 -0
  254. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_follow.py +0 -0
  255. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_fork.py +0 -0
  256. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_layout.py +0 -0
  257. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_codec.py +0 -0
  258. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_liveness.py +0 -0
  259. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_types.py +0 -0
  260. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_wait.py +0 -0
  261. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_native_waiters.py +0 -0
  262. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_bounded.py +0 -0
  263. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_bounded_dict.py +0 -0
  264. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_bounded_set.py +0 -0
  265. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_codec.py +0 -0
  266. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_forged.py +0 -0
  267. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_layout.py +0 -0
  268. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_numpy.py +0 -0
  269. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_stateful.py +0 -0
  270. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_properties_types.py +0 -0
  271. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_refs_lazy.py +0 -0
  272. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_stream_fork.py +0 -0
  273. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_choices.py +0 -0
  274. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_collections.py +0 -0
  275. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_numpy.py +0 -0
  276. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_records_attrs.py +0 -0
  277. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_records_msgspec.py +0 -0
  278. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_scalars.py +0 -0
  279. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/test_types_unions.py +0 -0
  280. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/type_checks/array_types.py +0 -0
  281. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/type_checks/bounded_types.py +0 -0
  282. {sharedbox-0.7.0 → sharedbox-0.8.0}/tests/type_checks/events_types.py +0 -0
  283. {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.1 and its protocols: create, publish, open, lock, read, write, capsule handle
18
- | |-- stream.hpp stream layout 1.1: create, open, the sender, readers, the gate, dead ends
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.1, from offset 0:
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` 1, `schema_hash`, `create_id`, `creator_start`,
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) and `types_size`, followed by reserved zero bytes. The header is
149
- 128 bytes.
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 74
159
- (described types).
160
- - The record, at a multiple of 64. The mapping size is rounded up to 4 KiB.
161
- `record + record_size <= 2**32 - 4096`.
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, and the description table. Every offset is
178
- 64-bit. The full specification is in `docs/reference/segment-layout.md`.
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.7.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(3, np.float32)
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