sharedbox 0.4.0__tar.gz → 0.4.1__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 (199) hide show
  1. sharedbox-0.4.1/.github/dependabot.yml +23 -0
  2. {sharedbox-0.4.0 → sharedbox-0.4.1}/.github/workflows/check-docs.yaml +2 -2
  3. {sharedbox-0.4.0 → sharedbox-0.4.1}/.github/workflows/ci.yaml +15 -15
  4. {sharedbox-0.4.0 → sharedbox-0.4.1}/.github/workflows/codspeed.yml +1 -1
  5. {sharedbox-0.4.0 → sharedbox-0.4.1}/.github/workflows/publish-docs.yaml +2 -2
  6. {sharedbox-0.4.0 → sharedbox-0.4.1}/CHANGELOG.md +13 -0
  7. {sharedbox-0.4.0 → sharedbox-0.4.1}/PKG-INFO +1 -1
  8. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_events.py +45 -0
  9. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_native/codec.cpp +8 -4
  10. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_version.py +3 -3
  11. sharedbox-0.4.1/tests/test_exit.py +53 -0
  12. {sharedbox-0.4.0 → sharedbox-0.4.1}/.clang-format +0 -0
  13. {sharedbox-0.4.0 → sharedbox-0.4.1}/.claude/settings.json +0 -0
  14. {sharedbox-0.4.0 → sharedbox-0.4.1}/.gitignore +0 -0
  15. {sharedbox-0.4.0 → sharedbox-0.4.1}/CLAUDE.md +0 -0
  16. {sharedbox-0.4.0 → sharedbox-0.4.1}/CMakeLists.txt +0 -0
  17. {sharedbox-0.4.0 → sharedbox-0.4.1}/LICENSE +0 -0
  18. {sharedbox-0.4.0 → sharedbox-0.4.1}/README.md +0 -0
  19. {sharedbox-0.4.0 → sharedbox-0.4.1}/benchmarks/test_bench_box.py +0 -0
  20. {sharedbox-0.4.0 → sharedbox-0.4.1}/cmake/sharedbox-config.cmake +0 -0
  21. {sharedbox-0.4.0 → sharedbox-0.4.1}/cmake/sharedbox-require-cxx.cmake +0 -0
  22. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/assets/benchmarks/ops-dark.svg +0 -0
  23. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/assets/benchmarks/ops-light.svg +0 -0
  24. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/assets/benchmarks/roundtrip-dark.svg +0 -0
  25. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/assets/benchmarks/roundtrip-light.svg +0 -0
  26. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/examples/change_several_fields_at_once.py +0 -0
  27. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/examples/clean_up_segments.py +0 -0
  28. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/examples/follow_a_whole_reference_graph.py +0 -0
  29. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/examples/name_a_box.py +0 -0
  30. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/examples/send_a_box_to_another_process.py +0 -0
  31. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/examples/set_defaults_and_check_values.py +0 -0
  32. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/examples/store_arrays.py +0 -0
  33. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/examples/store_collections.py +0 -0
  34. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/examples/store_records.py +0 -0
  35. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/examples/store_text_and_bytes.py +0 -0
  36. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/checking-a-process-is-alive.md +0 -0
  37. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/closing-and-lifetime.md +0 -0
  38. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/field-types.md +0 -0
  39. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/glossary.md +0 -0
  40. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/how-a-box-is-stored.md +0 -0
  41. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/how-the-module-is-built.md +0 -0
  42. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/index.md +0 -0
  43. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/limits.md +0 -0
  44. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/performance.md +0 -0
  45. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/reading-and-writing.md +0 -0
  46. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/references.md +0 -0
  47. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/waiting-for-changes.md +0 -0
  48. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/explanation/when-to-use-sharedbox.md +0 -0
  49. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/accept-a-box-in-c.md +0 -0
  50. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/accept-a-box-in-cpp.md +0 -0
  51. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/ai-contribution-policy.md +0 -0
  52. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/build-docs.md +0 -0
  53. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/change-several-fields-at-once.md +0 -0
  54. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/clean-up-segments.md +0 -0
  55. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/commits-and-prs.md +0 -0
  56. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/contribute.md +0 -0
  57. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/follow-a-whole-reference-graph.md +0 -0
  58. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/index.md +0 -0
  59. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/install-sharedbox.md +0 -0
  60. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/make-a-release.md +0 -0
  61. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/name-a-box.md +0 -0
  62. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/open-a-box-from-a-program.md +0 -0
  63. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/run-benchmarks.md +0 -0
  64. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/run-commit-checks.md +0 -0
  65. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/run-tests.md +0 -0
  66. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/send-a-box-to-another-process.md +0 -0
  67. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/set-defaults-and-check-values.md +0 -0
  68. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/set-up-development.md +0 -0
  69. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/store-arrays.md +0 -0
  70. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/store-collections.md +0 -0
  71. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/store-records.md +0 -0
  72. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/store-text-and-bytes.md +0 -0
  73. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/how-to/write-docs.md +0 -0
  74. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/index.md +0 -0
  75. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/reference/api/arrays.md +0 -0
  76. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/reference/api/box.md +0 -0
  77. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/reference/api/errors.md +0 -0
  78. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/reference/api/events.md +0 -0
  79. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/reference/api/index.md +0 -0
  80. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/reference/api/library-authors.md +0 -0
  81. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/reference/api/references.md +0 -0
  82. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/reference/changelog.md +0 -0
  83. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/reference/cpp-and-c-api.md +0 -0
  84. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/reference/index.md +0 -0
  85. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/reference/segment-layout.md +0 -0
  86. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/tutorials/index.md +0 -0
  87. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/tutorials/motor.py +0 -0
  88. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/tutorials/react-to-changes.md +0 -0
  89. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/tutorials/refer-to-another-box.md +0 -0
  90. {sharedbox-0.4.0 → sharedbox-0.4.1}/docs/tutorials/share-a-record.md +0 -0
  91. {sharedbox-0.4.0 → sharedbox-0.4.1}/include/sharedbox/sharedbox.hpp +0 -0
  92. {sharedbox-0.4.0 → sharedbox-0.4.1}/include/sharedbox/sharedbox_c.cpp +0 -0
  93. {sharedbox-0.4.0 → sharedbox-0.4.1}/include/sharedbox/sharedbox_c.h +0 -0
  94. {sharedbox-0.4.0 → sharedbox-0.4.1}/includes/abbreviations.md +0 -0
  95. {sharedbox-0.4.0 → sharedbox-0.4.1}/prek.toml +0 -0
  96. {sharedbox-0.4.0 → sharedbox-0.4.1}/pyproject.toml +0 -0
  97. {sharedbox-0.4.0 → sharedbox-0.4.1}/scripts/check_xrefs.py +0 -0
  98. {sharedbox-0.4.0 → sharedbox-0.4.1}/scripts/vscode_setup.py +0 -0
  99. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/__init__.py +0 -0
  100. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_arrays.py +0 -0
  101. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_box.py +0 -0
  102. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_follow.py +0 -0
  103. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_layout.py +0 -0
  104. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_native/codec.hpp +0 -0
  105. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_native/module.cpp +0 -0
  106. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_native/scalars.cpp +0 -0
  107. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_native/scalars.hpp +0 -0
  108. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_native/segment.cpp +0 -0
  109. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_native/segment.hpp +0 -0
  110. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_native/types.cpp +0 -0
  111. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_native/types.hpp +0 -0
  112. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_native.pyi +0 -0
  113. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_refs.py +0 -0
  114. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/_types.py +0 -0
  115. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/benchmarks/__init__.py +0 -0
  116. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/benchmarks/__main__.py +0 -0
  117. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/benchmarks/_app.py +0 -0
  118. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/benchmarks/cli.py +0 -0
  119. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/benchmarks/ops.py +0 -0
  120. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/benchmarks/plot.py +0 -0
  121. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/benchmarks/roundtrip.py +0 -0
  122. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/benchmarks/size.py +0 -0
  123. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/benchmarks/size_diff.py +0 -0
  124. {sharedbox-0.4.0 → sharedbox-0.4.1}/src/sharedbox/py.typed +0 -0
  125. {sharedbox-0.4.0 → sharedbox-0.4.1}/stubtest-allowlist.txt +0 -0
  126. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/conftest.py +0 -0
  127. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/CMakeLists.txt +0 -0
  128. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/compile_fail.cpp +0 -0
  129. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/consumer/CMakeLists.txt +0 -0
  130. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/consumer/consumer.c +0 -0
  131. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/table_builder.hpp +0 -0
  132. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_atomics.cpp +0 -0
  133. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_c_smoke.c +0 -0
  134. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_c_smoke_box.cpp +0 -0
  135. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_fork.cpp +0 -0
  136. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_layout2.cpp +0 -0
  137. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_lock.cpp +0 -0
  138. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_mapping.cpp +0 -0
  139. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_result.cpp +0 -0
  140. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_slots.cpp +0 -0
  141. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_small_shm.cpp +0 -0
  142. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_types.cpp +0 -0
  143. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_values.cpp +0 -0
  144. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_wait.cpp +0 -0
  145. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/test_windows_h.cpp +0 -0
  146. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/cpp/unique.hpp +0 -0
  147. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/crossproc.py +0 -0
  148. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/forged_open.py +0 -0
  149. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/forged_types.py +0 -0
  150. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/refs_future.py +0 -0
  151. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/stress/stress_helpers.py +0 -0
  152. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/stress/test_stress_contention.py +0 -0
  153. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/stress/test_stress_crashes.py +0 -0
  154. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/stress/test_stress_follow.py +0 -0
  155. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/stress/test_stress_lock_timeout.py +0 -0
  156. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/stress/test_stress_memory.py +0 -0
  157. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/stress/test_stress_waiters.py +0 -0
  158. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/stress/test_stress_watchers.py +0 -0
  159. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_benchbox_plot.py +0 -0
  160. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_box.py +0 -0
  161. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_capsule.py +0 -0
  162. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_doc_examples.py +0 -0
  163. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_doc_tutorials.py +0 -0
  164. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_events_async.py +0 -0
  165. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_events_bytes.py +0 -0
  166. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_events_signals.py +0 -0
  167. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_events_sync.py +0 -0
  168. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_fields.py +0 -0
  169. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_follow.py +0 -0
  170. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_fork.py +0 -0
  171. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_layout.py +0 -0
  172. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_layout_v1.py +0 -0
  173. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_native_codec.py +0 -0
  174. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_native_liveness.py +0 -0
  175. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_native_segment.py +0 -0
  176. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_native_types.py +0 -0
  177. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_native_wait.py +0 -0
  178. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_native_waiters.py +0 -0
  179. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_properties_codec.py +0 -0
  180. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_properties_forged.py +0 -0
  181. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_properties_layout.py +0 -0
  182. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_properties_stateful.py +0 -0
  183. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_properties_types.py +0 -0
  184. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_refs.py +0 -0
  185. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_refs_lazy.py +0 -0
  186. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_types_arrays.py +0 -0
  187. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_types_arrays_bfloat16.py +0 -0
  188. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_types_arrays_torch.py +0 -0
  189. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_types_choices.py +0 -0
  190. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_types_collections.py +0 -0
  191. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_types_records.py +0 -0
  192. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_types_records_attrs.py +0 -0
  193. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_types_records_msgspec.py +0 -0
  194. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_types_scalars.py +0 -0
  195. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/test_types_unions.py +0 -0
  196. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/type_checks/box_types.py +0 -0
  197. {sharedbox-0.4.0 → sharedbox-0.4.1}/tests/type_checks/events_types.py +0 -0
  198. {sharedbox-0.4.0 → sharedbox-0.4.1}/uv.lock +0 -0
  199. {sharedbox-0.4.0 → sharedbox-0.4.1}/zensical.toml +0 -0
@@ -0,0 +1,23 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ commit-message:
8
+ prefix: ci
9
+ groups:
10
+ actions:
11
+ patterns:
12
+ - "*"
13
+ - package-ecosystem: uv
14
+ directory: /
15
+ schedule:
16
+ interval: weekly
17
+ commit-message:
18
+ prefix: build
19
+ include: scope
20
+ groups:
21
+ python:
22
+ patterns:
23
+ - "*"
@@ -8,7 +8,7 @@ jobs:
8
8
  name: Build and check cross-references
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
- - uses: actions/checkout@v5
11
+ - uses: actions/checkout@v7
12
12
 
13
13
  - name: Install uv
14
14
  uses: astral-sh/setup-uv@v7
@@ -23,7 +23,7 @@ jobs:
23
23
  # the publish job deploys this artifact rather than building again,
24
24
  # so what reaches Pages is what was checked
25
25
  - name: Store the built site
26
- uses: actions/upload-artifact@v4
26
+ uses: actions/upload-artifact@v7
27
27
  with:
28
28
  name: docs-site
29
29
  path: site/
@@ -14,7 +14,7 @@ jobs:
14
14
  name: Lint
15
15
  runs-on: ubuntu-latest
16
16
  steps:
17
- - uses: actions/checkout@v5
17
+ - uses: actions/checkout@v7
18
18
 
19
19
  # local hooks in prek.toml call `uv run --locked --only-group lint ...`
20
20
  - uses: astral-sh/setup-uv@v7
@@ -62,7 +62,7 @@ jobs:
62
62
  ASAN_OPTIONS: detect_leaks=1
63
63
  UBSAN_OPTIONS: print_stacktrace=1
64
64
  steps:
65
- - uses: actions/checkout@v5
65
+ - uses: actions/checkout@v7
66
66
  with:
67
67
  fetch-depth: 0
68
68
 
@@ -108,13 +108,13 @@ jobs:
108
108
  - { os: ubuntu-latest, python: cp314t, platform_id: musllinux_x86_64 }
109
109
 
110
110
  steps:
111
- - uses: actions/checkout@v5
111
+ - uses: actions/checkout@v7
112
112
  with:
113
113
  fetch-depth: 0
114
114
  ref: ${{ github.event_name == 'release' && github.event.release.tag_name || '' }}
115
115
 
116
116
  - name: Build wheels
117
- uses: pypa/cibuildwheel@v3.2.0
117
+ uses: pypa/cibuildwheel@v4.2.1
118
118
  env:
119
119
  CIBW_BUILD: ${{ matrix.python }}-${{ matrix.platform_id }}
120
120
  CIBW_CONTAINER_ENGINE: "docker; create_args: --shm-size=1g"
@@ -125,7 +125,7 @@ jobs:
125
125
  output-dir: wheelhouse
126
126
 
127
127
  - name: Upload wheel artifacts
128
- uses: actions/upload-artifact@v4
128
+ uses: actions/upload-artifact@v7
129
129
  with:
130
130
  name: cibw-wheels-${{ matrix.python }}-${{ matrix.platform_id }}
131
131
  path: ./wheelhouse/*.whl
@@ -139,7 +139,7 @@ jobs:
139
139
  python src/sharedbox/benchmarks/size.py wheelhouse/*.whl --json wheelhouse/sizes.json --markdown >> "$GITHUB_STEP_SUMMARY"
140
140
 
141
141
  - name: Upload size artifact
142
- uses: actions/upload-artifact@v4
142
+ uses: actions/upload-artifact@v7
143
143
  with:
144
144
  name: wheel-sizes-${{ matrix.python }}-${{ matrix.platform_id }}
145
145
  path: wheelhouse/sizes.json
@@ -153,10 +153,10 @@ jobs:
153
153
  actions: read
154
154
  continue-on-error: true
155
155
  steps:
156
- - uses: actions/checkout@v5
156
+ - uses: actions/checkout@v7
157
157
 
158
158
  - name: Download this run's size artifacts
159
- uses: actions/download-artifact@v5
159
+ uses: actions/download-artifact@v8
160
160
  with:
161
161
  pattern: wheel-sizes-*
162
162
  path: sizes/this
@@ -173,7 +173,7 @@ jobs:
173
173
  - name: Download the baseline's size artifacts
174
174
  if: steps.baseline.outputs.run_id != ''
175
175
  continue-on-error: true
176
- uses: actions/download-artifact@v5
176
+ uses: actions/download-artifact@v8
177
177
  with:
178
178
  pattern: wheel-sizes-*
179
179
  path: sizes/main
@@ -199,11 +199,11 @@ jobs:
199
199
  - { os: ubuntu-latest, wheel: cibw-wheels-cp312-manylinux_x86_64 }
200
200
  - { os: windows-latest, wheel: cibw-wheels-cp312-win_amd64 }
201
201
  steps:
202
- - uses: actions/checkout@v5
202
+ - uses: actions/checkout@v7
203
203
 
204
204
  - uses: astral-sh/setup-uv@v7
205
205
 
206
- - uses: actions/download-artifact@v5
206
+ - uses: actions/download-artifact@v8
207
207
  with:
208
208
  name: ${{ matrix.wheel }}
209
209
  path: wheelhouse
@@ -218,7 +218,7 @@ jobs:
218
218
  "$python" -m pytest -m stress -q -p no:cacheprovider
219
219
  "$python" -m pytest tests/test_properties_codec.py tests/test_properties_layout.py tests/test_properties_forged.py tests/test_properties_stateful.py -q -p no:cacheprovider --hypothesis-profile=thorough
220
220
 
221
- - uses: actions/upload-artifact@v4
221
+ - uses: actions/upload-artifact@v7
222
222
  if: always()
223
223
  with:
224
224
  name: stress-${{ matrix.os }}
@@ -243,7 +243,7 @@ jobs:
243
243
  *) [ "$PRERELEASE" = "false" ] || { echo "::error::$TAG must not be marked pre-release"; exit 1; } ;;
244
244
  esac
245
245
 
246
- - uses: actions/checkout@v5
246
+ - uses: actions/checkout@v7
247
247
  with:
248
248
  fetch-depth: 0
249
249
  ref: ${{ github.event_name == 'release' && github.event.release.tag_name || '' }}
@@ -256,7 +256,7 @@ jobs:
256
256
  TAG: ${{ github.event.release.tag_name }}
257
257
  run: test -f "dist/sharedbox-${TAG#v}.tar.gz"
258
258
 
259
- - uses: actions/upload-artifact@v4
259
+ - uses: actions/upload-artifact@v7
260
260
  with:
261
261
  name: cibw-sdist
262
262
  path: dist/*.tar.gz
@@ -272,7 +272,7 @@ jobs:
272
272
  id-token: write
273
273
  if: github.event_name == 'release' && github.event.action == 'published'
274
274
  steps:
275
- - uses: actions/download-artifact@v5
275
+ - uses: actions/download-artifact@v8
276
276
  with:
277
277
  # unpacks all CIBW artifacts into dist/
278
278
  pattern: cibw-*
@@ -16,7 +16,7 @@ jobs:
16
16
  name: Run benchmarks
17
17
  runs-on: ubuntu-latest
18
18
  steps:
19
- - uses: actions/checkout@v5
19
+ - uses: actions/checkout@v7
20
20
  with:
21
21
  fetch-depth: 0
22
22
 
@@ -10,10 +10,10 @@ jobs:
10
10
  permissions:
11
11
  contents: write
12
12
  steps:
13
- - uses: actions/checkout@v5
13
+ - uses: actions/checkout@v7
14
14
 
15
15
  - name: Download the built site
16
- uses: actions/download-artifact@v5
16
+ uses: actions/download-artifact@v8
17
17
  with:
18
18
  name: docs-site
19
19
  path: site/
@@ -9,6 +9,19 @@ Dates are marked as `DD-MM-YYYY`
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ### Changed
13
+
14
+ - `SharedBox.update`: takes about 7 to 9 ns less per call on Windows.
15
+
16
+ ### Fixed
17
+
18
+ - `SharedBox.events`, `SharedBox.watch` and `BoxEvents.follow`: a process
19
+ that exits while watcher threads still run no longer aborts with
20
+ `terminate called without an active exception` on Linux with CPython
21
+ 3.11 to 3.13. At exit, sharedbox stops every watcher thread and waits
22
+ up to 2 seconds in all for them to end, including any callback they are
23
+ running.
24
+
12
25
  ## [0.4.0] - 05-10-2026
13
26
 
14
27
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sharedbox
3
- Version: 0.4.0
3
+ Version: 0.4.1
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,9 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import atexit
4
5
  import contextlib
5
6
  import logging
6
7
  import threading
8
+ import time
7
9
  from collections.abc import AsyncIterator, Callable, Generator, Iterator
8
10
  from concurrent.futures import CancelledError
9
11
  from typing import (
@@ -32,11 +34,14 @@ if TYPE_CHECKING:
32
34
  T = TypeVar("T")
33
35
  PENDING, DONE, CANCELLED = "pending", "done", "cancelled"
34
36
  STEP: Final = 1.0
37
+ # Seconds the exit handler waits for stopped watcher threads, together.
38
+ EXIT_WAIT: Final = 2.0
35
39
  logger = logging.getLogger("sharedbox")
36
40
  # Receives each change of a field as native values: the field, the new value, the old one.
37
41
  Sink: TypeAlias = "Callable[[FieldSpec, Any, Any], None]"
38
42
  # Marks watcher threads: one must not join another, since that one may be joining it.
39
43
  WATCHER_THREAD = threading.local()
44
+ RUNNING: set[Watcher] = set()
40
45
 
41
46
 
42
47
  def on_watcher_thread() -> bool:
@@ -44,6 +49,43 @@ def on_watcher_thread() -> bool:
44
49
  return getattr(WATCHER_THREAD, "active", False)
45
50
 
46
51
 
52
+ def stop_watchers() -> None:
53
+ """Stop every watcher thread and wait for them to end, before the interpreter finalizes.
54
+
55
+ The wait lasts at most `EXIT_WAIT` seconds in all; a thread still running
56
+ a callback after that is logged and left running. On CPython before 3.14, a daemon thread that takes the GIL back during
57
+ finalization is ended with `pthread_exit` on Linux. The unwinding that
58
+ starts there aborts the process when it reaches the native call that
59
+ released the GIL, such as a watcher's wait or a box's `close`.
60
+ """
61
+ # A copy, because watcher threads remove themselves from the set while this runs.
62
+ watchers = RUNNING.copy()
63
+ threads = [watcher._thread for watcher in watchers]
64
+ for watcher in watchers:
65
+ watcher.stop(wait=False)
66
+ # Bounded, so a callback that never returns cannot keep the process from exiting.
67
+ deadline = time.monotonic() + EXIT_WAIT
68
+ for thread in threads:
69
+ if thread is not None and thread is not threading.current_thread():
70
+ thread.join(max(0.0, deadline - time.monotonic()))
71
+ late = [
72
+ thread.name
73
+ for thread in threads
74
+ if thread is not None
75
+ and thread.is_alive()
76
+ and thread is not threading.current_thread()
77
+ ]
78
+ if late:
79
+ logger.warning(
80
+ "watcher threads still running at exit after %s s: %s",
81
+ EXIT_WAIT,
82
+ ", ".join(late),
83
+ )
84
+
85
+
86
+ atexit.register(stop_watchers)
87
+
88
+
47
89
  class FieldFuture(Generic[T]):
48
90
  """The next value written to one field of a box.
49
91
 
@@ -402,6 +444,7 @@ class Watcher:
402
444
  self._thread = None
403
445
  self._pending = []
404
446
  self._slot = None
447
+ RUNNING.discard(self)
405
448
 
406
449
  def _start_locked(self) -> None:
407
450
  if self._thread is None and not self._stop.is_set():
@@ -410,6 +453,7 @@ class Watcher:
410
453
  name=f"sharedbox-watch-{self._segment.name}",
411
454
  daemon=True,
412
455
  )
456
+ RUNNING.add(self)
413
457
  self._thread.start()
414
458
 
415
459
  def _claim(self) -> int | None:
@@ -456,6 +500,7 @@ class Watcher:
456
500
  # The step bounds how long a freed slot or a missed wake-up goes unnoticed.
457
501
  generation = self._segment.wait(generation, STEP, slot)
458
502
  finally:
503
+ RUNNING.discard(self)
459
504
  if self._slot is not None:
460
505
  self._segment.release_waiter(self._slot)
461
506
 
@@ -206,10 +206,14 @@ void encode_all(const Segment &s, const Types *types, std::span<const Pending> v
206
206
  // Checked before the narrowing cast, which would otherwise turn index 65536 into field 0.
207
207
  check_index(index, fields.size());
208
208
  const FieldDesc &field = fields[index];
209
- out[i] = {static_cast<std::uint16_t>(index),
210
- static_cast<std::uint8_t>(field.kind) <= kind_ref
211
- ? encode(field, names[index], values[i].value, buffers[i])
212
- : need(types, index).encode(index, values[i].value, buffers[i])};
209
+ // The members are written separately because MSVC copies a value built in one step through
210
+ // a stack temporary and reloads it, which stalls for about 10 ns per update.
211
+ const std::span<const std::byte> bytes =
212
+ static_cast<std::uint8_t>(field.kind) <= kind_ref
213
+ ? encode(field, names[index], values[i].value, buffers[i])
214
+ : need(types, index).encode(index, values[i].value, buffers[i]);
215
+ out[i].field = static_cast<std::uint16_t>(index);
216
+ out[i].bytes = bytes;
213
217
  }
214
218
  }
215
219
 
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.4.0'
22
- __version_tuple__ = version_tuple = (0, 4, 0)
21
+ __version__ = version = '0.4.1'
22
+ __version_tuple__ = version_tuple = (0, 4, 1)
23
23
 
24
- __commit_id__ = commit_id = 'g6937a5b84'
24
+ __commit_id__ = commit_id = 'gcd153a59e'
@@ -0,0 +1,53 @@
1
+ import multiprocessing as mp
2
+ import threading
3
+ import time
4
+ from typing import Annotated
5
+
6
+ import numpy as np
7
+
8
+ from sharedbox import DType, Shape, SharedBox
9
+
10
+
11
+ class Blob(SharedBox):
12
+ tick: int = 0
13
+ big: Annotated[np.ndarray, Shape(512, 512), DType("float64")] = np.zeros((512, 512))
14
+
15
+
16
+ def exit_during_a_callback(name: str) -> None:
17
+ """Return from the child while a callback is reading and closing fresh handles."""
18
+ box = Blob.attach(name)
19
+ started = threading.Event()
20
+
21
+ def churn(new: int, old: int) -> None:
22
+ started.set()
23
+ end = time.monotonic() + 1
24
+ while time.monotonic() < end:
25
+ with Blob.attach(name) as fresh:
26
+ _ = fresh.big
27
+
28
+ box.events.tick.connect(churn)
29
+ with Blob.attach(name) as writer:
30
+ while not started.wait(0.05):
31
+ writer.tick += 1
32
+
33
+
34
+ def test_a_process_exits_cleanly_while_a_callback_makes_native_calls(
35
+ unique_name: str,
36
+ ) -> None:
37
+ """Check that spawned processes exit with code 0 when a callback is inside native calls at exit."""
38
+ context = mp.get_context("spawn")
39
+ with Blob.create(unique_name):
40
+ children = [
41
+ context.Process(target=exit_during_a_callback, args=(unique_name,))
42
+ for _ in range(5)
43
+ ]
44
+ for child in children:
45
+ child.start()
46
+ for child in children:
47
+ child.join(60)
48
+ if child.is_alive():
49
+ child.kill()
50
+ child.join(10)
51
+ Blob.unlink(unique_name)
52
+ exitcodes = [child.exitcode for child in children]
53
+ assert exitcodes == [0] * len(children), f"exit codes: {exitcodes}"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes