scale-nucleus 0.1.22__py3-none-any.whl → 0.1.24__py3-none-any.whl

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.
nucleus/__init__.py CHANGED
@@ -7,8 +7,7 @@ import asyncio
7
7
  import json
8
8
  import logging
9
9
  import os
10
- import urllib.request
11
- from asyncio.tasks import Task
10
+ import time
12
11
  from typing import Any, Dict, List, Optional, Union
13
12
 
14
13
  import aiohttp
@@ -26,6 +25,7 @@ from .annotation import (
26
25
  Point,
27
26
  Point3D,
28
27
  PolygonAnnotation,
28
+ CategoryAnnotation,
29
29
  Segment,
30
30
  SegmentationAnnotation,
31
31
  )
@@ -41,15 +41,15 @@ from .constants import (
41
41
  ERROR_ITEMS,
42
42
  ERROR_PAYLOAD,
43
43
  ERRORS_KEY,
44
- JOB_ID_KEY,
45
- JOB_LAST_KNOWN_STATUS_KEY,
46
- JOB_TYPE_KEY,
47
- JOB_CREATION_TIME_KEY,
48
44
  IMAGE_KEY,
49
45
  IMAGE_URL_KEY,
50
46
  INDEX_CONTINUOUS_ENABLE_KEY,
51
47
  ITEM_METADATA_SCHEMA_KEY,
52
48
  ITEMS_KEY,
49
+ JOB_CREATION_TIME_KEY,
50
+ JOB_ID_KEY,
51
+ JOB_LAST_KNOWN_STATUS_KEY,
52
+ JOB_TYPE_KEY,
53
53
  KEEP_HISTORY_KEY,
54
54
  MESSAGE_KEY,
55
55
  MODEL_RUN_ID_KEY,
@@ -63,7 +63,7 @@ from .constants import (
63
63
  UPDATE_KEY,
64
64
  )
65
65
  from .dataset import Dataset
66
- from .dataset_item import DatasetItem, CameraParams, Quaternion
66
+ from .dataset_item import CameraParams, DatasetItem, Quaternion
67
67
  from .errors import (
68
68
  DatasetItemRetrievalError,
69
69
  ModelCreationError,
@@ -87,9 +87,9 @@ from .prediction import (
87
87
  PolygonPrediction,
88
88
  SegmentationPrediction,
89
89
  )
90
+ from .scene import Frame, LidarScene
90
91
  from .slice import Slice
91
92
  from .upload_response import UploadResponse
92
- from .scene import Frame, LidarScene
93
93
 
94
94
  # pylint: disable=E1101
95
95
  # TODO: refactor to reduce this file to under 1000 lines.
@@ -105,6 +105,11 @@ logging.getLogger(requests.packages.urllib3.__package__).setLevel(
105
105
  )
106
106
 
107
107
 
108
+ class RetryStrategy:
109
+ statuses = {503, 504}
110
+ sleep_times = [1, 3, 9]
111
+
112
+
108
113
  class NucleusClient:
109
114
  """
110
115
  Nucleus client.
@@ -511,28 +516,41 @@ class NucleusClient:
511
516
  content_type=file[1][2],
512
517
  )
513
518
 
514
- async with session.post(
515
- endpoint,
516
- data=form,
517
- auth=aiohttp.BasicAuth(self.api_key, ""),
518
- timeout=DEFAULT_NETWORK_TIMEOUT_SEC,
519
- ) as response:
520
- logger.info("API request has response code %s", response.status)
521
-
522
- try:
523
- data = await response.json()
524
- except aiohttp.client_exceptions.ContentTypeError:
525
- # In case of 404, the server returns text
526
- data = await response.text()
527
-
528
- if not response.ok:
529
- self.handle_bad_response(
530
- endpoint,
531
- session.post,
532
- aiohttp_response=(response.status, response.reason, data),
519
+ for sleep_time in RetryStrategy.sleep_times + [-1]:
520
+ async with session.post(
521
+ endpoint,
522
+ data=form,
523
+ auth=aiohttp.BasicAuth(self.api_key, ""),
524
+ timeout=DEFAULT_NETWORK_TIMEOUT_SEC,
525
+ ) as response:
526
+ logger.info(
527
+ "API request has response code %s", response.status
533
528
  )
534
529
 
535
- return data
530
+ try:
531
+ data = await response.json()
532
+ except aiohttp.client_exceptions.ContentTypeError:
533
+ # In case of 404, the server returns text
534
+ data = await response.text()
535
+ if (
536
+ response.status in RetryStrategy.statuses
537
+ and sleep_time != -1
538
+ ):
539
+ time.sleep(sleep_time)
540
+ continue
541
+
542
+ if not response.ok:
543
+ self.handle_bad_response(
544
+ endpoint,
545
+ session.post,
546
+ aiohttp_response=(
547
+ response.status,
548
+ response.reason,
549
+ data,
550
+ ),
551
+ )
552
+
553
+ return data
536
554
 
537
555
  def _process_append_requests(
538
556
  self,
@@ -564,6 +582,7 @@ class NucleusClient:
564
582
  BoxAnnotation,
565
583
  PolygonAnnotation,
566
584
  CuboidAnnotation,
585
+ CategoryAnnotation,
567
586
  SegmentationAnnotation,
568
587
  ]
569
588
  ],
@@ -1130,13 +1149,6 @@ class NucleusClient:
1130
1149
  requests_command=requests.post,
1131
1150
  )
1132
1151
 
1133
- def check_index_status(self, job_id: str):
1134
- return self.make_request(
1135
- {},
1136
- f"indexing/{job_id}",
1137
- requests_command=requests.get,
1138
- )
1139
-
1140
1152
  def delete_custom_index(self, dataset_id: str):
1141
1153
  return self.make_request(
1142
1154
  {},
@@ -1191,14 +1203,20 @@ class NucleusClient:
1191
1203
 
1192
1204
  logger.info("Posting to %s", endpoint)
1193
1205
 
1194
- response = requests_command(
1195
- endpoint,
1196
- json=payload,
1197
- headers={"Content-Type": "application/json"},
1198
- auth=(self.api_key, ""),
1199
- timeout=DEFAULT_NETWORK_TIMEOUT_SEC,
1200
- )
1201
- logger.info("API request has response code %s", response.status_code)
1206
+ for retry_wait_time in RetryStrategy.sleep_times:
1207
+ response = requests_command(
1208
+ endpoint,
1209
+ json=payload,
1210
+ headers={"Content-Type": "application/json"},
1211
+ auth=(self.api_key, ""),
1212
+ timeout=DEFAULT_NETWORK_TIMEOUT_SEC,
1213
+ )
1214
+ logger.info(
1215
+ "API request has response code %s", response.status_code
1216
+ )
1217
+ if response.status_code not in RetryStrategy.statuses:
1218
+ break
1219
+ time.sleep(retry_wait_time)
1202
1220
 
1203
1221
  if not response.ok:
1204
1222
  self.handle_bad_response(endpoint, requests_command, response)
nucleus/annotation.py CHANGED
@@ -8,6 +8,7 @@ from .constants import (
8
8
  ANNOTATION_ID_KEY,
9
9
  ANNOTATIONS_KEY,
10
10
  BOX_TYPE,
11
+ CATEGORY_TYPE,
11
12
  CUBOID_TYPE,
12
13
  DIMENSIONS_KEY,
13
14
  GEOMETRY_KEY,
@@ -20,6 +21,7 @@ from .constants import (
20
21
  POLYGON_TYPE,
21
22
  POSITION_KEY,
22
23
  REFERENCE_ID_KEY,
24
+ TAXONOMY_NAME_KEY,
23
25
  TYPE_KEY,
24
26
  VERTICES_KEY,
25
27
  WIDTH_KEY,
@@ -41,6 +43,8 @@ class Annotation:
41
43
  return PolygonAnnotation.from_json(payload)
42
44
  elif payload.get(TYPE_KEY, None) == CUBOID_TYPE:
43
45
  return CuboidAnnotation.from_json(payload)
46
+ elif payload.get(TYPE_KEY, None) == CATEGORY_TYPE:
47
+ return CategoryAnnotation.from_json(payload)
44
48
  else:
45
49
  return SegmentationAnnotation.from_json(payload)
46
50
 
@@ -120,6 +124,7 @@ class AnnotationTypes(Enum):
120
124
  BOX = BOX_TYPE
121
125
  POLYGON = POLYGON_TYPE
122
126
  CUBOID = CUBOID_TYPE
127
+ CATEGORY = CATEGORY_TYPE
123
128
 
124
129
 
125
130
  @dataclass # pylint: disable=R0902
@@ -291,6 +296,36 @@ class CuboidAnnotation(Annotation): # pylint: disable=R0902
291
296
  return payload
292
297
 
293
298
 
299
+ @dataclass
300
+ class CategoryAnnotation(Annotation):
301
+ label: str
302
+ taxonomy_name: str
303
+ reference_id: str
304
+ metadata: Optional[Dict] = None
305
+
306
+ def __post_init__(self):
307
+ self.metadata = self.metadata if self.metadata else {}
308
+
309
+ @classmethod
310
+ def from_json(cls, payload: dict):
311
+ return cls(
312
+ label=payload[LABEL_KEY],
313
+ taxonomy_name=payload[TAXONOMY_NAME_KEY],
314
+ reference_id=payload[REFERENCE_ID_KEY],
315
+ metadata=payload.get(METADATA_KEY, {}),
316
+ )
317
+
318
+ def to_payload(self) -> dict:
319
+ return {
320
+ LABEL_KEY: self.label,
321
+ TAXONOMY_NAME_KEY: self.taxonomy_name,
322
+ TYPE_KEY: CATEGORY_TYPE,
323
+ GEOMETRY_KEY: {},
324
+ REFERENCE_ID_KEY: self.reference_id,
325
+ METADATA_KEY: self.metadata,
326
+ }
327
+
328
+
294
329
  def is_local_path(path: str) -> bool:
295
330
  return urlparse(path).scheme not in {"https", "http", "s3", "gs"}
296
331
 
nucleus/constants.py CHANGED
@@ -10,7 +10,13 @@ SEGMENTATION_TYPE = "segmentation"
10
10
  CUBOID_TYPE = "cuboid"
11
11
  CATEGORY_TYPE = "category"
12
12
  MULTICATEGORY_TYPE = "multicategory"
13
- ANNOTATION_TYPES = (BOX_TYPE, POLYGON_TYPE, SEGMENTATION_TYPE, CUBOID_TYPE)
13
+ ANNOTATION_TYPES = (
14
+ BOX_TYPE,
15
+ POLYGON_TYPE,
16
+ SEGMENTATION_TYPE,
17
+ CUBOID_TYPE,
18
+ CATEGORY_TYPE,
19
+ )
14
20
  ANNOTATION_UPDATE_KEY = "update"
15
21
  AUTOTAGS_KEY = "autotags"
16
22
  AUTOTAG_SCORE_THRESHOLD = "score_threshold"
nucleus/dataset.py CHANGED
@@ -217,7 +217,6 @@ class Dataset:
217
217
  route=f"dataset/{self.id}/annotate?async=1",
218
218
  )
219
219
  return AsyncJob.from_json(response, self._client)
220
-
221
220
  return self._client.annotate_dataset(
222
221
  self.id, annotations, update=update, batch_size=batch_size
223
222
  )
@@ -428,10 +427,13 @@ class Dataset:
428
427
  return self._client.list_autotags(self.id)
429
428
 
430
429
  def create_custom_index(self, embeddings_urls: list, embedding_dim: int):
431
- return self._client.create_custom_index(
432
- self.id,
433
- embeddings_urls,
434
- embedding_dim,
430
+ return AsyncJob.from_json(
431
+ self._client.create_custom_index(
432
+ self.id,
433
+ embeddings_urls,
434
+ embedding_dim,
435
+ ),
436
+ self._client,
435
437
  )
436
438
 
437
439
  def delete_custom_index(self):
@@ -463,9 +465,6 @@ class Dataset:
463
465
  requests_command=requests.post,
464
466
  )
465
467
 
466
- def check_index_status(self, job_id: str):
467
- return self._client.check_index_status(job_id)
468
-
469
468
  def items_and_annotations(
470
469
  self,
471
470
  ) -> List[Dict[str, Union[DatasetItem, Dict[str, List[Annotation]]]]]:
nucleus/errors.py CHANGED
@@ -4,6 +4,11 @@ nucleus_client_version = pkg_resources.get_distribution(
4
4
  "scale-nucleus"
5
5
  ).version
6
6
 
7
+ INFRA_FLAKE_MESSAGES = [
8
+ "downstream duration timeout",
9
+ "upstream connect error or disconnect/reset before headers. reset reason: local reset",
10
+ ]
11
+
7
12
 
8
13
  class ModelCreationError(Exception):
9
14
  def __init__(self, message="Could not create the model"):
@@ -35,7 +40,7 @@ class NucleusAPIError(Exception):
35
40
  def __init__(
36
41
  self, endpoint, command, requests_response=None, aiohttp_response=None
37
42
  ):
38
- message = f"Your client is on version {nucleus_client_version}. Before reporting this error, please make sure you update to the latest version of the client by running pip install --upgrade scale-nucleus\n"
43
+ message = f"Your client is on version {nucleus_client_version}. If you have not recently done so, please make sure you have updated to the latest version of the client by running pip install --upgrade scale-nucleus\n"
39
44
  if requests_response is not None:
40
45
  message += f"Tried to {command.__name__} {endpoint}, but received {requests_response.status_code}: {requests_response.reason}."
41
46
  if hasattr(requests_response, "text"):
@@ -50,4 +55,10 @@ class NucleusAPIError(Exception):
50
55
  if data:
51
56
  message += f"\nThe detailed error is:\n{data}"
52
57
 
58
+ if any(
59
+ infra_flake_message in message
60
+ for infra_flake_message in INFRA_FLAKE_MESSAGES
61
+ ):
62
+ message += "\n This likely indicates temporary downtime of the API, please try again in a minute or two"
63
+
53
64
  super().__init__(message)
@@ -5,6 +5,7 @@ from .annotation import (
5
5
  BoxAnnotation,
6
6
  CuboidAnnotation,
7
7
  PolygonAnnotation,
8
+ CategoryAnnotation,
8
9
  SegmentationAnnotation,
9
10
  )
10
11
  from .prediction import (
@@ -60,6 +61,7 @@ def construct_annotation_payload(
60
61
  BoxAnnotation,
61
62
  PolygonAnnotation,
62
63
  CuboidAnnotation,
64
+ CategoryAnnotation,
63
65
  SegmentationAnnotation,
64
66
  ]
65
67
  ],
nucleus/utils.py CHANGED
@@ -14,6 +14,7 @@ from nucleus.annotation import (
14
14
  BoxAnnotation,
15
15
  CuboidAnnotation,
16
16
  PolygonAnnotation,
17
+ CategoryAnnotation,
17
18
  SegmentationAnnotation,
18
19
  )
19
20
 
@@ -22,6 +23,7 @@ from .constants import (
22
23
  ANNOTATIONS_KEY,
23
24
  BOX_TYPE,
24
25
  CUBOID_TYPE,
26
+ CATEGORY_TYPE,
25
27
  ITEM_KEY,
26
28
  POLYGON_TYPE,
27
29
  REFERENCE_ID_KEY,
@@ -116,6 +118,11 @@ def convert_export_payload(api_payload):
116
118
  for cuboid in row[CUBOID_TYPE]:
117
119
  cuboid[REFERENCE_ID_KEY] = row[ITEM_KEY][REFERENCE_ID_KEY]
118
120
  annotations[CUBOID_TYPE].append(CuboidAnnotation.from_json(cuboid))
121
+ for category in row[CATEGORY_TYPE]:
122
+ category[REFERENCE_ID_KEY] = row[ITEM_KEY][REFERENCE_ID_KEY]
123
+ annotations[CATEGORY_TYPE].append(
124
+ CategoryAnnotation.from_json(category)
125
+ )
119
126
  return_payload_row[ANNOTATIONS_KEY] = annotations
120
127
  return_payload.append(return_payload_row)
121
128
  return return_payload
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scale-nucleus
3
- Version: 0.1.22
3
+ Version: 0.1.24
4
4
  Summary: The official Python client library for Nucleus, the Data Platform for AI
5
5
  Home-page: https://scale.com/nucleus
6
6
  License: MIT
@@ -0,0 +1,21 @@
1
+ nucleus/__init__.py,sha256=105pVyWKhc34vRxhXTFbL9APvyH9Ka6FWOMOCElFsp8,40780
2
+ nucleus/annotation.py,sha256=tjkO_DCJIXQTTMI9gkWXe9W3lveyFsIQjlsM5jfyFyw,10007
3
+ nucleus/autocurate.py,sha256=ogEX3kbuKCciWODOnTjUHU-JSwhQ_34wbNvW4xA79oY,854
4
+ nucleus/constants.py,sha256=86tEkPqITYgd3SB_OWcG5LDcuAUGuc78kBtS5WOqo64,3026
5
+ nucleus/dataset.py,sha256=0amQbRnC3JbcDz_coJNvQsZsmfp41EYiqbXEtVh_m00,18290
6
+ nucleus/dataset_item.py,sha256=lKMMwNH9Iz5jxf1beIJSWrcD1UYNXbMbnPwenVW1He0,5781
7
+ nucleus/errors.py,sha256=quBOj9Dwi8NrC6SIqSI6DLv-fT49e315OSLirSiF4kQ,2338
8
+ nucleus/job.py,sha256=N2Ei3zJflcUyiZBavJOph3eLvckLANMrL7SwYzLUYAA,2301
9
+ nucleus/model.py,sha256=akuWKehw6u5fp-FfBuI2RobkSoceNN-huh9_G3rxWPo,2147
10
+ nucleus/model_run.py,sha256=-m_YzEqv253foD_ZQAIvD66CuDipvtKedzq9Pk0IBs4,7983
11
+ nucleus/payload_constructor.py,sha256=UN9J0NEL6gJqh-EAvwEc51eXJSTaK9ZMH1p0FDgMDsI,3567
12
+ nucleus/prediction.py,sha256=WJu5echvJKBjL67lQ6U9jM_LlbXvA1SPhUHyzdTeVpE,6276
13
+ nucleus/scene.py,sha256=w8mNU5Pt7U-jn9WQCL4Ch7AaZ2RHVPW8nTtIhlqTx0k,7803
14
+ nucleus/slice.py,sha256=zVLF6YyxU0ShJTERGTydcm1XiEx1yaVfJ1coq4H5KrI,5737
15
+ nucleus/upload_response.py,sha256=pwOb3iS6TbpoumC1Mao6Pyli7dXBRDcI0zjNfCMU4_c,2729
16
+ nucleus/url_utils.py,sha256=6iODEEVAa061-ROkqYM_Zhc4RbPHqOSYMczqYGVv4y0,660
17
+ nucleus/utils.py,sha256=WDBx8tw5MEFA1afS9Z0difBi6SQCk56SJX-hfDkBq5k,6194
18
+ scale_nucleus-0.1.24.dist-info/LICENSE,sha256=jaTGyQSQIZeWMo5iyYqgbAYHR9Bdy7nOzgE-Up3m_-g,1075
19
+ scale_nucleus-0.1.24.dist-info/WHEEL,sha256=DRf8A_Psd1SF2kVqTQOOFU1Xzl3-A2qljAxBMTOusUs,83
20
+ scale_nucleus-0.1.24.dist-info/METADATA,sha256=sxWeNc6pC9LBbOll4dfwRyqymKYOljHVHy8LslAoZvM,2656
21
+ scale_nucleus-0.1.24.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry 1.0.4
2
+ Generator: poetry 1.0.6
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,21 +0,0 @@
1
- nucleus/__init__.py,sha256=PRe5fr-9tPNkYDhqiGmJrEePcmd8h4JgbDUOFsXn7W8,40120
2
- nucleus/annotation.py,sha256=csyeeC_C50CCvR7J6BXJpfc85JfLxP3FX5QqABRtwm0,8992
3
- nucleus/autocurate.py,sha256=ogEX3kbuKCciWODOnTjUHU-JSwhQ_34wbNvW4xA79oY,854
4
- nucleus/constants.py,sha256=kjpahNP4_gRLWOdd0LXT_v6z3UYOQ3qbF2CX-37wJAc,2988
5
- nucleus/dataset.py,sha256=yRr6u7ucc-A0zjNmjbZbryjX7skivOvoUCckfLJqf5E,18309
6
- nucleus/dataset_item.py,sha256=lKMMwNH9Iz5jxf1beIJSWrcD1UYNXbMbnPwenVW1He0,5781
7
- nucleus/errors.py,sha256=XqBV5t0tEbV5yFM1RwZDjAvHWrkzsmTdprckzLZ6Kng,1924
8
- nucleus/job.py,sha256=N2Ei3zJflcUyiZBavJOph3eLvckLANMrL7SwYzLUYAA,2301
9
- nucleus/model.py,sha256=akuWKehw6u5fp-FfBuI2RobkSoceNN-huh9_G3rxWPo,2147
10
- nucleus/model_run.py,sha256=-m_YzEqv253foD_ZQAIvD66CuDipvtKedzq9Pk0IBs4,7983
11
- nucleus/payload_constructor.py,sha256=2O4jd774NBTOjDy_fBsN1SOTYKmtDkz4yYVPa_nuq-0,3511
12
- nucleus/prediction.py,sha256=WJu5echvJKBjL67lQ6U9jM_LlbXvA1SPhUHyzdTeVpE,6276
13
- nucleus/scene.py,sha256=w8mNU5Pt7U-jn9WQCL4Ch7AaZ2RHVPW8nTtIhlqTx0k,7803
14
- nucleus/slice.py,sha256=zVLF6YyxU0ShJTERGTydcm1XiEx1yaVfJ1coq4H5KrI,5737
15
- nucleus/upload_response.py,sha256=pwOb3iS6TbpoumC1Mao6Pyli7dXBRDcI0zjNfCMU4_c,2729
16
- nucleus/url_utils.py,sha256=6iODEEVAa061-ROkqYM_Zhc4RbPHqOSYMczqYGVv4y0,660
17
- nucleus/utils.py,sha256=sayZavDsvdPkvXm0QmvoSJ2_FZczje5n60uavHpOpgQ,5918
18
- scale_nucleus-0.1.22.dist-info/LICENSE,sha256=jaTGyQSQIZeWMo5iyYqgbAYHR9Bdy7nOzgE-Up3m_-g,1075
19
- scale_nucleus-0.1.22.dist-info/WHEEL,sha256=N0LZrBtofpkS5mJXgVHTCEy52Sam4D6PHQWC8HnMeTs,83
20
- scale_nucleus-0.1.22.dist-info/METADATA,sha256=Zh_k6vNFP6hkUn_FZgC0gPCL8Iwe2vSpBB2CM77mOSE,2656
21
- scale_nucleus-0.1.22.dist-info/RECORD,,