sharedbox 0.7.0__tar.gz → 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (302) hide show
  1. {sharedbox-0.7.0 → sharedbox-0.9.0}/.github/workflows/ci.yaml +1 -1
  2. {sharedbox-0.7.0 → sharedbox-0.9.0}/CHANGELOG.md +159 -0
  3. {sharedbox-0.7.0 → sharedbox-0.9.0}/CLAUDE.md +53 -20
  4. {sharedbox-0.7.0 → sharedbox-0.9.0}/PKG-INFO +3 -1
  5. sharedbox-0.9.0/docs/examples/attach_metadata.py +78 -0
  6. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/clean_up_segments.py +5 -1
  7. sharedbox-0.9.0/docs/examples/request_work_from_the_owner.py +95 -0
  8. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/send_items_through_a_stream.py +84 -1
  9. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/store_a_large_list.py +1 -1
  10. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/store_a_large_set_or_dict.py +2 -2
  11. sharedbox-0.9.0/docs/examples/store_a_value_with_its_time.py +49 -0
  12. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/store_arrays.py +23 -2
  13. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/field-types.md +1 -0
  14. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/glossary.md +36 -0
  15. sharedbox-0.9.0/docs/how-to/attach-metadata.md +193 -0
  16. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/clean-up-segments.md +14 -1
  17. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/index.md +10 -0
  18. sharedbox-0.9.0/docs/how-to/migrate-from-0.7.md +83 -0
  19. sharedbox-0.9.0/docs/how-to/request-work-from-the-owner.md +158 -0
  20. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/send-items-through-a-stream.md +109 -7
  21. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/store-a-large-list.md +2 -1
  22. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/store-a-large-set-or-dict.md +4 -3
  23. sharedbox-0.9.0/docs/how-to/store-a-value-with-its-time.md +80 -0
  24. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/store-arrays.md +43 -9
  25. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/api/arrays.md +5 -0
  26. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/api/box.md +40 -0
  27. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/api/errors.md +40 -0
  28. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/api/events.md +1 -0
  29. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/api/index.md +1 -1
  30. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/segment-layout.md +660 -60
  31. {sharedbox-0.7.0 → sharedbox-0.9.0}/include/sharedbox/box.hpp +665 -31
  32. {sharedbox-0.7.0 → sharedbox-0.9.0}/include/sharedbox/core.hpp +778 -29
  33. {sharedbox-0.7.0 → sharedbox-0.9.0}/include/sharedbox/sharedbox_c.cpp +37 -0
  34. {sharedbox-0.7.0 → sharedbox-0.9.0}/include/sharedbox/sharedbox_c.h +19 -1
  35. {sharedbox-0.7.0 → sharedbox-0.9.0}/include/sharedbox/stream.hpp +350 -77
  36. {sharedbox-0.7.0 → sharedbox-0.9.0}/includes/abbreviations.md +1 -0
  37. {sharedbox-0.7.0 → sharedbox-0.9.0}/pyproject.toml +3 -1
  38. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/__init__.py +35 -2
  39. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_arrays.py +77 -23
  40. sharedbox-0.9.0/src/sharedbox/_attr.py +360 -0
  41. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_bounded.py +160 -28
  42. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_box.py +302 -67
  43. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_events.py +315 -46
  44. sharedbox-0.9.0/src/sharedbox/_exit.py +193 -0
  45. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_follow.py +1 -1
  46. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_layout.py +227 -15
  47. sharedbox-0.9.0/src/sharedbox/_metadata.py +134 -0
  48. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/module.cpp +148 -30
  49. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/segment.cpp +220 -17
  50. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/segment.hpp +50 -7
  51. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/stream.cpp +273 -57
  52. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/types.cpp +367 -18
  53. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/types.hpp +67 -5
  54. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native.pyi +316 -21
  55. sharedbox-0.9.0/src/sharedbox/_request.py +385 -0
  56. sharedbox-0.9.0/src/sharedbox/_status.py +49 -0
  57. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_stream.py +610 -94
  58. sharedbox-0.9.0/src/sharedbox/_timed.py +34 -0
  59. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_types.py +227 -27
  60. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_version.py +3 -3
  61. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/ops.py +1 -1
  62. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/stream.py +2 -2
  63. {sharedbox-0.7.0 → sharedbox-0.9.0}/stubtest-allowlist.txt +1 -0
  64. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/CMakeLists.txt +1 -1
  65. sharedbox-0.9.0/tests/cpp/table_builder.hpp +175 -0
  66. sharedbox-0.9.0/tests/cpp/test_attr.cpp +621 -0
  67. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_c_smoke.c +17 -0
  68. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_c_smoke_box.cpp +2 -1
  69. sharedbox-0.9.0/tests/cpp/test_layout3.cpp +632 -0
  70. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_mapping.cpp +3 -3
  71. sharedbox-0.9.0/tests/cpp/test_metadata.cpp +1022 -0
  72. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_stream.cpp +399 -2
  73. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_stream_layout.cpp +77 -2
  74. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_types.cpp +167 -2
  75. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_write_in_place.cpp +15 -0
  76. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/crossproc.py +63 -2
  77. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/forged_types.py +4 -2
  78. sharedbox-0.9.0/tests/signatures.py +12 -0
  79. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/streamproc.py +89 -3
  80. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/stress/test_stress_read_into.py +2 -2
  81. sharedbox-0.9.0/tests/test_attr.py +869 -0
  82. sharedbox-0.9.0/tests/test_attr_fields.py +402 -0
  83. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_bounded_dict.py +39 -0
  84. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_bounded_list.py +51 -1
  85. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_bounded_set.py +37 -0
  86. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_box.py +2 -2
  87. sharedbox-0.9.0/tests/test_box_max_shape.py +292 -0
  88. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_events_bytes.py +1 -1
  89. sharedbox-0.9.0/tests/test_exit.py +257 -0
  90. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_fields.py +9 -8
  91. sharedbox-0.9.0/tests/test_metadata.py +509 -0
  92. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_native_segment.py +55 -2
  93. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_native_stream.py +6 -14
  94. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_properties_numpy.py +38 -2
  95. sharedbox-0.9.0/tests/test_properties_stream.py +353 -0
  96. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_properties_types.py +10 -1
  97. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_read_into.py +6 -6
  98. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_refs.py +2 -1
  99. sharedbox-0.9.0/tests/test_segment_status.py +173 -0
  100. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_stream.py +381 -13
  101. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_stream_async.py +71 -1
  102. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_stream_events.py +32 -1
  103. sharedbox-0.9.0/tests/test_stream_fetch.py +222 -0
  104. sharedbox-0.9.0/tests/test_stream_metadata.py +478 -0
  105. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_stream_processes.py +40 -0
  106. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_stream_writing.py +1 -1
  107. sharedbox-0.9.0/tests/test_timed.py +249 -0
  108. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_types_arrays.py +56 -35
  109. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_types_arrays_bfloat16.py +1 -1
  110. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_types_arrays_torch.py +1 -1
  111. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_types_numpy.py +1 -1
  112. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_types_records.py +3 -3
  113. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_value_reuse.py +1 -1
  114. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_writing.py +4 -4
  115. sharedbox-0.9.0/tests/type_checks/attr_types.py +49 -0
  116. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/type_checks/bounded_types.py +10 -2
  117. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/type_checks/box_types.py +13 -1
  118. sharedbox-0.9.0/tests/type_checks/metadata_types.py +132 -0
  119. sharedbox-0.9.0/tests/type_checks/timed_types.py +15 -0
  120. {sharedbox-0.7.0 → sharedbox-0.9.0}/uv.lock +68 -1
  121. {sharedbox-0.7.0 → sharedbox-0.9.0}/zensical.toml +4 -0
  122. sharedbox-0.7.0/tests/cpp/table_builder.hpp +0 -49
  123. sharedbox-0.7.0/tests/cpp/test_layout3.cpp +0 -188
  124. sharedbox-0.7.0/tests/test_exit.py +0 -53
  125. sharedbox-0.7.0/tests/test_properties_stream.py +0 -121
  126. {sharedbox-0.7.0 → sharedbox-0.9.0}/.clang-format +0 -0
  127. {sharedbox-0.7.0 → sharedbox-0.9.0}/.github/dependabot.yml +0 -0
  128. {sharedbox-0.7.0 → sharedbox-0.9.0}/.github/pull_request_template.md +0 -0
  129. {sharedbox-0.7.0 → sharedbox-0.9.0}/.github/workflows/check-changelog.yaml +0 -0
  130. {sharedbox-0.7.0 → sharedbox-0.9.0}/.github/workflows/check-docs.yaml +0 -0
  131. {sharedbox-0.7.0 → sharedbox-0.9.0}/.github/workflows/codspeed.yml +0 -0
  132. {sharedbox-0.7.0 → sharedbox-0.9.0}/.github/workflows/contention.yaml +0 -0
  133. {sharedbox-0.7.0 → sharedbox-0.9.0}/.github/workflows/ops.yaml +0 -0
  134. {sharedbox-0.7.0 → sharedbox-0.9.0}/.github/workflows/prepare-release.yaml +0 -0
  135. {sharedbox-0.7.0 → sharedbox-0.9.0}/.github/workflows/publish-docs.yaml +0 -0
  136. {sharedbox-0.7.0 → sharedbox-0.9.0}/.github/workflows/streams.yaml +0 -0
  137. {sharedbox-0.7.0 → sharedbox-0.9.0}/.gitignore +0 -0
  138. {sharedbox-0.7.0 → sharedbox-0.9.0}/CMakeLists.txt +0 -0
  139. {sharedbox-0.7.0 → sharedbox-0.9.0}/LICENSE +0 -0
  140. {sharedbox-0.7.0 → sharedbox-0.9.0}/README.md +0 -0
  141. {sharedbox-0.7.0 → sharedbox-0.9.0}/benchmarks/test_bench_box.py +0 -0
  142. {sharedbox-0.7.0 → sharedbox-0.9.0}/benchmarks/test_bench_stream.py +0 -0
  143. {sharedbox-0.7.0 → sharedbox-0.9.0}/changelog.d/.gitkeep +0 -0
  144. {sharedbox-0.7.0 → sharedbox-0.9.0}/cmake/sharedbox-config.cmake +0 -0
  145. {sharedbox-0.7.0 → sharedbox-0.9.0}/cmake/sharedbox-require-cxx.cmake +0 -0
  146. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/assets/benchmarks/ops.json +0 -0
  147. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/assets/benchmarks/roundtrip.json +0 -0
  148. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/assets/benchmarks/stream-matrix.json +0 -0
  149. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/assets/benchmarks/stream-throughput.json +0 -0
  150. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/diagrams/style.d2 +0 -0
  151. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/change_several_fields_at_once.py +0 -0
  152. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/follow_a_whole_reference_graph.py +0 -0
  153. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/name_a_box.py +0 -0
  154. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/send_a_box_to_another_process.py +0 -0
  155. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/set_defaults_and_check_values.py +0 -0
  156. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/store_collections.py +0 -0
  157. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/store_numpy_numbers.py +0 -0
  158. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/store_records.py +0 -0
  159. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/examples/store_text_and_bytes.py +0 -0
  160. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/checking-a-process-is-alive.md +0 -0
  161. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/closing-and-lifetime.md +0 -0
  162. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/how-a-box-is-stored.md +0 -0
  163. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/how-the-module-is-built.md +0 -0
  164. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/index.md +0 -0
  165. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/limits.md +0 -0
  166. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/performance.md +0 -0
  167. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/reading-and-writing.md +0 -0
  168. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/references.md +0 -0
  169. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/waiting-for-changes.md +0 -0
  170. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/explanation/when-to-use-sharedbox.md +0 -0
  171. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/accept-a-box-in-c.md +0 -0
  172. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/accept-a-box-in-cpp.md +0 -0
  173. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/ai-contribution-policy.md +0 -0
  174. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/build-docs.md +0 -0
  175. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/change-several-fields-at-once.md +0 -0
  176. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/commits-and-prs.md +0 -0
  177. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/contribute.md +0 -0
  178. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/follow-a-whole-reference-graph.md +0 -0
  179. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/install-sharedbox.md +0 -0
  180. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/make-a-release.md +0 -0
  181. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/name-a-box.md +0 -0
  182. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/open-a-box-from-a-program.md +0 -0
  183. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/run-benchmarks.md +0 -0
  184. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/run-commit-checks.md +0 -0
  185. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/run-tests.md +0 -0
  186. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/send-a-box-to-another-process.md +0 -0
  187. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/set-defaults-and-check-values.md +0 -0
  188. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/set-up-development.md +0 -0
  189. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/store-collections.md +0 -0
  190. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/store-numpy-numbers.md +0 -0
  191. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/store-records.md +0 -0
  192. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/store-text-and-bytes.md +0 -0
  193. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/how-to/write-docs.md +0 -0
  194. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/index.md +0 -0
  195. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/javascripts/charts.js +0 -0
  196. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/api/library-authors.md +0 -0
  197. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/api/references.md +0 -0
  198. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/api/streams.md +0 -0
  199. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/changelog.md +0 -0
  200. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/cpp-and-c-api.md +0 -0
  201. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/reference/index.md +0 -0
  202. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/tutorials/index.md +0 -0
  203. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/tutorials/mailbox.py +0 -0
  204. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/tutorials/react-to-changes.md +0 -0
  205. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/tutorials/refer-to-another-box.md +0 -0
  206. {sharedbox-0.7.0 → sharedbox-0.9.0}/docs/tutorials/share-a-record.md +0 -0
  207. {sharedbox-0.7.0 → sharedbox-0.9.0}/include/sharedbox/sharedbox.hpp +0 -0
  208. {sharedbox-0.7.0 → sharedbox-0.9.0}/prek.toml +0 -0
  209. {sharedbox-0.7.0 → sharedbox-0.9.0}/scripts/check_xrefs.py +0 -0
  210. {sharedbox-0.7.0 → sharedbox-0.9.0}/scripts/release_notes.py +0 -0
  211. {sharedbox-0.7.0 → sharedbox-0.9.0}/scripts/stream_compare.py +0 -0
  212. {sharedbox-0.7.0 → sharedbox-0.9.0}/scripts/vscode_setup.py +0 -0
  213. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/codec.cpp +0 -0
  214. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/codec.hpp +0 -0
  215. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/collection.cpp +0 -0
  216. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/collection.hpp +0 -0
  217. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/dict.cpp +0 -0
  218. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/dict.hpp +0 -0
  219. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/keys.cpp +0 -0
  220. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/keys.hpp +0 -0
  221. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/scalars.cpp +0 -0
  222. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/scalars.hpp +0 -0
  223. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/set.cpp +0 -0
  224. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/set.hpp +0 -0
  225. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_native/stream.hpp +0 -0
  226. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/_refs.py +0 -0
  227. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/__init__.py +0 -0
  228. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/__main__.py +0 -0
  229. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/_app.py +0 -0
  230. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/cli.py +0 -0
  231. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/contention.py +0 -0
  232. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/plot.py +0 -0
  233. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/roundtrip.py +0 -0
  234. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/size.py +0 -0
  235. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/benchmarks/size_diff.py +0 -0
  236. {sharedbox-0.7.0 → sharedbox-0.9.0}/src/sharedbox/py.typed +0 -0
  237. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/conftest.py +0 -0
  238. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/bench_stream.cpp +0 -0
  239. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/compile_fail.cpp +0 -0
  240. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/consumer/CMakeLists.txt +0 -0
  241. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/consumer/consumer.c +0 -0
  242. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_atomics.cpp +0 -0
  243. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_core_alone.cpp +0 -0
  244. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_fork.cpp +0 -0
  245. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_lock.cpp +0 -0
  246. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_result.cpp +0 -0
  247. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_slots.cpp +0 -0
  248. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_small_shm.cpp +0 -0
  249. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_values.cpp +0 -0
  250. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_wait.cpp +0 -0
  251. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/test_windows_h.cpp +0 -0
  252. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/cpp/unique.hpp +0 -0
  253. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/forged_open.py +0 -0
  254. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/os_names.py +0 -0
  255. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/refs_future.py +0 -0
  256. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/stress/stress_helpers.py +0 -0
  257. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/stress/test_stress_contention.py +0 -0
  258. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/stress/test_stress_crashes.py +0 -0
  259. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/stress/test_stress_follow.py +0 -0
  260. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/stress/test_stress_lock_timeout.py +0 -0
  261. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/stress/test_stress_memory.py +0 -0
  262. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/stress/test_stress_waiters.py +0 -0
  263. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/stress/test_stress_watchers.py +0 -0
  264. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_benchbox_all.py +0 -0
  265. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_benchbox_contention.py +0 -0
  266. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_benchbox_plot.py +0 -0
  267. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_benchbox_roundtrip.py +0 -0
  268. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_benchbox_stream.py +0 -0
  269. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_box_sync_async.py +0 -0
  270. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_capsule.py +0 -0
  271. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_doc_examples.py +0 -0
  272. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_doc_tutorials.py +0 -0
  273. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_events_async.py +0 -0
  274. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_events_future.py +0 -0
  275. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_events_signals.py +0 -0
  276. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_events_sync.py +0 -0
  277. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_follow.py +0 -0
  278. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_fork.py +0 -0
  279. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_layout.py +0 -0
  280. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_native_codec.py +0 -0
  281. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_native_liveness.py +0 -0
  282. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_native_types.py +0 -0
  283. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_native_wait.py +0 -0
  284. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_native_waiters.py +0 -0
  285. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_properties_bounded.py +0 -0
  286. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_properties_bounded_dict.py +0 -0
  287. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_properties_bounded_set.py +0 -0
  288. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_properties_codec.py +0 -0
  289. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_properties_forged.py +0 -0
  290. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_properties_layout.py +0 -0
  291. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_properties_stateful.py +0 -0
  292. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_refs_lazy.py +0 -0
  293. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_stream_fork.py +0 -0
  294. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_types_choices.py +0 -0
  295. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_types_collections.py +0 -0
  296. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_types_records_attrs.py +0 -0
  297. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_types_records_msgspec.py +0 -0
  298. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_types_scalars.py +0 -0
  299. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/test_types_unions.py +0 -0
  300. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/type_checks/array_types.py +0 -0
  301. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/type_checks/events_types.py +0 -0
  302. {sharedbox-0.7.0 → sharedbox-0.9.0}/tests/type_checks/stream_types.py +0 -0
@@ -114,7 +114,7 @@ jobs:
114
114
  ref: ${{ github.event_name == 'release' && github.event.release.tag_name || '' }}
115
115
 
116
116
  - name: Build wheels
117
- uses: pypa/cibuildwheel@v4.3.4
117
+ uses: pypa/cibuildwheel@v4.3.5
118
118
  env:
119
119
  CIBW_BUILD: ${{ matrix.python }}-${{ matrix.platform_id }}
120
120
  CIBW_CONTAINER_ENGINE: "docker; create_args: --shm-size=1g"
@@ -9,6 +9,165 @@ Dates are marked as `DD-MM-YYYY`
9
9
 
10
10
  <!-- towncrier release notes start -->
11
11
 
12
+ ## [0.9.0](https://github.com/jacopoabramo/sharedbox/releases/tag/v0.9.0) - 04-11-2026
13
+
14
+ ### Added
15
+
16
+ - `segment_status`: `segment_status("lab:counts")` returns a `SegmentStatus`,
17
+ `missing` when no box or stream is published under the name, `running` while the
18
+ process that created it runs and `creator_exited` once it has ended.
19
+ `core.hpp` adds `segment_status` and `segment_state` for C++. ([#94](https://github.com/jacopoabramo/sharedbox/pull/94))
20
+ - `SharedReceiveStream.fetch`, `fetch_into` and `fetch_future`: `reader.fetch(120)`
21
+ returns the item at position 120, waiting until the sender has sent it, and
22
+ leaves the reader's receive cursor and `missed` unchanged. `ItemGoneError`, a
23
+ `LookupError` whose `oldest` is the oldest position the ring held, is raised
24
+ once the ring no longer holds the item. `stream.hpp` adds
25
+ `stream_reader::fetch` and `fetch_with` for C++. ([#95](https://github.com/jacopoabramo/sharedbox/pull/95))
26
+ - `MaxShape` on box fields: a box field declared as
27
+ `Annotated[npt.NDArray[np.uint16], MaxShape((2048, 2048))]` holds an array of
28
+ any shape up to that maximum, as kind 75 of box layout 3.3, and every read
29
+ returns an array of the stored shape. A write stores the shape and only the
30
+ elements in use. `SharedBox.writing` takes `shape`, so
31
+ `box.writing("image", shape=(480, 640))` sets the shape and yields a view of
32
+ it under one lock. `read_into` raises `TypeError` for a `MaxShape` field. ([#97](https://github.com/jacopoabramo/sharedbox/pull/97))
33
+ - `BoundedList`, `BoundedSet` and `BoundedDict` take their element types as the
34
+ first arguments when the class fixes none: `BoundedList(int, [1, 2], capacity=4)`
35
+ equals `BoundedList[int]([1, 2], capacity=4)`, and
36
+ `BoundedDict(str, float, {"a": 1.0}, capacity=8)` takes the key and value types. ([#97](https://github.com/jacopoabramo/sharedbox/pull/97))
37
+ - `Timed` and `Timed.now`: a box field, record member, collection element or
38
+ stream item declared as `Timed[float]` stores the value with a timestamp in
39
+ nanoseconds since the Unix epoch, and `box.temperature = Timed.now(21.5)` stamps
40
+ it with the system clock when the write holds the box's lock, or just before a
41
+ stream send publishes the item. A `Timed` with a timestamp of its own is stored as
42
+ given. Timed values are kind 77, and streams use stream layout 1.5. ([#99](https://github.com/jacopoabramo/sharedbox/pull/99))
43
+ - `Attr`, `attr`, `AttrValue`, `RequestFuture` and `RequestServer`: a box
44
+ field declared as `exposure: Attr[float] = attr(timed=True, settable=True)`
45
+ holds a value its owner writes with
46
+ `box.exposure.publish(value=..., setpoint=...)` and any process reads with
47
+ `.value`, `.setpoint`, `.timestamp_ns` or `read()`, or watches with
48
+ `watch` and `events`. An attribute with arguments, such as
49
+ `move: Attr[float, [float]] = attr()`, or a settable one carries a typed
50
+ request to the one process that serves it: `box.move.serve(handler)`
51
+ answers with a sync or async handler, and `box.move(5.0)` or
52
+ `box.exposure.set(0.02)` returns a `RequestFuture` waited on with
53
+ `.result(timeout)` or `await`. Of the requests sent while the handler
54
+ runs, only the newest is handled next. `RequestError`,
55
+ `RequestSupersededError` and `ServerExitedError` report a failed, replaced
56
+ or abandoned request, and `AlreadyServedError` a second server. Attribute
57
+ fields are kind 76 of box layout 3.3, and `box.hpp` adds `attr_publish`,
58
+ `attr_read`, `attr_published`, `attr_value_at` and the request protocol
59
+ steps on `handle` for C++, and `type_view` gives an attribute's flags and
60
+ where its value, timestamp and setpoint start. ([#103](https://github.com/jacopoabramo/sharedbox/pull/103))
61
+
62
+ ### Fixed
63
+
64
+ - On Windows, an operating system failure to create or open a box or stream raises
65
+ `OSError` with the Windows error code in `winerror`, such as `PermissionError`
66
+ for a segment the process may not open, instead of `RuntimeError`. ([#96](https://github.com/jacopoabramo/sharedbox/pull/96))
67
+
68
+
69
+ ## [0.8.0](https://github.com/jacopoabramo/sharedbox/releases/tag/v0.8.0) - 30-10-2026
70
+
71
+ ### Breaking
72
+
73
+ - `sharedbox.Shape` takes its dimensions as one tuple, `Shape((480, 640))`, in
74
+ place of one argument per dimension. See
75
+ [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))
76
+ - `SharedBox` reserves the names `metadata`, `metadata_version` and
77
+ `metadata_version_of`: a field or `InitVar` with one of them raises
78
+ `TypeError` when the class is defined. Rename the field:
79
+
80
+ ```python
81
+ # 0.7
82
+ class Parcel(SharedBox):
83
+ metadata: Annotated[str, Capacity(64)] = ""
84
+
85
+ # 0.8
86
+ class Parcel(SharedBox):
87
+ label: Annotated[str, Capacity(64)] = ""
88
+ ```
89
+
90
+ `SharedBox.update` takes the keyword `metadata`, so a type checker refuses
91
+ `update(**values)` when the values of the dictionary have a type that the
92
+ box's metadata type does not accept, such as `str`:
93
+
94
+ ```python
95
+ # 0.7
96
+ def restock(parcel: Parcel, values: dict[str, str]) -> None:
97
+ parcel.update(**values)
98
+
99
+ # 0.8
100
+ def restock(parcel: Parcel, values: dict[str, Any]) -> None:
101
+ parcel.update(**values)
102
+ ```
103
+
104
+ See
105
+ [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))
106
+
107
+ ### Added
108
+
109
+ - `MaxShape`: an array of a stream item declared with a maximum shape, as in
110
+ `Annotated[npt.NDArray[np.uint16], MaxShape((2048, 2048))]`, stores its own
111
+ shape with each item, as kind 75 of stream layout 1.2. `writing()` views the
112
+ slot in the shape of the template's array, `slot.send` publishes the first
113
+ rows of that view without a copy, and `receive_into` returns a view of the
114
+ stored shape over `out`. ([#88](https://github.com/jacopoabramo/sharedbox/pull/88))
115
+ - `SharedStream.sender(on_full="release")`: a send that has waited its whole
116
+ `timeout` releases the lossless readers a full ring behind and sends, as in
117
+ `stream.sender(on_full="release").send(item, timeout=0.1)`. Each released
118
+ reader raises `ReaderReleased`, whose `missed` counts the items it lost, and
119
+ goes on at the newest item; `ReceiveEvents.released` reports the count to
120
+ callbacks. `SharedSendStream.asend` and `awriting` take a `timeout`. In
121
+ `stream.hpp`, `send`, `send_with` and `begin` take an `on_full` policy, and a
122
+ released reader's `receive` gives `status::released`, in stream layout 1.3. ([#89](https://github.com/jacopoabramo/sharedbox/pull/89))
123
+ - `MetadataFullError` and `MetadataGoneError`: raised when an add finds no
124
+ room for a version, and when the version a read asks for is no longer held. ([#91](https://github.com/jacopoabramo/sharedbox/pull/91))
125
+ - `SharedStream.metadata`: a stream keeps metadata the same way.
126
+ `SharedStream.create(Reading, capacity=8, metadata={"unit": "kg"})` stores
127
+ version 1. `SharedSendStream.send`, `asend`, `send_nowait` and
128
+ `SendSlot.send` take `metadata=` and add a version that applies from that
129
+ item. `SharedReceiveStream.metadata` and `metadata_version` give the version
130
+ of the item the reader received last. `SharedStream.create` takes
131
+ `metadata_capacity` and `metadata_versions`, and `create` and `attach` take
132
+ `metadata_type`. `sharedbox` 0.7.0 and earlier cannot open a stream created
133
+ with metadata or a `metadata_capacity`. ([#91](https://github.com/jacopoabramo/sharedbox/pull/91))
134
+ - `box.hpp`, `stream.hpp` and `sharedbox_c.h`: box layout 3.2 and stream
135
+ layout 1.4 hold metadata after the record or the description table.
136
+ `handle` and `stream` gain `metadata_head` and `read_metadata`, and
137
+ `stream_reader` gains `metadata_version_at`. `handle` and `stream_sender`
138
+ gain `add_metadata`. `handle::write(values, doc, lock_timeout)` and
139
+ `write_large` with the same arguments write fields and add a version in one
140
+ write, and
141
+ `stream_sender::can_add_metadata` checks an add without making it.
142
+ `sharedbox_c.h` adds `sbx_metadata_head`, `sbx_read_metadata`,
143
+ `sbx_add_metadata` and `SBX_E_METADATA_FULL`. ([#91](https://github.com/jacopoabramo/sharedbox/pull/91))
144
+ - `SharedBox.metadata`: a box keeps metadata, JSON-like values stored as CBOR
145
+ in its segment, in numbered versions. `Scale.create(name, metadata={"unit":
146
+ "kg"})` stores version 1, and `SharedBox.update(metadata=...)` adds the next
147
+ one from any process, with or without field values, in one write.
148
+ `metadata`, `metadata_version` and `metadata_version_of` read the versions,
149
+ a map as a read-only mapping and a list as a tuple. `BoxEvents.metadata` is
150
+ emitted with each new version. The class keywords `metadata_capacity` and
151
+ `metadata_versions` size the room for versions, and
152
+ `class Scale(SharedBox[Calibration])` gives the metadata's type to type
153
+ checkers. ([#91](https://github.com/jacopoabramo/sharedbox/pull/91))
154
+
155
+ ### Changed
156
+
157
+ - `sharedbox.DType` is a `StrEnum` with one member per element type:
158
+ `DType(numpy.uint8)` returns `DType.uint8`, which equals `"uint8"`. ([#88](https://github.com/jacopoabramo/sharedbox/pull/88))
159
+
160
+ ### Fixed
161
+
162
+ - `SharedBox` and `SharedStream` at exit: the `atexit` hook of `sharedbox`
163
+ closes every box and stream the process still has open and releases the
164
+ native objects of `SharedBox` subclasses, their watches and events groups,
165
+ so nanobind reports no leaked instances when a box class defined at module
166
+ level outlives shutdown. A box or stream used after the hook raises
167
+ `BoxClosedError` or `StreamClosedError`, and creating or attaching a box
168
+ raises `BoxClosedError`. ([#90](https://github.com/jacopoabramo/sharedbox/pull/90))
169
+
170
+
12
171
  ## [0.7.0](https://github.com/jacopoabramo/sharedbox/releases/tag/v0.7.0) - 28-10-2026
13
172
 
14
173
  ### 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.3 and its protocols: create, publish, open, lock, read, write, metadata, capsule handle
18
+ | |-- stream.hpp stream layout 1.5: create, open, the sender, readers, the gate, dead ends, metadata
19
19
  | |-- sharedbox.hpp includes every kind; the header consumers include
20
- | |-- 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
@@ -26,13 +26,20 @@ sharedbox/
26
26
  |-- src/sharedbox/
27
27
  | |-- __init__.py re-exports the public API, get_include()
28
28
  | |-- _box.py SharedBox: class keywords, fields, fields(), create/attach, __post_init__, update, snapshot, unlink, __sharedbox_box__
29
- | |-- _layout.py Capacity, field() and Field, field offsets and kind codes, schema hash
29
+ | |-- _layout.py Capacity, field(), attr() and Field, field offsets and kind codes, schema hash
30
30
  | |-- _types.py annotation parsing into TypeSpec, the description table
31
- | |-- _arrays.py Shape, DType, SupportsDLPack, register_array_type
32
- | |-- _events.py BoxEvents, FieldWatch, the watcher thread
31
+ | |-- _arrays.py Shape, MaxShape, DType, SupportsDLPack, register_array_type
32
+ | |-- _timed.py Timed: a value with its timestamp in ns, Timed.now and the mark a write replaces
33
+ | |-- _attr.py Attr and AttrValue: the handle of an attribute field, publish, read, requests and serve
34
+ | |-- _request.py request fields: RequestFuture, RequestServer, the request errors, serving threads and tasks
35
+ | |-- _events.py BoxEvents, FieldWatch, the watcher thread, which also completes request futures and kicks request servers
33
36
  | |-- _follow.py BoxEvents.follow and unfollow: forwarding the events of boxes that references reach
34
37
  | |-- _stream.py SharedStream, SharedSendStream, SharedReceiveStream, ReceiveEvents, the statistics classes, the worker threads
38
+ | |-- _metadata.py metadata documents: the type variable M, CBOR encoding, read-only decoding, the cache of decoded versions
35
39
  | |-- _refs.py reference fields: BoxRef, the class registry, BrokenReferenceError, UnknownBoxClassError
40
+ | |-- _status.py segment_status and SegmentStatus: whether a name holds a segment and its creator runs
41
+ | |-- _exit.py the exit handler: stops the watcher, request server and stream threads, closes boxes, streams and ends,
42
+ | | replaces the native objects of watchers and box classes
36
43
  | |-- _native.pyi hand-written stub for the extension
37
44
  | |-- py.typed
38
45
  | |-- benchmarks/ the benchbox command (extra: benchmarks)
@@ -67,11 +74,18 @@ sharedbox/
67
74
  | |-- test_follow.py BoxEvents.follow, unfollow and nested
68
75
  | |-- test_native_waiters.py waiter slots, dead owners, interrupt
69
76
  | |-- test_box_sync_async.py a sync and an async watch on one box in two threads
77
+ | |-- test_metadata.py metadata of a box: documents, versions, events, typed access, reserved names
78
+ | |-- test_segment_status.py segment_status: missing, running and an exited creator
70
79
  | |-- test_stream.py SharedStream, sender and reader, blocking and non-blocking calls
80
+ | |-- test_stream_metadata.py metadata of a stream: the version of each item, in one process and across processes
71
81
  | |-- test_stream_async.py asyncio calls on a stream, cancellation
72
82
  | |-- test_stream_events.py ReceiveEvents delivery
83
+ | |-- test_stream_fetch.py fetching an item by its position, in one process and across processes
73
84
  | |-- test_stream_processes.py streams across processes, dead senders and readers
74
85
  | |-- test_stream_fork.py stream ends inherited through fork
86
+ | |-- test_timed.py Timed fields and stream items: stamps, given times, where a Timed may go
87
+ | |-- test_attr.py attribute requests across processes: answers, superseding, a killed server, close, async handlers
88
+ | |-- test_attr_fields.py Attr in the layout: publish, read, watch, refused operations, where an Attr may go
75
89
  | |-- streamproc.py helpers that run a stream end in another process
76
90
  | |-- test_properties_*.py Hypothesis property tests, streams included
77
91
  | |-- stress/ stress tests, marker stress, deselected by default
@@ -91,7 +105,7 @@ sharedbox/
91
105
  | |-- examples/ one script per how-to guide, included by the guide
92
106
  | |-- javascripts/charts.js draws each div of class sbx-chart from a plotly figure JSON file
93
107
  | |-- 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
108
+ | `-- reference/segment-layout.md core 1.0, box layout 3.3, stream layout 1.5, names and protocols
95
109
  |-- includes/abbreviations.md acronym tooltips appended to every page
96
110
  |-- zensical.toml site configuration and navigation
97
111
  |-- .github/workflows/ci.yaml lint, docs check, C++ tests, cibuildwheel wheels, tests, stress,
@@ -135,18 +149,19 @@ class's identity (the `identity` class keyword, by default
135
149
  always asks for a new name and raises `SegmentExistsError` if it is taken.
136
150
 
137
151
  Every segment starts with the same 64-byte line, whatever its kind. Core
138
- version 1.0, box layout 3.1, from offset 0:
152
+ version 1.0, box layout 3.3, from offset 0:
139
153
 
140
154
  - Common line 0, written once at creation: `magic` (`SBX_BOX_` for a box,
141
155
  `SBX_STRM` for a stream), `core_major` 1, `core_minor` 0, `kind_major` 3,
142
- `kind_minor` 1, `schema_hash`, `create_id`, `creator_start`,
156
+ `kind_minor` 3, `schema_hash`, `create_id`, `creator_start`,
143
157
  `creator_pidns`, `creator_pid`, `waiter_slots`, `size`.
144
158
  - Box line 1, at offset 64: `seq` (sequence lock; the generation is
145
159
  `seq >> 1`, there is no generation field), `writer_pid`, `wake_word`,
146
160
  `waiters` and `sleepers`, which writes and waits change; then written
147
161
  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.
162
+ 128), `types_size`, `metadata_size` and `metadata` (the offset of the
163
+ metadata area, 0 for none); last `metadata_head`, the newest metadata
164
+ version, 0 for none, which each add changes. The header is 128 bytes.
150
165
  - The tail: `field_count` field table entries of 8 bytes (`u32 offset`,
151
166
  `u32 capacity_and_kind`: top 8 bits the kind code, the low 24 the
152
167
  capacity or size, or for kinds 64 and up the offset of the field's
@@ -155,10 +170,20 @@ version 1.0, box layout 3.1, from offset 0:
155
170
  `asleep_on`: the offset of the count the slot's current wait added 1 to,
156
171
  which freeing a dead owner's slot takes back), then the description
157
172
  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`.
173
+ scalars), 13 to 23 (fixed-width numbers and `datetime64`), and 64 to 77
174
+ (described types; 75, an array up to a maximum shape, in a stream
175
+ item or a box field, written as its shape line and the elements in use;
176
+ 76, an attribute, a box field only, holding the request part, the value
177
+ part with `published` and the setpoint; 77, a value with an
178
+ `i64` timestamp in ns, wherever a record may go, its timestamp `-2**63`
179
+ replaced by the time of the write).
180
+ - The record, at a multiple of 64. `record + record_size <= 2**32 - 4096`.
181
+ - The metadata area, when `metadata` is not 0, at the first multiple of 64
182
+ after the record, `metadata + metadata_size <= 2**32 - 4096`: a 64-byte
183
+ line (`entries`, `ring_size`), `entries` version entries of 32 bytes
184
+ (`seq`, `version`, `position`, `offset`, `length`), then a ring of
185
+ `ring_size` bytes holding one CBOR document per version. The mapping size
186
+ is rounded up to 4 KiB.
162
187
 
163
188
  `magic` is stored last on create, with release ordering, after the initial
164
189
  values are in the record; attach waits for it. Attach copies line 0 and the
@@ -174,8 +199,11 @@ layout 1.0 or 2.0, which 0.5 and earlier wrote.
174
199
  A stream's segment starts with a 256-byte header: the common line, a line
175
200
  of geometry written once, the sender's line and the readers' line. Then
176
201
  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`.
202
+ holding a `seq` and one item, the description table, and the metadata
203
+ area laid out as a box's. Stream layout 1.5 keeps `metadata_size` and
204
+ `metadata` in the geometry line and `metadata_head` in the sender's line.
205
+ Every offset is 64-bit. The full specification is in
206
+ `docs/reference/segment-layout.md`.
179
207
 
180
208
  ### Record encoding
181
209
 
@@ -190,8 +218,10 @@ capacity counts elements, 1 to 1048576.
190
218
 
191
219
  ### Lifecycle
192
220
 
193
- - `close()` interrupts and stops the watcher thread, stops forwarding
194
- started with `events.follow`, and detaches this box. Later reads and
221
+ - `close()` interrupts and stops the watcher thread, stops the box's
222
+ request servers, releasing each field, and cancels its pending request
223
+ futures, stops forwarding started with `events.follow`, and detaches
224
+ this box. Later reads and
195
225
  writes raise `BoxClosedError`. Garbage collection closes a box too, but
196
226
  forwarding it started keeps running until the cycle collector frees its
197
227
  events groups.
@@ -340,6 +370,9 @@ A read decodes the stored bytes; a value that cannot be changed (a
340
370
  `datetime`, or a frozen record, tuple or frozenset holding only such values)
341
371
  is reused until its field is written. `update(**values)` writes several
342
372
  fields at once; `watch(field)` and `events` report changes from any process.
373
+ A field declared `Attr[T]` with `attr()` is published by its owner with
374
+ `publish` and read by any process, and may take requests the owner serves;
375
+ see `docs/how-to/request-work-from-the-owner.md`.
343
376
  C++ and C code take a box through `__sharedbox_box__` or open it by name; see
344
377
  `docs/how-to/accept-a-box-in-cpp.md`, `docs/how-to/accept-a-box-in-c.md` and
345
378
  `docs/how-to/open-a-box-from-a-program.md`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sharedbox
3
- Version: 0.7.0
3
+ Version: 0.9.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")
@@ -1,7 +1,7 @@
1
1
  """The script of the guide "How to clean up segments"."""
2
2
 
3
3
  # --8<-- [start:close]
4
- from sharedbox import LockTimeoutError, SharedBox
4
+ from sharedbox import LockTimeoutError, SharedBox, segment_status
5
5
 
6
6
 
7
7
  class Delivery(SharedBox, name="example-delivery"):
@@ -18,6 +18,10 @@ delivery.close()
18
18
  Delivery.unlink()
19
19
  # --8<-- [end:unlink]
20
20
 
21
+ # --8<-- [start:status]
22
+ print(segment_status("example-delivery")) # missing
23
+ # --8<-- [end:status]
24
+
21
25
  creator = Delivery(True)
22
26
 
23
27
  # --8<-- [start:force-unlock]
@@ -0,0 +1,95 @@
1
+ """The script of the guide "How to request work from the process that owns a box"."""
2
+
3
+ # --8<-- [start:declare]
4
+ import asyncio
5
+ import threading
6
+
7
+ from sharedbox import Attr, RequestError, RequestSupersededError, SharedBox, attr
8
+
9
+
10
+ class Camera(SharedBox, name="example-camera"):
11
+ temperature: Attr[float] = attr(timed=True)
12
+ exposure: Attr[float] = attr(timed=True, settable=True, default=0.01)
13
+ move: Attr[float, [float]] = attr()
14
+ home: Attr[None] = attr()
15
+
16
+
17
+ # --8<-- [end:declare]
18
+
19
+ # --8<-- [start:publish]
20
+ camera = Camera()
21
+ camera.temperature.publish(value=21.5)
22
+ print(camera.temperature.value) # 21.5
23
+ reading = camera.temperature.read()
24
+ print(reading["value"], reading["timestamp_ns"] > 0) # 21.5 True
25
+ # --8<-- [end:publish]
26
+
27
+
28
+ # --8<-- [start:serve]
29
+ def set_exposure(value: float) -> float:
30
+ accepted = min(max(value, 0.001), 1.0)
31
+ camera.exposure.publish(setpoint=accepted)
32
+ camera.exposure.publish(value=accepted)
33
+ return accepted
34
+
35
+
36
+ def move(target: float) -> float:
37
+ if target > 100.0:
38
+ raise ValueError(f"{target} is out of range")
39
+ return target
40
+
41
+
42
+ exposure_server = camera.exposure.serve(set_exposure)
43
+ move_server = camera.move.serve(move)
44
+ # --8<-- [end:serve]
45
+
46
+ # --8<-- [start:request]
47
+ print(camera.exposure.set(5.0).result(timeout=5)) # 1.0
48
+ print(camera.exposure.setpoint) # 1.0
49
+ print(camera.move(5.0).result(timeout=5)) # 5.0
50
+
51
+
52
+ async def main() -> None:
53
+ print(await camera.exposure.set(0.02)) # 0.02
54
+
55
+
56
+ asyncio.run(main())
57
+ # --8<-- [end:request]
58
+
59
+ # --8<-- [start:error]
60
+ try:
61
+ camera.move(250.0).result(timeout=5)
62
+ except RequestError as error:
63
+ print(error) # Camera.move: ValueError: 250.0 is out of range
64
+ # --8<-- [end:error]
65
+
66
+ move_server.stop()
67
+
68
+ # --8<-- [start:supersede]
69
+ started = threading.Event()
70
+ release = threading.Event()
71
+
72
+
73
+ def slow_move(target: float) -> float:
74
+ started.set()
75
+ release.wait()
76
+ return target
77
+
78
+
79
+ move_server = camera.move.serve(slow_move)
80
+ first = camera.move(1.0)
81
+ started.wait()
82
+ second = camera.move(2.0)
83
+ third = camera.move(3.0)
84
+ release.set()
85
+ print(first.result(timeout=5), third.result(timeout=5)) # 1.0 3.0
86
+ try:
87
+ second.result(timeout=5)
88
+ except RequestSupersededError:
89
+ print("the second request was superseded")
90
+ # --8<-- [end:supersede]
91
+
92
+ move_server.stop()
93
+ exposure_server.stop()
94
+ camera.close()
95
+ Camera.unlink()
@@ -12,6 +12,9 @@ from sharedbox import (
12
12
  Capacity,
13
13
  DType,
14
14
  EndOfStream,
15
+ ItemGoneError,
16
+ MaxShape,
17
+ ReaderReleased,
15
18
  Shape,
16
19
  SharedReceiveStream,
17
20
  SharedSendStream,
@@ -23,7 +26,7 @@ from sharedbox import (
23
26
  class Letter:
24
27
  index: int
25
28
  recipient: Annotated[str, Capacity(32)]
26
- scan: Annotated[np.ndarray, Shape(32, 32), DType("uint8")]
29
+ scan: Annotated[np.ndarray, Shape((32, 32)), DType("uint8")]
27
30
 
28
31
 
29
32
  # --8<-- [end:types]
@@ -41,6 +44,23 @@ def register(stream: SharedStream[Letter], ready: "mp.synchronize.Event") -> Non
41
44
  # --8<-- [end:reader]
42
45
 
43
46
 
47
+ # --8<-- [start:fetch]
48
+ def fetch_letters(reader: SharedReceiveStream[Letter], first: int, last: int) -> None:
49
+ position = first
50
+ while position <= last:
51
+ try:
52
+ letter = reader.fetch(position, timeout=5)
53
+ except ItemGoneError as gone:
54
+ print("skipped", position, "to", gone.oldest - 1)
55
+ position = gone.oldest
56
+ continue
57
+ print("fetched", letter.index)
58
+ position += 1
59
+
60
+
61
+ # --8<-- [end:fetch]
62
+
63
+
44
64
  # --8<-- [start:tasks]
45
65
  async def produce(sender: SharedSendStream[Letter]) -> None:
46
66
  async with sender:
@@ -93,6 +113,50 @@ def send_filled(sender: SharedSendStream[Letter], count: int) -> None:
93
113
  # --8<-- [end:writing]
94
114
 
95
115
 
116
+ # --8<-- [start:shapes]
117
+ @dataclass(frozen=True)
118
+ class Sack:
119
+ index: int
120
+ weights: Annotated[np.ndarray, MaxShape((64,)), DType("float32")]
121
+
122
+
123
+ def send_sacks(sender: SharedSendStream[Sack]) -> None:
124
+ sender.send(Sack(0, np.array([1.5, 0.25, 4.0], np.float32)))
125
+ template = Sack(1, np.empty(64, np.float32))
126
+ with sender.writing(template) as slot:
127
+ weights = slot.item.weights
128
+ weights[:5] = 2.5
129
+ slot.send(replace(slot.item, weights=weights[:5]))
130
+
131
+
132
+ def read_sacks(reader: SharedReceiveStream[Sack]) -> None:
133
+ weights = np.empty(64, np.float32)
134
+ for sack in reader.iter_into({"weights": weights}):
135
+ print(sack.index, sack.weights.shape)
136
+
137
+
138
+ # --8<-- [end:shapes]
139
+
140
+
141
+ # --8<-- [start:release]
142
+ def sort_without_stalling(sender: SharedSendStream[Letter], count: int) -> None:
143
+ for i in range(count):
144
+ sender.send(Letter(i, "Ada", np.full((32, 32), i, np.uint8)), timeout=0.1)
145
+
146
+
147
+ def file_letters(reader: SharedReceiveStream[Letter]) -> None:
148
+ while True:
149
+ try:
150
+ for letter in reader:
151
+ print("filed", letter.index)
152
+ return
153
+ except ReaderReleased as released:
154
+ print("missed", released.missed)
155
+
156
+
157
+ # --8<-- [end:release]
158
+
159
+
96
160
  # --8<-- [start:main]
97
161
  if __name__ == "__main__":
98
162
  context = mp.get_context("spawn")
@@ -127,3 +191,22 @@ if __name__ == "__main__":
127
191
  for letter in reader:
128
192
  print(letter.index, int(letter.scan[0, 0]))
129
193
  SharedStream.unlink("example-sorting:filled")
194
+ with SharedStream.create(Sack, "example-sorting:sacks", capacity=4) as sacks:
195
+ sack_reader = sacks.reader()
196
+ with sacks.sender() as sack_sender:
197
+ send_sacks(sack_sender)
198
+ read_sacks(sack_reader)
199
+ SharedStream.unlink("example-sorting:sacks")
200
+ with SharedStream.create(Letter, "example-sorting:release", capacity=4) as stream:
201
+ reader = stream.reader()
202
+ with stream.sender(on_full="release") as sender:
203
+ sort_without_stalling(sender, 6)
204
+ file_letters(reader)
205
+ SharedStream.unlink("example-sorting:release")
206
+ with SharedStream.create(Letter, "example-sorting:fetch", capacity=8) as stream:
207
+ reader = stream.reader(mode="lossy")
208
+ with stream.sender() as sender:
209
+ for i in range(6):
210
+ sender.send(Letter(i, "Ada", np.full((32, 32), i, np.uint8)))
211
+ fetch_letters(reader, 2, 4)
212
+ SharedStream.unlink("example-sorting:fetch")
@@ -15,7 +15,7 @@ class Recording(SharedBox, name="example-recording"):
15
15
  # --8<-- [end:declare]
16
16
 
17
17
  # --8<-- [start:create]
18
- recording = Recording(BoundedList[float]([0.0] * 50000, capacity=65536))
18
+ recording = Recording(BoundedList(float, [0.0] * 50000, capacity=65536))
19
19
  # --8<-- [end:create]
20
20
 
21
21
  # --8<-- [start:change]