benchling-sdk 1.10.0a2__py3-none-any.whl → 1.10.0a3__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.
File without changes
@@ -0,0 +1,14 @@
1
+ class DuplicateBlockIdError(Exception):
2
+ """Error indicating that duplicate ids were present on blocks within a Canvas."""
3
+
4
+ pass
5
+
6
+
7
+ class NoMatchingBlocksError(Exception):
8
+ """
9
+ Error indicating that blocks were expected, but none matched.
10
+
11
+ Used to prevent requiring developers to handle Optional[_UiBlock] for type safety.
12
+ """
13
+
14
+ pass
@@ -4,137 +4,36 @@ from typing import cast, Dict, Generic, List, Optional, Protocol, Set, Type, Typ
4
4
 
5
5
  from benchling_api_client.v2.extensions import UnknownType
6
6
 
7
+ from benchling_sdk.apps.canvas.errors import DuplicateBlockIdError, NoMatchingBlocksError
8
+ from benchling_sdk.apps.canvas.types import (
9
+ _UI_BLOCK_MAPPINGS_CREATE,
10
+ _UI_BLOCK_MAPPINGS_UPDATE,
11
+ _UiBlock,
12
+ _UiBlockCreate,
13
+ _UiBlockType,
14
+ _UiBlockUpdate,
15
+ )
7
16
  from benchling_sdk.helpers.logging_helpers import log_stability_warning, StabilityLevel
8
17
  from benchling_sdk.models import (
9
18
  AppCanvas,
10
19
  AppCanvasApp,
11
20
  AppCanvasCreate,
12
21
  AppCanvasUpdate,
13
- ButtonUiBlock,
14
- ButtonUiBlockCreate,
15
- ButtonUiBlockUpdate,
16
- ChipUiBlock,
17
- ChipUiBlockCreate,
18
- ChipUiBlockUpdate,
19
22
  DropdownMultiValueUiBlock,
20
- DropdownMultiValueUiBlockCreate,
21
- DropdownMultiValueUiBlockUpdate,
22
23
  DropdownUiBlock,
23
- DropdownUiBlockCreate,
24
- DropdownUiBlockUpdate,
25
- MarkdownUiBlock,
26
- MarkdownUiBlockCreate,
27
- MarkdownUiBlockUpdate,
28
24
  SearchInputMultiValueUiBlock,
29
- SearchInputMultiValueUiBlockCreate,
30
- SearchInputMultiValueUiBlockUpdate,
31
25
  SearchInputUiBlock,
32
- SearchInputUiBlockCreate,
33
- SearchInputUiBlockUpdate,
34
26
  SectionUiBlock,
35
- SectionUiBlockCreate,
36
- SectionUiBlockUpdate,
37
27
  SelectorInputMultiValueUiBlock,
38
- SelectorInputMultiValueUiBlockCreate,
39
- SelectorInputMultiValueUiBlockUpdate,
40
28
  SelectorInputUiBlock,
41
- SelectorInputUiBlockCreate,
42
- SelectorInputUiBlockUpdate,
43
29
  TableUiBlock,
44
- TableUiBlockCreate,
45
- TableUiBlockUpdate,
46
30
  TextInputUiBlock,
47
- TextInputUiBlockCreate,
48
- TextInputUiBlockUpdate,
49
31
  )
50
32
 
51
33
  log_stability_warning(StabilityLevel.BETA)
52
34
 
53
35
  S = TypeVar("S", bound="_FilteredCanvasBuilderBlockStream")
54
36
 
55
- _UiBlock = Union[
56
- ButtonUiBlock,
57
- ChipUiBlock,
58
- DropdownMultiValueUiBlock,
59
- DropdownUiBlock,
60
- MarkdownUiBlock,
61
- SearchInputMultiValueUiBlock,
62
- SearchInputUiBlock,
63
- SectionUiBlock,
64
- SelectorInputMultiValueUiBlock,
65
- SelectorInputUiBlock,
66
- TableUiBlock,
67
- TextInputUiBlock,
68
- UnknownType,
69
- ]
70
-
71
- _UiBlockType = TypeVar(
72
- "_UiBlockType",
73
- bound=_UiBlock,
74
- )
75
-
76
- _UiBlockCreate = Union[
77
- ButtonUiBlockCreate,
78
- ChipUiBlockCreate,
79
- DropdownMultiValueUiBlockCreate,
80
- DropdownUiBlockCreate,
81
- MarkdownUiBlockCreate,
82
- SearchInputMultiValueUiBlockCreate,
83
- SearchInputUiBlockCreate,
84
- SectionUiBlockCreate,
85
- SelectorInputMultiValueUiBlockCreate,
86
- SelectorInputUiBlockCreate,
87
- TableUiBlockCreate,
88
- TextInputUiBlockCreate,
89
- UnknownType,
90
- ]
91
-
92
- _UiBlockUpdate = Union[
93
- ButtonUiBlockUpdate,
94
- ChipUiBlockUpdate,
95
- DropdownMultiValueUiBlockUpdate,
96
- DropdownUiBlockUpdate,
97
- MarkdownUiBlockUpdate,
98
- SearchInputMultiValueUiBlockUpdate,
99
- SearchInputUiBlockUpdate,
100
- SectionUiBlockUpdate,
101
- SelectorInputMultiValueUiBlockUpdate,
102
- SelectorInputUiBlockUpdate,
103
- TableUiBlockUpdate,
104
- TextInputUiBlockUpdate,
105
- UnknownType,
106
- ]
107
-
108
- _UI_BLOCK_MAPPINGS_CREATE = {
109
- ButtonUiBlock: ButtonUiBlockCreate,
110
- ChipUiBlock: ChipUiBlockCreate,
111
- DropdownMultiValueUiBlock: DropdownMultiValueUiBlockCreate,
112
- DropdownUiBlock: DropdownUiBlockCreate,
113
- MarkdownUiBlock: MarkdownUiBlockCreate,
114
- SearchInputMultiValueUiBlock: SearchInputMultiValueUiBlockCreate,
115
- SearchInputUiBlock: SearchInputUiBlockCreate,
116
- SectionUiBlock: SectionUiBlockCreate,
117
- SelectorInputMultiValueUiBlock: SelectorInputMultiValueUiBlockCreate,
118
- SelectorInputUiBlock: SelectorInputUiBlockCreate,
119
- TableUiBlock: TableUiBlockCreate,
120
- TextInputUiBlock: TextInputUiBlockCreate,
121
- }
122
-
123
- _UI_BLOCK_MAPPINGS_UPDATE = {
124
- ButtonUiBlock: ButtonUiBlockUpdate,
125
- ChipUiBlock: ChipUiBlockUpdate,
126
- DropdownMultiValueUiBlock: DropdownMultiValueUiBlockUpdate,
127
- DropdownUiBlock: DropdownUiBlockUpdate,
128
- MarkdownUiBlock: MarkdownUiBlockUpdate,
129
- SearchInputMultiValueUiBlock: SearchInputMultiValueUiBlockUpdate,
130
- SearchInputUiBlock: SearchInputUiBlockUpdate,
131
- SectionUiBlock: SectionUiBlockUpdate,
132
- SelectorInputMultiValueUiBlock: SelectorInputMultiValueUiBlockUpdate,
133
- SelectorInputUiBlock: SelectorInputUiBlockUpdate,
134
- TableUiBlock: TableUiBlockUpdate,
135
- TextInputUiBlock: TextInputUiBlockUpdate,
136
- }
137
-
138
37
 
139
38
  def _ui_block_to_create(block: _UiBlock) -> _UiBlockCreate:
140
39
  # Rely on the fact that the read/write shapes are compatible, for now
@@ -166,22 +65,6 @@ def _ui_block_to_update(block: _UiBlock) -> _UiBlockUpdate:
166
65
  return block # type: ignore
167
66
 
168
67
 
169
- class DuplicateBlockIdError(Exception):
170
- """Error indicating that duplicate ids were present on blocks within a Canvas."""
171
-
172
- pass
173
-
174
-
175
- class NoMatchingBlocksError(Exception):
176
- """
177
- Error indicating that blocks were expected, but none matched.
178
-
179
- Used to prevent requiring developers to handle Optional[_UiBlock] for type safety.
180
- """
181
-
182
- pass
183
-
184
-
185
68
  class _CanvasBuilderUiBlock(Generic[_UiBlockType]):
186
69
  """Internal UI block wrapper for CanvasBuilder."""
187
70
 
@@ -0,0 +1,125 @@
1
+ from typing import TypeVar, Union
2
+
3
+ from benchling_api_client.v2.extensions import UnknownType
4
+
5
+ from benchling_sdk.models import (
6
+ ButtonUiBlock,
7
+ ButtonUiBlockCreate,
8
+ ButtonUiBlockUpdate,
9
+ ChipUiBlock,
10
+ ChipUiBlockCreate,
11
+ ChipUiBlockUpdate,
12
+ DropdownMultiValueUiBlock,
13
+ DropdownMultiValueUiBlockCreate,
14
+ DropdownMultiValueUiBlockUpdate,
15
+ DropdownUiBlock,
16
+ DropdownUiBlockCreate,
17
+ DropdownUiBlockUpdate,
18
+ MarkdownUiBlock,
19
+ MarkdownUiBlockCreate,
20
+ MarkdownUiBlockUpdate,
21
+ SearchInputMultiValueUiBlock,
22
+ SearchInputMultiValueUiBlockCreate,
23
+ SearchInputMultiValueUiBlockUpdate,
24
+ SearchInputUiBlock,
25
+ SearchInputUiBlockCreate,
26
+ SearchInputUiBlockUpdate,
27
+ SectionUiBlock,
28
+ SectionUiBlockCreate,
29
+ SectionUiBlockUpdate,
30
+ SelectorInputMultiValueUiBlock,
31
+ SelectorInputMultiValueUiBlockCreate,
32
+ SelectorInputMultiValueUiBlockUpdate,
33
+ SelectorInputUiBlock,
34
+ SelectorInputUiBlockCreate,
35
+ SelectorInputUiBlockUpdate,
36
+ TableUiBlock,
37
+ TableUiBlockCreate,
38
+ TableUiBlockUpdate,
39
+ TextInputUiBlock,
40
+ TextInputUiBlockCreate,
41
+ TextInputUiBlockUpdate,
42
+ )
43
+
44
+ _UiBlock = Union[
45
+ ButtonUiBlock,
46
+ ChipUiBlock,
47
+ DropdownMultiValueUiBlock,
48
+ DropdownUiBlock,
49
+ MarkdownUiBlock,
50
+ SearchInputMultiValueUiBlock,
51
+ SearchInputUiBlock,
52
+ SectionUiBlock,
53
+ SelectorInputMultiValueUiBlock,
54
+ SelectorInputUiBlock,
55
+ TableUiBlock,
56
+ TextInputUiBlock,
57
+ UnknownType,
58
+ ]
59
+
60
+ _UiBlockType = TypeVar(
61
+ "_UiBlockType",
62
+ bound=_UiBlock,
63
+ )
64
+
65
+ _UiBlockCreate = Union[
66
+ ButtonUiBlockCreate,
67
+ ChipUiBlockCreate,
68
+ DropdownMultiValueUiBlockCreate,
69
+ DropdownUiBlockCreate,
70
+ MarkdownUiBlockCreate,
71
+ SearchInputMultiValueUiBlockCreate,
72
+ SearchInputUiBlockCreate,
73
+ SectionUiBlockCreate,
74
+ SelectorInputMultiValueUiBlockCreate,
75
+ SelectorInputUiBlockCreate,
76
+ TableUiBlockCreate,
77
+ TextInputUiBlockCreate,
78
+ UnknownType,
79
+ ]
80
+
81
+ _UiBlockUpdate = Union[
82
+ ButtonUiBlockUpdate,
83
+ ChipUiBlockUpdate,
84
+ DropdownMultiValueUiBlockUpdate,
85
+ DropdownUiBlockUpdate,
86
+ MarkdownUiBlockUpdate,
87
+ SearchInputMultiValueUiBlockUpdate,
88
+ SearchInputUiBlockUpdate,
89
+ SectionUiBlockUpdate,
90
+ SelectorInputMultiValueUiBlockUpdate,
91
+ SelectorInputUiBlockUpdate,
92
+ TableUiBlockUpdate,
93
+ TextInputUiBlockUpdate,
94
+ UnknownType,
95
+ ]
96
+
97
+ _UI_BLOCK_MAPPINGS_CREATE = {
98
+ ButtonUiBlock: ButtonUiBlockCreate,
99
+ ChipUiBlock: ChipUiBlockCreate,
100
+ DropdownMultiValueUiBlock: DropdownMultiValueUiBlockCreate,
101
+ DropdownUiBlock: DropdownUiBlockCreate,
102
+ MarkdownUiBlock: MarkdownUiBlockCreate,
103
+ SearchInputMultiValueUiBlock: SearchInputMultiValueUiBlockCreate,
104
+ SearchInputUiBlock: SearchInputUiBlockCreate,
105
+ SectionUiBlock: SectionUiBlockCreate,
106
+ SelectorInputMultiValueUiBlock: SelectorInputMultiValueUiBlockCreate,
107
+ SelectorInputUiBlock: SelectorInputUiBlockCreate,
108
+ TableUiBlock: TableUiBlockCreate,
109
+ TextInputUiBlock: TextInputUiBlockCreate,
110
+ }
111
+
112
+ _UI_BLOCK_MAPPINGS_UPDATE = {
113
+ ButtonUiBlock: ButtonUiBlockUpdate,
114
+ ChipUiBlock: ChipUiBlockUpdate,
115
+ DropdownMultiValueUiBlock: DropdownMultiValueUiBlockUpdate,
116
+ DropdownUiBlock: DropdownUiBlockUpdate,
117
+ MarkdownUiBlock: MarkdownUiBlockUpdate,
118
+ SearchInputMultiValueUiBlock: SearchInputMultiValueUiBlockUpdate,
119
+ SearchInputUiBlock: SearchInputUiBlockUpdate,
120
+ SectionUiBlock: SectionUiBlockUpdate,
121
+ SelectorInputMultiValueUiBlock: SelectorInputMultiValueUiBlockUpdate,
122
+ SelectorInputUiBlock: SelectorInputUiBlockUpdate,
123
+ TableUiBlock: TableUiBlockUpdate,
124
+ TextInputUiBlock: TextInputUiBlockUpdate,
125
+ }
@@ -1,6 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
2
 
3
- from benchling_sdk.apps.helpers.cryptography_helpers import _create_key_type
3
+ from benchling_sdk.apps.config.cryptography_helpers import _create_key_type
4
4
  from benchling_sdk.helpers.package_helpers import _required_packages_context, ExtrasPackage
5
5
 
6
6
 
@@ -0,0 +1,207 @@
1
+ import collections
2
+ from typing import Dict, List, Optional, OrderedDict, Protocol, Tuple
3
+
4
+ from benchling_api_client.v2.extensions import UnknownType
5
+ from ordered_set import OrderedSet
6
+
7
+ from benchling_sdk.apps.config.errors import UnsupportedConfigItemError
8
+ from benchling_sdk.apps.config.types import ConfigItemPath, ConfigurationReference
9
+ from benchling_sdk.benchling import Benchling
10
+ from benchling_sdk.models import AppConfigItem, ListAppConfigurationItemsSort
11
+
12
+
13
+ class ConfigProvider(Protocol):
14
+ """
15
+ Config provider.
16
+
17
+ Provides a list of ConfigurationReference.
18
+ """
19
+
20
+ def config(self) -> List[ConfigurationReference]:
21
+ """Implement to provide a list of configuration items, for instance from Benchling APIs."""
22
+ pass
23
+
24
+
25
+ class BenchlingConfigProvider(ConfigProvider):
26
+ """
27
+ Benchling Config provider.
28
+
29
+ Provides a BenchlingAppConfiguration retrieved from Benchling's API.
30
+ """
31
+
32
+ _app_id: str
33
+ _benchling: Benchling
34
+
35
+ def __init__(self, benchling: Benchling, app_id: str):
36
+ """
37
+ Initialize Benchling Config Provider.
38
+
39
+ :param benchling: A Benchling instance.
40
+ :param app_id: The app_id from which to retrieve configuration.
41
+ """
42
+ self._app_id = app_id
43
+ self._benchling = benchling
44
+
45
+ def config(self) -> List[ConfigurationReference]:
46
+ """Provide a Benchling app configuration from Benchling's APIs."""
47
+ app_pages = self._benchling.apps.list_app_configuration_items(
48
+ app_id=self._app_id,
49
+ page_size=100,
50
+ sort=ListAppConfigurationItemsSort.CREATEDATASC,
51
+ )
52
+
53
+ # Eager load all config items for now since we don't yet have a way of lazily querying by path
54
+ all_config_pages = list(app_pages)
55
+ # Punt on UnknownType for now as apps using manifests with new types + older client could lead to unpredictable results
56
+ all_config_items = [
57
+ _supported_config_item(config_item) for page in all_config_pages for config_item in page
58
+ ]
59
+
60
+ return all_config_items
61
+
62
+
63
+ class StaticConfigProvider(ConfigProvider):
64
+ """
65
+ Static Config provider.
66
+
67
+ Provides a BenchlingAppConfiguration from a static declaration. Useful for mocking or testing.
68
+ """
69
+
70
+ _configuration_items: List[ConfigurationReference]
71
+
72
+ def __init__(self, configuration_items: List[ConfigurationReference]):
73
+ """
74
+ Initialize Static Config Provider.
75
+
76
+ :param configuration_items: The configuration items to return.
77
+ """
78
+ self._configuration_items = configuration_items
79
+
80
+ def config(self) -> List[ConfigurationReference]:
81
+ """Provide Benchling app configuration items from a static reference."""
82
+ return self._configuration_items
83
+
84
+
85
+ class ConfigItemStore:
86
+ """
87
+ Dependency Link Store.
88
+
89
+ Marshalls an app configuration from the configuration provider into an indexable structure.
90
+ Only retrieves app configuration once unless its cache is invalidated.
91
+ """
92
+
93
+ _configuration_provider: ConfigProvider
94
+ _configuration: Optional[List[ConfigurationReference]] = None
95
+ _configuration_map: Optional[Dict[ConfigItemPath, ConfigurationReference]] = None
96
+ _array_path_row_names: Dict[Tuple[str, ...], OrderedSet[str]] = dict()
97
+
98
+ def __init__(self, configuration_provider: ConfigProvider):
99
+ """
100
+ Initialize Dependency Link Store.
101
+
102
+ :param configuration_provider: A ConfigProvider that will be invoked to provide the
103
+ underlying config from which to organize dependency links.
104
+ """
105
+ self._configuration_provider = configuration_provider
106
+ self._array_path_row_names = dict()
107
+
108
+ @property
109
+ def configuration(self) -> List[ConfigurationReference]:
110
+ """
111
+ Get the underlying configuration.
112
+
113
+ Return the raw, stored configuration. Can be used if the provided accessors are inadequate
114
+ to find particular configuration items.
115
+ """
116
+ if not self._configuration:
117
+ self._configuration = self._configuration_provider.config()
118
+ return self._configuration
119
+
120
+ @property
121
+ def configuration_path_map(self) -> Dict[ConfigItemPath, ConfigurationReference]:
122
+ """
123
+ Config links.
124
+
125
+ Return a map of configuration item paths to their corresponding configuration items.
126
+ """
127
+ if not self._configuration_map:
128
+ self._configuration_map = {tuple(item.path): item for item in self.configuration}
129
+ return self._configuration_map
130
+
131
+ def config_by_path(self, path: List[str]) -> Optional[ConfigurationReference]:
132
+ """
133
+ Config by path.
134
+
135
+ Find an app config item by its exact path match, if it exists. Does not search partial paths.
136
+ """
137
+ # Since we eager load all config now, we know that missing path means it's not configured in Benchling
138
+ # Later if we support lazy loading, we'll need to differentiate what's in our cache versus missing
139
+ return self.configuration_path_map.get(tuple(path))
140
+
141
+ def config_keys_by_path(self, path: List[str]) -> OrderedSet[str]:
142
+ """
143
+ Config keys by path.
144
+
145
+ Find a set of app config keys at the specified path, if any. Does not return keys that are nested
146
+ beyond the current level.
147
+
148
+ For instance, given paths:
149
+ ["One", "Two"]
150
+ ["One", "Two", "Three"]
151
+ ["One", "Two", "Four"]
152
+ ["One", "Two", "Three", "Five"]
153
+ ["Zero", "One", "Two", "Three"]
154
+
155
+ The expected return from this method when path=["One", "Two"] is a set {"Three", "Four"}.
156
+ """
157
+ # Convert path to tuple, as list is not hashable for dict keys
158
+ path_tuple = tuple(path)
159
+ if path_tuple not in self._array_path_row_names:
160
+ self._array_path_row_names[path_tuple] = OrderedSet(
161
+ [
162
+ config_item.path[len(path)]
163
+ # Use the list instead of configuration_map to preserve order
164
+ for config_item in self.configuration
165
+ # The +1 is the name of the array row
166
+ if len(config_item.path) >= len(path) + 1
167
+ # Ignoring flake8 error E203 because black keeps putting in whitespace padding :
168
+ and config_item.path[0 : len(path_tuple)] == path # noqa: E203
169
+ and config_item.value is not None
170
+ ]
171
+ )
172
+ return self._array_path_row_names[path_tuple]
173
+
174
+ def array_rows_to_dict(self, path: List[str]) -> OrderedDict[str, Dict[str, ConfigurationReference]]:
175
+ """Given a path to the root of a config array, return each element as a named dict."""
176
+ # TODO BNCH-52772 Improve docstring if we keep this method
177
+ array_keys = self.config_keys_by_path(path)
178
+ # Although we don't have a way of preserving order when pulling array elements from the API right now
179
+ # we should intentionally order these to accommodate a potential ordered future
180
+ array_elements_map = collections.OrderedDict()
181
+ for key in array_keys:
182
+ # Don't care about order for the keys within a row, only the order of the rows themselves
183
+ array_elements_map[key] = {
184
+ array_element_key: self.config_by_path([*path, key, array_element_key])
185
+ for array_element_key in self.config_keys_by_path([*path, key])
186
+ if self.config_by_path([*path, key, array_element_key]) is not None
187
+ }
188
+ # TODO BNCH-52772 MyPy thinks the inner dict values can be None
189
+ return array_elements_map # type: ignore
190
+
191
+ def invalidate_cache(self) -> None:
192
+ """
193
+ Invalidate Cache.
194
+
195
+ Will force retrieval of configuration from the ConfigProvider the next time the link store is accessed.
196
+ """
197
+ self._configuration = None
198
+ self._configuration_map = None
199
+ self._array_path_row_names = dict()
200
+
201
+
202
+ def _supported_config_item(config_item: AppConfigItem) -> ConfigurationReference:
203
+ if isinstance(config_item, UnknownType):
204
+ raise UnsupportedConfigItemError(
205
+ f"Unable to read app configuration with unsupported type: {config_item}"
206
+ )
207
+ return config_item
@@ -34,6 +34,7 @@ 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.framework import _supported_config_item, ConfigItemStore, StaticConfigProvider
37
38
  from benchling_sdk.apps.config.scalars import (
38
39
  BoolScalar,
39
40
  DateScalar,
@@ -48,7 +49,6 @@ from benchling_sdk.apps.config.scalars import (
48
49
  TextScalar,
49
50
  )
50
51
  from benchling_sdk.apps.config.types import ConfigurationReference
51
- from benchling_sdk.apps.framework import _supported_config_item, ConfigItemStore, StaticConfigProvider
52
52
  from benchling_sdk.apps.helpers.config_helpers import (
53
53
  element_definition_from_dependency,
54
54
  enum_from_dependency,