darwin-py 3.6.2__tar.gz → 3.6.4__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.
- {darwin_py-3.6.2 → darwin_py-3.6.4}/PKG-INFO +18 -1
- {darwin_py-3.6.2 → darwin_py-3.6.4}/README.md +17 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/cli.py +1 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/cli_functions.py +7 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/download_manager.py +107 -14
- darwin_py-3.6.4/darwin/dataset/json_formatter.py +353 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/remote_dataset.py +64 -10
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/utils.py +15 -6
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/datatypes.py +44 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/importer.py +19 -9
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/options.py +10 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/utils/utils.py +10 -1
- darwin_py-3.6.4/darwin/version/__init__.py +1 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/pyproject.toml +1 -1
- darwin_py-3.6.2/darwin/version/__init__.py +0 -1
- {darwin_py-3.6.2 → darwin_py-3.6.4}/LICENSE +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/backend_v2.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/client.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/config.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/identifier.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/local_dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/release.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/remote_dataset_v2.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/split_manager.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/storage_uploader.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/upload_manager.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/doc_enum.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exceptions.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/exporter.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/coco.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/cvat.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/darwin.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/dataloop.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/helpers/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/helpers/yolo_class_builder.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/instance_mask.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/mask.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/nifti.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/numpy_encoder.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/pascalvoc.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/semantic_mask.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/semantic_mask_grey.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/semantic_mask_index.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/yolo.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/yolo_segmented.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/extractor/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/extractor/video.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/client.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/datasets/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/datasets/create_dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/datasets/get_dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/datasets/list_datasets.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/datasets/remove_dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/archive_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/assign_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/delete_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/get.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/move_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/move_items_to_folder.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/restore_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/set_item_layout.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/set_item_priority.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/set_stage_to_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/tag_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/untag_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/uploads.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/properties/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/properties/create.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/properties/get.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/properties/update.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/team/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/team/get_raw.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/team/get_team.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/types/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/types/common.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/types/item.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/types/query.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/utils/pathutils.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/workflows/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/workflows/get_workflow.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/workflows/get_workflows.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/workflows/list_workflows.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/advanced_filters.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/darwinV2.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/item.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/page.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/properties.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/pydantic_base.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/release.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/sorting.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/team.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/team_member_role.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/typing.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/validators.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/workflow.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/exceptions.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/helpers/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/helpers/assertion.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/helpers/exception_handler.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/client.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/base.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/item.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/stage.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/team.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/team_member.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/v7_id.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/workflow.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/item.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/item_id.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/stage.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/team_member.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/workflow.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/types/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/pydantic_base.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/fixtures.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/test_create_dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/test_delete_dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/test_get_dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/test_list_datasets.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/fixtures.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/fixtures.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_archive_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_assign_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_delete_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_get_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_item_data_object.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_move_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_move_items_to_folder.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_restore_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_set_item_layout.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_set_priority.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_set_stage_to_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_tag_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_untag_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_upload_items.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/properties/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/properties/test_create.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/properties/test_get.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/properties/test_update.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/test_client.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/test_query.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/types/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/types/test_querystring.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/workflows/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/workflows/test_get_workflow.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/workflows/test_get_workflows.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/workflows/test_list_workflows.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data/.v7/metadata.json +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data/.v7/metadata_no_item_level_properties.json +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data/.v7/metadata_with_item_level_properties.json +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data/base_annotation.json +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/fixtures.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_advanced_filters.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_darwin.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_general_darwin_objects.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_page.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_properties.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_sorting.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_team.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_validators.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/dataset.json +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/edge.json +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/stage.json +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/stage_config.json +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/user.json +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/workflow.json +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/invalidvaluefortest.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_wfdataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_wfedge.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_wfstage.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_wfstage_config.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_wfuser.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_workflow.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/fixtures.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/fixtures.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/fixtures.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_datasetmeta.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_itemmeta.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_stagemeta.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_teammeta.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_v7_id.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_workflowmeta.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_item.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_item_id.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_stage.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_team_member.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_workflow.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/test_client.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/coco.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/coco_masks.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/csv_tags.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/csv_tags_video.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/darwin.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/dataloop.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/labelbox.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/labelbox_schemas.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/nifti.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/nifti_schemas.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/pascal_voc.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/superannotate.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/superannotate_schemas.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/item.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/item_sorter.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/path_utils.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/torch/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/torch/dataset.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/torch/transforms.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/torch/utils.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/utils/__init__.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/utils/flatten_list.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/utils/get_item_count.py +0 -0
- {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/validators.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: darwin-py
|
|
3
|
-
Version: 3.6.
|
|
3
|
+
Version: 3.6.4
|
|
4
4
|
Summary: Library and command line interface for darwin.v7labs.com
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -265,6 +265,23 @@ $ darwin dataset pull test:0.1
|
|
|
265
265
|
Dataset example-team/test:0.1 downloaded at /directory/choosen/at/authentication/time .
|
|
266
266
|
```
|
|
267
267
|
|
|
268
|
+
To indent native Darwin JSON files after download, add `--pretty-print`.
|
|
269
|
+
This formats local annotation files and metadata with two-space indentation.
|
|
270
|
+
It keeps the remote export unchanged and preserves all JSON values.
|
|
271
|
+
The flag is off by default.
|
|
272
|
+
|
|
273
|
+
```sh
|
|
274
|
+
darwin dataset pull example-team/test:0.1 --only-annotations --pretty-print
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Local formatting uses extra CPU time and temporary disk space for one formatted
|
|
278
|
+
file. The formatter processes one file at a time with bounded buffers. Media
|
|
279
|
+
downloads run sequentially with this option. They format affected annotations
|
|
280
|
+
again when they record local file paths.
|
|
281
|
+
Other pull steps still parse annotation files and can use more RAM.
|
|
282
|
+
If a formatting step fails, the affected file keeps the bytes it had before that
|
|
283
|
+
step. The command reports an error.
|
|
284
|
+
|
|
268
285
|
---
|
|
269
286
|
|
|
270
287
|
## Usage as a Python library
|
|
@@ -192,6 +192,23 @@ $ darwin dataset pull test:0.1
|
|
|
192
192
|
Dataset example-team/test:0.1 downloaded at /directory/choosen/at/authentication/time .
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
+
To indent native Darwin JSON files after download, add `--pretty-print`.
|
|
196
|
+
This formats local annotation files and metadata with two-space indentation.
|
|
197
|
+
It keeps the remote export unchanged and preserves all JSON values.
|
|
198
|
+
The flag is off by default.
|
|
199
|
+
|
|
200
|
+
```sh
|
|
201
|
+
darwin dataset pull example-team/test:0.1 --only-annotations --pretty-print
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Local formatting uses extra CPU time and temporary disk space for one formatted
|
|
205
|
+
file. The formatter processes one file at a time with bounded buffers. Media
|
|
206
|
+
downloads run sequentially with this option. They format affected annotations
|
|
207
|
+
again when they record local file paths.
|
|
208
|
+
Other pull steps still parse annotation files and can use more RAM.
|
|
209
|
+
If a formatting step fails, the affected file keeps the bytes it had before that
|
|
210
|
+
step. The command reports an error.
|
|
211
|
+
|
|
195
212
|
---
|
|
196
213
|
|
|
197
214
|
## Usage as a Python library
|
|
@@ -31,6 +31,7 @@ from darwin.client import Client
|
|
|
31
31
|
from darwin.config import Config
|
|
32
32
|
from darwin.dataset import RemoteDataset
|
|
33
33
|
from darwin.dataset.identifier import DatasetIdentifier
|
|
34
|
+
from darwin.dataset.json_formatter import PrettyPrintError
|
|
34
35
|
from darwin.dataset.release import Release
|
|
35
36
|
from darwin.dataset.split_manager import split_dataset
|
|
36
37
|
from darwin.dataset.upload_manager import LocalFile
|
|
@@ -365,6 +366,7 @@ def pull_dataset(
|
|
|
365
366
|
retry_interval: int = 10,
|
|
366
367
|
team: Optional[str] = None,
|
|
367
368
|
multi_processed: bool = True,
|
|
369
|
+
pretty_print: bool = False,
|
|
368
370
|
) -> None:
|
|
369
371
|
"""
|
|
370
372
|
Downloads a remote dataset (images and annotations) in the datasets directory.
|
|
@@ -396,6 +398,8 @@ def pull_dataset(
|
|
|
396
398
|
multi_processed: bool
|
|
397
399
|
If True (default), uses multiprocessing to download files in parallel.
|
|
398
400
|
If False, the dataset is downloaded on a single process.
|
|
401
|
+
pretty_print: bool
|
|
402
|
+
Indents downloaded annotation JSON locally. Defaults to False.
|
|
399
403
|
"""
|
|
400
404
|
identifier: DatasetIdentifier = DatasetIdentifier.parse(dataset_slug)
|
|
401
405
|
if team:
|
|
@@ -432,6 +436,7 @@ def pull_dataset(
|
|
|
432
436
|
retry_timeout=retry_timeout,
|
|
433
437
|
retry_interval=retry_interval,
|
|
434
438
|
multi_processed=multi_processed,
|
|
439
|
+
pretty_print=pretty_print,
|
|
435
440
|
)
|
|
436
441
|
print_new_version_info(client)
|
|
437
442
|
except NotFound:
|
|
@@ -444,6 +449,8 @@ def pull_dataset(
|
|
|
444
449
|
f"Version '{dataset.identifier}:{version}' is of format '{uef.format}', "
|
|
445
450
|
f"only the darwin formats ('json', 'darwin_json_2') are supported for `darwin dataset pull`"
|
|
446
451
|
)
|
|
452
|
+
except PrettyPrintError as error:
|
|
453
|
+
_error(str(error))
|
|
447
454
|
except MissingDependency as e:
|
|
448
455
|
_error(str(e))
|
|
449
456
|
|
|
@@ -7,6 +7,7 @@ import os
|
|
|
7
7
|
import time
|
|
8
8
|
import urllib
|
|
9
9
|
from collections import Counter
|
|
10
|
+
from dataclasses import dataclass
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from tempfile import TemporaryDirectory
|
|
12
13
|
from typing import (
|
|
@@ -27,6 +28,7 @@ from PIL import Image
|
|
|
27
28
|
from rich.console import Console
|
|
28
29
|
|
|
29
30
|
import darwin.datatypes as dt
|
|
31
|
+
from darwin.dataset.json_formatter import PrettyPrintError, pretty_print_json
|
|
30
32
|
from darwin.dataset.utils import (
|
|
31
33
|
sanitize_filename,
|
|
32
34
|
SUPPORTED_IMAGE_EXTENSIONS,
|
|
@@ -57,6 +59,7 @@ def download_all_images_from_annotations(
|
|
|
57
59
|
video_frames: bool = False,
|
|
58
60
|
force_slots: bool = False,
|
|
59
61
|
ignore_slots: bool = False,
|
|
62
|
+
pretty_print: bool = False,
|
|
60
63
|
) -> Tuple[Callable[[], Iterable[Any]], int]:
|
|
61
64
|
"""
|
|
62
65
|
Downloads all the images corresponding to a project.
|
|
@@ -82,13 +85,16 @@ def download_all_images_from_annotations(
|
|
|
82
85
|
force_slots: bool, default: False
|
|
83
86
|
Pulls all slots of items into deeper file structure ({prefix}/{item_name}/{slot_name}/{file_name})
|
|
84
87
|
If False, all multi-slotted items and items with slots containing multiple source files will be downloaded as the deeper file structure
|
|
88
|
+
pretty_print: bool, default: False
|
|
89
|
+
Preserves JSON tokens when adding local media paths and indents the result.
|
|
85
90
|
|
|
86
91
|
Returns
|
|
87
92
|
-------
|
|
88
93
|
generator : function
|
|
89
|
-
|
|
94
|
+
Factory for download callables. With ``pretty_print=True``, returns at most
|
|
95
|
+
one callable that downloads all media sequentially.
|
|
90
96
|
count : int
|
|
91
|
-
The files
|
|
97
|
+
The number of media files, which can exceed the number of callables.
|
|
92
98
|
|
|
93
99
|
Raises
|
|
94
100
|
------
|
|
@@ -112,6 +118,14 @@ def download_all_images_from_annotations(
|
|
|
112
118
|
if annotation is None:
|
|
113
119
|
continue
|
|
114
120
|
|
|
121
|
+
source_indices: Dict[Optional[str], List[Tuple[int, int]]] = {}
|
|
122
|
+
if pretty_print:
|
|
123
|
+
for slot_index, slot in enumerate(annotation.slots):
|
|
124
|
+
for source_index, source in enumerate(slot.source_files):
|
|
125
|
+
source_indices.setdefault(source.url, []).append(
|
|
126
|
+
(slot_index, source_index)
|
|
127
|
+
)
|
|
128
|
+
|
|
115
129
|
if not force_replace:
|
|
116
130
|
# Check the planned path for the image against the existing images
|
|
117
131
|
planned_image_paths = _get_planned_image_paths(
|
|
@@ -131,7 +145,9 @@ def download_all_images_from_annotations(
|
|
|
131
145
|
len(slot.source_files) > 1 for slot in annotation.slots
|
|
132
146
|
)
|
|
133
147
|
|
|
134
|
-
annotations_to_download_path.append(
|
|
148
|
+
annotations_to_download_path.append(
|
|
149
|
+
(annotation_path, force_slots_for_item, source_indices)
|
|
150
|
+
)
|
|
135
151
|
|
|
136
152
|
if remove_extra:
|
|
137
153
|
annotations = (
|
|
@@ -155,23 +171,100 @@ def download_all_images_from_annotations(
|
|
|
155
171
|
|
|
156
172
|
# Create the generator with the partial functions
|
|
157
173
|
download_functions: List = []
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
174
|
+
grouped_functions: List = []
|
|
175
|
+
for annotation_path, force_slots, source_indices in annotations_to_download_path:
|
|
176
|
+
file_download_functions = list(
|
|
177
|
+
lazy_download_image_from_annotation(
|
|
178
|
+
client,
|
|
179
|
+
annotation_path,
|
|
180
|
+
images_path,
|
|
181
|
+
annotation_format,
|
|
182
|
+
use_folders,
|
|
183
|
+
video_frames,
|
|
184
|
+
force_slots,
|
|
185
|
+
ignore_slots,
|
|
186
|
+
)
|
|
168
187
|
)
|
|
169
188
|
download_functions.extend(file_download_functions)
|
|
189
|
+
if pretty_print:
|
|
190
|
+
trace_functions = []
|
|
191
|
+
for function in file_download_functions:
|
|
192
|
+
if (
|
|
193
|
+
isinstance(function, functools.partial)
|
|
194
|
+
and function.func is _download_image_with_trace
|
|
195
|
+
):
|
|
196
|
+
trace_functions.append(function)
|
|
197
|
+
else:
|
|
198
|
+
grouped_functions.append(function)
|
|
199
|
+
if trace_functions:
|
|
200
|
+
grouped_functions.append(
|
|
201
|
+
_DownloadAnnotationMedia(
|
|
202
|
+
annotation_path, trace_functions, source_indices
|
|
203
|
+
)
|
|
204
|
+
)
|
|
170
205
|
|
|
171
206
|
if not use_folders:
|
|
172
207
|
_check_for_duplicate_local_filepaths(download_functions)
|
|
173
208
|
|
|
174
|
-
|
|
209
|
+
if pretty_print and grouped_functions:
|
|
210
|
+
grouped_functions = [_DownloadMediaSequentially(grouped_functions)]
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
lambda: grouped_functions if pretty_print else download_functions,
|
|
214
|
+
len(download_functions),
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _raise_media_errors(errors: List[Exception]) -> None:
|
|
219
|
+
if not errors:
|
|
220
|
+
return
|
|
221
|
+
if len(errors) == 1:
|
|
222
|
+
raise errors[0]
|
|
223
|
+
message = "\n".join(str(error) for error in errors)
|
|
224
|
+
for error in errors:
|
|
225
|
+
if isinstance(error, PrettyPrintError):
|
|
226
|
+
raise PrettyPrintError(message) from error
|
|
227
|
+
raise RuntimeError(message) from errors[0]
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@dataclass
|
|
231
|
+
class _DownloadMediaSequentially:
|
|
232
|
+
downloads: List[Callable[[], None]]
|
|
233
|
+
|
|
234
|
+
def __call__(self) -> None:
|
|
235
|
+
errors: List[Exception] = []
|
|
236
|
+
for download in self.downloads:
|
|
237
|
+
try:
|
|
238
|
+
download()
|
|
239
|
+
except Exception as error:
|
|
240
|
+
errors.append(error)
|
|
241
|
+
_raise_media_errors(errors)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@dataclass
|
|
245
|
+
class _DownloadAnnotationMedia:
|
|
246
|
+
annotation_path: Path
|
|
247
|
+
downloads: List[functools.partial]
|
|
248
|
+
source_indices: Dict[Optional[str], List[Tuple[int, int]]]
|
|
249
|
+
|
|
250
|
+
def __call__(self) -> None:
|
|
251
|
+
local_paths: Dict[Tuple[int, int], str] = {}
|
|
252
|
+
errors: List[Exception] = []
|
|
253
|
+
for download in self.downloads:
|
|
254
|
+
annotation, url, path, client = download.args
|
|
255
|
+
try:
|
|
256
|
+
_download_image(url, path, client)
|
|
257
|
+
except Exception as error:
|
|
258
|
+
errors.append(error)
|
|
259
|
+
else:
|
|
260
|
+
if annotation.version.major != 1:
|
|
261
|
+
for index in self.source_indices.get(url, []):
|
|
262
|
+
local_paths[index] = str(path)
|
|
263
|
+
try:
|
|
264
|
+
pretty_print_json(self.annotation_path, local_paths=local_paths)
|
|
265
|
+
except PrettyPrintError as error:
|
|
266
|
+
errors.append(error)
|
|
267
|
+
_raise_media_errors(errors)
|
|
175
268
|
|
|
176
269
|
|
|
177
270
|
def lazy_download_image_from_annotation(
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
"""Format JSON with bounded buffers without changing string or number tokens."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import stat
|
|
7
|
+
import tempfile
|
|
8
|
+
from contextlib import suppress
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from enum import Enum, auto
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import IO, Mapping, Optional, Protocol
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PrettyPrintError(Exception):
|
|
16
|
+
"""A local JSON file could not be formatted and retains its original bytes."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_CHUNK_SIZE = 64 * 1024
|
|
20
|
+
_STRING_SPECIAL = re.compile(r'["\\\x00-\x1f]')
|
|
21
|
+
_WHITESPACE = re.compile(r"[ \t\r\n]*")
|
|
22
|
+
_DIGITS = re.compile(r"[0-9]*")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _Writer(Protocol):
|
|
26
|
+
def write(self, value: str) -> object: ...
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class _Reader:
|
|
30
|
+
def __init__(self, source: IO[str]) -> None:
|
|
31
|
+
self.source = source
|
|
32
|
+
self.buffer = ""
|
|
33
|
+
self.offset = 0
|
|
34
|
+
|
|
35
|
+
def peek(self) -> str:
|
|
36
|
+
if self.offset == len(self.buffer):
|
|
37
|
+
self.buffer = self.source.read(_CHUNK_SIZE)
|
|
38
|
+
self.offset = 0
|
|
39
|
+
return self.buffer[self.offset : self.offset + 1]
|
|
40
|
+
|
|
41
|
+
def take(self) -> str:
|
|
42
|
+
char = self.peek()
|
|
43
|
+
self.offset += bool(char)
|
|
44
|
+
return char
|
|
45
|
+
|
|
46
|
+
def skip_whitespace(self) -> None:
|
|
47
|
+
while self.peek():
|
|
48
|
+
match = _WHITESPACE.match(self.buffer, self.offset)
|
|
49
|
+
assert match is not None
|
|
50
|
+
self.offset = match.end()
|
|
51
|
+
if self.offset < len(self.buffer):
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
def copy_digits(self, output: _Writer) -> None:
|
|
55
|
+
while self.peek():
|
|
56
|
+
match = _DIGITS.match(self.buffer, self.offset)
|
|
57
|
+
assert match is not None
|
|
58
|
+
end = match.end()
|
|
59
|
+
output.write(self.buffer[self.offset : end])
|
|
60
|
+
self.offset = end
|
|
61
|
+
if end < len(self.buffer):
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _copy_string(reader: _Reader, output: _Writer) -> None:
|
|
66
|
+
output.write(reader.take())
|
|
67
|
+
while reader.peek():
|
|
68
|
+
match = _STRING_SPECIAL.search(reader.buffer, reader.offset)
|
|
69
|
+
end = match.start() if match else len(reader.buffer)
|
|
70
|
+
output.write(reader.buffer[reader.offset : end])
|
|
71
|
+
reader.offset = end
|
|
72
|
+
if match is None:
|
|
73
|
+
continue
|
|
74
|
+
char = reader.take()
|
|
75
|
+
if char == '"':
|
|
76
|
+
output.write(char)
|
|
77
|
+
return
|
|
78
|
+
if char != "\\":
|
|
79
|
+
raise ValueError("Unescaped control character in string")
|
|
80
|
+
escape = reader.take()
|
|
81
|
+
if escape == "u":
|
|
82
|
+
output.write("\\u")
|
|
83
|
+
for _ in range(4):
|
|
84
|
+
digit = reader.take()
|
|
85
|
+
if not digit or digit not in "0123456789abcdefABCDEF":
|
|
86
|
+
raise ValueError("Invalid Unicode escape in string")
|
|
87
|
+
output.write(digit)
|
|
88
|
+
elif escape and escape in '"\\/bfnrt':
|
|
89
|
+
output.write("\\" + escape)
|
|
90
|
+
else:
|
|
91
|
+
raise ValueError("Invalid escape in string")
|
|
92
|
+
raise ValueError("Unterminated string")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _copy_number(reader: _Reader, output: _Writer) -> None:
|
|
96
|
+
if reader.peek() == "-":
|
|
97
|
+
output.write(reader.take())
|
|
98
|
+
char = reader.peek()
|
|
99
|
+
if char == "0":
|
|
100
|
+
output.write(reader.take())
|
|
101
|
+
elif char and char in "123456789":
|
|
102
|
+
reader.copy_digits(output)
|
|
103
|
+
else:
|
|
104
|
+
raise ValueError("Expected a digit in number")
|
|
105
|
+
if reader.peek() == ".":
|
|
106
|
+
output.write(reader.take())
|
|
107
|
+
if not reader.peek() or reader.peek() not in "0123456789":
|
|
108
|
+
raise ValueError("Expected a digit after decimal point")
|
|
109
|
+
reader.copy_digits(output)
|
|
110
|
+
if reader.peek() and reader.peek() in "eE":
|
|
111
|
+
output.write(reader.take())
|
|
112
|
+
if reader.peek() and reader.peek() in "+-":
|
|
113
|
+
output.write(reader.take())
|
|
114
|
+
if not reader.peek() or reader.peek() not in "0123456789":
|
|
115
|
+
raise ValueError("Expected a digit in exponent")
|
|
116
|
+
reader.copy_digits(output)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class _State(Enum):
|
|
120
|
+
VALUE = auto()
|
|
121
|
+
ARRAY_FIRST = auto()
|
|
122
|
+
ARRAY_VALUE = auto()
|
|
123
|
+
OBJECT_FIRST = auto()
|
|
124
|
+
OBJECT_KEY = auto()
|
|
125
|
+
COLON = auto()
|
|
126
|
+
OBJECT_VALUE = auto()
|
|
127
|
+
COMMA_OR_END = auto()
|
|
128
|
+
COMPLETE = auto()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class _Location(Enum):
|
|
132
|
+
OTHER = auto()
|
|
133
|
+
ROOT = auto()
|
|
134
|
+
ITEM = auto()
|
|
135
|
+
SLOTS = auto()
|
|
136
|
+
SLOT = auto()
|
|
137
|
+
SOURCES = auto()
|
|
138
|
+
SOURCE = auto()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class _Container:
|
|
143
|
+
closing: str
|
|
144
|
+
state: _State
|
|
145
|
+
location: _Location = _Location.OTHER
|
|
146
|
+
slot_index: int = -1
|
|
147
|
+
index: int = -1
|
|
148
|
+
key: str = ""
|
|
149
|
+
has_local_path: bool = False
|
|
150
|
+
discard: bool = False
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class _KeyWriter:
|
|
154
|
+
def __init__(self, output: _Writer) -> None:
|
|
155
|
+
self.output = output
|
|
156
|
+
self.token = ""
|
|
157
|
+
self.too_long = False
|
|
158
|
+
|
|
159
|
+
def write(self, value: str) -> None:
|
|
160
|
+
self.output.write(value)
|
|
161
|
+
if not self.too_long:
|
|
162
|
+
if len(self.token) + len(value) > 80:
|
|
163
|
+
self.token = ""
|
|
164
|
+
self.too_long = True
|
|
165
|
+
else:
|
|
166
|
+
self.token += value
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class _DiscardWriter:
|
|
170
|
+
def write(self, value: str) -> None:
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _child_location(current: _Container, opening: str) -> _Location:
|
|
175
|
+
if not current.closing and opening == "{":
|
|
176
|
+
return _Location.ROOT
|
|
177
|
+
if current.location == _Location.OTHER:
|
|
178
|
+
return _Location.OTHER
|
|
179
|
+
if current.location == _Location.SLOTS and opening == "{":
|
|
180
|
+
return _Location.SLOT
|
|
181
|
+
if current.location == _Location.SOURCES and opening == "{":
|
|
182
|
+
return _Location.SOURCE
|
|
183
|
+
return {
|
|
184
|
+
(_Location.ROOT, "item", "{"): _Location.ITEM,
|
|
185
|
+
(_Location.ITEM, "slots", "["): _Location.SLOTS,
|
|
186
|
+
(_Location.SLOT, "source_files", "["): _Location.SOURCES,
|
|
187
|
+
}.get((current.location, current.key, opening), _Location.OTHER)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _format_json(
|
|
191
|
+
source: IO[str],
|
|
192
|
+
destination: _Writer,
|
|
193
|
+
local_paths: Mapping[tuple[int, int], str],
|
|
194
|
+
) -> None:
|
|
195
|
+
reader = _Reader(source)
|
|
196
|
+
containers = [_Container("", _State.VALUE)]
|
|
197
|
+
discard = _DiscardWriter()
|
|
198
|
+
while containers:
|
|
199
|
+
reader.skip_whitespace()
|
|
200
|
+
char = reader.peek()
|
|
201
|
+
current = containers[-1]
|
|
202
|
+
output = discard if current.discard else destination
|
|
203
|
+
state = current.state
|
|
204
|
+
local_path = (
|
|
205
|
+
local_paths.get((current.slot_index, current.index))
|
|
206
|
+
if current.location == _Location.SOURCE
|
|
207
|
+
else None
|
|
208
|
+
)
|
|
209
|
+
if state == _State.COMPLETE:
|
|
210
|
+
if char:
|
|
211
|
+
raise ValueError("Unexpected content after JSON value")
|
|
212
|
+
output.write("\n")
|
|
213
|
+
return
|
|
214
|
+
if not char:
|
|
215
|
+
raise ValueError("Unexpected end of JSON")
|
|
216
|
+
|
|
217
|
+
if (
|
|
218
|
+
local_path is not None
|
|
219
|
+
and not current.has_local_path
|
|
220
|
+
and char == "}"
|
|
221
|
+
and state in (_State.OBJECT_FIRST, _State.COMMA_OR_END)
|
|
222
|
+
):
|
|
223
|
+
if state == _State.COMMA_OR_END:
|
|
224
|
+
output.write(",")
|
|
225
|
+
output.write("\n" + " " * (len(containers) - 1))
|
|
226
|
+
output.write('"local_path": ' + json.dumps(local_path))
|
|
227
|
+
current.has_local_path = True
|
|
228
|
+
state = current.state = _State.COMMA_OR_END
|
|
229
|
+
|
|
230
|
+
if state == _State.COMMA_OR_END:
|
|
231
|
+
if char == ",":
|
|
232
|
+
output.write(reader.take())
|
|
233
|
+
current.state = (
|
|
234
|
+
_State.OBJECT_KEY if current.closing == "}" else _State.ARRAY_VALUE
|
|
235
|
+
)
|
|
236
|
+
continue
|
|
237
|
+
if char != current.closing:
|
|
238
|
+
raise ValueError("Expected a comma or the end of a container")
|
|
239
|
+
output.write("\n" + " " * (len(containers) - 2))
|
|
240
|
+
elif state in (_State.ARRAY_FIRST, _State.OBJECT_FIRST):
|
|
241
|
+
if char != current.closing:
|
|
242
|
+
current.state = (
|
|
243
|
+
_State.OBJECT_KEY if current.closing == "}" else _State.ARRAY_VALUE
|
|
244
|
+
)
|
|
245
|
+
continue
|
|
246
|
+
elif state == _State.COLON:
|
|
247
|
+
if char != ":":
|
|
248
|
+
raise ValueError("Expected a colon after object key")
|
|
249
|
+
reader.take()
|
|
250
|
+
output.write(": ")
|
|
251
|
+
current.state = _State.OBJECT_VALUE
|
|
252
|
+
continue
|
|
253
|
+
elif state == _State.OBJECT_KEY:
|
|
254
|
+
if char != '"':
|
|
255
|
+
raise ValueError("Expected a quoted object key")
|
|
256
|
+
output.write("\n" + " " * (len(containers) - 1))
|
|
257
|
+
if local_paths and current.location in (
|
|
258
|
+
_Location.ROOT,
|
|
259
|
+
_Location.ITEM,
|
|
260
|
+
_Location.SLOT,
|
|
261
|
+
_Location.SOURCE,
|
|
262
|
+
):
|
|
263
|
+
key_writer = _KeyWriter(output)
|
|
264
|
+
_copy_string(reader, key_writer)
|
|
265
|
+
current.key = (
|
|
266
|
+
json.loads(key_writer.token) if not key_writer.too_long else ""
|
|
267
|
+
)
|
|
268
|
+
else:
|
|
269
|
+
_copy_string(reader, output)
|
|
270
|
+
current.state = _State.COLON
|
|
271
|
+
continue
|
|
272
|
+
else:
|
|
273
|
+
if state == _State.ARRAY_VALUE:
|
|
274
|
+
output.write("\n" + " " * (len(containers) - 1))
|
|
275
|
+
current.index += 1
|
|
276
|
+
replace_path = local_path is not None and current.key == "local_path"
|
|
277
|
+
if replace_path:
|
|
278
|
+
output.write(json.dumps(local_path))
|
|
279
|
+
current.has_local_path = True
|
|
280
|
+
output = discard
|
|
281
|
+
current.state = _State.COMMA_OR_END if current.closing else _State.COMPLETE
|
|
282
|
+
if char in "[{":
|
|
283
|
+
output.write(reader.take())
|
|
284
|
+
containers.append(
|
|
285
|
+
_Container(
|
|
286
|
+
"}" if char == "{" else "]",
|
|
287
|
+
_State.OBJECT_FIRST if char == "{" else _State.ARRAY_FIRST,
|
|
288
|
+
(
|
|
289
|
+
_child_location(current, char)
|
|
290
|
+
if local_paths
|
|
291
|
+
else _Location.OTHER
|
|
292
|
+
),
|
|
293
|
+
(
|
|
294
|
+
current.index
|
|
295
|
+
if current.location == _Location.SLOTS
|
|
296
|
+
else current.slot_index
|
|
297
|
+
),
|
|
298
|
+
current.index if current.location == _Location.SOURCES else -1,
|
|
299
|
+
discard=current.discard or replace_path,
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
elif char == '"':
|
|
303
|
+
_copy_string(reader, output)
|
|
304
|
+
elif char in "-0123456789":
|
|
305
|
+
_copy_number(reader, output)
|
|
306
|
+
elif char in "tfn":
|
|
307
|
+
literal = {"t": "true", "f": "false", "n": "null"}[char]
|
|
308
|
+
for expected in literal:
|
|
309
|
+
if reader.take() != expected:
|
|
310
|
+
raise ValueError("Invalid JSON literal")
|
|
311
|
+
output.write(literal)
|
|
312
|
+
else:
|
|
313
|
+
raise ValueError("Expected a JSON value")
|
|
314
|
+
continue
|
|
315
|
+
|
|
316
|
+
output.write(reader.take())
|
|
317
|
+
containers.pop()
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def pretty_print_json(
|
|
321
|
+
path: Path, *, local_paths: Optional[Mapping[tuple[int, int], str]] = None
|
|
322
|
+
) -> None:
|
|
323
|
+
"""Atomically indent one UTF-8 JSON file while preserving all value tokens.
|
|
324
|
+
|
|
325
|
+
Uses fixed input/output buffers plus container nesting state. The temporary
|
|
326
|
+
output requires space next to the original file until replacement succeeds.
|
|
327
|
+
``local_paths`` updates only indexed ``item.slots[].source_files[]`` paths.
|
|
328
|
+
"""
|
|
329
|
+
temporary_path = None
|
|
330
|
+
try:
|
|
331
|
+
mode = stat.S_IMODE(path.stat().st_mode)
|
|
332
|
+
with path.open("r", encoding="utf-8", newline="") as source:
|
|
333
|
+
with tempfile.NamedTemporaryFile(
|
|
334
|
+
mode="w",
|
|
335
|
+
encoding="utf-8",
|
|
336
|
+
newline="\n",
|
|
337
|
+
dir=path.parent,
|
|
338
|
+
prefix=".darwin-pretty-print-",
|
|
339
|
+
suffix=".tmp",
|
|
340
|
+
delete=False,
|
|
341
|
+
) as output:
|
|
342
|
+
temporary_path = Path(output.name)
|
|
343
|
+
_format_json(source, output.file, local_paths or {})
|
|
344
|
+
temporary_path.chmod(mode)
|
|
345
|
+
os.replace(temporary_path, path)
|
|
346
|
+
except (OSError, UnicodeError, ValueError) as error:
|
|
347
|
+
raise PrettyPrintError(
|
|
348
|
+
f"Could not pretty-print '{path}': {error}. The original file is unchanged."
|
|
349
|
+
) from error
|
|
350
|
+
finally:
|
|
351
|
+
if temporary_path is not None:
|
|
352
|
+
with suppress(OSError):
|
|
353
|
+
temporary_path.unlink(missing_ok=True)
|