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

Files changed (30) hide show
  1. {UncountablePythonSDK-0.0.30.dist-info → UncountablePythonSDK-0.0.33.dist-info}/METADATA +3 -3
  2. {UncountablePythonSDK-0.0.30.dist-info → UncountablePythonSDK-0.0.33.dist-info}/RECORD +30 -24
  3. {UncountablePythonSDK-0.0.30.dist-info → UncountablePythonSDK-0.0.33.dist-info}/WHEEL +1 -1
  4. docs/requirements.txt +5 -5
  5. pkgs/argument_parser/argument_parser.py +41 -2
  6. pkgs/argument_parser/case_convert.py +4 -3
  7. pkgs/serialization/__init__.py +2 -0
  8. pkgs/serialization/serial_class.py +31 -37
  9. pkgs/serialization/serial_union.py +81 -0
  10. pkgs/serialization_util/__init__.py +1 -7
  11. pkgs/serialization_util/convert_to_snakecase.py +27 -0
  12. pkgs/serialization_util/serialization_helpers.py +48 -59
  13. pkgs/type_spec/builder.py +57 -1
  14. pkgs/type_spec/emit_open_api.py +6 -0
  15. pkgs/type_spec/emit_python.py +23 -0
  16. pkgs/type_spec/emit_typescript.py +6 -0
  17. pkgs/type_spec/type_info/emit_type_info.py +43 -9
  18. uncountable/types/__init__.py +8 -0
  19. uncountable/types/api/entity/list_entities.py +7 -0
  20. uncountable/types/api/inputs/set_intermediate_type.py +43 -0
  21. uncountable/types/api/recipes/add_recipe_to_project.py +35 -0
  22. uncountable/types/api/recipes/associate_recipe_as_input.py +1 -0
  23. uncountable/types/api/recipes/edit_recipe_inputs.py +39 -1
  24. uncountable/types/api/recipes/remove_recipe_from_project.py +35 -0
  25. uncountable/types/api/recipes/set_recipe_outputs.py +2 -0
  26. uncountable/types/client_base.py +72 -0
  27. uncountable/types/inputs.py +1 -0
  28. uncountable/types/recipe_workflow_steps.py +14 -3
  29. uncountable/types/recipes.py +21 -0
  30. {UncountablePythonSDK-0.0.30.dist-info → UncountablePythonSDK-0.0.33.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.30
3
+ Version: 0.0.33
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
@@ -19,7 +19,7 @@ Description-Content-Type: text/markdown
19
19
  Requires-Dist: aiotus ==0.*
20
20
  Requires-Dist: aiohttp ==3.*
21
21
  Requires-Dist: requests ==2.*
22
- Requires-Dist: SQLAlchemy ==1.4.*
22
+ Requires-Dist: SQLAlchemy >=1.4.0
23
23
  Requires-Dist: APScheduler ==3.*
24
24
  Requires-Dist: python-dateutil ==2.*
25
25
  Requires-Dist: shelljob ==0.*
@@ -28,7 +28,7 @@ Provides-Extra: test
28
28
  Requires-Dist: mypy ==1.* ; extra == 'test'
29
29
  Requires-Dist: ruff ==0.* ; extra == 'test'
30
30
  Requires-Dist: pytest ==8.* ; extra == 'test'
31
- Requires-Dist: coverage[toml] ==6.* ; extra == 'test'
31
+ Requires-Dist: coverage[toml] ==7.* ; extra == 'test'
32
32
  Requires-Dist: pytest-cov ==5.* ; extra == 'test'
33
33
  Requires-Dist: pytest-xdist ==3.* ; extra == 'test'
34
34
 
@@ -3,7 +3,7 @@ docs/conf.py,sha256=YF5J-9g_Wg8wXmyHsGaE8xYlDEzqocNl3UWUmP0CwBg,1702
3
3
  docs/index.md,sha256=eEdirX_Ds6ICTRtIS5iT4irCquHcQyKN7E4M5QP9T8A,257
4
4
  docs/justfile,sha256=cvNcpb-ByPOF2aCrFlg3DDZBoYMx5W8xGdr13m9HcnI,215
5
5
  docs/quickstart.md,sha256=3GuJ0MB1O5kjlsrgAmdSkDq0rYqATrYy-tzEHDy8H-c,422
6
- docs/requirements.txt,sha256=jNFoVqyOwGZ4GToYiyoVeFFIzJTmXMLYbv5h2FU5Sr4,139
6
+ docs/requirements.txt,sha256=NnB0-jOOufUvD3q1csl7WvQ8oHOD-5emqp_SWUXExI0,138
7
7
  docs/static/logo_blue.png,sha256=SyYpMTVhhBbhF5Wl8lWaVwz-_p1MIR6dW6bVhufQRME,46708
8
8
  docs/static/favicons/android-chrome-192x192.png,sha256=XoF-AhD55JlSBDGsEPJKfT_VeXT-awhwKyZnxLhrwvk,1369
9
9
  docs/static/favicons/android-chrome-512x512.png,sha256=1S4xwY9YtJQ5ifFsZ-DOzssoyBYs0t9uwdOUmYx0Xso,3888
@@ -21,26 +21,28 @@ pkgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  pkgs/argument_parser/__init__.py,sha256=CsQ6QoPKSLLRVl-z6URAmPkiUL9ZPZoV4rJHgy_-RjA,385
22
22
  pkgs/argument_parser/_is_enum.py,sha256=Gw6jJa8nBwYGqXwwCZbSnWL8Rvr5alkg5lSVAqXtOZM,257
23
23
  pkgs/argument_parser/_is_namedtuple.py,sha256=Rjc1bKanIPPogl3qG5JPBxglG1TqWYOo1nxxhBASQWY,265
24
- pkgs/argument_parser/argument_parser.py,sha256=eqw4ge9Sh9z35Vztq_dUQUvVMluo7YgnmMjHNMhrhVw,15937
25
- pkgs/argument_parser/case_convert.py,sha256=J9wahIE-P95LvTqn4M4gDUx_RXeiW2SRo9i_1bz1E6A,558
26
- pkgs/serialization/__init__.py,sha256=quvXMSl1szddLTr4Yxo9KA9oBMoeX7qGpFkkAplFBbY,603
24
+ pkgs/argument_parser/argument_parser.py,sha256=pcU4IhgFkea-I6PhHUex43BoVaQvnoRV-Mw3qOqCdBQ,17274
25
+ pkgs/argument_parser/case_convert.py,sha256=NuJLJUJRbyVb6_Slen4uqaStEHbcOS1d-hBBfDrrw-c,605
26
+ pkgs/serialization/__init__.py,sha256=LifasRW0a50A3qRFmo2bf3FQ6TXhZWOTz2-CVTgPjcQ,753
27
27
  pkgs/serialization/missing_sentry.py,sha256=aM_9KxbCk9dVvXvcOKgkIQBqFWvLhv8QlIUCiuFEXMo,806
28
28
  pkgs/serialization/opaque_key.py,sha256=FIfXEE0DA1U8R_taFbQ1RCoTSgehrPjP06-qvo-GeNQ,177
29
- pkgs/serialization/serial_class.py,sha256=19UIvz2XPT4k1uXtScyjVfdhfvpehZOcjV6Z27p_VFQ,5193
30
- pkgs/serialization_util/__init__.py,sha256=4vX5j1pvd1NkznSVqwWqunVyOvQtLCgVuwRjVwDk7qg,447
29
+ pkgs/serialization/serial_class.py,sha256=r0hrQdIbJA_X0W0_jKEVrxi_JzVRT9qHCjsUgGu3cCI,5290
30
+ pkgs/serialization/serial_union.py,sha256=z8Ptj4bVHyb1ROfg0UPTwZ6Ef6iXLr0YJfAH5o_PU9A,2601
31
+ pkgs/serialization_util/__init__.py,sha256=MVKqHTUl2YnWZAFG9xCxu1SgmkQ5xPofrAGlYg6h7rI,330
31
32
  pkgs/serialization_util/_get_type_for_serialization.py,sha256=dW5_W9MFd6wgWfW5qlWork-GBb-QFLtiOZkjk2Zqn2M,1177
32
- pkgs/serialization_util/serialization_helpers.py,sha256=koyGyvdlEmGFK1BUiHtM2gTzAyHygbe1JpKLdZ-Aw84,5124
33
+ pkgs/serialization_util/convert_to_snakecase.py,sha256=H2BAo5ZdcCDN77RpLb-uP0s7-FQ5Ukwnsd3VYc1vD0M,583
34
+ pkgs/serialization_util/serialization_helpers.py,sha256=byotZo00SVREW_aM0sbKQEzuI7O3zwjk_nB_OQ6S5M8,4999
33
35
  pkgs/strenum_compat/__init__.py,sha256=wXRFeNvBm8RU6dy1PFJ5sRLgUIEeH_DVR95Sv5qpGbk,59
34
36
  pkgs/strenum_compat/strenum_compat.py,sha256=uOUAgpYTjHs1MX8dG81jRlyTkt3KNbkV_25zp7xTX2s,36
35
37
  pkgs/type_spec/__init__.py,sha256=h5DmJTca4QVV10sZR1x0-MlkZfuGYDfapR3zHvXfzto,19
36
38
  pkgs/type_spec/__main__.py,sha256=5bJaX9Y_-FavP0qwzhk-z-V97UY7uaezJTa1zhO_HHQ,1048
37
- pkgs/type_spec/builder.py,sha256=1C_ELB7KfK118QxZDLLc_Uf_nh5TAg96ilpOCfEQuN4,43685
39
+ pkgs/type_spec/builder.py,sha256=un86i9LqTmCMVj-g6lrZ8lU4JZEElzCfUlsn--GkTvA,46049
38
40
  pkgs/type_spec/config.py,sha256=INfEiDcUsZFUKasHprsE6i33siPB0RnfmTKOsWcGnQ8,5043
39
41
  pkgs/type_spec/emit_io_ts.py,sha256=Ghd8XYqyNYldHQDepwa9GLfHXcoi48ztBw84K28ETic,5707
40
- pkgs/type_spec/emit_open_api.py,sha256=rAxfPVsqJU7ass76dPhImgPao6AW6xyz-rMaQDhSp1I,23822
42
+ pkgs/type_spec/emit_open_api.py,sha256=Aw7Ct1itmAqhb_nsM9yDz87EoF0XWHM56MhKqtOLOio,24005
41
43
  pkgs/type_spec/emit_open_api_util.py,sha256=XAA6zH59aZWLVl0BvKAICXXl4sdBqx01QAtv5oB0bMI,2266
42
- pkgs/type_spec/emit_python.py,sha256=vuVgyts9zfEgRMTGsI6LKejMnq9_lxLxawjxoz4p7DE,44098
43
- pkgs/type_spec/emit_typescript.py,sha256=4hpCJwiDf-v8LJaNFVfFtf8zvtG73YNPFwwa_5NuffI,17729
44
+ pkgs/type_spec/emit_python.py,sha256=zP3AWJ5u0vzDcnvzSehCUgvXM0J9ZUtfLBVHerW6_wI,45164
45
+ pkgs/type_spec/emit_typescript.py,sha256=cdr5h8N70PuwORcvhURUujzwH9r1LVwJB8V2EoipGkw,17917
44
46
  pkgs/type_spec/emit_typescript_util.py,sha256=93FzJnpYse4PKFzgdw4DGV4zFTi5tF4WR-CIi7cW498,873
45
47
  pkgs/type_spec/load_types.py,sha256=xEHwdB_miR3vNs161Oy1luafE0VC-yk9-utAyCJmbEo,3629
46
48
  pkgs/type_spec/open_api_util.py,sha256=IGh-_snGPST_P_8FdYtO8MTEa9PUxRW6Rzg9X9EgQik,7114
@@ -52,7 +54,7 @@ pkgs/type_spec/actions_registry/emit_typescript.py,sha256=ben0W7qwaVCzLO-t3NEJPP
52
54
  pkgs/type_spec/parts/base.py.prepart,sha256=wGNoDyQnLolHRZGRwHQX5TrPfKnu558NXCocYvqyroc,2174
53
55
  pkgs/type_spec/parts/base.ts.prepart,sha256=2FJJvpg2olCcavxj0nbYWdwKl6KeScour2JjSvN42l8,1001
54
56
  pkgs/type_spec/type_info/__main__.py,sha256=pmVjVqXyVh8vKTNCTFgz80Sg74C5BKToP3E6GS-X_So,857
55
- pkgs/type_spec/type_info/emit_type_info.py,sha256=nk1pKa0drccGvzPKSpub3GVctsbu70reh3_yhhJzKAo,12052
57
+ pkgs/type_spec/type_info/emit_type_info.py,sha256=XqAyJgzkYY2woG5O-IA5O9gnfja_slTZGd-GnS7xPts,13280
56
58
  pkgs/type_spec/value_spec/__init__.py,sha256=Z-grlcZtxAfEXhPHsK0nD7PFLGsv4eqvunaPN7_TA84,83
57
59
  pkgs/type_spec/value_spec/__main__.py,sha256=-9L5pXYx02plnTetqNknaUZPieLRtzbyWdZDT6B-cWA,8294
58
60
  pkgs/type_spec/value_spec/convert_type.py,sha256=SAYyEV6orQJJbkXSE4hhtOQJ2vKUXJCKPeYPrB8G9oA,2272
@@ -76,13 +78,13 @@ uncountable/integration/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
76
78
  uncountable/integration/db/connect.py,sha256=YtQHJ1DBGPhxKFRCfiXqohOYUceKSxMVOJ88aPI48Ug,181
77
79
  uncountable/integration/executors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
80
  uncountable/integration/executors/script_executor.py,sha256=hM8E-aU8zyM6ZcBtqAqInKZ_BF93RLqEA0dU7y5FhWQ,841
79
- uncountable/types/__init__.py,sha256=ziCs6fh_WzMhHPGwFi7xCdFQoNT6PBzZyfrMANEm8g0,6816
81
+ uncountable/types/__init__.py,sha256=0UllKeQhqDy1AD5UH6tMPx0QqIuW7KFlVe1SnJkD3ro,7197
80
82
  uncountable/types/async_batch.py,sha256=aWiul1fK3-cXaCESUUJ_92FF-NuuwxSjn9n3jC9vY5o,1618
81
83
  uncountable/types/async_batch_processor.py,sha256=eNXKPOh-sCiarPvreLDEu4XbieWLe_5KXmiY984eZ5I,6083
82
84
  uncountable/types/base.py,sha256=w3BRf8SAvYPlKrcJtJcQ_WhCU3A9zy0VuRTRWRFKVUA,2709
83
85
  uncountable/types/calculations.py,sha256=16J-KKMp-I8ZQUkYNmKCHfAn6DGb99cFinALcDIdGHY,562
84
86
  uncountable/types/chemical_structure.py,sha256=zQKl53DGtQQONIUHFXuwjWLQaG7FPZY7x6SBSOzkGV0,758
85
- uncountable/types/client_base.py,sha256=RRf9_k0U4PBYrdKGu0YEZa_cWxImwyg6fayqj6yISPY,55671
87
+ uncountable/types/client_base.py,sha256=UpIXr8JvrOuA9TNHeU1paqTNiE4KHsSpzA6RCAS7bO8,58694
86
88
  uncountable/types/curves.py,sha256=qYyRntMmFNonEwTrGhquMLbgMqjyP1moQflNTP0FMec,1308
87
89
  uncountable/types/entity.py,sha256=NjMZrqBwQ7sZe_oUuJqy9IEG7dWZmFMkQQXJ0_odcnA,11637
88
90
  uncountable/types/experiment_groups.py,sha256=ZBEk06F4n98Jz3oEA09WaDmw5rqPs7iVAm_Ysr4gc_o,599
@@ -91,7 +93,7 @@ uncountable/types/fields.py,sha256=eGtZ6axTYGFxLmPAyri2LwlcR4SZ2sX2c6QDX0ybKz0,5
91
93
  uncountable/types/id_source.py,sha256=Y3suURq3L1SahZ2oHPD986SU0l3Ik-ZzH38aQKgc1Fg,1341
92
94
  uncountable/types/identifier.py,sha256=94-O3H_qNrA48tf3srwPwdu8HURkLl7_-88kUnwElZg,1455
93
95
  uncountable/types/input_attributes.py,sha256=u-JABoZ-Ij1Ynq5g6MxOgRdQeYbM7OnGP2q_N7KuVdw,826
94
- uncountable/types/inputs.py,sha256=q7fNGaSKIk3R6uXCEhSQpiHvXu82YcK3oZHDI7bxE88,1597
96
+ uncountable/types/inputs.py,sha256=KoJGGxvwqQy8Eiw5X6PVUsEHQABYMtCj40PwtECDeIc,1617
95
97
  uncountable/types/outputs.py,sha256=hSUlu41sisYKIZpPrj1G1DRfKm6hsKNcd1eNKFYb-4w,671
96
98
  uncountable/types/permissions.py,sha256=VVLnukQ4RbApFdE0sgWDZxnrRCV6gOtXxq6yM5GxQlk,1620
97
99
  uncountable/types/phases.py,sha256=eaqwQlSRC2Ug7YFL0gqLbg3wDDHeRvBOhcABG7khW8c,550
@@ -102,7 +104,8 @@ uncountable/types/recipe_links.py,sha256=RldSV7SdeBYa0bx02DzMg4jfPdgrlMRE40T16Fd
102
104
  uncountable/types/recipe_metadata.py,sha256=cebGg_lJzqZzGnKnDgmuQFrw4Xhoz6HEiGM6G0az120,1437
103
105
  uncountable/types/recipe_output_metadata.py,sha256=XJA8R1r4NTzyR_DhMkmH4ZtYD-vqpvBMji9Be8OcFmo,613
104
106
  uncountable/types/recipe_tags.py,sha256=lYpksHAxXCcIjZKR7JoZOTH2cBSovwxZaHwjZy_yqiQ,581
105
- uncountable/types/recipe_workflow_steps.py,sha256=LmyFwWWwJv30vuaQ4qtd0hzDdeJaIxHQZqwRb1Wi_6A,2626
107
+ uncountable/types/recipe_workflow_steps.py,sha256=ZMZI6SteOTVSolhPPZsSqU139i4NYFc1ACbS1rSBLJQ,2997
108
+ uncountable/types/recipes.py,sha256=tY8MNmQiky94eIFOxSLyflXVno3pfDygxJ6WPqJlyDU,549
106
109
  uncountable/types/response.py,sha256=ZI0CG7ZxBM2k5_W-6mNMU3UlB0p1i-0nrwOvsMaS-vU,620
107
110
  uncountable/types/units.py,sha256=_kZ7KkXIbRiY2fOdkTsbJBpWRah5TCC2WWiG05e-1DA,565
108
111
  uncountable/types/users.py,sha256=SUjNHBDcImKnnE7IN096Wfr1fmjNjCkQ7yQgKUPffz8,588
@@ -117,7 +120,7 @@ uncountable/types/api/entity/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8A
117
120
  uncountable/types/api/entity/create_entities.py,sha256=vzo5hS1qcmjQdfyCMarSu8MRcRGSiholOVSCfjXlA1k,1703
118
121
  uncountable/types/api/entity/create_entity.py,sha256=ausozCQ3qPM9YUQ87bOTCKOm-zkhn4CSLJr9jLc9n2U,1873
119
122
  uncountable/types/api/entity/get_entities_data.py,sha256=XjrJGZucIn1TYUlDLRnRA0JTQw-vXHIAT-m0H9hk37A,1170
120
- uncountable/types/api/entity/list_entities.py,sha256=_bIIZJj3N0E6YiHgqzfCOKxD1fQW6biWJQMp5wIVbBw,1514
123
+ uncountable/types/api/entity/list_entities.py,sha256=Bd_Ppy2bzWve8RbplyMkEHkg07CxEIskeiTdb9Ntlfo,1673
121
124
  uncountable/types/api/entity/lock_entity.py,sha256=SbRaKDbJfoPD9uVYiGlnrsPF_HZ_6m0hPAlalZwGyag,1029
122
125
  uncountable/types/api/entity/resolve_entity_ids.py,sha256=AidGpPmI9ATDv0E7vd9LDOl3n3beGxUlRojh5uZrkl4,1086
123
126
  uncountable/types/api/entity/set_values.py,sha256=LcYrKQm5ItYLK1Vx7rRq5i6jkMLDhfEBhF0FD1GowQs,958
@@ -138,6 +141,7 @@ uncountable/types/api/inputs/get_inputs_data.py,sha256=sqZ6xEjzQqRQYMaKXLGWR8WPh
138
141
  uncountable/types/api/inputs/set_input_attribute_values.py,sha256=yvWgIVzl818ewhUA4v3ldSBfmZKngdWTtFn7fqk-dOE,1341
139
142
  uncountable/types/api/inputs/set_input_category.py,sha256=ymjMoIiWTtygwI67aVgcw6FyjWW31zBxZb78-YTNs1Y,967
140
143
  uncountable/types/api/inputs/set_input_subcategories.py,sha256=hRS1FcFWPaenO_08Ihtfy_v7MUbpiFefdK2AMv16HaI,1004
144
+ uncountable/types/api/inputs/set_intermediate_type.py,sha256=tbN3vJ0559eMKZh0kXio1nI1zxWKhZy4bZRI2LOVlGc,1109
141
145
  uncountable/types/api/material_families/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
142
146
  uncountable/types/api/material_families/update_entity_material_families.py,sha256=h-vkua57yCNSO6P1akIxmmaFdwzOXPxm2s3Xt6x1c7c,1396
143
147
  uncountable/types/api/outputs/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
@@ -154,27 +158,29 @@ uncountable/types/api/recipe_links/create_recipe_link.py,sha256=1Ok6XMGFKBIPV-uR
154
158
  uncountable/types/api/recipe_metadata/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
155
159
  uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py,sha256=el6Pn5XqExX66ZEEHM8CHPxnyXSMZPdfdvLBz86sAVY,1267
156
160
  uncountable/types/api/recipes/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
161
+ uncountable/types/api/recipes/add_recipe_to_project.py,sha256=YSVxZ_tDBsyNlxCiqHF2E8pQXRjfq46X6QwoZmkbWWk,867
157
162
  uncountable/types/api/recipes/archive_recipes.py,sha256=XFCqBa90bG4Tpxu6fhUqzl7PWlY35bbzOmJf5EOviRM,852
158
- uncountable/types/api/recipes/associate_recipe_as_input.py,sha256=88a2lirEgrodyyE6og0oYtkGAWih2uJCZRmy45kTBow,976
163
+ uncountable/types/api/recipes/associate_recipe_as_input.py,sha256=Kd0ySjIL4TZKR0QVVjlIUwUSjefZ6VmVw82HV-Yofbg,1027
159
164
  uncountable/types/api/recipes/associate_recipe_as_lot.py,sha256=bTYjbnY3B7GKz4MV4UGn7vPjaqMkAfUTio8872d4iws,955
160
165
  uncountable/types/api/recipes/create_recipe.py,sha256=jizKdsc761zrJXOi0xlmge7-Z9QlzRQdbLNtUoVLQCI,1420
161
166
  uncountable/types/api/recipes/create_recipes.py,sha256=qwIYa8hfcjY7_VOFt9lxmVtJ-HOJqQN3GDNSbZsRCZU,1544
162
167
  uncountable/types/api/recipes/disassociate_recipe_as_input.py,sha256=L25fpiK1Y5PByPVVgsZy9t4podz3xSSLIwKHj8CUrSg,913
163
- uncountable/types/api/recipes/edit_recipe_inputs.py,sha256=pTw606AgLhb-oJjfj1WPyEcJ4B0tgZsvEKeqP5VZ1gY,3281
168
+ uncountable/types/api/recipes/edit_recipe_inputs.py,sha256=_dLulVZLqi-CrFIVMRts8h0OHx-nUG3vFOSKS1juGUc,4568
164
169
  uncountable/types/api/recipes/get_curve.py,sha256=UIWfpqtU5sQokaxwYfQFNFl6HMyzWEF_Sjd8UMz0U88,939
165
170
  uncountable/types/api/recipes/get_recipe_calculations.py,sha256=eQmkdZzCEuq8S2f_kf_7GPvDLX1pTnY1CRmkK0SkMCI,1472
166
171
  uncountable/types/api/recipes/get_recipe_links.py,sha256=hk5dfQjv7yU2r-S9b8vwWEJLPHqU0-M6SFiTLMR3fVk,985
167
172
  uncountable/types/api/recipes/get_recipe_names.py,sha256=uCpXZq5oWjr9a_Vf-yYPaVS72XOlLHgAlju6KHeQ3UA,986
168
173
  uncountable/types/api/recipes/get_recipe_output_metadata.py,sha256=L9s2ykPP4pd02Pc98LDisY8bgV8CToS6t6fXKTWqGRw,1464
169
174
  uncountable/types/api/recipes/get_recipes_data.py,sha256=TbJo4pkp5ePgbeH1oldThpq9jWLDYRUbrYMAv8T_LsI,5518
175
+ uncountable/types/api/recipes/remove_recipe_from_project.py,sha256=cr-VnqgBNek_WInmJln0UBn1GHMNQtRw3gsFTY_G91M,872
170
176
  uncountable/types/api/recipes/set_recipe_inputs.py,sha256=lFVfv-o_O5wHuMZdH63qlG4exFTlJM078oSAtb3XNxA,1426
171
177
  uncountable/types/api/recipes/set_recipe_metadata.py,sha256=Ba6ttd1JuS_Ypt-KpckSviWtOcQ-OTdTEJiaSYyoQL8,933
172
- uncountable/types/api/recipes/set_recipe_outputs.py,sha256=QYq39TNchQ80ET1C77OE9fwhbu_HmIoEDmrQJHkkCu0,1609
178
+ uncountable/types/api/recipes/set_recipe_outputs.py,sha256=VfUL59O21qPrgD7qQ9t5OVSGcGZ2r17lW_yEzuUkrtw,1722
173
179
  uncountable/types/api/recipes/set_recipe_tags.py,sha256=U710hgq9-t6QZGRB-ZGHskpt4iXwYEjIRb67eh3P518,2453
174
180
  uncountable/types/api/recipes/unarchive_recipes.py,sha256=WcwFYbBsX2SKXnoBQ8locnRn7Bj1rHdtrURQVOfqgfU,814
175
181
  uncountable/types/api/triggers/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
176
182
  uncountable/types/api/triggers/run_trigger.py,sha256=9m9M8-nlGB_sAU2Qm2lWugp4h4Osqj6QpjNfU8osd1U,901
177
- UncountablePythonSDK-0.0.30.dist-info/METADATA,sha256=f5hNs4fbAOqCQoHlWD1eEa5QJRz0EUazKU_TCppv--g,1577
178
- UncountablePythonSDK-0.0.30.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
179
- UncountablePythonSDK-0.0.30.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
180
- UncountablePythonSDK-0.0.30.dist-info/RECORD,,
183
+ UncountablePythonSDK-0.0.33.dist-info/METADATA,sha256=bPMG0vmoieJ8foThqNXZK5DRWNQO0mZz3ypltJAx6Ck,1577
184
+ UncountablePythonSDK-0.0.33.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
185
+ UncountablePythonSDK-0.0.33.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
186
+ UncountablePythonSDK-0.0.33.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (70.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
docs/requirements.txt CHANGED
@@ -1,7 +1,7 @@
1
- furo==2024.1.29
2
- myst-parser==2.0.0
3
- sphinx-autoapi==3.0.0
1
+ furo==2024.5.6
2
+ myst-parser==3.0.1
3
+ sphinx-autoapi==3.1.0
4
4
  sphinx-copybutton==0.5.2
5
- Sphinx==7.2.6
6
- sphinx_design==0.5.0
5
+ Sphinx==7.3.7
6
+ sphinx_design==0.6.0
7
7
  sphinx-favicon==1.0.1
@@ -10,7 +10,12 @@ from importlib import resources
10
10
  import dateutil.parser
11
11
  import yaml
12
12
 
13
- from pkgs.serialization import MissingSentryType, OpaqueKey, get_serial_class_data
13
+ from pkgs.serialization import (
14
+ MissingSentryType,
15
+ OpaqueKey,
16
+ get_serial_class_data,
17
+ get_serial_union_data,
18
+ )
14
19
 
15
20
  from ._is_enum import is_string_enum_class
16
21
  from ._is_namedtuple import is_namedtuple_type
@@ -112,6 +117,23 @@ def _invoke_membership_parser(
112
117
  raise ValueError(f"Expected value from {expected_values} but got value {value}")
113
118
 
114
119
 
120
+ def _build_parser_discriminated_union(
121
+ discriminator: str, discriminator_map: dict[str, ParserFunction[T]]
122
+ ) -> ParserFunction[T]:
123
+ def parse(value: typing.Any) -> typing.Any:
124
+ discriminant = value.get(discriminator)
125
+ if discriminant is None:
126
+ raise ValueError("missing-union-discriminant")
127
+ if not isinstance(discriminant, str):
128
+ raise ValueError("union-discriminant-is-not-string")
129
+ parser = discriminator_map.get(discriminant)
130
+ if parser is None:
131
+ raise ValueError("missing-type-for-union-discriminant", discriminant)
132
+ return parser(value)
133
+
134
+ return parse
135
+
136
+
115
137
  def _build_parser_inner(
116
138
  parsed_type: type[T],
117
139
  context: ParserContext,
@@ -130,6 +152,23 @@ def _build_parser_inner(
130
152
  are cached now, as they don't use the argument, and they're known to be safe.
131
153
  This is also enough to support some recursion.
132
154
  """
155
+
156
+ serial_union = get_serial_union_data(parsed_type)
157
+ if serial_union is not None:
158
+ discriminator = serial_union.discriminator
159
+ discriminator_map = serial_union.discriminator_map
160
+ if discriminator is None or discriminator_map is None:
161
+ # fallback to standard union parsing
162
+ parsed_type = serial_union.get_union_underlying()
163
+ else:
164
+ return _build_parser_discriminated_union(
165
+ discriminator,
166
+ {
167
+ key: _build_parser_inner(value, context)
168
+ for key, value in discriminator_map.items()
169
+ },
170
+ )
171
+
133
172
  if dataclasses.is_dataclass(parsed_type):
134
173
  return _build_parser_dataclass(parsed_type, context) # type: ignore[arg-type]
135
174
 
@@ -341,7 +380,7 @@ def _build_parser_dataclass(
341
380
 
342
381
  except Exception as e:
343
382
  raise ValueError(
344
- f"unable to parse field:{field.name}", field_raw_value
383
+ f"unable-to-parse-field:{field.name}", field_raw_value
345
384
  ) from e
346
385
 
347
386
  if context.options.strict_property_parsing:
@@ -4,9 +4,10 @@ import re
4
4
 
5
5
  @functools.lru_cache(maxsize=500000)
6
6
  def snake_to_camel_case(o: str) -> str:
7
- return "".join([
8
- part.title() if i > 0 else part for i, part in enumerate(o.split("_"))
9
- ])
7
+ tokens = o.split("_")
8
+ if len(tokens) < 2:
9
+ return o
10
+ return "".join([part.title() if i > 0 else part for i, part in enumerate(tokens)])
10
11
 
11
12
 
12
13
  def kebab_to_pascal_case(o: str) -> str:
@@ -8,3 +8,5 @@ from .serial_class import get_serial_class_data as get_serial_class_data
8
8
  from .serial_class import get_serial_string_enum_data as get_serial_string_enum_data
9
9
  from .serial_class import serial_class as serial_class
10
10
  from .serial_class import serial_string_enum as serial_string_enum
11
+ from .serial_union import serial_union_annotation as serial_union_annotation
12
+ from .serial_union import get_serial_union_data as get_serial_union_data
@@ -16,6 +16,9 @@ class _SerialClassData:
16
16
  parse_require: set[str] = dataclasses.field(default_factory=set)
17
17
 
18
18
 
19
+ EMPTY_SERIAL_CLASS_DATA = _SerialClassData()
20
+
21
+
19
22
  def serial_class(
20
23
  *,
21
24
  unconverted_keys: Optional[set[str]] = None,
@@ -57,58 +60,49 @@ def serial_class(
57
60
 
58
61
 
59
62
  class SerialClassDataInspector:
60
- bases: list[SerialClassDataInspector]
61
-
62
63
  def __init__(
63
- self, bases: list[SerialClassDataInspector], current: _SerialClassData
64
+ self,
65
+ current: _SerialClassData,
64
66
  ) -> None:
65
- self.bases = bases
66
67
  self.current = current
67
68
 
68
69
  def has_unconverted_key(self, key: str) -> bool:
69
- if key in self.current.unconverted_keys:
70
- return True
71
- for base in self.bases:
72
- if base.has_unconverted_key(key):
73
- return True
74
- return False
70
+ return key in self.current.unconverted_keys
75
71
 
76
72
  def has_unconverted_value(self, key: str) -> bool:
77
- if key in self.current.unconverted_values:
78
- return True
79
- for base in self.bases:
80
- if base.has_unconverted_value(key):
81
- return True
82
- return False
73
+ return key in self.current.unconverted_values
83
74
 
84
75
  def has_to_string_value(self, key: str) -> bool:
85
- if key in self.current.to_string_values:
86
- return True
87
- for base in self.bases:
88
- if base.has_to_string_value(key):
89
- return True
90
- return False
76
+ return key in self.current.to_string_values
91
77
 
92
78
  def has_parse_require(self, key: str) -> bool:
93
- if key in self.current.parse_require:
94
- return True
95
- for base in self.bases:
96
- if base.has_parse_require(key):
97
- return True
98
- return False
79
+ return key in self.current.parse_require
99
80
 
100
81
 
101
- def get_serial_class_data(type_class: type[Any]) -> SerialClassDataInspector:
102
- bases = (
103
- [get_serial_class_data(base) for base in type_class.__bases__]
104
- if type_class.__bases__ is not None
105
- else []
106
- )
107
- return SerialClassDataInspector(
108
- bases,
82
+ def _get_merged_serial_class_data(type_class: type[Any]) -> _SerialClassData | None:
83
+ base_class_data = (
109
84
  cast(_SerialClassData, type_class.__unc_serial_data)
110
85
  if hasattr(type_class, "__unc_serial_data")
111
- else _SerialClassData(),
86
+ else None
87
+ )
88
+ if type_class.__bases__ is not None:
89
+ for base in type_class.__bases__:
90
+ curr_base_class_data = _get_merged_serial_class_data(base)
91
+ if curr_base_class_data is not None:
92
+ if base_class_data is None:
93
+ base_class_data = _SerialClassData()
94
+ base_class_data.unconverted_keys |= curr_base_class_data.unconverted_keys
95
+ base_class_data.unconverted_values |= (
96
+ curr_base_class_data.unconverted_values
97
+ )
98
+ base_class_data.to_string_values |= curr_base_class_data.to_string_values
99
+ base_class_data.parse_require |= curr_base_class_data.parse_require
100
+ return base_class_data
101
+
102
+
103
+ def get_serial_class_data(type_class: type[Any]) -> SerialClassDataInspector:
104
+ return SerialClassDataInspector(
105
+ _get_merged_serial_class_data(type_class) or EMPTY_SERIAL_CLASS_DATA
112
106
  )
113
107
 
114
108
 
@@ -0,0 +1,81 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ T = typing.TypeVar("T")
5
+
6
+
7
+ class IdentityHashWrapper(typing.Generic[T]):
8
+ """This allows unhashable types to be used in the SerialUnion, like dict.
9
+ Since we have only one copy of the types themselves, we rely on
10
+ object identity for the hashing."""
11
+
12
+ def __init__(self, inner: T) -> None:
13
+ self.inner = inner
14
+
15
+ def __hash__(self) -> int:
16
+ return id(self.inner)
17
+
18
+
19
+ @dataclasses.dataclass(kw_only=True, frozen=True, eq=True)
20
+ class _SerialUnion:
21
+ """
22
+ This class is to be kept private, to provide flexibility in registration/lookup.
23
+ Places that need the data should access it via help classes/methods.
24
+ """
25
+
26
+ # If specified, indicates the Union has a discriminator which should be used to
27
+ # determine which type to parse.
28
+ discriminator: typing.Optional[str] = None
29
+ discriminator_map: typing.Optional[IdentityHashWrapper[dict[str, type]]] = None
30
+
31
+
32
+ def serial_union_annotation(
33
+ *,
34
+ discriminator: typing.Optional[str] = None,
35
+ discriminator_map: typing.Optional[dict[str, type]] = None,
36
+ ) -> _SerialUnion:
37
+ return _SerialUnion(
38
+ discriminator=discriminator,
39
+ discriminator_map=IdentityHashWrapper(discriminator_map)
40
+ if discriminator_map is not None
41
+ else None,
42
+ )
43
+
44
+
45
+ def _get_serial_union(parsed_type: type[T]) -> _SerialUnion | None:
46
+ if not hasattr(parsed_type, "__metadata__"):
47
+ return None
48
+ metadata = parsed_type.__metadata__ # type:ignore[attr-defined]
49
+ if not isinstance(metadata, tuple) or len(metadata) != 1:
50
+ return None
51
+ serial = metadata[0]
52
+ if not isinstance(serial, _SerialUnion):
53
+ return None
54
+ return serial
55
+
56
+
57
+ class SerialClassInspector(typing.Generic[T]):
58
+ def __init__(self, parsed_type: type[T], serial_union: _SerialUnion) -> None:
59
+ self._parsed_type = parsed_type
60
+ self._serial_union = serial_union
61
+
62
+ def get_union_underlying(self) -> type[T]:
63
+ return typing.get_args(self._parsed_type)[0] # type:ignore[no-any-return]
64
+
65
+ @property
66
+ def discriminator(self) -> typing.Optional[str]:
67
+ return self._serial_union.discriminator
68
+
69
+ @property
70
+ def discriminator_map(self) -> typing.Optional[dict[str, type]]:
71
+ if self._serial_union.discriminator_map is None:
72
+ return None
73
+ return self._serial_union.discriminator_map.inner
74
+
75
+
76
+ def get_serial_union_data(parsed_type: type[T]) -> SerialClassInspector[T] | None:
77
+ serial = _get_serial_union(parsed_type)
78
+ if serial is None:
79
+ return None
80
+
81
+ return SerialClassInspector(parsed_type, serial)
@@ -1,8 +1,5 @@
1
+ from .convert_to_snakecase import convert_dict_to_snake_case
1
2
  from .serialization_helpers import (
2
- convert_dict_to_snake_case,
3
- convert_to_camelcase,
4
- resolve_missing_to_none,
5
- serialize,
6
3
  serialize_for_api,
7
4
  serialize_for_storage,
8
5
  serialize_for_storage_dict,
@@ -10,9 +7,6 @@ from .serialization_helpers import (
10
7
 
11
8
  __all__: list[str] = [
12
9
  "convert_dict_to_snake_case",
13
- "convert_to_camelcase",
14
- "resolve_missing_to_none",
15
- "serialize",
16
10
  "serialize_for_api",
17
11
  "serialize_for_storage",
18
12
  "serialize_for_storage_dict",
@@ -0,0 +1,27 @@
1
+ from typing import (
2
+ Any,
3
+ )
4
+
5
+ from pkgs.argument_parser import camel_to_snake_case
6
+ from pkgs.serialization import (
7
+ MISSING_SENTRY,
8
+ OpaqueKey,
9
+ )
10
+
11
+
12
+ def _key_convert_to_snake_case(o: Any) -> Any:
13
+ if isinstance(o, OpaqueKey):
14
+ return o
15
+ if isinstance(o, str):
16
+ return camel_to_snake_case(o)
17
+ return o
18
+
19
+
20
+ def convert_dict_to_snake_case(data: Any) -> Any:
21
+ return {
22
+ _key_convert_to_snake_case(k): convert_dict_to_snake_case(v)
23
+ if isinstance(v, dict)
24
+ else v
25
+ for k, v in data.items()
26
+ if v != MISSING_SENTRY
27
+ }