benchling-sdk 1.18.1__py3-none-any.whl → 1.19.0__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.
Files changed (27) hide show
  1. benchling_sdk/helpers/client_helpers.py +5 -0
  2. benchling_sdk/helpers/task_helpers.py +145 -0
  3. benchling_sdk/services/v2/alpha/v2_alpha_assembly_service.py +6 -4
  4. benchling_sdk/services/v2/base_service.py +11 -0
  5. benchling_sdk/services/v2/beta/v2_beta_audit_service.py +18 -13
  6. benchling_sdk/services/v2/beta/v2_beta_data_frame_service.py +9 -9
  7. benchling_sdk/services/v2/stable/aa_sequence_service.py +23 -13
  8. benchling_sdk/services/v2/stable/app_service.py +4 -2
  9. benchling_sdk/services/v2/stable/assay_result_service.py +3 -3
  10. benchling_sdk/services/v2/stable/container_service.py +14 -7
  11. benchling_sdk/services/v2/stable/custom_entity_service.py +15 -7
  12. benchling_sdk/services/v2/stable/dna_alignments_service.py +9 -5
  13. benchling_sdk/services/v2/stable/dna_oligo_service.py +13 -7
  14. benchling_sdk/services/v2/stable/dna_sequence_service.py +28 -19
  15. benchling_sdk/services/v2/stable/export_service.py +4 -4
  16. benchling_sdk/services/v2/stable/feature_library_service.py +6 -3
  17. benchling_sdk/services/v2/stable/lab_automation_service.py +5 -5
  18. benchling_sdk/services/v2/stable/nucleotide_alignments_service.py +5 -5
  19. benchling_sdk/services/v2/stable/oligo_service.py +4 -3
  20. benchling_sdk/services/v2/stable/registry_service.py +3 -3
  21. benchling_sdk/services/v2/stable/rna_oligo_service.py +13 -7
  22. benchling_sdk/services/v2/stable/rna_sequence_service.py +17 -11
  23. benchling_sdk/services/v2/stable/task_service.py +4 -0
  24. {benchling_sdk-1.18.1.dist-info → benchling_sdk-1.19.0.dist-info}/METADATA +2 -2
  25. {benchling_sdk-1.18.1.dist-info → benchling_sdk-1.19.0.dist-info}/RECORD +27 -26
  26. {benchling_sdk-1.18.1.dist-info → benchling_sdk-1.19.0.dist-info}/LICENSE +0 -0
  27. {benchling_sdk-1.18.1.dist-info → benchling_sdk-1.19.0.dist-info}/WHEEL +0 -0
@@ -11,6 +11,11 @@ def replace_client_path(client: Client, base_path: str) -> Client:
11
11
  return client.with_base_url(updated_url.geturl())
12
12
 
13
13
 
14
+ def v2_stable_client(client: Client) -> Client:
15
+ """Override a client's base URL with a v2 stable path."""
16
+ return replace_client_path(client, "api/v2")
17
+
18
+
14
19
  def v2_alpha_client(client: Client) -> Client:
15
20
  """Override a client's base URL with a v2-alpha path."""
16
21
  return replace_client_path(client, "api/v2-alpha")
@@ -0,0 +1,145 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, cast, Generic, Optional, Type, TypeVar
3
+
4
+ from benchling_api_client.v2.stable.client import Client
5
+
6
+ from benchling_sdk.helpers.serialization_helpers import unset_as_none
7
+ from benchling_sdk.models import AsyncTaskErrors, AsyncTaskLink, AsyncTaskStatus
8
+
9
+ ResponseT = TypeVar("ResponseT")
10
+
11
+
12
+ @dataclass
13
+ class TaskCompletion(Generic[ResponseT]):
14
+ """Return type for TaskHelper.wait_for_task, same as AsyncTask but with a typed response."""
15
+
16
+ success: bool
17
+ errors: Optional[AsyncTaskErrors] = None
18
+ message: Optional[str] = None
19
+ response: Optional[ResponseT] = None
20
+
21
+
22
+ @dataclass
23
+ class TaskFailureException(Exception):
24
+ """Exception type used by :py:class:`.TaskHelper` methods."""
25
+
26
+ errors: AsyncTaskErrors
27
+ message: Optional[str]
28
+
29
+
30
+ class EmptyTaskResponse:
31
+ """A sentinel object used for tasks that do not return any response data on completion."""
32
+
33
+ pass
34
+
35
+
36
+ EMPTY_TASK_RESPONSE = EmptyTaskResponse()
37
+
38
+
39
+ class TaskHelper(AsyncTaskLink, Generic[ResponseT]):
40
+ """Used by Benchling async task endpoints to provide the task response in an appropriate type.
41
+
42
+ In the API spec, endpoints that create a long-running task are defined as returning an
43
+ :py:class:`benchling_sdk.models.AsyncTaskLink`, which can be used with
44
+ :py:class:`benchling_sdk.services.v2.stable.TaskService` to query the task status and
45
+ response. But AsyncTaskLink and the task query endpoint do not define a specific schema
46
+ for the task response.
47
+
48
+ To work around that limitation, those SDK endpoints now return a TaskHelper instead. This is
49
+ subclassed from AsyncTaskLink for backward compatibility, but unlike AsyncTaskLink, TaskHelper
50
+ knows what the type of the task response should be. It also retains an association with the API
51
+ client, so rather than calling a separate service method, you can simply call
52
+ :py:meth:`.TaskHelper.wait_for_completion` or :py:meth:`.TaskHelper.wait_for_response`.
53
+
54
+ You can access a task for up to 30 minutes after its completion, after which its data will no
55
+ longer be available.
56
+ """
57
+
58
+ _client: Client
59
+ _response_class: Type[ResponseT]
60
+ _response_decoder: Any
61
+ # _response_decoder is really a Callable, but adding that type hint would cause problems because
62
+ # mypy would treat it as an instance method and assume it should have a self parameter.
63
+
64
+ def __init__(self, from_task_link: AsyncTaskLink, client: Client, response_class: Type[ResponseT]):
65
+ """Initialize the instance. This should only be used internally by Benchling API methods."""
66
+ self.task_id = from_task_link.task_id
67
+ self.additional_properties = from_task_link.additional_properties
68
+ self._client = client
69
+ self._response_class = response_class
70
+
71
+ if response_class is not EmptyTaskResponse:
72
+ assert hasattr(response_class, "from_dict")
73
+ self._response_decoder = getattr(response_class, "from_dict")
74
+
75
+ def wait_for_completion(
76
+ self, interval_wait_seconds: int = 1, max_wait_seconds: int = 600
77
+ ) -> TaskCompletion[ResponseT]:
78
+ """Wait for the task to succeed or fail.
79
+
80
+ This is equivalent to the :py:meth:`benchling_sdk.services.v2.stable.task_service.TaskService.wait_for_task`
81
+ method in :py:class:`benchling_sdk.services.v2.stable.task_service.TaskService`, except that
82
+ instead of returning an :py:class:`benchling_sdk.models.AsyncTask` whose `response` property is
83
+ a dict, it returns a :py:class:`TaskCompletion` whose `response` property is the appropriate type
84
+ for the API method you called. For instance, if the method was `AaSequenceService.bulk_create`,
85
+ the response type will be :py:class:`benchling_sdk.models.BulkCreateAaSequencesAsyncTaskResponse`.
86
+
87
+ :param interval_wait_seconds: time to wait between API calls in seconds
88
+ :param max_wait_seconds: maximum wait time in seconds before raising an error
89
+ :return: The task completion status. Check `status` for success or failure
90
+ :rtype: TaskCompletion
91
+ :raises benchling_sdk.errors.WaitForTaskExpiredError: if the maximum wait time has elapsed
92
+ """
93
+ from benchling_sdk.helpers.client_helpers import v2_stable_client
94
+ from benchling_sdk.services.v2.stable.task_service import TaskService
95
+
96
+ # The "get task" polling endpoint is part of the v2 stable API; it doesn't exist in alpha or beta.
97
+ task_service = TaskService(v2_stable_client(self._client))
98
+
99
+ task = task_service.wait_for_task(
100
+ self.task_id, interval_wait_seconds=interval_wait_seconds, max_wait_seconds=max_wait_seconds
101
+ )
102
+ response: Optional[ResponseT]
103
+ if task.status != AsyncTaskStatus.SUCCEEDED:
104
+ response = None
105
+ elif self._response_class is EmptyTaskResponse:
106
+ response = cast(ResponseT, EMPTY_TASK_RESPONSE)
107
+ else:
108
+ response = self._response_decoder(task.response.to_dict())
109
+ errors = None if task.status != AsyncTaskStatus.FAILED else unset_as_none(task.errors)
110
+ message = None if task.status != AsyncTaskStatus.FAILED else unset_as_none(task.message)
111
+ return TaskCompletion(
112
+ success=task.status == AsyncTaskStatus.SUCCEEDED,
113
+ errors=errors,
114
+ message=message,
115
+ response=response,
116
+ )
117
+
118
+ def wait_for_response(self, interval_wait_seconds: int = 1, max_wait_seconds: int = 600) -> ResponseT:
119
+ """Wait for the task and return the response object on success, or raise an exception on failure.
120
+
121
+ This is a convenience method for calling :py:meth:`wait_for_completion` and then getting the
122
+ `response` property of the returned object if the task succeeded, in cases where you're not
123
+ interested in any :py:class:`.TaskCompletion` properties except the response.
124
+
125
+ The type of the returned object is depends on the API method you called. For instance, if the
126
+ method was `AaSequenceService.bulk_create`, the response type will be
127
+ :py:class:`benchling_sdk.models.BulkCreateAaSequencesAsyncTaskResponse`.
128
+
129
+ :param interval_wait_seconds: time to wait between API calls in seconds
130
+ :param max_wait_seconds: maximum wait time in seconds before raising an error
131
+ :return: The response object, if the task succeeded.
132
+ :rtype: ResponseT
133
+ :raises benchling_sdk.errors.WaitForTaskExpiredError: if the maximum wait time has elapsed
134
+ :raises .TaskFailureException: if the task failed
135
+ """
136
+ task_completion = self.wait_for_completion(interval_wait_seconds, max_wait_seconds)
137
+ if not task_completion.success:
138
+ raise TaskFailureException(task_completion.errors or AsyncTaskErrors(), task_completion.message)
139
+ assert task_completion.response, "completed task should have contained a response"
140
+ # This assertion matches the behavior of TaskHelper.wait_for_completion(): response is only set
141
+ # to None if the task failed, otherwise it should always be an object even if that object is an
142
+ # EmptyTaskResponse. This also matches how we define async task models in the API spec, where
143
+ # if we don't care about the response field, it's an empty object rather than null.
144
+
145
+ return task_completion.response
@@ -11,6 +11,7 @@ from benchling_api_client.v2.alpha.models.create_and_finalize_assembly_json_body
11
11
 
12
12
  from benchling_sdk.helpers.decorators import api_method
13
13
  from benchling_sdk.helpers.response_helpers import model_from_detailed
14
+ from benchling_sdk.helpers.task_helpers import TaskHelper
14
15
  from benchling_sdk.models import AsyncTaskLink
15
16
  from benchling_sdk.services.v2.base_service import BaseService
16
17
 
@@ -36,17 +37,18 @@ class V2AlphaAssemblyService(BaseService):
36
37
  return model_from_detailed(response)
37
38
 
38
39
  @api_method
39
- def create_and_finalize(self, create: CreateAndFinalizeAssemblyJsonBody) -> AsyncTaskLink:
40
+ def create_and_finalize(self, create: CreateAndFinalizeAssemblyJsonBody) -> TaskHelper[Assembly]:
40
41
  """
41
42
  Create and finalize a new bulk assembly in a single step.
42
43
 
43
- This endpoint launches a long-running task and returns the Task ID of the launched task.
44
- The task response contains a finalized bulk assembly.
44
+ This endpoint launches a long-running task and returns a
45
+ :py:class:`benchling_sdk.helpers.task_helpers.TaskHelper` for waiting on the task.
46
+ On success, the task's response will be an :py:class:`.Assembly`.
45
47
 
46
48
  See https://benchling.com/api/v2-alpha/reference#/Assemblies/CreateAndFinalizeAssembly
47
49
  """
48
50
  response = create_and_finalize_assembly.sync_detailed(client=self.client, json_body=create)
49
- return model_from_detailed(response)
51
+ return self._task_helper_from_response(response, Assembly)
50
52
 
51
53
  @api_method
52
54
  def validate(self, assembly: AssemblySpecShared) -> AsyncTaskLink:
@@ -1,9 +1,15 @@
1
1
  from abc import ABC
2
+ from typing import Type, TYPE_CHECKING
2
3
 
3
4
  from benchling_api_client.v2.stable.client import Client
5
+ from benchling_api_client.v2.types import Response
4
6
 
7
+ from benchling_sdk.helpers.response_helpers import model_from_detailed
5
8
  from benchling_sdk.helpers.retry_helpers import RetryStrategy
6
9
 
10
+ if TYPE_CHECKING:
11
+ from benchling_sdk.helpers.task_helpers import TaskHelper
12
+
7
13
 
8
14
  class BaseService(ABC):
9
15
  """Abstract class for Benchling functional namespaces."""
@@ -37,3 +43,8 @@ class BaseService(ABC):
37
43
  Override this in alpha and beta services that use a client other than self._client.
38
44
  """
39
45
  return cls(self._client, self._retry_strategy)
46
+
47
+ def _task_helper_from_response(self, response: Response, result_type: Type) -> "TaskHelper":
48
+ from benchling_sdk.helpers.task_helpers import TaskHelper
49
+
50
+ return TaskHelper(model_from_detailed(response), self.client, result_type)
@@ -2,8 +2,8 @@ from benchling_api_client.v2.beta.api.audit import audit_log
2
2
  from benchling_api_client.v2.beta.models.audit_log_export import AuditLogExport
3
3
 
4
4
  from benchling_sdk.helpers.decorators import api_method
5
- from benchling_sdk.helpers.response_helpers import model_from_detailed
6
- from benchling_sdk.models import AsyncTaskLink
5
+ from benchling_sdk.helpers.task_helpers import TaskHelper
6
+ from benchling_sdk.models import ExportAuditLogAsyncTaskResponse
7
7
  from benchling_sdk.services.v2.base_service import BaseService
8
8
 
9
9
 
@@ -17,25 +17,30 @@ class V2BetaAuditService(BaseService):
17
17
  """
18
18
 
19
19
  @api_method
20
- def get_audit_log(self, object_id: str, export: AuditLogExport) -> AsyncTaskLink:
20
+ def get_audit_log(
21
+ self, object_id: str, export: AuditLogExport
22
+ ) -> TaskHelper[ExportAuditLogAsyncTaskResponse]:
21
23
  """
22
24
  Export an audit log file for a Benchling object.
23
25
 
24
- This endpoint launches a long-running task and returns the Task ID of the
25
- launched task. The task response contains a link to download the exported audit
26
- log file from Amazon S3. This endpoint is subject to a rate limit of 500 requests
27
- per hour, in conjunction with the global request rate limit. Export throughput
28
- will additionally be rate limited around the scale of 70,000 total audit events
29
- exported in csv format or 30,000 total audit events exported in pdf format per hour.
26
+ This endpoint launches a long-running task and returns a
27
+ :py:class:`benchling_sdk.helpers.task_helpers.TaskHelper`. On success, the task's
28
+ response will be an :py:class:`.ExportAuditLogAsyncTaskResponse` containing a link to
29
+ download the exported audit log file from Amazon S3.
30
+
31
+ This endpoint is subject to a rate limit of 500 requests per hour, in conjunction with
32
+ the global request rate limit. Export throughput will additionally be rate limited around
33
+ the scale of 70,000 total audit events exported in csv format or 30,000 total audit events
34
+ exported in pdf format per hour.
30
35
 
31
36
  Example of submitting an export request and then getting the download URL from
32
37
  the completed task:
33
38
 
34
- task_link = benchling.v2.beta.audit.get_audit_log(object_id, export)
35
- task = benchling.tasks.wait_for_task(task_link.task_id)
36
- url = task.response["downloadURL"]
39
+ task = benchling.v2.beta.audit.get_audit_log(object_id, export)
40
+ task_result = task.wait_for_response()
41
+ url = task_result.download_url
37
42
 
38
43
  See https://benchling.com/api/v2-beta/reference#/Audit/auditLog
39
44
  """
40
45
  response = audit_log.sync_detailed(client=self.client, object_id=object_id, json_body=export)
41
- return model_from_detailed(response)
46
+ return self._task_helper_from_response(response, ExportAuditLogAsyncTaskResponse)
@@ -19,7 +19,7 @@ import httpx
19
19
  from benchling_sdk.errors import DataFrameInProgressError, InvalidDataFrameError, raise_for_status
20
20
  from benchling_sdk.helpers.decorators import api_method
21
21
  from benchling_sdk.helpers.response_helpers import model_from_detailed
22
- from benchling_sdk.models import AsyncTaskLink
22
+ from benchling_sdk.helpers.task_helpers import TaskHelper
23
23
  from benchling_sdk.services.v2.base_service import BaseService
24
24
 
25
25
  _DEFAULT_HTTP_TIMEOUT_UPLOAD_DATA_FRAME: float = 60.0
@@ -55,7 +55,7 @@ class V2BetaDataFrameService(BaseService):
55
55
  return model_from_detailed(response)
56
56
 
57
57
  @api_method
58
- def update(self, data_frame_id: str, data_frame: DataFrameUpdate) -> AsyncTaskLink:
58
+ def update(self, data_frame_id: str, data_frame: DataFrameUpdate) -> TaskHelper[DataFrame]:
59
59
  """
60
60
  Update a data frame.
61
61
 
@@ -64,7 +64,7 @@ class V2BetaDataFrameService(BaseService):
64
64
  response = patch_data_frame.sync_detailed(
65
65
  client=self.client, data_frame_id=data_frame_id, json_body=data_frame
66
66
  )
67
- return model_from_detailed(response)
67
+ return self._task_helper_from_response(response, DataFrame)
68
68
 
69
69
  def upload_bytes(
70
70
  self,
@@ -114,15 +114,15 @@ class V2BetaDataFrameService(BaseService):
114
114
  data_frame: DataFrameCreate,
115
115
  input_bytes: Union[BytesIO, bytes],
116
116
  timeout_seconds: float = _DEFAULT_HTTP_TIMEOUT_UPLOAD_DATA_FRAME,
117
- ) -> AsyncTaskLink:
117
+ ) -> TaskHelper[DataFrame]:
118
118
  """Create a data frame from bytes or BytesIO data.
119
119
 
120
120
  :param data_frame: The DataFrameCreate specification for the data. This must be provided, as it cannot be inferred from file names.
121
121
  :param input_bytes: Data to upload as bytes or BytesIO
122
122
  :param timeout_seconds: Extends the normal HTTP timeout settings since DataFrame uploads can be large
123
123
  Use this to extend even further if streams are very large
124
- :return: An AsyncTaskLink that can be polled to know when the data frame has completed processing
125
- :rtype: AsyncTaskLink
124
+ :return: A TaskHelper that can be polled to know when the data frame has completed processing
125
+ :rtype: TaskHelper[DataFrame]
126
126
  """
127
127
  # This is a current limit of the DataFrame API. We may need additional methods in the future
128
128
  # to allow multi upload
@@ -150,15 +150,15 @@ class V2BetaDataFrameService(BaseService):
150
150
  file: Path,
151
151
  data_frame: Optional[DataFrameCreate] = None,
152
152
  timeout_seconds: float = _DEFAULT_HTTP_TIMEOUT_UPLOAD_DATA_FRAME,
153
- ) -> AsyncTaskLink:
153
+ ) -> TaskHelper[DataFrame]:
154
154
  """Create a data frame from file data.
155
155
 
156
156
  :param file: A valid Path to an existing file containing the data to upload
157
157
  :param data_frame: The DataFrameCreate specification for the data. If not provided, it will be inferred from the file name
158
158
  :param timeout_seconds: Extends the normal HTTP timeout settings since DataFrame uploads can be large
159
159
  Use this to extend even further if streams are very large
160
- :return: An AsyncTaskLink that can be polled to know when the data frame has completed processing
161
- :rtype: AsyncTaskLink
160
+ :return: A TaskHelper that can be polled to know when the data frame has completed processing
161
+ :rtype: TaskHelper[DataFrame]
162
162
  """
163
163
  if file.is_dir():
164
164
  raise IsADirectoryError(
@@ -32,6 +32,7 @@ from benchling_sdk.helpers.serialization_helpers import (
32
32
  optional_array_query_param,
33
33
  schema_fields_query_param,
34
34
  )
35
+ from benchling_sdk.helpers.task_helpers import EmptyTaskResponse, TaskHelper
35
36
  from benchling_sdk.models import (
36
37
  AaSequence,
37
38
  AaSequenceBulkCreate,
@@ -49,10 +50,13 @@ from benchling_sdk.models import (
49
50
  AaSequencesUnarchive,
50
51
  AaSequenceUpdate,
51
52
  AaSequenceUpsert,
52
- AsyncTaskLink,
53
53
  AutoAnnotateAaSequences,
54
54
  BackTranslate,
55
+ BulkCreateAaSequencesAsyncTaskResponse,
56
+ BulkCreateDnaSequencesAsyncTaskResponse,
57
+ BulkUpdateAaSequencesAsyncTaskResponse,
55
58
  EntityArchiveReason,
59
+ FindMatchingRegionsAsyncTaskResponse,
56
60
  ListAASequencesSort,
57
61
  )
58
62
  from benchling_sdk.services.v2.base_service import BaseService
@@ -264,7 +268,9 @@ class AaSequenceService(BaseService):
264
268
  return aa_sequences_results.aa_sequences
265
269
 
266
270
  @api_method
267
- def bulk_create(self, aa_sequences: Iterable[AaSequenceBulkCreate]) -> AsyncTaskLink:
271
+ def bulk_create(
272
+ self, aa_sequences: Iterable[AaSequenceBulkCreate]
273
+ ) -> TaskHelper[BulkCreateAaSequencesAsyncTaskResponse]:
268
274
  """
269
275
  Bulk create AA sequences.
270
276
 
@@ -272,10 +278,12 @@ class AaSequenceService(BaseService):
272
278
  """
273
279
  body = AaSequencesBulkCreateRequest(list(aa_sequences))
274
280
  response = bulk_create_aa_sequences.sync_detailed(client=self.client, json_body=body)
275
- return model_from_detailed(response)
281
+ return self._task_helper_from_response(response, BulkCreateAaSequencesAsyncTaskResponse)
276
282
 
277
283
  @api_method
278
- def bulk_update(self, aa_sequences: Iterable[AaSequenceBulkUpdate]) -> AsyncTaskLink:
284
+ def bulk_update(
285
+ self, aa_sequences: Iterable[AaSequenceBulkUpdate]
286
+ ) -> TaskHelper[BulkUpdateAaSequencesAsyncTaskResponse]:
279
287
  """
280
288
  Bulk update AA sequences.
281
289
 
@@ -283,17 +291,17 @@ class AaSequenceService(BaseService):
283
291
  """
284
292
  body = AaSequencesBulkUpdateRequest(list(aa_sequences))
285
293
  response = bulk_update_aa_sequences.sync_detailed(client=self.client, json_body=body)
286
- return model_from_detailed(response)
294
+ return self._task_helper_from_response(response, BulkUpdateAaSequencesAsyncTaskResponse)
287
295
 
288
296
  @api_method
289
- def auto_annotate(self, auto_annotate: AutoAnnotateAaSequences) -> AsyncTaskLink:
297
+ def auto_annotate(self, auto_annotate: AutoAnnotateAaSequences) -> TaskHelper[EmptyTaskResponse]:
290
298
  """
291
299
  Auto-annotate AA sequences with matching features from specified Feature Libraries.
292
300
 
293
301
  See https://benchling.com/api/reference#/AA%20Sequences/autoAnnotateAaSequences
294
302
  """
295
303
  response = auto_annotate_aa_sequences.sync_detailed(client=self.client, json_body=auto_annotate)
296
- return model_from_detailed(response)
304
+ return self._task_helper_from_response(response, EmptyTaskResponse)
297
305
 
298
306
  @api_method
299
307
  def upsert(self, entity_registry_id: str, aa_sequence: AaSequenceUpsert) -> AaSequence:
@@ -310,7 +318,7 @@ class AaSequenceService(BaseService):
310
318
  @api_method
311
319
  def bulk_upsert(
312
320
  self, body: AaSequencesBulkUpsertRequest, returning: Optional[Iterable[str]] = None
313
- ) -> AsyncTaskLink:
321
+ ) -> TaskHelper[BulkUpdateAaSequencesAsyncTaskResponse]:
314
322
  """
315
323
  Bulk create or update AA sequences.
316
324
 
@@ -320,10 +328,12 @@ class AaSequenceService(BaseService):
320
328
  response = bulk_upsert_aa_sequences.sync_detailed(
321
329
  client=self.client, json_body=body, returning=none_as_unset(returning_string)
322
330
  )
323
- return model_from_detailed(response)
331
+ return self._task_helper_from_response(response, BulkUpdateAaSequencesAsyncTaskResponse)
324
332
 
325
333
  @api_method
326
- def find_matching_regions(self, find_matching_region: AaSequencesFindMatchingRegion) -> AsyncTaskLink:
334
+ def find_matching_regions(
335
+ self, find_matching_region: AaSequencesFindMatchingRegion
336
+ ) -> TaskHelper[FindMatchingRegionsAsyncTaskResponse]:
327
337
  """
328
338
  Find matching regions for AA sequences.
329
339
 
@@ -332,17 +342,17 @@ class AaSequenceService(BaseService):
332
342
  response = find_matching_regions_aa_sequences.sync_detailed(
333
343
  client=self.client, json_body=find_matching_region
334
344
  )
335
- return model_from_detailed(response)
345
+ return self._task_helper_from_response(response, FindMatchingRegionsAsyncTaskResponse)
336
346
 
337
347
  @api_method
338
- def back_translate(self, translate: BackTranslate) -> AsyncTaskLink:
348
+ def back_translate(self, translate: BackTranslate) -> TaskHelper[BulkCreateDnaSequencesAsyncTaskResponse]:
339
349
  """
340
350
  Create back-translated DNA sequences from AA sequences.
341
351
 
342
352
  See https://benchling.com/api/v2/reference#/AA%20Sequences/backTranslate
343
353
  """
344
354
  response = back_translate.sync_detailed(client=self.client, json_body=translate)
345
- return model_from_detailed(response)
355
+ return self._task_helper_from_response(response, BulkCreateDnaSequencesAsyncTaskResponse)
346
356
 
347
357
  @api_method
348
358
  def _match_bases_page(self, match_bases: AaSequencesMatchBases) -> Response[AaSequencesPaginatedList]:
@@ -442,7 +442,7 @@ class AppService(BaseService):
442
442
  # return PageIterator(api_call, results_extractor)
443
443
 
444
444
  @api_method
445
- def get_canvas_by_id(self, canvas_id: str) -> AppCanvas:
445
+ def get_canvas_by_id(self, canvas_id: str, returning: Optional[List[str]] = None) -> AppCanvas:
446
446
  """
447
447
  Get the current state of the App Canvas, including user input elements.
448
448
 
@@ -451,6 +451,7 @@ class AppService(BaseService):
451
451
  response = get_app_canvas.sync_detailed(
452
452
  client=self.client,
453
453
  canvas_id=canvas_id,
454
+ returning=none_as_unset(optional_array_query_param(returning)),
454
455
  )
455
456
  return model_from_detailed(response)
456
457
 
@@ -511,7 +512,7 @@ class AppService(BaseService):
511
512
  return model_from_detailed(response)
512
513
 
513
514
  @api_method
514
- def get_session_by_id(self, session_id: str) -> AppSession:
515
+ def get_session_by_id(self, session_id: str, returning: Optional[List[str]] = None) -> AppSession:
515
516
  """
516
517
  Get a Benchling App session.
517
518
 
@@ -520,6 +521,7 @@ class AppService(BaseService):
520
521
  response = get_app_session_by_id.sync_detailed(
521
522
  client=self.client,
522
523
  id=session_id,
524
+ returning=none_as_unset(optional_array_query_param(returning)),
523
525
  )
524
526
  return model_from_detailed(response)
525
527
 
@@ -25,6 +25,7 @@ from benchling_sdk.helpers.serialization_helpers import (
25
25
  none_as_unset,
26
26
  optional_array_query_param,
27
27
  )
28
+ from benchling_sdk.helpers.task_helpers import TaskHelper
28
29
  from benchling_sdk.helpers.transaction_manager import TransactionManager
29
30
  from benchling_sdk.models import (
30
31
  AssayResult,
@@ -37,7 +38,6 @@ from benchling_sdk.models import (
37
38
  AssayResultsCreateResponse,
38
39
  AssayResultsPaginatedList,
39
40
  AssayResultTransactionCreateResponse,
40
- AsyncTaskLink,
41
41
  ListAssayResultsSort,
42
42
  )
43
43
  from benchling_sdk.services.v2.base_service import BaseService
@@ -202,7 +202,7 @@ class AssayResultService(BaseService):
202
202
  @api_method
203
203
  def bulk_create(
204
204
  self, assay_results: Iterable[AssayResultCreate], table_id: Optional[str] = None
205
- ) -> AsyncTaskLink:
205
+ ) -> TaskHelper[AssayResultsCreateResponse]:
206
206
  """
207
207
  Create 1 or more results.
208
208
 
@@ -212,7 +212,7 @@ class AssayResultService(BaseService):
212
212
  assay_results=list(assay_results), **({"table_id": table_id} if table_id else {})
213
213
  )
214
214
  response = bulk_create_assay_results.sync_detailed(client=self.client, json_body=request_body)
215
- return model_from_detailed(response)
215
+ return self._task_helper_from_response(response, AssayResultsCreateResponse)
216
216
 
217
217
  @api_method
218
218
  def archive(self, assay_result_ids: Iterable[str]) -> AssayResultIdsResponse:
@@ -34,8 +34,10 @@ from benchling_sdk.helpers.serialization_helpers import (
34
34
  optional_array_query_param,
35
35
  schema_fields_query_param,
36
36
  )
37
+ from benchling_sdk.helpers.task_helpers import TaskHelper
37
38
  from benchling_sdk.models import (
38
- AsyncTaskLink,
39
+ BulkCreateContainersAsyncTaskResponse,
40
+ BulkUpdateContainersAsyncTaskResponse,
39
41
  Container,
40
42
  ContainerBulkUpdateItem,
41
43
  ContainerContent,
@@ -59,6 +61,7 @@ from benchling_sdk.models import (
59
61
  MultipleContainersTransfersList,
60
62
  PrintLabels,
61
63
  SampleRestrictionStatus,
64
+ TransfersAsyncTaskResponse,
62
65
  )
63
66
  from benchling_sdk.services.v2.base_service import BaseService
64
67
 
@@ -264,7 +267,9 @@ class ContainerService(BaseService):
264
267
  return containers_list.containers
265
268
 
266
269
  @api_method
267
- def bulk_create(self, containers: Iterable[ContainerCreate]) -> AsyncTaskLink:
270
+ def bulk_create(
271
+ self, containers: Iterable[ContainerCreate]
272
+ ) -> TaskHelper[BulkCreateContainersAsyncTaskResponse]:
268
273
  """
269
274
  Bulk create containers.
270
275
 
@@ -272,10 +277,12 @@ class ContainerService(BaseService):
272
277
  """
273
278
  body = ContainersBulkCreateRequest(list(containers))
274
279
  response = bulk_create_containers.sync_detailed(client=self.client, json_body=body)
275
- return model_from_detailed(response)
280
+ return self._task_helper_from_response(response, BulkCreateContainersAsyncTaskResponse)
276
281
 
277
282
  @api_method
278
- def bulk_update(self, containers: Iterable[ContainerBulkUpdateItem]) -> AsyncTaskLink:
283
+ def bulk_update(
284
+ self, containers: Iterable[ContainerBulkUpdateItem]
285
+ ) -> TaskHelper[BulkUpdateContainersAsyncTaskResponse]:
279
286
  """
280
287
  Bulk update containers.
281
288
 
@@ -283,7 +290,7 @@ class ContainerService(BaseService):
283
290
  """
284
291
  body = ContainersBulkUpdateRequest(list(containers))
285
292
  response = bulk_update_containers.sync_detailed(client=self.client, json_body=body)
286
- return model_from_detailed(response)
293
+ return self._task_helper_from_response(response, BulkUpdateContainersAsyncTaskResponse)
287
294
 
288
295
  @api_method
289
296
  def create(self, container: ContainerCreate) -> Container:
@@ -440,7 +447,7 @@ class ContainerService(BaseService):
440
447
  @api_method
441
448
  def transfer_into_containers(
442
449
  self, transfer_requests: Iterable[MultipleContainersTransfer]
443
- ) -> AsyncTaskLink:
450
+ ) -> TaskHelper[TransfersAsyncTaskResponse]:
444
451
  """
445
452
  Transfer into containers.
446
453
 
@@ -448,4 +455,4 @@ class ContainerService(BaseService):
448
455
  """
449
456
  multiple_requests = MultipleContainersTransfersList(transfers=list(transfer_requests))
450
457
  response = transfer_into_containers.sync_detailed(client=self.client, json_body=multiple_requests)
451
- return model_from_detailed(response)
458
+ return self._task_helper_from_response(response, TransfersAsyncTaskResponse)
@@ -26,8 +26,10 @@ from benchling_sdk.helpers.serialization_helpers import (
26
26
  optional_array_query_param,
27
27
  schema_fields_query_param,
28
28
  )
29
+ from benchling_sdk.helpers.task_helpers import TaskHelper
29
30
  from benchling_sdk.models import (
30
- AsyncTaskLink,
31
+ BulkCreateCustomEntitiesAsyncTaskResponse,
32
+ BulkUpdateCustomEntitiesAsyncTaskResponse,
31
33
  CustomEntitiesArchivalChange,
32
34
  CustomEntitiesArchive,
33
35
  CustomEntitiesBulkCreateRequest,
@@ -250,7 +252,9 @@ class CustomEntityService(BaseService):
250
252
  return custom_entities.custom_entities
251
253
 
252
254
  @api_method
253
- def bulk_create(self, entities: Iterable[CustomEntityBulkCreate]) -> AsyncTaskLink:
255
+ def bulk_create(
256
+ self, entities: Iterable[CustomEntityBulkCreate]
257
+ ) -> TaskHelper[BulkCreateCustomEntitiesAsyncTaskResponse]:
254
258
  """
255
259
  Bulk create custom entities.
256
260
 
@@ -258,10 +262,12 @@ class CustomEntityService(BaseService):
258
262
  """
259
263
  body = CustomEntitiesBulkCreateRequest(list(entities))
260
264
  response = bulk_create_custom_entities.sync_detailed(client=self.client, json_body=body)
261
- return model_from_detailed(response)
265
+ return self._task_helper_from_response(response, BulkCreateCustomEntitiesAsyncTaskResponse)
262
266
 
263
267
  @api_method
264
- def bulk_update(self, entities: Iterable[CustomEntityBulkUpdate]) -> AsyncTaskLink:
268
+ def bulk_update(
269
+ self, entities: Iterable[CustomEntityBulkUpdate]
270
+ ) -> TaskHelper[BulkUpdateCustomEntitiesAsyncTaskResponse]:
265
271
  """
266
272
  Bulk update custom entities.
267
273
 
@@ -269,7 +275,7 @@ class CustomEntityService(BaseService):
269
275
  """
270
276
  body = CustomEntitiesBulkUpdateRequest(list(entities))
271
277
  response = bulk_update_custom_entities.sync_detailed(client=self.client, json_body=body)
272
- return model_from_detailed(response)
278
+ return self._task_helper_from_response(response, BulkUpdateCustomEntitiesAsyncTaskResponse)
273
279
 
274
280
  @api_method
275
281
  def upsert(self, entity_registry_id: str, entity: CustomEntityUpsertRequest) -> CustomEntity:
@@ -284,11 +290,13 @@ class CustomEntityService(BaseService):
284
290
  return model_from_detailed(response)
285
291
 
286
292
  @api_method
287
- def bulk_upsert(self, body: CustomEntitiesBulkUpsertRequest) -> AsyncTaskLink:
293
+ def bulk_upsert(
294
+ self, body: CustomEntitiesBulkUpsertRequest
295
+ ) -> TaskHelper[BulkUpdateCustomEntitiesAsyncTaskResponse]:
288
296
  """
289
297
  Bulk update custom entities.
290
298
 
291
299
  See https://benchling.com/api/reference#/Custom%20Entities/bulkUpsertCustomEntities
292
300
  """
293
301
  response = bulk_upsert_custom_entities.sync_detailed(client=self.client, json_body=body)
294
- return model_from_detailed(response)
302
+ return self._task_helper_from_response(response, BulkUpdateCustomEntitiesAsyncTaskResponse)