benchling-sdk 1.18.0__py3-none-any.whl → 1.18.1__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/config/helpers.py +186 -0
- benchling_sdk/apps/config/mock_config.py +635 -0
- {benchling_sdk-1.18.0.dist-info → benchling_sdk-1.18.1.dist-info}/METADATA +1 -1
- {benchling_sdk-1.18.0.dist-info → benchling_sdk-1.18.1.dist-info}/RECORD +6 -4
- {benchling_sdk-1.18.0.dist-info → benchling_sdk-1.18.1.dist-info}/LICENSE +0 -0
- {benchling_sdk-1.18.0.dist-info → benchling_sdk-1.18.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,186 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from enum import Enum
|
3
|
+
from functools import lru_cache
|
4
|
+
from typing import cast, List, Optional, Union
|
5
|
+
|
6
|
+
from benchling_api_client.v2.beta.extensions import Enums
|
7
|
+
from benchling_api_client.v2.beta.models.base_manifest_config import BaseManifestConfig
|
8
|
+
from benchling_api_client.v2.beta.models.dropdown_dependency import DropdownDependency
|
9
|
+
from benchling_api_client.v2.beta.models.dropdown_dependency_types import DropdownDependencyTypes
|
10
|
+
from benchling_api_client.v2.beta.models.entity_schema_dependency import EntitySchemaDependency
|
11
|
+
from benchling_api_client.v2.beta.models.entity_schema_dependency_type import EntitySchemaDependencyType
|
12
|
+
from benchling_api_client.v2.beta.models.field_definitions_manifest import FieldDefinitionsManifest
|
13
|
+
from benchling_api_client.v2.beta.models.manifest_array_config import ManifestArrayConfig
|
14
|
+
from benchling_api_client.v2.beta.models.manifest_float_scalar_config import ManifestFloatScalarConfig
|
15
|
+
from benchling_api_client.v2.beta.models.manifest_integer_scalar_config import ManifestIntegerScalarConfig
|
16
|
+
from benchling_api_client.v2.beta.models.manifest_scalar_config import ManifestScalarConfig
|
17
|
+
from benchling_api_client.v2.beta.models.manifest_text_scalar_config import ManifestTextScalarConfig
|
18
|
+
from benchling_api_client.v2.beta.models.resource_dependency import ResourceDependency
|
19
|
+
from benchling_api_client.v2.beta.models.resource_dependency_types import ResourceDependencyTypes
|
20
|
+
from benchling_api_client.v2.beta.models.schema_dependency import SchemaDependency
|
21
|
+
from benchling_api_client.v2.beta.models.schema_dependency_subtypes import SchemaDependencySubtypes
|
22
|
+
from benchling_api_client.v2.beta.models.schema_dependency_types import SchemaDependencyTypes
|
23
|
+
from benchling_api_client.v2.beta.models.workflow_task_schema_dependency import WorkflowTaskSchemaDependency
|
24
|
+
from benchling_api_client.v2.beta.models.workflow_task_schema_dependency_output import (
|
25
|
+
WorkflowTaskSchemaDependencyOutput,
|
26
|
+
)
|
27
|
+
from benchling_api_client.v2.beta.models.workflow_task_schema_dependency_type import (
|
28
|
+
WorkflowTaskSchemaDependencyType,
|
29
|
+
)
|
30
|
+
from benchling_api_client.v2.extensions import UnknownType
|
31
|
+
from benchling_api_client.v2.stable.extensions import NotPresentError
|
32
|
+
|
33
|
+
_ArrayElementDependency = Union[
|
34
|
+
SchemaDependency,
|
35
|
+
EntitySchemaDependency,
|
36
|
+
WorkflowTaskSchemaDependency,
|
37
|
+
DropdownDependency,
|
38
|
+
ResourceDependency,
|
39
|
+
ManifestScalarConfig,
|
40
|
+
]
|
41
|
+
|
42
|
+
|
43
|
+
class _UnsupportedSubTypeError(Exception):
|
44
|
+
"""Error when an unsupported subtype is encountered."""
|
45
|
+
|
46
|
+
pass
|
47
|
+
|
48
|
+
|
49
|
+
class _ScalarConfigTypes(Enums.KnownString):
|
50
|
+
"""Enum type copied from an earlier version of benchling-api-client, for internal use only.
|
51
|
+
|
52
|
+
See BNCH-108704.
|
53
|
+
"""
|
54
|
+
|
55
|
+
TEXT = "text"
|
56
|
+
FLOAT = "float"
|
57
|
+
INTEGER = "integer"
|
58
|
+
BOOLEAN = "boolean"
|
59
|
+
DATE = "date"
|
60
|
+
DATETIME = "datetime"
|
61
|
+
SECURE_TEXT = "secure_text"
|
62
|
+
JSON = "json"
|
63
|
+
|
64
|
+
def __str__(self) -> str:
|
65
|
+
return str(self.value)
|
66
|
+
|
67
|
+
@staticmethod
|
68
|
+
@lru_cache(maxsize=None)
|
69
|
+
def of_unknown(val: str) -> "_ScalarConfigTypes":
|
70
|
+
if not isinstance(val, str):
|
71
|
+
raise ValueError(f"Value of _ScalarConfigTypes must be a string (encountered: {val})")
|
72
|
+
newcls = Enum("_ScalarConfigTypes", {"_UNKNOWN": val}, type=Enums.UnknownString) # type: ignore
|
73
|
+
return cast(_ScalarConfigTypes, getattr(newcls, "_UNKNOWN"))
|
74
|
+
|
75
|
+
|
76
|
+
def _field_definitions_from_dependency(
|
77
|
+
dependency: Union[
|
78
|
+
EntitySchemaDependency,
|
79
|
+
SchemaDependency,
|
80
|
+
WorkflowTaskSchemaDependency,
|
81
|
+
WorkflowTaskSchemaDependencyOutput,
|
82
|
+
]
|
83
|
+
) -> List[FieldDefinitionsManifest]:
|
84
|
+
"""Safely return a list of field definitions from a schema dependency or empty list."""
|
85
|
+
try:
|
86
|
+
if hasattr(dependency, "field_definitions"):
|
87
|
+
return dependency.field_definitions
|
88
|
+
# We can't seem to handle this programmatically by checking isinstance() or field truthiness
|
89
|
+
except NotPresentError:
|
90
|
+
pass
|
91
|
+
return []
|
92
|
+
|
93
|
+
|
94
|
+
def _element_definition_from_dependency(dependency: ManifestArrayConfig) -> List[_ArrayElementDependency]:
|
95
|
+
"""Safely return an element definition as a list of dependencies from an array dependency or empty list."""
|
96
|
+
try:
|
97
|
+
if hasattr(dependency, "element_definition"):
|
98
|
+
return [
|
99
|
+
_fix_element_definition_deserialization(element) for element in dependency.element_definition
|
100
|
+
]
|
101
|
+
# We can't seem to handle this programmatically by checking isinstance() or field truthiness
|
102
|
+
except NotPresentError:
|
103
|
+
pass
|
104
|
+
return []
|
105
|
+
|
106
|
+
|
107
|
+
def _enum_from_dependency(
|
108
|
+
dependency: Union[
|
109
|
+
ManifestFloatScalarConfig,
|
110
|
+
ManifestIntegerScalarConfig,
|
111
|
+
ManifestTextScalarConfig,
|
112
|
+
]
|
113
|
+
) -> List:
|
114
|
+
"""Safely return an enum from a scalar config."""
|
115
|
+
try:
|
116
|
+
if hasattr(dependency, "enum"):
|
117
|
+
return dependency.enum
|
118
|
+
# We can't seem to handle this programmatically by checking isinstance() or field truthiness
|
119
|
+
except NotPresentError:
|
120
|
+
pass
|
121
|
+
return []
|
122
|
+
|
123
|
+
|
124
|
+
# TODO BNCH-57036 All element definitions currently deserialize to UnknownType. Hack around this temporarily
|
125
|
+
def _fix_element_definition_deserialization(
|
126
|
+
element: Union[UnknownType, _ArrayElementDependency]
|
127
|
+
) -> _ArrayElementDependency:
|
128
|
+
if isinstance(element, UnknownType):
|
129
|
+
if "type" in element.value:
|
130
|
+
element_type = element.value["type"]
|
131
|
+
if element_type == WorkflowTaskSchemaDependencyType.WORKFLOW_TASK_SCHEMA:
|
132
|
+
return WorkflowTaskSchemaDependency.from_dict(element.value)
|
133
|
+
elif element_type == EntitySchemaDependencyType.ENTITY_SCHEMA:
|
134
|
+
return EntitySchemaDependency.from_dict(element.value)
|
135
|
+
elif element_type in [member.value for member in SchemaDependencyTypes]:
|
136
|
+
return SchemaDependency.from_dict(element.value)
|
137
|
+
elif element_type == DropdownDependencyTypes.DROPDOWN:
|
138
|
+
return DropdownDependency.from_dict(element.value)
|
139
|
+
elif element_type in [member.value for member in ResourceDependencyTypes]:
|
140
|
+
return ResourceDependency.from_dict(element.value)
|
141
|
+
elif element_type in [member.value for member in _ScalarConfigTypes]:
|
142
|
+
return type(element_type).from_dict(element.value)
|
143
|
+
raise NotImplementedError(f"No array deserialization fix for {element}")
|
144
|
+
return element
|
145
|
+
|
146
|
+
|
147
|
+
def _workflow_task_schema_output_from_dependency(
|
148
|
+
dependency: WorkflowTaskSchemaDependency,
|
149
|
+
) -> Optional[WorkflowTaskSchemaDependencyOutput]:
|
150
|
+
"""Safely return a workflow task schema output from a workflow task schema or None."""
|
151
|
+
try:
|
152
|
+
if hasattr(dependency, "output"):
|
153
|
+
return dependency.output
|
154
|
+
# We can't seem to handle this programmatically by checking isinstance() or output truthiness
|
155
|
+
except NotPresentError:
|
156
|
+
pass
|
157
|
+
return None
|
158
|
+
|
159
|
+
|
160
|
+
def _options_from_dependency(dependency: DropdownDependency) -> List[BaseManifestConfig]:
|
161
|
+
"""Safely return a list of options from a dropdown dependency or empty list."""
|
162
|
+
try:
|
163
|
+
if hasattr(dependency, "options"):
|
164
|
+
return dependency.options
|
165
|
+
# We can't seem to handle this programmatically by checking isinstance() or field truthiness
|
166
|
+
except NotPresentError:
|
167
|
+
pass
|
168
|
+
return []
|
169
|
+
|
170
|
+
|
171
|
+
def _subtype_from_entity_schema_dependency(
|
172
|
+
dependency: EntitySchemaDependency,
|
173
|
+
) -> Optional[SchemaDependencySubtypes]:
|
174
|
+
"""Safely return an entity schema dependency's subtype, if present."""
|
175
|
+
try:
|
176
|
+
if hasattr(dependency, "subtype") and dependency.subtype:
|
177
|
+
return dependency.subtype
|
178
|
+
# We can't seem to handle this programmatically by checking isinstance() or field truthiness
|
179
|
+
except NotPresentError:
|
180
|
+
pass
|
181
|
+
return None
|
182
|
+
|
183
|
+
|
184
|
+
def datetime_config_value_to_str(value: datetime) -> str:
|
185
|
+
"""Convert a datetime value to a valid string accepted by a datetime app config item."""
|
186
|
+
return value.strftime("%Y-%m-%d %I:%M:%S %p")
|
@@ -0,0 +1,635 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from datetime import date, datetime
|
4
|
+
import json
|
5
|
+
import random
|
6
|
+
import string
|
7
|
+
from typing import cast, Dict, get_args, List, Optional, Protocol, Union
|
8
|
+
|
9
|
+
from benchling_api_client.v2.beta.models.base_manifest_config import BaseManifestConfig
|
10
|
+
from benchling_api_client.v2.beta.models.benchling_app_manifest import BenchlingAppManifest
|
11
|
+
from benchling_api_client.v2.beta.models.dropdown_dependency import DropdownDependency
|
12
|
+
from benchling_api_client.v2.beta.models.entity_schema_dependency import EntitySchemaDependency
|
13
|
+
from benchling_api_client.v2.beta.models.field_definitions_manifest import FieldDefinitionsManifest
|
14
|
+
from benchling_api_client.v2.beta.models.manifest_array_config import ManifestArrayConfig
|
15
|
+
from benchling_api_client.v2.beta.models.manifest_boolean_scalar_config import ManifestBooleanScalarConfig
|
16
|
+
from benchling_api_client.v2.beta.models.manifest_date_scalar_config import ManifestDateScalarConfig
|
17
|
+
from benchling_api_client.v2.beta.models.manifest_datetime_scalar_config import ManifestDatetimeScalarConfig
|
18
|
+
from benchling_api_client.v2.beta.models.manifest_float_scalar_config import ManifestFloatScalarConfig
|
19
|
+
from benchling_api_client.v2.beta.models.manifest_integer_scalar_config import ManifestIntegerScalarConfig
|
20
|
+
from benchling_api_client.v2.beta.models.manifest_json_scalar_config import ManifestJsonScalarConfig
|
21
|
+
from benchling_api_client.v2.beta.models.manifest_scalar_config import ManifestScalarConfig
|
22
|
+
from benchling_api_client.v2.beta.models.manifest_secure_text_scalar_config import (
|
23
|
+
ManifestSecureTextScalarConfig,
|
24
|
+
)
|
25
|
+
from benchling_api_client.v2.beta.models.manifest_text_scalar_config import ManifestTextScalarConfig
|
26
|
+
from benchling_api_client.v2.beta.models.resource_dependency import ResourceDependency
|
27
|
+
from benchling_api_client.v2.beta.models.schema_dependency import SchemaDependency
|
28
|
+
from benchling_api_client.v2.beta.models.schema_dependency_subtypes import (
|
29
|
+
SchemaDependencySubtypes as SchemaDependencySubtypesBeta,
|
30
|
+
)
|
31
|
+
from benchling_api_client.v2.beta.models.workflow_task_schema_dependency import WorkflowTaskSchemaDependency
|
32
|
+
from benchling_api_client.v2.extensions import UnknownType
|
33
|
+
from benchling_api_client.v2.stable.types import UNSET, Unset
|
34
|
+
|
35
|
+
from benchling_sdk.apps.config.decryption_provider import BaseDecryptionProvider
|
36
|
+
from benchling_sdk.apps.config.errors import UnsupportedConfigItemError
|
37
|
+
from benchling_sdk.apps.config.framework import _supported_config_item, ConfigItemStore, StaticConfigProvider
|
38
|
+
from benchling_sdk.apps.config.helpers import (
|
39
|
+
_element_definition_from_dependency,
|
40
|
+
_enum_from_dependency,
|
41
|
+
_field_definitions_from_dependency,
|
42
|
+
_options_from_dependency,
|
43
|
+
_ScalarConfigTypes,
|
44
|
+
_subtype_from_entity_schema_dependency,
|
45
|
+
_workflow_task_schema_output_from_dependency,
|
46
|
+
datetime_config_value_to_str,
|
47
|
+
)
|
48
|
+
from benchling_sdk.apps.config.types import ConfigurationReference
|
49
|
+
from benchling_sdk.apps.types import JsonType
|
50
|
+
from benchling_sdk.helpers.logging_helpers import log_stability_warning, StabilityLevel
|
51
|
+
from benchling_sdk.models import (
|
52
|
+
AppConfigItem,
|
53
|
+
ArrayElementAppConfigItem,
|
54
|
+
ArrayElementAppConfigItemType,
|
55
|
+
BooleanAppConfigItem,
|
56
|
+
BooleanAppConfigItemType,
|
57
|
+
DateAppConfigItem,
|
58
|
+
DateAppConfigItemType,
|
59
|
+
DatetimeAppConfigItem,
|
60
|
+
DatetimeAppConfigItemType,
|
61
|
+
EntitySchemaAppConfigItem,
|
62
|
+
EntitySchemaAppConfigItemType,
|
63
|
+
FieldAppConfigItem,
|
64
|
+
FieldAppConfigItemType,
|
65
|
+
FloatAppConfigItem,
|
66
|
+
FloatAppConfigItemType,
|
67
|
+
GenericApiIdentifiedAppConfigItem,
|
68
|
+
GenericApiIdentifiedAppConfigItemType,
|
69
|
+
IntegerAppConfigItem,
|
70
|
+
IntegerAppConfigItemType,
|
71
|
+
JsonAppConfigItem,
|
72
|
+
JsonAppConfigItemType,
|
73
|
+
LinkedAppConfigResourceSummary,
|
74
|
+
SchemaDependencySubtypes,
|
75
|
+
SecureTextAppConfigItem,
|
76
|
+
SecureTextAppConfigItemType,
|
77
|
+
TextAppConfigItem,
|
78
|
+
TextAppConfigItemType,
|
79
|
+
)
|
80
|
+
|
81
|
+
ManifestDependencies = Union[
|
82
|
+
DropdownDependency,
|
83
|
+
EntitySchemaDependency,
|
84
|
+
ManifestArrayConfig,
|
85
|
+
ManifestScalarConfig,
|
86
|
+
ResourceDependency,
|
87
|
+
SchemaDependency,
|
88
|
+
WorkflowTaskSchemaDependency,
|
89
|
+
UnknownType,
|
90
|
+
]
|
91
|
+
|
92
|
+
log_stability_warning(StabilityLevel.BETA)
|
93
|
+
|
94
|
+
|
95
|
+
class MockDecryptionFunction(Protocol):
|
96
|
+
"""Mock out a decryption function for use with secure text."""
|
97
|
+
|
98
|
+
def __call__(self, ciphertext: str) -> str:
|
99
|
+
"""Return a string representing plaintext given input ciphertext."""
|
100
|
+
|
101
|
+
|
102
|
+
class MockDecryptionProvider(BaseDecryptionProvider):
|
103
|
+
"""
|
104
|
+
Mock Decryption Provider.
|
105
|
+
|
106
|
+
A generic class mocking a BaseDecryptionProvider. Can be passed a function to mock arbitrary decryption.
|
107
|
+
|
108
|
+
It's recommended to extend this class or use a specific implementation instead of initializing an instance.
|
109
|
+
"""
|
110
|
+
|
111
|
+
_mock_decryption_function: MockDecryptionFunction
|
112
|
+
|
113
|
+
def __init__(self, mock_decryption_function: MockDecryptionFunction):
|
114
|
+
"""
|
115
|
+
Init Mock Decryption Provider.
|
116
|
+
|
117
|
+
Pass a function that returns desired mocked plaintext given ciphertext.
|
118
|
+
"""
|
119
|
+
self._mock_decryption_function = mock_decryption_function
|
120
|
+
|
121
|
+
def decrypt(self, ciphertext: str) -> str:
|
122
|
+
"""
|
123
|
+
Decrypt.
|
124
|
+
|
125
|
+
Invokes the mocked decryption function provided when instantiating the class to return a "decrypted" value.
|
126
|
+
"""
|
127
|
+
return self._mock_decryption_function(ciphertext)
|
128
|
+
|
129
|
+
|
130
|
+
class MockConfigItemStore(ConfigItemStore):
|
131
|
+
"""
|
132
|
+
Mock App Config.
|
133
|
+
|
134
|
+
A helper class for easily mocking app config in various permutations.
|
135
|
+
|
136
|
+
Easily mock all config items from a manifest model (which can be loaded from
|
137
|
+
`benchling_sdk.apps.helpers.manifest_helpers.manifest_from_file()`.
|
138
|
+
"""
|
139
|
+
|
140
|
+
_config_items: List[AppConfigItem]
|
141
|
+
|
142
|
+
def __init__(self, config_items: List[AppConfigItem]):
|
143
|
+
"""
|
144
|
+
Init Mock Benchling App Config.
|
145
|
+
|
146
|
+
The class can be initialized by providing a list of AppConfigItem, but the recommended
|
147
|
+
usage is to mock directly from a manifest, like `MockBenchlingAppConfig.from_manifest()`
|
148
|
+
"""
|
149
|
+
super().__init__(StaticConfigProvider([_supported_config_item(item) for item in config_items]))
|
150
|
+
self._config_items = config_items
|
151
|
+
|
152
|
+
@classmethod
|
153
|
+
def from_manifest(cls, manifest: BenchlingAppManifest) -> MockConfigItemStore:
|
154
|
+
"""
|
155
|
+
From Manifest.
|
156
|
+
|
157
|
+
Reads a manifest amd mocks out all dependencies.
|
158
|
+
"""
|
159
|
+
config_items = mock_app_config_items_from_manifest(manifest)
|
160
|
+
return cls(config_items)
|
161
|
+
|
162
|
+
def with_replacement(self, replacement: AppConfigItem) -> MockConfigItemStore:
|
163
|
+
"""
|
164
|
+
With Replacement.
|
165
|
+
|
166
|
+
Returns a new MockBenchlingAppConfig with the app config item at the specified path replaced.
|
167
|
+
"""
|
168
|
+
# list() solves "List is invariant"
|
169
|
+
replaced_app_config = replace_mocked_config_item_by_path(
|
170
|
+
list(self.config_items), _config_path(replacement), replacement
|
171
|
+
)
|
172
|
+
return MockConfigItemStore(replaced_app_config)
|
173
|
+
|
174
|
+
def with_replacements(self, replacements: List[AppConfigItem]) -> MockConfigItemStore:
|
175
|
+
"""
|
176
|
+
With Replacement.
|
177
|
+
|
178
|
+
Returns a new MockBenchlingAppConfig with the app config item at the specified path replaced.
|
179
|
+
"""
|
180
|
+
# list() solves "List is invariant"
|
181
|
+
replaced_app_config: List[AppConfigItem] = list(self.config_items)
|
182
|
+
for replacement in replacements:
|
183
|
+
replaced_app_config = replace_mocked_config_item_by_path(
|
184
|
+
list(replaced_app_config), _config_path(replacement), replacement
|
185
|
+
)
|
186
|
+
return MockConfigItemStore(list(replaced_app_config))
|
187
|
+
|
188
|
+
@property
|
189
|
+
def config_items(self) -> List[ConfigurationReference]:
|
190
|
+
"""List the config items in the mock app config, excluding any unknown types."""
|
191
|
+
return [_supported_config_item(config_item) for config_item in self._config_items]
|
192
|
+
|
193
|
+
@property
|
194
|
+
def config_items_with_unknown(self) -> List[AppConfigItem]:
|
195
|
+
"""List the config items in the mock app config, including any unknown types."""
|
196
|
+
return self._config_items
|
197
|
+
|
198
|
+
|
199
|
+
class MockDecryptionProviderStatic(MockDecryptionProvider):
|
200
|
+
"""
|
201
|
+
Mock Decryption Provider Static.
|
202
|
+
|
203
|
+
Always return the same "decrypted" value regardless of what ciphertext is passed.
|
204
|
+
Useful if you only have a single secret value.
|
205
|
+
"""
|
206
|
+
|
207
|
+
def __init__(self, decrypt_value: str):
|
208
|
+
"""
|
209
|
+
Init Mock Decryption Provider Static.
|
210
|
+
|
211
|
+
Supply the string to always be returned.
|
212
|
+
"""
|
213
|
+
|
214
|
+
def decrypt(ciphertext: str) -> str:
|
215
|
+
return decrypt_value
|
216
|
+
|
217
|
+
super().__init__(decrypt)
|
218
|
+
|
219
|
+
|
220
|
+
class MockDecryptionProviderMapped(MockDecryptionProvider):
|
221
|
+
"""
|
222
|
+
Mock Decryption Provider Mapped.
|
223
|
+
|
224
|
+
Returns a "decrypted" value based on the input ciphertext.
|
225
|
+
Useful if you have multiple secrets to mock simultaneously.
|
226
|
+
"""
|
227
|
+
|
228
|
+
def __init__(self, decrypt_mapping: Dict[str, str]):
|
229
|
+
"""
|
230
|
+
Init Mock Decryption Provider Mapped.
|
231
|
+
|
232
|
+
Supply the dictionary mapping with ciphertext as keys and plaintext as values.
|
233
|
+
Any ciphertext decrypted without a corresponding value will result in a KeyError.
|
234
|
+
"""
|
235
|
+
|
236
|
+
def decrypt(ciphertext: str) -> str:
|
237
|
+
return decrypt_mapping[ciphertext]
|
238
|
+
|
239
|
+
super().__init__(decrypt)
|
240
|
+
|
241
|
+
|
242
|
+
def mock_app_config_items_from_manifest(manifest: BenchlingAppManifest) -> List[AppConfigItem]:
|
243
|
+
"""
|
244
|
+
Mock Benchling App Config Items.
|
245
|
+
|
246
|
+
This method accepts an app manifest model and creates mocked values for app the app config.
|
247
|
+
|
248
|
+
The concrete mocked out values, such as API Ids and schema names are nonsensical and random,
|
249
|
+
but are valid.
|
250
|
+
|
251
|
+
Code should avoid relying on specific values or conventions (such as API prefixes). If
|
252
|
+
specific dependency values need to be tested in isolation, the caller can selectively
|
253
|
+
override the randomized values with replace_mocked_config_item_by_path().
|
254
|
+
"""
|
255
|
+
root_config_items = [_mock_dependency(dependency) for dependency in manifest.configuration]
|
256
|
+
return [config_item for sub_config_items in root_config_items for config_item in sub_config_items]
|
257
|
+
|
258
|
+
|
259
|
+
def replace_mocked_config_item_by_path(
|
260
|
+
original_config: List[AppConfigItem], replacement_path: List[str], replacement_item: AppConfigItem
|
261
|
+
) -> List[AppConfigItem]:
|
262
|
+
"""Return an updated list of app config items with a specific config item replaced with the provided mock."""
|
263
|
+
replaced_config = [config for config in original_config if _config_path(config) != replacement_path]
|
264
|
+
replaced_config.append(replacement_item)
|
265
|
+
return replaced_config
|
266
|
+
|
267
|
+
|
268
|
+
def mock_bool_app_config_item(path: List[str], value: Optional[bool]) -> BooleanAppConfigItem:
|
269
|
+
"""Mock a bool app config item with a path and specified value."""
|
270
|
+
return BooleanAppConfigItem(
|
271
|
+
path=path,
|
272
|
+
value=value,
|
273
|
+
type=BooleanAppConfigItemType.BOOLEAN,
|
274
|
+
id=_random_string("aci_"),
|
275
|
+
)
|
276
|
+
|
277
|
+
|
278
|
+
def mock_date_app_config_item(path: List[str], value: Optional[date]) -> DateAppConfigItem:
|
279
|
+
"""Mock a date app config item with a path and specified value."""
|
280
|
+
return DateAppConfigItem(
|
281
|
+
path=path,
|
282
|
+
value=str(value) if value is not None else None,
|
283
|
+
type=DateAppConfigItemType.DATE,
|
284
|
+
id=_random_string("aci_"),
|
285
|
+
)
|
286
|
+
|
287
|
+
|
288
|
+
def mock_datetime_app_config_item(path: List[str], value: Optional[datetime]) -> DatetimeAppConfigItem:
|
289
|
+
"""Mock a datetime app config item with a path and specified value."""
|
290
|
+
return DatetimeAppConfigItem(
|
291
|
+
path=path,
|
292
|
+
value=datetime_config_value_to_str(value) if value else None,
|
293
|
+
type=DatetimeAppConfigItemType.DATETIME,
|
294
|
+
id=_random_string("aci_"),
|
295
|
+
)
|
296
|
+
|
297
|
+
|
298
|
+
def mock_float_app_config_item(path: List[str], value: Optional[float]) -> FloatAppConfigItem:
|
299
|
+
"""Mock a float app config item with a path and specified value."""
|
300
|
+
return FloatAppConfigItem(
|
301
|
+
path=path,
|
302
|
+
value=value,
|
303
|
+
type=FloatAppConfigItemType.FLOAT,
|
304
|
+
id=_random_string("aci_"),
|
305
|
+
)
|
306
|
+
|
307
|
+
|
308
|
+
def mock_int_app_config_item(path: List[str], value: Optional[int]) -> IntegerAppConfigItem:
|
309
|
+
"""Mock an int app config item with a path and specified value."""
|
310
|
+
return IntegerAppConfigItem(
|
311
|
+
path=path,
|
312
|
+
value=value,
|
313
|
+
type=IntegerAppConfigItemType.INTEGER,
|
314
|
+
id=_random_string("aci_"),
|
315
|
+
)
|
316
|
+
|
317
|
+
|
318
|
+
def mock_json_app_config_item(path: List[str], value: Optional[JsonType]) -> JsonAppConfigItem:
|
319
|
+
"""Mock an int app config item with a path and specified value."""
|
320
|
+
return JsonAppConfigItem(
|
321
|
+
path=path,
|
322
|
+
value=json.dumps(value) if value is not None else None,
|
323
|
+
type=JsonAppConfigItemType.JSON,
|
324
|
+
id=_random_string("aci_"),
|
325
|
+
)
|
326
|
+
|
327
|
+
|
328
|
+
def mock_secure_text_app_config_item(path: List[str], value: Optional[str]) -> SecureTextAppConfigItem:
|
329
|
+
"""Mock a secure text app config item with a path and specified value."""
|
330
|
+
return SecureTextAppConfigItem(
|
331
|
+
path=path,
|
332
|
+
value=value,
|
333
|
+
type=SecureTextAppConfigItemType.SECURE_TEXT,
|
334
|
+
id=_random_string("aci_"),
|
335
|
+
)
|
336
|
+
|
337
|
+
|
338
|
+
def mock_text_app_config_item(path: List[str], value: Optional[str]) -> TextAppConfigItem:
|
339
|
+
"""Mock a text app config item with a path and specified value."""
|
340
|
+
return TextAppConfigItem(
|
341
|
+
path=path,
|
342
|
+
value=value,
|
343
|
+
type=TextAppConfigItemType.TEXT,
|
344
|
+
id=_random_string("aci_"),
|
345
|
+
)
|
346
|
+
|
347
|
+
|
348
|
+
def _mock_dependency(
|
349
|
+
dependency: ManifestDependencies,
|
350
|
+
parent_path: Optional[List[str]] = None,
|
351
|
+
) -> List[AppConfigItem]:
|
352
|
+
"""Mock a dependency from its manifest definition."""
|
353
|
+
parent_path = parent_path if parent_path else []
|
354
|
+
# MyPy has trouble inferring lists with [config_item] + sub_items so use the syntax like:
|
355
|
+
# [*[config_item], *sub_items]
|
356
|
+
# See https://github.com/python/mypy/issues/3933#issuecomment-808739063
|
357
|
+
if isinstance(dependency, DropdownDependency):
|
358
|
+
linked_resource_id = _random_string("val_")
|
359
|
+
config_item = GenericApiIdentifiedAppConfigItem(
|
360
|
+
id=_random_string("aci_"),
|
361
|
+
path=parent_path + [dependency.name],
|
362
|
+
type=GenericApiIdentifiedAppConfigItemType.DROPDOWN,
|
363
|
+
value=_random_string("val_"),
|
364
|
+
linked_resource=_mock_linked_resource(linked_resource_id),
|
365
|
+
)
|
366
|
+
sub_items = [
|
367
|
+
_mock_subdependency(subdependency, dependency, parent_path=parent_path)
|
368
|
+
for subdependency in _options_from_dependency(dependency)
|
369
|
+
]
|
370
|
+
return [*[config_item], *sub_items]
|
371
|
+
elif isinstance(dependency, EntitySchemaDependency):
|
372
|
+
linked_resource_id = _random_string("val_")
|
373
|
+
subtype = _subtype_from_entity_schema_dependency(dependency)
|
374
|
+
optional_subtype: Union[SchemaDependencySubtypes, Unset] = (
|
375
|
+
_convert_entity_subtype(subtype) if subtype is not None else UNSET
|
376
|
+
)
|
377
|
+
entity_item = EntitySchemaAppConfigItem(
|
378
|
+
id=_random_string("aci_"),
|
379
|
+
path=parent_path + [dependency.name],
|
380
|
+
type=EntitySchemaAppConfigItemType.ENTITY_SCHEMA,
|
381
|
+
subtype=optional_subtype,
|
382
|
+
value=_random_string("val_"),
|
383
|
+
linked_resource=_mock_linked_resource(linked_resource_id),
|
384
|
+
)
|
385
|
+
sub_items = [
|
386
|
+
_mock_subdependency(subdependency, dependency, parent_path=parent_path)
|
387
|
+
for subdependency in _field_definitions_from_dependency(dependency)
|
388
|
+
]
|
389
|
+
return [*[entity_item], *sub_items]
|
390
|
+
elif isinstance(dependency, SchemaDependency):
|
391
|
+
linked_resource_id = _random_string("val_")
|
392
|
+
config_item = GenericApiIdentifiedAppConfigItem(
|
393
|
+
id=_random_string("aci_"),
|
394
|
+
path=parent_path + [dependency.name],
|
395
|
+
type=GenericApiIdentifiedAppConfigItemType(dependency.type),
|
396
|
+
value=_random_string("val_"),
|
397
|
+
linked_resource=_mock_linked_resource(linked_resource_id),
|
398
|
+
)
|
399
|
+
sub_items = [
|
400
|
+
_mock_subdependency(subdependency, dependency, parent_path=parent_path)
|
401
|
+
for subdependency in _field_definitions_from_dependency(dependency)
|
402
|
+
]
|
403
|
+
return [*[config_item], *sub_items]
|
404
|
+
elif isinstance(dependency, WorkflowTaskSchemaDependency):
|
405
|
+
linked_resource_id = _random_string("val_")
|
406
|
+
config_item = GenericApiIdentifiedAppConfigItem(
|
407
|
+
id=_random_string("aci_"),
|
408
|
+
path=parent_path + [dependency.name],
|
409
|
+
type=GenericApiIdentifiedAppConfigItemType.WORKFLOW_TASK_SCHEMA,
|
410
|
+
value=linked_resource_id,
|
411
|
+
linked_resource=_mock_linked_resource(linked_resource_id),
|
412
|
+
)
|
413
|
+
sub_items = [
|
414
|
+
_mock_subdependency(subdependency, dependency, parent_path=parent_path)
|
415
|
+
for subdependency in _field_definitions_from_dependency(dependency)
|
416
|
+
]
|
417
|
+
workflow_task_output = _workflow_task_schema_output_from_dependency(dependency)
|
418
|
+
if workflow_task_output:
|
419
|
+
output_fields = _field_definitions_from_dependency(workflow_task_output)
|
420
|
+
output_items = [
|
421
|
+
_mock_workflow_output_subdependency(subdependency, dependency, parent_path=parent_path)
|
422
|
+
for subdependency in output_fields
|
423
|
+
]
|
424
|
+
sub_items += output_items
|
425
|
+
return [*[config_item], *sub_items]
|
426
|
+
# Python can't compare isinstance for Union types until Python 3.10 so just do this for now
|
427
|
+
# from: https://github.com/python/typing/discussions/1132#discussioncomment-2560441
|
428
|
+
elif isinstance(dependency, get_args(ManifestScalarConfig)):
|
429
|
+
# Ignore type since MyPy definitely can't tell from above
|
430
|
+
return [_mock_scalar_dependency(dependency, parent_path=parent_path)] # type: ignore
|
431
|
+
elif isinstance(dependency, ManifestArrayConfig):
|
432
|
+
return _mock_array_dependency(dependency, parent_path=parent_path)
|
433
|
+
elif isinstance(dependency, UnknownType):
|
434
|
+
return [UnknownType(value="Unknown")]
|
435
|
+
else:
|
436
|
+
linked_resource_id = _random_string("val_")
|
437
|
+
return [
|
438
|
+
GenericApiIdentifiedAppConfigItem(
|
439
|
+
id=_random_string("aci_"),
|
440
|
+
path=parent_path + [dependency.name],
|
441
|
+
type=GenericApiIdentifiedAppConfigItemType(dependency.type),
|
442
|
+
value=linked_resource_id,
|
443
|
+
linked_resource=_mock_linked_resource(linked_resource_id),
|
444
|
+
)
|
445
|
+
]
|
446
|
+
|
447
|
+
|
448
|
+
def _convert_entity_subtype(manifest_subtype: SchemaDependencySubtypesBeta) -> SchemaDependencySubtypes:
|
449
|
+
# Manifest types and config types should be equivalent but are technically different Enums
|
450
|
+
source_value: str = manifest_subtype.value
|
451
|
+
return SchemaDependencySubtypes(source_value)
|
452
|
+
|
453
|
+
|
454
|
+
def _mock_scalar_dependency(
|
455
|
+
dependency: ManifestScalarConfig, parent_path: Optional[List[str]] = None
|
456
|
+
) -> AppConfigItem:
|
457
|
+
parent_path = parent_path if parent_path else []
|
458
|
+
if isinstance(dependency, ManifestBooleanScalarConfig):
|
459
|
+
bool_value = cast(bool, _mock_scalar_value(dependency))
|
460
|
+
bool_config = mock_bool_app_config_item([dependency.name], bool_value)
|
461
|
+
return _append_config_item_path(bool_config, parent_path)
|
462
|
+
elif isinstance(dependency, ManifestDateScalarConfig):
|
463
|
+
date_value = cast(date, _mock_scalar_value(dependency))
|
464
|
+
date_config = mock_date_app_config_item([dependency.name], date_value)
|
465
|
+
return _append_config_item_path(date_config, parent_path)
|
466
|
+
elif isinstance(dependency, ManifestDatetimeScalarConfig):
|
467
|
+
datetime_value = cast(datetime, _mock_scalar_value(dependency))
|
468
|
+
datetime_config = mock_datetime_app_config_item([dependency.name], datetime_value)
|
469
|
+
return _append_config_item_path(datetime_config, parent_path)
|
470
|
+
elif isinstance(dependency, ManifestFloatScalarConfig):
|
471
|
+
float_value = cast(float, _mock_scalar_value(dependency))
|
472
|
+
float_config = mock_float_app_config_item([dependency.name], float_value)
|
473
|
+
return _append_config_item_path(float_config, parent_path)
|
474
|
+
elif isinstance(dependency, ManifestIntegerScalarConfig):
|
475
|
+
int_value = cast(int, _mock_scalar_value(dependency))
|
476
|
+
int_config = mock_int_app_config_item([dependency.name], int_value)
|
477
|
+
return _append_config_item_path(int_config, parent_path)
|
478
|
+
elif isinstance(dependency, ManifestJsonScalarConfig):
|
479
|
+
json_value = cast(JsonType, _mock_scalar_value(dependency))
|
480
|
+
json_config = mock_json_app_config_item([dependency.name], json_value)
|
481
|
+
return _append_config_item_path(json_config, parent_path)
|
482
|
+
elif isinstance(dependency, ManifestSecureTextScalarConfig):
|
483
|
+
secure_text_value = cast(str, _mock_scalar_value(dependency))
|
484
|
+
secure_text_config = mock_secure_text_app_config_item([dependency.name], secure_text_value)
|
485
|
+
return _append_config_item_path(secure_text_config, parent_path)
|
486
|
+
else:
|
487
|
+
assert not isinstance(dependency, UnknownType), f"Unable to mock unknown type {dependency}"
|
488
|
+
text_value = cast(str, _mock_scalar_value(dependency))
|
489
|
+
text_config = mock_text_app_config_item([dependency.name], text_value)
|
490
|
+
return _append_config_item_path(text_config, parent_path)
|
491
|
+
|
492
|
+
|
493
|
+
def _append_config_item_path(config_item: AppConfigItem, parent_path: List[str]) -> AppConfigItem:
|
494
|
+
if isinstance(config_item, UnknownType):
|
495
|
+
return config_item
|
496
|
+
config_item.path = parent_path + config_item.path
|
497
|
+
return config_item
|
498
|
+
|
499
|
+
|
500
|
+
def _mock_array_dependency(
|
501
|
+
dependency: ManifestArrayConfig, parent_path: Optional[List[str]] = None, rows: int = 1
|
502
|
+
) -> List[AppConfigItem]:
|
503
|
+
config_rows = []
|
504
|
+
parent_path = parent_path if parent_path else []
|
505
|
+
for i in range(rows):
|
506
|
+
row = _mock_array_row(dependency, parent_path=parent_path)
|
507
|
+
elements = _element_definition_from_dependency(dependency)
|
508
|
+
element_configs = [_mock_dependency(element, row.path) for element in elements]
|
509
|
+
flattened_configs = [element for sublist in element_configs for element in sublist]
|
510
|
+
config_rows.append(row)
|
511
|
+
config_rows.extend(flattened_configs)
|
512
|
+
return config_rows
|
513
|
+
|
514
|
+
|
515
|
+
def _mock_array_row(dependency: ManifestArrayConfig, parent_path: Optional[List[str]] = None):
|
516
|
+
row_name = _random_string("Row ")
|
517
|
+
parent_path = parent_path if parent_path else []
|
518
|
+
return ArrayElementAppConfigItem(
|
519
|
+
id=_random_string("aci_"),
|
520
|
+
path=parent_path + [dependency.name, row_name],
|
521
|
+
type=ArrayElementAppConfigItemType.ARRAY_ELEMENT,
|
522
|
+
value=row_name,
|
523
|
+
)
|
524
|
+
|
525
|
+
|
526
|
+
def _mock_scalar_with_enum(dependency: ManifestScalarConfig) -> Union[float, int, str]:
|
527
|
+
assert isinstance(
|
528
|
+
dependency, (ManifestFloatScalarConfig, ManifestIntegerScalarConfig, ManifestTextScalarConfig)
|
529
|
+
)
|
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)
|
536
|
+
|
537
|
+
|
538
|
+
def _is_scalar_with_enum(dependency: ManifestScalarConfig) -> bool:
|
539
|
+
if isinstance(
|
540
|
+
dependency, (ManifestFloatScalarConfig, ManifestIntegerScalarConfig, ManifestTextScalarConfig)
|
541
|
+
):
|
542
|
+
# MyPy doesn't find this to be truthy without a specific len check
|
543
|
+
return len(_enum_from_dependency(dependency)) > 0
|
544
|
+
return False
|
545
|
+
|
546
|
+
|
547
|
+
def _mock_subdependency(
|
548
|
+
subdependency: Union[BaseManifestConfig, FieldDefinitionsManifest],
|
549
|
+
parent_config,
|
550
|
+
parent_path: Optional[List[str]] = None,
|
551
|
+
) -> AppConfigItem:
|
552
|
+
parent_path = parent_path if parent_path else []
|
553
|
+
if isinstance(parent_config, DropdownDependency):
|
554
|
+
linked_resource_id = _random_string("opt_")
|
555
|
+
return GenericApiIdentifiedAppConfigItem(
|
556
|
+
id=_random_string("aci_"),
|
557
|
+
path=parent_path + [parent_config.name, subdependency.name],
|
558
|
+
type=GenericApiIdentifiedAppConfigItemType.DROPDOWN_OPTION,
|
559
|
+
value=linked_resource_id,
|
560
|
+
linked_resource=_mock_linked_resource(linked_resource_id),
|
561
|
+
)
|
562
|
+
elif isinstance(parent_config, (EntitySchemaDependency, SchemaDependency, WorkflowTaskSchemaDependency)):
|
563
|
+
path = [parent_config.name, subdependency.name]
|
564
|
+
linked_resource_id = _random_string("tsf_")
|
565
|
+
app_config = FieldAppConfigItem(
|
566
|
+
id=_random_string("aci_"),
|
567
|
+
path=parent_path + path,
|
568
|
+
type=FieldAppConfigItemType.FIELD,
|
569
|
+
value=linked_resource_id,
|
570
|
+
linked_resource=_mock_linked_resource(linked_resource_id),
|
571
|
+
)
|
572
|
+
return app_config
|
573
|
+
raise RuntimeError(f"Can't mock unsupported dependency ({subdependency})")
|
574
|
+
|
575
|
+
|
576
|
+
def _mock_workflow_output_subdependency(
|
577
|
+
subdependency: Union[BaseManifestConfig, FieldDefinitionsManifest],
|
578
|
+
parent_config,
|
579
|
+
parent_path: Optional[List[str]] = None,
|
580
|
+
) -> AppConfigItem:
|
581
|
+
parent_path = parent_path if parent_path else []
|
582
|
+
linked_resource_id = _random_string("tsf_")
|
583
|
+
app_config = FieldAppConfigItem(
|
584
|
+
id=_random_string("aci_"),
|
585
|
+
path=parent_path + [parent_config.name, "output", subdependency.name],
|
586
|
+
type=FieldAppConfigItemType.FIELD,
|
587
|
+
value=linked_resource_id,
|
588
|
+
linked_resource=_mock_linked_resource(linked_resource_id),
|
589
|
+
)
|
590
|
+
return app_config
|
591
|
+
|
592
|
+
|
593
|
+
def _mock_linked_resource(id: str, name: Optional[str] = None) -> LinkedAppConfigResourceSummary:
|
594
|
+
return LinkedAppConfigResourceSummary(id=id, name=name if name else _random_string("Resource Name"))
|
595
|
+
|
596
|
+
|
597
|
+
def _mock_scalar_value(
|
598
|
+
dependency: ManifestScalarConfig,
|
599
|
+
) -> Union[bool, date, datetime, int, float, str, Dict[str, Union[str, float]]]:
|
600
|
+
"""Mock a scalar config value from its manifest definition."""
|
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
|
+
elif scalar_type == scalar_type.DATE:
|
612
|
+
return date.today()
|
613
|
+
elif scalar_type == scalar_type.DATETIME:
|
614
|
+
return datetime.now()
|
615
|
+
elif scalar_type == scalar_type.FLOAT:
|
616
|
+
return random.random()
|
617
|
+
elif scalar_type == scalar_type.INTEGER:
|
618
|
+
return random.randint(-1000, 1000)
|
619
|
+
elif scalar_type == scalar_type.JSON:
|
620
|
+
return json.dumps(
|
621
|
+
{_random_string(): [_random_string(), _random_string()], _random_string(): random.random()}
|
622
|
+
)
|
623
|
+
return _random_string()
|
624
|
+
|
625
|
+
|
626
|
+
def _random_string(prefix: str = "", random_length: int = 20) -> str:
|
627
|
+
"""Generate a randomized string up to a specified length with an optional prefix."""
|
628
|
+
delimited_prefix = f"{prefix}-" if prefix else ""
|
629
|
+
return f"{delimited_prefix}{''.join(random.choice(string.ascii_letters) for i in range(random_length))}"
|
630
|
+
|
631
|
+
|
632
|
+
def _config_path(config_item: AppConfigItem) -> List[str]:
|
633
|
+
if isinstance(config_item, UnknownType):
|
634
|
+
return config_item.value["path"]
|
635
|
+
return config_item.path
|
@@ -10,6 +10,8 @@ benchling_sdk/apps/config/cryptography_helpers.py,sha256=VCzgQURLAitYjcjdwFzMPSK
|
|
10
10
|
benchling_sdk/apps/config/decryption_provider.py,sha256=-HgjkBto8mYlooV0LXFenfaiP7PDhHRnKEHkK1P6gh4,2184
|
11
11
|
benchling_sdk/apps/config/errors.py,sha256=-qkfLdcUTuuRD-V4L2l7nzQWnWTRuifDtkNDv8tfHoo,800
|
12
12
|
benchling_sdk/apps/config/framework.py,sha256=a2cIkPugEVUfBNtHm2cbE3Qu326xb-SpMuM0Gi6U-3Q,13337
|
13
|
+
benchling_sdk/apps/config/helpers.py,sha256=xVOfiJPZn28et8Pg7-SsRZnvmA5KmzAgwXBWwBYuZEI,7938
|
14
|
+
benchling_sdk/apps/config/mock_config.py,sha256=laTcsVDjM4ae2gu53gfXbx1lVM-y8aSohIaa_Lf8xWs,26906
|
13
15
|
benchling_sdk/apps/config/types.py,sha256=XKfSGv-75CU-j1XwfXBGq8zbtnkF-PQnuY6Z2U47-Tg,953
|
14
16
|
benchling_sdk/apps/framework.py,sha256=G15mv20FH7FLHJrnXMPcuFdUsP3Va-grvb5A4eq0Qlk,3175
|
15
17
|
benchling_sdk/apps/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -117,7 +119,7 @@ benchling_sdk/services/v2/v2_alpha_service.py,sha256=rl4niKxjU-Rvdx5W6cjyXd4rUMo
|
|
117
119
|
benchling_sdk/services/v2/v2_beta_service.py,sha256=SEv2AsZG0phf_B4ORqjRO-3VBkYehu1hlUhRFHrpX3c,6093
|
118
120
|
benchling_sdk/services/v2/v2_stable_service.py,sha256=21TzqEFHygMUFv0z0z8_MtXpSHLgwYC7YbIIsHrljaw,28232
|
119
121
|
benchling_sdk/services/v2_service.py,sha256=cGX-Ps0hu7Oh1M7a0tu2zDN8-QG63dNDoK7w4eYo_OQ,3093
|
120
|
-
benchling_sdk-1.18.
|
121
|
-
benchling_sdk-1.18.
|
122
|
-
benchling_sdk-1.18.
|
123
|
-
benchling_sdk-1.18.
|
122
|
+
benchling_sdk-1.18.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
123
|
+
benchling_sdk-1.18.1.dist-info/METADATA,sha256=71VdfOrD6cQbkklt5_8-4ZwFJUg5zV2WefRNOL4_KKY,2108
|
124
|
+
benchling_sdk-1.18.1.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
|
125
|
+
benchling_sdk-1.18.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|