sharedbox 0.8.0__tar.gz → 0.9.0__tar.gz

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