UncountablePythonSDK 0.0.35__py3-none-any.whl → 0.0.37__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.

Potentially problematic release.


This version of UncountablePythonSDK might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.35
3
+ Version: 0.0.37
4
4
  Summary: Uncountable SDK
5
5
  Project-URL: Homepage, https://github.com/uncountableinc/uncountable-python-sdk
6
6
  Project-URL: Repository, https://github.com/uncountableinc/uncountable-python-sdk.git
@@ -37,11 +37,11 @@ pkgs/strenum_compat/__init__.py,sha256=wXRFeNvBm8RU6dy1PFJ5sRLgUIEeH_DVR95Sv5qpG
37
37
  pkgs/strenum_compat/strenum_compat.py,sha256=uOUAgpYTjHs1MX8dG81jRlyTkt3KNbkV_25zp7xTX2s,36
38
38
  pkgs/type_spec/__init__.py,sha256=h5DmJTca4QVV10sZR1x0-MlkZfuGYDfapR3zHvXfzto,19
39
39
  pkgs/type_spec/__main__.py,sha256=5bJaX9Y_-FavP0qwzhk-z-V97UY7uaezJTa1zhO_HHQ,1048
40
- pkgs/type_spec/builder.py,sha256=un86i9LqTmCMVj-g6lrZ8lU4JZEElzCfUlsn--GkTvA,46049
40
+ pkgs/type_spec/builder.py,sha256=xQcY2HcQTI2FSOMycgx3yD23_Oz3_LfWdyW65pDaHoc,46667
41
41
  pkgs/type_spec/config.py,sha256=INfEiDcUsZFUKasHprsE6i33siPB0RnfmTKOsWcGnQ8,5043
42
42
  pkgs/type_spec/emit_io_ts.py,sha256=Ghd8XYqyNYldHQDepwa9GLfHXcoi48ztBw84K28ETic,5707
43
- pkgs/type_spec/emit_open_api.py,sha256=Aw7Ct1itmAqhb_nsM9yDz87EoF0XWHM56MhKqtOLOio,24005
44
- pkgs/type_spec/emit_open_api_util.py,sha256=XAA6zH59aZWLVl0BvKAICXXl4sdBqx01QAtv5oB0bMI,2266
43
+ pkgs/type_spec/emit_open_api.py,sha256=NVVXAvjPHC_MPqch_SMY6klj3kPnckOa7fJsb-ZsFTs,24371
44
+ pkgs/type_spec/emit_open_api_util.py,sha256=9tWLMT6NTeCa2caO8DdWo6aYoi0b4AEFiAXNaaQKJIs,2373
45
45
  pkgs/type_spec/emit_python.py,sha256=zP3AWJ5u0vzDcnvzSehCUgvXM0J9ZUtfLBVHerW6_wI,45164
46
46
  pkgs/type_spec/emit_typescript.py,sha256=cdr5h8N70PuwORcvhURUujzwH9r1LVwJB8V2EoipGkw,17917
47
47
  pkgs/type_spec/emit_typescript_util.py,sha256=sR7ys3Ilnh6SQiXJbfYk4pxfOu0bDjbUFTEYEW-ud6c,863
@@ -65,7 +65,7 @@ uncountable/__init__.py,sha256=8l8XWNCKsu7TG94c-xa2KHpDegvxDC2FyQISdWC763Y,89
65
65
  uncountable/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
66
  uncountable/core/__init__.py,sha256=RFv0kO6rKFf1PtBPu83hCGmxqkJamRtsgQ9_-ztw7tA,341
67
67
  uncountable/core/async_batch.py,sha256=Zo02TICZ7AK81Qa02NQ8fp8uaijQph1FEta28K3cKb0,749
68
- uncountable/core/client.py,sha256=79pqKjYuoTzKnPWLMST1jS9Gcn4nxtOzWC-VaM67QgY,9401
68
+ uncountable/core/client.py,sha256=6T8qecNK0MypHs5gD6VdW7KpiHjX7_jMNiqkmEn3CHM,9657
69
69
  uncountable/core/file_upload.py,sha256=t4cutIFB5rNN2qVCx3H0HotSjCSJSxis2_QBarDuXyc,2833
70
70
  uncountable/core/types.py,sha256=s2CjqYJpsmbC7xMwxxT7kJ_V9bwokrjjWVVjpMcQpKI,333
71
71
  uncountable/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -79,13 +79,13 @@ uncountable/integration/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
79
79
  uncountable/integration/db/connect.py,sha256=YtQHJ1DBGPhxKFRCfiXqohOYUceKSxMVOJ88aPI48Ug,181
80
80
  uncountable/integration/executors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
81
  uncountable/integration/executors/script_executor.py,sha256=hM8E-aU8zyM6ZcBtqAqInKZ_BF93RLqEA0dU7y5FhWQ,841
82
- uncountable/types/__init__.py,sha256=fTUKIaZddDoFku6qycdhJkOZIBCypDGlmSWF8PV0WbA,7404
83
- uncountable/types/async_batch.py,sha256=aWiul1fK3-cXaCESUUJ_92FF-NuuwxSjn9n3jC9vY5o,1618
82
+ uncountable/types/__init__.py,sha256=5JfNm2Ldg-uhqdeek31q80XgYJWhupJ4SfphzQ55hlw,7589
83
+ uncountable/types/async_batch.py,sha256=tD_ncDOEGwoxIQwgqqm2cHFA71chc8sPGwnJA9feNC8,1706
84
84
  uncountable/types/async_batch_processor.py,sha256=eNXKPOh-sCiarPvreLDEu4XbieWLe_5KXmiY984eZ5I,6083
85
85
  uncountable/types/base.py,sha256=w3BRf8SAvYPlKrcJtJcQ_WhCU3A9zy0VuRTRWRFKVUA,2709
86
86
  uncountable/types/calculations.py,sha256=16J-KKMp-I8ZQUkYNmKCHfAn6DGb99cFinALcDIdGHY,562
87
87
  uncountable/types/chemical_structure.py,sha256=zQKl53DGtQQONIUHFXuwjWLQaG7FPZY7x6SBSOzkGV0,758
88
- uncountable/types/client_base.py,sha256=nDxjHLF6F4Z4ZQWPdEEIX9JKw9A4NbTjYekNM-Vu0ys,60351
88
+ uncountable/types/client_base.py,sha256=9urQDmyM-VOhNVCYn3f6oE_cHAwdLAJ7bTgEwfisVTk,63523
89
89
  uncountable/types/curves.py,sha256=qYyRntMmFNonEwTrGhquMLbgMqjyP1moQflNTP0FMec,1308
90
90
  uncountable/types/entity.py,sha256=NjMZrqBwQ7sZe_oUuJqy9IEG7dWZmFMkQQXJ0_odcnA,11637
91
91
  uncountable/types/experiment_groups.py,sha256=ZBEk06F4n98Jz3oEA09WaDmw5rqPs7iVAm_Ysr4gc_o,599
@@ -156,6 +156,7 @@ uncountable/types/api/project/get_projects.py,sha256=dMPq8CHxE4k1Vs42TmC4dp9b1jY
156
156
  uncountable/types/api/project/get_projects_data.py,sha256=HtUI5YN7S24v7o2VvNBWZkYFnGjFqJzBNoV0ojRx39E,1491
157
157
  uncountable/types/api/recipe_links/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
158
158
  uncountable/types/api/recipe_links/create_recipe_link.py,sha256=1Ok6XMGFKBIPV-uRsjvclEMCcAqyNVyrVLASLaJ8UrU,1213
159
+ uncountable/types/api/recipe_links/remove_recipe_link.py,sha256=S_6YYaxawmv28WCJx70vE2C2I_vkCVnOwCCYLywUK6c,1199
159
160
  uncountable/types/api/recipe_metadata/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
160
161
  uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py,sha256=el6Pn5XqExX66ZEEHM8CHPxnyXSMZPdfdvLBz86sAVY,1267
161
162
  uncountable/types/api/recipes/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
@@ -173,7 +174,7 @@ uncountable/types/api/recipes/get_recipe_links.py,sha256=hk5dfQjv7yU2r-S9b8vwWEJ
173
174
  uncountable/types/api/recipes/get_recipe_names.py,sha256=uCpXZq5oWjr9a_Vf-yYPaVS72XOlLHgAlju6KHeQ3UA,986
174
175
  uncountable/types/api/recipes/get_recipe_output_metadata.py,sha256=L9s2ykPP4pd02Pc98LDisY8bgV8CToS6t6fXKTWqGRw,1464
175
176
  uncountable/types/api/recipes/get_recipes_data.py,sha256=nX4sCRY_RxztVqV-DGVpAvpayy6pn6cumS2pD1xmC5k,5429
176
- uncountable/types/api/recipes/lock_recipes.py,sha256=vxpc52TgFzKxw1fEuCNPpFLVwcWClVXCLtDwJZWvdG0,2271
177
+ uncountable/types/api/recipes/lock_recipes.py,sha256=9s6ISt0EIA7bx3QBSm53gNxRlfmK1iwBaqRTJl2u_d8,1344
177
178
  uncountable/types/api/recipes/remove_recipe_from_project.py,sha256=cr-VnqgBNek_WInmJln0UBn1GHMNQtRw3gsFTY_G91M,872
178
179
  uncountable/types/api/recipes/set_recipe_inputs.py,sha256=lFVfv-o_O5wHuMZdH63qlG4exFTlJM078oSAtb3XNxA,1426
179
180
  uncountable/types/api/recipes/set_recipe_metadata.py,sha256=Ba6ttd1JuS_Ypt-KpckSviWtOcQ-OTdTEJiaSYyoQL8,933
@@ -181,9 +182,10 @@ uncountable/types/api/recipes/set_recipe_output_annotations.py,sha256=8JSWlHcPOJ
181
182
  uncountable/types/api/recipes/set_recipe_outputs.py,sha256=VfUL59O21qPrgD7qQ9t5OVSGcGZ2r17lW_yEzuUkrtw,1722
182
183
  uncountable/types/api/recipes/set_recipe_tags.py,sha256=U710hgq9-t6QZGRB-ZGHskpt4iXwYEjIRb67eh3P518,2453
183
184
  uncountable/types/api/recipes/unarchive_recipes.py,sha256=WcwFYbBsX2SKXnoBQ8locnRn7Bj1rHdtrURQVOfqgfU,814
185
+ uncountable/types/api/recipes/unlock_recipes.py,sha256=m_CC9LZW7GRVrAu9uwDTTgEZr63-dOSduBAI5Ciud2I,1103
184
186
  uncountable/types/api/triggers/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
185
187
  uncountable/types/api/triggers/run_trigger.py,sha256=9m9M8-nlGB_sAU2Qm2lWugp4h4Osqj6QpjNfU8osd1U,901
186
- UncountablePythonSDK-0.0.35.dist-info/METADATA,sha256=kcqCHmpipLKzLrLpPYjcDY58l99wCYxOVFO94JoJhTY,1577
187
- UncountablePythonSDK-0.0.35.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
188
- UncountablePythonSDK-0.0.35.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
189
- UncountablePythonSDK-0.0.35.dist-info/RECORD,,
188
+ UncountablePythonSDK-0.0.37.dist-info/METADATA,sha256=3-tRSmnyCgOtzaRuyGhiBDX6GoHPqmEr4P7evG4BxcE,1577
189
+ UncountablePythonSDK-0.0.37.dist-info/WHEEL,sha256=-oYQCr74JF3a37z2nRlQays_SX2MqOANoqVjBBAP2yE,91
190
+ UncountablePythonSDK-0.0.37.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
191
+ UncountablePythonSDK-0.0.37.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.3.0)
2
+ Generator: setuptools (71.0.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
pkgs/type_spec/builder.py CHANGED
@@ -18,6 +18,17 @@ from .util import parse_type_str, unused
18
18
  RawDict = dict[Any, Any]
19
19
 
20
20
 
21
+ class StabilityLevel(StrEnum):
22
+ """These are currently used for open api,
23
+ see: https://github.com/Tufin/oasdiff/blob/main/docs/STABILITY.md
24
+ """
25
+
26
+ draft = "draft"
27
+ alpha = "alpha"
28
+ beta = "beta"
29
+ stable = "stable"
30
+
31
+
21
32
  class PropertyExtant(StrEnum):
22
33
  required = "required"
23
34
  optional = "optional"
@@ -791,6 +802,7 @@ class SpecEndpoint:
791
802
  data_loader: bool
792
803
  is_sdk: bool
793
804
  is_beta: bool
805
+ stability_level: StabilityLevel | None
794
806
  # Don't emit TypeScript endpoint code
795
807
  suppress_ts: bool
796
808
  function: Optional[str]
@@ -816,6 +828,7 @@ class SpecEndpoint:
816
828
  "data_loader",
817
829
  "is_sdk",
818
830
  "is_beta",
831
+ "stability_level",
819
832
  "async_batch_path",
820
833
  "function",
821
834
  "suppress_ts",
@@ -857,6 +870,14 @@ class SpecEndpoint:
857
870
  assert isinstance(is_beta, bool)
858
871
  self.is_beta = is_beta
859
872
 
873
+ stability_level_raw = data.get("stability_level")
874
+ assert stability_level_raw is None or isinstance(stability_level_raw, str)
875
+ self.stability_level = (
876
+ StabilityLevel(stability_level_raw)
877
+ if stability_level_raw is not None
878
+ else None
879
+ )
880
+
860
881
  async_batch_path = data.get("async_batch_path")
861
882
  if async_batch_path is not None:
862
883
  assert isinstance(async_batch_path, str)
@@ -23,6 +23,7 @@ from .emit_open_api_util import (
23
23
  EmitOpenAPIGuide,
24
24
  EmitOpenAPIPath,
25
25
  EmitOpenAPIServer,
26
+ EmitOpenAPIStabilityLevel,
26
27
  EmitOpenAPITag,
27
28
  GlobalContextInfo,
28
29
  TagGroupToNamedTags,
@@ -256,6 +257,14 @@ def _emit_is_beta(is_beta: bool) -> DictApiSchema:
256
257
  return {}
257
258
 
258
259
 
260
+ def _emit_stability_level(
261
+ stability_level: EmitOpenAPIStabilityLevel | None,
262
+ ) -> DictApiSchema:
263
+ if stability_level is not None:
264
+ return {"x-stability-level": str(stability_level)}
265
+ return {}
266
+
267
+
259
268
  def _emit_endpoint_request_body(
260
269
  endpoint: EmitOpenAPIEndpoint,
261
270
  arguments_type: OpenAPIType | None,
@@ -349,6 +358,7 @@ def _emit_namespace(
349
358
  }
350
359
  | _emit_endpoint_description(endpoint.description, ctx.endpoint.guides)
351
360
  | _emit_is_beta(endpoint.is_beta)
361
+ | _emit_stability_level(endpoint.stability_level)
352
362
  | _emit_endpoint_parameters(endpoint, argument_type, ctx.endpoint.examples)
353
363
  | _emit_endpoint_request_body(endpoint, argument_type, ctx.endpoint.examples)
354
364
  | {
@@ -564,6 +574,7 @@ def _emit_endpoint(
564
574
  summary=f"{'/'.join(namespace.path[path_cutoff:])}",
565
575
  description=description,
566
576
  is_beta=namespace.endpoint.is_beta,
577
+ stability_level=namespace.endpoint.stability_level,
567
578
  examples=[
568
579
  EmitOpenAPIEndpointExample(
569
580
  ref_name=f"ex_{i}",
@@ -72,6 +72,9 @@ class EmitOpenAPIEndpointExample:
72
72
  data: dict[str, object]
73
73
 
74
74
 
75
+ EmitOpenAPIStabilityLevel = builder.StabilityLevel
76
+
77
+
75
78
  @dataclass
76
79
  class EmitOpenAPIEndpoint:
77
80
  method: str
@@ -79,6 +82,7 @@ class EmitOpenAPIEndpoint:
79
82
  summary: str
80
83
  description: str
81
84
  is_beta: bool
85
+ stability_level: EmitOpenAPIStabilityLevel | None
82
86
  examples: list[EmitOpenAPIEndpointExample]
83
87
  guides: list[EmitOpenAPIGuide]
84
88
 
@@ -4,6 +4,7 @@ import typing
4
4
  from dataclasses import dataclass
5
5
  from datetime import datetime, timedelta
6
6
  from enum import StrEnum
7
+ from importlib.metadata import PackageNotFoundError, version
7
8
  from urllib.parse import urljoin
8
9
  from uuid import uuid4
9
10
 
@@ -20,6 +21,13 @@ from .types import AuthDetailsAll, AuthDetailsApiKey, AuthDetailsOAuth
20
21
 
21
22
  DT = typing.TypeVar("DT")
22
23
  UNC_REQUEST_ID_HEADER = "X-UNC-REQUEST-ID"
24
+ UNC_SDK_VERSION_HEADER = "X-UNC-SDK-VERSION"
25
+
26
+
27
+ try:
28
+ __version__ = version("uncountable")
29
+ except PackageNotFoundError:
30
+ __version__ = "unknown"
23
31
 
24
32
 
25
33
  class EndpointMethod(StrEnum):
@@ -157,7 +165,7 @@ class Client(ClientMethods):
157
165
  try:
158
166
  data = response.json()
159
167
  if "error" in data:
160
- extra_details = data["error"]
168
+ extra_details = data
161
169
  except JSONDecodeError:
162
170
  pass
163
171
  raise APIResponseError.construct_error(
@@ -247,6 +255,7 @@ class Client(ClientMethods):
247
255
  ) -> HTTPRequest:
248
256
  headers = self._build_auth_headers()
249
257
  headers[UNC_REQUEST_ID_HEADER] = request_id
258
+ headers[UNC_SDK_VERSION_HEADER] = __version__
250
259
  method = api_request.method.lower()
251
260
  data = {"data": json.dumps(serialize_for_api(api_request.args))}
252
261
  match method:
@@ -65,6 +65,7 @@ from . import recipe_tags as recipe_tags_t
65
65
  from . import recipe_workflow_steps as recipe_workflow_steps_t
66
66
  from . import recipes as recipes_t
67
67
  from .api.recipes import remove_recipe_from_project as remove_recipe_from_project_t
68
+ from .api.recipe_links import remove_recipe_link as remove_recipe_link_t
68
69
  from .api.entity import resolve_entity_ids as resolve_entity_ids_t
69
70
  from .api.outputs import resolve_output_conditions as resolve_output_conditions_t
70
71
  from . import response as response_t
@@ -84,6 +85,7 @@ from .api.entity import transition_entity_phase as transition_entity_phase_t
84
85
  from .api.recipes import unarchive_recipes as unarchive_recipes_t
85
86
  from . import units as units_t
86
87
  from .api.entity import unlock_entity as unlock_entity_t
88
+ from .api.recipes import unlock_recipes as unlock_recipes_t
87
89
  from .api.material_families import update_entity_material_families as update_entity_material_families_t
88
90
  from .api.field_options import upsert_field_options as upsert_field_options_t
89
91
  from . import users as users_t
@@ -153,6 +155,7 @@ __all__: list[str] = [
153
155
  "recipe_workflow_steps_t",
154
156
  "recipes_t",
155
157
  "remove_recipe_from_project_t",
158
+ "remove_recipe_link_t",
156
159
  "resolve_entity_ids_t",
157
160
  "resolve_output_conditions_t",
158
161
  "response_t",
@@ -172,6 +175,7 @@ __all__: list[str] = [
172
175
  "unarchive_recipes_t",
173
176
  "units_t",
174
177
  "unlock_entity_t",
178
+ "unlock_recipes_t",
175
179
  "update_entity_material_families_t",
176
180
  "upsert_field_options_t",
177
181
  "users_t",
@@ -0,0 +1,38 @@
1
+ # DO NOT MODIFY -- This file is generated by type_spec
2
+ # flake8: noqa: F821
3
+ # ruff: noqa: E402
4
+ # fmt: off
5
+ # isort: skip_file
6
+ from __future__ import annotations
7
+ import typing # noqa: F401
8
+ import datetime # noqa: F401
9
+ from decimal import Decimal # noqa: F401
10
+ from dataclasses import dataclass
11
+ from ... import async_batch as async_batch_t
12
+ from ... import identifier as identifier_t
13
+ from ... import recipe_links as recipe_links_t
14
+
15
+ __all__: list[str] = [
16
+ "Arguments",
17
+ "Data",
18
+ "ENDPOINT_METHOD",
19
+ "ENDPOINT_PATH",
20
+ ]
21
+
22
+ ENDPOINT_METHOD = "POST"
23
+ ENDPOINT_PATH = "api/external/recipe_links/remove_recipe_link"
24
+
25
+
26
+ # DO NOT MODIFY -- This file is generated by type_spec
27
+ @dataclass(kw_only=True)
28
+ class Arguments:
29
+ recipe_from_key: identifier_t.IdentifierKey
30
+ recipe_to_key: identifier_t.IdentifierKey
31
+ link_type: typing.Union[typing.Literal[recipe_links_t.RecipeLinkType.CHILD], typing.Literal[recipe_links_t.RecipeLinkType.CONTROL], typing.Literal[recipe_links_t.RecipeLinkType.USER_LINK]]
32
+
33
+
34
+ # DO NOT MODIFY -- This file is generated by type_spec
35
+ @dataclass(kw_only=True)
36
+ class Data(async_batch_t.AsyncBatchActionReturn):
37
+ pass
38
+ # DO NOT MODIFY -- This file is generated by type_spec
@@ -9,8 +9,6 @@ import datetime # noqa: F401
9
9
  from decimal import Decimal # noqa: F401
10
10
  from pkgs.strenum_compat import StrEnum
11
11
  from dataclasses import dataclass
12
- from pkgs.serialization import serial_class
13
- from pkgs.serialization import serial_union_annotation
14
12
  from ... import identifier as identifier_t
15
13
 
16
14
  __all__: list[str] = [
@@ -18,10 +16,7 @@ __all__: list[str] = [
18
16
  "Data",
19
17
  "ENDPOINT_METHOD",
20
18
  "ENDPOINT_PATH",
21
- "RecipeLock",
22
- "RecipeLockAll",
23
19
  "RecipeLockBase",
24
- "RecipeLockInputs",
25
20
  "RecipeLockType",
26
21
  ]
27
22
 
@@ -39,47 +34,16 @@ class RecipeLockType(StrEnum):
39
34
  @dataclass(kw_only=True)
40
35
  class RecipeLockBase:
41
36
  recipe: identifier_t.IdentifierKey
42
- globally_removable: bool
43
- type: RecipeLockType
44
- lock_samples: typing.Optional[bool] = None
45
- comments: typing.Optional[str] = None
46
-
47
-
48
- # DO NOT MODIFY -- This file is generated by type_spec
49
- @serial_class(
50
- parse_require={"type"},
51
- )
52
- @dataclass(kw_only=True)
53
- class RecipeLockAll(RecipeLockBase):
54
- type: typing.Literal[RecipeLockType.ALL] = RecipeLockType.ALL
55
-
56
-
57
- # DO NOT MODIFY -- This file is generated by type_spec
58
- @serial_class(
59
- parse_require={"type"},
60
- )
61
- @dataclass(kw_only=True)
62
- class RecipeLockInputs(RecipeLockBase):
63
- type: typing.Literal[RecipeLockType.INPUTS_ONLY] = RecipeLockType.INPUTS_ONLY
64
-
65
-
66
- # DO NOT MODIFY -- This file is generated by type_spec
67
- RecipeLock = typing.Annotated[
68
- typing.Union[RecipeLockAll, RecipeLockInputs],
69
- serial_union_annotation(
70
- discriminator="type",
71
- discriminator_map={
72
- "all": RecipeLockAll,
73
- "inputs_only": RecipeLockInputs,
74
- },
75
- ),
76
- ]
77
37
 
78
38
 
79
39
  # DO NOT MODIFY -- This file is generated by type_spec
80
40
  @dataclass(kw_only=True)
81
41
  class Arguments:
82
- locks: list[RecipeLock]
42
+ type: RecipeLockType = RecipeLockType.ALL
43
+ recipes: list[identifier_t.IdentifierKey]
44
+ globally_removable: bool
45
+ lock_samples: typing.Optional[bool] = None
46
+ comments: typing.Optional[str] = None
83
47
 
84
48
 
85
49
  # DO NOT MODIFY -- This file is generated by type_spec
@@ -0,0 +1,43 @@
1
+ # DO NOT MODIFY -- This file is generated by type_spec
2
+ # flake8: noqa: F821
3
+ # ruff: noqa: E402
4
+ # fmt: off
5
+ # isort: skip_file
6
+ from __future__ import annotations
7
+ import typing # noqa: F401
8
+ import datetime # noqa: F401
9
+ from decimal import Decimal # noqa: F401
10
+ from pkgs.strenum_compat import StrEnum
11
+ from dataclasses import dataclass
12
+ from ... import identifier as identifier_t
13
+
14
+ __all__: list[str] = [
15
+ "Arguments",
16
+ "Data",
17
+ "ENDPOINT_METHOD",
18
+ "ENDPOINT_PATH",
19
+ "RecipeUnlockType",
20
+ ]
21
+
22
+ ENDPOINT_METHOD = "POST"
23
+ ENDPOINT_PATH = "api/external/recipes/unlock_recipes"
24
+
25
+
26
+ # DO NOT MODIFY -- This file is generated by type_spec
27
+ class RecipeUnlockType(StrEnum):
28
+ STANDARD = "standard"
29
+
30
+
31
+ # DO NOT MODIFY -- This file is generated by type_spec
32
+ @dataclass(kw_only=True)
33
+ class Arguments:
34
+ type: RecipeUnlockType = RecipeUnlockType.STANDARD
35
+ recipes: list[identifier_t.IdentifierKey]
36
+ unlock_samples: typing.Optional[bool] = None
37
+
38
+
39
+ # DO NOT MODIFY -- This file is generated by type_spec
40
+ @dataclass(kw_only=True)
41
+ class Data:
42
+ pass
43
+ # DO NOT MODIFY -- This file is generated by type_spec
@@ -27,6 +27,8 @@ class AsyncBatchRequestPath(StrEnum):
27
27
  SET_RECIPE_TAGS = "recipes/set_recipe_tags"
28
28
  EDIT_RECIPE_INPUTS = "recipes/edit_recipe_inputs"
29
29
  ARCHIVE_RECIPES = "recipes/archive"
30
+ LOCK_RECIPES = "recipes/lock_recipes"
31
+ UNLOCK_RECIPES = "recipes/unlock_recipes"
30
32
 
31
33
 
32
34
  # DO NOT MODIFY -- This file is generated by type_spec
@@ -58,6 +58,7 @@ from uncountable.types import recipe_links as recipe_links_t
58
58
  from uncountable.types import recipe_metadata as recipe_metadata_t
59
59
  from uncountable.types import recipe_workflow_steps as recipe_workflow_steps_t
60
60
  import uncountable.types.api.recipes.remove_recipe_from_project as remove_recipe_from_project_t
61
+ import uncountable.types.api.recipe_links.remove_recipe_link as remove_recipe_link_t
61
62
  import uncountable.types.api.entity.resolve_entity_ids as resolve_entity_ids_t
62
63
  import uncountable.types.api.outputs.resolve_output_conditions as resolve_output_conditions_t
63
64
  import uncountable.types.api.triggers.run_trigger as run_trigger_t
@@ -75,6 +76,7 @@ import uncountable.types.api.entity.set_values as set_values_t
75
76
  import uncountable.types.api.entity.transition_entity_phase as transition_entity_phase_t
76
77
  import uncountable.types.api.recipes.unarchive_recipes as unarchive_recipes_t
77
78
  import uncountable.types.api.entity.unlock_entity as unlock_entity_t
79
+ import uncountable.types.api.recipes.unlock_recipes as unlock_recipes_t
78
80
  import uncountable.types.api.material_families.update_entity_material_families as update_entity_material_families_t
79
81
  import uncountable.types.api.field_options.upsert_field_options as upsert_field_options_t
80
82
  from abc import ABC, abstractmethod
@@ -911,14 +913,29 @@ class ClientMethods(ABC):
911
913
  def lock_recipes(
912
914
  self,
913
915
  *,
914
- locks: list[lock_recipes_t.RecipeLock],
916
+ type: lock_recipes_t.RecipeLockType = lock_recipes_t.RecipeLockType.ALL,
917
+ recipes: list[identifier_t.IdentifierKey],
918
+ globally_removable: bool,
919
+ lock_samples: typing.Optional[bool] = None,
920
+ comments: typing.Optional[str] = None,
915
921
  ) -> lock_recipes_t.Data:
916
922
  """Lock experiments. Experiments will require unlocking to be editable. Edits to the experiments are blocked while they are locked.
917
923
 
918
- :param locks: The recipe locks to apply, a maximum of 100 can be sent
924
+ :param type: The type of lock to set.
925
+ All = both inputs and measurements are locked.
926
+ Inputs Only = only inputs are locked from editing.
927
+
928
+ :param recipes: The recipes to lock, a maximum of 100 can be sent
929
+ :param globally_removable: If true any user can unlock the experiment. If false the locking user is the only user that can unlock.
930
+ :param lock_samples: Should associated experiment test samples also be locked.
931
+ :param comments: Optional comment describing the purpose of locking
919
932
  """
920
933
  args = lock_recipes_t.Arguments(
921
- locks=locks,
934
+ type=type,
935
+ recipes=recipes,
936
+ globally_removable=globally_removable,
937
+ lock_samples=lock_samples,
938
+ comments=comments,
922
939
  )
923
940
  api_request = APIRequest(
924
941
  method=lock_recipes_t.ENDPOINT_METHOD,
@@ -971,6 +988,31 @@ class ClientMethods(ABC):
971
988
  )
972
989
  return self.do_request(api_request=api_request, return_type=remove_recipe_from_project_t.Data)
973
990
 
991
+ def remove_recipe_link(
992
+ self,
993
+ *,
994
+ recipe_from_key: identifier_t.IdentifierKey,
995
+ recipe_to_key: identifier_t.IdentifierKey,
996
+ link_type: typing.Union[typing.Literal[recipe_links_t.RecipeLinkType.CHILD], typing.Literal[recipe_links_t.RecipeLinkType.CONTROL], typing.Literal[recipe_links_t.RecipeLinkType.USER_LINK]],
997
+ ) -> remove_recipe_link_t.Data:
998
+ """Remove a link between two recipes. Skip if the link doesn't already exist
999
+
1000
+ :param recipe_from_key: Identifier for the recipe the link comes from
1001
+ :param recipe_to_key: Identifier for the recipe the link goes to
1002
+ :param link_type: The type of link being removed
1003
+ """
1004
+ args = remove_recipe_link_t.Arguments(
1005
+ recipe_from_key=recipe_from_key,
1006
+ recipe_to_key=recipe_to_key,
1007
+ link_type=link_type,
1008
+ )
1009
+ api_request = APIRequest(
1010
+ method=remove_recipe_link_t.ENDPOINT_METHOD,
1011
+ endpoint=remove_recipe_link_t.ENDPOINT_PATH,
1012
+ args=args,
1013
+ )
1014
+ return self.do_request(api_request=api_request, return_type=remove_recipe_link_t.Data)
1015
+
974
1016
  def resolve_entity_ids(
975
1017
  self,
976
1018
  *,
@@ -1333,6 +1375,31 @@ class ClientMethods(ABC):
1333
1375
  )
1334
1376
  return self.do_request(api_request=api_request, return_type=unlock_entity_t.Data)
1335
1377
 
1378
+ def unlock_recipes(
1379
+ self,
1380
+ *,
1381
+ type: unlock_recipes_t.RecipeUnlockType = unlock_recipes_t.RecipeUnlockType.STANDARD,
1382
+ recipes: list[identifier_t.IdentifierKey],
1383
+ unlock_samples: typing.Optional[bool] = None,
1384
+ ) -> unlock_recipes_t.Data:
1385
+ """Unlock experiments. Experiments will edtiable after unlocking if they are currently locked.
1386
+
1387
+ :param type: The method to unlock recipes. Default is standard.
1388
+ :param recipes: The recipes to unlock, a maximum of 100 can be sent
1389
+ :param unlock_samples: Should associated experiment test samples also be unlocked.
1390
+ """
1391
+ args = unlock_recipes_t.Arguments(
1392
+ type=type,
1393
+ recipes=recipes,
1394
+ unlock_samples=unlock_samples,
1395
+ )
1396
+ api_request = APIRequest(
1397
+ method=unlock_recipes_t.ENDPOINT_METHOD,
1398
+ endpoint=unlock_recipes_t.ENDPOINT_PATH,
1399
+ args=args,
1400
+ )
1401
+ return self.do_request(api_request=api_request, return_type=unlock_recipes_t.Data)
1402
+
1336
1403
  def update_entity_material_families(
1337
1404
  self,
1338
1405
  *,