benchling-sdk 1.10.0a4__py3-none-any.whl → 1.10.0a6__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.
- benchling_sdk/apps/canvas/framework.py +74 -68
- benchling_sdk/apps/canvas/types.py +4 -4
- benchling_sdk/apps/config/__init__.py +0 -3
- benchling_sdk/apps/config/decryption_provider.py +0 -3
- benchling_sdk/apps/config/errors.py +30 -0
- benchling_sdk/apps/config/framework.py +162 -26
- benchling_sdk/apps/{helpers/config_helpers.py → config/helpers.py} +16 -97
- benchling_sdk/apps/config/mock_config.py +68 -69
- benchling_sdk/apps/config/types.py +3 -1
- benchling_sdk/apps/status/framework.py +1 -21
- benchling_sdk/apps/status/helpers.py +20 -0
- benchling_sdk/apps/status/types.py +1 -1
- benchling_sdk/services/v2/stable/assay_result_service.py +18 -0
- {benchling_sdk-1.10.0a4.dist-info → benchling_sdk-1.10.0a6.dist-info}/METADATA +2 -2
- {benchling_sdk-1.10.0a4.dist-info → benchling_sdk-1.10.0a6.dist-info}/RECORD +17 -17
- benchling_sdk/apps/config/scalars.py +0 -190
- {benchling_sdk-1.10.0a4.dist-info → benchling_sdk-1.10.0a6.dist-info}/LICENSE +0 -0
- {benchling_sdk-1.10.0a4.dist-info → benchling_sdk-1.10.0a6.dist-info}/WHEEL +0 -0
@@ -8,12 +8,11 @@ from benchling_sdk.apps.canvas.errors import DuplicateBlockIdError, NoMatchingBl
|
|
8
8
|
from benchling_sdk.apps.canvas.types import (
|
9
9
|
_UI_BLOCK_MAPPINGS_CREATE,
|
10
10
|
_UI_BLOCK_MAPPINGS_UPDATE,
|
11
|
-
_UiBlock,
|
12
11
|
_UiBlockCreate,
|
13
|
-
_UiBlockType,
|
14
12
|
_UiBlockUpdate,
|
13
|
+
UiBlock,
|
14
|
+
UiBlockType,
|
15
15
|
)
|
16
|
-
from benchling_sdk.helpers.logging_helpers import log_stability_warning, StabilityLevel
|
17
16
|
from benchling_sdk.models import (
|
18
17
|
AppCanvas,
|
19
18
|
AppCanvasApp,
|
@@ -30,12 +29,10 @@ from benchling_sdk.models import (
|
|
30
29
|
TextInputUiBlock,
|
31
30
|
)
|
32
31
|
|
33
|
-
|
32
|
+
S = TypeVar("S", bound="FilteredCanvasBuilderBlockStream")
|
34
33
|
|
35
|
-
S = TypeVar("S", bound="_FilteredCanvasBuilderBlockStream")
|
36
34
|
|
37
|
-
|
38
|
-
def _ui_block_to_create(block: _UiBlock) -> _UiBlockCreate:
|
35
|
+
def _ui_block_to_create(block: UiBlock) -> _UiBlockCreate:
|
39
36
|
# Rely on the fact that the read/write shapes are compatible, for now
|
40
37
|
if isinstance(block, UnknownType):
|
41
38
|
return block
|
@@ -49,7 +46,7 @@ def _ui_block_to_create(block: _UiBlock) -> _UiBlockCreate:
|
|
49
46
|
return block # type: ignore
|
50
47
|
|
51
48
|
|
52
|
-
def _ui_block_to_update(block:
|
49
|
+
def _ui_block_to_update(block: UiBlock) -> _UiBlockUpdate:
|
53
50
|
# Rely on the fact that the read/write shapes are compatible, for now
|
54
51
|
# Update is functionally the same as create at the moment but different for type safety
|
55
52
|
# and reserved in case the shapes do diverge later
|
@@ -65,28 +62,27 @@ def _ui_block_to_update(block: _UiBlock) -> _UiBlockUpdate:
|
|
65
62
|
return block # type: ignore
|
66
63
|
|
67
64
|
|
68
|
-
class
|
65
|
+
class CanvasBuilderUiBlock(Generic[UiBlockType]):
|
69
66
|
"""Internal UI block wrapper for CanvasBuilder."""
|
70
67
|
|
71
|
-
_block:
|
68
|
+
_block: UiBlockType
|
72
69
|
_builder: CanvasBuilder
|
73
70
|
|
74
|
-
def __init__(self, block:
|
71
|
+
def __init__(self, block: UiBlockType, builder: CanvasBuilder):
|
72
|
+
"""Init CanvasBuilderUiBlock."""
|
75
73
|
self._block = block
|
76
74
|
self._builder = builder
|
77
75
|
|
78
76
|
@classmethod
|
79
|
-
def from_api_model(
|
80
|
-
cls, block: _UiBlockType, builder: CanvasBuilder
|
81
|
-
) -> _CanvasBuilderUiBlock[_UiBlockType]:
|
77
|
+
def from_api_model(cls, block: UiBlockType, builder: CanvasBuilder) -> CanvasBuilderUiBlock[UiBlockType]:
|
82
78
|
"""Create a _CanvasBuilderUiBlock from an underlying API model."""
|
83
79
|
return cls(block, builder)
|
84
80
|
|
85
|
-
def to_api_model(self) ->
|
81
|
+
def to_api_model(self) -> UiBlockType:
|
86
82
|
"""Convert to the underlying API model."""
|
87
83
|
return self._block
|
88
84
|
|
89
|
-
def children(self) ->
|
85
|
+
def children(self) -> CanvasBuilderBlockStream:
|
90
86
|
"""
|
91
87
|
Return children for blocks when applicable, such as for section blocks.
|
92
88
|
|
@@ -97,16 +93,16 @@ class _CanvasBuilderUiBlock(Generic[_UiBlockType]):
|
|
97
93
|
# MyPy can't recognize the type narrowing when we check .children below
|
98
94
|
section_block = cast(SectionUiBlock, model)
|
99
95
|
child_blocks = [
|
100
|
-
|
96
|
+
CanvasBuilderUiBlock.from_api_model(block, self._builder) for block in section_block.children
|
101
97
|
]
|
102
98
|
# Pass reference to parent block (self)
|
103
|
-
return
|
104
|
-
return
|
99
|
+
return CanvasBuilderBlockStream(self._builder, child_blocks, child_blocks, self)
|
100
|
+
return CanvasBuilderBlockStream(self._builder, [], [])
|
105
101
|
|
106
|
-
def replace(self, new_blocks: List[
|
102
|
+
def replace(self, new_blocks: List[UiBlock]) -> None:
|
107
103
|
"""Replace block with provided new_blocks."""
|
108
104
|
parent = self._parent_block()
|
109
|
-
model = cast(
|
105
|
+
model = cast(UiBlock, self.to_api_model())
|
110
106
|
if parent:
|
111
107
|
self.insert_after(new_blocks)
|
112
108
|
# Keeps MyPy happy; SectionUiBlock is not valid for .children of SectionUiBlock
|
@@ -120,7 +116,7 @@ class _CanvasBuilderUiBlock(Generic[_UiBlockType]):
|
|
120
116
|
def remove(self) -> None:
|
121
117
|
"""Remove block."""
|
122
118
|
parent = self._parent_block()
|
123
|
-
model = cast(
|
119
|
+
model = cast(UiBlock, self.to_api_model())
|
124
120
|
if parent:
|
125
121
|
# Keeps MyPy happy; SectionUiBlock is not valid for .children of SectionUiBlock
|
126
122
|
assert not (isinstance(model, SectionUiBlock) or isinstance(model, TableUiBlock))
|
@@ -129,15 +125,15 @@ class _CanvasBuilderUiBlock(Generic[_UiBlockType]):
|
|
129
125
|
# noinspection PyProtectedMember
|
130
126
|
self._builder._source_canvas.blocks.remove(model)
|
131
127
|
|
132
|
-
def insert_after(self, new_blocks: List[
|
128
|
+
def insert_after(self, new_blocks: List[UiBlock]) -> None:
|
133
129
|
"""Insert new_blocks after block."""
|
134
130
|
self._nested_insert(new_blocks, 1)
|
135
131
|
|
136
|
-
def insert_before(self, new_blocks: List[
|
132
|
+
def insert_before(self, new_blocks: List[UiBlock]) -> None:
|
137
133
|
"""Insert new_blocks before block."""
|
138
134
|
self._nested_insert(new_blocks, 0)
|
139
135
|
|
140
|
-
def _nested_insert(self, new_blocks: List[
|
136
|
+
def _nested_insert(self, new_blocks: List[UiBlock], offset: int) -> None:
|
141
137
|
"""
|
142
138
|
Nested insert.
|
143
139
|
|
@@ -152,7 +148,7 @@ class _CanvasBuilderUiBlock(Generic[_UiBlockType]):
|
|
152
148
|
# Using list() to solve "List" is invariant creates a copy which means this stops working
|
153
149
|
self._insert(new_blocks, offset, child_blocks, self.to_api_model()) # type: ignore
|
154
150
|
parent_block.children = child_blocks
|
155
|
-
parent_builder_block =
|
151
|
+
parent_builder_block = CanvasBuilderUiBlock.from_api_model(parent_block, self._builder)
|
156
152
|
parent_builder_block.insert_after([parent_block])
|
157
153
|
# noinspection PyProtectedMember
|
158
154
|
self._builder._source_canvas.blocks.remove(parent_builder_block.to_api_model())
|
@@ -161,9 +157,7 @@ class _CanvasBuilderUiBlock(Generic[_UiBlockType]):
|
|
161
157
|
self._insert(new_blocks, offset, self._builder._source_canvas.blocks, self.to_api_model())
|
162
158
|
|
163
159
|
@staticmethod
|
164
|
-
def _insert(
|
165
|
-
new_blocks: List[_UiBlock], offset: int, blocks: List[_UiBlock], target_block: _UiBlock
|
166
|
-
) -> None:
|
160
|
+
def _insert(new_blocks: List[UiBlock], offset: int, blocks: List[UiBlock], target_block: UiBlock) -> None:
|
167
161
|
"""Insert new_blocks before block as a side effect."""
|
168
162
|
index = blocks.index(target_block)
|
169
163
|
for count, new_block in enumerate(new_blocks):
|
@@ -178,26 +172,29 @@ class _CanvasBuilderUiBlock(Generic[_UiBlockType]):
|
|
178
172
|
return None
|
179
173
|
|
180
174
|
|
181
|
-
class
|
182
|
-
|
175
|
+
class CanvasBuilderFilter(Protocol):
|
176
|
+
"""Callable protocol for specifying a predicate for filtering UiBlocks."""
|
177
|
+
|
178
|
+
def __call__(self, block: UiBlockType) -> bool:
|
183
179
|
"""Return True if the UiBlock matches specified conditions."""
|
184
180
|
pass
|
185
181
|
|
186
182
|
|
187
|
-
class
|
188
|
-
"""
|
183
|
+
class FilteredCanvasBuilderBlockStream:
|
184
|
+
"""Filtered UI block list wrapper for CanvasBuilder."""
|
189
185
|
|
190
186
|
_builder: CanvasBuilder
|
191
|
-
_blocks: List[
|
192
|
-
_selected_blocks: List[
|
187
|
+
_blocks: List[CanvasBuilderUiBlock]
|
188
|
+
_selected_blocks: List[CanvasBuilderUiBlock]
|
193
189
|
_cursor: int
|
194
190
|
|
195
191
|
def __init__(
|
196
192
|
self,
|
197
193
|
builder: CanvasBuilder,
|
198
|
-
blocks: List[
|
199
|
-
selected_blocks: List[
|
194
|
+
blocks: List[CanvasBuilderUiBlock],
|
195
|
+
selected_blocks: List[CanvasBuilderUiBlock],
|
200
196
|
):
|
197
|
+
"""Init FilteredCanvasBuilderBlockStream."""
|
201
198
|
self._builder = builder
|
202
199
|
self._blocks = blocks
|
203
200
|
self._selected_blocks = selected_blocks
|
@@ -222,26 +219,26 @@ class _FilteredCanvasBuilderBlockStream:
|
|
222
219
|
"""
|
223
220
|
# noinspection PyProtectedMember
|
224
221
|
blocks = [
|
225
|
-
|
222
|
+
CanvasBuilderUiBlock.from_api_model(block, builder)
|
226
223
|
for block in builder._source_canvas.blocks
|
227
224
|
if not isinstance(block, UnknownType)
|
228
225
|
]
|
229
226
|
return cls(builder, blocks, blocks)
|
230
227
|
|
231
|
-
def filter(self, filter_function:
|
228
|
+
def filter(self, filter_function: CanvasBuilderFilter) -> FilteredCanvasBuilderBlockStream:
|
232
229
|
"""
|
233
230
|
Filter.
|
234
231
|
|
235
232
|
Accept a predicate that evaluates if a UiBlock should be included in the result or not.
|
236
233
|
Returns a new stream of blocks filtered to the predicate, which is further operable.
|
237
234
|
"""
|
238
|
-
return
|
235
|
+
return CanvasBuilderBlockStream(
|
239
236
|
self._builder,
|
240
237
|
self._blocks,
|
241
238
|
[block for block in self._selected_blocks if filter_function(block.to_api_model())],
|
242
239
|
)
|
243
240
|
|
244
|
-
def get_by_id(self, block_id: str) ->
|
241
|
+
def get_by_id(self, block_id: str) -> CanvasBuilderUiBlock:
|
245
242
|
"""
|
246
243
|
Get a block by its id.
|
247
244
|
|
@@ -253,8 +250,8 @@ class _FilteredCanvasBuilderBlockStream:
|
|
253
250
|
return matched_block
|
254
251
|
|
255
252
|
def _block_by_id(
|
256
|
-
self, block_id: str, blocks: List[
|
257
|
-
) -> Optional[
|
253
|
+
self, block_id: str, blocks: List[CanvasBuilderUiBlock]
|
254
|
+
) -> Optional[CanvasBuilderUiBlock]:
|
258
255
|
for block in blocks:
|
259
256
|
api_block = block.to_api_model()
|
260
257
|
if api_block.id == block_id:
|
@@ -270,13 +267,13 @@ class _FilteredCanvasBuilderBlockStream:
|
|
270
267
|
"""Return a count of the elements in the list of blocks."""
|
271
268
|
return len(self._selected_blocks)
|
272
269
|
|
273
|
-
def first(self) ->
|
270
|
+
def first(self) -> CanvasBuilderUiBlock:
|
274
271
|
"""Return the first block in the list."""
|
275
272
|
if len(self._selected_blocks) < 1:
|
276
273
|
raise NoMatchingBlocksError
|
277
274
|
return self._selected_blocks[0]
|
278
275
|
|
279
|
-
def last(self) ->
|
276
|
+
def last(self) -> CanvasBuilderUiBlock:
|
280
277
|
"""Return the last block in the list."""
|
281
278
|
if len(self._selected_blocks) < 1:
|
282
279
|
raise NoMatchingBlocksError
|
@@ -291,26 +288,27 @@ class _FilteredCanvasBuilderBlockStream:
|
|
291
288
|
self._builder._source_canvas.blocks = updated_blocks
|
292
289
|
|
293
290
|
|
294
|
-
class
|
291
|
+
class CanvasBuilderBlockStream(FilteredCanvasBuilderBlockStream):
|
295
292
|
"""
|
296
293
|
Internal UI block list wrapper for CanvasBuilder.
|
297
294
|
|
298
295
|
Possesses some additional operations unavailable to filtered block streams.
|
299
296
|
"""
|
300
297
|
|
301
|
-
_parent: Optional[
|
298
|
+
_parent: Optional[CanvasBuilderUiBlock]
|
302
299
|
|
303
300
|
def __init__(
|
304
301
|
self,
|
305
302
|
builder: CanvasBuilder,
|
306
|
-
blocks: List[
|
307
|
-
selected_blocks: List[
|
308
|
-
parent: Optional[
|
303
|
+
blocks: List[CanvasBuilderUiBlock],
|
304
|
+
selected_blocks: List[CanvasBuilderUiBlock],
|
305
|
+
parent: Optional[CanvasBuilderUiBlock] = None,
|
309
306
|
):
|
307
|
+
"""Init CanvasBuilderBlockStream."""
|
310
308
|
super().__init__(builder, blocks, selected_blocks)
|
311
309
|
self._parent = parent
|
312
310
|
|
313
|
-
def append(self, new_blocks: List[
|
311
|
+
def append(self, new_blocks: List[UiBlock]) -> None:
|
314
312
|
"""
|
315
313
|
Append new_blocks to the end of list of blocks.
|
316
314
|
|
@@ -367,7 +365,7 @@ class CanvasBuilder:
|
|
367
365
|
resource_id: str,
|
368
366
|
enabled: bool = True,
|
369
367
|
session_id: Optional[str] = None,
|
370
|
-
blocks: Optional[List[
|
368
|
+
blocks: Optional[List[UiBlock]] = None,
|
371
369
|
):
|
372
370
|
"""
|
373
371
|
Init AppCanvas.
|
@@ -410,25 +408,33 @@ class CanvasBuilder:
|
|
410
408
|
blocks=self._source_canvas.blocks,
|
411
409
|
)
|
412
410
|
|
413
|
-
def
|
411
|
+
def with_enabled(self, enabled: bool = True) -> CanvasBuilder:
|
414
412
|
"""
|
415
|
-
Return a new CanvasBuilder with the underlying canvas
|
413
|
+
Return a new CanvasBuilder with the underlying canvas enabled set to the specified value.
|
416
414
|
|
415
|
+
Specify `False` to disable the canvas.
|
417
416
|
This does not call the API, it only assigns state in the CanvasBuilder.
|
418
417
|
"""
|
419
|
-
return self._with_enabled(
|
418
|
+
return self._with_enabled(enabled)
|
420
419
|
|
421
|
-
def
|
420
|
+
def with_blocks(self, new_blocks: List[UiBlock]) -> CanvasBuilder:
|
422
421
|
"""
|
423
|
-
Return a new CanvasBuilder with the underlying
|
422
|
+
Return a new CanvasBuilder with the underlying blocks replaced.
|
424
423
|
|
425
424
|
This does not call the API, it only assigns state in the CanvasBuilder.
|
426
425
|
"""
|
427
|
-
return
|
426
|
+
return CanvasBuilder(
|
427
|
+
app_id=self._source_canvas.app.id,
|
428
|
+
feature_id=self._source_canvas.feature_id,
|
429
|
+
resource_id=self._source_canvas.resource_id,
|
430
|
+
enabled=self._source_canvas.enabled,
|
431
|
+
session_id=self._source_canvas.session_id,
|
432
|
+
blocks=new_blocks,
|
433
|
+
)
|
428
434
|
|
429
|
-
def
|
435
|
+
def with_session_id(self, session_id: Optional[str]) -> CanvasBuilder:
|
430
436
|
"""
|
431
|
-
Return a new CanvasBuilder with
|
437
|
+
Return a new CanvasBuilder with an optional session_id set.
|
432
438
|
|
433
439
|
This does not call the API, it only assigns state in the CanvasBuilder.
|
434
440
|
"""
|
@@ -437,8 +443,8 @@ class CanvasBuilder:
|
|
437
443
|
feature_id=self._source_canvas.feature_id,
|
438
444
|
resource_id=self._source_canvas.resource_id,
|
439
445
|
enabled=self._source_canvas.enabled,
|
440
|
-
session_id=
|
441
|
-
blocks=
|
446
|
+
session_id=session_id,
|
447
|
+
blocks=self._source_canvas.blocks,
|
442
448
|
)
|
443
449
|
|
444
450
|
def inputs_to_dict(self) -> Dict[str, Union[str, List[str]]]:
|
@@ -503,7 +509,7 @@ class CanvasBuilder:
|
|
503
509
|
|
504
510
|
def inputs_to_dict_multi_value(self) -> Dict[str, List[str]]:
|
505
511
|
"""
|
506
|
-
Read Inputs to dict, but only for
|
512
|
+
Read Inputs to dict, but only for multivalued blocks.
|
507
513
|
|
508
514
|
Return a dictionary of {block_id: block_value} for all blocks on the canvas with multivalued input values.
|
509
515
|
Blocks that only have read attributes are omitted. Excludes TableUiBlock.
|
@@ -529,9 +535,9 @@ class CanvasBuilder:
|
|
529
535
|
|
530
536
|
def _values_from_blocks(
|
531
537
|
self,
|
532
|
-
blocks: List[
|
538
|
+
blocks: List[UiBlock],
|
533
539
|
existing_keys: Optional[List[str]] = None,
|
534
|
-
included_classes: Optional[Set[Type[
|
540
|
+
included_classes: Optional[Set[Type[UiBlock]]] = None,
|
535
541
|
) -> Dict[str, Union[str, List[str]]]:
|
536
542
|
existing_keys = existing_keys if existing_keys else []
|
537
543
|
values: Dict[str, Union[str, List[str]]] = dict()
|
@@ -582,15 +588,15 @@ class CanvasBuilder:
|
|
582
588
|
)
|
583
589
|
|
584
590
|
@property
|
585
|
-
def blocks(self) ->
|
591
|
+
def blocks(self) -> CanvasBuilderBlockStream:
|
586
592
|
"""
|
587
593
|
Blocks.
|
588
594
|
|
589
595
|
Return a stream of blocks which can be iterated and operated on to mutate the canvas
|
590
596
|
stored by the builder.
|
591
597
|
"""
|
592
|
-
return
|
598
|
+
return CanvasBuilderBlockStream.from_builder(self)
|
593
599
|
|
594
600
|
|
595
|
-
def _is_included_class(included_classes: Set[Type[
|
601
|
+
def _is_included_class(included_classes: Set[Type[UiBlock]], target_class: UiBlock) -> bool:
|
596
602
|
return isinstance(target_class, tuple(c for c in included_classes))
|
@@ -41,7 +41,7 @@ from benchling_sdk.models import (
|
|
41
41
|
TextInputUiBlockUpdate,
|
42
42
|
)
|
43
43
|
|
44
|
-
|
44
|
+
UiBlock = Union[
|
45
45
|
ButtonUiBlock,
|
46
46
|
ChipUiBlock,
|
47
47
|
DropdownMultiValueUiBlock,
|
@@ -57,9 +57,9 @@ _UiBlock = Union[
|
|
57
57
|
UnknownType,
|
58
58
|
]
|
59
59
|
|
60
|
-
|
61
|
-
"
|
62
|
-
bound=
|
60
|
+
UiBlockType = TypeVar(
|
61
|
+
"UiBlockType",
|
62
|
+
bound=UiBlock,
|
63
63
|
)
|
64
64
|
|
65
65
|
_UiBlockCreate = Union[
|
@@ -11,9 +11,6 @@ class BaseDecryptionProvider(ABC):
|
|
11
11
|
Various implementations might use AWS KMS, Azure, etc.
|
12
12
|
"""
|
13
13
|
|
14
|
-
# TODO BNCH-52772 should we loosen this method to accept None and not attempt decryption,
|
15
|
-
# now that config is no longer type safe?
|
16
|
-
|
17
14
|
@abstractmethod
|
18
15
|
def decrypt(self, ciphertext: str) -> str:
|
19
16
|
"""
|
@@ -6,3 +6,33 @@ class UnsupportedConfigItemError(Exception):
|
|
6
6
|
"""
|
7
7
|
|
8
8
|
pass
|
9
|
+
|
10
|
+
|
11
|
+
class MissingRequiredConfigItemError(Exception):
|
12
|
+
"""
|
13
|
+
Missing required config item error.
|
14
|
+
|
15
|
+
A config item was invoked as required, but missing in the configuration item store.
|
16
|
+
"""
|
17
|
+
|
18
|
+
pass
|
19
|
+
|
20
|
+
|
21
|
+
class InaccessibleConfigItemError(Exception):
|
22
|
+
"""
|
23
|
+
Inaccessible config item error.
|
24
|
+
|
25
|
+
A config item linked resource was inaccessible (the caller does not have permissions).
|
26
|
+
"""
|
27
|
+
|
28
|
+
pass
|
29
|
+
|
30
|
+
|
31
|
+
class ConfigItemLinkedResourceError(Exception):
|
32
|
+
"""
|
33
|
+
Config item linked resource error.
|
34
|
+
|
35
|
+
A config item is not a type that has an associated linked resource.
|
36
|
+
"""
|
37
|
+
|
38
|
+
pass
|