sharedbox 0.3.0rc0__tar.gz → 0.4.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 (225) hide show
  1. sharedbox-0.4.0/.claude/settings.json +33 -0
  2. sharedbox-0.4.0/.github/workflows/check-docs.yaml +31 -0
  3. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/.github/workflows/ci.yaml +108 -2
  4. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/.github/workflows/codspeed.yml +0 -7
  5. sharedbox-0.4.0/.github/workflows/publish-docs.yaml +25 -0
  6. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/.gitignore +9 -0
  7. sharedbox-0.4.0/CHANGELOG.md +287 -0
  8. sharedbox-0.4.0/CLAUDE.md +306 -0
  9. sharedbox-0.4.0/CMakeLists.txt +72 -0
  10. sharedbox-0.4.0/PKG-INFO +90 -0
  11. sharedbox-0.4.0/README.md +66 -0
  12. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/benchmarks/test_bench_box.py +4 -5
  13. sharedbox-0.4.0/cmake/sharedbox-config.cmake +23 -0
  14. sharedbox-0.4.0/cmake/sharedbox-require-cxx.cmake +32 -0
  15. sharedbox-0.4.0/docs/assets/benchmarks/ops-dark.svg +1209 -0
  16. sharedbox-0.4.0/docs/assets/benchmarks/ops-light.svg +1209 -0
  17. sharedbox-0.4.0/docs/assets/benchmarks/roundtrip-dark.svg +334 -0
  18. sharedbox-0.4.0/docs/assets/benchmarks/roundtrip-light.svg +334 -0
  19. sharedbox-0.4.0/docs/examples/change_several_fields_at_once.py +44 -0
  20. sharedbox-0.4.0/docs/examples/clean_up_segments.py +38 -0
  21. sharedbox-0.4.0/docs/examples/follow_a_whole_reference_graph.py +46 -0
  22. sharedbox-0.4.0/docs/examples/name_a_box.py +45 -0
  23. sharedbox-0.4.0/docs/examples/send_a_box_to_another_process.py +49 -0
  24. sharedbox-0.4.0/docs/examples/set_defaults_and_check_values.py +79 -0
  25. sharedbox-0.4.0/docs/examples/store_arrays.py +40 -0
  26. sharedbox-0.4.0/docs/examples/store_collections.py +39 -0
  27. sharedbox-0.4.0/docs/examples/store_records.py +52 -0
  28. sharedbox-0.4.0/docs/examples/store_text_and_bytes.py +46 -0
  29. sharedbox-0.4.0/docs/explanation/checking-a-process-is-alive.md +63 -0
  30. sharedbox-0.4.0/docs/explanation/closing-and-lifetime.md +136 -0
  31. sharedbox-0.4.0/docs/explanation/field-types.md +91 -0
  32. sharedbox-0.4.0/docs/explanation/glossary.md +114 -0
  33. sharedbox-0.4.0/docs/explanation/how-a-box-is-stored.md +145 -0
  34. sharedbox-0.4.0/docs/explanation/how-the-module-is-built.md +68 -0
  35. sharedbox-0.4.0/docs/explanation/index.md +44 -0
  36. sharedbox-0.4.0/docs/explanation/limits.md +63 -0
  37. sharedbox-0.4.0/docs/explanation/performance.md +78 -0
  38. sharedbox-0.4.0/docs/explanation/reading-and-writing.md +121 -0
  39. sharedbox-0.4.0/docs/explanation/references.md +79 -0
  40. sharedbox-0.4.0/docs/explanation/waiting-for-changes.md +184 -0
  41. sharedbox-0.4.0/docs/explanation/when-to-use-sharedbox.md +41 -0
  42. sharedbox-0.4.0/docs/how-to/accept-a-box-in-c.md +83 -0
  43. sharedbox-0.4.0/docs/how-to/accept-a-box-in-cpp.md +174 -0
  44. sharedbox-0.4.0/docs/how-to/ai-contribution-policy.md +58 -0
  45. sharedbox-0.4.0/docs/how-to/build-docs.md +80 -0
  46. sharedbox-0.4.0/docs/how-to/change-several-fields-at-once.md +59 -0
  47. sharedbox-0.4.0/docs/how-to/clean-up-segments.md +82 -0
  48. sharedbox-0.4.0/docs/how-to/commits-and-prs.md +68 -0
  49. sharedbox-0.4.0/docs/how-to/contribute.md +39 -0
  50. sharedbox-0.4.0/docs/how-to/follow-a-whole-reference-graph.md +70 -0
  51. sharedbox-0.4.0/docs/how-to/index.md +56 -0
  52. sharedbox-0.4.0/docs/how-to/install-sharedbox.md +61 -0
  53. sharedbox-0.4.0/docs/how-to/make-a-release.md +87 -0
  54. sharedbox-0.4.0/docs/how-to/name-a-box.md +76 -0
  55. sharedbox-0.4.0/docs/how-to/open-a-box-from-a-program.md +89 -0
  56. sharedbox-0.4.0/docs/how-to/run-benchmarks.md +99 -0
  57. sharedbox-0.4.0/docs/how-to/run-commit-checks.md +76 -0
  58. sharedbox-0.4.0/docs/how-to/run-tests.md +152 -0
  59. sharedbox-0.4.0/docs/how-to/send-a-box-to-another-process.md +69 -0
  60. sharedbox-0.4.0/docs/how-to/set-defaults-and-check-values.md +75 -0
  61. sharedbox-0.4.0/docs/how-to/set-up-development.md +73 -0
  62. sharedbox-0.4.0/docs/how-to/store-arrays.md +58 -0
  63. sharedbox-0.4.0/docs/how-to/store-collections.md +59 -0
  64. sharedbox-0.4.0/docs/how-to/store-records.md +68 -0
  65. sharedbox-0.4.0/docs/how-to/store-text-and-bytes.md +66 -0
  66. sharedbox-0.4.0/docs/how-to/write-docs.md +138 -0
  67. sharedbox-0.4.0/docs/index.md +63 -0
  68. sharedbox-0.4.0/docs/reference/api/arrays.md +28 -0
  69. sharedbox-0.4.0/docs/reference/api/box.md +33 -0
  70. sharedbox-0.4.0/docs/reference/api/errors.md +43 -0
  71. sharedbox-0.4.0/docs/reference/api/events.md +22 -0
  72. sharedbox-0.4.0/docs/reference/api/index.md +20 -0
  73. sharedbox-0.4.0/docs/reference/api/library-authors.md +22 -0
  74. sharedbox-0.4.0/docs/reference/api/references.md +13 -0
  75. sharedbox-0.4.0/docs/reference/changelog.md +5 -0
  76. sharedbox-0.4.0/docs/reference/cpp-and-c-api.md +36 -0
  77. sharedbox-0.4.0/docs/reference/index.md +16 -0
  78. sharedbox-0.4.0/docs/reference/segment-layout.md +979 -0
  79. sharedbox-0.4.0/docs/tutorials/index.md +16 -0
  80. sharedbox-0.4.0/docs/tutorials/motor.py +102 -0
  81. sharedbox-0.4.0/docs/tutorials/react-to-changes.md +130 -0
  82. sharedbox-0.4.0/docs/tutorials/refer-to-another-box.md +92 -0
  83. sharedbox-0.4.0/docs/tutorials/share-a-record.md +115 -0
  84. sharedbox-0.4.0/include/sharedbox/sharedbox.hpp +3215 -0
  85. sharedbox-0.4.0/include/sharedbox/sharedbox_c.cpp +321 -0
  86. sharedbox-0.4.0/include/sharedbox/sharedbox_c.h +131 -0
  87. sharedbox-0.4.0/includes/abbreviations.md +11 -0
  88. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/prek.toml +1 -1
  89. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/pyproject.toml +53 -36
  90. sharedbox-0.4.0/scripts/check_xrefs.py +108 -0
  91. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/scripts/vscode_setup.py +6 -18
  92. sharedbox-0.4.0/src/sharedbox/__init__.py +59 -0
  93. sharedbox-0.4.0/src/sharedbox/_arrays.py +163 -0
  94. sharedbox-0.4.0/src/sharedbox/_box.py +1226 -0
  95. sharedbox-0.4.0/src/sharedbox/_events.py +605 -0
  96. sharedbox-0.4.0/src/sharedbox/_follow.py +437 -0
  97. sharedbox-0.4.0/src/sharedbox/_layout.py +499 -0
  98. sharedbox-0.4.0/src/sharedbox/_native/codec.cpp +216 -0
  99. sharedbox-0.4.0/src/sharedbox/_native/codec.hpp +87 -0
  100. sharedbox-0.4.0/src/sharedbox/_native/module.cpp +947 -0
  101. sharedbox-0.4.0/src/sharedbox/_native/scalars.cpp +490 -0
  102. sharedbox-0.4.0/src/sharedbox/_native/scalars.hpp +31 -0
  103. sharedbox-0.4.0/src/sharedbox/_native/segment.cpp +592 -0
  104. sharedbox-0.4.0/src/sharedbox/_native/segment.hpp +164 -0
  105. sharedbox-0.4.0/src/sharedbox/_native/types.cpp +833 -0
  106. sharedbox-0.4.0/src/sharedbox/_native/types.hpp +110 -0
  107. sharedbox-0.4.0/src/sharedbox/_native.pyi +371 -0
  108. sharedbox-0.4.0/src/sharedbox/_refs.py +187 -0
  109. sharedbox-0.4.0/src/sharedbox/_types.py +985 -0
  110. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/src/sharedbox/_version.py +3 -3
  111. sharedbox-0.4.0/src/sharedbox/benchmarks/__init__.py +5 -0
  112. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/src/sharedbox/benchmarks/_app.py +25 -7
  113. sharedbox-0.4.0/src/sharedbox/benchmarks/cli.py +21 -0
  114. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/src/sharedbox/benchmarks/ops.py +82 -19
  115. sharedbox-0.4.0/src/sharedbox/benchmarks/plot.py +161 -0
  116. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/src/sharedbox/benchmarks/roundtrip.py +7 -1
  117. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/src/sharedbox/benchmarks/size_diff.py +1 -1
  118. sharedbox-0.4.0/stubtest-allowlist.txt +14 -0
  119. sharedbox-0.4.0/tests/conftest.py +116 -0
  120. sharedbox-0.4.0/tests/cpp/CMakeLists.txt +99 -0
  121. sharedbox-0.4.0/tests/cpp/compile_fail.cpp +24 -0
  122. sharedbox-0.4.0/tests/cpp/consumer/CMakeLists.txt +9 -0
  123. sharedbox-0.4.0/tests/cpp/consumer/consumer.c +58 -0
  124. sharedbox-0.4.0/tests/cpp/table_builder.hpp +49 -0
  125. sharedbox-0.4.0/tests/cpp/test_atomics.cpp +159 -0
  126. sharedbox-0.4.0/tests/cpp/test_c_smoke.c +77 -0
  127. sharedbox-0.4.0/tests/cpp/test_c_smoke_box.cpp +39 -0
  128. sharedbox-0.4.0/tests/cpp/test_fork.cpp +57 -0
  129. sharedbox-0.4.0/tests/cpp/test_layout2.cpp +126 -0
  130. sharedbox-0.4.0/tests/cpp/test_lock.cpp +103 -0
  131. sharedbox-0.4.0/tests/cpp/test_mapping.cpp +311 -0
  132. sharedbox-0.4.0/tests/cpp/test_result.cpp +121 -0
  133. sharedbox-0.4.0/tests/cpp/test_slots.cpp +157 -0
  134. sharedbox-0.4.0/tests/cpp/test_small_shm.cpp +25 -0
  135. sharedbox-0.4.0/tests/cpp/test_types.cpp +293 -0
  136. sharedbox-0.4.0/tests/cpp/test_values.cpp +237 -0
  137. sharedbox-0.4.0/tests/cpp/test_wait.cpp +161 -0
  138. sharedbox-0.4.0/tests/cpp/test_windows_h.cpp +9 -0
  139. sharedbox-0.4.0/tests/cpp/unique.hpp +11 -0
  140. sharedbox-0.4.0/tests/crossproc.py +36 -0
  141. sharedbox-0.4.0/tests/forged_open.py +90 -0
  142. sharedbox-0.4.0/tests/forged_types.py +100 -0
  143. sharedbox-0.4.0/tests/refs_future.py +10 -0
  144. sharedbox-0.4.0/tests/stress/stress_helpers.py +107 -0
  145. sharedbox-0.4.0/tests/stress/test_stress_contention.py +117 -0
  146. sharedbox-0.4.0/tests/stress/test_stress_crashes.py +79 -0
  147. sharedbox-0.4.0/tests/stress/test_stress_follow.py +165 -0
  148. sharedbox-0.4.0/tests/stress/test_stress_lock_timeout.py +299 -0
  149. sharedbox-0.4.0/tests/stress/test_stress_memory.py +172 -0
  150. sharedbox-0.4.0/tests/stress/test_stress_waiters.py +315 -0
  151. sharedbox-0.4.0/tests/stress/test_stress_watchers.py +126 -0
  152. sharedbox-0.4.0/tests/test_benchbox_plot.py +63 -0
  153. sharedbox-0.4.0/tests/test_box.py +1040 -0
  154. sharedbox-0.4.0/tests/test_capsule.py +235 -0
  155. sharedbox-0.4.0/tests/test_doc_examples.py +25 -0
  156. sharedbox-0.4.0/tests/test_doc_tutorials.py +40 -0
  157. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/tests/test_events_async.py +16 -0
  158. sharedbox-0.4.0/tests/test_events_bytes.py +56 -0
  159. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/tests/test_events_signals.py +50 -0
  160. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/tests/test_events_sync.py +78 -0
  161. sharedbox-0.4.0/tests/test_fields.py +800 -0
  162. sharedbox-0.4.0/tests/test_follow.py +1182 -0
  163. sharedbox-0.4.0/tests/test_fork.py +348 -0
  164. sharedbox-0.4.0/tests/test_layout.py +203 -0
  165. sharedbox-0.4.0/tests/test_layout_v1.py +50 -0
  166. sharedbox-0.4.0/tests/test_native_codec.py +154 -0
  167. sharedbox-0.4.0/tests/test_native_liveness.py +66 -0
  168. sharedbox-0.4.0/tests/test_native_segment.py +859 -0
  169. sharedbox-0.4.0/tests/test_native_types.py +284 -0
  170. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/tests/test_native_wait.py +25 -17
  171. sharedbox-0.4.0/tests/test_native_waiters.py +405 -0
  172. sharedbox-0.4.0/tests/test_properties_codec.py +148 -0
  173. sharedbox-0.4.0/tests/test_properties_forged.py +24 -0
  174. sharedbox-0.4.0/tests/test_properties_layout.py +90 -0
  175. sharedbox-0.4.0/tests/test_properties_stateful.py +306 -0
  176. sharedbox-0.4.0/tests/test_properties_types.py +136 -0
  177. sharedbox-0.4.0/tests/test_refs.py +915 -0
  178. sharedbox-0.4.0/tests/test_refs_lazy.py +19 -0
  179. sharedbox-0.4.0/tests/test_types_arrays.py +212 -0
  180. sharedbox-0.4.0/tests/test_types_arrays_bfloat16.py +34 -0
  181. sharedbox-0.4.0/tests/test_types_arrays_torch.py +18 -0
  182. sharedbox-0.4.0/tests/test_types_choices.py +158 -0
  183. sharedbox-0.4.0/tests/test_types_collections.py +183 -0
  184. sharedbox-0.4.0/tests/test_types_records.py +311 -0
  185. sharedbox-0.4.0/tests/test_types_records_attrs.py +21 -0
  186. sharedbox-0.4.0/tests/test_types_records_msgspec.py +35 -0
  187. sharedbox-0.4.0/tests/test_types_scalars.py +142 -0
  188. sharedbox-0.4.0/tests/test_types_unions.py +170 -0
  189. sharedbox-0.4.0/tests/type_checks/box_types.py +96 -0
  190. sharedbox-0.4.0/tests/type_checks/events_types.py +23 -0
  191. sharedbox-0.4.0/uv.lock +2198 -0
  192. sharedbox-0.4.0/zensical.toml +216 -0
  193. sharedbox-0.3.0rc0/CHANGELOG.md +0 -55
  194. sharedbox-0.3.0rc0/CLAUDE.md +0 -193
  195. sharedbox-0.3.0rc0/CMakeLists.txt +0 -41
  196. sharedbox-0.3.0rc0/PKG-INFO +0 -222
  197. sharedbox-0.3.0rc0/README.md +0 -200
  198. sharedbox-0.3.0rc0/docs/api.md +0 -489
  199. sharedbox-0.3.0rc0/docs/design/native-segment.md +0 -546
  200. sharedbox-0.3.0rc0/src/sharedbox/__init__.py +0 -21
  201. sharedbox-0.3.0rc0/src/sharedbox/_box.py +0 -362
  202. sharedbox-0.3.0rc0/src/sharedbox/_events.py +0 -361
  203. sharedbox-0.3.0rc0/src/sharedbox/_layout.py +0 -192
  204. sharedbox-0.3.0rc0/src/sharedbox/_native/module.cpp +0 -86
  205. sharedbox-0.3.0rc0/src/sharedbox/_native/notifier.cpp +0 -80
  206. sharedbox-0.3.0rc0/src/sharedbox/_native/notifier.hpp +0 -31
  207. sharedbox-0.3.0rc0/src/sharedbox/_native/segment.cpp +0 -570
  208. sharedbox-0.3.0rc0/src/sharedbox/_native/segment.hpp +0 -76
  209. sharedbox-0.3.0rc0/src/sharedbox/_native.pyi +0 -88
  210. sharedbox-0.3.0rc0/src/sharedbox/benchmarks/__init__.py +0 -1
  211. sharedbox-0.3.0rc0/src/sharedbox/benchmarks/cli.py +0 -14
  212. sharedbox-0.3.0rc0/stubtest-allowlist.txt +0 -8
  213. sharedbox-0.3.0rc0/tests/conftest.py +0 -16
  214. sharedbox-0.3.0rc0/tests/test_box.py +0 -393
  215. sharedbox-0.3.0rc0/tests/test_fork.py +0 -126
  216. sharedbox-0.3.0rc0/tests/test_layout.py +0 -136
  217. sharedbox-0.3.0rc0/tests/test_native_segment.py +0 -309
  218. sharedbox-0.3.0rc0/tests/type_checks/box_types.py +0 -32
  219. sharedbox-0.3.0rc0/uv.lock +0 -763
  220. sharedbox-0.3.0rc0/vcpkg.json +0 -8
  221. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/.clang-format +0 -0
  222. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/LICENSE +0 -0
  223. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/src/sharedbox/benchmarks/__main__.py +0 -0
  224. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/src/sharedbox/benchmarks/size.py +0 -0
  225. {sharedbox-0.3.0rc0 → sharedbox-0.4.0}/src/sharedbox/py.typed +0 -0
@@ -0,0 +1,33 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(git status:*)",
5
+ "Bash(git diff:*)",
6
+ "Bash(git log:*)",
7
+ "Bash(git show:*)",
8
+ "Bash(git rev-parse:*)",
9
+ "Bash(git branch:*)",
10
+ "Bash(git merge-base:*)",
11
+ "Bash(git ls-files:*)",
12
+ "Bash(git fetch:*)",
13
+ "Bash(gh run list:*)",
14
+ "Bash(gh run view:*)",
15
+ "Bash(gh run watch:*)",
16
+ "Bash(gh pr view:*)",
17
+ "Bash(gh pr checks:*)",
18
+ "Bash(ls:*)",
19
+ "Bash(cat:*)",
20
+ "Bash(grep:*)",
21
+ "Bash(sed -n:*)",
22
+ "Bash(head:*)",
23
+ "Bash(tail:*)",
24
+ "Bash(wc:*)"
25
+ ],
26
+ "deny": [
27
+ "Bash(git push:*)",
28
+ "Bash(git reset --hard:*)",
29
+ "Bash(rm -rf:*)",
30
+ "Bash(gh pr merge:*)"
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,31 @@
1
+ name: Check documentation
2
+
3
+ on:
4
+ workflow_call:
5
+
6
+ jobs:
7
+ check-docs:
8
+ name: Build and check cross-references
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v5
12
+
13
+ - name: Install uv
14
+ uses: astral-sh/setup-uv@v7
15
+ with:
16
+ enable-cache: true
17
+ cache-dependency-glob: "uv.lock"
18
+ python-version: "3.11"
19
+
20
+ - name: Build docs and check cross-references
21
+ run: uv run --locked --only-group tox tox -e docs
22
+
23
+ # the publish job deploys this artifact rather than building again,
24
+ # so what reaches Pages is what was checked
25
+ - name: Store the built site
26
+ uses: actions/upload-artifact@v4
27
+ with:
28
+ name: docs-site
29
+ path: site/
30
+ if-no-files-found: error
31
+ retention-days: 7
@@ -7,6 +7,7 @@ on:
7
7
  branches: [ main ]
8
8
  release:
9
9
  types: [ published ]
10
+ workflow_dispatch:
10
11
 
11
12
  jobs:
12
13
  lint:
@@ -22,6 +23,73 @@ jobs:
22
23
  with:
23
24
  prek-version: "0.5.3"
24
25
 
26
+ docs:
27
+ name: Check documentation
28
+ uses: ./.github/workflows/check-docs.yaml
29
+
30
+ publish_docs:
31
+ name: Publish documentation
32
+ needs: [lint, docs]
33
+ # main and final releases only: a pre-release publishes a package and
34
+ # leaves the site describing the last final release
35
+ if: >-
36
+ (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
37
+ (github.event_name == 'release' && github.event.action == 'published' &&
38
+ !github.event.release.prerelease &&
39
+ !contains(github.event.release.tag_name, 'rc'))
40
+ uses: ./.github/workflows/publish-docs.yaml
41
+ permissions:
42
+ contents: write
43
+ concurrency:
44
+ group: pages
45
+ cancel-in-progress: false
46
+
47
+ cpp_header:
48
+ name: C++ header on ${{ matrix.os }} (${{ matrix.build }})
49
+ runs-on: ${{ matrix.os }}
50
+ timeout-minutes: 10
51
+ strategy:
52
+ fail-fast: false
53
+ matrix:
54
+ include:
55
+ - { os: ubuntu-latest, build: Release, flags: "" }
56
+ - { os: windows-latest, build: Release, flags: "" }
57
+ # No ThreadSanitizer: the sequence lock's reader copies the record while a writer may change it, by design.
58
+ - os: ubuntu-latest
59
+ build: RelWithDebInfo
60
+ flags: -fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=all -DSHAREDBOX_TEST_ROUNDS=20000
61
+ env:
62
+ ASAN_OPTIONS: detect_leaks=1
63
+ UBSAN_OPTIONS: print_stacktrace=1
64
+ steps:
65
+ - uses: actions/checkout@v5
66
+ with:
67
+ fetch-depth: 0
68
+
69
+ - name: Build and run the C++ tests
70
+ shell: bash
71
+ run: |
72
+ cmake -S tests/cpp -B build-cpp -DCMAKE_BUILD_TYPE=${{ matrix.build }} \
73
+ -DCMAKE_C_FLAGS_INIT="${{ matrix.flags }}" -DCMAKE_CXX_FLAGS_INIT="${{ matrix.flags }}"
74
+ cmake --build build-cpp --config ${{ matrix.build }}
75
+ ctest --test-dir build-cpp -C ${{ matrix.build }} --output-on-failure
76
+
77
+ - uses: astral-sh/setup-uv@v7
78
+
79
+ - name: Build the extension and run the C consumer
80
+ if: matrix.build == 'Release'
81
+ shell: bash
82
+ env:
83
+ SHAREDBOX_REQUIRE_C_CONSUMER: "1"
84
+ run: |
85
+ uv sync --dev
86
+ uv run --no-sync pytest tests/test_capsule.py -q
87
+
88
+ - name: Create a box larger than a 16 MiB /dev/shm
89
+ if: runner.os == 'Linux' && matrix.build == 'Release'
90
+ run: |
91
+ docker run --rm --shm-size=16m -v "$PWD":/src -w /src ubuntu:24.04 build-cpp/test_small_shm
92
+
25
93
  build_wheels:
26
94
  name: Build wheel for ${{ matrix.python }}-${{ matrix.platform_id }}
27
95
  runs-on: ${{ matrix.os }}
@@ -50,7 +118,7 @@ jobs:
50
118
  env:
51
119
  CIBW_BUILD: ${{ matrix.python }}-${{ matrix.platform_id }}
52
120
  CIBW_CONTAINER_ENGINE: "docker; create_args: --shm-size=1g"
53
- CIBW_TEST_REQUIRES: pytest
121
+ CIBW_TEST_REQUIRES: pytest hypothesis numpy
54
122
  CIBW_TEST_COMMAND: pytest {project}/tests
55
123
  with:
56
124
  package-dir: .
@@ -118,6 +186,44 @@ jobs:
118
186
  # compiled extension and psygnal, which this runner does not have installed.
119
187
  run: python src/sharedbox/benchmarks/size_diff.py sizes/this sizes/main >> "$GITHUB_STEP_SUMMARY"
120
188
 
189
+ stress:
190
+ name: Stress tests on ${{ matrix.os }}
191
+ if: github.event_name == 'workflow_dispatch'
192
+ needs: [build_wheels]
193
+ runs-on: ${{ matrix.os }}
194
+ timeout-minutes: 30
195
+ strategy:
196
+ fail-fast: false
197
+ matrix:
198
+ include:
199
+ - { os: ubuntu-latest, wheel: cibw-wheels-cp312-manylinux_x86_64 }
200
+ - { os: windows-latest, wheel: cibw-wheels-cp312-win_amd64 }
201
+ steps:
202
+ - uses: actions/checkout@v5
203
+
204
+ - uses: astral-sh/setup-uv@v7
205
+
206
+ - uses: actions/download-artifact@v5
207
+ with:
208
+ name: ${{ matrix.wheel }}
209
+ path: wheelhouse
210
+
211
+ - name: Run the stress tests against the built wheel
212
+ shell: bash
213
+ run: |
214
+ uv venv --python 3.12 .stress
215
+ uv pip install --python .stress wheelhouse/*.whl pytest hypothesis
216
+ python=.stress/bin/python
217
+ [ -x "$python" ] || python=.stress/Scripts/python.exe
218
+ "$python" -m pytest -m stress -q -p no:cacheprovider
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
+
221
+ - uses: actions/upload-artifact@v4
222
+ if: always()
223
+ with:
224
+ name: stress-${{ matrix.os }}
225
+ path: build/stress/*.json
226
+
121
227
  make_sdist:
122
228
  name: Make SDist
123
229
  runs-on: ubuntu-latest
@@ -157,7 +263,7 @@ jobs:
157
263
 
158
264
  upload_pypi:
159
265
  name: Upload to PyPI
160
- needs: [lint, build_wheels, make_sdist]
266
+ needs: [lint, docs, cpp_header, build_wheels, make_sdist]
161
267
  runs-on: ubuntu-latest
162
268
  environment:
163
269
  name: pypi
@@ -20,13 +20,6 @@ jobs:
20
20
  with:
21
21
  fetch-depth: 0
22
22
 
23
- - name: Set up vcpkg at the manifest baseline
24
- run: |
25
- git clone https://github.com/microsoft/vcpkg.git "${{ runner.temp }}/vcpkg"
26
- git -C "${{ runner.temp }}/vcpkg" checkout "$(jq -r '."builtin-baseline"' vcpkg.json)"
27
- "${{ runner.temp }}/vcpkg/bootstrap-vcpkg.sh" -disableMetrics
28
- echo "VCPKG_ROOT=${{ runner.temp }}/vcpkg" >> "$GITHUB_ENV"
29
-
30
23
  - uses: astral-sh/setup-uv@v7
31
24
 
32
25
  - name: Install the project
@@ -0,0 +1,25 @@
1
+ name: Publish documentation
2
+
3
+ on:
4
+ workflow_call:
5
+
6
+ jobs:
7
+ publish-docs:
8
+ name: Publish on pages
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ contents: write
12
+ steps:
13
+ - uses: actions/checkout@v5
14
+
15
+ - name: Download the built site
16
+ uses: actions/download-artifact@v5
17
+ with:
18
+ name: docs-site
19
+ path: site/
20
+
21
+ - name: Deploy to GitHub Pages
22
+ uses: peaceiris/actions-gh-pages@v4
23
+ with:
24
+ github_token: ${{ secrets.GITHUB_TOKEN }}
25
+ publish_dir: site
@@ -17,3 +17,12 @@ wheels/
17
17
  src/sharedbox/_version.py
18
18
 
19
19
  wheelhouse/
20
+
21
+ graphify-out/
22
+
23
+ build-cpp/
24
+
25
+ .hypothesis/
26
+ .claude/settings.local.json
27
+
28
+ site/
@@ -0,0 +1,287 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ Dates are marked as `DD-MM-YYYY`
9
+
10
+ ## [Unreleased]
11
+
12
+ ## [0.4.0] - 05-10-2026
13
+
14
+ ### Added
15
+
16
+ - `benchbox plot`: draws the results of `benchbox all` as SVG charts, in a
17
+ light and a dark variant. The `benchmarks` extra now includes
18
+ matplotlib.
19
+ - `SharedBox`: fields of type `complex`, `datetime.date`, `datetime.time`,
20
+ `datetime.datetime`, `datetime.timedelta`, `uuid.UUID` and
21
+ `Annotated[decimal.Decimal, Capacity(n)]`.
22
+ - `SharedBox`: `enum.Enum`, `enum.Flag` and `typing.Literal` fields.
23
+ - `SharedBox`: `X | None` and union fields.
24
+ - `SharedBox`: record fields: dataclasses, `NamedTuple`, fixed-length
25
+ tuples, `TypedDict`, attrs classes and `msgspec.Struct`.
26
+ - `SharedBox`: `list`, `tuple[T, ...]`, `set`, `frozenset`, `dict` and
27
+ `bytearray` fields and their `collections.abc` forms, with a `Capacity`
28
+ in elements.
29
+ - `Shape`, `DType`, `SupportsDLPack`, `register_array_type`: array fields
30
+ through DLPack.
31
+ - `sharedbox.hpp`: `type_view`, `handle::field_type`,
32
+ `handle::types_table`, `handle::major_version`, `oldest_layout_major`,
33
+ `time_value`, `datetime_value`, `timedelta_value`, the kind codes
34
+ `kind_complex` to `kind_decimal` and `kind_enum` to `kind_array`,
35
+ `max_type_depth`, `max_types_size`, `max_mapping_size`,
36
+ `first_described_kind`, `max_date_ordinal`, `unix_epoch_ordinal`,
37
+ `micros_per_day`, `max_offset_minutes`, `literal_none` to
38
+ `literal_enum`, `type_head`, `dl_dtype`, `literal_value`,
39
+ `encode_*` and `decode_*` functions for complex, date, time, datetime,
40
+ timedelta, uuid, flag and position values, `decode_bool`,
41
+ `decode_present`, `decode_tag`, `decode_length`, `handle::read_used`,
42
+ `handle::read_large`, `handle::read_record_large` and
43
+ `handle::write_large`.
44
+ - `sharedbox_c.h`: `sbx_field_desc`, the structs `sbx_time`,
45
+ `sbx_datetime` and `sbx_timedelta`, and typed reads and writes of
46
+ complex, date, time, datetime, timedelta, UUID, enum and literal
47
+ positions, flag bits and optional presence.
48
+ - `benchbox ops`: rows for datetime, record, list and array fields.
49
+
50
+ ```python
51
+ class Frame(SharedBox):
52
+ taken: datetime.datetime
53
+ image: Annotated[np.ndarray, Shape(480, 640), DType("uint8")]
54
+ tags: Annotated[list[Annotated[str, Capacity(16)]], Capacity(8)] = field(default_factory=list)
55
+ ```
56
+
57
+ ### Changed
58
+
59
+ - Segments use layout 2.0. Releases 0.3.0 and 0.3.1 cannot open a box made
60
+ by this version; this version opens boxes made by them.
61
+ - `SharedBox.events` and `SharedBox.watch()`: a field counts as changed
62
+ when its stored bytes change. Writing `NaN` again no longer emits;
63
+ `-0.0` after `0.0` does.
64
+ - `Capacity`: also sets the most elements of a collection.
65
+ - `sharedbox.hpp`: the inline namespace is `v2`; `handle::create` and
66
+ `handle::create_unpublished` take a description table.
67
+ - The `benchmarks` extra includes numpy.
68
+ - `SharedBox.update` and `SharedBox.snapshot`: native methods, unless a
69
+ class defines or inherits its own.
70
+
71
+ ### Fixed
72
+
73
+ - `SharedBox`: a class with more than one box base keeps the defaults and
74
+ default factories of the fields it inherits from every base, not only
75
+ the first.
76
+
77
+ ## [0.3.1] - 01-10-2026
78
+
79
+ ### Added
80
+
81
+ - `sharedbox.__version__`: the version of the installed package, as a string.
82
+
83
+ ## [0.3.0] - 01-10-2026
84
+
85
+ ### Added
86
+
87
+ - `SharedBox`: base class whose annotated fields live in a shared-memory segment.
88
+ - `Capacity`: byte capacity for `str` and `bytes` fields.
89
+ - `FieldWatch`: `for` and `async for` over new values of a field.
90
+ - `SharedBox.events`: psygnal `SignalGroup` with one `(new, old)` signal per
91
+ field.
92
+ - `BoxClosedError`, `LockTimeoutError`, `SchemaMismatchError`,
93
+ `SegmentExistsError`, `SegmentNotFoundError`.
94
+ - `benchbox`: command with `ops`, `roundtrip`, `size` and `all`
95
+ benchmarks, also run as `python -m sharedbox.benchmarks`.
96
+ - `benchmarks` extra: installs Typer and pyperf for `benchbox`.
97
+ - `SharedBox`: `identity` class keyword; it enters the schema hash and
98
+ names the box.
99
+ - `SharedBox`: `max_waiters` class keyword, 1 to 4096 box handles that
100
+ watch at once, default 64.
101
+ - `SharedBox.__sharedbox_box__()`: returns a `"sharedbox_box"` PyCapsule
102
+ for other extensions.
103
+ - `SupportsSharedBox`: protocol for functions that accept a box.
104
+ - `get_include()`: folder holding `sharedbox/sharedbox.hpp`,
105
+ `sharedbox/sharedbox_c.h` and `sharedbox/sharedbox_c.cpp`.
106
+ - `sharedbox.hpp`: header-only C++20 implementation of the segment layout,
107
+ installed in the wheel with a CMake config (`sharedbox::headers`), which
108
+ links `bcrypt` on Windows and `rt` and `Threads::Threads` on Linux.
109
+ - `sharedbox-config-version.cmake`: installed next to
110
+ `sharedbox-config.cmake`; `find_package(sharedbox 0.3)` accepts 0.3
111
+ versions only.
112
+ - `sharedbox_c.h`: minimal C interface (`sbx_open`, `sbx_import`,
113
+ `sbx_schema_hash`, `sbx_read`, `sbx_write`, `sbx_release`), built
114
+ through `sharedbox::c`.
115
+
116
+ ```python
117
+ class Frame(SharedBox, identity="camera/frame/1", max_waiters=16):
118
+ exposure: float = 0.01
119
+ count: int = 0
120
+ ```
121
+
122
+ - `field()`: per-field `default`, `default_factory`, `init`, `repr`,
123
+ `kw_only`, `metadata` and `doc` for `SharedBox` fields.
124
+ - `fields()`: the `Field` of each field of a `SharedBox` subclass or box.
125
+ - `Field`: read-only description of one `SharedBox` field.
126
+ - `SharedBox`: `dataclasses.InitVar` annotations, passed to
127
+ `__post_init__` and not stored.
128
+ - `SharedBox.__post_init__()`: runs after a box is created and before
129
+ other processes can attach to it.
130
+ - `SharedBox`: `inspect.signature()` of a subclass gives its constructor
131
+ parameters.
132
+ - `sharedbox.hpp`: `handle::create_unpublished()` and
133
+ `handle::publish()`.
134
+ - `SharedBox`: reference fields, annotated with a `SharedBox` subclass `X`
135
+ or `X | None`; reading one attaches the box it refers to, with that
136
+ box's own class.
137
+ - `BoxRef`: frozen dataclass (`name`, `schema_hash`, `create_id`, and the
138
+ `box_class` property) that `snapshot()`, `events` and `watch()` report
139
+ for a reference field.
140
+ - `BrokenReferenceError`: raised when the box a reference field refers to
141
+ was removed or created again since it was assigned.
142
+ - `UnknownBoxClassError`: raised when the box a reference field refers to
143
+ has a class this process has not defined.
144
+ - `SharedBox.snapshot()`: `follow` keyword; `follow=True` replaces each
145
+ reference with the snapshot of the box it refers to.
146
+ - `sharedbox.hpp`: `kind_ref` and `box_ref`, the kind code and stored value
147
+ of a reference field.
148
+
149
+ ```python
150
+ class Motor(SharedBox):
151
+ position: int
152
+ limit: int = field(default=100, kw_only=True, metadata={"unit": "mm"})
153
+ offset: InitVar[int] = 0
154
+
155
+ def __post_init__(self, offset: int) -> None:
156
+ self.position += offset
157
+
158
+
159
+ class Stage(SharedBox):
160
+ motor: Motor | None = None
161
+ ```
162
+
163
+ - `BoxEvents`: psygnal `SignalGroup` subclass that `SharedBox.events`
164
+ returns.
165
+ - `BoxEvents.follow()`: given a reference field, returns a group whose
166
+ signals are emitted for changes inside the box the field refers to,
167
+ whichever box that is; given no field, emits on `nested` every change
168
+ inside the boxes the reference fields reach.
169
+ - `BoxEvents.unfollow()`: stops forwarding started with `follow()`.
170
+ - `BoxEvents.nested`: `(path, new, old)` signal of a class with reference
171
+ fields.
172
+
173
+ ```python
174
+ motor_events = stage.events.follow("motor")
175
+ motor_events.position.connect(lambda new, old: print(new))
176
+ stage.events.follow()
177
+ stage.events.nested.connect(lambda path, new, old: print(path, new))
178
+ ```
179
+
180
+ - Documentation site at <https://jacopoabramo.github.io/sharedbox>:
181
+ tutorial, how-to guides, explanations and the API reference.
182
+
183
+ ### Changed
184
+
185
+ - Building the extension requires nanobind 3.1.0 or newer and a C++20
186
+ compiler.
187
+ - Wheels per platform: `cp311-cp311`, `cp312-abi3` for CPython 3.12 and
188
+ newer, and `cp314-cp314t` for free-threaded CPython 3.14.
189
+ - `SharedBox` fields are converted in the native module and packed by
190
+ alignment; segments use layout 1.0, a plain named mapping.
191
+ - `SharedBox`: the default box name is 16 hex digits of SHA-256 over the
192
+ identity, with no `sharedbox-` prefix. A box created by an earlier
193
+ release under `sharedbox-<hash>` is not found under the new default name.
194
+ - Shared-memory objects are named `sharedbox.<name>`
195
+ (`/dev/shm/sharedbox.<name>`, `Local\sharedbox.<name>`); boxes of
196
+ earlier releases are not found under them.
197
+ - `SegmentExistsError`: says whether the box's creator is still running,
198
+ runs in another pid namespace, or has exited, or that the name holds no
199
+ published box.
200
+ - A pickled `SharedBox` carries its class's schema hash and its box's
201
+ create id; unpickling with a different class, or after the box was
202
+ created again, raises `SchemaMismatchError`.
203
+ - `SharedBox.attach()`: shared memory that does not become a box within
204
+ 1 s raises `SegmentNotFoundError`.
205
+ - `SharedBox.attach()`: a segment of another layout major version raises
206
+ `SchemaMismatchError` naming the version.
207
+ - `SharedBox` fields: reading or writing one field runs no Python code;
208
+ deleting one raises `AttributeError`.
209
+ - `SharedBox`: a subclass whose `__slots__` names `_segment` raises
210
+ `TypeError`.
211
+ - `SharedBox`: an error about a field names the field as `Class.field`.
212
+ - `SharedBox`: a read or write that waits for another writer's lock lets
213
+ other threads run.
214
+ - `SharedBox.update()`: takes about half the time; field names are checked
215
+ only when one is unknown.
216
+ - `SharedBox.snapshot()`: builds its dict in the native module.
217
+ - `SharedBox`: the `kw_only` class keyword and a `KW_ONLY` annotation
218
+ apply only to the fields of the class that declares them; a subclass
219
+ keeps each inherited field's keyword-only setting.
220
+ - `SharedBox`: a subclass that sets an unannotated class attribute on the
221
+ name of an inherited field raises `TypeError`.
222
+ - `SegmentExistsError`: for a box whose creator is still running
223
+ `__post_init__`, says the box is being created by that pid.
224
+ - `SharedBox.__sharedbox_box__()`: raises `BufferError` before the box
225
+ is published.
226
+ - `sharedbox.hpp`: `handle::open` and `handle::from_capsule` accept a field
227
+ whose kind code they do not know and treat its bytes as opaque.
228
+ - `sharedbox.hpp`: `handle::write` returns `status::range` for a field whose
229
+ kind code it does not know, and `sbx_write` returns `SBX_E_RANGE`.
230
+ - `SharedBox.attach()`: a segment with a field of a kind this version
231
+ cannot read raises `SchemaMismatchError` naming the kind.
232
+ - `SharedBox`: an annotation naming an undefined class raises `TypeError`.
233
+ - `SharedBox`: a field named `follow`, `unfollow` or `nested` raises
234
+ `TypeError`.
235
+ - `SharedBox.close()`: called from an event callback, on a watcher thread,
236
+ does not wait for the watcher threads of the boxes it closes, and drops
237
+ the writes they had not delivered.
238
+
239
+ ### Fixed
240
+
241
+ - `SharedBox.watch()`: no longer yields the same value twice.
242
+ - `SharedBox.attach()`: no longer fails when it runs while another process
243
+ is still creating the box.
244
+ - `SharedBox`: a process that attaches while the box is being created sees
245
+ the initial values, never a zeroed record.
246
+ - `SharedBox.close()`: no longer deadlocks while another thread's read or
247
+ write waits for the write lock.
248
+ - `SharedBox.close()`: returns without waiting for the watcher thread's
249
+ 0.1 s poll.
250
+ - `SharedBox.watch()` and `SharedBox.events`: on Windows, a write no
251
+ longer reaches one of several waiting threads up to 50 ms late.
252
+ - `SharedBox.watch()` and `SharedBox.events`: keep delivering when every
253
+ waiter slot is taken.
254
+ - `SharedBox`: on Linux, a box larger than the free space of `/dev/shm`
255
+ raises `OSError` at creation instead of the process receiving `SIGBUS`.
256
+
257
+ ### Removed
258
+
259
+ - Python 3.10 support.
260
+ - `SharedDict` and `sharedbox.utils`.
261
+ - Boost and vcpkg: the build no longer needs `VCPKG_ROOT`.
262
+ - `docs/api.md`, `docs/library-authors.md` and `docs/design/`: their
263
+ content is on the documentation site.
264
+
265
+ ## [0.2.4] - 05-10-2025
266
+
267
+ ### Changed
268
+
269
+ - Rewrite codebase in nanobind
270
+
271
+ ### Fixed
272
+
273
+ - Parallelize CI so that each wheel is built with the correct version
274
+ - Also faster builds
275
+
276
+ ## [0.1.0] - 29-09-2025
277
+
278
+ ### Added
279
+
280
+ - Initial release
281
+
282
+ [Unreleased]: https://github.com/jacopoabramo/sharedbox/compare/v0.4.0...HEAD
283
+ [0.4.0]: https://github.com/jacopoabramo/sharedbox/compare/v0.3.1...v0.4.0
284
+ [0.3.1]: https://github.com/jacopoabramo/sharedbox/compare/v0.3.0...v0.3.1
285
+ [0.3.0]: https://github.com/jacopoabramo/sharedbox/compare/0.2.4...v0.3.0
286
+ [0.2.4]: https://github.com/jacopoabramo/sharedbox/compare/0.1.0...0.2.4
287
+ [0.1.0]: https://github.com/jacopoabramo/sharedbox/commits/0.1.0