Skip to content

Commit c285f0c

Browse files
Vaghinak BasentsyanVaghinak Basentsyan
authored andcommitted
tod
1 parent 812768d commit c285f0c

File tree

9 files changed

+83
-80
lines changed

9 files changed

+83
-80
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ SDK is available on PyPI:
2121
pip install superannotate
2222
```
2323

24-
The package officially supports Python 3.8+ and was tested under Linux and
24+
The package officially supports Python 3.6+ and was tested under Linux and
2525
Windows ([Anaconda](https://www.anaconda.com/products/individual#windows)) platforms.
2626

2727
For more detailed installation steps and package usage please have a look at the

docs/source/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ SDK is available on PyPI:
4848
pip install superannotate
4949
5050
51-
The package officially supports Python 3.8+ and was tested under Linux and
51+
The package officially supports Python 3.6+ and was tested under Linux and
5252
Windows (`Anaconda <https://www.anaconda.com/products/individual#windows>`_) platforms.
5353

5454
For more detailed installation steps and package usage please have a look at

docs/source/tutorial.sdk.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ SDK is available on PyPI:
1717
1818
pip install superannotate
1919
20-
The package officially supports Python 3.8+ and was tested under Linux and
20+
The package officially supports Python 3.6+ and was tested under Linux and
2121
Windows (`Anaconda <https://www.anaconda.com/products/individual#windows>`_) platforms.
2222

2323
For certain video related functions to work, ffmpeg package needs to be installed.

pytest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
minversion = 3.0
33
log_cli=true
44
python_files = test_*.py
5-
;addopts = -n32 --dist=loadscope
5+
addopts = -n32 --dist=loadscope

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,5 @@
4242
entry_points={
4343
'console_scripts': ['superannotatecli = superannotate.lib.app.bin.superannotate:main']
4444
},
45-
python_requires='>=3.8'
45+
python_requires='>=3.6'
4646
)

src/superannotate/lib/app/helpers.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@
22
from functools import wraps
33
from inspect import getfullargspec
44
from pathlib import Path
5-
from typing import get_args
6-
from typing import get_origin
75
from typing import get_type_hints
86
from typing import List
9-
from typing import Literal
107
from typing import Optional
118
from typing import Tuple
129
from typing import Union
@@ -134,23 +131,18 @@ def check_types(obj, **kwargs):
134131
if attr_name == "return":
135132
continue
136133
try:
137-
if get_origin(attr_type) is list:
138-
if get_args(attr_type):
134+
if attr_type.__origin__ is list:
135+
if attr_type.__args__:
139136
for elem in kwargs[attr_name]:
140-
if type(elem) in get_args(attr_type):
137+
if type(elem) in attr_type.__args__:
141138
continue
142139
else:
143140
errors.append(f"Invalid input {attr_name}")
144141
elif not isinstance(kwargs[attr_name], list):
145142
errors.append(f"Argument {attr_name} is not of type {attr_type}")
146-
if get_origin(attr_type) is Literal:
147-
if kwargs[attr_name] not in get_args(attr_type):
148-
errors.append(
149-
f'The value of {attr_name} should be {", ".join(get_args(attr_type))}'
150-
)
151-
elif get_origin(attr_type) is Union:
143+
elif attr_type.__origin__ is Union:
152144
if kwargs.get(attr_name) and not isinstance(
153-
kwargs[attr_name], get_args(attr_type)
145+
kwargs[attr_name], attr_type.__args__
154146
):
155147
errors.append(f"Argument {attr_name} is not of type {attr_type}")
156148
elif kwargs.get(attr_name) and not isinstance(kwargs[attr_name], attr_type):

src/superannotate/lib/app/interface/sdk_interface.py

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from io import BytesIO
1212
from pathlib import Path
1313
from typing import List
14-
from typing import Literal
1514
from typing import Optional
1615
from typing import Union
1716
from urllib.parse import urlparse
@@ -481,7 +480,7 @@ def search_folders(
481480
def get_image_bytes(
482481
project: Union[str, dict],
483482
image_name: str,
484-
variant: Literal["original", "lores"] = "original",
483+
variant: str = "original",
485484
):
486485
"""Returns an io.BytesIO() object of the image. Suitable for creating
487486
PIL.Image out of it.
@@ -597,17 +596,8 @@ def upload_images_from_public_urls_to_project(
597596
project: Union[str, dict],
598597
img_urls: List[str],
599598
img_names: Optional[List[str]] = None,
600-
annotation_status: Optional[
601-
Literal[
602-
"NotStarted",
603-
"InProgress",
604-
"QualityCheck",
605-
"Returned",
606-
"Completed",
607-
"Skipped",
608-
]
609-
] = "NotStarted",
610-
image_quality_in_editor: Optional[Literal["compressed", "original"]] = None,
599+
annotation_status: Optional[str] = "NotStarted",
600+
image_quality_in_editor: Optional[str] = None,
611601
):
612602
"""Uploads all images given in the list of URL strings in img_urls to the project.
613603
Sets status of all the uploaded images to annotation_status if it is not None.
@@ -823,7 +813,7 @@ def move_images(
823813
).data
824814
moved_count = len(moved_images)
825815
message_postfix = "{from_path} to {to_path}."
826-
message_prefix = "Copied images from "
816+
message_prefix = "Moved images from "
827817
if moved_count > 1 or moved_count == 0:
828818
message_prefix = f"Moved {moved_count}/{len(image_names)} images from "
829819
elif moved_count == 1:
@@ -992,7 +982,7 @@ def get_project_default_image_quality_in_editor(project: Union[str, dict]):
992982
@validate_input
993983
def set_project_default_image_quality_in_editor(
994984
project: Union[str, dict],
995-
image_quality_in_editor: Optional[Literal["compressed", "original"]],
985+
image_quality_in_editor: Optional[str],
996986
):
997987
"""Sets project's default image quality in editor setting.
998988
@@ -1087,9 +1077,7 @@ def get_image_metadata(project: Union[str, dict], image_name: str, *_, **__):
10871077
def set_images_annotation_statuses(
10881078
project: Union[str, dict],
10891079
image_names: List[str],
1090-
annotation_status: Literal[
1091-
"NotStarted", "InProgress", "QualityCheck", "Returned", "Completed", "Skipped",
1092-
],
1080+
annotation_status: str,
10931081
):
10941082
"""Sets annotation statuses of images
10951083
@@ -1497,7 +1485,7 @@ def upload_images_from_folder_to_project(
14971485
from_s3_bucket=None,
14981486
exclude_file_patterns: Optional[str] = constances.DEFAULT_FILE_EXCLUDE_PATTERNS,
14991487
recursive_subfolders: Optional[bool] = False,
1500-
image_quality_in_editor: Optional[Literal["compressed", "original"]] = None,
1488+
image_quality_in_editor: Optional[str] = None,
15011489
):
15021490
"""Uploads all images with given extensions from folder_path to the project.
15031491
Sets status of all the uploaded images to set_status if it is not None.
@@ -1689,7 +1677,7 @@ def upload_images_from_s3_bucket_to_project(
16891677
secretAccessKey: str,
16901678
bucket_name: str,
16911679
folder_path: str,
1692-
image_quality_in_editor: Optional[Literal["compressed", "original"]] = None,
1680+
image_quality_in_editor: Optional[str] = None,
16931681
):
16941682
"""Uploads all images from AWS S3 bucket to the project.
16951683

src/superannotate/lib/core/usecases.py

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from abc import abstractmethod
1313
from collections import defaultdict
1414
from collections import namedtuple
15-
from functools import cached_property
1615
from pathlib import Path
1716
from typing import Iterable
1817
from typing import List
@@ -4099,6 +4098,8 @@ def __init__(
40994098
super().__init__()
41004099

41014100
self._auth_data = None
4101+
self._s3_repo_instance = None
4102+
self._images_to_upload = None
41024103

41034104
self._project = project
41044105
self._folder = folder
@@ -4131,22 +4132,25 @@ def validate_project_type(self):
41314132
"The function does not support projects containing videos attached with URLs"
41324133
)
41334134

4134-
@cached_property
4135+
@property
41354136
def auth_data(self):
4136-
return self._backend_client.get_s3_upload_auth_token(
4137+
if not self._auth_data:
4138+
self._auth_data = self._backend_client.get_s3_upload_auth_token(
41374139
team_id=self._project.team_id,
41384140
folder_id=self._folder.uuid,
4139-
project_id=self._project.uuid,
4140-
)
4141+
project_id=self._project.uuid, )
4142+
return self._auth_data
41414143

4142-
@cached_property
4144+
@property
41434145
def s3_repository(self):
4144-
return self._s3_repo(
4146+
if not self._s3_repo_instance:
4147+
self._s3_repo_instance = self._s3_repo(
41454148
self.auth_data["accessKeyId"],
41464149
self.auth_data["secretAccessKey"],
41474150
self.auth_data["sessionToken"],
41484151
self.auth_data["bucket"],
41494152
)
4153+
return self._s3_repo_instance
41504154

41514155
def _upload_image(self, image_path: str):
41524156
ProcessedImage = namedtuple("ProcessedImage", ["uploaded", "path", "entity"])
@@ -4226,30 +4230,32 @@ def paths(self):
42264230
and "___pixel" not in path
42274231
]
42284232

4229-
@cached_property
4233+
@property
42304234
def images_to_upload(self):
4231-
paths = self.paths
4232-
filtered_paths = []
4233-
duplicated_paths = []
4234-
images = self._backend_client.get_bulk_images(
4235-
project_id=self._project.uuid,
4236-
team_id=self._project.team_id,
4237-
folder_id=self._folder.uuid,
4238-
images=[Path(image).name for image in paths],
4239-
)
4240-
image_names = [image["name"] for image in images]
4241-
4242-
for path in paths:
4243-
not_in_exclude_list = [
4244-
x not in Path(path).name for x in self.exclude_file_patterns
4245-
]
4246-
non_in_service_list = [x not in Path(path).name for x in image_names]
4247-
if all(not_in_exclude_list) and all(non_in_service_list):
4248-
filtered_paths.append(path)
4249-
if not all(non_in_service_list):
4250-
duplicated_paths.append(path)
4235+
if not self._images_to_upload:
4236+
paths = self.paths
4237+
filtered_paths = []
4238+
duplicated_paths = []
4239+
images = self._backend_client.get_bulk_images(
4240+
project_id=self._project.uuid,
4241+
team_id=self._project.team_id,
4242+
folder_id=self._folder.uuid,
4243+
images=[Path(image).name for image in paths],
4244+
)
4245+
image_names = [image["name"] for image in images]
42514246

4252-
return list(set(filtered_paths)), duplicated_paths
4247+
for path in paths:
4248+
not_in_exclude_list = [
4249+
x not in Path(path).name for x in self.exclude_file_patterns
4250+
]
4251+
non_in_service_list = [x not in Path(path).name for x in image_names]
4252+
if all(not_in_exclude_list) and all(non_in_service_list):
4253+
filtered_paths.append(path)
4254+
if not all(non_in_service_list):
4255+
duplicated_paths.append(path)
4256+
4257+
self._images_to_upload = list(set(filtered_paths)), duplicated_paths
4258+
return self._images_to_upload
42534259

42544260
def execute(self):
42554261
images_to_upload, duplications = self.images_to_upload

src/superannotate/lib/infrastructure/controller.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import copy
22
import io
3-
from functools import cached_property
43
from typing import Iterable
54
from typing import List
65
from typing import Optional
@@ -51,6 +50,12 @@ def __init__(self, logger, config_path=constances.CONFIG_FILE_LOCATION):
5150
logger=logger,
5251
)
5352
self._s3_upload_auth_data = None
53+
self._projects = None
54+
self._folders = None
55+
self._teams = None
56+
self._images = None
57+
self._ml_models = None
58+
self._team_id = None
5459

5560
def set_token(self, token):
5661
self.configs.insert(ConfigEntity("token", token))
@@ -60,33 +65,45 @@ def set_token(self, token):
6065
logger=self._logger,
6166
)
6267

63-
@cached_property
68+
@property
6469
def projects(self):
65-
return ProjectRepository(self._backend_client)
70+
if not self._projects:
71+
self._projects = ProjectRepository(self._backend_client)
72+
return self._projects
6673

67-
@cached_property
74+
@property
6875
def folders(self):
69-
return FolderRepository(self._backend_client)
76+
if not self._folders:
77+
self._folders = FolderRepository(self._backend_client)
78+
return self._folders
7079

71-
@cached_property
80+
@property
7281
def ml_models(self):
73-
return MLModelRepository(self._backend_client, self.team_id)
82+
if not self._ml_models:
83+
self._ml_models = MLModelRepository(self._backend_client, self.team_id)
84+
return self._ml_models
7485

75-
@cached_property
86+
@property
7687
def teams(self):
77-
return TeamRepository(self._backend_client)
88+
if not self._teams:
89+
self._teams = TeamRepository(self._backend_client)
90+
return self._teams
7891

79-
@cached_property
92+
@property
8093
def images(self):
81-
return ImageRepository(self._backend_client)
94+
if not self._images:
95+
self._images = ImageRepository(self._backend_client)
96+
return self._images
8297

8398
@property
8499
def configs(self):
85100
return ConfigRepository(self._config_path)
86101

87-
@cached_property
102+
@property
88103
def team_id(self) -> int:
89-
return int(self.configs.get_one("token").value.split("=")[-1])
104+
if not self._team_id:
105+
self._team_id = int(self.configs.get_one("token").value.split("=")[-1])
106+
return self._team_id
90107

91108
@timed_lru_cache(seconds=3600)
92109
def get_auth_data(self, project_id: int, team_id: int, folder_id: int):

0 commit comments

Comments
 (0)