UncountablePythonSDK 0.0.21__py3-none-any.whl → 0.0.23__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.21
3
+ Version: 0.0.23
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
@@ -14,6 +14,7 @@ docs/static/favicons/favicon-32x32.png,sha256=U4UU652zGnSeU3P9kUqxPeEnVf6zhtdNdN
14
14
  docs/static/favicons/manifest.json,sha256=6q_3nZkcg_x0xut4eE-xpdeMY1TydwiZIcbXlLAq9X8,437
15
15
  docs/static/favicons/mstile-150x150.png,sha256=eAK4QdEofhdLtfmjuPTpnX3MJqYnvGXsHYUjlcQekyY,1035
16
16
  docs/static/favicons/safari-pinned-tab.svg,sha256=S84fRnz0ZxLnQrKtmmFZytiRyu1xLtMR_RVy5jmwU7k,1926
17
+ examples/async_batch.py,sha256=wpf_3P547375vTIO4pKv5vw6WCkUnzqvw_S3idfhjvM,1122
17
18
  examples/create_entity.py,sha256=54AmZt83EpypxGcYZSIMmWlGz2oAgHFOsKuLSZOcHsI,625
18
19
  examples/upload_files.py,sha256=ZsMChgOioraVHv207YREpivAOf4dq3IxGIBoROoDX_4,482
19
20
  examples/recipe-import/importer.py,sha256=baD71xuNibxDTe3bGHsMEIZEf9Xtb-IumBNpCEV0RZU,1134
@@ -34,16 +35,16 @@ pkgs/strenum_compat/__init__.py,sha256=wXRFeNvBm8RU6dy1PFJ5sRLgUIEeH_DVR95Sv5qpG
34
35
  pkgs/strenum_compat/strenum_compat.py,sha256=uOUAgpYTjHs1MX8dG81jRlyTkt3KNbkV_25zp7xTX2s,36
35
36
  pkgs/type_spec/__init__.py,sha256=h5DmJTca4QVV10sZR1x0-MlkZfuGYDfapR3zHvXfzto,19
36
37
  pkgs/type_spec/__main__.py,sha256=5bJaX9Y_-FavP0qwzhk-z-V97UY7uaezJTa1zhO_HHQ,1048
37
- pkgs/type_spec/builder.py,sha256=W9VA7JhPkLw3lau3FjYlIiBGzRs5LBPTZr0tTO2KVuU,39570
38
+ pkgs/type_spec/builder.py,sha256=9fEcZkasqovH8W4PtQvXtPu4psS_a-HAyu6Wsfk5L8w,43041
38
39
  pkgs/type_spec/config.py,sha256=INfEiDcUsZFUKasHprsE6i33siPB0RnfmTKOsWcGnQ8,5043
39
- pkgs/type_spec/emit_io_ts.py,sha256=gCEfS81w_ifqjLVJ3_cpy9Gq03o6H5nEsh35WAkqGGE,5606
40
- pkgs/type_spec/emit_open_api.py,sha256=4ihGO_neLL1A9zdoytQ5YypgBqyM0WKzBHo9wtsBPuE,17772
41
- pkgs/type_spec/emit_open_api_util.py,sha256=gMUB-fSrnTp6NyK_HbY02SP6cqSRObo-KIojDu4rn44,1900
42
- pkgs/type_spec/emit_python.py,sha256=CCHlOIMKdGu1VoaP7ly6dZ2cYank9jaAr8NDN50rFiE,42411
43
- pkgs/type_spec/emit_typescript.py,sha256=KOCcPuwSLRSt3pIp06Nq9exledq-kWtrA4RnUpeGMi8,17505
40
+ pkgs/type_spec/emit_io_ts.py,sha256=Ghd8XYqyNYldHQDepwa9GLfHXcoi48ztBw84K28ETic,5707
41
+ pkgs/type_spec/emit_open_api.py,sha256=RJzzOZ7Ti1PNTPn42ZGW7OA6L9KRQhKOTw4elXzJKpE,23524
42
+ pkgs/type_spec/emit_open_api_util.py,sha256=F6qouGVm2-WGYkoubbBtRu00V4e30bWJ0fDBhigBEfg,2248
43
+ pkgs/type_spec/emit_python.py,sha256=aGPnp86DZN0N7c5eiBHVAT_smDf1uCLmRldDhxmWx9g,42673
44
+ pkgs/type_spec/emit_typescript.py,sha256=4hpCJwiDf-v8LJaNFVfFtf8zvtG73YNPFwwa_5NuffI,17729
44
45
  pkgs/type_spec/emit_typescript_util.py,sha256=93FzJnpYse4PKFzgdw4DGV4zFTi5tF4WR-CIi7cW498,873
45
- pkgs/type_spec/load_types.py,sha256=AB334CF-368dpMNXE2QIFT7WxESvuew2RK_GG2MREng,2211
46
- pkgs/type_spec/open_api_util.py,sha256=88zgI_lHi6SjCaNfKpoiCjd1o0Npysjk-7iCXw0a3Hs,7463
46
+ pkgs/type_spec/load_types.py,sha256=xEHwdB_miR3vNs161Oy1luafE0VC-yk9-utAyCJmbEo,3629
47
+ pkgs/type_spec/open_api_util.py,sha256=TFbK2bkYT6S4qPQGO3_G2mfVgtNB26d31kwaHQ9y99E,6730
47
48
  pkgs/type_spec/test.py,sha256=4ueujBq-pEgnX3Z69HyPmD-bullFXmpixcpVzfOkhP4,489
48
49
  pkgs/type_spec/util.py,sha256=6m6MPfY-SwjyZf2FWQKclswWB5o7gcdd-3tdpViPYOQ,4844
49
50
  pkgs/type_spec/actions_registry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -61,8 +62,8 @@ pkgs/type_spec/value_spec/types.py,sha256=a2zxbbCRWepY1l8OtjeCDKgBKFPFHVgV99oP6p
61
62
  type_spec/external/api/batch/execute_batch.yaml,sha256=gpdSev3sLEC_cMVSZdj-9bc_XDFDqdPdOII9Ojme2N8,1170
62
63
  type_spec/external/api/batch/execute_batch_load_async.yaml,sha256=gcn51NWLiSvlytz8k3_pDOVJLCGfdivKJPG4I9Q8CZc,435
63
64
  type_spec/external/api/chemical/convert_chemical_formats.yaml,sha256=EidTxMCRs-ko5yMFDptJPyAEWYZVruP41OG3cwyBLQQ,1069
64
- type_spec/external/api/entity/create_entities.yaml,sha256=NVA7I1b4xf81urMKaiaiQPgvykEvjKslfOAN2gOhMIE,1441
65
- type_spec/external/api/entity/create_entity.yaml,sha256=2itcDpiuZHckhn45ZJyrssMOS9_CgBwFGf-MQyUyImw,1636
65
+ type_spec/external/api/entity/create_entities.yaml,sha256=RKjmb_iY4dVHf3aQUCU-OrlbTLLsCkULQ9uEfa8BMFY,1506
66
+ type_spec/external/api/entity/create_entity.yaml,sha256=Orz-3RZsNy5cWXlA3BKVQDYGnGGXlCXOtsOxDSm_nRY,1701
66
67
  type_spec/external/api/entity/get_entities_data.yaml,sha256=3XujG7bOpuBQlfFrYtG3L4fBk7LsmdSekmP9iU0zjF0,796
67
68
  type_spec/external/api/entity/list_entities.yaml,sha256=H2YVv6il-XVKd_7IipZqauTDvWCrvHok7z47bDH2sI4,1798
68
69
  type_spec/external/api/entity/resolve_entity_ids.yaml,sha256=Zf3OhAohwLJO7wWj0e-sK5lhIsXlD8A5Bu3OGjY4-tA,732
@@ -89,7 +90,7 @@ type_spec/external/api/recipes/associate_recipe_as_lot.yaml,sha256=8wzeJg5njt4qG
89
90
  type_spec/external/api/recipes/create_recipe.yaml,sha256=mGLyKJI3pN_7nU4rcSqCO3WjuKhO_odZ2pewVgYcMUU,1322
90
91
  type_spec/external/api/recipes/create_recipes.yaml,sha256=eXMlXRpB5TFt1mUTECBa4aAIG3KrxYT2mJ5vxmZ9Q3A,1503
91
92
  type_spec/external/api/recipes/disassociate_recipe_as_input.yaml,sha256=qTKQCNBNwLnbr22DQVLA6b80BdBhwnDbX1c4KoCKUm8,477
92
- type_spec/external/api/recipes/edit_recipe_inputs.yaml,sha256=CmolwyHSGJOQ33nJH7SQUYK-p2CWNEQ9yTstNxALv18,2631
93
+ type_spec/external/api/recipes/edit_recipe_inputs.yaml,sha256=ljewBw77ucvl_m_KWmSJp-nVQGvVG6cb7xVOgPIlueU,2573
93
94
  type_spec/external/api/recipes/get_curve.yaml,sha256=zQpPwOYqojY-YwmTjbqoGtUxpYm3vne2sYpglWbPnpw,779
94
95
  type_spec/external/api/recipes/get_recipe_calculations.yaml,sha256=ZE7PzfWrjS7TiO4q7iyCwEj5In8GwO6fFIYGqUlTEXo,1240
95
96
  type_spec/external/api/recipes/get_recipe_links.yaml,sha256=Vwm0OVWl3VvDaI7chY_oZQqD8xZ1u09iFWKkZKn1ITo,766
@@ -103,8 +104,9 @@ type_spec/external/api/recipes/set_recipe_tags.yaml,sha256=IrdkbryxZjNy8n4aMNLRT
103
104
  type_spec/external/api/triggers/run_trigger.yaml,sha256=c8xDV3bQRjcRRDG4Y7kdQmMMu1fj3ae5eUi-Sdbsi54,405
104
105
  uncountable/__init__.py,sha256=281cC2hs8pbrD0jVKMol-tbWSh7Zcsc8oRT42dKteyE,102
105
106
  uncountable/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
- uncountable/core/__init__.py,sha256=OYipeBMLl_Mk2LApzJqzvOWSyQ12tdOXURp1GsKSjyE,190
107
- uncountable/core/client.py,sha256=ebBwERBKbdp7x5y77bFeyyGejIi02fjFZ4AMCJUfMpA,4507
107
+ uncountable/core/__init__.py,sha256=7xUnbSWJzS31sWg0jCe5nIksn5s0PVdwUrUmDttHfCY,258
108
+ uncountable/core/async_batch.py,sha256=0cRmCr6Z9sNxZyfY9Dl8wlCA4anISVZuHGgBegHhUbc,749
109
+ uncountable/core/client.py,sha256=7bLuACxMJZsckSfL2j-p-XThYdvDAUAwm5nND9s-v1o,6946
108
110
  uncountable/core/file_upload.py,sha256=zTpAFSd7_-TmEVWxOn1rDznyWE6_AdZyuDQC3LP34iI,2667
109
111
  uncountable/core/types.py,sha256=gQtCw1-WSRak_ypFlGI1Ea9iZBP9zDeFq6XQtiXBlZA,459
110
112
  uncountable/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -119,19 +121,19 @@ uncountable/integration/db/connect.py,sha256=iI9e8a2hfbFP-dvH0MGLsrG-RpM0dHKCL-o
119
121
  uncountable/integration/executors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
120
122
  uncountable/integration/executors/script_executor.py,sha256=6oMPAFe0PUdqt76e8jMi4vXszGVsVHLULob7Qbl3o38,816
121
123
  uncountable/types/__init__.py,sha256=fTNNsvFFkjp4acdBA8eS00OZVnDo_Zn7aRwPs8SFnrs,6024
122
- uncountable/types/async_batch.py,sha256=VMLB3ss1xzsFXovnWIaxgEVb4DWz7Agnf_MUNDaoRQc,1524
123
- uncountable/types/async_batch_processor.py,sha256=fwzfk0XY0L1ulzNi3dT8Aky5MyWPNK6GDStZ_I8Icoc,5914
124
+ uncountable/types/async_batch.py,sha256=FtIoDCeyt9rF5hknQs6Sw-vjiYBMJbe1re0-4bk_6VY,1578
125
+ uncountable/types/async_batch_processor.py,sha256=YksvTyJaZ3rqpZx4UXofUf7XU1Br4aNBNPZXB3LLtkA,5940
124
126
  uncountable/types/base.py,sha256=w3BRf8SAvYPlKrcJtJcQ_WhCU3A9zy0VuRTRWRFKVUA,2709
125
127
  uncountable/types/calculations.py,sha256=16J-KKMp-I8ZQUkYNmKCHfAn6DGb99cFinALcDIdGHY,562
126
128
  uncountable/types/chemical_structure.py,sha256=zQKl53DGtQQONIUHFXuwjWLQaG7FPZY7x6SBSOzkGV0,758
127
- uncountable/types/client_base.py,sha256=w-UnuzUSg8l_5kFhxo3w_b0_UC0eT-mtHheoA0f-U58,45967
129
+ uncountable/types/client_base.py,sha256=nsV7NWN5UoqjCfFSw9XLXT_rbpfZeWRrOLxqeZ20Sjw,47114
128
130
  uncountable/types/curves.py,sha256=qYyRntMmFNonEwTrGhquMLbgMqjyP1moQflNTP0FMec,1308
129
131
  uncountable/types/entity.py,sha256=NjMZrqBwQ7sZe_oUuJqy9IEG7dWZmFMkQQXJ0_odcnA,11637
130
132
  uncountable/types/experiment_groups.py,sha256=ZBEk06F4n98Jz3oEA09WaDmw5rqPs7iVAm_Ysr4gc_o,599
131
133
  uncountable/types/field_values.py,sha256=2unBAeBqQPqLQKaL6nGpnDDksZ-5MZepgEF3sgy6oOk,1670
132
134
  uncountable/types/fields.py,sha256=eGtZ6axTYGFxLmPAyri2LwlcR4SZ2sX2c6QDX0ybKz0,570
133
135
  uncountable/types/id_source.py,sha256=Y3suURq3L1SahZ2oHPD986SU0l3Ik-ZzH38aQKgc1Fg,1341
134
- uncountable/types/identifier.py,sha256=O_J3sHsG3pFeSIxUKmgIK97FZl6f4qkRDwsNLvY7ymE,1389
136
+ uncountable/types/identifier.py,sha256=94-O3H_qNrA48tf3srwPwdu8HURkLl7_-88kUnwElZg,1455
135
137
  uncountable/types/input_attributes.py,sha256=u-JABoZ-Ij1Ynq5g6MxOgRdQeYbM7OnGP2q_N7KuVdw,826
136
138
  uncountable/types/inputs.py,sha256=q7fNGaSKIk3R6uXCEhSQpiHvXu82YcK3oZHDI7bxE88,1597
137
139
  uncountable/types/outputs.py,sha256=hSUlu41sisYKIZpPrj1G1DRfKm6hsKNcd1eNKFYb-4w,671
@@ -144,7 +146,7 @@ uncountable/types/recipe_links.py,sha256=RldSV7SdeBYa0bx02DzMg4jfPdgrlMRE40T16Fd
144
146
  uncountable/types/recipe_metadata.py,sha256=cebGg_lJzqZzGnKnDgmuQFrw4Xhoz6HEiGM6G0az120,1437
145
147
  uncountable/types/recipe_output_metadata.py,sha256=XJA8R1r4NTzyR_DhMkmH4ZtYD-vqpvBMji9Be8OcFmo,613
146
148
  uncountable/types/recipe_tags.py,sha256=lYpksHAxXCcIjZKR7JoZOTH2cBSovwxZaHwjZy_yqiQ,581
147
- uncountable/types/recipe_workflow_steps.py,sha256=bHVkwlsGODLpRFMJ2Ja-5FV1kqYjed8J0J0gU1dmTLQ,2615
149
+ uncountable/types/recipe_workflow_steps.py,sha256=LmyFwWWwJv30vuaQ4qtd0hzDdeJaIxHQZqwRb1Wi_6A,2626
148
150
  uncountable/types/response.py,sha256=ZI0CG7ZxBM2k5_W-6mNMU3UlB0p1i-0nrwOvsMaS-vU,620
149
151
  uncountable/types/units.py,sha256=_kZ7KkXIbRiY2fOdkTsbJBpWRah5TCC2WWiG05e-1DA,565
150
152
  uncountable/types/users.py,sha256=SUjNHBDcImKnnE7IN096Wfr1fmjNjCkQ7yQgKUPffz8,588
@@ -156,8 +158,8 @@ uncountable/types/api/batch/execute_batch_load_async.py,sha256=dcdGFibO8fUDpC__X
156
158
  uncountable/types/api/chemical/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
157
159
  uncountable/types/api/chemical/convert_chemical_formats.py,sha256=COGzkfpTL_Ermg2cbasoVKGAxDAtJaTFay18IZtrWCA,1305
158
160
  uncountable/types/api/entity/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
159
- uncountable/types/api/entity/create_entities.py,sha256=oZVBovL3pdBfoKpaOXJcPtUyDxjKgFnYEqKfGx_ZcvE,1649
160
- uncountable/types/api/entity/create_entity.py,sha256=J6eY8b_XEQROFR7FI7In3zmFngaCBY5TcasMi4wy9t8,1819
161
+ uncountable/types/api/entity/create_entities.py,sha256=vzo5hS1qcmjQdfyCMarSu8MRcRGSiholOVSCfjXlA1k,1703
162
+ uncountable/types/api/entity/create_entity.py,sha256=ausozCQ3qPM9YUQ87bOTCKOm-zkhn4CSLJr9jLc9n2U,1873
161
163
  uncountable/types/api/entity/get_entities_data.py,sha256=XjrJGZucIn1TYUlDLRnRA0JTQw-vXHIAT-m0H9hk37A,1170
162
164
  uncountable/types/api/entity/list_entities.py,sha256=_bIIZJj3N0E6YiHgqzfCOKxD1fQW6biWJQMp5wIVbBw,1514
163
165
  uncountable/types/api/entity/resolve_entity_ids.py,sha256=AidGpPmI9ATDv0E7vd9LDOl3n3beGxUlRojh5uZrkl4,1086
@@ -193,7 +195,7 @@ uncountable/types/api/recipes/associate_recipe_as_lot.py,sha256=bTYjbnY3B7GKz4MV
193
195
  uncountable/types/api/recipes/create_recipe.py,sha256=Ni00efkcPkQ3WTIgDHzkfu1qoc52ReV9VT0wwwPOT4g,1364
194
196
  uncountable/types/api/recipes/create_recipes.py,sha256=qwIYa8hfcjY7_VOFt9lxmVtJ-HOJqQN3GDNSbZsRCZU,1544
195
197
  uncountable/types/api/recipes/disassociate_recipe_as_input.py,sha256=L25fpiK1Y5PByPVVgsZy9t4podz3xSSLIwKHj8CUrSg,913
196
- uncountable/types/api/recipes/edit_recipe_inputs.py,sha256=Ae7YMUsONp1bUVdaznetWiw-THPndnzFI-t4iMWV_eg,3338
198
+ uncountable/types/api/recipes/edit_recipe_inputs.py,sha256=pTw606AgLhb-oJjfj1WPyEcJ4B0tgZsvEKeqP5VZ1gY,3281
197
199
  uncountable/types/api/recipes/get_curve.py,sha256=UIWfpqtU5sQokaxwYfQFNFl6HMyzWEF_Sjd8UMz0U88,939
198
200
  uncountable/types/api/recipes/get_recipe_calculations.py,sha256=eQmkdZzCEuq8S2f_kf_7GPvDLX1pTnY1CRmkK0SkMCI,1472
199
201
  uncountable/types/api/recipes/get_recipe_links.py,sha256=hk5dfQjv7yU2r-S9b8vwWEJLPHqU0-M6SFiTLMR3fVk,985
@@ -206,7 +208,7 @@ uncountable/types/api/recipes/set_recipe_outputs.py,sha256=QYq39TNchQ80ET1C77OE9
206
208
  uncountable/types/api/recipes/set_recipe_tags.py,sha256=U710hgq9-t6QZGRB-ZGHskpt4iXwYEjIRb67eh3P518,2453
207
209
  uncountable/types/api/triggers/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
208
210
  uncountable/types/api/triggers/run_trigger.py,sha256=9m9M8-nlGB_sAU2Qm2lWugp4h4Osqj6QpjNfU8osd1U,901
209
- UncountablePythonSDK-0.0.21.dist-info/METADATA,sha256=_xqCDeMHkCp6YfUDs-Wr2wc7e2ErYtHzjA1f6YqcGfc,1613
210
- UncountablePythonSDK-0.0.21.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
211
- UncountablePythonSDK-0.0.21.dist-info/top_level.txt,sha256=HaMiBnH1wA7SG9-RVHIJPBH3l8X5gee2jUf-77Nz-Dk,41
212
- UncountablePythonSDK-0.0.21.dist-info/RECORD,,
211
+ UncountablePythonSDK-0.0.23.dist-info/METADATA,sha256=W6CHPkwSSSiPK9zlnXFS8WjX7sCL8o2-76dd4m2CJkY,1613
212
+ UncountablePythonSDK-0.0.23.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
213
+ UncountablePythonSDK-0.0.23.dist-info/top_level.txt,sha256=HaMiBnH1wA7SG9-RVHIJPBH3l8X5gee2jUf-77Nz-Dk,41
214
+ UncountablePythonSDK-0.0.23.dist-info/RECORD,,
@@ -0,0 +1,36 @@
1
+ from decimal import Decimal
2
+ from uncountable.core import AuthDetailsApiKey, Client
3
+ from uncountable.core import AsyncBatchProcessor
4
+ from uncountable.types import (
5
+ recipe_metadata,
6
+ )
7
+ from uncountable.types.identifier import IdentifierKeyBatchReference
8
+ from uncountable.types.recipe_identifiers import (
9
+ RecipeIdentifierEditableName,
10
+ RecipeIdentifiers,
11
+ )
12
+
13
+
14
+ client = Client(
15
+ base_url="https://app.uncountable.com",
16
+ auth_details=AuthDetailsApiKey(
17
+ api_id="X",
18
+ api_secret_key="X",
19
+ ),
20
+ )
21
+ batch_loader = AsyncBatchProcessor(client=client)
22
+ recipe_identifiers: RecipeIdentifiers = []
23
+ recipe_identifiers.append(
24
+ RecipeIdentifierEditableName(editable_name="My recipe from API")
25
+ )
26
+ req = batch_loader.create_recipe(
27
+ material_family_id=1, workflow_id=1, identifiers=recipe_identifiers
28
+ )
29
+ created_recipe_reference = req.batch_reference
30
+ batch_loader.set_recipe_metadata(
31
+ recipe_key=IdentifierKeyBatchReference(reference=created_recipe_reference),
32
+ recipe_metadata=[
33
+ recipe_metadata.MetadataValue(metadata_id=7, value_numeric=Decimal(38))
34
+ ],
35
+ )
36
+ job_id = batch_loader.send()
pkgs/type_spec/builder.py CHANGED
@@ -10,7 +10,7 @@ import re
10
10
  from collections import defaultdict
11
11
  from dataclasses import MISSING, dataclass
12
12
  from enum import Enum, StrEnum, auto
13
- from typing import Any, Optional
13
+ from typing import Any, Optional, Self
14
14
 
15
15
  from . import util
16
16
  from .util import parse_type_str, unused
@@ -86,6 +86,7 @@ class BaseTypeName(StrEnum):
86
86
  s_optional = "Optional"
87
87
  s_string = "String"
88
88
  s_tuple = "Tuple"
89
+ s_readonly_array = "ReadonlyArray"
89
90
  s_union = "Union"
90
91
 
91
92
  # For a root class that defines properties
@@ -184,6 +185,34 @@ class SpecTypeInstance(SpecType):
184
185
  return defn_type + self.parameters
185
186
 
186
187
 
188
+ @dataclass(kw_only=True)
189
+ class SpecEndpointExample:
190
+ summary: str
191
+ description: str
192
+ arguments: dict[str, object]
193
+ data: dict[str, object]
194
+
195
+
196
+ @dataclass(kw_only=True)
197
+ class SpecGuide:
198
+ title: str
199
+ markdown_content: str
200
+ html_content: str
201
+
202
+
203
+ @dataclass(kw_only=True, frozen=True)
204
+ class RootGuideKey:
205
+ pass
206
+
207
+
208
+ @dataclass(kw_only=True, frozen=True)
209
+ class EndpointGuideKey:
210
+ path: str
211
+
212
+
213
+ SpecGuideKey = RootGuideKey | EndpointGuideKey
214
+
215
+
187
216
  class SpecTypeLiteralWrapper(SpecType):
188
217
  def __init__(
189
218
  self,
@@ -672,6 +701,32 @@ class ResultType(StrEnum):
672
701
  RE_ENDPOINT_ROOT = re.compile(r"\${([_a-z]+)}")
673
702
 
674
703
 
704
+ @dataclass(kw_only=True, frozen=True)
705
+ class _EndpointPathDetails:
706
+ root: str
707
+ root_path: str
708
+ resolved_path: str
709
+
710
+
711
+ def _resolve_endpoint_path(
712
+ path: str, api_endpoints: dict[str, str]
713
+ ) -> _EndpointPathDetails:
714
+ root_path_source = path.split("/")[0]
715
+ root_match = RE_ENDPOINT_ROOT.fullmatch(root_path_source)
716
+ if root_match is None:
717
+ raise Exception(f"invalid-api-path-root:{root_path_source}")
718
+
719
+ root_var = root_match.group(1)
720
+ root_path = api_endpoints[root_var]
721
+
722
+ _, *rest_path = path.split("/", 1)
723
+ resolved_path = "/".join([root_path] + rest_path)
724
+
725
+ return _EndpointPathDetails(
726
+ root=root_var, root_path=root_path, resolved_path=resolved_path
727
+ )
728
+
729
+
675
730
  class SpecEndpoint:
676
731
  method: RouteMethod
677
732
  root: str
@@ -748,14 +803,10 @@ class SpecEndpoint:
748
803
 
749
804
  self.result_type = ResultType(data.get("result_type", ResultType.json.value))
750
805
 
806
+ path_details = _resolve_endpoint_path(data["path"], builder.api_endpoints)
807
+ self.root = path_details.root
808
+ self.path_root = path_details.root_path
751
809
  self.desc = data.get("desc")
752
-
753
- root_match = RE_ENDPOINT_ROOT.fullmatch(path[0])
754
- if root_match is None:
755
- raise Exception(f"invalid-api-path-root:{path[0]}")
756
-
757
- self.root = root_match.group(1)
758
- self.path_root = builder.api_endpoints[self.root]
759
810
  # IMPROVE: remove need for is_external flag
760
811
  self.is_external = self.path_root == "api/external"
761
812
  self.has_attachment = data.get("has_attachment", False)
@@ -764,6 +815,10 @@ class SpecEndpoint:
764
815
  not is_sdk or self.desc is not None
765
816
  ), f"Endpoint description required for SDK endpoints, missing: {path}"
766
817
 
818
+ @property
819
+ def resolved_path(self: Self) -> str:
820
+ return f"{self.path_root}/{self.path_dirname}/{self.path_basename}"
821
+
767
822
 
768
823
  def _parse_const(
769
824
  builder: SpecBuilder,
@@ -1014,6 +1069,8 @@ class SpecBuilder:
1014
1069
  self.pending: list[NamespaceDataPair] = []
1015
1070
  self.parts: dict[str, dict[str, str]] = defaultdict(dict)
1016
1071
  self.preparts: dict[str, dict[str, str]] = defaultdict(dict)
1072
+ self.examples: dict[str, list[SpecEndpointExample]] = defaultdict(list)
1073
+ self.guides: dict[SpecGuideKey, list[SpecGuide]] = defaultdict(list)
1017
1074
  self.api_endpoints = api_endpoints
1018
1075
  base_namespace = SpecNamespace(name=base_namespace_name)
1019
1076
  for base_type in BaseTypeName:
@@ -1198,5 +1255,53 @@ class SpecBuilder:
1198
1255
  def add_prepart_file(self, target: str, name: str, data: str) -> None:
1199
1256
  self.preparts[target][name] = data
1200
1257
 
1258
+ def add_example_file(self, data: dict[str, object]) -> None:
1259
+ path_details = _resolve_endpoint_path(str(data["path"]), self.api_endpoints)
1260
+
1261
+ examples_data = data["examples"]
1262
+ if not isinstance(examples_data, list):
1263
+ raise Exception(
1264
+ f"'examples' in example files are expected to be a list, endpoint_path={path_details.resolved_path}"
1265
+ )
1266
+ for example in examples_data:
1267
+ arguments = example["arguments"]
1268
+ data_example = example["data"]
1269
+ if not isinstance(arguments, dict) or not isinstance(data_example, dict):
1270
+ raise Exception(
1271
+ f"'arguments' and 'data' fields must be dictionaries for each endpoint example, endpoint={path_details.resolved_path}"
1272
+ )
1273
+ self.examples[path_details.resolved_path].append(
1274
+ SpecEndpointExample(
1275
+ summary=str(example["summary"]),
1276
+ description=str(example["description"]),
1277
+ arguments=arguments,
1278
+ data=data_example,
1279
+ )
1280
+ )
1281
+
1282
+ def add_guide_file(self, file_content: str) -> None:
1283
+ import markdown
1284
+
1285
+ md = markdown.Markdown(extensions=["meta"])
1286
+ html = md.convert(file_content)
1287
+ meta: dict[str, list[str]] = md.Meta # type: ignore[attr-defined]
1288
+ title_meta: list[str] | None = meta.get("title")
1289
+ if title_meta is None:
1290
+ raise Exception("guides requier a title in the meta section")
1291
+
1292
+ path_meta: list[str] | None = meta.get("path")
1293
+ guide_key: SpecGuideKey = RootGuideKey()
1294
+ if path_meta is not None:
1295
+ path_details = _resolve_endpoint_path("".join(path_meta), self.api_endpoints)
1296
+ guide_key = EndpointGuideKey(path=path_details.resolved_path)
1297
+
1298
+ self.guides[guide_key].append(
1299
+ SpecGuide(
1300
+ title="".join(title_meta),
1301
+ html_content=html,
1302
+ markdown_content=file_content,
1303
+ )
1304
+ )
1305
+
1201
1306
  def resolve_proper_name(self, stype: SpecTypeDefn) -> str:
1202
1307
  return f"{'.'.join(stype.namespace.path)}.{stype.name}"
@@ -118,7 +118,10 @@ def refer_to_io_ts(
118
118
  stype: builder.SpecType,
119
119
  ) -> str:
120
120
  if isinstance(stype, builder.SpecTypeInstance):
121
- if stype.defn_type.name == builder.BaseTypeName.s_list:
121
+ if (
122
+ stype.defn_type.name == builder.BaseTypeName.s_list
123
+ or stype.defn_type.name == builder.BaseTypeName.s_readonly_array
124
+ ):
122
125
  spec = refer_to_io_ts(ctx, stype.parameters[0])
123
126
  return f"IO.array({spec})"
124
127
  if stype.defn_type.name == builder.BaseTypeName.s_union: