superannotate 4.5.9__tar.gz → 4.6.1.dev1__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 (145) hide show
  1. {superannotate-4.5.9/src/superannotate.egg-info → superannotate-4.6.1.dev1}/PKG-INFO +7 -34
  2. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/README.rst +4 -31
  3. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/requirements.txt +2 -2
  4. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/__init__.py +5 -1
  5. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_converter.py +1 -1
  6. superannotate-4.6.1.dev1/src/superannotate/lib/app/interface/base_interface.py +395 -0
  7. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/cli_interface.py +3 -3
  8. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/sdk_interface.py +296 -167
  9. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/__init__.py +52 -5
  10. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/__init__.py +6 -1
  11. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/base.py +19 -3
  12. superannotate-4.6.1.dev1/src/superannotate/lib/core/entities/context.py +106 -0
  13. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/project.py +14 -1
  14. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/work_managament.py +1 -1
  15. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/exceptions.py +12 -0
  16. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/service_types.py +4 -0
  17. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/serviceproviders.py +90 -43
  18. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/__init__.py +1 -0
  19. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/annotations.py +93 -68
  20. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/images.py +12 -8
  21. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/items.py +10 -7
  22. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/projects.py +23 -18
  23. superannotate-4.6.1.dev1/src/superannotate/lib/core/usecases/work_management.py +397 -0
  24. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/annotation_adapter.py +3 -3
  25. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/controller.py +152 -71
  26. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/serviceprovider.py +35 -14
  27. superannotate-4.6.1.dev1/src/superannotate/lib/infrastructure/services/auth.py +144 -0
  28. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/http_client.py +39 -20
  29. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/work_management.py +82 -40
  30. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/utils.py +56 -14
  31. {superannotate-4.5.9 → superannotate-4.6.1.dev1/src/superannotate.egg-info}/PKG-INFO +7 -34
  32. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate.egg-info/SOURCES.txt +3 -0
  33. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate.egg-info/requires.txt +2 -2
  34. superannotate-4.5.9/src/superannotate/lib/app/interface/base_interface.py +0 -252
  35. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/LICENSE +0 -0
  36. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/MANIFEST.in +0 -0
  37. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/setup.cfg +0 -0
  38. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/setup.py +0 -0
  39. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/__init__.py +0 -0
  40. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/__init__.py +0 -0
  41. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/analytics/__init__.py +0 -0
  42. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/analytics/aggregators.py +0 -0
  43. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/analytics/common.py +0 -0
  44. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/bin/__init__.py +0 -0
  45. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/bin/superannotate.py +0 -0
  46. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/common.py +0 -0
  47. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/exceptions.py +0 -0
  48. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/helpers.py +0 -0
  49. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/__init__.py +0 -0
  50. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/conversion.py +0 -0
  51. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/__init__.py +0 -0
  52. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/baseStrategy.py +0 -0
  53. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/coco_converters/__init__.py +0 -0
  54. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_api.py +0 -0
  55. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_strategies.py +0 -0
  56. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_to_sa_vector.py +0 -0
  57. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/coco_converters/sa_vector_to_coco.py +0 -0
  58. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/converters.py +0 -0
  59. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/__init__.py +0 -0
  60. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/dataloop_helper.py +0 -0
  61. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/dataloop_strategies.py +0 -0
  62. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/dataloop_to_sa_vector.py +0 -0
  63. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/googlecloud_converters/__init__.py +0 -0
  64. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/googlecloud_converters/googlecloud_strategies.py +0 -0
  65. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/googlecloud_converters/googlecloud_to_sa_vector.py +0 -0
  66. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/__init__.py +0 -0
  67. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_helper.py +0 -0
  68. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_to_sa_vector.py +0 -0
  69. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/sa_json_helper.py +0 -0
  70. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/__init__.py +0 -0
  71. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/sagemaker_strategies.py +0 -0
  72. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/sagemaker_to_sa_vector.py +0 -0
  73. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/__init__.py +0 -0
  74. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_helper.py +0 -0
  75. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_strategies.py +0 -0
  76. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_to_sa_vector.py +0 -0
  77. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vgg_converters/__init__.py +0 -0
  78. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vgg_converters/vgg_helper.py +0 -0
  79. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vgg_converters/vgg_strategies.py +0 -0
  80. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vgg_converters/vgg_to_sa_vector.py +0 -0
  81. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/voc_converters/__init__.py +0 -0
  82. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_helper.py +0 -0
  83. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_strategies.py +0 -0
  84. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_to_sa_pixel.py +0 -0
  85. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_to_sa_vector.py +0 -0
  86. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vott_converters/__init__.py +0 -0
  87. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vott_converters/vott_strategies.py +0 -0
  88. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vott_converters/vott_to_sa_vector.py +0 -0
  89. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/yolo_converters/__init__.py +0 -0
  90. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/yolo_converters/yolo_strategies.py +0 -0
  91. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/yolo_converters/yolo_to_sa_vector.py +0 -0
  92. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/export_from_sa_conversions.py +0 -0
  93. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/import_to_sa_conversions.py +0 -0
  94. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/__init__.py +0 -0
  95. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/responses.py +0 -0
  96. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/sdk/__init__.py +0 -0
  97. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/sdk/folders.py +0 -0
  98. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/sdk/project.py +0 -0
  99. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/types.py +0 -0
  100. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/serializers.py +0 -0
  101. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/base_usecases.py +0 -0
  102. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/conditions.py +0 -0
  103. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/config.py +0 -0
  104. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/classes.py +0 -0
  105. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/filters.py +0 -0
  106. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/folder.py +0 -0
  107. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/integrations.py +0 -0
  108. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/items.py +0 -0
  109. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/multimodal_form.py +0 -0
  110. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/project_entities.py +0 -0
  111. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/enums.py +0 -0
  112. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/jsx_conditions.py +0 -0
  113. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/plugin.py +0 -0
  114. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/reporter.py +0 -0
  115. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/repositories.py +0 -0
  116. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/response.py +0 -0
  117. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/types.py +0 -0
  118. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/base.py +0 -0
  119. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/classes.py +0 -0
  120. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/custom_fields.py +0 -0
  121. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/folders.py +0 -0
  122. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/integrations.py +0 -0
  123. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/models.py +0 -0
  124. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/utils.py +0 -0
  125. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/video_convertor.py +0 -0
  126. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/__init__.py +0 -0
  127. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/custom_entities.py +0 -0
  128. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/helpers.py +0 -0
  129. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/query_builder.py +0 -0
  130. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/repositories.py +0 -0
  131. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/__init__.py +0 -0
  132. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/annotation.py +0 -0
  133. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/annotation_class.py +0 -0
  134. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/explore.py +0 -0
  135. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/folder.py +0 -0
  136. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/integration.py +0 -0
  137. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/item.py +0 -0
  138. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/item_service.py +0 -0
  139. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/project.py +0 -0
  140. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/telemetry_scoring.py +0 -0
  141. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/stream_data_handler.py +0 -0
  142. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/validators.py +0 -0
  143. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate.egg-info/dependency_links.txt +0 -0
  144. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate.egg-info/entry_points.txt +0 -0
  145. {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: superannotate
3
- Version: 4.5.9
3
+ Version: 4.6.1.dev1
4
4
  Summary: Python SDK to SuperAnnotate platform
5
5
  Home-page: https://github.com/superannotateai/superannotate-python-sdk
6
6
  Author: SuperAnnotate AI
@@ -19,12 +19,12 @@ Description-Content-Type: text/x-rst
19
19
  License-File: LICENSE
20
20
  Requires-Dist: pydantic~=2.5
21
21
  Requires-Dist: pydantic-extra-types~=2.11
22
- Requires-Dist: aiohttp~=3.8
22
+ Requires-Dist: aiohttp~=3.14
23
23
  Requires-Dist: boto3~=1.42
24
24
  Requires-Dist: opencv-python-headless~=4.7
25
25
  Requires-Dist: plotly~=6.5
26
26
  Requires-Dist: pandas~=2.0
27
- Requires-Dist: pillow~=12.1
27
+ Requires-Dist: pillow~=12.3
28
28
  Requires-Dist: tqdm~=4.66
29
29
  Requires-Dist: requests~=2.33
30
30
  Requires-Dist: aiofiles~=25.1
@@ -66,7 +66,7 @@ Authentication
66
66
  # by environment variable SA_TOKEN
67
67
  sa_client = SAClient()
68
68
  # by token
69
- sa_client = SAClient(token='<team token>')
69
+ sa_client = SAClient(token='<API key>')
70
70
  # by config file
71
71
  # default path is ~/.superannotate/config.ini
72
72
  sa_client = SAClient(config_path='~/.superannotate/dev_config.ini')
@@ -77,8 +77,9 @@ config.ini example
77
77
  .. code-block:: python
78
78
 
79
79
  [DEFAULT]
80
- SA_TOKEN = <Token>
80
+ SA_TOKEN = <API key>
81
81
  LOGGING_LEVEL = INFO
82
+ LOGGING_PATH = /Users/username/data/superannotate_logs
82
83
 
83
84
 
84
85
  Using superannotate
@@ -89,7 +90,7 @@ Using superannotate
89
90
  from superannotate import SAClient
90
91
 
91
92
 
92
- sa_client =SAClient()
93
+ sa_client = SAClient()
93
94
 
94
95
  project = 'Dogs'
95
96
 
@@ -145,34 +146,6 @@ Windows (`Anaconda <https://www.anaconda.com/products/individual#windows>`__
145
146
  ) platforms.
146
147
 
147
148
 
148
- Supported Features
149
- ------------------
150
-
151
- - search/get/create/clone/update/delete projects
152
- - search/get/create/delete folders
153
- - assign folders to project contributors
154
- - upload items to a project from a local or AWS S3 folder
155
- - attach items by URL or from an integrated storage, meanwhile keeping them secure in your cloud provider
156
- - get integrated cloud storages
157
- - upload annotations (also from local or AWS S3 folder)
158
- - delete annotations
159
- - set items annotations statuses
160
- - get/download/export annotations from a project (also to a local or AWS S3 folder)
161
- - invite/search team contributors or add contributors to a specific project
162
- - search/get/copy/move items in a project
163
- - query items using SA Query Language
164
- - define custom metadata for items and upload custom values (query based on your custom metadata)
165
- - upload priority scores
166
- - get available subsets (sets of segregated items), query items in a subset or add items to a subset
167
- - assign or anassign items to project contributors
168
- - download an image that has been uploaded to project
169
- - search/create/download/delete project annotation classes
170
- - search/download models
171
- - run predictions
172
- - convert annotations from/to COCO format
173
- - convert annotation from VOC, SuperVisely, LabelBox, DataLoop, VGG, VoTT, SageMaker, GoogleCloud, YOLO formats
174
- - CLI commands for simple tasks
175
-
176
149
  Questions and Issues
177
150
  --------------------
178
151
 
@@ -31,7 +31,7 @@ Authentication
31
31
  # by environment variable SA_TOKEN
32
32
  sa_client = SAClient()
33
33
  # by token
34
- sa_client = SAClient(token='<team token>')
34
+ sa_client = SAClient(token='<API key>')
35
35
  # by config file
36
36
  # default path is ~/.superannotate/config.ini
37
37
  sa_client = SAClient(config_path='~/.superannotate/dev_config.ini')
@@ -42,8 +42,9 @@ config.ini example
42
42
  .. code-block:: python
43
43
 
44
44
  [DEFAULT]
45
- SA_TOKEN = <Token>
45
+ SA_TOKEN = <API key>
46
46
  LOGGING_LEVEL = INFO
47
+ LOGGING_PATH = /Users/username/data/superannotate_logs
47
48
 
48
49
 
49
50
  Using superannotate
@@ -54,7 +55,7 @@ Using superannotate
54
55
  from superannotate import SAClient
55
56
 
56
57
 
57
- sa_client =SAClient()
58
+ sa_client = SAClient()
58
59
 
59
60
  project = 'Dogs'
60
61
 
@@ -110,34 +111,6 @@ Windows (`Anaconda <https://www.anaconda.com/products/individual#windows>`__
110
111
  ) platforms.
111
112
 
112
113
 
113
- Supported Features
114
- ------------------
115
-
116
- - search/get/create/clone/update/delete projects
117
- - search/get/create/delete folders
118
- - assign folders to project contributors
119
- - upload items to a project from a local or AWS S3 folder
120
- - attach items by URL or from an integrated storage, meanwhile keeping them secure in your cloud provider
121
- - get integrated cloud storages
122
- - upload annotations (also from local or AWS S3 folder)
123
- - delete annotations
124
- - set items annotations statuses
125
- - get/download/export annotations from a project (also to a local or AWS S3 folder)
126
- - invite/search team contributors or add contributors to a specific project
127
- - search/get/copy/move items in a project
128
- - query items using SA Query Language
129
- - define custom metadata for items and upload custom values (query based on your custom metadata)
130
- - upload priority scores
131
- - get available subsets (sets of segregated items), query items in a subset or add items to a subset
132
- - assign or anassign items to project contributors
133
- - download an image that has been uploaded to project
134
- - search/create/download/delete project annotation classes
135
- - search/download models
136
- - run predictions
137
- - convert annotations from/to COCO format
138
- - convert annotation from VOC, SuperVisely, LabelBox, DataLoop, VGG, VoTT, SageMaker, GoogleCloud, YOLO formats
139
- - CLI commands for simple tasks
140
-
141
114
  Questions and Issues
142
115
  --------------------
143
116
 
@@ -1,11 +1,11 @@
1
1
  pydantic~=2.5
2
2
  pydantic-extra-types~=2.11
3
- aiohttp~=3.8
3
+ aiohttp~=3.14
4
4
  boto3~=1.42
5
5
  opencv-python-headless~=4.7
6
6
  plotly~=6.5
7
7
  pandas~=2.0
8
- pillow~=12.1
8
+ pillow~=12.3
9
9
  tqdm~=4.66
10
10
  requests~=2.33
11
11
  aiofiles~=25.1
@@ -2,7 +2,7 @@ import logging
2
2
  import os
3
3
  import sys
4
4
 
5
- __version__ = "4.5.9"
5
+ __version__ = "4.6.1dev1"
6
6
 
7
7
 
8
8
  os.environ.update({"sa_version": __version__})
@@ -15,10 +15,12 @@ from lib.core import PACKAGE_VERSION_UPGRADE
15
15
  from lib.core import PACKAGE_VERSION_INFO_MESSAGE
16
16
  from lib.core import PACKAGE_VERSION_MAJOR_UPGRADE
17
17
  from lib.core.exceptions import AppException
18
+ from lib.core.exceptions import SAAuthError
18
19
  from lib.core.exceptions import FileChangedError
19
20
  from superannotate.lib.app.input_converters import export_annotation
20
21
  from superannotate.lib.app.input_converters import import_annotation
21
22
  from superannotate.lib.app.interface.sdk_interface import SAClient
23
+ from superannotate.lib.app.interface.sdk_interface import SAORGClient
22
24
  from superannotate.lib.app.interface.sdk_interface import ItemContext
23
25
 
24
26
  SESSIONS = {}
@@ -27,10 +29,12 @@ SESSIONS = {}
27
29
  __all__ = [
28
30
  "__version__",
29
31
  "SAClient",
32
+ "SAORGClient",
30
33
  "ItemContext",
31
34
  # Utils
32
35
  "enums",
33
36
  "AppException",
37
+ "SAAuthError",
34
38
  "FileChangedError",
35
39
  "import_annotation",
36
40
  "export_annotation",
@@ -199,7 +199,7 @@ class CocoBaseStrategy(baseStrategy):
199
199
  else:
200
200
  try:
201
201
  img = Image.open(image_path)
202
- img_width, img_height = img.size()
202
+ img_width, img_height = img.size
203
203
  except Exception as e:
204
204
  raise
205
205
 
@@ -0,0 +1,395 @@
1
+ from __future__ import annotations
2
+
3
+ import functools
4
+ import json
5
+ import logging
6
+ import os
7
+ import platform
8
+ import sys
9
+ from collections.abc import Iterable
10
+ from collections.abc import Sized
11
+ from inspect import signature
12
+ from pathlib import Path
13
+ from types import FunctionType
14
+
15
+ import lib.core as constants
16
+ from lib.app.interface.types import validate_arguments
17
+ from lib.core import CONFIG
18
+ from lib.core import CREDENTIALS_NOT_FOUND_ERROR
19
+ from lib.core import INVALID_CREDENTIALS_ERROR
20
+ from lib.core import INVALID_TOKEN_ERROR
21
+ from lib.core import setup_logging
22
+ from lib.core.entities.base import ConfigEntity
23
+ from lib.core.exceptions import AppException
24
+ from lib.core.exceptions import SAAuthError
25
+ from lib.infrastructure.controller import BaseController
26
+ from lib.infrastructure.utils import extract_project_folder_inputs
27
+ from lib.infrastructure.validators import wrap_error
28
+ from mixpanel import Mixpanel
29
+ from pydantic import ValidationError
30
+
31
+ logger = logging.getLogger("sa")
32
+
33
+
34
+ class BaseInterfaceFacade:
35
+ """Credential resolution, shared by every facade.
36
+
37
+ What a token has to resolve to is the facade's own business - named by its
38
+ CONTROLLER_CLASS rather than configured through this __init__: SAClient needs a
39
+ team, SAORGClient needs an organization key.
40
+ """
41
+
42
+ #: The controller class this facade drives. Named for the class, not the
43
+ #: instance: `self.controller` is the built one.
44
+ CONTROLLER_CLASS: type[BaseController]
45
+
46
+ @validate_arguments
47
+ def __init__(
48
+ self,
49
+ # Plain str, not TokenStr: the token's shape is checked by ConfigEntity in
50
+ # _resolve_config, so a malformed one is reported as the credential failure it
51
+ # is (SAAuthError) rather than as a generic bad argument.
52
+ token: str | None = None,
53
+ config_path: str | None = None,
54
+ team_id: int | None = None,
55
+ *,
56
+ config: dict | None = None,
57
+ ):
58
+ resolved = self._resolve_config(token, config_path, config)
59
+ if team_id is not None:
60
+ # An explicit team_id wins over whatever the config source provided.
61
+ resolved.TEAM_ID = team_id
62
+ setup_logging(resolved.LOGGING_LEVEL, resolved.LOGGING_PATH)
63
+ self.controller = self.CONTROLLER_CLASS(resolved)
64
+
65
+ @classmethod
66
+ def _resolve_config(
67
+ cls,
68
+ token: str | None,
69
+ config_path: str | None,
70
+ settings: dict | None = None,
71
+ ) -> ConfigEntity:
72
+ """Resolve credentials and settings.
73
+
74
+ Credentials come from the first source that carries them: the ``token``
75
+ argument, a config file, inline ``settings``, then the environment and the
76
+ default config files. Inline settings are applied over whatever that produced,
77
+ so a caller can configure a client - another backend, bigger chunks - without
78
+ writing a file for it.
79
+
80
+ Shared by every facade's ``__init__`` (``SAClient``, ``SAORGClient``).
81
+ """
82
+ settings = cls._validated_settings(settings)
83
+ try:
84
+ if token:
85
+ config = ConfigEntity(**{**settings, "SA_TOKEN": token})
86
+ elif config_path:
87
+ config = cls._merge_settings(
88
+ cls._resolve_config_from_path(config_path), settings
89
+ )
90
+ elif "SA_TOKEN" in settings:
91
+ config = ConfigEntity(**settings)
92
+ else:
93
+ config = cls._merge_settings(
94
+ cls._resolve_config_from_env_or_files(), settings
95
+ )
96
+ except ValidationError as e:
97
+ raise SAAuthError(wrap_error(e))
98
+ if not config:
99
+ raise SAAuthError(INVALID_CREDENTIALS_ERROR)
100
+ return config
101
+
102
+ @staticmethod
103
+ def _validated_settings(settings: dict | None) -> dict:
104
+ """Inline settings, with anything the SDK has no such setting for rejected.
105
+
106
+ ConfigEntity ignores what it does not recognise, so a mistyped key would
107
+ otherwise be dropped without a word - and a mistyped SA_TOKEN would send the
108
+ client off to authenticate as whatever the environment happens to hold.
109
+ """
110
+ if not settings:
111
+ return {}
112
+ known = set(ConfigEntity.model_fields) | {
113
+ field.alias
114
+ for field in ConfigEntity.model_fields.values()
115
+ if field.alias is not None
116
+ }
117
+ unknown = sorted(set(settings) - known)
118
+ if unknown:
119
+ raise AppException(
120
+ f"Unknown configuration: {', '.join(unknown)}. "
121
+ f"Available: {', '.join(sorted(known))}."
122
+ )
123
+ return dict(settings)
124
+
125
+ @staticmethod
126
+ def _merge_settings(config: ConfigEntity, settings: dict) -> ConfigEntity:
127
+ """``config`` with inline settings applied over it."""
128
+ if not settings:
129
+ return config
130
+ return ConfigEntity(**{**config.model_dump(by_alias=True), **settings})
131
+
132
+ @classmethod
133
+ def _resolve_config_from_path(cls, config_path: str) -> ConfigEntity:
134
+ """A config file the caller named explicitly (``.ini`` or ``.json``)."""
135
+ path = Path(config_path).expanduser()
136
+ if not path.is_file() or not os.access(path, os.R_OK):
137
+ raise AppException(f"SuperAnnotate config file {config_path} not found.")
138
+ if path.suffix == ".json":
139
+ return cls._retrieve_configs_from_json(path)
140
+ return cls._retrieve_configs_from_ini(path)
141
+
142
+ @classmethod
143
+ def _resolve_config_from_env_or_files(cls) -> ConfigEntity:
144
+ """No token or config path given: ``SA_TOKEN``, then the default config files."""
145
+ config = cls._retrieve_configs_from_env()
146
+ if config:
147
+ return config
148
+ if Path(constants.CONFIG_INI_FILE_LOCATION).exists():
149
+ return cls._retrieve_configs_from_ini(constants.CONFIG_INI_FILE_LOCATION)
150
+ if Path(constants.CONFIG_JSON_FILE_LOCATION).exists():
151
+ return cls._retrieve_configs_from_json(constants.CONFIG_JSON_FILE_LOCATION)
152
+ raise SAAuthError(CREDENTIALS_NOT_FOUND_ERROR)
153
+
154
+ @staticmethod
155
+ def _retrieve_configs_from_json(path: Path | str) -> ConfigEntity:
156
+ with open(path) as json_file:
157
+ json_data = json.load(json_file)
158
+ token = json_data["token"]
159
+ try:
160
+ config = ConfigEntity(SA_TOKEN=token)
161
+ except ValidationError:
162
+ raise SAAuthError(INVALID_TOKEN_ERROR)
163
+ host = json_data.get("main_endpoint")
164
+ verify_ssl = json_data.get("ssl_verify")
165
+ team_id = json_data.get("team_id")
166
+ if team_id is not None:
167
+ config.TEAM_ID = int(team_id)
168
+ if host:
169
+ config.API_URL = host
170
+ if verify_ssl:
171
+ config.VERIFY_SSL = verify_ssl
172
+ return config
173
+
174
+ @staticmethod
175
+ def _retrieve_configs_from_ini(path: Path | str) -> ConfigEntity:
176
+ import configparser
177
+
178
+ config_parser = configparser.ConfigParser()
179
+ config_parser.optionxform = str
180
+ config_parser.read(path)
181
+ config_data = {}
182
+ for key in config_parser["DEFAULT"]:
183
+ config_data[key.upper()] = config_parser["DEFAULT"][key]
184
+ return ConfigEntity(**config_data)
185
+
186
+ @staticmethod
187
+ def _retrieve_configs_from_env() -> ConfigEntity | None:
188
+ token = os.environ.get("SA_TOKEN")
189
+ if not token:
190
+ return None
191
+ config = ConfigEntity(**dict(os.environ))
192
+ host = os.environ.get("SA_URL")
193
+ verify_ssl = not os.environ.get("SA_SSL", "True").lower() in ("false", "f", "0")
194
+ if host:
195
+ config.API_URL = host
196
+ if verify_ssl:
197
+ config.VERIFY_SSL = verify_ssl
198
+ return config
199
+
200
+
201
+ class Tracker:
202
+ def get_mp_instance(self, client, explicit_credentials: bool = False) -> Mixpanel:
203
+ # client may have no .controller yet (e.g. __init__ failed before setting one).
204
+ controller = getattr(client, "controller", None)
205
+ if controller is not None:
206
+ api_url = controller.config["SA_URL"]
207
+ elif explicit_credentials:
208
+ api_url = constants.BACKEND_URL
209
+ else:
210
+ api_url = os.environ.get("SA_URL", constants.BACKEND_URL)
211
+ if api_url == constants.BACKEND_URL:
212
+ mp_token = "ca95ed96f80e8ec3be791e2d3097cf51"
213
+ else:
214
+ mp_token = "e741d4863e7e05b1a45833d01865ef0d"
215
+ return Mixpanel(mp_token)
216
+
217
+ @staticmethod
218
+ def get_default_payload(team_name, user_email, auth_type) -> dict:
219
+ """Built fresh per event: a cached dict would be shared between callers, and
220
+ would freeze sa_version and SA_ENV as they were on the first tracked call.
221
+ """
222
+ return {
223
+ "SDK": True,
224
+ "Team": team_name,
225
+ "User Email": user_email,
226
+ "Auth Type": auth_type,
227
+ "Version": os.environ["sa_version"],
228
+ "Python version": platform.python_version(),
229
+ "Python interpreter type": platform.python_implementation(),
230
+ "Env": os.environ.get("SA_ENV", "N/A"),
231
+ }
232
+
233
+ def __init__(self, function):
234
+ self.function = function
235
+ functools.update_wrapper(self, function)
236
+
237
+ @staticmethod
238
+ def _metrics_disabled() -> bool:
239
+ """Whether the caller turned telemetry off.
240
+
241
+ Read per call: a Tracker is built while its class is being created, so a value
242
+ captured in __init__ would ignore anything set after ``import superannotate``.
243
+ """
244
+ return os.environ.get("SA_SKIP_METRICS", "False").lower() in ("true", "1", "t")
245
+
246
+ @staticmethod
247
+ def extract_arguments(function, *args, **kwargs) -> dict:
248
+ bound_arguments = signature(function).bind(*args, **kwargs)
249
+ bound_arguments.apply_defaults()
250
+ return dict(bound_arguments.arguments)
251
+
252
+ @staticmethod
253
+ def default_parser(function_name: str, kwargs: dict) -> tuple:
254
+ properties = {}
255
+ for key, value in kwargs.items():
256
+ if key == "self":
257
+ continue
258
+ if key == "token":
259
+ properties["sa_token"] = str(bool(value))
260
+ elif key == "config_path":
261
+ properties[key] = str(bool(value))
262
+ elif value is None:
263
+ properties[key] = value
264
+ elif key == "project":
265
+ properties.update(extract_project_folder_inputs(value))
266
+ elif isinstance(value, (str, int, float, bool)):
267
+ properties[key] = value
268
+ elif isinstance(value, dict):
269
+ properties[key] = list(value.keys())
270
+ elif isinstance(value, Sized):
271
+ properties[key] = len(value)
272
+ elif isinstance(value, Iterable):
273
+ properties[key] = "N/A"
274
+ else:
275
+ properties[key] = str(value)
276
+ return function_name, properties
277
+
278
+ def _track(
279
+ self,
280
+ user_id: str,
281
+ event_name: str,
282
+ data: dict,
283
+ *,
284
+ client,
285
+ explicit_credentials: bool = False,
286
+ ):
287
+ if "pytest" in sys.modules:
288
+ return
289
+ self.get_mp_instance(client, explicit_credentials).track(
290
+ user_id, event_name, data
291
+ )
292
+
293
+ @classmethod
294
+ def _failure_reason(
295
+ cls, function_name: str, success: bool, error: BaseException | None
296
+ ):
297
+ """The original error message, for the "Auth Failure" event property.
298
+
299
+ Scoped to __init__ auth/credential failures only.
300
+ """
301
+ if success or function_name != "__init__" or error is None:
302
+ return None
303
+ return str(error) if isinstance(error, SAAuthError) else None
304
+
305
+ def _track_method(
306
+ self,
307
+ instance,
308
+ args,
309
+ kwargs,
310
+ success: bool,
311
+ error: BaseException | None = None,
312
+ ):
313
+ # Before anything is gathered: building the payload reads controller.team_name,
314
+ # which fetches the team from the backend. A caller who turned metrics off
315
+ # should not pay for a request that is never sent.
316
+ if self._metrics_disabled():
317
+ return
318
+ try:
319
+ function_name = self.function.__name__ if self.function else ""
320
+ arguments = self.extract_arguments(self.function, *args, **kwargs)
321
+ event_name, properties = self.default_parser(function_name, arguments)
322
+
323
+ # instance is args[0] - the actual object the call was made on, captured
324
+ # locally per call (see __call__), not shared/mutable state. It has no
325
+ # .controller yet when __init__ fails before setting one.
326
+ controller = getattr(instance, "controller", None)
327
+ user_email = team_name = auth_type = None
328
+ if controller is not None:
329
+ user_email = controller.current_user.email
330
+ auth_type = controller.token_context.scope.label
331
+ team_name = controller.team_name
332
+ elif instance is None:
333
+ return
334
+
335
+ properties["Success"] = success
336
+ properties["Class"] = instance.__class__.__name__
337
+ if error:
338
+ properties["Failure Reason"] = self._failure_reason(
339
+ function_name, success, error
340
+ )
341
+ default = self.get_default_payload(
342
+ team_name=team_name, user_email=user_email, auth_type=auth_type
343
+ )
344
+ self._track(
345
+ user_email or "",
346
+ event_name,
347
+ {**default, **properties, **CONFIG.get_current_session().data},
348
+ client=instance,
349
+ explicit_credentials=bool(
350
+ arguments.get("token") or arguments.get("config_path")
351
+ ),
352
+ )
353
+ except BaseException:
354
+ logger.debug("Skipped telemetry for this call.", exc_info=True)
355
+
356
+ def __get__(self, obj, owner=None):
357
+ if obj is not None:
358
+ tmp = functools.partial(self.__call__, obj)
359
+ functools.update_wrapper(tmp, self.function)
360
+ return tmp
361
+ return self
362
+
363
+ def __call__(self, *args, **kwargs):
364
+ success = True
365
+ error = None
366
+ # The instance the call is bound to (set by __get__ via functools.partial) -
367
+ # captured locally here, per call, rather than read back from shared state.
368
+ instance = args[0] if args else None
369
+ try:
370
+ result = self.function(*args, **kwargs)
371
+ except BaseException as e:
372
+ # BaseException, not Exception: a KeyboardInterrupt used to skip this and
373
+ # leave the call reported as a success.
374
+ success = False
375
+ error = e
376
+ raise
377
+ else:
378
+ return result
379
+ finally:
380
+ self._track_method(
381
+ instance, args=args, kwargs=kwargs, success=success, error=error
382
+ )
383
+
384
+
385
+ class TrackableMeta(type):
386
+ def __new__(mcs, name, bases, attrs):
387
+ for attr_name, attr_value in attrs.items():
388
+ if isinstance(
389
+ attr_value, FunctionType
390
+ ) and not attr_value.__name__.startswith("_"):
391
+ attrs[attr_name] = Tracker(validate_arguments(attr_value))
392
+ if "__init__" in attrs:
393
+ attrs["__init__"] = Tracker(validate_arguments(attrs["__init__"]))
394
+ tmp = super().__new__(mcs, name, bases, attrs)
395
+ return tmp
@@ -33,10 +33,10 @@ class CLIFacade:
33
33
  logging_path: str = constances.LOG_FILE_LOCATION,
34
34
  ):
35
35
  """
36
- To initialize CLI (and SDK) with team token
37
- Input the team SDK token from https://app.superannotate.com/team
36
+ To initialize CLI (and SDK) with API key.
37
+ The API key is available on the team setup page, for more details please visit our documentation at https://doc.superannotate.com/docs/api-keys.
38
38
 
39
- :param token: the team token
39
+ :param token: the API key
40
40
  :type token: str
41
41
 
42
42
  :param logging_level: logging level, default is "INFO"