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.
- {superannotate-4.5.9/src/superannotate.egg-info → superannotate-4.6.1.dev1}/PKG-INFO +7 -34
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/README.rst +4 -31
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/requirements.txt +2 -2
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/__init__.py +5 -1
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_converter.py +1 -1
- superannotate-4.6.1.dev1/src/superannotate/lib/app/interface/base_interface.py +395 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/cli_interface.py +3 -3
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/sdk_interface.py +296 -167
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/__init__.py +52 -5
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/__init__.py +6 -1
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/base.py +19 -3
- superannotate-4.6.1.dev1/src/superannotate/lib/core/entities/context.py +106 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/project.py +14 -1
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/work_managament.py +1 -1
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/exceptions.py +12 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/service_types.py +4 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/serviceproviders.py +90 -43
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/__init__.py +1 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/annotations.py +93 -68
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/images.py +12 -8
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/items.py +10 -7
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/projects.py +23 -18
- superannotate-4.6.1.dev1/src/superannotate/lib/core/usecases/work_management.py +397 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/annotation_adapter.py +3 -3
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/controller.py +152 -71
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/serviceprovider.py +35 -14
- superannotate-4.6.1.dev1/src/superannotate/lib/infrastructure/services/auth.py +144 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/http_client.py +39 -20
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/work_management.py +82 -40
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/utils.py +56 -14
- {superannotate-4.5.9 → superannotate-4.6.1.dev1/src/superannotate.egg-info}/PKG-INFO +7 -34
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate.egg-info/SOURCES.txt +3 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate.egg-info/requires.txt +2 -2
- superannotate-4.5.9/src/superannotate/lib/app/interface/base_interface.py +0 -252
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/LICENSE +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/MANIFEST.in +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/setup.cfg +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/setup.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/analytics/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/analytics/aggregators.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/analytics/common.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/bin/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/bin/superannotate.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/common.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/exceptions.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/helpers.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/conversion.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/baseStrategy.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/coco_converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_api.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_strategies.py +0 -0
- {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
- {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
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/converters.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/dataloop_helper.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/dataloop_strategies.py +0 -0
- {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
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/googlecloud_converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/googlecloud_converters/googlecloud_strategies.py +0 -0
- {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
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_helper.py +0 -0
- {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
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/sa_json_helper.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/sagemaker_strategies.py +0 -0
- {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
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_helper.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_strategies.py +0 -0
- {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
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vgg_converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vgg_converters/vgg_helper.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vgg_converters/vgg_strategies.py +0 -0
- {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
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/voc_converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_helper.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_strategies.py +0 -0
- {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
- {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
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vott_converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/vott_converters/vott_strategies.py +0 -0
- {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
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/yolo_converters/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/converters/yolo_converters/yolo_strategies.py +0 -0
- {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
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/export_from_sa_conversions.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/input_converters/import_to_sa_conversions.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/responses.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/sdk/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/sdk/folders.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/sdk/project.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/interface/types.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/app/serializers.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/base_usecases.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/conditions.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/config.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/classes.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/filters.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/folder.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/integrations.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/items.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/multimodal_form.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/entities/project_entities.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/enums.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/jsx_conditions.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/plugin.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/reporter.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/repositories.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/response.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/types.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/base.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/classes.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/custom_fields.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/folders.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/integrations.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/usecases/models.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/utils.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/core/video_convertor.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/custom_entities.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/helpers.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/query_builder.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/repositories.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/__init__.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/annotation.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/annotation_class.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/explore.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/folder.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/integration.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/item.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/item_service.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/project.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/services/telemetry_scoring.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/stream_data_handler.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate/lib/infrastructure/validators.py +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate.egg-info/dependency_links.txt +0 -0
- {superannotate-4.5.9 → superannotate-4.6.1.dev1}/src/superannotate.egg-info/entry_points.txt +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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='<
|
|
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 = <
|
|
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='<
|
|
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 = <
|
|
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
|
|
|
@@ -2,7 +2,7 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
import sys
|
|
4
4
|
|
|
5
|
-
__version__ = "4.
|
|
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",
|
|
@@ -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
|
|
37
|
-
|
|
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
|
|
39
|
+
:param token: the API key
|
|
40
40
|
:type token: str
|
|
41
41
|
|
|
42
42
|
:param logging_level: logging level, default is "INFO"
|