benchling-sdk 1.10.0a5__py3-none-any.whl → 1.10.0a7__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.
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import json
3
4
  from typing import cast, Dict, Generic, List, Optional, Protocol, Set, Type, TypeVar, Union
4
5
 
5
6
  from benchling_api_client.v2.extensions import UnknownType
@@ -13,6 +14,7 @@ from benchling_sdk.apps.canvas.types import (
13
14
  UiBlock,
14
15
  UiBlockType,
15
16
  )
17
+ from benchling_sdk.apps.types import JsonType
16
18
  from benchling_sdk.models import (
17
19
  AppCanvas,
18
20
  AppCanvasApp,
@@ -366,6 +368,7 @@ class CanvasBuilder:
366
368
  enabled: bool = True,
367
369
  session_id: Optional[str] = None,
368
370
  blocks: Optional[List[UiBlock]] = None,
371
+ data: Optional[JsonType] = None,
369
372
  ):
370
373
  """
371
374
  Init AppCanvas.
@@ -379,6 +382,7 @@ class CanvasBuilder:
379
382
  enabled=enabled,
380
383
  session_id=session_id,
381
384
  blocks=blocks if blocks else [],
385
+ data=json.dumps(data) if data is not None else None,
382
386
  )
383
387
 
384
388
  @classmethod
@@ -395,6 +399,7 @@ class CanvasBuilder:
395
399
  enabled=canvas.enabled,
396
400
  session_id=canvas.session_id,
397
401
  blocks=canvas.blocks,
402
+ data=json.loads(canvas.data) if canvas.data is not None else None,
398
403
  )
399
404
 
400
405
  def _with_enabled(self, value: bool) -> CanvasBuilder:
@@ -406,6 +411,7 @@ class CanvasBuilder:
406
411
  enabled=value,
407
412
  session_id=self._source_canvas.session_id,
408
413
  blocks=self._source_canvas.blocks,
414
+ data=self._source_canvas.data,
409
415
  )
410
416
 
411
417
  def with_enabled(self, enabled: bool = True) -> CanvasBuilder:
@@ -430,6 +436,23 @@ class CanvasBuilder:
430
436
  enabled=self._source_canvas.enabled,
431
437
  session_id=self._source_canvas.session_id,
432
438
  blocks=new_blocks,
439
+ data=self._source_canvas.data,
440
+ )
441
+
442
+ def with_data(self, new_data: Optional[JsonType]) -> CanvasBuilder:
443
+ """
444
+ Return a new CanvasBuilder with the underlying data replaced.
445
+
446
+ This does not call the API, it only assigns state in the CanvasBuilder.
447
+ """
448
+ return CanvasBuilder(
449
+ app_id=self._source_canvas.app.id,
450
+ feature_id=self._source_canvas.feature_id,
451
+ resource_id=self._source_canvas.resource_id,
452
+ enabled=self._source_canvas.enabled,
453
+ session_id=self._source_canvas.session_id,
454
+ blocks=self._source_canvas.blocks,
455
+ data=new_data,
433
456
  )
434
457
 
435
458
  def with_session_id(self, session_id: Optional[str]) -> CanvasBuilder:
@@ -445,6 +468,7 @@ class CanvasBuilder:
445
468
  enabled=self._source_canvas.enabled,
446
469
  session_id=session_id,
447
470
  blocks=self._source_canvas.blocks,
471
+ data=self._source_canvas.data,
448
472
  )
449
473
 
450
474
  def inputs_to_dict(self) -> Dict[str, Union[str, List[str]]]:
@@ -574,6 +598,7 @@ class CanvasBuilder:
574
598
  enabled=self._source_canvas.enabled,
575
599
  session_id=self._source_canvas.session_id,
576
600
  blocks=[_ui_block_to_update(block) for block in self._source_canvas.blocks],
601
+ data=self._source_canvas.data,
577
602
  )
578
603
 
579
604
  def to_create(self) -> AppCanvasCreate:
@@ -585,6 +610,7 @@ class CanvasBuilder:
585
610
  enabled=self._source_canvas.enabled,
586
611
  session_id=self._source_canvas.session_id,
587
612
  blocks=[_ui_block_to_create(block) for block in self._source_canvas.blocks],
613
+ data=self._source_canvas.data,
588
614
  )
589
615
 
590
616
  @property
@@ -597,6 +623,14 @@ class CanvasBuilder:
597
623
  """
598
624
  return CanvasBuilderBlockStream.from_builder(self)
599
625
 
626
+ def data_to_json(self) -> Optional[JsonType]:
627
+ """
628
+ Convert Canvas data to JSON.
629
+
630
+ Return a JSON object parsed from the string of the canvas's `data`, if present. Otherwise, return None.
631
+ """
632
+ return json.loads(self._source_canvas.data) if self._source_canvas.data is not None else None
633
+
600
634
 
601
635
  def _is_included_class(included_classes: Set[Type[UiBlock]], target_class: UiBlock) -> bool:
602
636
  return isinstance(target_class, tuple(c for c in included_classes))
@@ -1,3 +0,0 @@
1
- from benchling_sdk.helpers.logging_helpers import log_stability_warning, StabilityLevel
2
-
3
- log_stability_warning(StabilityLevel.BETA)
@@ -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
  """
@@ -77,7 +77,8 @@ class BenchlingConfigProvider(ConfigProvider):
77
77
 
78
78
  # Eager load all config items for now since we don't yet have a way of lazily querying by path
79
79
  all_config_pages = list(app_pages)
80
- # Punt on UnknownType for now as apps using manifests with new types + older client could lead to unpredictable results
80
+ # Punt on UnknownType for now as apps using manifests with new types +
81
+ # older client could lead to unpredictable results
81
82
  all_config_items = [
82
83
  _supported_config_item(config_item) for page in all_config_pages for config_item in page
83
84
  ]
@@ -220,13 +221,13 @@ class ConfigItemStore:
220
221
  """
221
222
  Dependency Link Store.
222
223
 
223
- Marshals an app configuration from the configuration provider into an indexable structure.
224
+ Marshals an app configuration from the configuration provider into an indexed structure.
224
225
  Only retrieves app configuration once unless its cache is invalidated.
225
226
  """
226
227
 
227
228
  _configuration_provider: ConfigProvider
228
229
  _configuration: Optional[List[ConfigurationReference]] = None
229
- _configuration_map: Optional[Dict[ConfigItemPath, ConfigItemWrapper]] = None
230
+ _configuration_dict: Optional[Dict[ConfigItemPath, ConfigItemWrapper]] = None
230
231
  _array_path_row_names: Dict[Tuple[str, ...], OrderedSet[str]] = dict()
231
232
 
232
233
  def __init__(self, configuration_provider: ConfigProvider):
@@ -252,17 +253,17 @@ class ConfigItemStore:
252
253
  return self._configuration
253
254
 
254
255
  @property
255
- def configuration_path_map(self) -> Dict[ConfigItemPath, ConfigItemWrapper]:
256
+ def configuration_path_dict(self) -> Dict[ConfigItemPath, ConfigItemWrapper]:
256
257
  """
257
258
  Config links.
258
259
 
259
- Return a map of configuration item paths to their corresponding configuration items.
260
+ Return a dict of configuration item paths to their corresponding configuration items.
260
261
  """
261
- if not self._configuration_map:
262
- self._configuration_map = {
262
+ if not self._configuration_dict:
263
+ self._configuration_dict = {
263
264
  tuple(item.path): ConfigItemWrapper(item, item.path) for item in self.configuration
264
265
  }
265
- return self._configuration_map
266
+ return self._configuration_dict
266
267
 
267
268
  def config_by_path(self, path: List[str]) -> ConfigItemWrapper:
268
269
  """
@@ -272,7 +273,7 @@ class ConfigItemStore:
272
273
  """
273
274
  # Since we eager load all config now, we know that missing path means it's not configured in Benchling
274
275
  # Later if we support lazy loading, we'll need to differentiate what's in our cache versus missing
275
- return self.configuration_path_map.get(tuple(path), ConfigItemWrapper(None, path))
276
+ return self.configuration_path_dict.get(tuple(path), ConfigItemWrapper(None, path))
276
277
 
277
278
  def config_keys_by_path(self, path: List[str]) -> OrderedSet[str]:
278
279
  """
@@ -296,7 +297,7 @@ class ConfigItemStore:
296
297
  self._array_path_row_names[path_tuple] = OrderedSet(
297
298
  [
298
299
  config_item.path[len(path)]
299
- # Use the list instead of configuration_map to preserve order
300
+ # Use the list instead of configuration_dict to preserve order
300
301
  for config_item in self.configuration
301
302
  # The +1 is the name of the array row
302
303
  if len(config_item.path) >= len(path) + 1
@@ -330,7 +331,7 @@ class ConfigItemStore:
330
331
  Will force retrieval of configuration from the ConfigProvider the next time the link store is accessed.
331
332
  """
332
333
  self._configuration = None
333
- self._configuration_map = None
334
+ self._configuration_dict = None
334
335
  self._array_path_row_names = dict()
335
336
 
336
337
 
@@ -1,7 +1,6 @@
1
- from datetime import date, datetime
1
+ from datetime import datetime
2
2
  from typing import List, Optional, Union
3
3
 
4
- from benchling_api_client.v2.beta.models.app_config_field_type import AppConfigFieldType
5
4
  from benchling_api_client.v2.beta.models.base_manifest_config import BaseManifestConfig
6
5
  from benchling_api_client.v2.beta.models.dropdown_dependency import DropdownDependency
7
6
  from benchling_api_client.v2.beta.models.dropdown_dependency_types import DropdownDependencyTypes
@@ -29,92 +28,7 @@ from benchling_api_client.v2.beta.models.workflow_task_schema_dependency_type im
29
28
  from benchling_api_client.v2.extensions import UnknownType
30
29
  from benchling_api_client.v2.stable.extensions import NotPresentError
31
30
 
32
- from benchling_sdk.apps.config.scalars import JsonType
33
- from benchling_sdk.models import (
34
- AaSequence,
35
- AssayResult,
36
- AssayRun,
37
- Box,
38
- Container,
39
- CustomEntity,
40
- DnaOligo,
41
- DnaSequence,
42
- Entry,
43
- Location,
44
- Mixture,
45
- Molecule,
46
- Plate,
47
- Request,
48
- RnaOligo,
49
- RnaSequence,
50
- WorkflowTask,
51
- )
52
-
53
- _MODEL_TYPES_FROM_SCHEMA_TYPE = {
54
- SchemaDependencyTypes.CONTAINER_SCHEMA: Container,
55
- SchemaDependencyTypes.PLATE_SCHEMA: Plate,
56
- SchemaDependencyTypes.BOX_SCHEMA: Box,
57
- SchemaDependencyTypes.LOCATION_SCHEMA: Location,
58
- SchemaDependencyTypes.ENTRY_SCHEMA: Entry,
59
- SchemaDependencyTypes.REQUEST_SCHEMA: Request,
60
- SchemaDependencyTypes.RESULT_SCHEMA: AssayResult,
61
- SchemaDependencyTypes.RUN_SCHEMA: AssayRun,
62
- SchemaDependencyTypes.WORKFLOW_TASK_SCHEMA: WorkflowTask,
63
- }
64
-
65
-
66
- _SCALAR_TYPES_FROM_CONFIG = {
67
- ScalarConfigTypes.BOOLEAN: bool,
68
- ScalarConfigTypes.DATE: date,
69
- ScalarConfigTypes.DATETIME: datetime,
70
- ScalarConfigTypes.FLOAT: float,
71
- ScalarConfigTypes.INTEGER: int,
72
- ScalarConfigTypes.JSON: JsonType,
73
- ScalarConfigTypes.TEXT: str,
74
- }
75
-
76
-
77
- _FIELD_SCALAR_TYPES_FROM_CONFIG = {
78
- AppConfigFieldType.BOOLEAN: bool,
79
- AppConfigFieldType.DATE: date,
80
- AppConfigFieldType.DATETIME: datetime,
81
- AppConfigFieldType.FLOAT: float,
82
- AppConfigFieldType.INTEGER: int,
83
- AppConfigFieldType.JSON: JsonType,
84
- AppConfigFieldType.TEXT: str,
85
- }
86
-
87
-
88
- ModelType = Union[AssayResult, AssayRun, Box, Container, Entry, Location, Plate, Request]
89
-
90
- _INSTANCE_FROM_SCHEMA_SUBTYPE = {
91
- SchemaDependencySubtypes.AA_SEQUENCE: AaSequence,
92
- SchemaDependencySubtypes.CUSTOM_ENTITY: CustomEntity,
93
- SchemaDependencySubtypes.DNA_SEQUENCE: DnaSequence,
94
- SchemaDependencySubtypes.DNA_OLIGO: DnaOligo,
95
- SchemaDependencySubtypes.MIXTURE: Mixture,
96
- SchemaDependencySubtypes.MOLECULE: Molecule,
97
- SchemaDependencySubtypes.RNA_OLIGO: RnaOligo,
98
- SchemaDependencySubtypes.RNA_SEQUENCE: RnaSequence,
99
- }
100
-
101
- EntitySubtype = Union[
102
- AaSequence, CustomEntity, DnaOligo, DnaSequence, Mixture, Molecule, RnaOligo, RnaSequence
103
- ]
104
-
105
- AnyDependency = Union[
106
- BaseManifestConfig,
107
- DropdownDependency,
108
- EntitySchemaDependency,
109
- FieldDefinitionsManifest,
110
- ManifestArrayConfig,
111
- ManifestScalarConfig,
112
- ResourceDependency,
113
- SchemaDependency,
114
- WorkflowTaskSchemaDependency,
115
- ]
116
-
117
- ArrayElementDependency = Union[
31
+ _ArrayElementDependency = Union[
118
32
  SchemaDependency,
119
33
  EntitySchemaDependency,
120
34
  WorkflowTaskSchemaDependency,
@@ -124,13 +38,13 @@ ArrayElementDependency = Union[
124
38
  ]
125
39
 
126
40
 
127
- class UnsupportedSubTypeError(Exception):
41
+ class _UnsupportedSubTypeError(Exception):
128
42
  """Error when an unsupported subtype is encountered."""
129
43
 
130
44
  pass
131
45
 
132
46
 
133
- def field_definitions_from_dependency(
47
+ def _field_definitions_from_dependency(
134
48
  dependency: Union[
135
49
  EntitySchemaDependency,
136
50
  SchemaDependency,
@@ -148,7 +62,7 @@ def field_definitions_from_dependency(
148
62
  return []
149
63
 
150
64
 
151
- def element_definition_from_dependency(dependency: ManifestArrayConfig) -> List[ArrayElementDependency]:
65
+ def _element_definition_from_dependency(dependency: ManifestArrayConfig) -> List[_ArrayElementDependency]:
152
66
  """Safely return an element definition as a list of dependencies from an array dependency or empty list."""
153
67
  try:
154
68
  if hasattr(dependency, "element_definition"):
@@ -161,7 +75,7 @@ def element_definition_from_dependency(dependency: ManifestArrayConfig) -> List[
161
75
  return []
162
76
 
163
77
 
164
- def enum_from_dependency(
78
+ def _enum_from_dependency(
165
79
  dependency: Union[
166
80
  ManifestFloatScalarConfig,
167
81
  ManifestIntegerScalarConfig,
@@ -180,8 +94,8 @@ def enum_from_dependency(
180
94
 
181
95
  # TODO BNCH-57036 All element definitions currently deserialize to UnknownType. Hack around this temporarily
182
96
  def _fix_element_definition_deserialization(
183
- element: Union[UnknownType, ArrayElementDependency]
184
- ) -> ArrayElementDependency:
97
+ element: Union[UnknownType, _ArrayElementDependency]
98
+ ) -> _ArrayElementDependency:
185
99
  if isinstance(element, UnknownType):
186
100
  if "type" in element.value:
187
101
  element_type = element.value["type"]
@@ -201,7 +115,7 @@ def _fix_element_definition_deserialization(
201
115
  return element
202
116
 
203
117
 
204
- def workflow_task_schema_output_from_dependency(
118
+ def _workflow_task_schema_output_from_dependency(
205
119
  dependency: WorkflowTaskSchemaDependency,
206
120
  ) -> Optional[WorkflowTaskSchemaDependencyOutput]:
207
121
  """Safely return a workflow task schema output from a workflow task schema or None."""
@@ -214,7 +128,7 @@ def workflow_task_schema_output_from_dependency(
214
128
  return None
215
129
 
216
130
 
217
- def options_from_dependency(dependency: DropdownDependency) -> List[BaseManifestConfig]:
131
+ def _options_from_dependency(dependency: DropdownDependency) -> List[BaseManifestConfig]:
218
132
  """Safely return a list of options from a dropdown dependency or empty list."""
219
133
  try:
220
134
  if hasattr(dependency, "options"):
@@ -225,7 +139,7 @@ def options_from_dependency(dependency: DropdownDependency) -> List[BaseManifest
225
139
  return []
226
140
 
227
141
 
228
- def subtype_from_entity_schema_dependency(
142
+ def _subtype_from_entity_schema_dependency(
229
143
  dependency: EntitySchemaDependency,
230
144
  ) -> Optional[SchemaDependencySubtypes]:
231
145
  """Safely return an entity schema dependency's subtype, if present."""
@@ -236,3 +150,8 @@ def subtype_from_entity_schema_dependency(
236
150
  except NotPresentError:
237
151
  pass
238
152
  return None
153
+
154
+
155
+ def datetime_config_value_to_str(value: datetime) -> str:
156
+ """Convert a datetime value to a valid string accepted by a datetime app config item."""
157
+ return value.strftime("%Y-%m-%d %I:%M:%S %p")
@@ -4,7 +4,7 @@ from datetime import date, datetime
4
4
  import json
5
5
  import random
6
6
  import string
7
- from typing import Dict, get_args, List, Optional, Protocol, Type, Union
7
+ from typing import cast, Dict, get_args, List, Optional, Protocol, Union
8
8
 
9
9
  from benchling_api_client.v2.beta.models.base_manifest_config import BaseManifestConfig
10
10
  from benchling_api_client.v2.beta.models.benchling_app_manifest import BenchlingAppManifest
@@ -34,29 +34,20 @@ from benchling_api_client.v2.extensions import UnknownType
34
34
  from benchling_api_client.v2.stable.types import UNSET, Unset
35
35
 
36
36
  from benchling_sdk.apps.config.decryption_provider import BaseDecryptionProvider
37
+ from benchling_sdk.apps.config.errors import UnsupportedConfigItemError
37
38
  from benchling_sdk.apps.config.framework import _supported_config_item, ConfigItemStore, StaticConfigProvider
38
- from benchling_sdk.apps.config.scalars import (
39
- BoolScalar,
40
- DateScalar,
41
- DateTimeScalar,
42
- FloatScalar,
43
- IntScalar,
44
- JsonScalar,
45
- JsonType,
46
- ScalarDefinition,
47
- ScalarType,
48
- SecureTextScalar,
49
- TextScalar,
39
+ from benchling_sdk.apps.config.helpers import (
40
+ _element_definition_from_dependency,
41
+ _enum_from_dependency,
42
+ _field_definitions_from_dependency,
43
+ _options_from_dependency,
44
+ _subtype_from_entity_schema_dependency,
45
+ _workflow_task_schema_output_from_dependency,
46
+ datetime_config_value_to_str,
50
47
  )
51
48
  from benchling_sdk.apps.config.types import ConfigurationReference
52
- from benchling_sdk.apps.helpers.config_helpers import (
53
- element_definition_from_dependency,
54
- enum_from_dependency,
55
- field_definitions_from_dependency,
56
- options_from_dependency,
57
- subtype_from_entity_schema_dependency,
58
- workflow_task_schema_output_from_dependency,
59
- )
49
+ from benchling_sdk.apps.types import JsonType
50
+ from benchling_sdk.helpers.logging_helpers import log_stability_warning, StabilityLevel
60
51
  from benchling_sdk.models import (
61
52
  AppConfigItem,
62
53
  ArrayElementAppConfigItem,
@@ -98,6 +89,8 @@ ManifestDependencies = Union[
98
89
  UnknownType,
99
90
  ]
100
91
 
92
+ log_stability_warning(StabilityLevel.BETA)
93
+
101
94
 
102
95
  class MockDecryptionFunction(Protocol):
103
96
  """Mock out a decryption function for use with secure text."""
@@ -296,7 +289,7 @@ def mock_datetime_app_config_item(path: List[str], value: Optional[datetime]) ->
296
289
  """Mock a datetime app config item with a path and specified value."""
297
290
  return DatetimeAppConfigItem(
298
291
  path=path,
299
- value=value.strftime(DateTimeScalar.expected_format()) if isinstance(value, datetime) else value,
292
+ value=datetime_config_value_to_str(value) if value else None,
300
293
  type=DatetimeAppConfigItemType.DATETIME,
301
294
  id=_random_string("aci_"),
302
295
  )
@@ -354,9 +347,10 @@ def mock_text_app_config_item(path: List[str], value: Optional[str]) -> TextAppC
354
347
 
355
348
  def _mock_dependency(
356
349
  dependency: ManifestDependencies,
357
- parent_path: List[str] = list(),
350
+ parent_path: Optional[List[str]] = None,
358
351
  ) -> List[AppConfigItem]:
359
352
  """Mock a dependency from its manifest definition."""
353
+ parent_path = parent_path if parent_path else []
360
354
  # MyPy has trouble inferring lists with [config_item] + sub_items so use the syntax like:
361
355
  # [*[config_item], *sub_items]
362
356
  # See https://github.com/python/mypy/issues/3933#issuecomment-808739063
@@ -371,12 +365,12 @@ def _mock_dependency(
371
365
  )
372
366
  sub_items = [
373
367
  _mock_subdependency(subdependency, dependency, parent_path=parent_path)
374
- for subdependency in options_from_dependency(dependency)
368
+ for subdependency in _options_from_dependency(dependency)
375
369
  ]
376
370
  return [*[config_item], *sub_items]
377
371
  elif isinstance(dependency, EntitySchemaDependency):
378
372
  linked_resource_id = _random_string("val_")
379
- subtype = subtype_from_entity_schema_dependency(dependency)
373
+ subtype = _subtype_from_entity_schema_dependency(dependency)
380
374
  optional_subtype: Union[SchemaDependencySubtypes, Unset] = (
381
375
  _convert_entity_subtype(subtype) if subtype is not None else UNSET
382
376
  )
@@ -390,7 +384,7 @@ def _mock_dependency(
390
384
  )
391
385
  sub_items = [
392
386
  _mock_subdependency(subdependency, dependency, parent_path=parent_path)
393
- for subdependency in field_definitions_from_dependency(dependency)
387
+ for subdependency in _field_definitions_from_dependency(dependency)
394
388
  ]
395
389
  return [*[entity_item], *sub_items]
396
390
  elif isinstance(dependency, SchemaDependency):
@@ -404,7 +398,7 @@ def _mock_dependency(
404
398
  )
405
399
  sub_items = [
406
400
  _mock_subdependency(subdependency, dependency, parent_path=parent_path)
407
- for subdependency in field_definitions_from_dependency(dependency)
401
+ for subdependency in _field_definitions_from_dependency(dependency)
408
402
  ]
409
403
  return [*[config_item], *sub_items]
410
404
  elif isinstance(dependency, WorkflowTaskSchemaDependency):
@@ -418,11 +412,11 @@ def _mock_dependency(
418
412
  )
419
413
  sub_items = [
420
414
  _mock_subdependency(subdependency, dependency, parent_path=parent_path)
421
- for subdependency in field_definitions_from_dependency(dependency)
415
+ for subdependency in _field_definitions_from_dependency(dependency)
422
416
  ]
423
- workflow_task_output = workflow_task_schema_output_from_dependency(dependency)
417
+ workflow_task_output = _workflow_task_schema_output_from_dependency(dependency)
424
418
  if workflow_task_output:
425
- output_fields = field_definitions_from_dependency(workflow_task_output)
419
+ output_fields = _field_definitions_from_dependency(workflow_task_output)
426
420
  output_items = [
427
421
  _mock_workflow_output_subdependency(subdependency, dependency, parent_path=parent_path)
428
422
  for subdependency in output_fields
@@ -458,39 +452,40 @@ def _convert_entity_subtype(manifest_subtype: SchemaDependencySubtypesBeta) -> S
458
452
 
459
453
 
460
454
  def _mock_scalar_dependency(
461
- dependency: ManifestScalarConfig, parent_path: List[str] = list()
455
+ dependency: ManifestScalarConfig, parent_path: Optional[List[str]] = None
462
456
  ) -> AppConfigItem:
457
+ parent_path = parent_path if parent_path else []
463
458
  if isinstance(dependency, ManifestBooleanScalarConfig):
464
- bool_value = _mock_scalar_value_with_conversion(dependency, BoolScalar)
459
+ bool_value = cast(bool, _mock_scalar_value(dependency))
465
460
  bool_config = mock_bool_app_config_item([dependency.name], bool_value)
466
461
  return _append_config_item_path(bool_config, parent_path)
467
462
  elif isinstance(dependency, ManifestDateScalarConfig):
468
- date_value = _mock_scalar_value_with_conversion(dependency, DateScalar)
463
+ date_value = cast(date, _mock_scalar_value(dependency))
469
464
  date_config = mock_date_app_config_item([dependency.name], date_value)
470
465
  return _append_config_item_path(date_config, parent_path)
471
466
  elif isinstance(dependency, ManifestDatetimeScalarConfig):
472
- datetime_value = _mock_scalar_value_with_conversion(dependency, DateTimeScalar)
467
+ datetime_value = cast(datetime, _mock_scalar_value(dependency))
473
468
  datetime_config = mock_datetime_app_config_item([dependency.name], datetime_value)
474
469
  return _append_config_item_path(datetime_config, parent_path)
475
470
  elif isinstance(dependency, ManifestFloatScalarConfig):
476
- float_value = _mock_scalar_value_with_conversion(dependency, FloatScalar)
471
+ float_value = cast(float, _mock_scalar_value(dependency))
477
472
  float_config = mock_float_app_config_item([dependency.name], float_value)
478
473
  return _append_config_item_path(float_config, parent_path)
479
474
  elif isinstance(dependency, ManifestIntegerScalarConfig):
480
- int_value = _mock_scalar_value_with_conversion(dependency, IntScalar)
475
+ int_value = cast(int, _mock_scalar_value(dependency))
481
476
  int_config = mock_int_app_config_item([dependency.name], int_value)
482
477
  return _append_config_item_path(int_config, parent_path)
483
478
  elif isinstance(dependency, ManifestJsonScalarConfig):
484
- json_value = _mock_scalar_value_with_conversion(dependency, JsonScalar)
479
+ json_value = cast(JsonType, _mock_scalar_value(dependency))
485
480
  json_config = mock_json_app_config_item([dependency.name], json_value)
486
481
  return _append_config_item_path(json_config, parent_path)
487
482
  elif isinstance(dependency, ManifestSecureTextScalarConfig):
488
- secure_text_value = _mock_scalar_value_with_conversion(dependency, SecureTextScalar)
483
+ secure_text_value = cast(str, _mock_scalar_value(dependency))
489
484
  secure_text_config = mock_secure_text_app_config_item([dependency.name], secure_text_value)
490
485
  return _append_config_item_path(secure_text_config, parent_path)
491
486
  else:
492
487
  assert not isinstance(dependency, UnknownType), f"Unable to mock unknown type {dependency}"
493
- text_value = _mock_scalar_value_with_conversion(dependency, TextScalar)
488
+ text_value = cast(str, _mock_scalar_value(dependency))
494
489
  text_config = mock_text_app_config_item([dependency.name], text_value)
495
490
  return _append_config_item_path(text_config, parent_path)
496
491
 
@@ -503,12 +498,13 @@ def _append_config_item_path(config_item: AppConfigItem, parent_path: List[str])
503
498
 
504
499
 
505
500
  def _mock_array_dependency(
506
- dependency: ManifestArrayConfig, parent_path: List[str] = list(), rows: int = 1
501
+ dependency: ManifestArrayConfig, parent_path: Optional[List[str]] = None, rows: int = 1
507
502
  ) -> List[AppConfigItem]:
508
503
  config_rows = []
504
+ parent_path = parent_path if parent_path else []
509
505
  for i in range(rows):
510
506
  row = _mock_array_row(dependency, parent_path=parent_path)
511
- elements = element_definition_from_dependency(dependency)
507
+ elements = _element_definition_from_dependency(dependency)
512
508
  element_configs = [_mock_dependency(element, row.path) for element in elements]
513
509
  flattened_configs = [element for sublist in element_configs for element in sublist]
514
510
  config_rows.append(row)
@@ -516,8 +512,9 @@ def _mock_array_dependency(
516
512
  return config_rows
517
513
 
518
514
 
519
- def _mock_array_row(dependency: ManifestArrayConfig, parent_path: List[str] = list()):
515
+ def _mock_array_row(dependency: ManifestArrayConfig, parent_path: Optional[List[str]] = None):
520
516
  row_name = _random_string("Row ")
517
+ parent_path = parent_path if parent_path else []
521
518
  return ArrayElementAppConfigItem(
522
519
  id=_random_string("aci_"),
523
520
  path=parent_path + [dependency.name, row_name],
@@ -526,25 +523,16 @@ def _mock_array_row(dependency: ManifestArrayConfig, parent_path: List[str] = li
526
523
  )
527
524
 
528
525
 
529
- def _mock_scalar_value_with_conversion(
530
- dependency: ManifestScalarConfig, scalar_definition_type: Type[ScalarDefinition[ScalarType]]
531
- ) -> Optional[ScalarType]:
532
- assert not isinstance(dependency, UnknownType), f"Unable to mock unknown type {dependency}"
533
- # These types should be equivalent and this appeases MyPy
534
- mocked_value_type = ScalarConfigTypes(dependency.type)
535
- mocked_value_str = (
536
- _mock_scalar_with_enum(dependency)
537
- if _is_scalar_with_enum(dependency)
538
- else _mock_scalar_value(mocked_value_type)
539
- )
540
- return scalar_definition_type.from_str(mocked_value_str)
541
-
542
-
543
- def _mock_scalar_with_enum(dependency: ManifestScalarConfig) -> str:
526
+ def _mock_scalar_with_enum(dependency: ManifestScalarConfig) -> Union[float, int, str]:
544
527
  assert isinstance(
545
528
  dependency, (ManifestFloatScalarConfig, ManifestIntegerScalarConfig, ManifestTextScalarConfig)
546
529
  )
547
- return str(random.choice(dependency.enum))
530
+ value = random.choice(dependency.enum)
531
+ if isinstance(dependency, ManifestFloatScalarConfig):
532
+ return cast(float, value)
533
+ elif isinstance(dependency, ManifestIntegerScalarConfig):
534
+ return cast(int, value)
535
+ return str(value)
548
536
 
549
537
 
550
538
  def _is_scalar_with_enum(dependency: ManifestScalarConfig) -> bool:
@@ -552,15 +540,16 @@ def _is_scalar_with_enum(dependency: ManifestScalarConfig) -> bool:
552
540
  dependency, (ManifestFloatScalarConfig, ManifestIntegerScalarConfig, ManifestTextScalarConfig)
553
541
  ):
554
542
  # MyPy doesn't find this to be truthy without a specific len check
555
- return len(enum_from_dependency(dependency)) > 0
543
+ return len(_enum_from_dependency(dependency)) > 0
556
544
  return False
557
545
 
558
546
 
559
547
  def _mock_subdependency(
560
548
  subdependency: Union[BaseManifestConfig, FieldDefinitionsManifest],
561
549
  parent_config,
562
- parent_path: List[str] = list(),
550
+ parent_path: Optional[List[str]] = None,
563
551
  ) -> AppConfigItem:
552
+ parent_path = parent_path if parent_path else []
564
553
  if isinstance(parent_config, DropdownDependency):
565
554
  linked_resource_id = _random_string("opt_")
566
555
  return GenericApiIdentifiedAppConfigItem(
@@ -587,8 +576,9 @@ def _mock_subdependency(
587
576
  def _mock_workflow_output_subdependency(
588
577
  subdependency: Union[BaseManifestConfig, FieldDefinitionsManifest],
589
578
  parent_config,
590
- parent_path: List[str] = list(),
579
+ parent_path: Optional[List[str]] = None,
591
580
  ) -> AppConfigItem:
581
+ parent_path = parent_path if parent_path else []
592
582
  linked_resource_id = _random_string("tsf_")
593
583
  app_config = FieldAppConfigItem(
594
584
  id=_random_string("aci_"),
@@ -604,18 +594,28 @@ def _mock_linked_resource(id: str, name: Optional[str] = None) -> LinkedAppConfi
604
594
  return LinkedAppConfigResourceSummary(id=id, name=name if name else _random_string("Resource Name"))
605
595
 
606
596
 
607
- def _mock_scalar_value(scalar_type: ScalarConfigTypes) -> str:
597
+ def _mock_scalar_value(
598
+ dependency: ManifestScalarConfig,
599
+ ) -> Union[bool, date, datetime, int, float, str, Dict[str, Union[str, float]]]:
608
600
  """Mock a scalar config value from its manifest definition."""
609
- if scalar_type == scalar_type.BOOLEAN:
610
- return "true"
601
+ if isinstance(dependency, UnknownType):
602
+ raise UnsupportedConfigItemError(
603
+ f"Unable to mock scalar value for unsupported dependency type {dependency}"
604
+ )
605
+ # These types should be equivalent and this appeases MyPy
606
+ scalar_type = ScalarConfigTypes(dependency.type)
607
+ if _is_scalar_with_enum(dependency):
608
+ return _mock_scalar_with_enum(dependency)
609
+ elif scalar_type == scalar_type.BOOLEAN:
610
+ return True
611
611
  elif scalar_type == scalar_type.DATE:
612
- return date.today().strftime("%Y-%m-%d")
612
+ return date.today()
613
613
  elif scalar_type == scalar_type.DATETIME:
614
- return datetime.now().strftime(DateTimeScalar.expected_format())
614
+ return datetime.now()
615
615
  elif scalar_type == scalar_type.FLOAT:
616
- return str(random.random())
616
+ return random.random()
617
617
  elif scalar_type == scalar_type.INTEGER:
618
- return str(random.randint(-1000, 1000))
618
+ return random.randint(-1000, 1000)
619
619
  elif scalar_type == scalar_type.JSON:
620
620
  return json.dumps(
621
621
  {_random_string(): [_random_string(), _random_string()], _random_string(): random.random()}
@@ -14,9 +14,8 @@ from benchling_sdk.apps.status.errors import (
14
14
  SessionClosedError,
15
15
  SessionContextClosedError,
16
16
  )
17
- from benchling_sdk.apps.status.types import _ReferencedSessionLinkType
18
17
  from benchling_sdk.errors import BenchlingError
19
- from benchling_sdk.helpers.logging_helpers import log_stability_warning, sdk_logger, StabilityLevel
18
+ from benchling_sdk.helpers.logging_helpers import sdk_logger
20
19
  from benchling_sdk.models import (
21
20
  AppCanvasUpdate,
22
21
  AppSession,
@@ -35,8 +34,6 @@ if TYPE_CHECKING:
35
34
 
36
35
  _DEFAULT_APP_ERROR_MESSAGE = "An unexpected error occurred in the app"
37
36
 
38
- log_stability_warning(StabilityLevel.BETA)
39
-
40
37
 
41
38
  class SessionProvider(Protocol):
42
39
  """Provide a Benchling App Session to convey app status."""
@@ -685,20 +682,3 @@ def continue_session_context(
685
682
  context_enter_handler,
686
683
  context_exit_handler,
687
684
  )
688
-
689
-
690
- def ref(reference: _ReferencedSessionLinkType) -> str:
691
- """
692
- Ref.
693
-
694
- Helper method for easily serializing a referenced object into a string embeddable in AppSessionMessageCreate
695
- content.
696
-
697
- Example:
698
- dna_sequence = benchling.dna_sequences.get_by_id("seq_1234")
699
- AppSessionMessageCreate(f"This is my DNA sequence {ref(dna_sequence)} for analysis"
700
- """
701
- # Not sure {} are possible in Benchling IDs, but the spec says we're escaping
702
- unescape_id = reference.id
703
- escaped_id = unescape_id.replace("{", "\\{").replace("}", "\\}")
704
- return f"{{id:{escaped_id}}}"
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from benchling_sdk.apps.status.types import ReferencedSessionLinkType
4
+
5
+
6
+ def ref(reference: ReferencedSessionLinkType) -> str:
7
+ """
8
+ Ref.
9
+
10
+ Helper method for easily serializing a referenced object into a string embeddable in AppSessionMessageCreate
11
+ content.
12
+
13
+ Example:
14
+ dna_sequence = benchling.dna_sequences.get_by_id("seq_1234")
15
+ AppSessionMessageCreate(f"This is my DNA sequence {ref(dna_sequence)} for analysis"
16
+ """
17
+ # Not sure {} are possible in Benchling IDs, but the spec says we're escaping
18
+ unescape_id = reference.id
19
+ escaped_id = unescape_id.replace("{", "\\{").replace("}", "\\}")
20
+ return f"{{id:{escaped_id}}}"
@@ -23,7 +23,7 @@ from benchling_sdk.models import (
23
23
 
24
24
  # Taken from CHIP_SUPPORTED_COLUMN_TYPES in Benchling server
25
25
  # Anything we miss, they can still embed the ID themselves in a message
26
- _ReferencedSessionLinkType = Union[
26
+ ReferencedSessionLinkType = Union[
27
27
  AaSequence,
28
28
  Blob,
29
29
  Box,
@@ -0,0 +1,3 @@
1
+ from typing import Any, Dict, List, Union
2
+
3
+ JsonType = Union[Dict[str, Any], List[Any], str, int, float, bool]
@@ -3,6 +3,7 @@ from typing import Iterable, List, Optional, Union
3
3
  from benchling_api_client.v2.stable.api.assay_results import (
4
4
  abort_assay_results_transaction,
5
5
  archive_assay_results,
6
+ bulk_create_assay_results,
6
7
  bulk_get_assay_results,
7
8
  commit_assay_results_transaction,
8
9
  create_assay_results,
@@ -31,10 +32,12 @@ from benchling_sdk.models import (
31
32
  AssayResultIdsRequest,
32
33
  AssayResultIdsResponse,
33
34
  AssayResultsArchive,
35
+ AssayResultsBulkCreateInTableRequest,
34
36
  AssayResultsBulkCreateRequest,
35
37
  AssayResultsCreateResponse,
36
38
  AssayResultsPaginatedList,
37
39
  AssayResultTransactionCreateResponse,
40
+ AsyncTaskLink,
38
41
  ListAssayResultsSort,
39
42
  )
40
43
  from benchling_sdk.services.v2.base_service import BaseService
@@ -196,6 +199,21 @@ class AssayResultService(BaseService):
196
199
  response = create_assay_results.sync_detailed(client=self.client, json_body=create_results)
197
200
  return model_from_detailed(response)
198
201
 
202
+ @api_method
203
+ def bulk_create(
204
+ self, assay_results: Iterable[AssayResultCreate], table_id: Optional[str] = None
205
+ ) -> AsyncTaskLink:
206
+ """
207
+ Create 1 or more results.
208
+
209
+ See https://benchling.com/api/reference#/Assay%20Results/bulkCreateAssayResults
210
+ """
211
+ request_body = AssayResultsBulkCreateInTableRequest(
212
+ assay_results=list(assay_results), **({"table_id": table_id} if table_id else {})
213
+ )
214
+ response = bulk_create_assay_results.sync_detailed(client=self.client, json_body=request_body)
215
+ return model_from_detailed(response)
216
+
199
217
  @api_method
200
218
  def archive(self, assay_result_ids: Iterable[str]) -> AssayResultIdsResponse:
201
219
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: benchling-sdk
3
- Version: 1.10.0a5
3
+ Version: 1.10.0a7
4
4
  Summary: SDK for interacting with the Benchling Platform.
5
5
  License: Apache-2.0
6
6
  Author: Benchling Support
@@ -17,7 +17,7 @@ Provides-Extra: python-jose
17
17
  Requires-Dist: PyYAML (>=6.0,<7.0)
18
18
  Requires-Dist: attrs (>=20.1.0,<23)
19
19
  Requires-Dist: backoff (>=1.10.0,<2.0.0)
20
- Requires-Dist: benchling-api-client (==2.0.232)
20
+ Requires-Dist: benchling-api-client (==2.0.243)
21
21
  Requires-Dist: certifi (>=2022.12.7)
22
22
  Requires-Dist: cryptography (>=41.0.3,<42.0.0) ; extra == "cryptography"
23
23
  Requires-Dist: dataclasses-json (>=0.5.2,<0.6.0)
@@ -3,25 +3,26 @@ benchling_sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  benchling_sdk/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  benchling_sdk/apps/canvas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  benchling_sdk/apps/canvas/errors.py,sha256=5YTZOhSV-O0WqURjB1a676BIO429drCKfz5aQjsezms,358
6
- benchling_sdk/apps/canvas/framework.py,sha256=99Ox8V-CvPjxWNyJQ0eaJmP-O8wABVBumjmfw7UMH5U,22971
6
+ benchling_sdk/apps/canvas/framework.py,sha256=a6XbCZIUsz4VmOCtu6kZnBQoQiLJtBk2Ndw94J0s4kM,24381
7
7
  benchling_sdk/apps/canvas/types.py,sha256=tmuU0kpzd1pXqxs3X6x5kjWpCZJrJMb6hnfN1CbtFG4,3613
8
- benchling_sdk/apps/config/__init__.py,sha256=8HK_CIvB57JMqDrQMQoMVauaeTqGg_dKgirIP4KjJRM,132
8
+ benchling_sdk/apps/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  benchling_sdk/apps/config/cryptography_helpers.py,sha256=VCzgQURLAitYjcjdwFzMPSKZ7MOD6ghxFdbPGA_6RbY,1778
10
- benchling_sdk/apps/config/decryption_provider.py,sha256=70AHEglmIqpTixI69tmT_1eS6DXIn5QQ6N9Z7PJOzak,2325
10
+ benchling_sdk/apps/config/decryption_provider.py,sha256=-HgjkBto8mYlooV0LXFenfaiP7PDhHRnKEHkK1P6gh4,2184
11
11
  benchling_sdk/apps/config/errors.py,sha256=-qkfLdcUTuuRD-V4L2l7nzQWnWTRuifDtkNDv8tfHoo,800
12
- benchling_sdk/apps/config/framework.py,sha256=539HphdYr6CdFpRtZsXeRaxxKFvsZfsIs7KPcIYgOe8,13320
13
- benchling_sdk/apps/config/mock_config.py,sha256=y48I41pp3MTaH9fM6v7KeKCwFM3DKzKzk60XP4ecKIA,26712
14
- benchling_sdk/apps/config/scalars.py,sha256=nxISpOn7GtEIkEtuLb4B4PkrYeRbG-QsS0AXRZLpZ00,5442
12
+ benchling_sdk/apps/config/framework.py,sha256=a2cIkPugEVUfBNtHm2cbE3Qu326xb-SpMuM0Gi6U-3Q,13337
13
+ benchling_sdk/apps/config/helpers.py,sha256=LTLDR0GCDoo_8Sx4kepQnMmgVEQT7MHfKAxIoAvV9TM,7077
14
+ benchling_sdk/apps/config/mock_config.py,sha256=EPUCxWiUSsWxWilnHAJIq-_f1q6ckZkXoIFv1S25hnM,26967
15
15
  benchling_sdk/apps/config/types.py,sha256=XKfSGv-75CU-j1XwfXBGq8zbtnkF-PQnuY6Z2U47-Tg,953
16
16
  benchling_sdk/apps/framework.py,sha256=H51rAzwH4PQHMGTxx2MdYgHMW1oKa_FLnozzY8XGL2g,3308
17
17
  benchling_sdk/apps/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- benchling_sdk/apps/helpers/config_helpers.py,sha256=_t_ccSS53WpaNnwcSx3Mx5ranRJgxIQWcpBoUSWFKFA,9318
19
18
  benchling_sdk/apps/helpers/manifest_helpers.py,sha256=yidiKA5mwWdAFR1ZSUu40QoG0Us-bc6V8tYHYqheEtM,945
20
19
  benchling_sdk/apps/helpers/webhook_helpers.py,sha256=T-ZJLWb37nzSKMQFAQQ8FJU7al_QFBgNRhTfWDfCfYU,7077
21
20
  benchling_sdk/apps/status/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
21
  benchling_sdk/apps/status/errors.py,sha256=E9PugMd0YwZQWweTztIZyqycECBuVvH6PcLfij0Cb3k,2279
23
- benchling_sdk/apps/status/framework.py,sha256=ZzH0vQMxVOPLkdkyzBbO9rUpW0HUJ1UhTmBFbGqEur0,27112
24
- benchling_sdk/apps/status/types.py,sha256=kmzA2PgUlodU5eP6QiryXm1vZHjjSx40K9zHZnDvOJ4,747
22
+ benchling_sdk/apps/status/framework.py,sha256=CWPIlj8WhRZeIImCLka9LmhXvzbK0be9ekdLxiUKKJk,26358
23
+ benchling_sdk/apps/status/helpers.py,sha256=o8jdgdxUYdGh2j0Gb3AYoeXI9Bu-ubyIkgWBuCGLKgE,705
24
+ benchling_sdk/apps/status/types.py,sha256=vE7-_7ns3mvPRJoVv-GQxc3389dIIH4mcG0VDNWpVhQ,746
25
+ benchling_sdk/apps/types.py,sha256=TBTfAB3IOoZjZqzTOK25kN3b6RTU9Z7tOMdBbq6u8lA,110
25
26
  benchling_sdk/auth/__init__.py,sha256=N4pJYVUnTLzg5HO9ZldHaI-Am97i6AOCdQS0M5QcVpA,120
26
27
  benchling_sdk/auth/api_key_auth.py,sha256=Ui-cnvGMjcwVPV_b2GdBaoTjEyHJIu9CjtZScVBEUvU,641
27
28
  benchling_sdk/auth/bearer_token_auth.py,sha256=nymI8V91evcnK-TWKkBXZwck8U1qSh4WaseyQbvF-Cg,1268
@@ -70,7 +71,7 @@ benchling_sdk/services/v2/stable/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
70
71
  benchling_sdk/services/v2/stable/aa_sequence_service.py,sha256=3RCNwyFBrryQowC9fbLoQHSSqKyGA6TQ0VQYBUyeg1U,11782
71
72
  benchling_sdk/services/v2/stable/api_service.py,sha256=e0RNp4Jne9UTaHOYMOxxXJpsyUO3WMSFPzN7hB8iliU,12371
72
73
  benchling_sdk/services/v2/stable/app_service.py,sha256=dy9FMVvMiJrkYbxZswKuB37EB9K_jCqQr9FfAa0BMgE,23126
73
- benchling_sdk/services/v2/stable/assay_result_service.py,sha256=hbptHLNbJFk5-2HBZJaSscKwSmVO4V9KcoruiMxnNKc,12659
74
+ benchling_sdk/services/v2/stable/assay_result_service.py,sha256=_qtSpMznVrGHlB2VnHI87IPjVQHCX1pZbLPJ_LYuW7U,13362
74
75
  benchling_sdk/services/v2/stable/assay_run_service.py,sha256=tLpG1pAztEoaDatqlgYlRZ_LgwDly1_UeWt4G4UXvd4,9023
75
76
  benchling_sdk/services/v2/stable/blob_service.py,sha256=KwchH3FGzPLfZx85GEG3voQp_ZxyVL_dds_9awRWa0Q,17115
76
77
  benchling_sdk/services/v2/stable/box_service.py,sha256=M40smqG-jQlioyRfLBu9Cr9aQmdh06crsYbH1fiZ0BM,12359
@@ -114,7 +115,7 @@ benchling_sdk/services/v2/v2_alpha_service.py,sha256=vNfYK0Dheml9ozR_0tzTlA3blPD
114
115
  benchling_sdk/services/v2/v2_beta_service.py,sha256=7v9ngWH_XTinkVC7bIxxbaA1UeFQqkzR4ANGtQQN-fA,11521
115
116
  benchling_sdk/services/v2/v2_stable_service.py,sha256=YIc-_0p6x1NQqj7awnWgCTHxbxYO7ZOolePZyW90whc,35246
116
117
  benchling_sdk/services/v2_service.py,sha256=3eoIjYEmGLPdWCpBN0pl7q7_HNWCsUvfvTn3Hcz0wSM,2860
117
- benchling_sdk-1.10.0a5.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
118
- benchling_sdk-1.10.0a5.dist-info/METADATA,sha256=WCDWEH27AZkjMCbVkMrtlnTlmj6FwLY5s7wjnZgUZkc,2126
119
- benchling_sdk-1.10.0a5.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
120
- benchling_sdk-1.10.0a5.dist-info/RECORD,,
118
+ benchling_sdk-1.10.0a7.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
119
+ benchling_sdk-1.10.0a7.dist-info/METADATA,sha256=GKfUuppZ9cw6GIv2w-jZQ_I4B5sm1I-2jXS2h_ccb4Q,2126
120
+ benchling_sdk-1.10.0a7.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
121
+ benchling_sdk-1.10.0a7.dist-info/RECORD,,
@@ -1,190 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from abc import ABC, abstractmethod
4
- from datetime import date, datetime
5
- import json
6
- from typing import Any, Dict, Generic, List, Optional, TypeVar, Union
7
-
8
- from benchling_sdk.models import (
9
- BooleanAppConfigItemType,
10
- DateAppConfigItemType,
11
- DatetimeAppConfigItemType,
12
- FloatAppConfigItemType,
13
- IntegerAppConfigItemType,
14
- JsonAppConfigItemType,
15
- SecureTextAppConfigItemType,
16
- TextAppConfigItemType,
17
- )
18
-
19
- JsonType = Union[Dict[str, Any], List[Any], str, int, float, bool]
20
- ScalarType = TypeVar("ScalarType", bool, date, datetime, float, int, JsonType, str)
21
- # JsonType support requires object to be unioned. Currently we do it inline.
22
- ScalarModelType = Union[bool, date, datetime, float, int, str]
23
- # Enum values cannot be used in literals, so copy the strings
24
- ScalarConfigItemType = Union[
25
- BooleanAppConfigItemType,
26
- DateAppConfigItemType,
27
- DatetimeAppConfigItemType,
28
- FloatAppConfigItemType,
29
- IntegerAppConfigItemType,
30
- JsonAppConfigItemType,
31
- SecureTextAppConfigItemType,
32
- TextAppConfigItemType,
33
- ]
34
-
35
-
36
- # TODO BNCH-52772 We should probably consider moving all of these to a weakly _ internal scoped usage
37
-
38
-
39
- class ScalarDefinition(ABC, Generic[ScalarType]):
40
- """
41
- Scalar definition.
42
-
43
- Map how ScalarConfigTypes values can be converted into corresponding Python types.
44
- """
45
-
46
- @classmethod
47
- def init(cls):
48
- """Init."""
49
- return cls()
50
-
51
- @classmethod
52
- @abstractmethod
53
- def from_str(cls, value: Optional[str]) -> Optional[ScalarType]:
54
- """
55
- From string.
56
-
57
- Given an optional string value of scalar configuration, produce an Optional instance of the
58
- specific ScalarType. For instance, converting str to int.
59
-
60
- Used when coercing Python types from string values in API responses.
61
- """
62
- pass
63
-
64
-
65
- class BoolScalar(ScalarDefinition[bool]):
66
- """
67
- Bool Scalar.
68
-
69
- Turn a Boolean-like string value into bool. Any permutation of "true" - case insensitive - is interpreted
70
- as True. Any other non-empty string is False.
71
- """
72
-
73
- @classmethod
74
- def from_str(cls, value: Optional[str]) -> Optional[bool]:
75
- """Convert optional str to optional bool."""
76
- # Though the spec declares str, this is actually being sent in JSON as a real Boolean
77
- # So runtime check defensively
78
- if value is not None:
79
- if isinstance(value, bool):
80
- return value
81
- if value.lower() == "true":
82
- return True
83
- return False
84
- return None
85
-
86
-
87
- class DateScalar(ScalarDefinition[date]):
88
- """
89
- Date Scalar.
90
-
91
- Turn an ISO formatted date like YYYY-MM-dd into a date.
92
- """
93
-
94
- @classmethod
95
- def from_str(cls, value: Optional[str]) -> Optional[date]:
96
- """Convert optional str to optional date."""
97
- return date.fromisoformat(value) if value is not None else None
98
-
99
-
100
- class DateTimeScalar(ScalarDefinition[datetime]):
101
- """
102
- Date Time Scalar.
103
-
104
- Turn a date time string into datetime.
105
- """
106
-
107
- @classmethod
108
- def from_str(cls, value: Optional[str]) -> Optional[datetime]:
109
- """Convert optional str to optional datetime."""
110
- return datetime.strptime(value, cls.expected_format()) if value is not None else None
111
-
112
- @staticmethod
113
- def expected_format() -> str:
114
- """Return the expected date mask for parsing string to datetime."""
115
- return "%Y-%m-%d %I:%M:%S %p"
116
-
117
-
118
- class IsoDateTimeScalar(ScalarDefinition[datetime]):
119
- """
120
- Iso Date Time Scalar.
121
-
122
- Turn a ISO 8601 date time string into datetime. Benchling fields use RFC 3339, unlike app config date times.
123
- """
124
-
125
- @classmethod
126
- def from_str(cls, value: Optional[str]) -> Optional[datetime]:
127
- """Convert optional str to optional datetime."""
128
- return datetime.fromisoformat(value) if value is not None else None
129
-
130
-
131
- class FloatScalar(ScalarDefinition[float]):
132
- """
133
- Float Scalar.
134
-
135
- Turn a string into float. Assumes the string, if not empty, is a valid floating point.
136
- """
137
-
138
- @classmethod
139
- def from_str(cls, value: Optional[str]) -> Optional[float]:
140
- """Convert optional str to optional float."""
141
- return float(value) if value is not None else None
142
-
143
-
144
- class IntScalar(ScalarDefinition[int]):
145
- """
146
- Int Scalar.
147
-
148
- Turn a string into int. Assumes the string, if not empty, is a valid integer.
149
- """
150
-
151
- @classmethod
152
- def from_str(cls, value: Optional[str]) -> Optional[int]:
153
- """Convert optional str to optional int."""
154
- return int(value) if value is not None else None
155
-
156
-
157
- class JsonScalar(ScalarDefinition[JsonType]):
158
- """
159
- Json Scalar.
160
-
161
- Turn a string into JSON. Assumes the string is a valid JSON string.
162
- """
163
-
164
- @classmethod
165
- def from_str(cls, value: Optional[str]) -> Optional[JsonType]:
166
- """Convert optional str to optional JsonType."""
167
- return json.loads(value) if value is not None else None
168
-
169
-
170
- class TextScalar(ScalarDefinition[str]):
171
- """
172
- Text Scalar.
173
-
174
- Text is already a string, so no conversion is performed.
175
- """
176
-
177
- @classmethod
178
- def from_str(cls, value: Optional[str]) -> Optional[str]:
179
- """Convert optional str to optional str. Implemented to appease ScalarDefinition contract."""
180
- return value
181
-
182
-
183
- class SecureTextScalar(TextScalar):
184
- """
185
- Secure Text Scalar.
186
-
187
- Text is already a string, so no conversion is performed.
188
- """
189
-
190
- pass