x4d-devkit 0.48.0rc1__tar.gz → 0.48.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.48.0rc1 → x4d_devkit-0.48.0rc2}/PKG-INFO +1 -1
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/pyproject.toml +1 -1
- x4d_devkit-0.48.0rc2/tests/test_image_work_session.py +228 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_sdk_api.py +1 -1
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/__init__.py +2 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/client.py +2 -0
- x4d_devkit-0.48.0rc2/x4d_devkit/client/image_work_sessions.py +80 -0
- x4d_devkit-0.48.0rc2/x4d_devkit/image_work_session.py +161 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit.egg-info/PKG-INFO +1 -1
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit.egg-info/SOURCES.txt +3 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/LICENSE +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/README.md +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/setup.cfg +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_annotation_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_annotation_reference.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_assets_client.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_boundary.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_box.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_build_info.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_category_audit.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_clean_basemap.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_clean_basemap_client.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_cli_auth.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_client_json_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_client_projects.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_clip_archives.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_contract_validation.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_coordinate_frames.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_current_state.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_current_state_identity_consumers_example.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_dataset_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_dataset_identity.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_detection_eval.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_evaluate.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_export_bundle.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_image_clip.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_image_geometry.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_image_review.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_inference_input.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_inference_schemas.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_inference_service.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_label_schemas.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_lidar_materialization.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_loader.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_matching.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_metrics.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_models.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_platform_pose.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_preannotation_client.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_reconstruction_client.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_release_gate.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_roadmark_preview.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_semantic_scene_client.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_semantic_scene_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_semantic_scene_independent.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_semantic_scene_native_clip.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_source_attributes.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_time_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_token.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_tracking_eval.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_training_identity.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_transform.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_transform_chain.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_validation.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_vector_map.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_vector_map_geometry.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_vector_map_session.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_work_session.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/tests/test_work_session_validation_reuse.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/__init__.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/_image_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/annotation_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/annotation_reference.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/audit.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/build_info.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/clean_basemap.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/cli.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/_origin.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/annotation_reference.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/assets.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/bags.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/capabilities.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/checkpoints.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/clean_basemap.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/clip_work_sessions.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/clips.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/dataset_identity.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/image_clips.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/image_review.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/label_schemas.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/preannotation.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/projects.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/reconstruction.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/semantic_scene.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/services.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/client/test_sets.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/consumer_identity.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/coordinate_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/core/__init__.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/core/loader.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/core/models.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/core/token.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/core/transform.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/current_state.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/dataset_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/dataset_identity.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/eval/__init__.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/eval/api.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/eval/box.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/eval/detection.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/eval/matching.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/eval/metrics.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/eval/tracking.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/export_bundle.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/image_clip.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/image_geometry.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/image_review.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/image_schema.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/inference/__init__.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/inference/input.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/inference/models.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/inference/schemas.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/inference/service.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/inference/tasks.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/json_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/label_schema.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/lidar_materialization.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/materialize.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/roadmark_preview.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/semantic_scene_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/time_contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/training_identity.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/validation/__init__.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/validation/contract.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/validation/platform_pose.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/validation/report.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/validation/validator.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/vector_map.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/vector_map_geometry.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit/work_session.py +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit.egg-info/dependency_links.txt +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit.egg-info/entry_points.txt +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.0rc2}/x4d_devkit.egg-info/requires.txt +0 -0
- {x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.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.48.
|
|
7
|
+
version = "0.48.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,228 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from x4d_devkit import X4DClient
|
|
7
|
+
from x4d_devkit.client import WorkSessionConflictError
|
|
8
|
+
from x4d_devkit.image_clip import build_image_identity
|
|
9
|
+
from x4d_devkit.image_work_session import (
|
|
10
|
+
IMAGE_SUBMIT_RECEIPT_VERSION,
|
|
11
|
+
IMAGE_WORK_SESSION_VERSION,
|
|
12
|
+
build_image_submit,
|
|
13
|
+
image_candidate,
|
|
14
|
+
parse_image_submit,
|
|
15
|
+
parse_image_submit_receipt,
|
|
16
|
+
parse_image_work_session,
|
|
17
|
+
)
|
|
18
|
+
from tests.image_fixtures import clip_payload, line_object
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def session():
|
|
22
|
+
return parse_image_work_session(
|
|
23
|
+
{"contract_version": IMAGE_WORK_SESSION_VERSION, "clip": clip_payload()}
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def candidate():
|
|
28
|
+
annotation = session().clip.to_dict()["image_annotation"]
|
|
29
|
+
annotation["objects"] = [line_object()]
|
|
30
|
+
annotation["completion"][0]["state"] = "complete"
|
|
31
|
+
return annotation
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def receipt(annotation=None):
|
|
35
|
+
ws = session()
|
|
36
|
+
value = image_candidate(ws.clip, annotation or candidate()).to_dict()
|
|
37
|
+
return {
|
|
38
|
+
"contract_version": IMAGE_SUBMIT_RECEIPT_VERSION,
|
|
39
|
+
"project_id": value["meta"]["project_id"],
|
|
40
|
+
"clip_id": value["meta"]["clip_id"],
|
|
41
|
+
**{
|
|
42
|
+
key: value["identity"][key]
|
|
43
|
+
for key in (
|
|
44
|
+
"source_revision",
|
|
45
|
+
"schema_fingerprint",
|
|
46
|
+
"annotation_revision",
|
|
47
|
+
"clip_revision",
|
|
48
|
+
)
|
|
49
|
+
},
|
|
50
|
+
"changed": True,
|
|
51
|
+
"projection_state": "ready",
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def client(handler):
|
|
56
|
+
value = X4DClient("https://platform.test", token="private")
|
|
57
|
+
value._http.close()
|
|
58
|
+
value._http = httpx.Client(
|
|
59
|
+
base_url="https://platform.test", transport=httpx.MockTransport(handler)
|
|
60
|
+
)
|
|
61
|
+
return value
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_complete_candidate_preserves_source_schema_other_views_and_stable_ranges():
|
|
65
|
+
ws = session()
|
|
66
|
+
request = build_image_submit(ws, candidate()).to_dict()
|
|
67
|
+
parsed = parse_image_submit(request)
|
|
68
|
+
assert parsed.to_dict()["image_annotation"] == candidate()
|
|
69
|
+
value = image_candidate(ws.clip, candidate()).to_dict()
|
|
70
|
+
assert (
|
|
71
|
+
value["identity"]["source_revision"]
|
|
72
|
+
== ws.clip.to_dict()["identity"]["source_revision"]
|
|
73
|
+
)
|
|
74
|
+
assert value["identity"]["schema_fingerprint"] == request["base_schema_fingerprint"]
|
|
75
|
+
assert (
|
|
76
|
+
value["image_annotation"]["objects"][0]["attribute_ranges"]
|
|
77
|
+
== line_object()["attribute_ranges"]
|
|
78
|
+
)
|
|
79
|
+
assert ws.clip.to_dict()["image_annotation"]["objects"] == []
|
|
80
|
+
request["image_annotation"]["objects"].clear()
|
|
81
|
+
assert parsed.to_dict()["image_annotation"]["objects"]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@pytest.mark.parametrize(
|
|
85
|
+
"change",
|
|
86
|
+
[
|
|
87
|
+
"version",
|
|
88
|
+
"extra",
|
|
89
|
+
"image-version",
|
|
90
|
+
"invalid-clip",
|
|
91
|
+
"missing-range",
|
|
92
|
+
"missing-scope",
|
|
93
|
+
"foreign-object",
|
|
94
|
+
],
|
|
95
|
+
)
|
|
96
|
+
def test_strict_session_and_candidate_rejection(change):
|
|
97
|
+
value = session().to_dict()
|
|
98
|
+
if change == "version":
|
|
99
|
+
value["contract_version"] = "x4d-clip-work-session-v10"
|
|
100
|
+
elif change == "extra":
|
|
101
|
+
value["ready"] = True
|
|
102
|
+
elif change == "image-version":
|
|
103
|
+
value["clip"]["meta"]["schema_version"] = "0.18"
|
|
104
|
+
elif change == "invalid-clip":
|
|
105
|
+
value["clip"]["identity"]["clip_revision"] = "sha256:" + "0" * 64
|
|
106
|
+
else:
|
|
107
|
+
ann = candidate()
|
|
108
|
+
if change == "missing-range":
|
|
109
|
+
ann["objects"][0]["attribute_ranges"][0]["start_vertex_id"] = "missing"
|
|
110
|
+
elif change == "missing-scope":
|
|
111
|
+
ann["completion"].pop()
|
|
112
|
+
else:
|
|
113
|
+
ann["objects"][0]["image_id"] = "foreign"
|
|
114
|
+
with pytest.raises(ValueError):
|
|
115
|
+
build_image_submit(session(), ann)
|
|
116
|
+
return
|
|
117
|
+
with pytest.raises(ValueError):
|
|
118
|
+
parse_image_work_session(value)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@pytest.mark.parametrize(
|
|
122
|
+
"key",
|
|
123
|
+
[
|
|
124
|
+
"contract_version",
|
|
125
|
+
"project_id",
|
|
126
|
+
"clip_id",
|
|
127
|
+
"base_source_revision",
|
|
128
|
+
"base_schema_fingerprint",
|
|
129
|
+
"base_annotation_revision",
|
|
130
|
+
"image_annotation",
|
|
131
|
+
],
|
|
132
|
+
)
|
|
133
|
+
def test_request_rejects_missing_fields_and_no_coercion(key):
|
|
134
|
+
value = build_image_submit(session(), candidate()).to_dict()
|
|
135
|
+
del value[key]
|
|
136
|
+
with pytest.raises(ValueError):
|
|
137
|
+
parse_image_submit(value)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_typed_open_submit_and_exact_retry_request():
|
|
141
|
+
requests = []
|
|
142
|
+
|
|
143
|
+
def handler(req):
|
|
144
|
+
import json
|
|
145
|
+
|
|
146
|
+
requests.append(json.loads(req.content))
|
|
147
|
+
return httpx.Response(
|
|
148
|
+
200,
|
|
149
|
+
json=session().to_dict() if req.url.path.endswith("open") else receipt(),
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
with client(handler) as c:
|
|
153
|
+
ws = c.image_work_sessions.open(project_id=7, clip_id="clip_test")
|
|
154
|
+
accepted = c.image_work_sessions.submit(
|
|
155
|
+
manifest=ws, image_annotation=candidate()
|
|
156
|
+
)
|
|
157
|
+
assert accepted.to_dict() == receipt()
|
|
158
|
+
c.image_work_sessions.submit(manifest=ws, image_annotation=candidate())
|
|
159
|
+
assert (
|
|
160
|
+
requests[1]
|
|
161
|
+
== requests[2]
|
|
162
|
+
== build_image_submit(session(), candidate()).to_dict()
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@pytest.mark.parametrize(
|
|
167
|
+
"key",
|
|
168
|
+
[
|
|
169
|
+
"project_id",
|
|
170
|
+
"clip_id",
|
|
171
|
+
"source_revision",
|
|
172
|
+
"schema_fingerprint",
|
|
173
|
+
"annotation_revision",
|
|
174
|
+
"clip_revision",
|
|
175
|
+
"changed",
|
|
176
|
+
"projection_state",
|
|
177
|
+
],
|
|
178
|
+
)
|
|
179
|
+
def test_typed_client_rejects_unrelated_or_invalid_receipt(key):
|
|
180
|
+
value = receipt()
|
|
181
|
+
value[key] = {
|
|
182
|
+
"project_id": 8,
|
|
183
|
+
"clip_id": "foreign",
|
|
184
|
+
"changed": 1,
|
|
185
|
+
"projection_state": "done",
|
|
186
|
+
}.get(key, "sha256:" + "0" * 64)
|
|
187
|
+
with client(lambda req: httpx.Response(200, json=value)) as c:
|
|
188
|
+
with pytest.raises(ValueError):
|
|
189
|
+
c.image_work_sessions.submit(
|
|
190
|
+
manifest=session(), image_annotation=candidate()
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_conflict_is_structured_and_no_unvalidated_candidate_is_sent():
|
|
195
|
+
conflict = {"annotation_revision": {"base": "old", "current": "new"}}
|
|
196
|
+
with client(
|
|
197
|
+
lambda req: httpx.Response(
|
|
198
|
+
409,
|
|
199
|
+
json={
|
|
200
|
+
"detail": {
|
|
201
|
+
"code": "work_session_conflict",
|
|
202
|
+
"extra": {"conflicts": conflict},
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
)
|
|
206
|
+
) as c:
|
|
207
|
+
with pytest.raises(WorkSessionConflictError) as exc:
|
|
208
|
+
c.image_work_sessions.submit(
|
|
209
|
+
manifest=session(), image_annotation=candidate()
|
|
210
|
+
)
|
|
211
|
+
assert exc.value.conflicts == conflict
|
|
212
|
+
with client(lambda req: pytest.fail("invalid candidate reached transport")) as c:
|
|
213
|
+
ann = copy.deepcopy(candidate())
|
|
214
|
+
ann["completion"] = []
|
|
215
|
+
with pytest.raises(ValueError):
|
|
216
|
+
c.image_work_sessions.submit(manifest=session(), image_annotation=ann)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def test_receipt_and_session_reject_invalid_identity_types():
|
|
220
|
+
value = receipt()
|
|
221
|
+
value["project_id"] = True
|
|
222
|
+
with pytest.raises(ValueError):
|
|
223
|
+
parse_image_submit_receipt(value)
|
|
224
|
+
value = session().to_dict()
|
|
225
|
+
value["clip"]["sample_data"][0]["width"] = 10
|
|
226
|
+
value["clip"]["identity"] = build_image_identity(value["clip"])
|
|
227
|
+
with pytest.raises(ValueError):
|
|
228
|
+
parse_image_work_session(value)
|
|
@@ -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.48.
|
|
14
|
+
assert x4d_devkit.__version__ == "0.48.0rc2"
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def test_loader_exposes_and_enforces_only_current_schema(tmp_path):
|
|
@@ -9,6 +9,7 @@ from x4d_devkit.client.clip_work_sessions import (
|
|
|
9
9
|
WorkSessionConflictError,
|
|
10
10
|
)
|
|
11
11
|
from x4d_devkit.client.client import X4DClient
|
|
12
|
+
from x4d_devkit.client.image_work_sessions import ImageWorkSessionsAPI
|
|
12
13
|
from x4d_devkit.client.clips import ClipsAPI
|
|
13
14
|
from x4d_devkit.client.dataset_identity import DatasetIdentityAPI
|
|
14
15
|
from x4d_devkit.client.label_schemas import (
|
|
@@ -41,6 +42,7 @@ from x4d_devkit.client.services import ServicesAPI
|
|
|
41
42
|
from x4d_devkit.client.test_sets import TestSetsAPI
|
|
42
43
|
|
|
43
44
|
__all__ = [
|
|
45
|
+
"ImageWorkSessionsAPI",
|
|
44
46
|
"CleanBasemapState",
|
|
45
47
|
"AssetsAPI",
|
|
46
48
|
"BagsAPI",
|
|
@@ -24,6 +24,7 @@ from x4d_devkit.client.clips import ClipsAPI
|
|
|
24
24
|
from x4d_devkit.client.dataset_identity import DatasetIdentityAPI
|
|
25
25
|
from x4d_devkit.client.label_schemas import LabelSchemasAPI
|
|
26
26
|
from x4d_devkit.client.image_clips import ImageClipsAPI
|
|
27
|
+
from x4d_devkit.client.image_work_sessions import ImageWorkSessionsAPI
|
|
27
28
|
from x4d_devkit.client.semantic_scene import SemanticSceneAPI
|
|
28
29
|
from x4d_devkit.client.semantic_scene_authoring import (
|
|
29
30
|
SemanticSceneAuthoringWorkSessionsAPI,
|
|
@@ -72,6 +73,7 @@ class X4DClient:
|
|
|
72
73
|
self.reconstruction = ReconstructionAPI(self)
|
|
73
74
|
self.clips = ClipsAPI(self)
|
|
74
75
|
self.image_clips = ImageClipsAPI(self)
|
|
76
|
+
self.image_work_sessions = ImageWorkSessionsAPI(self)
|
|
75
77
|
self.label_schemas = LabelSchemasAPI(self)
|
|
76
78
|
self.semantic_scene = SemanticSceneAPI(self)
|
|
77
79
|
self.semantic_scene_authoring_work_sessions = (
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Typed whole-Clip image authoring with verified acceptance receipts."""
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from x4d_devkit._image_contract import require
|
|
6
|
+
from x4d_devkit.client.clip_work_sessions import WorkSessionConflictError, _error_detail
|
|
7
|
+
from x4d_devkit.image_work_session import (
|
|
8
|
+
ImageWorkSession,
|
|
9
|
+
build_image_submit,
|
|
10
|
+
image_candidate,
|
|
11
|
+
parse_image_submit_receipt,
|
|
12
|
+
parse_image_work_session,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ImageWorkSessionsAPI:
|
|
17
|
+
def __init__(self, client):
|
|
18
|
+
self._client = client
|
|
19
|
+
|
|
20
|
+
def open(self, *, project_id: int, clip_id: str) -> ImageWorkSession:
|
|
21
|
+
from x4d_devkit._image_contract import identifier, integer
|
|
22
|
+
|
|
23
|
+
integer(project_id, "project_id", minimum=1)
|
|
24
|
+
identifier(clip_id, "clip_id")
|
|
25
|
+
value = parse_image_work_session(
|
|
26
|
+
self._client.post(
|
|
27
|
+
"/image-work-sessions/open",
|
|
28
|
+
json={"project_id": project_id, "clip_id": clip_id},
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
context = value.base_identity()
|
|
32
|
+
require(
|
|
33
|
+
context["project_id"] == project_id and context["clip_id"] == clip_id,
|
|
34
|
+
"image session belongs to another project/Clip",
|
|
35
|
+
"image_work_session.clip.meta",
|
|
36
|
+
)
|
|
37
|
+
return value
|
|
38
|
+
|
|
39
|
+
def submit(self, *, manifest: ImageWorkSession | dict, image_annotation: dict):
|
|
40
|
+
session = (
|
|
41
|
+
manifest
|
|
42
|
+
if type(manifest) is ImageWorkSession
|
|
43
|
+
else parse_image_work_session(manifest)
|
|
44
|
+
)
|
|
45
|
+
request = build_image_submit(session, image_annotation).to_dict()
|
|
46
|
+
candidate = image_candidate(session.clip, image_annotation).to_dict()
|
|
47
|
+
try:
|
|
48
|
+
response = self._client.post("/image-work-sessions/submit", json=request)
|
|
49
|
+
except httpx.HTTPStatusError as exc:
|
|
50
|
+
detail = _error_detail(exc)
|
|
51
|
+
if (
|
|
52
|
+
isinstance(detail, dict)
|
|
53
|
+
and detail.get("code") == "work_session_conflict"
|
|
54
|
+
):
|
|
55
|
+
raise WorkSessionConflictError(
|
|
56
|
+
detail.get("message") or "Image work-session baseline is stale",
|
|
57
|
+
conflicts=(detail.get("extra") or {}).get("conflicts", {}),
|
|
58
|
+
status_code=exc.response.status_code,
|
|
59
|
+
) from exc
|
|
60
|
+
raise
|
|
61
|
+
receipt = parse_image_submit_receipt(response)
|
|
62
|
+
result = receipt.to_dict()
|
|
63
|
+
for key in ("project_id", "clip_id"):
|
|
64
|
+
require(
|
|
65
|
+
result[key] == request[key],
|
|
66
|
+
"receipt belongs to another context",
|
|
67
|
+
"receipt." + key,
|
|
68
|
+
)
|
|
69
|
+
for key in (
|
|
70
|
+
"source_revision",
|
|
71
|
+
"schema_fingerprint",
|
|
72
|
+
"annotation_revision",
|
|
73
|
+
"clip_revision",
|
|
74
|
+
):
|
|
75
|
+
require(
|
|
76
|
+
result[key] == candidate["identity"][key],
|
|
77
|
+
"receipt does not accept the submitted candidate",
|
|
78
|
+
"receipt." + key,
|
|
79
|
+
)
|
|
80
|
+
return receipt
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Native image authoring grammar, independent of platform storage and 3D."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from x4d_devkit._image_contract import (
|
|
8
|
+
ImageDocument,
|
|
9
|
+
canonical,
|
|
10
|
+
document,
|
|
11
|
+
fields,
|
|
12
|
+
identifier,
|
|
13
|
+
integer,
|
|
14
|
+
require,
|
|
15
|
+
)
|
|
16
|
+
from x4d_devkit.image_clip import (
|
|
17
|
+
ImageClip,
|
|
18
|
+
_digest,
|
|
19
|
+
build_image_identity,
|
|
20
|
+
parse_image_clip,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
IMAGE_WORK_SESSION_VERSION = "x4d-image-work-session-v1"
|
|
25
|
+
IMAGE_SUBMIT_VERSION = "x4d-image-submit-v1"
|
|
26
|
+
IMAGE_SUBMIT_RECEIPT_VERSION = "x4d-image-submit-receipt-v1"
|
|
27
|
+
_BASE_KEYS = {
|
|
28
|
+
"base_source_revision",
|
|
29
|
+
"base_schema_fingerprint",
|
|
30
|
+
"base_annotation_revision",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ImageWorkSession(ImageDocument):
|
|
35
|
+
@property
|
|
36
|
+
def clip(self) -> ImageClip:
|
|
37
|
+
return parse_image_clip(self.to_dict()["clip"])
|
|
38
|
+
|
|
39
|
+
def base_identity(self) -> dict:
|
|
40
|
+
clip = self.to_dict()["clip"]
|
|
41
|
+
identity = clip["identity"]
|
|
42
|
+
return {
|
|
43
|
+
"project_id": clip["meta"]["project_id"],
|
|
44
|
+
"clip_id": clip["meta"]["clip_id"],
|
|
45
|
+
"base_source_revision": identity["source_revision"],
|
|
46
|
+
"base_schema_fingerprint": identity["schema_fingerprint"],
|
|
47
|
+
"base_annotation_revision": identity["annotation_revision"],
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def parse_image_work_session(value: Any) -> ImageWorkSession:
|
|
52
|
+
canonical(value)
|
|
53
|
+
fields(value, {"contract_version", "clip"}, "image_work_session")
|
|
54
|
+
require(
|
|
55
|
+
value["contract_version"] == IMAGE_WORK_SESSION_VERSION,
|
|
56
|
+
"unsupported image work-session version",
|
|
57
|
+
"image_work_session.contract_version",
|
|
58
|
+
)
|
|
59
|
+
parse_image_clip(value["clip"])
|
|
60
|
+
return document(value, ImageWorkSession)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def image_candidate(clip: ImageClip, image_annotation: Any) -> ImageClip:
|
|
64
|
+
"""Validate one complete replacement in its exact immutable source context."""
|
|
65
|
+
require(type(clip) is ImageClip, "expected a validated image Clip", "clip")
|
|
66
|
+
value = clip.to_dict()
|
|
67
|
+
value["image_annotation"] = image_annotation
|
|
68
|
+
value["identity"] = build_image_identity(value)
|
|
69
|
+
return parse_image_clip(value)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def parse_image_submit(value: Any) -> ImageDocument:
|
|
73
|
+
canonical(value)
|
|
74
|
+
fields(
|
|
75
|
+
value,
|
|
76
|
+
{"contract_version", "project_id", "clip_id", "image_annotation", *_BASE_KEYS},
|
|
77
|
+
"image_submit",
|
|
78
|
+
)
|
|
79
|
+
require(
|
|
80
|
+
value["contract_version"] == IMAGE_SUBMIT_VERSION,
|
|
81
|
+
"unsupported image submit version",
|
|
82
|
+
"image_submit.contract_version",
|
|
83
|
+
)
|
|
84
|
+
integer(value["project_id"], "image_submit.project_id", minimum=1)
|
|
85
|
+
identifier(value["clip_id"], "image_submit.clip_id")
|
|
86
|
+
for key in _BASE_KEYS:
|
|
87
|
+
_digest(value[key], "image_submit." + key)
|
|
88
|
+
fields(
|
|
89
|
+
value["image_annotation"],
|
|
90
|
+
{"contract_version", "clip_id", "objects", "descriptions", "completion"},
|
|
91
|
+
"image_submit.image_annotation",
|
|
92
|
+
)
|
|
93
|
+
require(
|
|
94
|
+
value["image_annotation"]["clip_id"] == value["clip_id"],
|
|
95
|
+
"candidate belongs to another Clip",
|
|
96
|
+
"image_submit.image_annotation.clip_id",
|
|
97
|
+
)
|
|
98
|
+
return document(value)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def build_image_submit(
|
|
102
|
+
manifest: ImageWorkSession | dict, image_annotation: Any
|
|
103
|
+
) -> ImageDocument:
|
|
104
|
+
session = (
|
|
105
|
+
manifest
|
|
106
|
+
if type(manifest) is ImageWorkSession
|
|
107
|
+
else parse_image_work_session(manifest)
|
|
108
|
+
)
|
|
109
|
+
candidate = image_candidate(session.clip, image_annotation)
|
|
110
|
+
return parse_image_submit(
|
|
111
|
+
{
|
|
112
|
+
"contract_version": IMAGE_SUBMIT_VERSION,
|
|
113
|
+
**session.base_identity(),
|
|
114
|
+
"image_annotation": candidate.to_dict()["image_annotation"],
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def parse_image_submit_receipt(value: Any) -> ImageDocument:
|
|
120
|
+
canonical(value)
|
|
121
|
+
fields(
|
|
122
|
+
value,
|
|
123
|
+
{
|
|
124
|
+
"contract_version",
|
|
125
|
+
"project_id",
|
|
126
|
+
"clip_id",
|
|
127
|
+
"source_revision",
|
|
128
|
+
"schema_fingerprint",
|
|
129
|
+
"annotation_revision",
|
|
130
|
+
"clip_revision",
|
|
131
|
+
"changed",
|
|
132
|
+
"projection_state",
|
|
133
|
+
},
|
|
134
|
+
"image_submit_receipt",
|
|
135
|
+
)
|
|
136
|
+
require(
|
|
137
|
+
value["contract_version"] == IMAGE_SUBMIT_RECEIPT_VERSION,
|
|
138
|
+
"unsupported image receipt version",
|
|
139
|
+
"image_submit_receipt.contract_version",
|
|
140
|
+
)
|
|
141
|
+
integer(value["project_id"], "image_submit_receipt.project_id", minimum=1)
|
|
142
|
+
identifier(value["clip_id"], "image_submit_receipt.clip_id")
|
|
143
|
+
for key in (
|
|
144
|
+
"source_revision",
|
|
145
|
+
"schema_fingerprint",
|
|
146
|
+
"annotation_revision",
|
|
147
|
+
"clip_revision",
|
|
148
|
+
):
|
|
149
|
+
_digest(value[key], "image_submit_receipt." + key)
|
|
150
|
+
require(
|
|
151
|
+
type(value["changed"]) is bool,
|
|
152
|
+
"changed must be boolean",
|
|
153
|
+
"image_submit_receipt.changed",
|
|
154
|
+
)
|
|
155
|
+
require(
|
|
156
|
+
type(value["projection_state"]) is str
|
|
157
|
+
and value["projection_state"] in {"ready", "pending"},
|
|
158
|
+
"invalid projection state",
|
|
159
|
+
"image_submit_receipt.projection_state",
|
|
160
|
+
)
|
|
161
|
+
return document(value)
|
|
@@ -26,6 +26,7 @@ tests/test_export_bundle.py
|
|
|
26
26
|
tests/test_image_clip.py
|
|
27
27
|
tests/test_image_geometry.py
|
|
28
28
|
tests/test_image_review.py
|
|
29
|
+
tests/test_image_work_session.py
|
|
29
30
|
tests/test_inference_input.py
|
|
30
31
|
tests/test_inference_schemas.py
|
|
31
32
|
tests/test_inference_service.py
|
|
@@ -77,6 +78,7 @@ x4d_devkit/image_clip.py
|
|
|
77
78
|
x4d_devkit/image_geometry.py
|
|
78
79
|
x4d_devkit/image_review.py
|
|
79
80
|
x4d_devkit/image_schema.py
|
|
81
|
+
x4d_devkit/image_work_session.py
|
|
80
82
|
x4d_devkit/json_contract.py
|
|
81
83
|
x4d_devkit/label_schema.py
|
|
82
84
|
x4d_devkit/lidar_materialization.py
|
|
@@ -109,6 +111,7 @@ x4d_devkit/client/clips.py
|
|
|
109
111
|
x4d_devkit/client/dataset_identity.py
|
|
110
112
|
x4d_devkit/client/image_clips.py
|
|
111
113
|
x4d_devkit/client/image_review.py
|
|
114
|
+
x4d_devkit/client/image_work_sessions.py
|
|
112
115
|
x4d_devkit/client/label_schemas.py
|
|
113
116
|
x4d_devkit/client/preannotation.py
|
|
114
117
|
x4d_devkit/client/projects.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
|
{x4d_devkit-0.48.0rc1 → x4d_devkit-0.48.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
|