Skip to content

Commit edd85ff

Browse files
committed
hinting improvements
1 parent e26e4f6 commit edd85ff

37 files changed

+408
-1588
lines changed

pytest.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
minversion = 3.7
33
log_cli=true
44
python_files = test_*.py
5-
pytest_plugins = ['pytest_profiling']
6-
;addopts = -n auto --dist=loadscope
5+
;pytest_plugins = ['pytest_profiling']
6+
addopts = -n auto --dist=loadscope

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ requests==2.26.0
44
requests-toolbelt>=0.9.1
55
aiohttp>=3.8.1
66
tqdm==4.64.0
7-
pillow>=7.2.0<9.0.1
7+
pillow>=7.2.0, <9.0.1
88
matplotlib>=3.3.1
99
xmltodict==0.12.0
1010
opencv-python>=4.4.0.42

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

Lines changed: 152 additions & 91 deletions
Large diffs are not rendered by default.
Lines changed: 4 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,17 @@
1-
import uuid
21
from functools import wraps
3-
from pathlib import Path
4-
from typing import Optional
52
from typing import Union
63

7-
from lib.core.enums import AnnotationStatus
8-
from lib.core.enums import ApprovalStatus
94
from lib.core.enums import BaseTitledEnum
10-
from lib.core.enums import ClassTypeEnum
11-
from lib.core.enums import FolderStatus
12-
from lib.core.enums import ProjectStatus
13-
from lib.core.enums import ProjectType
14-
from lib.core.enums import UserRole
155
from lib.core.exceptions import AppException
166
from lib.infrastructure.validators import wrap_error
17-
from pydantic import BaseModel
18-
from pydantic import conlist
197
from pydantic import constr
208
from pydantic import errors
21-
from pydantic import Extra
22-
from pydantic import Field
23-
from pydantic import parse_obj_as
24-
from pydantic import root_validator
259
from pydantic import StrictStr
2610
from pydantic import validate_arguments as pydantic_validate_arguments
2711
from pydantic import ValidationError
2812
from pydantic.errors import PydanticTypeError
2913
from pydantic.errors import StrRegexError
3014

31-
NotEmptyStr = constr(strict=True, min_length=1)
32-
3315

3416
class EnumMemberError(PydanticTypeError):
3517
code = "enum"
@@ -54,174 +36,20 @@ def validate(cls, value: Union[str]) -> Union[str]:
5436
regex=r"^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)"
5537
r"*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}"
5638
r"[a-zA-Z0-9])?)*$"
57-
).validate(value)
39+
).validate( # noqa
40+
value
41+
)
5842
except StrRegexError:
5943
raise ValueError("Invalid email")
6044
return value
6145

6246

63-
class ProjectStatusEnum(StrictStr):
64-
@classmethod
65-
def validate(cls, value: Union[str]) -> Union[str]:
66-
if cls.curtail_length and len(value) > cls.curtail_length:
67-
value = value[: cls.curtail_length]
68-
if value.lower() not in ProjectStatus.values():
69-
raise TypeError(
70-
f"Available statuses is {', '.join(ProjectStatus.titles())}. "
71-
)
72-
return value
73-
74-
75-
class FolderStatusEnum(StrictStr):
76-
@classmethod
77-
def validate(cls, value: Union[str]) -> Union[str]:
78-
if cls.curtail_length and len(value) > cls.curtail_length:
79-
value = value[: cls.curtail_length]
80-
if value.lower() not in FolderStatus.values():
81-
raise TypeError(
82-
f"Available statuses is {', '.join(FolderStatus.titles())}. "
83-
)
84-
return value
85-
86-
87-
class AnnotatorRole(StrictStr):
88-
ANNOTATOR_ROLES = (UserRole.ADMIN.name, UserRole.ANNOTATOR.name, UserRole.QA.name)
89-
90-
@classmethod
91-
def validate(cls, value: Union[str]) -> Union[str]:
92-
if cls.curtail_length and len(value) > cls.curtail_length:
93-
value = value[: cls.curtail_length]
94-
if value.lower() not in [role.lower() for role in cls.ANNOTATOR_ROLES]:
95-
raise TypeError(
96-
f"Invalid user role provided. Please specify one of {', '.join(cls.ANNOTATOR_ROLES)}. "
97-
)
98-
return value
99-
100-
101-
class AnnotationType(StrictStr):
102-
VALID_TYPES = ["bbox", "polygon", "point", "tag"]
103-
104-
@classmethod
105-
def validate(cls, value: Union[str]) -> Union[str]:
106-
if value.lower() not in cls.VALID_TYPES:
107-
raise TypeError(
108-
f"Available annotation_types are {', '.join(cls.VALID_TYPES)}. "
109-
)
110-
return value
111-
112-
113-
class AttachmentDict(BaseModel):
114-
url: StrictStr
115-
name: Optional[StrictStr] = Field(default_factory=lambda: str(uuid.uuid4()))
116-
117-
class Config:
118-
extra = Extra.ignore
119-
120-
def __hash__(self):
121-
return hash(self.name)
122-
123-
def __eq__(self, other):
124-
return self.url == other.url and self.name.strip() == other.name.strip()
125-
126-
127-
AttachmentArgType = Union[NotEmptyStr, Path, conlist(AttachmentDict, min_items=1)]
128-
129-
130-
class Setting(BaseModel):
131-
attribute: NotEmptyStr
132-
value: Union[NotEmptyStr, float, int]
133-
134-
class Config:
135-
extra = Extra.ignore
136-
137-
138-
class AttachmentArg(BaseModel):
139-
__root__: AttachmentArgType
140-
141-
def __getitem__(self, index):
142-
return self.__root__[index]
143-
144-
@property
145-
def data(self):
146-
return self.__root__
147-
148-
@root_validator(pre=True)
149-
def validate_root(cls, values):
150-
try:
151-
parse_obj_as(AttachmentArgType, values["__root__"])
152-
except ValidationError:
153-
raise ValueError(
154-
"The value must be str, path, or list of dicts with the required 'url' and optional 'name' keys"
155-
)
156-
return values
157-
158-
159-
class ImageQualityChoices(StrictStr):
160-
VALID_CHOICES = ["compressed", "original"]
161-
162-
@classmethod
163-
def validate(cls, value: Union[str]) -> Union[str]:
164-
super().validate(value)
165-
if value.lower() not in cls.VALID_CHOICES:
166-
raise TypeError(
167-
f"Image quality available choices are {', '.join(cls.VALID_CHOICES)}."
168-
)
169-
return value.lower()
170-
171-
172-
class ProjectTypes(StrictStr):
173-
@classmethod
174-
def validate(cls, value: Union[str]) -> Union[str]:
175-
if value.lower() not in ProjectType.values():
176-
raise TypeError(
177-
f" Available project types are {', '.join(ProjectType.titles())}. "
178-
)
179-
return value
180-
181-
182-
class ClassType(StrictStr):
183-
@classmethod
184-
def validate(cls, value: Union[str]) -> Union[str]:
185-
enum_values = [e.name.lower() for e in ClassTypeEnum]
186-
if value.lower() not in enum_values:
187-
raise TypeError(
188-
f"Invalid type provided. Please specify one of the {', '.join(enum_values)}. "
189-
)
190-
return value.lower()
191-
192-
193-
class AnnotationStatuses(StrictStr):
194-
@classmethod
195-
def validate(cls, value: Union[str]) -> Union[str]:
196-
if value.lower() not in AnnotationStatus.values():
197-
raise TypeError(
198-
f"Available an notation_statuses are {', '.join(AnnotationStatus.titles())}. "
199-
)
200-
return value
201-
202-
203-
class ApprovalStatuses(StrictStr):
204-
@classmethod
205-
def validate(cls, value: Union[str]) -> Union[str]:
206-
if value is None:
207-
return value
208-
if value.lower() not in ApprovalStatus.values() or not isinstance(value, str):
209-
raise TypeError(
210-
f"Available approval_status options are {', '.join(map(str, ApprovalStatus.titles()))}."
211-
)
212-
return value
213-
214-
@classmethod
215-
def __get_validators__(cls):
216-
yield cls.validate
217-
218-
21947
def validate_arguments(func):
22048
@wraps(func)
22149
def wrapped(self, *args, **kwargs):
22250
try:
22351
return pydantic_validate_arguments(func)(self, *args, **kwargs)
22452
except ValidationError as e:
225-
raise AppException(wrap_error(e))
53+
raise AppException(wrap_error(e)) from e
22654

22755
return wrapped

src/superannotate/lib/core/entities/project.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ class AttachmentEntity(BaseModel):
4747
class Config:
4848
extra = Extra.ignore
4949

50+
def __hash__(self):
51+
return hash(self.name)
52+
5053

5154
class WorkflowEntity(BaseModel):
5255
id: Optional[int]

src/superannotate/lib/core/enums.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import typing
12
from enum import Enum
23
from types import DynamicClassAttribute
34

@@ -27,6 +28,9 @@ def choices(cls):
2728
def name(self) -> str:
2829
return self.__doc__
2930

31+
def __unicode__(self):
32+
return self.__doc__
33+
3034
@DynamicClassAttribute
3135
def value(self):
3236
return super().value
@@ -51,15 +55,18 @@ def values(cls):
5155
return [enum.__doc__.lower() if enum else None for enum in list(cls)]
5256

5357
@classmethod
54-
def titles(cls):
55-
return [enum.__doc__ for enum in list(cls)]
58+
def titles(cls) -> typing.Tuple:
59+
return tuple(enum.__doc__ for enum in list(cls))
5660

5761
def equals(self, other: Enum):
5862
return self.__doc__.lower() == other.__doc__.lower()
5963

6064
def __eq__(self, other):
6165
return super().__eq__(other)
6266

67+
def __repr__(self):
68+
return self.name
69+
6370
def __hash__(self):
6471
return hash(self.name)
6572

src/superannotate/lib/core/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class Config:
2525
extra = Extra.allow
2626

2727

28-
class PriorityScore(BaseModel):
28+
class PriorityScoreEntity(BaseModel):
2929
name: NotEmptyStr
3030
priority: float
3131

src/superannotate/lib/core/usecases/annotations.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from lib.core.response import Response
4040
from lib.core.service_types import UploadAnnotationAuthData
4141
from lib.core.serviceproviders import BaseServiceProvider
42-
from lib.core.types import PriorityScore
42+
from lib.core.types import PriorityScoreEntity
4343
from lib.core.usecases.base import BaseReportableUseCase
4444
from lib.core.video_convertor import VideoFrameGenerator
4545
from pydantic import BaseModel
@@ -585,6 +585,7 @@ def get_existing_name_item_mapping(
585585

586586
@property
587587
def annotation_upload_data(self) -> UploadAnnotationAuthData:
588+
588589
CHUNK_SIZE = UploadAnnotationsFromFolderUseCase.CHUNK_SIZE_PATHS
589590

590591
if self._annotation_upload_data:
@@ -598,7 +599,7 @@ def annotation_upload_data(self) -> UploadAnnotationAuthData:
598599
item_ids=self._item_ids[i : i + CHUNK_SIZE],
599600
)
600601
if not tmp.ok:
601-
raise AppException(tmp.errors)
602+
raise AppException(tmp.error)
602603
else:
603604
images.update(tmp.data.images)
604605

@@ -1195,7 +1196,7 @@ def __init__(
11951196
reporter,
11961197
project: ProjectEntity,
11971198
folder: FolderEntity,
1198-
scores: List[PriorityScore],
1199+
scores: List[PriorityScoreEntity],
11991200
project_folder_name: str,
12001201
service_provider: BaseServiceProvider,
12011202
):

src/superannotate/lib/core/usecases/base.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ def __init__(self, reporter: Reporter):
6464
self.reporter = reporter
6565

6666

67-
class BaseUserBasedUseCase(BaseReportableUseCase, metaclass=ABCMeta):
67+
class BaseUserBasedUseCase(BaseUseCase, metaclass=ABCMeta):
6868
"""
6969
class contain validation of unique emails
7070
"""
7171

72-
def __init__(self, reporter: Reporter, emails: List[str]):
73-
super().__init__(reporter)
72+
def __init__(self, emails: List[str]):
73+
super().__init__()
7474
self._emails = emails

0 commit comments

Comments
 (0)