Skip to content

Commit 3f89094

Browse files
authored
Merge pull request #664 from superannotateai/FRIDAY_2178
Removed jsonschema
2 parents 9e38a60 + 8435c15 commit 3f89094

File tree

10 files changed

+75
-42
lines changed

10 files changed

+75
-42
lines changed

requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ pandas~=2.0
99
ffmpeg-python~=0.2
1010
pillow>=9.5,~=10.0
1111
tqdm~=4.66.1
12-
requests~=2.31.0
13-
aiofiles==23.1.0
12+
requests==2.*
13+
aiofiles==23.*
1414
fire==0.4.0
1515
mixpanel==4.8.3
16-
jsonschema==3.2.0
16+
superannotate-schemas==1.0.46

src/superannotate/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44

55

6-
__version__ = "4.4.17"
6+
__version__ = "4.4.18b1"
77

88
sys.path.append(os.path.split(os.path.realpath(__file__))[0])
99

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
"Tiled",
8080
"Other",
8181
"PointCloud",
82-
"CustomEditor",
82+
"GenAI",
8383
]
8484

8585
ANNOTATION_STATUS = Literal[

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class AttributeGroup(TimedBaseModel):
7373
group_type: Optional[GroupTypeEnum]
7474
class_id: Optional[StrictInt]
7575
name: Optional[StrictStr]
76+
isRequired: bool = Field(default=False)
7677
attributes: Optional[List[Attribute]]
7778
default_value: Any
7879

src/superannotate/lib/core/enums.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class ProjectType(BaseTitledEnum):
9393
TILED = "Tiled", 5
9494
OTHER = "Other", 6
9595
POINT_CLOUD = "PointCloud", 7
96-
CUSTOM_EDITOR = "CustomEditor", 8
96+
GEN_AI = "GenAI", 8
9797
UNSUPPORTED_TYPE_1 = "UnsupportedType", 9
9898
UNSUPPORTED_TYPE_2 = "UnsupportedType", 10
9999

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

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,8 @@
2525

2626
import aiofiles
2727
import boto3
28-
import jsonschema.validators
2928
import lib.core as constants
30-
from jsonschema import Draft7Validator
31-
from jsonschema import ValidationError
29+
import superannotate_schemas
3230
from lib.core.conditions import Condition
3331
from lib.core.conditions import CONDITION_EQ as EQ
3432
from lib.core.entities import BaseItemEntity
@@ -309,7 +307,7 @@ def __init__(
309307

310308
def validate_project_type(self):
311309
if self._project.type == constants.ProjectType.PIXEL.value:
312-
raise ValidationError("Unsupported project type.")
310+
raise AppException("Unsupported project type.")
313311

314312
def _validate_json(self, json_data: dict) -> list:
315313
if self._project.type >= constants.ProjectType.PIXEL.value:
@@ -1227,7 +1225,7 @@ def execute(self):
12271225

12281226
class ValidateAnnotationUseCase(BaseReportableUseCase):
12291227
DEFAULT_VERSION = "V1.00"
1230-
SCHEMAS: Dict[str, Draft7Validator] = {}
1228+
SCHEMAS: Dict[str, superannotate_schemas.Draft7Validator] = {}
12311229
PATTERN_MAP = {
12321230
"\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d(?:\\.\\d{3})Z": "does not match YYYY-MM-DDTHH:MM:SS.fffZ",
12331231
"^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$": "invalid email",
@@ -1278,15 +1276,17 @@ def oneOf(validator, oneOf, instance, schema): # noqa
12781276
const_key, instance
12791277
)
12801278
if not instance_type:
1281-
yield ValidationError("type required")
1279+
yield superannotate_schemas.ValidationError("type required")
12821280
return
12831281
if const_key and instance_type == _type:
12841282
errs = list(
12851283
validator.descend(instance, sub_schema, schema_path=index)
12861284
)
12871285
if not errs:
12881286
return
1289-
yield ValidationError("invalid instance", context=errs)
1287+
yield superannotate_schemas.ValidationError(
1288+
"invalid instance", context=errs
1289+
)
12901290
return
12911291
else:
12921292
subschemas = enumerate(oneOf)
@@ -1299,24 +1299,25 @@ def oneOf(validator, oneOf, instance, schema): # noqa
12991299
break
13001300
all_errors.extend(errs)
13011301
else:
1302-
yield ValidationError(
1302+
yield superannotate_schemas.ValidationError(
13031303
f"{instance!r} is not valid under any of the given schemas",
13041304
context=all_errors[:1],
13051305
)
1306-
# yield from jsonschema._validators.oneOf( # noqa
1307-
# validator, oneOf, instance, schema
1308-
# )
13091306
if const_key:
1310-
yield ValidationError(f"invalid {'.'.join(const_key)}")
1307+
yield superannotate_schemas.ValidationError(
1308+
f"invalid {'.'.join(const_key)}"
1309+
)
13111310

13121311
@staticmethod
13131312
def _pattern(validator, patrn, instance, schema):
13141313
if validator.is_type(instance, "string") and not re.search(patrn, instance):
13151314
_patrn = ValidateAnnotationUseCase.PATTERN_MAP.get(patrn)
13161315
if _patrn:
1317-
yield ValidationError(f"{instance} {_patrn}")
1316+
yield superannotate_schemas.ValidationError(f"{instance} {_patrn}")
13181317
else:
1319-
yield ValidationError(f"{instance} does not match {patrn}")
1318+
yield superannotate_schemas.ValidationError(
1319+
f"{instance} does not match {patrn}"
1320+
)
13201321

13211322
@staticmethod
13221323
def iter_errors(self, instance, _schema=None):
@@ -1325,7 +1326,7 @@ def iter_errors(self, instance, _schema=None):
13251326
if _schema is True:
13261327
return
13271328
elif _schema is False:
1328-
yield jsonschema.exceptions.ValidationError(
1329+
yield superannotate_schemas.ValidationError(
13291330
f"False schema does not allow {instance!r}",
13301331
validator=None,
13311332
validator_value=None,
@@ -1334,7 +1335,7 @@ def iter_errors(self, instance, _schema=None):
13341335
)
13351336
return
13361337

1337-
scope = jsonschema.validators._id_of(_schema) # noqa
1338+
scope = superannotate_schemas.validators._id_of(_schema) # noqa
13381339
_schema = copy.copy(_schema)
13391340
if scope:
13401341
self.resolver.push_scope(scope)
@@ -1344,7 +1345,7 @@ def iter_errors(self, instance, _schema=None):
13441345
ref = _schema.pop("$ref")
13451346
validators.append(("$ref", ref))
13461347

1347-
validators.extend(jsonschema.validators.iteritems(_schema))
1348+
validators.extend(superannotate_schemas.validators.iteritems(_schema))
13481349

13491350
for k, v in validators:
13501351
validator = self.VALIDATORS.get(k)
@@ -1381,7 +1382,7 @@ def extract_path(path):
13811382
real_path.append(item)
13821383
return real_path
13831384

1384-
def _get_validator(self, version: str) -> Draft7Validator:
1385+
def _get_validator(self, version: str) -> superannotate_schemas.Draft7Validator:
13851386
key = f"{self._project_type}__{version}"
13861387
validator = ValidateAnnotationUseCase.SCHEMAS.get(key)
13871388
if not validator:
@@ -1393,7 +1394,7 @@ def _get_validator(self, version: str) -> Draft7Validator:
13931394
if not schema_response.data:
13941395
ValidateAnnotationUseCase.SCHEMAS[key] = lambda x: x
13951396
return ValidateAnnotationUseCase.SCHEMAS[key]
1396-
validator = jsonschema.Draft7Validator(schema_response.data)
1397+
validator = superannotate_schemas.Draft7Validator(schema_response.data)
13971398
from functools import partial
13981399

13991400
iter_errors = partial(self.iter_errors, validator)

src/superannotate/lib/infrastructure/services/http_client.py

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import io
23
import json
34
import logging
45
import platform
@@ -13,7 +14,6 @@
1314

1415
import aiohttp
1516
import requests
16-
from aiohttp.client_exceptions import ClientError
1717
from lib.core.exceptions import AppException
1818
from lib.core.service_types import ServiceResponse
1919
from lib.core.serviceproviders import BaseClient
@@ -229,19 +229,39 @@ class AIOHttpSession(aiohttp.ClientSession):
229229
RETRY_LIMIT = 3
230230
BACKOFF_FACTOR = 0.3
231231

232-
async def request(self, *args, **kwargs) -> aiohttp.ClientResponse:
233-
attempts = self.RETRY_LIMIT
234-
delay = 0
235-
for _ in range(attempts):
236-
delay += self.BACKOFF_FACTOR
237-
attempts -= 1
238-
try:
239-
response = await super()._request(*args, **kwargs)
240-
except ClientError:
241-
if not attempts:
242-
raise
232+
class AIOHttpSession(aiohttp.ClientSession):
233+
RETRY_STATUS_CODES = [401, 403, 502, 503, 504]
234+
RETRY_LIMIT = 3
235+
BACKOFF_FACTOR = 0.3
236+
237+
@staticmethod
238+
def _copy_form_data(data: aiohttp.FormData) -> aiohttp.FormData:
239+
form_data = aiohttp.FormData(quote_fields=False)
240+
for field in data._fields: # noqa
241+
if isinstance(field[2], io.IOBase):
242+
field[2].seek(0)
243+
form_data.add_field(
244+
value=field[2],
245+
content_type=field[1].get("Content-Type", ""),
246+
**field[0],
247+
)
248+
return form_data
249+
250+
async def request(self, *args, **kwargs) -> aiohttp.ClientResponse:
251+
attempts = self.RETRY_LIMIT
252+
delay = 0
253+
for _ in range(attempts):
254+
delay += self.BACKOFF_FACTOR
255+
try:
256+
response = await super()._request(*args, **kwargs)
257+
if attempts <= 1 or response.status not in self.RETRY_STATUS_CODES:
258+
return response
259+
except (aiohttp.ClientError, RuntimeError) as e:
260+
if attempts <= 1:
261+
raise
262+
if isinstance(e, RuntimeError):
263+
data = kwargs["data"]
264+
if isinstance(data, aiohttp.FormData):
265+
kwargs["data"] = self._copy_form_data(data)
266+
attempts -= 1
243267
await asyncio.sleep(delay)
244-
continue
245-
if response.status not in self.RETRY_STATUS_CODES or not attempts:
246-
return response
247-
await asyncio.sleep(delay)

tests/integration/classes/test_create_annotation_class.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def test_create_annotation_class_with_attr_and_default_value(self):
4444
attribute_groups=[
4545
{
4646
"name": "test",
47+
"isRequired:": False,
4748
"attributes": [{"name": "Car"}, {"name": "Track"}, {"name": "Bus"}],
4849
"default_value": "Bus",
4950
}

tests/integration/export/test_export.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,4 @@ def test_upload_s3(self):
106106
Bucket=self.TEST_S3_BUCKET, Prefix=self.TMP_DIR
107107
).get("Contents", []):
108108
files.append(object_data["Key"])
109-
self.assertEqual(33, len(files))
109+
self.assertEqual(25, len(files))

tests/integration/projects/test_basic_project.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111
sa = SAClient()
1212

1313

14+
class TestGenAIProjectBasic(BaseTestCase):
15+
PROJECT_NAME = "TestGenAICreate"
16+
PROJECT_TYPE = "GenAI"
17+
PROJECT_DESCRIPTION = "DESCRIPTION"
18+
19+
def test_search(self):
20+
projects = sa.search_projects(self.PROJECT_NAME, return_metadata=True)
21+
assert projects
22+
23+
1424
class TestProjectBasic(BaseTestCase):
1525
PROJECT_NAME = "TestWorkflowGet"
1626
PROJECT_TYPE = "Vector"

0 commit comments

Comments
 (0)