x4d-devkit 0.53.0rc1__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.
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/PKG-INFO +1 -1
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/pyproject.toml +1 -1
- x4d_devkit-0.53.0rc2/tests/test_image_schema_registry_client.py +161 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_sdk_api.py +1 -1
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/__init__.py +7 -1
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/client.py +16 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/image_schemas.py +155 -1
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/PKG-INFO +1 -1
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/SOURCES.txt +1 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/LICENSE +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/README.md +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/setup.cfg +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_accounts.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_annotation_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_annotation_document_hash.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_annotation_reference.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_assets_client.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_boundary.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_box.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_build_info.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_category_audit.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_clean_basemap.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_clean_basemap_client.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_cli_auth.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_client_json_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_client_projects.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_clip_archives.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_contract_validation.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_coordinate_frames.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_current_state.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_current_state_identity_consumers_example.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_dataset_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_dataset_identity.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_detection_eval.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_evaluate.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_export_bundle.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_clip.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_conditional_rules.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_enum_set.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_export.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_geometry.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_groups.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_preannotation.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_reference.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_review.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_schema_migration.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_schema_upgrade.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_image_work_session.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_inference_input.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_inference_schemas.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_inference_service.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_label_schemas.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_lidar_materialization.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_loader.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_matching.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_metrics.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_models.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_parse_equivalence.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_platform_pose.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_preannotation_client.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_project_discovery.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_reconstruction_client.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_release_gate.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_roadmark_preview.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_semantic_scene_client.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_semantic_scene_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_semantic_scene_independent.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_semantic_scene_native_clip.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_source_attributes.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_time_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_token.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_tracking_eval.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_training_identity.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_transform.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_transform_chain.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_validation.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_vector_map.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_vector_map_geometry.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_vector_map_session.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_work_session.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_work_session_validation_reuse.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/__init__.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/_image_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/accounts.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/annotation_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/annotation_reference.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/audit.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/build_info.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/clean_basemap.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/cli.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/_origin.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/accounts.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/annotation_reference.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/assets.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/bags.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/capabilities.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/checkpoints.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/clean_basemap.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/clip_work_sessions.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/clips.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/dataset_identity.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/image_clips.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/image_exports.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/image_preannotations.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/image_review.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/image_work_sessions.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/label_schemas.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/preannotation.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/projects.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/reconstruction.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/semantic_scene.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/services.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/client/test_sets.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/consumer_identity.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/coordinate_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/core/__init__.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/core/loader.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/core/models.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/core/token.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/core/transform.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/current_state.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/dataset_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/dataset_identity.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/__init__.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/api.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/box.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/detection.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/matching.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/metrics.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/eval/tracking.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/export_bundle.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_clip.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_export.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_geometry.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_groups.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_preannotation.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_reference.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_review.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_schema.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_schema_migration.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_schema_upgrade.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/image_work_session.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/__init__.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/input.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/models.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/schemas.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/service.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/inference/tasks.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/json_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/label_schema.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/lidar_materialization.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/materialize.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/project_discovery.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/roadmark_preview.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/semantic_scene_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/time_contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/training_identity.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/validation/__init__.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/validation/contract.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/validation/platform_pose.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/validation/report.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/validation/validator.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/vector_map.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/vector_map_geometry.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit/work_session.py +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/dependency_links.txt +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/entry_points.txt +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/requires.txt +0 -0
- {x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/x4d_devkit.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "x4d-devkit"
|
|
7
|
-
version = "0.53.
|
|
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,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.53.
|
|
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
|
|
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()
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"""Separate native image registry reads and explicit admin upgrade/migration adapters."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from x4d_devkit._image_contract import ImageDocument, fields, integer, require, text
|
|
4
7
|
from x4d_devkit.image_schema import (
|
|
8
|
+
ImageSchemaRecord,
|
|
9
|
+
parse_image_schema_body,
|
|
5
10
|
parse_image_schema_record,
|
|
6
11
|
parse_image_project_schema,
|
|
7
12
|
)
|
|
@@ -23,6 +28,39 @@ from x4d_devkit.image_schema_migration import (
|
|
|
23
28
|
from x4d_devkit.label_schema import LabelSchemaBinding
|
|
24
29
|
|
|
25
30
|
|
|
31
|
+
_STRONG_ETAG = re.compile(r'"sha256:[0-9a-f]{64}"')
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class ImageSchemaEntry:
|
|
36
|
+
"""A registry record with the strong ETag its next write must match."""
|
|
37
|
+
|
|
38
|
+
record: ImageSchemaRecord
|
|
39
|
+
etag: str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(frozen=True)
|
|
43
|
+
class ImageProjectSchemaEntry:
|
|
44
|
+
"""A project's image Schema binding with its strong ETag."""
|
|
45
|
+
|
|
46
|
+
value: ImageDocument
|
|
47
|
+
etag: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _etag(value, path):
|
|
51
|
+
require(
|
|
52
|
+
type(value) is str and _STRONG_ETAG.fullmatch(value) is not None,
|
|
53
|
+
"Registry write requires a strong ETag",
|
|
54
|
+
path,
|
|
55
|
+
)
|
|
56
|
+
return value
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _version_path(schema_id, version):
|
|
60
|
+
LabelSchemaBinding(schema_id, version)
|
|
61
|
+
return f"/label-schemas/{schema_id}/versions/{version}"
|
|
62
|
+
|
|
63
|
+
|
|
26
64
|
class ImageSchemasAPI:
|
|
27
65
|
def __init__(self, client):
|
|
28
66
|
self._client = client
|
|
@@ -161,3 +199,119 @@ class ImageSchemasAPI:
|
|
|
161
199
|
|
|
162
200
|
def migration_request(self, preview, idempotency_key):
|
|
163
201
|
return build_image_schema_migration_request(preview, idempotency_key)
|
|
202
|
+
|
|
203
|
+
# Registry authoring. The server validates with the same Devkit; every
|
|
204
|
+
# response is checked to be exactly the requested record and lifecycle.
|
|
205
|
+
|
|
206
|
+
def _entry(self, method, path, *, expect, status, etag=None, **kwargs):
|
|
207
|
+
headers = {"If-Match": _etag(etag, "image_schemas.etag")} if etag else None
|
|
208
|
+
value, returned = self._client.exchange(method, path, headers=headers, **kwargs)
|
|
209
|
+
record = parse_image_schema_record(value)
|
|
210
|
+
require(
|
|
211
|
+
record.body == expect and record.to_dict()["status"] == status,
|
|
212
|
+
"Registry returned another Schema or lifecycle",
|
|
213
|
+
"image_schemas.record",
|
|
214
|
+
)
|
|
215
|
+
return ImageSchemaEntry(record, _etag(returned, "image_schemas.response_etag"))
|
|
216
|
+
|
|
217
|
+
def validate(self, body):
|
|
218
|
+
"""Server-side validation preview; returns the would-be draft record."""
|
|
219
|
+
expect = parse_image_schema_body(body).to_dict()
|
|
220
|
+
record = parse_image_schema_record(
|
|
221
|
+
self._client.post("/image-schemas/validate", json={"body": expect})
|
|
222
|
+
)
|
|
223
|
+
require(
|
|
224
|
+
record.body == expect and record.to_dict()["status"] == "draft",
|
|
225
|
+
"Validation returned another Schema",
|
|
226
|
+
"image_schemas.validate",
|
|
227
|
+
)
|
|
228
|
+
return record
|
|
229
|
+
|
|
230
|
+
def get_entry(self, *, schema_id, version):
|
|
231
|
+
value, etag = self._client.exchange("GET", _version_path(schema_id, version))
|
|
232
|
+
record = parse_image_schema_record(value)
|
|
233
|
+
require(
|
|
234
|
+
(record.schema_id, record.version) == (schema_id, version),
|
|
235
|
+
"Registry returned another Schema",
|
|
236
|
+
"image_schemas.identity",
|
|
237
|
+
)
|
|
238
|
+
return ImageSchemaEntry(record, _etag(etag, "image_schemas.response_etag"))
|
|
239
|
+
|
|
240
|
+
def create_draft(self, body):
|
|
241
|
+
expect = parse_image_schema_body(body).to_dict()
|
|
242
|
+
return self._entry(
|
|
243
|
+
"POST", "/label-schemas", expect=expect, status="draft", json={"body": expect}
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
def update_draft(self, body, *, etag):
|
|
247
|
+
expect = parse_image_schema_body(body).to_dict()
|
|
248
|
+
return self._entry(
|
|
249
|
+
"PUT",
|
|
250
|
+
_version_path(expect["schema_id"], expect["version"]),
|
|
251
|
+
expect=expect,
|
|
252
|
+
status="draft",
|
|
253
|
+
etag=etag,
|
|
254
|
+
json={"body": expect},
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def publish(self, entry):
|
|
258
|
+
"""Publish exactly the draft in ``entry``; published versions are immutable."""
|
|
259
|
+
body = entry.record.body
|
|
260
|
+
return self._entry(
|
|
261
|
+
"POST",
|
|
262
|
+
_version_path(body["schema_id"], body["version"]) + "/publish",
|
|
263
|
+
expect=body,
|
|
264
|
+
status="active",
|
|
265
|
+
etag=entry.etag,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
def deprecate(self, entry, *, reason):
|
|
269
|
+
body = entry.record.body
|
|
270
|
+
return self._entry(
|
|
271
|
+
"POST",
|
|
272
|
+
_version_path(body["schema_id"], body["version"]) + "/deprecate",
|
|
273
|
+
expect=body,
|
|
274
|
+
status="deprecated",
|
|
275
|
+
etag=entry.etag,
|
|
276
|
+
json={"reason": text(reason, "image_schemas.reason")},
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
def delete_draft(self, entry):
|
|
280
|
+
body = entry.record.body
|
|
281
|
+
value, _ = self._client.exchange(
|
|
282
|
+
"DELETE",
|
|
283
|
+
_version_path(body["schema_id"], body["version"]),
|
|
284
|
+
headers={"If-Match": _etag(entry.etag, "image_schemas.etag")},
|
|
285
|
+
)
|
|
286
|
+
require(value is None, "Draft deletion returns no document", "image_schemas.delete")
|
|
287
|
+
|
|
288
|
+
def get_project_entry(self, project_id):
|
|
289
|
+
integer(project_id, "project_id", minimum=1)
|
|
290
|
+
value, etag = self._client.exchange("GET", f"/projects/{project_id}/label-schema")
|
|
291
|
+
parsed = parse_image_project_schema(value)
|
|
292
|
+
require(parsed.to_dict()["project_id"] == project_id, "Registry returned another project", "image_schemas.project_id")
|
|
293
|
+
return ImageProjectSchemaEntry(parsed, _etag(etag, "image_schemas.response_etag"))
|
|
294
|
+
|
|
295
|
+
def bind_project(self, project_entry, *, schema_id, version):
|
|
296
|
+
"""Make an active version the project default; existing Clips keep theirs."""
|
|
297
|
+
project_id = project_entry.value.to_dict()["project_id"]
|
|
298
|
+
LabelSchemaBinding(schema_id, version)
|
|
299
|
+
value, etag = self._client.exchange(
|
|
300
|
+
"PUT",
|
|
301
|
+
f"/projects/{project_id}/label-schema",
|
|
302
|
+
headers={"If-Match": _etag(project_entry.etag, "image_schemas.etag")},
|
|
303
|
+
json={
|
|
304
|
+
"contract_version": "x4d-label-schema-binding-v2",
|
|
305
|
+
"schema_id": schema_id,
|
|
306
|
+
"version": version,
|
|
307
|
+
},
|
|
308
|
+
)
|
|
309
|
+
parsed = parse_image_project_schema(value).to_dict()
|
|
310
|
+
bound = parsed["label_schema"]["body"]
|
|
311
|
+
require(
|
|
312
|
+
parsed["project_id"] == project_id
|
|
313
|
+
and (bound["schema_id"], bound["version"]) == (schema_id, version),
|
|
314
|
+
"Registry bound another Schema",
|
|
315
|
+
"image_schemas.bind_project",
|
|
316
|
+
)
|
|
317
|
+
return ImageProjectSchemaEntry(parse_image_project_schema(parsed), _etag(etag, "image_schemas.response_etag"))
|
|
@@ -35,6 +35,7 @@ tests/test_image_preannotation.py
|
|
|
35
35
|
tests/test_image_reference.py
|
|
36
36
|
tests/test_image_review.py
|
|
37
37
|
tests/test_image_schema_migration.py
|
|
38
|
+
tests/test_image_schema_registry_client.py
|
|
38
39
|
tests/test_image_schema_upgrade.py
|
|
39
40
|
tests/test_image_work_session.py
|
|
40
41
|
tests/test_inference_input.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{x4d_devkit-0.53.0rc1 → x4d_devkit-0.53.0rc2}/tests/test_current_state_identity_consumers_example.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|