x4d-devkit 0.52.0rc2__tar.gz → 0.53.0rc2__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 (173) hide show
  1. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/PKG-INFO +1 -1
  2. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/pyproject.toml +1 -1
  3. x4d_devkit-0.53.0rc2/tests/test_image_schema_migration.py +283 -0
  4. x4d_devkit-0.53.0rc2/tests/test_image_schema_registry_client.py +161 -0
  5. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_sdk_api.py +1 -1
  6. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/__init__.py +7 -1
  7. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/client.py +16 -0
  8. x4d_devkit-0.53.0rc2/x4d_devkit/client/image_schemas.py +317 -0
  9. x4d_devkit-0.53.0rc2/x4d_devkit/image_schema_migration.py +550 -0
  10. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/PKG-INFO +1 -1
  11. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/SOURCES.txt +3 -0
  12. x4d_devkit-0.52.0rc2/x4d_devkit/client/image_schemas.py +0 -115
  13. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/LICENSE +0 -0
  14. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/README.md +0 -0
  15. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/setup.cfg +0 -0
  16. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_accounts.py +0 -0
  17. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_annotation_contract.py +0 -0
  18. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_annotation_document_hash.py +0 -0
  19. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_annotation_reference.py +0 -0
  20. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_assets_client.py +0 -0
  21. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_boundary.py +0 -0
  22. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_box.py +0 -0
  23. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_build_info.py +0 -0
  24. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_category_audit.py +0 -0
  25. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_clean_basemap.py +0 -0
  26. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_clean_basemap_client.py +0 -0
  27. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_cli_auth.py +0 -0
  28. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_client_json_contract.py +0 -0
  29. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_client_projects.py +0 -0
  30. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_clip_archives.py +0 -0
  31. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_contract_validation.py +0 -0
  32. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_coordinate_frames.py +0 -0
  33. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_current_state.py +0 -0
  34. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_current_state_identity_consumers_example.py +0 -0
  35. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_dataset_contract.py +0 -0
  36. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_dataset_identity.py +0 -0
  37. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_detection_eval.py +0 -0
  38. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_evaluate.py +0 -0
  39. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_export_bundle.py +0 -0
  40. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_image_clip.py +0 -0
  41. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_image_conditional_rules.py +0 -0
  42. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_image_enum_set.py +0 -0
  43. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_image_export.py +0 -0
  44. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_image_geometry.py +0 -0
  45. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_image_groups.py +0 -0
  46. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_image_preannotation.py +0 -0
  47. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_image_reference.py +0 -0
  48. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_image_review.py +0 -0
  49. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_image_schema_upgrade.py +0 -0
  50. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_image_work_session.py +0 -0
  51. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_inference_input.py +0 -0
  52. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_inference_schemas.py +0 -0
  53. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_inference_service.py +0 -0
  54. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_label_schemas.py +0 -0
  55. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_lidar_materialization.py +0 -0
  56. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_loader.py +0 -0
  57. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_matching.py +0 -0
  58. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_metrics.py +0 -0
  59. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_models.py +0 -0
  60. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_parse_equivalence.py +0 -0
  61. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_platform_pose.py +0 -0
  62. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_preannotation_client.py +0 -0
  63. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_project_discovery.py +0 -0
  64. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_reconstruction_client.py +0 -0
  65. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_release_gate.py +0 -0
  66. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_roadmark_preview.py +0 -0
  67. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_semantic_scene_authoring.py +0 -0
  68. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_semantic_scene_client.py +0 -0
  69. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_semantic_scene_contract.py +0 -0
  70. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_semantic_scene_independent.py +0 -0
  71. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_semantic_scene_native_clip.py +0 -0
  72. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_source_attributes.py +0 -0
  73. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_time_contract.py +0 -0
  74. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_token.py +0 -0
  75. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_tracking_eval.py +0 -0
  76. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_training_identity.py +0 -0
  77. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_transform.py +0 -0
  78. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_transform_chain.py +0 -0
  79. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_validation.py +0 -0
  80. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_vector_map.py +0 -0
  81. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_vector_map_geometry.py +0 -0
  82. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_vector_map_session.py +0 -0
  83. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_work_session.py +0 -0
  84. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/tests/test_work_session_validation_reuse.py +0 -0
  85. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/__init__.py +0 -0
  86. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/_image_contract.py +0 -0
  87. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/accounts.py +0 -0
  88. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/annotation_contract.py +0 -0
  89. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/annotation_reference.py +0 -0
  90. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/audit.py +0 -0
  91. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/build_info.py +0 -0
  92. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/clean_basemap.py +0 -0
  93. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/cli.py +0 -0
  94. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/_origin.py +0 -0
  95. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/accounts.py +0 -0
  96. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/annotation_reference.py +0 -0
  97. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/assets.py +0 -0
  98. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/bags.py +0 -0
  99. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/capabilities.py +0 -0
  100. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/checkpoints.py +0 -0
  101. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/clean_basemap.py +0 -0
  102. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/clip_work_sessions.py +0 -0
  103. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/clips.py +0 -0
  104. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/dataset_identity.py +0 -0
  105. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/image_clips.py +0 -0
  106. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/image_exports.py +0 -0
  107. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/image_preannotations.py +0 -0
  108. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/image_review.py +0 -0
  109. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/image_work_sessions.py +0 -0
  110. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/label_schemas.py +0 -0
  111. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/preannotation.py +0 -0
  112. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/projects.py +0 -0
  113. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/reconstruction.py +0 -0
  114. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/semantic_scene.py +0 -0
  115. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/semantic_scene_authoring.py +0 -0
  116. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/services.py +0 -0
  117. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/test_sets.py +0 -0
  118. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/consumer_identity.py +0 -0
  119. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/coordinate_contract.py +0 -0
  120. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/core/__init__.py +0 -0
  121. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/core/loader.py +0 -0
  122. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/core/models.py +0 -0
  123. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/core/token.py +0 -0
  124. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/core/transform.py +0 -0
  125. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/current_state.py +0 -0
  126. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/dataset_contract.py +0 -0
  127. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/dataset_identity.py +0 -0
  128. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/__init__.py +0 -0
  129. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/api.py +0 -0
  130. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/box.py +0 -0
  131. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/detection.py +0 -0
  132. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/matching.py +0 -0
  133. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/metrics.py +0 -0
  134. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/tracking.py +0 -0
  135. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/export_bundle.py +0 -0
  136. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_clip.py +0 -0
  137. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_export.py +0 -0
  138. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_geometry.py +0 -0
  139. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_groups.py +0 -0
  140. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_preannotation.py +0 -0
  141. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_reference.py +0 -0
  142. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_review.py +0 -0
  143. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_schema.py +0 -0
  144. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_schema_upgrade.py +0 -0
  145. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_work_session.py +0 -0
  146. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/__init__.py +0 -0
  147. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/input.py +0 -0
  148. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/models.py +0 -0
  149. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/schemas.py +0 -0
  150. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/service.py +0 -0
  151. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/tasks.py +0 -0
  152. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/json_contract.py +0 -0
  153. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/label_schema.py +0 -0
  154. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/lidar_materialization.py +0 -0
  155. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/materialize.py +0 -0
  156. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/project_discovery.py +0 -0
  157. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/roadmark_preview.py +0 -0
  158. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/semantic_scene_authoring.py +0 -0
  159. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/semantic_scene_contract.py +0 -0
  160. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/time_contract.py +0 -0
  161. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/training_identity.py +0 -0
  162. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/validation/__init__.py +0 -0
  163. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/validation/contract.py +0 -0
  164. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/validation/platform_pose.py +0 -0
  165. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/validation/report.py +0 -0
  166. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/validation/validator.py +0 -0
  167. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/vector_map.py +0 -0
  168. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/vector_map_geometry.py +0 -0
  169. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit/work_session.py +0 -0
  170. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/dependency_links.txt +0 -0
  171. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/entry_points.txt +0 -0
  172. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/requires.txt +0 -0
  173. {x4d_devkit-0.52.0rc2 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: x4d-devkit
3
- Version: 0.52.0rc2
3
+ Version: 0.53.0rc2
4
4
  Summary: Official X-4D external SDK and CLI for datasets, platform APIs, training bridges, and automation
5
5
  Author: windzu
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "x4d-devkit"
7
- version = "0.52.0rc2"
7
+ version = "0.53.0rc2"
8
8
  description = "Official X-4D external SDK and CLI for datasets, platform APIs, training bridges, and automation"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -0,0 +1,283 @@
1
+ from copy import deepcopy
2
+ import json
3
+
4
+ import httpx
5
+ import pytest
6
+
7
+ from x4d_devkit import X4DClient
8
+ from x4d_devkit._image_contract import fingerprint
9
+ from x4d_devkit.image_clip import build_image_identity
10
+ from x4d_devkit.image_schema import (
11
+ build_image_schema_snapshot,
12
+ parse_image_schema_body,
13
+ parse_image_schema_record,
14
+ )
15
+ from x4d_devkit.image_schema_migration import (
16
+ MAPPING_CONTRACT,
17
+ ImageSchemaMigrationConflict,
18
+ build_image_schema_migration_preview,
19
+ build_image_schema_migration_receipt,
20
+ build_image_schema_migration_request,
21
+ image_schema_migration_candidate,
22
+ migrate_image_annotation,
23
+ parse_image_schema_migration_mapping,
24
+ parse_image_schema_migration_preview,
25
+ parse_image_schema_migration_request,
26
+ )
27
+ from x4d_devkit.image_schema_upgrade import additive_image_schema_changes
28
+ from tests.image_fixtures import clip_payload, line_object
29
+
30
+
31
+ def snapshot(body):
32
+ return build_image_schema_snapshot(
33
+ parse_image_schema_record(
34
+ {
35
+ "contract_version": "x4d-image-label-schema-record-v1",
36
+ "status": "active",
37
+ "body": body,
38
+ "fingerprint": parse_image_schema_body(body).identity.fingerprint,
39
+ }
40
+ )
41
+ )
42
+
43
+
44
+ def target(clip, **changes):
45
+ """Non-additive 2.0.0: whole-line `visible` becomes segment `occluded`,
46
+ `style` becomes object-only `pattern`, and lines become groupable."""
47
+ body = deepcopy(clip["label_schema"]["body"])
48
+ body["version"] = changes.get("version", "2.0.0")
49
+ body["schema_id"] = changes.get("schema_id", body["schema_id"])
50
+ line = body["categories"][0]
51
+ line["groupable"] = True
52
+ line["attributes"] = [
53
+ {
54
+ "id": "occluded",
55
+ "display_name": "Occluded",
56
+ "type": "boolean",
57
+ "required": False,
58
+ "scopes": ["segment"],
59
+ "values": [],
60
+ },
61
+ {
62
+ "id": "pattern",
63
+ "display_name": "Pattern",
64
+ "type": "enum",
65
+ "required": False,
66
+ "scopes": ["object"],
67
+ "values": [
68
+ {"id": "a", "display_name": "A"},
69
+ {"id": "b", "display_name": "B"},
70
+ ],
71
+ },
72
+ ]
73
+ return snapshot(body)
74
+
75
+
76
+ def mapping(*rules):
77
+ return {"contract_version": MAPPING_CONTRACT, "rules": list(rules)}
78
+
79
+
80
+ def rule(scope, attribute_id, value, action):
81
+ return {
82
+ "match": {
83
+ "category_id": "line",
84
+ "scope": scope,
85
+ "attribute_id": attribute_id,
86
+ "value": value,
87
+ },
88
+ "action": action,
89
+ }
90
+
91
+
92
+ def with_line(obj=None):
93
+ clip = clip_payload()
94
+ clip["image_annotation"]["objects"] = [obj or line_object()]
95
+ clip["identity"] = build_image_identity(clip)
96
+ return clip
97
+
98
+
99
+ VISIBLE_TO_RANGE = rule(
100
+ "object", "visible", False, {"kind": "range", "attribute_id": "occluded", "value": True}
101
+ )
102
+ DISCARD_STYLE = rule("segment", "style", "a", {"kind": "discard"})
103
+
104
+
105
+ def test_target_is_not_an_additive_upgrade():
106
+ clip = with_line()
107
+ with pytest.raises(ValueError):
108
+ additive_image_schema_changes(clip["label_schema"]["body"], target(clip)["body"])
109
+
110
+
111
+ def test_unused_vocabulary_needs_no_rule_and_keeps_the_annotation_bytes():
112
+ clip = with_line({**line_object(), "attributes": {}, "attribute_ranges": []})
113
+ candidate, report = image_schema_migration_candidate(clip, target(clip), mapping())
114
+ result = candidate.to_dict()
115
+ assert result["image_annotation"] == clip["image_annotation"]
116
+ assert result["identity"]["annotation_revision"] == clip["identity"]["annotation_revision"]
117
+ assert result["identity"]["source_revision"] == clip["identity"]["source_revision"]
118
+ assert result["identity"]["schema_fingerprint"] != clip["identity"]["schema_fingerprint"]
119
+ assert report == {"changes": [], "removed_ranges": [], "rule_usage": [], "carried_values": 0}
120
+
121
+
122
+ def test_every_unrepresentable_occurrence_is_listed_with_its_location():
123
+ clip = with_line()
124
+ with pytest.raises(ImageSchemaMigrationConflict) as caught:
125
+ image_schema_migration_candidate(clip, target(clip), mapping())
126
+ assert [
127
+ (row["code"], row["object_id"], row["range_id"], row["attribute_id"], row["value"])
128
+ for row in caught.value.conflicts
129
+ ] == [
130
+ ("value_unmapped", "line_a", None, "visible", False),
131
+ ("value_unmapped", "line_a", "r0", "style", "a"),
132
+ ]
133
+ assert caught.value.details["conflicts"] == caught.value.conflicts
134
+
135
+
136
+ def test_explicit_rules_convert_and_discard_while_preserving_identity_and_geometry():
137
+ clip = with_line()
138
+ rules = mapping(VISIBLE_TO_RANGE, DISCARD_STYLE)
139
+ candidate, report = image_schema_migration_candidate(clip, target(clip), rules)
140
+ result = candidate.to_dict()
141
+ before, after = clip["image_annotation"]["objects"][0], result["image_annotation"]["objects"][0]
142
+ assert {k: v for k, v in after.items() if k not in ("attributes", "attribute_ranges")} == {
143
+ k: v for k, v in before.items() if k not in ("attributes", "attribute_ranges")
144
+ }
145
+ assert after["attributes"] == {}
146
+ [added] = after["attribute_ranges"]
147
+ assert added["start_vertex_id"] == "p0" and added["end_vertex_id"] == "p1"
148
+ assert added["attributes"] == {"occluded": True}
149
+ assert report["removed_ranges"] == [{"object_id": "line_a", "range_id": "r0"}]
150
+ assert report["rule_usage"] == [1, 1]
151
+ assert [(c["kind"], c["before"], c["after"]) for c in report["changes"]] == [
152
+ ("range", False, {"attribute_id": "occluded", "value": True}),
153
+ ("discard", "a", None),
154
+ ]
155
+ for name in ("meta", "sample", "sample_data", "calibrated_sensor"):
156
+ assert result[name] == clip[name]
157
+ again, _ = image_schema_migration_candidate(clip, target(clip), rules)
158
+ assert again.to_dict() == result # deterministic range IDs
159
+
160
+
161
+ def test_draft_annotations_migrate_with_the_same_function():
162
+ clip = with_line()
163
+ draft, report = migrate_image_annotation(
164
+ clip["image_annotation"],
165
+ target_schema=target(clip)["body"],
166
+ mapping=mapping(VISIBLE_TO_RANGE, DISCARD_STYLE),
167
+ )
168
+ full, _ = image_schema_migration_candidate(
169
+ clip, target(clip), mapping(VISIBLE_TO_RANGE, DISCARD_STYLE)
170
+ )
171
+ assert draft == full.to_dict()["image_annotation"]
172
+ assert report["rule_usage"] == [1, 1]
173
+
174
+
175
+ def test_a_mapped_value_must_be_valid_in_the_target_scope():
176
+ clip = with_line()
177
+ bad = rule("segment", "style", "a", {"kind": "set", "attribute_id": "pattern", "value": "a"})
178
+ with pytest.raises(ImageSchemaMigrationConflict) as caught:
179
+ image_schema_migration_candidate(clip, target(clip), mapping(VISIBLE_TO_RANGE, bad))
180
+ assert [row["code"] for row in caught.value.conflicts] == ["mapped_value_invalid"]
181
+
182
+
183
+ def test_two_values_cannot_land_on_the_same_attribute():
184
+ obj = {**line_object(), "attributes": {"visible": False, "style": "b"}, "attribute_ranges": []}
185
+ clip = with_line(obj)
186
+ rules = mapping(
187
+ rule("object", "visible", False, {"kind": "set", "attribute_id": "pattern", "value": "a"}),
188
+ rule("object", "style", "b", {"kind": "set", "attribute_id": "pattern", "value": "b"}),
189
+ )
190
+ with pytest.raises(ImageSchemaMigrationConflict) as caught:
191
+ image_schema_migration_candidate(clip, target(clip), rules)
192
+ assert [row["code"] for row in caught.value.conflicts] == ["attribute_collision"]
193
+
194
+
195
+ @pytest.mark.parametrize(
196
+ "rules",
197
+ [
198
+ [VISIBLE_TO_RANGE, VISIBLE_TO_RANGE],
199
+ [rule("segment", "style", "a", {"kind": "range", "attribute_id": "occluded", "value": True})],
200
+ [rule("object", "visible", False, {"kind": "rename"})],
201
+ [rule("object", "visible", False, {"kind": "discard", "attribute_id": "x"})],
202
+ [rule("image", "visible", False, {"kind": "discard"})],
203
+ [rule("object", "visible", 3, {"kind": "discard"})],
204
+ ],
205
+ )
206
+ def test_mapping_grammar_is_closed(rules):
207
+ with pytest.raises(ValueError):
208
+ parse_image_schema_migration_mapping(mapping(*rules))
209
+
210
+
211
+ @pytest.mark.parametrize("change", [{"schema_id": "another"}, {"version": "1.0.0"}])
212
+ def test_migration_keeps_the_schema_id_and_advances_the_version(change):
213
+ clip = with_line({**line_object(), "attributes": {}, "attribute_ranges": []})
214
+ with pytest.raises(ValueError):
215
+ image_schema_migration_candidate(clip, target(clip, **change), mapping())
216
+
217
+
218
+ @pytest.mark.parametrize("part", ["digest", "source", "report", "usage", "mapping", "unknown"])
219
+ def test_preview_rejects_tampering_even_with_a_recomputed_fingerprint(part):
220
+ clip = with_line()
221
+ value = build_image_schema_migration_preview(
222
+ clip, target(clip), mapping(VISIBLE_TO_RANGE, DISCARD_STYLE)
223
+ ).to_dict()
224
+ if part == "source":
225
+ value["after_identity"]["components"]["sample"] = "sha256:" + "a" * 64
226
+ if part == "report":
227
+ value["report"]["changes"] = []
228
+ value["report"]["removed_ranges"] = []
229
+ value["report"]["rule_usage"] = [0, 0]
230
+ if part == "usage":
231
+ value["report"]["rule_usage"] = [1]
232
+ if part == "mapping":
233
+ value["mapping"]["rules"].append(VISIBLE_TO_RANGE)
234
+ if part == "unknown":
235
+ value["automatic_repair"] = True
236
+ value["preview_fingerprint"] = (
237
+ "sha256:" + "0" * 64
238
+ if part == "digest"
239
+ else fingerprint({k: v for k, v in value.items() if k != "preview_fingerprint"})
240
+ )
241
+ with pytest.raises(ValueError):
242
+ parse_image_schema_migration_preview(value)
243
+
244
+
245
+ def test_typed_client_verifies_the_exact_preview_and_receipt():
246
+ clip = with_line()
247
+ snap = target(clip)
248
+ rules = mapping(VISIBLE_TO_RANGE, DISCARD_STYLE)
249
+ preview = build_image_schema_migration_preview(clip, snap, rules).to_dict()
250
+ bad = {"enabled": False}
251
+
252
+ def handler(request):
253
+ body = json.loads(request.content)
254
+ if request.url.path.endswith("/image-schema-migrations/preview"):
255
+ assert body == {
256
+ "project_id": 7,
257
+ "clip_id": "clip_test",
258
+ "target_schema": snap["identity"],
259
+ "mapping": rules,
260
+ }
261
+ return httpx.Response(200, json=preview)
262
+ assert request.url.path.endswith("/image-schema-migrations/commit")
263
+ receipt = build_image_schema_migration_receipt(body).to_dict()
264
+ if bad["enabled"]:
265
+ receipt["idempotency_key"] = "other-request"
266
+ return httpx.Response(200, json=receipt)
267
+
268
+ with X4DClient("http://x4d.test", token="test") as client:
269
+ client._http.close()
270
+ client._http = httpx.Client(
271
+ base_url="http://x4d.test", transport=httpx.MockTransport(handler)
272
+ )
273
+ parsed = client.image_schemas.preview_migration(
274
+ clip=clip, target_schema=snap, mapping=rules
275
+ )
276
+ request = client.image_schemas.migration_request(parsed, "explicit-migration")
277
+ assert parse_image_schema_migration_request(request.to_dict())
278
+ receipt = client.image_schemas.commit_migration(request).to_dict()
279
+ assert receipt["identity"] == preview["after_identity"]
280
+ bad["enabled"] = True
281
+ with pytest.raises(ValueError, match="another request"):
282
+ client.image_schemas.commit_migration(request)
283
+ assert build_image_schema_migration_request(parsed, "k").to_dict()["preview"] == preview
@@ -0,0 +1,161 @@
1
+ """Typed image Schema authoring against a strict in-memory registry."""
2
+
3
+ from copy import deepcopy
4
+ import hashlib
5
+ import json
6
+
7
+ import httpx
8
+ import pytest
9
+
10
+ from x4d_devkit import X4DClient
11
+ from x4d_devkit._image_contract import canonical
12
+ from x4d_devkit.image_schema import parse_image_schema_body
13
+ from tests.image_fixtures import schema_body
14
+
15
+
16
+ def etag(value):
17
+ return '"sha256:' + hashlib.sha256(canonical(value)).hexdigest() + '"'
18
+
19
+
20
+ class Registry:
21
+ def __init__(self):
22
+ self.rows = {}
23
+ self.project = None
24
+ self.tamper = None
25
+
26
+ def record(self, body, status):
27
+ return {
28
+ "contract_version": "x4d-image-label-schema-record-v1",
29
+ "status": status,
30
+ "body": body,
31
+ "fingerprint": parse_image_schema_body(body).identity.fingerprint,
32
+ }
33
+
34
+ def reply(self, value, status=200):
35
+ if self.tamper:
36
+ value = self.tamper(deepcopy(value))
37
+ return httpx.Response(status, json=value, headers={"ETag": etag(value)})
38
+
39
+ def __call__(self, request):
40
+ path = request.url.path.removeprefix("/api/v1")
41
+ body = json.loads(request.content) if request.content else None
42
+ match = request.headers.get("If-Match")
43
+ if path == "/image-schemas/validate":
44
+ return httpx.Response(200, json=self.record(body["body"], "draft"))
45
+ if path == "/label-schemas" and request.method == "POST":
46
+ key = (body["body"]["schema_id"], body["body"]["version"])
47
+ self.rows[key] = self.record(body["body"], "draft")
48
+ return self.reply(self.rows[key], 201)
49
+ if path.startswith("/label-schemas/"):
50
+ _, _, schema_id, _, version, *action = path.split("/")
51
+ row = self.rows[(schema_id, version)]
52
+ if request.method == "GET":
53
+ return self.reply(row)
54
+ if match != etag(row):
55
+ return httpx.Response(412, json={"detail": "changed"})
56
+ if request.method == "DELETE":
57
+ del self.rows[(schema_id, version)]
58
+ return httpx.Response(204)
59
+ if action == ["publish"]:
60
+ row["status"] = "active"
61
+ elif action == ["deprecate"]:
62
+ row["status"] = "deprecated"
63
+ else:
64
+ row["body"] = body["body"]
65
+ row["fingerprint"] = parse_image_schema_body(body["body"]).identity.fingerprint
66
+ return self.reply(row)
67
+ if path == "/projects/7/label-schema":
68
+ if request.method == "PUT":
69
+ if match != etag(self.project):
70
+ return httpx.Response(412, json={"detail": "changed"})
71
+ self.project = {**self.project, "label_schema": self.rows[(body["schema_id"], body["version"])]}
72
+ return self.reply(self.project)
73
+ raise AssertionError(path)
74
+
75
+
76
+ @pytest.fixture
77
+ def setup():
78
+ registry = Registry()
79
+ with X4DClient("http://x4d.test", token="test") as client:
80
+ client._http.close()
81
+ client._http = httpx.Client(base_url="http://x4d.test", transport=httpx.MockTransport(registry))
82
+ yield client.image_schemas, registry
83
+
84
+
85
+ def draft_body(version="2.0.0"):
86
+ body = schema_body()
87
+ body["version"] = version
88
+ return body
89
+
90
+
91
+ def test_draft_update_publish_and_bind_round_trip(setup):
92
+ api, registry = setup
93
+ body = draft_body()
94
+ assert api.validate(body).to_dict()["status"] == "draft"
95
+ entry = api.create_draft(body)
96
+ assert entry.record.to_dict()["status"] == "draft"
97
+ edited = deepcopy(body)
98
+ edited["description"] = "Edited draft"
99
+ entry = api.update_draft(edited, etag=entry.etag)
100
+ assert entry.record.body["description"] == "Edited draft"
101
+ assert api.get_entry(schema_id="test_image", version="2.0.0") == entry
102
+ entry = api.publish(entry)
103
+ assert entry.record.to_dict()["status"] == "active"
104
+ registry.project = {
105
+ "contract_version": "x4d-image-project-label-schema-v1",
106
+ "project_id": 7,
107
+ "label_schema": registry.record(schema_body(), "active"),
108
+ }
109
+ project = api.get_project_entry(7)
110
+ bound = api.bind_project(project, schema_id="test_image", version="2.0.0")
111
+ assert bound.value.to_dict()["label_schema"]["body"]["version"] == "2.0.0"
112
+ deprecated = api.deprecate(entry, reason="replaced")
113
+ assert deprecated.record.to_dict()["status"] == "deprecated"
114
+
115
+
116
+ def test_stale_etag_is_rejected_by_the_server(setup):
117
+ api, _ = setup
118
+ first = api.create_draft(draft_body())
119
+ api.update_draft({**draft_body(), "description": "one"}, etag=first.etag)
120
+ with pytest.raises(httpx.HTTPStatusError):
121
+ api.update_draft({**draft_body(), "description": "two"}, etag=first.etag)
122
+
123
+
124
+ def test_delete_draft_needs_the_current_etag(setup):
125
+ api, registry = setup
126
+ entry = api.create_draft(draft_body())
127
+ api.delete_draft(entry)
128
+ assert registry.rows == {}
129
+
130
+
131
+ def test_invalid_body_never_reaches_the_server(setup):
132
+ api, registry = setup
133
+ body = draft_body()
134
+ body["categories"][0]["geometry_type"] = "circle"
135
+ with pytest.raises(ValueError):
136
+ api.create_draft(body)
137
+ assert registry.rows == {}
138
+
139
+
140
+ @pytest.mark.parametrize("field", ["status", "body"])
141
+ def test_a_response_for_another_record_or_lifecycle_is_rejected(setup, field):
142
+ api, registry = setup
143
+ entry = api.create_draft(draft_body())
144
+
145
+ def tamper(value):
146
+ if field == "status":
147
+ value["status"] = "draft"
148
+ else:
149
+ value["body"]["description"] = "something else"
150
+ value["fingerprint"] = parse_image_schema_body(value["body"]).identity.fingerprint
151
+ return value
152
+
153
+ registry.tamper = tamper
154
+ with pytest.raises(ValueError, match="another Schema"):
155
+ api.publish(entry)
156
+
157
+
158
+ def test_a_weak_or_missing_etag_is_refused_locally(setup):
159
+ api, _ = setup
160
+ with pytest.raises(ValueError, match="strong ETag"):
161
+ api.update_draft(draft_body(), etag="W/\"x\"")
@@ -11,7 +11,7 @@ from x4d_devkit.eval.matching import MetricData
11
11
 
12
12
 
13
13
  def test_package_version_is_current_release():
14
- assert x4d_devkit.__version__ == "0.52.0rc2"
14
+ assert x4d_devkit.__version__ == "0.53.0rc2"
15
15
 
16
16
 
17
17
  def test_loader_exposes_and_enforces_only_current_schema(tmp_path):
@@ -43,9 +43,15 @@ from x4d_devkit.client.reconstruction import (
43
43
  from x4d_devkit.client.services import ServicesAPI
44
44
  from x4d_devkit.client.test_sets import TestSetsAPI
45
45
 
46
- from x4d_devkit.client.image_schemas import ImageSchemasAPI
46
+ from x4d_devkit.client.image_schemas import (
47
+ ImageProjectSchemaEntry,
48
+ ImageSchemaEntry,
49
+ ImageSchemasAPI,
50
+ )
47
51
 
48
52
  __all__ = [
53
+ "ImageProjectSchemaEntry",
54
+ "ImageSchemaEntry",
49
55
  "ImageSchemasAPI",
50
56
  "ImageWorkSessionsAPI",
51
57
  "ImageExportsAPI",
@@ -173,6 +173,22 @@ class X4DClient:
173
173
  resp.raise_for_status()
174
174
  return parse_i_json(resp.content, path=f"POST {self._url(path)} response")
175
175
 
176
+ def put(self, path: str, **kwargs):
177
+ resp = self._http.put(self._url(path), **kwargs)
178
+ resp.raise_for_status()
179
+ return parse_i_json(resp.content, path=f"PUT {self._url(path)} response")
180
+
181
+ def exchange(self, method: str, path: str, **kwargs):
182
+ """One request returning ``(strict JSON or None for 204, ETag or None)``."""
183
+ resp = self._http.request(method, self._url(path), **kwargs)
184
+ resp.raise_for_status()
185
+ value = (
186
+ parse_i_json(resp.content, path=f"{method} {self._url(path)} response")
187
+ if resp.content
188
+ else None
189
+ )
190
+ return value, resp.headers.get("ETag")
191
+
176
192
  def delete(self, path: str, **kwargs):
177
193
  resp = self._http.delete(self._url(path), **kwargs)
178
194
  resp.raise_for_status()