spatialdds 0.5.1__tar.gz → 0.5.2__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 (148) hide show
  1. {spatialdds-0.5.1 → spatialdds-0.5.2}/PKG-INFO +11 -2
  2. {spatialdds-0.5.1 → spatialdds-0.5.2}/README.md +10 -1
  3. {spatialdds-0.5.1 → spatialdds-0.5.2}/pyproject.toml +1 -1
  4. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/__init__.py +1 -1
  5. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/server/gw_http.py +49 -4
  6. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds.egg-info/PKG-INFO +11 -2
  7. {spatialdds-0.5.1 → spatialdds-0.5.2}/setup.cfg +0 -0
  8. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/idl/core.idl +0 -0
  9. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/idl/discovery.idl +0 -0
  10. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/idl/types.idl +0 -0
  11. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/builtin_Time.json +0 -0
  12. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_anchors_AnchorDelta.json +0 -0
  13. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_anchors_AnchorEntry.json +0 -0
  14. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSet.json +0 -0
  15. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSetRequest.json +0 -0
  16. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSetResponse.json +0 -0
  17. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_argeo_NodeGeo.json +0 -0
  18. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_argeo_QualityRequirements.json +0 -0
  19. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_argeo_VpsRequest.json +0 -0
  20. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_argeo_VpsResponse.json +0 -0
  21. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_common_AssetRef.json +0 -0
  22. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_common_FrameRef.json +0 -0
  23. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_common_KV.json +0 -0
  24. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_common_MetaKV.json +0 -0
  25. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_common_Vec2.json +0 -0
  26. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_Aabb3.json +0 -0
  27. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_BlobChunk.json +0 -0
  28. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_BlobRef.json +0 -0
  29. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_ComponentRef.json +0 -0
  30. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_Edge.json +0 -0
  31. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_EntityBinding.json +0 -0
  32. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_FrameTransform.json +0 -0
  33. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_FramedPose.json +0 -0
  34. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_GeoAnchor.json +0 -0
  35. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_GeoPose.json +0 -0
  36. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_NavSatStatus.json +0 -0
  37. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_Node.json +0 -0
  38. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_PlannedTrajectory.json +0 -0
  39. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_PlannedWaypoint.json +0 -0
  40. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_PoseSE3.json +0 -0
  41. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_SnapshotRequest.json +0 -0
  42. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_SnapshotResponse.json +0 -0
  43. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_TileKey.json +0 -0
  44. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_TileMeta.json +0 -0
  45. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_core_TilePatch.json +0 -0
  46. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_Announce.json +0 -0
  47. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_Capabilities.json +0 -0
  48. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_ContentAnnounce.json +0 -0
  49. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_CoverageElement.json +0 -0
  50. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_CoverageFilter.json +0 -0
  51. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_CoverageHint.json +0 -0
  52. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_CoverageQuery.json +0 -0
  53. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_CoverageResponse.json +0 -0
  54. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_Depart.json +0 -0
  55. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_KV.json +0 -0
  56. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_ProfileSupport.json +0 -0
  57. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_ServiceSummary.json +0 -0
  58. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_TopicMeta.json +0 -0
  59. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_Transform.json +0 -0
  60. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_disco_ValidityWindow.json +0 -0
  61. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_events_CrossingLine.json +0 -0
  62. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_events_SpatialEvent.json +0 -0
  63. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_events_SpatialZone.json +0 -0
  64. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_events_ZoneState.json +0 -0
  65. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_mapping_Edge.json +0 -0
  66. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_mapping_MapAlignment.json +0 -0
  67. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_mapping_MapEvent.json +0 -0
  68. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_mapping_MapMeta.json +0 -0
  69. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_mapping_MapQuality.json +0 -0
  70. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_owm_Entity.json +0 -0
  71. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_owm_ModelCommand.json +0 -0
  72. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_owm_ModelPose.json +0 -0
  73. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_semantics_Detection2D.json +0 -0
  74. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_semantics_Detection2DSet.json +0 -0
  75. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_semantics_Detection3D.json +0 -0
  76. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_semantics_Detection3DSet.json +0 -0
  77. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_semantics_FusedTrack.json +0 -0
  78. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_semantics_FusedTrackSet.json +0 -0
  79. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_semantics_Keypoint3D.json +0 -0
  80. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_semantics_KeypointLink.json +0 -0
  81. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_common_Axis.json +0 -0
  82. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_common_FrameHeader.json +0 -0
  83. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_common_FrameQuality.json +0 -0
  84. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_common_Linspace.json +0 -0
  85. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_common_ROI.json +0 -0
  86. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_common_ROIReply.json +0 -0
  87. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_common_ROIRequest.json +0 -0
  88. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_common_StreamMeta.json +0 -0
  89. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarDetection.json +0 -0
  90. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarDetectionSet.json +0 -0
  91. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarFrame.json +0 -0
  92. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarMeta.json +0 -0
  93. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadDetection.json +0 -0
  94. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadDetectionSet.json +0 -0
  95. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadSensorMeta.json +0 -0
  96. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadTensorFrame.json +0 -0
  97. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadTensorMeta.json +0 -0
  98. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioObservation.json +0 -0
  99. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioScan.json +0 -0
  100. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioSensorMeta.json +0 -0
  101. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamArraySet.json +0 -0
  102. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamFrame.json +0 -0
  103. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamMeta.json +0 -0
  104. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_vision_CamIntrinsics.json +0 -0
  105. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_vision_Keypoint2D.json +0 -0
  106. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_vision_Track2D.json +0 -0
  107. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionDetections.json +0 -0
  108. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionFrame.json +0 -0
  109. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionMeta.json +0 -0
  110. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_slam_frontend_CameraInfo.json +0 -0
  111. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_slam_frontend_Feature2D.json +0 -0
  112. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_slam_frontend_FeatureMatch.json +0 -0
  113. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_slam_frontend_KeyframeFeatures.json +0 -0
  114. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_slam_frontend_Landmark.json +0 -0
  115. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_slam_frontend_MatchSet.json +0 -0
  116. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_slam_frontend_TrackObs.json +0 -0
  117. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_slam_frontend_Tracklet.json +0 -0
  118. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_vio_FusedState.json +0 -0
  119. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_vio_ImuInfo.json +0 -0
  120. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_vio_ImuSample.json +0 -0
  121. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_vio_MagnetometerSample.json +0 -0
  122. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/schemas/spatial_vio_SensorFusionSample.json +0 -0
  123. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/artifacts/serializer_tables.json +0 -0
  124. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/canonical.py +0 -0
  125. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/canonjson.py +0 -0
  126. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/client.py +0 -0
  127. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/demo/assets/duck.gltf +0 -0
  128. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/demo/assets/fountain-label.txt +0 -0
  129. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/demo/assets/fountain-marker.gltf +0 -0
  130. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/demo/fountain-anchors.mcap +0 -0
  131. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/demo/manifests/content-fountain-label.json +0 -0
  132. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/demo/manifests/content-fountain-marker.json +0 -0
  133. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/errors.py +0 -0
  134. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/records.py +0 -0
  135. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/serve.py +0 -0
  136. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/server/__init__.py +0 -0
  137. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/server/commands.py +0 -0
  138. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/server/gateway.py +0 -0
  139. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/server/idlmodel.py +0 -0
  140. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/server/pin.py +0 -0
  141. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/server/publish.py +0 -0
  142. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/server/webjson.py +0 -0
  143. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds/server/xcdr2.py +0 -0
  144. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds.egg-info/SOURCES.txt +0 -0
  145. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds.egg-info/dependency_links.txt +0 -0
  146. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds.egg-info/entry_points.txt +0 -0
  147. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds.egg-info/requires.txt +0 -0
  148. {spatialdds-0.5.1 → spatialdds-0.5.2}/spatialdds.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spatialdds
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: SpatialDDS over plain HTTPS and a WebSocket: a client for the web binding (Appendix N), and the reference registry behind one command.
5
5
  Author: Open AR Cloud
6
6
  License: Apache-2.0
@@ -419,7 +419,16 @@ an object the app had already discarded. If you called `register()` before
419
419
  `make_app()`, 0.4.0 and 0.5.0 worked; if you called it after, the command lane
420
420
  advertised nothing and refused every POST.
421
421
 
422
- ## Not in 0.5.1
422
+ 0.5.2 makes `notify` safe from the thread its own docstring invited. It put
423
+ onto the subscriber queue directly, and `asyncio.Queue.put_nowait` from a
424
+ foreign thread enqueues without waking the loop — so a backend reading a bus
425
+ in a thread fed a parked subscriber nothing until the loop woke for some other
426
+ reason. Busy endpoint, invisible; idle endpoint, silent. Measured: a bridge
427
+ pumping 202 samples into two registered subscriber queues delivered 0 live
428
+ events. If you call `notify` only from the serving loop, 0.5.0 and 0.5.1 were
429
+ correct for you.
430
+
431
+ ## Not in 0.5.2
423
432
 
424
433
  Stated so nobody waits for them: no async API, no caching beyond ETag
425
434
  revalidation, no retry policy, no MCAP tooling beyond the journal the server
@@ -395,7 +395,16 @@ an object the app had already discarded. If you called `register()` before
395
395
  `make_app()`, 0.4.0 and 0.5.0 worked; if you called it after, the command lane
396
396
  advertised nothing and refused every POST.
397
397
 
398
- ## Not in 0.5.1
398
+ 0.5.2 makes `notify` safe from the thread its own docstring invited. It put
399
+ onto the subscriber queue directly, and `asyncio.Queue.put_nowait` from a
400
+ foreign thread enqueues without waking the loop — so a backend reading a bus
401
+ in a thread fed a parked subscriber nothing until the loop woke for some other
402
+ reason. Busy endpoint, invisible; idle endpoint, silent. Measured: a bridge
403
+ pumping 202 samples into two registered subscriber queues delivered 0 live
404
+ events. If you call `notify` only from the serving loop, 0.5.0 and 0.5.1 were
405
+ correct for you.
406
+
407
+ ## Not in 0.5.2
399
408
 
400
409
  Stated so nobody waits for them: no async API, no caching beyond ETag
401
410
  revalidation, no retry policy, no MCAP tooling beyond the journal the server
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "spatialdds"
7
- version = "0.5.1"
7
+ version = "0.5.2"
8
8
  description = "SpatialDDS over plain HTTPS and a WebSocket: a client for the web binding (Appendix N), and the reference registry behind one command."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -37,7 +37,7 @@ __all__ = [
37
37
  "content_manifest", "geo_anchor", "service_uri", "content_uri",
38
38
  ]
39
39
 
40
- __version__ = "0.5.1"
40
+ __version__ = "0.5.2"
41
41
  #: The SpatialDDS-spec commit whose Appendix N and generated artifacts this
42
42
  #: package implements. Package versions and specification versions move
43
43
  #: independently; this is the one that says what the wire looks like.
@@ -476,6 +476,24 @@ def _written(request: web.Request, doc: Any, created: bool,
476
476
  **_cache_headers(gateway.OPERATOR), **extra})
477
477
 
478
478
 
479
+ def _running_loop():
480
+ """The loop running in *this* thread, or None. Never raises."""
481
+ try:
482
+ return asyncio.get_running_loop()
483
+ except RuntimeError:
484
+ return None
485
+
486
+
487
+ async def _remember_loop(app: web.Application) -> None:
488
+ """Record the serving loop, so `notify` can reach it from a thread.
489
+
490
+ An `on_startup` signal rather than `make_app`, because `make_app` is
491
+ ordinarily called before there is a loop to record -- an embedder builds
492
+ the app, then hands it to `run_app`.
493
+ """
494
+ app["loop"] = asyncio.get_running_loop()
495
+
496
+
479
497
  def notify(app: web.Application, topic: str, event: dict) -> None:
480
498
  """Hand an event to every subscriber currently on that topic.
481
499
 
@@ -500,16 +518,38 @@ def notify(app: web.Application, topic: str, event: dict) -> None:
500
518
  "topic": topic,
501
519
  "key": key, "data": data})
502
520
 
503
- Synchronous on purpose: it only puts onto unbounded queues, so there is
504
- nothing to await, and a backend reading a DDS bus from a thread can call
505
- it without arranging a loop.
521
+ Synchronous on purpose: there is nothing to await, so a backend reading a
522
+ DDS bus from a thread can call it without arranging a loop. **That use is
523
+ now actually safe**, which it was not in 0.5.0 and 0.5.1: this put onto
524
+ the queue directly, and `asyncio.Queue.put_nowait` from a foreign thread
525
+ enqueues without waking the selector, so a parked subscriber resumed only
526
+ when the loop happened to wake for some other reason. On a busy endpoint
527
+ that is immediate and the bug invisible; on an idle one the events sat
528
+ unseen. The docstring promised the thread case before the code delivered
529
+ it, which is the worse half of the defect.
506
530
 
507
531
  Subscriptions only stay open to be woken when `live_subscriptions` is on
508
532
  --- see `make_app`. Calling this against an endpoint serving a finished
509
533
  recording is harmless and reaches nobody.
510
534
  """
535
+ loop = app.get("loop")
511
536
  for q in list(app["subs"].get(topic, ())):
512
- q.put_nowait(event)
537
+ if loop is None or loop is _running_loop():
538
+ q.put_nowait(event)
539
+ else:
540
+ # `asyncio.Queue.put_nowait` is not thread-safe, and the part
541
+ # that bites is not the enqueue -- it is the wakeup. It resolves
542
+ # the parked getter's future directly, and resolving a future
543
+ # from a foreign thread schedules the callback without waking
544
+ # the selector, so the subscriber's `await q.get()` resumes only
545
+ # when the loop next happens to wake for some other reason. On a
546
+ # busy endpoint that is immediate and the bug is invisible; on an
547
+ # idle one the events sit in the queue.
548
+ #
549
+ # Measured on 2026-10-17: a bridge pumping 202 samples into two
550
+ # registered subscriber queues delivered 0 live events, while the
551
+ # same call from the loop thread delivered every one.
552
+ loop.call_soon_threadsafe(q.put_nowait, event)
513
553
 
514
554
 
515
555
  async def _broadcast(request: web.Request, topic: str, event: dict) -> None:
@@ -888,6 +928,11 @@ def make_app(state: gateway.State, base: str, token: str | None,
888
928
  else commands.Registry())
889
929
  app["journal"] = journal
890
930
  app["subs"] = {}
931
+ #: The serving loop, filled in at startup. `notify` needs it to hand an
932
+ #: event across a thread boundary safely; None until then, which is
933
+ #: correct -- nothing can be subscribed before the app is running.
934
+ app["loop"] = None
935
+ app.on_startup.append(_remember_loop)
891
936
  app["assets"] = assets
892
937
  app.add_routes([
893
938
  web.get("/", root),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spatialdds
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: SpatialDDS over plain HTTPS and a WebSocket: a client for the web binding (Appendix N), and the reference registry behind one command.
5
5
  Author: Open AR Cloud
6
6
  License: Apache-2.0
@@ -419,7 +419,16 @@ an object the app had already discarded. If you called `register()` before
419
419
  `make_app()`, 0.4.0 and 0.5.0 worked; if you called it after, the command lane
420
420
  advertised nothing and refused every POST.
421
421
 
422
- ## Not in 0.5.1
422
+ 0.5.2 makes `notify` safe from the thread its own docstring invited. It put
423
+ onto the subscriber queue directly, and `asyncio.Queue.put_nowait` from a
424
+ foreign thread enqueues without waking the loop — so a backend reading a bus
425
+ in a thread fed a parked subscriber nothing until the loop woke for some other
426
+ reason. Busy endpoint, invisible; idle endpoint, silent. Measured: a bridge
427
+ pumping 202 samples into two registered subscriber queues delivered 0 live
428
+ events. If you call `notify` only from the serving loop, 0.5.0 and 0.5.1 were
429
+ correct for you.
430
+
431
+ ## Not in 0.5.2
423
432
 
424
433
  Stated so nobody waits for them: no async API, no caching beyond ETag
425
434
  revalidation, no retry policy, no MCAP tooling beyond the journal the server
File without changes