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.
Files changed (236) hide show
  1. {darwin_py-3.6.2 → darwin_py-3.6.4}/PKG-INFO +18 -1
  2. {darwin_py-3.6.2 → darwin_py-3.6.4}/README.md +17 -0
  3. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/cli.py +1 -0
  4. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/cli_functions.py +7 -0
  5. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/download_manager.py +107 -14
  6. darwin_py-3.6.4/darwin/dataset/json_formatter.py +353 -0
  7. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/remote_dataset.py +64 -10
  8. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/utils.py +15 -6
  9. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/datatypes.py +44 -0
  10. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/importer.py +19 -9
  11. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/options.py +10 -0
  12. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/utils/utils.py +10 -1
  13. darwin_py-3.6.4/darwin/version/__init__.py +1 -0
  14. {darwin_py-3.6.2 → darwin_py-3.6.4}/pyproject.toml +1 -1
  15. darwin_py-3.6.2/darwin/version/__init__.py +0 -1
  16. {darwin_py-3.6.2 → darwin_py-3.6.4}/LICENSE +0 -0
  17. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/__init__.py +0 -0
  18. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/backend_v2.py +0 -0
  19. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/client.py +0 -0
  20. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/config.py +0 -0
  21. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/__init__.py +0 -0
  22. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/identifier.py +0 -0
  23. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/local_dataset.py +0 -0
  24. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/release.py +0 -0
  25. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/remote_dataset_v2.py +0 -0
  26. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/split_manager.py +0 -0
  27. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/storage_uploader.py +0 -0
  28. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/dataset/upload_manager.py +0 -0
  29. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/doc_enum.py +0 -0
  30. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exceptions.py +0 -0
  31. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/__init__.py +0 -0
  32. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/exporter.py +0 -0
  33. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/__init__.py +0 -0
  34. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/coco.py +0 -0
  35. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/cvat.py +0 -0
  36. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/darwin.py +0 -0
  37. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/dataloop.py +0 -0
  38. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/helpers/__init__.py +0 -0
  39. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/helpers/yolo_class_builder.py +0 -0
  40. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/instance_mask.py +0 -0
  41. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/mask.py +0 -0
  42. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/nifti.py +0 -0
  43. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/numpy_encoder.py +0 -0
  44. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/pascalvoc.py +0 -0
  45. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/semantic_mask.py +0 -0
  46. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/semantic_mask_grey.py +0 -0
  47. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/semantic_mask_index.py +0 -0
  48. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/yolo.py +0 -0
  49. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/exporter/formats/yolo_segmented.py +0 -0
  50. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/extractor/__init__.py +0 -0
  51. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/extractor/video.py +0 -0
  52. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/__init__.py +0 -0
  53. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/__init__.py +0 -0
  54. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/client.py +0 -0
  55. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/datasets/__init__.py +0 -0
  56. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/datasets/create_dataset.py +0 -0
  57. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/datasets/get_dataset.py +0 -0
  58. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/datasets/list_datasets.py +0 -0
  59. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/datasets/remove_dataset.py +0 -0
  60. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/__init__.py +0 -0
  61. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/archive_items.py +0 -0
  62. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/assign_items.py +0 -0
  63. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/delete_items.py +0 -0
  64. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/get.py +0 -0
  65. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/move_items.py +0 -0
  66. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/move_items_to_folder.py +0 -0
  67. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/restore_items.py +0 -0
  68. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/set_item_layout.py +0 -0
  69. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/set_item_priority.py +0 -0
  70. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/set_stage_to_items.py +0 -0
  71. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/tag_items.py +0 -0
  72. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/untag_items.py +0 -0
  73. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/items/uploads.py +0 -0
  74. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/properties/__init__.py +0 -0
  75. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/properties/create.py +0 -0
  76. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/properties/get.py +0 -0
  77. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/properties/update.py +0 -0
  78. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/team/__init__.py +0 -0
  79. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/team/get_raw.py +0 -0
  80. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/team/get_team.py +0 -0
  81. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/types/__init__.py +0 -0
  82. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/types/common.py +0 -0
  83. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/types/item.py +0 -0
  84. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/types/query.py +0 -0
  85. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/utils/pathutils.py +0 -0
  86. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/workflows/__init__.py +0 -0
  87. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/workflows/get_workflow.py +0 -0
  88. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/workflows/get_workflows.py +0 -0
  89. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/core/workflows/list_workflows.py +0 -0
  90. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/__init__.py +0 -0
  91. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/advanced_filters.py +0 -0
  92. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/darwinV2.py +0 -0
  93. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/dataset.py +0 -0
  94. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/item.py +0 -0
  95. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/page.py +0 -0
  96. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/properties.py +0 -0
  97. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/pydantic_base.py +0 -0
  98. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/release.py +0 -0
  99. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/sorting.py +0 -0
  100. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/team.py +0 -0
  101. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/team_member_role.py +0 -0
  102. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/typing.py +0 -0
  103. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/validators.py +0 -0
  104. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/data_objects/workflow.py +0 -0
  105. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/exceptions.py +0 -0
  106. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/helpers/__init__.py +0 -0
  107. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/helpers/assertion.py +0 -0
  108. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/helpers/exception_handler.py +0 -0
  109. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/__init__.py +0 -0
  110. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/client.py +0 -0
  111. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/__init__.py +0 -0
  112. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/base.py +0 -0
  113. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/dataset.py +0 -0
  114. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/item.py +0 -0
  115. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/stage.py +0 -0
  116. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/team.py +0 -0
  117. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/team_member.py +0 -0
  118. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/v7_id.py +0 -0
  119. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/objects/workflow.py +0 -0
  120. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/__init__.py +0 -0
  121. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/dataset.py +0 -0
  122. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/item.py +0 -0
  123. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/item_id.py +0 -0
  124. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/stage.py +0 -0
  125. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/team_member.py +0 -0
  126. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/queries/workflow.py +0 -0
  127. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/meta/types/__init__.py +0 -0
  128. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/pydantic_base.py +0 -0
  129. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/__init__.py +0 -0
  130. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/__init__.py +0 -0
  131. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/__init__.py +0 -0
  132. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/fixtures.py +0 -0
  133. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/test_create_dataset.py +0 -0
  134. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/test_delete_dataset.py +0 -0
  135. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/test_get_dataset.py +0 -0
  136. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/datasets/test_list_datasets.py +0 -0
  137. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/fixtures.py +0 -0
  138. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/__init__.py +0 -0
  139. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/fixtures.py +0 -0
  140. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_archive_items.py +0 -0
  141. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_assign_items.py +0 -0
  142. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_delete_items.py +0 -0
  143. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_get_items.py +0 -0
  144. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_item_data_object.py +0 -0
  145. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_move_items.py +0 -0
  146. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_move_items_to_folder.py +0 -0
  147. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_restore_items.py +0 -0
  148. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_set_item_layout.py +0 -0
  149. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_set_priority.py +0 -0
  150. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_set_stage_to_items.py +0 -0
  151. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_tag_items.py +0 -0
  152. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_untag_items.py +0 -0
  153. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/items/test_upload_items.py +0 -0
  154. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/properties/__init__.py +0 -0
  155. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/properties/test_create.py +0 -0
  156. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/properties/test_get.py +0 -0
  157. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/properties/test_update.py +0 -0
  158. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/test_client.py +0 -0
  159. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/test_query.py +0 -0
  160. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/types/__init__.py +0 -0
  161. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/types/test_querystring.py +0 -0
  162. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/workflows/__init__.py +0 -0
  163. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/workflows/test_get_workflow.py +0 -0
  164. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/workflows/test_get_workflows.py +0 -0
  165. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/core/workflows/test_list_workflows.py +0 -0
  166. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data/.v7/metadata.json +0 -0
  167. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data/.v7/metadata_no_item_level_properties.json +0 -0
  168. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data/.v7/metadata_with_item_level_properties.json +0 -0
  169. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data/base_annotation.json +0 -0
  170. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/__init__.py +0 -0
  171. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/fixtures.py +0 -0
  172. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_advanced_filters.py +0 -0
  173. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_darwin.py +0 -0
  174. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_general_darwin_objects.py +0 -0
  175. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_page.py +0 -0
  176. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_properties.py +0 -0
  177. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_sorting.py +0 -0
  178. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_team.py +0 -0
  179. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/test_validators.py +0 -0
  180. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/__init__.py +0 -0
  181. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/dataset.json +0 -0
  182. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/edge.json +0 -0
  183. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/stage.json +0 -0
  184. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/stage_config.json +0 -0
  185. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/user.json +0 -0
  186. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/data/workflow.json +0 -0
  187. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/invalidvaluefortest.py +0 -0
  188. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_wfdataset.py +0 -0
  189. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_wfedge.py +0 -0
  190. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_wfstage.py +0 -0
  191. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_wfstage_config.py +0 -0
  192. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_wfuser.py +0 -0
  193. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/data_objects/workflow/test_workflow.py +0 -0
  194. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/fixtures.py +0 -0
  195. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/__init__.py +0 -0
  196. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/fixtures.py +0 -0
  197. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/fixtures.py +0 -0
  198. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_datasetmeta.py +0 -0
  199. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_itemmeta.py +0 -0
  200. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_stagemeta.py +0 -0
  201. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_teammeta.py +0 -0
  202. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_v7_id.py +0 -0
  203. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/objects/test_workflowmeta.py +0 -0
  204. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_dataset.py +0 -0
  205. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_item.py +0 -0
  206. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_item_id.py +0 -0
  207. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_stage.py +0 -0
  208. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_team_member.py +0 -0
  209. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/queries/test_workflow.py +0 -0
  210. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/future/tests/meta/test_client.py +0 -0
  211. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/__init__.py +0 -0
  212. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/__init__.py +0 -0
  213. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/coco.py +0 -0
  214. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/coco_masks.py +0 -0
  215. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/csv_tags.py +0 -0
  216. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/csv_tags_video.py +0 -0
  217. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/darwin.py +0 -0
  218. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/dataloop.py +0 -0
  219. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/labelbox.py +0 -0
  220. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/labelbox_schemas.py +0 -0
  221. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/nifti.py +0 -0
  222. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/nifti_schemas.py +0 -0
  223. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/pascal_voc.py +0 -0
  224. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/superannotate.py +0 -0
  225. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/importer/formats/superannotate_schemas.py +0 -0
  226. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/item.py +0 -0
  227. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/item_sorter.py +0 -0
  228. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/path_utils.py +0 -0
  229. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/torch/__init__.py +0 -0
  230. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/torch/dataset.py +0 -0
  231. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/torch/transforms.py +0 -0
  232. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/torch/utils.py +0 -0
  233. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/utils/__init__.py +0 -0
  234. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/utils/flatten_list.py +0 -0
  235. {darwin_py-3.6.2 → darwin_py-3.6.4}/darwin/utils/get_item_count.py +0 -0
  236. {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.2
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
@@ -176,6 +176,7 @@ def _run(args: Namespace, parser: ArgumentParser) -> None:
176
176
  args.retry_interval,
177
177
  args.team,
178
178
  multi_processed=not args.no_multi_processed,
179
+ pretty_print=args.pretty_print,
179
180
  )
180
181
  elif args.action == "import":
181
182
  f.dataset_import(
@@ -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
- Generator for doing the actual downloads
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 count
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((annotation_path, force_slots_for_item))
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
- for annotation_path, force_slots in annotations_to_download_path:
159
- file_download_functions = lazy_download_image_from_annotation(
160
- client,
161
- annotation_path,
162
- images_path,
163
- annotation_format,
164
- use_folders,
165
- video_frames,
166
- force_slots,
167
- ignore_slots,
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
- return lambda: download_functions, len(download_functions)
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)